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