blob: 4d858ec532955ea5ec402bf675bc763023964f5e [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;
Yorke Leeca6ec3b2013-08-29 14:21:43 -070023import android.app.TaskStackBuilder;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070024import android.bluetooth.BluetoothAdapter;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070025import android.bluetooth.IBluetoothHeadsetPhone;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070026import 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;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070034import android.media.AudioManager;
35import android.net.Uri;
36import android.os.AsyncResult;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070037import android.os.Handler;
38import android.os.IBinder;
39import android.os.IPowerManager;
40import android.os.Message;
41import android.os.PowerManager;
42import android.os.RemoteException;
43import android.os.ServiceManager;
44import android.os.SystemClock;
45import android.os.SystemProperties;
46import android.os.UpdateLock;
47import android.os.UserHandle;
48import android.preference.PreferenceManager;
49import android.provider.Settings.System;
50import android.telephony.ServiceState;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070051import android.util.Log;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070052
53import com.android.internal.telephony.Call;
54import com.android.internal.telephony.CallManager;
55import com.android.internal.telephony.IccCard;
56import com.android.internal.telephony.IccCardConstants;
57import com.android.internal.telephony.MmiCode;
58import com.android.internal.telephony.Phone;
59import com.android.internal.telephony.PhoneConstants;
60import com.android.internal.telephony.PhoneFactory;
61import com.android.internal.telephony.TelephonyCapabilities;
62import com.android.internal.telephony.TelephonyIntents;
63import com.android.internal.telephony.cdma.TtyIntent;
Santos Cordon593ab382013-08-06 21:58:23 -070064import com.android.phone.WiredHeadsetManager.WiredHeadsetListener;
Santos Cordon352ff652014-05-30 01:41:45 -070065import com.android.phone.common.CallLogAsync;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070066import com.android.server.sip.SipService;
67
68/**
69 * Global state for the telephony subsystem when running in the primary
70 * phone process.
71 */
Santos Cordonfc309812013-08-20 18:33:16 -070072public class PhoneGlobals extends ContextWrapper implements WiredHeadsetListener {
Santos Cordon7d4ddf62013-07-10 11:58:08 -070073 /* package */ static final String LOG_TAG = "PhoneApp";
74
75 /**
76 * Phone app-wide debug level:
77 * 0 - no debug logging
78 * 1 - normal debug logging if ro.debuggable is set (which is true in
79 * "eng" and "userdebug" builds but not "user" builds)
80 * 2 - ultra-verbose debug logging
81 *
82 * Most individual classes in the phone app have a local DBG constant,
83 * typically set to
84 * (PhoneApp.DBG_LEVEL >= 1) && (SystemProperties.getInt("ro.debuggable", 0) == 1)
85 * or else
86 * (PhoneApp.DBG_LEVEL >= 2)
87 * depending on the desired verbosity.
88 *
89 * ***** DO NOT SUBMIT WITH DBG_LEVEL > 0 *************
90 */
91 /* package */ static final int DBG_LEVEL = 0;
92
93 private static final boolean DBG =
94 (PhoneGlobals.DBG_LEVEL >= 1) && (SystemProperties.getInt("ro.debuggable", 0) == 1);
95 private static final boolean VDBG = (PhoneGlobals.DBG_LEVEL >= 2);
96
97 // Message codes; see mHandler below.
98 private static final int EVENT_SIM_NETWORK_LOCKED = 3;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070099 private static final int EVENT_SIM_STATE_CHANGED = 8;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700100 private static final int EVENT_DATA_ROAMING_DISCONNECTED = 10;
101 private static final int EVENT_DATA_ROAMING_OK = 11;
102 private static final int EVENT_UNSOL_CDMA_INFO_RECORD = 12;
103 private static final int EVENT_DOCK_STATE_CHANGED = 13;
104 private static final int EVENT_TTY_PREFERRED_MODE_CHANGED = 14;
105 private static final int EVENT_TTY_MODE_GET = 15;
106 private static final int EVENT_TTY_MODE_SET = 16;
107 private static final int EVENT_START_SIP_SERVICE = 17;
108
109 // The MMI codes are also used by the InCallScreen.
110 public static final int MMI_INITIATE = 51;
111 public static final int MMI_COMPLETE = 52;
112 public static final int MMI_CANCEL = 53;
113 // Don't use message codes larger than 99 here; those are reserved for
114 // the individual Activities of the Phone UI.
115
116 /**
117 * Allowable values for the wake lock code.
118 * SLEEP means the device can be put to sleep.
119 * PARTIAL means wake the processor, but we display can be kept off.
120 * FULL means wake both the processor and the display.
121 */
122 public enum WakeState {
123 SLEEP,
124 PARTIAL,
125 FULL
126 }
127
128 /**
129 * Intent Action used for hanging up the current call from Notification bar. This will
130 * choose first ringing call, first active call, or first background call (typically in
131 * HOLDING state).
132 */
133 public static final String ACTION_HANG_UP_ONGOING_CALL =
134 "com.android.phone.ACTION_HANG_UP_ONGOING_CALL";
135
136 /**
137 * Intent Action used for making a phone call from Notification bar.
138 * This is for missed call notifications.
139 */
140 public static final String ACTION_CALL_BACK_FROM_NOTIFICATION =
141 "com.android.phone.ACTION_CALL_BACK_FROM_NOTIFICATION";
142
143 /**
144 * Intent Action used for sending a SMS from notification bar.
145 * This is for missed call notifications.
146 */
147 public static final String ACTION_SEND_SMS_FROM_NOTIFICATION =
148 "com.android.phone.ACTION_SEND_SMS_FROM_NOTIFICATION";
149
150 private static PhoneGlobals sMe;
151
152 // A few important fields we expose to the rest of the package
153 // directly (rather than thru set/get methods) for efficiency.
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700154 CallController callController;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700155 CallManager mCM;
Santos Cordon63a84242013-07-23 13:32:52 -0700156 CallNotifier notifier;
157 CallerInfoCache callerInfoCache;
Santos Cordon63a84242013-07-23 13:32:52 -0700158 NotificationMgr notificationMgr;
159 Phone phone;
160 PhoneInterfaceManager phoneMgr;
161
Santos Cordon9b7bac72013-08-06 08:04:52 -0700162 private AudioRouter audioRouter;
Santos Cordon27a3c1f2013-08-06 07:49:27 -0700163 private BluetoothManager bluetoothManager;
Santos Cordon63a84242013-07-23 13:32:52 -0700164 private CallCommandService callCommandService;
Santos Cordon69a69192013-08-22 14:25:42 -0700165 private CallGatewayManager callGatewayManager;
Santos Cordon63a84242013-07-23 13:32:52 -0700166 private CallHandlerServiceProxy callHandlerServiceProxy;
167 private CallModeler callModeler;
168 private CallStateMonitor callStateMonitor;
Santos Cordon2eaff902013-08-05 04:37:55 -0700169 private DTMFTonePlayer dtmfTonePlayer;
Santos Cordon63a84242013-07-23 13:32:52 -0700170 private IBluetoothHeadsetPhone mBluetoothPhone;
171 private Ringer ringer;
Santos Cordon593ab382013-08-06 21:58:23 -0700172 private WiredHeadsetManager wiredHeadsetManager;
Santos Cordon63a84242013-07-23 13:32:52 -0700173
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700174 static int mDockState = Intent.EXTRA_DOCK_STATE_UNDOCKED;
175 static boolean sVoiceCapable = true;
176
177 // Internal PhoneApp Call state tracker
178 CdmaPhoneCallState cdmaPhoneCallState;
179
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700180 // The currently-active PUK entry activity and progress dialog.
181 // Normally, these are the Emergency Dialer and the subsequent
182 // progress dialog. null if there is are no such objects in
183 // the foreground.
184 private Activity mPUKEntryActivity;
185 private ProgressDialog mPUKEntryProgressDialog;
186
187 private boolean mIsSimPinEnabled;
188 private String mCachedSimPin;
189
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700190 // True if we are beginning a call, but the phone state has not changed yet
191 private boolean mBeginningCall;
192
193 // Last phone state seen by updatePhoneState()
194 private PhoneConstants.State mLastPhoneState = PhoneConstants.State.IDLE;
195
196 private WakeState mWakeState = WakeState.SLEEP;
197
198 private PowerManager mPowerManager;
199 private IPowerManager mPowerManagerService;
200 private PowerManager.WakeLock mWakeLock;
201 private PowerManager.WakeLock mPartialWakeLock;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700202 private KeyguardManager mKeyguardManager;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700203
204 private UpdateLock mUpdateLock;
205
206 // Broadcast receiver for various intent broadcasts (see onCreate())
207 private final BroadcastReceiver mReceiver = new PhoneAppBroadcastReceiver();
208
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700209 /** boolean indicating restoring mute state on InCallScreen.onResume() */
210 private boolean mShouldRestoreMuteOnInCallResume;
211
212 /**
213 * The singleton OtaUtils instance used for OTASP calls.
214 *
215 * The OtaUtils instance is created lazily the first time we need to
216 * make an OTASP call, regardless of whether it's an interactive or
217 * non-interactive OTASP call.
218 */
219 public OtaUtils otaUtils;
220
221 // Following are the CDMA OTA information Objects used during OTA Call.
222 // cdmaOtaProvisionData object store static OTA information that needs
223 // to be maintained even during Slider open/close scenarios.
224 // cdmaOtaConfigData object stores configuration info to control visiblity
225 // of each OTA Screens.
226 // cdmaOtaScreenState object store OTA Screen State information.
227 public OtaUtils.CdmaOtaProvisionData cdmaOtaProvisionData;
228 public OtaUtils.CdmaOtaConfigData cdmaOtaConfigData;
229 public OtaUtils.CdmaOtaScreenState cdmaOtaScreenState;
230 public OtaUtils.CdmaOtaInCallScreenUiState cdmaOtaInCallScreenUiState;
231
232 // TTY feature enabled on this platform
233 private boolean mTtyEnabled;
234 // Current TTY operating mode selected by user
235 private int mPreferredTtyMode = Phone.TTY_MODE_OFF;
236
237 /**
238 * Set the restore mute state flag. Used when we are setting the mute state
239 * OUTSIDE of user interaction {@link PhoneUtils#startNewCall(Phone)}
240 */
241 /*package*/void setRestoreMuteOnInCallResume (boolean mode) {
242 mShouldRestoreMuteOnInCallResume = mode;
243 }
244
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700245 Handler mHandler = new Handler() {
246 @Override
247 public void handleMessage(Message msg) {
248 PhoneConstants.State phoneState;
249 switch (msg.what) {
250 // Starts the SIP service. It's a no-op if SIP API is not supported
251 // on the deivce.
252 // TODO: Having the phone process host the SIP service is only
253 // temporary. Will move it to a persistent communication process
254 // later.
255 case EVENT_START_SIP_SERVICE:
256 SipService.start(getApplicationContext());
257 break;
258
259 // TODO: This event should be handled by the lock screen, just
260 // like the "SIM missing" and "Sim locked" cases (bug 1804111).
261 case EVENT_SIM_NETWORK_LOCKED:
262 if (getResources().getBoolean(R.bool.ignore_sim_network_locked_events)) {
263 // Some products don't have the concept of a "SIM network lock"
264 Log.i(LOG_TAG, "Ignoring EVENT_SIM_NETWORK_LOCKED event; "
265 + "not showing 'SIM network unlock' PIN entry screen");
266 } else {
267 // Normal case: show the "SIM network unlock" PIN entry screen.
268 // The user won't be able to do anything else until
269 // they enter a valid SIM network PIN.
270 Log.i(LOG_TAG, "show sim depersonal panel");
271 IccNetworkDepersonalizationPanel ndpPanel =
272 new IccNetworkDepersonalizationPanel(PhoneGlobals.getInstance());
273 ndpPanel.show();
274 }
275 break;
276
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700277 case EVENT_DATA_ROAMING_DISCONNECTED:
278 notificationMgr.showDataDisconnectedRoaming();
279 break;
280
281 case EVENT_DATA_ROAMING_OK:
282 notificationMgr.hideDataDisconnectedRoaming();
283 break;
284
285 case MMI_COMPLETE:
286 onMMIComplete((AsyncResult) msg.obj);
287 break;
288
289 case MMI_CANCEL:
290 PhoneUtils.cancelMmiCode(phone);
291 break;
292
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700293 case EVENT_SIM_STATE_CHANGED:
294 // Marks the event where the SIM goes into ready state.
295 // Right now, this is only used for the PUK-unlocking
296 // process.
297 if (msg.obj.equals(IccCardConstants.INTENT_VALUE_ICC_READY)) {
298 // when the right event is triggered and there
299 // are UI objects in the foreground, we close
300 // them to display the lock panel.
301 if (mPUKEntryActivity != null) {
302 mPUKEntryActivity.finish();
303 mPUKEntryActivity = null;
304 }
305 if (mPUKEntryProgressDialog != null) {
306 mPUKEntryProgressDialog.dismiss();
307 mPUKEntryProgressDialog = null;
308 }
309 }
310 break;
311
312 case EVENT_UNSOL_CDMA_INFO_RECORD:
313 //TODO: handle message here;
314 break;
315
316 case EVENT_DOCK_STATE_CHANGED:
317 // If the phone is docked/undocked during a call, and no wired or BT headset
318 // is connected: turn on/off the speaker accordingly.
319 boolean inDockMode = false;
320 if (mDockState != Intent.EXTRA_DOCK_STATE_UNDOCKED) {
321 inDockMode = true;
322 }
323 if (VDBG) Log.d(LOG_TAG, "received EVENT_DOCK_STATE_CHANGED. Phone inDock = "
324 + inDockMode);
325
326 phoneState = mCM.getState();
327 if (phoneState == PhoneConstants.State.OFFHOOK &&
Santos Cordon593ab382013-08-06 21:58:23 -0700328 !wiredHeadsetManager.isHeadsetPlugged() &&
329 !bluetoothManager.isBluetoothHeadsetAudioOn()) {
330 audioRouter.setSpeaker(inDockMode);
331
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700332 PhoneUtils.turnOnSpeaker(getApplicationContext(), inDockMode, true);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700333 }
334 break;
335
336 case EVENT_TTY_PREFERRED_MODE_CHANGED:
337 // TTY mode is only applied if a headset is connected
338 int ttyMode;
Santos Cordon593ab382013-08-06 21:58:23 -0700339 if (wiredHeadsetManager.isHeadsetPlugged()) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700340 ttyMode = mPreferredTtyMode;
341 } else {
342 ttyMode = Phone.TTY_MODE_OFF;
343 }
344 phone.setTTYMode(ttyMode, mHandler.obtainMessage(EVENT_TTY_MODE_SET));
345 break;
346
347 case EVENT_TTY_MODE_GET:
348 handleQueryTTYModeResponse(msg);
349 break;
350
351 case EVENT_TTY_MODE_SET:
352 handleSetTTYModeResponse(msg);
353 break;
354 }
355 }
356 };
357
358 public PhoneGlobals(Context context) {
359 super(context);
360 sMe = this;
361 }
362
363 public void onCreate() {
364 if (VDBG) Log.v(LOG_TAG, "onCreate()...");
365
366 ContentResolver resolver = getContentResolver();
367
368 // Cache the "voice capable" flag.
369 // This flag currently comes from a resource (which is
370 // overrideable on a per-product basis):
371 sVoiceCapable =
372 getResources().getBoolean(com.android.internal.R.bool.config_voice_capable);
373 // ...but this might eventually become a PackageManager "system
374 // feature" instead, in which case we'd do something like:
375 // sVoiceCapable =
376 // getPackageManager().hasSystemFeature(PackageManager.FEATURE_TELEPHONY_VOICE_CALLS);
377
378 if (phone == null) {
379 // Initialize the telephony framework
380 PhoneFactory.makeDefaultPhones(this);
381
382 // Get the default phone
383 phone = PhoneFactory.getDefaultPhone();
384
385 // Start TelephonyDebugService After the default phone is created.
386 Intent intent = new Intent(this, TelephonyDebugService.class);
387 startService(intent);
388
389 mCM = CallManager.getInstance();
390 mCM.registerPhone(phone);
391
392 // Create the NotificationMgr singleton, which is used to display
393 // status bar icons and control other status bar behavior.
394 notificationMgr = NotificationMgr.init(this);
395
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700396 mHandler.sendEmptyMessage(EVENT_START_SIP_SERVICE);
397
398 int phoneType = phone.getPhoneType();
399
400 if (phoneType == PhoneConstants.PHONE_TYPE_CDMA) {
401 // Create an instance of CdmaPhoneCallState and initialize it to IDLE
402 cdmaPhoneCallState = new CdmaPhoneCallState();
403 cdmaPhoneCallState.CdmaPhoneCallStateInit();
404 }
405
406 if (BluetoothAdapter.getDefaultAdapter() != null) {
407 // Start BluetoothPhoneService even if device is not voice capable.
408 // The device can still support VOIP.
409 startService(new Intent(this, BluetoothPhoneService.class));
410 bindService(new Intent(this, BluetoothPhoneService.class),
411 mBluetoothPhoneConnection, 0);
412 } else {
413 // Device is not bluetooth capable
414 mBluetoothPhone = null;
415 }
416
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700417 // before registering for phone state changes
418 mPowerManager = (PowerManager) getSystemService(Context.POWER_SERVICE);
419 mWakeLock = mPowerManager.newWakeLock(PowerManager.FULL_WAKE_LOCK, LOG_TAG);
420 // lock used to keep the processor awake, when we don't care for the display.
421 mPartialWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK
422 | PowerManager.ON_AFTER_RELEASE, LOG_TAG);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700423
424 mKeyguardManager = (KeyguardManager) getSystemService(Context.KEYGUARD_SERVICE);
425
426 // get a handle to the service so that we can use it later when we
427 // want to set the poke lock.
428 mPowerManagerService = IPowerManager.Stub.asInterface(
429 ServiceManager.getService("power"));
430
431 // Get UpdateLock to suppress system-update related events (e.g. dialog show-up)
432 // during phone calls.
433 mUpdateLock = new UpdateLock("phone");
434
435 if (DBG) Log.d(LOG_TAG, "onCreate: mUpdateLock: " + mUpdateLock);
436
437 CallLogger callLogger = new CallLogger(this, new CallLogAsync());
438
Jay Shrauner21a75342013-11-25 16:14:43 -0800439 callGatewayManager = CallGatewayManager.getInstance();
Santos Cordon69a69192013-08-22 14:25:42 -0700440
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700441 // Create the CallController singleton, which is the interface
442 // to the telephony layer for user-initiated telephony functionality
443 // (like making outgoing calls.)
Santos Cordon69a69192013-08-22 14:25:42 -0700444 callController = CallController.init(this, callLogger, callGatewayManager);
445
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700446 // Create the CallerInfoCache singleton, which remembers custom ring tone and
447 // send-to-voicemail settings.
448 //
449 // The asynchronous caching will start just after this call.
450 callerInfoCache = CallerInfoCache.init(this);
451
452 // Monitors call activity from the telephony layer
453 callStateMonitor = new CallStateMonitor(mCM);
454
Santos Cordon63a84242013-07-23 13:32:52 -0700455 // Creates call models for use with CallHandlerService.
David Braun35272072013-09-24 17:19:26 -0700456 callModeler = new CallModeler(callStateMonitor, mCM, callGatewayManager);
Santos Cordon63a84242013-07-23 13:32:52 -0700457
Santos Cordon2eaff902013-08-05 04:37:55 -0700458 // Plays DTMF Tones
459 dtmfTonePlayer = new DTMFTonePlayer(mCM, callModeler);
460
Santos Cordon593ab382013-08-06 21:58:23 -0700461 // Manages wired headset state
462 wiredHeadsetManager = new WiredHeadsetManager(this);
463 wiredHeadsetManager.addWiredHeadsetListener(this);
464
Santos Cordon2c2d3cf2013-08-08 03:53:47 -0700465 // Bluetooth manager
466 bluetoothManager = new BluetoothManager(this, mCM, callModeler);
Santos Cordon2c2d3cf2013-08-08 03:53:47 -0700467
468 ringer = Ringer.init(this, bluetoothManager);
469
Santos Cordon9b7bac72013-08-06 08:04:52 -0700470 // Audio router
Santos Cordon593ab382013-08-06 21:58:23 -0700471 audioRouter = new AudioRouter(this, bluetoothManager, wiredHeadsetManager, mCM);
Santos Cordon9b7bac72013-08-06 08:04:52 -0700472
Santos Cordon249efd02013-08-05 03:33:56 -0700473 // Service used by in-call UI to control calls
Santos Cordon9b7bac72013-08-06 08:04:52 -0700474 callCommandService = new CallCommandService(this, mCM, callModeler, dtmfTonePlayer,
David Braun35272072013-09-24 17:19:26 -0700475 audioRouter);
Santos Cordon249efd02013-08-05 03:33:56 -0700476
Santos Cordon89647a62013-07-16 13:38:09 -0700477 // Sends call state to the UI
Santos Cordon63a84242013-07-23 13:32:52 -0700478 callHandlerServiceProxy = new CallHandlerServiceProxy(this, callModeler,
Santos Cordon593ab382013-08-06 21:58:23 -0700479 callCommandService, audioRouter);
Santos Cordon89647a62013-07-16 13:38:09 -0700480
Santos Cordon117fee72014-05-16 17:56:12 -0700481 phoneMgr = PhoneInterfaceManager.init(this, phone, callHandlerServiceProxy);
Santos Cordon406c0342013-08-28 00:07:47 -0700482
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700483 // Create the CallNotifer singleton, which handles
484 // asynchronous events from the telephony layer (like
485 // launching the incoming-call UI when an incoming call comes
486 // in.)
Santos Cordon27a3c1f2013-08-06 07:49:27 -0700487 notifier = CallNotifier.init(this, phone, ringer, callLogger, callStateMonitor,
Santos Cordona5d5db82013-09-15 13:00:34 -0700488 bluetoothManager, callModeler);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700489
490 // register for ICC status
491 IccCard sim = phone.getIccCard();
492 if (sim != null) {
493 if (VDBG) Log.v(LOG_TAG, "register for ICC status");
494 sim.registerForNetworkLocked(mHandler, EVENT_SIM_NETWORK_LOCKED, null);
495 }
496
497 // register for MMI/USSD
498 mCM.registerForMmiComplete(mHandler, MMI_COMPLETE, null);
499
500 // register connection tracking to PhoneUtils
501 PhoneUtils.initializeConnectionHandler(mCM);
502
503 // Read platform settings for TTY feature
504 mTtyEnabled = getResources().getBoolean(R.bool.tty_enabled);
505
506 // Register for misc other intent broadcasts.
507 IntentFilter intentFilter =
508 new IntentFilter(Intent.ACTION_AIRPLANE_MODE_CHANGED);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700509 intentFilter.addAction(TelephonyIntents.ACTION_ANY_DATA_CONNECTION_STATE_CHANGED);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700510 intentFilter.addAction(Intent.ACTION_DOCK_EVENT);
511 intentFilter.addAction(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
512 intentFilter.addAction(TelephonyIntents.ACTION_RADIO_TECHNOLOGY_CHANGED);
513 intentFilter.addAction(TelephonyIntents.ACTION_SERVICE_STATE_CHANGED);
514 intentFilter.addAction(TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED);
515 if (mTtyEnabled) {
516 intentFilter.addAction(TtyIntent.TTY_PREFERRED_MODE_CHANGE_ACTION);
517 }
518 intentFilter.addAction(AudioManager.RINGER_MODE_CHANGED_ACTION);
519 registerReceiver(mReceiver, intentFilter);
520
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700521 //set the default values for the preferences in the phone.
522 PreferenceManager.setDefaultValues(this, R.xml.network_setting, false);
523
524 PreferenceManager.setDefaultValues(this, R.xml.call_feature_setting, false);
525
526 // Make sure the audio mode (along with some
527 // audio-mode-related state of our own) is initialized
528 // correctly, given the current state of the phone.
529 PhoneUtils.setAudioMode(mCM);
530 }
531
532 if (TelephonyCapabilities.supportsOtasp(phone)) {
533 cdmaOtaProvisionData = new OtaUtils.CdmaOtaProvisionData();
534 cdmaOtaConfigData = new OtaUtils.CdmaOtaConfigData();
535 cdmaOtaScreenState = new OtaUtils.CdmaOtaScreenState();
536 cdmaOtaInCallScreenUiState = new OtaUtils.CdmaOtaInCallScreenUiState();
537 }
538
539 // XXX pre-load the SimProvider so that it's ready
540 resolver.getType(Uri.parse("content://icc/adn"));
541
542 // start with the default value to set the mute state.
543 mShouldRestoreMuteOnInCallResume = false;
544
545 // TODO: Register for Cdma Information Records
546 // phone.registerCdmaInformationRecord(mHandler, EVENT_UNSOL_CDMA_INFO_RECORD, null);
547
548 // Read TTY settings and store it into BP NV.
549 // AP owns (i.e. stores) the TTY setting in AP settings database and pushes the setting
550 // to BP at power up (BP does not need to make the TTY setting persistent storage).
551 // This way, there is a single owner (i.e AP) for the TTY setting in the phone.
552 if (mTtyEnabled) {
553 mPreferredTtyMode = android.provider.Settings.Secure.getInt(
554 phone.getContext().getContentResolver(),
555 android.provider.Settings.Secure.PREFERRED_TTY_MODE,
556 Phone.TTY_MODE_OFF);
557 mHandler.sendMessage(mHandler.obtainMessage(EVENT_TTY_PREFERRED_MODE_CHANGED, 0));
558 }
559 // Read HAC settings and configure audio hardware
560 if (getResources().getBoolean(R.bool.hac_enabled)) {
561 int hac = android.provider.Settings.System.getInt(phone.getContext().getContentResolver(),
562 android.provider.Settings.System.HEARING_AID,
563 0);
564 AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
565 audioManager.setParameter(CallFeaturesSetting.HAC_KEY, hac != 0 ?
566 CallFeaturesSetting.HAC_VAL_ON :
567 CallFeaturesSetting.HAC_VAL_OFF);
568 }
Santos Cordonff506f52013-11-21 19:13:19 -0800569 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700570
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700571 /**
572 * Returns the singleton instance of the PhoneApp.
573 */
Sailesh Nepal1eaf22b2014-02-22 17:00:49 -0800574 public static PhoneGlobals getInstance() {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700575 if (sMe == null) {
576 throw new IllegalStateException("No PhoneGlobals here!");
577 }
578 return sMe;
579 }
580
581 /**
582 * Returns the singleton instance of the PhoneApp if running as the
583 * primary user, otherwise null.
584 */
585 static PhoneGlobals getInstanceIfPrimary() {
586 return sMe;
587 }
588
589 /**
590 * Returns the Phone associated with this instance
591 */
592 static Phone getPhone() {
593 return getInstance().phone;
594 }
595
596 Ringer getRinger() {
597 return ringer;
598 }
599
600 IBluetoothHeadsetPhone getBluetoothPhoneService() {
601 return mBluetoothPhone;
602 }
603
Santos Cordon27a3c1f2013-08-06 07:49:27 -0700604 /* package */ BluetoothManager getBluetoothManager() {
605 return bluetoothManager;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700606 }
607
Santos Cordon593ab382013-08-06 21:58:23 -0700608 /* package */ WiredHeadsetManager getWiredHeadsetManager() {
609 return wiredHeadsetManager;
610 }
611
612 /* package */ AudioRouter getAudioRouter() {
613 return audioRouter;
614 }
615
Sailesh Nepal1eaf22b2014-02-22 17:00:49 -0800616 public CallModeler getCallModeler() {
Santos Cordonad1ed6d2013-09-16 03:04:23 -0700617 return callModeler;
618 }
619
Santos Cordonde10b752013-09-19 04:11:33 -0700620 /* package */ CallManager getCallManager() {
621 return mCM;
622 }
623
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700624 /**
625 * Returns an Intent that can be used to go to the "Call log"
626 * UI (aka CallLogActivity) in the Contacts app.
627 *
628 * Watch out: there's no guarantee that the system has any activity to
629 * handle this intent. (In particular there may be no "Call log" at
630 * all on on non-voice-capable devices.)
631 */
632 /* package */ static Intent createCallLogIntent() {
633 Intent intent = new Intent(Intent.ACTION_VIEW, null);
634 intent.setType("vnd.android.cursor.dir/calls");
635 return intent;
636 }
637
Yorke Leeca6ec3b2013-08-29 14:21:43 -0700638 /* package */static PendingIntent createPendingCallLogIntent(Context context) {
639 final Intent callLogIntent = PhoneGlobals.createCallLogIntent();
640 final TaskStackBuilder taskStackBuilder = TaskStackBuilder.create(context);
641 taskStackBuilder.addNextIntent(callLogIntent);
642 return taskStackBuilder.getPendingIntent(0, 0);
643 }
644
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700645 /**
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700646 * Returns PendingIntent for hanging up ongoing phone call. This will typically be used from
647 * Notification context.
648 */
649 /* package */ static PendingIntent createHangUpOngoingCallPendingIntent(Context context) {
650 Intent intent = new Intent(PhoneGlobals.ACTION_HANG_UP_ONGOING_CALL, null,
651 context, NotificationBroadcastReceiver.class);
652 return PendingIntent.getBroadcast(context, 0, intent, 0);
653 }
654
655 /* package */ static PendingIntent getCallBackPendingIntent(Context context, String number) {
656 Intent intent = new Intent(ACTION_CALL_BACK_FROM_NOTIFICATION,
657 Uri.fromParts(Constants.SCHEME_TEL, number, null),
658 context, NotificationBroadcastReceiver.class);
659 return PendingIntent.getBroadcast(context, 0, intent, 0);
660 }
661
662 /* package */ static PendingIntent getSendSmsFromNotificationPendingIntent(
663 Context context, String number) {
664 Intent intent = new Intent(ACTION_SEND_SMS_FROM_NOTIFICATION,
665 Uri.fromParts(Constants.SCHEME_SMSTO, number, null),
666 context, NotificationBroadcastReceiver.class);
667 return PendingIntent.getBroadcast(context, 0, intent, 0);
668 }
669
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700670 boolean isSimPinEnabled() {
671 return mIsSimPinEnabled;
672 }
673
674 boolean authenticateAgainstCachedSimPin(String pin) {
675 return (mCachedSimPin != null && mCachedSimPin.equals(pin));
676 }
677
678 void setCachedSimPin(String pin) {
679 mCachedSimPin = pin;
680 }
681
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700682 /**
683 * Handles OTASP-related events from the telephony layer.
684 *
685 * While an OTASP call is active, the CallNotifier forwards
686 * OTASP-related telephony events to this method.
687 */
688 void handleOtaspEvent(Message msg) {
689 if (DBG) Log.d(LOG_TAG, "handleOtaspEvent(message " + msg + ")...");
690
691 if (otaUtils == null) {
692 // We shouldn't be getting OTASP events without ever
693 // having started the OTASP call in the first place!
694 Log.w(LOG_TAG, "handleOtaEvents: got an event but otaUtils is null! "
695 + "message = " + msg);
696 return;
697 }
698
699 otaUtils.onOtaProvisionStatusChanged((AsyncResult) msg.obj);
700 }
701
702 /**
703 * Similarly, handle the disconnect event of an OTASP call
704 * by forwarding it to the OtaUtils instance.
705 */
706 /* package */ void handleOtaspDisconnect() {
707 if (DBG) Log.d(LOG_TAG, "handleOtaspDisconnect()...");
708
709 if (otaUtils == null) {
710 // We shouldn't be getting OTASP events without ever
711 // having started the OTASP call in the first place!
712 Log.w(LOG_TAG, "handleOtaspDisconnect: otaUtils is null!");
713 return;
714 }
715
716 otaUtils.onOtaspDisconnect();
717 }
718
719 /**
720 * Sets the activity responsible for un-PUK-blocking the device
721 * so that we may close it when we receive a positive result.
722 * mPUKEntryActivity is also used to indicate to the device that
723 * we are trying to un-PUK-lock the phone. In other words, iff
724 * it is NOT null, then we are trying to unlock and waiting for
725 * the SIM to move to READY state.
726 *
727 * @param activity is the activity to close when PUK has
728 * finished unlocking. Can be set to null to indicate the unlock
729 * or SIM READYing process is over.
730 */
731 void setPukEntryActivity(Activity activity) {
732 mPUKEntryActivity = activity;
733 }
734
735 Activity getPUKEntryActivity() {
736 return mPUKEntryActivity;
737 }
738
739 /**
740 * Sets the dialog responsible for notifying the user of un-PUK-
741 * blocking - SIM READYing progress, so that we may dismiss it
742 * when we receive a positive result.
743 *
744 * @param dialog indicates the progress dialog informing the user
745 * of the state of the device. Dismissed upon completion of
746 * READYing process
747 */
748 void setPukEntryProgressDialog(ProgressDialog dialog) {
749 mPUKEntryProgressDialog = dialog;
750 }
751
752 ProgressDialog getPUKEntryProgressDialog() {
753 return mPUKEntryProgressDialog;
754 }
755
756 /**
757 * Controls whether or not the screen is allowed to sleep.
758 *
759 * Once sleep is allowed (WakeState is SLEEP), it will rely on the
760 * settings for the poke lock to determine when to timeout and let
761 * the device sleep {@link PhoneGlobals#setScreenTimeout}.
762 *
763 * @param ws tells the device to how to wake.
764 */
765 /* package */ void requestWakeState(WakeState ws) {
766 if (VDBG) Log.d(LOG_TAG, "requestWakeState(" + ws + ")...");
767 synchronized (this) {
768 if (mWakeState != ws) {
769 switch (ws) {
770 case PARTIAL:
771 // acquire the processor wake lock, and release the FULL
772 // lock if it is being held.
773 mPartialWakeLock.acquire();
774 if (mWakeLock.isHeld()) {
775 mWakeLock.release();
776 }
777 break;
778 case FULL:
779 // acquire the full wake lock, and release the PARTIAL
780 // lock if it is being held.
781 mWakeLock.acquire();
782 if (mPartialWakeLock.isHeld()) {
783 mPartialWakeLock.release();
784 }
785 break;
786 case SLEEP:
787 default:
788 // release both the PARTIAL and FULL locks.
789 if (mWakeLock.isHeld()) {
790 mWakeLock.release();
791 }
792 if (mPartialWakeLock.isHeld()) {
793 mPartialWakeLock.release();
794 }
795 break;
796 }
797 mWakeState = ws;
798 }
799 }
800 }
801
802 /**
803 * If we are not currently keeping the screen on, then poke the power
804 * manager to wake up the screen for the user activity timeout duration.
805 */
806 /* package */ void wakeUpScreen() {
807 synchronized (this) {
808 if (mWakeState == WakeState.SLEEP) {
809 if (DBG) Log.d(LOG_TAG, "pulse screen lock");
810 mPowerManager.wakeUp(SystemClock.uptimeMillis());
811 }
812 }
813 }
814
815 /**
816 * Sets the wake state and screen timeout based on the current state
817 * of the phone, and the current state of the in-call UI.
818 *
819 * This method is a "UI Policy" wrapper around
820 * {@link PhoneGlobals#requestWakeState} and {@link PhoneGlobals#setScreenTimeout}.
821 *
822 * It's safe to call this method regardless of the state of the Phone
823 * (e.g. whether or not it's idle), and regardless of the state of the
824 * Phone UI (e.g. whether or not the InCallScreen is active.)
825 */
826 /* package */ void updateWakeState() {
827 PhoneConstants.State state = mCM.getState();
828
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700829 // True if the speakerphone is in use. (If so, we *always* use
830 // the default timeout. Since the user is obviously not holding
831 // the phone up to his/her face, we don't need to worry about
832 // false touches, and thus don't need to turn the screen off so
833 // aggressively.)
834 // Note that we need to make a fresh call to this method any
835 // time the speaker state changes. (That happens in
836 // PhoneUtils.turnOnSpeaker().)
837 boolean isSpeakerInUse = (state == PhoneConstants.State.OFFHOOK) && PhoneUtils.isSpeakerOn(this);
838
839 // TODO (bug 1440854): The screen timeout *might* also need to
840 // depend on the bluetooth state, but this isn't as clear-cut as
841 // the speaker state (since while using BT it's common for the
842 // user to put the phone straight into a pocket, in which case the
843 // timeout should probably still be short.)
844
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700845 // Decide whether to force the screen on or not.
846 //
847 // Force the screen to be on if the phone is ringing or dialing,
848 // or if we're displaying the "Call ended" UI for a connection in
849 // the "disconnected" state.
850 // However, if the phone is disconnected while the user is in the
851 // middle of selecting a quick response message, we should not force
852 // the screen to be on.
853 //
854 boolean isRinging = (state == PhoneConstants.State.RINGING);
855 boolean isDialing = (phone.getForegroundCall().getState() == Call.State.DIALING);
Jay Shrauner6fe8fd62013-09-16 19:39:30 -0700856 boolean keepScreenOn = isRinging || isDialing;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700857 // keepScreenOn == true means we'll hold a full wake lock:
858 requestWakeState(keepScreenOn ? WakeState.FULL : WakeState.SLEEP);
859 }
860
861 /**
862 * Manually pokes the PowerManager's userActivity method. Since we
863 * set the {@link WindowManager.LayoutParams#INPUT_FEATURE_DISABLE_USER_ACTIVITY}
864 * flag while the InCallScreen is active when there is no proximity sensor,
865 * we need to do this for touch events that really do count as user activity
866 * (like pressing any onscreen UI elements.)
867 */
868 /* package */ void pokeUserActivity() {
869 if (VDBG) Log.d(LOG_TAG, "pokeUserActivity()...");
870 mPowerManager.userActivity(SystemClock.uptimeMillis(), false);
871 }
872
873 /**
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700874 * Notifies the phone app when the phone state changes.
875 *
Santos Cordonfc309812013-08-20 18:33:16 -0700876 * This method will updates various states inside Phone app (e.g. update-lock state, etc.)
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700877 */
878 /* package */ void updatePhoneState(PhoneConstants.State state) {
879 if (state != mLastPhoneState) {
880 mLastPhoneState = state;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700881
882 // Try to acquire or release UpdateLock.
883 //
884 // Watch out: we don't release the lock here when the screen is still in foreground.
885 // At that time InCallScreen will release it on onPause().
886 if (state != PhoneConstants.State.IDLE) {
887 // UpdateLock is a recursive lock, while we may get "acquire" request twice and
888 // "release" request once for a single call (RINGING + OFFHOOK and IDLE).
889 // We need to manually ensure the lock is just acquired once for each (and this
890 // will prevent other possible buggy situations too).
891 if (!mUpdateLock.isHeld()) {
892 mUpdateLock.acquire();
893 }
894 } else {
Jay Shraunera5d13212013-09-19 13:37:43 -0700895 if (mUpdateLock.isHeld()) {
Jay Shrauner6fe8fd62013-09-16 19:39:30 -0700896 mUpdateLock.release();
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700897 }
898 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700899 }
900 }
901
902 /* package */ PhoneConstants.State getPhoneState() {
903 return mLastPhoneState;
904 }
905
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700906 KeyguardManager getKeyguardManager() {
907 return mKeyguardManager;
908 }
909
910 private void onMMIComplete(AsyncResult r) {
911 if (VDBG) Log.d(LOG_TAG, "onMMIComplete()...");
912 MmiCode mmiCode = (MmiCode) r.result;
913 PhoneUtils.displayMMIComplete(phone, getInstance(), mmiCode, null, null);
914 }
915
916 private void initForNewRadioTechnology() {
917 if (DBG) Log.d(LOG_TAG, "initForNewRadioTechnology...");
918
919 if (phone.getPhoneType() == PhoneConstants.PHONE_TYPE_CDMA) {
920 // Create an instance of CdmaPhoneCallState and initialize it to IDLE
921 cdmaPhoneCallState = new CdmaPhoneCallState();
922 cdmaPhoneCallState.CdmaPhoneCallStateInit();
923 }
924 if (TelephonyCapabilities.supportsOtasp(phone)) {
925 //create instances of CDMA OTA data classes
926 if (cdmaOtaProvisionData == null) {
927 cdmaOtaProvisionData = new OtaUtils.CdmaOtaProvisionData();
928 }
929 if (cdmaOtaConfigData == null) {
930 cdmaOtaConfigData = new OtaUtils.CdmaOtaConfigData();
931 }
932 if (cdmaOtaScreenState == null) {
933 cdmaOtaScreenState = new OtaUtils.CdmaOtaScreenState();
934 }
935 if (cdmaOtaInCallScreenUiState == null) {
936 cdmaOtaInCallScreenUiState = new OtaUtils.CdmaOtaInCallScreenUiState();
937 }
938 } else {
939 //Clean up OTA data in GSM/UMTS. It is valid only for CDMA
940 clearOtaState();
941 }
942
943 ringer.updateRingerContextAfterRadioTechnologyChange(this.phone);
944 notifier.updateCallNotifierRegistrationsAfterRadioTechnologyChange();
945 callStateMonitor.updateAfterRadioTechnologyChange();
946
947 if (mBluetoothPhone != null) {
948 try {
949 mBluetoothPhone.updateBtHandsfreeAfterRadioTechnologyChange();
950 } catch (RemoteException e) {
951 Log.e(LOG_TAG, Log.getStackTraceString(new Throwable()));
952 }
953 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700954
955 // Update registration for ICC status after radio technology change
956 IccCard sim = phone.getIccCard();
957 if (sim != null) {
958 if (DBG) Log.d(LOG_TAG, "Update registration for ICC status...");
959
960 //Register all events new to the new active phone
961 sim.registerForNetworkLocked(mHandler, EVENT_SIM_NETWORK_LOCKED, null);
962 }
963 }
964
965
966 /**
Santos Cordon593ab382013-08-06 21:58:23 -0700967 * This is called when the wired headset state changes.
968 */
969 @Override
970 public void onWiredHeadsetConnection(boolean pluggedIn) {
971 PhoneConstants.State phoneState = mCM.getState();
972
Santos Cordon593ab382013-08-06 21:58:23 -0700973 // Force TTY state update according to new headset state
974 if (mTtyEnabled) {
975 mHandler.sendMessage(mHandler.obtainMessage(EVENT_TTY_PREFERRED_MODE_CHANGED, 0));
976 }
977 }
978
979 /**
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700980 * Receiver for misc intent broadcasts the Phone app cares about.
981 */
982 private class PhoneAppBroadcastReceiver extends BroadcastReceiver {
983 @Override
984 public void onReceive(Context context, Intent intent) {
985 String action = intent.getAction();
986 if (action.equals(Intent.ACTION_AIRPLANE_MODE_CHANGED)) {
987 boolean enabled = System.getInt(getContentResolver(),
988 System.AIRPLANE_MODE_ON, 0) == 0;
989 phone.setRadioPower(enabled);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700990 } else if (action.equals(TelephonyIntents.ACTION_ANY_DATA_CONNECTION_STATE_CHANGED)) {
991 if (VDBG) Log.d(LOG_TAG, "mReceiver: ACTION_ANY_DATA_CONNECTION_STATE_CHANGED");
992 if (VDBG) Log.d(LOG_TAG, "- state: " + intent.getStringExtra(PhoneConstants.STATE_KEY));
993 if (VDBG) Log.d(LOG_TAG, "- reason: "
994 + intent.getStringExtra(PhoneConstants.STATE_CHANGE_REASON_KEY));
995
996 // The "data disconnected due to roaming" notification is shown
997 // if (a) you have the "data roaming" feature turned off, and
998 // (b) you just lost data connectivity because you're roaming.
999 boolean disconnectedDueToRoaming =
1000 !phone.getDataRoamingEnabled()
1001 && "DISCONNECTED".equals(intent.getStringExtra(PhoneConstants.STATE_KEY))
1002 && Phone.REASON_ROAMING_ON.equals(
1003 intent.getStringExtra(PhoneConstants.STATE_CHANGE_REASON_KEY));
1004 mHandler.sendEmptyMessage(disconnectedDueToRoaming
1005 ? EVENT_DATA_ROAMING_DISCONNECTED
1006 : EVENT_DATA_ROAMING_OK);
Santos Cordon7d4ddf62013-07-10 11:58:08 -07001007 } else if ((action.equals(TelephonyIntents.ACTION_SIM_STATE_CHANGED)) &&
1008 (mPUKEntryActivity != null)) {
1009 // if an attempt to un-PUK-lock the device was made, while we're
1010 // receiving this state change notification, notify the handler.
1011 // NOTE: This is ONLY triggered if an attempt to un-PUK-lock has
1012 // been attempted.
1013 mHandler.sendMessage(mHandler.obtainMessage(EVENT_SIM_STATE_CHANGED,
1014 intent.getStringExtra(IccCardConstants.INTENT_KEY_ICC_STATE)));
1015 } else if (action.equals(TelephonyIntents.ACTION_RADIO_TECHNOLOGY_CHANGED)) {
1016 String newPhone = intent.getStringExtra(PhoneConstants.PHONE_NAME_KEY);
1017 Log.d(LOG_TAG, "Radio technology switched. Now " + newPhone + " is active.");
1018 initForNewRadioTechnology();
1019 } else if (action.equals(TelephonyIntents.ACTION_SERVICE_STATE_CHANGED)) {
1020 handleServiceStateChanged(intent);
1021 } else if (action.equals(TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED)) {
1022 if (TelephonyCapabilities.supportsEcm(phone)) {
1023 Log.d(LOG_TAG, "Emergency Callback Mode arrived in PhoneApp.");
1024 // Start Emergency Callback Mode service
1025 if (intent.getBooleanExtra("phoneinECMState", false)) {
1026 context.startService(new Intent(context,
1027 EmergencyCallbackModeService.class));
1028 }
1029 } else {
1030 // It doesn't make sense to get ACTION_EMERGENCY_CALLBACK_MODE_CHANGED
1031 // on a device that doesn't support ECM in the first place.
1032 Log.e(LOG_TAG, "Got ACTION_EMERGENCY_CALLBACK_MODE_CHANGED, "
1033 + "but ECM isn't supported for phone: " + phone.getPhoneName());
1034 }
1035 } else if (action.equals(Intent.ACTION_DOCK_EVENT)) {
1036 mDockState = intent.getIntExtra(Intent.EXTRA_DOCK_STATE,
1037 Intent.EXTRA_DOCK_STATE_UNDOCKED);
1038 if (VDBG) Log.d(LOG_TAG, "ACTION_DOCK_EVENT -> mDockState = " + mDockState);
1039 mHandler.sendMessage(mHandler.obtainMessage(EVENT_DOCK_STATE_CHANGED, 0));
1040 } else if (action.equals(TtyIntent.TTY_PREFERRED_MODE_CHANGE_ACTION)) {
1041 mPreferredTtyMode = intent.getIntExtra(TtyIntent.TTY_PREFFERED_MODE,
1042 Phone.TTY_MODE_OFF);
1043 if (VDBG) Log.d(LOG_TAG, "mReceiver: TTY_PREFERRED_MODE_CHANGE_ACTION");
1044 if (VDBG) Log.d(LOG_TAG, " mode: " + mPreferredTtyMode);
1045 mHandler.sendMessage(mHandler.obtainMessage(EVENT_TTY_PREFERRED_MODE_CHANGED, 0));
1046 } else if (action.equals(AudioManager.RINGER_MODE_CHANGED_ACTION)) {
1047 int ringerMode = intent.getIntExtra(AudioManager.EXTRA_RINGER_MODE,
1048 AudioManager.RINGER_MODE_NORMAL);
1049 if (ringerMode == AudioManager.RINGER_MODE_SILENT) {
1050 notifier.silenceRinger();
1051 }
1052 }
1053 }
1054 }
1055
Santos Cordon7d4ddf62013-07-10 11:58:08 -07001056 /**
1057 * Accepts broadcast Intents which will be prepared by {@link NotificationMgr} and thus
1058 * sent from framework's notification mechanism (which is outside Phone context).
1059 * This should be visible from outside, but shouldn't be in "exported" state.
1060 *
1061 * TODO: If possible merge this into PhoneAppBroadcastReceiver.
1062 */
1063 public static class NotificationBroadcastReceiver extends BroadcastReceiver {
1064 @Override
1065 public void onReceive(Context context, Intent intent) {
1066 String action = intent.getAction();
1067 // TODO: use "if (VDBG)" here.
1068 Log.d(LOG_TAG, "Broadcast from Notification: " + action);
1069
1070 if (action.equals(ACTION_HANG_UP_ONGOING_CALL)) {
1071 PhoneUtils.hangup(PhoneGlobals.getInstance().mCM);
1072 } else if (action.equals(ACTION_CALL_BACK_FROM_NOTIFICATION)) {
1073 // Collapse the expanded notification and the notification item itself.
1074 closeSystemDialogs(context);
1075 clearMissedCallNotification(context);
1076
1077 Intent callIntent = new Intent(Intent.ACTION_CALL_PRIVILEGED, intent.getData());
1078 callIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
1079 | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
1080 context.startActivity(callIntent);
1081 } else if (action.equals(ACTION_SEND_SMS_FROM_NOTIFICATION)) {
1082 // Collapse the expanded notification and the notification item itself.
1083 closeSystemDialogs(context);
1084 clearMissedCallNotification(context);
1085
1086 Intent smsIntent = new Intent(Intent.ACTION_SENDTO, intent.getData());
1087 smsIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
1088 context.startActivity(smsIntent);
1089 } else {
1090 Log.w(LOG_TAG, "Received hang-up request from notification,"
1091 + " but there's no call the system can hang up.");
1092 }
1093 }
1094
1095 private void closeSystemDialogs(Context context) {
1096 Intent intent = new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
1097 context.sendBroadcastAsUser(intent, UserHandle.ALL);
1098 }
1099
1100 private void clearMissedCallNotification(Context context) {
1101 Intent clearIntent = new Intent(context, ClearMissedCallsService.class);
1102 clearIntent.setAction(ClearMissedCallsService.ACTION_CLEAR_MISSED_CALLS);
1103 context.startService(clearIntent);
1104 }
1105 }
1106
1107 private void handleServiceStateChanged(Intent intent) {
1108 /**
1109 * This used to handle updating EriTextWidgetProvider this routine
1110 * and and listening for ACTION_SERVICE_STATE_CHANGED intents could
1111 * be removed. But leaving just in case it might be needed in the near
1112 * future.
1113 */
1114
1115 // If service just returned, start sending out the queued messages
1116 ServiceState ss = ServiceState.newFromBundle(intent.getExtras());
1117
1118 if (ss != null) {
1119 int state = ss.getState();
1120 notificationMgr.updateNetworkSelection(state);
1121 }
1122 }
1123
1124 public boolean isOtaCallInActiveState() {
1125 boolean otaCallActive = false;
Santos Cordon7d4ddf62013-07-10 11:58:08 -07001126 if (VDBG) Log.d(LOG_TAG, "- isOtaCallInActiveState " + otaCallActive);
1127 return otaCallActive;
1128 }
1129
1130 public boolean isOtaCallInEndState() {
1131 boolean otaCallEnded = false;
Santos Cordon7d4ddf62013-07-10 11:58:08 -07001132 if (VDBG) Log.d(LOG_TAG, "- isOtaCallInEndState " + otaCallEnded);
1133 return otaCallEnded;
1134 }
1135
1136 // it is safe to call clearOtaState() even if the InCallScreen isn't active
1137 public void clearOtaState() {
1138 if (DBG) Log.d(LOG_TAG, "- clearOtaState ...");
Jay Shrauner6fe8fd62013-09-16 19:39:30 -07001139 if (otaUtils != null) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -07001140 otaUtils.cleanOtaScreen(true);
1141 if (DBG) Log.d(LOG_TAG, " - clearOtaState clears OTA screen");
1142 }
1143 }
1144
1145 // it is safe to call dismissOtaDialogs() even if the InCallScreen isn't active
1146 public void dismissOtaDialogs() {
1147 if (DBG) Log.d(LOG_TAG, "- dismissOtaDialogs ...");
Jay Shrauner6fe8fd62013-09-16 19:39:30 -07001148 if (otaUtils != null) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -07001149 otaUtils.dismissAllOtaDialogs();
1150 if (DBG) Log.d(LOG_TAG, " - dismissOtaDialogs clears OTA dialogs");
1151 }
1152 }
1153
Santos Cordon7d4ddf62013-07-10 11:58:08 -07001154 private void handleQueryTTYModeResponse(Message msg) {
1155 AsyncResult ar = (AsyncResult) msg.obj;
1156 if (ar.exception != null) {
1157 if (DBG) Log.d(LOG_TAG, "handleQueryTTYModeResponse: Error getting TTY state.");
1158 } else {
1159 if (DBG) Log.d(LOG_TAG,
1160 "handleQueryTTYModeResponse: TTY enable state successfully queried.");
1161
1162 int ttymode = ((int[]) ar.result)[0];
1163 if (DBG) Log.d(LOG_TAG, "handleQueryTTYModeResponse:ttymode=" + ttymode);
1164
1165 Intent ttyModeChanged = new Intent(TtyIntent.TTY_ENABLED_CHANGE_ACTION);
1166 ttyModeChanged.putExtra("ttyEnabled", ttymode != Phone.TTY_MODE_OFF);
1167 sendBroadcastAsUser(ttyModeChanged, UserHandle.ALL);
1168
1169 String audioTtyMode;
1170 switch (ttymode) {
1171 case Phone.TTY_MODE_FULL:
1172 audioTtyMode = "tty_full";
1173 break;
1174 case Phone.TTY_MODE_VCO:
1175 audioTtyMode = "tty_vco";
1176 break;
1177 case Phone.TTY_MODE_HCO:
1178 audioTtyMode = "tty_hco";
1179 break;
1180 case Phone.TTY_MODE_OFF:
1181 default:
1182 audioTtyMode = "tty_off";
1183 break;
1184 }
1185 AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
1186 audioManager.setParameters("tty_mode="+audioTtyMode);
1187 }
1188 }
1189
1190 private void handleSetTTYModeResponse(Message msg) {
1191 AsyncResult ar = (AsyncResult) msg.obj;
1192
1193 if (ar.exception != null) {
1194 if (DBG) Log.d (LOG_TAG,
1195 "handleSetTTYModeResponse: Error setting TTY mode, ar.exception"
1196 + ar.exception);
1197 }
1198 phone.queryTTYMode(mHandler.obtainMessage(EVENT_TTY_MODE_GET));
1199 }
1200
1201 /**
1202 * "Call origin" may be used by Contacts app to specify where the phone call comes from.
1203 * Currently, the only permitted value for this extra is {@link #ALLOWED_EXTRA_CALL_ORIGIN}.
1204 * Any other value will be ignored, to make sure that malicious apps can't trick the in-call
1205 * UI into launching some random other app after a call ends.
1206 *
1207 * TODO: make this more generic. Note that we should let the "origin" specify its package
1208 * while we are now assuming it is "com.android.contacts"
1209 */
1210 public static final String EXTRA_CALL_ORIGIN = "com.android.phone.CALL_ORIGIN";
1211 private static final String DEFAULT_CALL_ORIGIN_PACKAGE = "com.android.dialer";
1212 private static final String ALLOWED_EXTRA_CALL_ORIGIN =
1213 "com.android.dialer.DialtactsActivity";
1214 /**
1215 * Used to determine if the preserved call origin is fresh enough.
1216 */
1217 private static final long CALL_ORIGIN_EXPIRATION_MILLIS = 30 * 1000;
1218
Santos Cordon7d4ddf62013-07-10 11:58:08 -07001219 /** Service connection */
1220 private final ServiceConnection mBluetoothPhoneConnection = new ServiceConnection() {
1221
1222 /** Handle the task of binding the local object to the service */
1223 public void onServiceConnected(ComponentName className, IBinder service) {
1224 Log.i(LOG_TAG, "Headset phone created, binding local service.");
1225 mBluetoothPhone = IBluetoothHeadsetPhone.Stub.asInterface(service);
1226 }
1227
1228 /** Handle the task of cleaning up the local binding */
1229 public void onServiceDisconnected(ComponentName className) {
1230 Log.i(LOG_TAG, "Headset phone disconnected, cleaning local binding.");
1231 mBluetoothPhone = null;
1232 }
1233 };
Santos Cordon83570472013-09-06 15:45:10 -07001234
Santos Cordon7d4ddf62013-07-10 11:58:08 -07001235}