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 android.annotation.NonNull;
20 import android.content.Context;
21 import android.content.Intent;
22 import android.net.ConnectivityManager;
23 import android.net.ConnectivityManager.NetworkCallback;
24 import android.net.Network;
25 import android.net.NetworkCapabilities;
26 import android.net.NetworkRequest;
27 import android.net.wifi.WifiManager;
28 import android.os.Handler;
29 import android.os.HandlerExecutor;
30 import android.os.Looper;
31 import android.os.Message;
32 import android.os.PersistableBundle;
33 import android.os.UserHandle;
34 import android.telephony.AccessNetworkConstants;
35 import android.telephony.CarrierConfigManager;
36 import android.telephony.SubscriptionInfo;
37 import android.telephony.SubscriptionManager;
38 import android.telephony.ims.ImsException;
39 import android.telephony.ims.ImsMmTelManager;
40 import android.telephony.ims.ImsReasonInfo;
41 import android.telephony.ims.RegistrationManager;
42 import android.telephony.ims.feature.MmTelFeature;
43 import android.telephony.ims.stub.ImsRegistrationImplBase;
44 import android.text.TextUtils;
45 import android.util.Log;
46 
47 import com.android.internal.util.IState;
48 import com.android.internal.util.Preconditions;
49 import com.android.internal.util.State;
50 import com.android.internal.util.StateMachine;
51 import com.android.server.wifi.WifiNative.InterfaceCallback;
52 import com.android.server.wifi.util.WifiHandler;
53 import com.android.wifi.resources.R;
54 
55 import java.io.FileDescriptor;
56 import java.io.PrintWriter;
57 import java.util.List;
58 
59 /**
60  * Manager WiFi in Client Mode where we connect to configured networks.
61  */
62 public class ClientModeManager implements ActiveModeManager {
63     private static final String TAG = "WifiClientModeManager";
64 
65     private final ClientModeStateMachine mStateMachine;
66 
67     private final Context mContext;
68     private final Clock mClock;
69     private final WifiNative mWifiNative;
70     private final WifiMetrics mWifiMetrics;
71     private final SarManager mSarManager;
72     private final WakeupController mWakeupController;
73     private final Listener mModeListener;
74     private final ClientModeImpl mClientModeImpl;
75 
76     private String mClientInterfaceName;
77     private boolean mIfaceIsUp = false;
78     private DeferStopHandler mDeferStopHandler;
79     private @Role int mRole = ROLE_UNSPECIFIED;
80     private @Role int mTargetRole = ROLE_UNSPECIFIED;
81     private int mActiveSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
82 
ClientModeManager(Context context, @NonNull Looper looper, Clock clock, WifiNative wifiNative, Listener listener, WifiMetrics wifiMetrics, SarManager sarManager, WakeupController wakeupController, ClientModeImpl clientModeImpl)83     ClientModeManager(Context context, @NonNull Looper looper, Clock clock, WifiNative wifiNative,
84             Listener listener, WifiMetrics wifiMetrics, SarManager sarManager,
85             WakeupController wakeupController, ClientModeImpl clientModeImpl) {
86         mContext = context;
87         mClock = clock;
88         mWifiNative = wifiNative;
89         mModeListener = listener;
90         mWifiMetrics = wifiMetrics;
91         mSarManager = sarManager;
92         mWakeupController = wakeupController;
93         mClientModeImpl = clientModeImpl;
94         mStateMachine = new ClientModeStateMachine(looper);
95         mDeferStopHandler = new DeferStopHandler(TAG, looper);
96     }
97 
98     /**
99      * Start client mode.
100      */
101     @Override
start()102     public void start() {
103         mTargetRole = ROLE_CLIENT_SCAN_ONLY;
104         mStateMachine.sendMessage(ClientModeStateMachine.CMD_START);
105     }
106 
107     /**
108      * Disconnect from any currently connected networks and stop client mode.
109      */
110     @Override
stop()111     public void stop() {
112         Log.d(TAG, " currentstate: " + getCurrentStateName());
113         mTargetRole = ROLE_UNSPECIFIED;
114         if (mIfaceIsUp) {
115             updateConnectModeState(WifiManager.WIFI_STATE_DISABLING,
116                     WifiManager.WIFI_STATE_ENABLED);
117         } else {
118             updateConnectModeState(WifiManager.WIFI_STATE_DISABLING,
119                     WifiManager.WIFI_STATE_ENABLING);
120         }
121         mDeferStopHandler.start(getWifiOffDeferringTimeMs());
122     }
123 
124     @Override
isStopping()125     public boolean isStopping() {
126         return mTargetRole == ROLE_UNSPECIFIED && mRole != ROLE_UNSPECIFIED;
127     }
128 
129     private class DeferStopHandler extends WifiHandler {
130         private boolean mIsDeferring = false;
131         private ImsMmTelManager mImsMmTelManager = null;
132         private Looper mLooper = null;
133         private final Runnable mRunnable = () -> continueToStopWifi();
134         private int mMaximumDeferringTimeMillis = 0;
135         private long mDeferringStartTimeMillis = 0;
136         private NetworkRequest mImsRequest = null;
137         private ConnectivityManager mConnectivityManager = null;
138 
139         private RegistrationManager.RegistrationCallback mImsRegistrationCallback =
140                 new RegistrationManager.RegistrationCallback() {
141                     @Override
142                     public void onRegistered(int imsRadioTech) {
143                         Log.d(TAG, "on IMS registered on type " + imsRadioTech);
144                         if (!mIsDeferring) return;
145 
146                         if (imsRadioTech != AccessNetworkConstants.TRANSPORT_TYPE_WLAN) {
147                             continueToStopWifi();
148                         }
149                     }
150 
151                     @Override
152                     public void onUnregistered(ImsReasonInfo imsReasonInfo) {
153                         Log.d(TAG, "on IMS unregistered");
154                         // Wait for onLost in NetworkCallback
155                     }
156                 };
157 
158         private NetworkCallback mImsNetworkCallback = new NetworkCallback() {
159             private int mRegisteredImsNetworkCount = 0;
160             @Override
161             public void onAvailable(Network network) {
162                 synchronized (this) {
163                     Log.d(TAG, "IMS network available: " + network);
164                     mRegisteredImsNetworkCount++;
165                 }
166             }
167 
168             @Override
169             public void onLost(Network network) {
170                 synchronized (this) {
171                     Log.d(TAG, "IMS network lost: " + network
172                             + " ,isDeferring: " + mIsDeferring
173                             + " ,registered IMS network count: " + mRegisteredImsNetworkCount);
174                     mRegisteredImsNetworkCount--;
175                     if (mIsDeferring && mRegisteredImsNetworkCount <= 0) {
176                         mRegisteredImsNetworkCount = 0;
177                         // Add delay for targets where IMS PDN down at modem takes additional delay.
178                         int delay = mContext.getResources()
179                                 .getInteger(R.integer.config_wifiDelayDisconnectOnImsLostMs);
180                         if (delay == 0 || !postDelayed(mRunnable, delay)) {
181                             continueToStopWifi();
182                         }
183                     }
184                 }
185             }
186         };
187 
DeferStopHandler(String tag, Looper looper)188         DeferStopHandler(String tag, Looper looper) {
189             super(tag, looper);
190             mLooper = looper;
191         }
192 
start(int delayMs)193         public void start(int delayMs) {
194             if (mIsDeferring) return;
195 
196             mMaximumDeferringTimeMillis = delayMs;
197             mDeferringStartTimeMillis = mClock.getElapsedSinceBootMillis();
198             // Most cases don't need delay, check it first to avoid unnecessary work.
199             if (delayMs == 0) {
200                 continueToStopWifi();
201                 return;
202             }
203 
204             mImsMmTelManager = ImsMmTelManager.createForSubscriptionId(mActiveSubId);
205             if (mImsMmTelManager == null || !postDelayed(mRunnable, delayMs)) {
206                 // if no delay or failed to add runnable, stop Wifi immediately.
207                 continueToStopWifi();
208                 return;
209             }
210 
211             mIsDeferring = true;
212             Log.d(TAG, "Start DeferWifiOff handler with deferring time "
213                     + delayMs + " ms for subId: " + mActiveSubId);
214             try {
215                 mImsMmTelManager.registerImsRegistrationCallback(
216                         new HandlerExecutor(new Handler(mLooper)),
217                         mImsRegistrationCallback);
218             } catch (RuntimeException | ImsException e) {
219                 Log.e(TAG, "registerImsRegistrationCallback failed", e);
220                 continueToStopWifi();
221                 return;
222             }
223 
224             mImsRequest = new NetworkRequest.Builder()
225                 .addCapability(NetworkCapabilities.NET_CAPABILITY_IMS)
226                 .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
227                 .build();
228 
229             mConnectivityManager =
230                     (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
231 
232             mConnectivityManager.registerNetworkCallback(mImsRequest, mImsNetworkCallback,
233                                                          new Handler(mLooper));
234         }
235 
continueToStopWifi()236         private void continueToStopWifi() {
237             Log.d(TAG, "The target role " + mTargetRole);
238 
239             int deferringDurationMillis =
240                     (int) (mClock.getElapsedSinceBootMillis() - mDeferringStartTimeMillis);
241             boolean isTimedOut = mMaximumDeferringTimeMillis > 0
242                     && deferringDurationMillis >= mMaximumDeferringTimeMillis;
243             if (mTargetRole == ROLE_UNSPECIFIED) {
244                 Log.d(TAG, "Continue to stop wifi");
245                 mStateMachine.quitNow();
246                 mWifiMetrics.noteWifiOff(mIsDeferring, isTimedOut, deferringDurationMillis);
247             } else if (mTargetRole == ROLE_CLIENT_SCAN_ONLY) {
248                 if (!mWifiNative.switchClientInterfaceToScanMode(mClientInterfaceName)) {
249                     mModeListener.onStartFailure();
250                 } else {
251                     mStateMachine.sendMessage(
252                             ClientModeStateMachine.CMD_SWITCH_TO_SCAN_ONLY_MODE_CONTINUE);
253                     mWifiMetrics.noteWifiOff(mIsDeferring, isTimedOut, deferringDurationMillis);
254                 }
255             } else {
256                 updateConnectModeState(WifiManager.WIFI_STATE_ENABLED,
257                         WifiManager.WIFI_STATE_DISABLING);
258             }
259 
260             if (!mIsDeferring) return;
261 
262             Log.d(TAG, "Stop DeferWifiOff handler.");
263             removeCallbacks(mRunnable);
264             if (mImsMmTelManager != null) {
265                 try {
266                     mImsMmTelManager.unregisterImsRegistrationCallback(mImsRegistrationCallback);
267                 } catch (RuntimeException e) {
268                     Log.e(TAG, "unregisterImsRegistrationCallback failed", e);
269                 }
270             }
271 
272             if (mConnectivityManager != null) {
273                 mConnectivityManager.unregisterNetworkCallback(mImsNetworkCallback);
274             }
275 
276             mIsDeferring = false;
277         }
278     }
279 
280     /**
281      * Get deferring time before turning off WiFi.
282      */
getWifiOffDeferringTimeMs()283     private int getWifiOffDeferringTimeMs() {
284         SubscriptionManager subscriptionManager = (SubscriptionManager) mContext.getSystemService(
285                 Context.TELEPHONY_SUBSCRIPTION_SERVICE);
286         if (subscriptionManager == null) {
287             Log.d(TAG, "SubscriptionManager not found");
288             return 0;
289         }
290 
291         List<SubscriptionInfo> subInfoList = subscriptionManager.getActiveSubscriptionInfoList();
292         if (subInfoList == null) {
293             Log.d(TAG, "Active SubscriptionInfo list not found");
294             return 0;
295         }
296 
297         // Get the maximum delay for the active subscription latched on IWLAN.
298         int maxDelay = 0;
299         for (SubscriptionInfo subInfo : subInfoList) {
300             int curDelay = getWifiOffDeferringTimeMs(subInfo.getSubscriptionId());
301             if (curDelay > maxDelay) {
302                 maxDelay = curDelay;
303                 mActiveSubId = subInfo.getSubscriptionId();
304             }
305         }
306         return maxDelay;
307     }
308 
getWifiOffDeferringTimeMs(int subId)309     private int getWifiOffDeferringTimeMs(int subId) {
310         if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
311             Log.d(TAG, "Invalid Subscription ID: " + subId);
312             return 0;
313         }
314 
315         ImsMmTelManager imsMmTelManager = ImsMmTelManager.createForSubscriptionId(subId);
316         // If no wifi calling, no delay
317         if (!imsMmTelManager.isAvailable(
318                     MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE,
319                     ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN)) {
320             Log.d(TAG, "IMS not registered over IWLAN for subId: " + subId);
321             return 0;
322         }
323 
324         CarrierConfigManager configManager =
325                 (CarrierConfigManager) mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE);
326         PersistableBundle config = configManager.getConfigForSubId(subId);
327         return (config != null)
328                 ? config.getInt(CarrierConfigManager.Ims.KEY_WIFI_OFF_DEFERRING_TIME_MILLIS_INT)
329                 : 0;
330     }
331 
332     @Override
getRole()333     public @Role int getRole() {
334         return mRole;
335     }
336 
337     @Override
setRole(@ole int role)338     public void setRole(@Role int role) {
339         Preconditions.checkState(CLIENT_ROLES.contains(role));
340         if (role == ROLE_CLIENT_SCAN_ONLY) {
341             mTargetRole = role;
342             // Switch client mode manager to scan only mode.
343             mStateMachine.sendMessage(ClientModeStateMachine.CMD_SWITCH_TO_SCAN_ONLY_MODE);
344         } else if (CLIENT_CONNECTIVITY_ROLES.contains(role)) {
345             mTargetRole = role;
346             // Switch client mode manager to connect mode.
347             mStateMachine.sendMessage(ClientModeStateMachine.CMD_SWITCH_TO_CONNECT_MODE, role);
348         }
349     }
350 
351     /**
352      * Dump info about this ClientMode manager.
353      */
354     @Override
dump(FileDescriptor fd, PrintWriter pw, String[] args)355     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
356         pw.println("--Dump of ClientModeManager--");
357 
358         pw.println("current StateMachine mode: " + getCurrentStateName());
359         pw.println("mRole: " + mRole);
360         pw.println("mTargetRole: " + mTargetRole);
361         pw.println("mClientInterfaceName: " + mClientInterfaceName);
362         pw.println("mIfaceIsUp: " + mIfaceIsUp);
363         mStateMachine.dump(fd, pw, args);
364     }
365 
getCurrentStateName()366     private String getCurrentStateName() {
367         IState currentState = mStateMachine.getCurrentState();
368 
369         if (currentState != null) {
370             return currentState.getName();
371         }
372 
373         return "StateMachine not active";
374     }
375 
376     /**
377      * Update Wifi state and send the broadcast.
378      * @param newState new Wifi state
379      * @param currentState current wifi state
380      */
updateConnectModeState(int newState, int currentState)381     private void updateConnectModeState(int newState, int currentState) {
382         if (newState == WifiManager.WIFI_STATE_UNKNOWN) {
383             // do not need to broadcast failure to system
384             return;
385         }
386         if (mRole != ROLE_CLIENT_PRIMARY) {
387             // do not raise public broadcast unless this is the primary client mode manager
388             return;
389         }
390 
391         mClientModeImpl.setWifiStateForApiCalls(newState);
392 
393         final Intent intent = new Intent(WifiManager.WIFI_STATE_CHANGED_ACTION);
394         intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
395         intent.putExtra(WifiManager.EXTRA_WIFI_STATE, newState);
396         intent.putExtra(WifiManager.EXTRA_PREVIOUS_WIFI_STATE, currentState);
397         mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
398     }
399 
400     private class ClientModeStateMachine extends StateMachine {
401         // Commands for the state machine.
402         public static final int CMD_START = 0;
403         public static final int CMD_SWITCH_TO_SCAN_ONLY_MODE = 1;
404         public static final int CMD_SWITCH_TO_CONNECT_MODE = 2;
405         public static final int CMD_INTERFACE_STATUS_CHANGED = 3;
406         public static final int CMD_INTERFACE_DESTROYED = 4;
407         public static final int CMD_INTERFACE_DOWN = 5;
408         public static final int CMD_SWITCH_TO_SCAN_ONLY_MODE_CONTINUE = 6;
409         private final State mIdleState = new IdleState();
410         private final State mStartedState = new StartedState();
411         private final State mScanOnlyModeState = new ScanOnlyModeState();
412         private final State mConnectModeState = new ConnectModeState();
413 
414         private final InterfaceCallback mWifiNativeInterfaceCallback = new InterfaceCallback() {
415             @Override
416             public void onDestroyed(String ifaceName) {
417                 if (mClientInterfaceName != null && mClientInterfaceName.equals(ifaceName)) {
418                     Log.d(TAG, "STA iface " + ifaceName + " was destroyed, stopping client mode");
419 
420                     // we must immediately clean up state in ClientModeImpl to unregister
421                     // all client mode related objects
422                     // Note: onDestroyed is only called from the main Wifi thread
423                     mClientModeImpl.handleIfaceDestroyed();
424 
425                     sendMessage(CMD_INTERFACE_DESTROYED);
426                 }
427             }
428 
429             @Override
430             public void onUp(String ifaceName) {
431                 if (mClientInterfaceName != null && mClientInterfaceName.equals(ifaceName)) {
432                     sendMessage(CMD_INTERFACE_STATUS_CHANGED, 1);
433                 }
434             }
435 
436             @Override
437             public void onDown(String ifaceName) {
438                 if (mClientInterfaceName != null && mClientInterfaceName.equals(ifaceName)) {
439                     sendMessage(CMD_INTERFACE_STATUS_CHANGED, 0);
440                 }
441             }
442         };
443 
ClientModeStateMachine(Looper looper)444         ClientModeStateMachine(Looper looper) {
445             super(TAG, looper);
446 
447             // CHECKSTYLE:OFF IndentationCheck
448             addState(mIdleState);
449             addState(mStartedState);
450                 addState(mScanOnlyModeState, mStartedState);
451                 addState(mConnectModeState, mStartedState);
452             // CHECKSTYLE:ON IndentationCheck
453 
454             setInitialState(mIdleState);
455             start();
456         }
457 
458         private class IdleState extends State {
459             @Override
enter()460             public void enter() {
461                 Log.d(TAG, "entering IdleState");
462                 mClientInterfaceName = null;
463                 mIfaceIsUp = false;
464             }
465 
466             @Override
processMessage(Message message)467             public boolean processMessage(Message message) {
468                 switch (message.what) {
469                     case CMD_START:
470                         // Always start in scan mode first.
471                         mClientInterfaceName =
472                                 mWifiNative.setupInterfaceForClientInScanMode(
473                                 mWifiNativeInterfaceCallback);
474                         if (TextUtils.isEmpty(mClientInterfaceName)) {
475                             Log.e(TAG, "Failed to create ClientInterface. Sit in Idle");
476                             mModeListener.onStartFailure();
477                             break;
478                         }
479                         transitionTo(mScanOnlyModeState);
480                         break;
481                     default:
482                         Log.d(TAG, "received an invalid message: " + message);
483                         return NOT_HANDLED;
484                 }
485                 return HANDLED;
486             }
487         }
488 
489         private class StartedState extends State {
490 
onUpChanged(boolean isUp)491             private void onUpChanged(boolean isUp) {
492                 if (isUp == mIfaceIsUp) {
493                     return;  // no change
494                 }
495                 mIfaceIsUp = isUp;
496                 if (!isUp) {
497                     // if the interface goes down we should exit and go back to idle state.
498                     Log.d(TAG, "interface down!");
499                     mStateMachine.sendMessage(CMD_INTERFACE_DOWN);
500                 }
501             }
502 
503             @Override
enter()504             public void enter() {
505                 Log.d(TAG, "entering StartedState");
506                 mIfaceIsUp = false;
507                 onUpChanged(mWifiNative.isInterfaceUp(mClientInterfaceName));
508             }
509 
510             @Override
processMessage(Message message)511             public boolean processMessage(Message message) {
512                 switch(message.what) {
513                     case CMD_START:
514                         // Already started, ignore this command.
515                         break;
516                     case CMD_SWITCH_TO_CONNECT_MODE:
517                         mRole = message.arg1; // could be any one of possible connect mode roles.
518                         updateConnectModeState(WifiManager.WIFI_STATE_ENABLING,
519                                 WifiManager.WIFI_STATE_DISABLED);
520                         if (!mWifiNative.switchClientInterfaceToConnectivityMode(
521                                 mClientInterfaceName)) {
522                             updateConnectModeState(WifiManager.WIFI_STATE_UNKNOWN,
523                                     WifiManager.WIFI_STATE_ENABLING);
524                             updateConnectModeState(WifiManager.WIFI_STATE_DISABLED,
525                                     WifiManager.WIFI_STATE_UNKNOWN);
526                             mModeListener.onStartFailure();
527                             break;
528                         }
529                         transitionTo(mConnectModeState);
530                         break;
531                     case CMD_SWITCH_TO_SCAN_ONLY_MODE:
532                         updateConnectModeState(WifiManager.WIFI_STATE_DISABLING,
533                                 WifiManager.WIFI_STATE_ENABLED);
534                         mDeferStopHandler.start(getWifiOffDeferringTimeMs());
535                         break;
536                     case CMD_SWITCH_TO_SCAN_ONLY_MODE_CONTINUE:
537                         transitionTo(mScanOnlyModeState);
538                         break;
539                     case CMD_INTERFACE_DOWN:
540                         Log.e(TAG, "Detected an interface down, reporting failure to "
541                                 + "SelfRecovery");
542                         mClientModeImpl.failureDetected(SelfRecovery.REASON_STA_IFACE_DOWN);
543                         transitionTo(mIdleState);
544                         break;
545                     case CMD_INTERFACE_STATUS_CHANGED:
546                         boolean isUp = message.arg1 == 1;
547                         onUpChanged(isUp);
548                         break;
549                     case CMD_INTERFACE_DESTROYED:
550                         Log.d(TAG, "interface destroyed - client mode stopping");
551                         mClientInterfaceName = null;
552                         transitionTo(mIdleState);
553                         break;
554                     default:
555                         return NOT_HANDLED;
556                 }
557                 return HANDLED;
558             }
559 
560             /**
561              * Clean up state, unregister listeners and update wifi state.
562              */
563             @Override
exit()564             public void exit() {
565                 mClientModeImpl.setOperationalMode(ClientModeImpl.DISABLED_MODE, null);
566 
567                 if (mClientInterfaceName != null) {
568                     mWifiNative.teardownInterface(mClientInterfaceName);
569                     mClientInterfaceName = null;
570                     mIfaceIsUp = false;
571                 }
572 
573                 // once we leave started, nothing else to do...  stop the state machine
574                 mRole = ROLE_UNSPECIFIED;
575                 mStateMachine.quitNow();
576                 mModeListener.onStopped();
577             }
578         }
579 
580         private class ScanOnlyModeState extends State {
581             @Override
enter()582             public void enter() {
583                 Log.d(TAG, "entering ScanOnlyModeState");
584                 mClientModeImpl.setOperationalMode(ClientModeImpl.SCAN_ONLY_MODE,
585                         mClientInterfaceName);
586                 mRole = ROLE_CLIENT_SCAN_ONLY;
587                 mModeListener.onStarted();
588 
589                 // Inform sar manager that scan only is being enabled
590                 mSarManager.setScanOnlyWifiState(WifiManager.WIFI_STATE_ENABLED);
591                 mWakeupController.start();
592             }
593 
594             @Override
processMessage(Message message)595             public boolean processMessage(Message message) {
596                 switch (message.what) {
597                     case CMD_SWITCH_TO_SCAN_ONLY_MODE:
598                         // Already in scan only mode, ignore this command.
599                         break;
600                     default:
601                         return NOT_HANDLED;
602                 }
603                 return HANDLED;
604             }
605 
606             @Override
exit()607             public void exit() {
608                 // Inform sar manager that scan only is being disabled
609                 mSarManager.setScanOnlyWifiState(WifiManager.WIFI_STATE_DISABLED);
610                 mWakeupController.stop();
611             }
612         }
613 
614         private class ConnectModeState extends State {
615             @Override
enter()616             public void enter() {
617                 Log.d(TAG, "entering ConnectModeState");
618                 mClientModeImpl.setOperationalMode(ClientModeImpl.CONNECT_MODE,
619                         mClientInterfaceName);
620                 mModeListener.onStarted();
621                 updateConnectModeState(WifiManager.WIFI_STATE_ENABLED,
622                         WifiManager.WIFI_STATE_ENABLING);
623 
624                 // Inform sar manager that wifi is Enabled
625                 mSarManager.setClientWifiState(WifiManager.WIFI_STATE_ENABLED);
626             }
627 
628             @Override
processMessage(Message message)629             public boolean processMessage(Message message) {
630                 switch (message.what) {
631                     case CMD_SWITCH_TO_CONNECT_MODE:
632                         int newRole = message.arg1;
633                         // Already in connect mode, only switching the connectivity roles.
634                         if (newRole != mRole) {
635                             mRole = newRole;
636                             mModeListener.onStarted();
637                         }
638                         break;
639                     case CMD_SWITCH_TO_SCAN_ONLY_MODE:
640                         updateConnectModeState(WifiManager.WIFI_STATE_DISABLING,
641                                 WifiManager.WIFI_STATE_ENABLED);
642                         return NOT_HANDLED; // Handled in StartedState.
643                     case CMD_INTERFACE_DOWN:
644                         updateConnectModeState(WifiManager.WIFI_STATE_DISABLING,
645                                 WifiManager.WIFI_STATE_UNKNOWN);
646                         return NOT_HANDLED; // Handled in StartedState.
647                     case CMD_INTERFACE_STATUS_CHANGED:
648                         boolean isUp = message.arg1 == 1;
649                         if (isUp == mIfaceIsUp) {
650                             break;  // no change
651                         }
652                         if (!isUp) {
653                             if (!mClientModeImpl.isConnectedMacRandomizationEnabled()) {
654                                 // Handle the error case where our underlying interface went down if
655                                 // we do not have mac randomization enabled (b/72459123).
656                                 // if the interface goes down we should exit and go back to idle
657                                 // state.
658                                 updateConnectModeState(WifiManager.WIFI_STATE_UNKNOWN,
659                                         WifiManager.WIFI_STATE_ENABLED);
660                             } else {
661                                 return HANDLED; // For MAC randomization, ignore...
662                             }
663                         }
664                         return NOT_HANDLED; // Handled in StartedState.
665                     case CMD_INTERFACE_DESTROYED:
666                         updateConnectModeState(WifiManager.WIFI_STATE_DISABLING,
667                                 WifiManager.WIFI_STATE_ENABLED);
668                         return NOT_HANDLED; // Handled in StartedState.
669                     default:
670                         return NOT_HANDLED;
671                 }
672                 return HANDLED;
673             }
674 
675             @Override
exit()676             public void exit() {
677                 updateConnectModeState(WifiManager.WIFI_STATE_DISABLED,
678                         WifiManager.WIFI_STATE_DISABLING);
679 
680                 // Inform sar manager that wifi is being disabled
681                 mSarManager.setClientWifiState(WifiManager.WIFI_STATE_DISABLED);
682             }
683         }
684     }
685 }
686