• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 static com.android.server.wifi.ActiveModeManager.ROLE_CLIENT_LOCAL_ONLY;
23 import static com.android.server.wifi.ActiveModeManager.ROLE_CLIENT_PRIMARY;
24 import static com.android.server.wifi.ActiveModeManager.ROLE_CLIENT_SCAN_ONLY;
25 import static com.android.server.wifi.ActiveModeManager.ROLE_CLIENT_SECONDARY_LONG_LIVED;
26 import static com.android.server.wifi.ActiveModeManager.ROLE_CLIENT_SECONDARY_TRANSIENT;
27 import static com.android.server.wifi.ActiveModeManager.ROLE_SOFTAP_TETHERED;
28 
29 import android.annotation.NonNull;
30 import android.annotation.Nullable;
31 import android.content.BroadcastReceiver;
32 import android.content.Context;
33 import android.content.Intent;
34 import android.content.IntentFilter;
35 import android.location.LocationManager;
36 import android.net.wifi.ISubsystemRestartCallback;
37 import android.net.wifi.IWifiConnectedNetworkScorer;
38 import android.net.wifi.SoftApCapability;
39 import android.net.wifi.SoftApConfiguration;
40 import android.net.wifi.WifiConfiguration;
41 import android.net.wifi.WifiManager;
42 import android.os.BatteryStatsManager;
43 import android.os.Build;
44 import android.os.Handler;
45 import android.os.IBinder;
46 import android.os.Looper;
47 import android.os.Message;
48 import android.os.Process;
49 import android.os.RemoteCallbackList;
50 import android.os.RemoteException;
51 import android.os.WorkSource;
52 import android.telephony.TelephonyManager;
53 import android.text.TextUtils;
54 import android.util.ArraySet;
55 import android.util.Log;
56 import android.util.Pair;
57 
58 import com.android.internal.annotations.VisibleForTesting;
59 import com.android.internal.util.IState;
60 import com.android.internal.util.Preconditions;
61 import com.android.internal.util.Protocol;
62 import com.android.internal.util.State;
63 import com.android.internal.util.StateMachine;
64 import com.android.server.wifi.ActiveModeManager.ClientConnectivityRole;
65 import com.android.server.wifi.ActiveModeManager.ClientInternetConnectivityRole;
66 import com.android.server.wifi.ActiveModeManager.ClientRole;
67 import com.android.server.wifi.ActiveModeManager.SoftApRole;
68 import com.android.server.wifi.util.WifiPermissionsUtil;
69 import com.android.wifi.resources.R;
70 
71 import java.io.FileDescriptor;
72 import java.io.PrintWriter;
73 import java.util.ArrayDeque;
74 import java.util.ArrayList;
75 import java.util.Collection;
76 import java.util.Collections;
77 import java.util.List;
78 import java.util.Objects;
79 import java.util.Set;
80 import java.util.concurrent.Executor;
81 import java.util.stream.Collectors;
82 import java.util.stream.Stream;
83 
84 /**
85  * This class provides the implementation for different WiFi operating modes.
86  */
87 public class ActiveModeWarden {
88     private static final String TAG = "WifiActiveModeWarden";
89     private static final String STATE_MACHINE_EXITED_STATE_NAME = "STATE_MACHINE_EXITED";
90     public static final WorkSource INTERNAL_REQUESTOR_WS = new WorkSource(Process.WIFI_UID);
91 
92     // Holder for active mode managers
93     private final Set<ConcreteClientModeManager> mClientModeManagers = new ArraySet<>();
94     private final Set<SoftApManager> mSoftApManagers = new ArraySet<>();
95 
96     private final Set<ModeChangeCallback> mCallbacks = new ArraySet<>();
97     private final Set<PrimaryClientModeManagerChangedCallback> mPrimaryChangedCallbacks =
98             new ArraySet<>();
99     // DefaultModeManager used to service API calls when there are no active client mode managers.
100     private final DefaultClientModeManager mDefaultClientModeManager;
101     private final WifiInjector mWifiInjector;
102     private final Looper mLooper;
103     private final Handler mHandler;
104     private final Context mContext;
105     private final WifiDiagnostics mWifiDiagnostics;
106     private final WifiSettingsStore mSettingsStore;
107     private final FrameworkFacade mFacade;
108     private final WifiPermissionsUtil mWifiPermissionsUtil;
109     private final BatteryStatsManager mBatteryStatsManager;
110     private final ScanRequestProxy mScanRequestProxy;
111     private final WifiNative mWifiNative;
112     private final WifiController mWifiController;
113     private final Graveyard mGraveyard;
114     private final WifiMetrics mWifiMetrics;
115     private final ExternalScoreUpdateObserverProxy mExternalScoreUpdateObserverProxy;
116     private final DppManager mDppManager;
117 
118     private WifiServiceImpl.SoftApCallbackInternal mSoftApCallback;
119     private WifiServiceImpl.SoftApCallbackInternal mLohsCallback;
120 
121     private final RemoteCallbackList<ISubsystemRestartCallback> mRestartCallbacks =
122             new RemoteCallbackList<>();
123 
124     private boolean mIsShuttingdown = false;
125     private boolean mVerboseLoggingEnabled = false;
126     /** Cache to store the external scorer for primary and secondary (MBB) client mode manager. */
127     @Nullable private Pair<IBinder, IWifiConnectedNetworkScorer> mClientModeManagerScorer;
128 
129     @Nullable
130     private ConcreteClientModeManager mLastPrimaryClientModeManager = null;
131 
132     @Nullable
133     private WorkSource mLastPrimaryClientModeManagerRequestorWs = null;
134     @Nullable
135     private WorkSource mLastScanOnlyClientModeManagerRequestorWs = null;
136 
137     /**
138      * Called from WifiServiceImpl to register a callback for notifications from SoftApManager
139      */
registerSoftApCallback(@onNull WifiServiceImpl.SoftApCallbackInternal callback)140     public void registerSoftApCallback(@NonNull WifiServiceImpl.SoftApCallbackInternal callback) {
141         mSoftApCallback = callback;
142     }
143 
144     /**
145      * Called from WifiServiceImpl to register a callback for notifications from SoftApManager
146      * for local-only hotspot.
147      */
registerLohsCallback(@onNull WifiServiceImpl.SoftApCallbackInternal callback)148     public void registerLohsCallback(@NonNull WifiServiceImpl.SoftApCallbackInternal callback) {
149         mLohsCallback = callback;
150     }
151 
152     /**
153      * Callbacks for indicating any mode manager changes to the rest of the system.
154      */
155     public interface ModeChangeCallback {
156         /**
157          * Invoked when new mode manager is added.
158          *
159          * @param activeModeManager Instance of {@link ActiveModeManager}.
160          */
onActiveModeManagerAdded(@onNull ActiveModeManager activeModeManager)161         void onActiveModeManagerAdded(@NonNull ActiveModeManager activeModeManager);
162 
163         /**
164          * Invoked when a mode manager is removed.
165          *
166          * @param activeModeManager Instance of {@link ActiveModeManager}.
167          */
onActiveModeManagerRemoved(@onNull ActiveModeManager activeModeManager)168         void onActiveModeManagerRemoved(@NonNull ActiveModeManager activeModeManager);
169 
170         /**
171          * Invoked when an existing mode manager's role is changed.
172          *
173          * @param activeModeManager Instance of {@link ActiveModeManager}.
174          */
onActiveModeManagerRoleChanged(@onNull ActiveModeManager activeModeManager)175         void onActiveModeManagerRoleChanged(@NonNull ActiveModeManager activeModeManager);
176     }
177 
178     /** Called when the primary ClientModeManager changes. */
179     public interface PrimaryClientModeManagerChangedCallback {
180         /**
181          * Note: The current implementation for changing primary CMM is not atomic (due to setRole()
182          * needing to go through StateMachine, which is async). Thus, when the primary CMM changes,
183          * the sequence of calls looks like this:
184          * 1. onChange(prevPrimaryCmm, null)
185          * 2. onChange(null, newPrimaryCmm)
186          * Nevertheless, at run time, these two calls should occur in rapid succession.
187          *
188          * @param prevPrimaryClientModeManager the previous primary ClientModeManager, or null if
189          *                                     there was no previous primary (e.g. Wifi was off).
190          * @param newPrimaryClientModeManager the new primary ClientModeManager, or null if there is
191          *                                    no longer a primary (e.g. Wifi was turned off).
192          */
onChange( @ullable ConcreteClientModeManager prevPrimaryClientModeManager, @Nullable ConcreteClientModeManager newPrimaryClientModeManager)193         void onChange(
194                 @Nullable ConcreteClientModeManager prevPrimaryClientModeManager,
195                 @Nullable ConcreteClientModeManager newPrimaryClientModeManager);
196     }
197 
198     /**
199      * Keep stopped {@link ActiveModeManager} instances so that they can be dumped to aid debugging.
200      *
201      * TODO(b/160283853): Find a smarter way to evict old ActiveModeManagers
202      */
203     private static class Graveyard {
204         private static final int INSTANCES_TO_KEEP = 3;
205 
206         private final ArrayDeque<ConcreteClientModeManager> mClientModeManagers =
207                 new ArrayDeque<>();
208         private final ArrayDeque<SoftApManager> mSoftApManagers = new ArrayDeque<>();
209 
210         /**
211          * Add this stopped {@link ConcreteClientModeManager} to the graveyard, and evict the oldest
212          * ClientModeManager if the graveyard is full.
213          */
inter(ConcreteClientModeManager clientModeManager)214         void inter(ConcreteClientModeManager clientModeManager) {
215             if (mClientModeManagers.size() == INSTANCES_TO_KEEP) {
216                 mClientModeManagers.removeFirst();
217             }
218             mClientModeManagers.addLast(clientModeManager);
219         }
220 
221         /**
222          * Add this stopped {@link SoftApManager} to the graveyard, and evict the oldest
223          * SoftApManager if the graveyard is full.
224          */
inter(SoftApManager softApManager)225         void inter(SoftApManager softApManager) {
226             if (mSoftApManagers.size() == INSTANCES_TO_KEEP) {
227                 mSoftApManagers.removeFirst();
228             }
229             mSoftApManagers.addLast(softApManager);
230         }
231 
232         /** Dump the contents of the graveyard. */
dump(FileDescriptor fd, PrintWriter pw, String[] args)233         void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
234             pw.println("Dump of ActiveModeWarden.Graveyard");
235             pw.println("Stopped ClientModeManagers: " + mClientModeManagers.size() + " total");
236             for (ConcreteClientModeManager clientModeManager : mClientModeManagers) {
237                 clientModeManager.dump(fd, pw, args);
238             }
239             pw.println("Stopped SoftApManagers: " + mSoftApManagers.size() + " total");
240             for (SoftApManager softApManager : mSoftApManagers) {
241                 softApManager.dump(fd, pw, args);
242             }
243             pw.println();
244         }
245     }
246 
ActiveModeWarden(WifiInjector wifiInjector, Looper looper, WifiNative wifiNative, DefaultClientModeManager defaultClientModeManager, BatteryStatsManager batteryStatsManager, WifiDiagnostics wifiDiagnostics, Context context, WifiSettingsStore settingsStore, FrameworkFacade facade, WifiPermissionsUtil wifiPermissionsUtil, WifiMetrics wifiMetrics, ExternalScoreUpdateObserverProxy externalScoreUpdateObserverProxy, DppManager dppManager)247     ActiveModeWarden(WifiInjector wifiInjector,
248             Looper looper,
249             WifiNative wifiNative,
250             DefaultClientModeManager defaultClientModeManager,
251             BatteryStatsManager batteryStatsManager,
252             WifiDiagnostics wifiDiagnostics,
253             Context context,
254             WifiSettingsStore settingsStore,
255             FrameworkFacade facade,
256             WifiPermissionsUtil wifiPermissionsUtil,
257             WifiMetrics wifiMetrics,
258             ExternalScoreUpdateObserverProxy externalScoreUpdateObserverProxy,
259             DppManager dppManager) {
260         mWifiInjector = wifiInjector;
261         mLooper = looper;
262         mHandler = new Handler(looper);
263         mContext = context;
264         mWifiDiagnostics = wifiDiagnostics;
265         mSettingsStore = settingsStore;
266         mFacade = facade;
267         mWifiPermissionsUtil = wifiPermissionsUtil;
268         mDefaultClientModeManager = defaultClientModeManager;
269         mBatteryStatsManager = batteryStatsManager;
270         mScanRequestProxy = wifiInjector.getScanRequestProxy();
271         mWifiNative = wifiNative;
272         mWifiMetrics = wifiMetrics;
273         mWifiController = new WifiController();
274         mExternalScoreUpdateObserverProxy = externalScoreUpdateObserverProxy;
275         mDppManager = dppManager;
276         mGraveyard = new Graveyard();
277 
278         wifiNative.registerStatusListener(isReady -> {
279             if (!isReady && !mIsShuttingdown) {
280                 mHandler.post(() -> {
281                     Log.e(TAG, "One of the native daemons died. Triggering recovery");
282                     wifiDiagnostics.triggerBugReportDataCapture(
283                             WifiDiagnostics.REPORT_REASON_WIFINATIVE_FAILURE);
284 
285                     // immediately trigger SelfRecovery if we receive a notice about an
286                     // underlying daemon failure
287                     // Note: SelfRecovery has a circular dependency with ActiveModeWarden and is
288                     // instantiated after ActiveModeWarden, so use WifiInjector to get the instance
289                     // instead of directly passing in SelfRecovery in the constructor.
290                     mWifiInjector.getSelfRecovery().trigger(SelfRecovery.REASON_WIFINATIVE_FAILURE);
291                 });
292             }
293         });
294 
295         registerPrimaryClientModeManagerChangedCallback(
296                 (prevPrimaryClientModeManager, newPrimaryClientModeManager) -> {
297                     // TODO (b/181363901): We can always propagate the external scorer to all
298                     // ClientModeImpl instances. WifiScoreReport already handles skipping external
299                     // scorer notification for local only & restricted STA + STA use-cases. For MBB
300                     // use-case, we may want the external scorer to be notified.
301                     if (prevPrimaryClientModeManager != null) {
302                         prevPrimaryClientModeManager.clearWifiConnectedNetworkScorer();
303                     }
304                     if (newPrimaryClientModeManager != null && mClientModeManagerScorer != null) {
305                         newPrimaryClientModeManager.setWifiConnectedNetworkScorer(
306                                 mClientModeManagerScorer.first,
307                                 mClientModeManagerScorer.second);
308                     }
309                 });
310     }
311 
invokeOnAddedCallbacks(@onNull ActiveModeManager activeModeManager)312     private void invokeOnAddedCallbacks(@NonNull ActiveModeManager activeModeManager) {
313         if (mVerboseLoggingEnabled) {
314             Log.v(TAG, "ModeManager added " + activeModeManager);
315         }
316         for (ModeChangeCallback callback : mCallbacks) {
317             callback.onActiveModeManagerAdded(activeModeManager);
318         }
319     }
320 
invokeOnRemovedCallbacks(@onNull ActiveModeManager activeModeManager)321     private void invokeOnRemovedCallbacks(@NonNull ActiveModeManager activeModeManager) {
322         if (mVerboseLoggingEnabled) {
323             Log.v(TAG, "ModeManager removed " + activeModeManager);
324         }
325         for (ModeChangeCallback callback : mCallbacks) {
326             callback.onActiveModeManagerRemoved(activeModeManager);
327         }
328     }
329 
invokeOnRoleChangedCallbacks(@onNull ActiveModeManager activeModeManager)330     private void invokeOnRoleChangedCallbacks(@NonNull ActiveModeManager activeModeManager) {
331         if (mVerboseLoggingEnabled) {
332             Log.v(TAG, "ModeManager role changed " + activeModeManager);
333         }
334         for (ModeChangeCallback callback : mCallbacks) {
335             callback.onActiveModeManagerRoleChanged(activeModeManager);
336         }
337     }
338 
invokeOnPrimaryClientModeManagerChangedCallbacks( @ullable ConcreteClientModeManager prevPrimaryClientModeManager, @Nullable ConcreteClientModeManager newPrimaryClientModeManager)339     private void invokeOnPrimaryClientModeManagerChangedCallbacks(
340             @Nullable ConcreteClientModeManager prevPrimaryClientModeManager,
341             @Nullable ConcreteClientModeManager newPrimaryClientModeManager) {
342         if (mVerboseLoggingEnabled) {
343             Log.v(TAG, "Primary ClientModeManager changed from " + prevPrimaryClientModeManager
344                     + " to " + newPrimaryClientModeManager);
345         }
346         for (PrimaryClientModeManagerChangedCallback callback : mPrimaryChangedCallbacks) {
347             callback.onChange(prevPrimaryClientModeManager, newPrimaryClientModeManager);
348         }
349     }
350 
351     /**
352      * Enable verbose logging.
353      */
enableVerboseLogging(boolean verbose)354     public void enableVerboseLogging(boolean verbose) {
355         mVerboseLoggingEnabled = verbose;
356         for (ActiveModeManager modeManager : getActiveModeManagers()) {
357             modeManager.enableVerboseLogging(verbose);
358         }
359     }
360 
361     /**
362      * See {@link android.net.wifi.WifiManager#setWifiConnectedNetworkScorer(Executor,
363      * WifiManager.WifiConnectedNetworkScorer)}
364      */
setWifiConnectedNetworkScorer(IBinder binder, IWifiConnectedNetworkScorer scorer)365     public boolean setWifiConnectedNetworkScorer(IBinder binder,
366             IWifiConnectedNetworkScorer scorer) {
367         try {
368             scorer.onSetScoreUpdateObserver(mExternalScoreUpdateObserverProxy);
369         } catch (RemoteException e) {
370             Log.e(TAG, "Unable to set score update observer " + scorer, e);
371             return false;
372         }
373         mClientModeManagerScorer = Pair.create(binder, scorer);
374         return getPrimaryClientModeManager().setWifiConnectedNetworkScorer(binder, scorer);
375     }
376 
377     /**
378      * See {@link WifiManager#clearWifiConnectedNetworkScorer()}
379      */
clearWifiConnectedNetworkScorer()380     public void clearWifiConnectedNetworkScorer() {
381         mClientModeManagerScorer = null;
382         getPrimaryClientModeManager().clearWifiConnectedNetworkScorer();
383     }
384 
385     /**
386      * Register for mode change callbacks.
387      */
registerModeChangeCallback(@onNull ModeChangeCallback callback)388     public void registerModeChangeCallback(@NonNull ModeChangeCallback callback) {
389         mCallbacks.add(Objects.requireNonNull(callback));
390     }
391 
392     /**
393      * Unregister mode change callback.
394      */
unregisterModeChangeCallback(@onNull ModeChangeCallback callback)395     public void unregisterModeChangeCallback(@NonNull ModeChangeCallback callback) {
396         mCallbacks.remove(Objects.requireNonNull(callback));
397     }
398 
399     /** Register for primary ClientModeManager changed callbacks. */
registerPrimaryClientModeManagerChangedCallback( @onNull PrimaryClientModeManagerChangedCallback callback)400     public void registerPrimaryClientModeManagerChangedCallback(
401             @NonNull PrimaryClientModeManagerChangedCallback callback) {
402         mPrimaryChangedCallbacks.add(Objects.requireNonNull(callback));
403         // If there is already a primary CMM when registering, send a callback with the info.
404         ConcreteClientModeManager cm = getPrimaryClientModeManagerNullable();
405         if (cm != null) callback.onChange(null, cm);
406     }
407 
408     /** Unregister for primary ClientModeManager changed callbacks. */
unregisterPrimaryClientModeManagerChangedCallback( @onNull PrimaryClientModeManagerChangedCallback callback)409     public void unregisterPrimaryClientModeManagerChangedCallback(
410             @NonNull PrimaryClientModeManagerChangedCallback callback) {
411         mPrimaryChangedCallbacks.remove(Objects.requireNonNull(callback));
412     }
413 
414     /**
415      * Notify that device is shutting down
416      * Keep it simple and don't add collection access codes
417      * to avoid concurrentModificationException when it is directly called from a different thread
418      */
notifyShuttingDown()419     public void notifyShuttingDown() {
420         mIsShuttingdown = true;
421     }
422 
423     /**
424      * @return Returns whether we can create more client mode managers or not.
425      */
canRequestMoreClientModeManagersInRole(@onNull WorkSource requestorWs, @NonNull ClientRole clientRole)426     public boolean canRequestMoreClientModeManagersInRole(@NonNull WorkSource requestorWs,
427             @NonNull ClientRole clientRole) {
428         if (!mWifiNative.isItPossibleToCreateStaIface(requestorWs)) {
429             return false;
430         }
431         if (clientRole == ROLE_CLIENT_LOCAL_ONLY) {
432             if (!mContext.getResources().getBoolean(
433                     R.bool.config_wifiMultiStaLocalOnlyConcurrencyEnabled)) {
434                 return false;
435             }
436             final int uid = requestorWs.getUid(0);
437             final String packageName = requestorWs.getPackageName(0);
438             // For peer to peer use-case, only allow secondary STA if the app is targeting S SDK
439             // or is a system app to provide backward compatibility.
440             return mWifiPermissionsUtil.isSystem(packageName, uid)
441                     || !mWifiPermissionsUtil.isTargetSdkLessThan(
442                             packageName, Build.VERSION_CODES.S, uid);
443         }
444         if (clientRole == ROLE_CLIENT_SECONDARY_TRANSIENT) {
445             return mContext.getResources().getBoolean(
446                     R.bool.config_wifiMultiStaNetworkSwitchingMakeBeforeBreakEnabled);
447         }
448         if (clientRole == ROLE_CLIENT_SECONDARY_LONG_LIVED) {
449             return mContext.getResources().getBoolean(
450                     R.bool.config_wifiMultiStaRestrictedConcurrencyEnabled);
451         }
452         Log.e(TAG, "Unrecognized role=" + clientRole);
453         return false;
454     }
455 
456     /**
457      * @return Returns whether we can create more SoftAp managers or not.
458      */
canRequestMoreSoftApManagers(@onNull WorkSource requestorWs)459     public boolean canRequestMoreSoftApManagers(@NonNull WorkSource requestorWs) {
460         return mWifiNative.isItPossibleToCreateApIface(requestorWs);
461     }
462 
463     /**
464      * @return Returns whether the device can support at least one concurrent client mode manager &
465      * softap manager.
466      */
isStaApConcurrencySupported()467     public boolean isStaApConcurrencySupported() {
468         return mWifiNative.isStaApConcurrencySupported();
469     }
470 
471     /**
472      * @return Returns whether the device can support at least two concurrent client mode managers
473      * and the local only use-case is enabled.
474      */
isStaStaConcurrencySupportedForLocalOnlyConnections()475     public boolean isStaStaConcurrencySupportedForLocalOnlyConnections() {
476         return mWifiNative.isStaStaConcurrencySupported()
477                 && mContext.getResources().getBoolean(
478                         R.bool.config_wifiMultiStaLocalOnlyConcurrencyEnabled);
479     }
480 
481     /**
482      * @return Returns whether the device can support at least two concurrent client mode managers
483      * and the mbb wifi switching is enabled.
484      */
isStaStaConcurrencySupportedForMbb()485     public boolean isStaStaConcurrencySupportedForMbb() {
486         return mWifiNative.isStaStaConcurrencySupported()
487                 && mContext.getResources().getBoolean(
488                         R.bool.config_wifiMultiStaNetworkSwitchingMakeBeforeBreakEnabled);
489     }
490 
491     /**
492      * @return Returns whether the device can support at least two concurrent client mode managers
493      * and the restricted use-case is enabled.
494      */
isStaStaConcurrencySupportedForRestrictedConnections()495     public boolean isStaStaConcurrencySupportedForRestrictedConnections() {
496         return mWifiNative.isStaStaConcurrencySupported()
497                 && mContext.getResources().getBoolean(
498                         R.bool.config_wifiMultiStaRestrictedConcurrencyEnabled);
499     }
500 
501     /** Begin listening to broadcasts and start the internal state machine. */
start()502     public void start() {
503         mContext.registerReceiver(new BroadcastReceiver() {
504             @Override
505             public void onReceive(Context context, Intent intent) {
506                 // Location mode has been toggled...  trigger with the scan change
507                 // update to make sure we are in the correct mode
508                 scanAlwaysModeChanged();
509             }
510         }, new IntentFilter(LocationManager.MODE_CHANGED_ACTION));
511         mContext.registerReceiver(new BroadcastReceiver() {
512             @Override
513             public void onReceive(Context context, Intent intent) {
514                 if (mSettingsStore.handleAirplaneModeToggled()) {
515                     airplaneModeToggled();
516                 }
517             }
518         }, new IntentFilter(Intent.ACTION_AIRPLANE_MODE_CHANGED));
519         mContext.registerReceiver(new BroadcastReceiver() {
520             @Override
521             public void onReceive(Context context, Intent intent) {
522                 boolean emergencyMode =
523                         intent.getBooleanExtra(TelephonyManager.EXTRA_PHONE_IN_ECM_STATE, false);
524                 emergencyCallbackModeChanged(emergencyMode);
525             }
526         }, new IntentFilter(TelephonyManager.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED));
527         boolean trackEmergencyCallState = mContext.getResources().getBoolean(
528                 R.bool.config_wifi_turn_off_during_emergency_call);
529         if (trackEmergencyCallState) {
530             mContext.registerReceiver(new BroadcastReceiver() {
531                 @Override
532                 public void onReceive(Context context, Intent intent) {
533                     boolean inCall = intent.getBooleanExtra(
534                             TelephonyManager.EXTRA_PHONE_IN_EMERGENCY_CALL, false);
535                     emergencyCallStateChanged(inCall);
536                 }
537             }, new IntentFilter(TelephonyManager.ACTION_EMERGENCY_CALL_STATE_CHANGED));
538         }
539 
540         mWifiController.start();
541     }
542 
543     /** Disable Wifi for recovery purposes. */
recoveryDisableWifi()544     public void recoveryDisableWifi() {
545         mWifiController.sendMessage(WifiController.CMD_RECOVERY_DISABLE_WIFI);
546     }
547 
548     /**
549      * Restart Wifi for recovery purposes.
550      * @param reason One of {@link SelfRecovery.RecoveryReason}
551      */
recoveryRestartWifi(@elfRecovery.RecoveryReason int reason, @Nullable String reasonDetail, boolean requestBugReport)552     public void recoveryRestartWifi(@SelfRecovery.RecoveryReason int reason,
553             @Nullable String reasonDetail, boolean requestBugReport) {
554         mWifiController.sendMessage(WifiController.CMD_RECOVERY_RESTART_WIFI, reason,
555                 requestBugReport ? 1 : 0, reasonDetail);
556     }
557 
558     /**
559      * register a callback to monitor the progress of Wi-Fi subsystem operation (started/finished)
560      * - started via {@link #recoveryRestartWifi(int, String, boolean)}.
561      */
registerSubsystemRestartCallback(ISubsystemRestartCallback callback)562     public boolean registerSubsystemRestartCallback(ISubsystemRestartCallback callback) {
563         return mRestartCallbacks.register(callback);
564     }
565 
566     /**
567      * unregister a callback to monitor the progress of Wi-Fi subsystem operation (started/finished)
568      * - started via {@link #recoveryRestartWifi(int, String, boolean)}. Callback is registered via
569      * {@link #registerSubsystemRestartCallback(ISubsystemRestartCallback)}.
570      */
unregisterSubsystemRestartCallback(ISubsystemRestartCallback callback)571     public boolean unregisterSubsystemRestartCallback(ISubsystemRestartCallback callback) {
572         return mRestartCallbacks.unregister(callback);
573     }
574 
575     /** Wifi has been toggled. */
wifiToggled(WorkSource requestorWs)576     public void wifiToggled(WorkSource requestorWs) {
577         mWifiController.sendMessage(WifiController.CMD_WIFI_TOGGLED, requestorWs);
578     }
579 
580     /** Airplane Mode has been toggled. */
airplaneModeToggled()581     public void airplaneModeToggled() {
582         mWifiController.sendMessage(WifiController.CMD_AIRPLANE_TOGGLED);
583     }
584 
585     /** Starts SoftAp. */
startSoftAp(SoftApModeConfiguration softApConfig, WorkSource requestorWs)586     public void startSoftAp(SoftApModeConfiguration softApConfig, WorkSource requestorWs) {
587         mWifiController.sendMessage(WifiController.CMD_SET_AP, 1, 0,
588                 Pair.create(softApConfig, requestorWs));
589     }
590 
591     /** Stop SoftAp. */
stopSoftAp(int mode)592     public void stopSoftAp(int mode) {
593         mWifiController.sendMessage(WifiController.CMD_SET_AP, 0, mode);
594     }
595 
596     /** Update SoftAp Capability. */
updateSoftApCapability(SoftApCapability capability)597     public void updateSoftApCapability(SoftApCapability capability) {
598         mWifiController.sendMessage(WifiController.CMD_UPDATE_AP_CAPABILITY, capability);
599     }
600 
601     /** Update SoftAp Configuration. */
updateSoftApConfiguration(SoftApConfiguration config)602     public void updateSoftApConfiguration(SoftApConfiguration config) {
603         mWifiController.sendMessage(WifiController.CMD_UPDATE_AP_CONFIG, config);
604     }
605 
606     /** Emergency Callback Mode has changed. */
emergencyCallbackModeChanged(boolean isInEmergencyCallbackMode)607     public void emergencyCallbackModeChanged(boolean isInEmergencyCallbackMode) {
608         mWifiController.sendMessage(
609                 WifiController.CMD_EMERGENCY_MODE_CHANGED, isInEmergencyCallbackMode ? 1 : 0);
610     }
611 
612     /** Emergency Call state has changed. */
emergencyCallStateChanged(boolean isInEmergencyCall)613     public void emergencyCallStateChanged(boolean isInEmergencyCall) {
614         mWifiController.sendMessage(
615                 WifiController.CMD_EMERGENCY_CALL_STATE_CHANGED, isInEmergencyCall ? 1 : 0);
616     }
617 
618     /** Scan always mode has changed. */
scanAlwaysModeChanged()619     public void scanAlwaysModeChanged() {
620         mWifiController.sendMessage(
621                 WifiController.CMD_SCAN_ALWAYS_MODE_CHANGED,
622                 // Scan only mode change is not considered a direct user interaction since user
623                 // is not explicitly turning on wifi scanning (side-effect of location toggle).
624                 // So, use the lowest priority internal requestor worksource to ensure that this
625                 // is treated with the lowest priority.
626                 INTERNAL_REQUESTOR_WS);
627     }
628 
629     /** emergency scan progress indication. */
setEmergencyScanRequestInProgress(boolean inProgress)630     public void setEmergencyScanRequestInProgress(boolean inProgress) {
631         mWifiController.sendMessage(
632                 WifiController.CMD_EMERGENCY_SCAN_STATE_CHANGED,
633                 inProgress ? 1 : 0, 0,
634                 // Emergency scans should have the highest priority, so use settings worksource.
635                 mFacade.getSettingsWorkSource(mContext));
636     }
637 
638     /**
639      * Listener to request a ModeManager instance for a particular operation.
640      */
641     public interface ExternalClientModeManagerRequestListener {
642         /**
643          * Returns an instance of ClientModeManager or null if the request failed (when wifi is
644          * off).
645          */
onAnswer(@ullable ClientModeManager modeManager)646         void onAnswer(@Nullable ClientModeManager modeManager);
647     }
648 
649     private static class AdditionalClientModeManagerRequestInfo {
650         @NonNull public final ExternalClientModeManagerRequestListener listener;
651         @NonNull public final WorkSource requestorWs;
652         @NonNull public final ClientConnectivityRole clientRole;
653         @NonNull public final String ssid;
654         @Nullable public final String bssid;
655 
AdditionalClientModeManagerRequestInfo( @onNull ExternalClientModeManagerRequestListener listener, @NonNull WorkSource requestorWs, @NonNull ClientConnectivityRole clientRole, @NonNull String ssid, @Nullable String bssid)656         AdditionalClientModeManagerRequestInfo(
657                 @NonNull  ExternalClientModeManagerRequestListener listener,
658                 @NonNull WorkSource requestorWs,
659                 @NonNull ClientConnectivityRole clientRole,
660                 @NonNull String ssid,
661                 // For some use-cases, bssid is selected by firmware.
662                 @Nullable String bssid) {
663             this.listener = listener;
664             this.requestorWs = requestorWs;
665             this.clientRole = clientRole;
666             this.ssid = ssid;
667             this.bssid = bssid;
668         }
669     }
670 
671     /**
672      * Request a local only client manager.
673      *
674      * @param listener used to receive the requested ClientModeManager. Will receive:
675      *                 1. null - if Wifi is toggled off
676      *                 2. The primary ClientModeManager - if a new ClientModeManager cannot be
677      *                    created.
678      *                 3. The new ClientModeManager - if it was created successfully.
679      * @param requestorWs the WorkSource for this request
680      */
requestLocalOnlyClientModeManager( @onNull ExternalClientModeManagerRequestListener listener, @NonNull WorkSource requestorWs, @NonNull String ssid, @NonNull String bssid)681     public void requestLocalOnlyClientModeManager(
682             @NonNull ExternalClientModeManagerRequestListener listener,
683             @NonNull WorkSource requestorWs, @NonNull String ssid, @NonNull String bssid) {
684         mWifiController.sendMessage(
685                 WifiController.CMD_REQUEST_ADDITIONAL_CLIENT_MODE_MANAGER,
686                 new AdditionalClientModeManagerRequestInfo(
687                         Objects.requireNonNull(listener), Objects.requireNonNull(requestorWs),
688                         ROLE_CLIENT_LOCAL_ONLY, ssid, bssid));
689     }
690 
691     /**
692      * Request a secondary long lived client manager.
693      *
694      * @param listener used to receive the requested ClientModeManager. Will receive:
695      *                 1. null - if Wifi is toggled off
696      *                 2. The primary ClientModeManager - if a new ClientModeManager cannot be
697      *                    created.
698      *                 3. The new ClientModeManager - if it was created successfully.
699      * @param requestorWs the WorkSource for this request
700      */
requestSecondaryLongLivedClientModeManager( @onNull ExternalClientModeManagerRequestListener listener, @NonNull WorkSource requestorWs, @NonNull String ssid, @Nullable String bssid)701     public void requestSecondaryLongLivedClientModeManager(
702             @NonNull ExternalClientModeManagerRequestListener listener,
703             @NonNull WorkSource requestorWs, @NonNull String ssid, @Nullable String bssid) {
704         mWifiController.sendMessage(
705                 WifiController.CMD_REQUEST_ADDITIONAL_CLIENT_MODE_MANAGER,
706                 new AdditionalClientModeManagerRequestInfo(
707                         Objects.requireNonNull(listener), Objects.requireNonNull(requestorWs),
708                         ROLE_CLIENT_SECONDARY_LONG_LIVED, ssid, bssid));
709     }
710 
711     /**
712      * Request a secondary transient client manager.
713      *
714      * @param listener used to receive the requested ClientModeManager. Will receive:
715      *                 1. null - if Wifi is toggled off.
716      *                 2. An existing secondary transient ClientModeManager - if it already exists.
717      *                 3. A new secondary transient ClientModeManager - if one doesn't exist and one
718      *                    was created successfully.
719      *                 4. The primary ClientModeManager - if a new ClientModeManager cannot be
720      *                    created.
721      * @param requestorWs the WorkSource for this request
722      */
requestSecondaryTransientClientModeManager( @onNull ExternalClientModeManagerRequestListener listener, @NonNull WorkSource requestorWs, @NonNull String ssid, @Nullable String bssid)723     public void requestSecondaryTransientClientModeManager(
724             @NonNull ExternalClientModeManagerRequestListener listener,
725             @NonNull WorkSource requestorWs, @NonNull String ssid, @Nullable String bssid) {
726         mWifiController.sendMessage(
727                 WifiController.CMD_REQUEST_ADDITIONAL_CLIENT_MODE_MANAGER,
728                 new AdditionalClientModeManagerRequestInfo(
729                         Objects.requireNonNull(listener), Objects.requireNonNull(requestorWs),
730                         ROLE_CLIENT_SECONDARY_TRANSIENT, ssid, bssid));
731     }
732 
733     /**
734      * Remove the provided client manager.
735      */
removeClientModeManager(ClientModeManager clientModeManager)736     public void removeClientModeManager(ClientModeManager clientModeManager) {
737         mWifiController.sendMessage(
738                 WifiController.CMD_REMOVE_ADDITIONAL_CLIENT_MODE_MANAGER, clientModeManager);
739     }
740 
741     /**
742      * Check whether we have a primary client mode manager (indicates wifi toggle on).
743      */
hasPrimaryClientModeManager()744     public boolean hasPrimaryClientModeManager() {
745         return getClientModeManagerInRole(ROLE_CLIENT_PRIMARY) != null;
746     }
747 
748     /**
749      * Returns primary client mode manager if any, else returns null
750      * This mode manager can be the default route on the device & will handle all external API
751      * calls.
752      * @return Instance of {@link ConcreteClientModeManager} or null.
753      */
754     @Nullable
getPrimaryClientModeManagerNullable()755     public ConcreteClientModeManager getPrimaryClientModeManagerNullable() {
756         return getClientModeManagerInRole(ROLE_CLIENT_PRIMARY);
757     }
758 
759     /**
760      * Returns primary client mode manager if any, else returns an instance of
761      * {@link ClientModeManager}.
762      * This mode manager can be the default route on the device & will handle all external API
763      * calls.
764      * @return Instance of {@link ClientModeManager}.
765      */
766     @NonNull
getPrimaryClientModeManager()767     public ClientModeManager getPrimaryClientModeManager() {
768         ClientModeManager cm = getPrimaryClientModeManagerNullable();
769         if (cm != null) return cm;
770         // If there is no primary client manager, return the default one.
771         return mDefaultClientModeManager;
772     }
773 
774     /**
775      * Returns all instances of ClientModeManager in
776      * {@link ActiveModeManager.ClientInternetConnectivityRole} roles.
777      * @return List of {@link ClientModeManager}.
778      */
779     @NonNull
getInternetConnectivityClientModeManagers()780     public List<ClientModeManager> getInternetConnectivityClientModeManagers() {
781         List<ClientModeManager> modeManagers = new ArrayList<>();
782         for (ConcreteClientModeManager manager : mClientModeManagers) {
783             if (manager.getRole() instanceof ClientInternetConnectivityRole) {
784                 modeManagers.add(manager);
785             }
786         }
787         return modeManagers;
788     }
789 
790     /** Stop all secondary transient ClientModeManagers. */
stopAllClientModeManagersInRole(ClientRole role)791     public void stopAllClientModeManagersInRole(ClientRole role) {
792         // there should only be at most one Make Before Break CMM, but check all of them to be safe.
793         for (ConcreteClientModeManager manager : mClientModeManagers) {
794             if (manager.getRole() == role) {
795                 stopAdditionalClientModeManager(manager);
796             }
797         }
798     }
799 
800     @NonNull
getClientModeManagers()801     public List<ClientModeManager> getClientModeManagers() {
802         return new ArrayList<>(mClientModeManagers);
803     }
804 
805     /**
806      * Returns scan only client mode manager, if any.
807      * This mode manager will only allow scanning.
808      * @return Instance of {@link ClientModeManager} or null if none present.
809      */
810     @Nullable
getScanOnlyClientModeManager()811     public ClientModeManager getScanOnlyClientModeManager() {
812         return getClientModeManagerInRole(ROLE_CLIENT_SCAN_ONLY);
813     }
814 
815     /**
816      * Returns tethered softap manager, if any.
817      * @return Instance of {@link SoftApManager} or null if none present.
818      */
819     @Nullable
getTetheredSoftApManager()820     public SoftApManager getTetheredSoftApManager() {
821         return getSoftApManagerInRole(ROLE_SOFTAP_TETHERED);
822     }
823 
824     /**
825      * Returns LOHS softap manager, if any.
826      * @return Instance of {@link SoftApManager} or null if none present.
827      */
828     @Nullable
getLocalOnlySoftApManager()829     public SoftApManager getLocalOnlySoftApManager() {
830         return getSoftApManagerInRole(ActiveModeManager.ROLE_SOFTAP_LOCAL_ONLY);
831     }
832 
hasAnyModeManager()833     private boolean hasAnyModeManager() {
834         return !mClientModeManagers.isEmpty() || !mSoftApManagers.isEmpty();
835     }
836 
hasAnyClientModeManager()837     private boolean hasAnyClientModeManager() {
838         return !mClientModeManagers.isEmpty();
839     }
840 
hasAnyClientModeManagerInConnectivityRole()841     private boolean hasAnyClientModeManagerInConnectivityRole() {
842         for (ConcreteClientModeManager manager : mClientModeManagers) {
843             if (manager.getRole() instanceof ClientConnectivityRole) return true;
844         }
845         return false;
846     }
847 
hasAnySoftApManager()848     private boolean hasAnySoftApManager() {
849         return !mSoftApManagers.isEmpty();
850     }
851 
852     /**
853      * @return true if all the client mode managers are in scan only role,
854      * false if there are no client mode managers present or if any of them are not in scan only
855      * role.
856      */
areAllClientModeManagersInScanOnlyRole()857     private boolean areAllClientModeManagersInScanOnlyRole() {
858         if (mClientModeManagers.isEmpty()) return false;
859         for (ConcreteClientModeManager manager : mClientModeManagers) {
860             if (manager.getRole() != ROLE_CLIENT_SCAN_ONLY) return false;
861         }
862         return true;
863     }
864 
865     /** Get any client mode manager in the given role, or null if none was found. */
866     @Nullable
getClientModeManagerInRole(ClientRole role)867     public ConcreteClientModeManager getClientModeManagerInRole(ClientRole role) {
868         for (ConcreteClientModeManager manager : mClientModeManagers) {
869             if (manager.getRole() == role) return manager;
870         }
871         return null;
872     }
873 
874     /** Get all client mode managers in the specified roles. */
875     @NonNull
getClientModeManagersInRoles(ClientRole... roles)876     public List<ConcreteClientModeManager> getClientModeManagersInRoles(ClientRole... roles) {
877         Set<ClientRole> rolesSet = Set.of(roles);
878         List<ConcreteClientModeManager> result = new ArrayList<>();
879         for (ConcreteClientModeManager manager : mClientModeManagers) {
880             ClientRole role = manager.getRole();
881             if (role != null && rolesSet.contains(role)) {
882                 result.add(manager);
883             }
884         }
885         return result;
886     }
887 
888     @Nullable
getSoftApManagerInRole(SoftApRole role)889     private SoftApManager getSoftApManagerInRole(SoftApRole role) {
890         for (SoftApManager manager : mSoftApManagers) {
891             if (manager.getRole() == role) return manager;
892         }
893         return null;
894     }
895 
getRoleForSoftApIpMode(int ipMode)896     private SoftApRole getRoleForSoftApIpMode(int ipMode) {
897         return ipMode == IFACE_IP_MODE_TETHERED
898                 ? ROLE_SOFTAP_TETHERED
899                 : ActiveModeManager.ROLE_SOFTAP_LOCAL_ONLY;
900     }
901 
902     /**
903      * Method to enable soft ap for wifi hotspot.
904      *
905      * The supplied SoftApModeConfiguration includes the target softap WifiConfiguration (or null if
906      * the persisted config is to be used) and the target operating mode (ex,
907      * {@link WifiManager#IFACE_IP_MODE_TETHERED} {@link WifiManager#IFACE_IP_MODE_LOCAL_ONLY}).
908      *
909      * @param softApConfig SoftApModeConfiguration for the hostapd softap
910      */
startSoftApModeManager( @onNull SoftApModeConfiguration softApConfig, @NonNull WorkSource requestorWs)911     private void startSoftApModeManager(
912             @NonNull SoftApModeConfiguration softApConfig, @NonNull WorkSource requestorWs) {
913         Log.d(TAG, "Starting SoftApModeManager config = " + softApConfig.getSoftApConfiguration());
914         Preconditions.checkState(softApConfig.getTargetMode() == IFACE_IP_MODE_LOCAL_ONLY
915                 || softApConfig.getTargetMode() == IFACE_IP_MODE_TETHERED);
916 
917         WifiServiceImpl.SoftApCallbackInternal callback =
918                 softApConfig.getTargetMode() == IFACE_IP_MODE_LOCAL_ONLY
919                         ? mLohsCallback : mSoftApCallback;
920         SoftApManager manager = mWifiInjector.makeSoftApManager(
921                 new SoftApListener(), callback, softApConfig, requestorWs,
922                 getRoleForSoftApIpMode(softApConfig.getTargetMode()), mVerboseLoggingEnabled);
923         mSoftApManagers.add(manager);
924     }
925 
926     /**
927      * Method to stop all soft ap for the specified mode.
928      *
929      * This method will stop any active softAp mode managers.
930      *
931      * @param ipMode the operating mode of APs to bring down (ex,
932      *             {@link WifiManager#IFACE_IP_MODE_TETHERED} or
933      *             {@link WifiManager#IFACE_IP_MODE_LOCAL_ONLY}).
934      *             Use {@link WifiManager#IFACE_IP_MODE_UNSPECIFIED} to stop all APs.
935      */
stopSoftApModeManagers(int ipMode)936     private void stopSoftApModeManagers(int ipMode) {
937         Log.d(TAG, "Shutting down all softap mode managers in mode " + ipMode);
938         for (SoftApManager softApManager : mSoftApManagers) {
939             if (ipMode == WifiManager.IFACE_IP_MODE_UNSPECIFIED
940                     || getRoleForSoftApIpMode(ipMode) == softApManager.getRole()) {
941                 softApManager.stop();
942             }
943         }
944     }
945 
updateCapabilityToSoftApModeManager(SoftApCapability capability)946     private void updateCapabilityToSoftApModeManager(SoftApCapability capability) {
947         for (SoftApManager softApManager : mSoftApManagers) {
948             softApManager.updateCapability(capability);
949         }
950     }
951 
updateConfigurationToSoftApModeManager(SoftApConfiguration config)952     private void updateConfigurationToSoftApModeManager(SoftApConfiguration config) {
953         for (SoftApManager softApManager : mSoftApManagers) {
954             softApManager.updateConfiguration(config);
955         }
956     }
957 
958     /**
959      * Method to enable a new primary client mode manager in scan only mode.
960      */
startScanOnlyClientModeManager(WorkSource requestorWs)961     private boolean startScanOnlyClientModeManager(WorkSource requestorWs) {
962         Log.d(TAG, "Starting primary ClientModeManager in scan only mode");
963         ConcreteClientModeManager manager = mWifiInjector.makeClientModeManager(
964                 new ClientListener(), requestorWs, ROLE_CLIENT_SCAN_ONLY, mVerboseLoggingEnabled);
965         mClientModeManagers.add(manager);
966         mLastScanOnlyClientModeManagerRequestorWs = requestorWs;
967         return true;
968     }
969 
970     /**
971      * Method to enable a new primary client mode manager in connect mode.
972      */
startPrimaryClientModeManager(WorkSource requestorWs)973     private boolean startPrimaryClientModeManager(WorkSource requestorWs) {
974         Log.d(TAG, "Starting primary ClientModeManager in connect mode");
975         ConcreteClientModeManager manager = mWifiInjector.makeClientModeManager(
976                 new ClientListener(), requestorWs, ROLE_CLIENT_PRIMARY, mVerboseLoggingEnabled);
977         mClientModeManagers.add(manager);
978         mLastPrimaryClientModeManagerRequestorWs = requestorWs;
979         return true;
980     }
981 
982     /**
983      * Method to enable a new primary client mode manager.
984      */
startPrimaryOrScanOnlyClientModeManager(WorkSource requestorWs)985     private boolean startPrimaryOrScanOnlyClientModeManager(WorkSource requestorWs) {
986         ActiveModeManager.ClientRole role = getRoleForPrimaryOrScanOnlyClientModeManager();
987         if (role == ROLE_CLIENT_PRIMARY) {
988             return startPrimaryClientModeManager(requestorWs);
989         } else if (role == ROLE_CLIENT_SCAN_ONLY) {
990             return startScanOnlyClientModeManager(requestorWs);
991         } else {
992             return false;
993         }
994     }
995 
996     /**
997      * Method to stop all client mode mangers.
998      */
stopAllClientModeManagers()999     private void stopAllClientModeManagers() {
1000         Log.d(TAG, "Shutting down all client mode managers");
1001         for (ConcreteClientModeManager clientModeManager : mClientModeManagers) {
1002             clientModeManager.stop();
1003         }
1004     }
1005 
1006     /**
1007      * Method to switch all primary client mode manager mode of operation to ScanOnly mode.
1008      */
switchAllPrimaryClientModeManagersToScanOnlyMode(@onNull WorkSource requestorWs)1009     private void switchAllPrimaryClientModeManagersToScanOnlyMode(@NonNull WorkSource requestorWs) {
1010         Log.d(TAG, "Switching all primary client mode managers to scan only mode");
1011         for (ConcreteClientModeManager clientModeManager : mClientModeManagers) {
1012             if (clientModeManager.getRole() != ROLE_CLIENT_PRIMARY) {
1013                 continue;
1014             }
1015             clientModeManager.setRole(ROLE_CLIENT_SCAN_ONLY, requestorWs);
1016         }
1017     }
1018 
1019     /**
1020      * Method to switch all client mode manager mode of operation (from ScanOnly To Connect &
1021      * vice-versa) based on the toggle state.
1022      */
switchAllPrimaryOrScanOnlyClientModeManagers()1023     private boolean switchAllPrimaryOrScanOnlyClientModeManagers() {
1024         Log.d(TAG, "Switching all client mode managers");
1025         for (ConcreteClientModeManager clientModeManager : mClientModeManagers) {
1026             if (clientModeManager.getRole() != ROLE_CLIENT_PRIMARY
1027                     && clientModeManager.getRole() != ROLE_CLIENT_SCAN_ONLY) {
1028                 continue;
1029             }
1030             if (!switchPrimaryOrScanOnlyClientModeManagerRole(clientModeManager)) {
1031                 return false;
1032             }
1033         }
1034         return true;
1035     }
1036 
getRoleForPrimaryOrScanOnlyClientModeManager()1037     private ActiveModeManager.ClientRole getRoleForPrimaryOrScanOnlyClientModeManager() {
1038         if (mSettingsStore.isWifiToggleEnabled()) {
1039             return ROLE_CLIENT_PRIMARY;
1040         } else if (mWifiController.shouldEnableScanOnlyMode()) {
1041             return ROLE_CLIENT_SCAN_ONLY;
1042         } else {
1043             Log.e(TAG, "Something is wrong, no client mode toggles enabled");
1044             return null;
1045         }
1046     }
1047 
1048     /**
1049      * Method to switch a client mode manager mode of operation (from ScanOnly To Connect &
1050      * vice-versa) based on the toggle state.
1051      */
switchPrimaryOrScanOnlyClientModeManagerRole( @onNull ConcreteClientModeManager modeManager)1052     private boolean switchPrimaryOrScanOnlyClientModeManagerRole(
1053             @NonNull ConcreteClientModeManager modeManager) {
1054         ActiveModeManager.ClientRole role = getRoleForPrimaryOrScanOnlyClientModeManager();
1055         final WorkSource lastRequestorWs;
1056         if (role == ROLE_CLIENT_PRIMARY) {
1057             lastRequestorWs = mLastPrimaryClientModeManagerRequestorWs;
1058         } else if (role == ROLE_CLIENT_SCAN_ONLY) {
1059             lastRequestorWs = mLastScanOnlyClientModeManagerRequestorWs;
1060         } else {
1061             return false;
1062         }
1063         modeManager.setRole(role, lastRequestorWs);
1064         return true;
1065     }
1066 
1067     /**
1068      * Method to start a new client mode manager.
1069      */
startAdditionalClientModeManager( ClientConnectivityRole role, @NonNull ExternalClientModeManagerRequestListener externalRequestListener, @NonNull WorkSource requestorWs)1070     private boolean startAdditionalClientModeManager(
1071             ClientConnectivityRole role,
1072             @NonNull ExternalClientModeManagerRequestListener externalRequestListener,
1073             @NonNull WorkSource requestorWs) {
1074         Log.d(TAG, "Starting additional ClientModeManager in role: " + role);
1075         ClientListener listener = new ClientListener(externalRequestListener);
1076         ConcreteClientModeManager manager = mWifiInjector.makeClientModeManager(
1077                 listener, requestorWs, role, mVerboseLoggingEnabled);
1078         mClientModeManagers.add(manager);
1079         return true;
1080     }
1081 
1082     /**
1083      * Method to switch role for an existing non-primary client mode manager.
1084      */
switchRoleForAdditionalClientModeManager( @onNull ConcreteClientModeManager manager, @NonNull ClientConnectivityRole role, @NonNull ExternalClientModeManagerRequestListener externalRequestListener, @NonNull WorkSource requestorWs)1085     private boolean switchRoleForAdditionalClientModeManager(
1086             @NonNull ConcreteClientModeManager manager,
1087             @NonNull ClientConnectivityRole role,
1088             @NonNull ExternalClientModeManagerRequestListener externalRequestListener,
1089             @NonNull WorkSource requestorWs) {
1090         Log.d(TAG, "Switching role for additional ClientModeManager to role: " + role);
1091         ClientListener listener = new ClientListener(externalRequestListener);
1092         manager.setRole(role, requestorWs, listener);
1093         return true;
1094     }
1095 
1096     /**
1097      * Method to stop client mode manger.
1098      */
stopAdditionalClientModeManager(ClientModeManager clientModeManager)1099     private void stopAdditionalClientModeManager(ClientModeManager clientModeManager) {
1100         if (clientModeManager.getRole() == ROLE_CLIENT_PRIMARY
1101                 || clientModeManager.getRole() == ROLE_CLIENT_SCAN_ONLY) return;
1102         Log.d(TAG, "Shutting down additional client mode manager: " + clientModeManager);
1103         clientModeManager.stop();
1104     }
1105 
1106     /**
1107      * Method to stop all active modes, for example, when toggling airplane mode.
1108      */
shutdownWifi()1109     private void shutdownWifi() {
1110         Log.d(TAG, "Shutting down all mode managers");
1111         for (ActiveModeManager manager : getActiveModeManagers()) {
1112             manager.stop();
1113         }
1114     }
1115 
1116     /**
1117      * Dump current state for active mode managers.
1118      *
1119      * Must be called from the main Wifi thread.
1120      */
dump(FileDescriptor fd, PrintWriter pw, String[] args)1121     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1122         pw.println("Dump of " + TAG);
1123         pw.println("Current wifi mode: " + getCurrentMode());
1124         pw.println("NumActiveModeManagers: " + getActiveModeManagerCount());
1125         mWifiController.dump(fd, pw, args);
1126         for (ActiveModeManager manager : getActiveModeManagers()) {
1127             manager.dump(fd, pw, args);
1128         }
1129         mGraveyard.dump(fd, pw, args);
1130         boolean isStaStaConcurrencySupported = mWifiNative.isStaStaConcurrencySupported();
1131         pw.println("STA + STA Concurrency Supported: " + isStaStaConcurrencySupported);
1132         if (isStaStaConcurrencySupported) {
1133             pw.println("   MBB use-case enabled: "
1134                     + mContext.getResources().getBoolean(
1135                             R.bool.config_wifiMultiStaNetworkSwitchingMakeBeforeBreakEnabled));
1136             pw.println("   Local only use-case enabled: "
1137                     + mContext.getResources().getBoolean(
1138                             R.bool.config_wifiMultiStaLocalOnlyConcurrencyEnabled));
1139             pw.println("   Restricted use-case enabled: "
1140                     + mContext.getResources().getBoolean(
1141                             R.bool.config_wifiMultiStaRestrictedConcurrencyEnabled));
1142         }
1143         pw.println("STA + AP Concurrency Supported: " + isStaApConcurrencySupported());
1144         mWifiInjector.getHalDeviceManager().dump(fd, pw, args);
1145     }
1146 
1147     @VisibleForTesting
getCurrentMode()1148     String getCurrentMode() {
1149         IState state = mWifiController.getCurrentState();
1150         return state == null ? STATE_MACHINE_EXITED_STATE_NAME : state.getName();
1151     }
1152 
1153     @VisibleForTesting
getActiveModeManagers()1154     Collection<ActiveModeManager> getActiveModeManagers() {
1155         ArrayList<ActiveModeManager> activeModeManagers = new ArrayList<>();
1156         activeModeManagers.addAll(mClientModeManagers);
1157         activeModeManagers.addAll(mSoftApManagers);
1158         return activeModeManagers;
1159     }
1160 
getActiveModeManagerCount()1161     private int getActiveModeManagerCount() {
1162         return mSoftApManagers.size() + mClientModeManagers.size();
1163     }
1164 
1165     @VisibleForTesting
isInEmergencyMode()1166     boolean isInEmergencyMode() {
1167         IState state = mWifiController.getCurrentState();
1168         return ((WifiController.BaseState) state).isInEmergencyMode();
1169     }
1170 
updateBatteryStats()1171     private void updateBatteryStats() {
1172         updateBatteryStatsWifiState(hasAnyModeManager());
1173         if (areAllClientModeManagersInScanOnlyRole()) {
1174             updateBatteryStatsScanModeActive();
1175         }
1176     }
1177 
1178     private class SoftApListener implements ActiveModeManager.Listener<SoftApManager> {
1179         @Override
onStarted(SoftApManager softApManager)1180         public void onStarted(SoftApManager softApManager) {
1181             updateBatteryStats();
1182             invokeOnAddedCallbacks(softApManager);
1183         }
1184 
1185         @Override
onRoleChanged(SoftApManager softApManager)1186         public void onRoleChanged(SoftApManager softApManager) {
1187             Log.w(TAG, "Role switched received on SoftApManager unexpectedly");
1188         }
1189 
1190         @Override
onStopped(SoftApManager softApManager)1191         public void onStopped(SoftApManager softApManager) {
1192             mSoftApManagers.remove(softApManager);
1193             mGraveyard.inter(softApManager);
1194             updateBatteryStats();
1195             mWifiController.sendMessage(WifiController.CMD_AP_STOPPED);
1196             invokeOnRemovedCallbacks(softApManager);
1197         }
1198 
1199         @Override
onStartFailure(SoftApManager softApManager)1200         public void onStartFailure(SoftApManager softApManager) {
1201             mSoftApManagers.remove(softApManager);
1202             mGraveyard.inter(softApManager);
1203             updateBatteryStats();
1204             mWifiController.sendMessage(WifiController.CMD_AP_START_FAILURE);
1205             // onStartFailure can be called when switching between roles. So, remove
1206             // update listeners.
1207             Log.e(TAG, "SoftApManager start failed!" + softApManager);
1208             invokeOnRemovedCallbacks(softApManager);
1209         }
1210     }
1211 
1212     private class ClientListener implements ActiveModeManager.Listener<ConcreteClientModeManager> {
1213         @Nullable
1214         private ExternalClientModeManagerRequestListener mExternalRequestListener; // one shot
1215 
ClientListener()1216         ClientListener() {
1217             this(null);
1218         }
1219 
ClientListener( @ullable ExternalClientModeManagerRequestListener externalRequestListener)1220         ClientListener(
1221                 @Nullable ExternalClientModeManagerRequestListener externalRequestListener) {
1222             mExternalRequestListener = externalRequestListener;
1223         }
1224 
1225         @WifiNative.MultiStaUseCase
getMultiStatUseCase()1226         private int getMultiStatUseCase() {
1227             // Note: The use-case setting finds the first non-primary client mode manager to set
1228             // the use-case to HAL. This does not extend to 3 STA concurrency when there are
1229             // 2 secondary STA client mode managers.
1230             for (ClientModeManager cmm : getClientModeManagers()) {
1231                 ClientRole clientRole = cmm.getRole();
1232                 if (clientRole == ROLE_CLIENT_LOCAL_ONLY
1233                         || clientRole == ROLE_CLIENT_SECONDARY_LONG_LIVED) {
1234                     return WifiNative.DUAL_STA_NON_TRANSIENT_UNBIASED;
1235                 } else if (clientRole == ROLE_CLIENT_SECONDARY_TRANSIENT) {
1236                     return WifiNative.DUAL_STA_TRANSIENT_PREFER_PRIMARY;
1237                 }
1238             }
1239             // if single STA, a safe default is PREFER_PRIMARY
1240             return WifiNative.DUAL_STA_TRANSIENT_PREFER_PRIMARY;
1241         }
1242 
1243         /**
1244          * Hardware needs to be configured for STA + STA before sending the callbacks to clients
1245          * letting them know that CM is ready for use.
1246          */
configureHwForMultiStaIfNecessary()1247         private void configureHwForMultiStaIfNecessary() {
1248             mWifiNative.setMultiStaUseCase(getMultiStatUseCase());
1249             String primaryIfaceName = getPrimaryClientModeManager().getInterfaceName();
1250             // if no primary exists (occurs briefly during Make Before Break), don't update the
1251             // primary and keep the previous primary. Only update WifiNative when the new primary is
1252             // activated.
1253             if (primaryIfaceName != null) {
1254                 mWifiNative.setMultiStaPrimaryConnection(primaryIfaceName);
1255             }
1256         }
1257 
onStartedOrRoleChanged(ConcreteClientModeManager clientModeManager)1258         private void onStartedOrRoleChanged(ConcreteClientModeManager clientModeManager) {
1259             updateClientScanMode();
1260             updateBatteryStats();
1261             configureHwForMultiStaIfNecessary();
1262             if (mExternalRequestListener != null) {
1263                 mExternalRequestListener.onAnswer(clientModeManager);
1264                 mExternalRequestListener = null; // reset after one shot.
1265             }
1266 
1267             // Report to SarManager
1268             reportWifiStateToSarManager();
1269         }
1270 
reportWifiStateToSarManager()1271         private void reportWifiStateToSarManager() {
1272             if (areAllClientModeManagersInScanOnlyRole()) {
1273                 // Inform sar manager that scan only is being enabled
1274                 mWifiInjector.getSarManager().setScanOnlyWifiState(WifiManager.WIFI_STATE_ENABLED);
1275             } else {
1276                 // Inform sar manager that scan only is being disabled
1277                 mWifiInjector.getSarManager().setScanOnlyWifiState(WifiManager.WIFI_STATE_DISABLED);
1278             }
1279             if (hasAnyClientModeManagerInConnectivityRole()) {
1280                 // Inform sar manager that wifi is Enabled
1281                 mWifiInjector.getSarManager().setClientWifiState(WifiManager.WIFI_STATE_ENABLED);
1282             } else {
1283                 // Inform sar manager that wifi is being disabled
1284                 mWifiInjector.getSarManager().setClientWifiState(WifiManager.WIFI_STATE_DISABLED);
1285             }
1286         }
1287 
onPrimaryChangedDueToStartedOrRoleChanged( ConcreteClientModeManager clientModeManager)1288         private void onPrimaryChangedDueToStartedOrRoleChanged(
1289                 ConcreteClientModeManager clientModeManager) {
1290             if (clientModeManager.getRole() != ROLE_CLIENT_PRIMARY
1291                     && clientModeManager == mLastPrimaryClientModeManager) {
1292                 // CMM was primary, but is no longer primary
1293                 invokeOnPrimaryClientModeManagerChangedCallbacks(clientModeManager, null);
1294                 mLastPrimaryClientModeManager = null;
1295             } else if (clientModeManager.getRole() == ROLE_CLIENT_PRIMARY
1296                     && clientModeManager != mLastPrimaryClientModeManager) {
1297                 // CMM is primary, but wasn't primary before
1298                 invokeOnPrimaryClientModeManagerChangedCallbacks(
1299                         mLastPrimaryClientModeManager, clientModeManager);
1300                 mLastPrimaryClientModeManager = clientModeManager;
1301             }
1302         }
1303 
1304         @Override
onStarted(@onNull ConcreteClientModeManager clientModeManager)1305         public void onStarted(@NonNull ConcreteClientModeManager clientModeManager) {
1306             onStartedOrRoleChanged(clientModeManager);
1307             invokeOnAddedCallbacks(clientModeManager);
1308             // invoke "added" callbacks before primary changed
1309             onPrimaryChangedDueToStartedOrRoleChanged(clientModeManager);
1310         }
1311 
1312         @Override
onRoleChanged(@onNull ConcreteClientModeManager clientModeManager)1313         public void onRoleChanged(@NonNull ConcreteClientModeManager clientModeManager) {
1314             onStartedOrRoleChanged(clientModeManager);
1315             invokeOnRoleChangedCallbacks(clientModeManager);
1316             onPrimaryChangedDueToStartedOrRoleChanged(clientModeManager);
1317         }
1318 
onStoppedOrStartFailure(ConcreteClientModeManager clientModeManager)1319         private void onStoppedOrStartFailure(ConcreteClientModeManager clientModeManager) {
1320             mClientModeManagers.remove(clientModeManager);
1321             mGraveyard.inter(clientModeManager);
1322             updateClientScanMode();
1323             updateBatteryStats();
1324             if (clientModeManager == mLastPrimaryClientModeManager) {
1325                 // CMM was primary, but was stopped
1326                 invokeOnPrimaryClientModeManagerChangedCallbacks(
1327                         mLastPrimaryClientModeManager, null);
1328                 mLastPrimaryClientModeManager = null;
1329             }
1330             // invoke "removed" callbacks after primary changed
1331             invokeOnRemovedCallbacks(clientModeManager);
1332 
1333             // Report to SarManager
1334             reportWifiStateToSarManager();
1335         }
1336 
1337         @Override
onStopped(@onNull ConcreteClientModeManager clientModeManager)1338         public void onStopped(@NonNull ConcreteClientModeManager clientModeManager) {
1339             onStoppedOrStartFailure(clientModeManager);
1340             mWifiController.sendMessage(WifiController.CMD_STA_STOPPED);
1341         }
1342 
1343         @Override
onStartFailure(@onNull ConcreteClientModeManager clientModeManager)1344         public void onStartFailure(@NonNull ConcreteClientModeManager clientModeManager) {
1345             Log.e(TAG, "ClientModeManager start failed!" + clientModeManager);
1346             // onStartFailure can be called when switching between roles. So, remove
1347             // update listeners.
1348             onStoppedOrStartFailure(clientModeManager);
1349             mWifiController.sendMessage(WifiController.CMD_STA_START_FAILURE);
1350         }
1351     }
1352 
1353     // Update the scan state based on all active mode managers.
updateClientScanMode()1354     private void updateClientScanMode() {
1355         boolean scanEnabled = hasAnyClientModeManager();
1356         boolean scanningForHiddenNetworksEnabled;
1357 
1358         if (mContext.getResources().getBoolean(R.bool.config_wifiScanHiddenNetworksScanOnlyMode)) {
1359             scanningForHiddenNetworksEnabled = hasAnyClientModeManager();
1360         } else {
1361             scanningForHiddenNetworksEnabled = hasAnyClientModeManagerInConnectivityRole();
1362         }
1363         mScanRequestProxy.enableScanning(scanEnabled, scanningForHiddenNetworksEnabled);
1364     }
1365 
1366     /**
1367      *  Helper method to report wifi state as on/off (doesn't matter which mode).
1368      *
1369      *  @param enabled boolean indicating that some mode has been turned on or off
1370      */
updateBatteryStatsWifiState(boolean enabled)1371     private void updateBatteryStatsWifiState(boolean enabled) {
1372         if (enabled) {
1373             if (getActiveModeManagerCount() == 1) {
1374                 // only report wifi on if we haven't already
1375                 mBatteryStatsManager.reportWifiOn();
1376             }
1377         } else {
1378             if (getActiveModeManagerCount() == 0) {
1379                 // only report if we don't have any active modes
1380                 mBatteryStatsManager.reportWifiOff();
1381             }
1382         }
1383     }
1384 
updateBatteryStatsScanModeActive()1385     private void updateBatteryStatsScanModeActive() {
1386         mBatteryStatsManager.reportWifiState(BatteryStatsManager.WIFI_STATE_OFF_SCANNING, null);
1387     }
1388 
1389     /**
1390      * Called to pull metrics from ActiveModeWarden to WifiMetrics when a dump is triggered, as
1391      * opposed to the more common push metrics which are reported to WifiMetrics as soon as they
1392      * occur.
1393      */
updateMetrics()1394     public void updateMetrics() {
1395         mWifiMetrics.setIsMakeBeforeBreakSupported(isStaStaConcurrencySupportedForMbb());
1396     }
1397 
1398     /**
1399      * WifiController is the class used to manage wifi state for various operating
1400      * modes (normal, airplane, wifi hotspot, etc.).
1401      */
1402     private class WifiController extends StateMachine {
1403         private static final String TAG = "WifiController";
1404 
1405         // Maximum limit to use for timeout delay if the value from overlay setting is too large.
1406         private static final int MAX_RECOVERY_TIMEOUT_DELAY_MS = 4000;
1407 
1408         private static final int BASE = Protocol.BASE_WIFI_CONTROLLER;
1409 
1410         static final int CMD_EMERGENCY_MODE_CHANGED                 = BASE + 1;
1411         static final int CMD_EMERGENCY_SCAN_STATE_CHANGED           = BASE + 2;
1412         static final int CMD_SCAN_ALWAYS_MODE_CHANGED               = BASE + 7;
1413         static final int CMD_WIFI_TOGGLED                           = BASE + 8;
1414         static final int CMD_AIRPLANE_TOGGLED                       = BASE + 9;
1415         static final int CMD_SET_AP                                 = BASE + 10;
1416         static final int CMD_EMERGENCY_CALL_STATE_CHANGED           = BASE + 14;
1417         static final int CMD_AP_STOPPED                             = BASE + 15;
1418         static final int CMD_STA_START_FAILURE                      = BASE + 16;
1419         // Command used to trigger a wifi stack restart when in active mode
1420         static final int CMD_RECOVERY_RESTART_WIFI                  = BASE + 17;
1421         // Internal command used to complete wifi stack restart
1422         private static final int CMD_RECOVERY_RESTART_WIFI_CONTINUE = BASE + 18;
1423         // Command to disable wifi when SelfRecovery is throttled or otherwise not doing full
1424         // recovery
1425         static final int CMD_RECOVERY_DISABLE_WIFI                   = BASE + 19;
1426         static final int CMD_STA_STOPPED                             = BASE + 20;
1427         static final int CMD_DEFERRED_RECOVERY_RESTART_WIFI          = BASE + 22;
1428         static final int CMD_AP_START_FAILURE                        = BASE + 23;
1429         static final int CMD_UPDATE_AP_CAPABILITY                    = BASE + 24;
1430         static final int CMD_UPDATE_AP_CONFIG                        = BASE + 25;
1431         static final int CMD_REQUEST_ADDITIONAL_CLIENT_MODE_MANAGER  = BASE + 26;
1432         static final int CMD_REMOVE_ADDITIONAL_CLIENT_MODE_MANAGER   = BASE + 27;
1433 
1434         private final EnabledState mEnabledState = new EnabledState();
1435         private final DisabledState mDisabledState = new DisabledState();
1436 
1437         private boolean mIsInEmergencyCall = false;
1438         private boolean mIsInEmergencyCallbackMode = false;
1439         private boolean mIsEmergencyScanInProgress = false;
1440 
WifiController()1441         WifiController() {
1442             super(TAG, mLooper);
1443 
1444             DefaultState defaultState = new DefaultState();
1445             addState(defaultState); {
1446                 addState(mDisabledState, defaultState);
1447                 addState(mEnabledState, defaultState);
1448             }
1449 
1450             setLogRecSize(100);
1451             setLogOnlyTransitions(false);
1452 
1453         }
1454 
1455         @Override
getWhatToString(int what)1456         protected String getWhatToString(int what) {
1457             switch (what) {
1458                 case CMD_AIRPLANE_TOGGLED:
1459                     return "CMD_AIRPLANE_TOGGLED";
1460                 case CMD_AP_START_FAILURE:
1461                     return "CMD_AP_START_FAILURE";
1462                 case CMD_AP_STOPPED:
1463                     return "CMD_AP_STOPPED";
1464                 case CMD_DEFERRED_RECOVERY_RESTART_WIFI:
1465                     return "CMD_DEFERRED_RECOVERY_RESTART_WIFI";
1466                 case CMD_EMERGENCY_CALL_STATE_CHANGED:
1467                     return "CMD_EMERGENCY_CALL_STATE_CHANGED";
1468                 case CMD_EMERGENCY_MODE_CHANGED:
1469                     return "CMD_EMERGENCY_MODE_CHANGED";
1470                 case CMD_RECOVERY_DISABLE_WIFI:
1471                     return "CMD_RECOVERY_DISABLE_WIFI";
1472                 case CMD_RECOVERY_RESTART_WIFI:
1473                     return "CMD_RECOVERY_RESTART_WIFI";
1474                 case CMD_RECOVERY_RESTART_WIFI_CONTINUE:
1475                     return "CMD_RECOVERY_RESTART_WIFI_CONTINUE";
1476                 case CMD_REMOVE_ADDITIONAL_CLIENT_MODE_MANAGER:
1477                     return "CMD_REMOVE_ADDITIONAL_CLIENT_MODE_MANAGER";
1478                 case CMD_REQUEST_ADDITIONAL_CLIENT_MODE_MANAGER:
1479                     return "CMD_REQUEST_ADDITIONAL_CLIENT_MODE_MANAGER";
1480                 case CMD_EMERGENCY_SCAN_STATE_CHANGED:
1481                     return "CMD_EMERGENCY_SCAN_STATE_CHANGED";
1482                 case CMD_SCAN_ALWAYS_MODE_CHANGED:
1483                     return "CMD_SCAN_ALWAYS_MODE_CHANGED";
1484                 case CMD_SET_AP:
1485                     return "CMD_SET_AP";
1486                 case CMD_STA_START_FAILURE:
1487                     return "CMD_STA_START_FAILURE";
1488                 case CMD_STA_STOPPED:
1489                     return "CMD_STA_STOPPED";
1490                 case CMD_UPDATE_AP_CAPABILITY:
1491                     return "CMD_UPDATE_AP_CAPABILITY";
1492                 case CMD_UPDATE_AP_CONFIG:
1493                     return "CMD_UPDATE_AP_CONFIG";
1494                 case CMD_WIFI_TOGGLED:
1495                     return "CMD_WIFI_TOGGLED";
1496                 default:
1497                     return "what:" + what;
1498             }
1499         }
1500 
1501         @Override
start()1502         public void start() {
1503             boolean isAirplaneModeOn = mSettingsStore.isAirplaneModeOn();
1504             boolean isWifiEnabled = mSettingsStore.isWifiToggleEnabled();
1505             boolean isScanningAlwaysAvailable = mSettingsStore.isScanAlwaysAvailable();
1506             boolean isLocationModeActive = mWifiPermissionsUtil.isLocationModeEnabled();
1507 
1508             log("isAirplaneModeOn = " + isAirplaneModeOn
1509                     + ", isWifiEnabled = " + isWifiEnabled
1510                     + ", isScanningAvailable = " + isScanningAlwaysAvailable
1511                     + ", isLocationModeActive = " + isLocationModeActive);
1512 
1513             // Initialize these values at bootup to defaults, will be overridden by API calls
1514             // for further toggles.
1515             mLastPrimaryClientModeManagerRequestorWs = mFacade.getSettingsWorkSource(mContext);
1516             mLastScanOnlyClientModeManagerRequestorWs = INTERNAL_REQUESTOR_WS;
1517             ActiveModeManager.ClientRole role = getRoleForPrimaryOrScanOnlyClientModeManager();
1518             if (role == ROLE_CLIENT_PRIMARY) {
1519                 startPrimaryClientModeManager(mLastPrimaryClientModeManagerRequestorWs);
1520                 setInitialState(mEnabledState);
1521             } else if (role == ROLE_CLIENT_SCAN_ONLY) {
1522                 startScanOnlyClientModeManager(mLastScanOnlyClientModeManagerRequestorWs);
1523                 setInitialState(mEnabledState);
1524             } else {
1525                 setInitialState(mDisabledState);
1526             }
1527             mWifiMetrics.noteWifiEnabledDuringBoot(mSettingsStore.isWifiToggleEnabled());
1528 
1529             // Initialize the lower layers before we start.
1530             mWifiNative.initialize();
1531             super.start();
1532         }
1533 
readWifiRecoveryDelay()1534         private int readWifiRecoveryDelay() {
1535             int recoveryDelayMillis = mContext.getResources().getInteger(
1536                     R.integer.config_wifi_framework_recovery_timeout_delay);
1537             if (recoveryDelayMillis > MAX_RECOVERY_TIMEOUT_DELAY_MS) {
1538                 recoveryDelayMillis = MAX_RECOVERY_TIMEOUT_DELAY_MS;
1539                 Log.w(TAG, "Overriding timeout delay with maximum limit value");
1540             }
1541             return recoveryDelayMillis;
1542         }
1543 
1544         abstract class BaseState extends State {
1545             @VisibleForTesting
isInEmergencyMode()1546             boolean isInEmergencyMode() {
1547                 return mIsInEmergencyCall || mIsInEmergencyCallbackMode;
1548             }
1549 
1550             /** Device is in emergency mode & carrier config requires wifi off in emergency mode */
isInEmergencyModeWhichRequiresWifiDisable()1551             private boolean isInEmergencyModeWhichRequiresWifiDisable() {
1552                 return isInEmergencyMode() && mFacade.getConfigWiFiDisableInECBM(mContext);
1553             }
1554 
updateEmergencyMode(Message msg)1555             private void updateEmergencyMode(Message msg) {
1556                 if (msg.what == CMD_EMERGENCY_CALL_STATE_CHANGED) {
1557                     mIsInEmergencyCall = msg.arg1 == 1;
1558                 } else if (msg.what == CMD_EMERGENCY_MODE_CHANGED) {
1559                     mIsInEmergencyCallbackMode = msg.arg1 == 1;
1560                 }
1561             }
1562 
enterEmergencyMode()1563             private void enterEmergencyMode() {
1564                 stopSoftApModeManagers(WifiManager.IFACE_IP_MODE_UNSPECIFIED);
1565                 boolean configWiFiDisableInECBM = mFacade.getConfigWiFiDisableInECBM(mContext);
1566                 log("Entering emergency callback mode, "
1567                         + "CarrierConfigManager.KEY_CONFIG_WIFI_DISABLE_IN_ECBM: "
1568                         + configWiFiDisableInECBM);
1569                 if (!mIsEmergencyScanInProgress) {
1570                     if (configWiFiDisableInECBM) {
1571                         shutdownWifi();
1572                     }
1573                 } else {
1574                     if (configWiFiDisableInECBM) {
1575                         switchAllPrimaryClientModeManagersToScanOnlyMode(
1576                                 mFacade.getSettingsWorkSource(mContext));
1577                     }
1578                 }
1579             }
1580 
exitEmergencyMode()1581             private void exitEmergencyMode() {
1582                 log("Exiting emergency callback mode");
1583                 // may be in DisabledState or EnabledState (depending on whether Wifi was shut down
1584                 // in enterEmergencyMode() or not based on getConfigWiFiDisableInECBM).
1585                 // Let CMD_WIFI_TOGGLED handling decide what the next state should be, or if we're
1586                 // already in the correct state.
1587 
1588                 // Assumes user toggled it on from settings before.
1589                 wifiToggled(mFacade.getSettingsWorkSource(mContext));
1590             }
1591 
processMessageInEmergencyMode(Message msg)1592             private boolean processMessageInEmergencyMode(Message msg) {
1593                 // In emergency mode: Some messages need special handling in this mode,
1594                 // all others are dropped.
1595                 switch (msg.what) {
1596                     case CMD_STA_STOPPED:
1597                     case CMD_AP_STOPPED:
1598                         log("Processing message in Emergency Callback Mode: " + msg);
1599                         if (!hasAnyModeManager()) {
1600                             log("No active mode managers, return to DisabledState.");
1601                             transitionTo(mDisabledState);
1602                         }
1603                         break;
1604                     case CMD_SET_AP:
1605                         // arg1 == 1 => enable AP
1606                         if (msg.arg1 == 1) {
1607                             log("AP cannot be started in Emergency Callback Mode: " + msg);
1608                             // SoftAP was disabled upon entering emergency mode. It also cannot
1609                             // be re-enabled during emergency mode. Drop the message and invoke
1610                             // the failure callback.
1611                             Pair<SoftApModeConfiguration, WorkSource> softApConfigAndWs =
1612                                     (Pair<SoftApModeConfiguration, WorkSource>) msg.obj;
1613                             SoftApModeConfiguration softApConfig = softApConfigAndWs.first;
1614                             WifiServiceImpl.SoftApCallbackInternal callback =
1615                                     softApConfig.getTargetMode() == IFACE_IP_MODE_LOCAL_ONLY
1616                                             ? mLohsCallback : mSoftApCallback;
1617                             // need to notify SoftApCallback that start/stop AP failed
1618                             callback.onStateChanged(WifiManager.WIFI_AP_STATE_FAILED,
1619                                     WifiManager.SAP_START_FAILURE_GENERAL);
1620                         }
1621                         break;
1622                     default:
1623                         log("Dropping message in emergency callback mode: " + msg);
1624                         break;
1625 
1626                 }
1627                 return HANDLED;
1628             }
1629 
handleEmergencyModeStateChange(Message msg)1630             private void handleEmergencyModeStateChange(Message msg) {
1631                 boolean wasInEmergencyMode = isInEmergencyMode();
1632                 updateEmergencyMode(msg);
1633                 boolean isInEmergencyMode = isInEmergencyMode();
1634                 if (!wasInEmergencyMode && isInEmergencyMode) {
1635                     enterEmergencyMode();
1636                 } else if (wasInEmergencyMode && !isInEmergencyMode) {
1637                     exitEmergencyMode();
1638                 }
1639             }
1640 
handleEmergencyScanStateChange(Message msg)1641             private void handleEmergencyScanStateChange(Message msg) {
1642                 final boolean scanInProgress = msg.arg1 == 1;
1643                 final WorkSource requestorWs = (WorkSource) msg.obj;
1644                 log("Processing scan state change: " + scanInProgress);
1645                 mIsEmergencyScanInProgress = scanInProgress;
1646                 if (isInEmergencyModeWhichRequiresWifiDisable())  {
1647                     // If wifi was disabled because of emergency mode
1648                     // (getConfigWiFiDisableInECBM == true), don't use the
1649                     // generic method to handle toggle change since that may put wifi in
1650                     // connectivity mode (since wifi toggle may actually be on underneath)
1651                     if (getCurrentState() == mDisabledState && scanInProgress) {
1652                         // go to scan only mode.
1653                         startScanOnlyClientModeManager(requestorWs);
1654                         transitionTo(mEnabledState);
1655                     } else if (getCurrentState() == mEnabledState && !scanInProgress) {
1656                         // shut down to go back to previous state.
1657                         stopAllClientModeManagers();
1658                     }
1659                 } else {
1660                     if (getCurrentState() == mDisabledState) {
1661                         handleStaToggleChangeInDisabledState(requestorWs);
1662                     } else if (getCurrentState() == mEnabledState) {
1663                         handleStaToggleChangeInEnabledState(requestorWs);
1664                     }
1665                 }
1666             }
1667 
1668             @Override
processMessage(Message msg)1669             public final boolean processMessage(Message msg) {
1670                 // potentially enter emergency mode
1671                 if (msg.what == CMD_EMERGENCY_CALL_STATE_CHANGED
1672                         || msg.what == CMD_EMERGENCY_MODE_CHANGED) {
1673                     handleEmergencyModeStateChange(msg);
1674                     return HANDLED;
1675                 } else if (msg.what == CMD_EMERGENCY_SCAN_STATE_CHANGED) {
1676                     // emergency scans need to be allowed even in emergency mode.
1677                     handleEmergencyScanStateChange(msg);
1678                     return HANDLED;
1679                 } else if (isInEmergencyMode()) {
1680                     return processMessageInEmergencyMode(msg);
1681                 } else {
1682                     // not in emergency mode, process messages normally
1683                     return processMessageFiltered(msg);
1684                 }
1685             }
1686 
processMessageFiltered(Message msg)1687             protected abstract boolean processMessageFiltered(Message msg);
1688         }
1689 
1690         class DefaultState extends State {
1691             @Override
processMessage(Message msg)1692             public boolean processMessage(Message msg) {
1693                 switch (msg.what) {
1694                     case CMD_SCAN_ALWAYS_MODE_CHANGED:
1695                     case CMD_EMERGENCY_SCAN_STATE_CHANGED:
1696                     case CMD_WIFI_TOGGLED:
1697                     case CMD_STA_STOPPED:
1698                     case CMD_STA_START_FAILURE:
1699                     case CMD_AP_STOPPED:
1700                     case CMD_AP_START_FAILURE:
1701                     case CMD_RECOVERY_RESTART_WIFI:
1702                     case CMD_RECOVERY_RESTART_WIFI_CONTINUE:
1703                     case CMD_DEFERRED_RECOVERY_RESTART_WIFI:
1704                     case CMD_REMOVE_ADDITIONAL_CLIENT_MODE_MANAGER:
1705                         break;
1706                     case CMD_REQUEST_ADDITIONAL_CLIENT_MODE_MANAGER:
1707                         AdditionalClientModeManagerRequestInfo requestInfo =
1708                                 (AdditionalClientModeManagerRequestInfo) msg.obj;
1709                         requestInfo.listener.onAnswer(null);
1710                         break;
1711                     case CMD_RECOVERY_DISABLE_WIFI:
1712                         log("Recovery has been throttled, disable wifi");
1713                         shutdownWifi();
1714                         // onStopped will move the state machine to "DisabledState".
1715                         break;
1716                     case CMD_AIRPLANE_TOGGLED:
1717                         if (mSettingsStore.isAirplaneModeOn()) {
1718                             log("Airplane mode toggled, shutdown all modes");
1719                             shutdownWifi();
1720                             // onStopped will move the state machine to "DisabledState".
1721                         } else {
1722                             log("Airplane mode disabled, determine next state");
1723                             if (shouldEnableSta()) {
1724                                 startPrimaryOrScanOnlyClientModeManager(
1725                                         // Assumes user toggled it on from settings before.
1726                                         mFacade.getSettingsWorkSource(mContext));
1727                                 transitionTo(mEnabledState);
1728                             }
1729                             // wifi should remain disabled, do not need to transition
1730                         }
1731                         break;
1732                     case CMD_UPDATE_AP_CAPABILITY:
1733                         updateCapabilityToSoftApModeManager((SoftApCapability) msg.obj);
1734                         break;
1735                     case CMD_UPDATE_AP_CONFIG:
1736                         updateConfigurationToSoftApModeManager((SoftApConfiguration) msg.obj);
1737                         break;
1738                     default:
1739                         throw new RuntimeException("WifiController.handleMessage " + msg.what);
1740                 }
1741                 return HANDLED;
1742             }
1743         }
1744 
shouldEnableScanOnlyMode()1745         private boolean shouldEnableScanOnlyMode() {
1746             return (mWifiPermissionsUtil.isLocationModeEnabled()
1747                     && mSettingsStore.isScanAlwaysAvailable())
1748                     || mIsEmergencyScanInProgress;
1749         }
1750 
shouldEnableSta()1751         private boolean shouldEnableSta() {
1752             return mSettingsStore.isWifiToggleEnabled() || shouldEnableScanOnlyMode();
1753         }
1754 
handleStaToggleChangeInDisabledState(WorkSource requestorWs)1755         private void handleStaToggleChangeInDisabledState(WorkSource requestorWs) {
1756             if (shouldEnableSta()) {
1757                 startPrimaryOrScanOnlyClientModeManager(requestorWs);
1758                 transitionTo(mEnabledState);
1759             }
1760         }
1761 
handleStaToggleChangeInEnabledState(WorkSource requestorWs)1762         private void handleStaToggleChangeInEnabledState(WorkSource requestorWs) {
1763             if (shouldEnableSta()) {
1764                 if (hasAnyClientModeManager()) {
1765                     switchAllPrimaryOrScanOnlyClientModeManagers();
1766                 } else {
1767                     startPrimaryOrScanOnlyClientModeManager(requestorWs);
1768                 }
1769             } else {
1770                 stopAllClientModeManagers();
1771             }
1772         }
1773 
1774         class DisabledState extends BaseState {
1775             @Override
enter()1776             public void enter() {
1777                 log("DisabledState.enter()");
1778                 super.enter();
1779                 if (hasAnyModeManager()) {
1780                     Log.e(TAG, "Entered DisabledState, but has active mode managers");
1781                 }
1782             }
1783 
1784             @Override
exit()1785             public void exit() {
1786                 log("DisabledState.exit()");
1787                 super.exit();
1788             }
1789 
1790             @Override
processMessageFiltered(Message msg)1791             public boolean processMessageFiltered(Message msg) {
1792                 switch (msg.what) {
1793                     case CMD_WIFI_TOGGLED:
1794                     case CMD_SCAN_ALWAYS_MODE_CHANGED:
1795                         handleStaToggleChangeInDisabledState((WorkSource) msg.obj);
1796                         break;
1797                     case CMD_SET_AP:
1798                         // note: CMD_SET_AP is handled/dropped in ECM mode - will not start here
1799                         if (msg.arg1 == 1) {
1800                             Pair<SoftApModeConfiguration, WorkSource> softApConfigAndWs =
1801                                     (Pair) msg.obj;
1802                             startSoftApModeManager(
1803                                     softApConfigAndWs.first, softApConfigAndWs.second);
1804                             transitionTo(mEnabledState);
1805                         }
1806                         break;
1807                     case CMD_RECOVERY_RESTART_WIFI:
1808                         log("Recovery triggered, already in disabled state");
1809                         sendMessageDelayed(CMD_RECOVERY_RESTART_WIFI_CONTINUE,
1810                                 Collections.emptyList(), readWifiRecoveryDelay());
1811                         break;
1812                     case CMD_DEFERRED_RECOVERY_RESTART_WIFI:
1813                         // wait mRecoveryDelayMillis for letting driver clean reset.
1814                         sendMessageDelayed(CMD_RECOVERY_RESTART_WIFI_CONTINUE,
1815                                 msg.obj, readWifiRecoveryDelay());
1816                         break;
1817                     case CMD_RECOVERY_RESTART_WIFI_CONTINUE:
1818                         log("Recovery in progress, start wifi");
1819                         List<ActiveModeManager> modeManagersBeforeRecovery = (List) msg.obj;
1820                         // No user controlled mode managers before recovery, so check if wifi
1821                         // was toggled on.
1822                         if (modeManagersBeforeRecovery.isEmpty()) {
1823                             if (shouldEnableSta()) {
1824                                 startPrimaryOrScanOnlyClientModeManager(
1825                                         // Assumes user toggled it on from settings before.
1826                                         mFacade.getSettingsWorkSource(mContext));
1827                                 transitionTo(mEnabledState);
1828                             }
1829                             break;
1830                         }
1831                         for (ActiveModeManager activeModeManager : modeManagersBeforeRecovery) {
1832                             if (activeModeManager instanceof ConcreteClientModeManager) {
1833                                 startPrimaryOrScanOnlyClientModeManager(
1834                                         activeModeManager.getRequestorWs());
1835                             } else if (activeModeManager instanceof SoftApManager) {
1836                                 SoftApManager softApManager = (SoftApManager) activeModeManager;
1837                                 startSoftApModeManager(
1838                                         softApManager.getSoftApModeConfiguration(),
1839                                         softApManager.getRequestorWs());
1840                             }
1841                         }
1842                         transitionTo(mEnabledState);
1843                         int numCallbacks = mRestartCallbacks.beginBroadcast();
1844                         for (int i = 0; i < numCallbacks; i++) {
1845                             try {
1846                                 mRestartCallbacks.getBroadcastItem(i).onSubsystemRestarted();
1847                             } catch (RemoteException e) {
1848                                 Log.e(TAG, "Failure calling onSubsystemRestarted" + e);
1849                             }
1850                         }
1851                         mRestartCallbacks.finishBroadcast();
1852                         break;
1853                     default:
1854                         return NOT_HANDLED;
1855                 }
1856                 return HANDLED;
1857             }
1858         }
1859 
1860         class EnabledState extends BaseState {
1861 
1862             private boolean mIsDisablingDueToAirplaneMode;
1863 
1864             @Override
enter()1865             public void enter() {
1866                 log("EnabledState.enter()");
1867                 super.enter();
1868                 if (!hasAnyModeManager()) {
1869                     Log.e(TAG, "Entered EnabledState, but no active mode managers");
1870                 }
1871                 mIsDisablingDueToAirplaneMode = false;
1872             }
1873 
1874             @Override
exit()1875             public void exit() {
1876                 log("EnabledState.exit()");
1877                 if (hasAnyModeManager()) {
1878                     Log.e(TAG, "Exiting EnabledState, but has active mode managers");
1879                 }
1880                 super.exit();
1881             }
1882 
isClientModeManagerConnectedOrConnectingToBssid( @onNull ClientModeManager clientModeManager, @NonNull String ssid, @NonNull String bssid)1883             private boolean isClientModeManagerConnectedOrConnectingToBssid(
1884                     @NonNull ClientModeManager clientModeManager,
1885                     @NonNull String ssid, @NonNull String bssid) {
1886                 WifiConfiguration connectedOrConnectingWifiConfiguration = coalesce(
1887                         clientModeManager.getConnectingWifiConfiguration(),
1888                         clientModeManager.getConnectedWifiConfiguration());
1889                 String connectedOrConnectingBssid = coalesce(
1890                         clientModeManager.getConnectingBssid(),
1891                         clientModeManager.getConnectedBssid());
1892                 String connectedOrConnectingSsid =
1893                         connectedOrConnectingWifiConfiguration == null
1894                                 ? null : connectedOrConnectingWifiConfiguration.SSID;
1895                 return Objects.equals(ssid, connectedOrConnectingSsid)
1896                         && Objects.equals(bssid, connectedOrConnectingBssid);
1897             }
1898 
1899             @Nullable
findAnyClientModeManagerConnectingOrConnectedToBssid( @onNull String ssid, @Nullable String bssid)1900             private ConcreteClientModeManager findAnyClientModeManagerConnectingOrConnectedToBssid(
1901                     @NonNull String ssid, @Nullable String bssid) {
1902                 if (bssid == null) {
1903                     return null;
1904                 }
1905                 for (ConcreteClientModeManager cmm : mClientModeManagers) {
1906                     if (isClientModeManagerConnectedOrConnectingToBssid(cmm, ssid, bssid)) {
1907                         return cmm;
1908                     }
1909                 }
1910                 return null;
1911             }
1912 
handleAdditionalClientModeManagerRequest( @onNull AdditionalClientModeManagerRequestInfo requestInfo)1913             private void handleAdditionalClientModeManagerRequest(
1914                     @NonNull AdditionalClientModeManagerRequestInfo requestInfo) {
1915                 ClientModeManager primaryManager = getPrimaryClientModeManager();
1916                 if (requestInfo.clientRole == ROLE_CLIENT_SECONDARY_TRANSIENT
1917                         && mDppManager.isSessionInProgress()) {
1918                     // When MBB is triggered, we could end up switching the primary interface
1919                     // after completion. So if we have any DPP session in progress, they will fail
1920                     // when the previous primary iface is removed after MBB completion.
1921                     Log.v(TAG, "DPP session in progress, fallback to single STA behavior "
1922                             + "using primary ClientModeManager=" + primaryManager);
1923                     requestInfo.listener.onAnswer(primaryManager);
1924                     return;
1925                 }
1926                 ConcreteClientModeManager cmmForSameBssid =
1927                         findAnyClientModeManagerConnectingOrConnectedToBssid(
1928                                 requestInfo.ssid, requestInfo.bssid);
1929                 if (cmmForSameBssid != null) {
1930                     // Can't allow 2 client mode managers triggering connection to same bssid.
1931                     Log.v(TAG, "Already connected to bssid=" + requestInfo.bssid
1932                             + " on ClientModeManager=" + cmmForSameBssid);
1933                     if (cmmForSameBssid.getRole() == ROLE_CLIENT_PRIMARY) {
1934                         // fallback to single STA behavior.
1935                         requestInfo.listener.onAnswer(cmmForSameBssid);
1936                         return;
1937                     }
1938                     // Existing secondary CMM connected to the same ssid/bssid.
1939                     if (!canRequestMoreClientModeManagersInRole(
1940                             requestInfo.requestorWs, requestInfo.clientRole)) {
1941                         Log.e(TAG, "New request cannot override existing request on "
1942                                 + "ClientModeManager=" + cmmForSameBssid);
1943                         // If the new request does not have priority over the existing request,
1944                         // reject it since we cannot have 2 CMM's connected to same ssid/bssid.
1945                         requestInfo.listener.onAnswer(null);
1946                         return;
1947                     }
1948                     // If the new request has a higher priority over the existing one, change it's
1949                     // role and send it to the new client.
1950                     // Switch role for non primary CMM & wait for it to complete before
1951                     // handing it to the requestor.
1952                     switchRoleForAdditionalClientModeManager(
1953                             cmmForSameBssid, requestInfo.clientRole, requestInfo.listener,
1954                             requestInfo.requestorWs);
1955                     return;
1956                 }
1957 
1958                 ClientModeManager cmmForSameRole =
1959                         getClientModeManagerInRole(requestInfo.clientRole);
1960                 if (cmmForSameRole != null) {
1961                     // Already have a client mode manager in the requested role.
1962                     // Note: This logic results in the framework not supporting more than 1 CMM in
1963                     // the same role concurrently. There is no use-case for that currently &
1964                     // none of the clients (i.e WifiNetworkFactory, WifiConnectivityManager, etc)
1965                     // are ready to support that either. If this assumption changes in the future
1966                     // when the device supports 3 STA's for example, change this logic!
1967                     Log.v(TAG, "Already exists ClientModeManager for role: " + cmmForSameRole);
1968                     requestInfo.listener.onAnswer(cmmForSameRole);
1969                     return;
1970                 }
1971                 if (canRequestMoreClientModeManagersInRole(
1972                         requestInfo.requestorWs, requestInfo.clientRole)) {
1973                     // Can create an additional client mode manager.
1974                     Log.v(TAG, "Starting a new ClientModeManager");
1975                     startAdditionalClientModeManager(
1976                             requestInfo.clientRole,
1977                             requestInfo.listener, requestInfo.requestorWs);
1978                     return;
1979                 }
1980                 // Fall back to single STA behavior.
1981                 Log.v(TAG, "Falling back to single STA behavior using primary ClientModeManager="
1982                         + primaryManager);
1983                 requestInfo.listener.onAnswer(primaryManager);
1984             }
1985 
1986             @Override
processMessageFiltered(Message msg)1987             public boolean processMessageFiltered(Message msg) {
1988                 switch (msg.what) {
1989                     case CMD_WIFI_TOGGLED:
1990                     case CMD_SCAN_ALWAYS_MODE_CHANGED:
1991                         handleStaToggleChangeInEnabledState((WorkSource) msg.obj);
1992                         break;
1993                     case CMD_REQUEST_ADDITIONAL_CLIENT_MODE_MANAGER:
1994                         handleAdditionalClientModeManagerRequest(
1995                                 (AdditionalClientModeManagerRequestInfo) msg.obj);
1996                         break;
1997                     case CMD_REMOVE_ADDITIONAL_CLIENT_MODE_MANAGER:
1998                         stopAdditionalClientModeManager((ClientModeManager) msg.obj);
1999                         break;
2000                     case CMD_SET_AP:
2001                         // note: CMD_SET_AP is handled/dropped in ECM mode - will not start here
2002                         if (msg.arg1 == 1) {
2003                             Pair<SoftApModeConfiguration, WorkSource> softApConfigAndWs =
2004                                     (Pair) msg.obj;
2005                             startSoftApModeManager(
2006                                     softApConfigAndWs.first, softApConfigAndWs.second);
2007                         } else {
2008                             stopSoftApModeManagers(msg.arg2);
2009                         }
2010                         break;
2011                     case CMD_AIRPLANE_TOGGLED:
2012                         // airplane mode toggled on is handled in the default state
2013                         if (mSettingsStore.isAirplaneModeOn()) {
2014                             mIsDisablingDueToAirplaneMode = true;
2015                             return NOT_HANDLED;
2016                         } else {
2017                             if (mIsDisablingDueToAirplaneMode) {
2018                                 // Previous airplane mode toggle on is being processed, defer the
2019                                 // message toggle off until previous processing is completed.
2020                                 // Once previous airplane mode toggle is complete, we should
2021                                 // transition to DisabledState. There, we will process the deferred
2022                                 // airplane mode toggle message to disable airplane mode.
2023                                 deferMessage(msg);
2024                             } else {
2025                                 // when airplane mode is toggled off, but wifi is on, we can keep it
2026                                 // on
2027                                 log("airplane mode toggled - and airplane mode is off. return "
2028                                         + "handled");
2029                             }
2030                             return HANDLED;
2031                         }
2032                     case CMD_AP_STOPPED:
2033                     case CMD_AP_START_FAILURE:
2034                         if (!hasAnyModeManager()) {
2035                             if (shouldEnableSta()) {
2036                                 log("SoftAp disabled, start client mode");
2037                                 startPrimaryOrScanOnlyClientModeManager(
2038                                         // Assumes user toggled it on from settings before.
2039                                         mFacade.getSettingsWorkSource(mContext));
2040                             } else {
2041                                 log("SoftAp mode disabled, return to DisabledState");
2042                                 transitionTo(mDisabledState);
2043                             }
2044                         } else {
2045                             log("AP disabled, remain in EnabledState.");
2046                         }
2047                         break;
2048                     case CMD_STA_START_FAILURE:
2049                     case CMD_STA_STOPPED:
2050                         // Client mode stopped. Head to Disabled to wait for next command if there
2051                         // no active mode managers.
2052                         if (!hasAnyModeManager()) {
2053                             log("STA disabled, return to DisabledState.");
2054                             transitionTo(mDisabledState);
2055                         } else {
2056                             log("STA disabled, remain in EnabledState.");
2057                         }
2058                         break;
2059                     case CMD_RECOVERY_RESTART_WIFI: {
2060                         final String bugTitle;
2061                         final String bugDetail = (String) msg.obj;
2062                         if (TextUtils.isEmpty(bugDetail)) {
2063                             bugTitle = "Wi-Fi BugReport";
2064                         } else {
2065                             bugTitle = "Wi-Fi BugReport: " + bugDetail;
2066                         }
2067                         log("Recovery triggered, disable wifi");
2068                         boolean bugReportRequested = msg.arg2 != 0;
2069                         if (bugReportRequested) {
2070                             mHandler.post(() ->
2071                                     mWifiDiagnostics.takeBugReport(bugTitle, bugDetail));
2072                         }
2073                         // Store all instances of tethered SAP + scan only/primary STA mode managers
2074                         List<ActiveModeManager> modeManagersBeforeRecovery = Stream.concat(
2075                                 mClientModeManagers.stream()
2076                                         .filter(m -> ROLE_CLIENT_SCAN_ONLY.equals(m.getRole())
2077                                                 || ROLE_CLIENT_PRIMARY.equals(m.getRole())),
2078                                 mSoftApManagers.stream()
2079                                         .filter(m -> ROLE_SOFTAP_TETHERED.equals(m.getRole())))
2080                                 .collect(Collectors.toList());
2081                         deferMessage(obtainMessage(CMD_DEFERRED_RECOVERY_RESTART_WIFI,
2082                                 modeManagersBeforeRecovery));
2083                         int numCallbacks = mRestartCallbacks.beginBroadcast();
2084                         for (int i = 0; i < numCallbacks; i++) {
2085                             try {
2086                                 mRestartCallbacks.getBroadcastItem(i).onSubsystemRestarting();
2087                             } catch (RemoteException e) {
2088                                 Log.e(TAG, "Failure calling onSubsystemRestarting" + e);
2089                             }
2090                         }
2091                         mRestartCallbacks.finishBroadcast();
2092                         shutdownWifi();
2093                         // onStopped will move the state machine to "DisabledState".
2094                         break;
2095                     }
2096                     default:
2097                         return NOT_HANDLED;
2098                 }
2099                 return HANDLED;
2100             }
2101         }
2102     }
2103 
coalesce(T a, T b)2104     private static <T> T coalesce(T a, T  b) {
2105         return a != null ? a : b;
2106     }
2107 }
2108