• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2016 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.server.wifi;
18 
19 import static android.net.wifi.WifiManager.WIFI_STATE_ENABLED;
20 import static android.net.wifi.WifiManager.WIFI_STATE_ENABLING;
21 
22 import android.annotation.NonNull;
23 import android.annotation.Nullable;
24 import android.content.Context;
25 import android.content.Intent;
26 import android.net.ConnectivityManager;
27 import android.net.ConnectivityManager.NetworkCallback;
28 import android.net.DhcpResultsParcelable;
29 import android.net.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                 } else {
411                     mStateMachine.sendMessage(
412                             ClientModeStateMachine.CMD_SWITCH_TO_SCAN_ONLY_MODE_CONTINUE,
413                             mTargetRoleChangeInfo);
414                     mWifiMetrics.noteWifiOff(mIsDeferring, isTimedOut, deferringDurationMillis);
415                 }
416             } else {
417                 updateConnectModeState(mRole, WifiManager.WIFI_STATE_ENABLED,
418                         WifiManager.WIFI_STATE_DISABLING);
419             }
420 
421             if (!mIsDeferring) return;
422 
423             Log.d(getTag(), "Stop DeferWifiOff handler.");
424             removeCallbacks(mRunnable);
425             if (mImsMmTelManager != null) {
426                 try {
427                     mImsMmTelManager.unregisterImsRegistrationCallback(mImsRegistrationCallback);
428                 } catch (RuntimeException e) {
429                     Log.e(getTag(), "unregisterImsRegistrationCallback failed", e);
430                 }
431             }
432 
433             if (mConnectivityManager != null && mImsNetworks.size() > 0) {
434                 for (ImsNetworkCallback c: mImsNetworks) {
435                     mConnectivityManager.unregisterNetworkCallback(c);
436                 }
437                 mImsNetworks.clear();
438             }
439 
440             mIsDeferring = false;
441             mIsImsNetworkUnregistered = false;
442         }
443     }
444 
isAnyImsServiceOverWlanAvailable(int subId)445     private boolean isAnyImsServiceOverWlanAvailable(int subId) {
446         ImsMmTelManager imsMmTelManager = ImsMmTelManager.createForSubscriptionId(subId);
447         try {
448             int[] possibleServiceOverWlan = new int[] {
449                 MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE,
450                 MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VIDEO,
451                 MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_UT,
452                 MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_SMS,
453                 MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_CALL_COMPOSER,
454             };
455             for (int i: possibleServiceOverWlan) {
456                 if (imsMmTelManager.isAvailable(i,
457                         ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN)) {
458                     return true;
459                 }
460             }
461         } catch (RuntimeException ex) {
462             Log.e(TAG, "IMS Manager is not available.", ex);
463         }
464         return false;
465     }
466 
467     /**
468      * Get deferring time before turning off WiFi.
469      */
getWifiOffDeferringTimeMs()470     private int getWifiOffDeferringTimeMs() {
471         SubscriptionManager subscriptionManager =
472                 mContext.getSystemService(SubscriptionManager.class);
473         if (subscriptionManager == null) {
474             Log.d(getTag(), "SubscriptionManager not found");
475             return 0;
476         }
477 
478         List<SubscriptionInfo> subInfoList = subscriptionManager
479                 .getCompleteActiveSubscriptionInfoList();
480         if (subInfoList == null) {
481             Log.d(getTag(), "Active SubscriptionInfo list not found");
482             return 0;
483         }
484 
485         // Get the maximum delay for the active subscription latched on IWLAN.
486         int maxDelay = 0;
487         for (SubscriptionInfo subInfo : subInfoList) {
488             int curDelay = getWifiOffDeferringTimeMs(subInfo.getSubscriptionId());
489             if (curDelay > maxDelay) {
490                 maxDelay = curDelay;
491                 mActiveSubId = subInfo.getSubscriptionId();
492             }
493         }
494         return maxDelay;
495     }
496 
getWifiOffDeferringTimeMs(int subId)497     private int getWifiOffDeferringTimeMs(int subId) {
498         if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
499             Log.d(getTag(), "Invalid Subscription ID: " + subId);
500             return 0;
501         }
502 
503         // If no IMS service over WLAN, no delay
504         if (!isAnyImsServiceOverWlanAvailable(subId)) {
505             Log.d(getTag(), "IMS not registered over IWLAN for subId: " + subId);
506             return 0;
507         }
508 
509         CarrierConfigManager configManager = mContext.getSystemService(CarrierConfigManager.class);
510         PersistableBundle config = configManager.getConfigForSubId(subId);
511         return (config != null)
512                 ? config.getInt(CarrierConfigManager.Ims.KEY_WIFI_OFF_DEFERRING_TIME_MILLIS_INT)
513                 : 0;
514     }
515 
516     @Override
getRole()517     @Nullable public ClientRole getRole() {
518         return mRole;
519     }
520 
521     /**
522      * Get the role this ClientModeManager is expected to become.
523      */
getTargetRole()524     @Nullable public ClientRole getTargetRole() {
525         return mTargetRoleChangeInfo == null ? null : mTargetRoleChangeInfo.role;
526     }
527 
528     @Override
getPreviousRole()529     @Nullable public ClientRole getPreviousRole() {
530         return mPreviousRole;
531     }
532 
533     @Override
getLastRoleChangeSinceBootMs()534     public long getLastRoleChangeSinceBootMs() {
535         return mLastRoleChangeSinceBootMs;
536     }
537 
538     /**
539      * Class to hold info needed for role change.
540      */
541     private static class RoleChangeInfo {
542         @Nullable public final ClientRole role;
543         @Nullable public final WorkSource requestorWs;
544         @Nullable public final Listener<ConcreteClientModeManager> modeListener;
545 
RoleChangeInfo(@ullable ClientRole role)546         RoleChangeInfo(@Nullable ClientRole role) {
547             this(role, null, null);
548         }
549 
RoleChangeInfo(@ullable ClientRole role, @Nullable WorkSource requestorWs, @Nullable Listener<ConcreteClientModeManager> modeListener)550         RoleChangeInfo(@Nullable ClientRole role, @Nullable WorkSource requestorWs,
551                 @Nullable Listener<ConcreteClientModeManager> modeListener) {
552             this.role = role;
553             this.requestorWs = requestorWs;
554             this.modeListener = modeListener;
555         }
556 
557         @Override
toString()558         public String toString() {
559             return "Role: " + role + ", RequestorWs: " + requestorWs
560                     + ", ModeListener: " + modeListener;
561         }
562     }
563 
564     /** Set the role of this ClientModeManager */
setRole(@onNull ClientRole role, @NonNull WorkSource requestorWs)565     public void setRole(@NonNull ClientRole role, @NonNull WorkSource requestorWs) {
566         setRole(role, requestorWs, null);
567     }
568 
569     /** Set the role of this ClientModeManager */
setRole(@onNull ClientRole role, @NonNull WorkSource requestorWs, @Nullable Listener<ConcreteClientModeManager> modeListener)570     public void setRole(@NonNull ClientRole role, @NonNull WorkSource requestorWs,
571             @Nullable Listener<ConcreteClientModeManager> modeListener) {
572         mTargetRoleChangeInfo = new RoleChangeInfo(role, requestorWs, modeListener);
573         if (role == ROLE_CLIENT_SCAN_ONLY) {
574             // Switch client mode manager to scan only mode.
575             mStateMachine.sendMessage(
576                     ClientModeStateMachine.CMD_SWITCH_TO_SCAN_ONLY_MODE);
577         } else {
578             // Switch client mode manager to connect mode.
579             mStateMachine.sendMessage(
580                     ClientModeStateMachine.CMD_SWITCH_TO_CONNECT_MODE,
581                     mTargetRoleChangeInfo);
582         }
583     }
584 
585     @Override
getInterfaceName()586     public String getInterfaceName() {
587         return mClientInterfaceName;
588     }
589 
590     @Override
getRequestorWs()591     public WorkSource getRequestorWs() {
592         return mRequestorWs;
593     }
594 
595     /**
596      * Keep stopped {@link ClientModeImpl} instances so that they can be dumped to aid debugging.
597      *
598      * TODO(b/160283853): Find a smarter way to evict old ClientModeImpls
599      */
600     private static class Graveyard {
601         private static final int INSTANCES_TO_KEEP = 3;
602 
603         private final ArrayDeque<ClientModeImpl> mClientModeImpls = new ArrayDeque<>();
604 
605         /**
606          * Add this stopped {@link ClientModeImpl} to the graveyard, and evict the oldest
607          * ClientModeImpl if the graveyard is full.
608          */
inter(ClientModeImpl clientModeImpl)609         void inter(ClientModeImpl clientModeImpl) {
610             if (mClientModeImpls.size() == INSTANCES_TO_KEEP) {
611                 mClientModeImpls.removeFirst();
612             }
613             mClientModeImpls.addLast(clientModeImpl);
614         }
615 
616         /** Dump the contents of the graveyard. */
dump(FileDescriptor fd, PrintWriter pw, String[] args)617         void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
618             pw.println("Dump of ConcreteClientModeManager.Graveyard");
619             pw.println("Stopped ClientModeImpls: " + mClientModeImpls.size() + " total");
620             for (ClientModeImpl clientModeImpl : mClientModeImpls) {
621                 clientModeImpl.dump(fd, pw, args);
622             }
623             pw.println();
624         }
625 
hasAllClientModeImplsQuit()626         boolean hasAllClientModeImplsQuit() {
627             for (ClientModeImpl cmi : mClientModeImpls) {
628                 if (!cmi.hasQuit()) return false;
629             }
630             return true;
631         }
632     }
633 
634     /**
635      * Dump info about this ClientMode manager.
636      */
637     @Override
dump(FileDescriptor fd, PrintWriter pw, String[] args)638     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
639         pw.println("Dump of ClientModeManager id=" + mId);
640         pw.println("current StateMachine mode: " + getCurrentStateName());
641         pw.println("mRole: " + mRole);
642         pw.println("mPreviousRole: " + mPreviousRole);
643         pw.println("mTargetRoleChangeInfo: " + mTargetRoleChangeInfo);
644         pw.println("mClientInterfaceName: " + mClientInterfaceName);
645         pw.println("mIfaceIsUp: " + mIfaceIsUp);
646         pw.println("mSecondaryInternet: " + mSecondaryInternet);
647         pw.println("mIsDbs: " + mIsDbs);
648         mStateMachine.dump(fd, pw, args);
649         pw.println();
650         if (mClientModeImpl == null) {
651             pw.println("No active ClientModeImpl instance");
652         } else {
653             mClientModeImpl.dump(fd, pw, args);
654         }
655         mGraveyard.dump(fd, pw, args);
656         pw.println();
657     }
658 
getCurrentStateName()659     private String getCurrentStateName() {
660         IState currentState = mStateMachine.getCurrentState();
661 
662         if (currentState != null) {
663             return currentState.getName();
664         }
665 
666         return "StateMachine not active";
667     }
668 
669     /**
670      * Update Wifi state and send the broadcast.
671      *
672      * @param role         Target/Set role for this client mode manager instance.
673      * @param newState     new Wifi state
674      * @param currentState current wifi state
675      */
updateConnectModeState(ClientRole role, int newState, int currentState)676     private void updateConnectModeState(ClientRole role, int newState, int currentState) {
677         if (role != ROLE_CLIENT_PRIMARY || !mWifiStateChangeBroadcastEnabled) {
678             // do not raise public broadcast unless this is the primary client mode manager
679             return;
680         }
681         // TODO(b/186881160): May need to restore per STA state for Battery state reported.
682         mWifiInjector.getActiveModeWarden().setWifiStateForApiCalls(newState);
683         if (newState == WifiManager.WIFI_STATE_UNKNOWN) {
684             // do not need to broadcast failure to system
685             return;
686         }
687 
688         // TODO(b/175839153): this broadcast should only be sent out when wifi is toggled on/off,
689         //  not per CMM
690         final Intent intent = new Intent(WifiManager.WIFI_STATE_CHANGED_ACTION);
691         intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
692         intent.putExtra(WifiManager.EXTRA_WIFI_STATE, newState);
693         intent.putExtra(WifiManager.EXTRA_PREVIOUS_WIFI_STATE, currentState);
694         String summary = "broadcast=WIFI_STATE_CHANGED_ACTION"
695                 + " EXTRA_WIFI_STATE=" + newState
696                 + " EXTRA_PREVIOUS_WIFI_STATE=" + currentState;
697         if (mVerboseLoggingEnabled) Log.d(getTag(), "Queuing " + summary);
698         ClientModeManagerBroadcastQueue.QueuedBroadcast broadcast =
699                 () -> {
700                     if (mVerboseLoggingEnabled) Log.d(getTag(), "Sending " + summary);
701                     mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
702                 };
703         if (mRole == null && role == ROLE_CLIENT_PRIMARY) {
704             // This CMM is intended to be the primary, but has not completed the mode transition
705             // yet. Need to force broadcast to be sent.
706             broadcast.send();
707         } else {
708             mBroadcastQueue.queueOrSendBroadcast(this, broadcast);
709         }
710     }
711 
712     private class ClientModeStateMachine extends StateMachine {
713         // Commands for the state machine.
714         public static final int CMD_START = 0;
715         public static final int CMD_SWITCH_TO_SCAN_ONLY_MODE = 1;
716         public static final int CMD_SWITCH_TO_CONNECT_MODE = 2;
717         public static final int CMD_INTERFACE_STATUS_CHANGED = 3;
718         public static final int CMD_INTERFACE_DESTROYED = 4;
719         public static final int CMD_INTERFACE_DOWN = 5;
720         public static final int CMD_SWITCH_TO_SCAN_ONLY_MODE_CONTINUE = 6;
721         public static final int CMD_INTERFACE_ADDED = 7;
722         private final State mIdleState;
723         private final State mStartedState;
724         private final State mScanOnlyModeState;
725         private final State mConnectModeState;
726         // Workaround since we cannot use transitionTo(mScanOnlyModeState, RoleChangeInfo)
727         private RoleChangeInfo mScanRoleChangeInfoToSetOnTransition = null;
728         // Workaround since we cannot use transitionTo(mConnectModeState, RoleChangeInfo)
729         private RoleChangeInfo mConnectRoleChangeInfoToSetOnTransition = null;
730 
731         @Nullable
732         private StateMachineObituary mObituary = null;
733 
734         private final InterfaceEventCallback mWifiNativeInterfaceEventCallback =
735                 new InterfaceEventCallback() {
736 
737             boolean mEnabling = false;
738 
739             @Override
740             public void onInterfaceLinkStateChanged(String ifaceName, boolean isLinkUp) {
741                 Log.d("InterfaceEventCallback",
742                         "onInterfaceLinkStateChanged, ifaceName=" + ifaceName + " up="
743                                 + isLinkUp + " CurrentState=" + getCurrentStateName());
744                 if (isLinkUp) {
745                     mEnabling = false;
746                 }
747             }
748 
749             @Override
750             public void onInterfaceAdded(String ifaceName) {
751                 Log.d("InterfaceEventCallback",
752                         "onInterfaceAdded, ifaceName=" + ifaceName
753                                 + " CurrentState=" + getCurrentStateName());
754                 if (mStateMachine.getCurrentState() == null) {
755                     Log.d(TAG, "StateMachine not active, trigger ifaceAddedDetected");
756                     mSelfRecovery.trigger(SelfRecovery.REASON_IFACE_ADDED);
757                 } else if (!mEnabling) {
758                     Log.d("InterfaceEventCallback", "send CMD_INTERFACE_ADDED");
759                     mStateMachine.sendMessage(CMD_INTERFACE_ADDED);
760                     mEnabling = true;
761                 } else {
762                     Log.d("InterfaceEventCallback", "wifi already in the start");
763                 }
764             }
765         };
766 
767         private final InterfaceCallback mWifiNativeInterfaceCallback = new InterfaceCallback() {
768             @Override
769             public void onDestroyed(String ifaceName) {
770                 if (mClientInterfaceName != null && mClientInterfaceName.equals(ifaceName)) {
771                     Log.d(getTag(), "STA iface " + ifaceName + " was destroyed, "
772                             + "stopping client mode");
773 
774                     // we must immediately clean up state in ClientModeImpl to unregister
775                     // all client mode related objects
776                     // Note: onDestroyed is only called from the main Wifi thread
777                     if (mClientModeImpl == null) {
778                         Log.w(getTag(), "Received mWifiNativeInterfaceCallback.onDestroyed "
779                                 + "callback when no ClientModeImpl instance is active.");
780                     } else {
781                         mClientModeImpl.handleIfaceDestroyed();
782                     }
783 
784                     // set it to null since the interface had been destroyed
785                     mClientInterfaceName = null;
786                     sendMessage(CMD_INTERFACE_DESTROYED);
787                 }
788             }
789 
790             @Override
791             public void onUp(String ifaceName) {
792                 if (mClientInterfaceName != null && mClientInterfaceName.equals(ifaceName)) {
793                     sendMessage(CMD_INTERFACE_STATUS_CHANGED, 1);
794                 }
795             }
796 
797             @Override
798             public void onDown(String ifaceName) {
799                 if (mClientInterfaceName != null && mClientInterfaceName.equals(ifaceName)) {
800                     sendMessage(CMD_INTERFACE_STATUS_CHANGED, 0);
801                 }
802             }
803         };
804 
ClientModeStateMachine(Looper looper)805         ClientModeStateMachine(Looper looper) {
806             super(TAG, looper);
807             final int threshold = mContext.getResources().getInteger(
808                     R.integer.config_wifiConfigurationWifiRunnerThresholdInMs);
809             mIdleState = new IdleState(threshold);
810             mStartedState = new StartedState(threshold);
811             mScanOnlyModeState = new ScanOnlyModeState(threshold);
812             mConnectModeState = new ConnectModeState(threshold);
813             // CHECKSTYLE:OFF IndentationCheck
814             addState(mIdleState);
815                 addState(mStartedState, mIdleState);
816                     addState(mScanOnlyModeState, mStartedState);
817                     addState(mConnectModeState, mStartedState);
818             // CHECKSTYLE:ON IndentationCheck
819 
820             setInitialState(mIdleState);
821             start();
822         }
823 
captureObituaryAndQuitNow()824         void captureObituaryAndQuitNow() {
825             // capture StateMachine LogRecs since we will lose them after we call quitNow()
826             // This is used for debugging.
827             mObituary = new StateMachineObituary(this);
828 
829             quitNow();
830         }
831 
832         @Override
dump(FileDescriptor fd, PrintWriter pw, String[] args)833         public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
834             if (mObituary == null) {
835                 // StateMachine hasn't quit yet, dump `this` via StateMachineObituary's dump()
836                 // method for consistency with `else` branch.
837                 new StateMachineObituary(this).dump(fd, pw, args);
838             } else {
839                 // StateMachine has quit and cleared all LogRecs.
840                 // Get them from the obituary instead.
841                 mObituary.dump(fd, pw, args);
842             }
843         }
844 
845         /**
846          * Return the additional string to be logged by LogRec.
847          *
848          * @param msg that was processed
849          * @return information to be logged as a String
850          */
851         @Override
getLogRecString(Message msg)852         protected String getLogRecString(Message msg) {
853             StringBuilder sb = new StringBuilder();
854             sb.append(msg.arg1)
855                     .append(" ").append(msg.arg2);
856             if (msg.obj != null) {
857                 sb.append(" ").append(msg.obj);
858             }
859             return sb.toString();
860         }
861 
862         /**
863          * Convert the |what| field in logs from int to String.
864          */
865         @Override
getWhatToString(int what)866         protected String getWhatToString(int what) {
867             switch (what) {
868                 case CMD_START:
869                     return "CMD_START";
870                 case CMD_SWITCH_TO_SCAN_ONLY_MODE:
871                     return "CMD_SWITCH_TO_SCAN_ONLY_MODE";
872                 case CMD_SWITCH_TO_CONNECT_MODE:
873                     return "CMD_SWITCH_TO_CONNECT_MODE";
874                 case CMD_INTERFACE_STATUS_CHANGED:
875                     return "CMD_INTERFACE_STATUS_CHANGED";
876                 case CMD_INTERFACE_DESTROYED:
877                     return "CMD_INTERFACE_DESTROYED";
878                 case CMD_INTERFACE_DOWN:
879                     return "CMD_INTERFACE_DOWN";
880                 case CMD_SWITCH_TO_SCAN_ONLY_MODE_CONTINUE:
881                     return "CMD_SWITCH_TO_SCAN_ONLY_MODE_CONTINUE";
882                 case RunnerState.STATE_ENTER_CMD:
883                     return "Enter";
884                 case RunnerState.STATE_EXIT_CMD:
885                     return "Exit";
886                 default:
887                     return "what:" + what;
888             }
889         }
890 
891         /**
892          * Reset this ConcreteClientModeManager when its role changes, so that it can be reused for
893          * another purpose.
894          */
reset()895         private void reset() {
896             // Therefore, the caller must ensure that the role change has been completed and these
897             // settings have already reset before setting them, otherwise the new setting would be
898             // lost.
899             setShouldReduceNetworkScore(false);
900         }
901 
setRoleInternal(@onNull RoleChangeInfo roleChangeInfo)902         private void setRoleInternal(@NonNull RoleChangeInfo roleChangeInfo) {
903             mPreviousRole = mRole;
904             mLastRoleChangeSinceBootMs = mClock.getElapsedSinceBootMillis();
905             mRole = roleChangeInfo.role;
906             if (roleChangeInfo.requestorWs != null) {
907                 mRequestorWs = roleChangeInfo.requestorWs;
908             }
909             if (roleChangeInfo.modeListener != null) {
910                 mModeListener = roleChangeInfo.modeListener;
911             }
912         }
913 
setRoleInternalAndInvokeCallback(@onNull RoleChangeInfo roleChangeInfo)914         private void setRoleInternalAndInvokeCallback(@NonNull RoleChangeInfo roleChangeInfo) {
915             if (roleChangeInfo.role == mRole) return;
916             if (mRole == null) {
917                 if (mVerboseLoggingEnabled) {
918                     Log.v(getTag(), "CurState:" + getCurrentStateName()
919                             + ", clientModeManager started in role: " + roleChangeInfo);
920                 }
921                 setRoleInternal(roleChangeInfo);
922                 mModeListener.onStarted(ConcreteClientModeManager.this);
923             } else {
924                 if (mVerboseLoggingEnabled) {
925                     Log.v(getTag(), "CurState:" + getCurrentStateName()
926                             + ", clientModeManager role changed: " + roleChangeInfo);
927                 }
928                 setRoleInternal(roleChangeInfo);
929                 reset();
930                 mModeListener.onRoleChanged(ConcreteClientModeManager.this);
931             }
932             if (mClientModeImpl != null) {
933                 mClientModeImpl.onRoleChanged();
934             }
935         }
936 
937         private class IdleState extends RunnerState {
IdleState(int threshold)938             IdleState(int threshold) {
939                 super(threshold, mWifiInjector.getWifiHandlerLocalLog());
940             }
941 
942             @Override
enterImpl()943             public void enterImpl() {
944                 Log.d(getTag(), "entering IdleState");
945                 mClientInterfaceName = null;
946                 mIfaceIsUp = false;
947             }
948 
949             @Override
exitImpl()950             public void exitImpl() {
951                 // Sometimes the wifi handler thread may become blocked that the statemachine
952                 // will exit in the IdleState without first entering StartedState. Trigger a
953                 // cleanup here in case the above sequence happens. This the statemachine was
954                 // started normally this will will not send a duplicate broadcast since mIsStopped
955                 // will get set to false the first time the exit happens.
956                 cleanupOnQuitIfApplicable();
957                 Log.d(getTag(), "IdleState.exit()");
958             }
959 
960             @Override
getMessageLogRec(int what)961             String getMessageLogRec(int what) {
962                 return ConcreteClientModeManager.class.getSimpleName() + "."
963                         + IdleState.class.getSimpleName() + "."
964                         + getWhatToString(what);
965             }
966 
967             @Override
processMessageImpl(Message message)968             public boolean processMessageImpl(Message message) {
969                 if (mVerboseLoggingEnabled) {
970                     Log.d(getTag(),
971                             getName() + " cmd = " + getWhatToString(message.what) + " "
972                                     + message.toString());
973                 }
974                 switch (message.what) {
975                     case CMD_START:
976                         // Always start in scan mode first.
977                         RoleChangeInfo roleChangeInfo = (RoleChangeInfo) message.obj;
978                         mClientInterfaceName = mWifiNative.setupInterfaceForClientInScanMode(
979                                 mWifiNativeInterfaceCallback, roleChangeInfo.requestorWs,
980                                 ConcreteClientModeManager.this);
981                         if (TextUtils.isEmpty(mClientInterfaceName)) {
982                             Log.e(getTag(), "Failed to create ClientInterface. Sit in Idle");
983                             takeBugReportInterfaceFailureIfNeeded(
984                                     "Wi-Fi BugReport (scan STA interface failure):",
985                                     "Failed to create client interface in idle state");
986                             mModeListener.onStartFailure(ConcreteClientModeManager.this);
987                             break;
988                         }
989                         mWifiNative.setWifiNativeInterfaceEventCallback(
990                                 mWifiNativeInterfaceEventCallback);
991                         if (roleChangeInfo.role instanceof ClientConnectivityRole) {
992                             sendMessage(CMD_SWITCH_TO_CONNECT_MODE, roleChangeInfo);
993                             transitionTo(mStartedState);
994                         } else {
995                             mScanRoleChangeInfoToSetOnTransition = roleChangeInfo;
996                             transitionTo(mScanOnlyModeState);
997                         }
998                         break;
999                     case CMD_INTERFACE_ADDED:
1000                         Log.d(getTag(), "IdleState received CMD_INTERFACE_ADDED");
1001                         mSelfRecovery.trigger(SelfRecovery.REASON_IFACE_ADDED);
1002                         break;
1003                     default:
1004                         Log.d(getTag(), getName() + ", received an invalid message: " + message);
1005                         return NOT_HANDLED;
1006                 }
1007                 return HANDLED;
1008             }
1009         }
1010 
1011         private class StartedState extends RunnerState {
StartedState(int threshold)1012             StartedState(int threshold) {
1013                 super(threshold, mWifiInjector.getWifiHandlerLocalLog());
1014             }
1015 
onUpChanged(boolean isUp)1016             private void onUpChanged(boolean isUp) {
1017                 if (isUp == mIfaceIsUp) {
1018                     return;  // no change
1019                 }
1020                 mIfaceIsUp = isUp;
1021                 if (!isUp) {
1022                     // if the interface goes down we should exit and go back to idle state.
1023                     Log.d(getTag(), getName() + ", interface down!");
1024                     mStateMachine.sendMessage(CMD_INTERFACE_DOWN);
1025                 }
1026                 if (mClientModeImpl != null) {
1027                     mClientModeImpl.onUpChanged(isUp);
1028                 }
1029             }
1030 
1031             @Override
enterImpl()1032             public void enterImpl() {
1033                 Log.d(getTag(), "entering StartedState");
1034                 mIfaceIsUp = false;
1035                 mIsStopped = false;
1036                 onUpChanged(mWifiNative.isInterfaceUp(mClientInterfaceName));
1037             }
1038 
1039             @Override
getMessageLogRec(int what)1040             String getMessageLogRec(int what) {
1041                 return ConcreteClientModeManager.class.getSimpleName() + "."
1042                         + StartedState.class.getSimpleName() + "."
1043                         + getWhatToString(what);
1044             }
1045 
1046             @Override
processMessageImpl(Message message)1047             public boolean processMessageImpl(Message message) {
1048                 if (mVerboseLoggingEnabled) {
1049                     Log.d(getTag(),
1050                             getName() + " cmd = " + getWhatToString(message.what) + " "
1051                                     + message.toString());
1052                 }
1053                 switch (message.what) {
1054                     case CMD_START:
1055                         // Already started, ignore this command.
1056                         break;
1057                     case CMD_SWITCH_TO_CONNECT_MODE: {
1058                         RoleChangeInfo roleChangeInfo = (RoleChangeInfo) message.obj;
1059                         updateConnectModeState(roleChangeInfo.role,
1060                                 WifiManager.WIFI_STATE_ENABLING,
1061                                 WifiManager.WIFI_STATE_DISABLED);
1062                         if (!mWifiNative.switchClientInterfaceToConnectivityMode(
1063                                 mClientInterfaceName, roleChangeInfo.requestorWs)) {
1064                             updateConnectModeState(roleChangeInfo.role,
1065                                     WifiManager.WIFI_STATE_UNKNOWN,
1066                                     WifiManager.WIFI_STATE_ENABLING);
1067                             updateConnectModeState(roleChangeInfo.role,
1068                                     WifiManager.WIFI_STATE_DISABLED,
1069                                     WifiManager.WIFI_STATE_UNKNOWN);
1070                             takeBugReportInterfaceFailureIfNeeded(
1071                                     "Wi-Fi BugReport (STA interface failure):",
1072                                     "Fail to switch to connection mode in started state");
1073                             mModeListener.onStartFailure(ConcreteClientModeManager.this);
1074                             break;
1075                         }
1076                         // Role set in the enter of ConnectModeState.
1077                         mConnectRoleChangeInfoToSetOnTransition = roleChangeInfo;
1078                         transitionTo(mConnectModeState);
1079                         break;
1080                     }
1081                     case CMD_SWITCH_TO_SCAN_ONLY_MODE:
1082                         updateConnectModeState(mRole, WifiManager.WIFI_STATE_DISABLING,
1083                                 WifiManager.WIFI_STATE_ENABLED);
1084                         mDeferStopHandler.start(getWifiOffDeferringTimeMs());
1085                         break;
1086                     case CMD_SWITCH_TO_SCAN_ONLY_MODE_CONTINUE: {
1087                         RoleChangeInfo roleChangeInfo = (RoleChangeInfo) message.obj;
1088                         mScanRoleChangeInfoToSetOnTransition = roleChangeInfo;
1089                         transitionTo(mScanOnlyModeState);
1090                         break;
1091                     }
1092                     case CMD_INTERFACE_DOWN:
1093                         Log.e(getTag(),
1094                                 getName() + ", detected an interface down, reporting failure to "
1095                                         + "SelfRecovery");
1096                         mSelfRecovery.trigger(SelfRecovery.REASON_STA_IFACE_DOWN);
1097                         // once interface down, nothing else to do...  stop the state machine
1098                         captureObituaryAndQuitNow();
1099                         break;
1100                     case CMD_INTERFACE_STATUS_CHANGED:
1101                         boolean isUp = message.arg1 == 1;
1102                         onUpChanged(isUp);
1103                         break;
1104                     case CMD_INTERFACE_DESTROYED:
1105                         Log.e(getTag(), getName() + ", interface destroyed - client mode stopping");
1106                         mClientInterfaceName = null;
1107                         // once interface destroyed, nothing else to do...  stop the state machine
1108                         captureObituaryAndQuitNow();
1109                         break;
1110                     default:
1111                         return NOT_HANDLED;
1112                 }
1113                 return HANDLED;
1114             }
1115 
1116             /**
1117              * Clean up state, unregister listeners and update wifi state.
1118              */
1119             @Override
exitImpl()1120             public void exitImpl() {
1121                 if (mClientInterfaceName != null) {
1122                     mWifiNative.teardownInterface(mClientInterfaceName);
1123                     mClientInterfaceName = null;
1124                     mIfaceIsUp = false;
1125                 }
1126 
1127                 Log.i(getTag(), "StartedState.exit(), setting mRole = null");
1128                 mIsStopped = true;
1129                 cleanupOnQuitIfApplicable();
1130             }
1131         }
1132 
1133         private class ScanOnlyModeState extends RunnerState {
ScanOnlyModeState(int threshold)1134             ScanOnlyModeState(int threshold) {
1135                 super(threshold, mWifiInjector.getWifiHandlerLocalLog());
1136             }
1137 
1138             @Override
enterImpl()1139             public void enterImpl() {
1140                 Log.d(getTag(), "entering ScanOnlyModeState");
1141 
1142                 if (mClientInterfaceName != null) {
1143                     mScanOnlyModeImpl = mWifiInjector.makeScanOnlyModeImpl(
1144                             mClientInterfaceName);
1145                 } else {
1146                     Log.e(getTag(), "Entered ScanOnlyModeState with a null interface name!");
1147                 }
1148 
1149                 if (mScanRoleChangeInfoToSetOnTransition == null
1150                         || (mScanRoleChangeInfoToSetOnTransition.role != ROLE_CLIENT_SCAN_ONLY)) {
1151                     Log.wtf(TAG, "Unexpected mScanRoleChangeInfoToSetOnTransition: "
1152                             + mScanRoleChangeInfoToSetOnTransition);
1153                     // Should never happen, but fallback to scan only to avoid a crash.
1154                     mScanRoleChangeInfoToSetOnTransition =
1155                             new RoleChangeInfo(ROLE_CLIENT_SCAN_ONLY);
1156                 }
1157 
1158                 setRoleInternalAndInvokeCallback(mScanRoleChangeInfoToSetOnTransition);
1159                 // If we're in ScanOnlyModeState, there is only 1 CMM. So it's ok to call
1160                 // WakeupController directly, there won't be multiple CMMs trampling over each other
1161                 mWakeupController.start();
1162                 mWifiNative.setScanMode(mClientInterfaceName, true);
1163             }
1164 
1165             @Override
getMessageLogRec(int what)1166             String getMessageLogRec(int what) {
1167                 return ConcreteClientModeManager.class.getSimpleName() + "."
1168                         + ScanOnlyModeState.class.getSimpleName() + "."
1169                         + getWhatToString(what);
1170             }
1171 
1172             @Override
processMessageImpl(Message message)1173             public boolean processMessageImpl(Message message) {
1174                 if (mVerboseLoggingEnabled) {
1175                     Log.d(getTag(),
1176                             getName() + " cmd = " + getWhatToString(message.what) + " "
1177                                     + message.toString());
1178                 }
1179                 switch (message.what) {
1180                     case CMD_SWITCH_TO_SCAN_ONLY_MODE:
1181                         // Already in scan only mode, ignore this command.
1182                         break;
1183                     default:
1184                         return NOT_HANDLED;
1185                 }
1186                 return HANDLED;
1187             }
1188 
1189             @Override
exitImpl()1190             public void exitImpl() {
1191                 mScanOnlyModeImpl = null;
1192                 mScanRoleChangeInfoToSetOnTransition = null;
1193 
1194                 // If we're in ScanOnlyModeState, there is only 1 CMM. So it's ok to call
1195                 // WakeupController directly, there won't be multiple CMMs trampling over each other
1196                 mWakeupController.stop();
1197                 mWifiNative.setScanMode(mClientInterfaceName, false);
1198             }
1199         }
1200 
1201         private class ConnectModeState extends RunnerState {
ConnectModeState(int threshold)1202             ConnectModeState(int threshold) {
1203                 super(threshold, mWifiInjector.getWifiHandlerLocalLog());
1204             }
1205 
1206             @Override
enterImpl()1207             public void enterImpl() {
1208                 Log.d(getTag(), "entering ConnectModeState, starting ClientModeImpl");
1209                 if (mClientInterfaceName == null) {
1210                     Log.e(getTag(), "Supposed to start ClientModeImpl, but iface is null!");
1211                 } else {
1212                     if (mClientModeImpl != null) {
1213                         Log.e(getTag(), "ConnectModeState.enter(): mClientModeImpl is already "
1214                                 + "instantiated?!");
1215                     }
1216                     mClientModeImpl = mWifiInjector.makeClientModeImpl(
1217                             mClientInterfaceName, ConcreteClientModeManager.this,
1218                             mVerboseLoggingEnabled);
1219                     mClientModeImpl.setShouldReduceNetworkScore(mShouldReduceNetworkScore);
1220                 }
1221                 if (mConnectRoleChangeInfoToSetOnTransition == null
1222                         || !(mConnectRoleChangeInfoToSetOnTransition.role
1223                         instanceof ClientConnectivityRole)) {
1224                     Log.wtf(TAG, "Unexpected mConnectRoleChangeInfoToSetOnTransition: "
1225                             + mConnectRoleChangeInfoToSetOnTransition);
1226                     // Should never happen, but fallback to primary to avoid a crash.
1227                     mConnectRoleChangeInfoToSetOnTransition =
1228                             new RoleChangeInfo(ROLE_CLIENT_PRIMARY);
1229                 }
1230 
1231                 // Could be any one of possible connect mode roles.
1232                 setRoleInternalAndInvokeCallback(mConnectRoleChangeInfoToSetOnTransition);
1233                 updateConnectModeState(mConnectRoleChangeInfoToSetOnTransition.role,
1234                         WIFI_STATE_ENABLED, WIFI_STATE_ENABLING);
1235             }
1236 
1237             @Override
getMessageLogRec(int what)1238             String getMessageLogRec(int what) {
1239                 return ConcreteClientModeManager.class.getSimpleName() + "."
1240                         + ConnectModeState.class.getSimpleName() + "."
1241                         + getWhatToString(what);
1242             }
1243 
1244             @Override
processMessageImpl(Message message)1245             public boolean processMessageImpl(Message message) {
1246                 if (mVerboseLoggingEnabled) {
1247                     Log.d(getTag(),
1248                             getName() + " cmd = " + getWhatToString(message.what) + " "
1249                                     + message.toString());
1250                 }
1251                 switch (message.what) {
1252                     case CMD_SWITCH_TO_CONNECT_MODE:
1253                         RoleChangeInfo roleChangeInfo = (RoleChangeInfo) message.obj;
1254                         // switching to connect mode when already in connect mode, just update the
1255                         // requestor WorkSource.
1256                         boolean success = mWifiNative.replaceStaIfaceRequestorWs(
1257                                 mClientInterfaceName, roleChangeInfo.requestorWs);
1258                         if (success) {
1259                             setRoleInternalAndInvokeCallback(roleChangeInfo);
1260                         } else {
1261                             // If this call failed, the iface would be torn down.
1262                             // Thus, simply abort and let the iface down handling take care of the
1263                             // rest.
1264                             Log.e(getTag(), getName() + ", Failed to switch ClientModeManager="
1265                                     + ConcreteClientModeManager.this + "'s requestorWs");
1266                         }
1267                         break;
1268                     case CMD_SWITCH_TO_SCAN_ONLY_MODE:
1269                     case CMD_INTERFACE_DESTROYED:
1270                         updateConnectModeState(mRole, WifiManager.WIFI_STATE_DISABLING,
1271                                 WifiManager.WIFI_STATE_ENABLED);
1272                         return NOT_HANDLED; // Handled in StartedState.
1273                     case CMD_INTERFACE_DOWN:
1274                         updateConnectModeState(mRole, WifiManager.WIFI_STATE_DISABLING,
1275                                 WifiManager.WIFI_STATE_UNKNOWN);
1276                         return NOT_HANDLED; // Handled in StartedState.
1277                     case CMD_INTERFACE_STATUS_CHANGED:
1278                         boolean isUp = message.arg1 == 1;
1279                         if (isUp == mIfaceIsUp) {
1280                             break;  // no change
1281                         }
1282                         if (!isUp) {
1283                             // TODO(b/201584491) Figure out what to do with this block of code
1284                             // handling iface down since most devices should have MAC randomization
1285                             // enabled, which makes the "else" block essentially no-op. Also, the
1286                             // "else" block would actually fully disable wifi which is not desirable
1287                             // behavior because the firmware can recover the iface after it is down.
1288                             if (mWifiGlobals.isConnectedMacRandomizationEnabled()) {
1289                                 return HANDLED; // For MAC randomization, ignore...
1290                             } else {
1291                                 // Handle the error case where our underlying interface went down if
1292                                 // we do not have mac randomization enabled (b/72459123).
1293                                 // if the interface goes down we should exit and go back to idle
1294                                 // state.
1295                                 updateConnectModeState(mRole, WifiManager.WIFI_STATE_UNKNOWN,
1296                                         WifiManager.WIFI_STATE_ENABLED);
1297                             }
1298                         }
1299                         return NOT_HANDLED; // Handled in StartedState.
1300                     default:
1301                         return NOT_HANDLED;
1302                 }
1303                 return HANDLED;
1304             }
1305 
1306             @Override
exitImpl()1307             public void exitImpl() {
1308                 updateConnectModeState(mRole, WifiManager.WIFI_STATE_DISABLED,
1309                         WifiManager.WIFI_STATE_DISABLING);
1310 
1311                 if (mClientModeImpl == null) {
1312                     Log.w(getTag(), "ConnectModeState.exit(): mClientModeImpl is already null?!");
1313                 } else {
1314                     Log.d(getTag(), "ConnectModeState.exit(): Stopping ClientModeImpl");
1315                     mClientModeImpl.stop();
1316                     mGraveyard.inter(mClientModeImpl);
1317                     mClientModeImpl = null;
1318                 }
1319 
1320                 mConnectRoleChangeInfoToSetOnTransition = null;
1321             }
1322         }
1323     }
1324 
1325     /** Called by a ClientModeImpl owned by this CMM informing it has fully stopped. */
onClientModeImplQuit()1326     public void onClientModeImplQuit() {
1327         cleanupOnQuitIfApplicable();
1328     }
1329 
1330     /**
1331      * Only clean up this CMM once the CMM and all associated ClientModeImpls have been stopped.
1332      * This is necessary because ClientModeImpl sends broadcasts during stop, and the role must
1333      * remain primary for {@link ClientModeManagerBroadcastQueue} to send them out.
1334      */
cleanupOnQuitIfApplicable()1335     private void cleanupOnQuitIfApplicable() {
1336         if (mIsStopped && mGraveyard.hasAllClientModeImplsQuit()) {
1337             mPreviousRole = mRole;
1338             mLastRoleChangeSinceBootMs = mClock.getElapsedSinceBootMillis();
1339             mRole = null;
1340             // only call onStopped() after role has been reset to null since ActiveModeWarden
1341             // expects the CMM to be fully stopped before onStopped().
1342             mModeListener.onStopped(ConcreteClientModeManager.this);
1343 
1344             // reset to false so that onStopped() won't be triggered again.
1345             mIsStopped = false;
1346         }
1347     }
1348 
takeBugReportInterfaceFailureIfNeeded(String bugTitle, String bugDetail)1349     private void takeBugReportInterfaceFailureIfNeeded(String bugTitle, String bugDetail) {
1350         if (mWifiInjector.getDeviceConfigFacade().isInterfaceFailureBugreportEnabled()) {
1351             mWifiInjector.getWifiDiagnostics().takeBugReport(bugTitle, bugDetail);
1352         }
1353     }
1354 
1355     @NonNull
getClientMode()1356     private ClientMode getClientMode() {
1357         if (mClientModeImpl != null) {
1358             return mClientModeImpl;
1359         }
1360         if (mScanOnlyModeImpl != null) {
1361             return mScanOnlyModeImpl;
1362         }
1363         return mDefaultClientModeManager;
1364     }
1365 
1366     /*
1367      * Note: These are simple wrappers over methods to {@link ClientModeImpl}.
1368      */
1369 
1370     @Override
connectNetwork(NetworkUpdateResult result, ActionListenerWrapper wrapper, int callingUid, @NonNull String packageName)1371     public void connectNetwork(NetworkUpdateResult result, ActionListenerWrapper wrapper,
1372             int callingUid, @NonNull String packageName) {
1373         getClientMode().connectNetwork(result, wrapper, callingUid, packageName);
1374     }
1375 
1376     @Override
saveNetwork(NetworkUpdateResult result, ActionListenerWrapper wrapper, int callingUid, @NonNull String packageName)1377     public void saveNetwork(NetworkUpdateResult result, ActionListenerWrapper wrapper,
1378             int callingUid, @NonNull String packageName) {
1379         getClientMode().saveNetwork(result, wrapper, callingUid, packageName);
1380     }
1381 
1382     @Override
disconnect()1383     public void disconnect() {
1384         getClientMode().disconnect();
1385     }
1386 
1387     @Override
reconnect(WorkSource ws)1388     public void reconnect(WorkSource ws) {
1389         getClientMode().reconnect(ws);
1390     }
1391 
1392     @Override
reassociate()1393     public void reassociate() {
1394         getClientMode().reassociate();
1395     }
1396 
1397     @Override
startConnectToNetwork(int networkId, int uid, String bssid)1398     public void startConnectToNetwork(int networkId, int uid, String bssid) {
1399         getClientMode().startConnectToNetwork(networkId, uid, bssid);
1400     }
1401 
1402     @Override
startRoamToNetwork(int networkId, String bssid)1403     public void startRoamToNetwork(int networkId, String bssid) {
1404         getClientMode().startRoamToNetwork(networkId, bssid);
1405     }
1406 
1407     @Override
onDeviceMobilityStateUpdated(@eviceMobilityState int newState)1408     public void onDeviceMobilityStateUpdated(@DeviceMobilityState int newState) {
1409         getClientMode().onDeviceMobilityStateUpdated(newState);
1410     }
1411 
1412     @Override
setLinkLayerStatsPollingInterval(int newIntervalMs)1413     public void setLinkLayerStatsPollingInterval(int newIntervalMs) {
1414         getClientMode().setLinkLayerStatsPollingInterval(newIntervalMs);
1415     }
1416 
1417     @Override
setWifiConnectedNetworkScorer( IBinder binder, IWifiConnectedNetworkScorer scorer)1418     public boolean setWifiConnectedNetworkScorer(
1419             IBinder binder, IWifiConnectedNetworkScorer scorer) {
1420         return getClientMode().setWifiConnectedNetworkScorer(binder, scorer);
1421     }
1422 
1423     @Override
clearWifiConnectedNetworkScorer()1424     public void clearWifiConnectedNetworkScorer() {
1425         getClientMode().clearWifiConnectedNetworkScorer();
1426     }
1427 
1428     @Override
onNetworkSwitchAccepted(int targetNetworkId, String targetBssid)1429     public void onNetworkSwitchAccepted(int targetNetworkId, String targetBssid) {
1430         getClientMode().onNetworkSwitchAccepted(targetNetworkId, targetBssid);
1431     }
1432 
1433     @Override
onNetworkSwitchRejected(int targetNetworkId, String targetBssid)1434     public void onNetworkSwitchRejected(int targetNetworkId, String targetBssid) {
1435         getClientMode().onNetworkSwitchRejected(targetNetworkId, targetBssid);
1436     }
1437 
1438     @Override
resetSimAuthNetworks(@lientModeImpl.ResetSimReason int resetReason)1439     public void resetSimAuthNetworks(@ClientModeImpl.ResetSimReason int resetReason) {
1440         getClientMode().resetSimAuthNetworks(resetReason);
1441     }
1442 
1443     @Override
onBluetoothConnectionStateChanged()1444     public void onBluetoothConnectionStateChanged() {
1445         getClientMode().onBluetoothConnectionStateChanged();
1446     }
1447 
1448     @Override
getConnectionInfo()1449     public WifiInfo getConnectionInfo() {
1450         return getClientMode().getConnectionInfo();
1451     }
1452 
1453     @Override
syncQueryPasspointIcon(long bssid, String fileName)1454     public boolean syncQueryPasspointIcon(long bssid, String fileName) {
1455         return getClientMode().syncQueryPasspointIcon(bssid, fileName);
1456     }
1457 
1458     @Override
getCurrentNetwork()1459     public Network getCurrentNetwork() {
1460         return getClientMode().getCurrentNetwork();
1461     }
1462 
1463     @Override
syncGetDhcpResultsParcelable()1464     public DhcpResultsParcelable syncGetDhcpResultsParcelable() {
1465         return getClientMode().syncGetDhcpResultsParcelable();
1466     }
1467 
1468     @Override
getSupportedFeatures()1469     public long getSupportedFeatures() {
1470         return getClientMode().getSupportedFeatures();
1471     }
1472 
1473     @Override
syncStartSubscriptionProvisioning(int callingUid, OsuProvider provider, IProvisioningCallback callback)1474     public boolean syncStartSubscriptionProvisioning(int callingUid, OsuProvider provider,
1475             IProvisioningCallback callback) {
1476         return getClientMode().syncStartSubscriptionProvisioning(
1477                 callingUid, provider, callback);
1478     }
1479 
1480     @Override
isWifiStandardSupported(@ifiAnnotations.WifiStandard int standard)1481     public boolean isWifiStandardSupported(@WifiAnnotations.WifiStandard int standard) {
1482         return getClientMode().isWifiStandardSupported(standard);
1483     }
1484 
1485     @Override
enableTdls(String remoteMacAddress, boolean enable)1486     public boolean enableTdls(String remoteMacAddress, boolean enable) {
1487         return getClientMode().enableTdls(remoteMacAddress, enable);
1488     }
1489 
1490     @Override
enableTdlsWithRemoteIpAddress(String remoteIpAddress, boolean enable)1491     public boolean enableTdlsWithRemoteIpAddress(String remoteIpAddress, boolean enable) {
1492         return getClientMode().enableTdlsWithRemoteIpAddress(remoteIpAddress, enable);
1493     }
1494 
1495     @Override
isTdlsOperationCurrentlyAvailable()1496     public boolean isTdlsOperationCurrentlyAvailable() {
1497         return getClientMode().isTdlsOperationCurrentlyAvailable();
1498     }
1499 
1500     @Override
getMaxSupportedConcurrentTdlsSessions()1501     public int getMaxSupportedConcurrentTdlsSessions() {
1502         return getClientMode().getMaxSupportedConcurrentTdlsSessions();
1503     }
1504 
1505     @Override
getNumberOfEnabledTdlsSessions()1506     public int getNumberOfEnabledTdlsSessions() {
1507         return getClientMode().getNumberOfEnabledTdlsSessions();
1508     }
1509 
1510     @Override
dumpIpClient(FileDescriptor fd, PrintWriter pw, String[] args)1511     public void dumpIpClient(FileDescriptor fd, PrintWriter pw, String[] args) {
1512         getClientMode().dumpIpClient(fd, pw, args);
1513     }
1514 
1515     @Override
dumpWifiScoreReport(FileDescriptor fd, PrintWriter pw, String[] args)1516     public void dumpWifiScoreReport(FileDescriptor fd, PrintWriter pw, String[] args) {
1517         getClientMode().dumpWifiScoreReport(fd, pw, args);
1518     }
1519 
1520     @Override
enableVerboseLogging(boolean verbose)1521     public void enableVerboseLogging(boolean verbose) {
1522         mVerboseLoggingEnabled = verbose;
1523         getClientMode().enableVerboseLogging(verbose);
1524     }
1525 
1526     @Override
getFactoryMacAddress()1527     public String getFactoryMacAddress() {
1528         return getClientMode().getFactoryMacAddress();
1529     }
1530 
1531     @Override
getConnectedWifiConfiguration()1532     public WifiConfiguration getConnectedWifiConfiguration() {
1533         return getClientMode().getConnectedWifiConfiguration();
1534     }
1535 
1536     @Override
getConnectingWifiConfiguration()1537     public WifiConfiguration getConnectingWifiConfiguration() {
1538         return getClientMode().getConnectingWifiConfiguration();
1539     }
1540 
1541     @Override
getConnectedBssid()1542     public String getConnectedBssid() {
1543         return getClientMode().getConnectedBssid();
1544     }
1545 
1546     @Override
getConnectingBssid()1547     public String getConnectingBssid() {
1548         return getClientMode().getConnectingBssid();
1549     }
1550 
1551     @Override
getWifiLinkLayerStats()1552     public WifiLinkLayerStats getWifiLinkLayerStats() {
1553         return getClientMode().getWifiLinkLayerStats();
1554     }
1555 
1556     @Override
setPowerSave(@owerSaveClientType int client, boolean ps)1557     public boolean setPowerSave(@PowerSaveClientType int client, boolean ps) {
1558         return getClientMode().setPowerSave(client, ps);
1559     }
1560 
1561     @Override
enablePowerSave()1562     public boolean enablePowerSave() {
1563         return getClientMode().enablePowerSave();
1564     }
1565 
1566     @Override
setLowLatencyMode(boolean enabled)1567     public boolean setLowLatencyMode(boolean enabled) {
1568         return getClientMode().setLowLatencyMode(enabled);
1569     }
1570 
1571     @Override
getMcastLockManagerFilterController()1572     public WifiMulticastLockManager.FilterController getMcastLockManagerFilterController() {
1573         return getClientMode().getMcastLockManagerFilterController();
1574     }
1575 
1576     @Override
isConnected()1577     public boolean isConnected() {
1578         return getClientMode().isConnected();
1579     }
1580 
1581     @Override
isConnecting()1582     public boolean isConnecting() {
1583         return getClientMode().isConnecting();
1584     }
1585 
1586     @Override
isRoaming()1587     public boolean isRoaming() {
1588         return getClientMode().isRoaming();
1589     }
1590 
1591     @Override
isDisconnected()1592     public boolean isDisconnected() {
1593         return getClientMode().isDisconnected();
1594     }
1595 
1596     @Override
isSupplicantTransientState()1597     public boolean isSupplicantTransientState() {
1598         return getClientMode().isSupplicantTransientState();
1599     }
1600 
1601     @Override
onCellularConnectivityChanged(@ifiDataStall.CellularDataStatusCode int status)1602     public void onCellularConnectivityChanged(@WifiDataStall.CellularDataStatusCode int status) {
1603         getClientMode().onCellularConnectivityChanged(status);
1604     }
1605 
1606     @Override
probeLink(LinkProbeCallback callback, int mcs)1607     public void probeLink(LinkProbeCallback callback, int mcs) {
1608         getClientMode().probeLink(callback, mcs);
1609     }
1610 
1611     @Override
sendMessageToClientModeImpl(Message msg)1612     public void sendMessageToClientModeImpl(Message msg) {
1613         getClientMode().sendMessageToClientModeImpl(msg);
1614     }
1615 
1616     @Override
getId()1617     public long getId() {
1618         return mId;
1619     }
1620 
1621     @Override
setMboCellularDataStatus(boolean available)1622     public void setMboCellularDataStatus(boolean available) {
1623         getClientMode().setMboCellularDataStatus(available);
1624     }
1625 
1626     @Override
getRoamingCapabilities()1627     public WifiNative.RoamingCapabilities getRoamingCapabilities() {
1628         return getClientMode().getRoamingCapabilities();
1629     }
1630 
1631     @Override
configureRoaming(WifiNative.RoamingConfig config)1632     public boolean configureRoaming(WifiNative.RoamingConfig config) {
1633         return getClientMode().configureRoaming(config);
1634     }
1635 
1636     @Override
enableRoaming(boolean enabled)1637     public boolean enableRoaming(boolean enabled) {
1638         return getClientMode().enableRoaming(enabled);
1639     }
1640 
1641     @Override
setCountryCode(String countryCode)1642     public boolean setCountryCode(String countryCode) {
1643         return getClientMode().setCountryCode(countryCode);
1644     }
1645 
1646     @Override
getTxPktFates()1647     public List<TxFateReport> getTxPktFates() {
1648         return getClientMode().getTxPktFates();
1649     }
1650 
1651     @Override
getRxPktFates()1652     public List<RxFateReport> getRxPktFates() {
1653         return getClientMode().getRxPktFates();
1654     }
1655 
1656     @Override
getDeviceWiphyCapabilities()1657     public DeviceWiphyCapabilities getDeviceWiphyCapabilities() {
1658         return getClientMode().getDeviceWiphyCapabilities();
1659     }
1660 
1661     @Override
requestAnqp(String bssid, Set<Integer> anqpIds, Set<Integer> hs20Subtypes)1662     public boolean requestAnqp(String bssid, Set<Integer> anqpIds, Set<Integer> hs20Subtypes) {
1663         return getClientMode().requestAnqp(bssid, anqpIds, hs20Subtypes);
1664     }
1665 
1666     @Override
requestVenueUrlAnqp(String bssid)1667     public boolean requestVenueUrlAnqp(String bssid) {
1668         return getClientMode().requestVenueUrlAnqp(bssid);
1669     }
1670 
1671     @Override
requestIcon(String bssid, String fileName)1672     public boolean requestIcon(String bssid, String fileName) {
1673         return getClientMode().requestIcon(bssid, fileName);
1674     }
1675 
1676     @Override
setShouldReduceNetworkScore(boolean shouldReduceNetworkScore)1677     public void setShouldReduceNetworkScore(boolean shouldReduceNetworkScore) {
1678         mShouldReduceNetworkScore = shouldReduceNetworkScore;
1679         getClientMode().setShouldReduceNetworkScore(shouldReduceNetworkScore);
1680     }
1681 
1682     @Override
toString()1683     public String toString() {
1684         return "ConcreteClientModeManager{id=" + getId()
1685                 + " iface=" + getInterfaceName()
1686                 + " role=" + getRole()
1687                 + "}";
1688     }
1689 
1690     @Override
updateCapabilities()1691     public void updateCapabilities() {
1692         getClientMode().updateCapabilities();
1693     }
1694 
1695     @Override
isAffiliatedLinkBssid(MacAddress bssid)1696     public boolean isAffiliatedLinkBssid(MacAddress bssid) {
1697         return getClientMode().isAffiliatedLinkBssid(bssid);
1698     }
1699 
1700     @Override
isMlo()1701     public boolean isMlo() {
1702         return getClientMode().isMlo();
1703     }
1704 }
1705