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