WifiConnectivityManager.java revision 09abbe29be6e552a2531b0367bd6d29647d33767
1/*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.server.wifi;
18
19import static com.android.server.wifi.WifiStateMachine.WIFI_WORK_SOURCE;
20
21import android.app.ActivityManager;
22import android.app.AlarmManager;
23import android.content.Context;
24import android.net.wifi.ScanResult;
25import android.net.wifi.WifiConfiguration;
26import android.net.wifi.WifiInfo;
27import android.net.wifi.WifiManager;
28import android.net.wifi.WifiScanner;
29import android.net.wifi.WifiScanner.PnoSettings;
30import android.net.wifi.WifiScanner.ScanSettings;
31import android.util.LocalLog;
32import android.util.Log;
33
34import com.android.internal.R;
35import com.android.server.wifi.util.ScanDetailUtil;
36
37import java.io.FileDescriptor;
38import java.io.PrintWriter;
39import java.util.ArrayList;
40import java.util.List;
41import java.util.Set;
42
43/**
44 * This class manages all the connectivity related scanning activities.
45 *
46 * When the screen is turned on or off, WiFi is connected or disconnected,
47 * or on-demand, a scan is initiatiated and the scan results are passed
48 * to QNS for it to make a recommendation on which network to connect to.
49 */
50public class WifiConnectivityManager {
51    private static final String TAG = "WifiConnectivityManager";
52
53    // Periodic scan interval in milli-seconds. This is the scan
54    // performed when screen is on.
55    private static final int PERIODIC_SCAN_INTERVAL_MS = 20000; // 20 seconds
56    // PNO scan interval in milli-seconds. This is the scan
57    // performed when screen is off.
58    private static final int PNO_SCAN_INTERVAL_MS = 160000; // 160 seconds
59    // Maximum number of retries when starting a scan failed
60    private static final int MAX_SCAN_RESTART_ALLOWED = 5;
61    // Number of milli-seconds to delay before retry starting
62    // a previously failed scan
63    private static final int RESTART_SCAN_DELAY_MS = 2000; // 2 seconds
64    // When in disconnected mode, a watchdog timer will be fired
65    // every WATCHDOG_INTERVAL_MS to start a single scan. This is
66    // to prevent caveat from things like PNO scan.
67    private static final int WATCHDOG_INTERVAL_MS = 1200000; // 20 minutes
68
69    // WifiStateMachine has a bunch of states. From the
70    // WifiConnectivityManager's perspective it only cares
71    // if it is in Connected state, Disconnected state or in
72    // transition between these two states.
73    public static final int WIFI_STATE_UNKNOWN = 0;
74    public static final int WIFI_STATE_CONNECTED = 1;
75    public static final int WIFI_STATE_DISCONNECTED = 2;
76    public static final int WIFI_STATE_TRANSITIONING = 3;
77
78    private final WifiStateMachine mStateMachine;
79    private final WifiScanner mScanner;
80    private final WifiConfigManager mConfigManager;
81    private final WifiInfo mWifiInfo;
82    private final WifiQualifiedNetworkSelector mQualifiedNetworkSelector;
83    private final AlarmManager mAlarmManager;
84    private final LocalLog mLocalLog = new LocalLog(ActivityManager.isLowRamDeviceStatic()
85                                                        ? 1024 : 16384);
86    private final WifiLastResortWatchdog mWifiLastResortWatchdog;
87    private boolean mDbg = false;
88    private boolean mWifiEnabled = false;
89    private boolean mWifiConnectivityManagerEnabled = true;
90    private boolean mForceSelectNetwork = false;
91    private boolean mScreenOn = false;
92    private int mWifiState = WIFI_STATE_UNKNOWN;
93    private boolean mUntrustedConnectionAllowed = false;
94    private int mScanRestartCount = 0;
95    private int mSingleScanRestartCount = 0;
96    // Due to b/28020168, timer based single scan will be scheduled every
97    // PERIODIC_SCAN_INTERVAL_MS to provide periodic scan.
98    private boolean mNoBackgroundScan = true;
99
100    // PNO settings
101    private int mMin5GHzRssi;
102    private int mMin24GHzRssi;
103    private int mInitialScoreMax;
104    private int mCurrentConnectionBonus;
105    private int mSameNetworkBonus;
106    private int mSecureBonus;
107    private int mBand5GHzBonus;
108
109    // A helper to log debugging information in the local log buffer, which can
110    // be retrieved in bugreport.
111    private void localLog(String log) {
112        mLocalLog.log(log);
113    }
114
115    // A periodic/PNO scan will be rescheduled up to MAX_SCAN_RESTART_ALLOWED times
116    // if the start scan command failed. An timer is used here to make it a deferred retry.
117    private final AlarmManager.OnAlarmListener mRestartScanListener =
118            new AlarmManager.OnAlarmListener() {
119                public void onAlarm() {
120                    startConnectivityScan(mForceSelectNetwork);
121                }
122            };
123
124    // A single scan will be rescheduled up to MAX_SCAN_RESTART_ALLOWED times
125    // if the start scan command failed. An timer is used here to make it a deferred retry.
126    private final AlarmManager.OnAlarmListener mRestartSingleScanListener =
127            new AlarmManager.OnAlarmListener() {
128                public void onAlarm() {
129                    startSingleScan();
130                }
131            };
132
133    // As a watchdog mechanism, a single scan will be scheduled every WATCHDOG_INTERVAL_MS
134    // if it is in the WIFI_STATE_DISCONNECTED state.
135    private final AlarmManager.OnAlarmListener mWatchdogListener =
136            new AlarmManager.OnAlarmListener() {
137                public void onAlarm() {
138                    watchdogHandler();
139                }
140            };
141
142    // Due to b/28020168, timer based single scan will be scheduled every
143    // PERIODIC_SCAN_INTERVAL_MS to provide periodic scan.
144    private final AlarmManager.OnAlarmListener mPeriodicScanTimerListener =
145            new AlarmManager.OnAlarmListener() {
146                public void onAlarm() {
147                    periodicScanTimerHandler();
148                }
149            };
150
151    /**
152     * Handles 'onResult' callbacks for the Periodic, Single & Pno ScanListener.
153     * Executes selection of potential network candidates, initiation of connection attempt to that
154     * network.
155     */
156    private void handleScanResults(List<ScanDetail> scanDetails, String listenerName) {
157        localLog(listenerName + " onResults: start QNS");
158        WifiConfiguration candidate =
159                mQualifiedNetworkSelector.selectQualifiedNetwork(mForceSelectNetwork,
160                mUntrustedConnectionAllowed, scanDetails,
161                mStateMachine.isLinkDebouncing(), mStateMachine.isConnected(),
162                mStateMachine.isDisconnected(),
163                mStateMachine.isSupplicantTransientState());
164        mWifiLastResortWatchdog.updateAvailableNetworks(
165                mQualifiedNetworkSelector.getFilteredScanDetails());
166        if (candidate != null) {
167            localLog(listenerName + ": QNS candidate-" + candidate.SSID);
168            connectToNetwork(candidate);
169        }
170
171    }
172
173    // Periodic scan results listener. A periodic scan is initiated when
174    // screen is on.
175    private class PeriodicScanListener implements WifiScanner.ScanListener {
176        private List<ScanDetail> mScanDetails = new ArrayList<ScanDetail>();
177
178        public void clearScanDetails() {
179            mScanDetails.clear();
180        }
181
182        @Override
183        public void onSuccess() {
184            localLog("PeriodicScanListener onSuccess");
185
186            // reset the count
187            mScanRestartCount = 0;
188        }
189
190        @Override
191        public void onFailure(int reason, String description) {
192            Log.e(TAG, "PeriodicScanListener onFailure:"
193                          + " reason: " + reason
194                          + " description: " + description);
195
196            // reschedule the scan
197            if (mScanRestartCount++ < MAX_SCAN_RESTART_ALLOWED) {
198                scheduleDelayedConnectivityScan();
199            } else {
200                mScanRestartCount = 0;
201                Log.e(TAG, "Failed to successfully start periodic scan for "
202                          + MAX_SCAN_RESTART_ALLOWED + " times");
203            }
204        }
205
206        @Override
207        public void onPeriodChanged(int periodInMs) {
208            localLog("PeriodicScanListener onPeriodChanged: "
209                          + "actual scan period " + periodInMs + "ms");
210        }
211
212        @Override
213        public void onResults(WifiScanner.ScanData[] results) {
214            handleScanResults(mScanDetails, "PeriodicScanListener");
215            clearScanDetails();
216        }
217
218        @Override
219        public void onFullResult(ScanResult fullScanResult) {
220            if (mDbg) {
221                localLog("PeriodicScanListener onFullResult: "
222                            + fullScanResult.SSID + " capabilities "
223                            + fullScanResult.capabilities);
224            }
225
226            mScanDetails.add(ScanDetailUtil.toScanDetail(fullScanResult));
227        }
228    }
229
230    private final PeriodicScanListener mPeriodicScanListener = new PeriodicScanListener();
231
232    // Single scan results listener. A single scan is initiated when
233    // Disconnected/ConnectedPNO scan found a valid network and woke up
234    // the system, or by the watchdog timer.
235    private class SingleScanListener implements WifiScanner.ScanListener {
236        private List<ScanDetail> mScanDetails = new ArrayList<ScanDetail>();
237
238        public void clearScanDetails() {
239            mScanDetails.clear();
240        }
241
242        @Override
243        public void onSuccess() {
244            localLog("SingleScanListener onSuccess");
245
246            // reset the count
247            mSingleScanRestartCount = 0;
248        }
249
250        @Override
251        public void onFailure(int reason, String description) {
252            Log.e(TAG, "SingleScanListener onFailure:"
253                          + " reason: " + reason
254                          + " description: " + description);
255
256            // reschedule the scan
257            if (mSingleScanRestartCount++ < MAX_SCAN_RESTART_ALLOWED) {
258                scheduleDelayedSingleScan();
259            } else {
260                mSingleScanRestartCount = 0;
261                Log.e(TAG, "Failed to successfully start single scan for "
262                          + MAX_SCAN_RESTART_ALLOWED + " times");
263            }
264        }
265
266        @Override
267        public void onPeriodChanged(int periodInMs) {
268            localLog("SingleScanListener onPeriodChanged: "
269                          + "actual scan period " + periodInMs + "ms");
270        }
271
272        @Override
273        public void onResults(WifiScanner.ScanData[] results) {
274            handleScanResults(mScanDetails, "SingleScanListener");
275        }
276
277        @Override
278        public void onFullResult(ScanResult fullScanResult) {
279            if (mDbg) {
280                localLog("SingleScanListener onFullResult: "
281                            + fullScanResult.SSID + " capabilities "
282                            + fullScanResult.capabilities);
283            }
284
285            mScanDetails.add(ScanDetailUtil.toScanDetail(fullScanResult));
286        }
287    }
288
289    // re-enable this when b/27695292 is fixed
290    // private final SingleScanListener mSingleScanListener = new SingleScanListener();
291
292    // PNO scan results listener for both disconected and connected PNO scanning.
293    // A PNO scan is initiated when screen is off.
294    private class PnoScanListener implements WifiScanner.PnoScanListener {
295        private List<ScanDetail> mScanDetails = new ArrayList<ScanDetail>();
296
297        public void clearScanDetails() {
298            mScanDetails.clear();
299        }
300
301        @Override
302        public void onSuccess() {
303            localLog("PnoScanListener onSuccess");
304
305            // reset the count
306            mScanRestartCount = 0;
307        }
308
309        @Override
310        public void onFailure(int reason, String description) {
311            Log.e(TAG, "PnoScanListener onFailure:"
312                          + " reason: " + reason
313                          + " description: " + description);
314
315            // reschedule the scan
316            if (mScanRestartCount++ < MAX_SCAN_RESTART_ALLOWED) {
317                scheduleDelayedConnectivityScan();
318            } else {
319                mScanRestartCount = 0;
320                Log.e(TAG, "Failed to successfully start PNO scan for "
321                          + MAX_SCAN_RESTART_ALLOWED + " times");
322            }
323        }
324
325        @Override
326        public void onPeriodChanged(int periodInMs) {
327            localLog("PnoScanListener onPeriodChanged: "
328                          + "actual scan period " + periodInMs + "ms");
329        }
330
331        // Currently the PNO scan results doesn't include IE,
332        // which contains information required by QNS. Ignore them
333        // for now.
334        @Override
335        public void onResults(WifiScanner.ScanData[] results) {
336        }
337
338        @Override
339        public void onFullResult(ScanResult fullScanResult) {
340        }
341
342        @Override
343        public void onPnoNetworkFound(ScanResult[] results) {
344            localLog("PnoScanListener: onPnoNetworkFound: results len = " + results.length);
345
346            for (ScanResult result: results) {
347                mScanDetails.add(ScanDetailUtil.toScanDetail(result));
348            }
349            handleScanResults(mScanDetails, "PnoScanListener");
350        }
351    }
352
353    private final PnoScanListener mPnoScanListener = new PnoScanListener();
354
355    /**
356     * WifiConnectivityManager constructor
357     */
358    public WifiConnectivityManager(Context context, WifiStateMachine stateMachine,
359                WifiScanner scanner, WifiConfigManager configManager, WifiInfo wifiInfo,
360                WifiQualifiedNetworkSelector qualifiedNetworkSelector,
361                WifiInjector wifiInjector) {
362        mStateMachine = stateMachine;
363        mScanner = scanner;
364        mConfigManager = configManager;
365        mWifiInfo = wifiInfo;
366        mQualifiedNetworkSelector =  qualifiedNetworkSelector;
367        mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
368        mWifiLastResortWatchdog = wifiInjector.getWifiLastResortWatchdog();
369
370        mMin5GHzRssi = WifiQualifiedNetworkSelector.MINIMUM_5G_ACCEPT_RSSI;
371        mMin24GHzRssi = WifiQualifiedNetworkSelector.MINIMUM_2G_ACCEPT_RSSI;
372        mBand5GHzBonus = WifiQualifiedNetworkSelector.BAND_AWARD_5GHz;
373        mCurrentConnectionBonus = mConfigManager.mCurrentNetworkBoost.get();
374        mSameNetworkBonus = context.getResources().getInteger(
375                                R.integer.config_wifi_framework_SAME_BSSID_AWARD);
376        mSecureBonus = context.getResources().getInteger(
377                            R.integer.config_wifi_framework_SECURITY_AWARD);
378        mInitialScoreMax = (mConfigManager.mThresholdSaturatedRssi24.get()
379                            + WifiQualifiedNetworkSelector.RSSI_SCORE_OFFSET)
380                            * WifiQualifiedNetworkSelector.RSSI_SCORE_SLOPE;
381
382        Log.i(TAG, "PNO settings:" + " min5GHzRssi " + mMin5GHzRssi
383                    + " min24GHzRssi " + mMin24GHzRssi
384                    + " currentConnectionBonus " + mCurrentConnectionBonus
385                    + " sameNetworkBonus " + mSameNetworkBonus
386                    + " secureNetworkBonus " + mSecureBonus
387                    + " initialScoreMax " + mInitialScoreMax);
388
389        Log.i(TAG, "ConnectivityScanManager initialized ");
390    }
391
392    /**
393     * Attempt to connect to a network candidate.
394     *
395     * Based on the currently connected network, this menthod determines whether we should
396     * connect or roam to the network candidate recommended by QNS.
397     */
398    private void connectToNetwork(WifiConfiguration candidate) {
399        ScanResult scanResultCandidate = candidate.getNetworkSelectionStatus().getCandidate();
400        if (scanResultCandidate == null) {
401            Log.e(TAG, "connectToNetwork: bad candidate - "  + candidate
402                    + " scanResult: " + scanResultCandidate);
403            return;
404        }
405
406        String targetBssid = scanResultCandidate.BSSID;
407        String targetAssociationId = candidate.SSID + " : " + targetBssid;
408        if (targetBssid != null && targetBssid.equals(mWifiInfo.getBSSID())) {
409            localLog("connectToNetwork: Already connected to" + targetAssociationId);
410            return;
411        }
412
413        WifiConfiguration currentConnectedNetwork = mConfigManager
414                .getWifiConfiguration(mWifiInfo.getNetworkId());
415        String currentAssociationId = (currentConnectedNetwork == null) ? "Disconnected" :
416                (mWifiInfo.getSSID() + " : " + mWifiInfo.getBSSID());
417
418        if (currentConnectedNetwork != null
419                && (currentConnectedNetwork.networkId == candidate.networkId
420                || currentConnectedNetwork.isLinked(candidate))) {
421            localLog("connectToNetwork: Roaming from " + currentAssociationId + " to "
422                        + targetAssociationId);
423            mStateMachine.autoRoamToNetwork(candidate.networkId, scanResultCandidate);
424        } else {
425            localLog("connectToNetwork: Reconnect from " + currentAssociationId + " to "
426                        + targetAssociationId);
427            mStateMachine.autoConnectToNetwork(candidate.networkId, scanResultCandidate.BSSID);
428        }
429    }
430
431    // Helper for selecting the band for connectivity scan
432    private int getScanBand() {
433        int freqBand = mStateMachine.getFrequencyBand();
434        if (freqBand == WifiManager.WIFI_FREQUENCY_BAND_5GHZ) {
435            return WifiScanner.WIFI_BAND_5_GHZ_WITH_DFS;
436        } else if (freqBand == WifiManager.WIFI_FREQUENCY_BAND_2GHZ) {
437            return WifiScanner.WIFI_BAND_24_GHZ;
438        } else {
439            return WifiScanner.WIFI_BAND_BOTH_WITH_DFS;
440        }
441    }
442
443    // Watchdog timer handler
444    private void watchdogHandler() {
445        localLog("watchdogHandler");
446
447        // Schedule the next timer and start a single scan if we are in disconnected state.
448        // Otherwise, the watchdog timer will be scheduled when entering disconnected
449        // state.
450        if (mWifiState == WIFI_STATE_DISCONNECTED) {
451            Log.i(TAG, "start a single scan from watchdogHandler");
452
453            scheduleWatchdogTimer();
454            startSingleScan();
455        }
456    }
457
458    // Periodic scan timer handler
459    private void periodicScanTimerHandler() {
460        localLog("periodicScanTimerHandler");
461
462        // Schedule the next timer and start a single scan if screen is on.
463        if (mScreenOn) {
464            schedulePeriodicScanTimer();
465            startSingleScan();
466        }
467    }
468
469    // Start a single scan for watchdog
470    private void startSingleScan() {
471        if (!mWifiEnabled || !mWifiConnectivityManagerEnabled) {
472            return;
473        }
474
475        ScanSettings settings = new ScanSettings();
476        settings.band = getScanBand();
477        settings.reportEvents = WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT
478                            | WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN;
479        settings.numBssidsPerScan = 0;
480
481        //Retrieve the list of hidden networkId's to scan for.
482        Set<Integer> hiddenNetworkIds = mConfigManager.getHiddenConfiguredNetworkIds();
483        if (hiddenNetworkIds != null && hiddenNetworkIds.size() > 0) {
484            int i = 0;
485            settings.hiddenNetworkIds = new int[hiddenNetworkIds.size()];
486            for (Integer netId : hiddenNetworkIds) {
487                settings.hiddenNetworkIds[i++] = netId;
488            }
489        }
490
491        // re-enable this when b/27695292 is fixed
492        // mSingleScanListener.clearScanDetails();
493        // mScanner.startScan(settings, mSingleScanListener, WIFI_WORK_SOURCE);
494        SingleScanListener singleScanListener = new SingleScanListener();
495        mScanner.startScan(settings, singleScanListener, WIFI_WORK_SOURCE);
496    }
497
498    // Start a periodic scan when screen is on
499    private void startPeriodicScan() {
500        // Due to b/28020168, timer based single scan will be scheduled every
501        // PERIODIC_SCAN_INTERVAL_MS to provide periodic scan.
502        if (mNoBackgroundScan) {
503            startSingleScan();
504            schedulePeriodicScanTimer();
505        } else {
506            ScanSettings settings = new ScanSettings();
507            settings.band = getScanBand();
508            settings.reportEvents = WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT
509                                | WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN;
510            settings.numBssidsPerScan = 0;
511            settings.periodInMs = PERIODIC_SCAN_INTERVAL_MS;
512
513            mPeriodicScanListener.clearScanDetails();
514            mScanner.startBackgroundScan(settings, mPeriodicScanListener, WIFI_WORK_SOURCE);
515        }
516    }
517
518    // Start a DisconnectedPNO scan when screen is off and Wifi is disconnected
519    private void startDisconnectedPnoScan() {
520        // Initialize PNO settings
521        PnoSettings pnoSettings = new PnoSettings();
522        ArrayList<PnoSettings.PnoNetwork> pnoNetworkList =
523                mConfigManager.retrieveDisconnectedPnoNetworkList();
524        int listSize = pnoNetworkList.size();
525
526        if (listSize == 0) {
527            // No saved network
528            localLog("No saved network for starting disconnected PNO.");
529            return;
530        }
531
532        pnoSettings.networkList = new PnoSettings.PnoNetwork[listSize];
533        pnoSettings.networkList = pnoNetworkList.toArray(pnoSettings.networkList);
534        pnoSettings.min5GHzRssi = mMin5GHzRssi;
535        pnoSettings.min24GHzRssi = mMin24GHzRssi;
536        pnoSettings.initialScoreMax = mInitialScoreMax;
537        pnoSettings.currentConnectionBonus = mCurrentConnectionBonus;
538        pnoSettings.sameNetworkBonus = mSameNetworkBonus;
539        pnoSettings.secureBonus = mSecureBonus;
540        pnoSettings.band5GHzBonus = mBand5GHzBonus;
541
542        // Initialize scan settings
543        ScanSettings scanSettings = new ScanSettings();
544        scanSettings.band = getScanBand();
545        scanSettings.reportEvents = WifiScanner.REPORT_EVENT_NO_BATCH;
546        scanSettings.numBssidsPerScan = 0;
547        scanSettings.periodInMs = PNO_SCAN_INTERVAL_MS;
548        // TODO: enable exponential back off scan later to further save energy
549        // scanSettings.maxPeriodInMs = 8 * scanSettings.periodInMs;
550
551        mPnoScanListener.clearScanDetails();
552
553        mScanner.startDisconnectedPnoScan(scanSettings, pnoSettings, mPnoScanListener);
554    }
555
556    // Start a ConnectedPNO scan when screen is off and Wifi is connected
557    private void startConnectedPnoScan() {
558        // Disable ConnectedPNO for now due to b/28020168
559        if (mNoBackgroundScan) {
560            return;
561        }
562
563        // Initialize PNO settings
564        PnoSettings pnoSettings = new PnoSettings();
565        ArrayList<PnoSettings.PnoNetwork> pnoNetworkList =
566                mConfigManager.retrieveConnectedPnoNetworkList();
567        int listSize = pnoNetworkList.size();
568
569        if (listSize == 0) {
570            // No saved network
571            localLog("No saved network for starting connected PNO.");
572            return;
573        }
574
575        pnoSettings.networkList = new PnoSettings.PnoNetwork[listSize];
576        pnoSettings.networkList = pnoNetworkList.toArray(pnoSettings.networkList);
577        pnoSettings.min5GHzRssi = mMin5GHzRssi;
578        pnoSettings.min24GHzRssi = mMin24GHzRssi;
579        pnoSettings.initialScoreMax = mInitialScoreMax;
580        pnoSettings.currentConnectionBonus = mCurrentConnectionBonus;
581        pnoSettings.sameNetworkBonus = mSameNetworkBonus;
582        pnoSettings.secureBonus = mSecureBonus;
583        pnoSettings.band5GHzBonus = mBand5GHzBonus;
584
585        // Initialize scan settings
586        ScanSettings scanSettings = new ScanSettings();
587        scanSettings.band = getScanBand();
588        scanSettings.reportEvents = WifiScanner.REPORT_EVENT_NO_BATCH;
589        scanSettings.numBssidsPerScan = 0;
590        scanSettings.periodInMs = PNO_SCAN_INTERVAL_MS;
591        // TODO: enable exponential back off scan later to further save energy
592        // scanSettings.maxPeriodInMs = 8 * scanSettings.periodInMs;
593
594        mPnoScanListener.clearScanDetails();
595
596        mScanner.startConnectedPnoScan(scanSettings, pnoSettings, mPnoScanListener);
597    }
598
599    // Set up watchdog timer
600    private void scheduleWatchdogTimer() {
601        Log.i(TAG, "scheduleWatchdogTimer");
602
603        mAlarmManager.set(AlarmManager.RTC_WAKEUP,
604                            System.currentTimeMillis() + WATCHDOG_INTERVAL_MS,
605                            "WifiConnectivityManager Schedule Watchdog Timer",
606                            mWatchdogListener, null);
607    }
608
609    // Set up periodic scan timer
610    private void schedulePeriodicScanTimer() {
611        mAlarmManager.set(AlarmManager.RTC_WAKEUP,
612                            System.currentTimeMillis() + PERIODIC_SCAN_INTERVAL_MS,
613                            "WifiConnectivityManager Schedule Periodic Scan Timer",
614                            mPeriodicScanTimerListener, null);
615    }
616
617    // Set up timer to start a delayed single scan after RESTART_SCAN_DELAY_MS
618    private void scheduleDelayedSingleScan() {
619        localLog("scheduleDelayedSingleScan");
620
621        mAlarmManager.set(AlarmManager.RTC_WAKEUP,
622                            System.currentTimeMillis() + RESTART_SCAN_DELAY_MS,
623                            "WifiConnectivityManager Restart Single Scan",
624                            mRestartSingleScanListener, null);
625    }
626
627    // Set up timer to start a delayed scan after RESTART_SCAN_DELAY_MS
628    private void scheduleDelayedConnectivityScan() {
629        localLog("scheduleDelayedConnectivityScan");
630
631        mAlarmManager.set(AlarmManager.RTC_WAKEUP,
632                            System.currentTimeMillis() + RESTART_SCAN_DELAY_MS,
633                            "WifiConnectivityManager Restart Scan",
634                            mRestartScanListener, null);
635
636    }
637
638    // Start a connectivity scan. The scan method is chosen according to
639    // the current screen state and WiFi state.
640    private void startConnectivityScan(boolean forceSelectNetwork) {
641        localLog("startConnectivityScan: screenOn=" + mScreenOn
642                        + " wifiState=" + mWifiState
643                        + " forceSelectNetwork=" + forceSelectNetwork
644                        + " wifiEnabled=" + mWifiEnabled
645                        + " wifiConnectivityManagerEnabled="
646                        + mWifiConnectivityManagerEnabled);
647
648        if (!mWifiEnabled || !mWifiConnectivityManagerEnabled) {
649            return;
650        }
651
652        // Always stop outstanding connecivity scan if there is any
653        stopConnectivityScan();
654
655        // Don't start a connectivity scan while Wifi is in the transition
656        // between connected and disconnected states.
657        if (mWifiState != WIFI_STATE_CONNECTED && mWifiState != WIFI_STATE_DISCONNECTED) {
658            return;
659        }
660
661        mForceSelectNetwork = forceSelectNetwork;
662
663        if (mScreenOn) {
664            startPeriodicScan();
665        } else { // screenOff
666            if (mWifiState == WIFI_STATE_CONNECTED) {
667                startConnectedPnoScan();
668            } else {
669                startDisconnectedPnoScan();
670            }
671        }
672    }
673
674    // Stop connectivity scan if there is any.
675    private void stopConnectivityScan() {
676        // Due to b/28020168, timer based single scan will be scheduled every
677        // PERIODIC_SCAN_INTERVAL_MS to provide periodic scan.
678        if (mNoBackgroundScan) {
679            mAlarmManager.cancel(mPeriodicScanTimerListener);
680        } else {
681            mScanner.stopBackgroundScan(mPeriodicScanListener);
682        }
683        mScanner.stopPnoScan(mPnoScanListener);
684        mScanRestartCount = 0;
685    }
686
687    /**
688     * Handler for screen state (on/off) changes
689     */
690    public void handleScreenStateChanged(boolean screenOn) {
691        localLog("handleScreenStateChanged: screenOn=" + screenOn);
692
693        mScreenOn = screenOn;
694
695        startConnectivityScan(false);
696    }
697
698    /**
699     * Handler for WiFi state (connected/disconnected) changes
700     */
701    public void handleConnectionStateChanged(int state) {
702        localLog("handleConnectionStateChanged: state=" + state);
703
704        mWifiState = state;
705
706        // Kick off the watchdog timer if entering disconnected state
707        if (mWifiState == WIFI_STATE_DISCONNECTED) {
708            scheduleWatchdogTimer();
709        }
710
711        startConnectivityScan(false);
712    }
713
714    /**
715     * Handler when user toggles whether untrusted connection is allowed
716     */
717    public void setUntrustedConnectionAllowed(boolean allowed) {
718        Log.i(TAG, "setUntrustedConnectionAllowed: allowed=" + allowed);
719
720        if (mUntrustedConnectionAllowed != allowed) {
721            mUntrustedConnectionAllowed = allowed;
722            startConnectivityScan(false);
723        }
724    }
725
726    /**
727     * Handler when user specifies a particular network to connect to
728     */
729    public void connectToUserSelectNetwork(int netId, boolean persistent) {
730        Log.i(TAG, "connectToUserSelectNetwork: netId=" + netId
731                   + " persist=" + persistent);
732
733        mQualifiedNetworkSelector.userSelectNetwork(netId, persistent);
734
735        // Initiate a scan which will trigger the connection to the user selected
736        // network when scan result is available.
737        startConnectivityScan(true);
738    }
739
740    /**
741     * Handler for on-demand connectivity scan
742     */
743    public void forceConnectivityScan() {
744        Log.i(TAG, "forceConnectivityScan");
745
746        startConnectivityScan(false);
747    }
748
749    /**
750     * Track whether a BSSID should be enabled or disabled for QNS
751     */
752    public boolean trackBssid(String bssid, boolean enable) {
753        Log.i(TAG, "trackBssid: " + (enable ? "enable " : "disable ") + bssid);
754
755        boolean ret = mQualifiedNetworkSelector
756                            .enableBssidForQualityNetworkSelection(bssid, enable);
757
758        if (ret && !enable) {
759            // Disabling a BSSID can happen when the AP candidate to connect to has
760            // no capacity for new stations. We start another scan immediately so that QNS
761            // can give us another candidate to connect to.
762            startConnectivityScan(false);
763        }
764
765        return ret;
766    }
767
768    /**
769     * Set band preference when doing scan and making connection
770     */
771    public void setUserPreferredBand(int band) {
772        Log.i(TAG, "User band preference: " + band);
773
774        mQualifiedNetworkSelector.setUserPreferredBand(band);
775        startConnectivityScan(false);
776    }
777
778    /**
779     * Inform WiFi is enabled for connection or not
780     */
781    public void setWifiEnabled(boolean enable) {
782        Log.i(TAG, "Set WiFi " + (enable ? "enabled" : "disabled"));
783
784        mWifiEnabled = enable;
785
786        if (!mWifiEnabled) {
787            stopConnectivityScan();
788        }
789    }
790
791    /**
792     * Turn on/off the WifiConnectivityMangager at runtime
793     */
794    public void enable(boolean enable) {
795        Log.i(TAG, "Set WiFiConnectivityManager " + (enable ? "enabled" : "disabled"));
796
797        mWifiConnectivityManagerEnabled = enable;
798
799        if (!mWifiConnectivityManagerEnabled) {
800            stopConnectivityScan();
801        }
802    }
803
804    /**
805     * Enable/disable verbose logging
806     */
807    public void enableVerboseLogging(int verbose) {
808        mDbg = verbose > 0;
809    }
810
811    /**
812     * Dump the local log buffer
813     */
814    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
815        pw.println("Dump of WifiConnectivityManager");
816        pw.println("WifiConnectivityManager - Log Begin ----");
817        mLocalLog.dump(fd, pw, args);
818        pw.println("WifiConnectivityManager - Log End ----");
819    }
820}
821