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