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 
17 package com.android.server.wifi;
18 
19 import static android.net.wifi.WifiManager.IFACE_IP_MODE_LOCAL_ONLY;
20 import static android.net.wifi.WifiManager.IFACE_IP_MODE_TETHERED;
21 
22 import android.annotation.NonNull;
23 import android.content.BroadcastReceiver;
24 import android.content.Context;
25 import android.content.Intent;
26 import android.content.IntentFilter;
27 import android.location.LocationManager;
28 import android.net.wifi.SoftApCapability;
29 import android.net.wifi.SoftApConfiguration;
30 import android.net.wifi.WifiManager;
31 import android.os.BatteryStatsManager;
32 import android.os.Handler;
33 import android.os.Looper;
34 import android.os.Message;
35 import android.util.ArraySet;
36 import android.util.Log;
37 
38 import com.android.internal.annotations.VisibleForTesting;
39 import com.android.internal.util.IState;
40 import com.android.internal.util.Preconditions;
41 import com.android.internal.util.Protocol;
42 import com.android.internal.util.State;
43 import com.android.internal.util.StateMachine;
44 import com.android.server.wifi.util.WifiPermissionsUtil;
45 import com.android.wifi.resources.R;
46 
47 import java.io.FileDescriptor;
48 import java.io.PrintWriter;
49 import java.util.Collection;
50 import java.util.List;
51 
52 /**
53  * This class provides the implementation for different WiFi operating modes.
54  */
55 public class ActiveModeWarden {
56     private static final String TAG = "WifiActiveModeWarden";
57     private static final String STATE_MACHINE_EXITED_STATE_NAME = "STATE_MACHINE_EXITED";
58 
59     // Holder for active mode managers
60     private final ArraySet<ActiveModeManager> mActiveModeManagers;
61     // DefaultModeManager used to service API calls when there are not active mode managers.
62     private final DefaultModeManager mDefaultModeManager;
63 
64     private final WifiInjector mWifiInjector;
65     private final Looper mLooper;
66     private final Handler mHandler;
67     private final Context mContext;
68     private final ClientModeImpl mClientModeImpl;
69     private final WifiSettingsStore mSettingsStore;
70     private final FrameworkFacade mFacade;
71     private final WifiPermissionsUtil mWifiPermissionsUtil;
72     private final BatteryStatsManager mBatteryStatsManager;
73     private final ScanRequestProxy mScanRequestProxy;
74     private final WifiNative mWifiNative;
75     private final WifiController mWifiController;
76 
77     private WifiManager.SoftApCallback mSoftApCallback;
78     private WifiManager.SoftApCallback mLohsCallback;
79 
80     private boolean mCanRequestMoreClientModeManagers = false;
81     private boolean mCanRequestMoreSoftApManagers = false;
82     private boolean mIsShuttingdown = false;
83 
84     /**
85      * Called from WifiServiceImpl to register a callback for notifications from SoftApManager
86      */
registerSoftApCallback(@onNull WifiManager.SoftApCallback callback)87     public void registerSoftApCallback(@NonNull WifiManager.SoftApCallback callback) {
88         mSoftApCallback = callback;
89     }
90 
91     /**
92      * Called from WifiServiceImpl to register a callback for notifications from SoftApManager
93      * for local-only hotspot.
94      */
registerLohsCallback(@onNull WifiManager.SoftApCallback callback)95     public void registerLohsCallback(@NonNull WifiManager.SoftApCallback callback) {
96         mLohsCallback = callback;
97     }
98 
ActiveModeWarden(WifiInjector wifiInjector, Looper looper, WifiNative wifiNative, DefaultModeManager defaultModeManager, BatteryStatsManager batteryStatsManager, BaseWifiDiagnostics wifiDiagnostics, Context context, ClientModeImpl clientModeImpl, WifiSettingsStore settingsStore, FrameworkFacade facade, WifiPermissionsUtil wifiPermissionsUtil)99     ActiveModeWarden(WifiInjector wifiInjector,
100                      Looper looper,
101                      WifiNative wifiNative,
102                      DefaultModeManager defaultModeManager,
103                      BatteryStatsManager batteryStatsManager,
104                      BaseWifiDiagnostics wifiDiagnostics,
105                      Context context,
106                      ClientModeImpl clientModeImpl,
107                      WifiSettingsStore settingsStore,
108                      FrameworkFacade facade,
109                      WifiPermissionsUtil wifiPermissionsUtil) {
110         mWifiInjector = wifiInjector;
111         mLooper = looper;
112         mHandler = new Handler(looper);
113         mContext = context;
114         mClientModeImpl = clientModeImpl;
115         mSettingsStore = settingsStore;
116         mFacade = facade;
117         mWifiPermissionsUtil = wifiPermissionsUtil;
118         mActiveModeManagers = new ArraySet<>();
119         mDefaultModeManager = defaultModeManager;
120         mBatteryStatsManager = batteryStatsManager;
121         mScanRequestProxy = wifiInjector.getScanRequestProxy();
122         mWifiNative = wifiNative;
123         mWifiController = new WifiController();
124 
125         wifiNative.registerStatusListener(isReady -> {
126             if (!isReady && !mIsShuttingdown) {
127                 mHandler.post(() -> {
128                     Log.e(TAG, "One of the native daemons died. Triggering recovery");
129                     wifiDiagnostics.captureBugReportData(
130                             WifiDiagnostics.REPORT_REASON_WIFINATIVE_FAILURE);
131 
132                     // immediately trigger SelfRecovery if we receive a notice about an
133                     // underlying daemon failure
134                     // Note: SelfRecovery has a circular dependency with ActiveModeWarden and is
135                     // instantiated after ActiveModeWarden, so use WifiInjector to get the instance
136                     // instead of directly passing in SelfRecovery in the constructor.
137                     mWifiInjector.getSelfRecovery().trigger(SelfRecovery.REASON_WIFINATIVE_FAILURE);
138                 });
139             }
140         });
141 
142         wifiNative.registerClientInterfaceAvailabilityListener(
143                 (isAvailable) -> mHandler.post(() -> {
144                     mCanRequestMoreClientModeManagers = isAvailable;
145                 }));
146         wifiNative.registerSoftApInterfaceAvailabilityListener(
147                 (isAvailable) -> mHandler.post(() -> {
148                     mCanRequestMoreSoftApManagers = isAvailable;
149                 }));
150     }
151 
152     /**
153      * Notify that device is shutting down
154      * Keep it simple and don't add collection access codes
155      * to avoid concurrentModificationException when it is directly called from a different thread
156      */
notifyShuttingDown()157     public void notifyShuttingDown() {
158         mIsShuttingdown = true;
159     }
160 
161     /**
162      * @return Returns whether we can create more client mode managers or not.
163      */
canRequestMoreClientModeManagers()164     public boolean canRequestMoreClientModeManagers() {
165         return mCanRequestMoreClientModeManagers;
166     }
167 
168     /**
169      * @return Returns whether we can create more SoftAp managers or not.
170      */
canRequestMoreSoftApManagers()171     public boolean canRequestMoreSoftApManagers() {
172         return mCanRequestMoreSoftApManagers;
173     }
174 
175     /**
176      * @return Returns whether the device can support at least one concurrent client mode manager &
177      * softap manager.
178      */
isStaApConcurrencySupported()179     public boolean isStaApConcurrencySupported() {
180         return mWifiNative.isStaApConcurrencySupported();
181     }
182 
183     /** Begin listening to broadcasts and start the internal state machine. */
start()184     public void start() {
185         mWifiController.start();
186     }
187 
188     /** Disable Wifi for recovery purposes. */
recoveryDisableWifi()189     public void recoveryDisableWifi() {
190         mWifiController.sendMessage(WifiController.CMD_RECOVERY_DISABLE_WIFI);
191     }
192 
193     /**
194      * Restart Wifi for recovery purposes.
195      * @param reason One of {@link SelfRecovery.RecoveryReason}
196      */
recoveryRestartWifi(@elfRecovery.RecoveryReason int reason)197     public void recoveryRestartWifi(@SelfRecovery.RecoveryReason int reason) {
198         mWifiController.sendMessage(WifiController.CMD_RECOVERY_RESTART_WIFI, reason);
199     }
200 
201     /** Wifi has been toggled. */
wifiToggled()202     public void wifiToggled() {
203         mWifiController.sendMessage(WifiController.CMD_WIFI_TOGGLED);
204     }
205 
206     /** Airplane Mode has been toggled. */
airplaneModeToggled()207     public void airplaneModeToggled() {
208         mWifiController.sendMessage(WifiController.CMD_AIRPLANE_TOGGLED);
209     }
210 
211     /** Starts SoftAp. */
startSoftAp(SoftApModeConfiguration softApConfig)212     public void startSoftAp(SoftApModeConfiguration softApConfig) {
213         mWifiController.sendMessage(WifiController.CMD_SET_AP, 1, 0, softApConfig);
214     }
215 
216     /** Stop SoftAp. */
stopSoftAp(int mode)217     public void stopSoftAp(int mode) {
218         mWifiController.sendMessage(WifiController.CMD_SET_AP, 0, mode);
219     }
220 
221     /** Update SoftAp Capability. */
updateSoftApCapability(SoftApCapability capability)222     public void updateSoftApCapability(SoftApCapability capability) {
223         mWifiController.sendMessage(WifiController.CMD_UPDATE_AP_CAPABILITY, capability);
224     }
225 
226     /** Update SoftAp Configuration. */
updateSoftApConfiguration(SoftApConfiguration config)227     public void updateSoftApConfiguration(SoftApConfiguration config) {
228         mWifiController.sendMessage(WifiController.CMD_UPDATE_AP_CONFIG, config);
229     }
230 
231     /** Emergency Callback Mode has changed. */
emergencyCallbackModeChanged(boolean isInEmergencyCallbackMode)232     public void emergencyCallbackModeChanged(boolean isInEmergencyCallbackMode) {
233         mWifiController.sendMessage(
234                 WifiController.CMD_EMERGENCY_MODE_CHANGED, isInEmergencyCallbackMode ? 1 : 0);
235     }
236 
237     /** Emergency Call state has changed. */
emergencyCallStateChanged(boolean isInEmergencyCall)238     public void emergencyCallStateChanged(boolean isInEmergencyCall) {
239         mWifiController.sendMessage(
240                 WifiController.CMD_EMERGENCY_CALL_STATE_CHANGED, isInEmergencyCall ? 1 : 0);
241     }
242 
243     /** Scan always mode has changed. */
scanAlwaysModeChanged()244     public void scanAlwaysModeChanged() {
245         mWifiController.sendMessage(WifiController.CMD_SCAN_ALWAYS_MODE_CHANGED);
246     }
247 
hasAnyModeManager()248     private boolean hasAnyModeManager() {
249         return !mActiveModeManagers.isEmpty();
250     }
251 
252     /**
253      * @return true if any mode managers in specified role.
254      */
hasAnyModeManagerInRole(@ctiveModeManager.Role int role)255     private boolean hasAnyModeManagerInRole(@ActiveModeManager.Role int role) {
256         for (ActiveModeManager manager : mActiveModeManagers) {
257             if (manager.getRole() == role) return true;
258         }
259         return false;
260     }
261 
262     /**
263      * @return true if any mode managers in one of the specified roles.
264      */
hasAnyModeManagerInOneOfRoles(List<Integer> rolesList)265     private boolean hasAnyModeManagerInOneOfRoles(List<Integer> rolesList) {
266         for (ActiveModeManager manager : mActiveModeManagers) {
267             if (rolesList.contains(manager.getRole())) return true;
268         }
269         return false;
270     }
271 
hasAnyClientModeManager()272     private boolean hasAnyClientModeManager() {
273         return hasAnyModeManagerInOneOfRoles(ActiveModeManager.CLIENT_ROLES);
274     }
275 
hasAnyClientModeManagerInConnectivityRole()276     private boolean hasAnyClientModeManagerInConnectivityRole() {
277         return hasAnyModeManagerInOneOfRoles(ActiveModeManager.CLIENT_CONNECTIVITY_ROLES);
278     }
279 
hasAnySoftApManager()280     private boolean hasAnySoftApManager() {
281         return hasAnyModeManagerInOneOfRoles(ActiveModeManager.SOFTAP_ROLES);
282     }
283 
284     /**
285      * @return true if any mode manager is stopping
286      */
hasAnyModeManagerStopping()287     private boolean hasAnyModeManagerStopping() {
288         for (ActiveModeManager manager : mActiveModeManagers) {
289             if (manager.isStopping()) return true;
290         }
291         return false;
292     }
293 
294     /**
295      * @return true if all the client mode managers are in scan only role,
296      * false if there are no client mode managers present or if any of them are not in scan only
297      * role.
298      */
areAllClientModeManagersInScanOnlyRole()299     private boolean areAllClientModeManagersInScanOnlyRole() {
300         boolean hasAnyClientModeManager = false;
301         for (ActiveModeManager manager : mActiveModeManagers) {
302             if (ActiveModeManager.CLIENT_ROLES.contains(manager.getRole())) {
303                 hasAnyClientModeManager = true;
304                 if (manager.getRole() != ActiveModeManager.ROLE_CLIENT_SCAN_ONLY) return false;
305             }
306         }
307         return hasAnyClientModeManager;
308     }
309 
getRoleForSoftApIpMode(int ipMode)310     private @ActiveModeManager.Role int getRoleForSoftApIpMode(int ipMode) {
311         return ipMode == IFACE_IP_MODE_TETHERED
312                 ? ActiveModeManager.ROLE_SOFTAP_TETHERED : ActiveModeManager.ROLE_SOFTAP_LOCAL_ONLY;
313     }
314 
315     /**
316      * Method to enable soft ap for wifi hotspot.
317      *
318      * The supplied SoftApModeConfiguration includes the target softap WifiConfiguration (or null if
319      * the persisted config is to be used) and the target operating mode (ex,
320      * {@link WifiManager#IFACE_IP_MODE_TETHERED} {@link WifiManager#IFACE_IP_MODE_LOCAL_ONLY}).
321      *
322      * @param softApConfig SoftApModeConfiguration for the hostapd softap
323      */
startSoftApModeManager(@onNull SoftApModeConfiguration softApConfig)324     private void startSoftApModeManager(@NonNull SoftApModeConfiguration softApConfig) {
325         Log.d(TAG, "Starting SoftApModeManager config = "
326                 + softApConfig.getSoftApConfiguration());
327         Preconditions.checkState(softApConfig.getTargetMode() == IFACE_IP_MODE_LOCAL_ONLY
328                 || softApConfig.getTargetMode() == IFACE_IP_MODE_TETHERED);
329 
330         WifiManager.SoftApCallback callback =
331                 softApConfig.getTargetMode() == IFACE_IP_MODE_LOCAL_ONLY
332                         ? mLohsCallback : mSoftApCallback;
333         SoftApListener listener = new SoftApListener();
334         ActiveModeManager manager =
335                 mWifiInjector.makeSoftApManager(listener, callback, softApConfig);
336         listener.setActiveModeManager(manager);
337         manager.start();
338         manager.setRole(getRoleForSoftApIpMode(softApConfig.getTargetMode()));
339         mActiveModeManagers.add(manager);
340     }
341 
342     /**
343      * Method to stop all soft ap for the specified mode.
344      *
345      * This method will stop any active softAp mode managers.
346      *
347      * @param ipMode the operating mode of APs to bring down (ex,
348      *             {@link WifiManager#IFACE_IP_MODE_TETHERED} or
349      *             {@link WifiManager#IFACE_IP_MODE_LOCAL_ONLY}).
350      *             Use {@link WifiManager#IFACE_IP_MODE_UNSPECIFIED} to stop all APs.
351      */
stopSoftApModeManagers(int ipMode)352     private void stopSoftApModeManagers(int ipMode) {
353         Log.d(TAG, "Shutting down all softap mode managers in mode " + ipMode);
354         for (ActiveModeManager manager : mActiveModeManagers) {
355             if (!(manager instanceof SoftApManager)) continue;
356             SoftApManager softApManager = (SoftApManager) manager;
357 
358             if (ipMode == WifiManager.IFACE_IP_MODE_UNSPECIFIED
359                     || getRoleForSoftApIpMode(ipMode) == softApManager.getRole()) {
360                 softApManager.stop();
361             }
362         }
363     }
364 
updateCapabilityToSoftApModeManager(SoftApCapability capability)365     private void updateCapabilityToSoftApModeManager(SoftApCapability capability) {
366         for (ActiveModeManager manager : mActiveModeManagers) {
367             if (!(manager instanceof SoftApManager)) continue;
368             SoftApManager softApManager = (SoftApManager) manager;
369             softApManager.updateCapability(capability);
370         }
371     }
372 
updateConfigurationToSoftApModeManager(SoftApConfiguration config)373     private void updateConfigurationToSoftApModeManager(SoftApConfiguration config) {
374         for (ActiveModeManager manager : mActiveModeManagers) {
375             if (!(manager instanceof SoftApManager)) continue;
376             SoftApManager softApManager = (SoftApManager) manager;
377             softApManager.updateConfiguration(config);
378         }
379     }
380 
381     /**
382      * Method to enable a new client mode manager.
383      */
startClientModeManager()384     private boolean startClientModeManager() {
385         Log.d(TAG, "Starting ClientModeManager");
386         ClientListener listener = new ClientListener();
387         ClientModeManager manager = mWifiInjector.makeClientModeManager(listener);
388         listener.setActiveModeManager(manager);
389         manager.start();
390         if (!switchClientModeManagerRole(manager)) {
391             return false;
392         }
393         mActiveModeManagers.add(manager);
394         return true;
395     }
396 
397     /**
398      * Method to stop all client mode mangers.
399      */
stopAllClientModeManagers()400     private void stopAllClientModeManagers() {
401         Log.d(TAG, "Shutting down all client mode managers");
402         for (ActiveModeManager manager : mActiveModeManagers) {
403             if (!(manager instanceof ClientModeManager)) continue;
404             ClientModeManager clientModeManager = (ClientModeManager) manager;
405             clientModeManager.stop();
406         }
407     }
408 
409     /**
410      * Method to switch all client mode manager mode of operation (from ScanOnly To Connect &
411      * vice-versa) based on the toggle state.
412      */
switchAllClientModeManagers()413     private boolean switchAllClientModeManagers() {
414         Log.d(TAG, "Switching all client mode managers");
415         for (ActiveModeManager manager : mActiveModeManagers) {
416             if (!(manager instanceof ClientModeManager)) continue;
417             ClientModeManager clientModeManager = (ClientModeManager) manager;
418             if (!switchClientModeManagerRole(clientModeManager)) {
419                 return false;
420             }
421         }
422         updateBatteryStats();
423         return true;
424     }
425 
426     /**
427      * Method to switch a client mode manager mode of operation (from ScanOnly To Connect &
428      * vice-versa) based on the toggle state.
429      */
switchClientModeManagerRole(@onNull ClientModeManager modeManager)430     private boolean switchClientModeManagerRole(@NonNull ClientModeManager modeManager) {
431         if (mSettingsStore.isWifiToggleEnabled()) {
432             modeManager.setRole(ActiveModeManager.ROLE_CLIENT_PRIMARY);
433         } else if (checkScanOnlyModeAvailable()) {
434             modeManager.setRole(ActiveModeManager.ROLE_CLIENT_SCAN_ONLY);
435         } else {
436             Log.e(TAG, "Something is wrong, no client mode toggles enabled");
437             return false;
438         }
439         return true;
440     }
441 
442     /**
443      * Method to stop all active modes, for example, when toggling airplane mode.
444      */
shutdownWifi()445     private void shutdownWifi() {
446         Log.d(TAG, "Shutting down all mode managers");
447         for (ActiveModeManager manager : mActiveModeManagers) {
448             manager.stop();
449         }
450     }
451 
452     /**
453      * Dump current state for active mode managers.
454      *
455      * Must be called from the main Wifi thread.
456      */
dump(FileDescriptor fd, PrintWriter pw, String[] args)457     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
458         pw.println("Dump of " + TAG);
459         pw.println("Current wifi mode: " + getCurrentMode());
460         pw.println("NumActiveModeManagers: " + mActiveModeManagers.size());
461         mWifiController.dump(fd, pw, args);
462         for (ActiveModeManager manager : mActiveModeManagers) {
463             manager.dump(fd, pw, args);
464         }
465     }
466 
467     @VisibleForTesting
getCurrentMode()468     String getCurrentMode() {
469         IState state = mWifiController.getCurrentState();
470         return state == null ? STATE_MACHINE_EXITED_STATE_NAME : state.getName();
471     }
472 
473     @VisibleForTesting
getActiveModeManagers()474     Collection<ActiveModeManager> getActiveModeManagers() {
475         return new ArraySet<>(mActiveModeManagers);
476     }
477 
478     @VisibleForTesting
isInEmergencyMode()479     boolean isInEmergencyMode() {
480         IState state = mWifiController.getCurrentState();
481         return ((WifiController.BaseState) state).isInEmergencyMode();
482     }
483 
484     /**
485      *  Helper class to wrap the ActiveModeManager callback objects.
486      */
487     private static class ModeCallback {
488         private ActiveModeManager mActiveManager;
489 
setActiveModeManager(ActiveModeManager manager)490         void setActiveModeManager(ActiveModeManager manager) {
491             mActiveManager = manager;
492         }
493 
getActiveModeManager()494         ActiveModeManager getActiveModeManager() {
495             return mActiveManager;
496         }
497     }
498 
updateBatteryStats()499     private void updateBatteryStats() {
500         updateBatteryStatsWifiState(hasAnyModeManager());
501         if (areAllClientModeManagersInScanOnlyRole()) {
502             updateBatteryStatsScanModeActive();
503         }
504     }
505 
506     private class SoftApListener extends ModeCallback implements ActiveModeManager.Listener {
507         @Override
onStarted()508         public void onStarted() {
509             updateBatteryStats();
510         }
511 
512         @Override
onStopped()513         public void onStopped() {
514             mActiveModeManagers.remove(getActiveModeManager());
515             updateBatteryStats();
516             mWifiController.sendMessage(WifiController.CMD_AP_STOPPED);
517         }
518 
519         @Override
onStartFailure()520         public void onStartFailure() {
521             mActiveModeManagers.remove(getActiveModeManager());
522             updateBatteryStats();
523             mWifiController.sendMessage(WifiController.CMD_AP_START_FAILURE);
524         }
525     }
526 
527     private class ClientListener extends ModeCallback implements ActiveModeManager.Listener {
528         @Override
onStarted()529         public void onStarted() {
530             updateClientScanMode();
531             updateBatteryStats();
532         }
533 
534         @Override
onStopped()535         public void onStopped() {
536             mActiveModeManagers.remove(getActiveModeManager());
537             updateClientScanMode();
538             updateBatteryStats();
539             mWifiController.sendMessage(WifiController.CMD_STA_STOPPED);
540         }
541 
542         @Override
onStartFailure()543         public void onStartFailure() {
544             mActiveModeManagers.remove(getActiveModeManager());
545             updateClientScanMode();
546             updateBatteryStats();
547             mWifiController.sendMessage(WifiController.CMD_STA_START_FAILURE);
548         }
549     }
550 
551     // Update the scan state based on all active mode managers.
updateClientScanMode()552     private void updateClientScanMode() {
553         boolean scanEnabled = hasAnyClientModeManager();
554         boolean scanningForHiddenNetworksEnabled;
555 
556         if (mContext.getResources().getBoolean(R.bool.config_wifiScanHiddenNetworksScanOnlyMode)) {
557             scanningForHiddenNetworksEnabled = hasAnyClientModeManager();
558         } else {
559             scanningForHiddenNetworksEnabled = hasAnyClientModeManagerInConnectivityRole();
560         }
561         mScanRequestProxy.enableScanning(scanEnabled, scanningForHiddenNetworksEnabled);
562     }
563 
564     /**
565      *  Helper method to report wifi state as on/off (doesn't matter which mode).
566      *
567      *  @param enabled boolean indicating that some mode has been turned on or off
568      */
updateBatteryStatsWifiState(boolean enabled)569     private void updateBatteryStatsWifiState(boolean enabled) {
570         if (enabled) {
571             if (mActiveModeManagers.size() == 1) {
572                 // only report wifi on if we haven't already
573                 mBatteryStatsManager.reportWifiOn();
574             }
575         } else {
576             if (mActiveModeManagers.size() == 0) {
577                 // only report if we don't have any active modes
578                 mBatteryStatsManager.reportWifiOff();
579             }
580         }
581     }
582 
updateBatteryStatsScanModeActive()583     private void updateBatteryStatsScanModeActive() {
584         mBatteryStatsManager.reportWifiState(BatteryStatsManager.WIFI_STATE_OFF_SCANNING, null);
585     }
586 
checkScanOnlyModeAvailable()587     private boolean checkScanOnlyModeAvailable() {
588         return mWifiPermissionsUtil.isLocationModeEnabled()
589                 && mSettingsStore.isScanAlwaysAvailable();
590     }
591 
592     /**
593      * WifiController is the class used to manage wifi state for various operating
594      * modes (normal, airplane, wifi hotspot, etc.).
595      */
596     private class WifiController extends StateMachine {
597         private static final String TAG = "WifiController";
598 
599         // Maximum limit to use for timeout delay if the value from overlay setting is too large.
600         private static final int MAX_RECOVERY_TIMEOUT_DELAY_MS = 4000;
601 
602         private static final int BASE = Protocol.BASE_WIFI_CONTROLLER;
603 
604         static final int CMD_EMERGENCY_MODE_CHANGED                 = BASE + 1;
605         static final int CMD_SCAN_ALWAYS_MODE_CHANGED               = BASE + 7;
606         static final int CMD_WIFI_TOGGLED                           = BASE + 8;
607         static final int CMD_AIRPLANE_TOGGLED                       = BASE + 9;
608         static final int CMD_SET_AP                                 = BASE + 10;
609         static final int CMD_EMERGENCY_CALL_STATE_CHANGED           = BASE + 14;
610         static final int CMD_AP_STOPPED                             = BASE + 15;
611         static final int CMD_STA_START_FAILURE                      = BASE + 16;
612         // Command used to trigger a wifi stack restart when in active mode
613         static final int CMD_RECOVERY_RESTART_WIFI                  = BASE + 17;
614         // Internal command used to complete wifi stack restart
615         private static final int CMD_RECOVERY_RESTART_WIFI_CONTINUE = BASE + 18;
616         // Command to disable wifi when SelfRecovery is throttled or otherwise not doing full
617         // recovery
618         static final int CMD_RECOVERY_DISABLE_WIFI                  = BASE + 19;
619         static final int CMD_STA_STOPPED                            = BASE + 20;
620         static final int CMD_DEFERRED_RECOVERY_RESTART_WIFI         = BASE + 22;
621         static final int CMD_AP_START_FAILURE                       = BASE + 23;
622         static final int CMD_UPDATE_AP_CAPABILITY                   = BASE + 24;
623         static final int CMD_UPDATE_AP_CONFIG                       = BASE + 25;
624 
625         private final EnabledState mEnabledState = new EnabledState();
626         private final DisabledState mDisabledState = new DisabledState();
627 
628         private boolean mIsInEmergencyCall = false;
629         private boolean mIsInEmergencyCallbackMode = false;
630 
WifiController()631         WifiController() {
632             super(TAG, mLooper);
633 
634             DefaultState defaultState = new DefaultState();
635             addState(defaultState); {
636                 addState(mDisabledState, defaultState);
637                 addState(mEnabledState, defaultState);
638             }
639 
640             setLogRecSize(100);
641             setLogOnlyTransitions(false);
642 
643         }
644 
645         @Override
start()646         public void start() {
647             boolean isAirplaneModeOn = mSettingsStore.isAirplaneModeOn();
648             boolean isWifiEnabled = mSettingsStore.isWifiToggleEnabled();
649             boolean isScanningAlwaysAvailable = mSettingsStore.isScanAlwaysAvailable();
650             boolean isLocationModeActive = mWifiPermissionsUtil.isLocationModeEnabled();
651 
652             log("isAirplaneModeOn = " + isAirplaneModeOn
653                     + ", isWifiEnabled = " + isWifiEnabled
654                     + ", isScanningAvailable = " + isScanningAlwaysAvailable
655                     + ", isLocationModeActive = " + isLocationModeActive);
656 
657             if (shouldEnableSta()) {
658                 startClientModeManager();
659                 setInitialState(mEnabledState);
660             } else {
661                 setInitialState(mDisabledState);
662             }
663             mContext.registerReceiver(new BroadcastReceiver() {
664                 @Override
665                 public void onReceive(Context context, Intent intent) {
666                     // Location mode has been toggled...  trigger with the scan change
667                     // update to make sure we are in the correct mode
668                     scanAlwaysModeChanged();
669                 }
670             }, new IntentFilter(LocationManager.MODE_CHANGED_ACTION));
671             super.start();
672         }
673 
readWifiRecoveryDelay()674         private int readWifiRecoveryDelay() {
675             int recoveryDelayMillis = mContext.getResources().getInteger(
676                     R.integer.config_wifi_framework_recovery_timeout_delay);
677             if (recoveryDelayMillis > MAX_RECOVERY_TIMEOUT_DELAY_MS) {
678                 recoveryDelayMillis = MAX_RECOVERY_TIMEOUT_DELAY_MS;
679                 Log.w(TAG, "Overriding timeout delay with maximum limit value");
680             }
681             return recoveryDelayMillis;
682         }
683 
684         abstract class BaseState extends State {
685             @VisibleForTesting
isInEmergencyMode()686             boolean isInEmergencyMode() {
687                 return mIsInEmergencyCall || mIsInEmergencyCallbackMode;
688             }
689 
updateEmergencyMode(Message msg)690             private void updateEmergencyMode(Message msg) {
691                 if (msg.what == CMD_EMERGENCY_CALL_STATE_CHANGED) {
692                     mIsInEmergencyCall = msg.arg1 == 1;
693                 } else if (msg.what == CMD_EMERGENCY_MODE_CHANGED) {
694                     mIsInEmergencyCallbackMode = msg.arg1 == 1;
695                 }
696             }
697 
enterEmergencyMode()698             private void enterEmergencyMode() {
699                 stopSoftApModeManagers(WifiManager.IFACE_IP_MODE_UNSPECIFIED);
700                 boolean configWiFiDisableInECBM = mFacade.getConfigWiFiDisableInECBM(mContext);
701                 log("WifiController msg getConfigWiFiDisableInECBM " + configWiFiDisableInECBM);
702                 if (configWiFiDisableInECBM) {
703                     shutdownWifi();
704                 }
705             }
706 
exitEmergencyMode()707             private void exitEmergencyMode() {
708                 if (shouldEnableSta()) {
709                     startClientModeManager();
710                     transitionTo(mEnabledState);
711                 } else {
712                     transitionTo(mDisabledState);
713                 }
714             }
715 
716             @Override
processMessage(Message msg)717             public final boolean processMessage(Message msg) {
718                 // potentially enter emergency mode
719                 if (msg.what == CMD_EMERGENCY_CALL_STATE_CHANGED
720                         || msg.what == CMD_EMERGENCY_MODE_CHANGED) {
721                     boolean wasInEmergencyMode = isInEmergencyMode();
722                     updateEmergencyMode(msg);
723                     boolean isInEmergencyMode = isInEmergencyMode();
724                     if (!wasInEmergencyMode && isInEmergencyMode) {
725                         enterEmergencyMode();
726                     } else if (wasInEmergencyMode && !isInEmergencyMode) {
727                         exitEmergencyMode();
728                     }
729                     return HANDLED;
730                 } else if (isInEmergencyMode()) {
731                     // already in emergency mode, drop all messages other than mode stop messages
732                     // triggered by emergency mode start.
733                     if (msg.what == CMD_STA_STOPPED || msg.what == CMD_AP_STOPPED) {
734                         if (!hasAnyModeManager()) {
735                             log("No active mode managers, return to DisabledState.");
736                             transitionTo(mDisabledState);
737                         }
738                     }
739                     return HANDLED;
740                 }
741                 // not in emergency mode, process messages normally
742                 return processMessageFiltered(msg);
743             }
744 
processMessageFiltered(Message msg)745             protected abstract boolean processMessageFiltered(Message msg);
746         }
747 
748         class DefaultState extends State {
749             @Override
processMessage(Message msg)750             public boolean processMessage(Message msg) {
751                 switch (msg.what) {
752                     case CMD_SCAN_ALWAYS_MODE_CHANGED:
753                     case CMD_WIFI_TOGGLED:
754                     case CMD_STA_STOPPED:
755                     case CMD_STA_START_FAILURE:
756                     case CMD_AP_STOPPED:
757                     case CMD_AP_START_FAILURE:
758                     case CMD_RECOVERY_RESTART_WIFI:
759                     case CMD_RECOVERY_RESTART_WIFI_CONTINUE:
760                     case CMD_DEFERRED_RECOVERY_RESTART_WIFI:
761                         break;
762                     case CMD_RECOVERY_DISABLE_WIFI:
763                         log("Recovery has been throttled, disable wifi");
764                         shutdownWifi();
765                         // onStopped will move the state machine to "DisabledState".
766                         break;
767                     case CMD_AIRPLANE_TOGGLED:
768                         if (mSettingsStore.isAirplaneModeOn()) {
769                             log("Airplane mode toggled, shutdown all modes");
770                             shutdownWifi();
771                             // onStopped will move the state machine to "DisabledState".
772                         } else {
773                             log("Airplane mode disabled, determine next state");
774                             if (shouldEnableSta()) {
775                                 startClientModeManager();
776                                 transitionTo(mEnabledState);
777                             }
778                             // wifi should remain disabled, do not need to transition
779                         }
780                         break;
781                     case CMD_UPDATE_AP_CAPABILITY:
782                         updateCapabilityToSoftApModeManager((SoftApCapability) msg.obj);
783                         break;
784                     case CMD_UPDATE_AP_CONFIG:
785                         updateConfigurationToSoftApModeManager((SoftApConfiguration) msg.obj);
786                         break;
787                     default:
788                         throw new RuntimeException("WifiController.handleMessage " + msg.what);
789                 }
790                 return HANDLED;
791             }
792         }
793 
shouldEnableSta()794         private boolean shouldEnableSta() {
795             return mSettingsStore.isWifiToggleEnabled() || checkScanOnlyModeAvailable();
796         }
797 
798         class DisabledState extends BaseState {
799             @Override
enter()800             public void enter() {
801                 log("DisabledState.enter()");
802                 super.enter();
803                 if (hasAnyModeManager()) {
804                     Log.e(TAG, "Entered DisabledState, but has active mode managers");
805                 }
806             }
807 
808             @Override
exit()809             public void exit() {
810                 log("DisabledState.exit()");
811                 super.exit();
812             }
813 
814             @Override
processMessageFiltered(Message msg)815             public boolean processMessageFiltered(Message msg) {
816                 switch (msg.what) {
817                     case CMD_WIFI_TOGGLED:
818                     case CMD_SCAN_ALWAYS_MODE_CHANGED:
819                         if (shouldEnableSta()) {
820                             startClientModeManager();
821                             transitionTo(mEnabledState);
822                         }
823                         break;
824                     case CMD_SET_AP:
825                         // note: CMD_SET_AP is handled/dropped in ECM mode - will not start here
826                         if (msg.arg1 == 1) {
827                             startSoftApModeManager((SoftApModeConfiguration) msg.obj);
828                             transitionTo(mEnabledState);
829                         }
830                         break;
831                     case CMD_RECOVERY_RESTART_WIFI:
832                         log("Recovery triggered, already in disabled state");
833                         // intentional fallthrough
834                     case CMD_DEFERRED_RECOVERY_RESTART_WIFI:
835                         // wait mRecoveryDelayMillis for letting driver clean reset.
836                         sendMessageDelayed(CMD_RECOVERY_RESTART_WIFI_CONTINUE,
837                                 readWifiRecoveryDelay());
838                         break;
839                     case CMD_RECOVERY_RESTART_WIFI_CONTINUE:
840                         if (shouldEnableSta()) {
841                             startClientModeManager();
842                             transitionTo(mEnabledState);
843                         }
844                         break;
845                     default:
846                         return NOT_HANDLED;
847                 }
848                 return HANDLED;
849             }
850         }
851 
852         class EnabledState extends BaseState {
853 
854             private boolean mIsDisablingDueToAirplaneMode;
855 
856             @Override
enter()857             public void enter() {
858                 log("EnabledState.enter()");
859                 super.enter();
860                 if (!hasAnyModeManager()) {
861                     Log.e(TAG, "Entered EnabledState, but no active mode managers");
862                 }
863                 mIsDisablingDueToAirplaneMode = false;
864             }
865 
866             @Override
exit()867             public void exit() {
868                 log("EnabledState.exit()");
869                 if (hasAnyModeManager()) {
870                     Log.e(TAG, "Existing EnabledState, but has active mode managers");
871                 }
872                 super.exit();
873             }
874 
875             @Override
processMessageFiltered(Message msg)876             public boolean processMessageFiltered(Message msg) {
877                 switch (msg.what) {
878                     case CMD_WIFI_TOGGLED:
879                     case CMD_SCAN_ALWAYS_MODE_CHANGED:
880                         if (shouldEnableSta()) {
881                             if (hasAnyClientModeManager()) {
882                                 switchAllClientModeManagers();
883                             } else {
884                                 startClientModeManager();
885                             }
886                         } else {
887                             stopAllClientModeManagers();
888                         }
889                         break;
890                     case CMD_SET_AP:
891                         // note: CMD_SET_AP is handled/dropped in ECM mode - will not start here
892                         if (msg.arg1 == 1) {
893                             startSoftApModeManager((SoftApModeConfiguration) msg.obj);
894                         } else {
895                             stopSoftApModeManagers(msg.arg2);
896                         }
897                         break;
898                     case CMD_AIRPLANE_TOGGLED:
899                         // airplane mode toggled on is handled in the default state
900                         if (mSettingsStore.isAirplaneModeOn()) {
901                             mIsDisablingDueToAirplaneMode = true;
902                             return NOT_HANDLED;
903                         } else {
904                             if (mIsDisablingDueToAirplaneMode) {
905                                 // Previous airplane mode toggle on is being processed, defer the
906                                 // message toggle off until previous processing is completed.
907                                 // Once previous airplane mode toggle is complete, we should
908                                 // transition to DisabledState. There, we will process the deferred
909                                 // airplane mode toggle message to disable airplane mode.
910                                 deferMessage(msg);
911                             } else {
912                                 // when airplane mode is toggled off, but wifi is on, we can keep it
913                                 // on
914                                 log("airplane mode toggled - and airplane mode is off. return "
915                                         + "handled");
916                             }
917                             return HANDLED;
918                         }
919                     case CMD_AP_STOPPED:
920                     case CMD_AP_START_FAILURE:
921                         if (!hasAnyModeManager()) {
922                             if (shouldEnableSta()) {
923                                 log("SoftAp disabled, start client mode");
924                                 startClientModeManager();
925                             } else {
926                                 log("SoftAp mode disabled, return to DisabledState");
927                                 transitionTo(mDisabledState);
928                             }
929                         } else {
930                             log("AP disabled, remain in EnabledState.");
931                         }
932                         break;
933                     case CMD_STA_START_FAILURE:
934                     case CMD_STA_STOPPED:
935                         // Client mode stopped. Head to Disabled to wait for next command if there
936                         // no active mode managers.
937                         if (!hasAnyModeManager()) {
938                             log("STA disabled, return to DisabledState.");
939                             transitionTo(mDisabledState);
940                         } else {
941                             log("STA disabled, remain in EnabledState.");
942                         }
943                         break;
944                     case CMD_RECOVERY_RESTART_WIFI:
945                         final String bugTitle;
946                         final String bugDetail;
947                         if (msg.arg1 < SelfRecovery.REASON_STRINGS.length && msg.arg1 >= 0) {
948                             bugDetail = SelfRecovery.REASON_STRINGS[msg.arg1];
949                             bugTitle = "Wi-Fi BugReport: " + bugDetail;
950                         } else {
951                             bugDetail = "";
952                             bugTitle = "Wi-Fi BugReport";
953                         }
954                         if (msg.arg1 != SelfRecovery.REASON_LAST_RESORT_WATCHDOG) {
955                             mHandler.post(() -> mClientModeImpl.takeBugReport(bugTitle, bugDetail));
956                         }
957                         log("Recovery triggered, disable wifi");
958                         deferMessage(obtainMessage(CMD_DEFERRED_RECOVERY_RESTART_WIFI));
959                         shutdownWifi();
960                         // onStopped will move the state machine to "DisabledState".
961                         break;
962                     default:
963                         return NOT_HANDLED;
964                 }
965                 return HANDLED;
966             }
967         }
968     }
969 }
970