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.net.wifi.WifiConfiguration;
22 import android.net.wifi.WifiManager;
23 import android.os.BatteryStats;
24 import android.os.Handler;
25 import android.os.Looper;
26 import android.os.Message;
27 import android.os.RemoteException;
28 import android.util.ArraySet;
29 import android.util.Log;
30 
31 import com.android.internal.app.IBatteryStats;
32 import com.android.internal.util.Protocol;
33 import com.android.internal.util.State;
34 import com.android.internal.util.StateMachine;
35 import com.android.server.wifi.WifiNative.StatusListener;
36 
37 import java.io.FileDescriptor;
38 import java.io.PrintWriter;
39 
40 /**
41  * This class provides the implementation for different WiFi operating modes.
42  */
43 public class ActiveModeWarden {
44     private static final String TAG = "WifiActiveModeWarden";
45 
46     private ModeStateMachine mModeStateMachine;
47 
48     // Holder for active mode managers
49     private final ArraySet<ActiveModeManager> mActiveModeManagers;
50     // DefaultModeManager used to service API calls when there are not active mode managers.
51     private DefaultModeManager mDefaultModeManager;
52 
53     private final WifiInjector mWifiInjector;
54     private final Context mContext;
55     private final Looper mLooper;
56     private final Handler mHandler;
57     private final WifiNative mWifiNative;
58     private final IBatteryStats mBatteryStats;
59     private final SelfRecovery mSelfRecovery;
60     private BaseWifiDiagnostics mWifiDiagnostics;
61     private final ScanRequestProxy mScanRequestProxy;
62 
63     // The base for wifi message types
64     static final int BASE = Protocol.BASE_WIFI;
65 
66     // The message identifiers below are mapped to those in ClientModeImpl when applicable.
67     // Start the soft access point
68     static final int CMD_START_AP                                       = BASE + 21;
69     // Indicates soft ap start failed
70     static final int CMD_START_AP_FAILURE                               = BASE + 22;
71     // Stop the soft access point
72     static final int CMD_STOP_AP                                        = BASE + 23;
73     // Soft access point teardown is completed
74     static final int CMD_AP_STOPPED                                     = BASE + 24;
75 
76     // Start Scan Only mode
77     static final int CMD_START_SCAN_ONLY_MODE                           = BASE + 200;
78     // Indicates that start Scan only mode failed
79     static final int CMD_START_SCAN_ONLY_MODE_FAILURE                   = BASE + 201;
80     // Indicates that scan only mode stopped
81     static final int CMD_STOP_SCAN_ONLY_MODE                            = BASE + 202;
82     // ScanOnly mode teardown is complete
83     static final int CMD_SCAN_ONLY_MODE_STOPPED                         = BASE + 203;
84     // ScanOnly mode failed
85     static final int CMD_SCAN_ONLY_MODE_FAILED                          = BASE + 204;
86 
87     // Start Client mode
88     static final int CMD_START_CLIENT_MODE                              = BASE + 300;
89     // Indicates that start client mode failed
90     static final int CMD_START_CLIENT_MODE_FAILURE                      = BASE + 301;
91     // Indicates that client mode stopped
92     static final int CMD_STOP_CLIENT_MODE                               = BASE + 302;
93     // Client mode teardown is complete
94     static final int CMD_CLIENT_MODE_STOPPED                            = BASE + 303;
95     // Client mode failed
96     static final int CMD_CLIENT_MODE_FAILED                             = BASE + 304;
97 
98     private StatusListener mWifiNativeStatusListener;
99 
100     private WifiManager.SoftApCallback mSoftApCallback;
101     private ScanOnlyModeManager.Listener mScanOnlyCallback;
102     private ClientModeManager.Listener mClientModeCallback;
103 
104     /**
105      * Called from WifiServiceImpl to register a callback for notifications from SoftApManager
106      */
registerSoftApCallback(@onNull WifiManager.SoftApCallback callback)107     public void registerSoftApCallback(@NonNull WifiManager.SoftApCallback callback) {
108         mSoftApCallback = callback;
109     }
110 
111     /**
112      * Called from WifiController to register a callback for notifications from ScanOnlyModeManager
113      */
registerScanOnlyCallback(@onNull ScanOnlyModeManager.Listener callback)114     public void registerScanOnlyCallback(@NonNull ScanOnlyModeManager.Listener callback) {
115         mScanOnlyCallback = callback;
116     }
117 
118     /**
119      * Called from WifiController to register a callback for notifications from ClientModeManager
120      */
registerClientModeCallback(@onNull ClientModeManager.Listener callback)121     public void registerClientModeCallback(@NonNull ClientModeManager.Listener callback) {
122         mClientModeCallback = callback;
123     }
124 
ActiveModeWarden(WifiInjector wifiInjector, Context context, Looper looper, WifiNative wifiNative, DefaultModeManager defaultModeManager, IBatteryStats batteryStats)125     ActiveModeWarden(WifiInjector wifiInjector,
126                      Context context,
127                      Looper looper,
128                      WifiNative wifiNative,
129                      DefaultModeManager defaultModeManager,
130                      IBatteryStats batteryStats) {
131         mWifiInjector = wifiInjector;
132         mContext = context;
133         mLooper = looper;
134         mHandler = new Handler(looper);
135         mWifiNative = wifiNative;
136         mActiveModeManagers = new ArraySet();
137         mDefaultModeManager = defaultModeManager;
138         mBatteryStats = batteryStats;
139         mSelfRecovery = mWifiInjector.getSelfRecovery();
140         mWifiDiagnostics = mWifiInjector.getWifiDiagnostics();
141         mScanRequestProxy = mWifiInjector.getScanRequestProxy();
142         mModeStateMachine = new ModeStateMachine();
143         mWifiNativeStatusListener = new WifiNativeStatusListener();
144         mWifiNative.registerStatusListener(mWifiNativeStatusListener);
145     }
146 
147     /**
148      * Method to switch wifi into client mode where connections to configured networks will be
149      * attempted.
150      */
enterClientMode()151     public void enterClientMode() {
152         changeMode(ModeStateMachine.CMD_START_CLIENT_MODE);
153     }
154 
155     /**
156      * Method to switch wifi into scan only mode where network connection attempts will not be made.
157      *
158      * This mode is utilized by location scans.  If wifi is disabled by a user, but they have
159      * previously configured their device to perform location scans, this mode allows wifi to
160      * fulfill the location scan requests but will not be used for connectivity.
161      */
enterScanOnlyMode()162     public void enterScanOnlyMode() {
163         changeMode(ModeStateMachine.CMD_START_SCAN_ONLY_MODE);
164     }
165 
166     /**
167      * Method to enable soft ap for wifi hotspot.
168      *
169      * The supplied SoftApModeConfiguration includes the target softap WifiConfiguration (or null if
170      * the persisted config is to be used) and the target operating mode (ex,
171      * {@link WifiManager.IFACE_IP_MODE_TETHERED} {@link WifiManager.IFACE_IP_MODE_LOCAL_ONLY}).
172      *
173      * @param wifiConfig SoftApModeConfiguration for the hostapd softap
174      */
enterSoftAPMode(@onNull SoftApModeConfiguration wifiConfig)175     public void enterSoftAPMode(@NonNull SoftApModeConfiguration wifiConfig) {
176         mHandler.post(() -> {
177             startSoftAp(wifiConfig);
178         });
179     }
180 
181     /**
182      * Method to stop soft ap for wifi hotspot.
183      *
184      * This method will stop any active softAp mode managers.
185      *
186      * @param mode the operating mode of APs to bring down (ex,
187      *             {@link WifiManager.IFACE_IP_MODE_TETHERED} or
188      *             {@link WifiManager.IFACE_IP_MODE_LOCAL_ONLY}).
189      *             Use {@link WifiManager.IFACE_IP_MODE_UNSPECIFIED} to stop all APs.
190      */
stopSoftAPMode(int mode)191     public void stopSoftAPMode(int mode) {
192         mHandler.post(() -> {
193             for (ActiveModeManager manager : mActiveModeManagers) {
194                 if (!(manager instanceof SoftApManager)) continue;
195                 SoftApManager softApManager = (SoftApManager) manager;
196 
197                 if (mode != WifiManager.IFACE_IP_MODE_UNSPECIFIED
198                         && mode != softApManager.getIpMode()) {
199                     continue;
200                 }
201                 softApManager.stop();
202             }
203             updateBatteryStatsWifiState(false);
204         });
205     }
206 
207     /**
208      * Method to disable wifi in sta/client mode scenarios.
209      *
210      * This mode will stop any client/scan modes and will not perform any network scans.
211      */
disableWifi()212     public void disableWifi() {
213         changeMode(ModeStateMachine.CMD_DISABLE_WIFI);
214     }
215 
216     /**
217      * Method to stop all active modes, for example, when toggling airplane mode.
218      */
shutdownWifi()219     public void shutdownWifi() {
220         mHandler.post(() -> {
221             for (ActiveModeManager manager : mActiveModeManagers) {
222                 manager.stop();
223             }
224             updateBatteryStatsWifiState(false);
225         });
226     }
227 
228     /**
229      * Dump current state for active mode managers.
230      *
231      * Must be called from ClientModeImpl thread.
232      */
dump(FileDescriptor fd, PrintWriter pw, String[] args)233     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
234         pw.println("Dump of " + TAG);
235 
236         pw.println("Current wifi mode: " + getCurrentMode());
237         pw.println("NumActiveModeManagers: " + mActiveModeManagers.size());
238         for (ActiveModeManager manager : mActiveModeManagers) {
239             manager.dump(fd, pw, args);
240         }
241     }
242 
getCurrentMode()243     protected String getCurrentMode() {
244         return mModeStateMachine.getCurrentMode();
245     }
246 
changeMode(int newMode)247     private void changeMode(int newMode) {
248         mModeStateMachine.sendMessage(newMode);
249     }
250 
251     /**
252      *  Helper class to wrap the ActiveModeManager callback objects.
253      */
254     private class ModeCallback {
255         ActiveModeManager mActiveManager;
256 
setActiveModeManager(ActiveModeManager manager)257         void setActiveModeManager(ActiveModeManager manager) {
258             mActiveManager = manager;
259         }
260 
getActiveModeManager()261         ActiveModeManager getActiveModeManager() {
262             return mActiveManager;
263         }
264     }
265 
266     private class ModeStateMachine extends StateMachine {
267         // Commands for the state machine  - these will be removed,
268         // along with the StateMachine itself
269         public static final int CMD_START_CLIENT_MODE    = 0;
270         public static final int CMD_START_SCAN_ONLY_MODE = 1;
271         public static final int CMD_DISABLE_WIFI         = 3;
272 
273         private final State mWifiDisabledState = new WifiDisabledState();
274         private final State mClientModeActiveState = new ClientModeActiveState();
275         private final State mScanOnlyModeActiveState = new ScanOnlyModeActiveState();
276 
ModeStateMachine()277         ModeStateMachine() {
278             super(TAG, mLooper);
279 
280             addState(mClientModeActiveState);
281             addState(mScanOnlyModeActiveState);
282             addState(mWifiDisabledState);
283 
284             Log.d(TAG, "Starting Wifi in WifiDisabledState");
285             setInitialState(mWifiDisabledState);
286             start();
287         }
288 
getCurrentMode()289         private String getCurrentMode() {
290             return getCurrentState().getName();
291         }
292 
checkForAndHandleModeChange(Message message)293         private boolean checkForAndHandleModeChange(Message message) {
294             switch(message.what) {
295                 case ModeStateMachine.CMD_START_CLIENT_MODE:
296                     Log.d(TAG, "Switching from " + getCurrentMode() + " to ClientMode");
297                     mModeStateMachine.transitionTo(mClientModeActiveState);
298                     break;
299                 case ModeStateMachine.CMD_START_SCAN_ONLY_MODE:
300                     Log.d(TAG, "Switching from " + getCurrentMode() + " to ScanOnlyMode");
301                     mModeStateMachine.transitionTo(mScanOnlyModeActiveState);
302                     break;
303                 case ModeStateMachine.CMD_DISABLE_WIFI:
304                     Log.d(TAG, "Switching from " + getCurrentMode() + " to WifiDisabled");
305                     mModeStateMachine.transitionTo(mWifiDisabledState);
306                     break;
307                 default:
308                     return NOT_HANDLED;
309             }
310             return HANDLED;
311         }
312 
313         class ModeActiveState extends State {
314             ActiveModeManager mManager;
315             @Override
processMessage(Message message)316             public boolean processMessage(Message message) {
317                 // handle messages for changing modes here
318                 return NOT_HANDLED;
319             }
320 
321             @Override
exit()322             public void exit() {
323                 // Active states must have a mode manager, so this should not be null, but it isn't
324                 // obvious from the structure - add a null check here, just in case this is missed
325                 // in the future
326                 if (mManager != null) {
327                     mManager.stop();
328                     mActiveModeManagers.remove(mManager);
329                     updateScanMode();
330                 }
331                 updateBatteryStatsWifiState(false);
332             }
333 
334             // Hook to be used by sub-classes of ModeActiveState to indicate the completion of
335             // bringup of the corresponding mode.
onModeActivationComplete()336             public void onModeActivationComplete() {
337                 updateScanMode();
338             }
339 
340             // Update the scan state based on all active mode managers.
341             // Note: This is an overkill currently because there is only 1 of scan-only or client
342             // mode present today.
updateScanMode()343             private void updateScanMode() {
344                 boolean scanEnabled = false;
345                 boolean scanningForHiddenNetworksEnabled = false;
346                 for (ActiveModeManager modeManager : mActiveModeManagers) {
347                     @ActiveModeManager.ScanMode int scanState = modeManager.getScanMode();
348                     switch (scanState) {
349                         case ActiveModeManager.SCAN_NONE:
350                             break;
351                         case ActiveModeManager.SCAN_WITHOUT_HIDDEN_NETWORKS:
352                             scanEnabled = true;
353                             break;
354                         case ActiveModeManager.SCAN_WITH_HIDDEN_NETWORKS:
355                             scanEnabled = true;
356                             scanningForHiddenNetworksEnabled = true;
357                             break;
358                     }
359                 }
360                 mScanRequestProxy.enableScanning(scanEnabled, scanningForHiddenNetworksEnabled);
361             }
362         }
363 
364         class WifiDisabledState extends ModeActiveState {
365             @Override
enter()366             public void enter() {
367                 Log.d(TAG, "Entering WifiDisabledState");
368             }
369 
370             @Override
processMessage(Message message)371             public boolean processMessage(Message message) {
372                 Log.d(TAG, "received a message in WifiDisabledState: " + message);
373                 if (checkForAndHandleModeChange(message)) {
374                     return HANDLED;
375                 }
376                 return NOT_HANDLED;
377             }
378 
379             @Override
exit()380             public void exit() {
381                 // do not have an active mode manager...  nothing to clean up
382             }
383 
384         }
385 
386         class ClientModeActiveState extends ModeActiveState {
387             ClientListener mListener;
388             private class ClientListener implements ClientModeManager.Listener {
389                 @Override
onStateChanged(int state)390                 public void onStateChanged(int state) {
391                     // make sure this listener is still active
392                     if (this != mListener) {
393                         Log.d(TAG, "Client mode state change from previous manager");
394                         return;
395                     }
396 
397                     Log.d(TAG, "State changed from client mode. state = " + state);
398 
399                     if (state == WifiManager.WIFI_STATE_UNKNOWN) {
400                         // error while setting up client mode or an unexpected failure.
401                         mModeStateMachine.sendMessage(CMD_CLIENT_MODE_FAILED, this);
402                     } else if (state == WifiManager.WIFI_STATE_DISABLED) {
403                         // client mode stopped
404                         mModeStateMachine.sendMessage(CMD_CLIENT_MODE_STOPPED, this);
405                     } else if (state == WifiManager.WIFI_STATE_ENABLED) {
406                         // client mode is ready to go
407                         Log.d(TAG, "client mode active");
408                         onModeActivationComplete();
409                     } else {
410                         // only care if client mode stopped or started, dropping
411                     }
412                 }
413             }
414 
415             @Override
enter()416             public void enter() {
417                 Log.d(TAG, "Entering ClientModeActiveState");
418 
419                 mListener = new ClientListener();
420                 mManager = mWifiInjector.makeClientModeManager(mListener);
421                 mManager.start();
422                 mActiveModeManagers.add(mManager);
423 
424                 updateBatteryStatsWifiState(true);
425             }
426 
427             @Override
exit()428             public void exit() {
429                 super.exit();
430                 mListener = null;
431             }
432 
433             @Override
processMessage(Message message)434             public boolean processMessage(Message message) {
435                 if (checkForAndHandleModeChange(message)) {
436                     return HANDLED;
437                 }
438 
439                 switch(message.what) {
440                     case CMD_START_CLIENT_MODE:
441                         Log.d(TAG, "Received CMD_START_CLIENT_MODE when active - drop");
442                         break;
443                     case CMD_CLIENT_MODE_FAILED:
444                         if (mListener != message.obj) {
445                             Log.d(TAG, "Client mode state change from previous manager");
446                             return HANDLED;
447                         }
448                         Log.d(TAG, "ClientMode failed, return to WifiDisabledState.");
449                         // notify WifiController that ClientMode failed
450                         mClientModeCallback.onStateChanged(WifiManager.WIFI_STATE_UNKNOWN);
451                         mModeStateMachine.transitionTo(mWifiDisabledState);
452                         break;
453                     case CMD_CLIENT_MODE_STOPPED:
454                         if (mListener != message.obj) {
455                             Log.d(TAG, "Client mode state change from previous manager");
456                             return HANDLED;
457                         }
458 
459                         Log.d(TAG, "ClientMode stopped, return to WifiDisabledState.");
460                         // notify WifiController that ClientMode stopped
461                         mClientModeCallback.onStateChanged(WifiManager.WIFI_STATE_DISABLED);
462                         mModeStateMachine.transitionTo(mWifiDisabledState);
463                         break;
464                     default:
465                         return NOT_HANDLED;
466                 }
467                 return NOT_HANDLED;
468             }
469         }
470 
471         class ScanOnlyModeActiveState extends ModeActiveState {
472             ScanOnlyListener mListener;
473             private class ScanOnlyListener implements ScanOnlyModeManager.Listener {
474                 @Override
onStateChanged(int state)475                 public void onStateChanged(int state) {
476                     if (this != mListener) {
477                         Log.d(TAG, "ScanOnly mode state change from previous manager");
478                         return;
479                     }
480 
481                     if (state == WifiManager.WIFI_STATE_UNKNOWN) {
482                         Log.d(TAG, "ScanOnlyMode mode failed");
483                         // error while setting up scan mode or an unexpected failure.
484                         mModeStateMachine.sendMessage(CMD_SCAN_ONLY_MODE_FAILED, this);
485                     } else if (state == WifiManager.WIFI_STATE_DISABLED) {
486                         Log.d(TAG, "ScanOnlyMode stopped");
487                         //scan only mode stopped
488                         mModeStateMachine.sendMessage(CMD_SCAN_ONLY_MODE_STOPPED, this);
489                     } else if (state == WifiManager.WIFI_STATE_ENABLED) {
490                         // scan mode is ready to go
491                         Log.d(TAG, "scan mode active");
492                         onModeActivationComplete();
493                     } else {
494                         Log.d(TAG, "unexpected state update: " + state);
495                     }
496                 }
497             }
498 
499             @Override
enter()500             public void enter() {
501                 Log.d(TAG, "Entering ScanOnlyModeActiveState");
502 
503                 mListener = new ScanOnlyListener();
504                 mManager = mWifiInjector.makeScanOnlyModeManager(mListener);
505                 mManager.start();
506                 mActiveModeManagers.add(mManager);
507 
508                 updateBatteryStatsWifiState(true);
509                 updateBatteryStatsScanModeActive();
510             }
511 
512             @Override
exit()513             public void exit() {
514                 super.exit();
515                 mListener = null;
516             }
517 
518             @Override
processMessage(Message message)519             public boolean processMessage(Message message) {
520                 if (checkForAndHandleModeChange(message)) {
521                     return HANDLED;
522                 }
523 
524                 switch(message.what) {
525                     case CMD_START_SCAN_ONLY_MODE:
526                         Log.d(TAG, "Received CMD_START_SCAN_ONLY_MODE when active - drop");
527                         break;
528                     case CMD_SCAN_ONLY_MODE_FAILED:
529                         if (mListener != message.obj) {
530                             Log.d(TAG, "ScanOnly mode state change from previous manager");
531                             return HANDLED;
532                         }
533 
534                         Log.d(TAG, "ScanOnlyMode failed, return to WifiDisabledState.");
535                         // notify WifiController that ScanOnlyMode failed
536                         mScanOnlyCallback.onStateChanged(WifiManager.WIFI_STATE_UNKNOWN);
537                         mModeStateMachine.transitionTo(mWifiDisabledState);
538                         break;
539                     case CMD_SCAN_ONLY_MODE_STOPPED:
540                         if (mListener != message.obj) {
541                             Log.d(TAG, "ScanOnly mode state change from previous manager");
542                             return HANDLED;
543                         }
544 
545                         Log.d(TAG, "ScanOnlyMode stopped, return to WifiDisabledState.");
546                         // notify WifiController that ScanOnlyMode stopped
547                         mScanOnlyCallback.onStateChanged(WifiManager.WIFI_STATE_DISABLED);
548                         mModeStateMachine.transitionTo(mWifiDisabledState);
549                         break;
550                     default:
551                         return NOT_HANDLED;
552                 }
553                 return HANDLED;
554             }
555         }
556     }  // class ModeStateMachine
557 
558     private class SoftApCallbackImpl extends ModeCallback implements WifiManager.SoftApCallback {
559         private int mMode;
560 
SoftApCallbackImpl(int mode)561         private SoftApCallbackImpl(int mode) {
562             mMode = mode;
563         }
564 
565         @Override
onStateChanged(int state, int reason)566         public void onStateChanged(int state, int reason) {
567             if (state == WifiManager.WIFI_AP_STATE_DISABLED) {
568                 mActiveModeManagers.remove(getActiveModeManager());
569                 updateBatteryStatsWifiState(false);
570             } else if (state == WifiManager.WIFI_AP_STATE_FAILED) {
571                 mActiveModeManagers.remove(getActiveModeManager());
572                 updateBatteryStatsWifiState(false);
573             }
574 
575             if (mSoftApCallback != null && mMode == WifiManager.IFACE_IP_MODE_TETHERED) {
576                 mSoftApCallback.onStateChanged(state, reason);
577             }
578         }
579 
580         @Override
onNumClientsChanged(int numClients)581         public void onNumClientsChanged(int numClients) {
582             if (mSoftApCallback == null) {
583                 Log.d(TAG, "SoftApCallback is null. Dropping NumClientsChanged event.");
584             } else if (mMode == WifiManager.IFACE_IP_MODE_TETHERED) {
585                 mSoftApCallback.onNumClientsChanged(numClients);
586             }
587         }
588     }
589 
startSoftAp(SoftApModeConfiguration softapConfig)590     private void startSoftAp(SoftApModeConfiguration softapConfig) {
591         Log.d(TAG, "Starting SoftApModeManager");
592 
593         WifiConfiguration config = softapConfig.getWifiConfiguration();
594         if (config != null && config.SSID != null) {
595             Log.d(TAG, "Passing config to SoftApManager! " + config);
596         } else {
597             config = null;
598         }
599 
600         SoftApCallbackImpl callback = new SoftApCallbackImpl(softapConfig.getTargetMode());
601         ActiveModeManager manager = mWifiInjector.makeSoftApManager(callback, softapConfig);
602         callback.setActiveModeManager(manager);
603         manager.start();
604         mActiveModeManagers.add(manager);
605         updateBatteryStatsWifiState(true);
606     }
607 
608     /**
609      *  Helper method to report wifi state as on/off (doesn't matter which mode).
610      *
611      *  @param enabled boolean indicating that some mode has been turned on or off
612      */
updateBatteryStatsWifiState(boolean enabled)613     private void updateBatteryStatsWifiState(boolean enabled) {
614         try {
615             if (enabled) {
616                 if (mActiveModeManagers.size() == 1) {
617                     // only report wifi on if we haven't already
618                     mBatteryStats.noteWifiOn();
619                 }
620             } else {
621                 if (mActiveModeManagers.size() == 0) {
622                     // only report if we don't have any active modes
623                     mBatteryStats.noteWifiOff();
624                 }
625             }
626         } catch (RemoteException e) {
627             Log.e(TAG, "Failed to note battery stats in wifi");
628         }
629     }
630 
updateBatteryStatsScanModeActive()631     private void updateBatteryStatsScanModeActive() {
632         try {
633             mBatteryStats.noteWifiState(BatteryStats.WIFI_STATE_OFF_SCANNING, null);
634         } catch (RemoteException e) {
635             Log.e(TAG, "Failed to note battery stats in wifi");
636         }
637     }
638 
639     // callback used to receive callbacks about underlying native failures
640     private final class WifiNativeStatusListener implements StatusListener {
641 
642         @Override
onStatusChanged(boolean isReady)643         public void onStatusChanged(boolean isReady) {
644             if (!isReady) {
645                 mHandler.post(() -> {
646                     Log.e(TAG, "One of the native daemons died. Triggering recovery");
647                     mWifiDiagnostics.captureBugReportData(
648                             WifiDiagnostics.REPORT_REASON_WIFINATIVE_FAILURE);
649 
650                     // immediately trigger SelfRecovery if we receive a notice about an
651                     // underlying daemon failure
652                     mWifiInjector.getSelfRecovery().trigger(SelfRecovery.REASON_WIFINATIVE_FAILURE);
653                 });
654             }
655         }
656     };
657 }
658