PhoneApp.java revision 4b8337277ec2e375c3536b97c40e6617a7b12990
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 com.android.internal.telephony.MmiCode;
20import com.android.internal.telephony.Phone;
21import com.android.internal.telephony.PhoneFactory;
22import com.android.internal.telephony.SimCard;
23import com.android.internal.telephony.TelephonyIntents;
24
25import android.app.Activity;
26import android.app.Application;
27import android.app.KeyguardManager;
28import android.app.ProgressDialog;
29import android.bluetooth.BluetoothHeadset;
30import android.bluetooth.BluetoothIntent;
31import android.content.BroadcastReceiver;
32import android.content.ContentResolver;
33import android.content.Context;
34import android.content.Intent;
35import android.content.IntentFilter;
36import android.media.AudioManager;
37import android.net.Uri;
38import android.os.AsyncResult;
39import android.os.Binder;
40import android.os.Handler;
41import android.os.IBinder;
42import android.os.IPowerManager;
43import android.os.LocalPowerManager;
44import android.os.Message;
45import android.os.PowerManager;
46import android.os.RemoteException;
47import android.os.ServiceManager;
48import android.os.SystemClock;
49import android.os.SystemProperties;
50import android.preference.PreferenceManager;
51import android.provider.Settings.System;
52import android.util.Config;
53import android.util.Log;
54import android.view.KeyEvent;
55import android.widget.Toast;
56
57/**
58 * Top-level Application class for the Phone app.
59 */
60public class PhoneApp extends Application {
61    /* package */ static final String LOG_TAG = "PhoneApp";
62    /* package */ static final boolean DBG =
63            (SystemProperties.getInt("ro.debuggable", 0) == 1);
64
65    // Message codes; see mHandler below.
66    private static final int EVENT_SIM_ABSENT = 1;
67    private static final int EVENT_SIM_LOCKED = 2;
68    private static final int EVENT_SIM_NETWORK_LOCKED = 3;
69    private static final int EVENT_WIRED_HEADSET_PLUG = 7;
70    private static final int EVENT_SIM_STATE_CHANGED = 8;
71    private static final int EVENT_UPDATE_INCALL_NOTIFICATION = 9;
72    private static final int EVENT_DATA_ROAMING_DISCONNECTED = 10;
73    private static final int EVENT_DATA_ROAMING_OK = 11;
74
75    // The MMI codes are also used by the InCallScreen.
76    public static final int MMI_INITIATE = 51;
77    public static final int MMI_COMPLETE = 52;
78    public static final int MMI_CANCEL = 53;
79    // Don't use message codes larger than 99 here; those are reserved for
80    // the individual Activities of the Phone UI.
81
82    /**
83     * Allowable values for the poke lock code (timeout between a user activity and the
84     * going to sleep), please refer to {@link com.android.server.PowerManagerService}
85     * for additional reference.
86     *   SHORT uses the short delay for the timeout (SHORT_KEYLIGHT_DELAY, 6 sec)
87     *   MEDIUM uses the medium delay for the timeout (MEDIUM_KEYLIGHT_DELAY, 15 sec)
88     *   DEFAULT is the system-wide default delay for the timeout (1 min)
89     */
90    public enum ScreenTimeoutDuration {
91        SHORT,
92        MEDIUM,
93        DEFAULT
94    }
95
96    /**
97     * Allowable values for the wake lock code.
98     *   SLEEP means the device can be put to sleep.
99     *   PARTIAL means wake the processor, but we display can be kept off.
100     *   FULL means wake both the processor and the display.
101     */
102    public enum WakeState {
103        SLEEP,
104        PARTIAL,
105        FULL
106    }
107
108    private static PhoneApp sMe;
109
110    // A few important fields we expose to the rest of the package
111    // directly (rather than thru set/get methods) for efficiency.
112    Phone phone;
113    CallNotifier notifier;
114    Ringer ringer;
115    BluetoothHandsfree mBtHandsfree;
116    PhoneInterfaceManager phoneMgr;
117    int mBluetoothHeadsetState = BluetoothHeadset.STATE_ERROR;
118    int mBluetoothHeadsetAudioState = BluetoothHeadset.STATE_ERROR;
119    boolean mShowBluetoothIndication = false;
120
121    // The InCallScreen instance (or null if the InCallScreen hasn't been
122    // created yet.)
123    private InCallScreen mInCallScreen;
124
125    // The currently-active PUK entry activity and progress dialog.
126    // Normally, these are the Emergency Dialer and the subsequent
127    // progress dialog.  null if there is are no such objects in
128    // the foreground.
129    private Activity mPUKEntryActivity;
130    private ProgressDialog mPUKEntryProgressDialog;
131
132    private boolean mIsSimPinEnabled;
133    private String mCachedSimPin;
134
135    // True if a wired headset is currently plugged in, based on the state
136    // from the latest Intent.ACTION_HEADSET_PLUG broadcast we received in
137    // mReceiver.onReceive().
138    private boolean mIsHeadsetPlugged;
139
140    private WakeState mWakeState = WakeState.SLEEP;
141    private ScreenTimeoutDuration mPokeLockSetting = ScreenTimeoutDuration.DEFAULT;
142    private IBinder mPokeLockToken = new Binder();
143    private IPowerManager mPowerManagerService;
144    private PowerManager.WakeLock mWakeLock;
145    private PowerManager.WakeLock mPartialWakeLock;
146    private KeyguardManager mKeyguardManager;
147    private KeyguardManager.KeyguardLock mKeyguardLock;
148
149    // Broadcast receiver for various intent broadcasts (see onCreate())
150    private final BroadcastReceiver mReceiver = new PhoneAppBroadcastReceiver();
151
152    // Broadcast receiver purely for ACTION_MEDIA_BUTTON broadcasts
153    private final BroadcastReceiver mMediaButtonReceiver = new MediaButtonBroadcastReceiver();
154
155    /** boolean indicating restoring mute state on InCallScreen.onResume() */
156    private boolean mShouldRestoreMuteOnInCallResume;
157
158    /**
159     * Set the restore mute state flag. Used when we are setting the mute state
160     * OUTSIDE of user interaction {@link PhoneUtils#startNewCall(Phone)}
161     */
162    /*package*/void setRestoreMuteOnInCallResume (boolean mode) {
163        mShouldRestoreMuteOnInCallResume = mode;
164    }
165
166    /**
167     * Get the restore mute state flag.
168     * This is used by the InCallScreen {@link InCallScreen#onResume()} to figure
169     * out if we need to restore the mute state for the current active call.
170     */
171    /*package*/boolean getRestoreMuteOnInCallResume () {
172        return mShouldRestoreMuteOnInCallResume;
173    }
174
175    Handler mHandler = new Handler() {
176        @Override
177        public void handleMessage(Message msg) {
178            switch (msg.what) {
179                case EVENT_SIM_LOCKED:
180//                    mIsSimPinEnabled = true;
181//
182//                    if (Config.LOGV) Log.v(LOG_TAG, "show sim unlock panel");
183//                    SimPinUnlockPanel pinUnlockPanel = new SimPinUnlockPanel(
184//                            PhoneApp.getInstance());
185//                    pinUnlockPanel.show();
186                    break;
187
188                case EVENT_SIM_ABSENT:
189// Don't need this now that the lock screen handles this case
190//                    if (Config.LOGV) Log.v(LOG_TAG, "show sim missing panel");
191//                    SimMissingPanel missingPanel = new SimMissingPanel(
192//                            PhoneApp.getInstance());
193//                    missingPanel.show();
194                    break;
195
196                case EVENT_SIM_NETWORK_LOCKED:
197                    if (Config.LOGV) Log.v(LOG_TAG, "show sim depersonal panel");
198                    SimNetworkDepersonalizationPanel ndpPanel =
199                        new SimNetworkDepersonalizationPanel(PhoneApp.getInstance());
200                    ndpPanel.show();
201                    break;
202
203                case EVENT_UPDATE_INCALL_NOTIFICATION:
204                    // Tell the NotificationMgr to update the "ongoing
205                    // call" icon in the status bar, if necessary.
206                    // Currently, this is triggered by a bluetooth headset
207                    // state change (since the status bar icon needs to
208                    // turn blue when bluetooth is active.)
209                    NotificationMgr.getDefault().updateInCallNotification();
210                    break;
211
212                case EVENT_DATA_ROAMING_DISCONNECTED:
213                    NotificationMgr.getDefault().showDataDisconnectedRoaming();
214                    break;
215
216                case EVENT_DATA_ROAMING_OK:
217                    NotificationMgr.getDefault().hideDataDisconnectedRoaming();
218                    break;
219
220                case MMI_COMPLETE:
221                    onMMIComplete((AsyncResult) msg.obj);
222                    break;
223
224                case MMI_CANCEL:
225                    PhoneUtils.cancelMmiCode(phone);
226                    break;
227
228                case EVENT_WIRED_HEADSET_PLUG:
229                    // Since the presence of a wired headset or bluetooth affects the
230                    // speakerphone, update the "speaker" state.  We ONLY want to do
231                    // this on the wired headset connect / disconnect events for now
232                    // though, so we're only triggering on EVENT_WIRED_HEADSET_PLUG.
233                    if (!isHeadsetPlugged() &&
234                            (mBtHandsfree == null || !mBtHandsfree.isAudioOn())) {
235                        // is the state is "not connected", restore the speaker state.
236                        PhoneUtils.restoreSpeakerMode(getApplicationContext());
237                    }
238                    NotificationMgr.getDefault().updateSpeakerNotification();
239                    break;
240
241                case EVENT_SIM_STATE_CHANGED:
242                    // Marks the event where the SIM goes into ready state.
243                    // Right now, this is only used for the PUK-unlocking
244                    // process.
245                    if (msg.obj.equals(SimCard.INTENT_VALUE_SIM_READY)) {
246                        // when the right event is triggered and there
247                        // are UI objects in the foreground, we close
248                        // them to display the lock panel.
249                        if (mPUKEntryActivity != null) {
250                            mPUKEntryActivity.finish();
251                            mPUKEntryActivity = null;
252                        }
253                        if (mPUKEntryProgressDialog != null) {
254                            mPUKEntryProgressDialog.dismiss();
255                            mPUKEntryProgressDialog = null;
256                        }
257                    }
258                    break;
259            }
260        }
261    };
262
263    public PhoneApp() {
264        sMe = this;
265    }
266
267    @Override
268    public void onCreate() {
269        if (Config.LOGV) Log.v(LOG_TAG, "onCreate()...");
270
271        ContentResolver resolver = getContentResolver();
272
273        if (phone == null) {
274            // Initialize the telephony framework
275            PhoneFactory.makeDefaultPhones(this);
276
277            // Get the default phone
278            phone = PhoneFactory.getDefaultPhone();
279
280            NotificationMgr.init(this);
281
282            phoneMgr = new PhoneInterfaceManager(this, phone);
283            if (getSystemService(Context.BLUETOOTH_SERVICE) != null) {
284                mBtHandsfree = new BluetoothHandsfree(this, phone);
285                startService(new Intent(this, BluetoothHeadsetService.class));
286            } else {
287                // Device is not bluetooth capable
288                mBtHandsfree = null;
289            }
290
291            ringer = new Ringer(phone);
292
293            SoundEffect.init();
294
295            // before registering for phone state changes
296            PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
297            mWakeLock = pm.newWakeLock(PowerManager.FULL_WAKE_LOCK
298                    | PowerManager.ACQUIRE_CAUSES_WAKEUP
299                    | PowerManager.ON_AFTER_RELEASE, LOG_TAG);
300            // lock used to keep the processor awake, when we don't care for the display.
301            mPartialWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK
302                    | PowerManager.ON_AFTER_RELEASE, LOG_TAG);
303            mKeyguardManager = (KeyguardManager) getSystemService(Context.KEYGUARD_SERVICE);
304            mKeyguardLock = mKeyguardManager.newKeyguardLock(LOG_TAG);
305
306            // get a handle to the service so that we can use it later when we
307            // want to set the poke lock.
308            mPowerManagerService = IPowerManager.Stub.asInterface(
309                    ServiceManager.getService("power"));
310
311            notifier = new CallNotifier(this, phone, ringer, mBtHandsfree);
312
313            // register for SIM status
314            SimCard sim = phone.getSimCard();
315            if (sim != null) {
316                if (Config.LOGV) Log.v(LOG_TAG, "register for SIM status");
317                sim.registerForAbsent(mHandler, EVENT_SIM_ABSENT, null);
318                sim.registerForLocked(mHandler, EVENT_SIM_LOCKED, null);
319                sim.registerForNetworkLocked(mHandler, EVENT_SIM_NETWORK_LOCKED, null);
320            }
321
322            // register for MMI/USSD
323            phone.registerForMmiComplete(mHandler, MMI_COMPLETE, null);
324
325            // register connection tracking to PhoneUtils
326            PhoneUtils.initializeConnectionHandler(phone);
327
328            // Register for misc other intent broadcasts.
329            IntentFilter intentFilter =
330                    new IntentFilter(Intent.ACTION_AIRPLANE_MODE_CHANGED);
331            intentFilter.addAction(BluetoothIntent.HEADSET_STATE_CHANGED_ACTION);
332            intentFilter.addAction(BluetoothIntent.HEADSET_AUDIO_STATE_CHANGED_ACTION);
333            intentFilter.addAction(TelephonyIntents.ACTION_ANY_DATA_CONNECTION_STATE_CHANGED);
334            intentFilter.addAction(Intent.ACTION_HEADSET_PLUG);
335            intentFilter.addAction(Intent.ACTION_BATTERY_LOW);
336            intentFilter.addAction(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
337            registerReceiver(mReceiver, intentFilter);
338
339            // Use a separate receiver for ACTION_MEDIA_BUTTON broadcasts,
340            // since we need to manually adjust its priority (to make sure
341            // we get these intents *before* the media player.)
342            IntentFilter mediaButtonIntentFilter =
343                    new IntentFilter(Intent.ACTION_MEDIA_BUTTON);
344            //
345            // Make sure we're higher priority than the media player's
346            // MediaButtonIntentReceiver (which currently has the default
347            // priority of zero; see apps/Music/AndroidManifest.xml.)
348            mediaButtonIntentFilter.setPriority(1);
349            //
350            registerReceiver(mMediaButtonReceiver, mediaButtonIntentFilter);
351
352            //set the default values for the preferences in the phone.
353            PreferenceManager.setDefaultValues(this, R.xml.network_setting, false);
354            PreferenceManager.setDefaultValues(this, R.xml.call_feature_setting, false);
355
356            // Make sure the audio mode (along with some
357            // audio-mode-related state of our own) is initialized
358            // correctly, given the current state of the phone.
359            switch (phone.getState()) {
360                case IDLE:
361                    if (DBG) Log.d(LOG_TAG, "Resetting audio state/mode: IDLE");
362                    PhoneUtils.setAudioControlState(PhoneUtils.AUDIO_IDLE);
363                    PhoneUtils.setAudioMode(this, AudioManager.MODE_NORMAL);
364                    break;
365                case RINGING:
366                    if (DBG) Log.d(LOG_TAG, "Resetting audio state/mode: RINGING");
367                    PhoneUtils.setAudioControlState(PhoneUtils.AUDIO_RINGING);
368                    PhoneUtils.setAudioMode(this, AudioManager.MODE_RINGTONE);
369                    break;
370                case OFFHOOK:
371                    if (DBG) Log.d(LOG_TAG, "Resetting audio state/mode: OFFHOOK");
372                    PhoneUtils.setAudioControlState(PhoneUtils.AUDIO_OFFHOOK);
373                    PhoneUtils.setAudioMode(this, AudioManager.MODE_IN_CALL);
374                    break;
375            }
376        }
377
378        // XXX pre-load the SimProvider so that it's ready
379        resolver.getType(Uri.parse("content://sim/adn"));
380
381        // start with the default value to set the mute state.
382        mShouldRestoreMuteOnInCallResume = false;
383   }
384
385    /**
386     * Returns the singleton instance of the PhoneApp.
387     */
388    static PhoneApp getInstance() {
389        return sMe;
390    }
391
392    Ringer getRinger() {
393        return ringer;
394    }
395
396    BluetoothHandsfree getBluetoothHandsfree() {
397        return mBtHandsfree;
398    }
399
400    static Intent createCallLogIntent() {
401        Intent  intent = new Intent(Intent.ACTION_VIEW, null);
402        intent.setType("vnd.android.cursor.dir/calls");
403        return intent;
404    }
405
406    /**
407     * Return an Intent that can be used to bring up the in-call screen.
408     *
409     * This intent can only be used from within the Phone app, since the
410     * InCallScreen is not exported from our AndroidManifest.
411     */
412    /* package */ static Intent createInCallIntent() {
413        Intent intent = new Intent(Intent.ACTION_MAIN, null);
414        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
415                | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
416                | Intent.FLAG_ACTIVITY_NO_USER_ACTION);
417        intent.setClassName("com.android.phone", getCallScreenClassName());
418        return intent;
419    }
420
421    /**
422     * Variation of createInCallIntent() that also specifies whether the
423     * DTMF dialpad should be initially visible when the InCallScreen
424     * comes up.
425     */
426    /* package */ static Intent createInCallIntent(boolean showDialpad) {
427        Intent intent = createInCallIntent();
428        intent.putExtra(InCallScreen.SHOW_DIALPAD_EXTRA, showDialpad);
429        return intent;
430    }
431
432    static String getCallScreenClassName() {
433        return InCallScreen.class.getName();
434    }
435
436    /**
437     * Starts the InCallScreen Activity.
438     */
439    void displayCallScreen() {
440        // if (DBG) Log.d(LOG_TAG, "displayCallScreen()...", new Throwable("stack dump"));
441        startActivity(createInCallIntent());
442        Profiler.callScreenRequested();
443    }
444
445    /**
446     * Helper function to check for one special feature of the CALL key:
447     * Normally, when the phone is idle, CALL takes you to the call log
448     * (see the handler for KEYCODE_CALL in PhoneWindow.onKeyUp().)
449     * But if the phone is in use (either off-hook or ringing) we instead
450     * handle the CALL button by taking you to the in-call UI.
451     *
452     * @return true if we intercepted the CALL keypress (i.e. the phone
453     *              was in use)
454     *
455     * @see DialerActivity#onCreate
456     */
457    boolean handleInCallOrRinging() {
458        if (phone.getState() != Phone.State.IDLE) {
459            // Phone is OFFHOOK or RINGING.
460            if (DBG) Log.v(LOG_TAG,
461                           "handleInCallOrRinging: show call screen");
462            displayCallScreen();
463            return true;
464        }
465        return false;
466    }
467
468    boolean isSimPinEnabled() {
469        return mIsSimPinEnabled;
470    }
471
472    boolean authenticateAgainstCachedSimPin(String pin) {
473        return (mCachedSimPin != null && mCachedSimPin.equals(pin));
474    }
475
476    void setCachedSimPin(String pin) {
477        mCachedSimPin = pin;
478    }
479
480    void setInCallScreenInstance(InCallScreen inCallScreen) {
481        mInCallScreen = inCallScreen;
482    }
483
484    /**
485     * @return true if the in-call UI is running as the foreground
486     * activity.  (In other words, from the perspective of the
487     * InCallScreen activity, return true between onResume() and
488     * onPause().)
489     *
490     * Note this method will return false if the screen is currently off,
491     * even if the InCallScreen *was* in the foreground just before the
492     * screen turned off.  (This is because the foreground activity is
493     * always "paused" while the screen is off.)
494     */
495    boolean isShowingCallScreen() {
496        if (mInCallScreen == null) return false;
497        return mInCallScreen.isForegroundActivity();
498    }
499
500    /**
501     * Dismisses the in-call UI.
502     *
503     * This also ensures that you won't be able to get back to the in-call
504     * UI via the BACK button (since this call removes the InCallScreen
505     * from the activity history.)
506     */
507    void dismissCallScreen() {
508        if (mInCallScreen != null) {
509            mInCallScreen.finish();
510        }
511    }
512
513    /**
514     * Sets the activity responsible for un-PUK-blocking the device
515     * so that we may close it when we receive a positive result.
516     * mPUKEntryActivity is also used to indicate to the device that
517     * we are trying to un-PUK-lock the phone. In other words, iff
518     * it is NOT null, then we are trying to unlock and waiting for
519     * the SIM to move to READY state.
520     *
521     * @param activity is the activity to close when PUK has
522     * finished unlocking. Can be set to null to indicate the unlock
523     * or SIM READYing process is over.
524     */
525    void setPukEntryActivity(Activity activity) {
526        mPUKEntryActivity = activity;
527    }
528
529    Activity getPUKEntryActivity() {
530        return mPUKEntryActivity;
531    }
532
533    /**
534     * Sets the dialog responsible for notifying the user of un-PUK-
535     * blocking - SIM READYing progress, so that we may dismiss it
536     * when we receive a positive result.
537     *
538     * @param dialog indicates the progress dialog informing the user
539     * of the state of the device.  Dismissed upon completion of
540     * READYing process
541     */
542    void setPukEntryProgressDialog(ProgressDialog dialog) {
543        mPUKEntryProgressDialog = dialog;
544    }
545
546    ProgressDialog getPUKEntryProgressDialog() {
547        return mPUKEntryProgressDialog;
548    }
549
550    /**
551     * Disables the keyguard.  This is used by the phone app to allow
552     * interaction with the Phone UI when the keyguard would otherwise be
553     * active (like receiving an incoming call while the device is
554     * locked.)
555     *
556     * Any call to this method MUST be followed (eventually)
557     * by a corresponding reenableKeyguard() call.
558     */
559    /* package */ void disableKeyguard() {
560        if (DBG) Log.d(LOG_TAG, "disable keyguard");
561        // if (DBG) Log.d(LOG_TAG, "disableKeyguard()...", new Throwable("stack dump"));
562        mKeyguardLock.disableKeyguard();
563    }
564
565    /**
566     * Re-enables the keyguard after a previous disableKeyguard() call.
567     *
568     * Any call to this method MUST correspond to (i.e. be balanced with)
569     * a previous disableKeyguard() call.
570     */
571    /* package */ void reenableKeyguard() {
572        if (DBG) Log.d(LOG_TAG, "re-enable keyguard");
573        // if (DBG) Log.d(LOG_TAG, "reenableKeyguard()...", new Throwable("stack dump"));
574        mKeyguardLock.reenableKeyguard();
575    }
576
577    /**
578     * Controls how quickly the screen times out.
579     *
580     * The poke lock controls how long it takes before the screen powers
581     * down, and therefore has no immediate effect when the current
582     * WakeState (see {@link PhoneApp#requestWakeState}) is FULL.
583     * If we're in a state where the screen *is* allowed to turn off,
584     * though, the poke lock will determine the timeout interval (long or
585     * short).
586     *
587     * @param shortPokeLock tells the device the timeout duration to use
588     * before going to sleep
589     * {@link com.android.server.PowerManagerService#SHORT_KEYLIGHT_DELAY}.
590     */
591    /* package */ void setScreenTimeout(ScreenTimeoutDuration duration) {
592        if (DBG) Log.d(LOG_TAG, "setScreenTimeout(" + duration + ")...");
593
594        // make sure we don't set the poke lock repeatedly so that we
595        // avoid triggering the userActivity calls in
596        // PowerManagerService.setPokeLock().
597        if (duration == mPokeLockSetting) {
598            return;
599        }
600        mPokeLockSetting = duration;
601
602        // This is kind of convoluted, but the basic thing to remember is
603        // that the poke lock just sends a message to the screen to tell
604        // it to stay on for a while.
605        // The default is 0, for a long timeout and should be set that way
606        // when we are heading back into a the keyguard / screen off
607        // state, and also when we're trying to keep the screen alive
608        // while ringing.  We'll also want to ignore the cheek events
609        // regardless of the timeout duration.
610        // The short timeout is really used whenever we want to give up
611        // the screen lock, such as when we're in call.
612        int pokeLockSetting = LocalPowerManager.POKE_LOCK_IGNORE_CHEEK_EVENTS;
613        switch (duration) {
614            case SHORT:
615                // Set the poke lock to timeout the display after a short
616                // timeout (5s). This ensures that the screen goes to sleep
617                // as soon as acceptably possible after we the wake lock
618                // has been released.
619                if (DBG) Log.d(LOG_TAG, "setting short poke lock");
620                pokeLockSetting |= LocalPowerManager.POKE_LOCK_SHORT_TIMEOUT;
621                break;
622
623            case MEDIUM:
624                // Set the poke lock to timeout the display after a medium
625                // timeout (15s). This ensures that the screen goes to sleep
626                // as soon as acceptably possible after we the wake lock
627                // has been released.
628                if (DBG) Log.d(LOG_TAG, "setting medium poke lock");
629                pokeLockSetting |= LocalPowerManager.POKE_LOCK_MEDIUM_TIMEOUT;
630                break;
631
632            case DEFAULT:
633            default:
634                // set the poke lock to timeout the display after a long
635                // delay by default.
636                // TODO: it may be nice to be able to disable cheek presses
637                // for long poke locks (emergency dialer, for instance).
638                if (DBG) Log.d(LOG_TAG, "reverting to normal long poke lock");
639                break;
640        }
641
642        // Send the request
643        try {
644            mPowerManagerService.setPokeLock(pokeLockSetting, mPokeLockToken, LOG_TAG);
645        } catch (RemoteException e) {
646        }
647    }
648
649    /**
650     * Controls whether or not the screen is allowed to sleep.
651     *
652     * Once sleep is allowed (WakeState is SLEEP), it will rely on the
653     * settings for the poke lock to determine when to timeout and let
654     * the device sleep {@link PhoneApp#setScreenTimeout}.
655     *
656     * @param ws tells the device to how to wake.
657     */
658    /* package */ void requestWakeState(WakeState ws) {
659        if (DBG) Log.d(LOG_TAG, "requestWakeState(" + ws + ")...");
660        if (mWakeState != ws) {
661            switch (ws) {
662                case PARTIAL:
663                    // acquire the processor wake lock, and release the FULL
664                    // lock if it is being held.
665                    if (DBG) Log.d(LOG_TAG, "acquire partial wake lock (CPU only)");
666                    mPartialWakeLock.acquire();
667                    if (mWakeLock.isHeld()) {
668                        mWakeLock.release();
669                    }
670                    break;
671                case FULL:
672                    // acquire the full wake lock, and release the PARTIAL
673                    // lock if it is being held.
674                    if (DBG) Log.d(LOG_TAG, "acquire full wake lock (CPU + Screen)");
675                    mWakeLock.acquire();
676                    if (mPartialWakeLock.isHeld()) {
677                        mPartialWakeLock.release();
678                    }
679                    break;
680                case SLEEP:
681                default:
682                    // release both the PARTIAL and FULL locks.
683                    if (DBG) Log.d(LOG_TAG, "release all wake locks");
684                    if (mWakeLock.isHeld()) {
685                        mWakeLock.release();
686                    }
687                    if (mPartialWakeLock.isHeld()) {
688                        mPartialWakeLock.release();
689                    }
690                    break;
691            }
692            mWakeState = ws;
693        }
694    }
695
696    /**
697     * If we are not currently keeping the screen on, then poke the power
698     * manager to wake up the screen for the user activity timeout duration.
699     */
700    /* package */ void wakeUpScreen() {
701        if (mWakeState == WakeState.SLEEP) {
702            if (DBG) Log.d(LOG_TAG, "pulse screen lock");
703            try {
704                mPowerManagerService.userActivityWithForce(SystemClock.uptimeMillis(), false, true);
705            } catch (RemoteException ex) {
706                // Ignore -- the system process is dead.
707            }
708        }
709    }
710
711    /**
712     * Sets the wake state and screen timeout based on the current state
713     * of the phone, and the current state of the in-call UI.
714     *
715     * This method is a "UI Policy" wrapper around
716     * {@link PhoneApp#requestWakeState} and {@link PhoneApp#setScreenTimeout}.
717     *
718     * It's safe to call this method regardless of the state of the Phone
719     * (e.g. whether or not it's idle), and regardless of the state of the
720     * Phone UI (e.g. whether or not the InCallScreen is active.)
721     */
722    /* package */ void updateWakeState() {
723        Phone.State state = phone.getState();
724
725        // True if the in-call UI is the foreground activity.
726        // (Note this will be false if the screen is currently off,
727        // since in that case *no* activity is in the foreground.)
728        boolean isShowingCallScreen = isShowingCallScreen();
729
730        // True if the InCallScreen's DTMF dialer is currently opened.
731        // (Note this does NOT imply whether or not the InCallScreen
732        // itself is visible.)
733        boolean isDialerOpened = (mInCallScreen != null) && mInCallScreen.isDialerOpened();
734
735        // True if the speakerphone is in use.  (If so, we *always* use
736        // the default timeout.  Since the user is obviously not holding
737        // the phone up to his/her face, we don't need to worry about
738        // false touches, and thus don't need to turn the screen off so
739        // aggressively.)
740        // Note that we need to make a fresh call to this method any
741        // time the speaker state changes.  (That happens in
742        // PhoneUtils.turnOnSpeaker().)
743        boolean isSpeakerInUse = (state == Phone.State.OFFHOOK) && PhoneUtils.isSpeakerOn(this);
744
745        // TODO (bug 1440854): The screen timeout *might* also need to
746        // depend on the bluetooth state, but this isn't as clear-cut as
747        // the speaker state (since while using BT it's common for the
748        // user to put the phone straight into a pocket, in which case the
749        // timeout should probably still be short.)
750
751        if (DBG) Log.d(LOG_TAG, "updateWakeState: isShowingCallScreen " + isShowingCallScreen
752                       + ", isDialerOpened " + isDialerOpened
753                       + ", isSpeakerInUse " + isSpeakerInUse + "...");
754
755        //
756        // (1) Set the screen timeout.
757        //
758        // Note that the "screen timeout" value we determine here is
759        // meaningless if the screen is forced on (see (2) below.)
760        //
761        if (!isShowingCallScreen || isSpeakerInUse) {
762            // Use the system-wide default timeout.
763            setScreenTimeout(ScreenTimeoutDuration.DEFAULT);
764        } else {
765            // We're on the in-call screen, and *not* using the speakerphone.
766            if (isDialerOpened) {
767                // The DTMF dialpad is up.  This case is special because
768                // the in-call UI has its own "touch lock" mechanism to
769                // disable the dialpad after a very short amount of idle
770                // time (to avoid false touches from the user's face while
771                // in-call.)
772                //
773                // In this case the *physical* screen just uses the
774                // system-wide default timeout.
775                setScreenTimeout(ScreenTimeoutDuration.DEFAULT);
776            } else {
777                // We're on the in-call screen, and not using the DTMF dialpad.
778                // There's actually no touchable UI onscreen at all in
779                // this state.  Also, the user is (most likely) not
780                // looking at the screen at all, since they're probably
781                // holding the phone up to their face.  Here we use a
782                // special screen timeout value specific to the in-call
783                // screen, purely to save battery life.
784                setScreenTimeout(ScreenTimeoutDuration.MEDIUM);
785            }
786        }
787
788        //
789        // (2) Decide whether to force the screen on or not.
790        //
791        // Force the screen to be on if the phone is ringing, or if we're
792        // displaying the "Call ended" UI for a connection in the
793        // "disconnected" state.
794        //
795        boolean isRinging = (state == Phone.State.RINGING);
796        boolean showingDisconnectedConnection =
797                PhoneUtils.hasDisconnectedConnections(phone) && isShowingCallScreen;
798        boolean keepScreenOn = isRinging || showingDisconnectedConnection;
799        if (DBG) Log.d(LOG_TAG, "updateWakeState: keepScreenOn = " + keepScreenOn
800                       + " (isRinging " + isRinging
801                       + ", showingDisconnectedConnection " + showingDisconnectedConnection + ")");
802        // keepScreenOn == true means we'll hold a full wake lock:
803        requestWakeState(keepScreenOn ? WakeState.FULL : WakeState.SLEEP);
804    }
805
806    /**
807     * Wrapper around the PowerManagerService.preventScreenOn() API.
808     * This allows the in-call UI to prevent the screen from turning on
809     * even if a subsequent call to updateWakeState() causes us to acquire
810     * a full wake lock.
811     */
812    /* package */ void preventScreenOn(boolean prevent) {
813        if (DBG) Log.d(LOG_TAG, "- preventScreenOn(" + prevent + ")...");
814        try {
815            mPowerManagerService.preventScreenOn(prevent);
816        } catch (RemoteException e) {
817            Log.w(LOG_TAG, "mPowerManagerService.preventScreenOn() failed: " + e);
818        }
819    }
820
821    KeyguardManager getKeyguardManager() {
822        return mKeyguardManager;
823    }
824
825    private void onMMIComplete(AsyncResult r) {
826        if (DBG) Log.d(LOG_TAG, "onMMIComplete()...");
827        MmiCode mmiCode = (MmiCode) r.result;
828        PhoneUtils.displayMMIComplete(phone, getInstance(), mmiCode, null, null);
829    }
830
831    /**
832     * @return true if a wired headset is currently plugged in.
833     *
834     * @see Intent.ACTION_HEADSET_PLUG (which we listen for in mReceiver.onReceive())
835     */
836    boolean isHeadsetPlugged() {
837        return mIsHeadsetPlugged;
838    }
839
840    /**
841     * @return true if the onscreen UI should currently be showing the
842     * special "bluetooth is active" indication in a couple of places (in
843     * which UI elements turn blue and/or show the bluetooth logo.)
844     *
845     * This depends on the BluetoothHeadset state *and* the current
846     * telephony state; see shouldShowBluetoothIndication().
847     *
848     * @see CallCard
849     * @see NotificationMgr.updateInCallNotification
850     */
851    /* package */ boolean showBluetoothIndication() {
852        return mShowBluetoothIndication;
853    }
854
855    /**
856     * Recomputes the mShowBluetoothIndication flag based on the current
857     * bluetooth state and current telephony state.
858     *
859     * This needs to be called any time the bluetooth headset state or the
860     * telephony state changes.
861     *
862     * @param forceUiUpdate if true, force the UI elements that care
863     *                      about this flag to update themselves.
864     */
865    /* package */ void updateBluetoothIndication(boolean forceUiUpdate) {
866        mShowBluetoothIndication = shouldShowBluetoothIndication(mBluetoothHeadsetState,
867                                                                 mBluetoothHeadsetAudioState,
868                                                                 phone);
869        if (forceUiUpdate) {
870            // Post Handler messages to the various components that might
871            // need to be refreshed based on the new state.
872            if (isShowingCallScreen()) mInCallScreen.updateBluetoothIndication();
873            mHandler.sendEmptyMessage(EVENT_UPDATE_INCALL_NOTIFICATION);
874        }
875    }
876
877    /**
878     * UI policy helper function for the couple of places in the UI that
879     * have some way of indicating that "bluetooth is in use."
880     *
881     * @return true if the onscreen UI should indicate that "bluetooth is in use",
882     *         based on the specified bluetooth headset state, and the
883     *         current state of the phone.
884     * @see showBluetoothIndication()
885     */
886    private static boolean shouldShowBluetoothIndication(int bluetoothState,
887                                                         int bluetoothAudioState,
888                                                         Phone phone) {
889        // We want the UI to indicate that "bluetooth is in use" in two
890        // slightly different cases:
891        //
892        // (a) The obvious case: if a bluetooth headset is currently in
893        //     use for an ongoing call.
894        //
895        // (b) The not-so-obvious case: if an incoming call is ringing,
896        //     and we expect that audio *will* be routed to a bluetooth
897        //     headset once the call is answered.
898
899        switch (phone.getState()) {
900            case OFFHOOK:
901                // This covers normal active calls, and also the case if
902                // the foreground call is DIALING or ALERTING.  In this
903                // case, bluetooth is considered "active" if a headset
904                // is connected *and* audio is being routed to it.
905                return ((bluetoothState == BluetoothHeadset.STATE_CONNECTED)
906                        && (bluetoothAudioState == BluetoothHeadset.AUDIO_STATE_CONNECTED));
907
908            case RINGING:
909                // If an incoming call is ringing, we're *not* yet routing
910                // audio to the headset (since there's no in-call audio
911                // yet!)  In this case, if a bluetooth headset is
912                // connected at all, we assume that it'll become active
913                // once the user answers the phone.
914                return (bluetoothState == BluetoothHeadset.STATE_CONNECTED);
915
916            default:  // Presumably IDLE
917                return false;
918        }
919    }
920
921
922    /**
923     * Receiver for misc intent broadcasts the Phone app cares about.
924     */
925    private class PhoneAppBroadcastReceiver extends BroadcastReceiver {
926        @Override
927        public void onReceive(Context context, Intent intent) {
928            String action = intent.getAction();
929            if (action.equals(Intent.ACTION_AIRPLANE_MODE_CHANGED)) {
930                boolean enabled = System.getInt(getContentResolver(),
931                        System.AIRPLANE_MODE_ON, 0) == 0;
932                phone.setRadioPower(enabled);
933            } else if (action.equals(BluetoothIntent.HEADSET_STATE_CHANGED_ACTION)) {
934                mBluetoothHeadsetState = intent.getIntExtra(BluetoothIntent.HEADSET_STATE,
935                                                            BluetoothHeadset.STATE_ERROR);
936                if (DBG) Log.d(LOG_TAG, "mReceiver: HEADSET_STATE_CHANGED_ACTION");
937                if (DBG) Log.d(LOG_TAG, "==> new state: " + mBluetoothHeadsetState);
938                updateBluetoothIndication(true);  // Also update any visible UI if necessary
939            } else if (action.equals(BluetoothIntent.HEADSET_AUDIO_STATE_CHANGED_ACTION)) {
940                mBluetoothHeadsetAudioState =
941                        intent.getIntExtra(BluetoothIntent.HEADSET_AUDIO_STATE,
942                                           BluetoothHeadset.STATE_ERROR);
943                if (DBG) Log.d(LOG_TAG, "mReceiver: HEADSET_AUDIO_STATE_CHANGED_ACTION");
944                if (DBG) Log.d(LOG_TAG, "==> new state: " + mBluetoothHeadsetAudioState);
945                updateBluetoothIndication(true);  // Also update any visible UI if necessary
946            } else if (action.equals(TelephonyIntents.ACTION_ANY_DATA_CONNECTION_STATE_CHANGED)) {
947                // if (DBG) Log.d(LOG_TAG, "mReceiver: ACTION_ANY_DATA_CONNECTION_STATE_CHANGED");
948                // if (DBG) Log.d(LOG_TAG, "- state: " + intent.getStringExtra(Phone.STATE_KEY));
949                // if (DBG) Log.d(LOG_TAG, "- reason: "
950                //                + intent.getStringExtra(Phone.STATE_CHANGE_REASON_KEY));
951
952                // The "data disconnected due to roaming" notification is
953                // visible if you've lost data connectivity because you're
954                // roaming and you have the "data roaming" feature turned off.
955                boolean disconnectedDueToRoaming = false;
956                if ("DISCONNECTED".equals(intent.getStringExtra(Phone.STATE_KEY))) {
957                    String reason = intent.getStringExtra(Phone.STATE_CHANGE_REASON_KEY);
958                    if (Phone.REASON_ROAMING_ON.equals(reason)) {
959                        // We just lost our data connection, and the reason
960                        // is that we started roaming.  This implies that
961                        // the user has data roaming turned off.
962                        disconnectedDueToRoaming = true;
963                    }
964                }
965                mHandler.sendEmptyMessage(disconnectedDueToRoaming
966                                          ? EVENT_DATA_ROAMING_DISCONNECTED
967                                          : EVENT_DATA_ROAMING_OK);
968            } else if (action.equals(Intent.ACTION_HEADSET_PLUG)) {
969                if (DBG) Log.d(LOG_TAG, "mReceiver: ACTION_HEADSET_PLUG");
970                if (DBG) Log.d(LOG_TAG, "    state: " + intent.getIntExtra("state", 0));
971                if (DBG) Log.d(LOG_TAG, "    name: " + intent.getStringExtra("name"));
972                mIsHeadsetPlugged = (intent.getIntExtra("state", 0) == 1);
973                mHandler.sendMessage(mHandler.obtainMessage(EVENT_WIRED_HEADSET_PLUG, 0));
974            } else if (action.equals(Intent.ACTION_BATTERY_LOW)) {
975                if (DBG) Log.d(LOG_TAG, "mReceiver: ACTION_BATTERY_LOW");
976                notifier.sendBatteryLow();  // Play a warning tone if in-call
977            } else if ((action.equals(TelephonyIntents.ACTION_SIM_STATE_CHANGED)) &&
978                    (mPUKEntryActivity != null)) {
979                // if an attempt to un-PUK-lock the device was made, while we're
980                // receiving this state change notification, notify the handler.
981                // NOTE: This is ONLY triggered if an attempt to un-PUK-lock has
982                // been attempted.
983                mHandler.sendMessage(mHandler.obtainMessage(EVENT_SIM_STATE_CHANGED,
984                        intent.getStringExtra(SimCard.INTENT_KEY_SIM_STATE)));
985            }
986        }
987    }
988
989    /**
990     * Broadcast receiver for the ACTION_MEDIA_BUTTON broadcast intent.
991     *
992     * This functionality isn't lumped in with the other intents in
993     * PhoneAppBroadcastReceiver because we instantiate this as a totally
994     * separate BroadcastReceiver instance, since we need to manually
995     * adjust its IntentFilter's priority (to make sure we get these
996     * intents *before* the media player.)
997     */
998    private class MediaButtonBroadcastReceiver extends BroadcastReceiver {
999        @Override
1000        public void onReceive(Context context, Intent intent) {
1001            KeyEvent event = (KeyEvent) intent.getParcelableExtra(Intent.EXTRA_KEY_EVENT);
1002            if (DBG) Log.d(LOG_TAG,
1003                           "MediaButtonBroadcastReceiver.onReceive()...  event = " + event);
1004            if ((event != null)
1005                && (event.getKeyCode() == KeyEvent.KEYCODE_HEADSETHOOK)
1006                && (event.getAction() == KeyEvent.ACTION_DOWN)) {
1007
1008                if (event.getRepeatCount() == 0) {
1009                    // Mute ONLY on the initial keypress.
1010                    if (DBG) Log.d(LOG_TAG, "MediaButtonBroadcastReceiver: HEADSETHOOK down!");
1011                    boolean consumed = PhoneUtils.handleHeadsetHook(phone);
1012                    if (DBG) Log.d(LOG_TAG, "==> called handleHeadsetHook(), consumed = " + consumed);
1013                    if (consumed) {
1014                        abortBroadcast();
1015                    }
1016                } else if (phone.getState() != Phone.State.IDLE) {
1017                    // As for any DOWN events other than the initial press, we consume
1018                    // (and ignore) those too if the phone is in use.  (Otherwise the
1019                    // music player will handle them, which would be confusing.)
1020                    abortBroadcast();
1021                }
1022            }
1023        }
1024    }
1025}
1026