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