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