1d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk/* 2d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk * Copyright (C) 2015 The Android Open Source Project 3d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk * 4d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk * Licensed under the Apache License, Version 2.0 (the "License"); 5d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk * you may not use this file except in compliance with the License. 6d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk * You may obtain a copy of the License at 7d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk * 8d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk * http://www.apache.org/licenses/LICENSE-2.0 9d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk * 10d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk * Unless required by applicable law or agreed to in writing, software 11d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk * distributed under the License is distributed on an "AS IS" BASIS, 12d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk * See the License for the specific language governing permissions and 14d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk * limitations under the License. 15d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk */ 16d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monkpackage com.android.settingslib.wifi; 17d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk 18d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monkimport android.content.BroadcastReceiver; 19d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monkimport android.content.Context; 20d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monkimport android.content.Intent; 21d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monkimport android.content.IntentFilter; 22ab313f8443c7dade78b7ae53d18dba002a144425Lorenzo Colittiimport android.net.ConnectivityManager; 23ab313f8443c7dade78b7ae53d18dba002a144425Lorenzo Colittiimport android.net.Network; 24ab313f8443c7dade78b7ae53d18dba002a144425Lorenzo Colittiimport android.net.NetworkCapabilities; 25d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monkimport android.net.NetworkInfo; 26d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monkimport android.net.NetworkInfo.DetailedState; 27ab313f8443c7dade78b7ae53d18dba002a144425Lorenzo Colittiimport android.net.NetworkRequest; 28d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monkimport android.net.wifi.ScanResult; 29d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monkimport android.net.wifi.WifiConfiguration; 30d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monkimport android.net.wifi.WifiInfo; 31d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monkimport android.net.wifi.WifiManager; 32d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monkimport android.os.Handler; 3330d8004dbf1941d9ac8c5c7839c135a1b87a87fcJason Monkimport android.os.Looper; 34d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monkimport android.os.Message; 35fcd4612f70bb22e11bf776c76afc45b03a5b026cVinit Deshpandeimport android.util.Log; 36d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monkimport android.widget.Toast; 37d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk 38d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monkimport com.android.internal.annotations.VisibleForTesting; 39d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monkimport com.android.settingslib.R; 40d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk 41d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monkimport java.io.PrintWriter; 42d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monkimport java.util.ArrayList; 43fcd4612f70bb22e11bf776c76afc45b03a5b026cVinit Deshpandeimport java.util.Collection; 44d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monkimport java.util.Collections; 45d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monkimport java.util.HashMap; 46fcd4612f70bb22e11bf776c76afc45b03a5b026cVinit Deshpandeimport java.util.Iterator; 47d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monkimport java.util.List; 48fcd4612f70bb22e11bf776c76afc45b03a5b026cVinit Deshpandeimport java.util.Map; 49d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monkimport java.util.concurrent.atomic.AtomicBoolean; 50d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk 51d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk/** 52d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk * Tracks saved or available wifi networks and their state. 53d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk */ 54d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monkpublic class WifiTracker { 55d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk private static final String TAG = "WifiTracker"; 56fcd4612f70bb22e11bf776c76afc45b03a5b026cVinit Deshpande private static final boolean DBG = false; 57d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk 58d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk /** verbose logging flag. this flag is set thru developer debugging options 59d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk * and used so as to assist with in-the-field WiFi connectivity debugging */ 60d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk public static int sVerboseLogging = 0; 61d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk 62d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk // TODO: Allow control of this? 63d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk // Combo scans can take 5-6s to complete - set to 10s. 64d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk private static final int WIFI_RESCAN_INTERVAL_MS = 10 * 1000; 65d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk 66d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk private final Context mContext; 67d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk private final WifiManager mWifiManager; 68d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk private final IntentFilter mFilter; 69ab313f8443c7dade78b7ae53d18dba002a144425Lorenzo Colitti private final ConnectivityManager mConnectivityManager; 70ab313f8443c7dade78b7ae53d18dba002a144425Lorenzo Colitti private final NetworkRequest mNetworkRequest; 71ab313f8443c7dade78b7ae53d18dba002a144425Lorenzo Colitti private WifiTrackerNetworkCallback mNetworkCallback; 72d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk 73d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk private final AtomicBoolean mConnected = new AtomicBoolean(false); 74d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk private final WifiListener mListener; 75d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk private final boolean mIncludeSaved; 76d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk private final boolean mIncludeScans; 77dcf00c9d0f6f985aef186cfe5d7b63761e2f8104Vinit Deshpande private final boolean mIncludePasspoints; 78d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk 7930d8004dbf1941d9ac8c5c7839c135a1b87a87fcJason Monk private final MainHandler mMainHandler; 8030d8004dbf1941d9ac8c5c7839c135a1b87a87fcJason Monk private final WorkHandler mWorkHandler; 8130d8004dbf1941d9ac8c5c7839c135a1b87a87fcJason Monk 82d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk private boolean mSavedNetworksExist; 83d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk private boolean mRegistered; 84d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk private ArrayList<AccessPoint> mAccessPoints = new ArrayList<>(); 85fcd4612f70bb22e11bf776c76afc45b03a5b026cVinit Deshpande private HashMap<String, Integer> mSeenBssids = new HashMap<>(); 86fcd4612f70bb22e11bf776c76afc45b03a5b026cVinit Deshpande private HashMap<String, ScanResult> mScanResultCache = new HashMap<>(); 87fcd4612f70bb22e11bf776c76afc45b03a5b026cVinit Deshpande private Integer mScanId = 0; 88fcd4612f70bb22e11bf776c76afc45b03a5b026cVinit Deshpande private static final int NUM_SCANS_TO_CONFIRM_AP_LOSS = 3; 89d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk 90d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk private NetworkInfo mLastNetworkInfo; 91d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk private WifiInfo mLastInfo; 92d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk 93d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk @VisibleForTesting 94d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk Scanner mScanner; 95d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk 9630d8004dbf1941d9ac8c5c7839c135a1b87a87fcJason Monk public WifiTracker(Context context, WifiListener wifiListener, 9730d8004dbf1941d9ac8c5c7839c135a1b87a87fcJason Monk boolean includeSaved, boolean includeScans) { 9830d8004dbf1941d9ac8c5c7839c135a1b87a87fcJason Monk this(context, wifiListener, null, includeSaved, includeScans); 99dcf00c9d0f6f985aef186cfe5d7b63761e2f8104Vinit Deshpande } 10030d8004dbf1941d9ac8c5c7839c135a1b87a87fcJason Monk 10130d8004dbf1941d9ac8c5c7839c135a1b87a87fcJason Monk public WifiTracker(Context context, WifiListener wifiListener, Looper workerLooper, 10230d8004dbf1941d9ac8c5c7839c135a1b87a87fcJason Monk boolean includeSaved, boolean includeScans) { 10330d8004dbf1941d9ac8c5c7839c135a1b87a87fcJason Monk this(context, wifiListener, workerLooper, includeSaved, includeScans, false); 10430d8004dbf1941d9ac8c5c7839c135a1b87a87fcJason Monk } 10530d8004dbf1941d9ac8c5c7839c135a1b87a87fcJason Monk 10630d8004dbf1941d9ac8c5c7839c135a1b87a87fcJason Monk public WifiTracker(Context context, WifiListener wifiListener, 10730d8004dbf1941d9ac8c5c7839c135a1b87a87fcJason Monk boolean includeSaved, boolean includeScans, boolean includePasspoints) { 10830d8004dbf1941d9ac8c5c7839c135a1b87a87fcJason Monk this(context, wifiListener, null, includeSaved, includeScans, includePasspoints); 10930d8004dbf1941d9ac8c5c7839c135a1b87a87fcJason Monk } 11030d8004dbf1941d9ac8c5c7839c135a1b87a87fcJason Monk 11130d8004dbf1941d9ac8c5c7839c135a1b87a87fcJason Monk public WifiTracker(Context context, WifiListener wifiListener, Looper workerLooper, 11230d8004dbf1941d9ac8c5c7839c135a1b87a87fcJason Monk boolean includeSaved, boolean includeScans, boolean includePasspoints) { 11330d8004dbf1941d9ac8c5c7839c135a1b87a87fcJason Monk this(context, wifiListener, workerLooper, includeSaved, includeScans, includePasspoints, 114ab313f8443c7dade78b7ae53d18dba002a144425Lorenzo Colitti context.getSystemService(WifiManager.class), 115ab313f8443c7dade78b7ae53d18dba002a144425Lorenzo Colitti context.getSystemService(ConnectivityManager.class), Looper.myLooper()); 116d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk } 117d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk 118d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk @VisibleForTesting 11930d8004dbf1941d9ac8c5c7839c135a1b87a87fcJason Monk WifiTracker(Context context, WifiListener wifiListener, Looper workerLooper, 12030d8004dbf1941d9ac8c5c7839c135a1b87a87fcJason Monk boolean includeSaved, boolean includeScans, boolean includePasspoints, 121ab313f8443c7dade78b7ae53d18dba002a144425Lorenzo Colitti WifiManager wifiManager, ConnectivityManager connectivityManager, 122ab313f8443c7dade78b7ae53d18dba002a144425Lorenzo Colitti Looper currentLooper) { 123d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk if (!includeSaved && !includeScans) { 124d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk throw new IllegalArgumentException("Must include either saved or scans"); 125d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk } 126d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk mContext = context; 127bf3d1af3d8bd32f410aa5cc44ccdc1df593ef210Jason Monk if (currentLooper == null) { 128bf3d1af3d8bd32f410aa5cc44ccdc1df593ef210Jason Monk // When we aren't on a looper thread, default to the main. 129bf3d1af3d8bd32f410aa5cc44ccdc1df593ef210Jason Monk currentLooper = Looper.getMainLooper(); 130bf3d1af3d8bd32f410aa5cc44ccdc1df593ef210Jason Monk } 1312b51cc30d523cce3609a498c466364caf3ce504eJason Monk mMainHandler = new MainHandler(currentLooper); 13230d8004dbf1941d9ac8c5c7839c135a1b87a87fcJason Monk mWorkHandler = new WorkHandler( 1332b51cc30d523cce3609a498c466364caf3ce504eJason Monk workerLooper != null ? workerLooper : currentLooper); 134d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk mWifiManager = wifiManager; 135d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk mIncludeSaved = includeSaved; 136d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk mIncludeScans = includeScans; 137dcf00c9d0f6f985aef186cfe5d7b63761e2f8104Vinit Deshpande mIncludePasspoints = includePasspoints; 138d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk mListener = wifiListener; 139ab313f8443c7dade78b7ae53d18dba002a144425Lorenzo Colitti mConnectivityManager = connectivityManager; 140d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk 141d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk // check if verbose logging has been turned on or off 142d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk sVerboseLogging = mWifiManager.getVerboseLoggingLevel(); 143d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk 144d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk mFilter = new IntentFilter(); 145d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk mFilter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION); 146d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk mFilter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION); 147d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk mFilter.addAction(WifiManager.NETWORK_IDS_CHANGED_ACTION); 148d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk mFilter.addAction(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION); 149d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk mFilter.addAction(WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION); 150d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk mFilter.addAction(WifiManager.LINK_CONFIGURATION_CHANGED_ACTION); 151d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk mFilter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION); 152ab313f8443c7dade78b7ae53d18dba002a144425Lorenzo Colitti 153ab313f8443c7dade78b7ae53d18dba002a144425Lorenzo Colitti mNetworkRequest = new NetworkRequest.Builder() 154ab313f8443c7dade78b7ae53d18dba002a144425Lorenzo Colitti .clearCapabilities() 155ab313f8443c7dade78b7ae53d18dba002a144425Lorenzo Colitti .addTransportType(NetworkCapabilities.TRANSPORT_WIFI) 156ab313f8443c7dade78b7ae53d18dba002a144425Lorenzo Colitti .build(); 157d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk } 158d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk 159d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk /** 160d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk * Forces an update of the wifi networks when not scanning. 161d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk */ 162d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk public void forceUpdate() { 163d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk updateAccessPoints(); 164d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk } 165d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk 166d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk /** 167d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk * Force a scan for wifi networks to happen now. 168d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk */ 169d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk public void forceScan() { 170d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk if (mWifiManager.isWifiEnabled() && mScanner != null) { 171d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk mScanner.forceScan(); 172d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk } 173d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk } 174d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk 175d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk /** 176d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk * Temporarily stop scanning for wifi networks. 177d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk */ 178d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk public void pauseScanning() { 179d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk if (mScanner != null) { 180d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk mScanner.pause(); 1816572eae9d278e7199440aeae9a91f9418e8ab4afJason Monk mScanner = null; 182d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk } 183d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk } 184d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk 185d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk /** 186d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk * Resume scanning for wifi networks after it has been paused. 187d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk */ 188d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk public void resumeScanning() { 1896572eae9d278e7199440aeae9a91f9418e8ab4afJason Monk if (mScanner == null) { 1906572eae9d278e7199440aeae9a91f9418e8ab4afJason Monk mScanner = new Scanner(); 1916572eae9d278e7199440aeae9a91f9418e8ab4afJason Monk } 192fcd4612f70bb22e11bf776c76afc45b03a5b026cVinit Deshpande 193dbd05a98e5ed8cd1a4678790df507d9741da5ba2Jason Monk mWorkHandler.sendEmptyMessage(WorkHandler.MSG_RESUME); 194d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk if (mWifiManager.isWifiEnabled()) { 195d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk mScanner.resume(); 196d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk } 19730d8004dbf1941d9ac8c5c7839c135a1b87a87fcJason Monk mWorkHandler.sendEmptyMessage(WorkHandler.MSG_UPDATE_ACCESS_POINTS); 198d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk } 199d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk 200d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk /** 201d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk * Start tracking wifi networks. 202d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk * Registers listeners and starts scanning for wifi networks. If this is not called 203d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk * then forceUpdate() must be called to populate getAccessPoints(). 204d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk */ 205d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk public void startTracking() { 206d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk resumeScanning(); 207d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk if (!mRegistered) { 208d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk mContext.registerReceiver(mReceiver, mFilter); 209ab313f8443c7dade78b7ae53d18dba002a144425Lorenzo Colitti // NetworkCallback objects cannot be reused. http://b/20701525 . 210ab313f8443c7dade78b7ae53d18dba002a144425Lorenzo Colitti mNetworkCallback = new WifiTrackerNetworkCallback(); 211ab313f8443c7dade78b7ae53d18dba002a144425Lorenzo Colitti mConnectivityManager.registerNetworkCallback(mNetworkRequest, mNetworkCallback); 212d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk mRegistered = true; 213d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk } 214d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk } 215d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk 216d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk /** 217d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk * Stop tracking wifi networks. 218d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk * Unregisters all listeners and stops scanning for wifi networks. This should always 219d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk * be called when done with a WifiTracker (if startTracking was called) to ensure 220d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk * proper cleanup. 221d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk */ 222d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk public void stopTracking() { 223d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk if (mRegistered) { 224a42ee81368f4ce469db5a787f0f3a3edd15d9931Jason Monk mWorkHandler.removeMessages(WorkHandler.MSG_UPDATE_ACCESS_POINTS); 225a42ee81368f4ce469db5a787f0f3a3edd15d9931Jason Monk mWorkHandler.removeMessages(WorkHandler.MSG_UPDATE_NETWORK_INFO); 226d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk mContext.unregisterReceiver(mReceiver); 227ab313f8443c7dade78b7ae53d18dba002a144425Lorenzo Colitti mConnectivityManager.unregisterNetworkCallback(mNetworkCallback); 228d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk mRegistered = false; 229d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk } 230d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk pauseScanning(); 231d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk } 232d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk 233d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk /** 234d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk * Gets the current list of access points. 235d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk */ 236d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk public List<AccessPoint> getAccessPoints() { 2371904081c62eeb1d22c7572829ed0591de7a09af6Jason Monk synchronized (mAccessPoints) { 2381904081c62eeb1d22c7572829ed0591de7a09af6Jason Monk return new ArrayList<>(mAccessPoints); 2391904081c62eeb1d22c7572829ed0591de7a09af6Jason Monk } 240d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk } 241d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk 242d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk public WifiManager getManager() { 243d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk return mWifiManager; 244d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk } 245d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk 246d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk public boolean isWifiEnabled() { 247d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk return mWifiManager.isWifiEnabled(); 248d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk } 249d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk 250d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk /** 251d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk * @return true when there are saved networks on the device, regardless 252d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk * of whether the WifiTracker is tracking saved networks. 253d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk */ 254d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk public boolean doSavedNetworksExist() { 255d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk return mSavedNetworksExist; 256d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk } 257d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk 258d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk public boolean isConnected() { 259d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk return mConnected.get(); 260d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk } 261d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk 262d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk public void dump(PrintWriter pw) { 263d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk pw.println(" - wifi tracker ------"); 2641904081c62eeb1d22c7572829ed0591de7a09af6Jason Monk for (AccessPoint accessPoint : getAccessPoints()) { 265d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk pw.println(" " + accessPoint); 266d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk } 267d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk } 268d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk 269dbd05a98e5ed8cd1a4678790df507d9741da5ba2Jason Monk private void handleResume() { 270dbd05a98e5ed8cd1a4678790df507d9741da5ba2Jason Monk mScanResultCache.clear(); 271dbd05a98e5ed8cd1a4678790df507d9741da5ba2Jason Monk mSeenBssids.clear(); 272dbd05a98e5ed8cd1a4678790df507d9741da5ba2Jason Monk mScanId = 0; 273dbd05a98e5ed8cd1a4678790df507d9741da5ba2Jason Monk } 274dbd05a98e5ed8cd1a4678790df507d9741da5ba2Jason Monk 275fcd4612f70bb22e11bf776c76afc45b03a5b026cVinit Deshpande private Collection<ScanResult> fetchScanResults() { 276fcd4612f70bb22e11bf776c76afc45b03a5b026cVinit Deshpande mScanId++; 277fcd4612f70bb22e11bf776c76afc45b03a5b026cVinit Deshpande final List<ScanResult> newResults = mWifiManager.getScanResults(); 278fcd4612f70bb22e11bf776c76afc45b03a5b026cVinit Deshpande for (ScanResult newResult : newResults) { 2799df79043907870fd4c1bd30b460b138169b92bbfMitchell Wills if (newResult.SSID == null || newResult.SSID.isEmpty()) { 2809df79043907870fd4c1bd30b460b138169b92bbfMitchell Wills continue; 2819df79043907870fd4c1bd30b460b138169b92bbfMitchell Wills } 282fcd4612f70bb22e11bf776c76afc45b03a5b026cVinit Deshpande mScanResultCache.put(newResult.BSSID, newResult); 283fcd4612f70bb22e11bf776c76afc45b03a5b026cVinit Deshpande mSeenBssids.put(newResult.BSSID, mScanId); 284fcd4612f70bb22e11bf776c76afc45b03a5b026cVinit Deshpande } 285fcd4612f70bb22e11bf776c76afc45b03a5b026cVinit Deshpande 286fcd4612f70bb22e11bf776c76afc45b03a5b026cVinit Deshpande if (mScanId > NUM_SCANS_TO_CONFIRM_AP_LOSS) { 287fcd4612f70bb22e11bf776c76afc45b03a5b026cVinit Deshpande if (DBG) Log.d(TAG, "------ Dumping SSIDs that were expired on this scan ------"); 288fcd4612f70bb22e11bf776c76afc45b03a5b026cVinit Deshpande Integer threshold = mScanId - NUM_SCANS_TO_CONFIRM_AP_LOSS; 289fcd4612f70bb22e11bf776c76afc45b03a5b026cVinit Deshpande for (Iterator<Map.Entry<String, Integer>> it = mSeenBssids.entrySet().iterator(); 290fcd4612f70bb22e11bf776c76afc45b03a5b026cVinit Deshpande it.hasNext(); /* nothing */) { 291fcd4612f70bb22e11bf776c76afc45b03a5b026cVinit Deshpande Map.Entry<String, Integer> e = it.next(); 292fcd4612f70bb22e11bf776c76afc45b03a5b026cVinit Deshpande if (e.getValue() < threshold) { 293fcd4612f70bb22e11bf776c76afc45b03a5b026cVinit Deshpande ScanResult result = mScanResultCache.get(e.getKey()); 294fcd4612f70bb22e11bf776c76afc45b03a5b026cVinit Deshpande if (DBG) Log.d(TAG, "Removing " + e.getKey() + ":(" + result.SSID + ")"); 295fcd4612f70bb22e11bf776c76afc45b03a5b026cVinit Deshpande mScanResultCache.remove(e.getKey()); 296fcd4612f70bb22e11bf776c76afc45b03a5b026cVinit Deshpande it.remove(); 297fcd4612f70bb22e11bf776c76afc45b03a5b026cVinit Deshpande } 298fcd4612f70bb22e11bf776c76afc45b03a5b026cVinit Deshpande } 299fcd4612f70bb22e11bf776c76afc45b03a5b026cVinit Deshpande if (DBG) Log.d(TAG, "---- Done Dumping SSIDs that were expired on this scan ----"); 300fcd4612f70bb22e11bf776c76afc45b03a5b026cVinit Deshpande } 301fcd4612f70bb22e11bf776c76afc45b03a5b026cVinit Deshpande 302fcd4612f70bb22e11bf776c76afc45b03a5b026cVinit Deshpande return mScanResultCache.values(); 303fcd4612f70bb22e11bf776c76afc45b03a5b026cVinit Deshpande } 304fcd4612f70bb22e11bf776c76afc45b03a5b026cVinit Deshpande 3055a42db2e9c4ce5700a321b9c2aa22189598ceeafMitchell Wills private WifiConfiguration getWifiConfigurationForNetworkId(int networkId) { 3065a42db2e9c4ce5700a321b9c2aa22189598ceeafMitchell Wills final List<WifiConfiguration> configs = mWifiManager.getConfiguredNetworks(); 3075a42db2e9c4ce5700a321b9c2aa22189598ceeafMitchell Wills if (configs != null) { 3085a42db2e9c4ce5700a321b9c2aa22189598ceeafMitchell Wills for (WifiConfiguration config : configs) { 3095a42db2e9c4ce5700a321b9c2aa22189598ceeafMitchell Wills if (mLastInfo != null && networkId == config.networkId && 3105a42db2e9c4ce5700a321b9c2aa22189598ceeafMitchell Wills !(config.selfAdded && config.numAssociation == 0)) { 3115a42db2e9c4ce5700a321b9c2aa22189598ceeafMitchell Wills return config; 3125a42db2e9c4ce5700a321b9c2aa22189598ceeafMitchell Wills } 3135a42db2e9c4ce5700a321b9c2aa22189598ceeafMitchell Wills } 3145a42db2e9c4ce5700a321b9c2aa22189598ceeafMitchell Wills } 3155a42db2e9c4ce5700a321b9c2aa22189598ceeafMitchell Wills return null; 3165a42db2e9c4ce5700a321b9c2aa22189598ceeafMitchell Wills } 3175a42db2e9c4ce5700a321b9c2aa22189598ceeafMitchell Wills 318d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk private void updateAccessPoints() { 319d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk // Swap the current access points into a cached list. 3201904081c62eeb1d22c7572829ed0591de7a09af6Jason Monk List<AccessPoint> cachedAccessPoints = getAccessPoints(); 32130d8004dbf1941d9ac8c5c7839c135a1b87a87fcJason Monk ArrayList<AccessPoint> accessPoints = new ArrayList<>(); 32230d8004dbf1941d9ac8c5c7839c135a1b87a87fcJason Monk 323d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk // Clear out the configs so we don't think something is saved when it isn't. 32430d8004dbf1941d9ac8c5c7839c135a1b87a87fcJason Monk for (AccessPoint accessPoint : cachedAccessPoints) { 325d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk accessPoint.clearConfig(); 326d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk } 327d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk 328d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk /** Lookup table to more quickly update AccessPoints by only considering objects with the 329d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk * correct SSID. Maps SSID -> List of AccessPoints with the given SSID. */ 330d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk Multimap<String, AccessPoint> apMap = new Multimap<String, AccessPoint>(); 331fc40600e30e281712f6c2fc899fc3894b8ad0050Vinit Deshpande WifiConfiguration connectionConfig = null; 3325a42db2e9c4ce5700a321b9c2aa22189598ceeafMitchell Wills if (mLastInfo != null) { 3335a42db2e9c4ce5700a321b9c2aa22189598ceeafMitchell Wills connectionConfig = getWifiConfigurationForNetworkId(mLastInfo.getNetworkId()); 3345a42db2e9c4ce5700a321b9c2aa22189598ceeafMitchell Wills } 335d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk 3360775a98e2095224d4b1618a4739543ce6e7f32ecSanket Padawe final Collection<ScanResult> results = fetchScanResults(); 3370775a98e2095224d4b1618a4739543ce6e7f32ecSanket Padawe 338d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk final List<WifiConfiguration> configs = mWifiManager.getConfiguredNetworks(); 339d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk if (configs != null) { 340d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk mSavedNetworksExist = configs.size() != 0; 341d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk for (WifiConfiguration config : configs) { 342d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk if (config.selfAdded && config.numAssociation == 0) { 343d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk continue; 344d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk } 34530d8004dbf1941d9ac8c5c7839c135a1b87a87fcJason Monk AccessPoint accessPoint = getCachedOrCreate(config, cachedAccessPoints); 346d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk if (mLastInfo != null && mLastNetworkInfo != null) { 347dcf00c9d0f6f985aef186cfe5d7b63761e2f8104Vinit Deshpande if (config.isPasspoint() == false) { 3485a42db2e9c4ce5700a321b9c2aa22189598ceeafMitchell Wills accessPoint.update(connectionConfig, mLastInfo, mLastNetworkInfo); 349dcf00c9d0f6f985aef186cfe5d7b63761e2f8104Vinit Deshpande } 350d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk } 351d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk if (mIncludeSaved) { 3520775a98e2095224d4b1618a4739543ce6e7f32ecSanket Padawe if (!config.isPasspoint() || mIncludePasspoints) { 3530775a98e2095224d4b1618a4739543ce6e7f32ecSanket Padawe // If saved network not present in scan result then set its Rssi to MAX_VALUE 3540775a98e2095224d4b1618a4739543ce6e7f32ecSanket Padawe boolean apFound = false; 3550775a98e2095224d4b1618a4739543ce6e7f32ecSanket Padawe for (ScanResult result : results) { 3560775a98e2095224d4b1618a4739543ce6e7f32ecSanket Padawe if (result.SSID.equals(accessPoint.getSsidStr())) { 3570775a98e2095224d4b1618a4739543ce6e7f32ecSanket Padawe apFound = true; 3580775a98e2095224d4b1618a4739543ce6e7f32ecSanket Padawe break; 3590775a98e2095224d4b1618a4739543ce6e7f32ecSanket Padawe } 3600775a98e2095224d4b1618a4739543ce6e7f32ecSanket Padawe } 3610775a98e2095224d4b1618a4739543ce6e7f32ecSanket Padawe if (!apFound) { 3620775a98e2095224d4b1618a4739543ce6e7f32ecSanket Padawe accessPoint.setRssi(Integer.MAX_VALUE); 3630775a98e2095224d4b1618a4739543ce6e7f32ecSanket Padawe } 36430d8004dbf1941d9ac8c5c7839c135a1b87a87fcJason Monk accessPoints.add(accessPoint); 3650775a98e2095224d4b1618a4739543ce6e7f32ecSanket Padawe } 366dcf00c9d0f6f985aef186cfe5d7b63761e2f8104Vinit Deshpande 367dcf00c9d0f6f985aef186cfe5d7b63761e2f8104Vinit Deshpande if (config.isPasspoint() == false) { 3686980d12c5864941e68933705c1f15a102ac348cbJason Monk apMap.put(accessPoint.getSsidStr(), accessPoint); 369dcf00c9d0f6f985aef186cfe5d7b63761e2f8104Vinit Deshpande } 370d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk } else { 371d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk // If we aren't using saved networks, drop them into the cache so that 372d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk // we have access to their saved info. 37330d8004dbf1941d9ac8c5c7839c135a1b87a87fcJason Monk cachedAccessPoints.add(accessPoint); 374d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk } 375d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk } 376d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk } 377d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk 378d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk if (results != null) { 379d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk for (ScanResult result : results) { 380d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk // Ignore hidden and ad-hoc networks. 381d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk if (result.SSID == null || result.SSID.length() == 0 || 382d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk result.capabilities.contains("[IBSS]")) { 383d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk continue; 384d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk } 385d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk 386d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk boolean found = false; 387d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk for (AccessPoint accessPoint : apMap.getAll(result.SSID)) { 388d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk if (accessPoint.update(result)) { 389d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk found = true; 390d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk break; 391d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk } 392d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk } 393d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk if (!found && mIncludeScans) { 39430d8004dbf1941d9ac8c5c7839c135a1b87a87fcJason Monk AccessPoint accessPoint = getCachedOrCreate(result, cachedAccessPoints); 395d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk if (mLastInfo != null && mLastNetworkInfo != null) { 3965a42db2e9c4ce5700a321b9c2aa22189598ceeafMitchell Wills accessPoint.update(connectionConfig, mLastInfo, mLastNetworkInfo); 397d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk } 398fc40600e30e281712f6c2fc899fc3894b8ad0050Vinit Deshpande 399a0d929e505432a8c84f3899696c910db16bd73bfVinit Deshpande if (result.isPasspointNetwork()) { 400dcf00c9d0f6f985aef186cfe5d7b63761e2f8104Vinit Deshpande WifiConfiguration config = mWifiManager.getMatchingWifiConfig(result); 401dcf00c9d0f6f985aef186cfe5d7b63761e2f8104Vinit Deshpande if (config != null) { 402dcf00c9d0f6f985aef186cfe5d7b63761e2f8104Vinit Deshpande accessPoint.update(config); 403dcf00c9d0f6f985aef186cfe5d7b63761e2f8104Vinit Deshpande } 404dcf00c9d0f6f985aef186cfe5d7b63761e2f8104Vinit Deshpande } 405dcf00c9d0f6f985aef186cfe5d7b63761e2f8104Vinit Deshpande 406fc40600e30e281712f6c2fc899fc3894b8ad0050Vinit Deshpande if (mLastInfo != null && mLastInfo.getBSSID() != null 407fc40600e30e281712f6c2fc899fc3894b8ad0050Vinit Deshpande && mLastInfo.getBSSID().equals(result.BSSID) 408fc40600e30e281712f6c2fc899fc3894b8ad0050Vinit Deshpande && connectionConfig != null && connectionConfig.isPasspoint()) { 409fc40600e30e281712f6c2fc899fc3894b8ad0050Vinit Deshpande /* This network is connected via this passpoint config */ 410fc40600e30e281712f6c2fc899fc3894b8ad0050Vinit Deshpande /* SSID match is not going to work for it; so update explicitly */ 411fc40600e30e281712f6c2fc899fc3894b8ad0050Vinit Deshpande accessPoint.update(connectionConfig); 412fc40600e30e281712f6c2fc899fc3894b8ad0050Vinit Deshpande } 413fc40600e30e281712f6c2fc899fc3894b8ad0050Vinit Deshpande 41430d8004dbf1941d9ac8c5c7839c135a1b87a87fcJason Monk accessPoints.add(accessPoint); 4156980d12c5864941e68933705c1f15a102ac348cbJason Monk apMap.put(accessPoint.getSsidStr(), accessPoint); 416d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk } 417d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk } 418d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk } 419d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk 420d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk // Pre-sort accessPoints to speed preference insertion 42130d8004dbf1941d9ac8c5c7839c135a1b87a87fcJason Monk Collections.sort(accessPoints); 422fcd4612f70bb22e11bf776c76afc45b03a5b026cVinit Deshpande 423fcd4612f70bb22e11bf776c76afc45b03a5b026cVinit Deshpande // Log accesspoints that were deleted 424fcd4612f70bb22e11bf776c76afc45b03a5b026cVinit Deshpande if (DBG) Log.d(TAG, "------ Dumping SSIDs that were not seen on this scan ------"); 425fcd4612f70bb22e11bf776c76afc45b03a5b026cVinit Deshpande for (AccessPoint prevAccessPoint : mAccessPoints) { 426fcd4612f70bb22e11bf776c76afc45b03a5b026cVinit Deshpande if (prevAccessPoint.getSsid() == null) continue; 427fcd4612f70bb22e11bf776c76afc45b03a5b026cVinit Deshpande String prevSsid = prevAccessPoint.getSsidStr(); 428fcd4612f70bb22e11bf776c76afc45b03a5b026cVinit Deshpande boolean found = false; 429fcd4612f70bb22e11bf776c76afc45b03a5b026cVinit Deshpande for (AccessPoint newAccessPoint : accessPoints) { 430fcd4612f70bb22e11bf776c76afc45b03a5b026cVinit Deshpande if (newAccessPoint.getSsid() != null && newAccessPoint.getSsid().equals(prevSsid)) { 431fcd4612f70bb22e11bf776c76afc45b03a5b026cVinit Deshpande found = true; 432fcd4612f70bb22e11bf776c76afc45b03a5b026cVinit Deshpande break; 433fcd4612f70bb22e11bf776c76afc45b03a5b026cVinit Deshpande } 434fcd4612f70bb22e11bf776c76afc45b03a5b026cVinit Deshpande } 435fcd4612f70bb22e11bf776c76afc45b03a5b026cVinit Deshpande if (!found) 436fcd4612f70bb22e11bf776c76afc45b03a5b026cVinit Deshpande if (DBG) Log.d(TAG, "Did not find " + prevSsid + " in this scan"); 437fcd4612f70bb22e11bf776c76afc45b03a5b026cVinit Deshpande } 438fcd4612f70bb22e11bf776c76afc45b03a5b026cVinit Deshpande if (DBG) Log.d(TAG, "---- Done dumping SSIDs that were not seen on this scan ----"); 439fcd4612f70bb22e11bf776c76afc45b03a5b026cVinit Deshpande 44030d8004dbf1941d9ac8c5c7839c135a1b87a87fcJason Monk mAccessPoints = accessPoints; 44130d8004dbf1941d9ac8c5c7839c135a1b87a87fcJason Monk mMainHandler.sendEmptyMessage(MainHandler.MSG_ACCESS_POINT_CHANGED); 442d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk } 443d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk 4441904081c62eeb1d22c7572829ed0591de7a09af6Jason Monk private AccessPoint getCachedOrCreate(ScanResult result, List<AccessPoint> cache) { 44530d8004dbf1941d9ac8c5c7839c135a1b87a87fcJason Monk final int N = cache.size(); 446d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk for (int i = 0; i < N; i++) { 44730d8004dbf1941d9ac8c5c7839c135a1b87a87fcJason Monk if (cache.get(i).matches(result)) { 44830d8004dbf1941d9ac8c5c7839c135a1b87a87fcJason Monk AccessPoint ret = cache.remove(i); 449d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk ret.update(result); 450d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk return ret; 451d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk } 452d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk } 453d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk return new AccessPoint(mContext, result); 454d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk } 455d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk 4561904081c62eeb1d22c7572829ed0591de7a09af6Jason Monk private AccessPoint getCachedOrCreate(WifiConfiguration config, List<AccessPoint> cache) { 45730d8004dbf1941d9ac8c5c7839c135a1b87a87fcJason Monk final int N = cache.size(); 458d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk for (int i = 0; i < N; i++) { 45930d8004dbf1941d9ac8c5c7839c135a1b87a87fcJason Monk if (cache.get(i).matches(config)) { 46030d8004dbf1941d9ac8c5c7839c135a1b87a87fcJason Monk AccessPoint ret = cache.remove(i); 461d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk ret.loadConfig(config); 462d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk return ret; 463d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk } 464d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk } 465d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk return new AccessPoint(mContext, config); 466d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk } 467d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk 468d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk private void updateNetworkInfo(NetworkInfo networkInfo) { 469e04ae8aced5d3e2cb8a3f6fd800e048bd5e572cfJason Monk /* sticky broadcasts can call this when wifi is disabled */ 470e04ae8aced5d3e2cb8a3f6fd800e048bd5e572cfJason Monk if (!mWifiManager.isWifiEnabled()) { 471e04ae8aced5d3e2cb8a3f6fd800e048bd5e572cfJason Monk mMainHandler.sendEmptyMessage(MainHandler.MSG_PAUSE_SCANNING); 472e04ae8aced5d3e2cb8a3f6fd800e048bd5e572cfJason Monk return; 473e04ae8aced5d3e2cb8a3f6fd800e048bd5e572cfJason Monk } 474d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk 475e04ae8aced5d3e2cb8a3f6fd800e048bd5e572cfJason Monk if (networkInfo != null && 476e04ae8aced5d3e2cb8a3f6fd800e048bd5e572cfJason Monk networkInfo.getDetailedState() == DetailedState.OBTAINING_IPADDR) { 477e04ae8aced5d3e2cb8a3f6fd800e048bd5e572cfJason Monk mMainHandler.sendEmptyMessage(MainHandler.MSG_PAUSE_SCANNING); 478e04ae8aced5d3e2cb8a3f6fd800e048bd5e572cfJason Monk } else { 479e04ae8aced5d3e2cb8a3f6fd800e048bd5e572cfJason Monk mMainHandler.sendEmptyMessage(MainHandler.MSG_RESUME_SCANNING); 480d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk } 481d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk 482d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk if (networkInfo != null) { 483d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk mLastNetworkInfo = networkInfo; 484d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk } 485d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk 4865a42db2e9c4ce5700a321b9c2aa22189598ceeafMitchell Wills WifiConfiguration connectionConfig = null; 487ab313f8443c7dade78b7ae53d18dba002a144425Lorenzo Colitti mLastInfo = mWifiManager.getConnectionInfo(); 4885a42db2e9c4ce5700a321b9c2aa22189598ceeafMitchell Wills if (mLastInfo != null) { 4895a42db2e9c4ce5700a321b9c2aa22189598ceeafMitchell Wills connectionConfig = getWifiConfigurationForNetworkId(mLastInfo.getNetworkId()); 4905a42db2e9c4ce5700a321b9c2aa22189598ceeafMitchell Wills } 4915a42db2e9c4ce5700a321b9c2aa22189598ceeafMitchell Wills 492d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk boolean reorder = false; 493d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk for (int i = mAccessPoints.size() - 1; i >= 0; --i) { 4945a42db2e9c4ce5700a321b9c2aa22189598ceeafMitchell Wills if (mAccessPoints.get(i).update(connectionConfig, mLastInfo, mLastNetworkInfo)) { 495d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk reorder = true; 496d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk } 497d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk } 498d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk if (reorder) { 4991904081c62eeb1d22c7572829ed0591de7a09af6Jason Monk synchronized (mAccessPoints) { 5001904081c62eeb1d22c7572829ed0591de7a09af6Jason Monk Collections.sort(mAccessPoints); 5011904081c62eeb1d22c7572829ed0591de7a09af6Jason Monk } 50230d8004dbf1941d9ac8c5c7839c135a1b87a87fcJason Monk mMainHandler.sendEmptyMessage(MainHandler.MSG_ACCESS_POINT_CHANGED); 503d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk } 504d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk } 505d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk 506d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk private void updateWifiState(int state) { 507cf0875a9b69da9da5e16563eca33609e775d9195Mitchell Wills mWorkHandler.obtainMessage(WorkHandler.MSG_UPDATE_WIFI_STATE, state, 0).sendToTarget(); 508d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk } 509d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk 510d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk public static List<AccessPoint> getCurrentAccessPoints(Context context, boolean includeSaved, 511dcf00c9d0f6f985aef186cfe5d7b63761e2f8104Vinit Deshpande boolean includeScans, boolean includePasspoints) { 512dcf00c9d0f6f985aef186cfe5d7b63761e2f8104Vinit Deshpande WifiTracker tracker = new WifiTracker(context, 51330d8004dbf1941d9ac8c5c7839c135a1b87a87fcJason Monk null, null, includeSaved, includeScans, includePasspoints); 514d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk tracker.forceUpdate(); 515d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk return tracker.getAccessPoints(); 516d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk } 517d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk 518d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk @VisibleForTesting 519d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk final BroadcastReceiver mReceiver = new BroadcastReceiver() { 520d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk @Override 521d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk public void onReceive(Context context, Intent intent) { 522d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk String action = intent.getAction(); 523d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk if (WifiManager.WIFI_STATE_CHANGED_ACTION.equals(action)) { 524d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk updateWifiState(intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE, 525d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk WifiManager.WIFI_STATE_UNKNOWN)); 526d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk } else if (WifiManager.SCAN_RESULTS_AVAILABLE_ACTION.equals(action) || 527d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION.equals(action) || 528d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk WifiManager.LINK_CONFIGURATION_CHANGED_ACTION.equals(action)) { 52930d8004dbf1941d9ac8c5c7839c135a1b87a87fcJason Monk mWorkHandler.sendEmptyMessage(WorkHandler.MSG_UPDATE_ACCESS_POINTS); 530d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk } else if (WifiManager.NETWORK_STATE_CHANGED_ACTION.equals(action)) { 531d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk NetworkInfo info = (NetworkInfo) intent.getParcelableExtra( 532d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk WifiManager.EXTRA_NETWORK_INFO); 533d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk mConnected.set(info.isConnected()); 53430d8004dbf1941d9ac8c5c7839c135a1b87a87fcJason Monk 53530d8004dbf1941d9ac8c5c7839c135a1b87a87fcJason Monk mMainHandler.sendEmptyMessage(MainHandler.MSG_CONNECTED_CHANGED); 53630d8004dbf1941d9ac8c5c7839c135a1b87a87fcJason Monk 53730d8004dbf1941d9ac8c5c7839c135a1b87a87fcJason Monk mWorkHandler.sendEmptyMessage(WorkHandler.MSG_UPDATE_ACCESS_POINTS); 53830d8004dbf1941d9ac8c5c7839c135a1b87a87fcJason Monk mWorkHandler.obtainMessage(WorkHandler.MSG_UPDATE_NETWORK_INFO, info) 53930d8004dbf1941d9ac8c5c7839c135a1b87a87fcJason Monk .sendToTarget(); 540d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk } 541d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk } 542d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk }; 543d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk 544ab313f8443c7dade78b7ae53d18dba002a144425Lorenzo Colitti private final class WifiTrackerNetworkCallback extends ConnectivityManager.NetworkCallback { 545ab313f8443c7dade78b7ae53d18dba002a144425Lorenzo Colitti public void onCapabilitiesChanged(Network network, NetworkCapabilities nc) { 546ab313f8443c7dade78b7ae53d18dba002a144425Lorenzo Colitti if (network.equals(mWifiManager.getCurrentNetwork())) { 547ab313f8443c7dade78b7ae53d18dba002a144425Lorenzo Colitti // We don't send a NetworkInfo object along with this message, because even if we 548ab313f8443c7dade78b7ae53d18dba002a144425Lorenzo Colitti // fetch one from ConnectivityManager, it might be older than the most recent 549ab313f8443c7dade78b7ae53d18dba002a144425Lorenzo Colitti // NetworkInfo message we got via a WIFI_STATE_CHANGED broadcast. 550ab313f8443c7dade78b7ae53d18dba002a144425Lorenzo Colitti mWorkHandler.sendEmptyMessage(WorkHandler.MSG_UPDATE_NETWORK_INFO); 551ab313f8443c7dade78b7ae53d18dba002a144425Lorenzo Colitti } 552ab313f8443c7dade78b7ae53d18dba002a144425Lorenzo Colitti } 553ab313f8443c7dade78b7ae53d18dba002a144425Lorenzo Colitti } 554ab313f8443c7dade78b7ae53d18dba002a144425Lorenzo Colitti 55530d8004dbf1941d9ac8c5c7839c135a1b87a87fcJason Monk private final class MainHandler extends Handler { 55630d8004dbf1941d9ac8c5c7839c135a1b87a87fcJason Monk private static final int MSG_CONNECTED_CHANGED = 0; 55730d8004dbf1941d9ac8c5c7839c135a1b87a87fcJason Monk private static final int MSG_WIFI_STATE_CHANGED = 1; 55830d8004dbf1941d9ac8c5c7839c135a1b87a87fcJason Monk private static final int MSG_ACCESS_POINT_CHANGED = 2; 559e04ae8aced5d3e2cb8a3f6fd800e048bd5e572cfJason Monk private static final int MSG_RESUME_SCANNING = 3; 560e04ae8aced5d3e2cb8a3f6fd800e048bd5e572cfJason Monk private static final int MSG_PAUSE_SCANNING = 4; 56130d8004dbf1941d9ac8c5c7839c135a1b87a87fcJason Monk 5622b51cc30d523cce3609a498c466364caf3ce504eJason Monk public MainHandler(Looper looper) { 5632b51cc30d523cce3609a498c466364caf3ce504eJason Monk super(looper); 5642b51cc30d523cce3609a498c466364caf3ce504eJason Monk } 5652b51cc30d523cce3609a498c466364caf3ce504eJason Monk 56630d8004dbf1941d9ac8c5c7839c135a1b87a87fcJason Monk @Override 56730d8004dbf1941d9ac8c5c7839c135a1b87a87fcJason Monk public void handleMessage(Message msg) { 56830d8004dbf1941d9ac8c5c7839c135a1b87a87fcJason Monk if (mListener == null) { 56930d8004dbf1941d9ac8c5c7839c135a1b87a87fcJason Monk return; 57030d8004dbf1941d9ac8c5c7839c135a1b87a87fcJason Monk } 57130d8004dbf1941d9ac8c5c7839c135a1b87a87fcJason Monk switch (msg.what) { 57230d8004dbf1941d9ac8c5c7839c135a1b87a87fcJason Monk case MSG_CONNECTED_CHANGED: 57330d8004dbf1941d9ac8c5c7839c135a1b87a87fcJason Monk mListener.onConnectedChanged(); 57430d8004dbf1941d9ac8c5c7839c135a1b87a87fcJason Monk break; 57530d8004dbf1941d9ac8c5c7839c135a1b87a87fcJason Monk case MSG_WIFI_STATE_CHANGED: 57630d8004dbf1941d9ac8c5c7839c135a1b87a87fcJason Monk mListener.onWifiStateChanged(msg.arg1); 57730d8004dbf1941d9ac8c5c7839c135a1b87a87fcJason Monk break; 57830d8004dbf1941d9ac8c5c7839c135a1b87a87fcJason Monk case MSG_ACCESS_POINT_CHANGED: 57930d8004dbf1941d9ac8c5c7839c135a1b87a87fcJason Monk mListener.onAccessPointsChanged(); 58030d8004dbf1941d9ac8c5c7839c135a1b87a87fcJason Monk break; 581e04ae8aced5d3e2cb8a3f6fd800e048bd5e572cfJason Monk case MSG_RESUME_SCANNING: 582e04ae8aced5d3e2cb8a3f6fd800e048bd5e572cfJason Monk if (mScanner != null) { 583e04ae8aced5d3e2cb8a3f6fd800e048bd5e572cfJason Monk mScanner.resume(); 584e04ae8aced5d3e2cb8a3f6fd800e048bd5e572cfJason Monk } 585e04ae8aced5d3e2cb8a3f6fd800e048bd5e572cfJason Monk break; 586e04ae8aced5d3e2cb8a3f6fd800e048bd5e572cfJason Monk case MSG_PAUSE_SCANNING: 587e04ae8aced5d3e2cb8a3f6fd800e048bd5e572cfJason Monk if (mScanner != null) { 588e04ae8aced5d3e2cb8a3f6fd800e048bd5e572cfJason Monk mScanner.pause(); 589e04ae8aced5d3e2cb8a3f6fd800e048bd5e572cfJason Monk } 590e04ae8aced5d3e2cb8a3f6fd800e048bd5e572cfJason Monk break; 59130d8004dbf1941d9ac8c5c7839c135a1b87a87fcJason Monk } 59230d8004dbf1941d9ac8c5c7839c135a1b87a87fcJason Monk } 59330d8004dbf1941d9ac8c5c7839c135a1b87a87fcJason Monk } 59430d8004dbf1941d9ac8c5c7839c135a1b87a87fcJason Monk 59530d8004dbf1941d9ac8c5c7839c135a1b87a87fcJason Monk private final class WorkHandler extends Handler { 59630d8004dbf1941d9ac8c5c7839c135a1b87a87fcJason Monk private static final int MSG_UPDATE_ACCESS_POINTS = 0; 59730d8004dbf1941d9ac8c5c7839c135a1b87a87fcJason Monk private static final int MSG_UPDATE_NETWORK_INFO = 1; 598dbd05a98e5ed8cd1a4678790df507d9741da5ba2Jason Monk private static final int MSG_RESUME = 2; 599cf0875a9b69da9da5e16563eca33609e775d9195Mitchell Wills private static final int MSG_UPDATE_WIFI_STATE = 3; 60030d8004dbf1941d9ac8c5c7839c135a1b87a87fcJason Monk 60130d8004dbf1941d9ac8c5c7839c135a1b87a87fcJason Monk public WorkHandler(Looper looper) { 60230d8004dbf1941d9ac8c5c7839c135a1b87a87fcJason Monk super(looper); 60330d8004dbf1941d9ac8c5c7839c135a1b87a87fcJason Monk } 60430d8004dbf1941d9ac8c5c7839c135a1b87a87fcJason Monk 60530d8004dbf1941d9ac8c5c7839c135a1b87a87fcJason Monk @Override 60630d8004dbf1941d9ac8c5c7839c135a1b87a87fcJason Monk public void handleMessage(Message msg) { 60730d8004dbf1941d9ac8c5c7839c135a1b87a87fcJason Monk switch (msg.what) { 60830d8004dbf1941d9ac8c5c7839c135a1b87a87fcJason Monk case MSG_UPDATE_ACCESS_POINTS: 60930d8004dbf1941d9ac8c5c7839c135a1b87a87fcJason Monk updateAccessPoints(); 61030d8004dbf1941d9ac8c5c7839c135a1b87a87fcJason Monk break; 61130d8004dbf1941d9ac8c5c7839c135a1b87a87fcJason Monk case MSG_UPDATE_NETWORK_INFO: 61230d8004dbf1941d9ac8c5c7839c135a1b87a87fcJason Monk updateNetworkInfo((NetworkInfo) msg.obj); 61330d8004dbf1941d9ac8c5c7839c135a1b87a87fcJason Monk break; 614dbd05a98e5ed8cd1a4678790df507d9741da5ba2Jason Monk case MSG_RESUME: 615dbd05a98e5ed8cd1a4678790df507d9741da5ba2Jason Monk handleResume(); 616dbd05a98e5ed8cd1a4678790df507d9741da5ba2Jason Monk break; 617cf0875a9b69da9da5e16563eca33609e775d9195Mitchell Wills case MSG_UPDATE_WIFI_STATE: 618cf0875a9b69da9da5e16563eca33609e775d9195Mitchell Wills if (msg.arg1 == WifiManager.WIFI_STATE_ENABLED) { 619cf0875a9b69da9da5e16563eca33609e775d9195Mitchell Wills if (mScanner != null) { 620cf0875a9b69da9da5e16563eca33609e775d9195Mitchell Wills // We only need to resume if mScanner isn't null because 621cf0875a9b69da9da5e16563eca33609e775d9195Mitchell Wills // that means we want to be scanning. 622cf0875a9b69da9da5e16563eca33609e775d9195Mitchell Wills mScanner.resume(); 623cf0875a9b69da9da5e16563eca33609e775d9195Mitchell Wills } 624cf0875a9b69da9da5e16563eca33609e775d9195Mitchell Wills } else { 625cf0875a9b69da9da5e16563eca33609e775d9195Mitchell Wills mLastInfo = null; 626cf0875a9b69da9da5e16563eca33609e775d9195Mitchell Wills mLastNetworkInfo = null; 627cf0875a9b69da9da5e16563eca33609e775d9195Mitchell Wills if (mScanner != null) { 628cf0875a9b69da9da5e16563eca33609e775d9195Mitchell Wills mScanner.pause(); 629cf0875a9b69da9da5e16563eca33609e775d9195Mitchell Wills } 630cf0875a9b69da9da5e16563eca33609e775d9195Mitchell Wills } 631cf0875a9b69da9da5e16563eca33609e775d9195Mitchell Wills mMainHandler.obtainMessage(MainHandler.MSG_WIFI_STATE_CHANGED, msg.arg1, 0) 632cf0875a9b69da9da5e16563eca33609e775d9195Mitchell Wills .sendToTarget(); 633cf0875a9b69da9da5e16563eca33609e775d9195Mitchell Wills break; 63430d8004dbf1941d9ac8c5c7839c135a1b87a87fcJason Monk } 63530d8004dbf1941d9ac8c5c7839c135a1b87a87fcJason Monk } 63630d8004dbf1941d9ac8c5c7839c135a1b87a87fcJason Monk } 63730d8004dbf1941d9ac8c5c7839c135a1b87a87fcJason Monk 638d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk @VisibleForTesting 639d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk class Scanner extends Handler { 6402b51cc30d523cce3609a498c466364caf3ce504eJason Monk static final int MSG_SCAN = 0; 6416572eae9d278e7199440aeae9a91f9418e8ab4afJason Monk 642d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk private int mRetry = 0; 643d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk 644d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk void resume() { 6456572eae9d278e7199440aeae9a91f9418e8ab4afJason Monk if (!hasMessages(MSG_SCAN)) { 6466572eae9d278e7199440aeae9a91f9418e8ab4afJason Monk sendEmptyMessage(MSG_SCAN); 647d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk } 648d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk } 649d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk 650d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk void forceScan() { 6516572eae9d278e7199440aeae9a91f9418e8ab4afJason Monk removeMessages(MSG_SCAN); 6526572eae9d278e7199440aeae9a91f9418e8ab4afJason Monk sendEmptyMessage(MSG_SCAN); 653d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk } 654d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk 655d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk void pause() { 656d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk mRetry = 0; 6576572eae9d278e7199440aeae9a91f9418e8ab4afJason Monk removeMessages(MSG_SCAN); 6586572eae9d278e7199440aeae9a91f9418e8ab4afJason Monk } 6596572eae9d278e7199440aeae9a91f9418e8ab4afJason Monk 6606572eae9d278e7199440aeae9a91f9418e8ab4afJason Monk @VisibleForTesting 6616572eae9d278e7199440aeae9a91f9418e8ab4afJason Monk boolean isScanning() { 6626572eae9d278e7199440aeae9a91f9418e8ab4afJason Monk return hasMessages(MSG_SCAN); 663d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk } 664d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk 665d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk @Override 666d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk public void handleMessage(Message message) { 6676572eae9d278e7199440aeae9a91f9418e8ab4afJason Monk if (message.what != MSG_SCAN) return; 668d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk if (mWifiManager.startScan()) { 669d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk mRetry = 0; 670d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk } else if (++mRetry >= 3) { 671d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk mRetry = 0; 672d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk if (mContext != null) { 673d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk Toast.makeText(mContext, R.string.wifi_fail_to_scan, Toast.LENGTH_LONG).show(); 674d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk } 675d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk return; 676d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk } 677d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk sendEmptyMessageDelayed(0, WIFI_RESCAN_INTERVAL_MS); 678d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk } 679d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk } 680d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk 681d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk /** A restricted multimap for use in constructAccessPoints */ 682d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk private static class Multimap<K,V> { 683d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk private final HashMap<K,List<V>> store = new HashMap<K,List<V>>(); 684d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk /** retrieve a non-null list of values with key K */ 685d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk List<V> getAll(K key) { 686d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk List<V> values = store.get(key); 687d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk return values != null ? values : Collections.<V>emptyList(); 688d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk } 689d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk 690d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk void put(K key, V val) { 691d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk List<V> curVals = store.get(key); 692d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk if (curVals == null) { 693d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk curVals = new ArrayList<V>(3); 694d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk store.put(key, curVals); 695d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk } 696d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk curVals.add(val); 697d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk } 698d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk } 699d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk 700d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk public interface WifiListener { 701d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk /** 702d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk * Called when the state of Wifi has changed, the state will be one of 703d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk * the following. 704d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk * 705d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk * <li>{@link WifiManager#WIFI_STATE_DISABLED}</li> 706d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk * <li>{@link WifiManager#WIFI_STATE_ENABLED}</li> 707d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk * <li>{@link WifiManager#WIFI_STATE_DISABLING}</li> 708d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk * <li>{@link WifiManager#WIFI_STATE_ENABLING}</li> 709d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk * <li>{@link WifiManager#WIFI_STATE_UNKNOWN}</li> 710d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk * <p> 711d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk * 712d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk * @param state The new state of wifi. 713d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk */ 714d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk void onWifiStateChanged(int state); 715d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk 716d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk /** 717d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk * Called when the connection state of wifi has changed and isConnected 718d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk * should be called to get the updated state. 719d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk */ 720d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk void onConnectedChanged(); 721d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk 722d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk /** 723d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk * Called to indicate the list of AccessPoints has been updated and 724d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk * getAccessPoints should be called to get the latest information. 725d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk */ 726d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk void onAccessPointsChanged(); 727d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk } 728d52356aa5e82c7c5db61672bbe8d0f44861f3e59Jason Monk} 729