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