• 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.WIFI_STATE_DISABLED;
20 import static android.net.wifi.WifiManager.WIFI_STATE_DISABLING;
21 import static android.net.wifi.WifiManager.WIFI_STATE_ENABLED;
22 import static android.net.wifi.WifiManager.WIFI_STATE_ENABLING;
23 import static android.net.wifi.WifiManager.WIFI_STATE_UNKNOWN;
24 
25 import android.annotation.NonNull;
26 import android.annotation.Nullable;
27 import android.content.Context;
28 import android.content.Intent;
29 import android.net.ConnectivityManager;
30 import android.net.ConnectivityManager.NetworkCallback;
31 import android.net.DhcpResultsParcelable;
32 import android.net.Network;
33 import android.net.NetworkCapabilities;
34 import android.net.NetworkRequest;
35 import android.net.wifi.IWifiConnectedNetworkScorer;
36 import android.net.wifi.WifiAnnotations;
37 import android.net.wifi.WifiConfiguration;
38 import android.net.wifi.WifiInfo;
39 import android.net.wifi.WifiManager;
40 import android.net.wifi.hotspot2.IProvisioningCallback;
41 import android.net.wifi.hotspot2.OsuProvider;
42 import android.net.wifi.nl80211.DeviceWiphyCapabilities;
43 import android.os.Handler;
44 import android.os.HandlerExecutor;
45 import android.os.IBinder;
46 import android.os.Looper;
47 import android.os.Message;
48 import android.os.PersistableBundle;
49 import android.os.UserHandle;
50 import android.os.WorkSource;
51 import android.telephony.AccessNetworkConstants;
52 import android.telephony.CarrierConfigManager;
53 import android.telephony.SubscriptionInfo;
54 import android.telephony.SubscriptionManager;
55 import android.telephony.ims.ImsException;
56 import android.telephony.ims.ImsMmTelManager;
57 import android.telephony.ims.ImsReasonInfo;
58 import android.telephony.ims.RegistrationManager;
59 import android.telephony.ims.feature.MmTelFeature;
60 import android.telephony.ims.stub.ImsRegistrationImplBase;
61 import android.text.TextUtils;
62 import android.util.Log;
63 
64 import com.android.internal.util.IState;
65 import com.android.internal.util.State;
66 import com.android.internal.util.StateMachine;
67 import com.android.server.wifi.WifiNative.InterfaceCallback;
68 import com.android.server.wifi.WifiNative.RxFateReport;
69 import com.android.server.wifi.WifiNative.TxFateReport;
70 import com.android.server.wifi.util.ActionListenerWrapper;
71 import com.android.server.wifi.util.StateMachineObituary;
72 import com.android.server.wifi.util.WifiHandler;
73 import com.android.wifi.resources.R;
74 
75 import java.io.FileDescriptor;
76 import java.io.PrintWriter;
77 import java.util.ArrayDeque;
78 import java.util.List;
79 import java.util.Set;
80 import java.util.concurrent.atomic.AtomicInteger;
81 
82 /**
83  * Manage WiFi in Client Mode where we connect to configured networks and in Scan Only Mode where
84  * we do not connect to configured networks but do perform scanning.
85  *
86  * An instance of this class is active to manage each client interface. This is in contrast to
87  * {@link DefaultClientModeManager} which handles calls when no client interfaces are active.
88  *
89  * This class will dynamically instantiate {@link ClientModeImpl} when it enters client mode, and
90  * tear it down when it exits client mode. No instance of ClientModeImpl will be active in
91  * scan-only mode, instead {@link ScanOnlyModeImpl} will be used to respond to calls.
92  *
93  * <pre>
94  *                                           ActiveModeWarden
95  *                                      /                        \
96  *                                     /                          \
97  *                        ConcreteClientModeManager         DefaultClientModeManager
98  *                      (Client Mode + Scan Only Mode)            (Wifi off)
99  *                             /            \
100  *                           /               \
101  *                     ClientModeImpl       ScanOnlyModeImpl
102  * </pre>
103  */
104 public class ConcreteClientModeManager implements ClientModeManager {
105     private static final String TAG = "WifiClientModeManager";
106 
107     private final ClientModeStateMachine mStateMachine;
108 
109     private final Context mContext;
110     private final Clock mClock;
111     private final WifiNative mWifiNative;
112     private final WifiMetrics mWifiMetrics;
113     private final WakeupController mWakeupController;
114     private final WifiInjector mWifiInjector;
115     private final SelfRecovery mSelfRecovery;
116     private final WifiGlobals mWifiGlobals;
117     private final DefaultClientModeManager mDefaultClientModeManager;
118     private final ClientModeManagerBroadcastQueue mBroadcastQueue;
119     private final long mId;
120     private final Graveyard mGraveyard = new Graveyard();
121 
122     private String mClientInterfaceName;
123     private boolean mIfaceIsUp = false;
124     private boolean mShouldReduceNetworkScore = false;
125     private final DeferStopHandler mDeferStopHandler;
126     @Nullable
127     private ClientRole mRole = null;
128     @Nullable
129     private ClientRole mPreviousRole = null;
130     private long mLastRoleChangeSinceBootMs = 0;
131     @Nullable
132     private WorkSource mRequestorWs = null;
133     @NonNull
134     private Listener<ConcreteClientModeManager> mModeListener;
135     /** Caches the latest role change request. This is needed for the IMS dereg delay */
136     @Nullable
137     private RoleChangeInfo mTargetRoleChangeInfo;
138     private boolean mVerboseLoggingEnabled = false;
139     private int mActiveSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
140     private boolean mWifiStateChangeBroadcastEnabled = true;
141     /**
142      * mClientModeImpl is only non-null when in {@link ClientModeStateMachine.ConnectModeState} -
143      * it will be null in all other states
144      */
145     @Nullable
146     private ClientModeImpl mClientModeImpl = null;
147 
148     @Nullable
149     private ScanOnlyModeImpl mScanOnlyModeImpl = null;
150 
151     /**
152      * One of  {@link WifiManager#WIFI_STATE_DISABLED},
153      * {@link WifiManager#WIFI_STATE_DISABLING},
154      * {@link WifiManager#WIFI_STATE_ENABLED},
155      * {@link WifiManager#WIFI_STATE_ENABLING},
156      * {@link WifiManager#WIFI_STATE_UNKNOWN}
157      */
158     private final AtomicInteger mWifiState = new AtomicInteger(WIFI_STATE_DISABLED);
159 
160     private boolean mIsStopped = true;
161 
ConcreteClientModeManager( Context context, @NonNull Looper looper, Clock clock, WifiNative wifiNative, @NonNull Listener<ConcreteClientModeManager> listener, WifiMetrics wifiMetrics, WakeupController wakeupController, WifiInjector wifiInjector, SelfRecovery selfRecovery, WifiGlobals wifiGlobals, DefaultClientModeManager defaultClientModeManager, long id, @NonNull WorkSource requestorWs, @NonNull ClientRole role, @NonNull ClientModeManagerBroadcastQueue broadcastQueue, boolean verboseLoggingEnabled)162     ConcreteClientModeManager(
163             Context context, @NonNull Looper looper, Clock clock,
164             WifiNative wifiNative, @NonNull Listener<ConcreteClientModeManager> listener,
165             WifiMetrics wifiMetrics,
166             WakeupController wakeupController, WifiInjector wifiInjector,
167             SelfRecovery selfRecovery, WifiGlobals wifiGlobals,
168             DefaultClientModeManager defaultClientModeManager, long id,
169             @NonNull WorkSource requestorWs, @NonNull ClientRole role,
170             @NonNull ClientModeManagerBroadcastQueue broadcastQueue,
171             boolean verboseLoggingEnabled) {
172         mContext = context;
173         mClock = clock;
174         mWifiNative = wifiNative;
175         mModeListener = listener;
176         mWifiMetrics = wifiMetrics;
177         mWakeupController = wakeupController;
178         mWifiInjector = wifiInjector;
179         mStateMachine = new ClientModeStateMachine(looper);
180         mDeferStopHandler = new DeferStopHandler(looper);
181         mSelfRecovery = selfRecovery;
182         mWifiGlobals = wifiGlobals;
183         mDefaultClientModeManager = defaultClientModeManager;
184         mId = id;
185         mTargetRoleChangeInfo = new RoleChangeInfo(role, requestorWs, listener);
186         mBroadcastQueue = broadcastQueue;
187         enableVerboseLogging(verboseLoggingEnabled);
188         mStateMachine.sendMessage(ClientModeStateMachine.CMD_START, mTargetRoleChangeInfo);
189     }
190 
getTag()191     private String getTag() {
192         return TAG + "[" + (mClientInterfaceName == null ? "unknown" : mClientInterfaceName) + "]";
193     }
194 
195     /**
196      * Sets whether to send WIFI_STATE_CHANGED broadcast for this ClientModeManager.
197      * @param enabled
198      */
setWifiStateChangeBroadcastEnabled(boolean enabled)199     public void setWifiStateChangeBroadcastEnabled(boolean enabled) {
200         mWifiStateChangeBroadcastEnabled = enabled;
201     }
202 
203     /**
204      * Disconnect from any currently connected networks and stop client mode.
205      */
206     @Override
stop()207     public void stop() {
208         Log.d(getTag(), " currentstate: " + getCurrentStateName());
209         mTargetRoleChangeInfo = null;
210         if (mIfaceIsUp) {
211             updateConnectModeState(mRole, WifiManager.WIFI_STATE_DISABLING,
212                     WifiManager.WIFI_STATE_ENABLED);
213         } else {
214             updateConnectModeState(mRole, WifiManager.WIFI_STATE_DISABLING,
215                     WifiManager.WIFI_STATE_ENABLING);
216         }
217         mDeferStopHandler.start(getWifiOffDeferringTimeMs());
218     }
219 
220     private class DeferStopHandler extends WifiHandler {
221         private boolean mIsDeferring = false;
222         private ImsMmTelManager mImsMmTelManager = null;
223         private Looper mLooper = null;
224         private final Runnable mRunnable = () -> continueToStopWifi();
225         private int mMaximumDeferringTimeMillis = 0;
226         private long mDeferringStartTimeMillis = 0;
227         private ConnectivityManager mConnectivityManager = null;
228         private boolean mIsImsNetworkLost = false;
229         private boolean mIsImsNetworkUnregistered = false;
230 
231         private final RegistrationManager.RegistrationCallback mImsRegistrationCallback =
232                 new RegistrationManager.RegistrationCallback() {
233                     @Override
234                     public void onRegistered(int imsRadioTech) {
235                         Log.d(getTag(), "on IMS registered on type " + imsRadioTech);
236                         if (!mIsDeferring) return;
237 
238                         if (imsRadioTech != AccessNetworkConstants.TRANSPORT_TYPE_WLAN) {
239                             continueToStopWifi();
240                         }
241                     }
242 
243                     @Override
244                     public void onUnregistered(ImsReasonInfo imsReasonInfo) {
245                         Log.d(getTag(), "on IMS unregistered");
246                         mIsImsNetworkUnregistered = true;
247                         checkAndContinueToStopWifi();
248                     }
249                 };
250 
251         private final class ImsNetworkCallback extends NetworkCallback {
252             private int mRegisteredImsNetworkCount = 0;
253 
254             @Override
onAvailable(Network network)255             public void onAvailable(Network network) {
256                 synchronized (this) {
257                     Log.d(getTag(), "IMS network available: " + network);
258                     mRegisteredImsNetworkCount++;
259                 }
260             }
261 
262             @Override
onLost(Network network)263             public void onLost(Network network) {
264                 synchronized (this) {
265                     Log.d(getTag(), "IMS network lost: " + network
266                             + " ,isDeferring: " + mIsDeferring
267                             + " ,registered IMS network count: " + mRegisteredImsNetworkCount);
268                     mRegisteredImsNetworkCount--;
269                     if (mIsDeferring && mRegisteredImsNetworkCount <= 0) {
270                         mRegisteredImsNetworkCount = 0;
271                         mIsImsNetworkLost = true;
272                         checkAndContinueToStopWifi();
273                     }
274                 }
275             }
276         }
277 
278         private NetworkCallback mImsNetworkCallback = null;
279 
DeferStopHandler(Looper looper)280         DeferStopHandler(Looper looper) {
281             super(TAG, looper);
282             mLooper = looper;
283         }
284 
start(int delayMs)285         public void start(int delayMs) {
286             if (mIsDeferring) return;
287 
288             mMaximumDeferringTimeMillis = delayMs;
289             mDeferringStartTimeMillis = mClock.getElapsedSinceBootMillis();
290             // Most cases don't need delay, check it first to avoid unnecessary work.
291             if (delayMs == 0) {
292                 continueToStopWifi();
293                 return;
294             }
295 
296             mImsMmTelManager = ImsMmTelManager.createForSubscriptionId(mActiveSubId);
297             if (mImsMmTelManager == null || !postDelayed(mRunnable, delayMs)) {
298                 // if no delay or failed to add runnable, stop Wifi immediately.
299                 continueToStopWifi();
300                 return;
301             }
302 
303             mIsDeferring = true;
304             Log.d(getTag(), "Start DeferWifiOff handler with deferring time "
305                     + delayMs + " ms for subId: " + mActiveSubId);
306             try {
307                 mImsMmTelManager.registerImsRegistrationCallback(
308                         new HandlerExecutor(new Handler(mLooper)),
309                         mImsRegistrationCallback);
310             } catch (RuntimeException | ImsException e) {
311                 Log.e(getTag(), "registerImsRegistrationCallback failed", e);
312                 continueToStopWifi();
313                 return;
314             }
315 
316             NetworkRequest imsRequest = new NetworkRequest.Builder()
317                     .addCapability(NetworkCapabilities.NET_CAPABILITY_IMS)
318                     .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
319                     .build();
320 
321             mConnectivityManager =
322                     (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
323 
324             mImsNetworkCallback = new ImsNetworkCallback();
325             mConnectivityManager.registerNetworkCallback(imsRequest, mImsNetworkCallback,
326                     new Handler(mLooper));
327         }
328 
checkAndContinueToStopWifi()329         private void checkAndContinueToStopWifi() {
330             if (mIsImsNetworkLost && mIsImsNetworkUnregistered) {
331                 // Add delay for targets where IMS PDN down at modem takes additional delay.
332                 int delay = mContext.getResources()
333                         .getInteger(R.integer.config_wifiDelayDisconnectOnImsLostMs);
334                 if (delay == 0 || !postDelayed(mRunnable, delay)) {
335                     continueToStopWifi();
336                 }
337             }
338         }
339 
continueToStopWifi()340         private void continueToStopWifi() {
341             Log.d(getTag(), "The target role change info " + mTargetRoleChangeInfo);
342 
343             int deferringDurationMillis =
344                     (int) (mClock.getElapsedSinceBootMillis() - mDeferringStartTimeMillis);
345             boolean isTimedOut = mMaximumDeferringTimeMillis > 0
346                     && deferringDurationMillis >= mMaximumDeferringTimeMillis;
347             if (mTargetRoleChangeInfo == null) {
348                 Log.d(getTag(), "Continue to stop wifi");
349                 mStateMachine.captureObituaryAndQuitNow();
350                 mWifiMetrics.noteWifiOff(mIsDeferring, isTimedOut, deferringDurationMillis);
351             } else if (mTargetRoleChangeInfo.role == ROLE_CLIENT_SCAN_ONLY) {
352                 if (!mWifiNative.switchClientInterfaceToScanMode(
353                         mClientInterfaceName, mTargetRoleChangeInfo.requestorWs)) {
354                     mModeListener.onStartFailure(ConcreteClientModeManager.this);
355                 } else {
356                     mStateMachine.sendMessage(
357                             ClientModeStateMachine.CMD_SWITCH_TO_SCAN_ONLY_MODE_CONTINUE,
358                             mTargetRoleChangeInfo);
359                     mWifiMetrics.noteWifiOff(mIsDeferring, isTimedOut, deferringDurationMillis);
360                 }
361             } else {
362                 updateConnectModeState(mRole, WifiManager.WIFI_STATE_ENABLED,
363                         WifiManager.WIFI_STATE_DISABLING);
364             }
365 
366             if (!mIsDeferring) return;
367 
368             Log.d(getTag(), "Stop DeferWifiOff handler.");
369             removeCallbacks(mRunnable);
370             if (mImsMmTelManager != null) {
371                 try {
372                     mImsMmTelManager.unregisterImsRegistrationCallback(mImsRegistrationCallback);
373                 } catch (RuntimeException e) {
374                     Log.e(getTag(), "unregisterImsRegistrationCallback failed", e);
375                 }
376             }
377 
378             if (mConnectivityManager != null && mImsNetworkCallback != null) {
379                 mConnectivityManager.unregisterNetworkCallback(mImsNetworkCallback);
380                 mImsNetworkCallback = null;
381             }
382 
383             mIsDeferring = false;
384             mIsImsNetworkLost = false;
385             mIsImsNetworkUnregistered = false;
386         }
387     }
388 
389     /**
390      * Get deferring time before turning off WiFi.
391      */
getWifiOffDeferringTimeMs()392     private int getWifiOffDeferringTimeMs() {
393         SubscriptionManager subscriptionManager = (SubscriptionManager) mContext.getSystemService(
394                 Context.TELEPHONY_SUBSCRIPTION_SERVICE);
395         if (subscriptionManager == null) {
396             Log.d(getTag(), "SubscriptionManager not found");
397             return 0;
398         }
399 
400         List<SubscriptionInfo> subInfoList = subscriptionManager.getActiveSubscriptionInfoList();
401         if (subInfoList == null) {
402             Log.d(getTag(), "Active SubscriptionInfo list not found");
403             return 0;
404         }
405 
406         // Get the maximum delay for the active subscription latched on IWLAN.
407         int maxDelay = 0;
408         for (SubscriptionInfo subInfo : subInfoList) {
409             int curDelay = getWifiOffDeferringTimeMs(subInfo.getSubscriptionId());
410             if (curDelay > maxDelay) {
411                 maxDelay = curDelay;
412                 mActiveSubId = subInfo.getSubscriptionId();
413             }
414         }
415         return maxDelay;
416     }
417 
getWifiOffDeferringTimeMs(int subId)418     private int getWifiOffDeferringTimeMs(int subId) {
419         if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
420             Log.d(getTag(), "Invalid Subscription ID: " + subId);
421             return 0;
422         }
423 
424         ImsMmTelManager imsMmTelManager = ImsMmTelManager.createForSubscriptionId(subId);
425         // If no wifi calling, no delay
426         try {
427             if (!imsMmTelManager.isAvailable(
428                     MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE,
429                     ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN)) {
430                 Log.d(getTag(), "IMS not registered over IWLAN for subId: " + subId);
431                 return 0;
432             }
433         } catch (RuntimeException ex) {
434             Log.e(TAG, "IMS Manager is not available.", ex);
435             return 0;
436         }
437 
438         CarrierConfigManager configManager =
439                 (CarrierConfigManager) mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE);
440         PersistableBundle config = configManager.getConfigForSubId(subId);
441         return (config != null)
442                 ? config.getInt(CarrierConfigManager.Ims.KEY_WIFI_OFF_DEFERRING_TIME_MILLIS_INT)
443                 : 0;
444     }
445 
446     @Override
getRole()447     @Nullable public ClientRole getRole() {
448         return mRole;
449     }
450 
451     @Override
getPreviousRole()452     @Nullable public ClientRole getPreviousRole() {
453         return mPreviousRole;
454     }
455 
456     @Override
getLastRoleChangeSinceBootMs()457     public long getLastRoleChangeSinceBootMs() {
458         return mLastRoleChangeSinceBootMs;
459     }
460 
461     /**
462      * Class to hold info needed for role change.
463      */
464     private static class RoleChangeInfo {
465         @Nullable public final ClientRole role;
466         @Nullable public final WorkSource requestorWs;
467         @Nullable public final Listener<ConcreteClientModeManager> modeListener;
468 
RoleChangeInfo(@ullable ClientRole role)469         RoleChangeInfo(@Nullable ClientRole role) {
470             this(role, null, null);
471         }
472 
RoleChangeInfo(@ullable ClientRole role, @Nullable WorkSource requestorWs, @Nullable Listener<ConcreteClientModeManager> modeListener)473         RoleChangeInfo(@Nullable ClientRole role, @Nullable WorkSource requestorWs,
474                 @Nullable Listener<ConcreteClientModeManager> modeListener) {
475             this.role = role;
476             this.requestorWs = requestorWs;
477             this.modeListener = modeListener;
478         }
479 
480         @Override
toString()481         public String toString() {
482             return "Role: " + role + ", RequestorWs: " + requestorWs
483                     + ", ModeListener: " + modeListener;
484         }
485     }
486 
487     /** Set the role of this ClientModeManager */
setRole(@onNull ClientRole role, @NonNull WorkSource requestorWs)488     public void setRole(@NonNull ClientRole role, @NonNull WorkSource requestorWs) {
489         setRole(role, requestorWs, null);
490     }
491 
492     /** Set the role of this ClientModeManager */
setRole(@onNull ClientRole role, @NonNull WorkSource requestorWs, @Nullable Listener<ConcreteClientModeManager> modeListener)493     public void setRole(@NonNull ClientRole role, @NonNull WorkSource requestorWs,
494             @Nullable Listener<ConcreteClientModeManager> modeListener) {
495         mTargetRoleChangeInfo = new RoleChangeInfo(role, requestorWs, modeListener);
496         if (role == ROLE_CLIENT_SCAN_ONLY) {
497             // Switch client mode manager to scan only mode.
498             mStateMachine.sendMessage(
499                     ClientModeStateMachine.CMD_SWITCH_TO_SCAN_ONLY_MODE);
500         } else {
501             // Switch client mode manager to connect mode.
502             mStateMachine.sendMessage(
503                     ClientModeStateMachine.CMD_SWITCH_TO_CONNECT_MODE,
504                     mTargetRoleChangeInfo);
505         }
506     }
507 
508     @Override
getInterfaceName()509     public String getInterfaceName() {
510         return mClientInterfaceName;
511     }
512 
513     @Override
getRequestorWs()514     public WorkSource getRequestorWs() {
515         return mRequestorWs;
516     }
517 
518     /**
519      * Keep stopped {@link ClientModeImpl} instances so that they can be dumped to aid debugging.
520      *
521      * TODO(b/160283853): Find a smarter way to evict old ClientModeImpls
522      */
523     private static class Graveyard {
524         private static final int INSTANCES_TO_KEEP = 3;
525 
526         private final ArrayDeque<ClientModeImpl> mClientModeImpls = new ArrayDeque<>();
527 
528         /**
529          * Add this stopped {@link ClientModeImpl} to the graveyard, and evict the oldest
530          * ClientModeImpl if the graveyard is full.
531          */
inter(ClientModeImpl clientModeImpl)532         void inter(ClientModeImpl clientModeImpl) {
533             if (mClientModeImpls.size() == INSTANCES_TO_KEEP) {
534                 mClientModeImpls.removeFirst();
535             }
536             mClientModeImpls.addLast(clientModeImpl);
537         }
538 
539         /** Dump the contents of the graveyard. */
dump(FileDescriptor fd, PrintWriter pw, String[] args)540         void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
541             pw.println("Dump of ConcreteClientModeManager.Graveyard");
542             pw.println("Stopped ClientModeImpls: " + mClientModeImpls.size() + " total");
543             for (ClientModeImpl clientModeImpl : mClientModeImpls) {
544                 clientModeImpl.dump(fd, pw, args);
545             }
546             pw.println();
547         }
548 
hasAllClientModeImplsQuit()549         boolean hasAllClientModeImplsQuit() {
550             for (ClientModeImpl cmi : mClientModeImpls) {
551                 if (!cmi.hasQuit()) return false;
552             }
553             return true;
554         }
555     }
556 
557     /**
558      * Dump info about this ClientMode manager.
559      */
560     @Override
dump(FileDescriptor fd, PrintWriter pw, String[] args)561     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
562         pw.println("Dump of ClientModeManager id=" + mId);
563         pw.println("current StateMachine mode: " + getCurrentStateName());
564         pw.println("mRole: " + mRole);
565         pw.println("mTargetRoleChangeInfo: " + mTargetRoleChangeInfo);
566         pw.println("mClientInterfaceName: " + mClientInterfaceName);
567         pw.println("mIfaceIsUp: " + mIfaceIsUp);
568         mStateMachine.dump(fd, pw, args);
569         pw.println();
570         pw.println("Wi-Fi is " + syncGetWifiStateByName());
571         if (mClientModeImpl == null) {
572             pw.println("No active ClientModeImpl instance");
573         } else {
574             mClientModeImpl.dump(fd, pw, args);
575         }
576         mGraveyard.dump(fd, pw, args);
577         pw.println();
578     }
579 
getCurrentStateName()580     private String getCurrentStateName() {
581         IState currentState = mStateMachine.getCurrentState();
582 
583         if (currentState != null) {
584             return currentState.getName();
585         }
586 
587         return "StateMachine not active";
588     }
589 
590     /**
591      * Update Wifi state and send the broadcast.
592      *
593      * @param role         Target/Set role for this client mode manager instance.
594      * @param newState     new Wifi state
595      * @param currentState current wifi state
596      */
updateConnectModeState(ClientRole role, int newState, int currentState)597     private void updateConnectModeState(ClientRole role, int newState, int currentState) {
598         setWifiStateForApiCalls(newState);
599 
600         if (newState == WifiManager.WIFI_STATE_UNKNOWN) {
601             // do not need to broadcast failure to system
602             return;
603         }
604         if (role != ROLE_CLIENT_PRIMARY || !mWifiStateChangeBroadcastEnabled) {
605             // do not raise public broadcast unless this is the primary client mode manager
606             return;
607         }
608 
609         // TODO(b/175839153): this broadcast should only be sent out when wifi is toggled on/off,
610         //  not per CMM
611         final Intent intent = new Intent(WifiManager.WIFI_STATE_CHANGED_ACTION);
612         intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
613         intent.putExtra(WifiManager.EXTRA_WIFI_STATE, newState);
614         intent.putExtra(WifiManager.EXTRA_PREVIOUS_WIFI_STATE, currentState);
615         String summary = "broadcast=WIFI_STATE_CHANGED_ACTION"
616                 + " EXTRA_WIFI_STATE=" + newState
617                 + " EXTRA_PREVIOUS_WIFI_STATE=" + currentState;
618         if (mVerboseLoggingEnabled) Log.d(getTag(), "Queuing " + summary);
619         ClientModeManagerBroadcastQueue.QueuedBroadcast broadcast =
620                 () -> {
621                     if (mVerboseLoggingEnabled) Log.d(getTag(), "Sending " + summary);
622                     mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
623                 };
624         if (mRole == null && role == ROLE_CLIENT_PRIMARY) {
625             // This CMM is intended to be the primary, but has not completed the mode transition
626             // yet. Need to force broadcast to be sent.
627             broadcast.send();
628         } else {
629             mBroadcastQueue.queueOrSendBroadcast(this, broadcast);
630         }
631     }
632 
setWifiStateForApiCalls(int newState)633     private void setWifiStateForApiCalls(int newState) {
634         switch (newState) {
635             case WIFI_STATE_DISABLING:
636             case WIFI_STATE_DISABLED:
637             case WIFI_STATE_ENABLING:
638             case WIFI_STATE_ENABLED:
639             case WIFI_STATE_UNKNOWN:
640                 if (mVerboseLoggingEnabled) {
641                     Log.d(getTag(), "setting wifi state to: " + newState);
642                 }
643                 mWifiState.set(newState);
644                 break;
645             default:
646                 Log.d(getTag(), "attempted to set an invalid state: " + newState);
647                 break;
648         }
649     }
650 
syncGetWifiStateByName()651     private String syncGetWifiStateByName() {
652         switch (mWifiState.get()) {
653             case WIFI_STATE_DISABLING:
654                 return "disabling";
655             case WIFI_STATE_DISABLED:
656                 return "disabled";
657             case WIFI_STATE_ENABLING:
658                 return "enabling";
659             case WIFI_STATE_ENABLED:
660                 return "enabled";
661             case WIFI_STATE_UNKNOWN:
662                 return "unknown state";
663             default:
664                 return "[invalid state]";
665         }
666     }
667 
668     private class ClientModeStateMachine extends StateMachine {
669         // Commands for the state machine.
670         public static final int CMD_START = 0;
671         public static final int CMD_SWITCH_TO_SCAN_ONLY_MODE = 1;
672         public static final int CMD_SWITCH_TO_CONNECT_MODE = 2;
673         public static final int CMD_INTERFACE_STATUS_CHANGED = 3;
674         public static final int CMD_INTERFACE_DESTROYED = 4;
675         public static final int CMD_INTERFACE_DOWN = 5;
676         public static final int CMD_SWITCH_TO_SCAN_ONLY_MODE_CONTINUE = 6;
677         private final State mIdleState = new IdleState();
678         private final State mStartedState = new StartedState();
679         private final State mScanOnlyModeState = new ScanOnlyModeState();
680         private final State mConnectModeState = new ConnectModeState();
681         // Workaround since we cannot use transitionTo(mScanOnlyModeState, RoleChangeInfo)
682         private RoleChangeInfo mScanRoleChangeInfoToSetOnTransition = null;
683         // Workaround since we cannot use transitionTo(mConnectModeState, RoleChangeInfo)
684         private RoleChangeInfo mConnectRoleChangeInfoToSetOnTransition = null;
685 
686         @Nullable
687         private StateMachineObituary mObituary = null;
688 
689         private final InterfaceCallback mWifiNativeInterfaceCallback = new InterfaceCallback() {
690             @Override
691             public void onDestroyed(String ifaceName) {
692                 if (mClientInterfaceName != null && mClientInterfaceName.equals(ifaceName)) {
693                     Log.d(getTag(), "STA iface " + ifaceName + " was destroyed, "
694                             + "stopping client mode");
695 
696                     // we must immediately clean up state in ClientModeImpl to unregister
697                     // all client mode related objects
698                     // Note: onDestroyed is only called from the main Wifi thread
699                     if (mClientModeImpl == null) {
700                         Log.w(getTag(), "Received mWifiNativeInterfaceCallback.onDestroyed "
701                                 + "callback when no ClientModeImpl instance is active.");
702                     } else {
703                         mClientModeImpl.handleIfaceDestroyed();
704                     }
705 
706                     sendMessage(CMD_INTERFACE_DESTROYED);
707                 }
708             }
709 
710             @Override
711             public void onUp(String ifaceName) {
712                 if (mClientInterfaceName != null && mClientInterfaceName.equals(ifaceName)) {
713                     sendMessage(CMD_INTERFACE_STATUS_CHANGED, 1);
714                 }
715             }
716 
717             @Override
718             public void onDown(String ifaceName) {
719                 if (mClientInterfaceName != null && mClientInterfaceName.equals(ifaceName)) {
720                     sendMessage(CMD_INTERFACE_STATUS_CHANGED, 0);
721                 }
722             }
723         };
724 
ClientModeStateMachine(Looper looper)725         ClientModeStateMachine(Looper looper) {
726             super(TAG, looper);
727 
728             // CHECKSTYLE:OFF IndentationCheck
729             addState(mIdleState);
730                 addState(mStartedState, mIdleState);
731                     addState(mScanOnlyModeState, mStartedState);
732                     addState(mConnectModeState, mStartedState);
733             // CHECKSTYLE:ON IndentationCheck
734 
735             setInitialState(mIdleState);
736             start();
737         }
738 
captureObituaryAndQuitNow()739         void captureObituaryAndQuitNow() {
740             // capture StateMachine LogRecs since we will lose them after we call quitNow()
741             // This is used for debugging.
742             mObituary = new StateMachineObituary(this);
743 
744             quitNow();
745         }
746 
747         @Override
dump(FileDescriptor fd, PrintWriter pw, String[] args)748         public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
749             if (mObituary == null) {
750                 // StateMachine hasn't quit yet, dump `this` via StateMachineObituary's dump()
751                 // method for consistency with `else` branch.
752                 new StateMachineObituary(this).dump(fd, pw, args);
753             } else {
754                 // StateMachine has quit and cleared all LogRecs.
755                 // Get them from the obituary instead.
756                 mObituary.dump(fd, pw, args);
757             }
758         }
759 
760         /**
761          * Reset this ConcreteClientModeManager when its role changes, so that it can be reused for
762          * another purpose.
763          */
reset()764         private void reset() {
765             // Therefore, the caller must ensure that the role change has been completed and these
766             // settings have already reset before setting them, otherwise the new setting would be
767             // lost.
768             setShouldReduceNetworkScore(false);
769         }
770 
setRoleInternal(@onNull RoleChangeInfo roleChangeInfo)771         private void setRoleInternal(@NonNull RoleChangeInfo roleChangeInfo) {
772             mPreviousRole = mRole;
773             mLastRoleChangeSinceBootMs = mClock.getElapsedSinceBootMillis();
774             mRole = roleChangeInfo.role;
775             if (roleChangeInfo.requestorWs != null) {
776                 mRequestorWs = roleChangeInfo.requestorWs;
777             }
778             if (roleChangeInfo.modeListener != null) {
779                 mModeListener = roleChangeInfo.modeListener;
780             }
781         }
782 
setRoleInternalAndInvokeCallback(@onNull RoleChangeInfo roleChangeInfo)783         private void setRoleInternalAndInvokeCallback(@NonNull RoleChangeInfo roleChangeInfo) {
784             if (roleChangeInfo.role == mRole) return;
785             if (mRole == null) {
786                 Log.v(getTag(), "ClientModeManager started in role: " + roleChangeInfo);
787                 setRoleInternal(roleChangeInfo);
788                 mModeListener.onStarted(ConcreteClientModeManager.this);
789             } else {
790                 Log.v(getTag(), "ClientModeManager role changed: " + roleChangeInfo);
791                 setRoleInternal(roleChangeInfo);
792                 reset();
793                 mModeListener.onRoleChanged(ConcreteClientModeManager.this);
794             }
795             if (mClientModeImpl != null) {
796                 mClientModeImpl.onRoleChanged();
797             }
798         }
799 
800         private class IdleState extends State {
801             @Override
enter()802             public void enter() {
803                 Log.d(getTag(), "entering IdleState");
804                 mClientInterfaceName = null;
805                 mIfaceIsUp = false;
806             }
807 
808             @Override
exit()809             public void exit() {
810                 // Sometimes the wifi handler thread may become blocked that the statemachine
811                 // will exit in the IdleState without first entering StartedState. Trigger a
812                 // cleanup here in case the above sequence happens. This the statemachine was
813                 // started normally this will will not send a duplicate broadcast since mIsStopped
814                 // will get set to false the first time the exit happens.
815                 cleanupOnQuitIfApplicable();
816             }
817 
818             @Override
processMessage(Message message)819             public boolean processMessage(Message message) {
820                 switch (message.what) {
821                     case CMD_START:
822                         // Always start in scan mode first.
823                         RoleChangeInfo roleChangeInfo = (RoleChangeInfo) message.obj;
824                         mClientInterfaceName = mWifiNative.setupInterfaceForClientInScanMode(
825                                 mWifiNativeInterfaceCallback, roleChangeInfo.requestorWs);
826                         if (TextUtils.isEmpty(mClientInterfaceName)) {
827                             Log.e(getTag(), "Failed to create ClientInterface. Sit in Idle");
828                             mModeListener.onStartFailure(ConcreteClientModeManager.this);
829                             break;
830                         }
831                         if (roleChangeInfo.role instanceof ClientConnectivityRole) {
832                             sendMessage(CMD_SWITCH_TO_CONNECT_MODE, roleChangeInfo);
833                             transitionTo(mStartedState);
834                         } else {
835                             mScanRoleChangeInfoToSetOnTransition = roleChangeInfo;
836                             transitionTo(mScanOnlyModeState);
837                         }
838                         break;
839                     default:
840                         Log.d(getTag(), "received an invalid message: " + message);
841                         return NOT_HANDLED;
842                 }
843                 return HANDLED;
844             }
845         }
846 
847         private class StartedState extends State {
848 
onUpChanged(boolean isUp)849             private void onUpChanged(boolean isUp) {
850                 if (isUp == mIfaceIsUp) {
851                     return;  // no change
852                 }
853                 mIfaceIsUp = isUp;
854                 if (!isUp) {
855                     // if the interface goes down we should exit and go back to idle state.
856                     Log.d(getTag(), "interface down!");
857                     mStateMachine.sendMessage(CMD_INTERFACE_DOWN);
858                 }
859             }
860 
861             @Override
enter()862             public void enter() {
863                 Log.d(getTag(), "entering StartedState");
864                 mIfaceIsUp = false;
865                 mIsStopped = false;
866                 onUpChanged(mWifiNative.isInterfaceUp(mClientInterfaceName));
867             }
868 
869             @Override
processMessage(Message message)870             public boolean processMessage(Message message) {
871                 switch (message.what) {
872                     case CMD_START:
873                         // Already started, ignore this command.
874                         break;
875                     case CMD_SWITCH_TO_CONNECT_MODE: {
876                         RoleChangeInfo roleChangeInfo = (RoleChangeInfo) message.obj;
877                         updateConnectModeState(roleChangeInfo.role,
878                                 WifiManager.WIFI_STATE_ENABLING,
879                                 WifiManager.WIFI_STATE_DISABLED);
880                         if (!mWifiNative.switchClientInterfaceToConnectivityMode(
881                                 mClientInterfaceName, roleChangeInfo.requestorWs)) {
882                             updateConnectModeState(roleChangeInfo.role,
883                                     WifiManager.WIFI_STATE_UNKNOWN,
884                                     WifiManager.WIFI_STATE_ENABLING);
885                             updateConnectModeState(roleChangeInfo.role,
886                                     WifiManager.WIFI_STATE_DISABLED,
887                                     WifiManager.WIFI_STATE_UNKNOWN);
888                             mModeListener.onStartFailure(ConcreteClientModeManager.this);
889                             break;
890                         }
891                         // Role set in the enter of ConnectModeState.
892                         mConnectRoleChangeInfoToSetOnTransition = roleChangeInfo;
893                         transitionTo(mConnectModeState);
894                         break;
895                     }
896                     case CMD_SWITCH_TO_SCAN_ONLY_MODE:
897                         updateConnectModeState(mRole, WifiManager.WIFI_STATE_DISABLING,
898                                 WifiManager.WIFI_STATE_ENABLED);
899                         mDeferStopHandler.start(getWifiOffDeferringTimeMs());
900                         break;
901                     case CMD_SWITCH_TO_SCAN_ONLY_MODE_CONTINUE: {
902                         RoleChangeInfo roleChangeInfo = (RoleChangeInfo) message.obj;
903                         mScanRoleChangeInfoToSetOnTransition = roleChangeInfo;
904                         transitionTo(mScanOnlyModeState);
905                         break;
906                     }
907                     case CMD_INTERFACE_DOWN:
908                         Log.e(getTag(), "Detected an interface down, reporting failure to "
909                                 + "SelfRecovery");
910                         mSelfRecovery.trigger(SelfRecovery.REASON_STA_IFACE_DOWN);
911                         // once interface down, nothing else to do...  stop the state machine
912                         captureObituaryAndQuitNow();
913                         break;
914                     case CMD_INTERFACE_STATUS_CHANGED:
915                         boolean isUp = message.arg1 == 1;
916                         onUpChanged(isUp);
917                         break;
918                     case CMD_INTERFACE_DESTROYED:
919                         Log.e(getTag(), "interface destroyed - client mode stopping");
920                         mClientInterfaceName = null;
921                         // once interface destroyed, nothing else to do...  stop the state machine
922                         captureObituaryAndQuitNow();
923                         break;
924                     default:
925                         return NOT_HANDLED;
926                 }
927                 return HANDLED;
928             }
929 
930             /**
931              * Clean up state, unregister listeners and update wifi state.
932              */
933             @Override
exit()934             public void exit() {
935                 if (mClientInterfaceName != null) {
936                     mWifiNative.teardownInterface(mClientInterfaceName);
937                     mClientInterfaceName = null;
938                     mIfaceIsUp = false;
939                 }
940 
941                 Log.i(getTag(), "StartedState#exit(), setting mRole = null");
942                 mIsStopped = true;
943                 cleanupOnQuitIfApplicable();
944             }
945         }
946 
947         private class ScanOnlyModeState extends State {
948             @Override
enter()949             public void enter() {
950                 Log.d(getTag(), "entering ScanOnlyModeState");
951 
952                 if (mClientInterfaceName != null) {
953                     mScanOnlyModeImpl = mWifiInjector.makeScanOnlyModeImpl(
954                             mClientInterfaceName);
955                 } else {
956                     Log.e(getTag(), "Entered ScanOnlyModeState with a null interface name!");
957                 }
958 
959                 if (mScanRoleChangeInfoToSetOnTransition == null
960                         || (mScanRoleChangeInfoToSetOnTransition.role != ROLE_CLIENT_SCAN_ONLY)) {
961                     Log.wtf(TAG, "Unexpected mScanRoleChangeInfoToSetOnTransition: "
962                             + mScanRoleChangeInfoToSetOnTransition);
963                     // Should never happen, but fallback to scan only to avoid a crash.
964                     mScanRoleChangeInfoToSetOnTransition =
965                             new RoleChangeInfo(ROLE_CLIENT_SCAN_ONLY);
966                 }
967 
968                 setRoleInternalAndInvokeCallback(mScanRoleChangeInfoToSetOnTransition);
969                 // If we're in ScanOnlyModeState, there is only 1 CMM. So it's ok to call
970                 // WakeupController directly, there won't be multiple CMMs trampling over each other
971                 mWakeupController.start();
972                 mWifiNative.setScanMode(mClientInterfaceName, true);
973             }
974 
975             @Override
processMessage(Message message)976             public boolean processMessage(Message message) {
977                 switch (message.what) {
978                     case CMD_SWITCH_TO_SCAN_ONLY_MODE:
979                         // Already in scan only mode, ignore this command.
980                         break;
981                     default:
982                         return NOT_HANDLED;
983                 }
984                 return HANDLED;
985             }
986 
987             @Override
exit()988             public void exit() {
989                 mScanOnlyModeImpl = null;
990                 mScanRoleChangeInfoToSetOnTransition = null;
991 
992                 // If we're in ScanOnlyModeState, there is only 1 CMM. So it's ok to call
993                 // WakeupController directly, there won't be multiple CMMs trampling over each other
994                 mWakeupController.stop();
995                 mWifiNative.setScanMode(mClientInterfaceName, false);
996             }
997         }
998 
999         private class ConnectModeState extends State {
1000             @Override
enter()1001             public void enter() {
1002                 Log.d(getTag(), "entering ConnectModeState, starting ClientModeImpl");
1003                 if (mClientInterfaceName == null) {
1004                     Log.e(getTag(), "Supposed to start ClientModeImpl, but iface is null!");
1005                 } else {
1006                     if (mClientModeImpl != null) {
1007                         Log.e(getTag(), "ConnectModeState.enter(): mClientModeImpl is already "
1008                                 + "instantiated?!");
1009                     }
1010                     mClientModeImpl = mWifiInjector.makeClientModeImpl(
1011                             mClientInterfaceName, ConcreteClientModeManager.this,
1012                             mVerboseLoggingEnabled);
1013                     mClientModeImpl.setShouldReduceNetworkScore(mShouldReduceNetworkScore);
1014                 }
1015                 if (mConnectRoleChangeInfoToSetOnTransition == null
1016                         || !(mConnectRoleChangeInfoToSetOnTransition.role
1017                         instanceof ClientConnectivityRole)) {
1018                     Log.wtf(TAG, "Unexpected mConnectRoleChangeInfoToSetOnTransition: "
1019                             + mConnectRoleChangeInfoToSetOnTransition);
1020                     // Should never happen, but fallback to primary to avoid a crash.
1021                     mConnectRoleChangeInfoToSetOnTransition =
1022                             new RoleChangeInfo(ROLE_CLIENT_PRIMARY);
1023                 }
1024 
1025                 // Could be any one of possible connect mode roles.
1026                 setRoleInternalAndInvokeCallback(mConnectRoleChangeInfoToSetOnTransition);
1027                 updateConnectModeState(mConnectRoleChangeInfoToSetOnTransition.role,
1028                         WIFI_STATE_ENABLED, WIFI_STATE_ENABLING);
1029             }
1030 
1031             @Override
processMessage(Message message)1032             public boolean processMessage(Message message) {
1033                 switch (message.what) {
1034                     case CMD_SWITCH_TO_CONNECT_MODE:
1035                         RoleChangeInfo roleChangeInfo = (RoleChangeInfo) message.obj;
1036                         // switching to connect mode when already in connect mode, just update the
1037                         // requestor WorkSource.
1038                         boolean success = mWifiNative.replaceStaIfaceRequestorWs(
1039                                 mClientInterfaceName, roleChangeInfo.requestorWs);
1040                         if (success) {
1041                             setRoleInternalAndInvokeCallback(roleChangeInfo);
1042                         } else {
1043                             // If this call failed, the iface would be torn down.
1044                             // Thus, simply abort and let the iface down handling take care of the
1045                             // rest.
1046                             Log.e(getTag(), "Failed to switch ClientModeManager="
1047                                     + ConcreteClientModeManager.this + "'s requestorWs");
1048                         }
1049                         break;
1050                     case CMD_SWITCH_TO_SCAN_ONLY_MODE:
1051                     case CMD_INTERFACE_DESTROYED:
1052                         updateConnectModeState(mRole, WifiManager.WIFI_STATE_DISABLING,
1053                                 WifiManager.WIFI_STATE_ENABLED);
1054                         return NOT_HANDLED; // Handled in StartedState.
1055                     case CMD_INTERFACE_DOWN:
1056                         updateConnectModeState(mRole, WifiManager.WIFI_STATE_DISABLING,
1057                                 WifiManager.WIFI_STATE_UNKNOWN);
1058                         return NOT_HANDLED; // Handled in StartedState.
1059                     case CMD_INTERFACE_STATUS_CHANGED:
1060                         boolean isUp = message.arg1 == 1;
1061                         if (isUp == mIfaceIsUp) {
1062                             break;  // no change
1063                         }
1064                         if (!isUp) {
1065                             if (mWifiGlobals.isConnectedMacRandomizationEnabled()
1066                                     && getClientMode().isConnecting()) {
1067                                 return HANDLED; // For MAC randomization, ignore...
1068                             } else {
1069                                 // Handle the error case where our underlying interface went down if
1070                                 // we do not have mac randomization enabled (b/72459123).
1071                                 // if the interface goes down we should exit and go back to idle
1072                                 // state.
1073                                 updateConnectModeState(mRole, WifiManager.WIFI_STATE_UNKNOWN,
1074                                         WifiManager.WIFI_STATE_ENABLED);
1075                             }
1076                         }
1077                         return NOT_HANDLED; // Handled in StartedState.
1078                     default:
1079                         return NOT_HANDLED;
1080                 }
1081                 return HANDLED;
1082             }
1083 
1084             @Override
exit()1085             public void exit() {
1086                 updateConnectModeState(mRole, WifiManager.WIFI_STATE_DISABLED,
1087                         WifiManager.WIFI_STATE_DISABLING);
1088 
1089                 if (mClientModeImpl == null) {
1090                     Log.w(getTag(), "ConnectModeState.exit(): mClientModeImpl is already null?!");
1091                 } else {
1092                     Log.d(getTag(), "Stopping ClientModeImpl");
1093                     mClientModeImpl.stop();
1094                     mGraveyard.inter(mClientModeImpl);
1095                     mClientModeImpl = null;
1096                 }
1097 
1098                 mConnectRoleChangeInfoToSetOnTransition = null;
1099             }
1100         }
1101     }
1102 
1103     /** Called by a ClientModeImpl owned by this CMM informing it has fully stopped. */
onClientModeImplQuit()1104     public void onClientModeImplQuit() {
1105         cleanupOnQuitIfApplicable();
1106     }
1107 
1108     /**
1109      * Only clean up this CMM once the CMM and all associated ClientModeImpls have been stopped.
1110      * This is necessary because ClientModeImpl sends broadcasts during stop, and the role must
1111      * remain primary for {@link ClientModeManagerBroadcastQueue} to send them out.
1112      */
cleanupOnQuitIfApplicable()1113     private void cleanupOnQuitIfApplicable() {
1114         if (mIsStopped && mGraveyard.hasAllClientModeImplsQuit()) {
1115             mPreviousRole = mRole;
1116             mLastRoleChangeSinceBootMs = mClock.getElapsedSinceBootMillis();
1117             mRole = null;
1118             // only call onStopped() after role has been reset to null since ActiveModeWarden
1119             // expects the CMM to be fully stopped before onStopped().
1120             mModeListener.onStopped(ConcreteClientModeManager.this);
1121 
1122             // reset to false so that onStopped() won't be triggered again.
1123             mIsStopped = false;
1124         }
1125     }
1126 
1127     @Override
syncGetWifiState()1128     public int syncGetWifiState() {
1129         return mWifiState.get();
1130     }
1131 
1132     @NonNull
getClientMode()1133     private ClientMode getClientMode() {
1134         if (mClientModeImpl != null) {
1135             return mClientModeImpl;
1136         }
1137         if (mScanOnlyModeImpl != null) {
1138             return mScanOnlyModeImpl;
1139         }
1140         return mDefaultClientModeManager;
1141     }
1142 
1143     /*
1144      * Note: These are simple wrappers over methods to {@link ClientModeImpl}.
1145      */
1146 
1147     @Override
connectNetwork(NetworkUpdateResult result, ActionListenerWrapper wrapper, int callingUid)1148     public void connectNetwork(NetworkUpdateResult result, ActionListenerWrapper wrapper,
1149             int callingUid) {
1150         getClientMode().connectNetwork(result, wrapper, callingUid);
1151     }
1152 
1153     @Override
saveNetwork(NetworkUpdateResult result, ActionListenerWrapper wrapper, int callingUid)1154     public void saveNetwork(NetworkUpdateResult result, ActionListenerWrapper wrapper,
1155             int callingUid) {
1156         getClientMode().saveNetwork(result, wrapper, callingUid);
1157     }
1158 
1159     @Override
disconnect()1160     public void disconnect() {
1161         getClientMode().disconnect();
1162     }
1163 
1164     @Override
reconnect(WorkSource ws)1165     public void reconnect(WorkSource ws) {
1166         getClientMode().reconnect(ws);
1167     }
1168 
1169     @Override
reassociate()1170     public void reassociate() {
1171         getClientMode().reassociate();
1172     }
1173 
1174     @Override
startConnectToNetwork(int networkId, int uid, String bssid)1175     public void startConnectToNetwork(int networkId, int uid, String bssid) {
1176         getClientMode().startConnectToNetwork(networkId, uid, bssid);
1177     }
1178 
1179     @Override
startRoamToNetwork(int networkId, String bssid)1180     public void startRoamToNetwork(int networkId, String bssid) {
1181         getClientMode().startRoamToNetwork(networkId, bssid);
1182     }
1183 
1184     @Override
setWifiConnectedNetworkScorer( IBinder binder, IWifiConnectedNetworkScorer scorer)1185     public boolean setWifiConnectedNetworkScorer(
1186             IBinder binder, IWifiConnectedNetworkScorer scorer) {
1187         return getClientMode().setWifiConnectedNetworkScorer(binder, scorer);
1188     }
1189 
1190     @Override
clearWifiConnectedNetworkScorer()1191     public void clearWifiConnectedNetworkScorer() {
1192         getClientMode().clearWifiConnectedNetworkScorer();
1193     }
1194 
1195     @Override
resetSimAuthNetworks(@lientModeImpl.ResetSimReason int resetReason)1196     public void resetSimAuthNetworks(@ClientModeImpl.ResetSimReason int resetReason) {
1197         getClientMode().resetSimAuthNetworks(resetReason);
1198     }
1199 
1200     @Override
onBluetoothConnectionStateChanged()1201     public void onBluetoothConnectionStateChanged() {
1202         getClientMode().onBluetoothConnectionStateChanged();
1203     }
1204 
1205     @Override
syncRequestConnectionInfo()1206     public WifiInfo syncRequestConnectionInfo() {
1207         return getClientMode().syncRequestConnectionInfo();
1208     }
1209 
1210     @Override
syncQueryPasspointIcon(long bssid, String fileName)1211     public boolean syncQueryPasspointIcon(long bssid, String fileName) {
1212         return getClientMode().syncQueryPasspointIcon(bssid, fileName);
1213     }
1214 
1215     @Override
syncGetCurrentNetwork()1216     public Network syncGetCurrentNetwork() {
1217         return getClientMode().syncGetCurrentNetwork();
1218     }
1219 
1220     @Override
syncGetDhcpResultsParcelable()1221     public DhcpResultsParcelable syncGetDhcpResultsParcelable() {
1222         return getClientMode().syncGetDhcpResultsParcelable();
1223     }
1224 
1225     @Override
getSupportedFeatures()1226     public long getSupportedFeatures() {
1227         return getClientMode().getSupportedFeatures();
1228     }
1229 
1230     @Override
syncStartSubscriptionProvisioning(int callingUid, OsuProvider provider, IProvisioningCallback callback)1231     public boolean syncStartSubscriptionProvisioning(int callingUid, OsuProvider provider,
1232             IProvisioningCallback callback) {
1233         return getClientMode().syncStartSubscriptionProvisioning(
1234                 callingUid, provider, callback);
1235     }
1236 
1237     @Override
isWifiStandardSupported(@ifiAnnotations.WifiStandard int standard)1238     public boolean isWifiStandardSupported(@WifiAnnotations.WifiStandard int standard) {
1239         return getClientMode().isWifiStandardSupported(standard);
1240     }
1241 
1242     @Override
enableTdls(String remoteMacAddress, boolean enable)1243     public void enableTdls(String remoteMacAddress, boolean enable) {
1244         getClientMode().enableTdls(remoteMacAddress, enable);
1245     }
1246 
1247     @Override
dumpIpClient(FileDescriptor fd, PrintWriter pw, String[] args)1248     public void dumpIpClient(FileDescriptor fd, PrintWriter pw, String[] args) {
1249         getClientMode().dumpIpClient(fd, pw, args);
1250     }
1251 
1252     @Override
dumpWifiScoreReport(FileDescriptor fd, PrintWriter pw, String[] args)1253     public void dumpWifiScoreReport(FileDescriptor fd, PrintWriter pw, String[] args) {
1254         getClientMode().dumpWifiScoreReport(fd, pw, args);
1255     }
1256 
1257     @Override
enableVerboseLogging(boolean verbose)1258     public void enableVerboseLogging(boolean verbose) {
1259         mVerboseLoggingEnabled = verbose;
1260         getClientMode().enableVerboseLogging(verbose);
1261     }
1262 
1263     @Override
getFactoryMacAddress()1264     public String getFactoryMacAddress() {
1265         return getClientMode().getFactoryMacAddress();
1266     }
1267 
1268     @Override
getConnectedWifiConfiguration()1269     public WifiConfiguration getConnectedWifiConfiguration() {
1270         return getClientMode().getConnectedWifiConfiguration();
1271     }
1272 
1273     @Override
getConnectingWifiConfiguration()1274     public WifiConfiguration getConnectingWifiConfiguration() {
1275         return getClientMode().getConnectingWifiConfiguration();
1276     }
1277 
1278     @Override
getConnectedBssid()1279     public String getConnectedBssid() {
1280         return getClientMode().getConnectedBssid();
1281     }
1282 
1283     @Override
getConnectingBssid()1284     public String getConnectingBssid() {
1285         return getClientMode().getConnectingBssid();
1286     }
1287 
1288     @Override
getWifiLinkLayerStats()1289     public WifiLinkLayerStats getWifiLinkLayerStats() {
1290         return getClientMode().getWifiLinkLayerStats();
1291     }
1292 
1293     @Override
setPowerSave(boolean ps)1294     public boolean setPowerSave(boolean ps) {
1295         return getClientMode().setPowerSave(ps);
1296     }
1297 
1298     @Override
setLowLatencyMode(boolean enabled)1299     public boolean setLowLatencyMode(boolean enabled) {
1300         return getClientMode().setLowLatencyMode(enabled);
1301     }
1302 
1303     @Override
getMcastLockManagerFilterController()1304     public WifiMulticastLockManager.FilterController getMcastLockManagerFilterController() {
1305         return getClientMode().getMcastLockManagerFilterController();
1306     }
1307 
1308     @Override
isConnected()1309     public boolean isConnected() {
1310         return getClientMode().isConnected();
1311     }
1312 
1313     @Override
isConnecting()1314     public boolean isConnecting() {
1315         return getClientMode().isConnecting();
1316     }
1317 
1318     @Override
isRoaming()1319     public boolean isRoaming() {
1320         return getClientMode().isRoaming();
1321     }
1322 
1323     @Override
isDisconnected()1324     public boolean isDisconnected() {
1325         return getClientMode().isDisconnected();
1326     }
1327 
1328     @Override
isSupplicantTransientState()1329     public boolean isSupplicantTransientState() {
1330         return getClientMode().isSupplicantTransientState();
1331     }
1332 
1333     @Override
onCellularConnectivityChanged(@ifiDataStall.CellularDataStatusCode int status)1334     public void onCellularConnectivityChanged(@WifiDataStall.CellularDataStatusCode int status) {
1335         getClientMode().onCellularConnectivityChanged(status);
1336     }
1337 
1338     @Override
probeLink(LinkProbeCallback callback, int mcs)1339     public void probeLink(LinkProbeCallback callback, int mcs) {
1340         getClientMode().probeLink(callback, mcs);
1341     }
1342 
1343     @Override
sendMessageToClientModeImpl(Message msg)1344     public void sendMessageToClientModeImpl(Message msg) {
1345         getClientMode().sendMessageToClientModeImpl(msg);
1346     }
1347 
1348     @Override
getId()1349     public long getId() {
1350         return mId;
1351     }
1352 
1353     @Override
setMboCellularDataStatus(boolean available)1354     public void setMboCellularDataStatus(boolean available) {
1355         getClientMode().setMboCellularDataStatus(available);
1356     }
1357 
1358     @Override
getRoamingCapabilities()1359     public WifiNative.RoamingCapabilities getRoamingCapabilities() {
1360         return getClientMode().getRoamingCapabilities();
1361     }
1362 
1363     @Override
configureRoaming(WifiNative.RoamingConfig config)1364     public boolean configureRoaming(WifiNative.RoamingConfig config) {
1365         return getClientMode().configureRoaming(config);
1366     }
1367 
1368     @Override
enableRoaming(boolean enabled)1369     public boolean enableRoaming(boolean enabled) {
1370         return getClientMode().enableRoaming(enabled);
1371     }
1372 
1373     @Override
setCountryCode(String countryCode)1374     public boolean setCountryCode(String countryCode) {
1375         return getClientMode().setCountryCode(countryCode);
1376     }
1377 
1378     @Override
getTxPktFates()1379     public List<TxFateReport> getTxPktFates() {
1380         return getClientMode().getTxPktFates();
1381     }
1382 
1383     @Override
getRxPktFates()1384     public List<RxFateReport> getRxPktFates() {
1385         return getClientMode().getRxPktFates();
1386     }
1387 
1388     @Override
getDeviceWiphyCapabilities()1389     public DeviceWiphyCapabilities getDeviceWiphyCapabilities() {
1390         return getClientMode().getDeviceWiphyCapabilities();
1391     }
1392 
1393     @Override
requestAnqp(String bssid, Set<Integer> anqpIds, Set<Integer> hs20Subtypes)1394     public boolean requestAnqp(String bssid, Set<Integer> anqpIds, Set<Integer> hs20Subtypes) {
1395         return getClientMode().requestAnqp(bssid, anqpIds, hs20Subtypes);
1396     }
1397 
1398     @Override
requestVenueUrlAnqp(String bssid)1399     public boolean requestVenueUrlAnqp(String bssid) {
1400         return getClientMode().requestVenueUrlAnqp(bssid);
1401     }
1402 
1403     @Override
requestIcon(String bssid, String fileName)1404     public boolean requestIcon(String bssid, String fileName) {
1405         return getClientMode().requestIcon(bssid, fileName);
1406     }
1407 
1408     @Override
setShouldReduceNetworkScore(boolean shouldReduceNetworkScore)1409     public void setShouldReduceNetworkScore(boolean shouldReduceNetworkScore) {
1410         mShouldReduceNetworkScore = shouldReduceNetworkScore;
1411         getClientMode().setShouldReduceNetworkScore(shouldReduceNetworkScore);
1412     }
1413 
1414     @Override
toString()1415     public String toString() {
1416         return "ConcreteClientModeManager{id=" + getId()
1417                 + " iface=" + getInterfaceName()
1418                 + " role=" + getRole()
1419                 + "}";
1420     }
1421 }
1422