PhoneApp.java revision ba681d3ccc712991a2a7558b2bb5d8dbb4fcc464
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 android.app.Activity; 20import android.app.Application; 21import android.app.KeyguardManager; 22import android.app.ProgressDialog; 23import android.app.StatusBarManager; 24import android.bluetooth.BluetoothAdapter; 25import android.bluetooth.BluetoothHeadset; 26import android.content.BroadcastReceiver; 27import android.content.ContentResolver; 28import android.content.Context; 29import android.content.Intent; 30import android.content.IntentFilter; 31import android.content.res.Configuration; 32import android.media.AudioManager; 33import android.net.Uri; 34import android.os.AsyncResult; 35import android.os.Binder; 36import android.os.Handler; 37import android.os.IBinder; 38import android.os.IPowerManager; 39import android.os.LocalPowerManager; 40import android.os.Message; 41import android.os.PowerManager; 42import android.os.RemoteException; 43import android.os.ServiceManager; 44import android.os.SystemClock; 45import android.os.SystemProperties; 46import android.preference.PreferenceManager; 47import android.provider.Settings.System; 48import android.telephony.ServiceState; 49import android.util.Config; 50import android.util.Log; 51import android.view.KeyEvent; 52import android.widget.Toast; 53 54import com.android.internal.telephony.Call; 55import com.android.internal.telephony.CallManager; 56import com.android.internal.telephony.IccCard; 57import com.android.internal.telephony.MmiCode; 58import com.android.internal.telephony.Phone; 59import com.android.internal.telephony.PhoneFactory; 60import com.android.internal.telephony.TelephonyIntents; 61import com.android.internal.telephony.cdma.EriInfo; 62import com.android.phone.OtaUtils.CdmaOtaScreenState; 63import com.android.internal.telephony.cdma.TtyIntent; 64import com.android.internal.telephony.sip.SipPhoneFactory; 65 66/** 67 * Top-level Application class for the Phone app. 68 */ 69public class PhoneApp extends Application implements AccelerometerListener.OrientationListener { 70 /* package */ static final String LOG_TAG = "PhoneApp"; 71 72 /** 73 * Phone app-wide debug level: 74 * 0 - no debug logging 75 * 1 - normal debug logging if ro.debuggable is set (which is true in 76 * "eng" and "userdebug" builds but not "user" builds) 77 * 2 - ultra-verbose debug logging 78 * 79 * Most individual classes in the phone app have a local DBG constant, 80 * typically set to 81 * (PhoneApp.DBG_LEVEL >= 1) && (SystemProperties.getInt("ro.debuggable", 0) == 1) 82 * or else 83 * (PhoneApp.DBG_LEVEL >= 2) 84 * depending on the desired verbosity. 85 */ 86 /* package */ static final int DBG_LEVEL = 1; 87 88 private static final boolean DBG = 89 (PhoneApp.DBG_LEVEL >= 1) && (SystemProperties.getInt("ro.debuggable", 0) == 1); 90 private static final boolean VDBG = (PhoneApp.DBG_LEVEL >= 2); 91 92 // Message codes; see mHandler below. 93 private static final int EVENT_SIM_NETWORK_LOCKED = 3; 94 private static final int EVENT_WIRED_HEADSET_PLUG = 7; 95 private static final int EVENT_SIM_STATE_CHANGED = 8; 96 private static final int EVENT_UPDATE_INCALL_NOTIFICATION = 9; 97 private static final int EVENT_DATA_ROAMING_DISCONNECTED = 10; 98 private static final int EVENT_DATA_ROAMING_OK = 11; 99 private static final int EVENT_UNSOL_CDMA_INFO_RECORD = 12; 100 private static final int EVENT_DOCK_STATE_CHANGED = 13; 101 private static final int EVENT_TTY_PREFERRED_MODE_CHANGED = 14; 102 private static final int EVENT_TTY_MODE_GET = 15; 103 private static final int EVENT_TTY_MODE_SET = 16; 104 105 // The MMI codes are also used by the InCallScreen. 106 public static final int MMI_INITIATE = 51; 107 public static final int MMI_COMPLETE = 52; 108 public static final int MMI_CANCEL = 53; 109 // Don't use message codes larger than 99 here; those are reserved for 110 // the individual Activities of the Phone UI. 111 112 /** 113 * Allowable values for the poke lock code (timeout between a user activity and the 114 * going to sleep), please refer to {@link com.android.server.PowerManagerService} 115 * for additional reference. 116 * SHORT uses the short delay for the timeout (SHORT_KEYLIGHT_DELAY, 6 sec) 117 * MEDIUM uses the medium delay for the timeout (MEDIUM_KEYLIGHT_DELAY, 15 sec) 118 * DEFAULT is the system-wide default delay for the timeout (1 min) 119 */ 120 public enum ScreenTimeoutDuration { 121 SHORT, 122 MEDIUM, 123 DEFAULT 124 } 125 126 /** 127 * Allowable values for the wake lock code. 128 * SLEEP means the device can be put to sleep. 129 * PARTIAL means wake the processor, but we display can be kept off. 130 * FULL means wake both the processor and the display. 131 */ 132 public enum WakeState { 133 SLEEP, 134 PARTIAL, 135 FULL 136 } 137 138 private static PhoneApp sMe; 139 140 // A few important fields we expose to the rest of the package 141 // directly (rather than thru set/get methods) for efficiency. 142 Phone phone; 143 CallNotifier notifier; 144 Ringer ringer; 145 BluetoothHandsfree mBtHandsfree; 146 PhoneInterfaceManager phoneMgr; 147 CallManager mCM; 148 int mBluetoothHeadsetState = BluetoothHeadset.STATE_ERROR; 149 int mBluetoothHeadsetAudioState = BluetoothHeadset.STATE_ERROR; 150 boolean mShowBluetoothIndication = false; 151 static int mDockState = Intent.EXTRA_DOCK_STATE_UNDOCKED; 152 153 // Internal PhoneApp Call state tracker 154 CdmaPhoneCallState cdmaPhoneCallState; 155 156 // The InCallScreen instance (or null if the InCallScreen hasn't been 157 // created yet.) 158 private InCallScreen mInCallScreen; 159 160 // The currently-active PUK entry activity and progress dialog. 161 // Normally, these are the Emergency Dialer and the subsequent 162 // progress dialog. null if there is are no such objects in 163 // the foreground. 164 private Activity mPUKEntryActivity; 165 private ProgressDialog mPUKEntryProgressDialog; 166 167 private boolean mIsSimPinEnabled; 168 private String mCachedSimPin; 169 170 // True if a wired headset is currently plugged in, based on the state 171 // from the latest Intent.ACTION_HEADSET_PLUG broadcast we received in 172 // mReceiver.onReceive(). 173 private boolean mIsHeadsetPlugged; 174 175 // True if the keyboard is currently *not* hidden 176 // Gets updated whenever there is a Configuration change 177 private boolean mIsHardKeyboardOpen; 178 179 // True if we are beginning a call, but the phone state has not changed yet 180 private boolean mBeginningCall; 181 182 // Last phone state seen by updatePhoneState() 183 Phone.State mLastPhoneState = Phone.State.IDLE; 184 185 private WakeState mWakeState = WakeState.SLEEP; 186 private ScreenTimeoutDuration mScreenTimeoutDuration = ScreenTimeoutDuration.DEFAULT; 187 private boolean mIgnoreTouchUserActivity = false; 188 private IBinder mPokeLockToken = new Binder(); 189 private IPowerManager mPowerManagerService; 190 private PowerManager.WakeLock mWakeLock; 191 private PowerManager.WakeLock mPartialWakeLock; 192 private PowerManager.WakeLock mProximityWakeLock; 193 private KeyguardManager mKeyguardManager; 194 private StatusBarManager mStatusBarManager; 195 private int mStatusBarDisableCount; 196 private AccelerometerListener mAccelerometerListener; 197 private int mOrientation = AccelerometerListener.ORIENTATION_UNKNOWN; 198 199 // Broadcast receiver for various intent broadcasts (see onCreate()) 200 private final BroadcastReceiver mReceiver = new PhoneAppBroadcastReceiver(); 201 202 // Broadcast receiver purely for ACTION_MEDIA_BUTTON broadcasts 203 private final BroadcastReceiver mMediaButtonReceiver = new MediaButtonBroadcastReceiver(); 204 205 /** boolean indicating restoring mute state on InCallScreen.onResume() */ 206 private boolean mShouldRestoreMuteOnInCallResume; 207 208 // Following are the CDMA OTA information Objects used during OTA Call. 209 // cdmaOtaProvisionData object store static OTA information that needs 210 // to be maintained even during Slider open/close scenarios. 211 // cdmaOtaConfigData object stores configuration info to control visiblity 212 // of each OTA Screens. 213 // cdmaOtaScreenState object store OTA Screen State information. 214 public OtaUtils.CdmaOtaProvisionData cdmaOtaProvisionData; 215 public OtaUtils.CdmaOtaConfigData cdmaOtaConfigData; 216 public OtaUtils.CdmaOtaScreenState cdmaOtaScreenState; 217 public OtaUtils.CdmaOtaInCallScreenUiState cdmaOtaInCallScreenUiState; 218 219 // TTY feature enabled on this platform 220 private boolean mTtyEnabled; 221 // Current TTY operating mode selected by user 222 private int mPreferredTtyMode = Phone.TTY_MODE_OFF; 223 224 /** 225 * Set the restore mute state flag. Used when we are setting the mute state 226 * OUTSIDE of user interaction {@link PhoneUtils#startNewCall(Phone)} 227 */ 228 /*package*/void setRestoreMuteOnInCallResume (boolean mode) { 229 mShouldRestoreMuteOnInCallResume = mode; 230 } 231 232 /** 233 * Get the restore mute state flag. 234 * This is used by the InCallScreen {@link InCallScreen#onResume()} to figure 235 * out if we need to restore the mute state for the current active call. 236 */ 237 /*package*/boolean getRestoreMuteOnInCallResume () { 238 return mShouldRestoreMuteOnInCallResume; 239 } 240 241 Handler mHandler = new Handler() { 242 @Override 243 public void handleMessage(Message msg) { 244 Phone.State phoneState; 245 switch (msg.what) { 246 247 // TODO: This event should be handled by the lock screen, just 248 // like the "SIM missing" and "Sim locked" cases (bug 1804111). 249 case EVENT_SIM_NETWORK_LOCKED: 250 if (getResources().getBoolean(R.bool.ignore_sim_network_locked_events)) { 251 // Some products don't have the concept of a "SIM network lock" 252 Log.i(LOG_TAG, "Ignoring EVENT_SIM_NETWORK_LOCKED event; " 253 + "not showing 'SIM network unlock' PIN entry screen"); 254 } else { 255 // Normal case: show the "SIM network unlock" PIN entry screen. 256 // The user won't be able to do anything else until 257 // they enter a valid SIM network PIN. 258 Log.i(LOG_TAG, "show sim depersonal panel"); 259 IccNetworkDepersonalizationPanel ndpPanel = 260 new IccNetworkDepersonalizationPanel(PhoneApp.getInstance()); 261 ndpPanel.show(); 262 } 263 break; 264 265 case EVENT_UPDATE_INCALL_NOTIFICATION: 266 // Tell the NotificationMgr to update the "ongoing 267 // call" icon in the status bar, if necessary. 268 // Currently, this is triggered by a bluetooth headset 269 // state change (since the status bar icon needs to 270 // turn blue when bluetooth is active.) 271 if (DBG) Log.d (LOG_TAG, "- updating in-call notification from handler..."); 272 NotificationMgr.getDefault().updateInCallNotification(); 273 break; 274 275 case EVENT_DATA_ROAMING_DISCONNECTED: 276 NotificationMgr.getDefault().showDataDisconnectedRoaming(); 277 break; 278 279 case EVENT_DATA_ROAMING_OK: 280 NotificationMgr.getDefault().hideDataDisconnectedRoaming(); 281 break; 282 283 case MMI_COMPLETE: 284 onMMIComplete((AsyncResult) msg.obj); 285 break; 286 287 case MMI_CANCEL: 288 PhoneUtils.cancelMmiCode(phone); 289 break; 290 291 case EVENT_WIRED_HEADSET_PLUG: 292 // Since the presence of a wired headset or bluetooth affects the 293 // speakerphone, update the "speaker" state. We ONLY want to do 294 // this on the wired headset connect / disconnect events for now 295 // though, so we're only triggering on EVENT_WIRED_HEADSET_PLUG. 296 297 phoneState = mCM.getState(); 298 // Do not change speaker state if phone is not off hook 299 if (phoneState == Phone.State.OFFHOOK) { 300 if (mBtHandsfree == null || !mBtHandsfree.isAudioOn()) { 301 if (!isHeadsetPlugged()) { 302 // if the state is "not connected", restore the speaker state. 303 PhoneUtils.restoreSpeakerMode(getApplicationContext()); 304 } else { 305 // if the state is "connected", force the speaker off without 306 // storing the state. 307 PhoneUtils.turnOnSpeaker(getApplicationContext(), false, false); 308 } 309 } 310 } 311 // Update the Proximity sensor based on headset state 312 updateProximitySensorMode(phoneState); 313 314 // Force TTY state update according to new headset state 315 if (mTtyEnabled) { 316 sendMessage(obtainMessage(EVENT_TTY_PREFERRED_MODE_CHANGED, 0)); 317 } 318 break; 319 320 case EVENT_SIM_STATE_CHANGED: 321 // Marks the event where the SIM goes into ready state. 322 // Right now, this is only used for the PUK-unlocking 323 // process. 324 if (msg.obj.equals(IccCard.INTENT_VALUE_ICC_READY)) { 325 // when the right event is triggered and there 326 // are UI objects in the foreground, we close 327 // them to display the lock panel. 328 if (mPUKEntryActivity != null) { 329 mPUKEntryActivity.finish(); 330 mPUKEntryActivity = null; 331 } 332 if (mPUKEntryProgressDialog != null) { 333 mPUKEntryProgressDialog.dismiss(); 334 mPUKEntryProgressDialog = null; 335 } 336 } 337 break; 338 339 case EVENT_UNSOL_CDMA_INFO_RECORD: 340 //TODO: handle message here; 341 break; 342 343 case EVENT_DOCK_STATE_CHANGED: 344 // If the phone is docked/undocked during a call, and no wired or BT headset 345 // is connected: turn on/off the speaker accordingly. 346 boolean inDockMode = false; 347 if (mDockState == Intent.EXTRA_DOCK_STATE_DESK || 348 mDockState == Intent.EXTRA_DOCK_STATE_CAR) { 349 inDockMode = true; 350 } 351 if (VDBG) Log.d(LOG_TAG, "received EVENT_DOCK_STATE_CHANGED. Phone inDock = " 352 + inDockMode); 353 354 phoneState = mCM.getState(); 355 if (phoneState == Phone.State.OFFHOOK && 356 !isHeadsetPlugged() && 357 !(mBtHandsfree != null && mBtHandsfree.isAudioOn())) { 358 PhoneUtils.turnOnSpeaker(getApplicationContext(), inDockMode, true); 359 360 if (mInCallScreen != null) { 361 mInCallScreen.requestUpdateTouchUi(); 362 } 363 } 364 365 case EVENT_TTY_PREFERRED_MODE_CHANGED: 366 // TTY mode is only applied if a headset is connected 367 int ttyMode; 368 if (isHeadsetPlugged()) { 369 ttyMode = mPreferredTtyMode; 370 } else { 371 ttyMode = Phone.TTY_MODE_OFF; 372 } 373 phone.setTTYMode(ttyMode, mHandler.obtainMessage(EVENT_TTY_MODE_SET)); 374 break; 375 376 case EVENT_TTY_MODE_GET: 377 handleQueryTTYModeResponse(msg); 378 break; 379 380 case EVENT_TTY_MODE_SET: 381 handleSetTTYModeResponse(msg); 382 break; 383 } 384 } 385 }; 386 387 public PhoneApp() { 388 sMe = this; 389 } 390 391 @Override 392 public void onCreate() { 393 if (VDBG) Log.v(LOG_TAG, "onCreate()..."); 394 395 ContentResolver resolver = getContentResolver(); 396 397 if (phone == null) { 398 // Initialize the telephony framework 399 PhoneFactory.makeDefaultPhones(this); 400 401 // Get the default phone 402 phone = PhoneFactory.getDefaultPhone(); 403 404 mCM = CallManager.getInstance(); 405 mCM.registerPhone(phone); 406 407 408 NotificationMgr.init(this); 409 410 phoneMgr = new PhoneInterfaceManager(this, phone); 411 412 413 int phoneType = phone.getPhoneType(); 414 415 if (phoneType == Phone.PHONE_TYPE_CDMA) { 416 // Create an instance of CdmaPhoneCallState and initialize it to IDLE 417 cdmaPhoneCallState = new CdmaPhoneCallState(); 418 cdmaPhoneCallState.CdmaPhoneCallStateInit(); 419 } 420 421 if (BluetoothAdapter.getDefaultAdapter() != null) { 422 mBtHandsfree = new BluetoothHandsfree(this, mCM); 423 startService(new Intent(this, BluetoothHeadsetService.class)); 424 } else { 425 // Device is not bluetooth capable 426 mBtHandsfree = null; 427 } 428 429 ringer = new Ringer(this); 430 431 // before registering for phone state changes 432 PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE); 433 mWakeLock = pm.newWakeLock(PowerManager.FULL_WAKE_LOCK 434 | PowerManager.ACQUIRE_CAUSES_WAKEUP, 435 LOG_TAG); 436 // lock used to keep the processor awake, when we don't care for the display. 437 mPartialWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK 438 | PowerManager.ON_AFTER_RELEASE, LOG_TAG); 439 // Wake lock used to control proximity sensor behavior. 440 if ((pm.getSupportedWakeLockFlags() 441 & PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK) != 0x0) { 442 mProximityWakeLock = 443 pm.newWakeLock(PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK, LOG_TAG); 444 } 445 if (DBG) Log.d(LOG_TAG, "onCreate: mProximityWakeLock: " + mProximityWakeLock); 446 447 // create mAccelerometerListener only if we are using the proximity sensor 448 if (proximitySensorModeEnabled()) { 449 mAccelerometerListener = new AccelerometerListener(this, this); 450 } 451 452 mKeyguardManager = (KeyguardManager) getSystemService(Context.KEYGUARD_SERVICE); 453 mStatusBarManager = (StatusBarManager) getSystemService(Context.STATUS_BAR_SERVICE); 454 455 // get a handle to the service so that we can use it later when we 456 // want to set the poke lock. 457 mPowerManagerService = IPowerManager.Stub.asInterface( 458 ServiceManager.getService("power")); 459 460 notifier = new CallNotifier(this, phone, ringer, mBtHandsfree, new CallLogAsync()); 461 462 // register for ICC status 463 IccCard sim = phone.getIccCard(); 464 if (sim != null) { 465 if (VDBG) Log.v(LOG_TAG, "register for ICC status"); 466 sim.registerForNetworkLocked(mHandler, EVENT_SIM_NETWORK_LOCKED, null); 467 } 468 469 // register for MMI/USSD 470 mCM.registerForMmiComplete(mHandler, MMI_COMPLETE, null); 471 472 // register connection tracking to PhoneUtils 473 PhoneUtils.initializeConnectionHandler(mCM); 474 475 // Read platform settings for TTY feature 476 mTtyEnabled = getResources().getBoolean(R.bool.tty_enabled); 477 478 // Register for misc other intent broadcasts. 479 IntentFilter intentFilter = 480 new IntentFilter(Intent.ACTION_AIRPLANE_MODE_CHANGED); 481 intentFilter.addAction(BluetoothHeadset.ACTION_STATE_CHANGED); 482 intentFilter.addAction(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED); 483 intentFilter.addAction(TelephonyIntents.ACTION_ANY_DATA_CONNECTION_STATE_CHANGED); 484 intentFilter.addAction(Intent.ACTION_HEADSET_PLUG); 485 intentFilter.addAction(Intent.ACTION_DOCK_EVENT); 486 intentFilter.addAction(Intent.ACTION_BATTERY_LOW); 487 intentFilter.addAction(TelephonyIntents.ACTION_SIM_STATE_CHANGED); 488 intentFilter.addAction(TelephonyIntents.ACTION_RADIO_TECHNOLOGY_CHANGED); 489 intentFilter.addAction(TelephonyIntents.ACTION_SERVICE_STATE_CHANGED); 490 intentFilter.addAction(TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED); 491 if (mTtyEnabled) { 492 intentFilter.addAction(TtyIntent.TTY_PREFERRED_MODE_CHANGE_ACTION); 493 } 494 intentFilter.addAction(AudioManager.RINGER_MODE_CHANGED_ACTION); 495 registerReceiver(mReceiver, intentFilter); 496 497 // Use a separate receiver for ACTION_MEDIA_BUTTON broadcasts, 498 // since we need to manually adjust its priority (to make sure 499 // we get these intents *before* the media player.) 500 IntentFilter mediaButtonIntentFilter = 501 new IntentFilter(Intent.ACTION_MEDIA_BUTTON); 502 // 503 // Make sure we're higher priority than the media player's 504 // MediaButtonIntentReceiver (which currently has the default 505 // priority of zero; see apps/Music/AndroidManifest.xml.) 506 mediaButtonIntentFilter.setPriority(1); 507 // 508 registerReceiver(mMediaButtonReceiver, mediaButtonIntentFilter); 509 510 //set the default values for the preferences in the phone. 511 PreferenceManager.setDefaultValues(this, R.xml.network_setting, false); 512 513 PreferenceManager.setDefaultValues(this, R.xml.call_feature_setting, false); 514 515 // Make sure the audio mode (along with some 516 // audio-mode-related state of our own) is initialized 517 // correctly, given the current state of the phone. 518 PhoneUtils.setAudioMode(mCM); 519 } 520 521 boolean phoneIsCdma = (phone.getPhoneType() == Phone.PHONE_TYPE_CDMA); 522 523 if (phoneIsCdma) { 524 cdmaOtaProvisionData = new OtaUtils.CdmaOtaProvisionData(); 525 cdmaOtaConfigData = new OtaUtils.CdmaOtaConfigData(); 526 cdmaOtaScreenState = new OtaUtils.CdmaOtaScreenState(); 527 cdmaOtaInCallScreenUiState = new OtaUtils.CdmaOtaInCallScreenUiState(); 528 } 529 530 // XXX pre-load the SimProvider so that it's ready 531 resolver.getType(Uri.parse("content://icc/adn")); 532 533 // start with the default value to set the mute state. 534 mShouldRestoreMuteOnInCallResume = false; 535 536 // TODO: Register for Cdma Information Records 537 // phone.registerCdmaInformationRecord(mHandler, EVENT_UNSOL_CDMA_INFO_RECORD, null); 538 539 // Read TTY settings and store it into BP NV. 540 // AP owns (i.e. stores) the TTY setting in AP settings database and pushes the setting 541 // to BP at power up (BP does not need to make the TTY setting persistent storage). 542 // This way, there is a single owner (i.e AP) for the TTY setting in the phone. 543 if (mTtyEnabled) { 544 mPreferredTtyMode = android.provider.Settings.Secure.getInt( 545 phone.getContext().getContentResolver(), 546 android.provider.Settings.Secure.PREFERRED_TTY_MODE, 547 Phone.TTY_MODE_OFF); 548 mHandler.sendMessage(mHandler.obtainMessage(EVENT_TTY_PREFERRED_MODE_CHANGED, 0)); 549 } 550 // Read HAC settings and configure audio hardware 551 if (getResources().getBoolean(R.bool.hac_enabled)) { 552 int hac = android.provider.Settings.System.getInt(phone.getContext().getContentResolver(), 553 android.provider.Settings.System.HEARING_AID, 554 0); 555 AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE); 556 audioManager.setParameter(CallFeaturesSetting.HAC_KEY, hac != 0 ? 557 CallFeaturesSetting.HAC_VAL_ON : 558 CallFeaturesSetting.HAC_VAL_OFF); 559 } 560 } 561 562 @Override 563 public void onConfigurationChanged(Configuration newConfig) { 564 if (newConfig.hardKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_NO) { 565 mIsHardKeyboardOpen = true; 566 } else { 567 mIsHardKeyboardOpen = false; 568 } 569 570 // Update the Proximity sensor based on keyboard state 571 updateProximitySensorMode(mCM.getState()); 572 super.onConfigurationChanged(newConfig); 573 } 574 575 /** 576 * Returns the singleton instance of the PhoneApp. 577 */ 578 static PhoneApp getInstance() { 579 return sMe; 580 } 581 582 /** 583 * Returns the Phone associated with this instance 584 */ 585 static Phone getPhone() { 586 return getInstance().phone; 587 } 588 589 Ringer getRinger() { 590 return ringer; 591 } 592 593 BluetoothHandsfree getBluetoothHandsfree() { 594 return mBtHandsfree; 595 } 596 597 static Intent createCallLogIntent() { 598 Intent intent = new Intent(Intent.ACTION_VIEW, null); 599 intent.setType("vnd.android.cursor.dir/calls"); 600 return intent; 601 } 602 603 /** 604 * Return an Intent that can be used to bring up the in-call screen. 605 * 606 * This intent can only be used from within the Phone app, since the 607 * InCallScreen is not exported from our AndroidManifest. 608 */ 609 /* package */ static Intent createInCallIntent() { 610 Intent intent = new Intent(Intent.ACTION_MAIN, null); 611 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK 612 | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS 613 | Intent.FLAG_ACTIVITY_NO_USER_ACTION); 614 intent.setClassName("com.android.phone", getCallScreenClassName()); 615 return intent; 616 } 617 618 /** 619 * Variation of createInCallIntent() that also specifies whether the 620 * DTMF dialpad should be initially visible when the InCallScreen 621 * comes up. 622 */ 623 /* package */ static Intent createInCallIntent(boolean showDialpad) { 624 Intent intent = createInCallIntent(); 625 intent.putExtra(InCallScreen.SHOW_DIALPAD_EXTRA, showDialpad); 626 return intent; 627 } 628 629 static String getCallScreenClassName() { 630 return InCallScreen.class.getName(); 631 } 632 633 /** 634 * Starts the InCallScreen Activity. 635 */ 636 private void displayCallScreen() { 637 if (VDBG) Log.d(LOG_TAG, "displayCallScreen()..."); 638 startActivity(createInCallIntent()); 639 Profiler.callScreenRequested(); 640 } 641 642 boolean isSimPinEnabled() { 643 return mIsSimPinEnabled; 644 } 645 646 boolean authenticateAgainstCachedSimPin(String pin) { 647 return (mCachedSimPin != null && mCachedSimPin.equals(pin)); 648 } 649 650 void setCachedSimPin(String pin) { 651 mCachedSimPin = pin; 652 } 653 654 void setInCallScreenInstance(InCallScreen inCallScreen) { 655 mInCallScreen = inCallScreen; 656 } 657 658 /** 659 * @return true if the in-call UI is running as the foreground 660 * activity. (In other words, from the perspective of the 661 * InCallScreen activity, return true between onResume() and 662 * onPause().) 663 * 664 * Note this method will return false if the screen is currently off, 665 * even if the InCallScreen *was* in the foreground just before the 666 * screen turned off. (This is because the foreground activity is 667 * always "paused" while the screen is off.) 668 */ 669 boolean isShowingCallScreen() { 670 if (mInCallScreen == null) return false; 671 return mInCallScreen.isForegroundActivity(); 672 } 673 674 /** 675 * Dismisses the in-call UI. 676 * 677 * This also ensures that you won't be able to get back to the in-call 678 * UI via the BACK button (since this call removes the InCallScreen 679 * from the activity history.) 680 * For OTA Call, it call InCallScreen api to handle OTA Call End scenario 681 * to display OTA Call End screen. 682 */ 683 void dismissCallScreen() { 684 if (mInCallScreen != null) { 685 if ((phone.getPhoneType() == Phone.PHONE_TYPE_CDMA) && 686 (mInCallScreen.isOtaCallInActiveState() 687 || mInCallScreen.isOtaCallInEndState() 688 || ((cdmaOtaScreenState != null) 689 && (cdmaOtaScreenState.otaScreenState 690 != CdmaOtaScreenState.OtaScreenState.OTA_STATUS_UNDEFINED)))) { 691 // TODO: During OTA Call, display should not become dark to 692 // allow user to see OTA UI update. Phone app needs to hold 693 // a SCREEN_DIM_WAKE_LOCK wake lock during the entire OTA call. 694 wakeUpScreen(); 695 // If InCallScreen is not in foreground we resume it to show the OTA call end screen 696 // Fire off the InCallScreen intent 697 displayCallScreen(); 698 699 mInCallScreen.handleOtaCallEnd(); 700 return; 701 } else { 702 mInCallScreen.finish(); 703 } 704 } 705 } 706 707 /** 708 * Handle OTA events 709 * 710 * When OTA call is active and display becomes dark, then CallNotifier will 711 * handle OTA Events by calling this api which then calls OtaUtil function. 712 */ 713 void handleOtaEvents(Message msg) { 714 715 if (DBG) Log.d(LOG_TAG, "Enter handleOtaEvents"); 716 if ((mInCallScreen != null) && (!isShowingCallScreen())) { 717 if (mInCallScreen.otaUtils != null) { 718 mInCallScreen.otaUtils.onOtaProvisionStatusChanged((AsyncResult) msg.obj); 719 } 720 } 721 } 722 723 724 /** 725 * Sets the activity responsible for un-PUK-blocking the device 726 * so that we may close it when we receive a positive result. 727 * mPUKEntryActivity is also used to indicate to the device that 728 * we are trying to un-PUK-lock the phone. In other words, iff 729 * it is NOT null, then we are trying to unlock and waiting for 730 * the SIM to move to READY state. 731 * 732 * @param activity is the activity to close when PUK has 733 * finished unlocking. Can be set to null to indicate the unlock 734 * or SIM READYing process is over. 735 */ 736 void setPukEntryActivity(Activity activity) { 737 mPUKEntryActivity = activity; 738 } 739 740 Activity getPUKEntryActivity() { 741 return mPUKEntryActivity; 742 } 743 744 /** 745 * Sets the dialog responsible for notifying the user of un-PUK- 746 * blocking - SIM READYing progress, so that we may dismiss it 747 * when we receive a positive result. 748 * 749 * @param dialog indicates the progress dialog informing the user 750 * of the state of the device. Dismissed upon completion of 751 * READYing process 752 */ 753 void setPukEntryProgressDialog(ProgressDialog dialog) { 754 mPUKEntryProgressDialog = dialog; 755 } 756 757 ProgressDialog getPUKEntryProgressDialog() { 758 return mPUKEntryProgressDialog; 759 } 760 761 /** 762 * Disables the status bar. This is used by the phone app when in-call UI is active. 763 * 764 * Any call to this method MUST be followed (eventually) 765 * by a corresponding reenableStatusBar() call. 766 */ 767 /* package */ void disableStatusBar() { 768 if (DBG) Log.d(LOG_TAG, "disable status bar"); 769 synchronized (this) { 770 if (mStatusBarDisableCount++ == 0) { 771 if (DBG) Log.d(LOG_TAG, "StatusBarManager.DISABLE_EXPAND"); 772 mStatusBarManager.disable(StatusBarManager.DISABLE_EXPAND); 773 } 774 } 775 } 776 777 /** 778 * Re-enables the status bar after a previous disableStatusBar() call. 779 * 780 * Any call to this method MUST correspond to (i.e. be balanced with) 781 * a previous disableStatusBar() call. 782 */ 783 /* package */ void reenableStatusBar() { 784 if (DBG) Log.d(LOG_TAG, "re-enable status bar"); 785 synchronized (this) { 786 if (mStatusBarDisableCount > 0) { 787 if (--mStatusBarDisableCount == 0) { 788 if (DBG) Log.d(LOG_TAG, "StatusBarManager.DISABLE_NONE"); 789 mStatusBarManager.disable(StatusBarManager.DISABLE_NONE); 790 } 791 } else { 792 Log.e(LOG_TAG, "mStatusBarDisableCount is already zero"); 793 } 794 } 795 } 796 797 /** 798 * Controls how quickly the screen times out. 799 * 800 * The poke lock controls how long it takes before the screen powers 801 * down, and therefore has no immediate effect when the current 802 * WakeState (see {@link PhoneApp#requestWakeState}) is FULL. 803 * If we're in a state where the screen *is* allowed to turn off, 804 * though, the poke lock will determine the timeout interval (long or 805 * short). 806 * 807 * @param shortPokeLock tells the device the timeout duration to use 808 * before going to sleep 809 * {@link com.android.server.PowerManagerService#SHORT_KEYLIGHT_DELAY}. 810 */ 811 /* package */ void setScreenTimeout(ScreenTimeoutDuration duration) { 812 if (VDBG) Log.d(LOG_TAG, "setScreenTimeout(" + duration + ")..."); 813 814 // make sure we don't set the poke lock repeatedly so that we 815 // avoid triggering the userActivity calls in 816 // PowerManagerService.setPokeLock(). 817 if (duration == mScreenTimeoutDuration) { 818 return; 819 } 820 // stick with default timeout if we are using the proximity sensor 821 if (proximitySensorModeEnabled()) { 822 return; 823 } 824 mScreenTimeoutDuration = duration; 825 updatePokeLock(); 826 } 827 828 /** 829 * Update the state of the poke lock held by the phone app, 830 * based on the current desired screen timeout and the 831 * current "ignore user activity on touch" flag. 832 */ 833 private void updatePokeLock() { 834 // This is kind of convoluted, but the basic thing to remember is 835 // that the poke lock just sends a message to the screen to tell 836 // it to stay on for a while. 837 // The default is 0, for a long timeout and should be set that way 838 // when we are heading back into a the keyguard / screen off 839 // state, and also when we're trying to keep the screen alive 840 // while ringing. We'll also want to ignore the cheek events 841 // regardless of the timeout duration. 842 // The short timeout is really used whenever we want to give up 843 // the screen lock, such as when we're in call. 844 int pokeLockSetting = LocalPowerManager.POKE_LOCK_IGNORE_CHEEK_EVENTS; 845 switch (mScreenTimeoutDuration) { 846 case SHORT: 847 // Set the poke lock to timeout the display after a short 848 // timeout (5s). This ensures that the screen goes to sleep 849 // as soon as acceptably possible after we the wake lock 850 // has been released. 851 pokeLockSetting |= LocalPowerManager.POKE_LOCK_SHORT_TIMEOUT; 852 break; 853 854 case MEDIUM: 855 // Set the poke lock to timeout the display after a medium 856 // timeout (15s). This ensures that the screen goes to sleep 857 // as soon as acceptably possible after we the wake lock 858 // has been released. 859 pokeLockSetting |= LocalPowerManager.POKE_LOCK_MEDIUM_TIMEOUT; 860 break; 861 862 case DEFAULT: 863 default: 864 // set the poke lock to timeout the display after a long 865 // delay by default. 866 // TODO: it may be nice to be able to disable cheek presses 867 // for long poke locks (emergency dialer, for instance). 868 break; 869 } 870 871 if (mIgnoreTouchUserActivity) { 872 pokeLockSetting |= LocalPowerManager.POKE_LOCK_IGNORE_TOUCH_AND_CHEEK_EVENTS; 873 } 874 875 // Send the request 876 try { 877 mPowerManagerService.setPokeLock(pokeLockSetting, mPokeLockToken, LOG_TAG); 878 } catch (RemoteException e) { 879 Log.w(LOG_TAG, "mPowerManagerService.setPokeLock() failed: " + e); 880 } 881 } 882 883 /** 884 * Controls whether or not the screen is allowed to sleep. 885 * 886 * Once sleep is allowed (WakeState is SLEEP), it will rely on the 887 * settings for the poke lock to determine when to timeout and let 888 * the device sleep {@link PhoneApp#setScreenTimeout}. 889 * 890 * @param ws tells the device to how to wake. 891 */ 892 /* package */ void requestWakeState(WakeState ws) { 893 if (VDBG) Log.d(LOG_TAG, "requestWakeState(" + ws + ")..."); 894 synchronized (this) { 895 if (mWakeState != ws) { 896 switch (ws) { 897 case PARTIAL: 898 // acquire the processor wake lock, and release the FULL 899 // lock if it is being held. 900 mPartialWakeLock.acquire(); 901 if (mWakeLock.isHeld()) { 902 mWakeLock.release(); 903 } 904 break; 905 case FULL: 906 // acquire the full wake lock, and release the PARTIAL 907 // lock if it is being held. 908 mWakeLock.acquire(); 909 if (mPartialWakeLock.isHeld()) { 910 mPartialWakeLock.release(); 911 } 912 break; 913 case SLEEP: 914 default: 915 // release both the PARTIAL and FULL locks. 916 if (mWakeLock.isHeld()) { 917 mWakeLock.release(); 918 } 919 if (mPartialWakeLock.isHeld()) { 920 mPartialWakeLock.release(); 921 } 922 break; 923 } 924 mWakeState = ws; 925 } 926 } 927 } 928 929 /** 930 * If we are not currently keeping the screen on, then poke the power 931 * manager to wake up the screen for the user activity timeout duration. 932 */ 933 /* package */ void wakeUpScreen() { 934 synchronized (this) { 935 if (mWakeState == WakeState.SLEEP) { 936 if (DBG) Log.d(LOG_TAG, "pulse screen lock"); 937 try { 938 mPowerManagerService.userActivityWithForce(SystemClock.uptimeMillis(), false, true); 939 } catch (RemoteException ex) { 940 // Ignore -- the system process is dead. 941 } 942 } 943 } 944 } 945 946 /** 947 * Sets the wake state and screen timeout based on the current state 948 * of the phone, and the current state of the in-call UI. 949 * 950 * This method is a "UI Policy" wrapper around 951 * {@link PhoneApp#requestWakeState} and {@link PhoneApp#setScreenTimeout}. 952 * 953 * It's safe to call this method regardless of the state of the Phone 954 * (e.g. whether or not it's idle), and regardless of the state of the 955 * Phone UI (e.g. whether or not the InCallScreen is active.) 956 */ 957 /* package */ void updateWakeState() { 958 Phone.State state = mCM.getState(); 959 960 // True if the in-call UI is the foreground activity. 961 // (Note this will be false if the screen is currently off, 962 // since in that case *no* activity is in the foreground.) 963 boolean isShowingCallScreen = isShowingCallScreen(); 964 965 // True if the InCallScreen's DTMF dialer is currently opened. 966 // (Note this does NOT imply whether or not the InCallScreen 967 // itself is visible.) 968 boolean isDialerOpened = (mInCallScreen != null) && mInCallScreen.isDialerOpened(); 969 970 // True if the speakerphone is in use. (If so, we *always* use 971 // the default timeout. Since the user is obviously not holding 972 // the phone up to his/her face, we don't need to worry about 973 // false touches, and thus don't need to turn the screen off so 974 // aggressively.) 975 // Note that we need to make a fresh call to this method any 976 // time the speaker state changes. (That happens in 977 // PhoneUtils.turnOnSpeaker().) 978 boolean isSpeakerInUse = (state == Phone.State.OFFHOOK) && PhoneUtils.isSpeakerOn(this); 979 980 // TODO (bug 1440854): The screen timeout *might* also need to 981 // depend on the bluetooth state, but this isn't as clear-cut as 982 // the speaker state (since while using BT it's common for the 983 // user to put the phone straight into a pocket, in which case the 984 // timeout should probably still be short.) 985 986 if (DBG) Log.d(LOG_TAG, "updateWakeState: callscreen " + isShowingCallScreen 987 + ", dialer " + isDialerOpened 988 + ", speaker " + isSpeakerInUse + "..."); 989 990 // 991 // (1) Set the screen timeout. 992 // 993 // Note that the "screen timeout" value we determine here is 994 // meaningless if the screen is forced on (see (2) below.) 995 // 996 if (!isShowingCallScreen || isSpeakerInUse) { 997 // Use the system-wide default timeout. 998 setScreenTimeout(ScreenTimeoutDuration.DEFAULT); 999 } else { 1000 // We're on the in-call screen, and *not* using the speakerphone. 1001 if (isDialerOpened) { 1002 // The DTMF dialpad is up. This case is special because 1003 // the in-call UI has its own "touch lock" mechanism to 1004 // disable the dialpad after a very short amount of idle 1005 // time (to avoid false touches from the user's face while 1006 // in-call.) 1007 // 1008 // In this case the *physical* screen just uses the 1009 // system-wide default timeout. 1010 setScreenTimeout(ScreenTimeoutDuration.DEFAULT); 1011 } else { 1012 // We're on the in-call screen, and not using the DTMF dialpad. 1013 // There's actually no touchable UI onscreen at all in 1014 // this state. Also, the user is (most likely) not 1015 // looking at the screen at all, since they're probably 1016 // holding the phone up to their face. Here we use a 1017 // special screen timeout value specific to the in-call 1018 // screen, purely to save battery life. 1019 setScreenTimeout(ScreenTimeoutDuration.MEDIUM); 1020 } 1021 } 1022 1023 // 1024 // (2) Decide whether to force the screen on or not. 1025 // 1026 // Force the screen to be on if the phone is ringing or dialing, 1027 // or if we're displaying the "Call ended" UI for a connection in 1028 // the "disconnected" state. 1029 // 1030 boolean isRinging = (state == Phone.State.RINGING); 1031 boolean isDialing = (phone.getForegroundCall().getState() == Call.State.DIALING); 1032 boolean showingDisconnectedConnection = 1033 PhoneUtils.hasDisconnectedConnections(phone) && isShowingCallScreen; 1034 boolean keepScreenOn = isRinging || isDialing || showingDisconnectedConnection; 1035 if (DBG) Log.d(LOG_TAG, "updateWakeState: keepScreenOn = " + keepScreenOn 1036 + " (isRinging " + isRinging 1037 + ", isDialing " + isDialing 1038 + ", showingDisc " + showingDisconnectedConnection + ")"); 1039 // keepScreenOn == true means we'll hold a full wake lock: 1040 requestWakeState(keepScreenOn ? WakeState.FULL : WakeState.SLEEP); 1041 } 1042 1043 /** 1044 * Wrapper around the PowerManagerService.preventScreenOn() API. 1045 * This allows the in-call UI to prevent the screen from turning on 1046 * even if a subsequent call to updateWakeState() causes us to acquire 1047 * a full wake lock. 1048 */ 1049 /* package */ void preventScreenOn(boolean prevent) { 1050 if (VDBG) Log.d(LOG_TAG, "- preventScreenOn(" + prevent + ")..."); 1051 try { 1052 mPowerManagerService.preventScreenOn(prevent); 1053 } catch (RemoteException e) { 1054 Log.w(LOG_TAG, "mPowerManagerService.preventScreenOn() failed: " + e); 1055 } 1056 } 1057 1058 /** 1059 * Sets or clears the flag that tells the PowerManager that touch 1060 * (and cheek) events should NOT be considered "user activity". 1061 * 1062 * Since the in-call UI is totally insensitive to touch in most 1063 * states, we set this flag whenever the InCallScreen is in the 1064 * foreground. (Otherwise, repeated unintentional touches could 1065 * prevent the device from going to sleep.) 1066 * 1067 * There *are* some some touch events that really do count as user 1068 * activity, though. For those, we need to manually poke the 1069 * PowerManager's userActivity method; see pokeUserActivity(). 1070 */ 1071 /* package */ void setIgnoreTouchUserActivity(boolean ignore) { 1072 if (VDBG) Log.d(LOG_TAG, "setIgnoreTouchUserActivity(" + ignore + ")..."); 1073 mIgnoreTouchUserActivity = ignore; 1074 updatePokeLock(); 1075 } 1076 1077 /** 1078 * Manually pokes the PowerManager's userActivity method. Since we 1079 * hold the POKE_LOCK_IGNORE_TOUCH_AND_CHEEK_EVENTS poke lock while 1080 * the InCallScreen is active, we need to do this for touch events 1081 * that really do count as user activity (like DTMF key presses, or 1082 * unlocking the "touch lock" overlay.) 1083 */ 1084 /* package */ void pokeUserActivity() { 1085 if (VDBG) Log.d(LOG_TAG, "pokeUserActivity()..."); 1086 try { 1087 mPowerManagerService.userActivity(SystemClock.uptimeMillis(), false); 1088 } catch (RemoteException e) { 1089 Log.w(LOG_TAG, "mPowerManagerService.userActivity() failed: " + e); 1090 } 1091 } 1092 1093 /** 1094 * Set when a new outgoing call is beginning, so we can update 1095 * the proximity sensor state. 1096 * Cleared when the InCallScreen is no longer in the foreground, 1097 * in case the call fails without changing the telephony state. 1098 */ 1099 /* package */ void setBeginningCall(boolean beginning) { 1100 // Note that we are beginning a new call, for proximity sensor support 1101 mBeginningCall = beginning; 1102 // Update the Proximity sensor based on mBeginningCall state 1103 updateProximitySensorMode(mCM.getState()); 1104 } 1105 1106 /** 1107 * Updates the wake lock used to control proximity sensor behavior, 1108 * based on the current state of the phone. This method is called 1109 * from the CallNotifier on any phone state change. 1110 * 1111 * On devices that have a proximity sensor, to avoid false touches 1112 * during a call, we hold a PROXIMITY_SCREEN_OFF_WAKE_LOCK wake lock 1113 * whenever the phone is off hook. (When held, that wake lock causes 1114 * the screen to turn off automatically when the sensor detects an 1115 * object close to the screen.) 1116 * 1117 * This method is a no-op for devices that don't have a proximity 1118 * sensor. 1119 * 1120 * Note this method doesn't care if the InCallScreen is the foreground 1121 * activity or not. That's because we want the proximity sensor to be 1122 * enabled any time the phone is in use, to avoid false cheek events 1123 * for whatever app you happen to be running. 1124 * 1125 * Proximity wake lock will *not* be held if any one of the 1126 * conditions is true while on a call: 1127 * 1) If the audio is routed via Bluetooth 1128 * 2) If a wired headset is connected 1129 * 3) if the speaker is ON 1130 * 4) If the slider is open(i.e. the hardkeyboard is *not* hidden) 1131 * 1132 * @param state current state of the phone (see {@link Phone#State}) 1133 */ 1134 /* package */ void updateProximitySensorMode(Phone.State state) { 1135 if (VDBG) Log.d(LOG_TAG, "updateProximitySensorMode: state = " + state); 1136 1137 if (proximitySensorModeEnabled()) { 1138 synchronized (mProximityWakeLock) { 1139 // turn proximity sensor off and turn screen on immediately if 1140 // we are using a headset, the keyboard is open, or the device 1141 // is being held in a horizontal position. 1142 boolean screenOnImmediately = (isHeadsetPlugged() 1143 || PhoneUtils.isSpeakerOn(this) 1144 || ((mBtHandsfree != null) && mBtHandsfree.isAudioOn()) 1145 || mIsHardKeyboardOpen); 1146 // We do not keep the screen off when we are horizontal, but we do not force it 1147 // on when we become horizontal until the proximity sensor goes negative. 1148 boolean horizontal = (mOrientation == AccelerometerListener.ORIENTATION_HORIZONTAL); 1149 1150 if (((state == Phone.State.OFFHOOK) || mBeginningCall) && 1151 !screenOnImmediately && !horizontal) { 1152 // Phone is in use! Arrange for the screen to turn off 1153 // automatically when the sensor detects a close object. 1154 if (!mProximityWakeLock.isHeld()) { 1155 if (DBG) Log.d(LOG_TAG, "updateProximitySensorMode: acquiring..."); 1156 mProximityWakeLock.acquire(); 1157 } else { 1158 if (VDBG) Log.d(LOG_TAG, "updateProximitySensorMode: lock already held."); 1159 } 1160 } else { 1161 // Phone is either idle, or ringing. We don't want any 1162 // special proximity sensor behavior in either case. 1163 if (mProximityWakeLock.isHeld()) { 1164 if (DBG) Log.d(LOG_TAG, "updateProximitySensorMode: releasing..."); 1165 // Wait until user has moved the phone away from his head if we are 1166 // releasing due to the phone call ending. 1167 // Qtherwise, turn screen on immediately 1168 int flags = 1169 (screenOnImmediately ? 0 : PowerManager.WAIT_FOR_PROXIMITY_NEGATIVE); 1170 mProximityWakeLock.release(flags); 1171 } else { 1172 if (VDBG) { 1173 Log.d(LOG_TAG, "updateProximitySensorMode: lock already released."); 1174 } 1175 } 1176 } 1177 } 1178 } 1179 } 1180 1181 public void orientationChanged(int orientation) { 1182 mOrientation = orientation; 1183 updateProximitySensorMode(mCM.getState()); 1184 } 1185 1186 /** 1187 * Notifies the phone app when the phone state changes. 1188 * Currently used only for proximity sensor support. 1189 */ 1190 /* package */ void updatePhoneState(Phone.State state) { 1191 if (state != mLastPhoneState) { 1192 mLastPhoneState = state; 1193 updateProximitySensorMode(state); 1194 if (mAccelerometerListener != null) { 1195 // use accelerometer to augment proximity sensor when in call 1196 mOrientation = AccelerometerListener.ORIENTATION_UNKNOWN; 1197 mAccelerometerListener.enable(state == Phone.State.OFFHOOK); 1198 } 1199 // clear our beginning call flag 1200 mBeginningCall = false; 1201 // While we are in call, the in-call screen should dismiss the keyguard. 1202 // This allows the user to press Home to go directly home without going through 1203 // an insecure lock screen. 1204 // But we do not want to do this if there is no active call so we do not 1205 // bypass the keyguard if the call is not answered or declined. 1206 if (mInCallScreen != null) { 1207 mInCallScreen.updateKeyguardPolicy(state == Phone.State.OFFHOOK); 1208 } 1209 } 1210 } 1211 1212 /* package */ Phone.State getPhoneState() { 1213 return mLastPhoneState; 1214 } 1215 1216 /** 1217 * @return true if this device supports the "proximity sensor 1218 * auto-lock" feature while in-call (see updateProximitySensorMode()). 1219 */ 1220 /* package */ boolean proximitySensorModeEnabled() { 1221 return (mProximityWakeLock != null); 1222 } 1223 1224 KeyguardManager getKeyguardManager() { 1225 return mKeyguardManager; 1226 } 1227 1228 private void onMMIComplete(AsyncResult r) { 1229 if (VDBG) Log.d(LOG_TAG, "onMMIComplete()..."); 1230 MmiCode mmiCode = (MmiCode) r.result; 1231 PhoneUtils.displayMMIComplete(phone, getInstance(), mmiCode, null, null); 1232 } 1233 1234 private void initForNewRadioTechnology() { 1235 if (DBG) Log.d(LOG_TAG, "initForNewRadioTechnology..."); 1236 1237 if (phone.getPhoneType() == Phone.PHONE_TYPE_CDMA) { 1238 // Create an instance of CdmaPhoneCallState and initialize it to IDLE 1239 cdmaPhoneCallState = new CdmaPhoneCallState(); 1240 cdmaPhoneCallState.CdmaPhoneCallStateInit(); 1241 1242 //create instances of CDMA OTA data classes 1243 if (cdmaOtaProvisionData == null) { 1244 cdmaOtaProvisionData = new OtaUtils.CdmaOtaProvisionData(); 1245 } 1246 if (cdmaOtaConfigData == null) { 1247 cdmaOtaConfigData = new OtaUtils.CdmaOtaConfigData(); 1248 } 1249 if (cdmaOtaScreenState == null) { 1250 cdmaOtaScreenState = new OtaUtils.CdmaOtaScreenState(); 1251 } 1252 if (cdmaOtaInCallScreenUiState == null) { 1253 cdmaOtaInCallScreenUiState = new OtaUtils.CdmaOtaInCallScreenUiState(); 1254 } 1255 } else { 1256 //Clean up OTA data in GSM/UMTS. It is valid only for CDMA 1257 clearOtaState(); 1258 } 1259 1260 ringer.updateRingerContextAfterRadioTechnologyChange(this.phone); 1261 notifier.updateCallNotifierRegistrationsAfterRadioTechnologyChange(); 1262 if (mBtHandsfree != null) { 1263 mBtHandsfree.updateBtHandsfreeAfterRadioTechnologyChange(); 1264 } 1265 if (mInCallScreen != null) { 1266 mInCallScreen.updateAfterRadioTechnologyChange(); 1267 } 1268 1269 // Update registration for ICC status after radio technology change 1270 IccCard sim = phone.getIccCard(); 1271 if (sim != null) { 1272 if (DBG) Log.d(LOG_TAG, "Update registration for ICC status..."); 1273 1274 //Register all events new to the new active phone 1275 sim.registerForNetworkLocked(mHandler, EVENT_SIM_NETWORK_LOCKED, null); 1276 } 1277 } 1278 1279 1280 /** 1281 * @return true if a wired headset is currently plugged in. 1282 * 1283 * @see Intent.ACTION_HEADSET_PLUG (which we listen for in mReceiver.onReceive()) 1284 */ 1285 boolean isHeadsetPlugged() { 1286 return mIsHeadsetPlugged; 1287 } 1288 1289 /** 1290 * @return true if the onscreen UI should currently be showing the 1291 * special "bluetooth is active" indication in a couple of places (in 1292 * which UI elements turn blue and/or show the bluetooth logo.) 1293 * 1294 * This depends on the BluetoothHeadset state *and* the current 1295 * telephony state; see shouldShowBluetoothIndication(). 1296 * 1297 * @see CallCard 1298 * @see NotificationMgr.updateInCallNotification 1299 */ 1300 /* package */ boolean showBluetoothIndication() { 1301 return mShowBluetoothIndication; 1302 } 1303 1304 /** 1305 * Recomputes the mShowBluetoothIndication flag based on the current 1306 * bluetooth state and current telephony state. 1307 * 1308 * This needs to be called any time the bluetooth headset state or the 1309 * telephony state changes. 1310 * 1311 * @param forceUiUpdate if true, force the UI elements that care 1312 * about this flag to update themselves. 1313 */ 1314 /* package */ void updateBluetoothIndication(boolean forceUiUpdate) { 1315 mShowBluetoothIndication = shouldShowBluetoothIndication(mBluetoothHeadsetState, 1316 mBluetoothHeadsetAudioState, 1317 mCM); 1318 if (forceUiUpdate) { 1319 // Post Handler messages to the various components that might 1320 // need to be refreshed based on the new state. 1321 if (isShowingCallScreen()) mInCallScreen.requestUpdateBluetoothIndication(); 1322 if (DBG) Log.d (LOG_TAG, "- updating in-call notification for BT state change..."); 1323 mHandler.sendEmptyMessage(EVENT_UPDATE_INCALL_NOTIFICATION); 1324 } 1325 1326 // Update the Proximity sensor based on Bluetooth audio state 1327 updateProximitySensorMode(mCM.getState()); 1328 } 1329 1330 /** 1331 * UI policy helper function for the couple of places in the UI that 1332 * have some way of indicating that "bluetooth is in use." 1333 * 1334 * @return true if the onscreen UI should indicate that "bluetooth is in use", 1335 * based on the specified bluetooth headset state, and the 1336 * current state of the phone. 1337 * @see showBluetoothIndication() 1338 */ 1339 private static boolean shouldShowBluetoothIndication(int bluetoothState, 1340 int bluetoothAudioState, 1341 CallManager cm) { 1342 // We want the UI to indicate that "bluetooth is in use" in two 1343 // slightly different cases: 1344 // 1345 // (a) The obvious case: if a bluetooth headset is currently in 1346 // use for an ongoing call. 1347 // 1348 // (b) The not-so-obvious case: if an incoming call is ringing, 1349 // and we expect that audio *will* be routed to a bluetooth 1350 // headset once the call is answered. 1351 1352 switch (cm.getState()) { 1353 case OFFHOOK: 1354 // This covers normal active calls, and also the case if 1355 // the foreground call is DIALING or ALERTING. In this 1356 // case, bluetooth is considered "active" if a headset 1357 // is connected *and* audio is being routed to it. 1358 return ((bluetoothState == BluetoothHeadset.STATE_CONNECTED) 1359 && (bluetoothAudioState == BluetoothHeadset.AUDIO_STATE_CONNECTED)); 1360 1361 case RINGING: 1362 // If an incoming call is ringing, we're *not* yet routing 1363 // audio to the headset (since there's no in-call audio 1364 // yet!) In this case, if a bluetooth headset is 1365 // connected at all, we assume that it'll become active 1366 // once the user answers the phone. 1367 return (bluetoothState == BluetoothHeadset.STATE_CONNECTED); 1368 1369 default: // Presumably IDLE 1370 return false; 1371 } 1372 } 1373 1374 1375 /** 1376 * Receiver for misc intent broadcasts the Phone app cares about. 1377 */ 1378 private class PhoneAppBroadcastReceiver extends BroadcastReceiver { 1379 @Override 1380 public void onReceive(Context context, Intent intent) { 1381 String action = intent.getAction(); 1382 if (action.equals(Intent.ACTION_AIRPLANE_MODE_CHANGED)) { 1383 boolean enabled = System.getInt(getContentResolver(), 1384 System.AIRPLANE_MODE_ON, 0) == 0; 1385 phone.setRadioPower(enabled); 1386 } else if (action.equals(BluetoothHeadset.ACTION_STATE_CHANGED)) { 1387 mBluetoothHeadsetState = intent.getIntExtra(BluetoothHeadset.EXTRA_STATE, 1388 BluetoothHeadset.STATE_ERROR); 1389 if (VDBG) Log.d(LOG_TAG, "mReceiver: HEADSET_STATE_CHANGED_ACTION"); 1390 if (VDBG) Log.d(LOG_TAG, "==> new state: " + mBluetoothHeadsetState); 1391 updateBluetoothIndication(true); // Also update any visible UI if necessary 1392 } else if (action.equals(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED)) { 1393 mBluetoothHeadsetAudioState = 1394 intent.getIntExtra(BluetoothHeadset.EXTRA_AUDIO_STATE, 1395 BluetoothHeadset.STATE_ERROR); 1396 if (VDBG) Log.d(LOG_TAG, "mReceiver: HEADSET_AUDIO_STATE_CHANGED_ACTION"); 1397 if (VDBG) Log.d(LOG_TAG, "==> new state: " + mBluetoothHeadsetAudioState); 1398 updateBluetoothIndication(true); // Also update any visible UI if necessary 1399 } else if (action.equals(TelephonyIntents.ACTION_ANY_DATA_CONNECTION_STATE_CHANGED)) { 1400 if (VDBG) Log.d(LOG_TAG, "mReceiver: ACTION_ANY_DATA_CONNECTION_STATE_CHANGED"); 1401 if (VDBG) Log.d(LOG_TAG, "- state: " + intent.getStringExtra(Phone.STATE_KEY)); 1402 if (VDBG) Log.d(LOG_TAG, "- reason: " 1403 + intent.getStringExtra(Phone.STATE_CHANGE_REASON_KEY)); 1404 1405 // The "data disconnected due to roaming" notification is 1406 // visible if you've lost data connectivity because you're 1407 // roaming and you have the "data roaming" feature turned off. 1408 boolean disconnectedDueToRoaming = false; 1409 if ("DISCONNECTED".equals(intent.getStringExtra(Phone.STATE_KEY))) { 1410 String reason = intent.getStringExtra(Phone.STATE_CHANGE_REASON_KEY); 1411 if (Phone.REASON_ROAMING_ON.equals(reason)) { 1412 // We just lost our data connection, and the reason 1413 // is that we started roaming. This implies that 1414 // the user has data roaming turned off. 1415 disconnectedDueToRoaming = true; 1416 } 1417 } 1418 mHandler.sendEmptyMessage(disconnectedDueToRoaming 1419 ? EVENT_DATA_ROAMING_DISCONNECTED 1420 : EVENT_DATA_ROAMING_OK); 1421 } else if (action.equals(Intent.ACTION_HEADSET_PLUG)) { 1422 if (VDBG) Log.d(LOG_TAG, "mReceiver: ACTION_HEADSET_PLUG"); 1423 if (VDBG) Log.d(LOG_TAG, " state: " + intent.getIntExtra("state", 0)); 1424 if (VDBG) Log.d(LOG_TAG, " name: " + intent.getStringExtra("name")); 1425 mIsHeadsetPlugged = (intent.getIntExtra("state", 0) == 1); 1426 mHandler.sendMessage(mHandler.obtainMessage(EVENT_WIRED_HEADSET_PLUG, 0)); 1427 } else if (action.equals(Intent.ACTION_BATTERY_LOW)) { 1428 if (VDBG) Log.d(LOG_TAG, "mReceiver: ACTION_BATTERY_LOW"); 1429 notifier.sendBatteryLow(); // Play a warning tone if in-call 1430 } else if ((action.equals(TelephonyIntents.ACTION_SIM_STATE_CHANGED)) && 1431 (mPUKEntryActivity != null)) { 1432 // if an attempt to un-PUK-lock the device was made, while we're 1433 // receiving this state change notification, notify the handler. 1434 // NOTE: This is ONLY triggered if an attempt to un-PUK-lock has 1435 // been attempted. 1436 mHandler.sendMessage(mHandler.obtainMessage(EVENT_SIM_STATE_CHANGED, 1437 intent.getStringExtra(IccCard.INTENT_KEY_ICC_STATE))); 1438 } else if (action.equals(TelephonyIntents.ACTION_RADIO_TECHNOLOGY_CHANGED)) { 1439 String newPhone = intent.getStringExtra(Phone.PHONE_NAME_KEY); 1440 Log.d(LOG_TAG, "Radio technology switched. Now " + newPhone + " is active."); 1441 initForNewRadioTechnology(); 1442 } else if (action.equals(TelephonyIntents.ACTION_SERVICE_STATE_CHANGED)) { 1443 handleServiceStateChanged(intent); 1444 } else if (action.equals(TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED)) { 1445 if (phone.getPhoneType() == Phone.PHONE_TYPE_CDMA) { 1446 Log.d(LOG_TAG, "Emergency Callback Mode arrived in PhoneApp."); 1447 // Start Emergency Callback Mode service 1448 if (intent.getBooleanExtra("phoneinECMState", false)) { 1449 context.startService(new Intent(context, 1450 EmergencyCallbackModeService.class)); 1451 } 1452 } else { 1453 Log.e(LOG_TAG, "Error! Emergency Callback Mode not supported for " + 1454 phone.getPhoneName() + " phones"); 1455 } 1456 } else if (action.equals(Intent.ACTION_DOCK_EVENT)) { 1457 mDockState = intent.getIntExtra(Intent.EXTRA_DOCK_STATE, 1458 Intent.EXTRA_DOCK_STATE_UNDOCKED); 1459 if (VDBG) Log.d(LOG_TAG, "ACTION_DOCK_EVENT -> mDockState = " + mDockState); 1460 mHandler.sendMessage(mHandler.obtainMessage(EVENT_DOCK_STATE_CHANGED, 0)); 1461 } else if (action.equals(TtyIntent.TTY_PREFERRED_MODE_CHANGE_ACTION)) { 1462 mPreferredTtyMode = intent.getIntExtra(TtyIntent.TTY_PREFFERED_MODE, 1463 Phone.TTY_MODE_OFF); 1464 if (VDBG) Log.d(LOG_TAG, "mReceiver: TTY_PREFERRED_MODE_CHANGE_ACTION"); 1465 if (VDBG) Log.d(LOG_TAG, " mode: " + mPreferredTtyMode); 1466 mHandler.sendMessage(mHandler.obtainMessage(EVENT_TTY_PREFERRED_MODE_CHANGED, 0)); 1467 } else if (action.equals(AudioManager.RINGER_MODE_CHANGED_ACTION)) { 1468 int ringerMode = intent.getIntExtra(AudioManager.EXTRA_RINGER_MODE, 1469 AudioManager.RINGER_MODE_NORMAL); 1470 if(ringerMode == AudioManager.RINGER_MODE_SILENT) { 1471 notifier.silenceRinger(); 1472 } 1473 } 1474 } 1475 } 1476 1477 /** 1478 * Broadcast receiver for the ACTION_MEDIA_BUTTON broadcast intent. 1479 * 1480 * This functionality isn't lumped in with the other intents in 1481 * PhoneAppBroadcastReceiver because we instantiate this as a totally 1482 * separate BroadcastReceiver instance, since we need to manually 1483 * adjust its IntentFilter's priority (to make sure we get these 1484 * intents *before* the media player.) 1485 */ 1486 private class MediaButtonBroadcastReceiver extends BroadcastReceiver { 1487 @Override 1488 public void onReceive(Context context, Intent intent) { 1489 KeyEvent event = (KeyEvent) intent.getParcelableExtra(Intent.EXTRA_KEY_EVENT); 1490 if (VDBG) Log.d(LOG_TAG, 1491 "MediaButtonBroadcastReceiver.onReceive()... event = " + event); 1492 if ((event != null) 1493 && (event.getKeyCode() == KeyEvent.KEYCODE_HEADSETHOOK)) { 1494 if (VDBG) Log.d(LOG_TAG, "MediaButtonBroadcastReceiver: HEADSETHOOK"); 1495 boolean consumed = PhoneUtils.handleHeadsetHook(phone, event); 1496 if (VDBG) Log.d(LOG_TAG, "==> handleHeadsetHook(): consumed = " + consumed); 1497 if (consumed) { 1498 // If a headset is attached and the press is consumed, also update 1499 // any UI items (such as an InCallScreen mute button) that may need to 1500 // be updated if their state changed. 1501 if (isShowingCallScreen()) { 1502 updateInCallScreenTouchUi(); 1503 } 1504 abortBroadcast(); 1505 } 1506 } else { 1507 if (mCM.getState() != Phone.State.IDLE) { 1508 // If the phone is anything other than completely idle, 1509 // then we consume and ignore any media key events, 1510 // Otherwise it is too easy to accidentally start 1511 // playing music while a phone call is in progress. 1512 if (VDBG) Log.d(LOG_TAG, "MediaButtonBroadcastReceiver: consumed"); 1513 abortBroadcast(); 1514 } 1515 } 1516 } 1517 } 1518 1519 private void handleServiceStateChanged(Intent intent) { 1520 /** 1521 * This used to handle updating EriTextWidgetProvider this routine 1522 * and and listening for ACTION_SERVICE_STATE_CHANGED intents could 1523 * be removed. But leaving just in case it might be needed in the near 1524 * future. 1525 */ 1526 1527 // If service just returned, start sending out the queued messages 1528 ServiceState ss = ServiceState.newFromBundle(intent.getExtras()); 1529 1530 boolean hasService = true; 1531 boolean isCdma = false; 1532 String eriText = ""; 1533 1534 if (ss != null) { 1535 int state = ss.getState(); 1536 NotificationMgr.getDefault().updateNetworkSelection(state); 1537 switch (state) { 1538 case ServiceState.STATE_OUT_OF_SERVICE: 1539 case ServiceState.STATE_POWER_OFF: 1540 hasService = false; 1541 break; 1542 } 1543 } else { 1544 hasService = false; 1545 } 1546 } 1547 1548 public boolean isOtaCallInActiveState() { 1549 boolean otaCallActive = false; 1550 if (mInCallScreen != null) { 1551 otaCallActive = mInCallScreen.isOtaCallInActiveState(); 1552 } 1553 if (VDBG) Log.d(LOG_TAG, "- isOtaCallInActiveState " + otaCallActive); 1554 return otaCallActive; 1555 } 1556 1557 public boolean isOtaCallInEndState() { 1558 boolean otaCallEnded = false; 1559 if (mInCallScreen != null) { 1560 otaCallEnded = mInCallScreen.isOtaCallInEndState(); 1561 } 1562 if (VDBG) Log.d(LOG_TAG, "- isOtaCallInEndState " + otaCallEnded); 1563 return otaCallEnded; 1564 } 1565 1566 // it is safe to call clearOtaState() even if the InCallScreen isn't active 1567 public void clearOtaState() { 1568 if (DBG) Log.d(LOG_TAG, "- clearOtaState ..."); 1569 if ((mInCallScreen != null) 1570 && (mInCallScreen.otaUtils != null)) { 1571 mInCallScreen.otaUtils.cleanOtaScreen(true); 1572 if (DBG) Log.d(LOG_TAG, " - clearOtaState clears OTA screen"); 1573 } 1574 } 1575 1576 // it is safe to call dismissOtaDialogs() even if the InCallScreen isn't active 1577 public void dismissOtaDialogs() { 1578 if (DBG) Log.d(LOG_TAG, "- dismissOtaDialogs ..."); 1579 if ((mInCallScreen != null) 1580 && (mInCallScreen.otaUtils != null)) { 1581 mInCallScreen.otaUtils.dismissAllOtaDialogs(); 1582 if (DBG) Log.d(LOG_TAG, " - dismissOtaDialogs clears OTA dialogs"); 1583 } 1584 } 1585 1586 // it is safe to call clearInCallScreenMode() even if the InCallScreen isn't active 1587 public void clearInCallScreenMode() { 1588 if (DBG) Log.d(LOG_TAG, "- clearInCallScreenMode ..."); 1589 if (mInCallScreen != null) { 1590 mInCallScreen.resetInCallScreenMode(); 1591 } 1592 } 1593 1594 // Update InCallScreen's touch UI. It is safe to call even if InCallScreen isn't active 1595 public void updateInCallScreenTouchUi() { 1596 if (DBG) Log.d(LOG_TAG, "- updateInCallScreenTouchUi ..."); 1597 if (mInCallScreen != null) { 1598 mInCallScreen.requestUpdateTouchUi(); 1599 } 1600 } 1601 1602 private void handleQueryTTYModeResponse(Message msg) { 1603 AsyncResult ar = (AsyncResult) msg.obj; 1604 if (ar.exception != null) { 1605 if (DBG) Log.d(LOG_TAG, "handleQueryTTYModeResponse: Error getting TTY state."); 1606 } else { 1607 if (DBG) Log.d(LOG_TAG, 1608 "handleQueryTTYModeResponse: TTY enable state successfully queried."); 1609 1610 int ttymode = ((int[]) ar.result)[0]; 1611 if (DBG) Log.d(LOG_TAG, "handleQueryTTYModeResponse:ttymode=" + ttymode); 1612 1613 Intent ttyModeChanged = new Intent(TtyIntent.TTY_ENABLED_CHANGE_ACTION); 1614 ttyModeChanged.putExtra("ttyEnabled", ttymode != Phone.TTY_MODE_OFF); 1615 sendBroadcast(ttyModeChanged); 1616 1617 String audioTtyMode; 1618 switch (ttymode) { 1619 case Phone.TTY_MODE_FULL: 1620 audioTtyMode = "tty_full"; 1621 break; 1622 case Phone.TTY_MODE_VCO: 1623 audioTtyMode = "tty_vco"; 1624 break; 1625 case Phone.TTY_MODE_HCO: 1626 audioTtyMode = "tty_hco"; 1627 break; 1628 case Phone.TTY_MODE_OFF: 1629 default: 1630 audioTtyMode = "tty_off"; 1631 break; 1632 } 1633 AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE); 1634 audioManager.setParameters("tty_mode="+audioTtyMode); 1635 } 1636 } 1637 1638 private void handleSetTTYModeResponse(Message msg) { 1639 AsyncResult ar = (AsyncResult) msg.obj; 1640 1641 if (ar.exception != null) { 1642 if (DBG) Log.d (LOG_TAG, 1643 "handleSetTTYModeResponse: Error setting TTY mode, ar.exception" 1644 + ar.exception); 1645 } 1646 phone.queryTTYMode(mHandler.obtainMessage(EVENT_TTY_MODE_GET)); 1647 } 1648 1649 /* package */ void clearUserActivityTimeout() { 1650 try { 1651 mPowerManagerService.clearUserActivityTimeout(SystemClock.uptimeMillis(), 1652 10*1000 /* 10 sec */); 1653 } catch (RemoteException ex) { 1654 // System process is dead. 1655 } 1656 } 1657} 1658