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