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