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