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