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