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