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