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