chromium/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/geo/VisibleNetworksTracker.java

// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

package org.chromium.chrome.browser.omnibox.geo;

import android.content.Context;
import android.os.SystemClock;

import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;

import org.chromium.base.Log;
import org.chromium.base.ThreadUtils;

/** VisibleNetworksTracker keeps track of the visible networks. */
class VisibleNetworksTracker {
    private static final String TAG = "VNTracker";

    @VisibleForTesting static final int AGE_THRESHOLD = 5 * 60 * 1000; // 5 min

    @Nullable private static VisibleNetworks sVisibleNetworks;
    private static long sVisibleNetworksTime = Long.MAX_VALUE;

    private static boolean sOngoingRefresh;

    private static VisibleNetworks sVisibleNetworksForTesting;
    private static boolean sUseVisibleNetworksForTesting;

    /**
     * Returns last known visible networks. It returns the cached value if the cache is valid or it
     * computes the simplest possible visibleNetworks fast, and triggers a background asynchronous
     * refresh. Might return null if visible networks cannot be computed.
     */
    static @Nullable VisibleNetworks getLastKnownVisibleNetworks(final Context context) {
        if (sUseVisibleNetworksForTesting) return sVisibleNetworksForTesting;

        if (isValidCachedVisibleNetworks()) return getCachedVisibleNetworks();

        VisibleNetworks visibleNetworks = null;
        try {
            // Include only the connected cell/wifi to minimize latency and compute the simplest
            // visible networks possible.
            visibleNetworks = PlatformNetworksManager.computeConnectedNetworks(context);
        } catch (Exception e) {
            Log.e(TAG, "Failed to get the visible networks. Error: ", e.toString());
        }
        // Update cache asynchronously.
        refreshVisibleNetworks(context);

        return visibleNetworks;
    }

    /**
     * Determines if the visible networks need to be refreshed and asynchronously updates them if
     * needed.
     */
    static void refreshVisibleNetworks(final Context context) {
        ThreadUtils.assertOnUiThread();
        if (isValidCachedVisibleNetworks() || sOngoingRefresh) {
            return;
        }

        sOngoingRefresh = true;
        PlatformNetworksManager.computeVisibleNetworks(
                context, VisibleNetworksTracker::setCachedVisibleNetworks);
    }

    @Nullable
    @VisibleForTesting
    static VisibleNetworks getCachedVisibleNetworks() {
        return sVisibleNetworks;
    }

    @VisibleForTesting
    static long getCachedVisibleNetworksTime() {
        return sVisibleNetworksTime;
    }

    @VisibleForTesting
    static void clearCache() {
        setCachedVisibleNetworks(null);
        sVisibleNetworksTime = Long.MAX_VALUE;
    }

    static void setVisibleNetworksForTesting(VisibleNetworks visibleNetworksForTesting) {
        sVisibleNetworksForTesting = visibleNetworksForTesting;
        sUseVisibleNetworksForTesting = true;
    }

    private static void setCachedVisibleNetworks(VisibleNetworks visibleNetworks) {
        ThreadUtils.assertOnUiThread();
        sOngoingRefresh = false;
        sVisibleNetworks = visibleNetworks;
        sVisibleNetworksTime = SystemClock.elapsedRealtime();
    }

    private static boolean isValidCachedVisibleNetworks() {
        return sVisibleNetworks != null
                && sVisibleNetworksTime != Long.MAX_VALUE
                && !sVisibleNetworks.isEmpty()
                && SystemClock.elapsedRealtime() - sVisibleNetworksTime < AGE_THRESHOLD;
    }
}