WifiController.java revision 2a7c2bec5d307a9e52de429c6303b7df9de900dc
1/* 2 * Copyright (C) 2013 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 17package com.android.server.wifi; 18 19import static android.net.wifi.WifiManager.WIFI_MODE_FULL; 20import static android.net.wifi.WifiManager.WIFI_MODE_FULL_HIGH_PERF; 21import static android.net.wifi.WifiManager.WIFI_MODE_NO_LOCKS_HELD; 22import static android.net.wifi.WifiManager.WIFI_MODE_SCAN_ONLY; 23 24import android.app.AlarmManager; 25import android.app.PendingIntent; 26import android.content.BroadcastReceiver; 27import android.content.Context; 28import android.content.Intent; 29import android.content.IntentFilter; 30import android.database.ContentObserver; 31import android.net.ConnectivityManager; 32import android.net.NetworkInfo; 33import android.net.wifi.WifiConfiguration; 34import android.net.wifi.WifiManager; 35import android.os.Handler; 36import android.os.Looper; 37import android.os.Message; 38import android.os.SystemClock; 39import android.os.WorkSource; 40import android.provider.Settings; 41import android.util.Slog; 42 43import com.android.internal.util.Protocol; 44import com.android.internal.util.State; 45import com.android.internal.util.StateMachine; 46 47import java.io.FileDescriptor; 48import java.io.PrintWriter; 49 50/** 51 * WifiController is the class used to manage on/off state of WifiStateMachine for various operating 52 * modes (normal, airplane, wifi hotspot, etc.). 53 */ 54public class WifiController extends StateMachine { 55 private static final String TAG = "WifiController"; 56 private static final boolean DBG = false; 57 private Context mContext; 58 private boolean mScreenOff; 59 private boolean mDeviceIdle; 60 private int mPluggedType; 61 private int mStayAwakeConditions; 62 private long mIdleMillis; 63 private int mSleepPolicy; 64 private boolean mFirstUserSignOnSeen = false; 65 66 private AlarmManager mAlarmManager; 67 private PendingIntent mIdleIntent; 68 private static final int IDLE_REQUEST = 0; 69 70 /** 71 * See {@link Settings.Global#WIFI_IDLE_MS}. This is the default value if a 72 * Settings.Global value is not present. This timeout value is chosen as 73 * the approximate point at which the battery drain caused by Wi-Fi 74 * being enabled but not active exceeds the battery drain caused by 75 * re-establishing a connection to the mobile data network. 76 */ 77 private static final long DEFAULT_IDLE_MS = 15 * 60 * 1000; /* 15 minutes */ 78 79 /** 80 * See {@link Settings.Global#WIFI_REENABLE_DELAY_MS}. This is the default value if a 81 * Settings.Global value is not present. This is the minimum time after wifi is disabled 82 * we'll act on an enable. Enable requests received before this delay will be deferred. 83 */ 84 private static final long DEFAULT_REENABLE_DELAY_MS = 500; 85 86 // finding that delayed messages can sometimes be delivered earlier than expected 87 // probably rounding errors.. add a margin to prevent problems 88 private static final long DEFER_MARGIN_MS = 5; 89 90 NetworkInfo mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_WIFI, 0, "WIFI", ""); 91 92 private static final String ACTION_DEVICE_IDLE = 93 "com.android.server.WifiManager.action.DEVICE_IDLE"; 94 95 /* References to values tracked in WifiService */ 96 private final WifiStateMachine mWifiStateMachine; 97 private final WifiSettingsStore mSettingsStore; 98 private final WifiLockManager mWifiLockManager; 99 100 /** 101 * Temporary for computing UIDS that are responsible for starting WIFI. 102 * Protected by mWifiStateTracker lock. 103 */ 104 private final WorkSource mTmpWorkSource = new WorkSource(); 105 106 private long mReEnableDelayMillis; 107 108 private FrameworkFacade mFacade; 109 110 private static final int BASE = Protocol.BASE_WIFI_CONTROLLER; 111 112 static final int CMD_EMERGENCY_MODE_CHANGED = BASE + 1; 113 static final int CMD_SCREEN_ON = BASE + 2; 114 static final int CMD_SCREEN_OFF = BASE + 3; 115 static final int CMD_BATTERY_CHANGED = BASE + 4; 116 static final int CMD_DEVICE_IDLE = BASE + 5; 117 static final int CMD_LOCKS_CHANGED = BASE + 6; 118 static final int CMD_SCAN_ALWAYS_MODE_CHANGED = BASE + 7; 119 static final int CMD_WIFI_TOGGLED = BASE + 8; 120 static final int CMD_AIRPLANE_TOGGLED = BASE + 9; 121 static final int CMD_SET_AP = BASE + 10; 122 static final int CMD_DEFERRED_TOGGLE = BASE + 11; 123 static final int CMD_USER_PRESENT = BASE + 12; 124 static final int CMD_AP_START_FAILURE = BASE + 13; 125 static final int CMD_EMERGENCY_CALL_STATE_CHANGED = BASE + 14; 126 static final int CMD_AP_STOPPED = BASE + 15; 127 static final int CMD_STA_START_FAILURE = BASE + 16; 128 129 private DefaultState mDefaultState = new DefaultState(); 130 private StaEnabledState mStaEnabledState = new StaEnabledState(); 131 private ApStaDisabledState mApStaDisabledState = new ApStaDisabledState(); 132 private StaDisabledWithScanState mStaDisabledWithScanState = new StaDisabledWithScanState(); 133 private ApEnabledState mApEnabledState = new ApEnabledState(); 134 private DeviceActiveState mDeviceActiveState = new DeviceActiveState(); 135 private DeviceInactiveState mDeviceInactiveState = new DeviceInactiveState(); 136 private ScanOnlyLockHeldState mScanOnlyLockHeldState = new ScanOnlyLockHeldState(); 137 private FullLockHeldState mFullLockHeldState = new FullLockHeldState(); 138 private FullHighPerfLockHeldState mFullHighPerfLockHeldState = new FullHighPerfLockHeldState(); 139 private NoLockHeldState mNoLockHeldState = new NoLockHeldState(); 140 private EcmState mEcmState = new EcmState(); 141 142 WifiController(Context context, WifiStateMachine wsm, WifiSettingsStore wss, 143 WifiLockManager wifiLockManager, Looper looper, FrameworkFacade f) { 144 super(TAG, looper); 145 mFacade = f; 146 mContext = context; 147 mWifiStateMachine = wsm; 148 mSettingsStore = wss; 149 mWifiLockManager = wifiLockManager; 150 151 mAlarmManager = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE); 152 Intent idleIntent = new Intent(ACTION_DEVICE_IDLE, null); 153 mIdleIntent = mFacade.getBroadcast(mContext, IDLE_REQUEST, idleIntent, 0); 154 155 addState(mDefaultState); 156 addState(mApStaDisabledState, mDefaultState); 157 addState(mStaEnabledState, mDefaultState); 158 addState(mDeviceActiveState, mStaEnabledState); 159 addState(mDeviceInactiveState, mStaEnabledState); 160 addState(mScanOnlyLockHeldState, mDeviceInactiveState); 161 addState(mFullLockHeldState, mDeviceInactiveState); 162 addState(mFullHighPerfLockHeldState, mDeviceInactiveState); 163 addState(mNoLockHeldState, mDeviceInactiveState); 164 addState(mStaDisabledWithScanState, mDefaultState); 165 addState(mApEnabledState, mDefaultState); 166 addState(mEcmState, mDefaultState); 167 168 boolean isAirplaneModeOn = mSettingsStore.isAirplaneModeOn(); 169 boolean isWifiEnabled = mSettingsStore.isWifiToggleEnabled(); 170 boolean isScanningAlwaysAvailable = mSettingsStore.isScanAlwaysAvailable(); 171 172 log("isAirplaneModeOn = " + isAirplaneModeOn + 173 ", isWifiEnabled = " + isWifiEnabled + 174 ", isScanningAvailable = " + isScanningAlwaysAvailable); 175 176 if (isScanningAlwaysAvailable) { 177 setInitialState(mStaDisabledWithScanState); 178 } else { 179 setInitialState(mApStaDisabledState); 180 } 181 182 setLogRecSize(100); 183 setLogOnlyTransitions(false); 184 185 IntentFilter filter = new IntentFilter(); 186 filter.addAction(ACTION_DEVICE_IDLE); 187 filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION); 188 filter.addAction(WifiManager.WIFI_AP_STATE_CHANGED_ACTION); 189 filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION); 190 mContext.registerReceiver( 191 new BroadcastReceiver() { 192 @Override 193 public void onReceive(Context context, Intent intent) { 194 String action = intent.getAction(); 195 if (action.equals(ACTION_DEVICE_IDLE)) { 196 sendMessage(CMD_DEVICE_IDLE); 197 } else if (action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) { 198 mNetworkInfo = (NetworkInfo) intent.getParcelableExtra( 199 WifiManager.EXTRA_NETWORK_INFO); 200 } else if (action.equals(WifiManager.WIFI_AP_STATE_CHANGED_ACTION)) { 201 int state = intent.getIntExtra( 202 WifiManager.EXTRA_WIFI_AP_STATE, 203 WifiManager.WIFI_AP_STATE_FAILED); 204 if (state == WifiManager.WIFI_AP_STATE_FAILED) { 205 loge(TAG + "SoftAP start failed"); 206 sendMessage(CMD_AP_START_FAILURE); 207 } else if (state == WifiManager.WIFI_AP_STATE_DISABLED) { 208 sendMessage(CMD_AP_STOPPED); 209 } 210 } else if (action.equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) { 211 int state = intent.getIntExtra( 212 WifiManager.EXTRA_WIFI_STATE, 213 WifiManager.WIFI_STATE_UNKNOWN); 214 if (state == WifiManager.WIFI_STATE_UNKNOWN) { 215 loge(TAG + "Wifi turn on failed"); 216 sendMessage(CMD_STA_START_FAILURE); 217 } 218 } 219 } 220 }, 221 new IntentFilter(filter)); 222 223 initializeAndRegisterForSettingsChange(looper); 224 } 225 226 private void initializeAndRegisterForSettingsChange(Looper looper) { 227 Handler handler = new Handler(looper); 228 readStayAwakeConditions(); 229 registerForStayAwakeModeChange(handler); 230 readWifiIdleTime(); 231 registerForWifiIdleTimeChange(handler); 232 readWifiSleepPolicy(); 233 registerForWifiSleepPolicyChange(handler); 234 readWifiReEnableDelay(); 235 } 236 237 private void readStayAwakeConditions() { 238 mStayAwakeConditions = mFacade.getIntegerSetting(mContext, 239 Settings.Global.STAY_ON_WHILE_PLUGGED_IN, 0); 240 } 241 242 private void readWifiIdleTime() { 243 mIdleMillis = mFacade.getLongSetting(mContext, 244 Settings.Global.WIFI_IDLE_MS, DEFAULT_IDLE_MS); 245 } 246 247 private void readWifiSleepPolicy() { 248 mSleepPolicy = mFacade.getIntegerSetting(mContext, 249 Settings.Global.WIFI_SLEEP_POLICY, 250 Settings.Global.WIFI_SLEEP_POLICY_NEVER); 251 } 252 253 private void readWifiReEnableDelay() { 254 mReEnableDelayMillis = mFacade.getLongSetting(mContext, 255 Settings.Global.WIFI_REENABLE_DELAY_MS, DEFAULT_REENABLE_DELAY_MS); 256 } 257 258 /** 259 * Observes settings changes to scan always mode. 260 */ 261 private void registerForStayAwakeModeChange(Handler handler) { 262 ContentObserver contentObserver = new ContentObserver(handler) { 263 @Override 264 public void onChange(boolean selfChange) { 265 readStayAwakeConditions(); 266 } 267 }; 268 269 mContext.getContentResolver().registerContentObserver( 270 Settings.Global.getUriFor(Settings.Global.STAY_ON_WHILE_PLUGGED_IN), 271 false, contentObserver); 272 } 273 274 /** 275 * Observes settings changes to scan always mode. 276 */ 277 private void registerForWifiIdleTimeChange(Handler handler) { 278 ContentObserver contentObserver = new ContentObserver(handler) { 279 @Override 280 public void onChange(boolean selfChange) { 281 readWifiIdleTime(); 282 } 283 }; 284 285 mContext.getContentResolver().registerContentObserver( 286 Settings.Global.getUriFor(Settings.Global.WIFI_IDLE_MS), 287 false, contentObserver); 288 } 289 290 /** 291 * Observes changes to wifi sleep policy 292 */ 293 private void registerForWifiSleepPolicyChange(Handler handler) { 294 ContentObserver contentObserver = new ContentObserver(handler) { 295 @Override 296 public void onChange(boolean selfChange) { 297 readWifiSleepPolicy(); 298 } 299 }; 300 mContext.getContentResolver().registerContentObserver( 301 Settings.Global.getUriFor(Settings.Global.WIFI_SLEEP_POLICY), 302 false, contentObserver); 303 } 304 305 /** 306 * Determines whether the Wi-Fi chipset should stay awake or be put to 307 * sleep. Looks at the setting for the sleep policy and the current 308 * conditions. 309 * 310 * @see #shouldDeviceStayAwake(int) 311 */ 312 private boolean shouldWifiStayAwake(int pluggedType) { 313 if (mSleepPolicy == Settings.Global.WIFI_SLEEP_POLICY_NEVER) { 314 // Never sleep 315 return true; 316 } else if ((mSleepPolicy == Settings.Global.WIFI_SLEEP_POLICY_NEVER_WHILE_PLUGGED) && 317 (pluggedType != 0)) { 318 // Never sleep while plugged, and we're plugged 319 return true; 320 } else { 321 // Default 322 return shouldDeviceStayAwake(pluggedType); 323 } 324 } 325 326 /** 327 * Determine whether the bit value corresponding to {@code pluggedType} is set in 328 * the bit string mStayAwakeConditions. This determines whether the device should 329 * stay awake based on the current plugged type. 330 * 331 * @param pluggedType the type of plug (USB, AC, or none) for which the check is 332 * being made 333 * @return {@code true} if {@code pluggedType} indicates that the device is 334 * supposed to stay awake, {@code false} otherwise. 335 */ 336 private boolean shouldDeviceStayAwake(int pluggedType) { 337 return (mStayAwakeConditions & pluggedType) != 0; 338 } 339 340 private void updateBatteryWorkSource() { 341 mTmpWorkSource.clear(); 342 if (mDeviceIdle) { 343 mTmpWorkSource.add(mWifiLockManager.createMergedWorkSource()); 344 } 345 mWifiStateMachine.updateBatteryWorkSource(mTmpWorkSource); 346 } 347 348 class DefaultState extends State { 349 @Override 350 public boolean processMessage(Message msg) { 351 switch (msg.what) { 352 case CMD_SCREEN_ON: 353 mAlarmManager.cancel(mIdleIntent); 354 mScreenOff = false; 355 mDeviceIdle = false; 356 updateBatteryWorkSource(); 357 break; 358 case CMD_SCREEN_OFF: 359 mScreenOff = true; 360 /* 361 * Set a timer to put Wi-Fi to sleep, but only if the screen is off 362 * AND the "stay on while plugged in" setting doesn't match the 363 * current power conditions (i.e, not plugged in, plugged in to USB, 364 * or plugged in to AC). 365 */ 366 if (!shouldWifiStayAwake(mPluggedType)) { 367 //Delayed shutdown if wifi is connected 368 if (mNetworkInfo.getDetailedState() == 369 NetworkInfo.DetailedState.CONNECTED) { 370 if (DBG) Slog.d(TAG, "set idle timer: " + mIdleMillis + " ms"); 371 mAlarmManager.set(AlarmManager.RTC_WAKEUP, 372 System.currentTimeMillis() + mIdleMillis, mIdleIntent); 373 } else { 374 sendMessage(CMD_DEVICE_IDLE); 375 } 376 } 377 break; 378 case CMD_DEVICE_IDLE: 379 mDeviceIdle = true; 380 updateBatteryWorkSource(); 381 break; 382 case CMD_BATTERY_CHANGED: 383 /* 384 * Set a timer to put Wi-Fi to sleep, but only if the screen is off 385 * AND we are transitioning from a state in which the device was supposed 386 * to stay awake to a state in which it is not supposed to stay awake. 387 * If "stay awake" state is not changing, we do nothing, to avoid resetting 388 * the already-set timer. 389 */ 390 int pluggedType = msg.arg1; 391 if (DBG) Slog.d(TAG, "battery changed pluggedType: " + pluggedType); 392 if (mScreenOff && shouldWifiStayAwake(mPluggedType) && 393 !shouldWifiStayAwake(pluggedType)) { 394 long triggerTime = System.currentTimeMillis() + mIdleMillis; 395 if (DBG) Slog.d(TAG, "set idle timer for " + mIdleMillis + "ms"); 396 mAlarmManager.set(AlarmManager.RTC_WAKEUP, triggerTime, mIdleIntent); 397 } 398 399 mPluggedType = pluggedType; 400 break; 401 case CMD_SET_AP: 402 case CMD_SCAN_ALWAYS_MODE_CHANGED: 403 case CMD_LOCKS_CHANGED: 404 case CMD_WIFI_TOGGLED: 405 case CMD_AIRPLANE_TOGGLED: 406 case CMD_EMERGENCY_MODE_CHANGED: 407 case CMD_EMERGENCY_CALL_STATE_CHANGED: 408 case CMD_AP_START_FAILURE: 409 case CMD_AP_STOPPED: 410 case CMD_STA_START_FAILURE: 411 break; 412 case CMD_USER_PRESENT: 413 mFirstUserSignOnSeen = true; 414 break; 415 case CMD_DEFERRED_TOGGLE: 416 log("DEFERRED_TOGGLE ignored due to state change"); 417 break; 418 default: 419 throw new RuntimeException("WifiController.handleMessage " + msg.what); 420 } 421 return HANDLED; 422 } 423 424 } 425 426 class ApStaDisabledState extends State { 427 private int mDeferredEnableSerialNumber = 0; 428 private boolean mHaveDeferredEnable = false; 429 private long mDisabledTimestamp; 430 431 @Override 432 public void enter() { 433 mWifiStateMachine.setSupplicantRunning(false); 434 // Supplicant can't restart right away, so not the time we switched off 435 mDisabledTimestamp = SystemClock.elapsedRealtime(); 436 mDeferredEnableSerialNumber++; 437 mHaveDeferredEnable = false; 438 mWifiStateMachine.clearANQPCache(); 439 } 440 @Override 441 public boolean processMessage(Message msg) { 442 switch (msg.what) { 443 case CMD_WIFI_TOGGLED: 444 case CMD_AIRPLANE_TOGGLED: 445 if (mSettingsStore.isWifiToggleEnabled()) { 446 if (doDeferEnable(msg)) { 447 if (mHaveDeferredEnable) { 448 // have 2 toggles now, inc serial number an ignore both 449 mDeferredEnableSerialNumber++; 450 } 451 mHaveDeferredEnable = !mHaveDeferredEnable; 452 break; 453 } 454 if (mDeviceIdle == false) { 455 transitionTo(mDeviceActiveState); 456 } else { 457 checkLocksAndTransitionWhenDeviceIdle(); 458 } 459 } else if (mSettingsStore.isScanAlwaysAvailable()) { 460 transitionTo(mStaDisabledWithScanState); 461 } 462 break; 463 case CMD_SCAN_ALWAYS_MODE_CHANGED: 464 if (mSettingsStore.isScanAlwaysAvailable()) { 465 transitionTo(mStaDisabledWithScanState); 466 } 467 break; 468 case CMD_SET_AP: 469 if (msg.arg1 == 1) { 470 if (msg.arg2 == 0) { // previous wifi state has not been saved yet 471 mSettingsStore.setWifiSavedState(WifiSettingsStore.WIFI_DISABLED); 472 } 473 mWifiStateMachine.setHostApRunning((WifiConfiguration) msg.obj, 474 true); 475 transitionTo(mApEnabledState); 476 } 477 break; 478 case CMD_DEFERRED_TOGGLE: 479 if (msg.arg1 != mDeferredEnableSerialNumber) { 480 log("DEFERRED_TOGGLE ignored due to serial mismatch"); 481 break; 482 } 483 log("DEFERRED_TOGGLE handled"); 484 sendMessage((Message)(msg.obj)); 485 break; 486 default: 487 return NOT_HANDLED; 488 } 489 return HANDLED; 490 } 491 492 private boolean doDeferEnable(Message msg) { 493 long delaySoFar = SystemClock.elapsedRealtime() - mDisabledTimestamp; 494 if (delaySoFar >= mReEnableDelayMillis) { 495 return false; 496 } 497 498 log("WifiController msg " + msg + " deferred for " + 499 (mReEnableDelayMillis - delaySoFar) + "ms"); 500 501 // need to defer this action. 502 Message deferredMsg = obtainMessage(CMD_DEFERRED_TOGGLE); 503 deferredMsg.obj = Message.obtain(msg); 504 deferredMsg.arg1 = ++mDeferredEnableSerialNumber; 505 sendMessageDelayed(deferredMsg, mReEnableDelayMillis - delaySoFar + DEFER_MARGIN_MS); 506 return true; 507 } 508 509 } 510 511 class StaEnabledState extends State { 512 @Override 513 public void enter() { 514 mWifiStateMachine.setSupplicantRunning(true); 515 } 516 @Override 517 public boolean processMessage(Message msg) { 518 switch (msg.what) { 519 case CMD_WIFI_TOGGLED: 520 if (! mSettingsStore.isWifiToggleEnabled()) { 521 if (mSettingsStore.isScanAlwaysAvailable()) { 522 transitionTo(mStaDisabledWithScanState); 523 } else { 524 transitionTo(mApStaDisabledState); 525 } 526 } 527 break; 528 case CMD_AIRPLANE_TOGGLED: 529 /* When wi-fi is turned off due to airplane, 530 * disable entirely (including scan) 531 */ 532 if (! mSettingsStore.isWifiToggleEnabled()) { 533 transitionTo(mApStaDisabledState); 534 } 535 break; 536 case CMD_STA_START_FAILURE: 537 if (!mSettingsStore.isScanAlwaysAvailable()) { 538 transitionTo(mApStaDisabledState); 539 } else { 540 transitionTo(mStaDisabledWithScanState); 541 } 542 break; 543 case CMD_EMERGENCY_CALL_STATE_CHANGED: 544 case CMD_EMERGENCY_MODE_CHANGED: 545 boolean getConfigWiFiDisableInECBM = mFacade.getConfigWiFiDisableInECBM(mContext); 546 log("WifiController msg " + msg + " getConfigWiFiDisableInECBM " 547 + getConfigWiFiDisableInECBM); 548 if ((msg.arg1 == 1) && getConfigWiFiDisableInECBM) { 549 transitionTo(mEcmState); 550 } 551 break; 552 case CMD_SET_AP: 553 if (msg.arg1 == 1) { 554 // remeber that we were enabled 555 mSettingsStore.setWifiSavedState(WifiSettingsStore.WIFI_ENABLED); 556 deferMessage(obtainMessage(msg.what, msg.arg1, 1, msg.obj)); 557 transitionTo(mApStaDisabledState); 558 } 559 break; 560 default: 561 return NOT_HANDLED; 562 563 } 564 return HANDLED; 565 } 566 } 567 568 class StaDisabledWithScanState extends State { 569 private int mDeferredEnableSerialNumber = 0; 570 private boolean mHaveDeferredEnable = false; 571 private long mDisabledTimestamp; 572 573 @Override 574 public void enter() { 575 mWifiStateMachine.setSupplicantRunning(true); 576 mWifiStateMachine.setOperationalMode(WifiStateMachine.SCAN_ONLY_WITH_WIFI_OFF_MODE); 577 mWifiStateMachine.setDriverStart(true); 578 // Supplicant can't restart right away, so not the time we switched off 579 mDisabledTimestamp = SystemClock.elapsedRealtime(); 580 mDeferredEnableSerialNumber++; 581 mHaveDeferredEnable = false; 582 mWifiStateMachine.clearANQPCache(); 583 } 584 585 @Override 586 public boolean processMessage(Message msg) { 587 switch (msg.what) { 588 case CMD_WIFI_TOGGLED: 589 if (mSettingsStore.isWifiToggleEnabled()) { 590 if (doDeferEnable(msg)) { 591 if (mHaveDeferredEnable) { 592 // have 2 toggles now, inc serial number and ignore both 593 mDeferredEnableSerialNumber++; 594 } 595 mHaveDeferredEnable = !mHaveDeferredEnable; 596 break; 597 } 598 if (mDeviceIdle == false) { 599 transitionTo(mDeviceActiveState); 600 } else { 601 checkLocksAndTransitionWhenDeviceIdle(); 602 } 603 } 604 break; 605 case CMD_AIRPLANE_TOGGLED: 606 if (mSettingsStore.isAirplaneModeOn() && 607 ! mSettingsStore.isWifiToggleEnabled()) { 608 transitionTo(mApStaDisabledState); 609 } 610 break; 611 case CMD_SCAN_ALWAYS_MODE_CHANGED: 612 if (! mSettingsStore.isScanAlwaysAvailable()) { 613 transitionTo(mApStaDisabledState); 614 } 615 break; 616 case CMD_SET_AP: 617 // Before starting tethering, turn off supplicant for scan mode 618 if (msg.arg1 == 1) { 619 mSettingsStore.setWifiSavedState(WifiSettingsStore.WIFI_DISABLED); 620 deferMessage(obtainMessage(msg.what, msg.arg1, 1, msg.obj)); 621 transitionTo(mApStaDisabledState); 622 } 623 break; 624 case CMD_DEFERRED_TOGGLE: 625 if (msg.arg1 != mDeferredEnableSerialNumber) { 626 log("DEFERRED_TOGGLE ignored due to serial mismatch"); 627 break; 628 } 629 logd("DEFERRED_TOGGLE handled"); 630 sendMessage((Message)(msg.obj)); 631 break; 632 default: 633 return NOT_HANDLED; 634 } 635 return HANDLED; 636 } 637 638 private boolean doDeferEnable(Message msg) { 639 long delaySoFar = SystemClock.elapsedRealtime() - mDisabledTimestamp; 640 if (delaySoFar >= mReEnableDelayMillis) { 641 return false; 642 } 643 644 log("WifiController msg " + msg + " deferred for " + 645 (mReEnableDelayMillis - delaySoFar) + "ms"); 646 647 // need to defer this action. 648 Message deferredMsg = obtainMessage(CMD_DEFERRED_TOGGLE); 649 deferredMsg.obj = Message.obtain(msg); 650 deferredMsg.arg1 = ++mDeferredEnableSerialNumber; 651 sendMessageDelayed(deferredMsg, mReEnableDelayMillis - delaySoFar + DEFER_MARGIN_MS); 652 return true; 653 } 654 655 } 656 657 /** 658 * Only transition out of this state when AP failed to start or AP is stopped. 659 */ 660 class ApEnabledState extends State { 661 /** 662 * Save the pending state when stopping the AP, so that it will transition 663 * to the correct state when AP is stopped. This is to avoid a possible 664 * race condition where the new state might try to update the driver/interface 665 * state before AP is completely torn down. 666 */ 667 private State mPendingState = null; 668 669 /** 670 * Determine the next state based on the current settings (e.g. saved 671 * wifi state). 672 */ 673 private State getNextWifiState() { 674 if (mSettingsStore.getWifiSavedState() == WifiSettingsStore.WIFI_ENABLED) { 675 return mDeviceActiveState; 676 } 677 678 if (mSettingsStore.isScanAlwaysAvailable()) { 679 return mStaDisabledWithScanState; 680 } 681 682 return mApStaDisabledState; 683 } 684 685 @Override 686 public boolean processMessage(Message msg) { 687 switch (msg.what) { 688 case CMD_AIRPLANE_TOGGLED: 689 if (mSettingsStore.isAirplaneModeOn()) { 690 mWifiStateMachine.setHostApRunning(null, false); 691 mPendingState = mApStaDisabledState; 692 } 693 break; 694 case CMD_WIFI_TOGGLED: 695 if (mSettingsStore.isWifiToggleEnabled()) { 696 mWifiStateMachine.setHostApRunning(null, false); 697 mPendingState = mStaEnabledState; 698 } 699 break; 700 case CMD_SET_AP: 701 if (msg.arg1 == 0) { 702 mWifiStateMachine.setHostApRunning(null, false); 703 mPendingState = getNextWifiState(); 704 } 705 break; 706 case CMD_AP_STOPPED: 707 if (mPendingState == null) { 708 /** 709 * Stop triggered internally, either tether notification 710 * timed out or wifi is untethered for some reason. 711 */ 712 mPendingState = getNextWifiState(); 713 } 714 if (mPendingState == mDeviceActiveState && mDeviceIdle) { 715 checkLocksAndTransitionWhenDeviceIdle(); 716 } else { 717 // go ahead and transition because we are not idle or we are not going 718 // to the active state. 719 transitionTo(mPendingState); 720 } 721 break; 722 case CMD_EMERGENCY_CALL_STATE_CHANGED: 723 case CMD_EMERGENCY_MODE_CHANGED: 724 if (msg.arg1 == 1) { 725 mWifiStateMachine.setHostApRunning(null, false); 726 mPendingState = mEcmState; 727 } 728 break; 729 case CMD_AP_START_FAILURE: 730 transitionTo(getNextWifiState()); 731 break; 732 default: 733 return NOT_HANDLED; 734 } 735 return HANDLED; 736 } 737 } 738 739 class EcmState extends State { 740 // we can enter EcmState either because an emergency call started or because 741 // emergency callback mode started. This count keeps track of how many such 742 // events happened; so we can exit after all are undone 743 744 private int mEcmEntryCount; 745 @Override 746 public void enter() { 747 mWifiStateMachine.setSupplicantRunning(false); 748 mWifiStateMachine.clearANQPCache(); 749 mEcmEntryCount = 1; 750 } 751 752 @Override 753 public boolean processMessage(Message msg) { 754 if (msg.what == CMD_EMERGENCY_CALL_STATE_CHANGED) { 755 if (msg.arg1 == 1) { 756 // nothing to do - just says emergency call started 757 mEcmEntryCount++; 758 } else if (msg.arg1 == 0) { 759 // emergency call ended 760 decrementCountAndReturnToAppropriateState(); 761 } 762 return HANDLED; 763 } else if (msg.what == CMD_EMERGENCY_MODE_CHANGED) { 764 765 if (msg.arg1 == 1) { 766 // Transitioned into emergency callback mode 767 mEcmEntryCount++; 768 } else if (msg.arg1 == 0) { 769 // out of emergency callback mode 770 decrementCountAndReturnToAppropriateState(); 771 } 772 return HANDLED; 773 } else { 774 return NOT_HANDLED; 775 } 776 } 777 778 private void decrementCountAndReturnToAppropriateState() { 779 boolean exitEcm = false; 780 781 if (mEcmEntryCount == 0) { 782 loge("mEcmEntryCount is 0; exiting Ecm"); 783 exitEcm = true; 784 } else if (--mEcmEntryCount == 0) { 785 exitEcm = true; 786 } 787 788 if (exitEcm) { 789 if (mSettingsStore.isWifiToggleEnabled()) { 790 if (mDeviceIdle == false) { 791 transitionTo(mDeviceActiveState); 792 } else { 793 checkLocksAndTransitionWhenDeviceIdle(); 794 } 795 } else if (mSettingsStore.isScanAlwaysAvailable()) { 796 transitionTo(mStaDisabledWithScanState); 797 } else { 798 transitionTo(mApStaDisabledState); 799 } 800 } 801 } 802 } 803 804 /* Parent: StaEnabledState */ 805 class DeviceActiveState extends State { 806 @Override 807 public void enter() { 808 mWifiStateMachine.setOperationalMode(WifiStateMachine.CONNECT_MODE); 809 mWifiStateMachine.setDriverStart(true); 810 mWifiStateMachine.setHighPerfModeEnabled(false); 811 } 812 813 @Override 814 public boolean processMessage(Message msg) { 815 if (msg.what == CMD_DEVICE_IDLE) { 816 checkLocksAndTransitionWhenDeviceIdle(); 817 // We let default state handle the rest of work 818 } else if (msg.what == CMD_USER_PRESENT) { 819 // TLS networks can't connect until user unlocks keystore. KeyStore 820 // unlocks when the user punches PIN after the reboot. So use this 821 // trigger to get those networks connected. 822 if (mFirstUserSignOnSeen == false) { 823 mWifiStateMachine.reloadTlsNetworksAndReconnect(); 824 } 825 mFirstUserSignOnSeen = true; 826 return HANDLED; 827 } 828 return NOT_HANDLED; 829 } 830 } 831 832 /* Parent: StaEnabledState */ 833 class DeviceInactiveState extends State { 834 @Override 835 public boolean processMessage(Message msg) { 836 switch (msg.what) { 837 case CMD_LOCKS_CHANGED: 838 checkLocksAndTransitionWhenDeviceIdle(); 839 updateBatteryWorkSource(); 840 return HANDLED; 841 case CMD_SCREEN_ON: 842 transitionTo(mDeviceActiveState); 843 // More work in default state 844 return NOT_HANDLED; 845 default: 846 return NOT_HANDLED; 847 } 848 } 849 } 850 851 /* Parent: DeviceInactiveState. Device is inactive, but an app is holding a scan only lock. */ 852 class ScanOnlyLockHeldState extends State { 853 @Override 854 public void enter() { 855 mWifiStateMachine.setOperationalMode(WifiStateMachine.SCAN_ONLY_MODE); 856 mWifiStateMachine.setDriverStart(true); 857 } 858 } 859 860 /* Parent: DeviceInactiveState. Device is inactive, but an app is holding a full lock. */ 861 class FullLockHeldState extends State { 862 @Override 863 public void enter() { 864 mWifiStateMachine.setOperationalMode(WifiStateMachine.CONNECT_MODE); 865 mWifiStateMachine.setDriverStart(true); 866 mWifiStateMachine.setHighPerfModeEnabled(false); 867 } 868 } 869 870 /* Parent: DeviceInactiveState. Device is inactive, but an app is holding a high perf lock. */ 871 class FullHighPerfLockHeldState extends State { 872 @Override 873 public void enter() { 874 mWifiStateMachine.setOperationalMode(WifiStateMachine.CONNECT_MODE); 875 mWifiStateMachine.setDriverStart(true); 876 mWifiStateMachine.setHighPerfModeEnabled(true); 877 } 878 } 879 880 /* Parent: DeviceInactiveState. Device is inactive and no app is holding a wifi lock. */ 881 class NoLockHeldState extends State { 882 @Override 883 public void enter() { 884 mWifiStateMachine.setDriverStart(false); 885 } 886 } 887 888 private void checkLocksAndTransitionWhenDeviceIdle() { 889 switch (mWifiLockManager.getStrongestLockMode()) { 890 case WIFI_MODE_NO_LOCKS_HELD: 891 if (mSettingsStore.isScanAlwaysAvailable()) { 892 transitionTo(mScanOnlyLockHeldState); 893 } else { 894 transitionTo(mNoLockHeldState); 895 } 896 break; 897 case WIFI_MODE_FULL: 898 transitionTo(mFullLockHeldState); 899 break; 900 case WIFI_MODE_FULL_HIGH_PERF: 901 transitionTo(mFullHighPerfLockHeldState); 902 break; 903 case WIFI_MODE_SCAN_ONLY: 904 transitionTo(mScanOnlyLockHeldState); 905 break; 906 } 907 } 908 909 @Override 910 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 911 super.dump(fd, pw, args); 912 913 pw.println("mScreenOff " + mScreenOff); 914 pw.println("mDeviceIdle " + mDeviceIdle); 915 pw.println("mPluggedType " + mPluggedType); 916 pw.println("mIdleMillis " + mIdleMillis); 917 pw.println("mSleepPolicy " + mSleepPolicy); 918 } 919} 920