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