WifiConnectivityManager.java revision 466158a6669d51541ce6c5c4e04a71dad36cdb4e
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.currentNetworkBoost.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.thresholdSaturatedRssi24.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    // Stop a PNO scan
531    private void stopPnoScan() {
532        // Initialize PNO settings
533        PnoSettings pnoSettings = new PnoSettings();
534        ArrayList<PnoSettings.PnoNetwork> pnoNetworkList =
535                mConfigManager.retrieveDisconnectedPnoNetworkList(false);
536        int listSize = pnoNetworkList.size();
537
538        pnoSettings.networkList = new PnoSettings.PnoNetwork[listSize];
539        pnoSettings.networkList = pnoNetworkList.toArray(pnoSettings.networkList);
540
541        mScanner.stopPnoScan(pnoSettings, mPnoScanListener);
542    }
543
544    // Start a DisconnectedPNO scan when screen is off and Wifi is disconnected
545    private void startDisconnectedPnoScan() {
546        // Initialize PNO settings
547        PnoSettings pnoSettings = new PnoSettings();
548        ArrayList<PnoSettings.PnoNetwork> pnoNetworkList =
549                mConfigManager.retrieveDisconnectedPnoNetworkList(true);
550        int listSize = pnoNetworkList.size();
551
552        if (listSize == 0) {
553            // No saved network
554            localLog("No saved network for starting disconnected PNO.");
555            return;
556        }
557
558        pnoSettings.networkList = new PnoSettings.PnoNetwork[listSize];
559        pnoSettings.networkList = pnoNetworkList.toArray(pnoSettings.networkList);
560        pnoSettings.min5GHzRssi = mMin5GHzRssi;
561        pnoSettings.min24GHzRssi = mMin24GHzRssi;
562        pnoSettings.initialScoreMax = mInitialScoreMax;
563        pnoSettings.currentConnectionBonus = mCurrentConnectionBonus;
564        pnoSettings.sameNetworkBonus = mSameNetworkBonus;
565        pnoSettings.secureBonus = mSecureBonus;
566        pnoSettings.band5GHzBonus = mBand5GHzBonus;
567
568        // Initialize scan settings
569        ScanSettings scanSettings = new ScanSettings();
570        scanSettings.band = getScanBand();
571        scanSettings.reportEvents = WifiScanner.REPORT_EVENT_NO_BATCH;
572        scanSettings.numBssidsPerScan = 0;
573        scanSettings.periodInMs = PNO_SCAN_INTERVAL_MS;
574        // TODO: enable exponential back off scan later to further save energy
575        // scanSettings.maxPeriodInMs = 8 * scanSettings.periodInMs;
576
577        mPnoScanListener.clearScanDetails();
578
579        mScanner.startDisconnectedPnoScan(scanSettings, pnoSettings, mPnoScanListener);
580    }
581
582    // Start a ConnectedPNO scan when screen is off and Wifi is connected
583    private void startConnectedPnoScan() {
584        // Disable ConnectedPNO for now due to b/28020168
585        if (mNoBackgroundScan) {
586            return;
587        }
588
589        // Initialize PNO settings
590        PnoSettings pnoSettings = new PnoSettings();
591        ArrayList<PnoSettings.PnoNetwork> pnoNetworkList =
592                mConfigManager.retrieveConnectedPnoNetworkList();
593        int listSize = pnoNetworkList.size();
594
595        if (listSize == 0) {
596            // No saved network
597            localLog("No saved network for starting connected PNO.");
598            return;
599        }
600
601        pnoSettings.networkList = new PnoSettings.PnoNetwork[listSize];
602        pnoSettings.networkList = pnoNetworkList.toArray(pnoSettings.networkList);
603        pnoSettings.min5GHzRssi = mMin5GHzRssi;
604        pnoSettings.min24GHzRssi = mMin24GHzRssi;
605        pnoSettings.initialScoreMax = mInitialScoreMax;
606        pnoSettings.currentConnectionBonus = mCurrentConnectionBonus;
607        pnoSettings.sameNetworkBonus = mSameNetworkBonus;
608        pnoSettings.secureBonus = mSecureBonus;
609        pnoSettings.band5GHzBonus = mBand5GHzBonus;
610
611        // Initialize scan settings
612        ScanSettings scanSettings = new ScanSettings();
613        scanSettings.band = getScanBand();
614        scanSettings.reportEvents = WifiScanner.REPORT_EVENT_NO_BATCH;
615        scanSettings.numBssidsPerScan = 0;
616        scanSettings.periodInMs = PNO_SCAN_INTERVAL_MS;
617        // TODO: enable exponential back off scan later to further save energy
618        // scanSettings.maxPeriodInMs = 8 * scanSettings.periodInMs;
619
620        mPnoScanListener.clearScanDetails();
621
622        mScanner.startConnectedPnoScan(scanSettings, pnoSettings, mPnoScanListener);
623    }
624
625    // Set up watchdog timer
626    private void scheduleWatchdogTimer() {
627        Log.i(TAG, "scheduleWatchdogTimer");
628
629        mAlarmManager.set(AlarmManager.RTC_WAKEUP,
630                            System.currentTimeMillis() + WATCHDOG_INTERVAL_MS,
631                            "WifiConnectivityManager Schedule Watchdog Timer",
632                            mWatchdogListener, null);
633    }
634
635    // Set up periodic scan timer
636    private void schedulePeriodicScanTimer() {
637        mAlarmManager.set(AlarmManager.RTC_WAKEUP,
638                            System.currentTimeMillis() + PERIODIC_SCAN_INTERVAL_MS,
639                            "WifiConnectivityManager Schedule Periodic Scan Timer",
640                            mPeriodicScanTimerListener, null);
641    }
642
643    // Set up timer to start a delayed single scan after RESTART_SCAN_DELAY_MS
644    private void scheduleDelayedSingleScan() {
645        localLog("scheduleDelayedSingleScan");
646
647        mAlarmManager.set(AlarmManager.RTC_WAKEUP,
648                            System.currentTimeMillis() + RESTART_SCAN_DELAY_MS,
649                            "WifiConnectivityManager Restart Single Scan",
650                            mRestartSingleScanListener, null);
651    }
652
653    // Set up timer to start a delayed scan after RESTART_SCAN_DELAY_MS
654    private void scheduleDelayedConnectivityScan() {
655        localLog("scheduleDelayedConnectivityScan");
656
657        mAlarmManager.set(AlarmManager.RTC_WAKEUP,
658                            System.currentTimeMillis() + RESTART_SCAN_DELAY_MS,
659                            "WifiConnectivityManager Restart Scan",
660                            mRestartScanListener, null);
661
662    }
663
664    // Start a connectivity scan. The scan method is chosen according to
665    // the current screen state and WiFi state.
666    private void startConnectivityScan(boolean forceSelectNetwork) {
667        localLog("startConnectivityScan: screenOn=" + mScreenOn
668                        + " wifiState=" + mWifiState
669                        + " forceSelectNetwork=" + forceSelectNetwork
670                        + " wifiEnabled=" + mWifiEnabled
671                        + " wifiConnectivityManagerEnabled="
672                        + mWifiConnectivityManagerEnabled);
673
674        if (!mWifiEnabled || !mWifiConnectivityManagerEnabled) {
675            return;
676        }
677
678        // Always stop outstanding connecivity scan if there is any
679        stopConnectivityScan();
680
681        // Don't start a connectivity scan while Wifi is in the transition
682        // between connected and disconnected states.
683        if (mWifiState != WIFI_STATE_CONNECTED && mWifiState != WIFI_STATE_DISCONNECTED) {
684            return;
685        }
686
687        mForceSelectNetwork = forceSelectNetwork;
688
689        if (mScreenOn) {
690            startPeriodicScan();
691        } else { // screenOff
692            if (mWifiState == WIFI_STATE_CONNECTED) {
693                startConnectedPnoScan();
694            } else {
695                startDisconnectedPnoScan();
696            }
697        }
698    }
699
700    // Stop connectivity scan if there is any.
701    private void stopConnectivityScan() {
702        // Due to b/28020168, timer based single scan will be scheduled every
703        // PERIODIC_SCAN_INTERVAL_MS to provide periodic scan.
704        if (mNoBackgroundScan) {
705            mAlarmManager.cancel(mPeriodicScanTimerListener);
706        } else {
707            mScanner.stopBackgroundScan(mPeriodicScanListener);
708        }
709        stopPnoScan();
710        mScanRestartCount = 0;
711    }
712
713    /**
714     * Handler for screen state (on/off) changes
715     */
716    public void handleScreenStateChanged(boolean screenOn) {
717        localLog("handleScreenStateChanged: screenOn=" + screenOn);
718
719        mScreenOn = screenOn;
720
721        startConnectivityScan(false);
722    }
723
724    /**
725     * Handler for WiFi state (connected/disconnected) changes
726     */
727    public void handleConnectionStateChanged(int state) {
728        localLog("handleConnectionStateChanged: state=" + state);
729
730        mWifiState = state;
731
732        // Kick off the watchdog timer if entering disconnected state
733        if (mWifiState == WIFI_STATE_DISCONNECTED) {
734            scheduleWatchdogTimer();
735        }
736
737        startConnectivityScan(false);
738    }
739
740    /**
741     * Handler when user toggles whether untrusted connection is allowed
742     */
743    public void setUntrustedConnectionAllowed(boolean allowed) {
744        Log.i(TAG, "setUntrustedConnectionAllowed: allowed=" + allowed);
745
746        if (mUntrustedConnectionAllowed != allowed) {
747            mUntrustedConnectionAllowed = allowed;
748            startConnectivityScan(false);
749        }
750    }
751
752    /**
753     * Handler when user specifies a particular network to connect to
754     */
755    public void connectToUserSelectNetwork(int netId, boolean persistent) {
756        Log.i(TAG, "connectToUserSelectNetwork: netId=" + netId
757                   + " persist=" + persistent);
758
759        mQualifiedNetworkSelector.userSelectNetwork(netId, persistent);
760
761        // Initiate a scan which will trigger the connection to the user selected
762        // network when scan result is available.
763        startConnectivityScan(true);
764    }
765
766    /**
767     * Handler for on-demand connectivity scan
768     */
769    public void forceConnectivityScan() {
770        Log.i(TAG, "forceConnectivityScan");
771
772        startConnectivityScan(false);
773    }
774
775    /**
776     * Track whether a BSSID should be enabled or disabled for QNS
777     */
778    public boolean trackBssid(String bssid, boolean enable) {
779        Log.i(TAG, "trackBssid: " + (enable ? "enable " : "disable ") + bssid);
780
781        boolean ret = mQualifiedNetworkSelector
782                            .enableBssidForQualityNetworkSelection(bssid, enable);
783
784        if (ret && !enable) {
785            // Disabling a BSSID can happen when the AP candidate to connect to has
786            // no capacity for new stations. We start another scan immediately so that QNS
787            // can give us another candidate to connect to.
788            startConnectivityScan(false);
789        }
790
791        return ret;
792    }
793
794    /**
795     * Set band preference when doing scan and making connection
796     */
797    public void setUserPreferredBand(int band) {
798        Log.i(TAG, "User band preference: " + band);
799
800        mQualifiedNetworkSelector.setUserPreferredBand(band);
801        startConnectivityScan(false);
802    }
803
804    /**
805     * Inform WiFi is enabled for connection or not
806     */
807    public void setWifiEnabled(boolean enable) {
808        Log.i(TAG, "Set WiFi " + (enable ? "enabled" : "disabled"));
809
810        mWifiEnabled = enable;
811
812        if (!mWifiEnabled) {
813            stopConnectivityScan();
814        }
815    }
816
817    /**
818     * Turn on/off the WifiConnectivityMangager at runtime
819     */
820    public void enable(boolean enable) {
821        Log.i(TAG, "Set WiFiConnectivityManager " + (enable ? "enabled" : "disabled"));
822
823        mWifiConnectivityManagerEnabled = enable;
824
825        if (!mWifiConnectivityManagerEnabled) {
826            stopConnectivityScan();
827        }
828    }
829
830    /**
831     * Enable/disable verbose logging
832     */
833    public void enableVerboseLogging(int verbose) {
834        mDbg = verbose > 0;
835    }
836
837    /**
838     * Dump the local log buffer
839     */
840    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
841        pw.println("Dump of WifiConnectivityManager");
842        pw.println("WifiConnectivityManager - Log Begin ----");
843        mLocalLog.dump(fd, pw, args);
844        pw.println("WifiConnectivityManager - Log End ----");
845    }
846}
847