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