Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 0edf09bd authored by Tony Mantler's avatar Tony Mantler
Browse files

Make WifiTracker lifecycle aware

Bug: 66953027
Test: m -j checkbuild
Change-Id: I0609a16a3b2afd2af689ff44896a9a82138fa370
parent 2f3072bc
Loading
Loading
Loading
Loading
+62 −42
Original line number Diff line number Diff line
@@ -24,7 +24,6 @@ import android.net.ConnectivityManager;
import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkInfo;
import android.net.NetworkInfo.DetailedState;
import android.net.NetworkKey;
import android.net.NetworkRequest;
import android.net.NetworkScoreManager;
@@ -36,10 +35,14 @@ import android.net.wifi.WifiManager;
import android.net.wifi.WifiNetworkScoreCache;
import android.net.wifi.WifiNetworkScoreCache.CacheListener;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
import android.os.Process;
import android.provider.Settings;
import android.support.annotation.GuardedBy;
import android.support.annotation.NonNull;
import android.support.annotation.VisibleForTesting;
import android.text.format.DateUtils;
import android.util.ArraySet;
import android.util.Log;
@@ -47,8 +50,12 @@ import android.util.SparseArray;
import android.util.SparseIntArray;
import android.widget.Toast;

import com.android.internal.annotations.VisibleForTesting;
import com.android.settingslib.R;
import com.android.settingslib.core.lifecycle.Lifecycle;
import com.android.settingslib.core.lifecycle.LifecycleObserver;
import com.android.settingslib.core.lifecycle.events.OnDestroy;
import com.android.settingslib.core.lifecycle.events.OnStart;
import com.android.settingslib.core.lifecycle.events.OnStop;

import java.io.PrintWriter;
import java.util.ArrayList;
@@ -64,7 +71,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
/**
 * Tracks saved or available wifi networks and their state.
 */
public class WifiTracker {
public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestroy {
    /**
     * Default maximum age in millis of cached scored networks in
     * {@link AccessPoint#mScoredNetworkCache} to be used for speed label generation.
@@ -80,7 +87,7 @@ public class WifiTracker {
     * and used so as to assist with in-the-field WiFi connectivity debugging  */
    public static boolean sVerboseLogging;

    // TODO(b/36733768): Remove flag includeSaved and includePasspoints.
    // TODO(b/36733768): Remove flag includeSaved

    // TODO: Allow control of this?
    // Combo scans can take 5-6s to complete - set to 10s.
@@ -96,9 +103,9 @@ public class WifiTracker {
    private final WifiListener mListener;
    private final boolean mIncludeSaved;
    private final boolean mIncludeScans;
    private final boolean mIncludePasspoints;
    @VisibleForTesting final MainHandler mMainHandler;
    @VisibleForTesting final WorkHandler mWorkHandler;
    @VisibleForTesting MainHandler mMainHandler;
    @VisibleForTesting WorkHandler mWorkHandler;
    private HandlerThread mWorkThread;

    private WifiTrackerNetworkCallback mNetworkCallback;

@@ -142,7 +149,7 @@ public class WifiTracker {
    private WifiInfo mLastInfo;

    private final NetworkScoreManager mNetworkScoreManager;
    private final WifiNetworkScoreCache mScoreCache;
    private WifiNetworkScoreCache mScoreCache;
    private boolean mNetworkScoringUiEnabled;
    private long mMaxSpeedLabelScoreCacheAge;

@@ -169,51 +176,43 @@ public class WifiTracker {
        return filter;
    }

    /**
     * Use the lifecycle constructor below whenever possible
     */
    @Deprecated
    public WifiTracker(Context context, WifiListener wifiListener,
            boolean includeSaved, boolean includeScans) {
        this(context, wifiListener, null, includeSaved, includeScans);
    }

    public WifiTracker(Context context, WifiListener wifiListener, Looper workerLooper,
            boolean includeSaved, boolean includeScans) {
        this(context, wifiListener, workerLooper, includeSaved, includeScans, false);
        this(context, wifiListener, includeSaved, includeScans,
                context.getSystemService(WifiManager.class),
                context.getSystemService(ConnectivityManager.class),
                context.getSystemService(NetworkScoreManager.class),
                newIntentFilter());
    }

    public WifiTracker(Context context, WifiListener wifiListener,
            boolean includeSaved, boolean includeScans, boolean includePasspoints) {
        this(context, wifiListener, null, includeSaved, includeScans, includePasspoints);
    }

    public WifiTracker(Context context, WifiListener wifiListener, Looper workerLooper,
                       boolean includeSaved, boolean includeScans, boolean includePasspoints) {
        this(context, wifiListener, workerLooper, includeSaved, includeScans, includePasspoints,
            @NonNull Lifecycle lifecycle, boolean includeSaved, boolean includeScans) {
        this(context, wifiListener, includeSaved, includeScans,
                context.getSystemService(WifiManager.class),
                context.getSystemService(ConnectivityManager.class),
                context.getSystemService(NetworkScoreManager.class),
                Looper.myLooper(), newIntentFilter());
                newIntentFilter());
        lifecycle.addObserver(this);
    }

    @VisibleForTesting
    WifiTracker(Context context, WifiListener wifiListener, Looper workerLooper,
                boolean includeSaved, boolean includeScans, boolean includePasspoints,
    WifiTracker(Context context, WifiListener wifiListener,
            boolean includeSaved, boolean includeScans,
            WifiManager wifiManager, ConnectivityManager connectivityManager,
                NetworkScoreManager networkScoreManager, Looper currentLooper,
            NetworkScoreManager networkScoreManager,
            IntentFilter filter) {
        if (!includeSaved && !includeScans) {
            throw new IllegalArgumentException("Must include either saved or scans");
        }
        mContext = context;
        if (currentLooper == null) {
            // When we aren't on a looper thread, default to the main.
            currentLooper = Looper.getMainLooper();
        }
        mMainHandler = new MainHandler(currentLooper);
        mWorkHandler = new WorkHandler(
                workerLooper != null ? workerLooper : currentLooper);
        mMainHandler = new MainHandler(Looper.getMainLooper());
        mWifiManager = wifiManager;
        mIncludeSaved = includeSaved;
        mIncludeScans = includeScans;
        mIncludePasspoints = includePasspoints;
        mListener = wifiListener;
        mConnectivityManager = connectivityManager;

@@ -229,7 +228,22 @@ public class WifiTracker {

        mNetworkScoreManager = networkScoreManager;

        mScoreCache = new WifiNetworkScoreCache(context, new CacheListener(mWorkHandler) {
        final HandlerThread workThread = new HandlerThread(TAG
                + "{" + Integer.toHexString(System.identityHashCode(this)) + "}",
                Process.THREAD_PRIORITY_BACKGROUND);
        workThread.start();
        setWorkThread(workThread);
    }

    /**
     * Sanity warning: this wipes out mScoreCache, so use with extreme caution
     * @param workThread substitute Handler thread, for testing purposes only
     */
    @VisibleForTesting
    void setWorkThread(HandlerThread workThread) {
        mWorkThread = workThread;
        mWorkHandler = new WorkHandler(workThread.getLooper());
        mScoreCache = new WifiNetworkScoreCache(mContext, new CacheListener(mWorkHandler) {
            @Override
            public void networkCacheUpdated(List<ScoredNetwork> networks) {
                synchronized (mLock) {
@@ -244,6 +258,11 @@ public class WifiTracker {
        });
    }

    @Override
    public void onDestroy() {
        mWorkThread.quit();
    }

    /** Synchronously update the list of access points with the latest information. */
    @MainThread
    public void forceUpdate() {
@@ -312,8 +331,9 @@ public class WifiTracker {
     * <p>Registers listeners and starts scanning for wifi networks. If this is not called
     * then forceUpdate() must be called to populate getAccessPoints().
     */
    @Override
    @MainThread
    public void startTracking() {
    public void onStart() {
        synchronized (mLock) {
            registerScoreCache();

@@ -361,15 +381,16 @@ public class WifiTracker {
    /**
     * Stop tracking wifi networks and scores.
     *
     * <p>This should always be called when done with a WifiTracker (if startTracking was called) to
     * <p>This should always be called when done with a WifiTracker (if onStart was called) to
     * ensure proper cleanup and prevent any further callbacks from occurring.
     *
     * <p>Calling this method will set the {@link #mStaleScanResults} bit, which prevents
     * {@link WifiListener#onAccessPointsChanged()} callbacks from being invoked (until the bit
     * is unset on the next SCAN_RESULTS_AVAILABLE_ACTION).
     */
    @Override
    @MainThread
    public void stopTracking() {
    public void onStop() {
        synchronized (mLock) {
            if (mRegistered) {
                mContext.unregisterReceiver(mReceiver);
@@ -769,9 +790,8 @@ public class WifiTracker {
    }

    public static List<AccessPoint> getCurrentAccessPoints(Context context, boolean includeSaved,
            boolean includeScans, boolean includePasspoints) {
        WifiTracker tracker = new WifiTracker(context,
                null, null, includeSaved, includeScans, includePasspoints);
            boolean includeScans) {
        WifiTracker tracker = new WifiTracker(context, null, includeSaved, includeScans);
        tracker.forceUpdate();
        tracker.copyAndNotifyListeners(false /*notifyListeners*/);
        return tracker.getAccessPoints();
+6 −5
Original line number Diff line number Diff line
@@ -16,8 +16,10 @@
package com.android.settingslib.wifi;

import android.content.Context;
import android.os.Looper;
import android.support.annotation.Keep;
import android.support.annotation.NonNull;

import com.android.settingslib.core.lifecycle.Lifecycle;

/**
 * Factory method used to inject WifiTracker instances.
@@ -31,12 +33,11 @@ public class WifiTrackerFactory {
    }

    public static WifiTracker create(
            Context context, WifiTracker.WifiListener wifiListener, Looper workerLooper,
            boolean includeSaved, boolean includeScans, boolean includePasspoints) {
            Context context, WifiTracker.WifiListener wifiListener, @NonNull Lifecycle lifecycle,
            boolean includeSaved, boolean includeScans) {
        if(sTestingWifiTracker != null) {
            return sTestingWifiTracker;
        }
        return new WifiTracker(
                context, wifiListener, workerLooper, includeSaved, includeScans, includePasspoints);
        return new WifiTracker(context, wifiListener, lifecycle, includeSaved, includeScans);
    }
}
+12 −21
Original line number Diff line number Diff line
@@ -54,7 +54,6 @@ import android.net.wifi.WifiSsid;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.SystemClock;
import android.provider.Settings;
import android.support.test.InstrumentationRegistry;
@@ -147,10 +146,7 @@ public class WifiTrackerTest {
    private CountDownLatch mAccessPointsChangedLatch;
    private CountDownLatch mRequestScoresLatch;
    private Handler mScannerHandler;
    private HandlerThread mMainThread;
    private HandlerThread mWorkerThread;
    private Looper mWorkerLooper;
    private Looper mMainLooper;

    private int mOriginalScoringUiSettingValue;

@@ -162,10 +158,6 @@ public class WifiTrackerTest {

        mWorkerThread = new HandlerThread("TestHandlerWorkerThread");
        mWorkerThread.start();
        mWorkerLooper = mWorkerThread.getLooper();
        mMainThread = new HandlerThread("TestHandlerThread");
        mMainThread.start();
        mMainLooper = mMainThread.getLooper();

        // Make sure the scanner doesn't try to run on the testing thread.
        HandlerThread scannerThread = new HandlerThread("ScannerWorkerThread");
@@ -283,18 +275,17 @@ public class WifiTrackerTest {
    }

    private WifiTracker createMockedWifiTracker() {
        return new WifiTracker(
        final WifiTracker wifiTracker = new WifiTracker(
                mContext,
                mockWifiListener,
                mWorkerLooper,
                true,
                true,
                true,
                mockWifiManager,
                mockConnectivityManager,
                mockNetworkScoreManager,
                mMainLooper,
                new IntentFilter()); // empty filter to ignore system broadcasts
        wifiTracker.setWorkThread(mWorkerThread);
        return wifiTracker;
    }

    private void startTracking(WifiTracker tracker)  throws InterruptedException {
@@ -302,7 +293,7 @@ public class WifiTrackerTest {
        mScannerHandler.post(new Runnable() {
            @Override
            public void run() {
                tracker.startTracking();
                tracker.onStart();
                latch.countDown();
            }
        });
@@ -406,7 +397,7 @@ public class WifiTrackerTest {
        scanResult.capabilities = "";

        WifiTracker tracker = new WifiTracker(
                InstrumentationRegistry.getTargetContext(), null, mWorkerLooper, true, true);
                InstrumentationRegistry.getTargetContext(), null, true, true);

        AccessPoint result = tracker.getCachedOrCreate(scanResult, new ArrayList<AccessPoint>());
        assertTrue(result.mAccessPointListener != null);
@@ -422,7 +413,7 @@ public class WifiTrackerTest {
        configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);

        WifiTracker tracker = new WifiTracker(
                InstrumentationRegistry.getTargetContext(), null, mWorkerLooper, true, true);
                InstrumentationRegistry.getTargetContext(), null, true, true);

        AccessPoint result = tracker.getCachedOrCreate(configuration, new ArrayList<AccessPoint>());
        assertTrue(result.mAccessPointListener != null);
@@ -452,7 +443,7 @@ public class WifiTrackerTest {
                        .unregisterNetworkScoreCache(NetworkKey.TYPE_WIFI, scoreCache);

        // Test unregister
        tracker.stopTracking();
        tracker.onStop();

        assertTrue("Latch timed out", latch.await(LATCH_TIMEOUT, TimeUnit.MILLISECONDS));
        verify(mockNetworkScoreManager)
@@ -496,7 +487,7 @@ public class WifiTrackerTest {
        // Start the tracker and inject the initial scan results and then stop tracking
        WifiTracker tracker =  createTrackerWithImmediateBroadcastsAndInjectInitialScanResults();

        tracker.stopTracking();
        tracker.onStop();
        mRequestedKeys.clear();

        mRequestScoresLatch = new CountDownLatch(1);
@@ -515,7 +506,7 @@ public class WifiTrackerTest {
        // Start the tracker and inject the initial scan results and then stop tracking
        WifiTracker tracker =  createTrackerWithImmediateBroadcastsAndInjectInitialScanResults();
        updateScoresAndWaitForAccessPointsChangedCallback(tracker);
        tracker.stopTracking();
        tracker.onStop();

        assertThat(mScoreCacheCaptor.getValue().getScoredNetwork(NETWORK_KEY_1)).isNotNull();
    }
@@ -675,7 +666,7 @@ public class WifiTrackerTest {
        WifiTracker tracker =  createTrackerWithImmediateBroadcastsAndInjectInitialScanResults();
        WifiNetworkScoreCache cache = mScoreCacheCaptor.getValue();

        tracker.stopTracking();
        tracker.onStop();
        verify(mockNetworkScoreManager).unregisterNetworkScoreCache(NetworkKey.TYPE_WIFI, cache);

        // Verify listener is unregistered so updating a score does not throw an error by posting
@@ -795,7 +786,7 @@ public class WifiTrackerTest {
        tracker.mMainHandler.sendEmptyMessage(
                WifiTracker.MainHandler.MSG_WIFI_STATE_CHANGED);

        tracker.stopTracking();
        tracker.onStop();

        verify(mockWifiListener, atMost(1)).onAccessPointsChanged();
        verify(mockWifiListener, atMost(1)).onConnectedChanged();
@@ -821,7 +812,7 @@ public class WifiTrackerTest {
        startTracking(tracker);
        waitForHandlersToProcessCurrentlyEnqueuedMessages(tracker);

        tracker.stopTracking();
        tracker.onStop();
        waitForHandlersToProcessCurrentlyEnqueuedMessages(tracker);

        startTracking(tracker);
+10 −5
Original line number Diff line number Diff line
@@ -20,7 +20,6 @@ import android.app.ActivityManager;
import android.content.Context;
import android.content.Intent;
import android.net.wifi.WifiManager.ActionListener;
import android.os.Looper;
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
@@ -59,13 +58,19 @@ public class AccessPointControllerImpl

    private int mCurrentUser;

    public AccessPointControllerImpl(Context context, Looper bgLooper) {
    public AccessPointControllerImpl(Context context) {
        mContext = context;
        mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
        mWifiTracker = new WifiTracker(context, this, bgLooper, false, true);
        mWifiTracker = new WifiTracker(context, this, false, true);
        mCurrentUser = ActivityManager.getCurrentUser();
    }

    @Override
    protected void finalize() throws Throwable {
        super.finalize();
        mWifiTracker.onDestroy();
    }

    public boolean canConfigWifi() {
        return !mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_WIFI,
                new UserHandle(mCurrentUser));
@@ -81,7 +86,7 @@ public class AccessPointControllerImpl
        if (DEBUG) Log.d(TAG, "addCallback " + callback);
        mCallbacks.add(callback);
        if (mCallbacks.size() == 1) {
            mWifiTracker.startTracking();
            mWifiTracker.onStart();
        }
    }

@@ -91,7 +96,7 @@ public class AccessPointControllerImpl
        if (DEBUG) Log.d(TAG, "removeCallback " + callback);
        mCallbacks.remove(callback);
        if (mCallbacks.isEmpty()) {
            mWifiTracker.stopTracking();
            mWifiTracker.onStop();
        }
    }

+1 −3
Original line number Diff line number Diff line
@@ -58,10 +58,8 @@ import java.util.ArrayList;
import java.util.BitSet;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;

import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;

@@ -151,7 +149,7 @@ public class NetworkControllerImpl extends BroadcastReceiver
                (WifiManager) context.getSystemService(Context.WIFI_SERVICE),
                SubscriptionManager.from(context), Config.readConfig(context), bgLooper,
                new CallbackHandler(),
                new AccessPointControllerImpl(context, bgLooper),
                new AccessPointControllerImpl(context),
                new DataUsageController(context),
                new SubscriptionDefaults(),
                deviceProvisionedController);