• 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.SAP_CLIENT_DISCONNECT_REASON_CODE_UNSPECIFIED;
20 
21 import static com.android.server.wifi.util.ApConfigUtil.ERROR_GENERIC;
22 import static com.android.server.wifi.util.ApConfigUtil.ERROR_NO_CHANNEL;
23 import static com.android.server.wifi.util.ApConfigUtil.ERROR_UNSUPPORTED_CONFIGURATION;
24 import static com.android.server.wifi.util.ApConfigUtil.SUCCESS;
25 
26 import android.annotation.NonNull;
27 import android.annotation.Nullable;
28 import android.app.compat.CompatChanges;
29 import android.content.BroadcastReceiver;
30 import android.content.Context;
31 import android.content.Intent;
32 import android.content.IntentFilter;
33 import android.net.MacAddress;
34 import android.net.wifi.ScanResult;
35 import android.net.wifi.SoftApCapability;
36 import android.net.wifi.SoftApConfiguration;
37 import android.net.wifi.SoftApInfo;
38 import android.net.wifi.WifiAnnotations;
39 import android.net.wifi.WifiClient;
40 import android.net.wifi.WifiContext;
41 import android.net.wifi.WifiInfo;
42 import android.net.wifi.WifiManager;
43 import android.net.wifi.WifiSsid;
44 import android.os.BatteryManager;
45 import android.os.Handler;
46 import android.os.Looper;
47 import android.os.Message;
48 import android.os.SystemClock;
49 import android.os.UserHandle;
50 import android.os.WorkSource;
51 import android.text.TextUtils;
52 import android.util.Log;
53 
54 import com.android.internal.annotations.VisibleForTesting;
55 import com.android.internal.util.IState;
56 import com.android.internal.util.Preconditions;
57 import com.android.internal.util.State;
58 import com.android.internal.util.StateMachine;
59 import com.android.internal.util.WakeupMessage;
60 import com.android.modules.utils.build.SdkLevel;
61 import com.android.server.wifi.WifiNative.InterfaceCallback;
62 import com.android.server.wifi.WifiNative.SoftApHalCallback;
63 import com.android.server.wifi.coex.CoexManager;
64 import com.android.server.wifi.coex.CoexManager.CoexListener;
65 import com.android.server.wifi.util.ApConfigUtil;
66 import com.android.server.wifi.util.WaitingState;
67 import com.android.wifi.resources.R;
68 
69 import java.io.FileDescriptor;
70 import java.io.PrintWriter;
71 import java.text.SimpleDateFormat;
72 import java.util.ArrayList;
73 import java.util.Date;
74 import java.util.HashMap;
75 import java.util.HashSet;
76 import java.util.Iterator;
77 import java.util.List;
78 import java.util.Locale;
79 import java.util.Map;
80 import java.util.Set;
81 import java.util.stream.Collectors;
82 
83 /**
84  * Manage WiFi in AP mode.
85  * The internal state machine runs under the ClientModeImpl handler thread context.
86  */
87 public class SoftApManager implements ActiveModeManager {
88     private static final String TAG = "SoftApManager";
89 
90     @VisibleForTesting
91     public static final String SOFT_AP_SEND_MESSAGE_TIMEOUT_TAG = TAG
92             + " Soft AP Send Message Timeout on ";
93     private final WifiContext mContext;
94     private final FrameworkFacade mFrameworkFacade;
95     private final WifiNative mWifiNative;
96     // This will only be null if SdkLevel is not at least S
97     @Nullable private final CoexManager mCoexManager;
98     private final ClientModeImplMonitor mCmiMonitor;
99     private final ActiveModeWarden mActiveModeWarden;
100     private final SoftApNotifier mSoftApNotifier;
101     private final InterfaceConflictManager mInterfaceConflictManager;
102     private final WifiInjector mWifiInjector;
103 
104     @VisibleForTesting
105     static final long SOFT_AP_PENDING_DISCONNECTION_CHECK_DELAY_MS = 1000;
106 
107     private static final long SCHEDULE_IDLE_INSTANCE_SHUTDOWN_TIMEOUT_DELAY_MS = 10;
108 
109     private String mCountryCode;
110 
111     private final SoftApStateMachine mStateMachine;
112 
113     private final Listener<SoftApManager> mModeListener;
114     private final WifiServiceImpl.SoftApCallbackInternal mSoftApCallback;
115 
116     private String mApInterfaceName;
117     private boolean mIfaceIsUp;
118     private boolean mIfaceIsDestroyed;
119 
120     private final WifiApConfigStore mWifiApConfigStore;
121 
122     private final ClientModeImplListener mCmiListener = new ClientModeImplListener() {
123         @Override
124         public void onL2Connected(@NonNull ConcreteClientModeManager clientModeManager) {
125             SoftApManager.this.onL2Connected(clientModeManager);
126         }
127     };
128 
129     private final WifiMetrics mWifiMetrics;
130     private final long mId;
131 
132     private boolean mIsUnsetBssid;
133 
134     private boolean mVerboseLoggingEnabled = false;
135 
136     /**
137      * Original configuration, which is the passed configuration when init or
138      * the user-configured {@code WifiApConfigStore#getApConfiguration}tethering}
139      * settings when input is null.
140      *
141      * Use it when doing configuration update to know if the input configuration was changed.
142      * For others use case, it should use {@code mCurrentSoftApConfiguration}.
143      */
144     @NonNull
145     private final SoftApModeConfiguration mOriginalModeConfiguration;
146 
147 
148     /**
149      * Current Soft AP configuration which is used to start Soft AP.
150      * The configuration may be changed because
151      * 1. bssid is changed because MAC randomization
152      * 2. bands are changed because fallback to single AP mode mechanism.
153      */
154     @Nullable
155     private SoftApConfiguration mCurrentSoftApConfiguration;
156 
157     @NonNull
158     private Map<String, SoftApInfo> mCurrentSoftApInfoMap = new HashMap<>();
159 
160     @NonNull
161     private SoftApCapability mCurrentSoftApCapability;
162 
163     private Map<String, List<WifiClient>> mConnectedClientWithApInfoMap = new HashMap<>();
164     @VisibleForTesting
165     Map<WifiClient, Integer> mPendingDisconnectClients = new HashMap<>();
166 
167     private boolean mTimeoutEnabled = false;
168     private boolean mBridgedModeOpportunisticsShutdownTimeoutEnabled = false;
169 
170     private final SarManager mSarManager;
171 
172     private String mStartTimestamp;
173 
174     private long mDefaultShutdownTimeoutMillis;
175 
176     private long mDefaultShutdownIdleInstanceInBridgedModeTimeoutMillis;
177 
178     private final boolean mIsDisableShutDownBridgedModeIdleInstanceTimerWhenPlugged;
179 
180     private static final SimpleDateFormat FORMATTER = new SimpleDateFormat("MM-dd HH:mm:ss.SSS");
181 
182     private WifiDiagnostics mWifiDiagnostics;
183 
184     @Nullable
185     private SoftApRole mRole = null;
186     @Nullable
187     private WorkSource mRequestorWs = null;
188 
189     private boolean mEverReportMetricsForMaxClient = false;
190 
191     @NonNull
192     private Set<MacAddress> mBlockedClientList = new HashSet<>();
193 
194     @NonNull
195     private Set<MacAddress> mAllowedClientList = new HashSet<>();
196 
197     @NonNull
198     private Set<Integer> mSafeChannelFrequencyList = new HashSet<>();
199 
200     private boolean mIsPlugged = false;
201 
202     /**
203      * A map stores shutdown timeouts for each Soft Ap instance.
204      * There are three timeout messages now.
205      * 1. <mApInterfaceName, timeout> which uses to monitor whole Soft AP interface.
206      * It works on single AP mode and bridged AP mode.
207      *
208      * 2. <instance_lower_band, timeout> which is used to shutdown the AP when there are no
209      * connected devices. It is scheduled only in bridged mode to move dual mode AP to single
210      * mode AP in lower band.
211      *
212      * 3. <instance_higher_band, timeout> which is used to shutdown the AP when there are no
213      * connected devices. It is scheduled only in bridged mode to move dual mode AP to single
214      * mode AP in higher band.
215      */
216     @VisibleForTesting
217     public Map<String, WakeupMessage> mSoftApTimeoutMessageMap = new HashMap<>();
218 
219     /**
220      * Listener for soft AP events.
221      */
222     private final SoftApHalCallback mSoftApHalCallback = new SoftApHalCallback() {
223         @Override
224         public void onFailure() {
225             mStateMachine.sendMessage(SoftApStateMachine.CMD_FAILURE);
226         }
227 
228         @Override
229         public void onInstanceFailure(String instanceName) {
230             mStateMachine.sendMessage(SoftApStateMachine.CMD_FAILURE, instanceName);
231         }
232 
233         @Override
234         public void onInfoChanged(String apIfaceInstance, int frequency,
235                 @WifiAnnotations.Bandwidth int bandwidth,
236                 @WifiAnnotations.WifiStandard int generation,
237                 MacAddress apIfaceInstanceMacAddress) {
238             SoftApInfo apInfo = new SoftApInfo();
239             apInfo.setFrequency(frequency);
240             apInfo.setBandwidth(bandwidth);
241             apInfo.setWifiStandard(generation);
242             if (apIfaceInstanceMacAddress != null) {
243                 apInfo.setBssid(apIfaceInstanceMacAddress);
244             }
245             apInfo.setApInstanceIdentifier(apIfaceInstance != null
246                     ? apIfaceInstance : mApInterfaceName);
247             mStateMachine.sendMessage(
248                     SoftApStateMachine.CMD_AP_INFO_CHANGED, 0, 0, apInfo);
249         }
250 
251         @Override
252         public void onConnectedClientsChanged(String apIfaceInstance, MacAddress clientAddress,
253                 boolean isConnected) {
254             if (clientAddress != null) {
255                 WifiClient client = new WifiClient(clientAddress, apIfaceInstance != null
256                         ? apIfaceInstance : mApInterfaceName);
257                 mStateMachine.sendMessage(SoftApStateMachine.CMD_ASSOCIATED_STATIONS_CHANGED,
258                         isConnected ? 1 : 0, 0, client);
259             } else {
260                 Log.e(getTag(), "onConnectedClientsChanged: Invalid type returned");
261             }
262         }
263     };
264 
265     // This will only be null if SdkLevel is not at least S
266     @Nullable private final CoexListener mCoexListener;
267 
updateSafeChannelFrequencyList()268     private void updateSafeChannelFrequencyList() {
269         if (!SdkLevel.isAtLeastS() || mCurrentSoftApConfiguration == null) {
270             return;
271         }
272         mSafeChannelFrequencyList.clear();
273         for (int configuredBand : mCurrentSoftApConfiguration.getBands()) {
274             for (int band : SoftApConfiguration.BAND_TYPES) {
275                 if ((band & configuredBand) == 0) {
276                     continue;
277                 }
278                 for (int channel : mCurrentSoftApCapability.getSupportedChannelList(band)) {
279                     mSafeChannelFrequencyList.add(
280                             ApConfigUtil.convertChannelToFrequency(channel, band));
281                 }
282             }
283         }
284         if ((mCoexManager.getCoexRestrictions() & WifiManager.COEX_RESTRICTION_SOFTAP) != 0) {
285             mSafeChannelFrequencyList.removeAll(
286                     ApConfigUtil.getUnsafeChannelFreqsFromCoex(mCoexManager));
287         }
288         if (isBridgedMode() && mCurrentSoftApInfoMap.size() == 2) {
289             // Logging only for bridged use case since it only used to fallback to single AP mode.
290             Log.d(getTag(), "SafeChannelFrequencyList = " + mSafeChannelFrequencyList);
291         }
292     }
293 
configureInternalConfiguration()294     private void configureInternalConfiguration() {
295         if (mCurrentSoftApConfiguration == null) {
296             return;
297         }
298         mBlockedClientList = new HashSet<>(mCurrentSoftApConfiguration.getBlockedClientList());
299         mAllowedClientList = new HashSet<>(mCurrentSoftApConfiguration.getAllowedClientList());
300         mTimeoutEnabled = mCurrentSoftApConfiguration.isAutoShutdownEnabled();
301         mBridgedModeOpportunisticsShutdownTimeoutEnabled =
302                 mCurrentSoftApConfiguration.isBridgedModeOpportunisticShutdownEnabledInternal();
303     }
304 
updateChangeableConfiguration(SoftApConfiguration newConfig)305     private void updateChangeableConfiguration(SoftApConfiguration newConfig) {
306         if (mCurrentSoftApConfiguration == null || newConfig == null) {
307             return;
308         }
309         /**
310          * update configurations only which mentioned in WifiManager#setSoftApConfiguration
311          */
312         long newShutdownTimeoutMillis = newConfig.getShutdownTimeoutMillis();
313         // Compatibility check is used for unit test only since the SoftApManager is created by
314         // the unit test thread (not the system_server) when running unit test. In other cases,
315         // the SoftApManager would run in system server(i.e. always bypasses the app compat check).
316         if (CompatChanges.isChangeEnabled(SoftApConfiguration.REMOVE_ZERO_FOR_TIMEOUT_SETTING)
317                 && newShutdownTimeoutMillis == 0) {
318             newShutdownTimeoutMillis = SoftApConfiguration.DEFAULT_TIMEOUT;
319         }
320         SoftApConfiguration.Builder newConfigurBuilder =
321                 new SoftApConfiguration.Builder(mCurrentSoftApConfiguration)
322                 .setAllowedClientList(newConfig.getAllowedClientList())
323                 .setBlockedClientList(newConfig.getBlockedClientList())
324                 .setClientControlByUserEnabled(newConfig.isClientControlByUserEnabled())
325                 .setMaxNumberOfClients(newConfig.getMaxNumberOfClients())
326                 .setShutdownTimeoutMillis(newShutdownTimeoutMillis)
327                 .setAutoShutdownEnabled(newConfig.isAutoShutdownEnabled());
328         if (SdkLevel.isAtLeastS()) {
329             newConfigurBuilder.setBridgedModeOpportunisticShutdownEnabled(
330                     newConfig.isBridgedModeOpportunisticShutdownEnabledInternal());
331         }
332         mCurrentSoftApConfiguration = newConfigurBuilder.build();
333         configureInternalConfiguration();
334     }
335 
SoftApManager( @onNull WifiContext context, @NonNull Looper looper, @NonNull FrameworkFacade framework, @NonNull WifiNative wifiNative, @NonNull WifiInjector wifiInjector, @NonNull CoexManager coexManager, @NonNull InterfaceConflictManager interfaceConflictManager, @NonNull Listener<SoftApManager> listener, @NonNull WifiServiceImpl.SoftApCallbackInternal callback, @NonNull WifiApConfigStore wifiApConfigStore, @NonNull SoftApModeConfiguration apConfig, @NonNull WifiMetrics wifiMetrics, @NonNull SarManager sarManager, @NonNull WifiDiagnostics wifiDiagnostics, @NonNull SoftApNotifier softApNotifier, @NonNull ClientModeImplMonitor cmiMonitor, @NonNull ActiveModeWarden activeModeWarden, long id, @NonNull WorkSource requestorWs, @NonNull SoftApRole role, boolean verboseLoggingEnabled)336     public SoftApManager(
337             @NonNull WifiContext context,
338             @NonNull Looper looper,
339             @NonNull FrameworkFacade framework,
340             @NonNull WifiNative wifiNative,
341             @NonNull WifiInjector wifiInjector,
342             @NonNull CoexManager coexManager,
343             @NonNull InterfaceConflictManager interfaceConflictManager,
344             @NonNull Listener<SoftApManager> listener,
345             @NonNull WifiServiceImpl.SoftApCallbackInternal callback,
346             @NonNull WifiApConfigStore wifiApConfigStore,
347             @NonNull SoftApModeConfiguration apConfig,
348             @NonNull WifiMetrics wifiMetrics,
349             @NonNull SarManager sarManager,
350             @NonNull WifiDiagnostics wifiDiagnostics,
351             @NonNull SoftApNotifier softApNotifier,
352             @NonNull ClientModeImplMonitor cmiMonitor,
353             @NonNull ActiveModeWarden activeModeWarden,
354             long id,
355             @NonNull WorkSource requestorWs,
356             @NonNull SoftApRole role,
357             boolean verboseLoggingEnabled) {
358         mContext = context;
359         mFrameworkFacade = framework;
360         mSoftApNotifier = softApNotifier;
361         mWifiNative = wifiNative;
362         mWifiInjector = wifiInjector;
363         mCoexManager = coexManager;
364         mInterfaceConflictManager = interfaceConflictManager;
365         if (SdkLevel.isAtLeastS()) {
366             mCoexListener = new CoexListener() {
367                 @Override
368                 public void onCoexUnsafeChannelsChanged() {
369                     if (mCurrentSoftApConfiguration == null) {
370                         return;
371                     }
372                     mStateMachine.sendMessage(
373                             SoftApStateMachine.CMD_SAFE_CHANNEL_FREQUENCY_CHANGED);
374                 }
375             };
376         } else {
377             mCoexListener = null;
378         }
379         mCountryCode = apConfig.getCountryCode();
380         mModeListener = listener;
381         mSoftApCallback = callback;
382         mWifiApConfigStore = wifiApConfigStore;
383         mCurrentSoftApConfiguration = apConfig.getSoftApConfiguration();
384         mCurrentSoftApCapability = apConfig.getCapability();
385         // null is a valid input and means we use the user-configured tethering settings.
386         if (mCurrentSoftApConfiguration == null) {
387             mCurrentSoftApConfiguration = mWifiApConfigStore.getApConfiguration();
388             // may still be null if we fail to load the default config
389         }
390         // Store mode configuration before update the configuration.
391         mOriginalModeConfiguration = new SoftApModeConfiguration(apConfig.getTargetMode(),
392                 mCurrentSoftApConfiguration, mCurrentSoftApCapability, mCountryCode);
393         if (mCurrentSoftApConfiguration != null) {
394             mIsUnsetBssid = mCurrentSoftApConfiguration.getBssid() == null;
395             if (mCurrentSoftApCapability.areFeaturesSupported(
396                     SoftApCapability.SOFTAP_FEATURE_MAC_ADDRESS_CUSTOMIZATION)) {
397                 mCurrentSoftApConfiguration = mWifiApConfigStore.randomizeBssidIfUnset(
398                         mContext, mCurrentSoftApConfiguration);
399             }
400         }
401         mWifiMetrics = wifiMetrics;
402         mSarManager = sarManager;
403         mWifiDiagnostics = wifiDiagnostics;
404         mStateMachine = new SoftApStateMachine(looper);
405         configureInternalConfiguration();
406         mDefaultShutdownTimeoutMillis = mContext.getResources().getInteger(
407                 R.integer.config_wifiFrameworkSoftApShutDownTimeoutMilliseconds);
408         mDefaultShutdownIdleInstanceInBridgedModeTimeoutMillis = mContext.getResources().getInteger(
409                 R.integer
410                 .config_wifiFrameworkSoftApShutDownIdleInstanceInBridgedModeTimeoutMillisecond);
411         mIsDisableShutDownBridgedModeIdleInstanceTimerWhenPlugged = mContext.getResources()
412                 .getBoolean(R.bool
413                 .config_wifiFrameworkSoftApDisableBridgedModeShutdownIdleInstanceWhenCharging);
414         mCmiMonitor = cmiMonitor;
415         mActiveModeWarden = activeModeWarden;
416         mCmiMonitor.registerListener(mCmiListener);
417         updateSafeChannelFrequencyList();
418         mId = id;
419         mRole = role;
420         enableVerboseLogging(verboseLoggingEnabled);
421         mStateMachine.sendMessage(SoftApStateMachine.CMD_START, requestorWs);
422     }
423 
424     @Override
getId()425     public long getId() {
426         return mId;
427     }
428 
getTag()429     private String getTag() {
430         return TAG + "[" + (mApInterfaceName == null ? "unknown" : mApInterfaceName) + "]";
431     }
432 
433     /**
434      * Stop soft AP.
435      */
436     @Override
stop()437     public void stop() {
438         Log.d(getTag(), " currentstate: " + getCurrentStateName());
439         mStateMachine.sendMessage(SoftApStateMachine.CMD_STOP);
440     }
441 
isOweTransition()442     private boolean isOweTransition() {
443         return (SdkLevel.isAtLeastT() && mCurrentSoftApConfiguration != null
444                 && mCurrentSoftApConfiguration.getSecurityType()
445                         == SoftApConfiguration.SECURITY_TYPE_WPA3_OWE_TRANSITION);
446     }
447 
isBridgedMode()448     private boolean isBridgedMode() {
449         return (SdkLevel.isAtLeastS() && mCurrentSoftApConfiguration != null
450                 && (mCurrentSoftApConfiguration.getBands().length > 1));
451     }
452 
isBridgeRequired()453     private boolean isBridgeRequired() {
454         return isBridgedMode() || isOweTransition();
455     }
456 
getShutdownTimeoutMillis()457     private long getShutdownTimeoutMillis() {
458         long timeout = mCurrentSoftApConfiguration.getShutdownTimeoutMillis();
459         return timeout > 0 ? timeout : mDefaultShutdownTimeoutMillis;
460     }
461 
getShutdownIdleInstanceInBridgedModeTimeoutMillis()462     private long getShutdownIdleInstanceInBridgedModeTimeoutMillis() {
463         long timeout = mCurrentSoftApConfiguration
464                 .getBridgedModeOpportunisticShutdownTimeoutMillisInternal();
465         return timeout > 0 ? timeout : mDefaultShutdownIdleInstanceInBridgedModeTimeoutMillis;
466     }
467 
getHighestFrequencyInstance(Set<String> candidateInstances)468     private String getHighestFrequencyInstance(Set<String> candidateInstances) {
469         int currentHighestFrequencyOnAP = 0;
470         String highestFrequencyInstance = null;
471         for (String instance : candidateInstances) {
472             SoftApInfo info = mCurrentSoftApInfoMap.get(instance);
473             if (info == null) {
474                 Log.wtf(getTag(), "Invalid instance name, no way to get the frequency");
475                 return "";
476             }
477             int frequencyOnInstance = info.getFrequency();
478             if (frequencyOnInstance > currentHighestFrequencyOnAP) {
479                 currentHighestFrequencyOnAP = frequencyOnInstance;
480                 highestFrequencyInstance = instance;
481             }
482         }
483         return highestFrequencyInstance;
484     }
485 
486     @Override
getRole()487     @Nullable public SoftApRole getRole() {
488         return mRole;
489     }
490 
491     @Override
getPreviousRole()492     @Nullable public ClientRole getPreviousRole() {
493         return null;
494     }
495 
496     @Override
getLastRoleChangeSinceBootMs()497     public long getLastRoleChangeSinceBootMs() {
498         return 0;
499     }
500 
501     /** Set the role of this SoftApManager */
setRole(SoftApRole role)502     public void setRole(SoftApRole role) {
503         // softap does not allow in-place switching of roles.
504         Preconditions.checkState(mRole == null);
505         mRole = role;
506     }
507 
508     @Override
getInterfaceName()509     public String getInterfaceName() {
510         return mApInterfaceName;
511     }
512 
513     @Override
getRequestorWs()514     public WorkSource getRequestorWs() {
515         return mRequestorWs;
516     }
517 
518     /**
519      * Update AP capability. Called when carrier config or device resouce config changed.
520      *
521      * @param capability new AP capability.
522      */
updateCapability(@onNull SoftApCapability capability)523     public void updateCapability(@NonNull SoftApCapability capability) {
524         mStateMachine.sendMessage(SoftApStateMachine.CMD_UPDATE_CAPABILITY, capability);
525     }
526 
527     /**
528      * Update AP configuration. Called when setting update config via
529      * {@link WifiManager#setSoftApConfiguration(SoftApConfiguration)}
530      *
531      * @param config new AP config.
532      */
updateConfiguration(@onNull SoftApConfiguration config)533     public void updateConfiguration(@NonNull SoftApConfiguration config) {
534         mStateMachine.sendMessage(SoftApStateMachine.CMD_UPDATE_CONFIG, config);
535     }
536 
537     /**
538      * Retrieve the {@link SoftApModeConfiguration} instance associated with this mode manager.
539      */
getSoftApModeConfiguration()540     public SoftApModeConfiguration getSoftApModeConfiguration() {
541         return new SoftApModeConfiguration(mOriginalModeConfiguration.getTargetMode(),
542                 mCurrentSoftApConfiguration, mCurrentSoftApCapability, mCountryCode);
543     }
544 
545     /**
546      * Retrieve the name of the Bridged AP iface instance to remove for a downgrade, or null if a
547      * downgrade is not possible.
548      */
getBridgedApDowngradeIfaceInstanceForRemoval()549     public String getBridgedApDowngradeIfaceInstanceForRemoval() {
550         if (!isBridgedMode() || mCurrentSoftApInfoMap.size() == 0) {
551             return null;
552         }
553         List<String> instances = mWifiNative.getBridgedApInstances(mApInterfaceName);
554         if (instances == null || instances.size() == 1) {
555             return null;
556         }
557         return getHighestFrequencyInstance(mCurrentSoftApInfoMap.keySet());
558     }
559 
560     /**
561      * Dump info about this softap manager.
562      */
563     @Override
dump(FileDescriptor fd, PrintWriter pw, String[] args)564     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
565         pw.println("Dump of SoftApManager id=" + mId);
566 
567         pw.println("current StateMachine mode: " + getCurrentStateName());
568         pw.println("mRole: " + mRole);
569         pw.println("mApInterfaceName: " + mApInterfaceName);
570         pw.println("mIfaceIsUp: " + mIfaceIsUp);
571         pw.println("mSoftApCountryCode: " + mCountryCode);
572         pw.println("mOriginalModeConfiguration.targetMode: "
573                 + mOriginalModeConfiguration.getTargetMode());
574         pw.println("mCurrentSoftApConfiguration: " + mCurrentSoftApConfiguration);
575         pw.println("mCurrentSoftApCapability: " + mCurrentSoftApCapability);
576         pw.println("getConnectedClientList().size(): " + getConnectedClientList().size());
577         pw.println("mTimeoutEnabled: " + mTimeoutEnabled);
578         pw.println("mBridgedModeOpportunisticsShutdownTimeoutEnabled: "
579                 + mBridgedModeOpportunisticsShutdownTimeoutEnabled);
580         pw.println("mCurrentSoftApInfoMap " + mCurrentSoftApInfoMap);
581         pw.println("mStartTimestamp: " + mStartTimestamp);
582         pw.println("mSafeChannelFrequencyList: " + mSafeChannelFrequencyList.stream()
583                 .map(Object::toString)
584                 .collect(Collectors.joining(",")));
585         mStateMachine.dump(fd, pw, args);
586     }
587 
588     @Override
enableVerboseLogging(boolean verbose)589     public void enableVerboseLogging(boolean verbose) {
590         mVerboseLoggingEnabled = verbose;
591     }
592 
593     @Override
toString()594     public String toString() {
595         return "SoftApManager{id=" + getId()
596                 + " iface=" + getInterfaceName()
597                 + " role=" + getRole()
598                 + "}";
599     }
600 
601     /**
602      * A ClientModeImpl instance has been L2 connected.
603      *
604      * @param newPrimary the corresponding ConcreteClientModeManager instance for the ClientModeImpl
605      *                   that has been L2 connected.
606      */
onL2Connected(@onNull ConcreteClientModeManager clientModeManager)607     private void onL2Connected(@NonNull ConcreteClientModeManager clientModeManager) {
608         Log.d(getTag(), "onL2Connected called");
609         mStateMachine.sendMessage(SoftApStateMachine.CMD_HANDLE_WIFI_CONNECTED,
610                 clientModeManager.getConnectionInfo());
611     }
612 
613 
getCurrentStateName()614     private String getCurrentStateName() {
615         IState currentState = mStateMachine.getCurrentState();
616 
617         if (currentState != null) {
618             return currentState.getName();
619         }
620 
621         return "StateMachine not active";
622     }
623 
624     /**
625      * Update AP state.
626      *
627      * @param newState     new AP state
628      * @param currentState current AP state
629      * @param reason       Failure reason if the new AP state is in failure state
630      */
updateApState(int newState, int currentState, int reason)631     private void updateApState(int newState, int currentState, int reason) {
632         mSoftApCallback.onStateChanged(newState, reason);
633 
634         //send the AP state change broadcast
635         final Intent intent = new Intent(WifiManager.WIFI_AP_STATE_CHANGED_ACTION);
636         intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
637         intent.putExtra(WifiManager.EXTRA_WIFI_AP_STATE, newState);
638         intent.putExtra(WifiManager.EXTRA_PREVIOUS_WIFI_AP_STATE, currentState);
639         if (newState == WifiManager.WIFI_AP_STATE_FAILED) {
640             //only set reason number when softAP start failed
641             intent.putExtra(WifiManager.EXTRA_WIFI_AP_FAILURE_REASON, reason);
642         }
643 
644         intent.putExtra(WifiManager.EXTRA_WIFI_AP_INTERFACE_NAME, mApInterfaceName);
645         intent.putExtra(WifiManager.EXTRA_WIFI_AP_MODE, mOriginalModeConfiguration.getTargetMode());
646         if (SdkLevel.isAtLeastSv2()) {
647             mContext.sendBroadcastAsUser(intent, UserHandle.ALL,
648                     android.Manifest.permission.ACCESS_WIFI_STATE);
649         } else {
650             mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
651         }
652     }
653 
setMacAddress()654     private int setMacAddress() {
655         MacAddress mac = mCurrentSoftApConfiguration.getBssid();
656 
657         if (mac == null) {
658             // If no BSSID is explicitly requested, (re-)configure the factory MAC address. Some
659             // drivers may not support setting the MAC at all, so fail soft in this case.
660             if (!mWifiNative.resetApMacToFactoryMacAddress(mApInterfaceName)) {
661                 Log.w(getTag(), "failed to reset to factory MAC address; "
662                         + "continuing with current MAC");
663             }
664         } else {
665             if (mWifiNative.isApSetMacAddressSupported(mApInterfaceName)) {
666                 if (!mWifiNative.setApMacAddress(mApInterfaceName, mac)) {
667                     Log.e(getTag(), "failed to set explicitly requested MAC address");
668                     return ERROR_GENERIC;
669                 }
670             } else if (!mIsUnsetBssid) {
671                 // If hardware does not support MAC address setter,
672                 // only report the error for non randomization.
673                 return ERROR_UNSUPPORTED_CONFIGURATION;
674             }
675         }
676 
677         return SUCCESS;
678     }
679 
680     /**
681      * Dynamic update the country code when Soft AP enabled.
682      *
683      * @param countryCode 2 byte ASCII string. For ex: US, CA.
684      * @return true if request is sent successfully, false otherwise.
685      */
updateCountryCode(@onNull String countryCode)686     public boolean updateCountryCode(@NonNull String countryCode) {
687         if (ApConfigUtil.isSoftApDynamicCountryCodeSupported(mContext)
688                 && mCurrentSoftApCapability.areFeaturesSupported(
689                         SoftApCapability.SOFTAP_FEATURE_ACS_OFFLOAD)) {
690             mStateMachine.sendMessage(SoftApStateMachine.CMD_UPDATE_COUNTRY_CODE, countryCode);
691             return true;
692         }
693         return false;
694     }
695 
setCountryCode()696     private int setCountryCode() {
697         int band = mCurrentSoftApConfiguration.getBand();
698         if (TextUtils.isEmpty(mCountryCode)) {
699             if (band == SoftApConfiguration.BAND_5GHZ || band == SoftApConfiguration.BAND_6GHZ) {
700                 // Country code is mandatory for 5GHz/6GHz band.
701                 Log.e(getTag(), "Invalid country code, "
702                         + "required for setting up soft ap in band:" + band);
703                 return ERROR_GENERIC;
704             }
705             // Absence of country code is not fatal for 2Ghz & Any band options.
706             return SUCCESS;
707         }
708         if (!mWifiNative.setApCountryCode(
709                 mApInterfaceName, mCountryCode.toUpperCase(Locale.ROOT))) {
710             if (band == SoftApConfiguration.BAND_5GHZ || band == SoftApConfiguration.BAND_6GHZ) {
711                 // Return an error if failed to set country code when AP is configured for
712                 // 5GHz/6GHz band.
713                 Log.e(getTag(), "Failed to set country code, "
714                         + "required for setting up soft ap in band: " + band);
715                 return ERROR_GENERIC;
716             }
717             // Failure to set country code is not fatal for other band options.
718         }
719         return SUCCESS;
720     }
721 
722     /**
723      * Start a soft AP instance as configured.
724      *
725      * @return integer result code
726      */
startSoftAp()727     private int startSoftAp() {
728         if (SdkLevel.isAtLeastS()) {
729             Log.d(getTag(), "startSoftAp: channels " + mCurrentSoftApConfiguration.getChannels()
730                     + " iface " + mApInterfaceName + " country " + mCountryCode);
731         } else {
732             Log.d(getTag(), "startSoftAp: band " + mCurrentSoftApConfiguration.getBand());
733         }
734 
735         int result = setMacAddress();
736         if (result != SUCCESS) {
737             return result;
738         }
739 
740         // Make a copy of configuration for updating AP band and channel.
741         SoftApConfiguration.Builder localConfigBuilder =
742                 new SoftApConfiguration.Builder(mCurrentSoftApConfiguration);
743 
744         result = ApConfigUtil.updateApChannelConfig(
745                 mWifiNative, mCoexManager, mContext.getResources(), mCountryCode,
746                 localConfigBuilder, mCurrentSoftApConfiguration, mCurrentSoftApCapability);
747         if (result != SUCCESS) {
748             Log.e(getTag(), "Failed to update AP band and channel");
749             return result;
750         }
751 
752         if (mCurrentSoftApConfiguration.isHiddenSsid()) {
753             Log.d(getTag(), "SoftAP is a hidden network");
754         }
755 
756         if (!ApConfigUtil.checkSupportAllConfiguration(
757                 mCurrentSoftApConfiguration, mCurrentSoftApCapability)) {
758             Log.d(getTag(), "Unsupported Configuration detect! config = "
759                     + mCurrentSoftApConfiguration);
760             return ERROR_UNSUPPORTED_CONFIGURATION;
761         }
762 
763         if (!mWifiNative.startSoftAp(mApInterfaceName,
764                   localConfigBuilder.build(),
765                   mOriginalModeConfiguration.getTargetMode() ==  WifiManager.IFACE_IP_MODE_TETHERED,
766                   mSoftApHalCallback)) {
767             Log.e(getTag(), "Soft AP start failed");
768             return ERROR_GENERIC;
769         }
770 
771         mWifiDiagnostics.startLogging(mApInterfaceName);
772         mStartTimestamp = FORMATTER.format(new Date(System.currentTimeMillis()));
773         Log.d(getTag(), "Soft AP is started ");
774 
775         return SUCCESS;
776     }
777 
handleStartSoftApFailure(int result)778     private void handleStartSoftApFailure(int result) {
779         if (result == SUCCESS) {
780             return;
781         }
782         int failureReason = WifiManager.SAP_START_FAILURE_GENERAL;
783         if (result == ERROR_NO_CHANNEL) {
784             failureReason = WifiManager.SAP_START_FAILURE_NO_CHANNEL;
785         } else if (result == ERROR_UNSUPPORTED_CONFIGURATION) {
786             failureReason = WifiManager.SAP_START_FAILURE_UNSUPPORTED_CONFIGURATION;
787         }
788         updateApState(WifiManager.WIFI_AP_STATE_FAILED,
789                 WifiManager.WIFI_AP_STATE_ENABLING,
790                 failureReason);
791         stopSoftAp();
792         mWifiMetrics.incrementSoftApStartResult(false, failureReason);
793         mModeListener.onStartFailure(SoftApManager.this);
794     }
795 
796     /**
797      * Disconnect all connected clients on active softap interface(s).
798      * This is usually done just before stopSoftAp().
799      */
disconnectAllClients()800     private void disconnectAllClients() {
801         for (WifiClient client : getConnectedClientList()) {
802             mWifiNative.forceClientDisconnect(mApInterfaceName, client.getMacAddress(),
803                     SAP_CLIENT_DISCONNECT_REASON_CODE_UNSPECIFIED);
804         }
805     }
806 
807     /**
808      * Teardown soft AP and teardown the interface.
809      */
stopSoftAp()810     private void stopSoftAp() {
811         disconnectAllClients();
812         mWifiDiagnostics.stopLogging(mApInterfaceName);
813         mWifiNative.teardownInterface(mApInterfaceName);
814         Log.d(getTag(), "Soft AP is stopped");
815     }
816 
addClientToPendingDisconnectionList(WifiClient client, int reason)817     private void addClientToPendingDisconnectionList(WifiClient client, int reason) {
818         Log.d(getTag(), "Fail to disconnect client: " + client.getMacAddress()
819                 + ", add it into pending list");
820         mPendingDisconnectClients.put(client, reason);
821         mStateMachine.getHandler().removeMessages(
822                 SoftApStateMachine.CMD_FORCE_DISCONNECT_PENDING_CLIENTS);
823         mStateMachine.sendMessageDelayed(
824                 SoftApStateMachine.CMD_FORCE_DISCONNECT_PENDING_CLIENTS,
825                 SOFT_AP_PENDING_DISCONNECTION_CHECK_DELAY_MS);
826     }
827 
getConnectedClientList()828     private List<WifiClient> getConnectedClientList() {
829         List<WifiClient> connectedClientList = new ArrayList<>();
830         for (List<WifiClient> it : mConnectedClientWithApInfoMap.values()) {
831             connectedClientList.addAll(it);
832         }
833         return connectedClientList;
834     }
835 
checkSoftApClient(SoftApConfiguration config, WifiClient newClient)836     private boolean checkSoftApClient(SoftApConfiguration config, WifiClient newClient) {
837         if (!mCurrentSoftApCapability.areFeaturesSupported(
838                 SoftApCapability.SOFTAP_FEATURE_CLIENT_FORCE_DISCONNECT)) {
839             return true;
840         }
841 
842         if (mBlockedClientList.contains(newClient.getMacAddress())) {
843             Log.d(getTag(), "Force disconnect for client: " + newClient + "in blocked list");
844             if (!mWifiNative.forceClientDisconnect(
845                     mApInterfaceName, newClient.getMacAddress(),
846                     WifiManager.SAP_CLIENT_BLOCK_REASON_CODE_BLOCKED_BY_USER)) {
847                 addClientToPendingDisconnectionList(newClient,
848                         WifiManager.SAP_CLIENT_BLOCK_REASON_CODE_BLOCKED_BY_USER);
849             }
850             return false;
851         }
852         if (config.isClientControlByUserEnabled()
853                 && !mAllowedClientList.contains(newClient.getMacAddress())) {
854             mSoftApCallback.onBlockedClientConnecting(newClient,
855                     WifiManager.SAP_CLIENT_BLOCK_REASON_CODE_BLOCKED_BY_USER);
856             Log.d(getTag(), "Force disconnect for unauthorized client: " + newClient);
857             if (!mWifiNative.forceClientDisconnect(
858                     mApInterfaceName, newClient.getMacAddress(),
859                     WifiManager.SAP_CLIENT_BLOCK_REASON_CODE_BLOCKED_BY_USER)) {
860                 addClientToPendingDisconnectionList(newClient,
861                         WifiManager.SAP_CLIENT_BLOCK_REASON_CODE_BLOCKED_BY_USER);
862             }
863             return false;
864         }
865         int maxConfig = mCurrentSoftApCapability.getMaxSupportedClients();
866         if (config.getMaxNumberOfClients() > 0) {
867             maxConfig = Math.min(maxConfig, config.getMaxNumberOfClients());
868         }
869 
870         if (getConnectedClientList().size() >= maxConfig) {
871             Log.i(getTag(), "No more room for new client:" + newClient);
872             if (!mWifiNative.forceClientDisconnect(
873                     mApInterfaceName, newClient.getMacAddress(),
874                     WifiManager.SAP_CLIENT_BLOCK_REASON_CODE_NO_MORE_STAS)) {
875                 addClientToPendingDisconnectionList(newClient,
876                         WifiManager.SAP_CLIENT_BLOCK_REASON_CODE_NO_MORE_STAS);
877             }
878             mSoftApCallback.onBlockedClientConnecting(newClient,
879                     WifiManager.SAP_CLIENT_BLOCK_REASON_CODE_NO_MORE_STAS);
880             // Avoid report the max client blocked in the same settings.
881             if (!mEverReportMetricsForMaxClient) {
882                 mWifiMetrics.noteSoftApClientBlocked(maxConfig);
883                 mEverReportMetricsForMaxClient = true;
884             }
885             return false;
886         }
887         return true;
888     }
889 
890     private class SoftApStateMachine extends StateMachine {
891         // Commands for the state machine.
892         public static final int CMD_START = 0;
893         public static final int CMD_STOP = 1;
894         public static final int CMD_FAILURE = 2;
895         public static final int CMD_INTERFACE_STATUS_CHANGED = 3;
896         public static final int CMD_ASSOCIATED_STATIONS_CHANGED = 4;
897         public static final int CMD_NO_ASSOCIATED_STATIONS_TIMEOUT = 5;
898         public static final int CMD_INTERFACE_DESTROYED = 7;
899         public static final int CMD_INTERFACE_DOWN = 8;
900         public static final int CMD_AP_INFO_CHANGED = 9;
901         public static final int CMD_UPDATE_CAPABILITY = 10;
902         public static final int CMD_UPDATE_CONFIG = 11;
903         public static final int CMD_FORCE_DISCONNECT_PENDING_CLIENTS = 12;
904         public static final int CMD_NO_ASSOCIATED_STATIONS_TIMEOUT_ON_ONE_INSTANCE = 13;
905         public static final int CMD_SAFE_CHANNEL_FREQUENCY_CHANGED = 14;
906         public static final int CMD_HANDLE_WIFI_CONNECTED = 15;
907         public static final int CMD_UPDATE_COUNTRY_CODE = 16;
908         public static final int CMD_DRIVER_COUNTRY_CODE_CHANGED = 17;
909         public static final int CMD_DRIVER_COUNTRY_CODE_CHANGE_TIMED_OUT = 18;
910         public static final int CMD_PLUGGED_STATE_CHANGED = 19;
911 
912         private final State mActiveState = new ActiveState();
913         private final State mIdleState;
914         private final State mWaitingForDriverCountryCodeChangedState;
915         private final WaitingState mWaitingForIcmDialogState = new WaitingState(this);
916         private final State mStartedState;
917 
918         private final InterfaceCallback mWifiNativeInterfaceCallback = new InterfaceCallback() {
919             @Override
920             public void onDestroyed(String ifaceName) {
921                 if (mApInterfaceName != null && mApInterfaceName.equals(ifaceName)) {
922                     sendMessage(CMD_INTERFACE_DESTROYED);
923                 }
924             }
925 
926             @Override
927             public void onUp(String ifaceName) {
928                 if (mApInterfaceName != null && mApInterfaceName.equals(ifaceName)) {
929                     sendMessage(CMD_INTERFACE_STATUS_CHANGED, 1);
930                 }
931             }
932 
933             @Override
934             public void onDown(String ifaceName) {
935                 if (mApInterfaceName != null && mApInterfaceName.equals(ifaceName)) {
936                     sendMessage(CMD_INTERFACE_STATUS_CHANGED, 0);
937                 }
938             }
939         };
940 
SoftApStateMachine(Looper looper)941         SoftApStateMachine(Looper looper) {
942             super(TAG, looper);
943 
944             final int threshold =  mContext.getResources().getInteger(
945                     R.integer.config_wifiConfigurationWifiRunnerThresholdInMs);
946             mIdleState = new IdleState(threshold);
947             mWaitingForDriverCountryCodeChangedState =
948                     new WaitingForDriverCountryCodeChangedState(threshold);
949             mStartedState = new StartedState(threshold);
950             // CHECKSTYLE:OFF IndentationCheck
951             addState(mActiveState);
952                 addState(mIdleState, mActiveState);
953                 addState(mWaitingForDriverCountryCodeChangedState, mActiveState);
954                 addState(mWaitingForIcmDialogState, mActiveState);
955                 addState(mStartedState, mActiveState);
956             // CHECKSTYLE:ON IndentationCheck
957 
958             setInitialState(mIdleState);
959             start();
960         }
961 
962 
963         private class ActiveState extends State {
964             @Override
exit()965             public void exit() {
966                 mModeListener.onStopped(SoftApManager.this);
967                 mCmiMonitor.unregisterListener(mCmiListener);
968             }
969         }
970 
971         @Override
getWhatToString(int what)972         protected String getWhatToString(int what) {
973             switch (what) {
974                 case CMD_START:
975                     return "CMD_START";
976                 case CMD_STOP:
977                     return "CMD_STOP";
978                 case CMD_FAILURE:
979                     return "CMD_FAILURE";
980                 case CMD_INTERFACE_STATUS_CHANGED:
981                     return "CMD_INTERFACE_STATUS_CHANGED";
982                 case CMD_ASSOCIATED_STATIONS_CHANGED:
983                     return "CMD_ASSOCIATED_STATIONS_CHANGED";
984                 case CMD_NO_ASSOCIATED_STATIONS_TIMEOUT:
985                     return "CMD_NO_ASSOCIATED_STATIONS_TIMEOUT";
986                 case CMD_INTERFACE_DESTROYED:
987                     return "CMD_INTERFACE_DESTROYED";
988                 case CMD_INTERFACE_DOWN:
989                     return "CMD_INTERFACE_DOWN";
990                 case CMD_AP_INFO_CHANGED:
991                     return "CMD_AP_INFO_CHANGED";
992                 case CMD_UPDATE_CAPABILITY:
993                     return "CMD_UPDATE_CAPABILITY";
994                 case CMD_UPDATE_CONFIG:
995                     return "CMD_UPDATE_CONFIG";
996                 case CMD_FORCE_DISCONNECT_PENDING_CLIENTS:
997                     return "CMD_FORCE_DISCONNECT_PENDING_CLIENTS";
998                 case CMD_NO_ASSOCIATED_STATIONS_TIMEOUT_ON_ONE_INSTANCE:
999                     return "CMD_NO_ASSOCIATED_STATIONS_TIMEOUT_ON_ONE_INSTANCE";
1000                 case CMD_SAFE_CHANNEL_FREQUENCY_CHANGED:
1001                     return "CMD_SAFE_CHANNEL_FREQUENCY_CHANGED";
1002                 case CMD_HANDLE_WIFI_CONNECTED:
1003                     return "CMD_HANDLE_WIFI_CONNECTED";
1004                 case CMD_UPDATE_COUNTRY_CODE:
1005                     return "CMD_UPDATE_COUNTRY_CODE";
1006                 case CMD_DRIVER_COUNTRY_CODE_CHANGED:
1007                     return "CMD_DRIVER_COUNTRY_CODE_CHANGED";
1008                 case CMD_DRIVER_COUNTRY_CODE_CHANGE_TIMED_OUT:
1009                     return "CMD_DRIVER_COUNTRY_CODE_CHANGE_TIMED_OUT";
1010                 case CMD_PLUGGED_STATE_CHANGED:
1011                     return "CMD_PLUGGED_STATE_CHANGED";
1012                 case RunnerState.STATE_ENTER_CMD:
1013                     return "Enter";
1014                 case RunnerState.STATE_EXIT_CMD:
1015                     return "Exit";
1016                 default:
1017                     return "what:" + what;
1018             }
1019         }
1020 
1021         private class IdleState extends RunnerState {
IdleState(int threshold)1022             IdleState(int threshold) {
1023                 super(threshold, mWifiInjector.getWifiHandlerLocalLog());
1024             }
1025 
1026             @Override
enterImpl()1027             public void enterImpl() {
1028                 mApInterfaceName = null;
1029                 mIfaceIsUp = false;
1030                 mIfaceIsDestroyed = false;
1031             }
1032 
1033             @Override
exitImpl()1034             public void exitImpl() {
1035             }
1036 
1037             @Override
getMessageLogRec(int what)1038             String getMessageLogRec(int what) {
1039                 return SoftApManager.class.getSimpleName() + "." + IdleState.class.getSimpleName()
1040                         + "." + getWhatToString(what);
1041             }
1042 
1043             @Override
processMessageImpl(Message message)1044             public boolean processMessageImpl(Message message) {
1045                 switch (message.what) {
1046                     case CMD_STOP:
1047                         quitNow();
1048                         break;
1049                     case CMD_START:
1050                         mRequestorWs = (WorkSource) message.obj;
1051                         WifiSsid wifiSsid = mCurrentSoftApConfiguration != null
1052                                 ? mCurrentSoftApConfiguration.getWifiSsid() : null;
1053                         if (wifiSsid == null || wifiSsid.getBytes().length == 0) {
1054                             Log.e(getTag(), "Unable to start soft AP without valid configuration");
1055                             updateApState(WifiManager.WIFI_AP_STATE_FAILED,
1056                                     WifiManager.WIFI_AP_STATE_DISABLED,
1057                                     WifiManager.SAP_START_FAILURE_GENERAL);
1058                             mWifiMetrics.incrementSoftApStartResult(
1059                                     false, WifiManager.SAP_START_FAILURE_GENERAL);
1060                             mModeListener.onStartFailure(SoftApManager.this);
1061                             break;
1062                         }
1063                         if (isBridgedMode()) {
1064                             boolean isFallbackToSingleAp = false;
1065                             final List<ClientModeManager> cmms =
1066                                     mActiveModeWarden.getClientModeManagers();
1067                             // Checking STA status only when device supports STA + AP concurrency
1068                             // since STA would be dropped when device doesn't support it.
1069                             if (cmms.size() != 0 && mWifiNative.isStaApConcurrencySupported()) {
1070                                 if (ApConfigUtil.isStaWithBridgedModeSupported(mContext)) {
1071                                     for (ClientModeManager cmm
1072                                             : mActiveModeWarden.getClientModeManagers()) {
1073                                         WifiInfo wifiConnectedInfo = cmm.getConnectionInfo();
1074                                         int wifiFrequency = wifiConnectedInfo.getFrequency();
1075                                         if (wifiFrequency > 0
1076                                                 && !mSafeChannelFrequencyList.contains(
1077                                                 wifiFrequency)) {
1078                                             Log.d(getTag(), "Wifi connected to unavailable freq: "
1079                                                     + wifiFrequency);
1080                                             isFallbackToSingleAp = true;
1081                                             break;
1082                                         }
1083                                     }
1084                                 } else {
1085                                     // The client mode exist but DUT doesn't support
1086                                     // STA + bridged AP, we should fallback to single AP mode.
1087                                     Log.d(getTag(), " STA iface exist but device doesn't support"
1088                                             + " STA + Bridged AP");
1089                                     isFallbackToSingleAp = true;
1090                                 }
1091                             }
1092                             if (mWifiNative.isSoftApInstanceDiedHandlerSupported()
1093                                     && !TextUtils.equals(mCountryCode,
1094                                       mCurrentSoftApCapability.getCountryCode())) {
1095                                 Log.i(getTag(), "CountryCode changed, bypass the supported band"
1096                                         + "capability check, mCountryCode = " + mCountryCode
1097                                         + ", base country in SoftApCapability = "
1098                                         + mCurrentSoftApCapability.getCountryCode());
1099                             } else {
1100                                 SoftApConfiguration tempConfig =
1101                                         ApConfigUtil.removeUnavailableBandsFromConfig(
1102                                                 mCurrentSoftApConfiguration,
1103                                                 mCurrentSoftApCapability, mCoexManager, mContext);
1104                                 if (tempConfig == null) {
1105                                     handleStartSoftApFailure(ERROR_UNSUPPORTED_CONFIGURATION);
1106                                     break;
1107                                 }
1108                                 mCurrentSoftApConfiguration = tempConfig;
1109                                 if (mCurrentSoftApConfiguration.getBands().length == 1) {
1110                                     isFallbackToSingleAp = true;
1111                                     Log.i(getTag(), "Removed unavailable bands"
1112                                             + " - fallback to single AP");
1113                                 }
1114                             }
1115                             // Fall back to Single AP if it's not possible to create a Bridged AP.
1116                             if (!mWifiNative.isItPossibleToCreateBridgedApIface(mRequestorWs)) {
1117                                 isFallbackToSingleAp = true;
1118                             }
1119                             // Fall back to single AP if creating a single AP does not require
1120                             // destroying an existing iface, but creating a bridged AP does.
1121                             if (mWifiNative.shouldDowngradeToSingleApForConcurrency(mRequestorWs)) {
1122                                 Log.d(getTag(), "Creating bridged AP will destroy an existing"
1123                                         + " iface, but single AP will not.");
1124                                 isFallbackToSingleAp = true;
1125                             }
1126                             if (isFallbackToSingleAp) {
1127                                 int newSingleApBand = 0;
1128                                 for (int configuredBand : mCurrentSoftApConfiguration.getBands()) {
1129                                     newSingleApBand |= configuredBand;
1130                                 }
1131                                 newSingleApBand = ApConfigUtil.append24GToBandIf24GSupported(
1132                                         newSingleApBand, mContext);
1133                                 Log.i(getTag(), "Fallback to single AP mode with band "
1134                                         + newSingleApBand);
1135                                 mCurrentSoftApConfiguration =
1136                                         new SoftApConfiguration.Builder(mCurrentSoftApConfiguration)
1137                                         .setBand(newSingleApBand)
1138                                         .build();
1139                             }
1140                         }
1141 
1142                         // Remove 6GHz from requested bands if security type is restricted
1143                         // Note: 6GHz only band is already handled by initial validation
1144                         SoftApConfiguration tempConfig =
1145                                 ApConfigUtil.remove6gBandForUnsupportedSecurity(
1146                                     mCurrentSoftApConfiguration);
1147                         if (tempConfig == null) {
1148                             handleStartSoftApFailure(ERROR_UNSUPPORTED_CONFIGURATION);
1149                             break;
1150                         }
1151                         mCurrentSoftApConfiguration = tempConfig;
1152                         // Don't show the ICM dialog if this is for tethering.
1153                         boolean bypassDialog = mOriginalModeConfiguration.getTargetMode()
1154                                 == WifiManager.IFACE_IP_MODE_TETHERED;
1155                         int icmResult = mInterfaceConflictManager
1156                                 .manageInterfaceConflictForStateMachine(
1157                                         TAG, message, mStateMachine, mWaitingForIcmDialogState,
1158                                         mIdleState, isBridgeRequired()
1159                                                 ? HalDeviceManager.HDM_CREATE_IFACE_AP_BRIDGE
1160                                                 : HalDeviceManager.HDM_CREATE_IFACE_AP,
1161                                         mRequestorWs, bypassDialog);
1162                         if (icmResult == InterfaceConflictManager.ICM_ABORT_COMMAND) {
1163                             Log.e(getTag(), "User refused to set up interface");
1164                             updateApState(WifiManager.WIFI_AP_STATE_FAILED,
1165                                     WifiManager.WIFI_AP_STATE_DISABLED,
1166                                     WifiManager.SAP_START_FAILURE_USER_REJECTED);
1167                             mModeListener.onStartFailure(SoftApManager.this);
1168                             break;
1169                         } else if (icmResult
1170                                 == InterfaceConflictManager.ICM_SKIP_COMMAND_WAIT_FOR_USER) {
1171                             break;
1172                         }
1173 
1174                         mApInterfaceName = mWifiNative.setupInterfaceForSoftApMode(
1175                                 mWifiNativeInterfaceCallback, mRequestorWs,
1176                                 mCurrentSoftApConfiguration.getBand(), isBridgeRequired(),
1177                                 SoftApManager.this);
1178                         if (TextUtils.isEmpty(mApInterfaceName)) {
1179                             Log.e(getTag(), "setup failure when creating ap interface.");
1180                             updateApState(WifiManager.WIFI_AP_STATE_FAILED,
1181                                     WifiManager.WIFI_AP_STATE_DISABLED,
1182                                     WifiManager.SAP_START_FAILURE_GENERAL);
1183                             mWifiMetrics.incrementSoftApStartResult(
1184                                     false, WifiManager.SAP_START_FAILURE_GENERAL);
1185                             mModeListener.onStartFailure(SoftApManager.this);
1186                             break;
1187                         }
1188                         mSoftApNotifier.dismissSoftApShutdownTimeoutExpiredNotification();
1189                         updateApState(WifiManager.WIFI_AP_STATE_ENABLING,
1190                                 WifiManager.WIFI_AP_STATE_DISABLED, 0);
1191                         int result = setCountryCode();
1192                         if (result != SUCCESS) {
1193                             handleStartSoftApFailure(result);
1194                             break;
1195                         }
1196                         if (mContext.getResources().getBoolean(
1197                                 R.bool.config_wifiDriverSupportedNl80211RegChangedEvent)
1198                                 && !TextUtils.isEmpty(mCountryCode)
1199                                 && !TextUtils.equals(
1200                                         mCountryCode, mCurrentSoftApCapability.getCountryCode())) {
1201                             Log.i(getTag(), "Need to wait for driver country code update before"
1202                                     + " starting");
1203                             transitionTo(mWaitingForDriverCountryCodeChangedState);
1204                             break;
1205                         }
1206                         result = startSoftAp();
1207                         if (result != SUCCESS) {
1208                             handleStartSoftApFailure(result);
1209                             break;
1210                         }
1211                         transitionTo(mStartedState);
1212                         break;
1213                     case CMD_UPDATE_CAPABILITY:
1214                         SoftApCapability capability = (SoftApCapability) message.obj;
1215                         mCurrentSoftApCapability = new SoftApCapability(capability);
1216                         updateSafeChannelFrequencyList();
1217                         break;
1218                     case CMD_UPDATE_CONFIG:
1219                         SoftApConfiguration newConfig = (SoftApConfiguration) message.obj;
1220                         Log.d(getTag(), "Configuration changed to " + newConfig);
1221                         // Idle mode, update all configurations.
1222                         mCurrentSoftApConfiguration = newConfig;
1223                         configureInternalConfiguration();
1224                         break;
1225                     case CMD_UPDATE_COUNTRY_CODE:
1226                         String countryCode = (String) message.obj;
1227                         if (!TextUtils.isEmpty(countryCode)) {
1228                             mCountryCode = countryCode;
1229                         }
1230                         break;
1231                     default:
1232                         // Ignore all other commands.
1233                         break;
1234                 }
1235 
1236                 return HANDLED;
1237             }
1238         }
1239 
1240         private class WaitingForDriverCountryCodeChangedState extends RunnerState {
1241             private static final int TIMEOUT_MS = 5_000;
1242 
1243             private final WifiCountryCode.ChangeListener mCountryCodeChangeListener =
1244                     countryCode -> sendMessage(CMD_DRIVER_COUNTRY_CODE_CHANGED, countryCode);
1245 
WaitingForDriverCountryCodeChangedState(int threshold)1246             WaitingForDriverCountryCodeChangedState(int threshold) {
1247                 super(threshold, mWifiInjector.getWifiHandlerLocalLog());
1248             }
1249 
1250             @Override
enterImpl()1251             void enterImpl() {
1252                 mWifiInjector.getWifiCountryCode().registerListener(mCountryCodeChangeListener);
1253                 sendMessageDelayed(CMD_DRIVER_COUNTRY_CODE_CHANGE_TIMED_OUT, TIMEOUT_MS);
1254             }
1255 
1256             @Override
exitImpl()1257             void exitImpl() {
1258                 mWifiInjector.getWifiCountryCode().unregisterListener(mCountryCodeChangeListener);
1259                 removeMessages(CMD_DRIVER_COUNTRY_CODE_CHANGE_TIMED_OUT);
1260             }
1261 
1262             @Override
processMessageImpl(Message message)1263             boolean processMessageImpl(Message message) {
1264                 if (message.what == CMD_DRIVER_COUNTRY_CODE_CHANGED) {
1265                     if (!TextUtils.equals(mCountryCode, (String) message.obj)) {
1266                         Log.i(getTag(), "Ignore country code changed: " + message.obj);
1267                         return HANDLED;
1268                     }
1269                     Log.i(getTag(), "Driver country code change to " + message.obj
1270                             + ", continue starting.");
1271                     mCurrentSoftApCapability.setCountryCode(mCountryCode);
1272                     mCurrentSoftApCapability =
1273                             ApConfigUtil.updateSoftApCapabilityWithAvailableChannelList(
1274                                     mCurrentSoftApCapability, mContext, mWifiNative);
1275                     updateSafeChannelFrequencyList();
1276                 } else if (message.what == CMD_DRIVER_COUNTRY_CODE_CHANGE_TIMED_OUT) {
1277                     Log.i(getTag(), "Timed out waiting for driver country code change, "
1278                             + "continue starting anyway.");
1279                 } else {
1280                     Log.i(getTag(), "Defer " + getWhatToString(message.what)
1281                             + " while waiting for driver country code change.");
1282                     deferMessage(message);
1283                     return HANDLED;
1284                 }
1285                 int result = startSoftAp();
1286                 if (result != SUCCESS) {
1287                     handleStartSoftApFailure(result);
1288                     transitionTo(mIdleState);
1289                     return HANDLED;
1290                 }
1291                 transitionTo(mStartedState);
1292                 return HANDLED;
1293             }
1294 
1295             @Override
getMessageLogRec(int what)1296             String getMessageLogRec(int what) {
1297                 return SoftApManager.class.getSimpleName() + "." + RunnerState.class.getSimpleName()
1298                         + "." + getWhatToString(what);
1299             }
1300         }
1301 
1302         private class StartedState extends RunnerState {
StartedState(int threshold)1303             StartedState(int threshold) {
1304                 super(threshold, mWifiInjector.getWifiHandlerLocalLog());
1305             }
1306 
1307             BroadcastReceiver mBatteryPluggedReceiver = new BroadcastReceiver() {
1308                 @Override
1309                 public void onReceive(Context context, Intent intent) {
1310                     sendMessage(CMD_PLUGGED_STATE_CHANGED,
1311                             intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0));
1312                 }
1313             };
1314 
rescheduleBothBridgedInstancesTimeoutMessage()1315             private void rescheduleBothBridgedInstancesTimeoutMessage() {
1316                 Set<String> instances = mCurrentSoftApInfoMap.keySet();
1317                 String HighestFrequencyInstance = getHighestFrequencyInstance(instances);
1318                 // Schedule bridged mode timeout on all instances if needed
1319                 for (String instance : instances) {
1320                     long timeout = getShutdownIdleInstanceInBridgedModeTimeoutMillis();
1321                     if (!TextUtils.equals(instance, HighestFrequencyInstance)) {
1322                         //  Make sure that we shutdown the higher frequency instance first by adding
1323                         //  offset for lower frequency instance.
1324                         timeout += SCHEDULE_IDLE_INSTANCE_SHUTDOWN_TIMEOUT_DELAY_MS;
1325                     }
1326                     rescheduleTimeoutMessageIfNeeded(instance, timeout);
1327                 }
1328             }
1329 
1330             /**
1331              * Schedule timeout message depends on Soft Ap instance
1332              *
1333              * @param changedInstance the schedule should change on specific instance only.
1334              *                        If changedInstance is mApInterfaceName, it means that
1335              *                        we need to reschedule all of timeout message.
1336              */
rescheduleTimeoutMessages(@onNull String changedInstance)1337             private void rescheduleTimeoutMessages(@NonNull String changedInstance) {
1338                 // Don't trigger bridged mode shutdown timeout when only one active instance
1339                 // In Dual AP, one instance may already be closed due to LTE coexistence or DFS
1340                 // restrictions or due to inactivity. i.e. mCurrentSoftApInfoMap.size() is 1)
1341                 if (isBridgedMode() &&  mCurrentSoftApInfoMap.size() == 2) {
1342                     if (TextUtils.equals(mApInterfaceName, changedInstance)) {
1343                         rescheduleBothBridgedInstancesTimeoutMessage();
1344                     } else {
1345                         rescheduleTimeoutMessageIfNeeded(changedInstance,
1346                                 getShutdownIdleInstanceInBridgedModeTimeoutMillis());
1347                     }
1348                 }
1349 
1350                 // Always evaluate timeout schedule on tetheringInterface
1351                 rescheduleTimeoutMessageIfNeeded(mApInterfaceName, getShutdownTimeoutMillis());
1352             }
1353 
removeIfaceInstanceFromBridgedApIface(String instanceName)1354             private void removeIfaceInstanceFromBridgedApIface(String instanceName) {
1355                 if (TextUtils.isEmpty(instanceName)) {
1356                     return;
1357                 }
1358                 if (mCurrentSoftApInfoMap.containsKey(instanceName)) {
1359                     Log.i(getTag(), "remove instance " + instanceName + "("
1360                             + mCurrentSoftApInfoMap.get(instanceName).getFrequency()
1361                             + ") from bridged iface " + mApInterfaceName);
1362                     mWifiNative.removeIfaceInstanceFromBridgedApIface(mApInterfaceName,
1363                             instanceName);
1364                     // Remove the info and update it.
1365                     updateSoftApInfo(mCurrentSoftApInfoMap.get(instanceName), true);
1366                 }
1367             }
1368 
1369             /**
1370              * Schedule the timeout message when timeout control is enabled and there is no client
1371              * connect to the instance.
1372              *
1373              * @param instance The key of the {@code mSoftApTimeoutMessageMap},
1374              *                 @see mSoftApTimeoutMessageMap for details.
1375              */
rescheduleTimeoutMessageIfNeeded(String instance, long timeoutValue)1376             private void rescheduleTimeoutMessageIfNeeded(String instance, long timeoutValue) {
1377                 final boolean isTetheringInterface =
1378                         TextUtils.equals(mApInterfaceName, instance);
1379                 final boolean timeoutEnabled = isTetheringInterface ? mTimeoutEnabled
1380                         : (mBridgedModeOpportunisticsShutdownTimeoutEnabled && !mIsPlugged);
1381                 final int clientNumber = isTetheringInterface
1382                         ? getConnectedClientList().size()
1383                         : mConnectedClientWithApInfoMap.get(instance).size();
1384                 Log.d(getTag(), "rescheduleTimeoutMessageIfNeeded " + instance + ", timeoutEnabled="
1385                         + timeoutEnabled + ", isPlugged=" + mIsPlugged + ", clientNumber="
1386                         + clientNumber);
1387                 if (!timeoutEnabled || clientNumber != 0) {
1388                     cancelTimeoutMessage(instance);
1389                     return;
1390                 }
1391                 scheduleTimeoutMessage(instance, timeoutValue);
1392             }
1393 
scheduleTimeoutMessage(String instance, long timeout)1394             private void scheduleTimeoutMessage(String instance, long timeout) {
1395                 if (mSoftApTimeoutMessageMap.containsKey(instance)) {
1396                     mSoftApTimeoutMessageMap.get(instance).schedule(
1397                             SystemClock.elapsedRealtime() + timeout);
1398                     Log.d(getTag(), "Timeout message scheduled, on " + instance + ", delay = "
1399                             + timeout);
1400                 }
1401             }
1402 
cancelTimeoutMessage(String instance)1403             private void cancelTimeoutMessage(String instance) {
1404                 if (mSoftApTimeoutMessageMap.containsKey(instance)) {
1405                     mSoftApTimeoutMessageMap.get(instance).cancel();
1406                     Log.d(getTag(), "Timeout message canceled on " + instance);
1407                 }
1408             }
1409 
1410             /**
1411              * When configuration changed, it need to force some clients disconnect to match the
1412              * configuration.
1413              */
updateClientConnection()1414             private void updateClientConnection() {
1415                 if (!mCurrentSoftApCapability.areFeaturesSupported(
1416                         SoftApCapability.SOFTAP_FEATURE_CLIENT_FORCE_DISCONNECT)) {
1417                     return;
1418                 }
1419                 final int maxAllowedClientsByHardwareAndCarrier =
1420                         mCurrentSoftApCapability.getMaxSupportedClients();
1421                 final int userApConfigMaxClientCount =
1422                         mCurrentSoftApConfiguration.getMaxNumberOfClients();
1423                 int finalMaxClientCount = maxAllowedClientsByHardwareAndCarrier;
1424                 if (userApConfigMaxClientCount > 0) {
1425                     finalMaxClientCount = Math.min(userApConfigMaxClientCount,
1426                             maxAllowedClientsByHardwareAndCarrier);
1427                 }
1428                 List<WifiClient> currentClients = getConnectedClientList();
1429                 int targetDisconnectClientNumber = currentClients.size() - finalMaxClientCount;
1430                 List<WifiClient> allowedConnectedList = new ArrayList<>();
1431                 Iterator<WifiClient> iterator = currentClients.iterator();
1432                 while (iterator.hasNext()) {
1433                     WifiClient client = iterator.next();
1434                     if (mBlockedClientList.contains(client.getMacAddress())
1435                               || (mCurrentSoftApConfiguration.isClientControlByUserEnabled()
1436                               && !mAllowedClientList.contains(client.getMacAddress()))) {
1437                         Log.d(getTag(), "Force disconnect for not allowed client: " + client);
1438                         if (!mWifiNative.forceClientDisconnect(
1439                                 mApInterfaceName, client.getMacAddress(),
1440                                 WifiManager.SAP_CLIENT_BLOCK_REASON_CODE_BLOCKED_BY_USER)) {
1441                             addClientToPendingDisconnectionList(client,
1442                                     WifiManager.SAP_CLIENT_BLOCK_REASON_CODE_BLOCKED_BY_USER);
1443                         }
1444                         targetDisconnectClientNumber--;
1445                     } else {
1446                         allowedConnectedList.add(client);
1447                     }
1448                 }
1449 
1450                 if (targetDisconnectClientNumber > 0) {
1451                     Iterator<WifiClient> allowedClientIterator = allowedConnectedList.iterator();
1452                     while (allowedClientIterator.hasNext()) {
1453                         if (targetDisconnectClientNumber == 0) break;
1454                         WifiClient allowedClient = allowedClientIterator.next();
1455                         Log.d(getTag(), "Force disconnect for client due to no more room: "
1456                                 + allowedClient);
1457                         if (!mWifiNative.forceClientDisconnect(
1458                                 mApInterfaceName, allowedClient.getMacAddress(),
1459                                 WifiManager.SAP_CLIENT_BLOCK_REASON_CODE_NO_MORE_STAS)) {
1460                             addClientToPendingDisconnectionList(allowedClient,
1461                                     WifiManager.SAP_CLIENT_BLOCK_REASON_CODE_NO_MORE_STAS);
1462                         }
1463                         targetDisconnectClientNumber--;
1464                     }
1465                 }
1466             }
1467 
1468             /**
1469              * Set stations associated with this soft AP
1470              * @param client The station for which connection state changed.
1471              * @param isConnected True for the connection changed to connect, otherwise false.
1472              */
updateConnectedClients(WifiClient client, boolean isConnected)1473             private void updateConnectedClients(WifiClient client, boolean isConnected) {
1474                 if (client == null) {
1475                     return;
1476                 }
1477 
1478                 if (null != mPendingDisconnectClients.remove(client)) {
1479                     Log.d(getTag(), "Remove client: " + client.getMacAddress()
1480                             + "from pending disconnectionlist");
1481                 }
1482 
1483                 String apInstanceIdentifier = client.getApInstanceIdentifier();
1484                 List clientList = mConnectedClientWithApInfoMap.computeIfAbsent(
1485                         apInstanceIdentifier, k -> new ArrayList<>());
1486                 int index = clientList.indexOf(client);
1487 
1488                 if ((index != -1) == isConnected) {
1489                     Log.e(getTag(), "Drop client connection event, client "
1490                             + client + "isConnected: " + isConnected
1491                             + " , duplicate event or client is blocked");
1492                     return;
1493                 }
1494                 if (isConnected) {
1495                     boolean isAllow = checkSoftApClient(mCurrentSoftApConfiguration, client);
1496                     if (isAllow) {
1497                         clientList.add(client);
1498                     } else {
1499                         return;
1500                     }
1501                 } else {
1502                     if (null == clientList.remove(index)) {
1503                         Log.e(getTag(), "client doesn't exist in list, it should NOT happen");
1504                     }
1505                 }
1506 
1507                 // Update clients list.
1508                 mConnectedClientWithApInfoMap.put(apInstanceIdentifier, clientList);
1509                 SoftApInfo currentInfoWithClientsChanged = mCurrentSoftApInfoMap
1510                         .get(apInstanceIdentifier);
1511                 Log.d(getTag(), "The connected wifi stations have changed with count: "
1512                         + clientList.size() + ": " + clientList + " on the AP which info is "
1513                         + currentInfoWithClientsChanged);
1514 
1515                 if (mSoftApCallback != null) {
1516                     mSoftApCallback.onConnectedClientsOrInfoChanged(mCurrentSoftApInfoMap,
1517                             mConnectedClientWithApInfoMap, isBridgeRequired());
1518                 } else {
1519                     Log.e(getTag(),
1520                             "SoftApCallback is null. Dropping ConnectedClientsChanged event.");
1521                 }
1522 
1523                 mWifiMetrics.addSoftApNumAssociatedStationsChangedEvent(
1524                         getConnectedClientList().size(),
1525                         mConnectedClientWithApInfoMap.get(apInstanceIdentifier).size(),
1526                         mOriginalModeConfiguration.getTargetMode(),
1527                         mCurrentSoftApInfoMap.get(apInstanceIdentifier));
1528 
1529                 rescheduleTimeoutMessages(apInstanceIdentifier);
1530             }
1531 
1532             /**
1533              * @param apInfo, the new SoftApInfo changed. Null used to clean up.
1534              */
updateSoftApInfo(@ullable SoftApInfo apInfo, boolean isRemoved)1535             private void updateSoftApInfo(@Nullable SoftApInfo apInfo, boolean isRemoved) {
1536                 Log.d(getTag(), "SoftApInfo update " + apInfo + ", isRemoved: " + isRemoved);
1537                 if (apInfo == null) {
1538                     // Clean up
1539                     mCurrentSoftApInfoMap.clear();
1540                     mConnectedClientWithApInfoMap.clear();
1541                     mSoftApCallback.onConnectedClientsOrInfoChanged(mCurrentSoftApInfoMap,
1542                             mConnectedClientWithApInfoMap, isBridgeRequired());
1543                     return;
1544                 }
1545                 String changedInstance = apInfo.getApInstanceIdentifier();
1546                 if (apInfo.equals(mCurrentSoftApInfoMap.get(changedInstance))) {
1547                     if (isRemoved) {
1548                         boolean isClientConnected =
1549                                 mConnectedClientWithApInfoMap.get(changedInstance).size() > 0;
1550                         mCurrentSoftApInfoMap.remove(changedInstance);
1551                         mSoftApTimeoutMessageMap.remove(changedInstance);
1552                         mConnectedClientWithApInfoMap.remove(changedInstance);
1553                         mSoftApCallback.onConnectedClientsOrInfoChanged(mCurrentSoftApInfoMap,
1554                                 mConnectedClientWithApInfoMap, isBridgeRequired());
1555                         if (isClientConnected) {
1556                             mWifiMetrics.addSoftApNumAssociatedStationsChangedEvent(
1557                                     getConnectedClientList().size(), 0,
1558                                     mOriginalModeConfiguration.getTargetMode(), apInfo);
1559                         }
1560                         if (isBridgeRequired()) {
1561                             mWifiMetrics.addSoftApInstanceDownEventInDualMode(
1562                                     mOriginalModeConfiguration.getTargetMode(), apInfo);
1563                         }
1564                     }
1565                     return;
1566                 }
1567 
1568                 // Make sure an empty client list is created when info updated
1569                 List clientList = mConnectedClientWithApInfoMap.computeIfAbsent(
1570                         changedInstance, k -> new ArrayList<>());
1571 
1572                 if (clientList.size() != 0) {
1573                     Log.e(getTag(), "The info: " + apInfo
1574                             + " changed when client connected, it should NOT happen!!");
1575                 }
1576 
1577                 mCurrentSoftApInfoMap.put(changedInstance, new SoftApInfo(apInfo));
1578                 mSoftApCallback.onConnectedClientsOrInfoChanged(mCurrentSoftApInfoMap,
1579                         mConnectedClientWithApInfoMap, isBridgeRequired());
1580 
1581                 boolean isNeedToScheduleTimeoutMessage = false;
1582                 if (!mSoftApTimeoutMessageMap.containsKey(mApInterfaceName)) {
1583                     // First info update, create WakeupMessage for mApInterfaceName.
1584                     mSoftApTimeoutMessageMap.put(mApInterfaceName, new WakeupMessage(
1585                             mContext, mStateMachine.getHandler(),
1586                             SOFT_AP_SEND_MESSAGE_TIMEOUT_TAG + mApInterfaceName,
1587                             SoftApStateMachine.CMD_NO_ASSOCIATED_STATIONS_TIMEOUT));
1588                     isNeedToScheduleTimeoutMessage = true;
1589                 }
1590 
1591                 if (isBridgedMode()
1592                         && !mSoftApTimeoutMessageMap.containsKey(changedInstance)) {
1593                     mSoftApTimeoutMessageMap.put(changedInstance,
1594                             new WakeupMessage(mContext, mStateMachine.getHandler(),
1595                             SOFT_AP_SEND_MESSAGE_TIMEOUT_TAG + changedInstance,
1596                             SoftApStateMachine.CMD_NO_ASSOCIATED_STATIONS_TIMEOUT_ON_ONE_INSTANCE,
1597                             0, 0, changedInstance));
1598                     isNeedToScheduleTimeoutMessage = true;
1599                 }
1600 
1601                 // Trigger schedule after mCurrentSoftApInfoMap is updated.
1602                 if (isNeedToScheduleTimeoutMessage) {
1603                     rescheduleTimeoutMessages(mApInterfaceName);
1604                 }
1605 
1606                 // ignore invalid freq and softap disable case for metrics
1607                 if (apInfo.getFrequency() > 0
1608                         && apInfo.getBandwidth() != SoftApInfo.CHANNEL_WIDTH_INVALID) {
1609                     mWifiMetrics.addSoftApChannelSwitchedEvent(
1610                             new ArrayList<>(mCurrentSoftApInfoMap.values()),
1611                             mOriginalModeConfiguration.getTargetMode(), isBridgeRequired());
1612                     updateUserBandPreferenceViolationMetricsIfNeeded(apInfo);
1613                 }
1614             }
1615 
onUpChanged(boolean isUp)1616             private void onUpChanged(boolean isUp) {
1617                 if (isUp == mIfaceIsUp) {
1618                     return;  // no change
1619                 }
1620 
1621                 mIfaceIsUp = isUp;
1622                 if (isUp) {
1623                     Log.d(getTag(), "SoftAp is ready for use");
1624                     updateApState(WifiManager.WIFI_AP_STATE_ENABLED,
1625                             WifiManager.WIFI_AP_STATE_ENABLING, 0);
1626                     mModeListener.onStarted(SoftApManager.this);
1627                     mWifiMetrics.incrementSoftApStartResult(true, 0);
1628                     mCurrentSoftApInfoMap.clear();
1629                     mConnectedClientWithApInfoMap.clear();
1630                     if (mSoftApCallback != null) {
1631                         mSoftApCallback.onConnectedClientsOrInfoChanged(mCurrentSoftApInfoMap,
1632                                 mConnectedClientWithApInfoMap, isBridgeRequired());
1633                     }
1634                 } else {
1635                     // the interface was up, but goes down
1636                     sendMessage(CMD_INTERFACE_DOWN);
1637                 }
1638                 mWifiMetrics.addSoftApUpChangedEvent(isUp,
1639                         mOriginalModeConfiguration.getTargetMode(),
1640                         mDefaultShutdownTimeoutMillis, isBridgeRequired());
1641                 if (isUp) {
1642                     mWifiMetrics.updateSoftApConfiguration(mCurrentSoftApConfiguration,
1643                             mOriginalModeConfiguration.getTargetMode(), isBridgeRequired());
1644                     mWifiMetrics.updateSoftApCapability(mCurrentSoftApCapability,
1645                             mOriginalModeConfiguration.getTargetMode(), isBridgeRequired());
1646                 }
1647             }
1648 
1649             @Override
enterImpl()1650             public void enterImpl() {
1651                 mIfaceIsUp = false;
1652                 mIfaceIsDestroyed = false;
1653                 onUpChanged(mWifiNative.isInterfaceUp(mApInterfaceName));
1654 
1655                 Handler handler = mStateMachine.getHandler();
1656                 if (SdkLevel.isAtLeastS()) {
1657                     mCoexManager.registerCoexListener(mCoexListener);
1658                 }
1659                 if (mIsDisableShutDownBridgedModeIdleInstanceTimerWhenPlugged) {
1660                     Intent stickyIntent = mContext.registerReceiver(mBatteryPluggedReceiver,
1661                             new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
1662                     mIsPlugged = stickyIntent.getIntExtra(BatteryManager.EXTRA_STATUS, 0) != 0;
1663                 }
1664                 mSarManager.setSapWifiState(WifiManager.WIFI_AP_STATE_ENABLED);
1665                 Log.d(getTag(), "Resetting connected clients on start");
1666                 mConnectedClientWithApInfoMap.clear();
1667                 mPendingDisconnectClients.clear();
1668                 mEverReportMetricsForMaxClient = false;
1669             }
1670 
1671             @Override
exitImpl()1672             public void exitImpl() {
1673                 if (!mIfaceIsDestroyed) {
1674                     stopSoftAp();
1675                 }
1676                 if (SdkLevel.isAtLeastS()) {
1677                     mCoexManager.unregisterCoexListener(mCoexListener);
1678                 }
1679                 if (getConnectedClientList().size() != 0) {
1680                     Log.d(getTag(), "Resetting num stations on stop");
1681                     for (List<WifiClient> it : mConnectedClientWithApInfoMap.values()) {
1682                         if (it.size() != 0) {
1683                             mWifiMetrics.addSoftApNumAssociatedStationsChangedEvent(
1684                                     0, 0, mOriginalModeConfiguration.getTargetMode(),
1685                                     mCurrentSoftApInfoMap
1686                                             .get(it.get(0).getApInstanceIdentifier()));
1687                         }
1688                     }
1689                     mConnectedClientWithApInfoMap.clear();
1690                     if (mSoftApCallback != null) {
1691                         mSoftApCallback.onConnectedClientsOrInfoChanged(mCurrentSoftApInfoMap,
1692                                 mConnectedClientWithApInfoMap, isBridgeRequired());
1693                     }
1694                 }
1695                 mPendingDisconnectClients.clear();
1696                 for (String key : mSoftApTimeoutMessageMap.keySet()) {
1697                     cancelTimeoutMessage(key);
1698                 }
1699                 mSoftApTimeoutMessageMap.clear();
1700                 if (mIsDisableShutDownBridgedModeIdleInstanceTimerWhenPlugged) {
1701                     mContext.unregisterReceiver(mBatteryPluggedReceiver);
1702                 }
1703                 // Need this here since we are exiting |Started| state and won't handle any
1704                 // future CMD_INTERFACE_STATUS_CHANGED events after this point
1705                 mWifiMetrics.addSoftApUpChangedEvent(false,
1706                         mOriginalModeConfiguration.getTargetMode(),
1707                         mDefaultShutdownTimeoutMillis, isBridgeRequired());
1708                 updateApState(WifiManager.WIFI_AP_STATE_DISABLED,
1709                         WifiManager.WIFI_AP_STATE_DISABLING, 0);
1710 
1711                 mSarManager.setSapWifiState(WifiManager.WIFI_AP_STATE_DISABLED);
1712 
1713                 mApInterfaceName = null;
1714                 mIfaceIsUp = false;
1715                 mIfaceIsDestroyed = false;
1716                 mRole = null;
1717                 updateSoftApInfo(null, false);
1718             }
1719 
updateUserBandPreferenceViolationMetricsIfNeeded(SoftApInfo apInfo)1720             private void updateUserBandPreferenceViolationMetricsIfNeeded(SoftApInfo apInfo) {
1721                 // The band preference violation only need to detect in single AP mode.
1722                 if (isBridgeRequired()) return;
1723                 int band = mCurrentSoftApConfiguration.getBand();
1724                 boolean bandPreferenceViolated =
1725                         (ScanResult.is24GHz(apInfo.getFrequency())
1726                             && !ApConfigUtil.containsBand(band,
1727                                     SoftApConfiguration.BAND_2GHZ))
1728                         || (ScanResult.is5GHz(apInfo.getFrequency())
1729                             && !ApConfigUtil.containsBand(band,
1730                                     SoftApConfiguration.BAND_5GHZ))
1731                         || (ScanResult.is6GHz(apInfo.getFrequency())
1732                             && !ApConfigUtil.containsBand(band,
1733                                     SoftApConfiguration.BAND_6GHZ));
1734 
1735                 if (bandPreferenceViolated) {
1736                     Log.e(getTag(), "Channel does not satisfy user band preference: "
1737                             + apInfo.getFrequency());
1738                     mWifiMetrics.incrementNumSoftApUserBandPreferenceUnsatisfied();
1739                 }
1740             }
1741 
1742             @Override
getMessageLogRec(int what)1743             String getMessageLogRec(int what) {
1744                 return SoftApManager.class.getSimpleName() + "." + RunnerState.class.getSimpleName()
1745                         + "." + getWhatToString(what);
1746             }
1747 
1748             @Override
processMessageImpl(Message message)1749             public boolean processMessageImpl(Message message) {
1750                 switch (message.what) {
1751                     case CMD_ASSOCIATED_STATIONS_CHANGED:
1752                         if (!(message.obj instanceof WifiClient)) {
1753                             Log.e(getTag(), "Invalid type returned for"
1754                                     + " CMD_ASSOCIATED_STATIONS_CHANGED");
1755                             break;
1756                         }
1757                         boolean isConnected = (message.arg1 == 1);
1758                         WifiClient client = (WifiClient) message.obj;
1759                         Log.d(getTag(), "CMD_ASSOCIATED_STATIONS_CHANGED, Client: "
1760                                 + client.getMacAddress().toString() + " isConnected: "
1761                                 + isConnected);
1762                         updateConnectedClients(client, isConnected);
1763                         break;
1764                     case CMD_AP_INFO_CHANGED:
1765                         if (!(message.obj instanceof SoftApInfo)) {
1766                             Log.e(getTag(), "Invalid type returned for"
1767                                     + " CMD_AP_INFO_CHANGED");
1768                             break;
1769                         }
1770                         SoftApInfo apInfo = (SoftApInfo) message.obj;
1771                         if (apInfo.getFrequency() < 0) {
1772                             Log.e(getTag(), "Invalid ap channel frequency: "
1773                                     + apInfo.getFrequency());
1774                             break;
1775                         }
1776                         // Update shutdown timeout
1777                         apInfo.setAutoShutdownTimeoutMillis(mTimeoutEnabled
1778                                 ? getShutdownTimeoutMillis() : 0);
1779                         updateSoftApInfo(apInfo, false);
1780                         break;
1781                     case CMD_INTERFACE_STATUS_CHANGED:
1782                         boolean isUp = message.arg1 == 1;
1783                         onUpChanged(isUp);
1784                         break;
1785                     case CMD_STOP:
1786                         if (mIfaceIsUp) {
1787                             updateApState(WifiManager.WIFI_AP_STATE_DISABLING,
1788                                     WifiManager.WIFI_AP_STATE_ENABLED, 0);
1789                         } else {
1790                             updateApState(WifiManager.WIFI_AP_STATE_DISABLING,
1791                                     WifiManager.WIFI_AP_STATE_ENABLING, 0);
1792                         }
1793                         quitNow();
1794                         break;
1795                     case CMD_START:
1796                         // Already started, ignore this command.
1797                         break;
1798                     case CMD_NO_ASSOCIATED_STATIONS_TIMEOUT:
1799                         if (!mTimeoutEnabled) {
1800                             Log.i(getTag(), "Timeout message received while timeout is disabled."
1801                                     + " Dropping.");
1802                             break;
1803                         }
1804                         if (getConnectedClientList().size() != 0) {
1805                             Log.i(getTag(), "Timeout message received but has clients. "
1806                                     + "Dropping.");
1807                             break;
1808                         }
1809                         mSoftApNotifier.showSoftApShutdownTimeoutExpiredNotification();
1810                         Log.i(getTag(), "Timeout message received. Stopping soft AP.");
1811                         updateApState(WifiManager.WIFI_AP_STATE_DISABLING,
1812                                 WifiManager.WIFI_AP_STATE_ENABLED, 0);
1813                         quitNow();
1814                         break;
1815                     case CMD_NO_ASSOCIATED_STATIONS_TIMEOUT_ON_ONE_INSTANCE:
1816                         String idleInstance = (String) message.obj;
1817                         if (!isBridgedMode() || mCurrentSoftApInfoMap.size() != 2) {
1818                             Log.d(getTag(), "Ignore Bridged Mode Timeout message received"
1819                                     + " in single AP state. Dropping it from " + idleInstance);
1820                             break;
1821                         }
1822                         if (!mBridgedModeOpportunisticsShutdownTimeoutEnabled) {
1823                             Log.i(getTag(), "Bridged Mode Timeout message received"
1824                                     + " while timeout is disabled. Dropping.");
1825                             break;
1826                         }
1827                         Log.d(getTag(), "Instance idle timout on " + idleInstance);
1828                         removeIfaceInstanceFromBridgedApIface(idleInstance);
1829                         break;
1830                     case CMD_INTERFACE_DESTROYED:
1831                         Log.d(getTag(), "Interface was cleanly destroyed.");
1832                         updateApState(WifiManager.WIFI_AP_STATE_DISABLING,
1833                                 WifiManager.WIFI_AP_STATE_ENABLED, 0);
1834                         mIfaceIsDestroyed = true;
1835                         quitNow();
1836                         break;
1837                     case CMD_FAILURE:
1838                         String instance = (String) message.obj;
1839                         if (isBridgedMode()) {
1840                             List<String> instances =
1841                                     mWifiNative.getBridgedApInstances(mApInterfaceName);
1842                             if (instance != null) {
1843                                 Log.i(getTag(), "receive instanceFailure on " + instance);
1844                                 removeIfaceInstanceFromBridgedApIface(instance);
1845                                 // there is an available instance, keep AP on.
1846                                 if (mCurrentSoftApInfoMap.size() == 1) {
1847                                     break;
1848                                 }
1849                             } else if (mCurrentSoftApInfoMap.size() == 1 && instances != null
1850                                     && instances.size() == 1) {
1851                                 if (!mCurrentSoftApInfoMap.containsKey(instances.get(0))) {
1852                                     // there is an available instance but the info doesn't be
1853                                     // updated, keep AP on and remove unavailable instance info.
1854                                     for (String unavailableInstance
1855                                             : mCurrentSoftApInfoMap.keySet()) {
1856                                         removeIfaceInstanceFromBridgedApIface(unavailableInstance);
1857                                     }
1858                                     break;
1859                                 }
1860                             }
1861                         }
1862                         Log.w(getTag(), "hostapd failure, stop and report failure");
1863                         /* fall through */
1864                     case CMD_INTERFACE_DOWN:
1865                         Log.w(getTag(), "interface error, stop and report failure");
1866                         updateApState(WifiManager.WIFI_AP_STATE_FAILED,
1867                                 WifiManager.WIFI_AP_STATE_ENABLED,
1868                                 WifiManager.SAP_START_FAILURE_GENERAL);
1869                         updateApState(WifiManager.WIFI_AP_STATE_DISABLING,
1870                                 WifiManager.WIFI_AP_STATE_FAILED, 0);
1871                         quitNow();
1872                         break;
1873                     case CMD_UPDATE_CAPABILITY:
1874                         SoftApCapability capability = (SoftApCapability) message.obj;
1875                         mCurrentSoftApCapability = new SoftApCapability(capability);
1876                         mWifiMetrics.updateSoftApCapability(mCurrentSoftApCapability,
1877                                 mOriginalModeConfiguration.getTargetMode(), isBridgeRequired());
1878                         updateClientConnection();
1879                         updateSafeChannelFrequencyList();
1880                         break;
1881                     case CMD_UPDATE_CONFIG:
1882                         SoftApConfiguration newConfig = (SoftApConfiguration) message.obj;
1883                         SoftApConfiguration originalConfig =
1884                                 mOriginalModeConfiguration.getSoftApConfiguration();
1885                         if (!ApConfigUtil.checkConfigurationChangeNeedToRestart(
1886                                 originalConfig, newConfig)) {
1887                             Log.d(getTag(), "Configuration changed to " + newConfig);
1888                             if (mCurrentSoftApConfiguration.getMaxNumberOfClients()
1889                                     != newConfig.getMaxNumberOfClients()) {
1890                                 Log.d(getTag(), "Max Client changed, reset to record the metrics");
1891                                 mEverReportMetricsForMaxClient = false;
1892                             }
1893                             boolean needRescheduleTimeoutMessage =
1894                                     mCurrentSoftApConfiguration.getShutdownTimeoutMillis()
1895                                     != newConfig.getShutdownTimeoutMillis()
1896                                     || mTimeoutEnabled != newConfig.isAutoShutdownEnabled()
1897                                     || mBridgedModeOpportunisticsShutdownTimeoutEnabled
1898                                     != newConfig
1899                                     .isBridgedModeOpportunisticShutdownEnabledInternal();
1900                             updateChangeableConfiguration(newConfig);
1901                             updateClientConnection();
1902                             if (needRescheduleTimeoutMessage) {
1903                                 for (String key : mSoftApTimeoutMessageMap.keySet()) {
1904                                     cancelTimeoutMessage(key);
1905                                 }
1906                                 rescheduleTimeoutMessages(mApInterfaceName);
1907                                 // Update SoftApInfo
1908                                 for (SoftApInfo info : mCurrentSoftApInfoMap.values()) {
1909                                     SoftApInfo newInfo = new SoftApInfo(info);
1910                                     newInfo.setAutoShutdownTimeoutMillis(mTimeoutEnabled
1911                                             ? getShutdownTimeoutMillis() : 0);
1912                                     updateSoftApInfo(newInfo, false);
1913                                 }
1914                             }
1915                             mWifiMetrics.updateSoftApConfiguration(
1916                                     mCurrentSoftApConfiguration,
1917                                     mOriginalModeConfiguration.getTargetMode(),
1918                                     isBridgeRequired());
1919                         } else {
1920                             Log.d(getTag(), "Ignore the config: " + newConfig
1921                                     + " update since it requires restart");
1922                         }
1923                         break;
1924                     case CMD_UPDATE_COUNTRY_CODE:
1925                         String countryCode = (String) message.obj;
1926                         if (!TextUtils.isEmpty(countryCode)
1927                                 && !TextUtils.equals(mCountryCode, countryCode)
1928                                 && mWifiNative.setApCountryCode(
1929                                 mApInterfaceName, countryCode.toUpperCase(Locale.ROOT))) {
1930                             Log.i(getTag(), "Update country code when Soft AP enabled from "
1931                                     + mCountryCode + " to " + countryCode);
1932                             mCountryCode = countryCode;
1933                         }
1934                         break;
1935                     case CMD_FORCE_DISCONNECT_PENDING_CLIENTS:
1936                         if (mPendingDisconnectClients.size() != 0) {
1937                             Log.d(getTag(), "Disconnect pending list is NOT empty");
1938                             mPendingDisconnectClients.forEach((pendingClient, reason)->
1939                                     mWifiNative.forceClientDisconnect(mApInterfaceName,
1940                                     pendingClient.getMacAddress(), reason));
1941                             sendMessageDelayed(
1942                                     SoftApStateMachine.CMD_FORCE_DISCONNECT_PENDING_CLIENTS,
1943                                     SOFT_AP_PENDING_DISCONNECTION_CHECK_DELAY_MS);
1944                         }
1945                         break;
1946                     case CMD_SAFE_CHANNEL_FREQUENCY_CHANGED:
1947                         updateSafeChannelFrequencyList();
1948                         if (!isBridgedMode() || mCurrentSoftApInfoMap.size() != 2) {
1949                             Log.d(getTag(), "Ignore safe channel changed in single AP state");
1950                             break;
1951                         }
1952                         Set<String> unavailableInstances = new HashSet<>();
1953                         for (SoftApInfo currentInfo : mCurrentSoftApInfoMap.values()) {
1954                             int sapFreq = currentInfo.getFrequency();
1955                             if (!mSafeChannelFrequencyList.contains(sapFreq)) {
1956                                 int sapBand = ApConfigUtil.convertFrequencyToBand(sapFreq);
1957                                 if (sapBand != ApConfigUtil.removeUnavailableBands(
1958                                             mCurrentSoftApCapability,
1959                                             sapBand, mCoexManager)) {
1960                                     unavailableInstances.add(currentInfo.getApInstanceIdentifier());
1961                                 }
1962                             }
1963                         }
1964                         removeIfaceInstanceFromBridgedApIface(
1965                                 getHighestFrequencyInstance(unavailableInstances));
1966                         break;
1967                     case CMD_HANDLE_WIFI_CONNECTED:
1968                         if (!isBridgeRequired() || mCurrentSoftApInfoMap.size() != 2) {
1969                             Log.d(getTag(), "Ignore wifi connected in single AP state");
1970                             break;
1971                         }
1972                         WifiInfo wifiInfo = (WifiInfo) message.obj;
1973                         int wifiFreq = wifiInfo.getFrequency();
1974                         String targetShutDownInstance = "";
1975                         if (wifiFreq > 0 && !mSafeChannelFrequencyList.contains(wifiFreq)) {
1976                             Log.i(getTag(), "Wifi connected to freq:" + wifiFreq
1977                                     + " which is unavailable for SAP");
1978                             for (SoftApInfo sapInfo : mCurrentSoftApInfoMap.values()) {
1979                                 if (ApConfigUtil.convertFrequencyToBand(sapInfo.getFrequency())
1980                                           == ApConfigUtil.convertFrequencyToBand(wifiFreq)) {
1981                                     targetShutDownInstance = sapInfo.getApInstanceIdentifier();
1982                                     Log.d(getTag(), "Remove the " + targetShutDownInstance
1983                                             + " instance which is running on the same band as "
1984                                             + "the wifi connection on an unsafe channel");
1985                                     break;
1986                                 }
1987                             }
1988                             // Wifi may connect to different band as the SAP. For instances:
1989                             // Wifi connect to 6Ghz but bridged AP is running on 2.4Ghz + 5Ghz.
1990                             // In this case, targetShutDownInstance will be empty, shutdown the
1991                             // highest frequency instance.
1992                             removeIfaceInstanceFromBridgedApIface(
1993                                     TextUtils.isEmpty(targetShutDownInstance)
1994                                     ? getHighestFrequencyInstance(mCurrentSoftApInfoMap.keySet())
1995                                     : targetShutDownInstance);
1996                         }
1997                         break;
1998                     case CMD_PLUGGED_STATE_CHANGED:
1999                         boolean newIsPlugged = (message.arg1 != 0);
2000                         if (mIsPlugged != newIsPlugged) {
2001                             mIsPlugged = newIsPlugged;
2002                             if (mCurrentSoftApInfoMap.size() == 2) {
2003                                 rescheduleBothBridgedInstancesTimeoutMessage();
2004                             }
2005                         }
2006                         break;
2007                     default:
2008                         return NOT_HANDLED;
2009                 }
2010                 return HANDLED;
2011             }
2012         }
2013     }
2014 }
2015