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 com.android.server.wifi.util.ApConfigUtil.ERROR_GENERIC;
20 import static com.android.server.wifi.util.ApConfigUtil.ERROR_NO_CHANNEL;
21 import static com.android.server.wifi.util.ApConfigUtil.SUCCESS;
22 
23 import android.annotation.NonNull;
24 import android.content.Context;
25 import android.content.Intent;
26 import android.database.ContentObserver;
27 import android.net.MacAddress;
28 import android.net.wifi.ScanResult;
29 import android.net.wifi.WifiClient;
30 import android.net.wifi.WifiConfiguration;
31 import android.net.wifi.WifiManager;
32 import android.os.Handler;
33 import android.os.Looper;
34 import android.os.Message;
35 import android.os.SystemClock;
36 import android.os.UserHandle;
37 import android.provider.Settings;
38 import android.text.TextUtils;
39 import android.util.Log;
40 
41 import com.android.internal.R;
42 import com.android.internal.annotations.VisibleForTesting;
43 import com.android.internal.util.IState;
44 import com.android.internal.util.State;
45 import com.android.internal.util.StateMachine;
46 import com.android.internal.util.WakeupMessage;
47 import com.android.server.wifi.WifiNative.InterfaceCallback;
48 import com.android.server.wifi.WifiNative.SoftApListener;
49 import com.android.server.wifi.util.ApConfigUtil;
50 import com.android.server.wifi.wificond.NativeWifiClient;
51 
52 import java.io.FileDescriptor;
53 import java.io.PrintWriter;
54 import java.util.ArrayList;
55 import java.util.List;
56 import java.util.Locale;
57 
58 /**
59  * Manage WiFi in AP mode.
60  * The internal state machine runs under the ClientModeImpl handler thread context.
61  */
62 public class SoftApManager implements ActiveModeManager {
63     private static final String TAG = "SoftApManager";
64 
65     // Minimum limit to use for timeout delay if the value from overlay setting is too small.
66     private static final int MIN_SOFT_AP_TIMEOUT_DELAY_MS = 600_000;  // 10 minutes
67 
68     @VisibleForTesting
69     public static final String SOFT_AP_SEND_MESSAGE_TIMEOUT_TAG = TAG
70             + " Soft AP Send Message Timeout";
71 
72     private final Context mContext;
73     private final FrameworkFacade mFrameworkFacade;
74     private final WifiNative mWifiNative;
75 
76     private final String mCountryCode;
77 
78     private final SoftApStateMachine mStateMachine;
79 
80     private final WifiManager.SoftApCallback mCallback;
81 
82     private String mApInterfaceName;
83     private boolean mIfaceIsUp;
84     private boolean mIfaceIsDestroyed;
85 
86     private final WifiApConfigStore mWifiApConfigStore;
87 
88     private final WifiMetrics mWifiMetrics;
89 
90     private final int mMode;
91     private WifiConfiguration mApConfig;
92 
93     private int mReportedFrequency = -1;
94     private int mReportedBandwidth = -1;
95 
96     private List<WifiClient> mConnectedClients = new ArrayList<>();
97     private boolean mTimeoutEnabled = false;
98 
99     private final SarManager mSarManager;
100 
101     private long mStartTimestamp = -1;
102 
103     private BaseWifiDiagnostics mWifiDiagnostics;
104 
105     /**
106      * Listener for soft AP events.
107      */
108     private final SoftApListener mSoftApListener = new SoftApListener() {
109 
110         @Override
111         public void onFailure() {
112             mStateMachine.sendMessage(SoftApStateMachine.CMD_FAILURE);
113         }
114 
115         @Override
116         public void onConnectedClientsChanged(List<NativeWifiClient> clients) {
117             mStateMachine.sendMessage(SoftApStateMachine.CMD_ASSOCIATED_STATIONS_CHANGED,
118                     clients);
119         }
120 
121         @Override
122         public void onSoftApChannelSwitched(int frequency, int bandwidth) {
123             mStateMachine.sendMessage(
124                     SoftApStateMachine.CMD_SOFT_AP_CHANNEL_SWITCHED, frequency, bandwidth);
125         }
126     };
127 
SoftApManager(@onNull Context context, @NonNull Looper looper, @NonNull FrameworkFacade framework, @NonNull WifiNative wifiNative, String countryCode, @NonNull WifiManager.SoftApCallback callback, @NonNull WifiApConfigStore wifiApConfigStore, @NonNull SoftApModeConfiguration apConfig, @NonNull WifiMetrics wifiMetrics, @NonNull SarManager sarManager, @NonNull BaseWifiDiagnostics wifiDiagnostics)128     public SoftApManager(@NonNull Context context,
129                          @NonNull Looper looper,
130                          @NonNull FrameworkFacade framework,
131                          @NonNull WifiNative wifiNative,
132                          String countryCode,
133                          @NonNull WifiManager.SoftApCallback callback,
134                          @NonNull WifiApConfigStore wifiApConfigStore,
135                          @NonNull SoftApModeConfiguration apConfig,
136                          @NonNull WifiMetrics wifiMetrics,
137                          @NonNull SarManager sarManager,
138                          @NonNull BaseWifiDiagnostics wifiDiagnostics) {
139         mContext = context;
140         mFrameworkFacade = framework;
141         mWifiNative = wifiNative;
142         mCountryCode = countryCode;
143         mCallback = callback;
144         mWifiApConfigStore = wifiApConfigStore;
145         mMode = apConfig.getTargetMode();
146         WifiConfiguration config = apConfig.getWifiConfiguration();
147         if (config == null) {
148             mApConfig = mWifiApConfigStore.getApConfiguration();
149         } else {
150             mApConfig = config;
151         }
152         mWifiMetrics = wifiMetrics;
153         mSarManager = sarManager;
154         mWifiDiagnostics = wifiDiagnostics;
155         mStateMachine = new SoftApStateMachine(looper);
156     }
157 
158     /**
159      * Start soft AP with the supplied config.
160      */
start()161     public void start() {
162         mStateMachine.sendMessage(SoftApStateMachine.CMD_START, mApConfig);
163     }
164 
165     /**
166      * Stop soft AP.
167      */
stop()168     public void stop() {
169         Log.d(TAG, " currentstate: " + getCurrentStateName());
170         if (mApInterfaceName != null) {
171             if (mIfaceIsUp) {
172                 updateApState(WifiManager.WIFI_AP_STATE_DISABLING,
173                         WifiManager.WIFI_AP_STATE_ENABLED, 0);
174             } else {
175                 updateApState(WifiManager.WIFI_AP_STATE_DISABLING,
176                         WifiManager.WIFI_AP_STATE_ENABLING, 0);
177             }
178         }
179         mStateMachine.quitNow();
180     }
181 
getScanMode()182     public @ScanMode int getScanMode() {
183         return SCAN_NONE;
184     }
185 
getIpMode()186     public int getIpMode() {
187         return mMode;
188     }
189 
190     /**
191      * Dump info about this softap manager.
192      */
dump(FileDescriptor fd, PrintWriter pw, String[] args)193     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
194         pw.println("--Dump of SoftApManager--");
195 
196         pw.println("current StateMachine mode: " + getCurrentStateName());
197         pw.println("mApInterfaceName: " + mApInterfaceName);
198         pw.println("mIfaceIsUp: " + mIfaceIsUp);
199         pw.println("mMode: " + mMode);
200         pw.println("mCountryCode: " + mCountryCode);
201         if (mApConfig != null) {
202             pw.println("mApConfig.SSID: " + mApConfig.SSID);
203             pw.println("mApConfig.apBand: " + mApConfig.apBand);
204             pw.println("mApConfig.hiddenSSID: " + mApConfig.hiddenSSID);
205         } else {
206             pw.println("mApConfig: null");
207         }
208         pw.println("mConnectedClients.size(): " + mConnectedClients.size());
209         pw.println("mTimeoutEnabled: " + mTimeoutEnabled);
210         pw.println("mReportedFrequency: " + mReportedFrequency);
211         pw.println("mReportedBandwidth: " + mReportedBandwidth);
212         pw.println("mStartTimestamp: " + mStartTimestamp);
213     }
214 
getCurrentStateName()215     private String getCurrentStateName() {
216         IState currentState = mStateMachine.getCurrentState();
217 
218         if (currentState != null) {
219             return currentState.getName();
220         }
221 
222         return "StateMachine not active";
223     }
224 
225     /**
226      * Update AP state.
227      * @param newState new AP state
228      * @param currentState current AP state
229      * @param reason Failure reason if the new AP state is in failure state
230      */
updateApState(int newState, int currentState, int reason)231     private void updateApState(int newState, int currentState, int reason) {
232         mCallback.onStateChanged(newState, reason);
233 
234         //send the AP state change broadcast
235         final Intent intent = new Intent(WifiManager.WIFI_AP_STATE_CHANGED_ACTION);
236         intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
237         intent.putExtra(WifiManager.EXTRA_WIFI_AP_STATE, newState);
238         intent.putExtra(WifiManager.EXTRA_PREVIOUS_WIFI_AP_STATE, currentState);
239         if (newState == WifiManager.WIFI_AP_STATE_FAILED) {
240             //only set reason number when softAP start failed
241             intent.putExtra(WifiManager.EXTRA_WIFI_AP_FAILURE_REASON, reason);
242         }
243 
244         intent.putExtra(WifiManager.EXTRA_WIFI_AP_INTERFACE_NAME, mApInterfaceName);
245         intent.putExtra(WifiManager.EXTRA_WIFI_AP_MODE, mMode);
246         mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
247     }
248 
249     /**
250      * Start a soft AP instance with the given configuration.
251      * @param config AP configuration
252      * @return integer result code
253      */
startSoftAp(WifiConfiguration config)254     private int startSoftAp(WifiConfiguration config) {
255         if (config == null || config.SSID == null) {
256             Log.e(TAG, "Unable to start soft AP without valid configuration");
257             return ERROR_GENERIC;
258         }
259         // Setup country code
260         if (TextUtils.isEmpty(mCountryCode)) {
261             if (config.apBand == WifiConfiguration.AP_BAND_5GHZ) {
262                 // Country code is mandatory for 5GHz band.
263                 Log.e(TAG, "Invalid country code, required for setting up "
264                         + "soft ap in 5GHz");
265                 return ERROR_GENERIC;
266             }
267             // Absence of country code is not fatal for 2Ghz & Any band options.
268         } else if (!mWifiNative.setCountryCodeHal(
269                 mApInterfaceName, mCountryCode.toUpperCase(Locale.ROOT))) {
270             if (config.apBand == WifiConfiguration.AP_BAND_5GHZ) {
271                 // Return an error if failed to set country code when AP is configured for
272                 // 5GHz band.
273                 Log.e(TAG, "Failed to set country code, required for setting up "
274                         + "soft ap in 5GHz");
275                 return ERROR_GENERIC;
276             }
277             // Failure to set country code is not fatal for 2Ghz & Any band options.
278         }
279 
280         // Make a copy of configuration for updating AP band and channel.
281         WifiConfiguration localConfig = new WifiConfiguration(config);
282 
283         int result = ApConfigUtil.updateApChannelConfig(
284                 mWifiNative, mCountryCode,
285                 mWifiApConfigStore.getAllowed2GChannel(), localConfig);
286 
287         if (result != SUCCESS) {
288             Log.e(TAG, "Failed to update AP band and channel");
289             return result;
290         }
291 
292         if (localConfig.hiddenSSID) {
293             Log.d(TAG, "SoftAP is a hidden network");
294         }
295         if (!mWifiNative.startSoftAp(mApInterfaceName, localConfig, mSoftApListener)) {
296             Log.e(TAG, "Soft AP start failed");
297             return ERROR_GENERIC;
298         }
299         mWifiDiagnostics.startLogging(mApInterfaceName);
300         mStartTimestamp = SystemClock.elapsedRealtime();
301         Log.d(TAG, "Soft AP is started");
302 
303         return SUCCESS;
304     }
305 
306     /**
307      * Teardown soft AP and teardown the interface.
308      */
stopSoftAp()309     private void stopSoftAp() {
310         mWifiDiagnostics.stopLogging(mApInterfaceName);
311         mWifiNative.teardownInterface(mApInterfaceName);
312         Log.d(TAG, "Soft AP is stopped");
313     }
314 
315     private class SoftApStateMachine extends StateMachine {
316         // Commands for the state machine.
317         public static final int CMD_START = 0;
318         public static final int CMD_FAILURE = 2;
319         public static final int CMD_INTERFACE_STATUS_CHANGED = 3;
320         public static final int CMD_ASSOCIATED_STATIONS_CHANGED = 4;
321         public static final int CMD_NO_ASSOCIATED_STATIONS_TIMEOUT = 5;
322         public static final int CMD_TIMEOUT_TOGGLE_CHANGED = 6;
323         public static final int CMD_INTERFACE_DESTROYED = 7;
324         public static final int CMD_INTERFACE_DOWN = 8;
325         public static final int CMD_SOFT_AP_CHANNEL_SWITCHED = 9;
326 
327         private final State mIdleState = new IdleState();
328         private final State mStartedState = new StartedState();
329 
330         private final InterfaceCallback mWifiNativeInterfaceCallback = new InterfaceCallback() {
331             @Override
332             public void onDestroyed(String ifaceName) {
333                 if (mApInterfaceName != null && mApInterfaceName.equals(ifaceName)) {
334                     sendMessage(CMD_INTERFACE_DESTROYED);
335                 }
336             }
337 
338             @Override
339             public void onUp(String ifaceName) {
340                 if (mApInterfaceName != null && mApInterfaceName.equals(ifaceName)) {
341                     sendMessage(CMD_INTERFACE_STATUS_CHANGED, 1);
342                 }
343             }
344 
345             @Override
346             public void onDown(String ifaceName) {
347                 if (mApInterfaceName != null && mApInterfaceName.equals(ifaceName)) {
348                     sendMessage(CMD_INTERFACE_STATUS_CHANGED, 0);
349                 }
350             }
351         };
352 
SoftApStateMachine(Looper looper)353         SoftApStateMachine(Looper looper) {
354             super(TAG, looper);
355 
356             addState(mIdleState);
357             addState(mStartedState);
358 
359             setInitialState(mIdleState);
360             start();
361         }
362 
363         private class IdleState extends State {
364             @Override
enter()365             public void enter() {
366                 mApInterfaceName = null;
367                 mIfaceIsUp = false;
368                 mIfaceIsDestroyed = false;
369             }
370 
371             @Override
processMessage(Message message)372             public boolean processMessage(Message message) {
373                 switch (message.what) {
374                     case CMD_START:
375                         mApInterfaceName = mWifiNative.setupInterfaceForSoftApMode(
376                                 mWifiNativeInterfaceCallback);
377                         if (TextUtils.isEmpty(mApInterfaceName)) {
378                             Log.e(TAG, "setup failure when creating ap interface.");
379                             updateApState(WifiManager.WIFI_AP_STATE_FAILED,
380                                     WifiManager.WIFI_AP_STATE_DISABLED,
381                                     WifiManager.SAP_START_FAILURE_GENERAL);
382                             mWifiMetrics.incrementSoftApStartResult(
383                                     false, WifiManager.SAP_START_FAILURE_GENERAL);
384                             break;
385                         }
386                         updateApState(WifiManager.WIFI_AP_STATE_ENABLING,
387                                 WifiManager.WIFI_AP_STATE_DISABLED, 0);
388                         int result = startSoftAp((WifiConfiguration) message.obj);
389                         if (result != SUCCESS) {
390                             int failureReason = WifiManager.SAP_START_FAILURE_GENERAL;
391                             if (result == ERROR_NO_CHANNEL) {
392                                 failureReason = WifiManager.SAP_START_FAILURE_NO_CHANNEL;
393                             }
394                             updateApState(WifiManager.WIFI_AP_STATE_FAILED,
395                                           WifiManager.WIFI_AP_STATE_ENABLING,
396                                           failureReason);
397                             stopSoftAp();
398                             mWifiMetrics.incrementSoftApStartResult(false, failureReason);
399                             break;
400                         }
401                         transitionTo(mStartedState);
402                         break;
403                     default:
404                         // Ignore all other commands.
405                         break;
406                 }
407 
408                 return HANDLED;
409             }
410         }
411 
412         private class StartedState extends State {
413             private int mTimeoutDelay;
414             private WakeupMessage mSoftApTimeoutMessage;
415             private SoftApTimeoutEnabledSettingObserver mSettingObserver;
416 
417             /**
418              * Observer for timeout settings changes.
419              */
420             private class SoftApTimeoutEnabledSettingObserver extends ContentObserver {
SoftApTimeoutEnabledSettingObserver(Handler handler)421                 SoftApTimeoutEnabledSettingObserver(Handler handler) {
422                     super(handler);
423                 }
424 
register()425                 public void register() {
426                     mFrameworkFacade.registerContentObserver(mContext,
427                             Settings.Global.getUriFor(Settings.Global.SOFT_AP_TIMEOUT_ENABLED),
428                             true, this);
429                     mTimeoutEnabled = getValue();
430                 }
431 
unregister()432                 public void unregister() {
433                     mFrameworkFacade.unregisterContentObserver(mContext, this);
434                 }
435 
436                 @Override
onChange(boolean selfChange)437                 public void onChange(boolean selfChange) {
438                     super.onChange(selfChange);
439                     mStateMachine.sendMessage(SoftApStateMachine.CMD_TIMEOUT_TOGGLE_CHANGED,
440                             getValue() ? 1 : 0);
441                 }
442 
getValue()443                 private boolean getValue() {
444                     boolean enabled = mFrameworkFacade.getIntegerSetting(mContext,
445                             Settings.Global.SOFT_AP_TIMEOUT_ENABLED, 1) == 1;
446                     return enabled;
447                 }
448             }
449 
getConfigSoftApTimeoutDelay()450             private int getConfigSoftApTimeoutDelay() {
451                 int delay = mContext.getResources().getInteger(
452                         R.integer.config_wifi_framework_soft_ap_timeout_delay);
453                 if (delay < MIN_SOFT_AP_TIMEOUT_DELAY_MS) {
454                     delay = MIN_SOFT_AP_TIMEOUT_DELAY_MS;
455                     Log.w(TAG, "Overriding timeout delay with minimum limit value");
456                 }
457                 Log.d(TAG, "Timeout delay: " + delay);
458                 return delay;
459             }
460 
scheduleTimeoutMessage()461             private void scheduleTimeoutMessage() {
462                 if (!mTimeoutEnabled) {
463                     return;
464                 }
465                 mSoftApTimeoutMessage.schedule(SystemClock.elapsedRealtime() + mTimeoutDelay);
466                 Log.d(TAG, "Timeout message scheduled");
467             }
468 
cancelTimeoutMessage()469             private void cancelTimeoutMessage() {
470                 mSoftApTimeoutMessage.cancel();
471                 Log.d(TAG, "Timeout message canceled");
472             }
473 
474             /**
475              * Set stations associated with this soft AP
476              * @param clients The connected stations
477              */
setConnectedClients(List<NativeWifiClient> clients)478             private void setConnectedClients(List<NativeWifiClient> clients) {
479                 if (clients == null) {
480                     return;
481                 }
482 
483                 List<WifiClient> convertedClients = createWifiClients(clients);
484                 if (mConnectedClients.equals(convertedClients)) {
485                     return;
486                 }
487 
488                 mConnectedClients = new ArrayList<>(convertedClients);
489                 Log.d(TAG, "The connected wifi stations have changed with count: "
490                         + clients.size());
491 
492                 if (mCallback != null) {
493                     mCallback.onConnectedClientsChanged(mConnectedClients);
494                 } else {
495                     Log.e(TAG,
496                             "SoftApCallback is null. Dropping ConnectedClientsChanged event."
497                     );
498                 }
499                 mWifiMetrics.addSoftApNumAssociatedStationsChangedEvent(mConnectedClients.size(),
500                         mMode);
501 
502                 if (mConnectedClients.size() == 0) {
503                     scheduleTimeoutMessage();
504                 } else {
505                     cancelTimeoutMessage();
506                 }
507             }
508 
createWifiClients(List<NativeWifiClient> nativeClients)509             private List<WifiClient> createWifiClients(List<NativeWifiClient> nativeClients) {
510                 List<WifiClient> clients = new ArrayList<>();
511                 if (nativeClients == null || nativeClients.size() == 0) {
512                     return clients;
513                 }
514 
515                 for (int i = 0; i < nativeClients.size(); i++) {
516                     MacAddress macAddress = MacAddress.fromBytes(nativeClients.get(i).macAddress);
517                     WifiClient client = new WifiClient(macAddress);
518                     clients.add(client);
519                 }
520 
521                 return clients;
522             }
523 
onUpChanged(boolean isUp)524             private void onUpChanged(boolean isUp) {
525                 if (isUp == mIfaceIsUp) {
526                     return;  // no change
527                 }
528 
529                 mIfaceIsUp = isUp;
530                 if (isUp) {
531                     Log.d(TAG, "SoftAp is ready for use");
532                     updateApState(WifiManager.WIFI_AP_STATE_ENABLED,
533                             WifiManager.WIFI_AP_STATE_ENABLING, 0);
534                     mWifiMetrics.incrementSoftApStartResult(true, 0);
535                     if (mCallback != null) {
536                         mCallback.onConnectedClientsChanged(mConnectedClients);
537                     }
538                 } else {
539                     // the interface was up, but goes down
540                     sendMessage(CMD_INTERFACE_DOWN);
541                 }
542                 mWifiMetrics.addSoftApUpChangedEvent(isUp, mMode);
543             }
544 
545             @Override
enter()546             public void enter() {
547                 mIfaceIsUp = false;
548                 mIfaceIsDestroyed = false;
549                 onUpChanged(mWifiNative.isInterfaceUp(mApInterfaceName));
550 
551                 mTimeoutDelay = getConfigSoftApTimeoutDelay();
552                 Handler handler = mStateMachine.getHandler();
553                 mSoftApTimeoutMessage = new WakeupMessage(mContext, handler,
554                         SOFT_AP_SEND_MESSAGE_TIMEOUT_TAG,
555                         SoftApStateMachine.CMD_NO_ASSOCIATED_STATIONS_TIMEOUT);
556                 mSettingObserver = new SoftApTimeoutEnabledSettingObserver(handler);
557 
558                 if (mSettingObserver != null) {
559                     mSettingObserver.register();
560                 }
561 
562                 mSarManager.setSapWifiState(WifiManager.WIFI_AP_STATE_ENABLED);
563 
564                 Log.d(TAG, "Resetting connected clients on start");
565                 mConnectedClients = new ArrayList<>();
566                 scheduleTimeoutMessage();
567             }
568 
569             @Override
exit()570             public void exit() {
571                 if (!mIfaceIsDestroyed) {
572                     stopSoftAp();
573                 }
574 
575                 if (mSettingObserver != null) {
576                     mSettingObserver.unregister();
577                 }
578                 Log.d(TAG, "Resetting num stations on stop");
579                 setConnectedClients(new ArrayList<>());
580                 cancelTimeoutMessage();
581                 // Need this here since we are exiting |Started| state and won't handle any
582                 // future CMD_INTERFACE_STATUS_CHANGED events after this point
583                 mWifiMetrics.addSoftApUpChangedEvent(false, mMode);
584                 updateApState(WifiManager.WIFI_AP_STATE_DISABLED,
585                         WifiManager.WIFI_AP_STATE_DISABLING, 0);
586 
587                 mSarManager.setSapWifiState(WifiManager.WIFI_AP_STATE_DISABLED);
588                 mApInterfaceName = null;
589                 mIfaceIsUp = false;
590                 mIfaceIsDestroyed = false;
591                 mStateMachine.quitNow();
592             }
593 
updateUserBandPreferenceViolationMetricsIfNeeded()594             private void updateUserBandPreferenceViolationMetricsIfNeeded() {
595                 boolean bandPreferenceViolated = false;
596                 if (mApConfig.apBand == WifiConfiguration.AP_BAND_2GHZ
597                         && ScanResult.is5GHz(mReportedFrequency)) {
598                     bandPreferenceViolated = true;
599                 } else if (mApConfig.apBand == WifiConfiguration.AP_BAND_5GHZ
600                         && ScanResult.is24GHz(mReportedFrequency)) {
601                     bandPreferenceViolated = true;
602                 }
603                 if (bandPreferenceViolated) {
604                     Log.e(TAG, "Channel does not satisfy user band preference: "
605                             + mReportedFrequency);
606                     mWifiMetrics.incrementNumSoftApUserBandPreferenceUnsatisfied();
607                 }
608             }
609 
610             @Override
processMessage(Message message)611             public boolean processMessage(Message message) {
612                 switch (message.what) {
613                     case CMD_ASSOCIATED_STATIONS_CHANGED:
614                         if (!(message.obj instanceof List)) {
615                             Log.e(TAG, "Invalid type returned for"
616                                     + " CMD_ASSOCIATED_STATIONS_CHANGED");
617                             break;
618                         }
619 
620                         Log.d(TAG, "Setting connected stations on"
621                                 + " CMD_ASSOCIATED_STATIONS_CHANGED");
622                         setConnectedClients((List<NativeWifiClient>) message.obj);
623                         break;
624                     case CMD_SOFT_AP_CHANNEL_SWITCHED:
625                         mReportedFrequency = message.arg1;
626                         mReportedBandwidth = message.arg2;
627                         Log.d(TAG, "Channel switched. Frequency: " + mReportedFrequency
628                                 + " Bandwidth: " + mReportedBandwidth);
629                         mWifiMetrics.addSoftApChannelSwitchedEvent(mReportedFrequency,
630                                 mReportedBandwidth, mMode);
631                         updateUserBandPreferenceViolationMetricsIfNeeded();
632                         break;
633                     case CMD_TIMEOUT_TOGGLE_CHANGED:
634                         boolean isEnabled = (message.arg1 == 1);
635                         if (mTimeoutEnabled == isEnabled) {
636                             break;
637                         }
638                         mTimeoutEnabled = isEnabled;
639                         if (!mTimeoutEnabled) {
640                             cancelTimeoutMessage();
641                         }
642                         if (mTimeoutEnabled && mConnectedClients.size() == 0) {
643                             scheduleTimeoutMessage();
644                         }
645                         break;
646                     case CMD_INTERFACE_STATUS_CHANGED:
647                         boolean isUp = message.arg1 == 1;
648                         onUpChanged(isUp);
649                         break;
650                     case CMD_START:
651                         // Already started, ignore this command.
652                         break;
653                     case CMD_NO_ASSOCIATED_STATIONS_TIMEOUT:
654                         if (!mTimeoutEnabled) {
655                             Log.wtf(TAG, "Timeout message received while timeout is disabled."
656                                     + " Dropping.");
657                             break;
658                         }
659                         if (mConnectedClients.size() != 0) {
660                             Log.wtf(TAG, "Timeout message received but has clients. Dropping.");
661                             break;
662                         }
663                         Log.i(TAG, "Timeout message received. Stopping soft AP.");
664                         updateApState(WifiManager.WIFI_AP_STATE_DISABLING,
665                                 WifiManager.WIFI_AP_STATE_ENABLED, 0);
666                         transitionTo(mIdleState);
667                         break;
668                     case CMD_INTERFACE_DESTROYED:
669                         Log.d(TAG, "Interface was cleanly destroyed.");
670                         updateApState(WifiManager.WIFI_AP_STATE_DISABLING,
671                                 WifiManager.WIFI_AP_STATE_ENABLED, 0);
672                         mIfaceIsDestroyed = true;
673                         transitionTo(mIdleState);
674                         break;
675                     case CMD_FAILURE:
676                         Log.w(TAG, "hostapd failure, stop and report failure");
677                         /* fall through */
678                     case CMD_INTERFACE_DOWN:
679                         Log.w(TAG, "interface error, stop and report failure");
680                         updateApState(WifiManager.WIFI_AP_STATE_FAILED,
681                                 WifiManager.WIFI_AP_STATE_ENABLED,
682                                 WifiManager.SAP_START_FAILURE_GENERAL);
683                         updateApState(WifiManager.WIFI_AP_STATE_DISABLING,
684                                 WifiManager.WIFI_AP_STATE_FAILED, 0);
685                         transitionTo(mIdleState);
686                         break;
687                     default:
688                         return NOT_HANDLED;
689                 }
690                 return HANDLED;
691             }
692         }
693     }
694 }
695