• 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.content.Intent;
29 import android.net.MacAddress;
30 import android.net.wifi.ScanResult;
31 import android.net.wifi.SoftApCapability;
32 import android.net.wifi.SoftApConfiguration;
33 import android.net.wifi.SoftApInfo;
34 import android.net.wifi.WifiAnnotations;
35 import android.net.wifi.WifiClient;
36 import android.net.wifi.WifiInfo;
37 import android.net.wifi.WifiManager;
38 import android.os.Handler;
39 import android.os.Looper;
40 import android.os.Message;
41 import android.os.SystemClock;
42 import android.os.UserHandle;
43 import android.os.WorkSource;
44 import android.text.TextUtils;
45 import android.util.Log;
46 
47 import com.android.internal.annotations.VisibleForTesting;
48 import com.android.internal.util.IState;
49 import com.android.internal.util.Preconditions;
50 import com.android.internal.util.State;
51 import com.android.internal.util.StateMachine;
52 import com.android.internal.util.WakeupMessage;
53 import com.android.modules.utils.build.SdkLevel;
54 import com.android.server.wifi.WifiNative.InterfaceCallback;
55 import com.android.server.wifi.WifiNative.SoftApListener;
56 import com.android.server.wifi.coex.CoexManager;
57 import com.android.server.wifi.coex.CoexManager.CoexListener;
58 import com.android.server.wifi.util.ApConfigUtil;
59 import com.android.wifi.resources.R;
60 
61 import java.io.FileDescriptor;
62 import java.io.PrintWriter;
63 import java.text.SimpleDateFormat;
64 import java.util.ArrayList;
65 import java.util.Collections;
66 import java.util.Date;
67 import java.util.HashMap;
68 import java.util.HashSet;
69 import java.util.Iterator;
70 import java.util.List;
71 import java.util.Locale;
72 import java.util.Map;
73 import java.util.Set;
74 
75 /**
76  * Manage WiFi in AP mode.
77  * The internal state machine runs under the ClientModeImpl handler thread context.
78  */
79 public class SoftApManager implements ActiveModeManager {
80     private static final String TAG = "SoftApManager";
81 
82     @VisibleForTesting
83     public static final String SOFT_AP_SEND_MESSAGE_TIMEOUT_TAG = TAG
84             + " Soft AP Send Message Timeout";
85     @VisibleForTesting
86     public static final String SOFT_AP_SEND_MESSAGE_IDLE_IN_BRIDGED_MODE_TIMEOUT_TAG = TAG
87             + " Soft AP Send Message Bridged Mode Idle Timeout";
88 
89 
90     private final WifiContext mContext;
91     private final FrameworkFacade mFrameworkFacade;
92     private final WifiNative mWifiNative;
93     // This will only be null if SdkLevel is not at least S
94     @Nullable private final CoexManager mCoexManager;
95     private final ClientModeImplMonitor mCmiMonitor;
96     private final ActiveModeWarden mActiveModeWarden;
97     private final SoftApNotifier mSoftApNotifier;
98 
99     @VisibleForTesting
100     static final long SOFT_AP_PENDING_DISCONNECTION_CHECK_DELAY_MS = 1000;
101 
102     private String mCountryCode;
103 
104     private final SoftApStateMachine mStateMachine;
105 
106     private final Listener<SoftApManager> mModeListener;
107     private final WifiServiceImpl.SoftApCallbackInternal mSoftApCallback;
108 
109     private String mApInterfaceName;
110     private boolean mIfaceIsUp;
111     private boolean mIfaceIsDestroyed;
112 
113     private final WifiApConfigStore mWifiApConfigStore;
114 
115     private final WifiMetrics mWifiMetrics;
116     private final long mId;
117 
118     private boolean mIsUnsetBssid;
119 
120     private boolean mVerboseLoggingEnabled = false;
121 
122     /**
123      * Original configuration, which is the passed configuration when init or
124      * the user-configured {@code WifiApConfigStore#getApConfiguration}tethering}
125      * settings when input is null.
126      *
127      * Use it when doing configuration update to know if the input configuration was changed.
128      * For others use case, it should use {@code mCurrentSoftApConfiguration}.
129      */
130     @NonNull
131     private final SoftApModeConfiguration mOriginalModeConfiguration;
132 
133 
134     /**
135      * Current Soft AP configuration which is used to start Soft AP.
136      * The configuration may be changed because
137      * 1. bssid is changed because MAC randomization
138      * 2. bands are changed because fallback to single AP mode mechanism.
139      */
140     @Nullable
141     private SoftApConfiguration mCurrentSoftApConfiguration;
142 
143     @NonNull
144     private Map<String, SoftApInfo> mCurrentSoftApInfoMap = new HashMap<>();
145 
146     @NonNull
147     private SoftApCapability mCurrentSoftApCapability;
148 
149     private Map<String, List<WifiClient>> mConnectedClientWithApInfoMap = new HashMap<>();
150     @VisibleForTesting
151     Map<WifiClient, Integer> mPendingDisconnectClients = new HashMap<>();
152 
153     private boolean mTimeoutEnabled = false;
154     private boolean mBridgedModeOpportunisticsShutdownTimeoutEnabled = false;
155 
156     private final SarManager mSarManager;
157 
158     private String mStartTimestamp;
159 
160     private long mDefaultShutdownTimeoutMillis;
161 
162     private long mDefaultShutdownIdleInstanceInBridgedModeTimeoutMillis;
163 
164     private static final SimpleDateFormat FORMATTER = new SimpleDateFormat("MM-dd HH:mm:ss.SSS");
165 
166     private WifiDiagnostics mWifiDiagnostics;
167 
168     @Nullable
169     private SoftApRole mRole = null;
170     @Nullable
171     private WorkSource mRequestorWs = null;
172 
173     private boolean mEverReportMetricsForMaxClient = false;
174 
175     @NonNull
176     private Set<MacAddress> mBlockedClientList = new HashSet<>();
177 
178     @NonNull
179     private Set<MacAddress> mAllowedClientList = new HashSet<>();
180 
181     @NonNull
182     private Set<Integer> mSafeChannelFrequencyList = new HashSet<>();
183 
184     @VisibleForTesting
185     public WakeupMessage mSoftApTimeoutMessage;
186     @VisibleForTesting
187     public WakeupMessage mSoftApBridgedModeIdleInstanceTimeoutMessage;
188 
189     // Internal flag which is used to avoid the timer re-schedule.
190     private boolean mIsBridgedModeIdleInstanceTimerActive = false;
191 
192     /**
193      * Listener for soft AP events.
194      */
195     private final SoftApListener mSoftApListener = new SoftApListener() {
196         @Override
197         public void onFailure() {
198             mStateMachine.sendMessage(SoftApStateMachine.CMD_FAILURE);
199         }
200 
201         @Override
202         public void onInfoChanged(String apIfaceInstance, int frequency,
203                 @WifiAnnotations.Bandwidth int bandwidth,
204                 @WifiAnnotations.WifiStandard int generation,
205                 MacAddress apIfaceInstanceMacAddress) {
206             SoftApInfo apInfo = new SoftApInfo();
207             apInfo.setFrequency(frequency);
208             apInfo.setBandwidth(bandwidth);
209             apInfo.setWifiStandard(generation);
210             if (apIfaceInstanceMacAddress != null) {
211                 apInfo.setBssid(apIfaceInstanceMacAddress);
212             }
213             apInfo.setApInstanceIdentifier(apIfaceInstance != null
214                     ? apIfaceInstance : mApInterfaceName);
215             mStateMachine.sendMessage(
216                     SoftApStateMachine.CMD_AP_INFO_CHANGED, 0, 0, apInfo);
217         }
218 
219         @Override
220         public void onConnectedClientsChanged(String apIfaceInstance, MacAddress clientAddress,
221                 boolean isConnected) {
222             if (clientAddress != null) {
223                 WifiClient client = new WifiClient(clientAddress, apIfaceInstance != null
224                         ? apIfaceInstance : mApInterfaceName);
225                 mStateMachine.sendMessage(SoftApStateMachine.CMD_ASSOCIATED_STATIONS_CHANGED,
226                         isConnected ? 1 : 0, 0, client);
227             } else {
228                 Log.e(getTag(), "onConnectedClientsChanged: Invalid type returned");
229             }
230         }
231     };
232 
233     private final CoexListener mCoexListener = new CoexListener() {
234         @Override
235         public void onCoexUnsafeChannelsChanged() {
236             if (mCurrentSoftApConfiguration == null) {
237                 return;
238             }
239             mStateMachine.sendMessage(SoftApStateMachine.CMD_SAFE_CHANNEL_FREQUENCY_CHANGED);
240         }
241     };
242 
updateSafeChannelFrequencyList()243     private void updateSafeChannelFrequencyList() {
244         if (!SdkLevel.isAtLeastS() || mCurrentSoftApConfiguration == null) {
245             return;
246         }
247         mSafeChannelFrequencyList.clear();
248         for (int configuredBand : mCurrentSoftApConfiguration.getBands()) {
249             for (int band : SoftApConfiguration.BAND_TYPES) {
250                 if ((band & configuredBand) == 0) {
251                     continue;
252                 }
253                 for (int channel : mCurrentSoftApCapability.getSupportedChannelList(band)) {
254                     mSafeChannelFrequencyList.add(
255                             ApConfigUtil.convertChannelToFrequency(channel, band));
256                 }
257             }
258         }
259         if ((mCoexManager.getCoexRestrictions() & WifiManager.COEX_RESTRICTION_SOFTAP) != 0) {
260             mSafeChannelFrequencyList.removeAll(
261                     ApConfigUtil.getUnsafeChannelFreqsFromCoex(mCoexManager));
262         }
263         Log.d(getTag(), "SafeChannelFrequencyList = " + mSafeChannelFrequencyList);
264     }
265 
configureInternalConfiguration()266     private void configureInternalConfiguration() {
267         if (mCurrentSoftApConfiguration == null) {
268             return;
269         }
270         mBlockedClientList = new HashSet<>(mCurrentSoftApConfiguration.getBlockedClientList());
271         mAllowedClientList = new HashSet<>(mCurrentSoftApConfiguration.getAllowedClientList());
272         mTimeoutEnabled = mCurrentSoftApConfiguration.isAutoShutdownEnabled();
273         mBridgedModeOpportunisticsShutdownTimeoutEnabled =
274                 mCurrentSoftApConfiguration.isBridgedModeOpportunisticShutdownEnabledInternal();
275     }
276 
updateChangeableConfiguration(SoftApConfiguration newConfig)277     private void updateChangeableConfiguration(SoftApConfiguration newConfig) {
278         if (mCurrentSoftApConfiguration == null || newConfig == null) {
279             return;
280         }
281         /**
282          * update configurations only which mentioned in WifiManager#setSoftApConfiguration
283          */
284         SoftApConfiguration.Builder newConfigurBuilder =
285                 new SoftApConfiguration.Builder(mCurrentSoftApConfiguration)
286                 .setAllowedClientList(newConfig.getAllowedClientList())
287                 .setBlockedClientList(newConfig.getBlockedClientList())
288                 .setClientControlByUserEnabled(newConfig.isClientControlByUserEnabled())
289                 .setMaxNumberOfClients(newConfig.getMaxNumberOfClients())
290                 .setShutdownTimeoutMillis(newConfig.getShutdownTimeoutMillis())
291                 .setAutoShutdownEnabled(newConfig.isAutoShutdownEnabled());
292         if (SdkLevel.isAtLeastS()) {
293             newConfigurBuilder.setBridgedModeOpportunisticShutdownEnabled(
294                     newConfig.isBridgedModeOpportunisticShutdownEnabledInternal());
295         }
296         mCurrentSoftApConfiguration = newConfigurBuilder.build();
297         configureInternalConfiguration();
298     }
299 
SoftApManager( @onNull WifiContext context, @NonNull Looper looper, @NonNull FrameworkFacade framework, @NonNull WifiNative wifiNative, @NonNull CoexManager coexManager, String countryCode, @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)300     public SoftApManager(
301             @NonNull WifiContext context,
302             @NonNull Looper looper,
303             @NonNull FrameworkFacade framework,
304             @NonNull WifiNative wifiNative,
305             @NonNull CoexManager coexManager,
306             String countryCode,
307             @NonNull Listener<SoftApManager> listener,
308             @NonNull WifiServiceImpl.SoftApCallbackInternal callback,
309             @NonNull WifiApConfigStore wifiApConfigStore,
310             @NonNull SoftApModeConfiguration apConfig,
311             @NonNull WifiMetrics wifiMetrics,
312             @NonNull SarManager sarManager,
313             @NonNull WifiDiagnostics wifiDiagnostics,
314             @NonNull SoftApNotifier softApNotifier,
315             @NonNull ClientModeImplMonitor cmiMonitor,
316             @NonNull ActiveModeWarden activeModeWarden,
317             long id,
318             @NonNull WorkSource requestorWs,
319             @NonNull SoftApRole role,
320             boolean verboseLoggingEnabled) {
321         mContext = context;
322         mFrameworkFacade = framework;
323         mSoftApNotifier = softApNotifier;
324         mWifiNative = wifiNative;
325         mCoexManager = coexManager;
326         mCountryCode = countryCode;
327         mModeListener = listener;
328         mSoftApCallback = callback;
329         mWifiApConfigStore = wifiApConfigStore;
330         mCurrentSoftApConfiguration = apConfig.getSoftApConfiguration();
331         mCurrentSoftApCapability = apConfig.getCapability();
332         // null is a valid input and means we use the user-configured tethering settings.
333         if (mCurrentSoftApConfiguration == null) {
334             mCurrentSoftApConfiguration = mWifiApConfigStore.getApConfiguration();
335             // may still be null if we fail to load the default config
336         }
337         // Store mode configuration before update the configuration.
338         mOriginalModeConfiguration = new SoftApModeConfiguration(apConfig.getTargetMode(),
339                 mCurrentSoftApConfiguration, mCurrentSoftApCapability);
340         if (mCurrentSoftApConfiguration != null) {
341             mIsUnsetBssid = mCurrentSoftApConfiguration.getBssid() == null;
342             if (mCurrentSoftApCapability.areFeaturesSupported(
343                     SoftApCapability.SOFTAP_FEATURE_MAC_ADDRESS_CUSTOMIZATION)) {
344                 mCurrentSoftApConfiguration = mWifiApConfigStore.randomizeBssidIfUnset(
345                         mContext, mCurrentSoftApConfiguration);
346             }
347         }
348         mWifiMetrics = wifiMetrics;
349         mSarManager = sarManager;
350         mWifiDiagnostics = wifiDiagnostics;
351         mStateMachine = new SoftApStateMachine(looper);
352         configureInternalConfiguration();
353         mDefaultShutdownTimeoutMillis = mContext.getResources().getInteger(
354                 R.integer.config_wifiFrameworkSoftApShutDownTimeoutMilliseconds);
355         mDefaultShutdownIdleInstanceInBridgedModeTimeoutMillis = mContext.getResources().getInteger(
356                 R.integer
357                 .config_wifiFrameworkSoftApShutDownIdleInstanceInBridgedModeTimeoutMillisecond);
358         mCmiMonitor = cmiMonitor;
359         mActiveModeWarden = activeModeWarden;
360         mCmiMonitor.registerListener(new ClientModeImplListener() {
361             @Override
362             public void onL2Connected(@NonNull ConcreteClientModeManager clientModeManager) {
363                 SoftApManager.this.onL2Connected(clientModeManager);
364             }
365         });
366         updateSafeChannelFrequencyList();
367         mId = id;
368         mRole = role;
369         enableVerboseLogging(verboseLoggingEnabled);
370         mStateMachine.sendMessage(SoftApStateMachine.CMD_START, requestorWs);
371     }
372 
373     @Override
getId()374     public long getId() {
375         return mId;
376     }
377 
getTag()378     private String getTag() {
379         return TAG + "[" + (mApInterfaceName == null ? "unknown" : mApInterfaceName) + "]";
380     }
381 
382     /**
383      * Stop soft AP.
384      */
385     @Override
stop()386     public void stop() {
387         Log.d(getTag(), " currentstate: " + getCurrentStateName());
388         mStateMachine.sendMessage(SoftApStateMachine.CMD_STOP);
389     }
390 
isBridgedMode()391     private boolean isBridgedMode() {
392         return (SdkLevel.isAtLeastS() && mCurrentSoftApConfiguration != null
393                 && mCurrentSoftApConfiguration.getBands().length > 1);
394     }
395 
getShutdownTimeoutMillis()396     private long getShutdownTimeoutMillis() {
397         long timeout = mCurrentSoftApConfiguration.getShutdownTimeoutMillis();
398         return timeout > 0 ? timeout : mDefaultShutdownTimeoutMillis;
399     }
400 
401     @Override
getRole()402     @Nullable public SoftApRole getRole() {
403         return mRole;
404     }
405 
406     @Override
getPreviousRole()407     @Nullable public ClientRole getPreviousRole() {
408         return null;
409     }
410 
411     @Override
getLastRoleChangeSinceBootMs()412     public long getLastRoleChangeSinceBootMs() {
413         return 0;
414     }
415 
416     /** Set the role of this SoftApManager */
setRole(SoftApRole role)417     public void setRole(SoftApRole role) {
418         // softap does not allow in-place switching of roles.
419         Preconditions.checkState(mRole == null);
420         mRole = role;
421     }
422 
423     @Override
getInterfaceName()424     public String getInterfaceName() {
425         return mApInterfaceName;
426     }
427 
428     @Override
getRequestorWs()429     public WorkSource getRequestorWs() {
430         return mRequestorWs;
431     }
432 
433     /**
434      * Update AP capability. Called when carrier config or device resouce config changed.
435      *
436      * @param capability new AP capability.
437      */
updateCapability(@onNull SoftApCapability capability)438     public void updateCapability(@NonNull SoftApCapability capability) {
439         mStateMachine.sendMessage(SoftApStateMachine.CMD_UPDATE_CAPABILITY, capability);
440     }
441 
442     /**
443      * Update AP configuration. Called when setting update config via
444      * {@link WifiManager#setSoftApConfiguration(SoftApConfiguration)}
445      *
446      * @param config new AP config.
447      */
updateConfiguration(@onNull SoftApConfiguration config)448     public void updateConfiguration(@NonNull SoftApConfiguration config) {
449         mStateMachine.sendMessage(SoftApStateMachine.CMD_UPDATE_CONFIG, config);
450     }
451 
452     /**
453      * Retrieve the {@link SoftApModeConfiguration} instance associated with this mode manager.
454      */
getSoftApModeConfiguration()455     public SoftApModeConfiguration getSoftApModeConfiguration() {
456         return new SoftApModeConfiguration(mOriginalModeConfiguration.getTargetMode(),
457                 mCurrentSoftApConfiguration, mCurrentSoftApCapability);
458     }
459 
460     /**
461      * Dump info about this softap manager.
462      */
463     @Override
dump(FileDescriptor fd, PrintWriter pw, String[] args)464     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
465         pw.println("Dump of SoftApManager id=" + mId);
466 
467         pw.println("current StateMachine mode: " + getCurrentStateName());
468         pw.println("mRole: " + mRole);
469         pw.println("mApInterfaceName: " + mApInterfaceName);
470         pw.println("mIfaceIsUp: " + mIfaceIsUp);
471         pw.println("mSoftApCountryCode: " + mCountryCode);
472         pw.println("mOriginalModeConfiguration.targetMode: "
473                 + mOriginalModeConfiguration.getTargetMode());
474         pw.println("mCurrentSoftApConfiguration.SSID: " + mCurrentSoftApConfiguration.getSsid());
475         pw.println("mCurrentSoftApConfiguration.mBand: " + mCurrentSoftApConfiguration.getBand());
476         pw.println("mCurrentSoftApConfiguration.hiddenSSID: "
477                 + mCurrentSoftApConfiguration.isHiddenSsid());
478         pw.println("getConnectedClientList().size(): " + getConnectedClientList().size());
479         pw.println("mTimeoutEnabled: " + mTimeoutEnabled);
480         pw.println("mBridgedModeOpportunisticsShutdownTimeoutEnabled: "
481                 + mBridgedModeOpportunisticsShutdownTimeoutEnabled);
482         pw.println("mCurrentSoftApInfoMap " + mCurrentSoftApInfoMap);
483         pw.println("mStartTimestamp: " + mStartTimestamp);
484         mStateMachine.dump(fd, pw, args);
485     }
486 
487     @Override
enableVerboseLogging(boolean verbose)488     public void enableVerboseLogging(boolean verbose) {
489         mVerboseLoggingEnabled = verbose;
490     }
491 
492     @Override
toString()493     public String toString() {
494         return "SoftApManager{id=" + getId()
495                 + " iface=" + getInterfaceName()
496                 + " role=" + getRole()
497                 + "}";
498     }
499 
500     /**
501      * A ClientModeImpl instance has been L2 connected.
502      *
503      * @param newPrimary the corresponding ConcreteClientModeManager instance for the ClientModeImpl
504      *                   that has been L2 connected.
505      */
onL2Connected(@onNull ConcreteClientModeManager clientModeManager)506     private void onL2Connected(@NonNull ConcreteClientModeManager clientModeManager) {
507         Log.d(getTag(), "onL2Connected called");
508         mStateMachine.sendMessage(SoftApStateMachine.CMD_HANDLE_WIFI_CONNECTED,
509                 clientModeManager.syncRequestConnectionInfo());
510     }
511 
512 
getCurrentStateName()513     private String getCurrentStateName() {
514         IState currentState = mStateMachine.getCurrentState();
515 
516         if (currentState != null) {
517             return currentState.getName();
518         }
519 
520         return "StateMachine not active";
521     }
522 
523     /**
524      * Update AP state.
525      *
526      * @param newState     new AP state
527      * @param currentState current AP state
528      * @param reason       Failure reason if the new AP state is in failure state
529      */
updateApState(int newState, int currentState, int reason)530     private void updateApState(int newState, int currentState, int reason) {
531         mSoftApCallback.onStateChanged(newState, reason);
532 
533         //send the AP state change broadcast
534         final Intent intent = new Intent(WifiManager.WIFI_AP_STATE_CHANGED_ACTION);
535         intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
536         intent.putExtra(WifiManager.EXTRA_WIFI_AP_STATE, newState);
537         intent.putExtra(WifiManager.EXTRA_PREVIOUS_WIFI_AP_STATE, currentState);
538         if (newState == WifiManager.WIFI_AP_STATE_FAILED) {
539             //only set reason number when softAP start failed
540             intent.putExtra(WifiManager.EXTRA_WIFI_AP_FAILURE_REASON, reason);
541         }
542 
543         intent.putExtra(WifiManager.EXTRA_WIFI_AP_INTERFACE_NAME, mApInterfaceName);
544         intent.putExtra(WifiManager.EXTRA_WIFI_AP_MODE, mOriginalModeConfiguration.getTargetMode());
545         mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
546     }
547 
setMacAddress()548     private int setMacAddress() {
549         MacAddress mac = mCurrentSoftApConfiguration.getBssid();
550 
551         if (mac == null) {
552             // If no BSSID is explicitly requested, (re-)configure the factory MAC address. Some
553             // drivers may not support setting the MAC at all, so fail soft in this case.
554             if (!mWifiNative.resetApMacToFactoryMacAddress(mApInterfaceName)) {
555                 Log.w(getTag(), "failed to reset to factory MAC address; "
556                         + "continuing with current MAC");
557             }
558         } else {
559             if (mWifiNative.isApSetMacAddressSupported(mApInterfaceName)) {
560                 if (!mWifiNative.setApMacAddress(mApInterfaceName, mac)) {
561                     Log.e(getTag(), "failed to set explicitly requested MAC address");
562                     return ERROR_GENERIC;
563                 }
564             } else if (!mIsUnsetBssid) {
565                 // If hardware does not support MAC address setter,
566                 // only report the error for non randomization.
567                 return ERROR_UNSUPPORTED_CONFIGURATION;
568             }
569         }
570 
571         return SUCCESS;
572     }
573 
574     /**
575      * Dynamic update the country code when Soft AP enabled.
576      *
577      * @param countryCode 2 byte ASCII string. For ex: US, CA.
578      * @return true if request is sent successfully, false otherwise.
579      */
updateCountryCode(@onNull String countryCode)580     public boolean updateCountryCode(@NonNull String countryCode) {
581         if (ApConfigUtil.isSoftApDynamicCountryCodeSupported(mContext)
582                 && mCurrentSoftApCapability.areFeaturesSupported(
583                         SoftApCapability.SOFTAP_FEATURE_ACS_OFFLOAD)) {
584             mStateMachine.sendMessage(SoftApStateMachine.CMD_UPDATE_COUNTRY_CODE, countryCode);
585             return true;
586         }
587         return false;
588     }
589 
setCountryCode()590     private int setCountryCode() {
591         int band = mCurrentSoftApConfiguration.getBand();
592         if (TextUtils.isEmpty(mCountryCode)) {
593             if (band == SoftApConfiguration.BAND_5GHZ) {
594                 // Country code is mandatory for 5GHz band.
595                 Log.e(getTag(), "Invalid country code, required for setting up soft ap in 5GHz");
596                 return ERROR_GENERIC;
597             }
598             // Absence of country code is not fatal for 2Ghz & Any band options.
599             return SUCCESS;
600         }
601         if (!mWifiNative.setApCountryCode(
602                 mApInterfaceName, mCountryCode.toUpperCase(Locale.ROOT))) {
603             if (band == SoftApConfiguration.BAND_5GHZ) {
604                 // Return an error if failed to set country code when AP is configured for
605                 // 5GHz band.
606                 Log.e(getTag(), "Failed to set country code, "
607                         + "required for setting up soft ap in 5GHz");
608                 return ERROR_GENERIC;
609             }
610             // Failure to set country code is not fatal for other band options.
611         }
612         return SUCCESS;
613     }
614 
615     /**
616      * Start a soft AP instance as configured.
617      *
618      * @return integer result code
619      */
startSoftAp()620     private int startSoftAp() {
621         Log.d(getTag(), "band " + mCurrentSoftApConfiguration.getBand() + " iface "
622                 + mApInterfaceName + " country " + mCountryCode);
623 
624         int result = setMacAddress();
625         if (result != SUCCESS) {
626             return result;
627         }
628 
629         result = setCountryCode();
630         if (result != SUCCESS) {
631             return result;
632         }
633 
634         // Make a copy of configuration for updating AP band and channel.
635         SoftApConfiguration.Builder localConfigBuilder =
636                 new SoftApConfiguration.Builder(mCurrentSoftApConfiguration);
637 
638         boolean acsEnabled = mCurrentSoftApCapability.areFeaturesSupported(
639                 SoftApCapability.SOFTAP_FEATURE_ACS_OFFLOAD);
640 
641         result = ApConfigUtil.updateApChannelConfig(
642                 mWifiNative, mCoexManager, mContext.getResources(), mCountryCode,
643                 localConfigBuilder, mCurrentSoftApConfiguration, acsEnabled);
644         if (result != SUCCESS) {
645             Log.e(getTag(), "Failed to update AP band and channel");
646             return result;
647         }
648 
649         if (mCurrentSoftApConfiguration.isHiddenSsid()) {
650             Log.d(getTag(), "SoftAP is a hidden network");
651         }
652 
653         if (!ApConfigUtil.checkSupportAllConfiguration(
654                 mCurrentSoftApConfiguration, mCurrentSoftApCapability)) {
655             Log.d(getTag(), "Unsupported Configuration detect! config = "
656                     + mCurrentSoftApConfiguration);
657             return ERROR_UNSUPPORTED_CONFIGURATION;
658         }
659 
660         if (!mWifiNative.startSoftAp(mApInterfaceName,
661                   localConfigBuilder.build(),
662                   mOriginalModeConfiguration.getTargetMode() ==  WifiManager.IFACE_IP_MODE_TETHERED,
663                   mSoftApListener)) {
664             Log.e(getTag(), "Soft AP start failed");
665             return ERROR_GENERIC;
666         }
667 
668         mWifiDiagnostics.startLogging(mApInterfaceName);
669         mStartTimestamp = FORMATTER.format(new Date(System.currentTimeMillis()));
670         Log.d(getTag(), "Soft AP is started ");
671 
672         return SUCCESS;
673     }
674 
675     /**
676      * Disconnect all connected clients on active softap interface(s).
677      * This is usually done just before stopSoftAp().
678      */
disconnectAllClients()679     private void disconnectAllClients() {
680         for (WifiClient client : getConnectedClientList()) {
681             mWifiNative.forceClientDisconnect(mApInterfaceName, client.getMacAddress(),
682                     SAP_CLIENT_DISCONNECT_REASON_CODE_UNSPECIFIED);
683         }
684     }
685 
686     /**
687      * Teardown soft AP and teardown the interface.
688      */
stopSoftAp()689     private void stopSoftAp() {
690         disconnectAllClients();
691         mWifiDiagnostics.stopLogging(mApInterfaceName);
692         mWifiNative.teardownInterface(mApInterfaceName);
693         Log.d(getTag(), "Soft AP is stopped");
694     }
695 
addClientToPendingDisconnectionList(WifiClient client, int reason)696     private void addClientToPendingDisconnectionList(WifiClient client, int reason) {
697         Log.d(getTag(), "Fail to disconnect client: " + client.getMacAddress()
698                 + ", add it into pending list");
699         mPendingDisconnectClients.put(client, reason);
700         mStateMachine.getHandler().removeMessages(
701                 SoftApStateMachine.CMD_FORCE_DISCONNECT_PENDING_CLIENTS);
702         mStateMachine.sendMessageDelayed(
703                 SoftApStateMachine.CMD_FORCE_DISCONNECT_PENDING_CLIENTS,
704                 SOFT_AP_PENDING_DISCONNECTION_CHECK_DELAY_MS);
705     }
706 
getConnectedClientList()707     private List<WifiClient> getConnectedClientList() {
708         List<WifiClient> connectedClientList = new ArrayList<>();
709         for (List<WifiClient> it : mConnectedClientWithApInfoMap.values()) {
710             connectedClientList.addAll(it);
711         }
712         return connectedClientList;
713     }
714 
checkSoftApClient(SoftApConfiguration config, WifiClient newClient)715     private boolean checkSoftApClient(SoftApConfiguration config, WifiClient newClient) {
716         if (!mCurrentSoftApCapability.areFeaturesSupported(
717                 SoftApCapability.SOFTAP_FEATURE_CLIENT_FORCE_DISCONNECT)) {
718             return true;
719         }
720 
721         if (mBlockedClientList.contains(newClient.getMacAddress())) {
722             Log.d(getTag(), "Force disconnect for client: " + newClient + "in blocked list");
723             if (!mWifiNative.forceClientDisconnect(
724                     mApInterfaceName, newClient.getMacAddress(),
725                     WifiManager.SAP_CLIENT_BLOCK_REASON_CODE_BLOCKED_BY_USER)) {
726                 addClientToPendingDisconnectionList(newClient,
727                         WifiManager.SAP_CLIENT_BLOCK_REASON_CODE_BLOCKED_BY_USER);
728             }
729             return false;
730         }
731         if (config.isClientControlByUserEnabled()
732                 && !mAllowedClientList.contains(newClient.getMacAddress())) {
733             mSoftApCallback.onBlockedClientConnecting(newClient,
734                     WifiManager.SAP_CLIENT_BLOCK_REASON_CODE_BLOCKED_BY_USER);
735             Log.d(getTag(), "Force disconnect for unauthorized client: " + newClient);
736             if (!mWifiNative.forceClientDisconnect(
737                     mApInterfaceName, newClient.getMacAddress(),
738                     WifiManager.SAP_CLIENT_BLOCK_REASON_CODE_BLOCKED_BY_USER)) {
739                 addClientToPendingDisconnectionList(newClient,
740                         WifiManager.SAP_CLIENT_BLOCK_REASON_CODE_BLOCKED_BY_USER);
741             }
742             return false;
743         }
744         int maxConfig = mCurrentSoftApCapability.getMaxSupportedClients();
745         if (config.getMaxNumberOfClients() > 0) {
746             maxConfig = Math.min(maxConfig, config.getMaxNumberOfClients());
747         }
748 
749         if (getConnectedClientList().size() >= maxConfig) {
750             Log.i(getTag(), "No more room for new client:" + newClient);
751             if (!mWifiNative.forceClientDisconnect(
752                     mApInterfaceName, newClient.getMacAddress(),
753                     WifiManager.SAP_CLIENT_BLOCK_REASON_CODE_NO_MORE_STAS)) {
754                 addClientToPendingDisconnectionList(newClient,
755                         WifiManager.SAP_CLIENT_BLOCK_REASON_CODE_NO_MORE_STAS);
756             }
757             mSoftApCallback.onBlockedClientConnecting(newClient,
758                     WifiManager.SAP_CLIENT_BLOCK_REASON_CODE_NO_MORE_STAS);
759             // Avoid report the max client blocked in the same settings.
760             if (!mEverReportMetricsForMaxClient) {
761                 mWifiMetrics.noteSoftApClientBlocked(maxConfig);
762                 mEverReportMetricsForMaxClient = true;
763             }
764             return false;
765         }
766         return true;
767     }
768 
769     private class SoftApStateMachine extends StateMachine {
770         // Commands for the state machine.
771         public static final int CMD_START = 0;
772         public static final int CMD_STOP = 1;
773         public static final int CMD_FAILURE = 2;
774         public static final int CMD_INTERFACE_STATUS_CHANGED = 3;
775         public static final int CMD_ASSOCIATED_STATIONS_CHANGED = 4;
776         public static final int CMD_NO_ASSOCIATED_STATIONS_TIMEOUT = 5;
777         public static final int CMD_INTERFACE_DESTROYED = 7;
778         public static final int CMD_INTERFACE_DOWN = 8;
779         public static final int CMD_AP_INFO_CHANGED = 9;
780         public static final int CMD_UPDATE_CAPABILITY = 10;
781         public static final int CMD_UPDATE_CONFIG = 11;
782         public static final int CMD_FORCE_DISCONNECT_PENDING_CLIENTS = 12;
783         public static final int CMD_NO_ASSOCIATED_STATIONS_TIMEOUT_ON_ONE_INSTANCE = 13;
784         public static final int CMD_SAFE_CHANNEL_FREQUENCY_CHANGED = 14;
785         public static final int CMD_HANDLE_WIFI_CONNECTED = 15;
786         public static final int CMD_UPDATE_COUNTRY_CODE = 16;
787 
788         private final State mIdleState = new IdleState();
789         private final State mStartedState = new StartedState();
790 
791         private final InterfaceCallback mWifiNativeInterfaceCallback = new InterfaceCallback() {
792             @Override
793             public void onDestroyed(String ifaceName) {
794                 if (mApInterfaceName != null && mApInterfaceName.equals(ifaceName)) {
795                     sendMessage(CMD_INTERFACE_DESTROYED);
796                 }
797             }
798 
799             @Override
800             public void onUp(String ifaceName) {
801                 if (mApInterfaceName != null && mApInterfaceName.equals(ifaceName)) {
802                     sendMessage(CMD_INTERFACE_STATUS_CHANGED, 1);
803                 }
804             }
805 
806             @Override
807             public void onDown(String ifaceName) {
808                 if (mApInterfaceName != null && mApInterfaceName.equals(ifaceName)) {
809                     sendMessage(CMD_INTERFACE_STATUS_CHANGED, 0);
810                 }
811             }
812         };
813 
SoftApStateMachine(Looper looper)814         SoftApStateMachine(Looper looper) {
815             super(TAG, looper);
816 
817             // CHECKSTYLE:OFF IndentationCheck
818             addState(mIdleState);
819                 addState(mStartedState, mIdleState);
820             // CHECKSTYLE:ON IndentationCheck
821 
822             setInitialState(mIdleState);
823             start();
824         }
825 
826         private class IdleState extends State {
827             @Override
enter()828             public void enter() {
829                 mApInterfaceName = null;
830                 mIfaceIsUp = false;
831                 mIfaceIsDestroyed = false;
832             }
833 
834             @Override
exit()835             public void exit() {
836                 mModeListener.onStopped(SoftApManager.this);
837             }
838 
839             @Override
processMessage(Message message)840             public boolean processMessage(Message message) {
841                 switch (message.what) {
842                     case CMD_STOP:
843                         mStateMachine.quitNow();
844                         break;
845                     case CMD_START:
846                         mRequestorWs = (WorkSource) message.obj;
847                         if (mCurrentSoftApConfiguration == null
848                                 || mCurrentSoftApConfiguration.getSsid() == null) {
849                             Log.e(getTag(), "Unable to start soft AP without valid configuration");
850                             updateApState(WifiManager.WIFI_AP_STATE_FAILED,
851                                     WifiManager.WIFI_AP_STATE_DISABLED,
852                                     WifiManager.SAP_START_FAILURE_GENERAL);
853                             mWifiMetrics.incrementSoftApStartResult(
854                                     false, WifiManager.SAP_START_FAILURE_GENERAL);
855                             mModeListener.onStartFailure(SoftApManager.this);
856                             break;
857                         }
858                         if (isBridgedMode()) {
859                             boolean isFallbackToSingleAp = false;
860                             int newSingleApBand = 0;
861                             for (ClientModeManager cmm
862                                     : mActiveModeWarden.getClientModeManagers()) {
863                                 WifiInfo wifiConnectedInfo = cmm.syncRequestConnectionInfo();
864                                 int wifiFrequency = wifiConnectedInfo.getFrequency();
865                                 if (wifiFrequency > 0
866                                         && !mSafeChannelFrequencyList.contains(
867                                         wifiFrequency)) {
868                                     Log.d(getTag(), "Wifi connected to unavailable freq: "
869                                             + wifiFrequency);
870                                     isFallbackToSingleAp = true;
871                                     break;
872                                 }
873                             }
874                             for (int configuredBand : mCurrentSoftApConfiguration.getBands()) {
875                                 int availableBand = ApConfigUtil.removeUnavailableBands(
876                                         mCurrentSoftApCapability,
877                                         configuredBand, mCoexManager);
878                                 if (configuredBand != availableBand) {
879                                     isFallbackToSingleAp = true;
880                                 }
881                                 newSingleApBand |= availableBand;
882                             }
883                             if (isFallbackToSingleAp) {
884                                 newSingleApBand = ApConfigUtil.append24GToBandIf24GSupported(
885                                         newSingleApBand, mContext);
886                                 Log.i(getTag(), "Fallback to single AP mode with band "
887                                         + newSingleApBand);
888                                 mCurrentSoftApConfiguration =
889                                         new SoftApConfiguration.Builder(mCurrentSoftApConfiguration)
890                                         .setBand(newSingleApBand)
891                                         .build();
892                             }
893                         }
894                         mApInterfaceName = mWifiNative.setupInterfaceForSoftApMode(
895                                 mWifiNativeInterfaceCallback, mRequestorWs,
896                                 mCurrentSoftApConfiguration.getBand(), isBridgedMode());
897                         if (TextUtils.isEmpty(mApInterfaceName)) {
898                             Log.e(getTag(), "setup failure when creating ap interface.");
899                             updateApState(WifiManager.WIFI_AP_STATE_FAILED,
900                                     WifiManager.WIFI_AP_STATE_DISABLED,
901                                     WifiManager.SAP_START_FAILURE_GENERAL);
902                             mWifiMetrics.incrementSoftApStartResult(
903                                     false, WifiManager.SAP_START_FAILURE_GENERAL);
904                             mModeListener.onStartFailure(SoftApManager.this);
905                             break;
906                         }
907                         mSoftApNotifier.dismissSoftApShutdownTimeoutExpiredNotification();
908                         updateApState(WifiManager.WIFI_AP_STATE_ENABLING,
909                                 WifiManager.WIFI_AP_STATE_DISABLED, 0);
910                         int result = startSoftAp();
911                         if (result != SUCCESS) {
912                             int failureReason = WifiManager.SAP_START_FAILURE_GENERAL;
913                             if (result == ERROR_NO_CHANNEL) {
914                                 failureReason = WifiManager.SAP_START_FAILURE_NO_CHANNEL;
915                             } else if (result == ERROR_UNSUPPORTED_CONFIGURATION) {
916                                 failureReason = WifiManager
917                                         .SAP_START_FAILURE_UNSUPPORTED_CONFIGURATION;
918                             }
919                             updateApState(WifiManager.WIFI_AP_STATE_FAILED,
920                                     WifiManager.WIFI_AP_STATE_ENABLING,
921                                     failureReason);
922                             stopSoftAp();
923                             mWifiMetrics.incrementSoftApStartResult(false, failureReason);
924                             mModeListener.onStartFailure(SoftApManager.this);
925                             break;
926                         }
927                         transitionTo(mStartedState);
928                         break;
929                     case CMD_UPDATE_CAPABILITY:
930                         // Capability should only changed by carrier requirement. Only apply to
931                         // Tether Mode
932                         if (mOriginalModeConfiguration.getTargetMode()
933                                 ==  WifiManager.IFACE_IP_MODE_TETHERED) {
934                             SoftApCapability capability = (SoftApCapability) message.obj;
935                             mCurrentSoftApCapability = new SoftApCapability(capability);
936                         }
937                         break;
938                     case CMD_UPDATE_CONFIG:
939                         SoftApConfiguration newConfig = (SoftApConfiguration) message.obj;
940                         Log.d(getTag(), "Configuration changed to " + newConfig);
941                         // Idle mode, update all configurations.
942                         mCurrentSoftApConfiguration = newConfig;
943                         configureInternalConfiguration();
944                         break;
945                     case CMD_UPDATE_COUNTRY_CODE:
946                         String countryCode = (String) message.obj;
947                         if (!TextUtils.isEmpty(countryCode)) {
948                             mCountryCode = countryCode;
949                         }
950                         break;
951                     default:
952                         // Ignore all other commands.
953                         break;
954                 }
955 
956                 return HANDLED;
957             }
958         }
959 
960         private class StartedState extends State {
scheduleTimeoutMessages()961             private void scheduleTimeoutMessages() {
962                 // When SAP started, the mCurrentSoftApInfoMap is 0 because info does not update.
963                 // Don't trigger bridged mode shutdown timeout when only one active instance
964                 // In Dual AP, one instance may already be closed due to LTE coexistence or DFS
965                 // restrictions or due to inactivity. i.e. mCurrentSoftApInfoMap.size() is 1)
966                 final int connectedClients = getConnectedClientList().size();
967                 if (isBridgedMode() && mCurrentSoftApInfoMap.size() != 1) {
968                     if (mBridgedModeOpportunisticsShutdownTimeoutEnabled
969                             && (connectedClients == 0 || getIdleInstances().size() != 0)) {
970                         if (!mIsBridgedModeIdleInstanceTimerActive) {
971                             mSoftApBridgedModeIdleInstanceTimeoutMessage.schedule(SystemClock
972                                     .elapsedRealtime()
973                                     + mDefaultShutdownIdleInstanceInBridgedModeTimeoutMillis);
974                             mIsBridgedModeIdleInstanceTimerActive = true;
975                             Log.d(getTag(), "Bridged mode instance opportunistic timeout message"
976                                     + " scheduled, delay = "
977                                     + mDefaultShutdownIdleInstanceInBridgedModeTimeoutMillis);
978                         }
979                     } else {
980                         cancelBridgedModeIdleInstanceTimeoutMessage();
981                     }
982                 }
983                 if (!mTimeoutEnabled || connectedClients != 0) {
984                     cancelTimeoutMessage();
985                     return;
986                 }
987                 long timeout = getShutdownTimeoutMillis();
988                 mSoftApTimeoutMessage.schedule(SystemClock.elapsedRealtime()
989                         + timeout);
990                 Log.d(getTag(), "Timeout message scheduled, delay = "
991                         + timeout);
992             }
993 
getHighestFrequencyInstance(Set<String> candidateInstances)994             private String getHighestFrequencyInstance(Set<String> candidateInstances) {
995                 int currentHighestFrequencyOnAP = 0;
996                 String highestFrequencyInstance = null;
997                 for (String instance : candidateInstances) {
998                     SoftApInfo info = mCurrentSoftApInfoMap.get(instance);
999                     if (info == null) {
1000                         Log.wtf(getTag(), "Invalid instance name, no way to get the frequency");
1001                         return "";
1002                     }
1003                     int frequencyOnInstance = info.getFrequency();
1004                     if (frequencyOnInstance > currentHighestFrequencyOnAP) {
1005                         currentHighestFrequencyOnAP = frequencyOnInstance;
1006                         highestFrequencyInstance = instance;
1007                     }
1008                 }
1009                 return highestFrequencyInstance;
1010             }
1011 
removeIfaceInstanceFromBridgedApIface(String instanceName)1012             private void removeIfaceInstanceFromBridgedApIface(String instanceName) {
1013                 if (TextUtils.isEmpty(instanceName)) {
1014                     return;
1015                 }
1016                 if (mCurrentSoftApInfoMap.containsKey(instanceName)) {
1017                     Log.i(getTag(), "remove instance " + instanceName + "("
1018                             + mCurrentSoftApInfoMap.get(instanceName).getFrequency()
1019                             + ") from bridged iface " + mApInterfaceName);
1020                     mWifiNative.removeIfaceInstanceFromBridgedApIface(mApInterfaceName,
1021                             instanceName);
1022                     // Remove the info and update it.
1023                     updateSoftApInfo(mCurrentSoftApInfoMap.get(instanceName), true);
1024                 }
1025             }
1026 
getIdleInstances()1027             private Set<String> getIdleInstances() {
1028                 Set<String> idleInstances = new HashSet<String>();
1029                 for (String instance : mConnectedClientWithApInfoMap.keySet()) {
1030                     if (mConnectedClientWithApInfoMap.getOrDefault(
1031                             instance, Collections.emptyList()).size() == 0) {
1032                         idleInstances.add(instance);
1033                     }
1034                 }
1035                 return idleInstances;
1036             }
1037 
cancelTimeoutMessage()1038             private void cancelTimeoutMessage() {
1039                 mSoftApTimeoutMessage.cancel();
1040                 Log.d(getTag(), "Timeout message canceled");
1041             }
1042 
cancelBridgedModeIdleInstanceTimeoutMessage()1043             private void cancelBridgedModeIdleInstanceTimeoutMessage() {
1044                 mSoftApBridgedModeIdleInstanceTimeoutMessage.cancel();
1045                 mIsBridgedModeIdleInstanceTimerActive = false;
1046                 Log.d(getTag(), "Bridged mode idle instance timeout message canceled");
1047             }
1048 
1049             /**
1050              * When configuration changed, it need to force some clients disconnect to match the
1051              * configuration.
1052              */
updateClientConnection()1053             private void updateClientConnection() {
1054                 if (!mCurrentSoftApCapability.areFeaturesSupported(
1055                         SoftApCapability.SOFTAP_FEATURE_CLIENT_FORCE_DISCONNECT)) {
1056                     return;
1057                 }
1058                 final int maxAllowedClientsByHardwareAndCarrier =
1059                         mCurrentSoftApCapability.getMaxSupportedClients();
1060                 final int userApConfigMaxClientCount =
1061                         mCurrentSoftApConfiguration.getMaxNumberOfClients();
1062                 int finalMaxClientCount = maxAllowedClientsByHardwareAndCarrier;
1063                 if (userApConfigMaxClientCount > 0) {
1064                     finalMaxClientCount = Math.min(userApConfigMaxClientCount,
1065                             maxAllowedClientsByHardwareAndCarrier);
1066                 }
1067                 List<WifiClient> currentClients = getConnectedClientList();
1068                 int targetDisconnectClientNumber = currentClients.size() - finalMaxClientCount;
1069                 List<WifiClient> allowedConnectedList = new ArrayList<>();
1070                 Iterator<WifiClient> iterator = currentClients.iterator();
1071                 while (iterator.hasNext()) {
1072                     WifiClient client = iterator.next();
1073                     if (mBlockedClientList.contains(client.getMacAddress())
1074                               || (mCurrentSoftApConfiguration.isClientControlByUserEnabled()
1075                               && !mAllowedClientList.contains(client.getMacAddress()))) {
1076                         Log.d(getTag(), "Force disconnect for not allowed client: " + client);
1077                         if (!mWifiNative.forceClientDisconnect(
1078                                 mApInterfaceName, client.getMacAddress(),
1079                                 WifiManager.SAP_CLIENT_BLOCK_REASON_CODE_BLOCKED_BY_USER)) {
1080                             addClientToPendingDisconnectionList(client,
1081                                     WifiManager.SAP_CLIENT_BLOCK_REASON_CODE_BLOCKED_BY_USER);
1082                         }
1083                         targetDisconnectClientNumber--;
1084                     } else {
1085                         allowedConnectedList.add(client);
1086                     }
1087                 }
1088 
1089                 if (targetDisconnectClientNumber > 0) {
1090                     Iterator<WifiClient> allowedClientIterator = allowedConnectedList.iterator();
1091                     while (allowedClientIterator.hasNext()) {
1092                         if (targetDisconnectClientNumber == 0) break;
1093                         WifiClient allowedClient = allowedClientIterator.next();
1094                         Log.d(getTag(), "Force disconnect for client due to no more room: "
1095                                 + allowedClient);
1096                         if (!mWifiNative.forceClientDisconnect(
1097                                 mApInterfaceName, allowedClient.getMacAddress(),
1098                                 WifiManager.SAP_CLIENT_BLOCK_REASON_CODE_NO_MORE_STAS)) {
1099                             addClientToPendingDisconnectionList(allowedClient,
1100                                     WifiManager.SAP_CLIENT_BLOCK_REASON_CODE_NO_MORE_STAS);
1101                         }
1102                         targetDisconnectClientNumber--;
1103                     }
1104                 }
1105             }
1106 
1107             /**
1108              * Set stations associated with this soft AP
1109              * @param client The station for which connection state changed.
1110              * @param isConnected True for the connection changed to connect, otherwise false.
1111              */
updateConnectedClients(WifiClient client, boolean isConnected)1112             private void updateConnectedClients(WifiClient client, boolean isConnected) {
1113                 if (client == null) {
1114                     return;
1115                 }
1116 
1117                 if (null != mPendingDisconnectClients.remove(client)) {
1118                     Log.d(getTag(), "Remove client: " + client.getMacAddress()
1119                             + "from pending disconnectionlist");
1120                 }
1121 
1122                 String apInstanceIdentifier = client.getApInstanceIdentifier();
1123                 List clientList = mConnectedClientWithApInfoMap.computeIfAbsent(
1124                         apInstanceIdentifier, k -> new ArrayList<>());
1125                 int index = clientList.indexOf(client);
1126 
1127                 if ((index != -1) == isConnected) {
1128                     Log.e(getTag(), "Drop client connection event, client "
1129                             + client + "isConnected: " + isConnected
1130                             + " , duplicate event or client is blocked");
1131                     return;
1132                 }
1133                 if (isConnected) {
1134                     boolean isAllow = checkSoftApClient(mCurrentSoftApConfiguration, client);
1135                     if (isAllow) {
1136                         clientList.add(client);
1137                     } else {
1138                         return;
1139                     }
1140                 } else {
1141                     if (null == clientList.remove(index)) {
1142                         Log.e(getTag(), "client doesn't exist in list, it should NOT happen");
1143                     }
1144                 }
1145 
1146                 // Update clients list.
1147                 mConnectedClientWithApInfoMap.put(apInstanceIdentifier, clientList);
1148                 SoftApInfo currentInfoWithClientsChanged = mCurrentSoftApInfoMap
1149                         .get(apInstanceIdentifier);
1150                 Log.d(getTag(), "The connected wifi stations have changed with count: "
1151                         + clientList.size() + ": " + clientList + " on the AP which info is "
1152                         + currentInfoWithClientsChanged);
1153 
1154                 if (mSoftApCallback != null) {
1155                     mSoftApCallback.onConnectedClientsOrInfoChanged(mCurrentSoftApInfoMap,
1156                             mConnectedClientWithApInfoMap, isBridgedMode());
1157                 } else {
1158                     Log.e(getTag(),
1159                             "SoftApCallback is null. Dropping ConnectedClientsChanged event.");
1160                 }
1161 
1162                 mWifiMetrics.addSoftApNumAssociatedStationsChangedEvent(
1163                         getConnectedClientList().size(),
1164                         mConnectedClientWithApInfoMap.get(apInstanceIdentifier).size(),
1165                         mOriginalModeConfiguration.getTargetMode(),
1166                         mCurrentSoftApInfoMap.get(apInstanceIdentifier));
1167 
1168                 scheduleTimeoutMessages();
1169             }
1170 
1171             /**
1172              * @param apInfo, the new SoftApInfo changed. Null used to clean up.
1173              */
updateSoftApInfo(@ullable SoftApInfo apInfo, boolean isRemoved)1174             private void updateSoftApInfo(@Nullable SoftApInfo apInfo, boolean isRemoved) {
1175                 Log.d(getTag(), "SoftApInfo update " + apInfo + ", isRemoved: " + isRemoved);
1176                 if (apInfo == null) {
1177                     // Clean up
1178                     mCurrentSoftApInfoMap.clear();
1179                     mConnectedClientWithApInfoMap.clear();
1180                     mSoftApCallback.onConnectedClientsOrInfoChanged(mCurrentSoftApInfoMap,
1181                             mConnectedClientWithApInfoMap, isBridgedMode());
1182                     return;
1183                 }
1184                 String changedInstance = apInfo.getApInstanceIdentifier();
1185                 if (apInfo.equals(mCurrentSoftApInfoMap.get(changedInstance))) {
1186                     if (isRemoved) {
1187                         boolean isClientConnected =
1188                                 mConnectedClientWithApInfoMap.get(changedInstance).size() > 0;
1189                         mCurrentSoftApInfoMap.remove(changedInstance);
1190                         mConnectedClientWithApInfoMap.remove(changedInstance);
1191                         mSoftApCallback.onConnectedClientsOrInfoChanged(mCurrentSoftApInfoMap,
1192                                 mConnectedClientWithApInfoMap, isBridgedMode());
1193                         if (isClientConnected) {
1194                             mWifiMetrics.addSoftApNumAssociatedStationsChangedEvent(
1195                                     getConnectedClientList().size(), 0,
1196                                     mOriginalModeConfiguration.getTargetMode(), apInfo);
1197                         }
1198                         if (isBridgedMode()) {
1199                             mWifiMetrics.addSoftApInstanceDownEventInDualMode(
1200                                     mOriginalModeConfiguration.getTargetMode(), apInfo);
1201                         }
1202                     }
1203                     return;
1204                 }
1205 
1206                 // Make sure an empty client list is created when info updated
1207                 List clientList = mConnectedClientWithApInfoMap.computeIfAbsent(
1208                         changedInstance, k -> new ArrayList<>());
1209 
1210                 if (clientList.size() != 0) {
1211                     Log.e(getTag(), "The info: " + apInfo
1212                             + " changed when client connected, it should NOT happen!!");
1213                 }
1214 
1215                 // Update the info when getting two infos in bridged mode.
1216                 // TODO: b/173999527. It may only one instance come up when starting bridged AP.
1217                 // Consider the handling with co-ex mechanism in bridged mode.
1218                 boolean waitForAnotherSoftApInfoInBridgedMode =
1219                         isBridgedMode() && mCurrentSoftApInfoMap.size() == 0;
1220 
1221                 mCurrentSoftApInfoMap.put(changedInstance, new SoftApInfo(apInfo));
1222                 if (!waitForAnotherSoftApInfoInBridgedMode) {
1223                     mSoftApCallback.onConnectedClientsOrInfoChanged(mCurrentSoftApInfoMap,
1224                             mConnectedClientWithApInfoMap, isBridgedMode());
1225                 }
1226 
1227                 // ignore invalid freq and softap disable case for metrics
1228                 if (apInfo.getFrequency() > 0
1229                         && apInfo.getBandwidth() != SoftApInfo.CHANNEL_WIDTH_INVALID) {
1230                     mWifiMetrics.addSoftApChannelSwitchedEvent(
1231                             new ArrayList<>(mCurrentSoftApInfoMap.values()),
1232                             mOriginalModeConfiguration.getTargetMode(), isBridgedMode());
1233                     updateUserBandPreferenceViolationMetricsIfNeeded(apInfo);
1234                 }
1235             }
1236 
onUpChanged(boolean isUp)1237             private void onUpChanged(boolean isUp) {
1238                 if (isUp == mIfaceIsUp) {
1239                     return;  // no change
1240                 }
1241 
1242                 mIfaceIsUp = isUp;
1243                 if (isUp) {
1244                     Log.d(getTag(), "SoftAp is ready for use");
1245                     updateApState(WifiManager.WIFI_AP_STATE_ENABLED,
1246                             WifiManager.WIFI_AP_STATE_ENABLING, 0);
1247                     mModeListener.onStarted(SoftApManager.this);
1248                     mWifiMetrics.incrementSoftApStartResult(true, 0);
1249                     mCurrentSoftApInfoMap.clear();
1250                     mConnectedClientWithApInfoMap.clear();
1251                     if (mSoftApCallback != null) {
1252                         mSoftApCallback.onConnectedClientsOrInfoChanged(mCurrentSoftApInfoMap,
1253                                 mConnectedClientWithApInfoMap, isBridgedMode());
1254                     }
1255                 } else {
1256                     // the interface was up, but goes down
1257                     sendMessage(CMD_INTERFACE_DOWN);
1258                 }
1259                 mWifiMetrics.addSoftApUpChangedEvent(isUp,
1260                         mOriginalModeConfiguration.getTargetMode(),
1261                         mDefaultShutdownTimeoutMillis, isBridgedMode());
1262                 if (isUp) {
1263                     mWifiMetrics.updateSoftApConfiguration(mCurrentSoftApConfiguration,
1264                             mOriginalModeConfiguration.getTargetMode(), isBridgedMode());
1265                     mWifiMetrics.updateSoftApCapability(mCurrentSoftApCapability,
1266                             mOriginalModeConfiguration.getTargetMode(), isBridgedMode());
1267                 }
1268             }
1269 
1270             @Override
enter()1271             public void enter() {
1272                 mIfaceIsUp = false;
1273                 mIfaceIsDestroyed = false;
1274                 onUpChanged(mWifiNative.isInterfaceUp(mApInterfaceName));
1275 
1276                 Handler handler = mStateMachine.getHandler();
1277                 mSoftApTimeoutMessage = new WakeupMessage(mContext, handler,
1278                         SOFT_AP_SEND_MESSAGE_TIMEOUT_TAG,
1279                         SoftApStateMachine.CMD_NO_ASSOCIATED_STATIONS_TIMEOUT);
1280 
1281                 mSoftApBridgedModeIdleInstanceTimeoutMessage = new WakeupMessage(mContext, handler,
1282                         SOFT_AP_SEND_MESSAGE_IDLE_IN_BRIDGED_MODE_TIMEOUT_TAG,
1283                         SoftApStateMachine.CMD_NO_ASSOCIATED_STATIONS_TIMEOUT_ON_ONE_INSTANCE);
1284                 if (SdkLevel.isAtLeastS()) {
1285                     mCoexManager.registerCoexListener(mCoexListener);
1286                 }
1287                 mSarManager.setSapWifiState(WifiManager.WIFI_AP_STATE_ENABLED);
1288                 Log.d(getTag(), "Resetting connected clients on start");
1289                 mConnectedClientWithApInfoMap.clear();
1290                 mPendingDisconnectClients.clear();
1291                 mEverReportMetricsForMaxClient = false;
1292                 scheduleTimeoutMessages();
1293             }
1294 
1295             @Override
exit()1296             public void exit() {
1297                 if (!mIfaceIsDestroyed) {
1298                     stopSoftAp();
1299                 }
1300                 if (SdkLevel.isAtLeastS()) {
1301                     mCoexManager.unregisterCoexListener(mCoexListener);
1302                 }
1303                 if (getConnectedClientList().size() != 0) {
1304                     Log.d(getTag(), "Resetting num stations on stop");
1305                     for (List<WifiClient> it : mConnectedClientWithApInfoMap.values()) {
1306                         if (it.size() != 0) {
1307                             mWifiMetrics.addSoftApNumAssociatedStationsChangedEvent(
1308                                     0, 0, mOriginalModeConfiguration.getTargetMode(),
1309                                     mCurrentSoftApInfoMap
1310                                             .get(it.get(0).getApInstanceIdentifier()));
1311                         }
1312                     }
1313                     mConnectedClientWithApInfoMap.clear();
1314                     if (mSoftApCallback != null) {
1315                         mSoftApCallback.onConnectedClientsOrInfoChanged(mCurrentSoftApInfoMap,
1316                                 mConnectedClientWithApInfoMap, isBridgedMode());
1317                     }
1318                 }
1319                 mPendingDisconnectClients.clear();
1320                 cancelTimeoutMessage();
1321                 cancelBridgedModeIdleInstanceTimeoutMessage();
1322 
1323                 // Need this here since we are exiting |Started| state and won't handle any
1324                 // future CMD_INTERFACE_STATUS_CHANGED events after this point
1325                 mWifiMetrics.addSoftApUpChangedEvent(false,
1326                         mOriginalModeConfiguration.getTargetMode(),
1327                         mDefaultShutdownTimeoutMillis, isBridgedMode());
1328                 updateApState(WifiManager.WIFI_AP_STATE_DISABLED,
1329                         WifiManager.WIFI_AP_STATE_DISABLING, 0);
1330 
1331                 mSarManager.setSapWifiState(WifiManager.WIFI_AP_STATE_DISABLED);
1332 
1333                 mApInterfaceName = null;
1334                 mIfaceIsUp = false;
1335                 mIfaceIsDestroyed = false;
1336                 mRole = null;
1337                 updateSoftApInfo(null, false);
1338             }
1339 
updateUserBandPreferenceViolationMetricsIfNeeded(SoftApInfo apInfo)1340             private void updateUserBandPreferenceViolationMetricsIfNeeded(SoftApInfo apInfo) {
1341                 // The band preference violation only need to detect in single AP mode.
1342                 if (isBridgedMode()) return;
1343                 int band = mCurrentSoftApConfiguration.getBand();
1344                 boolean bandPreferenceViolated =
1345                         (ScanResult.is24GHz(apInfo.getFrequency())
1346                             && !ApConfigUtil.containsBand(band,
1347                                     SoftApConfiguration.BAND_2GHZ))
1348                         || (ScanResult.is5GHz(apInfo.getFrequency())
1349                             && !ApConfigUtil.containsBand(band,
1350                                     SoftApConfiguration.BAND_5GHZ))
1351                         || (ScanResult.is6GHz(apInfo.getFrequency())
1352                             && !ApConfigUtil.containsBand(band,
1353                                     SoftApConfiguration.BAND_6GHZ));
1354 
1355                 if (bandPreferenceViolated) {
1356                     Log.e(getTag(), "Channel does not satisfy user band preference: "
1357                             + apInfo.getFrequency());
1358                     mWifiMetrics.incrementNumSoftApUserBandPreferenceUnsatisfied();
1359                 }
1360             }
1361 
1362             @Override
processMessage(Message message)1363             public boolean processMessage(Message message) {
1364                 switch (message.what) {
1365                     case CMD_ASSOCIATED_STATIONS_CHANGED:
1366                         if (!(message.obj instanceof WifiClient)) {
1367                             Log.e(getTag(), "Invalid type returned for"
1368                                     + " CMD_ASSOCIATED_STATIONS_CHANGED");
1369                             break;
1370                         }
1371                         boolean isConnected = (message.arg1 == 1);
1372                         WifiClient client = (WifiClient) message.obj;
1373                         Log.d(getTag(), "CMD_ASSOCIATED_STATIONS_CHANGED, Client: "
1374                                 + client.getMacAddress().toString() + " isConnected: "
1375                                 + isConnected);
1376                         updateConnectedClients(client, isConnected);
1377                         break;
1378                     case CMD_AP_INFO_CHANGED:
1379                         if (!(message.obj instanceof SoftApInfo)) {
1380                             Log.e(getTag(), "Invalid type returned for"
1381                                     + " CMD_AP_INFO_CHANGED");
1382                             break;
1383                         }
1384                         SoftApInfo apInfo = (SoftApInfo) message.obj;
1385                         if (apInfo.getFrequency() < 0) {
1386                             Log.e(getTag(), "Invalid ap channel frequency: "
1387                                     + apInfo.getFrequency());
1388                             break;
1389                         }
1390                         // Update shutdown timeout
1391                         apInfo.setAutoShutdownTimeoutMillis(mTimeoutEnabled
1392                                 ? getShutdownTimeoutMillis() : 0);
1393                         updateSoftApInfo(apInfo, false);
1394                         break;
1395                     case CMD_INTERFACE_STATUS_CHANGED:
1396                         boolean isUp = message.arg1 == 1;
1397                         onUpChanged(isUp);
1398                         break;
1399                     case CMD_STOP:
1400                         if (mIfaceIsUp) {
1401                             updateApState(WifiManager.WIFI_AP_STATE_DISABLING,
1402                                     WifiManager.WIFI_AP_STATE_ENABLED, 0);
1403                         } else {
1404                             updateApState(WifiManager.WIFI_AP_STATE_DISABLING,
1405                                     WifiManager.WIFI_AP_STATE_ENABLING, 0);
1406                         }
1407                         quitNow();
1408                         break;
1409                     case CMD_START:
1410                         // Already started, ignore this command.
1411                         break;
1412                     case CMD_NO_ASSOCIATED_STATIONS_TIMEOUT:
1413                         if (!mTimeoutEnabled) {
1414                             Log.wtf(getTag(), "Timeout message received while timeout is disabled."
1415                                     + " Dropping.");
1416                             break;
1417                         }
1418                         if (getConnectedClientList().size() != 0) {
1419                             Log.wtf(getTag(), "Timeout message received but has clients. "
1420                                     + "Dropping.");
1421                             break;
1422                         }
1423                         mSoftApNotifier.showSoftApShutdownTimeoutExpiredNotification();
1424                         Log.i(getTag(), "Timeout message received. Stopping soft AP.");
1425                         updateApState(WifiManager.WIFI_AP_STATE_DISABLING,
1426                                 WifiManager.WIFI_AP_STATE_ENABLED, 0);
1427                         quitNow();
1428                         break;
1429                     case CMD_NO_ASSOCIATED_STATIONS_TIMEOUT_ON_ONE_INSTANCE:
1430                         if (!isBridgedMode() || mCurrentSoftApInfoMap.size() != 2) {
1431                             Log.wtf(getTag(), "Ignore Bridged Mode Timeout message received"
1432                                     + " in single AP state. Dropping");
1433                             break;
1434                         }
1435                         if (!mBridgedModeOpportunisticsShutdownTimeoutEnabled) {
1436                             Log.wtf(getTag(), "Bridged Mode Timeout message received"
1437                                     + " while timeout is disabled. Dropping.");
1438                             break;
1439                         }
1440                         Set<String> idleInstances = getIdleInstances();
1441                         if (idleInstances.size() == 0) {
1442                             break;
1443                         }
1444                         Log.d(getTag(), "Instance idle timout, the number of the idle instances is "
1445                                 + idleInstances.size());
1446                         removeIfaceInstanceFromBridgedApIface(
1447                                 getHighestFrequencyInstance(idleInstances));
1448                         break;
1449                     case CMD_INTERFACE_DESTROYED:
1450                         Log.d(getTag(), "Interface was cleanly destroyed.");
1451                         updateApState(WifiManager.WIFI_AP_STATE_DISABLING,
1452                                 WifiManager.WIFI_AP_STATE_ENABLED, 0);
1453                         mIfaceIsDestroyed = true;
1454                         quitNow();
1455                         break;
1456                     case CMD_FAILURE:
1457                         Log.w(getTag(), "hostapd failure, stop and report failure");
1458                         /* fall through */
1459                     case CMD_INTERFACE_DOWN:
1460                         Log.w(getTag(), "interface error, stop and report failure");
1461                         updateApState(WifiManager.WIFI_AP_STATE_FAILED,
1462                                 WifiManager.WIFI_AP_STATE_ENABLED,
1463                                 WifiManager.SAP_START_FAILURE_GENERAL);
1464                         updateApState(WifiManager.WIFI_AP_STATE_DISABLING,
1465                                 WifiManager.WIFI_AP_STATE_FAILED, 0);
1466                         quitNow();
1467                         break;
1468                     case CMD_UPDATE_CAPABILITY:
1469                         // Capability should only changed by carrier requirement. Only apply to
1470                         // Tether Mode
1471                         if (mOriginalModeConfiguration.getTargetMode()
1472                                 ==  WifiManager.IFACE_IP_MODE_TETHERED) {
1473                             SoftApCapability capability = (SoftApCapability) message.obj;
1474                             mCurrentSoftApCapability = new SoftApCapability(capability);
1475                             mWifiMetrics.updateSoftApCapability(mCurrentSoftApCapability,
1476                                     mOriginalModeConfiguration.getTargetMode(), isBridgedMode());
1477                             updateClientConnection();
1478                         }
1479                         break;
1480                     case CMD_UPDATE_CONFIG:
1481                         SoftApConfiguration newConfig = (SoftApConfiguration) message.obj;
1482                         SoftApConfiguration originalConfig =
1483                                 mOriginalModeConfiguration.getSoftApConfiguration();
1484                         if (!ApConfigUtil.checkConfigurationChangeNeedToRestart(
1485                                 originalConfig, newConfig)) {
1486                             Log.d(getTag(), "Configuration changed to " + newConfig);
1487                             if (mCurrentSoftApConfiguration.getMaxNumberOfClients()
1488                                     != newConfig.getMaxNumberOfClients()) {
1489                                 Log.d(getTag(), "Max Client changed, reset to record the metrics");
1490                                 mEverReportMetricsForMaxClient = false;
1491                             }
1492                             boolean needRescheduleTimer =
1493                                     mCurrentSoftApConfiguration.getShutdownTimeoutMillis()
1494                                     != newConfig.getShutdownTimeoutMillis()
1495                                     || mTimeoutEnabled != newConfig.isAutoShutdownEnabled()
1496                                     || mBridgedModeOpportunisticsShutdownTimeoutEnabled
1497                                     != newConfig
1498                                     .isBridgedModeOpportunisticShutdownEnabledInternal();
1499                             updateChangeableConfiguration(newConfig);
1500                             updateClientConnection();
1501                             if (needRescheduleTimer) {
1502                                 cancelTimeoutMessage();
1503                                 cancelBridgedModeIdleInstanceTimeoutMessage();
1504                                 scheduleTimeoutMessages();
1505                                 // Update SoftApInfo
1506                                 for (SoftApInfo info : mCurrentSoftApInfoMap.values()) {
1507                                     SoftApInfo newInfo = new SoftApInfo(info);
1508                                     newInfo.setAutoShutdownTimeoutMillis(mTimeoutEnabled
1509                                             ? getShutdownTimeoutMillis() : 0);
1510                                     updateSoftApInfo(newInfo, false);
1511                                 }
1512                             }
1513                             mWifiMetrics.updateSoftApConfiguration(
1514                                     mCurrentSoftApConfiguration,
1515                                     mOriginalModeConfiguration.getTargetMode(), isBridgedMode());
1516                         } else {
1517                             Log.d(getTag(), "Ignore the config: " + newConfig
1518                                     + " update since it requires restart");
1519                         }
1520                         break;
1521                     case CMD_UPDATE_COUNTRY_CODE:
1522                         String countryCode = (String) message.obj;
1523                         if (!TextUtils.isEmpty(countryCode)
1524                                 && !TextUtils.equals(mCountryCode, countryCode)
1525                                 && mWifiNative.setApCountryCode(
1526                                 mApInterfaceName, countryCode.toUpperCase(Locale.ROOT))) {
1527                             Log.i(getTag(), "Update country code when Soft AP enabled from "
1528                                     + mCountryCode + " to " + countryCode);
1529                             mCountryCode = countryCode;
1530                         }
1531                         break;
1532                     case CMD_FORCE_DISCONNECT_PENDING_CLIENTS:
1533                         if (mPendingDisconnectClients.size() != 0) {
1534                             Log.d(getTag(), "Disconnect pending list is NOT empty");
1535                             mPendingDisconnectClients.forEach((pendingClient, reason)->
1536                                     mWifiNative.forceClientDisconnect(mApInterfaceName,
1537                                     pendingClient.getMacAddress(), reason));
1538                             sendMessageDelayed(
1539                                     SoftApStateMachine.CMD_FORCE_DISCONNECT_PENDING_CLIENTS,
1540                                     SOFT_AP_PENDING_DISCONNECTION_CHECK_DELAY_MS);
1541                         }
1542                         break;
1543                     case CMD_SAFE_CHANNEL_FREQUENCY_CHANGED:
1544                         updateSafeChannelFrequencyList();
1545                         if (!isBridgedMode() || mCurrentSoftApInfoMap.size() != 2) {
1546                             Log.d(getTag(), "Ignore safe channel changed in single AP state");
1547                             break;
1548                         }
1549                         Set<String> unavailableInstances = new HashSet<>();
1550                         for (SoftApInfo currentInfo : mCurrentSoftApInfoMap.values()) {
1551                             int sapFreq = currentInfo.getFrequency();
1552                             if (!mSafeChannelFrequencyList.contains(sapFreq)) {
1553                                 int sapBand = ApConfigUtil.convertFrequencyToBand(sapFreq);
1554                                 if (sapBand != ApConfigUtil.removeUnavailableBands(
1555                                             mCurrentSoftApCapability,
1556                                             sapBand, mCoexManager)) {
1557                                     unavailableInstances.add(currentInfo.getApInstanceIdentifier());
1558                                 }
1559                             }
1560                         }
1561                         removeIfaceInstanceFromBridgedApIface(
1562                                 getHighestFrequencyInstance(unavailableInstances));
1563                         break;
1564                     case CMD_HANDLE_WIFI_CONNECTED:
1565                         if (!isBridgedMode() || mCurrentSoftApInfoMap.size() != 2) {
1566                             Log.d(getTag(), "Ignore wifi connected in single AP state");
1567                             break;
1568                         }
1569                         WifiInfo wifiInfo = (WifiInfo) message.obj;
1570                         int wifiFreq = wifiInfo.getFrequency();
1571                         String targetShutDownInstance = "";
1572                         if (!mSafeChannelFrequencyList.contains(wifiFreq)) {
1573                             Log.i(getTag(), "Wifi connected to freq:" + wifiFreq
1574                                     + " which is unavailable for SAP");
1575                             for (SoftApInfo sapInfo : mCurrentSoftApInfoMap.values()) {
1576                                 if (ApConfigUtil.convertFrequencyToBand(sapInfo.getFrequency())
1577                                           == ApConfigUtil.convertFrequencyToBand(wifiFreq)) {
1578                                     targetShutDownInstance = sapInfo.getApInstanceIdentifier();
1579                                     Log.d(getTag(), "Remove the " + targetShutDownInstance
1580                                             + " instance which is running on the same band as "
1581                                             + "the wifi connection on an unsafe channel");
1582                                     break;
1583                                 }
1584                             }
1585                             // Wifi may connect to different band as the SAP. For instances:
1586                             // Wifi connect to 6Ghz but bridged AP is running on 2.4Ghz + 5Ghz.
1587                             // In this case, targetShutDownInstance will be empty, shutdown the
1588                             // highest frequency instance.
1589                             removeIfaceInstanceFromBridgedApIface(
1590                                     TextUtils.isEmpty(targetShutDownInstance)
1591                                     ? getHighestFrequencyInstance(mCurrentSoftApInfoMap.keySet())
1592                                     : targetShutDownInstance);
1593                         }
1594                         break;
1595                     default:
1596                         return NOT_HANDLED;
1597                 }
1598                 return HANDLED;
1599             }
1600         }
1601     }
1602 }
1603