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.content.Intent;
28 import android.net.MacAddress;
29 import android.net.wifi.ScanResult;
30 import android.net.wifi.SoftApCapability;
31 import android.net.wifi.SoftApConfiguration;
32 import android.net.wifi.SoftApInfo;
33 import android.net.wifi.WifiAnnotations;
34 import android.net.wifi.WifiClient;
35 import android.net.wifi.WifiManager;
36 import android.net.wifi.nl80211.NativeWifiClient;
37 import android.os.Handler;
38 import android.os.Looper;
39 import android.os.Message;
40 import android.os.SystemClock;
41 import android.os.UserHandle;
42 import android.text.TextUtils;
43 import android.util.Log;
44 
45 import com.android.internal.annotations.VisibleForTesting;
46 import com.android.internal.util.IState;
47 import com.android.internal.util.Preconditions;
48 import com.android.internal.util.State;
49 import com.android.internal.util.StateMachine;
50 import com.android.internal.util.WakeupMessage;
51 import com.android.server.wifi.WifiNative.InterfaceCallback;
52 import com.android.server.wifi.WifiNative.SoftApListener;
53 import com.android.server.wifi.util.ApConfigUtil;
54 import com.android.wifi.resources.R;
55 
56 import java.io.FileDescriptor;
57 import java.io.PrintWriter;
58 import java.text.SimpleDateFormat;
59 import java.util.ArrayList;
60 import java.util.Date;
61 import java.util.HashSet;
62 import java.util.Iterator;
63 import java.util.List;
64 import java.util.Locale;
65 import java.util.Set;
66 
67 /**
68  * Manage WiFi in AP mode.
69  * The internal state machine runs under the ClientModeImpl handler thread context.
70  */
71 public class SoftApManager implements ActiveModeManager {
72     private static final String TAG = "SoftApManager";
73 
74     @VisibleForTesting
75     public static final String SOFT_AP_SEND_MESSAGE_TIMEOUT_TAG = TAG
76             + " Soft AP Send Message Timeout";
77 
78     private final WifiContext mContext;
79     private final FrameworkFacade mFrameworkFacade;
80     private final WifiNative mWifiNative;
81 
82     @VisibleForTesting
83     SoftApNotifier mSoftApNotifier;
84 
85     private final String mCountryCode;
86 
87     private final SoftApStateMachine mStateMachine;
88 
89     private final Listener mModeListener;
90     private final WifiManager.SoftApCallback mSoftApCallback;
91 
92     private String mApInterfaceName;
93     private boolean mIfaceIsUp;
94     private boolean mIfaceIsDestroyed;
95 
96     private final WifiApConfigStore mWifiApConfigStore;
97 
98     private final WifiMetrics mWifiMetrics;
99 
100     private boolean mIsRandomizeBssid;
101 
102     @NonNull
103     private SoftApModeConfiguration mApConfig;
104 
105     @NonNull
106     private SoftApInfo mCurrentSoftApInfo = new SoftApInfo();
107 
108     @NonNull
109     private SoftApCapability mCurrentSoftApCapability;
110 
111     private List<WifiClient> mConnectedClients = new ArrayList<>();
112     private boolean mTimeoutEnabled = false;
113 
114     private final SarManager mSarManager;
115 
116     private String mStartTimestamp;
117 
118     private long mDefaultShutDownTimeoutMills;
119 
120     private static final SimpleDateFormat FORMATTER = new SimpleDateFormat("MM-dd HH:mm:ss.SSS");
121 
122     private BaseWifiDiagnostics mWifiDiagnostics;
123 
124     private @Role int mRole = ROLE_UNSPECIFIED;
125     private @Role int mTargetRole = ROLE_UNSPECIFIED;
126 
127     private boolean mEverReportMetricsForMaxClient = false;
128 
129     @NonNull
130     private Set<MacAddress> mBlockedClientList = new HashSet<>();
131 
132     @NonNull
133     private Set<MacAddress> mAllowedClientList = new HashSet<>();
134 
135     /**
136      * Listener for soft AP events.
137      */
138     private final SoftApListener mSoftApListener = new SoftApListener() {
139 
140         @Override
141         public void onFailure() {
142             mStateMachine.sendMessage(SoftApStateMachine.CMD_FAILURE);
143         }
144 
145         @Override
146         public void onConnectedClientsChanged(NativeWifiClient client, boolean isConnected) {
147             if (client != null) {
148                 mStateMachine.sendMessage(SoftApStateMachine.CMD_ASSOCIATED_STATIONS_CHANGED,
149                         isConnected ? 1 : 0, 0, client);
150             } else {
151                 Log.e(TAG, "onConnectedClientsChanged: Invalid type returned");
152             }
153         }
154 
155         @Override
156         public void onSoftApChannelSwitched(int frequency,
157                 @WifiAnnotations.Bandwidth int bandwidth) {
158             mStateMachine.sendMessage(
159                     SoftApStateMachine.CMD_SOFT_AP_CHANNEL_SWITCHED, frequency, bandwidth);
160         }
161     };
162 
SoftApManager(@onNull WifiContext context, @NonNull Looper looper, @NonNull FrameworkFacade framework, @NonNull WifiNative wifiNative, String countryCode, @NonNull Listener listener, @NonNull WifiManager.SoftApCallback callback, @NonNull WifiApConfigStore wifiApConfigStore, @NonNull SoftApModeConfiguration apConfig, @NonNull WifiMetrics wifiMetrics, @NonNull SarManager sarManager, @NonNull BaseWifiDiagnostics wifiDiagnostics)163     public SoftApManager(@NonNull WifiContext context,
164                          @NonNull Looper looper,
165                          @NonNull FrameworkFacade framework,
166                          @NonNull WifiNative wifiNative,
167                          String countryCode,
168                          @NonNull Listener listener,
169                          @NonNull WifiManager.SoftApCallback callback,
170                          @NonNull WifiApConfigStore wifiApConfigStore,
171                          @NonNull SoftApModeConfiguration apConfig,
172                          @NonNull WifiMetrics wifiMetrics,
173                          @NonNull SarManager sarManager,
174                          @NonNull BaseWifiDiagnostics wifiDiagnostics) {
175         mContext = context;
176         mFrameworkFacade = framework;
177         mSoftApNotifier = new SoftApNotifier(mContext, mFrameworkFacade);
178         mWifiNative = wifiNative;
179         mCountryCode = countryCode;
180         mModeListener = listener;
181         mSoftApCallback = callback;
182         mWifiApConfigStore = wifiApConfigStore;
183         SoftApConfiguration softApConfig = apConfig.getSoftApConfiguration();
184         mCurrentSoftApCapability = apConfig.getCapability();
185         // null is a valid input and means we use the user-configured tethering settings.
186         if (softApConfig == null) {
187             softApConfig = mWifiApConfigStore.getApConfiguration();
188             // may still be null if we fail to load the default config
189         }
190         if (softApConfig != null) {
191             mIsRandomizeBssid = softApConfig.getBssid() == null;
192             softApConfig = mWifiApConfigStore.randomizeBssidIfUnset(mContext, softApConfig);
193         }
194         mApConfig = new SoftApModeConfiguration(apConfig.getTargetMode(),
195                 softApConfig, mCurrentSoftApCapability);
196         mWifiMetrics = wifiMetrics;
197         mSarManager = sarManager;
198         mWifiDiagnostics = wifiDiagnostics;
199         mStateMachine = new SoftApStateMachine(looper);
200         if (softApConfig != null) {
201             mBlockedClientList = new HashSet<>(softApConfig.getBlockedClientList());
202             mAllowedClientList = new HashSet<>(softApConfig.getAllowedClientList());
203             mTimeoutEnabled = softApConfig.isAutoShutdownEnabled();
204         }
205         mDefaultShutDownTimeoutMills = mContext.getResources().getInteger(
206                 R.integer.config_wifiFrameworkSoftApShutDownTimeoutMilliseconds);
207     }
208 
209     /**
210      * Start soft AP, as configured in the constructor.
211      */
212     @Override
start()213     public void start() {
214         mStateMachine.sendMessage(SoftApStateMachine.CMD_START);
215     }
216 
217     /**
218      * Stop soft AP.
219      */
220     @Override
stop()221     public void stop() {
222         Log.d(TAG, " currentstate: " + getCurrentStateName());
223         mTargetRole = ROLE_UNSPECIFIED;
224         if (mApInterfaceName != null) {
225             if (mIfaceIsUp) {
226                 updateApState(WifiManager.WIFI_AP_STATE_DISABLING,
227                         WifiManager.WIFI_AP_STATE_ENABLED, 0);
228             } else {
229                 updateApState(WifiManager.WIFI_AP_STATE_DISABLING,
230                         WifiManager.WIFI_AP_STATE_ENABLING, 0);
231             }
232         }
233         mStateMachine.quitNow();
234     }
235 
236     @Override
isStopping()237     public boolean isStopping() {
238         return mTargetRole == ROLE_UNSPECIFIED && mRole != ROLE_UNSPECIFIED;
239     }
240 
241     @Override
getRole()242     public @Role int getRole() {
243         return mRole;
244     }
245 
246     @Override
setRole(@ole int role)247     public void setRole(@Role int role) {
248         // softap does not allow in-place switching of roles.
249         Preconditions.checkState(mRole == ROLE_UNSPECIFIED);
250         Preconditions.checkState(SOFTAP_ROLES.contains(role));
251         mTargetRole = role;
252         mRole = role;
253     }
254 
255     /**
256      * Update AP capability. Called when carrier config or device resouce config changed.
257      *
258      * @param capability new AP capability.
259      */
updateCapability(@onNull SoftApCapability capability)260     public void updateCapability(@NonNull SoftApCapability capability) {
261         mStateMachine.sendMessage(SoftApStateMachine.CMD_UPDATE_CAPABILITY, capability);
262     }
263 
264     /**
265      * Update AP configuration. Called when setting update config via
266      * {@link WifiManager#setSoftApConfiguration(SoftApConfiguration)}
267      *
268      * @param config new AP config.
269      */
updateConfiguration(@onNull SoftApConfiguration config)270     public void updateConfiguration(@NonNull SoftApConfiguration config) {
271         mStateMachine.sendMessage(SoftApStateMachine.CMD_UPDATE_CONFIG, config);
272     }
273 
274     /**
275      * Dump info about this softap manager.
276      */
277     @Override
dump(FileDescriptor fd, PrintWriter pw, String[] args)278     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
279         pw.println("--Dump of SoftApManager--");
280 
281         pw.println("current StateMachine mode: " + getCurrentStateName());
282         pw.println("mRole: " + mRole);
283         pw.println("mApInterfaceName: " + mApInterfaceName);
284         pw.println("mIfaceIsUp: " + mIfaceIsUp);
285         pw.println("mSoftApCountryCode: " + mCountryCode);
286         pw.println("mApConfig.targetMode: " + mApConfig.getTargetMode());
287         SoftApConfiguration softApConfig = mApConfig.getSoftApConfiguration();
288         pw.println("mApConfig.SoftApConfiguration.SSID: " + softApConfig.getSsid());
289         pw.println("mApConfig.SoftApConfiguration.mBand: " + softApConfig.getBand());
290         pw.println("mApConfig.SoftApConfiguration.hiddenSSID: " + softApConfig.isHiddenSsid());
291         pw.println("mConnectedClients.size(): " + mConnectedClients.size());
292         pw.println("mTimeoutEnabled: " + mTimeoutEnabled);
293         pw.println("mCurrentSoftApInfo " + mCurrentSoftApInfo);
294         pw.println("mStartTimestamp: " + mStartTimestamp);
295         mStateMachine.dump(fd, pw, args);
296     }
297 
getCurrentStateName()298     private String getCurrentStateName() {
299         IState currentState = mStateMachine.getCurrentState();
300 
301         if (currentState != null) {
302             return currentState.getName();
303         }
304 
305         return "StateMachine not active";
306     }
307 
308     /**
309      * Update AP state.
310      *
311      * @param newState     new AP state
312      * @param currentState current AP state
313      * @param reason       Failure reason if the new AP state is in failure state
314      */
updateApState(int newState, int currentState, int reason)315     private void updateApState(int newState, int currentState, int reason) {
316         mSoftApCallback.onStateChanged(newState, reason);
317 
318         //send the AP state change broadcast
319         final Intent intent = new Intent(WifiManager.WIFI_AP_STATE_CHANGED_ACTION);
320         intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
321         intent.putExtra(WifiManager.EXTRA_WIFI_AP_STATE, newState);
322         intent.putExtra(WifiManager.EXTRA_PREVIOUS_WIFI_AP_STATE, currentState);
323         if (newState == WifiManager.WIFI_AP_STATE_FAILED) {
324             //only set reason number when softAP start failed
325             intent.putExtra(WifiManager.EXTRA_WIFI_AP_FAILURE_REASON, reason);
326         }
327 
328         intent.putExtra(WifiManager.EXTRA_WIFI_AP_INTERFACE_NAME, mApInterfaceName);
329         intent.putExtra(WifiManager.EXTRA_WIFI_AP_MODE, mApConfig.getTargetMode());
330         mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
331     }
332 
setMacAddress()333     private int setMacAddress() {
334         MacAddress mac = mApConfig.getSoftApConfiguration().getBssid();
335 
336         if (mac == null) {
337             // If no BSSID is explicitly requested, (re-)configure the factory MAC address. Some
338             // drivers may not support setting the MAC at all, so fail soft in this case.
339             mac = mWifiNative.getFactoryMacAddress(mApInterfaceName);
340             if (mac == null) {
341                 Log.e(TAG, "failed to get factory MAC address");
342                 return ERROR_GENERIC;
343             }
344 
345             if (!mWifiNative.setMacAddress(mApInterfaceName, mac)) {
346                 Log.w(TAG, "failed to reset to factory MAC address; continuing with current MAC");
347             }
348             return SUCCESS;
349         }
350 
351 
352         if (mWifiNative.isSetMacAddressSupported(mApInterfaceName)) {
353             if (!mWifiNative.setMacAddress(mApInterfaceName, mac)) {
354                 Log.e(TAG, "failed to set explicitly requested MAC address");
355                 return ERROR_GENERIC;
356             }
357         } else if (!mIsRandomizeBssid) {
358             // If hardware does not support MAC address setter,
359             // only report the error for non randomization.
360             return ERROR_UNSUPPORTED_CONFIGURATION;
361         }
362         return SUCCESS;
363     }
364 
setCountryCode()365     private int setCountryCode() {
366         int band = mApConfig.getSoftApConfiguration().getBand();
367         if (TextUtils.isEmpty(mCountryCode)) {
368             if (band == SoftApConfiguration.BAND_5GHZ) {
369                 // Country code is mandatory for 5GHz band.
370                 Log.e(TAG, "Invalid country code, required for setting up soft ap in 5GHz");
371                 return ERROR_GENERIC;
372             }
373             // Absence of country code is not fatal for 2Ghz & Any band options.
374             return SUCCESS;
375         }
376 
377         if (!mWifiNative.setCountryCodeHal(
378                 mApInterfaceName, mCountryCode.toUpperCase(Locale.ROOT))) {
379             if (band == SoftApConfiguration.BAND_5GHZ) {
380                 // Return an error if failed to set country code when AP is configured for
381                 // 5GHz band.
382                 Log.e(TAG, "Failed to set country code, required for setting up soft ap in 5GHz");
383                 return ERROR_GENERIC;
384             }
385             // Failure to set country code is not fatal for other band options.
386         }
387         return SUCCESS;
388     }
389 
390     /**
391      * Start a soft AP instance as configured.
392      *
393      * @return integer result code
394      */
startSoftAp()395     private int startSoftAp() {
396         SoftApConfiguration config = mApConfig.getSoftApConfiguration();
397         if (config == null || config.getSsid() == null) {
398             Log.e(TAG, "Unable to start soft AP without valid configuration");
399             return ERROR_GENERIC;
400         }
401 
402         Log.d(TAG, "band " + config.getBand() + " iface "
403                 + mApInterfaceName + " country " + mCountryCode);
404 
405         int result = setMacAddress();
406         if (result != SUCCESS) {
407             return result;
408         }
409 
410         result = setCountryCode();
411         if (result != SUCCESS) {
412             return result;
413         }
414 
415         // Make a copy of configuration for updating AP band and channel.
416         SoftApConfiguration.Builder localConfigBuilder = new SoftApConfiguration.Builder(config);
417 
418         boolean acsEnabled = mCurrentSoftApCapability.areFeaturesSupported(
419                 SoftApCapability.SOFTAP_FEATURE_ACS_OFFLOAD);
420 
421         result = ApConfigUtil.updateApChannelConfig(
422                 mWifiNative, mContext.getResources(), mCountryCode, localConfigBuilder, config,
423                 acsEnabled);
424         if (result != SUCCESS) {
425             Log.e(TAG, "Failed to update AP band and channel");
426             return result;
427         }
428 
429         if (config.isHiddenSsid()) {
430             Log.d(TAG, "SoftAP is a hidden network");
431         }
432 
433         if (!ApConfigUtil.checkSupportAllConfiguration(config, mCurrentSoftApCapability)) {
434             Log.d(TAG, "Unsupported Configuration detect! config = " + config);
435             return ERROR_UNSUPPORTED_CONFIGURATION;
436         }
437 
438         if (!mWifiNative.startSoftAp(mApInterfaceName,
439                   localConfigBuilder.build(), mSoftApListener)) {
440             Log.e(TAG, "Soft AP start failed");
441             return ERROR_GENERIC;
442         }
443 
444         mWifiDiagnostics.startLogging(mApInterfaceName);
445         mStartTimestamp = FORMATTER.format(new Date(System.currentTimeMillis()));
446         Log.d(TAG, "Soft AP is started ");
447 
448         return SUCCESS;
449     }
450 
451     /**
452      * Disconnect all connected clients on active softap interface(s).
453      * This is usually done just before stopSoftAp().
454      */
disconnectAllClients()455     private void disconnectAllClients() {
456         for (WifiClient client : mConnectedClients) {
457             mWifiNative.forceClientDisconnect(mApInterfaceName, client.getMacAddress(),
458                     SAP_CLIENT_DISCONNECT_REASON_CODE_UNSPECIFIED);
459         }
460     }
461 
462     /**
463      * Teardown soft AP and teardown the interface.
464      */
stopSoftAp()465     private void stopSoftAp() {
466         disconnectAllClients();
467         mWifiDiagnostics.stopLogging(mApInterfaceName);
468         mWifiNative.teardownInterface(mApInterfaceName);
469         Log.d(TAG, "Soft AP is stopped");
470     }
471 
checkSoftApClient(SoftApConfiguration config, WifiClient newClient)472     private boolean checkSoftApClient(SoftApConfiguration config, WifiClient newClient) {
473         if (!mCurrentSoftApCapability.areFeaturesSupported(
474                 SoftApCapability.SOFTAP_FEATURE_CLIENT_FORCE_DISCONNECT)) {
475             return true;
476         }
477 
478         if (mBlockedClientList.contains(newClient.getMacAddress())) {
479             Log.d(TAG, "Force disconnect for client: " + newClient + "in blocked list");
480             mWifiNative.forceClientDisconnect(
481                     mApInterfaceName, newClient.getMacAddress(),
482                     WifiManager.SAP_CLIENT_BLOCK_REASON_CODE_BLOCKED_BY_USER);
483             return false;
484         }
485         if (config.isClientControlByUserEnabled()
486                 && !mAllowedClientList.contains(newClient.getMacAddress())) {
487             mSoftApCallback.onBlockedClientConnecting(newClient,
488                     WifiManager.SAP_CLIENT_BLOCK_REASON_CODE_BLOCKED_BY_USER);
489             Log.d(TAG, "Force disconnect for unauthorized client: " + newClient);
490             mWifiNative.forceClientDisconnect(
491                     mApInterfaceName, newClient.getMacAddress(),
492                     WifiManager.SAP_CLIENT_BLOCK_REASON_CODE_BLOCKED_BY_USER);
493             return false;
494         }
495         int maxConfig = mCurrentSoftApCapability.getMaxSupportedClients();
496         if (config.getMaxNumberOfClients() > 0) {
497             maxConfig = Math.min(maxConfig, config.getMaxNumberOfClients());
498         }
499 
500         if (mConnectedClients.size() >= maxConfig) {
501             Log.i(TAG, "No more room for new client:" + newClient);
502             mWifiNative.forceClientDisconnect(
503                     mApInterfaceName, newClient.getMacAddress(),
504                     WifiManager.SAP_CLIENT_BLOCK_REASON_CODE_NO_MORE_STAS);
505             mSoftApCallback.onBlockedClientConnecting(newClient,
506                     WifiManager.SAP_CLIENT_BLOCK_REASON_CODE_NO_MORE_STAS);
507             // Avoid report the max client blocked in the same settings.
508             if (!mEverReportMetricsForMaxClient) {
509                 mWifiMetrics.noteSoftApClientBlocked(maxConfig);
510                 mEverReportMetricsForMaxClient = true;
511             }
512             return false;
513         }
514         return true;
515     }
516 
517     private class SoftApStateMachine extends StateMachine {
518         // Commands for the state machine.
519         public static final int CMD_START = 0;
520         public static final int CMD_FAILURE = 2;
521         public static final int CMD_INTERFACE_STATUS_CHANGED = 3;
522         public static final int CMD_ASSOCIATED_STATIONS_CHANGED = 4;
523         public static final int CMD_NO_ASSOCIATED_STATIONS_TIMEOUT = 5;
524         public static final int CMD_INTERFACE_DESTROYED = 7;
525         public static final int CMD_INTERFACE_DOWN = 8;
526         public static final int CMD_SOFT_AP_CHANNEL_SWITCHED = 9;
527         public static final int CMD_UPDATE_CAPABILITY = 10;
528         public static final int CMD_UPDATE_CONFIG = 11;
529 
530         private final State mIdleState = new IdleState();
531         private final State mStartedState = new StartedState();
532 
533         private final InterfaceCallback mWifiNativeInterfaceCallback = new InterfaceCallback() {
534             @Override
535             public void onDestroyed(String ifaceName) {
536                 if (mApInterfaceName != null && mApInterfaceName.equals(ifaceName)) {
537                     sendMessage(CMD_INTERFACE_DESTROYED);
538                 }
539             }
540 
541             @Override
542             public void onUp(String ifaceName) {
543                 if (mApInterfaceName != null && mApInterfaceName.equals(ifaceName)) {
544                     sendMessage(CMD_INTERFACE_STATUS_CHANGED, 1);
545                 }
546             }
547 
548             @Override
549             public void onDown(String ifaceName) {
550                 if (mApInterfaceName != null && mApInterfaceName.equals(ifaceName)) {
551                     sendMessage(CMD_INTERFACE_STATUS_CHANGED, 0);
552                 }
553             }
554         };
555 
SoftApStateMachine(Looper looper)556         SoftApStateMachine(Looper looper) {
557             super(TAG, looper);
558 
559             addState(mIdleState);
560             addState(mStartedState);
561 
562             setInitialState(mIdleState);
563             start();
564         }
565 
566         private class IdleState extends State {
567             @Override
enter()568             public void enter() {
569                 mApInterfaceName = null;
570                 mIfaceIsUp = false;
571                 mIfaceIsDestroyed = false;
572             }
573 
574             @Override
processMessage(Message message)575             public boolean processMessage(Message message) {
576                 switch (message.what) {
577                     case CMD_START:
578                         mApInterfaceName = mWifiNative.setupInterfaceForSoftApMode(
579                                 mWifiNativeInterfaceCallback);
580                         if (TextUtils.isEmpty(mApInterfaceName)) {
581                             Log.e(TAG, "setup failure when creating ap interface.");
582                             updateApState(WifiManager.WIFI_AP_STATE_FAILED,
583                                     WifiManager.WIFI_AP_STATE_DISABLED,
584                                     WifiManager.SAP_START_FAILURE_GENERAL);
585                             mWifiMetrics.incrementSoftApStartResult(
586                                     false, WifiManager.SAP_START_FAILURE_GENERAL);
587                             mModeListener.onStartFailure();
588                             break;
589                         }
590                         mSoftApNotifier.dismissSoftApShutDownTimeoutExpiredNotification();
591                         updateApState(WifiManager.WIFI_AP_STATE_ENABLING,
592                                 WifiManager.WIFI_AP_STATE_DISABLED, 0);
593                         int result = startSoftAp();
594                         if (result != SUCCESS) {
595                             int failureReason = WifiManager.SAP_START_FAILURE_GENERAL;
596                             if (result == ERROR_NO_CHANNEL) {
597                                 failureReason = WifiManager.SAP_START_FAILURE_NO_CHANNEL;
598                             } else if (result == ERROR_UNSUPPORTED_CONFIGURATION) {
599                                 failureReason = WifiManager
600                                         .SAP_START_FAILURE_UNSUPPORTED_CONFIGURATION;
601                             }
602                             updateApState(WifiManager.WIFI_AP_STATE_FAILED,
603                                     WifiManager.WIFI_AP_STATE_ENABLING,
604                                     failureReason);
605                             stopSoftAp();
606                             mWifiMetrics.incrementSoftApStartResult(false, failureReason);
607                             mModeListener.onStartFailure();
608                             break;
609                         }
610                         transitionTo(mStartedState);
611                         break;
612                     case CMD_UPDATE_CAPABILITY:
613                         // Capability should only changed by carrier requirement. Only apply to
614                         // Tether Mode
615                         if (mApConfig.getTargetMode() ==  WifiManager.IFACE_IP_MODE_TETHERED) {
616                             SoftApCapability capability = (SoftApCapability) message.obj;
617                             mCurrentSoftApCapability = new SoftApCapability(capability);
618                         }
619                         break;
620                     case CMD_UPDATE_CONFIG:
621                         SoftApConfiguration newConfig = (SoftApConfiguration) message.obj;
622                         Log.d(TAG, "Configuration changed to " + newConfig);
623                         mApConfig = new SoftApModeConfiguration(mApConfig.getTargetMode(),
624                                 newConfig, mCurrentSoftApCapability);
625                         mBlockedClientList = new HashSet<>(newConfig.getBlockedClientList());
626                         mAllowedClientList = new HashSet<>(newConfig.getAllowedClientList());
627                         mTimeoutEnabled = newConfig.isAutoShutdownEnabled();
628                         break;
629                     default:
630                         // Ignore all other commands.
631                         break;
632                 }
633 
634                 return HANDLED;
635             }
636         }
637 
638         private class StartedState extends State {
639             private WakeupMessage mSoftApTimeoutMessage;
640 
scheduleTimeoutMessage()641             private void scheduleTimeoutMessage() {
642                 if (!mTimeoutEnabled || mConnectedClients.size() != 0) {
643                     cancelTimeoutMessage();
644                     return;
645                 }
646                 long timeout = mApConfig.getSoftApConfiguration().getShutdownTimeoutMillis();
647                 if (timeout == 0) {
648                     timeout =  mDefaultShutDownTimeoutMills;
649                 }
650                 mSoftApTimeoutMessage.schedule(SystemClock.elapsedRealtime()
651                         + timeout);
652                 Log.d(TAG, "Timeout message scheduled, delay = "
653                         + timeout);
654             }
655 
cancelTimeoutMessage()656             private void cancelTimeoutMessage() {
657                 mSoftApTimeoutMessage.cancel();
658                 Log.d(TAG, "Timeout message canceled");
659             }
660 
661             /**
662              * When configuration changed, it need to force some clients disconnect to match the
663              * configuration.
664              */
updateClientConnection()665             private void updateClientConnection() {
666                 if (!mCurrentSoftApCapability.areFeaturesSupported(
667                         SoftApCapability.SOFTAP_FEATURE_CLIENT_FORCE_DISCONNECT)) {
668                     return;
669                 }
670                 final int maxAllowedClientsByHardwareAndCarrier =
671                         mCurrentSoftApCapability.getMaxSupportedClients();
672                 final int userApConfigMaxClientCount =
673                         mApConfig.getSoftApConfiguration().getMaxNumberOfClients();
674                 int finalMaxClientCount = maxAllowedClientsByHardwareAndCarrier;
675                 if (userApConfigMaxClientCount > 0) {
676                     finalMaxClientCount = Math.min(userApConfigMaxClientCount,
677                             maxAllowedClientsByHardwareAndCarrier);
678                 }
679                 int targetDisconnectClientNumber = mConnectedClients.size() - finalMaxClientCount;
680                 List<WifiClient> allowedConnectedList = new ArrayList<>();
681                 Iterator<WifiClient> iterator = mConnectedClients.iterator();
682                 while (iterator.hasNext()) {
683                     WifiClient client = iterator.next();
684                     if (mBlockedClientList.contains(client.getMacAddress())
685                               || (mApConfig.getSoftApConfiguration().isClientControlByUserEnabled()
686                               && !mAllowedClientList.contains(client.getMacAddress()))) {
687                         Log.d(TAG, "Force disconnect for not allowed client: " + client);
688                         mWifiNative.forceClientDisconnect(
689                                 mApInterfaceName, client.getMacAddress(),
690                                 WifiManager.SAP_CLIENT_BLOCK_REASON_CODE_BLOCKED_BY_USER);
691                         targetDisconnectClientNumber--;
692                     } else {
693                         allowedConnectedList.add(client);
694                     }
695                 }
696 
697                 if (targetDisconnectClientNumber > 0) {
698                     Iterator<WifiClient> allowedClientIterator = allowedConnectedList.iterator();
699                     while (allowedClientIterator.hasNext()) {
700                         if (targetDisconnectClientNumber == 0) break;
701                         WifiClient allowedClient = allowedClientIterator.next();
702                         Log.d(TAG, "Force disconnect for client due to no more room: "
703                                 + allowedClient);
704                         mWifiNative.forceClientDisconnect(
705                                 mApInterfaceName, allowedClient.getMacAddress(),
706                                 WifiManager.SAP_CLIENT_BLOCK_REASON_CODE_NO_MORE_STAS);
707                         targetDisconnectClientNumber--;
708                     }
709                 }
710             }
711 
712             /**
713              * Set stations associated with this soft AP
714              * @param client The station for which connection state changed.
715              * @param isConnected True for the connection changed to connect, otherwise false.
716              */
updateConnectedClients(WifiClient client, boolean isConnected)717             private void updateConnectedClients(WifiClient client, boolean isConnected) {
718                 if (client == null) {
719                     return;
720                 }
721 
722                 int index = mConnectedClients.indexOf(client);
723                 if ((index != -1) == isConnected) {
724                     Log.e(TAG, "Drop client connection event, client "
725                             + client + "isConnected: " + isConnected
726                             + " , duplicate event or client is blocked");
727                     return;
728                 }
729                 if (isConnected) {
730                     boolean isAllow = checkSoftApClient(
731                             mApConfig.getSoftApConfiguration(), client);
732                     if (isAllow) {
733                         mConnectedClients.add(client);
734                     } else {
735                         return;
736                     }
737                 } else {
738                     mConnectedClients.remove(index);
739                 }
740 
741                 Log.d(TAG, "The connected wifi stations have changed with count: "
742                         + mConnectedClients.size() + ": " + mConnectedClients);
743 
744                 if (mSoftApCallback != null) {
745                     mSoftApCallback.onConnectedClientsChanged(mConnectedClients);
746                 } else {
747                     Log.e(TAG,
748                             "SoftApCallback is null. Dropping ConnectedClientsChanged event."
749                     );
750                 }
751 
752                 mWifiMetrics.addSoftApNumAssociatedStationsChangedEvent(
753                         mConnectedClients.size(), mApConfig.getTargetMode());
754 
755                 scheduleTimeoutMessage();
756             }
757 
setSoftApChannel(int freq, @WifiAnnotations.Bandwidth int apBandwidth)758             private void setSoftApChannel(int freq, @WifiAnnotations.Bandwidth int apBandwidth) {
759                 Log.d(TAG, "Channel switched. Frequency: " + freq
760                         + " Bandwidth: " + apBandwidth);
761 
762                 if (freq == mCurrentSoftApInfo.getFrequency()
763                         && apBandwidth == mCurrentSoftApInfo.getBandwidth()) {
764                     return; // no change
765                 }
766 
767                 mCurrentSoftApInfo.setFrequency(freq);
768                 mCurrentSoftApInfo.setBandwidth(apBandwidth);
769                 mSoftApCallback.onInfoChanged(mCurrentSoftApInfo);
770 
771                 // ignore invalid freq and softap disable case for metrics
772                 if (freq > 0 && apBandwidth != SoftApInfo.CHANNEL_WIDTH_INVALID) {
773                     mWifiMetrics.addSoftApChannelSwitchedEvent(mCurrentSoftApInfo.getFrequency(),
774                             mCurrentSoftApInfo.getBandwidth(), mApConfig.getTargetMode());
775                     updateUserBandPreferenceViolationMetricsIfNeeded();
776                 }
777             }
778 
onUpChanged(boolean isUp)779             private void onUpChanged(boolean isUp) {
780                 if (isUp == mIfaceIsUp) {
781                     return;  // no change
782                 }
783 
784                 mIfaceIsUp = isUp;
785                 if (isUp) {
786                     Log.d(TAG, "SoftAp is ready for use");
787                     updateApState(WifiManager.WIFI_AP_STATE_ENABLED,
788                             WifiManager.WIFI_AP_STATE_ENABLING, 0);
789                     mModeListener.onStarted();
790                     mWifiMetrics.incrementSoftApStartResult(true, 0);
791                     if (mSoftApCallback != null) {
792                         mSoftApCallback.onConnectedClientsChanged(mConnectedClients);
793                     }
794                 } else {
795                     // the interface was up, but goes down
796                     sendMessage(CMD_INTERFACE_DOWN);
797                 }
798                 mWifiMetrics.addSoftApUpChangedEvent(isUp, mApConfig.getTargetMode(),
799                         mDefaultShutDownTimeoutMills);
800                 if (isUp) {
801                     mWifiMetrics.updateSoftApConfiguration(mApConfig.getSoftApConfiguration(),
802                             mApConfig.getTargetMode());
803                     mWifiMetrics.updateSoftApCapability(mCurrentSoftApCapability,
804                             mApConfig.getTargetMode());
805                 }
806             }
807 
808             @Override
enter()809             public void enter() {
810                 mIfaceIsUp = false;
811                 mIfaceIsDestroyed = false;
812                 onUpChanged(mWifiNative.isInterfaceUp(mApInterfaceName));
813 
814                 Handler handler = mStateMachine.getHandler();
815                 mSoftApTimeoutMessage = new WakeupMessage(mContext, handler,
816                         SOFT_AP_SEND_MESSAGE_TIMEOUT_TAG,
817                         SoftApStateMachine.CMD_NO_ASSOCIATED_STATIONS_TIMEOUT);
818 
819                 mSarManager.setSapWifiState(WifiManager.WIFI_AP_STATE_ENABLED);
820 
821                 Log.d(TAG, "Resetting connected clients on start");
822                 mConnectedClients.clear();
823                 mEverReportMetricsForMaxClient = false;
824                 scheduleTimeoutMessage();
825             }
826 
827             @Override
exit()828             public void exit() {
829                 if (!mIfaceIsDestroyed) {
830                     stopSoftAp();
831                 }
832 
833                 Log.d(TAG, "Resetting num stations on stop");
834                 if (mConnectedClients.size() != 0) {
835                     mConnectedClients.clear();
836                     if (mSoftApCallback != null) {
837                         mSoftApCallback.onConnectedClientsChanged(mConnectedClients);
838                     }
839                     mWifiMetrics.addSoftApNumAssociatedStationsChangedEvent(
840                             0, mApConfig.getTargetMode());
841                 }
842                 cancelTimeoutMessage();
843 
844                 // Need this here since we are exiting |Started| state and won't handle any
845                 // future CMD_INTERFACE_STATUS_CHANGED events after this point
846                 mWifiMetrics.addSoftApUpChangedEvent(false, mApConfig.getTargetMode(),
847                         mDefaultShutDownTimeoutMills);
848                 updateApState(WifiManager.WIFI_AP_STATE_DISABLED,
849                         WifiManager.WIFI_AP_STATE_DISABLING, 0);
850 
851                 mSarManager.setSapWifiState(WifiManager.WIFI_AP_STATE_DISABLED);
852                 mApInterfaceName = null;
853                 mIfaceIsUp = false;
854                 mIfaceIsDestroyed = false;
855                 mRole = ROLE_UNSPECIFIED;
856                 mStateMachine.quitNow();
857                 mModeListener.onStopped();
858                 setSoftApChannel(0, SoftApInfo.CHANNEL_WIDTH_INVALID);
859             }
860 
updateUserBandPreferenceViolationMetricsIfNeeded()861             private void updateUserBandPreferenceViolationMetricsIfNeeded() {
862                 int band = mApConfig.getSoftApConfiguration().getBand();
863                 boolean bandPreferenceViolated =
864                         (ScanResult.is24GHz(mCurrentSoftApInfo.getFrequency())
865                             && !ApConfigUtil.containsBand(band,
866                                     SoftApConfiguration.BAND_2GHZ))
867                         || (ScanResult.is5GHz(mCurrentSoftApInfo.getFrequency())
868                             && !ApConfigUtil.containsBand(band,
869                                     SoftApConfiguration.BAND_5GHZ))
870                         || (ScanResult.is6GHz(mCurrentSoftApInfo.getFrequency())
871                             && !ApConfigUtil.containsBand(band,
872                                     SoftApConfiguration.BAND_6GHZ));
873 
874                 if (bandPreferenceViolated) {
875                     Log.e(TAG, "Channel does not satisfy user band preference: "
876                             + mCurrentSoftApInfo.getFrequency());
877                     mWifiMetrics.incrementNumSoftApUserBandPreferenceUnsatisfied();
878                 }
879             }
880 
881             @Override
processMessage(Message message)882             public boolean processMessage(Message message) {
883                 switch (message.what) {
884                     case CMD_ASSOCIATED_STATIONS_CHANGED:
885                         if (!(message.obj instanceof NativeWifiClient)) {
886                             Log.e(TAG, "Invalid type returned for"
887                                     + " CMD_ASSOCIATED_STATIONS_CHANGED");
888                             break;
889                         }
890                         NativeWifiClient nativeClient = (NativeWifiClient) message.obj;
891                         boolean isConnected = (message.arg1 == 1);
892                         if (nativeClient != null && nativeClient.getMacAddress() != null) {
893                             WifiClient client = new WifiClient(nativeClient.getMacAddress());
894                             Log.d(TAG, "CMD_ASSOCIATED_STATIONS_CHANGED, Client: "
895                                     + nativeClient.getMacAddress().toString() + " isConnected: "
896                                     + isConnected);
897                             updateConnectedClients(client, isConnected);
898                         }
899                         break;
900                     case CMD_SOFT_AP_CHANNEL_SWITCHED:
901                         if (message.arg1 < 0) {
902                             Log.e(TAG, "Invalid ap channel frequency: " + message.arg1);
903                             break;
904                         }
905                         setSoftApChannel(message.arg1, message.arg2);
906                         break;
907                     case CMD_INTERFACE_STATUS_CHANGED:
908                         boolean isUp = message.arg1 == 1;
909                         onUpChanged(isUp);
910                         break;
911                     case CMD_START:
912                         // Already started, ignore this command.
913                         break;
914                     case CMD_NO_ASSOCIATED_STATIONS_TIMEOUT:
915                         if (!mTimeoutEnabled) {
916                             Log.wtf(TAG, "Timeout message received while timeout is disabled."
917                                     + " Dropping.");
918                             break;
919                         }
920                         if (mConnectedClients.size() != 0) {
921                             Log.wtf(TAG, "Timeout message received but has clients. Dropping.");
922                             break;
923                         }
924                         mSoftApNotifier.showSoftApShutDownTimeoutExpiredNotification();
925                         Log.i(TAG, "Timeout message received. Stopping soft AP.");
926                         updateApState(WifiManager.WIFI_AP_STATE_DISABLING,
927                                 WifiManager.WIFI_AP_STATE_ENABLED, 0);
928                         transitionTo(mIdleState);
929                         break;
930                     case CMD_INTERFACE_DESTROYED:
931                         Log.d(TAG, "Interface was cleanly destroyed.");
932                         updateApState(WifiManager.WIFI_AP_STATE_DISABLING,
933                                 WifiManager.WIFI_AP_STATE_ENABLED, 0);
934                         mIfaceIsDestroyed = true;
935                         transitionTo(mIdleState);
936                         break;
937                     case CMD_FAILURE:
938                         Log.w(TAG, "hostapd failure, stop and report failure");
939                         /* fall through */
940                     case CMD_INTERFACE_DOWN:
941                         Log.w(TAG, "interface error, stop and report failure");
942                         updateApState(WifiManager.WIFI_AP_STATE_FAILED,
943                                 WifiManager.WIFI_AP_STATE_ENABLED,
944                                 WifiManager.SAP_START_FAILURE_GENERAL);
945                         updateApState(WifiManager.WIFI_AP_STATE_DISABLING,
946                                 WifiManager.WIFI_AP_STATE_FAILED, 0);
947                         transitionTo(mIdleState);
948                         break;
949                     case CMD_UPDATE_CAPABILITY:
950                         // Capability should only changed by carrier requirement. Only apply to
951                         // Tether Mode
952                         if (mApConfig.getTargetMode() ==  WifiManager.IFACE_IP_MODE_TETHERED) {
953                             SoftApCapability capability = (SoftApCapability) message.obj;
954                             mCurrentSoftApCapability = new SoftApCapability(capability);
955                             mWifiMetrics.updateSoftApCapability(mCurrentSoftApCapability,
956                                     mApConfig.getTargetMode());
957                             updateClientConnection();
958                         }
959                         break;
960                     case CMD_UPDATE_CONFIG:
961                         SoftApConfiguration newConfig = (SoftApConfiguration) message.obj;
962                         SoftApConfiguration currentConfig = mApConfig.getSoftApConfiguration();
963                         if (mIsRandomizeBssid) {
964                             // Current bssid is ramdon because unset. Set back to null..
965                             currentConfig = new SoftApConfiguration.Builder(currentConfig)
966                                     .setBssid(null)
967                                     .build();
968                         }
969                         if (!ApConfigUtil.checkConfigurationChangeNeedToRestart(
970                                 currentConfig, newConfig)) {
971                             Log.d(TAG, "Configuration changed to " + newConfig);
972                             if (mApConfig.getSoftApConfiguration().getMaxNumberOfClients()
973                                     != newConfig.getMaxNumberOfClients()) {
974                                 Log.d(TAG, "Max Client changed, reset to record the metrics");
975                                 mEverReportMetricsForMaxClient = false;
976                             }
977                             boolean needRescheduleTimer =
978                                     mApConfig.getSoftApConfiguration().getShutdownTimeoutMillis()
979                                     != newConfig.getShutdownTimeoutMillis()
980                                     || mTimeoutEnabled != newConfig.isAutoShutdownEnabled();
981                             mBlockedClientList = new HashSet<>(newConfig.getBlockedClientList());
982                             mAllowedClientList = new HashSet<>(newConfig.getAllowedClientList());
983                             mTimeoutEnabled = newConfig.isAutoShutdownEnabled();
984                             mApConfig = new SoftApModeConfiguration(mApConfig.getTargetMode(),
985                                     newConfig, mCurrentSoftApCapability);
986                             updateClientConnection();
987                             if (needRescheduleTimer) {
988                                 cancelTimeoutMessage();
989                                 scheduleTimeoutMessage();
990                             }
991                             mWifiMetrics.updateSoftApConfiguration(
992                                     mApConfig.getSoftApConfiguration(),
993                                     mApConfig.getTargetMode());
994                         } else {
995                             Log.d(TAG, "Ignore the config: " + newConfig
996                                     + " update since it requires restart");
997                         }
998                         break;
999                     default:
1000                         return NOT_HANDLED;
1001                 }
1002                 return HANDLED;
1003             }
1004         }
1005     }
1006 }
1007