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