PhoneApp.java revision 4b8337277ec2e375c3536b97c40e6617a7b12990
1/* 2 * Copyright (C) 2006 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.phone; 18 19import com.android.internal.telephony.MmiCode; 20import com.android.internal.telephony.Phone; 21import com.android.internal.telephony.PhoneFactory; 22import com.android.internal.telephony.SimCard; 23import com.android.internal.telephony.TelephonyIntents; 24 25import android.app.Activity; 26import android.app.Application; 27import android.app.KeyguardManager; 28import android.app.ProgressDialog; 29import android.bluetooth.BluetoothHeadset; 30import android.bluetooth.BluetoothIntent; 31import android.content.BroadcastReceiver; 32import android.content.ContentResolver; 33import android.content.Context; 34import android.content.Intent; 35import android.content.IntentFilter; 36import android.media.AudioManager; 37import android.net.Uri; 38import android.os.AsyncResult; 39import android.os.Binder; 40import android.os.Handler; 41import android.os.IBinder; 42import android.os.IPowerManager; 43import android.os.LocalPowerManager; 44import android.os.Message; 45import android.os.PowerManager; 46import android.os.RemoteException; 47import android.os.ServiceManager; 48import android.os.SystemClock; 49import android.os.SystemProperties; 50import android.preference.PreferenceManager; 51import android.provider.Settings.System; 52import android.util.Config; 53import android.util.Log; 54import android.view.KeyEvent; 55import android.widget.Toast; 56 57/** 58 * Top-level Application class for the Phone app. 59 */ 60public class PhoneApp extends Application { 61 /* package */ static final String LOG_TAG = "PhoneApp"; 62 /* package */ static final boolean DBG = 63 (SystemProperties.getInt("ro.debuggable", 0) == 1); 64 65 // Message codes; see mHandler below. 66 private static final int EVENT_SIM_ABSENT = 1; 67 private static final int EVENT_SIM_LOCKED = 2; 68 private static final int EVENT_SIM_NETWORK_LOCKED = 3; 69 private static final int EVENT_WIRED_HEADSET_PLUG = 7; 70 private static final int EVENT_SIM_STATE_CHANGED = 8; 71 private static final int EVENT_UPDATE_INCALL_NOTIFICATION = 9; 72 private static final int EVENT_DATA_ROAMING_DISCONNECTED = 10; 73 private static final int EVENT_DATA_ROAMING_OK = 11; 74 75 // The MMI codes are also used by the InCallScreen. 76 public static final int MMI_INITIATE = 51; 77 public static final int MMI_COMPLETE = 52; 78 public static final int MMI_CANCEL = 53; 79 // Don't use message codes larger than 99 here; those are reserved for 80 // the individual Activities of the Phone UI. 81 82 /** 83 * Allowable values for the poke lock code (timeout between a user activity and the 84 * going to sleep), please refer to {@link com.android.server.PowerManagerService} 85 * for additional reference. 86 * SHORT uses the short delay for the timeout (SHORT_KEYLIGHT_DELAY, 6 sec) 87 * MEDIUM uses the medium delay for the timeout (MEDIUM_KEYLIGHT_DELAY, 15 sec) 88 * DEFAULT is the system-wide default delay for the timeout (1 min) 89 */ 90 public enum ScreenTimeoutDuration { 91 SHORT, 92 MEDIUM, 93 DEFAULT 94 } 95 96 /** 97 * Allowable values for the wake lock code. 98 * SLEEP means the device can be put to sleep. 99 * PARTIAL means wake the processor, but we display can be kept off. 100 * FULL means wake both the processor and the display. 101 */ 102 public enum WakeState { 103 SLEEP, 104 PARTIAL, 105 FULL 106 } 107 108 private static PhoneApp sMe; 109 110 // A few important fields we expose to the rest of the package 111 // directly (rather than thru set/get methods) for efficiency. 112 Phone phone; 113 CallNotifier notifier; 114 Ringer ringer; 115 BluetoothHandsfree mBtHandsfree; 116 PhoneInterfaceManager phoneMgr; 117 int mBluetoothHeadsetState = BluetoothHeadset.STATE_ERROR; 118 int mBluetoothHeadsetAudioState = BluetoothHeadset.STATE_ERROR; 119 boolean mShowBluetoothIndication = false; 120 121 // The InCallScreen instance (or null if the InCallScreen hasn't been 122 // created yet.) 123 private InCallScreen mInCallScreen; 124 125 // The currently-active PUK entry activity and progress dialog. 126 // Normally, these are the Emergency Dialer and the subsequent 127 // progress dialog. null if there is are no such objects in 128 // the foreground. 129 private Activity mPUKEntryActivity; 130 private ProgressDialog mPUKEntryProgressDialog; 131 132 private boolean mIsSimPinEnabled; 133 private String mCachedSimPin; 134 135 // True if a wired headset is currently plugged in, based on the state 136 // from the latest Intent.ACTION_HEADSET_PLUG broadcast we received in 137 // mReceiver.onReceive(). 138 private boolean mIsHeadsetPlugged; 139 140 private WakeState mWakeState = WakeState.SLEEP; 141 private ScreenTimeoutDuration mPokeLockSetting = ScreenTimeoutDuration.DEFAULT; 142 private IBinder mPokeLockToken = new Binder(); 143 private IPowerManager mPowerManagerService; 144 private PowerManager.WakeLock mWakeLock; 145 private PowerManager.WakeLock mPartialWakeLock; 146 private KeyguardManager mKeyguardManager; 147 private KeyguardManager.KeyguardLock mKeyguardLock; 148 149 // Broadcast receiver for various intent broadcasts (see onCreate()) 150 private final BroadcastReceiver mReceiver = new PhoneAppBroadcastReceiver(); 151 152 // Broadcast receiver purely for ACTION_MEDIA_BUTTON broadcasts 153 private final BroadcastReceiver mMediaButtonReceiver = new MediaButtonBroadcastReceiver(); 154 155 /** boolean indicating restoring mute state on InCallScreen.onResume() */ 156 private boolean mShouldRestoreMuteOnInCallResume; 157 158 /** 159 * Set the restore mute state flag. Used when we are setting the mute state 160 * OUTSIDE of user interaction {@link PhoneUtils#startNewCall(Phone)} 161 */ 162 /*package*/void setRestoreMuteOnInCallResume (boolean mode) { 163 mShouldRestoreMuteOnInCallResume = mode; 164 } 165 166 /** 167 * Get the restore mute state flag. 168 * This is used by the InCallScreen {@link InCallScreen#onResume()} to figure 169 * out if we need to restore the mute state for the current active call. 170 */ 171 /*package*/boolean getRestoreMuteOnInCallResume () { 172 return mShouldRestoreMuteOnInCallResume; 173 } 174 175 Handler mHandler = new Handler() { 176 @Override 177 public void handleMessage(Message msg) { 178 switch (msg.what) { 179 case EVENT_SIM_LOCKED: 180// mIsSimPinEnabled = true; 181// 182// if (Config.LOGV) Log.v(LOG_TAG, "show sim unlock panel"); 183// SimPinUnlockPanel pinUnlockPanel = new SimPinUnlockPanel( 184// PhoneApp.getInstance()); 185// pinUnlockPanel.show(); 186 break; 187 188 case EVENT_SIM_ABSENT: 189// Don't need this now that the lock screen handles this case 190// if (Config.LOGV) Log.v(LOG_TAG, "show sim missing panel"); 191// SimMissingPanel missingPanel = new SimMissingPanel( 192// PhoneApp.getInstance()); 193// missingPanel.show(); 194 break; 195 196 case EVENT_SIM_NETWORK_LOCKED: 197 if (Config.LOGV) Log.v(LOG_TAG, "show sim depersonal panel"); 198 SimNetworkDepersonalizationPanel ndpPanel = 199 new SimNetworkDepersonalizationPanel(PhoneApp.getInstance()); 200 ndpPanel.show(); 201 break; 202 203 case EVENT_UPDATE_INCALL_NOTIFICATION: 204 // Tell the NotificationMgr to update the "ongoing 205 // call" icon in the status bar, if necessary. 206 // Currently, this is triggered by a bluetooth headset 207 // state change (since the status bar icon needs to 208 // turn blue when bluetooth is active.) 209 NotificationMgr.getDefault().updateInCallNotification(); 210 break; 211 212 case EVENT_DATA_ROAMING_DISCONNECTED: 213 NotificationMgr.getDefault().showDataDisconnectedRoaming(); 214 break; 215 216 case EVENT_DATA_ROAMING_OK: 217 NotificationMgr.getDefault().hideDataDisconnectedRoaming(); 218 break; 219 220 case MMI_COMPLETE: 221 onMMIComplete((AsyncResult) msg.obj); 222 break; 223 224 case MMI_CANCEL: 225 PhoneUtils.cancelMmiCode(phone); 226 break; 227 228 case EVENT_WIRED_HEADSET_PLUG: 229 // Since the presence of a wired headset or bluetooth affects the 230 // speakerphone, update the "speaker" state. We ONLY want to do 231 // this on the wired headset connect / disconnect events for now 232 // though, so we're only triggering on EVENT_WIRED_HEADSET_PLUG. 233 if (!isHeadsetPlugged() && 234 (mBtHandsfree == null || !mBtHandsfree.isAudioOn())) { 235 // is the state is "not connected", restore the speaker state. 236 PhoneUtils.restoreSpeakerMode(getApplicationContext()); 237 } 238 NotificationMgr.getDefault().updateSpeakerNotification(); 239 break; 240 241 case EVENT_SIM_STATE_CHANGED: 242 // Marks the event where the SIM goes into ready state. 243 // Right now, this is only used for the PUK-unlocking 244 // process. 245 if (msg.obj.equals(SimCard.INTENT_VALUE_SIM_READY)) { 246 // when the right event is triggered and there 247 // are UI objects in the foreground, we close 248 // them to display the lock panel. 249 if (mPUKEntryActivity != null) { 250 mPUKEntryActivity.finish(); 251 mPUKEntryActivity = null; 252 } 253 if (mPUKEntryProgressDialog != null) { 254 mPUKEntryProgressDialog.dismiss(); 255 mPUKEntryProgressDialog = null; 256 } 257 } 258 break; 259 } 260 } 261 }; 262 263 public PhoneApp() { 264 sMe = this; 265 } 266 267 @Override 268 public void onCreate() { 269 if (Config.LOGV) Log.v(LOG_TAG, "onCreate()..."); 270 271 ContentResolver resolver = getContentResolver(); 272 273 if (phone == null) { 274 // Initialize the telephony framework 275 PhoneFactory.makeDefaultPhones(this); 276 277 // Get the default phone 278 phone = PhoneFactory.getDefaultPhone(); 279 280 NotificationMgr.init(this); 281 282 phoneMgr = new PhoneInterfaceManager(this, phone); 283 if (getSystemService(Context.BLUETOOTH_SERVICE) != null) { 284 mBtHandsfree = new BluetoothHandsfree(this, phone); 285 startService(new Intent(this, BluetoothHeadsetService.class)); 286 } else { 287 // Device is not bluetooth capable 288 mBtHandsfree = null; 289 } 290 291 ringer = new Ringer(phone); 292 293 SoundEffect.init(); 294 295 // before registering for phone state changes 296 PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE); 297 mWakeLock = pm.newWakeLock(PowerManager.FULL_WAKE_LOCK 298 | PowerManager.ACQUIRE_CAUSES_WAKEUP 299 | PowerManager.ON_AFTER_RELEASE, LOG_TAG); 300 // lock used to keep the processor awake, when we don't care for the display. 301 mPartialWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK 302 | PowerManager.ON_AFTER_RELEASE, LOG_TAG); 303 mKeyguardManager = (KeyguardManager) getSystemService(Context.KEYGUARD_SERVICE); 304 mKeyguardLock = mKeyguardManager.newKeyguardLock(LOG_TAG); 305 306 // get a handle to the service so that we can use it later when we 307 // want to set the poke lock. 308 mPowerManagerService = IPowerManager.Stub.asInterface( 309 ServiceManager.getService("power")); 310 311 notifier = new CallNotifier(this, phone, ringer, mBtHandsfree); 312 313 // register for SIM status 314 SimCard sim = phone.getSimCard(); 315 if (sim != null) { 316 if (Config.LOGV) Log.v(LOG_TAG, "register for SIM status"); 317 sim.registerForAbsent(mHandler, EVENT_SIM_ABSENT, null); 318 sim.registerForLocked(mHandler, EVENT_SIM_LOCKED, null); 319 sim.registerForNetworkLocked(mHandler, EVENT_SIM_NETWORK_LOCKED, null); 320 } 321 322 // register for MMI/USSD 323 phone.registerForMmiComplete(mHandler, MMI_COMPLETE, null); 324 325 // register connection tracking to PhoneUtils 326 PhoneUtils.initializeConnectionHandler(phone); 327 328 // Register for misc other intent broadcasts. 329 IntentFilter intentFilter = 330 new IntentFilter(Intent.ACTION_AIRPLANE_MODE_CHANGED); 331 intentFilter.addAction(BluetoothIntent.HEADSET_STATE_CHANGED_ACTION); 332 intentFilter.addAction(BluetoothIntent.HEADSET_AUDIO_STATE_CHANGED_ACTION); 333 intentFilter.addAction(TelephonyIntents.ACTION_ANY_DATA_CONNECTION_STATE_CHANGED); 334 intentFilter.addAction(Intent.ACTION_HEADSET_PLUG); 335 intentFilter.addAction(Intent.ACTION_BATTERY_LOW); 336 intentFilter.addAction(TelephonyIntents.ACTION_SIM_STATE_CHANGED); 337 registerReceiver(mReceiver, intentFilter); 338 339 // Use a separate receiver for ACTION_MEDIA_BUTTON broadcasts, 340 // since we need to manually adjust its priority (to make sure 341 // we get these intents *before* the media player.) 342 IntentFilter mediaButtonIntentFilter = 343 new IntentFilter(Intent.ACTION_MEDIA_BUTTON); 344 // 345 // Make sure we're higher priority than the media player's 346 // MediaButtonIntentReceiver (which currently has the default 347 // priority of zero; see apps/Music/AndroidManifest.xml.) 348 mediaButtonIntentFilter.setPriority(1); 349 // 350 registerReceiver(mMediaButtonReceiver, mediaButtonIntentFilter); 351 352 //set the default values for the preferences in the phone. 353 PreferenceManager.setDefaultValues(this, R.xml.network_setting, false); 354 PreferenceManager.setDefaultValues(this, R.xml.call_feature_setting, false); 355 356 // Make sure the audio mode (along with some 357 // audio-mode-related state of our own) is initialized 358 // correctly, given the current state of the phone. 359 switch (phone.getState()) { 360 case IDLE: 361 if (DBG) Log.d(LOG_TAG, "Resetting audio state/mode: IDLE"); 362 PhoneUtils.setAudioControlState(PhoneUtils.AUDIO_IDLE); 363 PhoneUtils.setAudioMode(this, AudioManager.MODE_NORMAL); 364 break; 365 case RINGING: 366 if (DBG) Log.d(LOG_TAG, "Resetting audio state/mode: RINGING"); 367 PhoneUtils.setAudioControlState(PhoneUtils.AUDIO_RINGING); 368 PhoneUtils.setAudioMode(this, AudioManager.MODE_RINGTONE); 369 break; 370 case OFFHOOK: 371 if (DBG) Log.d(LOG_TAG, "Resetting audio state/mode: OFFHOOK"); 372 PhoneUtils.setAudioControlState(PhoneUtils.AUDIO_OFFHOOK); 373 PhoneUtils.setAudioMode(this, AudioManager.MODE_IN_CALL); 374 break; 375 } 376 } 377 378 // XXX pre-load the SimProvider so that it's ready 379 resolver.getType(Uri.parse("content://sim/adn")); 380 381 // start with the default value to set the mute state. 382 mShouldRestoreMuteOnInCallResume = false; 383 } 384 385 /** 386 * Returns the singleton instance of the PhoneApp. 387 */ 388 static PhoneApp getInstance() { 389 return sMe; 390 } 391 392 Ringer getRinger() { 393 return ringer; 394 } 395 396 BluetoothHandsfree getBluetoothHandsfree() { 397 return mBtHandsfree; 398 } 399 400 static Intent createCallLogIntent() { 401 Intent intent = new Intent(Intent.ACTION_VIEW, null); 402 intent.setType("vnd.android.cursor.dir/calls"); 403 return intent; 404 } 405 406 /** 407 * Return an Intent that can be used to bring up the in-call screen. 408 * 409 * This intent can only be used from within the Phone app, since the 410 * InCallScreen is not exported from our AndroidManifest. 411 */ 412 /* package */ static Intent createInCallIntent() { 413 Intent intent = new Intent(Intent.ACTION_MAIN, null); 414 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK 415 | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS 416 | Intent.FLAG_ACTIVITY_NO_USER_ACTION); 417 intent.setClassName("com.android.phone", getCallScreenClassName()); 418 return intent; 419 } 420 421 /** 422 * Variation of createInCallIntent() that also specifies whether the 423 * DTMF dialpad should be initially visible when the InCallScreen 424 * comes up. 425 */ 426 /* package */ static Intent createInCallIntent(boolean showDialpad) { 427 Intent intent = createInCallIntent(); 428 intent.putExtra(InCallScreen.SHOW_DIALPAD_EXTRA, showDialpad); 429 return intent; 430 } 431 432 static String getCallScreenClassName() { 433 return InCallScreen.class.getName(); 434 } 435 436 /** 437 * Starts the InCallScreen Activity. 438 */ 439 void displayCallScreen() { 440 // if (DBG) Log.d(LOG_TAG, "displayCallScreen()...", new Throwable("stack dump")); 441 startActivity(createInCallIntent()); 442 Profiler.callScreenRequested(); 443 } 444 445 /** 446 * Helper function to check for one special feature of the CALL key: 447 * Normally, when the phone is idle, CALL takes you to the call log 448 * (see the handler for KEYCODE_CALL in PhoneWindow.onKeyUp().) 449 * But if the phone is in use (either off-hook or ringing) we instead 450 * handle the CALL button by taking you to the in-call UI. 451 * 452 * @return true if we intercepted the CALL keypress (i.e. the phone 453 * was in use) 454 * 455 * @see DialerActivity#onCreate 456 */ 457 boolean handleInCallOrRinging() { 458 if (phone.getState() != Phone.State.IDLE) { 459 // Phone is OFFHOOK or RINGING. 460 if (DBG) Log.v(LOG_TAG, 461 "handleInCallOrRinging: show call screen"); 462 displayCallScreen(); 463 return true; 464 } 465 return false; 466 } 467 468 boolean isSimPinEnabled() { 469 return mIsSimPinEnabled; 470 } 471 472 boolean authenticateAgainstCachedSimPin(String pin) { 473 return (mCachedSimPin != null && mCachedSimPin.equals(pin)); 474 } 475 476 void setCachedSimPin(String pin) { 477 mCachedSimPin = pin; 478 } 479 480 void setInCallScreenInstance(InCallScreen inCallScreen) { 481 mInCallScreen = inCallScreen; 482 } 483 484 /** 485 * @return true if the in-call UI is running as the foreground 486 * activity. (In other words, from the perspective of the 487 * InCallScreen activity, return true between onResume() and 488 * onPause().) 489 * 490 * Note this method will return false if the screen is currently off, 491 * even if the InCallScreen *was* in the foreground just before the 492 * screen turned off. (This is because the foreground activity is 493 * always "paused" while the screen is off.) 494 */ 495 boolean isShowingCallScreen() { 496 if (mInCallScreen == null) return false; 497 return mInCallScreen.isForegroundActivity(); 498 } 499 500 /** 501 * Dismisses the in-call UI. 502 * 503 * This also ensures that you won't be able to get back to the in-call 504 * UI via the BACK button (since this call removes the InCallScreen 505 * from the activity history.) 506 */ 507 void dismissCallScreen() { 508 if (mInCallScreen != null) { 509 mInCallScreen.finish(); 510 } 511 } 512 513 /** 514 * Sets the activity responsible for un-PUK-blocking the device 515 * so that we may close it when we receive a positive result. 516 * mPUKEntryActivity is also used to indicate to the device that 517 * we are trying to un-PUK-lock the phone. In other words, iff 518 * it is NOT null, then we are trying to unlock and waiting for 519 * the SIM to move to READY state. 520 * 521 * @param activity is the activity to close when PUK has 522 * finished unlocking. Can be set to null to indicate the unlock 523 * or SIM READYing process is over. 524 */ 525 void setPukEntryActivity(Activity activity) { 526 mPUKEntryActivity = activity; 527 } 528 529 Activity getPUKEntryActivity() { 530 return mPUKEntryActivity; 531 } 532 533 /** 534 * Sets the dialog responsible for notifying the user of un-PUK- 535 * blocking - SIM READYing progress, so that we may dismiss it 536 * when we receive a positive result. 537 * 538 * @param dialog indicates the progress dialog informing the user 539 * of the state of the device. Dismissed upon completion of 540 * READYing process 541 */ 542 void setPukEntryProgressDialog(ProgressDialog dialog) { 543 mPUKEntryProgressDialog = dialog; 544 } 545 546 ProgressDialog getPUKEntryProgressDialog() { 547 return mPUKEntryProgressDialog; 548 } 549 550 /** 551 * Disables the keyguard. This is used by the phone app to allow 552 * interaction with the Phone UI when the keyguard would otherwise be 553 * active (like receiving an incoming call while the device is 554 * locked.) 555 * 556 * Any call to this method MUST be followed (eventually) 557 * by a corresponding reenableKeyguard() call. 558 */ 559 /* package */ void disableKeyguard() { 560 if (DBG) Log.d(LOG_TAG, "disable keyguard"); 561 // if (DBG) Log.d(LOG_TAG, "disableKeyguard()...", new Throwable("stack dump")); 562 mKeyguardLock.disableKeyguard(); 563 } 564 565 /** 566 * Re-enables the keyguard after a previous disableKeyguard() call. 567 * 568 * Any call to this method MUST correspond to (i.e. be balanced with) 569 * a previous disableKeyguard() call. 570 */ 571 /* package */ void reenableKeyguard() { 572 if (DBG) Log.d(LOG_TAG, "re-enable keyguard"); 573 // if (DBG) Log.d(LOG_TAG, "reenableKeyguard()...", new Throwable("stack dump")); 574 mKeyguardLock.reenableKeyguard(); 575 } 576 577 /** 578 * Controls how quickly the screen times out. 579 * 580 * The poke lock controls how long it takes before the screen powers 581 * down, and therefore has no immediate effect when the current 582 * WakeState (see {@link PhoneApp#requestWakeState}) is FULL. 583 * If we're in a state where the screen *is* allowed to turn off, 584 * though, the poke lock will determine the timeout interval (long or 585 * short). 586 * 587 * @param shortPokeLock tells the device the timeout duration to use 588 * before going to sleep 589 * {@link com.android.server.PowerManagerService#SHORT_KEYLIGHT_DELAY}. 590 */ 591 /* package */ void setScreenTimeout(ScreenTimeoutDuration duration) { 592 if (DBG) Log.d(LOG_TAG, "setScreenTimeout(" + duration + ")..."); 593 594 // make sure we don't set the poke lock repeatedly so that we 595 // avoid triggering the userActivity calls in 596 // PowerManagerService.setPokeLock(). 597 if (duration == mPokeLockSetting) { 598 return; 599 } 600 mPokeLockSetting = duration; 601 602 // This is kind of convoluted, but the basic thing to remember is 603 // that the poke lock just sends a message to the screen to tell 604 // it to stay on for a while. 605 // The default is 0, for a long timeout and should be set that way 606 // when we are heading back into a the keyguard / screen off 607 // state, and also when we're trying to keep the screen alive 608 // while ringing. We'll also want to ignore the cheek events 609 // regardless of the timeout duration. 610 // The short timeout is really used whenever we want to give up 611 // the screen lock, such as when we're in call. 612 int pokeLockSetting = LocalPowerManager.POKE_LOCK_IGNORE_CHEEK_EVENTS; 613 switch (duration) { 614 case SHORT: 615 // Set the poke lock to timeout the display after a short 616 // timeout (5s). This ensures that the screen goes to sleep 617 // as soon as acceptably possible after we the wake lock 618 // has been released. 619 if (DBG) Log.d(LOG_TAG, "setting short poke lock"); 620 pokeLockSetting |= LocalPowerManager.POKE_LOCK_SHORT_TIMEOUT; 621 break; 622 623 case MEDIUM: 624 // Set the poke lock to timeout the display after a medium 625 // timeout (15s). This ensures that the screen goes to sleep 626 // as soon as acceptably possible after we the wake lock 627 // has been released. 628 if (DBG) Log.d(LOG_TAG, "setting medium poke lock"); 629 pokeLockSetting |= LocalPowerManager.POKE_LOCK_MEDIUM_TIMEOUT; 630 break; 631 632 case DEFAULT: 633 default: 634 // set the poke lock to timeout the display after a long 635 // delay by default. 636 // TODO: it may be nice to be able to disable cheek presses 637 // for long poke locks (emergency dialer, for instance). 638 if (DBG) Log.d(LOG_TAG, "reverting to normal long poke lock"); 639 break; 640 } 641 642 // Send the request 643 try { 644 mPowerManagerService.setPokeLock(pokeLockSetting, mPokeLockToken, LOG_TAG); 645 } catch (RemoteException e) { 646 } 647 } 648 649 /** 650 * Controls whether or not the screen is allowed to sleep. 651 * 652 * Once sleep is allowed (WakeState is SLEEP), it will rely on the 653 * settings for the poke lock to determine when to timeout and let 654 * the device sleep {@link PhoneApp#setScreenTimeout}. 655 * 656 * @param ws tells the device to how to wake. 657 */ 658 /* package */ void requestWakeState(WakeState ws) { 659 if (DBG) Log.d(LOG_TAG, "requestWakeState(" + ws + ")..."); 660 if (mWakeState != ws) { 661 switch (ws) { 662 case PARTIAL: 663 // acquire the processor wake lock, and release the FULL 664 // lock if it is being held. 665 if (DBG) Log.d(LOG_TAG, "acquire partial wake lock (CPU only)"); 666 mPartialWakeLock.acquire(); 667 if (mWakeLock.isHeld()) { 668 mWakeLock.release(); 669 } 670 break; 671 case FULL: 672 // acquire the full wake lock, and release the PARTIAL 673 // lock if it is being held. 674 if (DBG) Log.d(LOG_TAG, "acquire full wake lock (CPU + Screen)"); 675 mWakeLock.acquire(); 676 if (mPartialWakeLock.isHeld()) { 677 mPartialWakeLock.release(); 678 } 679 break; 680 case SLEEP: 681 default: 682 // release both the PARTIAL and FULL locks. 683 if (DBG) Log.d(LOG_TAG, "release all wake locks"); 684 if (mWakeLock.isHeld()) { 685 mWakeLock.release(); 686 } 687 if (mPartialWakeLock.isHeld()) { 688 mPartialWakeLock.release(); 689 } 690 break; 691 } 692 mWakeState = ws; 693 } 694 } 695 696 /** 697 * If we are not currently keeping the screen on, then poke the power 698 * manager to wake up the screen for the user activity timeout duration. 699 */ 700 /* package */ void wakeUpScreen() { 701 if (mWakeState == WakeState.SLEEP) { 702 if (DBG) Log.d(LOG_TAG, "pulse screen lock"); 703 try { 704 mPowerManagerService.userActivityWithForce(SystemClock.uptimeMillis(), false, true); 705 } catch (RemoteException ex) { 706 // Ignore -- the system process is dead. 707 } 708 } 709 } 710 711 /** 712 * Sets the wake state and screen timeout based on the current state 713 * of the phone, and the current state of the in-call UI. 714 * 715 * This method is a "UI Policy" wrapper around 716 * {@link PhoneApp#requestWakeState} and {@link PhoneApp#setScreenTimeout}. 717 * 718 * It's safe to call this method regardless of the state of the Phone 719 * (e.g. whether or not it's idle), and regardless of the state of the 720 * Phone UI (e.g. whether or not the InCallScreen is active.) 721 */ 722 /* package */ void updateWakeState() { 723 Phone.State state = phone.getState(); 724 725 // True if the in-call UI is the foreground activity. 726 // (Note this will be false if the screen is currently off, 727 // since in that case *no* activity is in the foreground.) 728 boolean isShowingCallScreen = isShowingCallScreen(); 729 730 // True if the InCallScreen's DTMF dialer is currently opened. 731 // (Note this does NOT imply whether or not the InCallScreen 732 // itself is visible.) 733 boolean isDialerOpened = (mInCallScreen != null) && mInCallScreen.isDialerOpened(); 734 735 // True if the speakerphone is in use. (If so, we *always* use 736 // the default timeout. Since the user is obviously not holding 737 // the phone up to his/her face, we don't need to worry about 738 // false touches, and thus don't need to turn the screen off so 739 // aggressively.) 740 // Note that we need to make a fresh call to this method any 741 // time the speaker state changes. (That happens in 742 // PhoneUtils.turnOnSpeaker().) 743 boolean isSpeakerInUse = (state == Phone.State.OFFHOOK) && PhoneUtils.isSpeakerOn(this); 744 745 // TODO (bug 1440854): The screen timeout *might* also need to 746 // depend on the bluetooth state, but this isn't as clear-cut as 747 // the speaker state (since while using BT it's common for the 748 // user to put the phone straight into a pocket, in which case the 749 // timeout should probably still be short.) 750 751 if (DBG) Log.d(LOG_TAG, "updateWakeState: isShowingCallScreen " + isShowingCallScreen 752 + ", isDialerOpened " + isDialerOpened 753 + ", isSpeakerInUse " + isSpeakerInUse + "..."); 754 755 // 756 // (1) Set the screen timeout. 757 // 758 // Note that the "screen timeout" value we determine here is 759 // meaningless if the screen is forced on (see (2) below.) 760 // 761 if (!isShowingCallScreen || isSpeakerInUse) { 762 // Use the system-wide default timeout. 763 setScreenTimeout(ScreenTimeoutDuration.DEFAULT); 764 } else { 765 // We're on the in-call screen, and *not* using the speakerphone. 766 if (isDialerOpened) { 767 // The DTMF dialpad is up. This case is special because 768 // the in-call UI has its own "touch lock" mechanism to 769 // disable the dialpad after a very short amount of idle 770 // time (to avoid false touches from the user's face while 771 // in-call.) 772 // 773 // In this case the *physical* screen just uses the 774 // system-wide default timeout. 775 setScreenTimeout(ScreenTimeoutDuration.DEFAULT); 776 } else { 777 // We're on the in-call screen, and not using the DTMF dialpad. 778 // There's actually no touchable UI onscreen at all in 779 // this state. Also, the user is (most likely) not 780 // looking at the screen at all, since they're probably 781 // holding the phone up to their face. Here we use a 782 // special screen timeout value specific to the in-call 783 // screen, purely to save battery life. 784 setScreenTimeout(ScreenTimeoutDuration.MEDIUM); 785 } 786 } 787 788 // 789 // (2) Decide whether to force the screen on or not. 790 // 791 // Force the screen to be on if the phone is ringing, or if we're 792 // displaying the "Call ended" UI for a connection in the 793 // "disconnected" state. 794 // 795 boolean isRinging = (state == Phone.State.RINGING); 796 boolean showingDisconnectedConnection = 797 PhoneUtils.hasDisconnectedConnections(phone) && isShowingCallScreen; 798 boolean keepScreenOn = isRinging || showingDisconnectedConnection; 799 if (DBG) Log.d(LOG_TAG, "updateWakeState: keepScreenOn = " + keepScreenOn 800 + " (isRinging " + isRinging 801 + ", showingDisconnectedConnection " + showingDisconnectedConnection + ")"); 802 // keepScreenOn == true means we'll hold a full wake lock: 803 requestWakeState(keepScreenOn ? WakeState.FULL : WakeState.SLEEP); 804 } 805 806 /** 807 * Wrapper around the PowerManagerService.preventScreenOn() API. 808 * This allows the in-call UI to prevent the screen from turning on 809 * even if a subsequent call to updateWakeState() causes us to acquire 810 * a full wake lock. 811 */ 812 /* package */ void preventScreenOn(boolean prevent) { 813 if (DBG) Log.d(LOG_TAG, "- preventScreenOn(" + prevent + ")..."); 814 try { 815 mPowerManagerService.preventScreenOn(prevent); 816 } catch (RemoteException e) { 817 Log.w(LOG_TAG, "mPowerManagerService.preventScreenOn() failed: " + e); 818 } 819 } 820 821 KeyguardManager getKeyguardManager() { 822 return mKeyguardManager; 823 } 824 825 private void onMMIComplete(AsyncResult r) { 826 if (DBG) Log.d(LOG_TAG, "onMMIComplete()..."); 827 MmiCode mmiCode = (MmiCode) r.result; 828 PhoneUtils.displayMMIComplete(phone, getInstance(), mmiCode, null, null); 829 } 830 831 /** 832 * @return true if a wired headset is currently plugged in. 833 * 834 * @see Intent.ACTION_HEADSET_PLUG (which we listen for in mReceiver.onReceive()) 835 */ 836 boolean isHeadsetPlugged() { 837 return mIsHeadsetPlugged; 838 } 839 840 /** 841 * @return true if the onscreen UI should currently be showing the 842 * special "bluetooth is active" indication in a couple of places (in 843 * which UI elements turn blue and/or show the bluetooth logo.) 844 * 845 * This depends on the BluetoothHeadset state *and* the current 846 * telephony state; see shouldShowBluetoothIndication(). 847 * 848 * @see CallCard 849 * @see NotificationMgr.updateInCallNotification 850 */ 851 /* package */ boolean showBluetoothIndication() { 852 return mShowBluetoothIndication; 853 } 854 855 /** 856 * Recomputes the mShowBluetoothIndication flag based on the current 857 * bluetooth state and current telephony state. 858 * 859 * This needs to be called any time the bluetooth headset state or the 860 * telephony state changes. 861 * 862 * @param forceUiUpdate if true, force the UI elements that care 863 * about this flag to update themselves. 864 */ 865 /* package */ void updateBluetoothIndication(boolean forceUiUpdate) { 866 mShowBluetoothIndication = shouldShowBluetoothIndication(mBluetoothHeadsetState, 867 mBluetoothHeadsetAudioState, 868 phone); 869 if (forceUiUpdate) { 870 // Post Handler messages to the various components that might 871 // need to be refreshed based on the new state. 872 if (isShowingCallScreen()) mInCallScreen.updateBluetoothIndication(); 873 mHandler.sendEmptyMessage(EVENT_UPDATE_INCALL_NOTIFICATION); 874 } 875 } 876 877 /** 878 * UI policy helper function for the couple of places in the UI that 879 * have some way of indicating that "bluetooth is in use." 880 * 881 * @return true if the onscreen UI should indicate that "bluetooth is in use", 882 * based on the specified bluetooth headset state, and the 883 * current state of the phone. 884 * @see showBluetoothIndication() 885 */ 886 private static boolean shouldShowBluetoothIndication(int bluetoothState, 887 int bluetoothAudioState, 888 Phone phone) { 889 // We want the UI to indicate that "bluetooth is in use" in two 890 // slightly different cases: 891 // 892 // (a) The obvious case: if a bluetooth headset is currently in 893 // use for an ongoing call. 894 // 895 // (b) The not-so-obvious case: if an incoming call is ringing, 896 // and we expect that audio *will* be routed to a bluetooth 897 // headset once the call is answered. 898 899 switch (phone.getState()) { 900 case OFFHOOK: 901 // This covers normal active calls, and also the case if 902 // the foreground call is DIALING or ALERTING. In this 903 // case, bluetooth is considered "active" if a headset 904 // is connected *and* audio is being routed to it. 905 return ((bluetoothState == BluetoothHeadset.STATE_CONNECTED) 906 && (bluetoothAudioState == BluetoothHeadset.AUDIO_STATE_CONNECTED)); 907 908 case RINGING: 909 // If an incoming call is ringing, we're *not* yet routing 910 // audio to the headset (since there's no in-call audio 911 // yet!) In this case, if a bluetooth headset is 912 // connected at all, we assume that it'll become active 913 // once the user answers the phone. 914 return (bluetoothState == BluetoothHeadset.STATE_CONNECTED); 915 916 default: // Presumably IDLE 917 return false; 918 } 919 } 920 921 922 /** 923 * Receiver for misc intent broadcasts the Phone app cares about. 924 */ 925 private class PhoneAppBroadcastReceiver extends BroadcastReceiver { 926 @Override 927 public void onReceive(Context context, Intent intent) { 928 String action = intent.getAction(); 929 if (action.equals(Intent.ACTION_AIRPLANE_MODE_CHANGED)) { 930 boolean enabled = System.getInt(getContentResolver(), 931 System.AIRPLANE_MODE_ON, 0) == 0; 932 phone.setRadioPower(enabled); 933 } else if (action.equals(BluetoothIntent.HEADSET_STATE_CHANGED_ACTION)) { 934 mBluetoothHeadsetState = intent.getIntExtra(BluetoothIntent.HEADSET_STATE, 935 BluetoothHeadset.STATE_ERROR); 936 if (DBG) Log.d(LOG_TAG, "mReceiver: HEADSET_STATE_CHANGED_ACTION"); 937 if (DBG) Log.d(LOG_TAG, "==> new state: " + mBluetoothHeadsetState); 938 updateBluetoothIndication(true); // Also update any visible UI if necessary 939 } else if (action.equals(BluetoothIntent.HEADSET_AUDIO_STATE_CHANGED_ACTION)) { 940 mBluetoothHeadsetAudioState = 941 intent.getIntExtra(BluetoothIntent.HEADSET_AUDIO_STATE, 942 BluetoothHeadset.STATE_ERROR); 943 if (DBG) Log.d(LOG_TAG, "mReceiver: HEADSET_AUDIO_STATE_CHANGED_ACTION"); 944 if (DBG) Log.d(LOG_TAG, "==> new state: " + mBluetoothHeadsetAudioState); 945 updateBluetoothIndication(true); // Also update any visible UI if necessary 946 } else if (action.equals(TelephonyIntents.ACTION_ANY_DATA_CONNECTION_STATE_CHANGED)) { 947 // if (DBG) Log.d(LOG_TAG, "mReceiver: ACTION_ANY_DATA_CONNECTION_STATE_CHANGED"); 948 // if (DBG) Log.d(LOG_TAG, "- state: " + intent.getStringExtra(Phone.STATE_KEY)); 949 // if (DBG) Log.d(LOG_TAG, "- reason: " 950 // + intent.getStringExtra(Phone.STATE_CHANGE_REASON_KEY)); 951 952 // The "data disconnected due to roaming" notification is 953 // visible if you've lost data connectivity because you're 954 // roaming and you have the "data roaming" feature turned off. 955 boolean disconnectedDueToRoaming = false; 956 if ("DISCONNECTED".equals(intent.getStringExtra(Phone.STATE_KEY))) { 957 String reason = intent.getStringExtra(Phone.STATE_CHANGE_REASON_KEY); 958 if (Phone.REASON_ROAMING_ON.equals(reason)) { 959 // We just lost our data connection, and the reason 960 // is that we started roaming. This implies that 961 // the user has data roaming turned off. 962 disconnectedDueToRoaming = true; 963 } 964 } 965 mHandler.sendEmptyMessage(disconnectedDueToRoaming 966 ? EVENT_DATA_ROAMING_DISCONNECTED 967 : EVENT_DATA_ROAMING_OK); 968 } else if (action.equals(Intent.ACTION_HEADSET_PLUG)) { 969 if (DBG) Log.d(LOG_TAG, "mReceiver: ACTION_HEADSET_PLUG"); 970 if (DBG) Log.d(LOG_TAG, " state: " + intent.getIntExtra("state", 0)); 971 if (DBG) Log.d(LOG_TAG, " name: " + intent.getStringExtra("name")); 972 mIsHeadsetPlugged = (intent.getIntExtra("state", 0) == 1); 973 mHandler.sendMessage(mHandler.obtainMessage(EVENT_WIRED_HEADSET_PLUG, 0)); 974 } else if (action.equals(Intent.ACTION_BATTERY_LOW)) { 975 if (DBG) Log.d(LOG_TAG, "mReceiver: ACTION_BATTERY_LOW"); 976 notifier.sendBatteryLow(); // Play a warning tone if in-call 977 } else if ((action.equals(TelephonyIntents.ACTION_SIM_STATE_CHANGED)) && 978 (mPUKEntryActivity != null)) { 979 // if an attempt to un-PUK-lock the device was made, while we're 980 // receiving this state change notification, notify the handler. 981 // NOTE: This is ONLY triggered if an attempt to un-PUK-lock has 982 // been attempted. 983 mHandler.sendMessage(mHandler.obtainMessage(EVENT_SIM_STATE_CHANGED, 984 intent.getStringExtra(SimCard.INTENT_KEY_SIM_STATE))); 985 } 986 } 987 } 988 989 /** 990 * Broadcast receiver for the ACTION_MEDIA_BUTTON broadcast intent. 991 * 992 * This functionality isn't lumped in with the other intents in 993 * PhoneAppBroadcastReceiver because we instantiate this as a totally 994 * separate BroadcastReceiver instance, since we need to manually 995 * adjust its IntentFilter's priority (to make sure we get these 996 * intents *before* the media player.) 997 */ 998 private class MediaButtonBroadcastReceiver extends BroadcastReceiver { 999 @Override 1000 public void onReceive(Context context, Intent intent) { 1001 KeyEvent event = (KeyEvent) intent.getParcelableExtra(Intent.EXTRA_KEY_EVENT); 1002 if (DBG) Log.d(LOG_TAG, 1003 "MediaButtonBroadcastReceiver.onReceive()... event = " + event); 1004 if ((event != null) 1005 && (event.getKeyCode() == KeyEvent.KEYCODE_HEADSETHOOK) 1006 && (event.getAction() == KeyEvent.ACTION_DOWN)) { 1007 1008 if (event.getRepeatCount() == 0) { 1009 // Mute ONLY on the initial keypress. 1010 if (DBG) Log.d(LOG_TAG, "MediaButtonBroadcastReceiver: HEADSETHOOK down!"); 1011 boolean consumed = PhoneUtils.handleHeadsetHook(phone); 1012 if (DBG) Log.d(LOG_TAG, "==> called handleHeadsetHook(), consumed = " + consumed); 1013 if (consumed) { 1014 abortBroadcast(); 1015 } 1016 } else if (phone.getState() != Phone.State.IDLE) { 1017 // As for any DOWN events other than the initial press, we consume 1018 // (and ignore) those too if the phone is in use. (Otherwise the 1019 // music player will handle them, which would be confusing.) 1020 abortBroadcast(); 1021 } 1022 } 1023 } 1024 } 1025} 1026