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