Loading packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java +155 −203 Original line number Original line Diff line number Diff line Loading @@ -103,7 +103,6 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro // TODO: Allow control of this? // TODO: Allow control of this? // Combo scans can take 5-6s to complete - set to 10s. // Combo scans can take 5-6s to complete - set to 10s. private static final int WIFI_RESCAN_INTERVAL_MS = 10 * 1000; private static final int WIFI_RESCAN_INTERVAL_MS = 10 * 1000; private static final int NUM_SCANS_TO_CONFIRM_AP_LOSS = 3; private final Context mContext; private final Context mContext; private final WifiManager mWifiManager; private final WifiManager mWifiManager; Loading @@ -117,22 +116,32 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro private WifiTrackerNetworkCallback mNetworkCallback; private WifiTrackerNetworkCallback mNetworkCallback; @GuardedBy("mLock") /** private boolean mRegistered; * Synchronization lock for managing concurrency between main and worker threads. * * <p>This lock should be held for all modifications to {@link #mInternalAccessPoints}. */ private final Object mLock = new Object(); /** The list of AccessPoints, aggregated visible ScanResults with metadata. */ /** The list of AccessPoints, aggregated visible ScanResults with metadata. */ @GuardedBy("mLock") @GuardedBy("mLock") private final List<AccessPoint> mInternalAccessPoints = new ArrayList<>(); private final List<AccessPoint> mInternalAccessPoints = new ArrayList<>(); @GuardedBy("mLock") private final Set<NetworkKey> mRequestedScores = new ArraySet<>(); /** /** * Synchronization lock for managing concurrency between main and worker threads. * Tracks whether fresh scan results have been received since scanning start. * * * <p>This lock should be held for all modifications to {@link #mInternalAccessPoints}. * <p>If this variable is false, we will not evict the scan result cache or invoke callbacks * so that we do not update the UI with stale data / clear out existing UI elements prematurely. */ */ private final Object mLock = new Object(); private boolean mStaleScanResults = true; // TODO(sghuman): Change this to be keyed on AccessPoint.getKey // Does not need to be locked as it only updated on the worker thread, with the exception of // during onStart, which occurs before the receiver is registered on the work handler. private final HashMap<String, ScanResult> mScanResultCache = new HashMap<>(); private final HashMap<String, ScanResult> mScanResultCache = new HashMap<>(); private boolean mRegistered; private NetworkInfo mLastNetworkInfo; private NetworkInfo mLastNetworkInfo; private WifiInfo mLastInfo; private WifiInfo mLastInfo; Loading @@ -142,21 +151,11 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro private boolean mNetworkScoringUiEnabled; private boolean mNetworkScoringUiEnabled; private long mMaxSpeedLabelScoreCacheAge; private long mMaxSpeedLabelScoreCacheAge; @GuardedBy("mLock") private final Set<NetworkKey> mRequestedScores = new ArraySet<>(); @VisibleForTesting @VisibleForTesting Scanner mScanner; Scanner mScanner; /** * Tracks whether fresh scan results have been received since scanning start. * * <p>If this variable is false, we will not evict the scan result cache or invoke callbacks * so that we do not update the UI with stale data / clear out existing UI elements prematurely. */ @GuardedBy("mLock") private boolean mStaleScanResults = true; private static IntentFilter newIntentFilter() { private static IntentFilter newIntentFilter() { IntentFilter filter = new IntentFilter(); IntentFilter filter = new IntentFilter(); filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION); filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION); Loading Loading @@ -239,9 +238,7 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro mScoreCache = new WifiNetworkScoreCache(mContext, new CacheListener(mWorkHandler) { mScoreCache = new WifiNetworkScoreCache(mContext, new CacheListener(mWorkHandler) { @Override @Override public void networkCacheUpdated(List<ScoredNetwork> networks) { public void networkCacheUpdated(List<ScoredNetwork> networks) { synchronized (mLock) { if (!mRegistered) return; if (!mRegistered) return; } if (Log.isLoggable(TAG, Log.VERBOSE)) { if (Log.isLoggable(TAG, Log.VERBOSE)) { Log.v(TAG, "Score cache was updated with networks: " + networks); Log.v(TAG, "Score cache was updated with networks: " + networks); Loading @@ -256,24 +253,6 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro mWorkThread.quit(); mWorkThread.quit(); } } /** Synchronously update the list of access points with the latest information. */ @MainThread private void forceUpdate() { synchronized (mLock) { mLastInfo = mWifiManager.getConnectionInfo(); mLastNetworkInfo = mConnectivityManager.getNetworkInfo(mWifiManager.getCurrentNetwork()); final List<ScanResult> newScanResults = mWifiManager.getScanResults(); if (isVerboseLoggingEnabled()) { Log.i(TAG, "Fetched scan results: " + newScanResults); } List<WifiConfiguration> configs = mWifiManager.getConfiguredNetworks(); mInternalAccessPoints.clear(); updateAccessPointsLocked(newScanResults, configs); } } /** /** * Temporarily stop scanning for wifi networks. * Temporarily stop scanning for wifi networks. * * Loading @@ -284,10 +263,8 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro mScanner.pause(); mScanner.pause(); mScanner = null; mScanner = null; } } synchronized (mLock) { mStaleScanResults = true; mStaleScanResults = true; } } } /** /** * Resume scanning for wifi networks after it has been paused. * Resume scanning for wifi networks after it has been paused. Loading @@ -313,7 +290,9 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro @Override @Override @MainThread @MainThread public void onStart() { public void onStart() { synchronized (mLock) { // fetch current ScanResults instead of waiting for broadcast of fresh results forceUpdate(); registerScoreCache(); registerScoreCache(); mNetworkScoringUiEnabled = mNetworkScoringUiEnabled = Loading @@ -335,10 +314,20 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro mConnectivityManager.registerNetworkCallback(mNetworkRequest, mNetworkCallback); mConnectivityManager.registerNetworkCallback(mNetworkRequest, mNetworkCallback); mRegistered = true; mRegistered = true; } } // fetch current ScanResults instead of waiting for broadcast of fresh results forceUpdate(); } } /** * Synchronously update the list of access points with the latest information. * * <p>Intended to only be invoked within {@link #onStart()}. */ @MainThread private void forceUpdate() { mLastInfo = mWifiManager.getConnectionInfo(); mLastNetworkInfo = mConnectivityManager.getNetworkInfo(mWifiManager.getCurrentNetwork()); fetchScansAndConfigsAndUpdateAccessPoints(); } } private void registerScoreCache() { private void registerScoreCache() { Loading Loading @@ -373,7 +362,6 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro @Override @Override @MainThread @MainThread public void onStop() { public void onStop() { synchronized (mLock) { if (mRegistered) { if (mRegistered) { mContext.unregisterReceiver(mReceiver); mContext.unregisterReceiver(mReceiver); mConnectivityManager.unregisterNetworkCallback(mNetworkCallback); mConnectivityManager.unregisterNetworkCallback(mNetworkCallback); Loading @@ -384,7 +372,6 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro mWorkHandler.removeCallbacksAndMessages(null /* remove all */); mWorkHandler.removeCallbacksAndMessages(null /* remove all */); } } } private void unregisterScoreCache() { private void unregisterScoreCache() { mNetworkScoreManager.unregisterNetworkScoreCache(NetworkKey.TYPE_WIFI, mScoreCache); mNetworkScoreManager.unregisterNetworkScoreCache(NetworkKey.TYPE_WIFI, mScoreCache); Loading @@ -407,9 +394,6 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro */ */ @AnyThread @AnyThread public List<AccessPoint> getAccessPoints() { public List<AccessPoint> getAccessPoints() { // TODO(sghuman): Investigate how to eliminate or reduce the need for locking now that we // have transitioned to a single worker thread model. synchronized (mLock) { synchronized (mLock) { return new ArrayList<>(mInternalAccessPoints); return new ArrayList<>(mInternalAccessPoints); } } Loading Loading @@ -444,13 +428,10 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro } } } } private void handleResume() { evictOldScans(); } private ArrayMap<String, List<ScanResult>> updateScanResultCache( private ArrayMap<String, List<ScanResult>> updateScanResultCache( final List<ScanResult> newResults) { final List<ScanResult> newResults) { // TODO(sghuman): Delete this and replace it with the Map of Ap Keys to ScanResults // TODO(sghuman): Delete this and replace it with the Map of Ap Keys to ScanResults for // memory efficiency for (ScanResult newResult : newResults) { for (ScanResult newResult : newResults) { if (newResult.SSID == null || newResult.SSID.isEmpty()) { if (newResult.SSID == null || newResult.SSID.isEmpty()) { continue; continue; Loading Loading @@ -517,36 +498,22 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro } } /** /** * Safely modify {@link #mInternalAccessPoints} by acquiring {@link #mLock} first. * Retrieves latest scan results and wifi configs, then calls * * {@link #updateAccessPoints(List, List)}. * <p>Will not perform the update if {@link #mStaleScanResults} is true */ */ private void updateAccessPoints() { private void fetchScansAndConfigsAndUpdateAccessPoints() { List<WifiConfiguration> configs = mWifiManager.getConfiguredNetworks(); final List<ScanResult> newScanResults = mWifiManager.getScanResults(); final List<ScanResult> newScanResults = mWifiManager.getScanResults(); if (isVerboseLoggingEnabled()) { if (isVerboseLoggingEnabled()) { Log.i(TAG, "Fetched scan results: " + newScanResults); Log.i(TAG, "Fetched scan results: " + newScanResults); } } synchronized (mLock) { List<WifiConfiguration> configs = mWifiManager.getConfiguredNetworks(); if(!mStaleScanResults) { updateAccessPoints(newScanResults, configs); updateAccessPointsLocked(newScanResults, configs); } } } } /** /** Update the internal list of access points. */ * Update the internal list of access points. private void updateAccessPoints(final List<ScanResult> newScanResults, * * <p>Do not call directly (except for forceUpdate), use {@link #updateAccessPoints()} which * acquires the lock first. */ @GuardedBy("mLock") private void updateAccessPointsLocked(final List<ScanResult> newScanResults, List<WifiConfiguration> configs) { List<WifiConfiguration> configs) { // TODO(sghuman): Reduce the synchronization time by only holding the lock when // modifying lists exposed to operations on the MainThread (getAccessPoints, stopTracking, // startTracking, etc). // Map configs and scan results necessary to make AccessPoints // Map configs and scan results necessary to make AccessPoints final Map<String, WifiConfiguration> configsByKey = new ArrayMap(configs.size()); final Map<String, WifiConfiguration> configsByKey = new ArrayMap(configs.size()); Loading @@ -560,15 +527,19 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro WifiConfiguration connectionConfig = null; WifiConfiguration connectionConfig = null; if (mLastInfo != null) { if (mLastInfo != null) { // TODO(sghuman): This call is unneccessary, as we already have all the configs. // TODO(sghuman): Refactor to match config network id when updating configs below, and // Refactor to match config network id when updating configs below, and then update // then update network info and wifi info only on match // networkinfo and wifi info only on match connectionConfig = getWifiConfigurationForNetworkId( connectionConfig = getWifiConfigurationForNetworkId( mLastInfo.getNetworkId(), configs); mLastInfo.getNetworkId(), configs); } } // Rather than dropping and reacquiring the lock multiple times in this method, we lock // once for efficiency of lock acquisition time and readability synchronized (mLock) { // Swap the current access points into a cached list for maintaining AP listeners // Swap the current access points into a cached list for maintaining AP listeners List<AccessPoint> cachedAccessPoints = new ArrayList<>(mInternalAccessPoints); List<AccessPoint> cachedAccessPoints; cachedAccessPoints = new ArrayList<>(mInternalAccessPoints); ArrayList<AccessPoint> accessPoints = new ArrayList<>(); ArrayList<AccessPoint> accessPoints = new ArrayList<>(); final List<NetworkKey> scoresToRequest = new ArrayList<>(); final List<NetworkKey> scoresToRequest = new ArrayList<>(); Loading Loading @@ -624,6 +595,7 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro mInternalAccessPoints.clear(); mInternalAccessPoints.clear(); mInternalAccessPoints.addAll(accessPoints); mInternalAccessPoints.addAll(accessPoints); } conditionallyNotifyListeners(); conditionallyNotifyListeners(); } } Loading @@ -644,21 +616,6 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro return accessPoint; return accessPoint; } } @VisibleForTesting AccessPoint getCachedOrCreate(WifiConfiguration config, List<AccessPoint> cache) { final int N = cache.size(); for (int i = 0; i < N; i++) { if (cache.get(i).matches(config)) { AccessPoint ret = cache.remove(i); ret.loadConfig(config); return ret; } } final AccessPoint accessPoint = new AccessPoint(mContext, config); return accessPoint; } private void updateNetworkInfo(NetworkInfo networkInfo) { private void updateNetworkInfo(NetworkInfo networkInfo) { /* Sticky broadcasts can call this when wifi is disabled */ /* Sticky broadcasts can call this when wifi is disabled */ Loading Loading @@ -763,9 +720,6 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro public void onReceive(Context context, Intent intent) { public void onReceive(Context context, Intent intent) { String action = intent.getAction(); String action = intent.getAction(); // TODO(sghuman): Improve efficiency of synchronization by synchonizing on individual // methods and only use lock when iterating/modifying collections synchronized (mLock) { if (WifiManager.WIFI_STATE_CHANGED_ACTION.equals(action)) { if (WifiManager.WIFI_STATE_CHANGED_ACTION.equals(action)) { updateWifiState( updateWifiState( intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE, intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE, Loading @@ -773,26 +727,30 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro } else if (WifiManager.SCAN_RESULTS_AVAILABLE_ACTION.equals(action)) { } else if (WifiManager.SCAN_RESULTS_AVAILABLE_ACTION.equals(action)) { mStaleScanResults = false; mStaleScanResults = false; updateAccessPoints(); fetchScansAndConfigsAndUpdateAccessPoints(); } else if (WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION.equals(action) } else if (WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION.equals(action) || WifiManager.LINK_CONFIGURATION_CHANGED_ACTION.equals(action)) { || WifiManager.LINK_CONFIGURATION_CHANGED_ACTION.equals(action)) { updateAccessPoints(); fetchScansAndConfigsAndUpdateAccessPoints(); } else if (WifiManager.NETWORK_STATE_CHANGED_ACTION.equals(action)) { } else if (WifiManager.NETWORK_STATE_CHANGED_ACTION.equals(action)) { // TODO(sghuman): Refactor these methods so they cannot result in duplicate // TODO(sghuman): Refactor these methods so they cannot result in duplicate // onAccessPointsChanged updates being called from this intent. // onAccessPointsChanged updates being called from this intent. NetworkInfo info = intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO); NetworkInfo info = intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO); updateNetworkInfo(info); updateNetworkInfo(info); updateAccessPoints(); fetchScansAndConfigsAndUpdateAccessPoints(); } else if (WifiManager.RSSI_CHANGED_ACTION.equals(action)) { } else if (WifiManager.RSSI_CHANGED_ACTION.equals(action)) { NetworkInfo info = NetworkInfo info = mConnectivityManager.getNetworkInfo(mWifiManager.getCurrentNetwork()); mConnectivityManager.getNetworkInfo(mWifiManager.getCurrentNetwork()); updateNetworkInfo(info); updateNetworkInfo(info); } } } } } }; }; /** Handles updates to WifiState. */ /** * Handles updates to WifiState. * * <p>If Wifi is not enabled in the enabled state, {@link #mStaleScanResults} will be set to * true. */ private void updateWifiState(int state) { private void updateWifiState(int state) { if (state == WifiManager.WIFI_STATE_ENABLED) { if (state == WifiManager.WIFI_STATE_ENABLED) { if (mScanner != null) { if (mScanner != null) { Loading @@ -807,10 +765,8 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro if (mScanner != null) { if (mScanner != null) { mScanner.pause(); mScanner.pause(); } } synchronized (mLock) { mStaleScanResults = true; mStaleScanResults = true; } } } mListener.onWifiStateChanged(state); mListener.onWifiStateChanged(state); } } Loading @@ -823,11 +779,7 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro // We don't send a NetworkInfo object along with this message, because even if we // We don't send a NetworkInfo object along with this message, because even if we // fetch one from ConnectivityManager, it might be older than the most recent // fetch one from ConnectivityManager, it might be older than the most recent // NetworkInfo message we got via a WIFI_STATE_CHANGED broadcast. // NetworkInfo message we got via a WIFI_STATE_CHANGED broadcast. mWorkHandler.post(() -> { mWorkHandler.post(() -> updateNetworkInfo(null)); synchronized (mLock) { updateNetworkInfo(null); } }); } } } } } } Loading Loading
packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java +155 −203 Original line number Original line Diff line number Diff line Loading @@ -103,7 +103,6 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro // TODO: Allow control of this? // TODO: Allow control of this? // Combo scans can take 5-6s to complete - set to 10s. // Combo scans can take 5-6s to complete - set to 10s. private static final int WIFI_RESCAN_INTERVAL_MS = 10 * 1000; private static final int WIFI_RESCAN_INTERVAL_MS = 10 * 1000; private static final int NUM_SCANS_TO_CONFIRM_AP_LOSS = 3; private final Context mContext; private final Context mContext; private final WifiManager mWifiManager; private final WifiManager mWifiManager; Loading @@ -117,22 +116,32 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro private WifiTrackerNetworkCallback mNetworkCallback; private WifiTrackerNetworkCallback mNetworkCallback; @GuardedBy("mLock") /** private boolean mRegistered; * Synchronization lock for managing concurrency between main and worker threads. * * <p>This lock should be held for all modifications to {@link #mInternalAccessPoints}. */ private final Object mLock = new Object(); /** The list of AccessPoints, aggregated visible ScanResults with metadata. */ /** The list of AccessPoints, aggregated visible ScanResults with metadata. */ @GuardedBy("mLock") @GuardedBy("mLock") private final List<AccessPoint> mInternalAccessPoints = new ArrayList<>(); private final List<AccessPoint> mInternalAccessPoints = new ArrayList<>(); @GuardedBy("mLock") private final Set<NetworkKey> mRequestedScores = new ArraySet<>(); /** /** * Synchronization lock for managing concurrency between main and worker threads. * Tracks whether fresh scan results have been received since scanning start. * * * <p>This lock should be held for all modifications to {@link #mInternalAccessPoints}. * <p>If this variable is false, we will not evict the scan result cache or invoke callbacks * so that we do not update the UI with stale data / clear out existing UI elements prematurely. */ */ private final Object mLock = new Object(); private boolean mStaleScanResults = true; // TODO(sghuman): Change this to be keyed on AccessPoint.getKey // Does not need to be locked as it only updated on the worker thread, with the exception of // during onStart, which occurs before the receiver is registered on the work handler. private final HashMap<String, ScanResult> mScanResultCache = new HashMap<>(); private final HashMap<String, ScanResult> mScanResultCache = new HashMap<>(); private boolean mRegistered; private NetworkInfo mLastNetworkInfo; private NetworkInfo mLastNetworkInfo; private WifiInfo mLastInfo; private WifiInfo mLastInfo; Loading @@ -142,21 +151,11 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro private boolean mNetworkScoringUiEnabled; private boolean mNetworkScoringUiEnabled; private long mMaxSpeedLabelScoreCacheAge; private long mMaxSpeedLabelScoreCacheAge; @GuardedBy("mLock") private final Set<NetworkKey> mRequestedScores = new ArraySet<>(); @VisibleForTesting @VisibleForTesting Scanner mScanner; Scanner mScanner; /** * Tracks whether fresh scan results have been received since scanning start. * * <p>If this variable is false, we will not evict the scan result cache or invoke callbacks * so that we do not update the UI with stale data / clear out existing UI elements prematurely. */ @GuardedBy("mLock") private boolean mStaleScanResults = true; private static IntentFilter newIntentFilter() { private static IntentFilter newIntentFilter() { IntentFilter filter = new IntentFilter(); IntentFilter filter = new IntentFilter(); filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION); filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION); Loading Loading @@ -239,9 +238,7 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro mScoreCache = new WifiNetworkScoreCache(mContext, new CacheListener(mWorkHandler) { mScoreCache = new WifiNetworkScoreCache(mContext, new CacheListener(mWorkHandler) { @Override @Override public void networkCacheUpdated(List<ScoredNetwork> networks) { public void networkCacheUpdated(List<ScoredNetwork> networks) { synchronized (mLock) { if (!mRegistered) return; if (!mRegistered) return; } if (Log.isLoggable(TAG, Log.VERBOSE)) { if (Log.isLoggable(TAG, Log.VERBOSE)) { Log.v(TAG, "Score cache was updated with networks: " + networks); Log.v(TAG, "Score cache was updated with networks: " + networks); Loading @@ -256,24 +253,6 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro mWorkThread.quit(); mWorkThread.quit(); } } /** Synchronously update the list of access points with the latest information. */ @MainThread private void forceUpdate() { synchronized (mLock) { mLastInfo = mWifiManager.getConnectionInfo(); mLastNetworkInfo = mConnectivityManager.getNetworkInfo(mWifiManager.getCurrentNetwork()); final List<ScanResult> newScanResults = mWifiManager.getScanResults(); if (isVerboseLoggingEnabled()) { Log.i(TAG, "Fetched scan results: " + newScanResults); } List<WifiConfiguration> configs = mWifiManager.getConfiguredNetworks(); mInternalAccessPoints.clear(); updateAccessPointsLocked(newScanResults, configs); } } /** /** * Temporarily stop scanning for wifi networks. * Temporarily stop scanning for wifi networks. * * Loading @@ -284,10 +263,8 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro mScanner.pause(); mScanner.pause(); mScanner = null; mScanner = null; } } synchronized (mLock) { mStaleScanResults = true; mStaleScanResults = true; } } } /** /** * Resume scanning for wifi networks after it has been paused. * Resume scanning for wifi networks after it has been paused. Loading @@ -313,7 +290,9 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro @Override @Override @MainThread @MainThread public void onStart() { public void onStart() { synchronized (mLock) { // fetch current ScanResults instead of waiting for broadcast of fresh results forceUpdate(); registerScoreCache(); registerScoreCache(); mNetworkScoringUiEnabled = mNetworkScoringUiEnabled = Loading @@ -335,10 +314,20 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro mConnectivityManager.registerNetworkCallback(mNetworkRequest, mNetworkCallback); mConnectivityManager.registerNetworkCallback(mNetworkRequest, mNetworkCallback); mRegistered = true; mRegistered = true; } } // fetch current ScanResults instead of waiting for broadcast of fresh results forceUpdate(); } } /** * Synchronously update the list of access points with the latest information. * * <p>Intended to only be invoked within {@link #onStart()}. */ @MainThread private void forceUpdate() { mLastInfo = mWifiManager.getConnectionInfo(); mLastNetworkInfo = mConnectivityManager.getNetworkInfo(mWifiManager.getCurrentNetwork()); fetchScansAndConfigsAndUpdateAccessPoints(); } } private void registerScoreCache() { private void registerScoreCache() { Loading Loading @@ -373,7 +362,6 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro @Override @Override @MainThread @MainThread public void onStop() { public void onStop() { synchronized (mLock) { if (mRegistered) { if (mRegistered) { mContext.unregisterReceiver(mReceiver); mContext.unregisterReceiver(mReceiver); mConnectivityManager.unregisterNetworkCallback(mNetworkCallback); mConnectivityManager.unregisterNetworkCallback(mNetworkCallback); Loading @@ -384,7 +372,6 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro mWorkHandler.removeCallbacksAndMessages(null /* remove all */); mWorkHandler.removeCallbacksAndMessages(null /* remove all */); } } } private void unregisterScoreCache() { private void unregisterScoreCache() { mNetworkScoreManager.unregisterNetworkScoreCache(NetworkKey.TYPE_WIFI, mScoreCache); mNetworkScoreManager.unregisterNetworkScoreCache(NetworkKey.TYPE_WIFI, mScoreCache); Loading @@ -407,9 +394,6 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro */ */ @AnyThread @AnyThread public List<AccessPoint> getAccessPoints() { public List<AccessPoint> getAccessPoints() { // TODO(sghuman): Investigate how to eliminate or reduce the need for locking now that we // have transitioned to a single worker thread model. synchronized (mLock) { synchronized (mLock) { return new ArrayList<>(mInternalAccessPoints); return new ArrayList<>(mInternalAccessPoints); } } Loading Loading @@ -444,13 +428,10 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro } } } } private void handleResume() { evictOldScans(); } private ArrayMap<String, List<ScanResult>> updateScanResultCache( private ArrayMap<String, List<ScanResult>> updateScanResultCache( final List<ScanResult> newResults) { final List<ScanResult> newResults) { // TODO(sghuman): Delete this and replace it with the Map of Ap Keys to ScanResults // TODO(sghuman): Delete this and replace it with the Map of Ap Keys to ScanResults for // memory efficiency for (ScanResult newResult : newResults) { for (ScanResult newResult : newResults) { if (newResult.SSID == null || newResult.SSID.isEmpty()) { if (newResult.SSID == null || newResult.SSID.isEmpty()) { continue; continue; Loading Loading @@ -517,36 +498,22 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro } } /** /** * Safely modify {@link #mInternalAccessPoints} by acquiring {@link #mLock} first. * Retrieves latest scan results and wifi configs, then calls * * {@link #updateAccessPoints(List, List)}. * <p>Will not perform the update if {@link #mStaleScanResults} is true */ */ private void updateAccessPoints() { private void fetchScansAndConfigsAndUpdateAccessPoints() { List<WifiConfiguration> configs = mWifiManager.getConfiguredNetworks(); final List<ScanResult> newScanResults = mWifiManager.getScanResults(); final List<ScanResult> newScanResults = mWifiManager.getScanResults(); if (isVerboseLoggingEnabled()) { if (isVerboseLoggingEnabled()) { Log.i(TAG, "Fetched scan results: " + newScanResults); Log.i(TAG, "Fetched scan results: " + newScanResults); } } synchronized (mLock) { List<WifiConfiguration> configs = mWifiManager.getConfiguredNetworks(); if(!mStaleScanResults) { updateAccessPoints(newScanResults, configs); updateAccessPointsLocked(newScanResults, configs); } } } } /** /** Update the internal list of access points. */ * Update the internal list of access points. private void updateAccessPoints(final List<ScanResult> newScanResults, * * <p>Do not call directly (except for forceUpdate), use {@link #updateAccessPoints()} which * acquires the lock first. */ @GuardedBy("mLock") private void updateAccessPointsLocked(final List<ScanResult> newScanResults, List<WifiConfiguration> configs) { List<WifiConfiguration> configs) { // TODO(sghuman): Reduce the synchronization time by only holding the lock when // modifying lists exposed to operations on the MainThread (getAccessPoints, stopTracking, // startTracking, etc). // Map configs and scan results necessary to make AccessPoints // Map configs and scan results necessary to make AccessPoints final Map<String, WifiConfiguration> configsByKey = new ArrayMap(configs.size()); final Map<String, WifiConfiguration> configsByKey = new ArrayMap(configs.size()); Loading @@ -560,15 +527,19 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro WifiConfiguration connectionConfig = null; WifiConfiguration connectionConfig = null; if (mLastInfo != null) { if (mLastInfo != null) { // TODO(sghuman): This call is unneccessary, as we already have all the configs. // TODO(sghuman): Refactor to match config network id when updating configs below, and // Refactor to match config network id when updating configs below, and then update // then update network info and wifi info only on match // networkinfo and wifi info only on match connectionConfig = getWifiConfigurationForNetworkId( connectionConfig = getWifiConfigurationForNetworkId( mLastInfo.getNetworkId(), configs); mLastInfo.getNetworkId(), configs); } } // Rather than dropping and reacquiring the lock multiple times in this method, we lock // once for efficiency of lock acquisition time and readability synchronized (mLock) { // Swap the current access points into a cached list for maintaining AP listeners // Swap the current access points into a cached list for maintaining AP listeners List<AccessPoint> cachedAccessPoints = new ArrayList<>(mInternalAccessPoints); List<AccessPoint> cachedAccessPoints; cachedAccessPoints = new ArrayList<>(mInternalAccessPoints); ArrayList<AccessPoint> accessPoints = new ArrayList<>(); ArrayList<AccessPoint> accessPoints = new ArrayList<>(); final List<NetworkKey> scoresToRequest = new ArrayList<>(); final List<NetworkKey> scoresToRequest = new ArrayList<>(); Loading Loading @@ -624,6 +595,7 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro mInternalAccessPoints.clear(); mInternalAccessPoints.clear(); mInternalAccessPoints.addAll(accessPoints); mInternalAccessPoints.addAll(accessPoints); } conditionallyNotifyListeners(); conditionallyNotifyListeners(); } } Loading @@ -644,21 +616,6 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro return accessPoint; return accessPoint; } } @VisibleForTesting AccessPoint getCachedOrCreate(WifiConfiguration config, List<AccessPoint> cache) { final int N = cache.size(); for (int i = 0; i < N; i++) { if (cache.get(i).matches(config)) { AccessPoint ret = cache.remove(i); ret.loadConfig(config); return ret; } } final AccessPoint accessPoint = new AccessPoint(mContext, config); return accessPoint; } private void updateNetworkInfo(NetworkInfo networkInfo) { private void updateNetworkInfo(NetworkInfo networkInfo) { /* Sticky broadcasts can call this when wifi is disabled */ /* Sticky broadcasts can call this when wifi is disabled */ Loading Loading @@ -763,9 +720,6 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro public void onReceive(Context context, Intent intent) { public void onReceive(Context context, Intent intent) { String action = intent.getAction(); String action = intent.getAction(); // TODO(sghuman): Improve efficiency of synchronization by synchonizing on individual // methods and only use lock when iterating/modifying collections synchronized (mLock) { if (WifiManager.WIFI_STATE_CHANGED_ACTION.equals(action)) { if (WifiManager.WIFI_STATE_CHANGED_ACTION.equals(action)) { updateWifiState( updateWifiState( intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE, intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE, Loading @@ -773,26 +727,30 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro } else if (WifiManager.SCAN_RESULTS_AVAILABLE_ACTION.equals(action)) { } else if (WifiManager.SCAN_RESULTS_AVAILABLE_ACTION.equals(action)) { mStaleScanResults = false; mStaleScanResults = false; updateAccessPoints(); fetchScansAndConfigsAndUpdateAccessPoints(); } else if (WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION.equals(action) } else if (WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION.equals(action) || WifiManager.LINK_CONFIGURATION_CHANGED_ACTION.equals(action)) { || WifiManager.LINK_CONFIGURATION_CHANGED_ACTION.equals(action)) { updateAccessPoints(); fetchScansAndConfigsAndUpdateAccessPoints(); } else if (WifiManager.NETWORK_STATE_CHANGED_ACTION.equals(action)) { } else if (WifiManager.NETWORK_STATE_CHANGED_ACTION.equals(action)) { // TODO(sghuman): Refactor these methods so they cannot result in duplicate // TODO(sghuman): Refactor these methods so they cannot result in duplicate // onAccessPointsChanged updates being called from this intent. // onAccessPointsChanged updates being called from this intent. NetworkInfo info = intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO); NetworkInfo info = intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO); updateNetworkInfo(info); updateNetworkInfo(info); updateAccessPoints(); fetchScansAndConfigsAndUpdateAccessPoints(); } else if (WifiManager.RSSI_CHANGED_ACTION.equals(action)) { } else if (WifiManager.RSSI_CHANGED_ACTION.equals(action)) { NetworkInfo info = NetworkInfo info = mConnectivityManager.getNetworkInfo(mWifiManager.getCurrentNetwork()); mConnectivityManager.getNetworkInfo(mWifiManager.getCurrentNetwork()); updateNetworkInfo(info); updateNetworkInfo(info); } } } } } }; }; /** Handles updates to WifiState. */ /** * Handles updates to WifiState. * * <p>If Wifi is not enabled in the enabled state, {@link #mStaleScanResults} will be set to * true. */ private void updateWifiState(int state) { private void updateWifiState(int state) { if (state == WifiManager.WIFI_STATE_ENABLED) { if (state == WifiManager.WIFI_STATE_ENABLED) { if (mScanner != null) { if (mScanner != null) { Loading @@ -807,10 +765,8 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro if (mScanner != null) { if (mScanner != null) { mScanner.pause(); mScanner.pause(); } } synchronized (mLock) { mStaleScanResults = true; mStaleScanResults = true; } } } mListener.onWifiStateChanged(state); mListener.onWifiStateChanged(state); } } Loading @@ -823,11 +779,7 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro // We don't send a NetworkInfo object along with this message, because even if we // We don't send a NetworkInfo object along with this message, because even if we // fetch one from ConnectivityManager, it might be older than the most recent // fetch one from ConnectivityManager, it might be older than the most recent // NetworkInfo message we got via a WIFI_STATE_CHANGED broadcast. // NetworkInfo message we got via a WIFI_STATE_CHANGED broadcast. mWorkHandler.post(() -> { mWorkHandler.post(() -> updateNetworkInfo(null)); synchronized (mLock) { updateNetworkInfo(null); } }); } } } } } } Loading