blob: 165ae67b9f5014a42e5aae3d0cece3967c401792 [file] [log] [blame]
Santos Cordon7d4ddf62013-07-10 11:58:08 -07001/*
2 * Copyright (C) 2006 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.phone;
18
19import android.app.Activity;
20import android.app.KeyguardManager;
21import android.app.PendingIntent;
22import android.app.ProgressDialog;
23import android.bluetooth.BluetoothAdapter;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070024import android.bluetooth.IBluetoothHeadsetPhone;
25import android.content.ActivityNotFoundException;
26import android.content.BroadcastReceiver;
27import android.content.ComponentName;
28import android.content.ContentResolver;
29import android.content.Context;
30import android.content.ContextWrapper;
31import android.content.Intent;
32import android.content.IntentFilter;
33import android.content.ServiceConnection;
34import android.content.res.Configuration;
35import android.media.AudioManager;
36import android.net.Uri;
37import android.os.AsyncResult;
38import android.os.Binder;
39import android.os.Handler;
40import android.os.IBinder;
41import android.os.IPowerManager;
42import android.os.Message;
43import android.os.PowerManager;
44import android.os.RemoteException;
45import android.os.ServiceManager;
46import android.os.SystemClock;
47import android.os.SystemProperties;
48import android.os.UpdateLock;
49import android.os.UserHandle;
50import android.preference.PreferenceManager;
51import android.provider.Settings.System;
52import android.telephony.ServiceState;
53import android.text.TextUtils;
54import android.util.Log;
55import android.util.Slog;
56import android.view.KeyEvent;
57
58import com.android.internal.telephony.Call;
59import com.android.internal.telephony.CallManager;
60import com.android.internal.telephony.IccCard;
61import com.android.internal.telephony.IccCardConstants;
62import com.android.internal.telephony.MmiCode;
63import com.android.internal.telephony.Phone;
64import com.android.internal.telephony.PhoneConstants;
65import com.android.internal.telephony.PhoneFactory;
66import com.android.internal.telephony.TelephonyCapabilities;
67import com.android.internal.telephony.TelephonyIntents;
68import com.android.internal.telephony.cdma.TtyIntent;
69import com.android.phone.common.CallLogAsync;
70import com.android.phone.OtaUtils.CdmaOtaScreenState;
Santos Cordon593ab382013-08-06 21:58:23 -070071import com.android.phone.WiredHeadsetManager.WiredHeadsetListener;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070072import com.android.server.sip.SipService;
Santos Cordon593ab382013-08-06 21:58:23 -070073import com.android.services.telephony.common.AudioMode;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070074
75/**
76 * Global state for the telephony subsystem when running in the primary
77 * phone process.
78 */
Santos Cordonfc309812013-08-20 18:33:16 -070079public class PhoneGlobals extends ContextWrapper implements WiredHeadsetListener {
Santos Cordon7d4ddf62013-07-10 11:58:08 -070080 /* 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;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700106 private static final int EVENT_SIM_STATE_CHANGED = 8;
107 private static final int EVENT_UPDATE_INCALL_NOTIFICATION = 9;
108 private static final int EVENT_DATA_ROAMING_DISCONNECTED = 10;
109 private static final int EVENT_DATA_ROAMING_OK = 11;
110 private static final int EVENT_UNSOL_CDMA_INFO_RECORD = 12;
111 private static final int EVENT_DOCK_STATE_CHANGED = 13;
112 private static final int EVENT_TTY_PREFERRED_MODE_CHANGED = 14;
113 private static final int EVENT_TTY_MODE_GET = 15;
114 private static final int EVENT_TTY_MODE_SET = 16;
115 private static final int EVENT_START_SIP_SERVICE = 17;
116
117 // The MMI codes are also used by the InCallScreen.
118 public static final int MMI_INITIATE = 51;
119 public static final int MMI_COMPLETE = 52;
120 public static final int MMI_CANCEL = 53;
121 // Don't use message codes larger than 99 here; those are reserved for
122 // the individual Activities of the Phone UI.
123
124 /**
125 * Allowable values for the wake lock code.
126 * SLEEP means the device can be put to sleep.
127 * PARTIAL means wake the processor, but we display can be kept off.
128 * FULL means wake both the processor and the display.
129 */
130 public enum WakeState {
131 SLEEP,
132 PARTIAL,
133 FULL
134 }
135
136 /**
137 * Intent Action used for hanging up the current call from Notification bar. This will
138 * choose first ringing call, first active call, or first background call (typically in
139 * HOLDING state).
140 */
141 public static final String ACTION_HANG_UP_ONGOING_CALL =
142 "com.android.phone.ACTION_HANG_UP_ONGOING_CALL";
143
144 /**
145 * Intent Action used for making a phone call from Notification bar.
146 * This is for missed call notifications.
147 */
148 public static final String ACTION_CALL_BACK_FROM_NOTIFICATION =
149 "com.android.phone.ACTION_CALL_BACK_FROM_NOTIFICATION";
150
151 /**
152 * Intent Action used for sending a SMS from notification bar.
153 * This is for missed call notifications.
154 */
155 public static final String ACTION_SEND_SMS_FROM_NOTIFICATION =
156 "com.android.phone.ACTION_SEND_SMS_FROM_NOTIFICATION";
157
158 private static PhoneGlobals sMe;
159
160 // A few important fields we expose to the rest of the package
161 // directly (rather than thru set/get methods) for efficiency.
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700162 CallController callController;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700163 CallManager mCM;
Santos Cordon63a84242013-07-23 13:32:52 -0700164 CallNotifier notifier;
165 CallerInfoCache callerInfoCache;
166 InCallUiState inCallUiState;
167 NotificationMgr notificationMgr;
168 Phone phone;
169 PhoneInterfaceManager phoneMgr;
170
Santos Cordon9b7bac72013-08-06 08:04:52 -0700171 private AudioRouter audioRouter;
Santos Cordon27a3c1f2013-08-06 07:49:27 -0700172 private BluetoothManager bluetoothManager;
Santos Cordon63a84242013-07-23 13:32:52 -0700173 private CallCommandService callCommandService;
Santos Cordon69a69192013-08-22 14:25:42 -0700174 private CallGatewayManager callGatewayManager;
Santos Cordon63a84242013-07-23 13:32:52 -0700175 private CallHandlerServiceProxy callHandlerServiceProxy;
176 private CallModeler callModeler;
177 private CallStateMonitor callStateMonitor;
Santos Cordon2eaff902013-08-05 04:37:55 -0700178 private DTMFTonePlayer dtmfTonePlayer;
Christine Chenee09a492013-08-06 16:02:29 -0700179 private RejectWithTextMessageManager rejectWithTextMessageManager;
Santos Cordon63a84242013-07-23 13:32:52 -0700180 private IBluetoothHeadsetPhone mBluetoothPhone;
181 private Ringer ringer;
Santos Cordon593ab382013-08-06 21:58:23 -0700182 private WiredHeadsetManager wiredHeadsetManager;
Santos Cordon63a84242013-07-23 13:32:52 -0700183
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700184 static int mDockState = Intent.EXTRA_DOCK_STATE_UNDOCKED;
185 static boolean sVoiceCapable = true;
186
187 // Internal PhoneApp Call state tracker
188 CdmaPhoneCallState cdmaPhoneCallState;
189
190 // The InCallScreen instance (or null if the InCallScreen hasn't been
191 // created yet.)
192 private InCallScreen mInCallScreen;
193
194 // The currently-active PUK entry activity and progress dialog.
195 // Normally, these are the Emergency Dialer and the subsequent
196 // progress dialog. null if there is are no such objects in
197 // the foreground.
198 private Activity mPUKEntryActivity;
199 private ProgressDialog mPUKEntryProgressDialog;
200
201 private boolean mIsSimPinEnabled;
202 private String mCachedSimPin;
203
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700204 // True if we are beginning a call, but the phone state has not changed yet
205 private boolean mBeginningCall;
206
207 // Last phone state seen by updatePhoneState()
208 private PhoneConstants.State mLastPhoneState = PhoneConstants.State.IDLE;
209
210 private WakeState mWakeState = WakeState.SLEEP;
211
212 private PowerManager mPowerManager;
213 private IPowerManager mPowerManagerService;
214 private PowerManager.WakeLock mWakeLock;
215 private PowerManager.WakeLock mPartialWakeLock;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700216 private KeyguardManager mKeyguardManager;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700217
218 private UpdateLock mUpdateLock;
219
220 // Broadcast receiver for various intent broadcasts (see onCreate())
221 private final BroadcastReceiver mReceiver = new PhoneAppBroadcastReceiver();
222
223 // Broadcast receiver purely for ACTION_MEDIA_BUTTON broadcasts
224 private final BroadcastReceiver mMediaButtonReceiver = new MediaButtonBroadcastReceiver();
225
226 /** boolean indicating restoring mute state on InCallScreen.onResume() */
227 private boolean mShouldRestoreMuteOnInCallResume;
228
229 /**
230 * The singleton OtaUtils instance used for OTASP calls.
231 *
232 * The OtaUtils instance is created lazily the first time we need to
233 * make an OTASP call, regardless of whether it's an interactive or
234 * non-interactive OTASP call.
235 */
236 public OtaUtils otaUtils;
237
238 // Following are the CDMA OTA information Objects used during OTA Call.
239 // cdmaOtaProvisionData object store static OTA information that needs
240 // to be maintained even during Slider open/close scenarios.
241 // cdmaOtaConfigData object stores configuration info to control visiblity
242 // of each OTA Screens.
243 // cdmaOtaScreenState object store OTA Screen State information.
244 public OtaUtils.CdmaOtaProvisionData cdmaOtaProvisionData;
245 public OtaUtils.CdmaOtaConfigData cdmaOtaConfigData;
246 public OtaUtils.CdmaOtaScreenState cdmaOtaScreenState;
247 public OtaUtils.CdmaOtaInCallScreenUiState cdmaOtaInCallScreenUiState;
248
249 // TTY feature enabled on this platform
250 private boolean mTtyEnabled;
251 // Current TTY operating mode selected by user
252 private int mPreferredTtyMode = Phone.TTY_MODE_OFF;
253
254 /**
255 * Set the restore mute state flag. Used when we are setting the mute state
256 * OUTSIDE of user interaction {@link PhoneUtils#startNewCall(Phone)}
257 */
258 /*package*/void setRestoreMuteOnInCallResume (boolean mode) {
259 mShouldRestoreMuteOnInCallResume = mode;
260 }
261
262 /**
263 * Get the restore mute state flag.
264 * This is used by the InCallScreen {@link InCallScreen#onResume()} to figure
265 * out if we need to restore the mute state for the current active call.
266 */
267 /*package*/boolean getRestoreMuteOnInCallResume () {
268 return mShouldRestoreMuteOnInCallResume;
269 }
270
271 Handler mHandler = new Handler() {
272 @Override
273 public void handleMessage(Message msg) {
274 PhoneConstants.State phoneState;
275 switch (msg.what) {
276 // Starts the SIP service. It's a no-op if SIP API is not supported
277 // on the deivce.
278 // TODO: Having the phone process host the SIP service is only
279 // temporary. Will move it to a persistent communication process
280 // later.
281 case EVENT_START_SIP_SERVICE:
282 SipService.start(getApplicationContext());
283 break;
284
285 // TODO: This event should be handled by the lock screen, just
286 // like the "SIM missing" and "Sim locked" cases (bug 1804111).
287 case EVENT_SIM_NETWORK_LOCKED:
288 if (getResources().getBoolean(R.bool.ignore_sim_network_locked_events)) {
289 // Some products don't have the concept of a "SIM network lock"
290 Log.i(LOG_TAG, "Ignoring EVENT_SIM_NETWORK_LOCKED event; "
291 + "not showing 'SIM network unlock' PIN entry screen");
292 } else {
293 // Normal case: show the "SIM network unlock" PIN entry screen.
294 // The user won't be able to do anything else until
295 // they enter a valid SIM network PIN.
296 Log.i(LOG_TAG, "show sim depersonal panel");
297 IccNetworkDepersonalizationPanel ndpPanel =
298 new IccNetworkDepersonalizationPanel(PhoneGlobals.getInstance());
299 ndpPanel.show();
300 }
301 break;
302
303 case EVENT_UPDATE_INCALL_NOTIFICATION:
304 // Tell the NotificationMgr to update the "ongoing
305 // call" icon in the status bar, if necessary.
306 // Currently, this is triggered by a bluetooth headset
307 // state change (since the status bar icon needs to
308 // turn blue when bluetooth is active.)
309 if (DBG) Log.d (LOG_TAG, "- updating in-call notification from handler...");
310 notificationMgr.updateInCallNotification();
311 break;
312
313 case EVENT_DATA_ROAMING_DISCONNECTED:
314 notificationMgr.showDataDisconnectedRoaming();
315 break;
316
317 case EVENT_DATA_ROAMING_OK:
318 notificationMgr.hideDataDisconnectedRoaming();
319 break;
320
321 case MMI_COMPLETE:
322 onMMIComplete((AsyncResult) msg.obj);
323 break;
324
325 case MMI_CANCEL:
326 PhoneUtils.cancelMmiCode(phone);
327 break;
328
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700329 case EVENT_SIM_STATE_CHANGED:
330 // Marks the event where the SIM goes into ready state.
331 // Right now, this is only used for the PUK-unlocking
332 // process.
333 if (msg.obj.equals(IccCardConstants.INTENT_VALUE_ICC_READY)) {
334 // when the right event is triggered and there
335 // are UI objects in the foreground, we close
336 // them to display the lock panel.
337 if (mPUKEntryActivity != null) {
338 mPUKEntryActivity.finish();
339 mPUKEntryActivity = null;
340 }
341 if (mPUKEntryProgressDialog != null) {
342 mPUKEntryProgressDialog.dismiss();
343 mPUKEntryProgressDialog = null;
344 }
345 }
346 break;
347
348 case EVENT_UNSOL_CDMA_INFO_RECORD:
349 //TODO: handle message here;
350 break;
351
352 case EVENT_DOCK_STATE_CHANGED:
353 // If the phone is docked/undocked during a call, and no wired or BT headset
354 // is connected: turn on/off the speaker accordingly.
355 boolean inDockMode = false;
356 if (mDockState != Intent.EXTRA_DOCK_STATE_UNDOCKED) {
357 inDockMode = true;
358 }
359 if (VDBG) Log.d(LOG_TAG, "received EVENT_DOCK_STATE_CHANGED. Phone inDock = "
360 + inDockMode);
361
362 phoneState = mCM.getState();
363 if (phoneState == PhoneConstants.State.OFFHOOK &&
Santos Cordon593ab382013-08-06 21:58:23 -0700364 !wiredHeadsetManager.isHeadsetPlugged() &&
365 !bluetoothManager.isBluetoothHeadsetAudioOn()) {
366 audioRouter.setSpeaker(inDockMode);
367
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700368 PhoneUtils.turnOnSpeaker(getApplicationContext(), inDockMode, true);
369 updateInCallScreen(); // Has no effect if the InCallScreen isn't visible
370 }
371 break;
372
373 case EVENT_TTY_PREFERRED_MODE_CHANGED:
374 // TTY mode is only applied if a headset is connected
375 int ttyMode;
Santos Cordon593ab382013-08-06 21:58:23 -0700376 if (wiredHeadsetManager.isHeadsetPlugged()) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700377 ttyMode = mPreferredTtyMode;
378 } else {
379 ttyMode = Phone.TTY_MODE_OFF;
380 }
381 phone.setTTYMode(ttyMode, mHandler.obtainMessage(EVENT_TTY_MODE_SET));
382 break;
383
384 case EVENT_TTY_MODE_GET:
385 handleQueryTTYModeResponse(msg);
386 break;
387
388 case EVENT_TTY_MODE_SET:
389 handleSetTTYModeResponse(msg);
390 break;
391 }
392 }
393 };
394
395 public PhoneGlobals(Context context) {
396 super(context);
397 sMe = this;
398 }
399
400 public void onCreate() {
401 if (VDBG) Log.v(LOG_TAG, "onCreate()...");
402
403 ContentResolver resolver = getContentResolver();
404
405 // Cache the "voice capable" flag.
406 // This flag currently comes from a resource (which is
407 // overrideable on a per-product basis):
408 sVoiceCapable =
409 getResources().getBoolean(com.android.internal.R.bool.config_voice_capable);
410 // ...but this might eventually become a PackageManager "system
411 // feature" instead, in which case we'd do something like:
412 // sVoiceCapable =
413 // getPackageManager().hasSystemFeature(PackageManager.FEATURE_TELEPHONY_VOICE_CALLS);
414
415 if (phone == null) {
416 // Initialize the telephony framework
417 PhoneFactory.makeDefaultPhones(this);
418
419 // Get the default phone
420 phone = PhoneFactory.getDefaultPhone();
421
422 // Start TelephonyDebugService After the default phone is created.
423 Intent intent = new Intent(this, TelephonyDebugService.class);
424 startService(intent);
425
426 mCM = CallManager.getInstance();
427 mCM.registerPhone(phone);
428
429 // Create the NotificationMgr singleton, which is used to display
430 // status bar icons and control other status bar behavior.
431 notificationMgr = NotificationMgr.init(this);
432
433 phoneMgr = PhoneInterfaceManager.init(this, phone);
434
435 mHandler.sendEmptyMessage(EVENT_START_SIP_SERVICE);
436
437 int phoneType = phone.getPhoneType();
438
439 if (phoneType == PhoneConstants.PHONE_TYPE_CDMA) {
440 // Create an instance of CdmaPhoneCallState and initialize it to IDLE
441 cdmaPhoneCallState = new CdmaPhoneCallState();
442 cdmaPhoneCallState.CdmaPhoneCallStateInit();
443 }
444
445 if (BluetoothAdapter.getDefaultAdapter() != null) {
446 // Start BluetoothPhoneService even if device is not voice capable.
447 // The device can still support VOIP.
448 startService(new Intent(this, BluetoothPhoneService.class));
449 bindService(new Intent(this, BluetoothPhoneService.class),
450 mBluetoothPhoneConnection, 0);
451 } else {
452 // Device is not bluetooth capable
453 mBluetoothPhone = null;
454 }
455
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700456 // before registering for phone state changes
457 mPowerManager = (PowerManager) getSystemService(Context.POWER_SERVICE);
458 mWakeLock = mPowerManager.newWakeLock(PowerManager.FULL_WAKE_LOCK, LOG_TAG);
459 // lock used to keep the processor awake, when we don't care for the display.
460 mPartialWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK
461 | PowerManager.ON_AFTER_RELEASE, LOG_TAG);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700462
463 mKeyguardManager = (KeyguardManager) getSystemService(Context.KEYGUARD_SERVICE);
464
465 // get a handle to the service so that we can use it later when we
466 // want to set the poke lock.
467 mPowerManagerService = IPowerManager.Stub.asInterface(
468 ServiceManager.getService("power"));
469
470 // Get UpdateLock to suppress system-update related events (e.g. dialog show-up)
471 // during phone calls.
472 mUpdateLock = new UpdateLock("phone");
473
474 if (DBG) Log.d(LOG_TAG, "onCreate: mUpdateLock: " + mUpdateLock);
475
476 CallLogger callLogger = new CallLogger(this, new CallLogAsync());
477
Santos Cordon69a69192013-08-22 14:25:42 -0700478 callGatewayManager = new CallGatewayManager();
479
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700480 // Create the CallController singleton, which is the interface
481 // to the telephony layer for user-initiated telephony functionality
482 // (like making outgoing calls.)
Santos Cordon69a69192013-08-22 14:25:42 -0700483 callController = CallController.init(this, callLogger, callGatewayManager);
484
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700485 // ...and also the InCallUiState instance, used by the CallController to
486 // keep track of some "persistent state" of the in-call UI.
487 inCallUiState = InCallUiState.init(this);
488
489 // Create the CallerInfoCache singleton, which remembers custom ring tone and
490 // send-to-voicemail settings.
491 //
492 // The asynchronous caching will start just after this call.
493 callerInfoCache = CallerInfoCache.init(this);
494
495 // Monitors call activity from the telephony layer
496 callStateMonitor = new CallStateMonitor(mCM);
497
Christine Chenee09a492013-08-06 16:02:29 -0700498 // Rejects calls with TextMessages
499 rejectWithTextMessageManager = new RejectWithTextMessageManager();
500
Santos Cordon63a84242013-07-23 13:32:52 -0700501 // Creates call models for use with CallHandlerService.
Santos Cordon69a69192013-08-22 14:25:42 -0700502 callModeler = new CallModeler(callStateMonitor, mCM, rejectWithTextMessageManager,
503 callGatewayManager);
Santos Cordon63a84242013-07-23 13:32:52 -0700504
Santos Cordon2eaff902013-08-05 04:37:55 -0700505 // Plays DTMF Tones
506 dtmfTonePlayer = new DTMFTonePlayer(mCM, callModeler);
507
Santos Cordon593ab382013-08-06 21:58:23 -0700508 // Manages wired headset state
509 wiredHeadsetManager = new WiredHeadsetManager(this);
510 wiredHeadsetManager.addWiredHeadsetListener(this);
511
Santos Cordon2c2d3cf2013-08-08 03:53:47 -0700512 // Bluetooth manager
513 bluetoothManager = new BluetoothManager(this, mCM, callModeler);
Santos Cordon2c2d3cf2013-08-08 03:53:47 -0700514
515 ringer = Ringer.init(this, bluetoothManager);
516
Santos Cordon9b7bac72013-08-06 08:04:52 -0700517 // Audio router
Santos Cordon593ab382013-08-06 21:58:23 -0700518 audioRouter = new AudioRouter(this, bluetoothManager, wiredHeadsetManager, mCM);
Santos Cordon9b7bac72013-08-06 08:04:52 -0700519
Santos Cordon249efd02013-08-05 03:33:56 -0700520 // Service used by in-call UI to control calls
Santos Cordon9b7bac72013-08-06 08:04:52 -0700521 callCommandService = new CallCommandService(this, mCM, callModeler, dtmfTonePlayer,
Christine Chenee09a492013-08-06 16:02:29 -0700522 audioRouter, rejectWithTextMessageManager);
Santos Cordon249efd02013-08-05 03:33:56 -0700523
Santos Cordon89647a62013-07-16 13:38:09 -0700524 // Sends call state to the UI
Santos Cordon63a84242013-07-23 13:32:52 -0700525 callHandlerServiceProxy = new CallHandlerServiceProxy(this, callModeler,
Santos Cordon593ab382013-08-06 21:58:23 -0700526 callCommandService, audioRouter);
Santos Cordon89647a62013-07-16 13:38:09 -0700527
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700528 // Create the CallNotifer singleton, which handles
529 // asynchronous events from the telephony layer (like
530 // launching the incoming-call UI when an incoming call comes
531 // in.)
Santos Cordon27a3c1f2013-08-06 07:49:27 -0700532 notifier = CallNotifier.init(this, phone, ringer, callLogger, callStateMonitor,
533 bluetoothManager);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700534
535 // register for ICC status
536 IccCard sim = phone.getIccCard();
537 if (sim != null) {
538 if (VDBG) Log.v(LOG_TAG, "register for ICC status");
539 sim.registerForNetworkLocked(mHandler, EVENT_SIM_NETWORK_LOCKED, null);
540 }
541
542 // register for MMI/USSD
543 mCM.registerForMmiComplete(mHandler, MMI_COMPLETE, null);
544
545 // register connection tracking to PhoneUtils
546 PhoneUtils.initializeConnectionHandler(mCM);
547
548 // Read platform settings for TTY feature
549 mTtyEnabled = getResources().getBoolean(R.bool.tty_enabled);
550
551 // Register for misc other intent broadcasts.
552 IntentFilter intentFilter =
553 new IntentFilter(Intent.ACTION_AIRPLANE_MODE_CHANGED);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700554 intentFilter.addAction(TelephonyIntents.ACTION_ANY_DATA_CONNECTION_STATE_CHANGED);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700555 intentFilter.addAction(Intent.ACTION_DOCK_EVENT);
556 intentFilter.addAction(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
557 intentFilter.addAction(TelephonyIntents.ACTION_RADIO_TECHNOLOGY_CHANGED);
558 intentFilter.addAction(TelephonyIntents.ACTION_SERVICE_STATE_CHANGED);
559 intentFilter.addAction(TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED);
560 if (mTtyEnabled) {
561 intentFilter.addAction(TtyIntent.TTY_PREFERRED_MODE_CHANGE_ACTION);
562 }
563 intentFilter.addAction(AudioManager.RINGER_MODE_CHANGED_ACTION);
564 registerReceiver(mReceiver, intentFilter);
565
566 // Use a separate receiver for ACTION_MEDIA_BUTTON broadcasts,
567 // since we need to manually adjust its priority (to make sure
568 // we get these intents *before* the media player.)
569 IntentFilter mediaButtonIntentFilter =
570 new IntentFilter(Intent.ACTION_MEDIA_BUTTON);
571 // TODO verify the independent priority doesn't need to be handled thanks to the
572 // private intent handler registration
573 // Make sure we're higher priority than the media player's
574 // MediaButtonIntentReceiver (which currently has the default
575 // priority of zero; see apps/Music/AndroidManifest.xml.)
576 mediaButtonIntentFilter.setPriority(1);
577 //
578 registerReceiver(mMediaButtonReceiver, mediaButtonIntentFilter);
579 // register the component so it gets priority for calls
580 AudioManager am = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
581 am.registerMediaButtonEventReceiverForCalls(new ComponentName(this.getPackageName(),
582 MediaButtonBroadcastReceiver.class.getName()));
583
584 //set the default values for the preferences in the phone.
585 PreferenceManager.setDefaultValues(this, R.xml.network_setting, false);
586
587 PreferenceManager.setDefaultValues(this, R.xml.call_feature_setting, false);
588
589 // Make sure the audio mode (along with some
590 // audio-mode-related state of our own) is initialized
591 // correctly, given the current state of the phone.
592 PhoneUtils.setAudioMode(mCM);
593 }
594
595 if (TelephonyCapabilities.supportsOtasp(phone)) {
596 cdmaOtaProvisionData = new OtaUtils.CdmaOtaProvisionData();
597 cdmaOtaConfigData = new OtaUtils.CdmaOtaConfigData();
598 cdmaOtaScreenState = new OtaUtils.CdmaOtaScreenState();
599 cdmaOtaInCallScreenUiState = new OtaUtils.CdmaOtaInCallScreenUiState();
600 }
601
602 // XXX pre-load the SimProvider so that it's ready
603 resolver.getType(Uri.parse("content://icc/adn"));
604
605 // start with the default value to set the mute state.
606 mShouldRestoreMuteOnInCallResume = false;
607
608 // TODO: Register for Cdma Information Records
609 // phone.registerCdmaInformationRecord(mHandler, EVENT_UNSOL_CDMA_INFO_RECORD, null);
610
611 // Read TTY settings and store it into BP NV.
612 // AP owns (i.e. stores) the TTY setting in AP settings database and pushes the setting
613 // to BP at power up (BP does not need to make the TTY setting persistent storage).
614 // This way, there is a single owner (i.e AP) for the TTY setting in the phone.
615 if (mTtyEnabled) {
616 mPreferredTtyMode = android.provider.Settings.Secure.getInt(
617 phone.getContext().getContentResolver(),
618 android.provider.Settings.Secure.PREFERRED_TTY_MODE,
619 Phone.TTY_MODE_OFF);
620 mHandler.sendMessage(mHandler.obtainMessage(EVENT_TTY_PREFERRED_MODE_CHANGED, 0));
621 }
622 // Read HAC settings and configure audio hardware
623 if (getResources().getBoolean(R.bool.hac_enabled)) {
624 int hac = android.provider.Settings.System.getInt(phone.getContext().getContentResolver(),
625 android.provider.Settings.System.HEARING_AID,
626 0);
627 AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
628 audioManager.setParameter(CallFeaturesSetting.HAC_KEY, hac != 0 ?
629 CallFeaturesSetting.HAC_VAL_ON :
630 CallFeaturesSetting.HAC_VAL_OFF);
631 }
632 }
633
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700634 /**
635 * Returns the singleton instance of the PhoneApp.
636 */
637 static PhoneGlobals getInstance() {
638 if (sMe == null) {
639 throw new IllegalStateException("No PhoneGlobals here!");
640 }
641 return sMe;
642 }
643
644 /**
645 * Returns the singleton instance of the PhoneApp if running as the
646 * primary user, otherwise null.
647 */
648 static PhoneGlobals getInstanceIfPrimary() {
649 return sMe;
650 }
651
652 /**
653 * Returns the Phone associated with this instance
654 */
655 static Phone getPhone() {
656 return getInstance().phone;
657 }
658
659 Ringer getRinger() {
660 return ringer;
661 }
662
663 IBluetoothHeadsetPhone getBluetoothPhoneService() {
664 return mBluetoothPhone;
665 }
666
Santos Cordon27a3c1f2013-08-06 07:49:27 -0700667 /* package */ BluetoothManager getBluetoothManager() {
668 return bluetoothManager;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700669 }
670
Santos Cordon593ab382013-08-06 21:58:23 -0700671 /* package */ WiredHeadsetManager getWiredHeadsetManager() {
672 return wiredHeadsetManager;
673 }
674
675 /* package */ AudioRouter getAudioRouter() {
676 return audioRouter;
677 }
678
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700679 /**
680 * Returns an Intent that can be used to go to the "Call log"
681 * UI (aka CallLogActivity) in the Contacts app.
682 *
683 * Watch out: there's no guarantee that the system has any activity to
684 * handle this intent. (In particular there may be no "Call log" at
685 * all on on non-voice-capable devices.)
686 */
687 /* package */ static Intent createCallLogIntent() {
688 Intent intent = new Intent(Intent.ACTION_VIEW, null);
689 intent.setType("vnd.android.cursor.dir/calls");
690 return intent;
691 }
692
693 /**
694 * Return an Intent that can be used to bring up the in-call screen.
695 *
696 * This intent can only be used from within the Phone app, since the
697 * InCallScreen is not exported from our AndroidManifest.
698 */
699 /* package */ static Intent createInCallIntent() {
700 Intent intent = new Intent(Intent.ACTION_MAIN, null);
701 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
702 | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
703 | Intent.FLAG_ACTIVITY_NO_USER_ACTION);
704 intent.setClassName("com.android.phone", getCallScreenClassName());
705 return intent;
706 }
707
708 /**
709 * Variation of createInCallIntent() that also specifies whether the
710 * DTMF dialpad should be initially visible when the InCallScreen
711 * comes up.
712 */
713 /* package */ static Intent createInCallIntent(boolean showDialpad) {
714 Intent intent = createInCallIntent();
715 intent.putExtra(InCallScreen.SHOW_DIALPAD_EXTRA, showDialpad);
716 return intent;
717 }
718
719 /**
720 * Returns PendingIntent for hanging up ongoing phone call. This will typically be used from
721 * Notification context.
722 */
723 /* package */ static PendingIntent createHangUpOngoingCallPendingIntent(Context context) {
724 Intent intent = new Intent(PhoneGlobals.ACTION_HANG_UP_ONGOING_CALL, null,
725 context, NotificationBroadcastReceiver.class);
726 return PendingIntent.getBroadcast(context, 0, intent, 0);
727 }
728
729 /* package */ static PendingIntent getCallBackPendingIntent(Context context, String number) {
730 Intent intent = new Intent(ACTION_CALL_BACK_FROM_NOTIFICATION,
731 Uri.fromParts(Constants.SCHEME_TEL, number, null),
732 context, NotificationBroadcastReceiver.class);
733 return PendingIntent.getBroadcast(context, 0, intent, 0);
734 }
735
736 /* package */ static PendingIntent getSendSmsFromNotificationPendingIntent(
737 Context context, String number) {
738 Intent intent = new Intent(ACTION_SEND_SMS_FROM_NOTIFICATION,
739 Uri.fromParts(Constants.SCHEME_SMSTO, number, null),
740 context, NotificationBroadcastReceiver.class);
741 return PendingIntent.getBroadcast(context, 0, intent, 0);
742 }
743
744 private static String getCallScreenClassName() {
Chiao Cheng7f7c6522013-07-16 18:39:35 -0700745 //InCallScreen.class.getName();
746 return "blah";
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700747 }
748
749 /**
750 * Starts the InCallScreen Activity.
751 */
752 /* package */ void displayCallScreen() {
753 if (VDBG) Log.d(LOG_TAG, "displayCallScreen()...");
754
755 // On non-voice-capable devices we shouldn't ever be trying to
756 // bring up the InCallScreen in the first place.
757 if (!sVoiceCapable) {
758 Log.w(LOG_TAG, "displayCallScreen() not allowed: non-voice-capable device",
759 new Throwable("stack dump")); // Include a stack trace since this warning
760 // indicates a bug in our caller
761 return;
762 }
763
764 try {
Chiao Cheng7f7c6522013-07-16 18:39:35 -0700765 //startActivity(createInCallIntent());
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700766 } catch (ActivityNotFoundException e) {
767 // It's possible that the in-call UI might not exist (like on
768 // non-voice-capable devices), so don't crash if someone
769 // accidentally tries to bring it up...
770 Log.w(LOG_TAG, "displayCallScreen: transition to InCallScreen failed: " + e);
771 }
772 Profiler.callScreenRequested();
773 }
774
775 boolean isSimPinEnabled() {
776 return mIsSimPinEnabled;
777 }
778
779 boolean authenticateAgainstCachedSimPin(String pin) {
780 return (mCachedSimPin != null && mCachedSimPin.equals(pin));
781 }
782
783 void setCachedSimPin(String pin) {
784 mCachedSimPin = pin;
785 }
786
787 void setInCallScreenInstance(InCallScreen inCallScreen) {
788 mInCallScreen = inCallScreen;
789 }
790
791 /**
792 * @return true if the in-call UI is running as the foreground
793 * activity. (In other words, from the perspective of the
794 * InCallScreen activity, return true between onResume() and
795 * onPause().)
796 *
797 * Note this method will return false if the screen is currently off,
798 * even if the InCallScreen *was* in the foreground just before the
799 * screen turned off. (This is because the foreground activity is
800 * always "paused" while the screen is off.)
801 */
802 boolean isShowingCallScreen() {
803 if (mInCallScreen == null) return false;
804 return mInCallScreen.isForegroundActivity();
805 }
806
807 /**
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700808 * Dismisses the in-call UI.
809 *
810 * This also ensures that you won't be able to get back to the in-call
811 * UI via the BACK button (since this call removes the InCallScreen
812 * from the activity history.)
813 * For OTA Call, it call InCallScreen api to handle OTA Call End scenario
814 * to display OTA Call End screen.
815 */
816 /* package */ void dismissCallScreen() {
817 if (mInCallScreen != null) {
818 if ((TelephonyCapabilities.supportsOtasp(phone)) &&
819 (mInCallScreen.isOtaCallInActiveState()
820 || mInCallScreen.isOtaCallInEndState()
821 || ((cdmaOtaScreenState != null)
822 && (cdmaOtaScreenState.otaScreenState
823 != CdmaOtaScreenState.OtaScreenState.OTA_STATUS_UNDEFINED)))) {
824 // TODO: During OTA Call, display should not become dark to
825 // allow user to see OTA UI update. Phone app needs to hold
826 // a SCREEN_DIM_WAKE_LOCK wake lock during the entire OTA call.
827 wakeUpScreen();
828 // If InCallScreen is not in foreground we resume it to show the OTA call end screen
829 // Fire off the InCallScreen intent
830 displayCallScreen();
831
832 mInCallScreen.handleOtaCallEnd();
833 return;
834 } else {
835 mInCallScreen.finish();
836 }
837 }
838 }
839
840 /**
841 * Handles OTASP-related events from the telephony layer.
842 *
843 * While an OTASP call is active, the CallNotifier forwards
844 * OTASP-related telephony events to this method.
845 */
846 void handleOtaspEvent(Message msg) {
847 if (DBG) Log.d(LOG_TAG, "handleOtaspEvent(message " + msg + ")...");
848
849 if (otaUtils == null) {
850 // We shouldn't be getting OTASP events without ever
851 // having started the OTASP call in the first place!
852 Log.w(LOG_TAG, "handleOtaEvents: got an event but otaUtils is null! "
853 + "message = " + msg);
854 return;
855 }
856
857 otaUtils.onOtaProvisionStatusChanged((AsyncResult) msg.obj);
858 }
859
860 /**
861 * Similarly, handle the disconnect event of an OTASP call
862 * by forwarding it to the OtaUtils instance.
863 */
864 /* package */ void handleOtaspDisconnect() {
865 if (DBG) Log.d(LOG_TAG, "handleOtaspDisconnect()...");
866
867 if (otaUtils == null) {
868 // We shouldn't be getting OTASP events without ever
869 // having started the OTASP call in the first place!
870 Log.w(LOG_TAG, "handleOtaspDisconnect: otaUtils is null!");
871 return;
872 }
873
874 otaUtils.onOtaspDisconnect();
875 }
876
877 /**
878 * Sets the activity responsible for un-PUK-blocking the device
879 * so that we may close it when we receive a positive result.
880 * mPUKEntryActivity is also used to indicate to the device that
881 * we are trying to un-PUK-lock the phone. In other words, iff
882 * it is NOT null, then we are trying to unlock and waiting for
883 * the SIM to move to READY state.
884 *
885 * @param activity is the activity to close when PUK has
886 * finished unlocking. Can be set to null to indicate the unlock
887 * or SIM READYing process is over.
888 */
889 void setPukEntryActivity(Activity activity) {
890 mPUKEntryActivity = activity;
891 }
892
893 Activity getPUKEntryActivity() {
894 return mPUKEntryActivity;
895 }
896
897 /**
898 * Sets the dialog responsible for notifying the user of un-PUK-
899 * blocking - SIM READYing progress, so that we may dismiss it
900 * when we receive a positive result.
901 *
902 * @param dialog indicates the progress dialog informing the user
903 * of the state of the device. Dismissed upon completion of
904 * READYing process
905 */
906 void setPukEntryProgressDialog(ProgressDialog dialog) {
907 mPUKEntryProgressDialog = dialog;
908 }
909
910 ProgressDialog getPUKEntryProgressDialog() {
911 return mPUKEntryProgressDialog;
912 }
913
914 /**
915 * Controls whether or not the screen is allowed to sleep.
916 *
917 * Once sleep is allowed (WakeState is SLEEP), it will rely on the
918 * settings for the poke lock to determine when to timeout and let
919 * the device sleep {@link PhoneGlobals#setScreenTimeout}.
920 *
921 * @param ws tells the device to how to wake.
922 */
923 /* package */ void requestWakeState(WakeState ws) {
924 if (VDBG) Log.d(LOG_TAG, "requestWakeState(" + ws + ")...");
925 synchronized (this) {
926 if (mWakeState != ws) {
927 switch (ws) {
928 case PARTIAL:
929 // acquire the processor wake lock, and release the FULL
930 // lock if it is being held.
931 mPartialWakeLock.acquire();
932 if (mWakeLock.isHeld()) {
933 mWakeLock.release();
934 }
935 break;
936 case FULL:
937 // acquire the full wake lock, and release the PARTIAL
938 // lock if it is being held.
939 mWakeLock.acquire();
940 if (mPartialWakeLock.isHeld()) {
941 mPartialWakeLock.release();
942 }
943 break;
944 case SLEEP:
945 default:
946 // release both the PARTIAL and FULL locks.
947 if (mWakeLock.isHeld()) {
948 mWakeLock.release();
949 }
950 if (mPartialWakeLock.isHeld()) {
951 mPartialWakeLock.release();
952 }
953 break;
954 }
955 mWakeState = ws;
956 }
957 }
958 }
959
960 /**
961 * If we are not currently keeping the screen on, then poke the power
962 * manager to wake up the screen for the user activity timeout duration.
963 */
964 /* package */ void wakeUpScreen() {
965 synchronized (this) {
966 if (mWakeState == WakeState.SLEEP) {
967 if (DBG) Log.d(LOG_TAG, "pulse screen lock");
968 mPowerManager.wakeUp(SystemClock.uptimeMillis());
969 }
970 }
971 }
972
973 /**
974 * Sets the wake state and screen timeout based on the current state
975 * of the phone, and the current state of the in-call UI.
976 *
977 * This method is a "UI Policy" wrapper around
978 * {@link PhoneGlobals#requestWakeState} and {@link PhoneGlobals#setScreenTimeout}.
979 *
980 * It's safe to call this method regardless of the state of the Phone
981 * (e.g. whether or not it's idle), and regardless of the state of the
982 * Phone UI (e.g. whether or not the InCallScreen is active.)
983 */
984 /* package */ void updateWakeState() {
985 PhoneConstants.State state = mCM.getState();
986
987 // True if the in-call UI is the foreground activity.
988 // (Note this will be false if the screen is currently off,
989 // since in that case *no* activity is in the foreground.)
990 boolean isShowingCallScreen = isShowingCallScreen();
991
992 // True if the InCallScreen's DTMF dialer is currently opened.
993 // (Note this does NOT imply whether or not the InCallScreen
994 // itself is visible.)
995 boolean isDialerOpened = (mInCallScreen != null) && mInCallScreen.isDialerOpened();
996
997 // True if the speakerphone is in use. (If so, we *always* use
998 // the default timeout. Since the user is obviously not holding
999 // the phone up to his/her face, we don't need to worry about
1000 // false touches, and thus don't need to turn the screen off so
1001 // aggressively.)
1002 // Note that we need to make a fresh call to this method any
1003 // time the speaker state changes. (That happens in
1004 // PhoneUtils.turnOnSpeaker().)
1005 boolean isSpeakerInUse = (state == PhoneConstants.State.OFFHOOK) && PhoneUtils.isSpeakerOn(this);
1006
1007 // TODO (bug 1440854): The screen timeout *might* also need to
1008 // depend on the bluetooth state, but this isn't as clear-cut as
1009 // the speaker state (since while using BT it's common for the
1010 // user to put the phone straight into a pocket, in which case the
1011 // timeout should probably still be short.)
1012
1013 if (DBG) Log.d(LOG_TAG, "updateWakeState: callscreen " + isShowingCallScreen
1014 + ", dialer " + isDialerOpened
1015 + ", speaker " + isSpeakerInUse + "...");
1016
1017 //
1018 // Decide whether to force the screen on or not.
1019 //
1020 // Force the screen to be on if the phone is ringing or dialing,
1021 // or if we're displaying the "Call ended" UI for a connection in
1022 // the "disconnected" state.
1023 // However, if the phone is disconnected while the user is in the
1024 // middle of selecting a quick response message, we should not force
1025 // the screen to be on.
1026 //
1027 boolean isRinging = (state == PhoneConstants.State.RINGING);
1028 boolean isDialing = (phone.getForegroundCall().getState() == Call.State.DIALING);
1029 boolean showingQuickResponseDialog = (mInCallScreen != null) &&
1030 mInCallScreen.isQuickResponseDialogShowing();
1031 boolean showingDisconnectedConnection =
1032 PhoneUtils.hasDisconnectedConnections(phone) && isShowingCallScreen;
1033 boolean keepScreenOn = isRinging || isDialing ||
1034 (showingDisconnectedConnection && !showingQuickResponseDialog);
1035 if (DBG) Log.d(LOG_TAG, "updateWakeState: keepScreenOn = " + keepScreenOn
1036 + " (isRinging " + isRinging
1037 + ", isDialing " + isDialing
1038 + ", showingQuickResponse " + showingQuickResponseDialog
1039 + ", showingDisc " + showingDisconnectedConnection + ")");
1040 // keepScreenOn == true means we'll hold a full wake lock:
1041 requestWakeState(keepScreenOn ? WakeState.FULL : WakeState.SLEEP);
1042 }
1043
1044 /**
1045 * Manually pokes the PowerManager's userActivity method. Since we
1046 * set the {@link WindowManager.LayoutParams#INPUT_FEATURE_DISABLE_USER_ACTIVITY}
1047 * flag while the InCallScreen is active when there is no proximity sensor,
1048 * we need to do this for touch events that really do count as user activity
1049 * (like pressing any onscreen UI elements.)
1050 */
1051 /* package */ void pokeUserActivity() {
1052 if (VDBG) Log.d(LOG_TAG, "pokeUserActivity()...");
1053 mPowerManager.userActivity(SystemClock.uptimeMillis(), false);
1054 }
1055
1056 /**
Santos Cordon7d4ddf62013-07-10 11:58:08 -07001057 * Notifies the phone app when the phone state changes.
1058 *
Santos Cordonfc309812013-08-20 18:33:16 -07001059 * This method will updates various states inside Phone app (e.g. update-lock state, etc.)
Santos Cordon7d4ddf62013-07-10 11:58:08 -07001060 */
1061 /* package */ void updatePhoneState(PhoneConstants.State state) {
1062 if (state != mLastPhoneState) {
1063 mLastPhoneState = state;
Santos Cordon7d4ddf62013-07-10 11:58:08 -07001064
1065 // Try to acquire or release UpdateLock.
1066 //
1067 // Watch out: we don't release the lock here when the screen is still in foreground.
1068 // At that time InCallScreen will release it on onPause().
1069 if (state != PhoneConstants.State.IDLE) {
1070 // UpdateLock is a recursive lock, while we may get "acquire" request twice and
1071 // "release" request once for a single call (RINGING + OFFHOOK and IDLE).
1072 // We need to manually ensure the lock is just acquired once for each (and this
1073 // will prevent other possible buggy situations too).
1074 if (!mUpdateLock.isHeld()) {
1075 mUpdateLock.acquire();
1076 }
1077 } else {
1078 if (!isShowingCallScreen()) {
1079 if (!mUpdateLock.isHeld()) {
1080 mUpdateLock.release();
1081 }
1082 } else {
1083 // For this case InCallScreen will take care of the release() call.
1084 }
1085 }
1086
Santos Cordon7d4ddf62013-07-10 11:58:08 -07001087 // While we are in call, the in-call screen should dismiss the keyguard.
1088 // This allows the user to press Home to go directly home without going through
1089 // an insecure lock screen.
1090 // But we do not want to do this if there is no active call so we do not
1091 // bypass the keyguard if the call is not answered or declined.
1092 if (mInCallScreen != null) {
1093 mInCallScreen.updateKeyguardPolicy(state == PhoneConstants.State.OFFHOOK);
1094 }
1095 }
1096 }
1097
1098 /* package */ PhoneConstants.State getPhoneState() {
1099 return mLastPhoneState;
1100 }
1101
1102 /**
1103 * Returns UpdateLock object.
1104 */
1105 /* package */ UpdateLock getUpdateLock() {
1106 return mUpdateLock;
1107 }
1108
Santos Cordon7d4ddf62013-07-10 11:58:08 -07001109 KeyguardManager getKeyguardManager() {
1110 return mKeyguardManager;
1111 }
1112
1113 private void onMMIComplete(AsyncResult r) {
1114 if (VDBG) Log.d(LOG_TAG, "onMMIComplete()...");
1115 MmiCode mmiCode = (MmiCode) r.result;
1116 PhoneUtils.displayMMIComplete(phone, getInstance(), mmiCode, null, null);
1117 }
1118
1119 private void initForNewRadioTechnology() {
1120 if (DBG) Log.d(LOG_TAG, "initForNewRadioTechnology...");
1121
1122 if (phone.getPhoneType() == PhoneConstants.PHONE_TYPE_CDMA) {
1123 // Create an instance of CdmaPhoneCallState and initialize it to IDLE
1124 cdmaPhoneCallState = new CdmaPhoneCallState();
1125 cdmaPhoneCallState.CdmaPhoneCallStateInit();
1126 }
1127 if (TelephonyCapabilities.supportsOtasp(phone)) {
1128 //create instances of CDMA OTA data classes
1129 if (cdmaOtaProvisionData == null) {
1130 cdmaOtaProvisionData = new OtaUtils.CdmaOtaProvisionData();
1131 }
1132 if (cdmaOtaConfigData == null) {
1133 cdmaOtaConfigData = new OtaUtils.CdmaOtaConfigData();
1134 }
1135 if (cdmaOtaScreenState == null) {
1136 cdmaOtaScreenState = new OtaUtils.CdmaOtaScreenState();
1137 }
1138 if (cdmaOtaInCallScreenUiState == null) {
1139 cdmaOtaInCallScreenUiState = new OtaUtils.CdmaOtaInCallScreenUiState();
1140 }
1141 } else {
1142 //Clean up OTA data in GSM/UMTS. It is valid only for CDMA
1143 clearOtaState();
1144 }
1145
1146 ringer.updateRingerContextAfterRadioTechnologyChange(this.phone);
1147 notifier.updateCallNotifierRegistrationsAfterRadioTechnologyChange();
1148 callStateMonitor.updateAfterRadioTechnologyChange();
1149
1150 if (mBluetoothPhone != null) {
1151 try {
1152 mBluetoothPhone.updateBtHandsfreeAfterRadioTechnologyChange();
1153 } catch (RemoteException e) {
1154 Log.e(LOG_TAG, Log.getStackTraceString(new Throwable()));
1155 }
1156 }
1157 if (mInCallScreen != null) {
1158 mInCallScreen.updateAfterRadioTechnologyChange();
1159 }
1160
1161 // Update registration for ICC status after radio technology change
1162 IccCard sim = phone.getIccCard();
1163 if (sim != null) {
1164 if (DBG) Log.d(LOG_TAG, "Update registration for ICC status...");
1165
1166 //Register all events new to the new active phone
1167 sim.registerForNetworkLocked(mHandler, EVENT_SIM_NETWORK_LOCKED, null);
1168 }
1169 }
1170
1171
1172 /**
Santos Cordon593ab382013-08-06 21:58:23 -07001173 * This is called when the wired headset state changes.
1174 */
1175 @Override
1176 public void onWiredHeadsetConnection(boolean pluggedIn) {
1177 PhoneConstants.State phoneState = mCM.getState();
1178
Santos Cordon593ab382013-08-06 21:58:23 -07001179 // Force TTY state update according to new headset state
1180 if (mTtyEnabled) {
1181 mHandler.sendMessage(mHandler.obtainMessage(EVENT_TTY_PREFERRED_MODE_CHANGED, 0));
1182 }
1183 }
1184
1185 /**
Santos Cordon7d4ddf62013-07-10 11:58:08 -07001186 * Receiver for misc intent broadcasts the Phone app cares about.
1187 */
1188 private class PhoneAppBroadcastReceiver extends BroadcastReceiver {
1189 @Override
1190 public void onReceive(Context context, Intent intent) {
1191 String action = intent.getAction();
1192 if (action.equals(Intent.ACTION_AIRPLANE_MODE_CHANGED)) {
1193 boolean enabled = System.getInt(getContentResolver(),
1194 System.AIRPLANE_MODE_ON, 0) == 0;
1195 phone.setRadioPower(enabled);
Santos Cordon7d4ddf62013-07-10 11:58:08 -07001196 } else if (action.equals(TelephonyIntents.ACTION_ANY_DATA_CONNECTION_STATE_CHANGED)) {
1197 if (VDBG) Log.d(LOG_TAG, "mReceiver: ACTION_ANY_DATA_CONNECTION_STATE_CHANGED");
1198 if (VDBG) Log.d(LOG_TAG, "- state: " + intent.getStringExtra(PhoneConstants.STATE_KEY));
1199 if (VDBG) Log.d(LOG_TAG, "- reason: "
1200 + intent.getStringExtra(PhoneConstants.STATE_CHANGE_REASON_KEY));
1201
1202 // The "data disconnected due to roaming" notification is shown
1203 // if (a) you have the "data roaming" feature turned off, and
1204 // (b) you just lost data connectivity because you're roaming.
1205 boolean disconnectedDueToRoaming =
1206 !phone.getDataRoamingEnabled()
1207 && "DISCONNECTED".equals(intent.getStringExtra(PhoneConstants.STATE_KEY))
1208 && Phone.REASON_ROAMING_ON.equals(
1209 intent.getStringExtra(PhoneConstants.STATE_CHANGE_REASON_KEY));
1210 mHandler.sendEmptyMessage(disconnectedDueToRoaming
1211 ? EVENT_DATA_ROAMING_DISCONNECTED
1212 : EVENT_DATA_ROAMING_OK);
Santos Cordon7d4ddf62013-07-10 11:58:08 -07001213 } else if ((action.equals(TelephonyIntents.ACTION_SIM_STATE_CHANGED)) &&
1214 (mPUKEntryActivity != null)) {
1215 // if an attempt to un-PUK-lock the device was made, while we're
1216 // receiving this state change notification, notify the handler.
1217 // NOTE: This is ONLY triggered if an attempt to un-PUK-lock has
1218 // been attempted.
1219 mHandler.sendMessage(mHandler.obtainMessage(EVENT_SIM_STATE_CHANGED,
1220 intent.getStringExtra(IccCardConstants.INTENT_KEY_ICC_STATE)));
1221 } else if (action.equals(TelephonyIntents.ACTION_RADIO_TECHNOLOGY_CHANGED)) {
1222 String newPhone = intent.getStringExtra(PhoneConstants.PHONE_NAME_KEY);
1223 Log.d(LOG_TAG, "Radio technology switched. Now " + newPhone + " is active.");
1224 initForNewRadioTechnology();
1225 } else if (action.equals(TelephonyIntents.ACTION_SERVICE_STATE_CHANGED)) {
1226 handleServiceStateChanged(intent);
1227 } else if (action.equals(TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED)) {
1228 if (TelephonyCapabilities.supportsEcm(phone)) {
1229 Log.d(LOG_TAG, "Emergency Callback Mode arrived in PhoneApp.");
1230 // Start Emergency Callback Mode service
1231 if (intent.getBooleanExtra("phoneinECMState", false)) {
1232 context.startService(new Intent(context,
1233 EmergencyCallbackModeService.class));
1234 }
1235 } else {
1236 // It doesn't make sense to get ACTION_EMERGENCY_CALLBACK_MODE_CHANGED
1237 // on a device that doesn't support ECM in the first place.
1238 Log.e(LOG_TAG, "Got ACTION_EMERGENCY_CALLBACK_MODE_CHANGED, "
1239 + "but ECM isn't supported for phone: " + phone.getPhoneName());
1240 }
1241 } else if (action.equals(Intent.ACTION_DOCK_EVENT)) {
1242 mDockState = intent.getIntExtra(Intent.EXTRA_DOCK_STATE,
1243 Intent.EXTRA_DOCK_STATE_UNDOCKED);
1244 if (VDBG) Log.d(LOG_TAG, "ACTION_DOCK_EVENT -> mDockState = " + mDockState);
1245 mHandler.sendMessage(mHandler.obtainMessage(EVENT_DOCK_STATE_CHANGED, 0));
1246 } else if (action.equals(TtyIntent.TTY_PREFERRED_MODE_CHANGE_ACTION)) {
1247 mPreferredTtyMode = intent.getIntExtra(TtyIntent.TTY_PREFFERED_MODE,
1248 Phone.TTY_MODE_OFF);
1249 if (VDBG) Log.d(LOG_TAG, "mReceiver: TTY_PREFERRED_MODE_CHANGE_ACTION");
1250 if (VDBG) Log.d(LOG_TAG, " mode: " + mPreferredTtyMode);
1251 mHandler.sendMessage(mHandler.obtainMessage(EVENT_TTY_PREFERRED_MODE_CHANGED, 0));
1252 } else if (action.equals(AudioManager.RINGER_MODE_CHANGED_ACTION)) {
1253 int ringerMode = intent.getIntExtra(AudioManager.EXTRA_RINGER_MODE,
1254 AudioManager.RINGER_MODE_NORMAL);
1255 if (ringerMode == AudioManager.RINGER_MODE_SILENT) {
1256 notifier.silenceRinger();
1257 }
1258 }
1259 }
1260 }
1261
1262 /**
1263 * Broadcast receiver for the ACTION_MEDIA_BUTTON broadcast intent.
1264 *
1265 * This functionality isn't lumped in with the other intents in
1266 * PhoneAppBroadcastReceiver because we instantiate this as a totally
1267 * separate BroadcastReceiver instance, since we need to manually
1268 * adjust its IntentFilter's priority (to make sure we get these
1269 * intents *before* the media player.)
1270 */
1271 private class MediaButtonBroadcastReceiver extends BroadcastReceiver {
1272 @Override
1273 public void onReceive(Context context, Intent intent) {
1274 KeyEvent event = (KeyEvent) intent.getParcelableExtra(Intent.EXTRA_KEY_EVENT);
1275 if (VDBG) Log.d(LOG_TAG,
1276 "MediaButtonBroadcastReceiver.onReceive()... event = " + event);
1277 if ((event != null)
1278 && (event.getKeyCode() == KeyEvent.KEYCODE_HEADSETHOOK)) {
1279 if (VDBG) Log.d(LOG_TAG, "MediaButtonBroadcastReceiver: HEADSETHOOK");
1280 boolean consumed = PhoneUtils.handleHeadsetHook(phone, event);
1281 if (VDBG) Log.d(LOG_TAG, "==> handleHeadsetHook(): consumed = " + consumed);
1282 if (consumed) {
1283 // If a headset is attached and the press is consumed, also update
1284 // any UI items (such as an InCallScreen mute button) that may need to
1285 // be updated if their state changed.
1286 updateInCallScreen(); // Has no effect if the InCallScreen isn't visible
1287 abortBroadcast();
1288 }
1289 } else {
1290 if (mCM.getState() != PhoneConstants.State.IDLE) {
1291 // If the phone is anything other than completely idle,
1292 // then we consume and ignore any media key events,
1293 // Otherwise it is too easy to accidentally start
1294 // playing music while a phone call is in progress.
1295 if (VDBG) Log.d(LOG_TAG, "MediaButtonBroadcastReceiver: consumed");
1296 abortBroadcast();
1297 }
1298 }
1299 }
1300 }
1301
1302 /**
1303 * Accepts broadcast Intents which will be prepared by {@link NotificationMgr} and thus
1304 * sent from framework's notification mechanism (which is outside Phone context).
1305 * This should be visible from outside, but shouldn't be in "exported" state.
1306 *
1307 * TODO: If possible merge this into PhoneAppBroadcastReceiver.
1308 */
1309 public static class NotificationBroadcastReceiver extends BroadcastReceiver {
1310 @Override
1311 public void onReceive(Context context, Intent intent) {
1312 String action = intent.getAction();
1313 // TODO: use "if (VDBG)" here.
1314 Log.d(LOG_TAG, "Broadcast from Notification: " + action);
1315
1316 if (action.equals(ACTION_HANG_UP_ONGOING_CALL)) {
1317 PhoneUtils.hangup(PhoneGlobals.getInstance().mCM);
1318 } else if (action.equals(ACTION_CALL_BACK_FROM_NOTIFICATION)) {
1319 // Collapse the expanded notification and the notification item itself.
1320 closeSystemDialogs(context);
1321 clearMissedCallNotification(context);
1322
1323 Intent callIntent = new Intent(Intent.ACTION_CALL_PRIVILEGED, intent.getData());
1324 callIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
1325 | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
1326 context.startActivity(callIntent);
1327 } else if (action.equals(ACTION_SEND_SMS_FROM_NOTIFICATION)) {
1328 // Collapse the expanded notification and the notification item itself.
1329 closeSystemDialogs(context);
1330 clearMissedCallNotification(context);
1331
1332 Intent smsIntent = new Intent(Intent.ACTION_SENDTO, intent.getData());
1333 smsIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
1334 context.startActivity(smsIntent);
1335 } else {
1336 Log.w(LOG_TAG, "Received hang-up request from notification,"
1337 + " but there's no call the system can hang up.");
1338 }
1339 }
1340
1341 private void closeSystemDialogs(Context context) {
1342 Intent intent = new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
1343 context.sendBroadcastAsUser(intent, UserHandle.ALL);
1344 }
1345
1346 private void clearMissedCallNotification(Context context) {
1347 Intent clearIntent = new Intent(context, ClearMissedCallsService.class);
1348 clearIntent.setAction(ClearMissedCallsService.ACTION_CLEAR_MISSED_CALLS);
1349 context.startService(clearIntent);
1350 }
1351 }
1352
1353 private void handleServiceStateChanged(Intent intent) {
1354 /**
1355 * This used to handle updating EriTextWidgetProvider this routine
1356 * and and listening for ACTION_SERVICE_STATE_CHANGED intents could
1357 * be removed. But leaving just in case it might be needed in the near
1358 * future.
1359 */
1360
1361 // If service just returned, start sending out the queued messages
1362 ServiceState ss = ServiceState.newFromBundle(intent.getExtras());
1363
1364 if (ss != null) {
1365 int state = ss.getState();
1366 notificationMgr.updateNetworkSelection(state);
1367 }
1368 }
1369
1370 public boolean isOtaCallInActiveState() {
1371 boolean otaCallActive = false;
1372 if (mInCallScreen != null) {
1373 otaCallActive = mInCallScreen.isOtaCallInActiveState();
1374 }
1375 if (VDBG) Log.d(LOG_TAG, "- isOtaCallInActiveState " + otaCallActive);
1376 return otaCallActive;
1377 }
1378
1379 public boolean isOtaCallInEndState() {
1380 boolean otaCallEnded = false;
1381 if (mInCallScreen != null) {
1382 otaCallEnded = mInCallScreen.isOtaCallInEndState();
1383 }
1384 if (VDBG) Log.d(LOG_TAG, "- isOtaCallInEndState " + otaCallEnded);
1385 return otaCallEnded;
1386 }
1387
1388 // it is safe to call clearOtaState() even if the InCallScreen isn't active
1389 public void clearOtaState() {
1390 if (DBG) Log.d(LOG_TAG, "- clearOtaState ...");
1391 if ((mInCallScreen != null)
1392 && (otaUtils != null)) {
1393 otaUtils.cleanOtaScreen(true);
1394 if (DBG) Log.d(LOG_TAG, " - clearOtaState clears OTA screen");
1395 }
1396 }
1397
1398 // it is safe to call dismissOtaDialogs() even if the InCallScreen isn't active
1399 public void dismissOtaDialogs() {
1400 if (DBG) Log.d(LOG_TAG, "- dismissOtaDialogs ...");
1401 if ((mInCallScreen != null)
1402 && (otaUtils != null)) {
1403 otaUtils.dismissAllOtaDialogs();
1404 if (DBG) Log.d(LOG_TAG, " - dismissOtaDialogs clears OTA dialogs");
1405 }
1406 }
1407
1408 // it is safe to call clearInCallScreenMode() even if the InCallScreen isn't active
1409 public void clearInCallScreenMode() {
1410 if (DBG) Log.d(LOG_TAG, "- clearInCallScreenMode ...");
1411 if (mInCallScreen != null) {
1412 mInCallScreen.resetInCallScreenMode();
1413 }
1414 }
1415
1416 /**
1417 * Force the in-call UI to refresh itself, if it's currently visible.
1418 *
1419 * This method can be used any time there's a state change anywhere in
1420 * the phone app that needs to be reflected in the onscreen UI.
1421 *
1422 * Note that it's *not* necessary to manually refresh the in-call UI
1423 * (via this method) for regular telephony state changes like
1424 * DIALING -> ALERTING -> ACTIVE, since the InCallScreen already
1425 * listens for those state changes itself.
1426 *
1427 * This method does *not* force the in-call UI to come up if it's not
1428 * already visible. To do that, use displayCallScreen().
1429 */
1430 /* package */ void updateInCallScreen() {
1431 if (DBG) Log.d(LOG_TAG, "- updateInCallScreen()...");
1432 if (mInCallScreen != null) {
1433 // Post an updateScreen() request. Note that the
1434 // updateScreen() call will end up being a no-op if the
1435 // InCallScreen isn't the foreground activity.
1436 mInCallScreen.requestUpdateScreen();
1437 }
1438 }
1439
1440 private void handleQueryTTYModeResponse(Message msg) {
1441 AsyncResult ar = (AsyncResult) msg.obj;
1442 if (ar.exception != null) {
1443 if (DBG) Log.d(LOG_TAG, "handleQueryTTYModeResponse: Error getting TTY state.");
1444 } else {
1445 if (DBG) Log.d(LOG_TAG,
1446 "handleQueryTTYModeResponse: TTY enable state successfully queried.");
1447
1448 int ttymode = ((int[]) ar.result)[0];
1449 if (DBG) Log.d(LOG_TAG, "handleQueryTTYModeResponse:ttymode=" + ttymode);
1450
1451 Intent ttyModeChanged = new Intent(TtyIntent.TTY_ENABLED_CHANGE_ACTION);
1452 ttyModeChanged.putExtra("ttyEnabled", ttymode != Phone.TTY_MODE_OFF);
1453 sendBroadcastAsUser(ttyModeChanged, UserHandle.ALL);
1454
1455 String audioTtyMode;
1456 switch (ttymode) {
1457 case Phone.TTY_MODE_FULL:
1458 audioTtyMode = "tty_full";
1459 break;
1460 case Phone.TTY_MODE_VCO:
1461 audioTtyMode = "tty_vco";
1462 break;
1463 case Phone.TTY_MODE_HCO:
1464 audioTtyMode = "tty_hco";
1465 break;
1466 case Phone.TTY_MODE_OFF:
1467 default:
1468 audioTtyMode = "tty_off";
1469 break;
1470 }
1471 AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
1472 audioManager.setParameters("tty_mode="+audioTtyMode);
1473 }
1474 }
1475
1476 private void handleSetTTYModeResponse(Message msg) {
1477 AsyncResult ar = (AsyncResult) msg.obj;
1478
1479 if (ar.exception != null) {
1480 if (DBG) Log.d (LOG_TAG,
1481 "handleSetTTYModeResponse: Error setting TTY mode, ar.exception"
1482 + ar.exception);
1483 }
1484 phone.queryTTYMode(mHandler.obtainMessage(EVENT_TTY_MODE_GET));
1485 }
1486
1487 /**
1488 * "Call origin" may be used by Contacts app to specify where the phone call comes from.
1489 * Currently, the only permitted value for this extra is {@link #ALLOWED_EXTRA_CALL_ORIGIN}.
1490 * Any other value will be ignored, to make sure that malicious apps can't trick the in-call
1491 * UI into launching some random other app after a call ends.
1492 *
1493 * TODO: make this more generic. Note that we should let the "origin" specify its package
1494 * while we are now assuming it is "com.android.contacts"
1495 */
1496 public static final String EXTRA_CALL_ORIGIN = "com.android.phone.CALL_ORIGIN";
1497 private static final String DEFAULT_CALL_ORIGIN_PACKAGE = "com.android.dialer";
1498 private static final String ALLOWED_EXTRA_CALL_ORIGIN =
1499 "com.android.dialer.DialtactsActivity";
1500 /**
1501 * Used to determine if the preserved call origin is fresh enough.
1502 */
1503 private static final long CALL_ORIGIN_EXPIRATION_MILLIS = 30 * 1000;
1504
1505 public void setLatestActiveCallOrigin(String callOrigin) {
1506 inCallUiState.latestActiveCallOrigin = callOrigin;
1507 if (callOrigin != null) {
1508 inCallUiState.latestActiveCallOriginTimeStamp = SystemClock.elapsedRealtime();
1509 } else {
1510 inCallUiState.latestActiveCallOriginTimeStamp = 0;
1511 }
1512 }
1513
1514 /**
1515 * Reset call origin depending on its timestamp.
1516 *
1517 * See if the current call origin preserved by the app is fresh enough or not. If it is,
1518 * previous call origin will be used as is. If not, call origin will be reset.
1519 *
1520 * This will be effective especially for 3rd party apps which want to bypass phone calls with
1521 * their own telephone lines. In that case Phone app may finish the phone call once and make
1522 * another for the external apps, which will drop call origin information in Intent.
1523 * Even in that case we are sure the second phone call should be initiated just after the first
1524 * phone call, so here we restore it from the previous information iff the second call is done
1525 * fairly soon.
1526 */
1527 public void resetLatestActiveCallOrigin() {
1528 final long callOriginTimestamp = inCallUiState.latestActiveCallOriginTimeStamp;
1529 final long currentTimestamp = SystemClock.elapsedRealtime();
1530 if (VDBG) {
1531 Log.d(LOG_TAG, "currentTimeMillis: " + currentTimestamp
1532 + ", saved timestamp for call origin: " + callOriginTimestamp);
1533 }
1534 if (inCallUiState.latestActiveCallOriginTimeStamp > 0
1535 && (currentTimestamp - callOriginTimestamp < CALL_ORIGIN_EXPIRATION_MILLIS)) {
1536 if (VDBG) {
1537 Log.d(LOG_TAG, "Resume previous call origin (" +
1538 inCallUiState.latestActiveCallOrigin + ")");
1539 }
1540 // Do nothing toward call origin itself but update the timestamp just in case.
1541 inCallUiState.latestActiveCallOriginTimeStamp = currentTimestamp;
1542 } else {
1543 if (VDBG) Log.d(LOG_TAG, "Drop previous call origin and set the current one to null");
1544 setLatestActiveCallOrigin(null);
1545 }
1546 }
1547
1548 /**
1549 * @return Intent which will be used when in-call UI is shown and the phone call is hang up.
1550 * By default CallLog screen will be introduced, but the destination may change depending on
1551 * its latest call origin state.
1552 */
1553 public Intent createPhoneEndIntentUsingCallOrigin() {
1554 if (TextUtils.equals(inCallUiState.latestActiveCallOrigin, ALLOWED_EXTRA_CALL_ORIGIN)) {
1555 if (VDBG) Log.d(LOG_TAG, "Valid latestActiveCallOrigin("
1556 + inCallUiState.latestActiveCallOrigin + ") was found. "
1557 + "Go back to the previous screen.");
1558 // Right now we just launch the Activity which launched in-call UI. Note that we're
1559 // assuming the origin is from "com.android.dialer", which may be incorrect in the
1560 // future.
1561 final Intent intent = new Intent();
1562 intent.setClassName(DEFAULT_CALL_ORIGIN_PACKAGE, inCallUiState.latestActiveCallOrigin);
1563 return intent;
1564 } else {
1565 if (VDBG) Log.d(LOG_TAG, "Current latestActiveCallOrigin ("
1566 + inCallUiState.latestActiveCallOrigin + ") is not valid. "
1567 + "Just use CallLog as a default destination.");
1568 return PhoneGlobals.createCallLogIntent();
1569 }
1570 }
1571
1572 /** Service connection */
1573 private final ServiceConnection mBluetoothPhoneConnection = new ServiceConnection() {
1574
1575 /** Handle the task of binding the local object to the service */
1576 public void onServiceConnected(ComponentName className, IBinder service) {
1577 Log.i(LOG_TAG, "Headset phone created, binding local service.");
1578 mBluetoothPhone = IBluetoothHeadsetPhone.Stub.asInterface(service);
1579 }
1580
1581 /** Handle the task of cleaning up the local binding */
1582 public void onServiceDisconnected(ComponentName className) {
1583 Log.i(LOG_TAG, "Headset phone disconnected, cleaning local binding.");
1584 mBluetoothPhone = null;
1585 }
1586 };
1587}