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