Home | History | Annotate | Download | only in phone
      1 /*
      2  * Copyright (C) 2006 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.phone;
     18 
     19 import android.app.Activity;
     20 import android.app.AlertDialog;
     21 import android.app.Dialog;
     22 import android.bluetooth.BluetoothDevice;
     23 import android.bluetooth.BluetoothHeadset;
     24 import android.content.BroadcastReceiver;
     25 import android.content.Context;
     26 import android.content.DialogInterface.OnCancelListener;
     27 import android.content.DialogInterface;
     28 import android.content.Intent;
     29 import android.content.IntentFilter;
     30 import android.content.res.Resources;
     31 import android.graphics.PixelFormat;
     32 import android.graphics.Typeface;
     33 import android.graphics.drawable.Drawable;
     34 import android.media.AudioManager;
     35 import android.net.Uri;
     36 import android.os.AsyncResult;
     37 import android.os.Bundle;
     38 import android.os.Handler;
     39 import android.os.Message;
     40 import android.os.SystemClock;
     41 import android.os.SystemProperties;
     42 import android.provider.Settings;
     43 import android.telephony.PhoneNumberUtils;
     44 import android.telephony.ServiceState;
     45 import android.text.TextUtils;
     46 import android.text.method.DialerKeyListener;
     47 import android.util.EventLog;
     48 import android.util.Log;
     49 import android.view.KeyEvent;
     50 import android.view.Menu;
     51 import android.view.MotionEvent;
     52 import android.view.View;
     53 import android.view.ViewConfiguration;
     54 import android.view.ViewGroup;
     55 import android.view.Window;
     56 import android.view.WindowManager;
     57 import android.view.accessibility.AccessibilityEvent;
     58 import android.view.animation.Animation;
     59 import android.view.animation.AnimationUtils;
     60 import android.widget.EditText;
     61 import android.widget.LinearLayout;
     62 import android.widget.SlidingDrawer;
     63 import android.widget.TextView;
     64 import android.widget.Toast;
     65 
     66 import com.android.internal.telephony.Call;
     67 import com.android.internal.telephony.Connection;
     68 import com.android.internal.telephony.MmiCode;
     69 import com.android.internal.telephony.Phone;
     70 import com.android.phone.OtaUtils.CdmaOtaInCallScreenUiState;
     71 import com.android.phone.OtaUtils.CdmaOtaScreenState;
     72 
     73 import java.util.List;
     74 
     75 /**
     76  * Phone app "in call" screen.
     77  */
     78 public class InCallScreen extends Activity
     79         implements View.OnClickListener, View.OnTouchListener {
     80     private static final String LOG_TAG = "InCallScreen";
     81 
     82     private static final boolean DBG =
     83             (PhoneApp.DBG_LEVEL >= 1) && (SystemProperties.getInt("ro.debuggable", 0) == 1);
     84     private static final boolean VDBG = (PhoneApp.DBG_LEVEL >= 2);
     85 
     86     /**
     87      * Intent extra used to specify whether the DTMF dialpad should be
     88      * initially visible when bringing up the InCallScreen.  (If this
     89      * extra is present, the dialpad will be initially shown if the extra
     90      * has the boolean value true, and initially hidden otherwise.)
     91      */
     92     // TODO: Should be EXTRA_SHOW_DIALPAD for consistency.
     93     static final String SHOW_DIALPAD_EXTRA = "com.android.phone.ShowDialpad";
     94 
     95     /**
     96      * Intent extra to specify the package name of the gateway
     97      * provider.  Used to get the name displayed in the in-call screen
     98      * during the call setup. The value is a string.
     99      */
    100     // TODO: This extra is currently set by the gateway application as
    101     // a temporary measure. Ultimately, the framework will securely
    102     // set it.
    103     /* package */ static final String EXTRA_GATEWAY_PROVIDER_PACKAGE =
    104             "com.android.phone.extra.GATEWAY_PROVIDER_PACKAGE";
    105 
    106     /**
    107      * Intent extra to specify the URI of the provider to place the
    108      * call. The value is a string. It holds the gateway address
    109      * (phone gateway URL should start with the 'tel:' scheme) that
    110      * will actually be contacted to call the number passed in the
    111      * intent URL or in the EXTRA_PHONE_NUMBER extra.
    112      */
    113     // TODO: Should the value be a Uri (Parcelable)? Need to make sure
    114     // MMI code '#' don't get confused as URI fragments.
    115     /* package */ static final String EXTRA_GATEWAY_URI =
    116             "com.android.phone.extra.GATEWAY_URI";
    117 
    118     // Amount of time (in msec) that we display the "Call ended" state.
    119     // The "short" value is for calls ended by the local user, and the
    120     // "long" value is for calls ended by the remote caller.
    121     private static final int CALL_ENDED_SHORT_DELAY =  200;  // msec
    122     private static final int CALL_ENDED_LONG_DELAY = 2000;  // msec
    123 
    124     // Amount of time (in msec) that we keep the in-call menu onscreen
    125     // *after* the user changes the state of one of the toggle buttons.
    126     private static final int MENU_DISMISS_DELAY =  1000;  // msec
    127 
    128     // Amount of time that we display the PAUSE alert Dialog showing the
    129     // post dial string yet to be send out to the n/w
    130     private static final int PAUSE_PROMPT_DIALOG_TIMEOUT = 2000;  //msec
    131 
    132     // The "touch lock" overlay timeout comes from Gservices; this is the default.
    133     private static final int TOUCH_LOCK_DELAY_DEFAULT =  6000;  // msec
    134 
    135     // Amount of time for Displaying "Dialing" for 3way Calling origination
    136     private static final int THREEWAY_CALLERINFO_DISPLAY_TIME = 3000; // msec
    137 
    138     // Amount of time that we display the provider's overlay if applicable.
    139     private static final int PROVIDER_OVERLAY_TIMEOUT = 5000;  // msec
    140 
    141     // These are values for the settings of the auto retry mode:
    142     // 0 = disabled
    143     // 1 = enabled
    144     // TODO (Moto):These constants don't really belong here,
    145     // they should be moved to Settings where the value is being looked up in the first place
    146     static final int AUTO_RETRY_OFF = 0;
    147     static final int AUTO_RETRY_ON = 1;
    148 
    149     // Message codes; see mHandler below.
    150     // Note message codes < 100 are reserved for the PhoneApp.
    151     private static final int PHONE_STATE_CHANGED = 101;
    152     private static final int PHONE_DISCONNECT = 102;
    153     private static final int EVENT_HEADSET_PLUG_STATE_CHANGED = 103;
    154     private static final int POST_ON_DIAL_CHARS = 104;
    155     private static final int WILD_PROMPT_CHAR_ENTERED = 105;
    156     private static final int ADD_VOICEMAIL_NUMBER = 106;
    157     private static final int DONT_ADD_VOICEMAIL_NUMBER = 107;
    158     private static final int DELAYED_CLEANUP_AFTER_DISCONNECT = 108;
    159     private static final int SUPP_SERVICE_FAILED = 110;
    160     private static final int DISMISS_MENU = 111;
    161     private static final int ALLOW_SCREEN_ON = 112;
    162     private static final int TOUCH_LOCK_TIMER = 113;
    163     private static final int REQUEST_UPDATE_BLUETOOTH_INDICATION = 114;
    164     private static final int PHONE_CDMA_CALL_WAITING = 115;
    165     private static final int THREEWAY_CALLERINFO_DISPLAY_DONE = 116;
    166     private static final int EVENT_OTA_PROVISION_CHANGE = 117;
    167     private static final int REQUEST_CLOSE_SPC_ERROR_NOTICE = 118;
    168     private static final int REQUEST_CLOSE_OTA_FAILURE_NOTICE = 119;
    169     private static final int EVENT_PAUSE_DIALOG_COMPLETE = 120;
    170     private static final int EVENT_HIDE_PROVIDER_OVERLAY = 121;  // Time to remove the overlay.
    171     private static final int REQUEST_UPDATE_TOUCH_UI = 122;
    172 
    173     //following constants are used for OTA Call
    174     public static final String ACTION_SHOW_ACTIVATION =
    175            "com.android.phone.InCallScreen.SHOW_ACTIVATION";
    176     public static final String OTA_NUMBER = "*228";
    177     public static final String EXTRA_OTA_CALL = "android.phone.extra.OTA_CALL";
    178 
    179     // When InCallScreenMode is UNDEFINED set the default action
    180     // to ACTION_UNDEFINED so if we are resumed the activity will
    181     // know its undefined. In particular checkIsOtaCall will return
    182     // false.
    183     public static final String ACTION_UNDEFINED = "com.android.phone.InCallScreen.UNDEFINED";
    184 
    185     // High-level "modes" of the in-call UI.
    186     private enum InCallScreenMode {
    187         /**
    188          * Normal in-call UI elements visible.
    189          */
    190         NORMAL,
    191         /**
    192          * "Manage conference" UI is visible, totally replacing the
    193          * normal in-call UI.
    194          */
    195         MANAGE_CONFERENCE,
    196         /**
    197          * Non-interactive UI state.  Call card is visible,
    198          * displaying information about the call that just ended.
    199          */
    200         CALL_ENDED,
    201          /**
    202          * Normal OTA in-call UI elements visible.
    203          */
    204         OTA_NORMAL,
    205         /**
    206          * OTA call ended UI visible, replacing normal OTA in-call UI.
    207          */
    208         OTA_ENDED,
    209         /**
    210          * Default state when not on call
    211          */
    212         UNDEFINED
    213     }
    214     private InCallScreenMode mInCallScreenMode = InCallScreenMode.UNDEFINED;
    215 
    216     // Possible error conditions that can happen on startup.
    217     // These are returned as status codes from the various helper
    218     // functions we call from onCreate() and/or onResume().
    219     // See syncWithPhoneState() and checkIfOkToInitiateOutgoingCall() for details.
    220     private enum InCallInitStatus {
    221         SUCCESS,
    222         VOICEMAIL_NUMBER_MISSING,
    223         POWER_OFF,
    224         EMERGENCY_ONLY,
    225         OUT_OF_SERVICE,
    226         PHONE_NOT_IN_USE,
    227         NO_PHONE_NUMBER_SUPPLIED,
    228         DIALED_MMI,
    229         CALL_FAILED
    230     }
    231     private InCallInitStatus mInCallInitialStatus;  // see onResume()
    232 
    233     private boolean mRegisteredForPhoneStates;
    234     private boolean mNeedShowCallLostDialog;
    235 
    236     private Phone mPhone;
    237     private Call mForegroundCall;
    238     private Call mBackgroundCall;
    239     private Call mRingingCall;
    240 
    241     private BluetoothHandsfree mBluetoothHandsfree;
    242     private BluetoothHeadset mBluetoothHeadset;
    243     private boolean mBluetoothConnectionPending;
    244     private long mBluetoothConnectionRequestTime;
    245 
    246     // Main in-call UI ViewGroups
    247     private ViewGroup mMainFrame;
    248     private ViewGroup mInCallPanel;
    249 
    250     // Main in-call UI elements:
    251     private CallCard mCallCard;
    252 
    253     // UI controls:
    254     private InCallControlState mInCallControlState;
    255     private InCallMenu mInCallMenu;  // used on some devices
    256     private InCallTouchUi mInCallTouchUi;  // used on some devices
    257     private ManageConferenceUtils mManageConferenceUtils;
    258 
    259     // DTMF Dialer controller and its view:
    260     private DTMFTwelveKeyDialer mDialer;
    261     private DTMFTwelveKeyDialerView mDialerView;
    262 
    263     // TODO: Move these providers related fields in their own class.
    264     // Optional overlay when a 3rd party provider is used.
    265     private boolean mProviderOverlayVisible = false;
    266     private CharSequence mProviderLabel;
    267     private Drawable mProviderIcon;
    268     private Uri mProviderGatewayUri;
    269     // The formated address extracted from mProviderGatewayUri. User visible.
    270     private String mProviderAddress;
    271 
    272     // For OTA Call
    273     public OtaUtils otaUtils;
    274 
    275     private EditText mWildPromptText;
    276 
    277     // "Touch lock overlay" feature
    278     private boolean mUseTouchLockOverlay;  // True if we use this feature on the current device
    279     private View mTouchLockOverlay;  // The overlay over the whole screen
    280     private View mTouchLockIcon;  // The "lock" icon in the middle of the screen
    281     private Animation mTouchLockFadeIn;
    282     private long mTouchLockLastTouchTime;  // in SystemClock.uptimeMillis() time base
    283 
    284     // Various dialogs we bring up (see dismissAllDialogs()).
    285     // TODO: convert these all to use the "managed dialogs" framework.
    286     //
    287     // The MMI started dialog can actually be one of 2 items:
    288     //   1. An alert dialog if the MMI code is a normal MMI
    289     //   2. A progress dialog if the user requested a USSD
    290     private Dialog mMmiStartedDialog;
    291     private AlertDialog mMissingVoicemailDialog;
    292     private AlertDialog mGenericErrorDialog;
    293     private AlertDialog mSuppServiceFailureDialog;
    294     private AlertDialog mWaitPromptDialog;
    295     private AlertDialog mWildPromptDialog;
    296     private AlertDialog mCallLostDialog;
    297     private AlertDialog mPausePromptDialog;
    298     // NOTE: if you add a new dialog here, be sure to add it to dismissAllDialogs() also.
    299 
    300     // TODO: If the Activity class ever provides an easy way to get the
    301     // current "activity lifecycle" state, we can remove these flags.
    302     private boolean mIsDestroyed = false;
    303     private boolean mIsForegroundActivity = false;
    304 
    305     // For use with CDMA Pause/Wait dialogs
    306     private String mPostDialStrAfterPause;
    307     private boolean mPauseInProgress = false;
    308 
    309     // Flag indicating whether or not we should bring up the Call Log when
    310     // exiting the in-call UI due to the Phone becoming idle.  (This is
    311     // true if the most recently disconnected Call was initiated by the
    312     // user, or false if it was an incoming call.)
    313     // This flag is used by delayedCleanupAfterDisconnect(), and is set by
    314     // onDisconnect() (which is the only place that either posts a
    315     // DELAYED_CLEANUP_AFTER_DISCONNECT event *or* calls
    316     // delayedCleanupAfterDisconnect() directly.)
    317     private boolean mShowCallLogAfterDisconnect;
    318 
    319     private Handler mHandler = new Handler() {
    320         @Override
    321         public void handleMessage(Message msg) {
    322             if (mIsDestroyed) {
    323                 if (DBG) log("Handler: ignoring message " + msg + "; we're destroyed!");
    324                 return;
    325             }
    326             if (!mIsForegroundActivity) {
    327                 if (DBG) log("Handler: handling message " + msg + " while not in foreground");
    328                 // Continue anyway; some of the messages below *want* to
    329                 // be handled even if we're not the foreground activity
    330                 // (like DELAYED_CLEANUP_AFTER_DISCONNECT), and they all
    331                 // should at least be safe to handle if we're not in the
    332                 // foreground...
    333             }
    334 
    335             PhoneApp app = PhoneApp.getInstance();
    336             switch (msg.what) {
    337                 case SUPP_SERVICE_FAILED:
    338                     onSuppServiceFailed((AsyncResult) msg.obj);
    339                     break;
    340 
    341                 case PHONE_STATE_CHANGED:
    342                     onPhoneStateChanged((AsyncResult) msg.obj);
    343                     break;
    344 
    345                 case PHONE_DISCONNECT:
    346                     onDisconnect((AsyncResult) msg.obj);
    347                     break;
    348 
    349                 case EVENT_HEADSET_PLUG_STATE_CHANGED:
    350                     // Update the in-call UI, since some UI elements (in
    351                     // particular the "Speaker" menu button) change state
    352                     // depending on whether a headset is plugged in.
    353                     // TODO: A full updateScreen() is overkill here, since
    354                     // the value of PhoneApp.isHeadsetPlugged() only affects a
    355                     // single menu item.  (But even a full updateScreen()
    356                     // is still pretty cheap, so let's keep this simple
    357                     // for now.)
    358                     if (!isBluetoothAudioConnected()) {
    359                         if (msg.arg1 == 1) {
    360                             // If the dialpad is open, we need to start the timer that will
    361                             // eventually bring up the "touch lock" overlay.
    362                             if (mDialer.isOpened() && !isTouchLocked()) {
    363                                 resetTouchLockTimer();
    364                             }
    365                         }
    366                     }
    367                     updateScreen();
    368                     break;
    369 
    370                 case PhoneApp.MMI_INITIATE:
    371                     onMMIInitiate((AsyncResult) msg.obj);
    372                     break;
    373 
    374                 case PhoneApp.MMI_CANCEL:
    375                     onMMICancel();
    376                     break;
    377 
    378                 // handle the mmi complete message.
    379                 // since the message display class has been replaced with
    380                 // a system dialog in PhoneUtils.displayMMIComplete(), we
    381                 // should finish the activity here to close the window.
    382                 case PhoneApp.MMI_COMPLETE:
    383                     // Check the code to see if the request is ready to
    384                     // finish, this includes any MMI state that is not
    385                     // PENDING.
    386                     MmiCode mmiCode = (MmiCode) ((AsyncResult) msg.obj).result;
    387                     // if phone is a CDMA phone display feature code completed message
    388                     int phoneType = mPhone.getPhoneType();
    389                     if (phoneType == Phone.PHONE_TYPE_CDMA) {
    390                         PhoneUtils.displayMMIComplete(mPhone, app, mmiCode, null, null);
    391                     } else if (phoneType == Phone.PHONE_TYPE_GSM) {
    392                         if (mmiCode.getState() != MmiCode.State.PENDING) {
    393                             if (DBG) log("Got MMI_COMPLETE, finishing InCallScreen...");
    394                             endInCallScreenSession();
    395                         }
    396                     }
    397                     break;
    398 
    399                 case POST_ON_DIAL_CHARS:
    400                     handlePostOnDialChars((AsyncResult) msg.obj, (char) msg.arg1);
    401                     break;
    402 
    403                 case ADD_VOICEMAIL_NUMBER:
    404                     addVoiceMailNumberPanel();
    405                     break;
    406 
    407                 case DONT_ADD_VOICEMAIL_NUMBER:
    408                     dontAddVoiceMailNumber();
    409                     break;
    410 
    411                 case DELAYED_CLEANUP_AFTER_DISCONNECT:
    412                     delayedCleanupAfterDisconnect();
    413                     break;
    414 
    415                 case DISMISS_MENU:
    416                     // dismissMenu() has no effect if the menu is already closed.
    417                     dismissMenu(true);  // dismissImmediate = true
    418                     break;
    419 
    420                 case ALLOW_SCREEN_ON:
    421                     if (VDBG) log("ALLOW_SCREEN_ON message...");
    422                     // Undo our previous call to preventScreenOn(true).
    423                     // (Note this will cause the screen to turn on
    424                     // immediately, if it's currently off because of a
    425                     // prior preventScreenOn(true) call.)
    426                     app.preventScreenOn(false);
    427                     break;
    428 
    429                 case TOUCH_LOCK_TIMER:
    430                     if (VDBG) log("TOUCH_LOCK_TIMER...");
    431                     touchLockTimerExpired();
    432                     break;
    433 
    434                 case REQUEST_UPDATE_BLUETOOTH_INDICATION:
    435                     if (VDBG) log("REQUEST_UPDATE_BLUETOOTH_INDICATION...");
    436                     // The bluetooth headset state changed, so some UI
    437                     // elements may need to update.  (There's no need to
    438                     // look up the current state here, since any UI
    439                     // elements that care about the bluetooth state get it
    440                     // directly from PhoneApp.showBluetoothIndication().)
    441                     updateScreen();
    442                     break;
    443 
    444                 case PHONE_CDMA_CALL_WAITING:
    445                     if (DBG) log("Received PHONE_CDMA_CALL_WAITING event ...");
    446                     Connection cn = mRingingCall.getLatestConnection();
    447 
    448                     // Only proceed if we get a valid connection object
    449                     if (cn != null) {
    450                         // Finally update screen with Call waiting info and request
    451                         // screen to wake up
    452                         updateScreen();
    453                         app.updateWakeState();
    454                     }
    455                     break;
    456 
    457                 case THREEWAY_CALLERINFO_DISPLAY_DONE:
    458                     if (DBG) log("Received THREEWAY_CALLERINFO_DISPLAY_DONE event ...");
    459                     if (app.cdmaPhoneCallState.getCurrentCallState()
    460                             == CdmaPhoneCallState.PhoneCallState.THRWAY_ACTIVE) {
    461                         // Set the mThreeWayCallOrigStateDialing state to true
    462                         app.cdmaPhoneCallState.setThreeWayCallOrigState(false);
    463 
    464                         //Finally update screen with with the current on going call
    465                         updateScreen();
    466                     }
    467                     break;
    468 
    469                 case EVENT_OTA_PROVISION_CHANGE:
    470                     if (otaUtils != null) {
    471                         otaUtils.onOtaProvisionStatusChanged((AsyncResult) msg.obj);
    472                     }
    473                     break;
    474 
    475                 case REQUEST_CLOSE_SPC_ERROR_NOTICE:
    476                     if (otaUtils != null) {
    477                         otaUtils.onOtaCloseSpcNotice();
    478                     }
    479                     break;
    480 
    481                 case REQUEST_CLOSE_OTA_FAILURE_NOTICE:
    482                     if (otaUtils != null) {
    483                         otaUtils.onOtaCloseFailureNotice();
    484                     }
    485                     break;
    486 
    487                 case EVENT_PAUSE_DIALOG_COMPLETE:
    488                     if (mPausePromptDialog != null) {
    489                         if (DBG) log("- DISMISSING mPausePromptDialog.");
    490                         mPausePromptDialog.dismiss();  // safe even if already dismissed
    491                         mPausePromptDialog = null;
    492                     }
    493                     break;
    494 
    495                 case EVENT_HIDE_PROVIDER_OVERLAY:
    496                     mProviderOverlayVisible = false;
    497                     updateProviderOverlay();  // Clear the overlay.
    498                     break;
    499 
    500                 case REQUEST_UPDATE_TOUCH_UI:
    501                     updateInCallTouchUi();
    502                     break;
    503             }
    504         }
    505     };
    506 
    507     private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
    508             @Override
    509             public void onReceive(Context context, Intent intent) {
    510                 String action = intent.getAction();
    511                 if (action.equals(Intent.ACTION_HEADSET_PLUG)) {
    512                     // Listen for ACTION_HEADSET_PLUG broadcasts so that we
    513                     // can update the onscreen UI when the headset state changes.
    514                     // if (DBG) log("mReceiver: ACTION_HEADSET_PLUG");
    515                     // if (DBG) log("==> intent: " + intent);
    516                     // if (DBG) log("    state: " + intent.getIntExtra("state", 0));
    517                     // if (DBG) log("    name: " + intent.getStringExtra("name"));
    518                     // send the event and add the state as an argument.
    519                     Message message = Message.obtain(mHandler, EVENT_HEADSET_PLUG_STATE_CHANGED,
    520                             intent.getIntExtra("state", 0), 0);
    521                     mHandler.sendMessage(message);
    522                 }
    523             }
    524         };
    525 
    526 
    527     @Override
    528     protected void onCreate(Bundle icicle) {
    529         if (DBG) log("onCreate()...  this = " + this);
    530 
    531         Profiler.callScreenOnCreate();
    532 
    533         super.onCreate(icicle);
    534 
    535         final PhoneApp app = PhoneApp.getInstance();
    536         app.setInCallScreenInstance(this);
    537 
    538         // set this flag so this activity will stay in front of the keyguard
    539         int flags = WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED;
    540         if (app.getPhoneState() == Phone.State.OFFHOOK) {
    541             // While we are in call, the in-call screen should dismiss the keyguard.
    542             // This allows the user to press Home to go directly home without going through
    543             // an insecure lock screen.
    544             // But we do not want to do this if there is no active call so we do not
    545             // bypass the keyguard if the call is not answered or declined.
    546             flags |= WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD;
    547         }
    548         getWindow().addFlags(flags);
    549 
    550         setPhone(app.phone);  // Sets mPhone and mForegroundCall/mBackgroundCall/mRingingCall
    551 
    552         mBluetoothHandsfree = app.getBluetoothHandsfree();
    553         if (VDBG) log("- mBluetoothHandsfree: " + mBluetoothHandsfree);
    554 
    555         if (mBluetoothHandsfree != null) {
    556             // The PhoneApp only creates a BluetoothHandsfree instance in the
    557             // first place if BluetoothAdapter.getDefaultAdapter()
    558             // succeeds.  So at this point we know the device is BT-capable.
    559             mBluetoothHeadset = new BluetoothHeadset(this, null);
    560             if (VDBG) log("- Got BluetoothHeadset: " + mBluetoothHeadset);
    561         }
    562 
    563         requestWindowFeature(Window.FEATURE_NO_TITLE);
    564 
    565         // Inflate everything in incall_screen.xml and add it to the screen.
    566         setContentView(R.layout.incall_screen);
    567 
    568         initInCallScreen();
    569 
    570         // Create the dtmf dialer.  The dialer view we use depends on the
    571         // current platform:
    572         //
    573         // - On non-prox-sensor devices, it's the dialpad contained inside
    574         //   a SlidingDrawer widget (see dtmf_twelve_key_dialer.xml).
    575         //
    576         // - On "full touch UI" devices, it's the compact non-sliding
    577         //   dialpad that appears on the upper half of the screen,
    578         //   above the main cluster of InCallTouchUi buttons
    579         //   (see non_drawer_dialpad.xml).
    580         //
    581         // TODO: These should both be ViewStubs, and right here we should
    582         // inflate one or the other.  (Also, while doing that, let's also
    583         // move this block of code over to initInCallScreen().)
    584         //
    585         SlidingDrawer dialerDrawer;
    586         if (isTouchUiEnabled()) {
    587             // This is a "full touch" device.
    588             mDialerView = (DTMFTwelveKeyDialerView) findViewById(R.id.non_drawer_dtmf_dialer);
    589             if (DBG) log("- Full touch device!  Found dialerView: " + mDialerView);
    590             dialerDrawer = null;  // No SlidingDrawer used on this device.
    591         } else {
    592             // Use the old-style dialpad contained within the SlidingDrawer.
    593             mDialerView = (DTMFTwelveKeyDialerView) findViewById(R.id.dtmf_dialer);
    594             if (DBG) log("- Using SlidingDrawer-based dialpad.  Found dialerView: " + mDialerView);
    595             dialerDrawer = (SlidingDrawer) findViewById(R.id.dialer_container);
    596             if (DBG) log("  ...and the SlidingDrawer: " + dialerDrawer);
    597         }
    598         // Sanity-check that (regardless of the device) at least the
    599         // dialer view is present:
    600         if (mDialerView == null) {
    601             Log.e(LOG_TAG, "onCreate: couldn't find dialerView", new IllegalStateException());
    602         }
    603         // Finally, create the DTMFTwelveKeyDialer instance.
    604         mDialer = new DTMFTwelveKeyDialer(this, mDialerView, dialerDrawer);
    605 
    606         registerForPhoneStates();
    607 
    608         // No need to change wake state here; that happens in onResume() when we
    609         // are actually displayed.
    610 
    611         // Handle the Intent we were launched with, but only if this is the
    612         // the very first time we're being launched (ie. NOT if we're being
    613         // re-initialized after previously being shut down.)
    614         // Once we're up and running, any future Intents we need
    615         // to handle will come in via the onNewIntent() method.
    616         if (icicle == null) {
    617             if (DBG) log("onCreate(): this is our very first launch, checking intent...");
    618 
    619             // Stash the result code from internalResolveIntent() in the
    620             // mInCallInitialStatus field.  If it's an error code, we'll
    621             // handle it in onResume().
    622             mInCallInitialStatus = internalResolveIntent(getIntent());
    623             if (DBG) log("onCreate(): mInCallInitialStatus = " + mInCallInitialStatus);
    624             if (mInCallInitialStatus != InCallInitStatus.SUCCESS) {
    625                 Log.w(LOG_TAG, "onCreate: status " + mInCallInitialStatus
    626                       + " from internalResolveIntent()");
    627                 // See onResume() for the actual error handling.
    628             }
    629         } else {
    630             mInCallInitialStatus = InCallInitStatus.SUCCESS;
    631         }
    632 
    633         // The "touch lock overlay" feature is used only on devices that
    634         // *don't* use a proximity sensor to turn the screen off while in-call.
    635         mUseTouchLockOverlay = !app.proximitySensorModeEnabled();
    636 
    637         Profiler.callScreenCreated();
    638         if (DBG) log("onCreate(): exit");
    639     }
    640 
    641     /**
    642      * Sets the Phone object used internally by the InCallScreen.
    643      *
    644      * In normal operation this is called from onCreate(), and the
    645      * passed-in Phone object comes from the PhoneApp.
    646      * For testing, test classes can use this method to
    647      * inject a test Phone instance.
    648      */
    649     /* package */ void setPhone(Phone phone) {
    650         mPhone = phone;
    651         // Hang onto the three Call objects too; they're singletons that
    652         // are constant (and never null) for the life of the Phone.
    653         mForegroundCall = mPhone.getForegroundCall();
    654         mBackgroundCall = mPhone.getBackgroundCall();
    655         mRingingCall = mPhone.getRingingCall();
    656     }
    657 
    658     @Override
    659     protected void onResume() {
    660         if (DBG) log("onResume()...");
    661         super.onResume();
    662 
    663         mIsForegroundActivity = true;
    664 
    665         final PhoneApp app = PhoneApp.getInstance();
    666 
    667         app.disableStatusBar();
    668 
    669         // Touch events are never considered "user activity" while the
    670         // InCallScreen is active, so that unintentional touches won't
    671         // prevent the device from going to sleep.
    672         app.setIgnoreTouchUserActivity(true);
    673 
    674         // Disable the status bar "window shade" the entire time we're on
    675         // the in-call screen.
    676         NotificationMgr.getDefault().getStatusBarMgr().enableExpandedView(false);
    677 
    678         // Listen for broadcast intents that might affect the onscreen UI.
    679         registerReceiver(mReceiver, new IntentFilter(Intent.ACTION_HEADSET_PLUG));
    680 
    681         // Keep a "dialer session" active when we're in the foreground.
    682         // (This is needed to play DTMF tones.)
    683         mDialer.startDialerSession();
    684 
    685         // Check for any failures that happened during onCreate() or onNewIntent().
    686         if (DBG) log("- onResume: initial status = " + mInCallInitialStatus);
    687         if (mInCallInitialStatus != InCallInitStatus.SUCCESS) {
    688             if (DBG) log("- onResume: failure during startup: " + mInCallInitialStatus);
    689 
    690             // Don't bring up the regular Phone UI!  Instead bring up
    691             // something more specific to let the user deal with the
    692             // problem.
    693             handleStartupError(mInCallInitialStatus);
    694 
    695             // But it *is* OK to continue with the rest of onResume(),
    696             // since any further setup steps (like updateScreen() and the
    697             // CallCard setup) will fall back to a "blank" state if the
    698             // phone isn't in use.
    699             mInCallInitialStatus = InCallInitStatus.SUCCESS;
    700         }
    701 
    702         // Set the volume control handler while we are in the foreground.
    703         final boolean bluetoothConnected = isBluetoothAudioConnected();
    704 
    705         if (bluetoothConnected) {
    706             setVolumeControlStream(AudioManager.STREAM_BLUETOOTH_SCO);
    707         } else {
    708             setVolumeControlStream(AudioManager.STREAM_VOICE_CALL);
    709         }
    710 
    711         takeKeyEvents(true);
    712 
    713         boolean phoneIsCdma = (mPhone.getPhoneType() == Phone.PHONE_TYPE_CDMA);
    714 
    715         boolean inOtaCall = false;
    716         if (phoneIsCdma) {
    717             inOtaCall = initOtaState();
    718         }
    719         if (!inOtaCall) {
    720             // Always start off in NORMAL mode
    721             setInCallScreenMode(InCallScreenMode.NORMAL);
    722         }
    723 
    724         // Before checking the state of the phone, clean up any
    725         // connections in the DISCONNECTED state.
    726         // (The DISCONNECTED state is used only to drive the "call ended"
    727         // UI; it's totally useless when *entering* the InCallScreen.)
    728         mPhone.clearDisconnected();
    729 
    730         InCallInitStatus status = syncWithPhoneState();
    731         if (status != InCallInitStatus.SUCCESS) {
    732             if (DBG) log("- syncWithPhoneState failed! status = " + status);
    733             // Couldn't update the UI, presumably because the phone is totally
    734             // idle.  But don't endInCallScreenSession immediately, since we might still
    735             // have an error dialog up that the user needs to see.
    736             // (And in that case, the error dialog is responsible for calling
    737             // endInCallScreenSession when the user dismisses it.)
    738         } else if (phoneIsCdma) {
    739             if (mInCallScreenMode == InCallScreenMode.OTA_NORMAL ||
    740                     mInCallScreenMode == InCallScreenMode.OTA_ENDED) {
    741                 mDialer.setHandleVisible(false);
    742                 if (mInCallPanel != null) mInCallPanel.setVisibility(View.GONE);
    743                 updateScreen();
    744                 return;
    745             }
    746         }
    747 
    748         // InCallScreen is now active.
    749         EventLog.writeEvent(EventLogTags.PHONE_UI_ENTER);
    750 
    751         // Update the poke lock and wake lock when we move to
    752         // the foreground.
    753         //
    754         // But we need to do something special if we're coming
    755         // to the foreground while an incoming call is ringing:
    756         if (mPhone.getState() == Phone.State.RINGING) {
    757             // If the phone is ringing, we *should* already be holding a
    758             // full wake lock (which we would have acquired before
    759             // firing off the intent that brought us here; see
    760             // PhoneUtils.showIncomingCallUi().)
    761             //
    762             // We also called preventScreenOn(true) at that point, to
    763             // avoid cosmetic glitches while we were being launched.
    764             // So now we need to post an ALLOW_SCREEN_ON message to
    765             // (eventually) undo the prior preventScreenOn(true) call.
    766             //
    767             // (In principle we shouldn't do this until after our first
    768             // layout/draw pass.  But in practice, the delay caused by
    769             // simply waiting for the end of the message queue is long
    770             // enough to avoid any flickering of the lock screen before
    771             // the InCallScreen comes up.)
    772             if (VDBG) log("- posting ALLOW_SCREEN_ON message...");
    773             mHandler.removeMessages(ALLOW_SCREEN_ON);
    774             mHandler.sendEmptyMessage(ALLOW_SCREEN_ON);
    775 
    776             // TODO: There ought to be a more elegant way of doing this,
    777             // probably by having the PowerManager and ActivityManager
    778             // work together to let apps request that the screen on/off
    779             // state be synchronized with the Activity lifecycle.
    780             // (See bug 1648751.)
    781         } else {
    782             // The phone isn't ringing; this is either an outgoing call, or
    783             // we're returning to a call in progress.  There *shouldn't* be
    784             // any prior preventScreenOn(true) call that we need to undo,
    785             // but let's do this just to be safe:
    786             app.preventScreenOn(false);
    787         }
    788         app.updateWakeState();
    789 
    790         // The "touch lock" overlay is NEVER visible when we resume.
    791         // (In particular, this check ensures that we won't still be
    792         // locked after the user wakes up the screen by pressing MENU.)
    793         enableTouchLock(false);
    794         // ...but if the dialpad is open we DO need to start the timer
    795         // that will eventually bring up the "touch lock" overlay.
    796         if (mDialer.isOpened()) resetTouchLockTimer();
    797 
    798         // Restore the mute state if the last mute state change was NOT
    799         // done by the user.
    800         if (app.getRestoreMuteOnInCallResume()) {
    801             PhoneUtils.restoreMuteState(mPhone);
    802             app.setRestoreMuteOnInCallResume(false);
    803         }
    804 
    805         Profiler.profileViewCreate(getWindow(), InCallScreen.class.getName());
    806         if (VDBG) log("onResume() done.");
    807     }
    808 
    809     // onPause is guaranteed to be called when the InCallScreen goes
    810     // in the background.
    811     @Override
    812     protected void onPause() {
    813         if (DBG) log("onPause()...");
    814         super.onPause();
    815 
    816         mIsForegroundActivity = false;
    817 
    818         // Force a clear of the provider overlay' frame. Since the
    819         // overlay is removed using a timed message, it is
    820         // possible we missed it if the prev call was interrupted.
    821         mProviderOverlayVisible = false;
    822         updateProviderOverlay();
    823 
    824         final PhoneApp app = PhoneApp.getInstance();
    825 
    826         // A safety measure to disable proximity sensor in case call failed
    827         // and the telephony state did not change.
    828         app.setBeginningCall(false);
    829 
    830         // Make sure the "Manage conference" chronometer is stopped when
    831         // we move away from the foreground.
    832         mManageConferenceUtils.stopConferenceTime();
    833 
    834         // as a catch-all, make sure that any dtmf tones are stopped
    835         // when the UI is no longer in the foreground.
    836         mDialer.onDialerKeyUp(null);
    837 
    838         // Release any "dialer session" resources, now that we're no
    839         // longer in the foreground.
    840         mDialer.stopDialerSession();
    841 
    842         // If the device is put to sleep as the phone call is ending,
    843         // we may see cases where the DELAYED_CLEANUP_AFTER_DISCONNECT
    844         // event gets handled AFTER the device goes to sleep and wakes
    845         // up again.
    846 
    847         // This is because it is possible for a sleep command
    848         // (executed with the End Call key) to come during the 2
    849         // seconds that the "Call Ended" screen is up.  Sleep then
    850         // pauses the device (including the cleanup event) and
    851         // resumes the event when it wakes up.
    852 
    853         // To fix this, we introduce a bit of code that pushes the UI
    854         // to the background if we pause and see a request to
    855         // DELAYED_CLEANUP_AFTER_DISCONNECT.
    856 
    857         // Note: We can try to finish directly, by:
    858         //  1. Removing the DELAYED_CLEANUP_AFTER_DISCONNECT messages
    859         //  2. Calling delayedCleanupAfterDisconnect directly
    860 
    861         // However, doing so can cause problems between the phone
    862         // app and the keyguard - the keyguard is trying to sleep at
    863         // the same time that the phone state is changing.  This can
    864         // end up causing the sleep request to be ignored.
    865         if (mHandler.hasMessages(DELAYED_CLEANUP_AFTER_DISCONNECT)
    866                 && mPhone.getState() != Phone.State.RINGING) {
    867             if (DBG) log("DELAYED_CLEANUP_AFTER_DISCONNECT detected, moving UI to background.");
    868             endInCallScreenSession();
    869         }
    870 
    871         EventLog.writeEvent(EventLogTags.PHONE_UI_EXIT);
    872 
    873         // Clean up the menu, in case we get paused while the menu is up
    874         // for some reason.
    875         dismissMenu(true);  // dismiss immediately
    876 
    877         // Dismiss any dialogs we may have brought up, just to be 100%
    878         // sure they won't still be around when we get back here.
    879         dismissAllDialogs();
    880 
    881         // Re-enable the status bar (which we disabled in onResume().)
    882         NotificationMgr.getDefault().getStatusBarMgr().enableExpandedView(true);
    883 
    884         // Unregister for broadcast intents.  (These affect the visible UI
    885         // of the InCallScreen, so we only care about them while we're in the
    886         // foreground.)
    887         unregisterReceiver(mReceiver);
    888 
    889         // Re-enable "user activity" for touch events.
    890         // We actually do this slightly *after* onPause(), to work around a
    891         // race condition where a touch can come in after we've paused
    892         // but before the device actually goes to sleep.
    893         // TODO: The PowerManager itself should prevent this from happening.
    894         mHandler.postDelayed(new Runnable() {
    895                 public void run() {
    896                     app.setIgnoreTouchUserActivity(false);
    897                 }
    898             }, 500);
    899 
    900         app.reenableStatusBar();
    901 
    902         // Make sure we revert the poke lock and wake lock when we move to
    903         // the background.
    904         app.updateWakeState();
    905 
    906         // clear the dismiss keyguard flag so we are back to the default state
    907         // when we next resume
    908         updateKeyguardPolicy(false);
    909     }
    910 
    911     @Override
    912     protected void onStop() {
    913         if (DBG) log("onStop()...");
    914         super.onStop();
    915 
    916         stopTimer();
    917 
    918         Phone.State state = mPhone.getState();
    919         if (DBG) log("onStop: state = " + state);
    920 
    921         if (state == Phone.State.IDLE) {
    922             final PhoneApp app = PhoneApp.getInstance();
    923             // when OTA Activation, OTA Success/Failure dialog or OTA SPC
    924             // failure dialog is running, do not destroy inCallScreen. Because call
    925             // is already ended and dialog will not get redrawn on slider event.
    926             if ((app.cdmaOtaProvisionData != null) && (app.cdmaOtaScreenState != null)
    927                     && ((app.cdmaOtaScreenState.otaScreenState !=
    928                             CdmaOtaScreenState.OtaScreenState.OTA_STATUS_ACTIVATION)
    929                         && (app.cdmaOtaScreenState.otaScreenState !=
    930                             CdmaOtaScreenState.OtaScreenState.OTA_STATUS_SUCCESS_FAILURE_DLG)
    931                         && (!app.cdmaOtaProvisionData.inOtaSpcState))) {
    932                 // we don't want the call screen to remain in the activity history
    933                 // if there are not active or ringing calls.
    934                 if (DBG) log("- onStop: calling finish() to clear activity history...");
    935                 moveTaskToBack(true);
    936                 if (otaUtils != null) {
    937                     otaUtils.cleanOtaScreen(true);
    938                 }
    939             }
    940         }
    941     }
    942 
    943     @Override
    944     protected void onDestroy() {
    945         if (DBG) log("onDestroy()...");
    946         super.onDestroy();
    947 
    948         // Set the magic flag that tells us NOT to handle any handler
    949         // messages that come in asynchronously after we get destroyed.
    950         mIsDestroyed = true;
    951 
    952         final PhoneApp app = PhoneApp.getInstance();
    953         app.setInCallScreenInstance(null);
    954 
    955         // Clear out the InCallScreen references in various helper objects
    956         // (to let them know we've been destroyed).
    957         if (mInCallMenu != null) {
    958             mInCallMenu.clearInCallScreenReference();
    959         }
    960         if (mCallCard != null) {
    961             mCallCard.setInCallScreenInstance(null);
    962         }
    963         if (mInCallTouchUi != null) {
    964             mInCallTouchUi.setInCallScreenInstance(null);
    965         }
    966 
    967         mDialer.clearInCallScreenReference();
    968         mDialer = null;
    969 
    970         unregisterForPhoneStates();
    971         // No need to change wake state here; that happens in onPause() when we
    972         // are moving out of the foreground.
    973 
    974         if (mBluetoothHeadset != null) {
    975             mBluetoothHeadset.close();
    976             mBluetoothHeadset = null;
    977         }
    978 
    979         // Dismiss all dialogs, to be absolutely sure we won't leak any of
    980         // them while changing orientation.
    981         dismissAllDialogs();
    982     }
    983 
    984     /**
    985      * Dismisses the in-call screen.
    986      *
    987      * We never *really* finish() the InCallScreen, since we don't want to
    988      * get destroyed and then have to be re-created from scratch for the
    989      * next call.  Instead, we just move ourselves to the back of the
    990      * activity stack.
    991      *
    992      * This also means that we'll no longer be reachable via the BACK
    993      * button (since moveTaskToBack() puts us behind the Home app, but the
    994      * home app doesn't allow the BACK key to move you any farther down in
    995      * the history stack.)
    996      *
    997      * (Since the Phone app itself is never killed, this basically means
    998      * that we'll keep a single InCallScreen instance around for the
    999      * entire uptime of the device.  This noticeably improves the UI
   1000      * responsiveness for incoming calls.)
   1001      */
   1002     @Override
   1003     public void finish() {
   1004         if (DBG) log("finish()...");
   1005         moveTaskToBack(true);
   1006     }
   1007 
   1008     /**
   1009      * End the current in call screen session.
   1010      *
   1011      * This must be called when an InCallScreen session has
   1012      * complete so that the next invocation via an onResume will
   1013      * not be in an old state.
   1014      */
   1015     public void endInCallScreenSession() {
   1016         if (DBG) log("endInCallScreenSession()...");
   1017         moveTaskToBack(true);
   1018         setInCallScreenMode(InCallScreenMode.UNDEFINED);
   1019     }
   1020 
   1021     /* package */ boolean isForegroundActivity() {
   1022         return mIsForegroundActivity;
   1023     }
   1024 
   1025     /* package */ void updateKeyguardPolicy(boolean dismissKeyguard) {
   1026         if (dismissKeyguard) {
   1027             getWindow().addFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD);
   1028         } else {
   1029             getWindow().clearFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD);
   1030         }
   1031     }
   1032 
   1033     private void registerForPhoneStates() {
   1034         if (!mRegisteredForPhoneStates) {
   1035             mPhone.registerForPreciseCallStateChanged(mHandler, PHONE_STATE_CHANGED, null);
   1036             mPhone.registerForDisconnect(mHandler, PHONE_DISCONNECT, null);
   1037             int phoneType = mPhone.getPhoneType();
   1038             if (phoneType == Phone.PHONE_TYPE_GSM) {
   1039                 mPhone.registerForMmiInitiate(mHandler, PhoneApp.MMI_INITIATE, null);
   1040 
   1041                 // register for the MMI complete message.  Upon completion,
   1042                 // PhoneUtils will bring up a system dialog instead of the
   1043                 // message display class in PhoneUtils.displayMMIComplete().
   1044                 // We'll listen for that message too, so that we can finish
   1045                 // the activity at the same time.
   1046                 mPhone.registerForMmiComplete(mHandler, PhoneApp.MMI_COMPLETE, null);
   1047             } else if (phoneType == Phone.PHONE_TYPE_CDMA) {
   1048                 if (DBG) log("Registering for Call Waiting.");
   1049                 mPhone.registerForCallWaiting(mHandler, PHONE_CDMA_CALL_WAITING, null);
   1050             } else {
   1051                 throw new IllegalStateException("Unexpected phone type: " + phoneType);
   1052             }
   1053 
   1054             mPhone.setOnPostDialCharacter(mHandler, POST_ON_DIAL_CHARS, null);
   1055             mPhone.registerForSuppServiceFailed(mHandler, SUPP_SERVICE_FAILED, null);
   1056             if (phoneType == Phone.PHONE_TYPE_CDMA) {
   1057                 mPhone.registerForCdmaOtaStatusChange(mHandler, EVENT_OTA_PROVISION_CHANGE, null);
   1058             }
   1059             mRegisteredForPhoneStates = true;
   1060         }
   1061     }
   1062 
   1063     private void unregisterForPhoneStates() {
   1064         mPhone.unregisterForPreciseCallStateChanged(mHandler);
   1065         mPhone.unregisterForDisconnect(mHandler);
   1066         mPhone.unregisterForMmiInitiate(mHandler);
   1067         mPhone.unregisterForCallWaiting(mHandler);
   1068         mPhone.setOnPostDialCharacter(null, POST_ON_DIAL_CHARS, null);
   1069         mPhone.unregisterForCdmaOtaStatusChange(mHandler);
   1070         mRegisteredForPhoneStates = false;
   1071     }
   1072 
   1073     /* package */ void updateAfterRadioTechnologyChange() {
   1074         if (DBG) Log.d(LOG_TAG, "updateAfterRadioTechnologyChange()...");
   1075         // Unregister for all events from the old obsolete phone
   1076         unregisterForPhoneStates();
   1077 
   1078         // (Re)register for all events relevant to the new active phone
   1079         registerForPhoneStates();
   1080     }
   1081 
   1082     @Override
   1083     protected void onNewIntent(Intent intent) {
   1084         if (DBG) log("onNewIntent: intent=" + intent);
   1085 
   1086         // We're being re-launched with a new Intent.  Since we keep
   1087         // around a single InCallScreen instance for the life of the phone
   1088         // process (see finish()), this sequence will happen EVERY time
   1089         // there's a new incoming or outgoing call except for the very
   1090         // first time the InCallScreen gets created.  This sequence will
   1091         // also happen if the InCallScreen is already in the foreground
   1092         // (e.g. getting a new ACTION_CALL intent while we were already
   1093         // using the other line.)
   1094 
   1095         // Stash away the new intent so that we can get it in the future
   1096         // by calling getIntent().  (Otherwise getIntent() will return the
   1097         // original Intent from when we first got created!)
   1098         setIntent(intent);
   1099 
   1100         // Activities are always paused before receiving a new intent, so
   1101         // we can count on our onResume() method being called next.
   1102 
   1103         // Just like in onCreate(), handle this intent, and stash the
   1104         // result code from internalResolveIntent() in the
   1105         // mInCallInitialStatus field.  If it's an error code, we'll
   1106         // handle it in onResume().
   1107         mInCallInitialStatus = internalResolveIntent(intent);
   1108         if (mInCallInitialStatus != InCallInitStatus.SUCCESS) {
   1109             Log.w(LOG_TAG, "onNewIntent: status " + mInCallInitialStatus
   1110                   + " from internalResolveIntent()");
   1111             // See onResume() for the actual error handling.
   1112         }
   1113     }
   1114 
   1115     /* package */ InCallInitStatus internalResolveIntent(Intent intent) {
   1116         if (intent == null || intent.getAction() == null) {
   1117             return InCallInitStatus.SUCCESS;
   1118         }
   1119 
   1120         checkIsOtaCall(intent);
   1121 
   1122         String action = intent.getAction();
   1123         if (DBG) log("internalResolveIntent: action=" + action);
   1124 
   1125         // The calls to setRestoreMuteOnInCallResume() inform the phone
   1126         // that we're dealing with new connections (either a placing an
   1127         // outgoing call or answering an incoming one, and NOT handling
   1128         // an aborted "Add Call" request), so we should let the mute state
   1129         // be handled by the PhoneUtils phone state change handler.
   1130         final PhoneApp app = PhoneApp.getInstance();
   1131         // If OTA Activation is configured for Power up scenario, then
   1132         // InCallScreen UI started with Intent of ACTION_SHOW_ACTIVATION
   1133         // to show OTA Activation screen at power up.
   1134         if ((action.equals(ACTION_SHOW_ACTIVATION))
   1135                 && ((mPhone.getPhoneType() == Phone.PHONE_TYPE_CDMA))) {
   1136             setInCallScreenMode(InCallScreenMode.OTA_NORMAL);
   1137             if ((app.cdmaOtaProvisionData != null)
   1138                     && (!app.cdmaOtaProvisionData.isOtaCallIntentProcessed)) {
   1139                 app.cdmaOtaProvisionData.isOtaCallIntentProcessed = true;
   1140                 app.cdmaOtaScreenState.otaScreenState =
   1141                         CdmaOtaScreenState.OtaScreenState.OTA_STATUS_ACTIVATION;
   1142             }
   1143             return InCallInitStatus.SUCCESS;
   1144         } else if (action.equals(Intent.ACTION_ANSWER)) {
   1145             internalAnswerCall();
   1146             app.setRestoreMuteOnInCallResume(false);
   1147             return InCallInitStatus.SUCCESS;
   1148         } else if (action.equals(Intent.ACTION_CALL)
   1149                 || action.equals(Intent.ACTION_CALL_EMERGENCY)) {
   1150             app.setRestoreMuteOnInCallResume(false);
   1151 
   1152             // If a provider is used, extract the info to build the
   1153             // overlay and route the call.  The overlay will be
   1154             // displayed the first time updateScreen is called.
   1155             if (PhoneUtils.hasPhoneProviderExtras(intent)) {
   1156                 mProviderLabel = PhoneUtils.getProviderLabel(this, intent);
   1157                 mProviderIcon = PhoneUtils.getProviderIcon(this, intent);
   1158                 mProviderGatewayUri = PhoneUtils.getProviderGatewayUri(intent);
   1159                 mProviderAddress = PhoneUtils.formatProviderUri(mProviderGatewayUri);
   1160                 mProviderOverlayVisible = true;
   1161 
   1162                 if (TextUtils.isEmpty(mProviderLabel) || null == mProviderIcon ||
   1163                     null == mProviderGatewayUri || TextUtils.isEmpty(mProviderAddress)) {
   1164                     clearProvider();
   1165                 }
   1166             } else {
   1167                 clearProvider();
   1168             }
   1169             InCallInitStatus status = placeCall(intent);
   1170             if (status == InCallInitStatus.SUCCESS) {
   1171                 // Notify the phone app that a call is beginning so it can
   1172                 // enable the proximity sensor
   1173                 app.setBeginningCall(true);
   1174             }
   1175             return status;
   1176         } else if (action.equals(intent.ACTION_MAIN)) {
   1177             // The MAIN action is used to bring up the in-call screen without
   1178             // doing any other explicit action, like when you return to the
   1179             // current call after previously bailing out of the in-call UI.
   1180             // SHOW_DIALPAD_EXTRA can be used here to specify whether the DTMF
   1181             // dialpad should be initially visible.  If the extra isn't
   1182             // present at all, we just leave the dialpad in its previous state.
   1183 
   1184             if ((mInCallScreenMode == InCallScreenMode.OTA_NORMAL)
   1185                     || (mInCallScreenMode == InCallScreenMode.OTA_ENDED)) {
   1186                 // If in OTA Call, update the OTA UI
   1187                 updateScreen();
   1188                 return InCallInitStatus.SUCCESS;
   1189             }
   1190             if (intent.hasExtra(SHOW_DIALPAD_EXTRA)) {
   1191                 boolean showDialpad = intent.getBooleanExtra(SHOW_DIALPAD_EXTRA, false);
   1192                 if (VDBG) log("- internalResolveIntent: SHOW_DIALPAD_EXTRA: " + showDialpad);
   1193                 if (showDialpad) {
   1194                     mDialer.openDialer(false);  // no "opening" animation
   1195                 } else {
   1196                     mDialer.closeDialer(false);  // no "closing" animation
   1197                 }
   1198             }
   1199             return InCallInitStatus.SUCCESS;
   1200         } else if (action.equals(ACTION_UNDEFINED)) {
   1201             return InCallInitStatus.SUCCESS;
   1202         } else {
   1203             Log.w(LOG_TAG, "internalResolveIntent: unexpected intent action: " + action);
   1204             // But continue the best we can (basically treating this case
   1205             // like ACTION_MAIN...)
   1206             return InCallInitStatus.SUCCESS;
   1207         }
   1208     }
   1209 
   1210     private void stopTimer() {
   1211         if (mCallCard != null) mCallCard.stopTimer();
   1212     }
   1213 
   1214     private void initInCallScreen() {
   1215         if (VDBG) log("initInCallScreen()...");
   1216 
   1217         // Have the WindowManager filter out touch events that are "too fat".
   1218         getWindow().addFlags(WindowManager.LayoutParams.FLAG_IGNORE_CHEEK_PRESSES);
   1219 
   1220         // Run in a 32-bit window, which improves the appearance of some
   1221         // semitransparent artwork in the in-call UI (like the CallCard
   1222         // photo borders).
   1223         getWindow().setFormat(PixelFormat.RGBX_8888);
   1224 
   1225         mMainFrame = (ViewGroup) findViewById(R.id.mainFrame);
   1226         mInCallPanel = (ViewGroup) findViewById(R.id.inCallPanel);
   1227 
   1228         // Initialize the CallCard.
   1229         mCallCard = (CallCard) findViewById(R.id.callCard);
   1230         if (VDBG) log("  - mCallCard = " + mCallCard);
   1231         mCallCard.setInCallScreenInstance(this);
   1232 
   1233         // Onscreen touch UI elements (used on some platforms)
   1234         initInCallTouchUi();
   1235 
   1236         // Helper class to keep track of enabledness/state of UI controls
   1237         mInCallControlState = new InCallControlState(this, mPhone);
   1238 
   1239         // Helper class to run the "Manage conference" UI
   1240         mManageConferenceUtils = new ManageConferenceUtils(this, mPhone);
   1241     }
   1242 
   1243     /**
   1244      * Returns true if the phone is "in use", meaning that at least one line
   1245      * is active (ie. off hook or ringing or dialing).  Conversely, a return
   1246      * value of false means there's currently no phone activity at all.
   1247      */
   1248     private boolean phoneIsInUse() {
   1249         return mPhone.getState() != Phone.State.IDLE;
   1250     }
   1251 
   1252     private boolean handleDialerKeyDown(int keyCode, KeyEvent event) {
   1253         if (VDBG) log("handleDialerKeyDown: keyCode " + keyCode + ", event " + event + "...");
   1254 
   1255         // As soon as the user starts typing valid dialable keys on the
   1256         // keyboard (presumably to type DTMF tones) we start passing the
   1257         // key events to the DTMFDialer's onDialerKeyDown.  We do so
   1258         // only if the okToDialDTMFTones() conditions pass.
   1259         if (okToDialDTMFTones()) {
   1260             return mDialer.onDialerKeyDown(event);
   1261 
   1262             // TODO: If the dialpad isn't currently visible, maybe
   1263             // consider automatically bringing it up right now?
   1264             // (Just to make sure the user sees the digits widget...)
   1265             // But this probably isn't too critical since it's awkward to
   1266             // use the hard keyboard while in-call in the first place,
   1267             // especially now that the in-call UI is portrait-only...
   1268         }
   1269 
   1270         return false;
   1271     }
   1272 
   1273     @Override
   1274     public void onBackPressed() {
   1275         if (DBG) log("onBackPressed()...");
   1276 
   1277         // To consume this BACK press, the code here should just do
   1278         // something and return.  Otherwise, call super.onBackPressed() to
   1279         // get the default implementation (which simply finishes the
   1280         // current activity.)
   1281 
   1282         if (!mRingingCall.isIdle()) {
   1283             // While an incoming call is ringing, BACK behaves just like
   1284             // ENDCALL: it stops the ringing and rejects the current call.
   1285             // (This is only enabled on some platforms, though.)
   1286             if (getResources().getBoolean(R.bool.allow_back_key_to_reject_incoming_call)) {
   1287                 if (DBG) log("BACK key while ringing: reject the call");
   1288                 internalHangupRingingCall();
   1289 
   1290                 // Don't consume the key; instead let the BACK event *also*
   1291                 // get handled normally by the framework (which presumably
   1292                 // will cause us to exit out of this activity.)
   1293                 super.onBackPressed();
   1294                 return;
   1295             } else {
   1296                 // The BACK key is disabled; don't reject the call, but
   1297                 // *do* consume the keypress (otherwise we'll exit out of
   1298                 // this activity.)
   1299                 if (DBG) log("BACK key while ringing: ignored");
   1300                 return;
   1301             }
   1302         }
   1303 
   1304         // BACK is also used to exit out of any "special modes" of the
   1305         // in-call UI:
   1306 
   1307         if (mDialer.isOpened()) {
   1308             // Take down the "touch lock" overlay *immediately* to let the
   1309             // user clearly see the DTMF dialpad's closing animation.
   1310             enableTouchLock(false);
   1311 
   1312             mDialer.closeDialer(true);  // do the "closing" animation
   1313             return;
   1314         }
   1315 
   1316         if (mInCallScreenMode == InCallScreenMode.MANAGE_CONFERENCE) {
   1317             // Hide the Manage Conference panel, return to NORMAL mode.
   1318             setInCallScreenMode(InCallScreenMode.NORMAL);
   1319             return;
   1320         }
   1321 
   1322         // Nothing special to do.  Fall back to the default behavior.
   1323         super.onBackPressed();
   1324     }
   1325 
   1326     /**
   1327      * Handles the green CALL key while in-call.
   1328      * @return true if we consumed the event.
   1329      */
   1330     private boolean handleCallKey() {
   1331         // The green CALL button means either "Answer", "Unhold", or
   1332         // "Swap calls", or can be a no-op, depending on the current state
   1333         // of the Phone.
   1334 
   1335         final boolean hasRingingCall = !mRingingCall.isIdle();
   1336         final boolean hasActiveCall = !mForegroundCall.isIdle();
   1337         final boolean hasHoldingCall = !mBackgroundCall.isIdle();
   1338 
   1339         int phoneType = mPhone.getPhoneType();
   1340         if (phoneType == Phone.PHONE_TYPE_CDMA) {
   1341             // The green CALL button means either "Answer", "Swap calls/On Hold", or
   1342             // "Add to 3WC", depending on the current state of the Phone.
   1343 
   1344             PhoneApp app = PhoneApp.getInstance();
   1345             CdmaPhoneCallState.PhoneCallState currCallState =
   1346                 app.cdmaPhoneCallState.getCurrentCallState();
   1347             if (hasRingingCall) {
   1348                 //Scenario 1: Accepting the First Incoming and Call Waiting call
   1349                 if (DBG) log("answerCall: First Incoming and Call Waiting scenario");
   1350                 internalAnswerCall();  // Automatically holds the current active call,
   1351                                        // if there is one
   1352             } else if ((currCallState == CdmaPhoneCallState.PhoneCallState.THRWAY_ACTIVE)
   1353                     && (hasActiveCall)) {
   1354                 //Scenario 2: Merging 3Way calls
   1355                 if (DBG) log("answerCall: Merge 3-way call scenario");
   1356                 // Merge calls
   1357                 PhoneUtils.mergeCalls(mPhone);
   1358             } else if (currCallState == CdmaPhoneCallState.PhoneCallState.CONF_CALL) {
   1359                 //Scenario 3: Switching between two Call waiting calls or drop the latest
   1360                 // connection if in a 3Way merge scenario
   1361                 if (DBG) log("answerCall: Switch btwn 2 calls scenario");
   1362                 internalSwapCalls();
   1363             }
   1364         } else if (phoneType == Phone.PHONE_TYPE_GSM) {
   1365             if (hasRingingCall) {
   1366                 // If an incoming call is ringing, the CALL button is actually
   1367                 // handled by the PhoneWindowManager.  (We do this to make
   1368                 // sure that we'll respond to the key even if the InCallScreen
   1369                 // hasn't come to the foreground yet.)
   1370                 //
   1371                 // We'd only ever get here in the extremely rare case that the
   1372                 // incoming call started ringing *after*
   1373                 // PhoneWindowManager.interceptKeyTq() but before the event
   1374                 // got here, or else if the PhoneWindowManager had some
   1375                 // problem connecting to the ITelephony service.
   1376                 Log.w(LOG_TAG, "handleCallKey: incoming call is ringing!"
   1377                       + " (PhoneWindowManager should have handled this key.)");
   1378                 // But go ahead and handle the key as normal, since the
   1379                 // PhoneWindowManager presumably did NOT handle it:
   1380 
   1381                 // There's an incoming ringing call: CALL means "Answer".
   1382                 internalAnswerCall();
   1383             } else if (hasActiveCall && hasHoldingCall) {
   1384                 // Two lines are in use: CALL means "Swap calls".
   1385                 if (DBG) log("handleCallKey: both lines in use ==> swap calls.");
   1386                 internalSwapCalls();
   1387             } else if (hasHoldingCall) {
   1388                 // There's only one line in use, AND it's on hold.
   1389                 // In this case CALL is a shortcut for "unhold".
   1390                 if (DBG) log("handleCallKey: call on hold ==> unhold.");
   1391                 PhoneUtils.switchHoldingAndActive(mPhone);  // Really means "unhold" in this state
   1392             } else {
   1393                 // The most common case: there's only one line in use, and
   1394                 // it's an active call (i.e. it's not on hold.)
   1395                 // In this case CALL is a no-op.
   1396                 // (This used to be a shortcut for "add call", but that was a
   1397                 // bad idea because "Add call" is so infrequently-used, and
   1398                 // because the user experience is pretty confusing if you
   1399                 // inadvertently trigger it.)
   1400                 if (VDBG) log("handleCallKey: call in foregound ==> ignoring.");
   1401                 // But note we still consume this key event; see below.
   1402             }
   1403         } else {
   1404             throw new IllegalStateException("Unexpected phone type: " + phoneType);
   1405         }
   1406 
   1407         // We *always* consume the CALL key, since the system-wide default
   1408         // action ("go to the in-call screen") is useless here.
   1409         return true;
   1410     }
   1411 
   1412     boolean isKeyEventAcceptableDTMF (KeyEvent event) {
   1413         return (mDialer != null && mDialer.isKeyEventAcceptable(event));
   1414     }
   1415 
   1416     /**
   1417      * Overriden to track relevant focus changes.
   1418      *
   1419      * If a key is down and some time later the focus changes, we may
   1420      * NOT recieve the keyup event; logically the keyup event has not
   1421      * occured in this window.  This issue is fixed by treating a focus
   1422      * changed event as an interruption to the keydown, making sure
   1423      * that any code that needs to be run in onKeyUp is ALSO run here.
   1424      *
   1425      * Note, this focus change event happens AFTER the in-call menu is
   1426      * displayed, so mIsMenuDisplayed should always be correct by the
   1427      * time this method is called in the framework, please see:
   1428      * {@link onCreatePanelView}, {@link onOptionsMenuClosed}
   1429      */
   1430     @Override
   1431     public void onWindowFocusChanged(boolean hasFocus) {
   1432         // the dtmf tones should no longer be played
   1433         if (VDBG) log("onWindowFocusChanged(" + hasFocus + ")...");
   1434         if (!hasFocus && mDialer != null) {
   1435             if (VDBG) log("- onWindowFocusChanged: faking onDialerKeyUp()...");
   1436             mDialer.onDialerKeyUp(null);
   1437         }
   1438     }
   1439 
   1440     @Override
   1441     public boolean dispatchKeyEvent(KeyEvent event) {
   1442         // if (DBG) log("dispatchKeyEvent(event " + event + ")...");
   1443 
   1444         // Intercept some events before they get dispatched to our views.
   1445         switch (event.getKeyCode()) {
   1446             case KeyEvent.KEYCODE_DPAD_CENTER:
   1447             case KeyEvent.KEYCODE_DPAD_UP:
   1448             case KeyEvent.KEYCODE_DPAD_DOWN:
   1449             case KeyEvent.KEYCODE_DPAD_LEFT:
   1450             case KeyEvent.KEYCODE_DPAD_RIGHT:
   1451                 // Disable DPAD keys and trackball clicks if the touch lock
   1452                 // overlay is up, since "touch lock" really means "disable
   1453                 // the DTMF dialpad" (rather than only disabling touch events.)
   1454                 if (mDialer.isOpened() && isTouchLocked()) {
   1455                     if (DBG) log("- ignoring DPAD event while touch-locked...");
   1456                     return true;
   1457                 }
   1458                 break;
   1459 
   1460             default:
   1461                 break;
   1462         }
   1463 
   1464         return super.dispatchKeyEvent(event);
   1465     }
   1466 
   1467     @Override
   1468     public boolean onKeyUp(int keyCode, KeyEvent event) {
   1469         // if (DBG) log("onKeyUp(keycode " + keyCode + ")...");
   1470 
   1471         // push input to the dialer.
   1472         if ((mDialer != null) && (mDialer.onDialerKeyUp(event))){
   1473             return true;
   1474         } else if (keyCode == KeyEvent.KEYCODE_CALL) {
   1475             // Always consume CALL to be sure the PhoneWindow won't do anything with it
   1476             return true;
   1477         }
   1478         return super.onKeyUp(keyCode, event);
   1479     }
   1480 
   1481     @Override
   1482     public boolean onKeyDown(int keyCode, KeyEvent event) {
   1483         // if (DBG) log("onKeyDown(keycode " + keyCode + ")...");
   1484 
   1485         switch (keyCode) {
   1486             case KeyEvent.KEYCODE_CALL:
   1487                 boolean handled = handleCallKey();
   1488                 if (!handled) {
   1489                     Log.w(LOG_TAG, "InCallScreen should always handle KEYCODE_CALL in onKeyDown");
   1490                 }
   1491                 // Always consume CALL to be sure the PhoneWindow won't do anything with it
   1492                 return true;
   1493 
   1494             // Note there's no KeyEvent.KEYCODE_ENDCALL case here.
   1495             // The standard system-wide handling of the ENDCALL key
   1496             // (see PhoneWindowManager's handling of KEYCODE_ENDCALL)
   1497             // already implements exactly what the UI spec wants,
   1498             // namely (1) "hang up" if there's a current active call,
   1499             // or (2) "don't answer" if there's a current ringing call.
   1500 
   1501             case KeyEvent.KEYCODE_CAMERA:
   1502                 // Disable the CAMERA button while in-call since it's too
   1503                 // easy to press accidentally.
   1504                 return true;
   1505 
   1506             case KeyEvent.KEYCODE_VOLUME_UP:
   1507             case KeyEvent.KEYCODE_VOLUME_DOWN:
   1508                 if (mPhone.getState() == Phone.State.RINGING) {
   1509                     // If an incoming call is ringing, the VOLUME buttons are
   1510                     // actually handled by the PhoneWindowManager.  (We do
   1511                     // this to make sure that we'll respond to them even if
   1512                     // the InCallScreen hasn't come to the foreground yet.)
   1513                     //
   1514                     // We'd only ever get here in the extremely rare case that the
   1515                     // incoming call started ringing *after*
   1516                     // PhoneWindowManager.interceptKeyTq() but before the event
   1517                     // got here, or else if the PhoneWindowManager had some
   1518                     // problem connecting to the ITelephony service.
   1519                     Log.w(LOG_TAG, "VOLUME key: incoming call is ringing!"
   1520                           + " (PhoneWindowManager should have handled this key.)");
   1521                     // But go ahead and handle the key as normal, since the
   1522                     // PhoneWindowManager presumably did NOT handle it:
   1523 
   1524                     final CallNotifier notifier = PhoneApp.getInstance().notifier;
   1525                     if (notifier.isRinging()) {
   1526                         // ringer is actually playing, so silence it.
   1527                         PhoneUtils.setAudioControlState(PhoneUtils.AUDIO_IDLE);
   1528                         if (DBG) log("VOLUME key: silence ringer");
   1529                         notifier.silenceRinger();
   1530                     }
   1531 
   1532                     // As long as an incoming call is ringing, we always
   1533                     // consume the VOLUME keys.
   1534                     return true;
   1535                 }
   1536                 break;
   1537 
   1538             case KeyEvent.KEYCODE_MENU:
   1539                 // Special case for the MENU key: if the "touch lock"
   1540                 // overlay is up (over the DTMF dialpad), allow MENU to
   1541                 // dismiss the overlay just as if you had double-tapped
   1542                 // the onscreen icon.
   1543                 // (We do this because MENU is normally used to bring the
   1544                 // UI back after the screen turns off, and the touch lock
   1545                 // overlay "feels" very similar to the screen going off.
   1546                 // This is also here to be "backward-compatibile" with the
   1547                 // 1.0 behavior, where you *needed* to hit MENU to bring
   1548                 // back the dialpad after 6 seconds of idle time.)
   1549                 if (mDialer.isOpened() && isTouchLocked()) {
   1550                     if (VDBG) log("- allowing MENU to dismiss touch lock overlay...");
   1551                     // Take down the touch lock overlay, but post a
   1552                     // message in the future to bring it back later.
   1553                     enableTouchLock(false);
   1554                     resetTouchLockTimer();
   1555                     return true;
   1556                 }
   1557                 break;
   1558 
   1559             case KeyEvent.KEYCODE_MUTE:
   1560                 PhoneUtils.setMute(mPhone, !PhoneUtils.getMute(mPhone));
   1561                 return true;
   1562 
   1563             // Various testing/debugging features, enabled ONLY when VDBG == true.
   1564             case KeyEvent.KEYCODE_SLASH:
   1565                 if (VDBG) {
   1566                     log("----------- InCallScreen View dump --------------");
   1567                     // Dump starting from the top-level view of the entire activity:
   1568                     Window w = this.getWindow();
   1569                     View decorView = w.getDecorView();
   1570                     decorView.debug();
   1571                     return true;
   1572                 }
   1573                 break;
   1574             case KeyEvent.KEYCODE_EQUALS:
   1575                 if (VDBG) {
   1576                     log("----------- InCallScreen call state dump --------------");
   1577                     PhoneUtils.dumpCallState(mPhone);
   1578                     return true;
   1579                 }
   1580                 break;
   1581             case KeyEvent.KEYCODE_GRAVE:
   1582                 if (VDBG) {
   1583                     // Placeholder for other misc temp testing
   1584                     log("------------ Temp testing -----------------");
   1585                     return true;
   1586                 }
   1587                 break;
   1588         }
   1589 
   1590         if (event.getRepeatCount() == 0 && handleDialerKeyDown(keyCode, event)) {
   1591             return true;
   1592         }
   1593 
   1594         return super.onKeyDown(keyCode, event);
   1595     }
   1596 
   1597     /**
   1598      * Handle a failure notification for a supplementary service
   1599      * (i.e. conference, switch, separate, transfer, etc.).
   1600      */
   1601     void onSuppServiceFailed(AsyncResult r) {
   1602         Phone.SuppService service = (Phone.SuppService) r.result;
   1603         if (DBG) log("onSuppServiceFailed: " + service);
   1604 
   1605         int errorMessageResId;
   1606         switch (service) {
   1607             case SWITCH:
   1608                 // Attempt to switch foreground and background/incoming calls failed
   1609                 // ("Failed to switch calls")
   1610                 errorMessageResId = R.string.incall_error_supp_service_switch;
   1611                 break;
   1612 
   1613             case SEPARATE:
   1614                 // Attempt to separate a call from a conference call
   1615                 // failed ("Failed to separate out call")
   1616                 errorMessageResId = R.string.incall_error_supp_service_separate;
   1617                 break;
   1618 
   1619             case TRANSFER:
   1620                 // Attempt to connect foreground and background calls to
   1621                 // each other (and hanging up user's line) failed ("Call
   1622                 // transfer failed")
   1623                 errorMessageResId = R.string.incall_error_supp_service_transfer;
   1624                 break;
   1625 
   1626             case CONFERENCE:
   1627                 // Attempt to add a call to conference call failed
   1628                 // ("Conference call failed")
   1629                 errorMessageResId = R.string.incall_error_supp_service_conference;
   1630                 break;
   1631 
   1632             case REJECT:
   1633                 // Attempt to reject an incoming call failed
   1634                 // ("Call rejection failed")
   1635                 errorMessageResId = R.string.incall_error_supp_service_reject;
   1636                 break;
   1637 
   1638             case HANGUP:
   1639                 // Attempt to release a call failed ("Failed to release call(s)")
   1640                 errorMessageResId = R.string.incall_error_supp_service_hangup;
   1641                 break;
   1642 
   1643             case UNKNOWN:
   1644             default:
   1645                 // Attempt to use a service we don't recognize or support
   1646                 // ("Unsupported service" or "Selected service failed")
   1647                 errorMessageResId = R.string.incall_error_supp_service_unknown;
   1648                 break;
   1649         }
   1650 
   1651         // mSuppServiceFailureDialog is a generic dialog used for any
   1652         // supp service failure, and there's only ever have one
   1653         // instance at a time.  So just in case a previous dialog is
   1654         // still around, dismiss it.
   1655         if (mSuppServiceFailureDialog != null) {
   1656             if (DBG) log("- DISMISSING mSuppServiceFailureDialog.");
   1657             mSuppServiceFailureDialog.dismiss();  // It's safe to dismiss() a dialog
   1658                                                   // that's already dismissed.
   1659             mSuppServiceFailureDialog = null;
   1660         }
   1661 
   1662         mSuppServiceFailureDialog = new AlertDialog.Builder(this)
   1663                 .setMessage(errorMessageResId)
   1664                 .setPositiveButton(R.string.ok, null)
   1665                 .setCancelable(true)
   1666                 .create();
   1667         mSuppServiceFailureDialog.getWindow().addFlags(
   1668                 WindowManager.LayoutParams.FLAG_BLUR_BEHIND);
   1669         mSuppServiceFailureDialog.show();
   1670     }
   1671 
   1672     /**
   1673      * Something has changed in the phone's state.  Update the UI.
   1674      */
   1675     private void onPhoneStateChanged(AsyncResult r) {
   1676         if (DBG) log("onPhoneStateChanged()...");
   1677 
   1678         // There's nothing to do here if we're not the foreground activity.
   1679         // (When we *do* eventually come to the foreground, we'll do a
   1680         // full update then.)
   1681         if (!mIsForegroundActivity) {
   1682             if (DBG) log("onPhoneStateChanged: Activity not in foreground! Bailing out...");
   1683             return;
   1684         }
   1685 
   1686         updateScreen();
   1687 
   1688         // Make sure we update the poke lock and wake lock when certain
   1689         // phone state changes occur.
   1690         PhoneApp.getInstance().updateWakeState();
   1691     }
   1692 
   1693     /**
   1694      * Updates the UI after a phone connection is disconnected, as follows:
   1695      *
   1696      * - If this was a missed or rejected incoming call, and no other
   1697      *   calls are active, dismiss the in-call UI immediately.  (The
   1698      *   CallNotifier will still create a "missed call" notification if
   1699      *   necessary.)
   1700      *
   1701      * - With any other disconnect cause, if the phone is now totally
   1702      *   idle, display the "Call ended" state for a couple of seconds.
   1703      *
   1704      * - Or, if the phone is still in use, stay on the in-call screen
   1705      *   (and update the UI to reflect the current state of the Phone.)
   1706      *
   1707      * @param r r.result contains the connection that just ended
   1708      */
   1709     private void onDisconnect(AsyncResult r) {
   1710         Connection c = (Connection) r.result;
   1711         Connection.DisconnectCause cause = c.getDisconnectCause();
   1712         if (DBG) log("onDisconnect: " + c + ", cause=" + cause);
   1713 
   1714         boolean currentlyIdle = !phoneIsInUse();
   1715         int autoretrySetting = AUTO_RETRY_OFF;
   1716         boolean phoneIsCdma = (mPhone.getPhoneType() == Phone.PHONE_TYPE_CDMA);
   1717         if (phoneIsCdma) {
   1718             // Get the Auto-retry setting only if Phone State is IDLE,
   1719             // else let it stay as AUTO_RETRY_OFF
   1720             if (currentlyIdle) {
   1721                 autoretrySetting = android.provider.Settings.System.getInt(mPhone.getContext().
   1722                         getContentResolver(), android.provider.Settings.System.CALL_AUTO_RETRY, 0);
   1723             }
   1724         }
   1725 
   1726         // for OTA Call, only if in OTA NORMAL mode, handle OTA END scenario
   1727         final PhoneApp app = PhoneApp.getInstance();
   1728         if ((mInCallScreenMode == InCallScreenMode.OTA_NORMAL)
   1729                 && ((app.cdmaOtaProvisionData != null)
   1730                 && (!app.cdmaOtaProvisionData.inOtaSpcState))) {
   1731             setInCallScreenMode(InCallScreenMode.OTA_ENDED);
   1732             updateScreen();
   1733             return;
   1734         } else if ((mInCallScreenMode == InCallScreenMode.OTA_ENDED)
   1735                 || ((app.cdmaOtaProvisionData != null) && app.cdmaOtaProvisionData.inOtaSpcState)) {
   1736            if (DBG) log("onDisconnect: OTA Call end already handled");
   1737            return;
   1738         }
   1739 
   1740         // Any time a call disconnects, clear out the "history" of DTMF
   1741         // digits you typed (to make sure it doesn't persist from one call
   1742         // to the next.)
   1743         mDialer.clearDigits();
   1744 
   1745         // Under certain call disconnected states, we want to alert the user
   1746         // with a dialog instead of going through the normal disconnect
   1747         // routine.
   1748         if (cause == Connection.DisconnectCause.CALL_BARRED) {
   1749             showGenericErrorDialog(R.string.callFailed_cb_enabled, false);
   1750             return;
   1751         } else if (cause == Connection.DisconnectCause.FDN_BLOCKED) {
   1752             showGenericErrorDialog(R.string.callFailed_fdn_only, false);
   1753             return;
   1754         } else if (cause == Connection.DisconnectCause.CS_RESTRICTED) {
   1755             showGenericErrorDialog(R.string.callFailed_dsac_restricted, false);
   1756             return;
   1757         } else if (cause == Connection.DisconnectCause.CS_RESTRICTED_EMERGENCY) {
   1758             showGenericErrorDialog(R.string.callFailed_dsac_restricted_emergency, false);
   1759             return;
   1760         } else if (cause == Connection.DisconnectCause.CS_RESTRICTED_NORMAL) {
   1761             showGenericErrorDialog(R.string.callFailed_dsac_restricted_normal, false);
   1762             return;
   1763         }
   1764 
   1765         if (phoneIsCdma) {
   1766             Call.State callState = PhoneApp.getInstance().notifier.getPreviousCdmaCallState();
   1767             if ((callState == Call.State.ACTIVE)
   1768                     && (cause != Connection.DisconnectCause.INCOMING_MISSED)
   1769                     && (cause != Connection.DisconnectCause.NORMAL)
   1770                     && (cause != Connection.DisconnectCause.LOCAL)
   1771                     && (cause != Connection.DisconnectCause.INCOMING_REJECTED)) {
   1772                 showCallLostDialog();
   1773             } else if ((callState == Call.State.DIALING || callState == Call.State.ALERTING)
   1774                         && (cause != Connection.DisconnectCause.INCOMING_MISSED)
   1775                         && (cause != Connection.DisconnectCause.NORMAL)
   1776                         && (cause != Connection.DisconnectCause.LOCAL)
   1777                         && (cause != Connection.DisconnectCause.INCOMING_REJECTED)) {
   1778 
   1779                     if (mNeedShowCallLostDialog) {
   1780                         // Show the dialog now since the call that just failed was a retry.
   1781                         showCallLostDialog();
   1782                         mNeedShowCallLostDialog = false;
   1783                     } else {
   1784                         if (autoretrySetting == AUTO_RETRY_OFF) {
   1785                             // Show the dialog for failed call if Auto Retry is OFF in Settings.
   1786                             showCallLostDialog();
   1787                             mNeedShowCallLostDialog = false;
   1788                         } else {
   1789                             // Set the mNeedShowCallLostDialog flag now, so we'll know to show
   1790                             // the dialog if *this* call fails.
   1791                             mNeedShowCallLostDialog = true;
   1792                         }
   1793                     }
   1794             }
   1795         }
   1796 
   1797         // Explicitly clean up up any DISCONNECTED connections
   1798         // in a conference call.
   1799         // [Background: Even after a connection gets disconnected, its
   1800         // Connection object still stays around for a few seconds, in the
   1801         // DISCONNECTED state.  With regular calls, this state drives the
   1802         // "call ended" UI.  But when a single person disconnects from a
   1803         // conference call there's no "call ended" state at all; in that
   1804         // case we blow away any DISCONNECTED connections right now to make sure
   1805         // the UI updates instantly to reflect the current state.]
   1806         Call call = c.getCall();
   1807         if (call != null) {
   1808             // We only care about situation of a single caller
   1809             // disconnecting from a conference call.  In that case, the
   1810             // call will have more than one Connection (including the one
   1811             // that just disconnected, which will be in the DISCONNECTED
   1812             // state) *and* at least one ACTIVE connection.  (If the Call
   1813             // has *no* ACTIVE connections, that means that the entire
   1814             // conference call just ended, so we *do* want to show the
   1815             // "Call ended" state.)
   1816             List<Connection> connections = call.getConnections();
   1817             if (connections != null && connections.size() > 1) {
   1818                 for (Connection conn : connections) {
   1819                     if (conn.getState() == Call.State.ACTIVE) {
   1820                         // This call still has at least one ACTIVE connection!
   1821                         // So blow away any DISCONNECTED connections
   1822                         // (including, presumably, the one that just
   1823                         // disconnected from this conference call.)
   1824 
   1825                         // We also force the wake state to refresh, just in
   1826                         // case the disconnected connections are removed
   1827                         // before the phone state change.
   1828                         if (VDBG) log("- Still-active conf call; clearing DISCONNECTED...");
   1829                         app.updateWakeState();
   1830                         mPhone.clearDisconnected();  // This happens synchronously.
   1831                         break;
   1832                     }
   1833                 }
   1834             }
   1835         }
   1836 
   1837         // Retrieve the emergency call retry count from this intent, in
   1838         // case we need to retry the call again.
   1839         int emergencyCallRetryCount = getIntent().getIntExtra(
   1840                 EmergencyCallHandler.EMERGENCY_CALL_RETRY_KEY,
   1841                 EmergencyCallHandler.INITIAL_ATTEMPT);
   1842 
   1843         // Note: see CallNotifier.onDisconnect() for some other behavior
   1844         // that might be triggered by a disconnect event, like playing the
   1845         // busy/congestion tone.
   1846 
   1847         // Keep track of whether this call was user-initiated or not.
   1848         // (This affects where we take the user next; see delayedCleanupAfterDisconnect().)
   1849         mShowCallLogAfterDisconnect = !c.isIncoming();
   1850 
   1851         // We bail out immediately (and *don't* display the "call ended"
   1852         // state at all) in a couple of cases, including those where we
   1853         // are waiting for the radio to finish powering up for an
   1854         // emergency call:
   1855         boolean bailOutImmediately =
   1856                 ((cause == Connection.DisconnectCause.INCOMING_MISSED)
   1857                  || (cause == Connection.DisconnectCause.INCOMING_REJECTED)
   1858                  || ((cause == Connection.DisconnectCause.OUT_OF_SERVICE)
   1859                          && (emergencyCallRetryCount > 0)))
   1860                 && currentlyIdle;
   1861 
   1862         if (bailOutImmediately) {
   1863             if (VDBG) log("- onDisconnect: bailOutImmediately...");
   1864             // Exit the in-call UI!
   1865             // (This is basically the same "delayed cleanup" we do below,
   1866             // just with zero delay.  Since the Phone is currently idle,
   1867             // this call is guaranteed to immediately finish this activity.)
   1868             delayedCleanupAfterDisconnect();
   1869 
   1870             // Retry the call, by resending the intent to the emergency
   1871             // call handler activity.
   1872             if ((cause == Connection.DisconnectCause.OUT_OF_SERVICE)
   1873                     && (emergencyCallRetryCount > 0)) {
   1874                 startActivity(getIntent()
   1875                         .setClassName(this, EmergencyCallHandler.class.getName()));
   1876             }
   1877         } else {
   1878             if (VDBG) log("- onDisconnect: delayed bailout...");
   1879             // Stay on the in-call screen for now.  (Either the phone is
   1880             // still in use, or the phone is idle but we want to display
   1881             // the "call ended" state for a couple of seconds.)
   1882 
   1883             // Force a UI update in case we need to display anything
   1884             // special given this connection's DisconnectCause (see
   1885             // CallCard.getCallFailedString()).
   1886             updateScreen();
   1887 
   1888             // Display the special "Call ended" state when the phone is idle
   1889             // but there's still a call in the DISCONNECTED state:
   1890             if (currentlyIdle
   1891                 && ((mForegroundCall.getState() == Call.State.DISCONNECTED)
   1892                     || (mBackgroundCall.getState() == Call.State.DISCONNECTED))) {
   1893                 if (VDBG) log("- onDisconnect: switching to 'Call ended' state...");
   1894                 setInCallScreenMode(InCallScreenMode.CALL_ENDED);
   1895             }
   1896 
   1897             // Some other misc cleanup that we do if the call that just
   1898             // disconnected was the foreground call.
   1899             final boolean hasActiveCall = !mForegroundCall.isIdle();
   1900             if (!hasActiveCall) {
   1901                 if (VDBG) log("- onDisconnect: cleaning up after FG call disconnect...");
   1902 
   1903                 // Dismiss any dialogs which are only meaningful for an
   1904                 // active call *and* which become moot if the call ends.
   1905                 if (mWaitPromptDialog != null) {
   1906                     if (VDBG) log("- DISMISSING mWaitPromptDialog.");
   1907                     mWaitPromptDialog.dismiss();  // safe even if already dismissed
   1908                     mWaitPromptDialog = null;
   1909                 }
   1910                 if (mWildPromptDialog != null) {
   1911                     if (VDBG) log("- DISMISSING mWildPromptDialog.");
   1912                     mWildPromptDialog.dismiss();  // safe even if already dismissed
   1913                     mWildPromptDialog = null;
   1914                 }
   1915                 if (mPausePromptDialog != null) {
   1916                     if (DBG) log("- DISMISSING mPausePromptDialog.");
   1917                     mPausePromptDialog.dismiss();  // safe even if already dismissed
   1918                     mPausePromptDialog = null;
   1919                 }
   1920             }
   1921 
   1922             // Updating the screen wake state is done in onPhoneStateChanged().
   1923 
   1924 
   1925             // CDMA: We only clean up if the Phone state is IDLE as we might receive an
   1926             // onDisconnect for a Call Collision case (rare but possible).
   1927             // For Call collision cases i.e. when the user makes an out going call
   1928             // and at the same time receives an Incoming Call, the Incoming Call is given
   1929             // higher preference. At this time framework sends a disconnect for the Out going
   1930             // call connection hence we should *not* bring down the InCallScreen as the Phone
   1931             // State would be RINGING
   1932             if (mPhone.getPhoneType() == Phone.PHONE_TYPE_CDMA) {
   1933                 if (!currentlyIdle) {
   1934                     // Clean up any connections in the DISCONNECTED state.
   1935                     // This is necessary cause in CallCollision the foreground call might have
   1936                     // connections in DISCONNECTED state which needs to be cleared.
   1937                     mPhone.clearDisconnected();
   1938 
   1939                     // The phone is still in use.  Stay here in this activity.
   1940                     // But we don't need to keep the screen on.
   1941                     if (DBG) log("onDisconnect: Call Collision case - staying on InCallScreen.");
   1942                     if (DBG) PhoneUtils.dumpCallState(mPhone);
   1943                     return;
   1944                 }
   1945             }
   1946 
   1947             // Finally, arrange for delayedCleanupAfterDisconnect() to get
   1948             // called after a short interval (during which we display the
   1949             // "call ended" state.)  At that point, if the
   1950             // Phone is idle, we'll finish out of this activity.
   1951             int callEndedDisplayDelay =
   1952                     (cause == Connection.DisconnectCause.LOCAL)
   1953                     ? CALL_ENDED_SHORT_DELAY : CALL_ENDED_LONG_DELAY;
   1954             mHandler.removeMessages(DELAYED_CLEANUP_AFTER_DISCONNECT);
   1955             mHandler.sendEmptyMessageDelayed(DELAYED_CLEANUP_AFTER_DISCONNECT,
   1956                                              callEndedDisplayDelay);
   1957         }
   1958 
   1959         // Remove 3way timer (only meaningful for CDMA)
   1960         mHandler.removeMessages(THREEWAY_CALLERINFO_DISPLAY_DONE);
   1961     }
   1962 
   1963     /**
   1964      * Brings up the "MMI Started" dialog.
   1965      */
   1966     private void onMMIInitiate(AsyncResult r) {
   1967         if (VDBG) log("onMMIInitiate()...  AsyncResult r = " + r);
   1968 
   1969         // Watch out: don't do this if we're not the foreground activity,
   1970         // mainly since in the Dialog.show() might fail if we don't have a
   1971         // valid window token any more...
   1972         // (Note that this exact sequence can happen if you try to start
   1973         // an MMI code while the radio is off or out of service.)
   1974         if (!mIsForegroundActivity) {
   1975             if (VDBG) log("Activity not in foreground! Bailing out...");
   1976             return;
   1977         }
   1978 
   1979         // Also, if any other dialog is up right now (presumably the
   1980         // generic error dialog displaying the "Starting MMI..."  message)
   1981         // take it down before bringing up the real "MMI Started" dialog
   1982         // in its place.
   1983         dismissAllDialogs();
   1984 
   1985         MmiCode mmiCode = (MmiCode) r.result;
   1986         if (VDBG) log("  - MmiCode: " + mmiCode);
   1987 
   1988         Message message = Message.obtain(mHandler, PhoneApp.MMI_CANCEL);
   1989         mMmiStartedDialog = PhoneUtils.displayMMIInitiate(this, mmiCode,
   1990                                                           message, mMmiStartedDialog);
   1991     }
   1992 
   1993     /**
   1994      * Handles an MMI_CANCEL event, which is triggered by the button
   1995      * (labeled either "OK" or "Cancel") on the "MMI Started" dialog.
   1996      * @see onMMIInitiate
   1997      * @see PhoneUtils.cancelMmiCode
   1998      */
   1999     private void onMMICancel() {
   2000         if (VDBG) log("onMMICancel()...");
   2001 
   2002         // First of all, cancel the outstanding MMI code (if possible.)
   2003         PhoneUtils.cancelMmiCode(mPhone);
   2004 
   2005         // Regardless of whether the current MMI code was cancelable, the
   2006         // PhoneApp will get an MMI_COMPLETE event very soon, which will
   2007         // take us to the MMI Complete dialog (see
   2008         // PhoneUtils.displayMMIComplete().)
   2009         //
   2010         // But until that event comes in, we *don't* want to stay here on
   2011         // the in-call screen, since we'll be visible in a
   2012         // partially-constructed state as soon as the "MMI Started" dialog
   2013         // gets dismissed.  So let's forcibly bail out right now.
   2014         if (DBG) log("onMMICancel: finishing InCallScreen...");
   2015         endInCallScreenSession();
   2016     }
   2017 
   2018     /**
   2019      * Handles the POST_ON_DIAL_CHARS message from the Phone
   2020      * (see our call to mPhone.setOnPostDialCharacter() above.)
   2021      *
   2022      * TODO: NEED TO TEST THIS SEQUENCE now that we no longer handle
   2023      * "dialable" key events here in the InCallScreen: we do directly to the
   2024      * Dialer UI instead.  Similarly, we may now need to go directly to the
   2025      * Dialer to handle POST_ON_DIAL_CHARS too.
   2026      */
   2027     private void handlePostOnDialChars(AsyncResult r, char ch) {
   2028         Connection c = (Connection) r.result;
   2029 
   2030         if (c != null) {
   2031             Connection.PostDialState state =
   2032                     (Connection.PostDialState) r.userObj;
   2033 
   2034             if (VDBG) log("handlePostOnDialChar: state = " +
   2035                     state + ", ch = " + ch);
   2036 
   2037             int phoneType = mPhone.getPhoneType();
   2038             switch (state) {
   2039                 case STARTED:
   2040                     if (phoneType == Phone.PHONE_TYPE_CDMA) {
   2041                         mDialer.stopLocalToneCdma();
   2042                         if (mPauseInProgress) {
   2043                             showPausePromptDialogCDMA(c, mPostDialStrAfterPause);
   2044                         }
   2045                         mPauseInProgress = false;
   2046                         mDialer.startLocalToneCdma(ch);
   2047                     }
   2048                     // TODO: is this needed, now that you can't actually
   2049                     // type DTMF chars or dial directly from here?
   2050                     // If so, we'd need to yank you out of the in-call screen
   2051                     // here too (and take you to the 12-key dialer in "in-call" mode.)
   2052                     // displayPostDialedChar(ch);
   2053                     break;
   2054 
   2055                 case WAIT:
   2056                     if (DBG) log("handlePostOnDialChars: show WAIT prompt...");
   2057                     String postDialStr = c.getRemainingPostDialString();
   2058                     if (phoneType == Phone.PHONE_TYPE_CDMA) {
   2059                         mDialer.stopLocalToneCdma();
   2060                         showWaitPromptDialogCDMA(c, postDialStr);
   2061                     } else if (phoneType == Phone.PHONE_TYPE_GSM) {
   2062                         showWaitPromptDialogGSM(c, postDialStr);
   2063                     } else {
   2064                         throw new IllegalStateException("Unexpected phone type: " + phoneType);
   2065                     }
   2066                     break;
   2067 
   2068                 case WILD:
   2069                     if (DBG) log("handlePostOnDialChars: show WILD prompt");
   2070                     showWildPromptDialog(c);
   2071                     break;
   2072 
   2073                 case COMPLETE:
   2074                     if (phoneType == Phone.PHONE_TYPE_CDMA) {
   2075                         mDialer.stopLocalToneCdma();
   2076                     }
   2077                     break;
   2078 
   2079                 case PAUSE:
   2080                     if (phoneType == Phone.PHONE_TYPE_CDMA) {
   2081                         mPostDialStrAfterPause = c.getRemainingPostDialString();
   2082                         mDialer.stopLocalToneCdma();
   2083                         mPauseInProgress = true;
   2084                     }
   2085                     break;
   2086 
   2087                 default:
   2088                     break;
   2089             }
   2090         }
   2091     }
   2092 
   2093     private void showWaitPromptDialogGSM(final Connection c, String postDialStr) {
   2094         if (DBG) log("showWaitPromptDialogGSM: '" + postDialStr + "'...");
   2095 
   2096         Resources r = getResources();
   2097         StringBuilder buf = new StringBuilder();
   2098         buf.append(r.getText(R.string.wait_prompt_str));
   2099         buf.append(postDialStr);
   2100 
   2101         // if (DBG) log("- mWaitPromptDialog = " + mWaitPromptDialog);
   2102         if (mWaitPromptDialog != null) {
   2103             if (DBG) log("- DISMISSING mWaitPromptDialog.");
   2104             mWaitPromptDialog.dismiss();  // safe even if already dismissed
   2105             mWaitPromptDialog = null;
   2106         }
   2107 
   2108         mWaitPromptDialog = new AlertDialog.Builder(this)
   2109                 .setMessage(buf.toString())
   2110                 .setPositiveButton(R.string.send_button, new DialogInterface.OnClickListener() {
   2111                         public void onClick(DialogInterface dialog, int whichButton) {
   2112                             if (DBG) log("handle WAIT_PROMPT_CONFIRMED, proceed...");
   2113                             c.proceedAfterWaitChar();
   2114                             PhoneApp.getInstance().pokeUserActivity();
   2115                         }
   2116                     })
   2117                 .setOnCancelListener(new DialogInterface.OnCancelListener() {
   2118                         public void onCancel(DialogInterface dialog) {
   2119                             if (DBG) log("handle POST_DIAL_CANCELED!");
   2120                             c.cancelPostDial();
   2121                             PhoneApp.getInstance().pokeUserActivity();
   2122                         }
   2123                     })
   2124                 .create();
   2125         mWaitPromptDialog.getWindow().addFlags(
   2126                 WindowManager.LayoutParams.FLAG_BLUR_BEHIND);
   2127         mWaitPromptDialog.show();
   2128     }
   2129 
   2130     /**
   2131      * Processes the CDMA specific requirements of a WAIT character in a
   2132      * dial string.
   2133      *
   2134      * Pop up an alert dialog with OK and Cancel buttons to allow user to
   2135      * Accept or Reject the WAIT inserted as part of the Dial string.
   2136      */
   2137     private void showWaitPromptDialogCDMA(final Connection c, String postDialStr) {
   2138         if (DBG) log("showWaitPromptDialogCDMA: '" + postDialStr + "'...");
   2139 
   2140         Resources r = getResources();
   2141         StringBuilder buf = new StringBuilder();
   2142         buf.append(r.getText(R.string.wait_prompt_str));
   2143         buf.append(postDialStr);
   2144 
   2145         // if (DBG) log("- mWaitPromptDialog = " + mWaitPromptDialog);
   2146         if (mWaitPromptDialog != null) {
   2147             if (DBG) log("- DISMISSING mWaitPromptDialog.");
   2148             mWaitPromptDialog.dismiss();  // safe even if already dismissed
   2149             mWaitPromptDialog = null;
   2150         }
   2151 
   2152         mWaitPromptDialog = new AlertDialog.Builder(this)
   2153                 .setMessage(buf.toString())
   2154                 .setPositiveButton(R.string.pause_prompt_yes,
   2155                     new DialogInterface.OnClickListener() {
   2156                         public void onClick(DialogInterface dialog, int whichButton) {
   2157                             if (DBG) log("handle WAIT_PROMPT_CONFIRMED, proceed...");
   2158                             c.proceedAfterWaitChar();
   2159                         }
   2160                     })
   2161                 .setNegativeButton(R.string.pause_prompt_no, new DialogInterface.OnClickListener() {
   2162                         public void onClick(DialogInterface dialog, int whichButton) {
   2163                             if (DBG) log("handle POST_DIAL_CANCELED!");
   2164                             c.cancelPostDial();
   2165                         }
   2166                     })
   2167                 .create();
   2168         mWaitPromptDialog.getWindow().addFlags(
   2169                 WindowManager.LayoutParams.FLAG_BLUR_BEHIND);
   2170         mWaitPromptDialog.show();
   2171     }
   2172 
   2173     /**
   2174      * Pop up an alert dialog which waits for 2 seconds for each P (Pause) Character entered
   2175      * as part of the Dial String.
   2176      */
   2177     private void showPausePromptDialogCDMA(final Connection c, String postDialStrAfterPause) {
   2178         Resources r = getResources();
   2179         StringBuilder buf = new StringBuilder();
   2180         buf.append(r.getText(R.string.pause_prompt_str));
   2181         buf.append(postDialStrAfterPause);
   2182 
   2183         if (mPausePromptDialog != null) {
   2184             if (DBG) log("- DISMISSING mPausePromptDialog.");
   2185             mPausePromptDialog.dismiss();  // safe even if already dismissed
   2186             mPausePromptDialog = null;
   2187         }
   2188 
   2189         mPausePromptDialog = new AlertDialog.Builder(this)
   2190                 .setMessage(buf.toString())
   2191                 .create();
   2192         mPausePromptDialog.show();
   2193         // 2 second timer
   2194         Message msg = Message.obtain(mHandler, EVENT_PAUSE_DIALOG_COMPLETE);
   2195         mHandler.sendMessageDelayed(msg, PAUSE_PROMPT_DIALOG_TIMEOUT);
   2196     }
   2197 
   2198     private View createWildPromptView() {
   2199         LinearLayout result = new LinearLayout(this);
   2200         result.setOrientation(LinearLayout.VERTICAL);
   2201         result.setPadding(5, 5, 5, 5);
   2202 
   2203         LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(
   2204                         ViewGroup.LayoutParams.MATCH_PARENT,
   2205                         ViewGroup.LayoutParams.WRAP_CONTENT);
   2206 
   2207         TextView promptMsg = new TextView(this);
   2208         promptMsg.setTextSize(14);
   2209         promptMsg.setTypeface(Typeface.DEFAULT_BOLD);
   2210         promptMsg.setText(getResources().getText(R.string.wild_prompt_str));
   2211 
   2212         result.addView(promptMsg, lp);
   2213 
   2214         mWildPromptText = new EditText(this);
   2215         mWildPromptText.setKeyListener(DialerKeyListener.getInstance());
   2216         mWildPromptText.setMovementMethod(null);
   2217         mWildPromptText.setTextSize(14);
   2218         mWildPromptText.setMaxLines(1);
   2219         mWildPromptText.setHorizontallyScrolling(true);
   2220         mWildPromptText.setBackgroundResource(android.R.drawable.editbox_background);
   2221 
   2222         LinearLayout.LayoutParams lp2 = new LinearLayout.LayoutParams(
   2223                         ViewGroup.LayoutParams.MATCH_PARENT,
   2224                         ViewGroup.LayoutParams.WRAP_CONTENT);
   2225         lp2.setMargins(0, 3, 0, 0);
   2226 
   2227         result.addView(mWildPromptText, lp2);
   2228 
   2229         return result;
   2230     }
   2231 
   2232     private void showWildPromptDialog(final Connection c) {
   2233         View v = createWildPromptView();
   2234 
   2235         if (mWildPromptDialog != null) {
   2236             if (VDBG) log("- DISMISSING mWildPromptDialog.");
   2237             mWildPromptDialog.dismiss();  // safe even if already dismissed
   2238             mWildPromptDialog = null;
   2239         }
   2240 
   2241         mWildPromptDialog = new AlertDialog.Builder(this)
   2242                 .setView(v)
   2243                 .setPositiveButton(
   2244                         R.string.send_button,
   2245                         new DialogInterface.OnClickListener() {
   2246                             public void onClick(DialogInterface dialog, int whichButton) {
   2247                                 if (VDBG) log("handle WILD_PROMPT_CHAR_ENTERED, proceed...");
   2248                                 String replacement = null;
   2249                                 if (mWildPromptText != null) {
   2250                                     replacement = mWildPromptText.getText().toString();
   2251                                     mWildPromptText = null;
   2252                                 }
   2253                                 c.proceedAfterWildChar(replacement);
   2254                                 PhoneApp.getInstance().pokeUserActivity();
   2255                             }
   2256                         })
   2257                 .setOnCancelListener(
   2258                         new DialogInterface.OnCancelListener() {
   2259                             public void onCancel(DialogInterface dialog) {
   2260                                 if (VDBG) log("handle POST_DIAL_CANCELED!");
   2261                                 c.cancelPostDial();
   2262                                 PhoneApp.getInstance().pokeUserActivity();
   2263                             }
   2264                         })
   2265                 .create();
   2266         mWildPromptDialog.getWindow().addFlags(
   2267                 WindowManager.LayoutParams.FLAG_BLUR_BEHIND);
   2268         mWildPromptDialog.show();
   2269 
   2270         mWildPromptText.requestFocus();
   2271     }
   2272 
   2273     /**
   2274      * Updates the state of the in-call UI based on the current state of
   2275      * the Phone.
   2276      */
   2277     private void updateScreen() {
   2278         if (DBG) log("updateScreen()...");
   2279 
   2280         // Don't update anything if we're not in the foreground (there's
   2281         // no point updating our UI widgets since we're not visible!)
   2282         // Also note this check also ensures we won't update while we're
   2283         // in the middle of pausing, which could cause a visible glitch in
   2284         // the "activity ending" transition.
   2285         if (!mIsForegroundActivity) {
   2286             if (DBG) log("- updateScreen: not the foreground Activity! Bailing out...");
   2287             return;
   2288         }
   2289 
   2290         // Update the state of the in-call menu items.
   2291         if (mInCallMenu != null) {
   2292             // TODO: do this only if the menu is visible!
   2293             if (DBG) log("- updateScreen: updating menu items...");
   2294             boolean okToShowMenu = mInCallMenu.updateItems(mPhone);
   2295             if (!okToShowMenu) {
   2296                 // Uh oh: we were only trying to update the state of the
   2297                 // menu items, but the logic in InCallMenu.updateItems()
   2298                 // just decided the menu shouldn't be visible at all!
   2299                 // (That's probably means that the call ended
   2300                 // asynchronously while the menu was up.)
   2301                 //
   2302                 // So take the menu down ASAP.
   2303                 if (DBG) log("- updateScreen: Tried to update menu; now need to dismiss!");
   2304                 // dismissMenu() has no effect if the menu is already closed.
   2305                 dismissMenu(true);  // dismissImmediate = true
   2306             }
   2307         }
   2308 
   2309         final PhoneApp app = PhoneApp.getInstance();
   2310 
   2311         if (mInCallScreenMode == InCallScreenMode.OTA_NORMAL) {
   2312             if (DBG) log("- updateScreen: OTA call state NORMAL...");
   2313             if (otaUtils != null) {
   2314                 if (DBG) log("- updateScreen: otaUtils is not null, call otaShowProperScreen");
   2315                 otaUtils.otaShowProperScreen();
   2316             }
   2317             return;
   2318         } else if (mInCallScreenMode == InCallScreenMode.OTA_ENDED) {
   2319             if (DBG) log("- updateScreen: OTA call ended state ...");
   2320             // Wake up the screen when we get notification, good or bad.
   2321             PhoneApp.getInstance().wakeUpScreen();
   2322             if (app.cdmaOtaScreenState.otaScreenState
   2323                 == CdmaOtaScreenState.OtaScreenState.OTA_STATUS_ACTIVATION) {
   2324                 if (DBG) log("- updateScreen: OTA_STATUS_ACTIVATION");
   2325                 if (otaUtils != null) {
   2326                     if (DBG) log("- updateScreen: otaUtils is not null, "
   2327                                   + "call otaShowActivationScreen");
   2328                     otaUtils.otaShowActivateScreen();
   2329                 }
   2330             } else {
   2331                 if (DBG) log("- updateScreen: OTA Call end state for Dialogs");
   2332                 if (otaUtils != null) {
   2333                     if (DBG) log("- updateScreen: Show OTA Success Failure dialog");
   2334                     otaUtils.otaShowSuccessFailure();
   2335                 }
   2336             }
   2337             return;
   2338         } else if (mInCallScreenMode == InCallScreenMode.MANAGE_CONFERENCE) {
   2339             if (DBG) log("- updateScreen: manage conference mode (NOT updating in-call UI)...");
   2340             updateManageConferencePanelIfNecessary();
   2341             return;
   2342         } else if (mInCallScreenMode == InCallScreenMode.CALL_ENDED) {
   2343             if (DBG) log("- updateScreen: call ended state (NOT updating in-call UI)...");
   2344             // Actually we do need to update one thing: the background.
   2345             updateInCallBackground();
   2346             return;
   2347         }
   2348 
   2349         if (DBG) log("- updateScreen: updating the in-call UI...");
   2350         mCallCard.updateState(mPhone);
   2351         updateDialpadVisibility();
   2352         updateInCallTouchUi();
   2353         updateProviderOverlay();
   2354         updateMenuButtonHint();
   2355         updateInCallBackground();
   2356 
   2357         // Forcibly take down all dialog if an incoming call is ringing.
   2358         if (!mRingingCall.isIdle()) {
   2359             dismissAllDialogs();
   2360         } else {
   2361             // Wait prompt dialog is not currently up.  But it *should* be
   2362             // up if the FG call has a connection in the WAIT state and
   2363             // the phone isn't ringing.
   2364             String postDialStr = null;
   2365             List<Connection> fgConnections = mForegroundCall.getConnections();
   2366             int phoneType = mPhone.getPhoneType();
   2367             if (phoneType == Phone.PHONE_TYPE_CDMA) {
   2368                 Connection fgLatestConnection = mForegroundCall.getLatestConnection();
   2369                 if (PhoneApp.getInstance().cdmaPhoneCallState.getCurrentCallState() ==
   2370                         CdmaPhoneCallState.PhoneCallState.CONF_CALL) {
   2371                     for (Connection cn : fgConnections) {
   2372                         if ((cn != null) && (cn.getPostDialState() ==
   2373                                 Connection.PostDialState.WAIT)) {
   2374                             cn.cancelPostDial();
   2375                         }
   2376                     }
   2377                 } else if ((fgLatestConnection != null)
   2378                      && (fgLatestConnection.getPostDialState() == Connection.PostDialState.WAIT)) {
   2379                     if(DBG) log("show the Wait dialog for CDMA");
   2380                     postDialStr = fgLatestConnection.getRemainingPostDialString();
   2381                     showWaitPromptDialogCDMA(fgLatestConnection, postDialStr);
   2382                 }
   2383             } else if (phoneType == Phone.PHONE_TYPE_GSM) {
   2384                 for (Connection cn : fgConnections) {
   2385                     if ((cn != null) && (cn.getPostDialState() == Connection.PostDialState.WAIT)) {
   2386                         postDialStr = cn.getRemainingPostDialString();
   2387                         showWaitPromptDialogGSM(cn, postDialStr);
   2388                     }
   2389                 }
   2390             } else {
   2391                 throw new IllegalStateException("Unexpected phone type: " + phoneType);
   2392             }
   2393         }
   2394     }
   2395 
   2396     /**
   2397      * (Re)synchronizes the onscreen UI with the current state of the
   2398      * Phone.
   2399      *
   2400      * @return InCallInitStatus.SUCCESS if we successfully updated the UI, or
   2401      *    InCallInitStatus.PHONE_NOT_IN_USE if there was no phone state to sync
   2402      *    with (ie. the phone was completely idle).  In the latter case, we
   2403      *    shouldn't even be in the in-call UI in the first place, and it's
   2404      *    the caller's responsibility to bail out of this activity by
   2405      *    calling endInCallScreenSession if appropriate.
   2406      */
   2407     private InCallInitStatus syncWithPhoneState() {
   2408         boolean updateSuccessful = false;
   2409         if (DBG) log("syncWithPhoneState()...");
   2410         if (DBG) PhoneUtils.dumpCallState(mPhone);
   2411         if (VDBG) dumpBluetoothState();
   2412 
   2413         // Make sure the Phone is "in use".  (If not, we shouldn't be on
   2414         // this screen in the first place.)
   2415 
   2416         // Need to treat running MMI codes as a connection as well.
   2417         // Do not check for getPendingMmiCodes when phone is a CDMA phone
   2418         int phoneType = mPhone.getPhoneType();
   2419 
   2420         if ((phoneType == Phone.PHONE_TYPE_CDMA)
   2421                 && ((mInCallScreenMode == InCallScreenMode.OTA_NORMAL)
   2422                 || (mInCallScreenMode == InCallScreenMode.OTA_ENDED))) {
   2423             // Even when OTA Call ends, need to show OTA End UI,
   2424             // so return Success to allow UI update.
   2425             return InCallInitStatus.SUCCESS;
   2426         }
   2427 
   2428         if ((phoneType == Phone.PHONE_TYPE_CDMA)
   2429                 || !mForegroundCall.isIdle() || !mBackgroundCall.isIdle() || !mRingingCall.isIdle()
   2430                 || !mPhone.getPendingMmiCodes().isEmpty()) {
   2431             if (VDBG) log("syncWithPhoneState: it's ok to be here; update the screen...");
   2432             updateScreen();
   2433             return InCallInitStatus.SUCCESS;
   2434         }
   2435 
   2436         if (DBG) log("syncWithPhoneState: phone is idle; we shouldn't be here!");
   2437         return InCallInitStatus.PHONE_NOT_IN_USE;
   2438     }
   2439 
   2440     /**
   2441      * Given the Intent we were initially launched with,
   2442      * figure out the actual phone number we should dial.
   2443      *
   2444      * @return the phone number corresponding to the
   2445      *   specified Intent, or null if the Intent is not
   2446      *   an ACTION_CALL intent or if the intent's data is
   2447      *   malformed or missing.
   2448      *
   2449      * @throws VoiceMailNumberMissingException if the intent
   2450      *   contains a "voicemail" URI, but there's no voicemail
   2451      *   number configured on the device.
   2452      */
   2453     private String getInitialNumber(Intent intent)
   2454             throws PhoneUtils.VoiceMailNumberMissingException {
   2455         String action = intent.getAction();
   2456 
   2457         if (action == null) {
   2458             return null;
   2459         }
   2460 
   2461         if (action != null && action.equals(Intent.ACTION_CALL) &&
   2462                 intent.hasExtra(Intent.EXTRA_PHONE_NUMBER)) {
   2463             return intent.getStringExtra(Intent.EXTRA_PHONE_NUMBER);
   2464         }
   2465 
   2466         return PhoneUtils.getNumberFromIntent(this, mPhone, intent);
   2467     }
   2468 
   2469     /**
   2470      * Make a call to whomever the intent tells us to.
   2471      *
   2472      * @param intent the Intent we were launched with
   2473      * @return InCallInitStatus.SUCCESS if we successfully initiated an
   2474      *    outgoing call.  If there was some kind of failure, return one of
   2475      *    the other InCallInitStatus codes indicating what went wrong.
   2476      */
   2477     private InCallInitStatus placeCall(Intent intent) {
   2478         if (VDBG) log("placeCall()...  intent = " + intent);
   2479 
   2480         String number;
   2481 
   2482         // Check the current ServiceState to make sure it's OK
   2483         // to even try making a call.
   2484         InCallInitStatus okToCallStatus = checkIfOkToInitiateOutgoingCall();
   2485 
   2486         try {
   2487             number = getInitialNumber(intent);
   2488         } catch (PhoneUtils.VoiceMailNumberMissingException ex) {
   2489             // If the call status is NOT in an acceptable state, it
   2490             // may effect the way the voicemail number is being
   2491             // retrieved.  Mask the VoiceMailNumberMissingException
   2492             // with the underlying issue of the phone state.
   2493             if (okToCallStatus != InCallInitStatus.SUCCESS) {
   2494                 if (DBG) log("Voicemail number not reachable in current SIM card state.");
   2495                 return okToCallStatus;
   2496             }
   2497             if (DBG) log("VoiceMailNumberMissingException from getInitialNumber()");
   2498             return InCallInitStatus.VOICEMAIL_NUMBER_MISSING;
   2499         }
   2500 
   2501         if (number == null) {
   2502             Log.w(LOG_TAG, "placeCall: couldn't get a phone number from Intent " + intent);
   2503             return InCallInitStatus.NO_PHONE_NUMBER_SUPPLIED;
   2504         }
   2505 
   2506         boolean isEmergencyNumber = PhoneNumberUtils.isEmergencyNumber(number);
   2507         boolean isEmergencyIntent = Intent.ACTION_CALL_EMERGENCY.equals(intent.getAction());
   2508 
   2509         if (isEmergencyNumber && !isEmergencyIntent) {
   2510             Log.e(LOG_TAG, "Non-CALL_EMERGENCY Intent " + intent
   2511                     + " attempted to call emergency number " + number
   2512                     + ".");
   2513             return InCallInitStatus.CALL_FAILED;
   2514         } else if (!isEmergencyNumber && isEmergencyIntent) {
   2515             Log.e(LOG_TAG, "Received CALL_EMERGENCY Intent " + intent
   2516                     + " with non-emergency number " + number
   2517                     + " -- failing call.");
   2518             return InCallInitStatus.CALL_FAILED;
   2519         }
   2520 
   2521         // If we're trying to call an emergency number, then it's OK to
   2522         // proceed in certain states where we'd usually just bring up
   2523         // an error dialog:
   2524         // - If we're in EMERGENCY_ONLY mode, then (obviously) you're allowed
   2525         //   to dial emergency numbers.
   2526         // - If we're OUT_OF_SERVICE, we still attempt to make a call,
   2527         //   since the radio will register to any available network.
   2528 
   2529         if (isEmergencyNumber
   2530             && ((okToCallStatus == InCallInitStatus.EMERGENCY_ONLY)
   2531                 || (okToCallStatus == InCallInitStatus.OUT_OF_SERVICE))) {
   2532             if (DBG) log("placeCall: Emergency number detected with status = " + okToCallStatus);
   2533             okToCallStatus = InCallInitStatus.SUCCESS;
   2534             if (DBG) log("==> UPDATING status to: " + okToCallStatus);
   2535         }
   2536 
   2537         if (okToCallStatus != InCallInitStatus.SUCCESS) {
   2538             // If this is an emergency call, we call the emergency call
   2539             // handler activity to turn on the radio and do whatever else
   2540             // is needed. For now, we finish the InCallScreen (since were
   2541             // expecting a callback when the emergency call handler dictates
   2542             // it) and just return the success state.
   2543             if (isEmergencyNumber && (okToCallStatus == InCallInitStatus.POWER_OFF)) {
   2544                 startActivity(intent.setClassName(this, EmergencyCallHandler.class.getName()));
   2545                 if (DBG) log("placeCall: starting EmergencyCallHandler, finishing InCallScreen...");
   2546                 endInCallScreenSession();
   2547                 return InCallInitStatus.SUCCESS;
   2548             } else {
   2549                 return okToCallStatus;
   2550             }
   2551         }
   2552 
   2553         final PhoneApp app = PhoneApp.getInstance();
   2554 
   2555         if ((mPhone.getPhoneType() == Phone.PHONE_TYPE_CDMA) && (mPhone.isOtaSpNumber(number))) {
   2556             if (DBG) log("placeCall: isOtaSpNumber() returns true");
   2557             setInCallScreenMode(InCallScreenMode.OTA_NORMAL);
   2558             if (app.cdmaOtaProvisionData != null) {
   2559                 app.cdmaOtaProvisionData.isOtaCallCommitted = false;
   2560             }
   2561         }
   2562 
   2563         mNeedShowCallLostDialog = false;
   2564 
   2565         // We have a valid number, so try to actually place a call:
   2566         // make sure we pass along the intent's URI which is a
   2567         // reference to the contact. We may have a provider gateway
   2568         // phone number to use for the outgoing call.
   2569         int callStatus;
   2570         Uri contactUri = intent.getData();
   2571 
   2572         if (null != mProviderGatewayUri &&
   2573             !(isEmergencyNumber || isEmergencyIntent) &&
   2574             PhoneUtils.isRoutableViaGateway(number)) {  // Filter out MMI, OTA and other codes.
   2575 
   2576             callStatus = PhoneUtils.placeCallVia(
   2577                 this, mPhone, number, contactUri, mProviderGatewayUri);
   2578         } else {
   2579             callStatus = PhoneUtils.placeCall(mPhone, number, contactUri);
   2580         }
   2581 
   2582         switch (callStatus) {
   2583             case PhoneUtils.CALL_STATUS_DIALED:
   2584                 if (VDBG) log("placeCall: PhoneUtils.placeCall() succeeded for regular call '"
   2585                              + number + "'.");
   2586 
   2587                 if (mInCallScreenMode == InCallScreenMode.OTA_NORMAL) {
   2588                     app.cdmaOtaScreenState.otaScreenState =
   2589                             CdmaOtaScreenState.OtaScreenState.OTA_STATUS_LISTENING;
   2590                     updateScreen();
   2591                 }
   2592 
   2593                 // Any time we initiate a call, force the DTMF dialpad to
   2594                 // close.  (We want to make sure the user can see the regular
   2595                 // in-call UI while the new call is dialing, and when it
   2596                 // first gets connected.)
   2597                 mDialer.closeDialer(false);  // no "closing" animation
   2598 
   2599                 // Also, in case a previous call was already active (i.e. if
   2600                 // we just did "Add call"), clear out the "history" of DTMF
   2601                 // digits you typed, to make sure it doesn't persist from the
   2602                 // previous call to the new call.
   2603                 // TODO: it would be more precise to do this when the actual
   2604                 // phone state change happens (i.e. when a new foreground
   2605                 // call appears and the previous call moves to the
   2606                 // background), but the InCallScreen doesn't keep enough
   2607                 // state right now to notice that specific transition in
   2608                 // onPhoneStateChanged().
   2609                 mDialer.clearDigits();
   2610 
   2611                 if (mPhone.getPhoneType() == Phone.PHONE_TYPE_CDMA) {
   2612                     // Start the 2 second timer for 3 Way CallerInfo
   2613                     if (app.cdmaPhoneCallState.getCurrentCallState()
   2614                             == CdmaPhoneCallState.PhoneCallState.THRWAY_ACTIVE) {
   2615                         //Unmute for the second MO call
   2616                         PhoneUtils.setMuteInternal(mPhone, false);
   2617 
   2618                         //Start the timer for displaying "Dialing" for second call
   2619                         Message msg = Message.obtain(mHandler, THREEWAY_CALLERINFO_DISPLAY_DONE);
   2620                         mHandler.sendMessageDelayed(msg, THREEWAY_CALLERINFO_DISPLAY_TIME);
   2621 
   2622                         // Set the mThreeWayCallOrigStateDialing state to true
   2623                         app.cdmaPhoneCallState.setThreeWayCallOrigState(true);
   2624 
   2625                         //Update screen to show 3way dialing
   2626                         updateScreen();
   2627                     }
   2628                 }
   2629 
   2630                 return InCallInitStatus.SUCCESS;
   2631             case PhoneUtils.CALL_STATUS_DIALED_MMI:
   2632                 if (DBG) log("placeCall: specified number was an MMI code: '" + number + "'.");
   2633                 // The passed-in number was an MMI code, not a regular phone number!
   2634                 // This isn't really a failure; the Dialer may have deliberately
   2635                 // fired an ACTION_CALL intent to dial an MMI code, like for a
   2636                 // USSD call.
   2637                 //
   2638                 // Presumably an MMI_INITIATE message will come in shortly
   2639                 // (and we'll bring up the "MMI Started" dialog), or else
   2640                 // an MMI_COMPLETE will come in (which will take us to a
   2641                 // different Activity; see PhoneUtils.displayMMIComplete()).
   2642                 return InCallInitStatus.DIALED_MMI;
   2643             case PhoneUtils.CALL_STATUS_FAILED:
   2644                 Log.w(LOG_TAG, "placeCall: PhoneUtils.placeCall() FAILED for number '"
   2645                       + number + "'.");
   2646                 // We couldn't successfully place the call; there was some
   2647                 // failure in the telephony layer.
   2648                 return InCallInitStatus.CALL_FAILED;
   2649             default:
   2650                 Log.w(LOG_TAG, "placeCall: unknown callStatus " + callStatus
   2651                       + " from PhoneUtils.placeCall() for number '" + number + "'.");
   2652                 return InCallInitStatus.SUCCESS;  // Try to continue anyway...
   2653         }
   2654     }
   2655 
   2656     /**
   2657      * Checks the current ServiceState to make sure it's OK
   2658      * to try making an outgoing call to the specified number.
   2659      *
   2660      * @return InCallInitStatus.SUCCESS if it's OK to try calling the specified
   2661      *    number.  If not, like if the radio is powered off or we have no
   2662      *    signal, return one of the other InCallInitStatus codes indicating what
   2663      *    the problem is.
   2664      */
   2665     private InCallInitStatus checkIfOkToInitiateOutgoingCall() {
   2666         // Watch out: do NOT use PhoneStateIntentReceiver.getServiceState() here;
   2667         // that's not guaranteed to be fresh.  To synchronously get the
   2668         // CURRENT service state, ask the Phone object directly:
   2669         int state = mPhone.getServiceState().getState();
   2670         if (VDBG) log("checkIfOkToInitiateOutgoingCall: ServiceState = " + state);
   2671 
   2672         switch (state) {
   2673             case ServiceState.STATE_IN_SERVICE:
   2674                 // Normal operation.  It's OK to make outgoing calls.
   2675                 return InCallInitStatus.SUCCESS;
   2676 
   2677             case ServiceState.STATE_POWER_OFF:
   2678                 // Radio is explictly powered off.
   2679                 return InCallInitStatus.POWER_OFF;
   2680 
   2681             case ServiceState.STATE_EMERGENCY_ONLY:
   2682                 // The phone is registered, but locked. Only emergency
   2683                 // numbers are allowed.
   2684                 // Note that as of Android 2.0 at least, the telephony layer
   2685                 // does not actually use ServiceState.STATE_EMERGENCY_ONLY,
   2686                 // mainly since there's no guarantee that the radio/RIL can
   2687                 // make this distinction.  So in practice the
   2688                 // InCallInitStatus.EMERGENCY_ONLY state and the string
   2689                 // "incall_error_emergency_only" are totally unused.
   2690                 return InCallInitStatus.EMERGENCY_ONLY;
   2691 
   2692             case ServiceState.STATE_OUT_OF_SERVICE:
   2693                 // No network connection.
   2694                 return InCallInitStatus.OUT_OF_SERVICE;
   2695 
   2696             default:
   2697                 throw new IllegalStateException("Unexpected ServiceState: " + state);
   2698         }
   2699     }
   2700 
   2701     private void handleMissingVoiceMailNumber() {
   2702         if (DBG) log("handleMissingVoiceMailNumber");
   2703 
   2704         final Message msg = Message.obtain(mHandler);
   2705         msg.what = DONT_ADD_VOICEMAIL_NUMBER;
   2706 
   2707         final Message msg2 = Message.obtain(mHandler);
   2708         msg2.what = ADD_VOICEMAIL_NUMBER;
   2709 
   2710         mMissingVoicemailDialog = new AlertDialog.Builder(this)
   2711                 .setTitle(R.string.no_vm_number)
   2712                 .setMessage(R.string.no_vm_number_msg)
   2713                 .setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
   2714                         public void onClick(DialogInterface dialog, int which) {
   2715                             if (VDBG) log("Missing voicemail AlertDialog: POSITIVE click...");
   2716                             msg.sendToTarget();  // see dontAddVoiceMailNumber()
   2717                             PhoneApp.getInstance().pokeUserActivity();
   2718                         }})
   2719                 .setNegativeButton(R.string.add_vm_number_str,
   2720                                    new DialogInterface.OnClickListener() {
   2721                         public void onClick(DialogInterface dialog, int which) {
   2722                             if (VDBG) log("Missing voicemail AlertDialog: NEGATIVE click...");
   2723                             msg2.sendToTarget();  // see addVoiceMailNumber()
   2724                             PhoneApp.getInstance().pokeUserActivity();
   2725                         }})
   2726                 .setOnCancelListener(new OnCancelListener() {
   2727                         public void onCancel(DialogInterface dialog) {
   2728                             if (VDBG) log("Missing voicemail AlertDialog: CANCEL handler...");
   2729                             msg.sendToTarget();  // see dontAddVoiceMailNumber()
   2730                             PhoneApp.getInstance().pokeUserActivity();
   2731                         }})
   2732                 .create();
   2733 
   2734         // When the dialog is up, completely hide the in-call UI
   2735         // underneath (which is in a partially-constructed state).
   2736         mMissingVoicemailDialog.getWindow().addFlags(
   2737                 WindowManager.LayoutParams.FLAG_DIM_BEHIND);
   2738 
   2739         mMissingVoicemailDialog.show();
   2740     }
   2741 
   2742     private void addVoiceMailNumberPanel() {
   2743         if (mMissingVoicemailDialog != null) {
   2744             mMissingVoicemailDialog.dismiss();
   2745             mMissingVoicemailDialog = null;
   2746         }
   2747         if (DBG) log("addVoiceMailNumberPanel: finishing InCallScreen...");
   2748         endInCallScreenSession();
   2749 
   2750         if (DBG) log("show vm setting");
   2751 
   2752         // navigate to the Voicemail setting in the Call Settings activity.
   2753         Intent intent = new Intent(CallFeaturesSetting.ACTION_ADD_VOICEMAIL);
   2754         intent.setClass(this, CallFeaturesSetting.class);
   2755         startActivity(intent);
   2756     }
   2757 
   2758     private void dontAddVoiceMailNumber() {
   2759         if (mMissingVoicemailDialog != null) {
   2760             mMissingVoicemailDialog.dismiss();
   2761             mMissingVoicemailDialog = null;
   2762         }
   2763         if (DBG) log("dontAddVoiceMailNumber: finishing InCallScreen...");
   2764         endInCallScreenSession();
   2765     }
   2766 
   2767     /**
   2768      * Do some delayed cleanup after a Phone call gets disconnected.
   2769      *
   2770      * This method gets called a couple of seconds after any DISCONNECT
   2771      * event from the Phone; it's triggered by the
   2772      * DELAYED_CLEANUP_AFTER_DISCONNECT message we send in onDisconnect().
   2773      *
   2774      * If the Phone is totally idle right now, that means we've already
   2775      * shown the "call ended" state for a couple of seconds, and it's now
   2776      * time to endInCallScreenSession this activity.
   2777      *
   2778      * If the Phone is *not* idle right now, that probably means that one
   2779      * call ended but the other line is still in use.  In that case, we
   2780      * *don't* exit the in-call screen, but we at least turn off the
   2781      * backlight (which we turned on in onDisconnect().)
   2782      */
   2783     private void delayedCleanupAfterDisconnect() {
   2784         if (VDBG) log("delayedCleanupAfterDisconnect()...  Phone state = " + mPhone.getState());
   2785 
   2786         // Clean up any connections in the DISCONNECTED state.
   2787         //
   2788         // [Background: Even after a connection gets disconnected, its
   2789         // Connection object still stays around, in the special
   2790         // DISCONNECTED state.  This is necessary because we we need the
   2791         // caller-id information from that Connection to properly draw the
   2792         // "Call ended" state of the CallCard.
   2793         //   But at this point we truly don't need that connection any
   2794         // more, so tell the Phone that it's now OK to to clean up any
   2795         // connections still in that state.]
   2796         mPhone.clearDisconnected();
   2797 
   2798         if (!phoneIsInUse()) {
   2799             // Phone is idle!  We should exit this screen now.
   2800             if (DBG) log("- delayedCleanupAfterDisconnect: phone is idle...");
   2801 
   2802             // And (finally!) exit from the in-call screen
   2803             // (but not if we're already in the process of pausing...)
   2804             if (mIsForegroundActivity) {
   2805                 if (DBG) log("- delayedCleanupAfterDisconnect: finishing InCallScreen...");
   2806 
   2807                 // If this is a call that was initiated by the user, and
   2808                 // we're *not* in emergency mode, finish the call by
   2809                 // taking the user to the Call Log.
   2810                 // Otherwise we simply call endInCallScreenSession, which will take us
   2811                 // back to wherever we came from.
   2812                 if (mShowCallLogAfterDisconnect && !isPhoneStateRestricted()) {
   2813                     if (VDBG) log("- Show Call Log after disconnect...");
   2814                     final Intent intent = PhoneApp.createCallLogIntent();
   2815                     intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
   2816                     startActivity(intent);
   2817                     // Even in this case we still call endInCallScreenSession (below),
   2818                     // to make sure we don't stay in the activity history.
   2819                 }
   2820 
   2821                 endInCallScreenSession();
   2822             }
   2823         } else {
   2824             // The phone is still in use.  Stay here in this activity, but
   2825             // we don't need to keep the screen on.
   2826             if (DBG) log("- delayedCleanupAfterDisconnect: staying on the InCallScreen...");
   2827             if (DBG) PhoneUtils.dumpCallState(mPhone);
   2828         }
   2829     }
   2830 
   2831 
   2832     //
   2833     // Callbacks for buttons / menu items.
   2834     //
   2835 
   2836     public void onClick(View view) {
   2837         int id = view.getId();
   2838         if (VDBG) log("onClick(View " + view + ", id " + id + ")...");
   2839         if (VDBG && view instanceof InCallMenuItemView) {
   2840             InCallMenuItemView item = (InCallMenuItemView) view;
   2841             log("  ==> menu item! " + item);
   2842         }
   2843 
   2844         // Most menu items dismiss the menu immediately once you click
   2845         // them.  But some items (the "toggle" buttons) are different:
   2846         // they want the menu to stay visible for a second afterwards to
   2847         // give you feedback about the state change.
   2848         boolean dismissMenuImmediate = true;
   2849 
   2850         switch (id) {
   2851             case R.id.menuAnswerAndHold:
   2852                 if (VDBG) log("onClick: AnswerAndHold...");
   2853                 internalAnswerCall();  // Automatically holds the current active call
   2854                 break;
   2855 
   2856             case R.id.menuAnswerAndEnd:
   2857                 if (VDBG) log("onClick: AnswerAndEnd...");
   2858                 internalAnswerAndEnd();
   2859                 break;
   2860 
   2861             case R.id.menuAnswer:
   2862                 if (DBG) log("onClick: Answer...");
   2863                 internalAnswerCall();
   2864                 break;
   2865 
   2866             case R.id.menuIgnore:
   2867                 if (DBG) log("onClick: Ignore...");
   2868                 internalHangupRingingCall();
   2869                 break;
   2870 
   2871             case R.id.menuSwapCalls:
   2872                 if (DBG) log("onClick: SwapCalls...");
   2873                 internalSwapCalls();
   2874                 break;
   2875 
   2876             case R.id.menuMergeCalls:
   2877                 if (VDBG) log("onClick: MergeCalls...");
   2878                 PhoneUtils.mergeCalls(mPhone);
   2879                 break;
   2880 
   2881             case R.id.menuManageConference:
   2882                 if (VDBG) log("onClick: ManageConference...");
   2883                 // Show the Manage Conference panel.
   2884                 setInCallScreenMode(InCallScreenMode.MANAGE_CONFERENCE);
   2885                 break;
   2886 
   2887             case R.id.menuShowDialpad:
   2888                 if (VDBG) log("onClick: Show/hide dialpad...");
   2889                 onShowHideDialpad();
   2890                 break;
   2891 
   2892             case R.id.manage_done:  // mButtonManageConferenceDone
   2893                 if (VDBG) log("onClick: mButtonManageConferenceDone...");
   2894                 // Hide the Manage Conference panel, return to NORMAL mode.
   2895                 setInCallScreenMode(InCallScreenMode.NORMAL);
   2896                 break;
   2897 
   2898             case R.id.menuSpeaker:
   2899                 if (VDBG) log("onClick: Speaker...");
   2900                 onSpeakerClick();
   2901                 // This is a "toggle" button; let the user see the new state for a moment.
   2902                 dismissMenuImmediate = false;
   2903                 break;
   2904 
   2905             case R.id.menuBluetooth:
   2906                 if (VDBG) log("onClick: Bluetooth...");
   2907                 onBluetoothClick();
   2908                 // This is a "toggle" button; let the user see the new state for a moment.
   2909                 dismissMenuImmediate = false;
   2910                 break;
   2911 
   2912             case R.id.menuMute:
   2913                 if (VDBG) log("onClick: Mute...");
   2914                 onMuteClick();
   2915                 // This is a "toggle" button; let the user see the new state for a moment.
   2916                 dismissMenuImmediate = false;
   2917                 break;
   2918 
   2919             case R.id.menuHold:
   2920                 if (VDBG) log("onClick: Hold...");
   2921                 onHoldClick();
   2922                 // This is a "toggle" button; let the user see the new state for a moment.
   2923                 dismissMenuImmediate = false;
   2924                 break;
   2925 
   2926             case R.id.menuAddCall:
   2927                 if (VDBG) log("onClick: AddCall...");
   2928                 PhoneUtils.startNewCall(mPhone);  // Fires off an ACTION_DIAL intent
   2929                 break;
   2930 
   2931             case R.id.menuEndCall:
   2932                 if (VDBG) log("onClick: EndCall...");
   2933                 internalHangup();
   2934                 break;
   2935 
   2936             default:
   2937                 if  ((mInCallScreenMode == InCallScreenMode.OTA_NORMAL
   2938                         || mInCallScreenMode == InCallScreenMode.OTA_ENDED)
   2939                         && otaUtils != null) {
   2940                     otaUtils.onClickHandler(id);
   2941                 } else {
   2942                     Log.w(LOG_TAG,
   2943                             "Got click from unexpected View ID " + id + " (View = " + view + ")");
   2944                 }
   2945                 break;
   2946         }
   2947 
   2948         EventLog.writeEvent(EventLogTags.PHONE_UI_BUTTON_CLICK,
   2949                 (view instanceof TextView) ? ((TextView) view).getText() : "");
   2950 
   2951         // If the user just clicked a "stateful" menu item (i.e. one of
   2952         // the toggle buttons), we keep the menu onscreen briefly to
   2953         // provide visual feedback.  Since we want the user to see the
   2954         // *new* current state, force the menu items to update right now.
   2955         //
   2956         // Note that some toggle buttons ("Hold" in particular) do NOT
   2957         // immediately change the state of the Phone.  In that case, the
   2958         // updateItems() call below won't have any visible effect.
   2959         // Instead, the menu will get updated by the updateScreen() call
   2960         // that happens from onPhoneStateChanged().
   2961 
   2962         if (!dismissMenuImmediate) {
   2963             // TODO: mInCallMenu.updateItems() is a very big hammer; it
   2964             // would be more efficient to update *only* the menu item(s)
   2965             // we just changed.  (Doing it this way doesn't seem to cause
   2966             // a noticeable performance problem, though.)
   2967             if (VDBG) log("- onClick: updating menu to show 'new' current state...");
   2968             boolean okToShowMenu = mInCallMenu.updateItems(mPhone);
   2969             if (!okToShowMenu) {
   2970                 // Uh oh.  All we tried to do was update the state of the
   2971                 // menu items, but the logic in InCallMenu.updateItems()
   2972                 // just decided the menu shouldn't be visible at all!
   2973                 // (That probably means that the call ended asynchronously
   2974                 // while the menu was up.)
   2975                 //
   2976                 // That's OK; just make sure to take the menu down ASAP.
   2977                 if (VDBG) log("onClick: Tried to update menu, but now need to take it down!");
   2978                 dismissMenuImmediate = true;
   2979             }
   2980         }
   2981 
   2982         // Any menu item counts as explicit "user activity".
   2983         PhoneApp.getInstance().pokeUserActivity();
   2984 
   2985         // Finally, *any* action handled here closes the menu (either
   2986         // immediately, or after a short delay).
   2987         //
   2988         // Note that some of the clicks we handle here aren't even menu
   2989         // items in the first place, like the mButtonManageConferenceDone
   2990         // button.  That's OK; if the menu is already closed, the
   2991         // dismissMenu() call does nothing.
   2992         dismissMenu(dismissMenuImmediate);
   2993     }
   2994 
   2995     private void onHoldClick() {
   2996         if (VDBG) log("onHoldClick()...");
   2997 
   2998         final boolean hasActiveCall = !mForegroundCall.isIdle();
   2999         final boolean hasHoldingCall = !mBackgroundCall.isIdle();
   3000         if (VDBG) log("- hasActiveCall = " + hasActiveCall
   3001                       + ", hasHoldingCall = " + hasHoldingCall);
   3002         boolean newHoldState;
   3003         boolean holdButtonEnabled;
   3004         if (hasActiveCall && !hasHoldingCall) {
   3005             // There's only one line in use, and that line is active.
   3006             PhoneUtils.switchHoldingAndActive(mPhone);  // Really means "hold" in this state
   3007             newHoldState = true;
   3008             holdButtonEnabled = true;
   3009         } else if (!hasActiveCall && hasHoldingCall) {
   3010             // There's only one line in use, and that line is on hold.
   3011             PhoneUtils.switchHoldingAndActive(mPhone);  // Really means "unhold" in this state
   3012             newHoldState = false;
   3013             holdButtonEnabled = true;
   3014         } else {
   3015             // Either zero or 2 lines are in use; "hold/unhold" is meaningless.
   3016             newHoldState = false;
   3017             holdButtonEnabled = false;
   3018         }
   3019         // TODO: we *could* now forcibly update the "Hold" button based on
   3020         // "newHoldState" and "holdButtonEnabled".  But for now, do
   3021         // nothing here, and instead let the menu get updated when the
   3022         // onPhoneStateChanged() callback comes in.  (This seems to be
   3023         // responsive enough.)
   3024 
   3025         // Also, any time we hold or unhold, force the DTMF dialpad to close.
   3026         mDialer.closeDialer(true);  // do the "closing" animation
   3027     }
   3028 
   3029     private void onSpeakerClick() {
   3030         if (VDBG) log("onSpeakerClick()...");
   3031 
   3032         // TODO: Turning on the speaker seems to enable the mic
   3033         //   whether or not the "mute" feature is active!
   3034         // Not sure if this is an feature of the telephony API
   3035         //   that I need to handle specially, or just a bug.
   3036         boolean newSpeakerState = !PhoneUtils.isSpeakerOn(this);
   3037         if (newSpeakerState && isBluetoothAvailable() && isBluetoothAudioConnected()) {
   3038             disconnectBluetoothAudio();
   3039         }
   3040         PhoneUtils.turnOnSpeaker(this, newSpeakerState, true);
   3041 
   3042         if (newSpeakerState) {
   3043             // The "touch lock" overlay is NEVER used when the speaker is on.
   3044             enableTouchLock(false);
   3045         } else {
   3046             // User just turned the speaker *off*.  If the dialpad
   3047             // is open, we need to start the timer that will
   3048             // eventually bring up the "touch lock" overlay.
   3049             if (mDialer.isOpened() && !isTouchLocked()) {
   3050                 resetTouchLockTimer();
   3051             }
   3052         }
   3053     }
   3054 
   3055     private void onMuteClick() {
   3056         if (VDBG) log("onMuteClick()...");
   3057         boolean newMuteState = !PhoneUtils.getMute(mPhone);
   3058         PhoneUtils.setMute(mPhone, newMuteState);
   3059     }
   3060 
   3061     private void onBluetoothClick() {
   3062         if (VDBG) log("onBluetoothClick()...");
   3063 
   3064         if (isBluetoothAvailable()) {
   3065             // Toggle the bluetooth audio connection state:
   3066             if (isBluetoothAudioConnected()) {
   3067                 disconnectBluetoothAudio();
   3068             } else {
   3069                 // Manually turn the speaker phone off, instead of allowing the
   3070                 // Bluetooth audio routing handle it.  This ensures that the rest
   3071                 // of the speakerphone code is executed, and reciprocates the
   3072                 // menuSpeaker code above in onClick().  The onClick() code
   3073                 // disconnects the active bluetooth headsets when the
   3074                 // speakerphone is turned on.
   3075                 if (PhoneUtils.isSpeakerOn(this)) {
   3076                     PhoneUtils.turnOnSpeaker(this, false, true);
   3077                 }
   3078 
   3079                 connectBluetoothAudio();
   3080             }
   3081         } else {
   3082             // Bluetooth isn't available; the "Audio" button shouldn't have
   3083             // been enabled in the first place!
   3084             Log.w(LOG_TAG, "Got onBluetoothClick, but bluetooth is unavailable");
   3085         }
   3086     }
   3087 
   3088     private void onShowHideDialpad() {
   3089         if (VDBG) log("onShowHideDialpad()...");
   3090         if (mDialer.isOpened()) {
   3091             mDialer.closeDialer(true);  // do the "closing" animation
   3092         } else {
   3093             mDialer.openDialer(true);  // do the "opening" animation
   3094         }
   3095         mDialer.setHandleVisible(true);
   3096     }
   3097 
   3098     /**
   3099      * Handles button clicks from the InCallTouchUi widget.
   3100      */
   3101     /* package */ void handleOnscreenButtonClick(int id) {
   3102         if (DBG) log("handleOnscreenButtonClick(id " + id + ")...");
   3103 
   3104         switch (id) {
   3105             // TODO: since every button here corresponds to a menu item that we
   3106             // already handle in onClick(), maybe merge the guts of these two
   3107             // methods into a separate helper that takes an ID (of either a menu
   3108             // item *or* touch button) and does the appropriate user action.
   3109 
   3110             // Actions while an incoming call is ringing:
   3111             case R.id.answerButton:
   3112                 internalAnswerCall();
   3113                 break;
   3114             case R.id.rejectButton:
   3115                 internalHangupRingingCall();
   3116                 break;
   3117 
   3118             // The other regular (single-tap) buttons used while in-call:
   3119             case R.id.holdButton:
   3120                 onHoldClick();
   3121                 break;
   3122             case R.id.swapButton:
   3123                 internalSwapCalls();
   3124                 break;
   3125             case R.id.endButton:
   3126                 internalHangup();
   3127                 break;
   3128             case R.id.dialpadButton:
   3129                 onShowHideDialpad();
   3130                 break;
   3131             case R.id.bluetoothButton:
   3132                 onBluetoothClick();
   3133                 break;
   3134             case R.id.muteButton:
   3135                 onMuteClick();
   3136                 break;
   3137             case R.id.speakerButton:
   3138                 onSpeakerClick();
   3139                 break;
   3140             case R.id.addButton:
   3141                 PhoneUtils.startNewCall(mPhone);  // Fires off an ACTION_DIAL intent
   3142                 break;
   3143             case R.id.mergeButton:
   3144             case R.id.cdmaMergeButton:
   3145                 PhoneUtils.mergeCalls(mPhone);
   3146                 break;
   3147             case R.id.manageConferencePhotoButton:
   3148                 // Show the Manage Conference panel.
   3149                 setInCallScreenMode(InCallScreenMode.MANAGE_CONFERENCE);
   3150                 break;
   3151 
   3152             default:
   3153                 Log.w(LOG_TAG, "handleOnscreenButtonClick: unexpected ID " + id);
   3154                 break;
   3155         }
   3156 
   3157         // Just in case the user clicked a "stateful" menu item (i.e. one
   3158         // of the toggle buttons), we force the in-call buttons to update,
   3159         // to make sure the user sees the *new* current state.
   3160         //
   3161         // (But note that some toggle buttons may *not* immediately change
   3162         // the state of the Phone, in which case the updateInCallTouchUi()
   3163         // call here won't have any visible effect.  Instead, those
   3164         // buttons will get updated by the updateScreen() call that gets
   3165         // triggered when the onPhoneStateChanged() event comes in.)
   3166         //
   3167         // TODO: updateInCallTouchUi() is overkill here; it would be
   3168         // more efficient to update *only* the affected button(s).
   3169         // Consider adding API for that.  (This is lo-pri since
   3170         // updateInCallTouchUi() is pretty cheap already...)
   3171         updateInCallTouchUi();
   3172     }
   3173 
   3174     /**
   3175      * Update the network provider's overlay based on the value of
   3176      * mProviderOverlayVisible.
   3177      * If false the overlay is hidden otherwise it is shown.  A
   3178      * delayed message is posted to take the overalay down after
   3179      * PROVIDER_OVERLAY_TIMEOUT. This ensures the user will see the
   3180      * overlay even if the call setup phase is very short.
   3181      */
   3182     private void updateProviderOverlay() {
   3183         if (VDBG) log("updateProviderOverlay: " + mProviderOverlayVisible);
   3184 
   3185         ViewGroup overlay = (ViewGroup) findViewById(R.id.inCallProviderOverlay);
   3186 
   3187         if (mProviderOverlayVisible) {
   3188             CharSequence template = getText(R.string.calling_via_template);
   3189             CharSequence text = TextUtils.expandTemplate(template, mProviderLabel,
   3190                                                          mProviderAddress);
   3191 
   3192             TextView message = (TextView) findViewById(R.id.callingVia);
   3193             message.setCompoundDrawablesWithIntrinsicBounds(mProviderIcon, null, null, null);
   3194             message.setText(text);
   3195 
   3196             overlay.setVisibility(View.VISIBLE);
   3197 
   3198             // Remove any zombie messages and then send a message to
   3199             // self to remove the overlay after some time.
   3200             mHandler.removeMessages(EVENT_HIDE_PROVIDER_OVERLAY);
   3201             Message msg = Message.obtain(mHandler, EVENT_HIDE_PROVIDER_OVERLAY);
   3202             mHandler.sendMessageDelayed(msg, PROVIDER_OVERLAY_TIMEOUT);
   3203         } else {
   3204             overlay.setVisibility(View.GONE);
   3205         }
   3206     }
   3207 
   3208     /**
   3209      * Updates the "Press Menu for more options" hint based on the current
   3210      * state of the Phone.
   3211      */
   3212     private void updateMenuButtonHint() {
   3213         if (VDBG) log("updateMenuButtonHint()...");
   3214         boolean hintVisible = true;
   3215 
   3216         final boolean hasRingingCall = !mRingingCall.isIdle();
   3217         final boolean hasActiveCall = !mForegroundCall.isIdle();
   3218         final boolean hasHoldingCall = !mBackgroundCall.isIdle();
   3219 
   3220         // The hint is hidden only when there's no menu at all,
   3221         // which only happens in a few specific cases:
   3222         if (mInCallScreenMode == InCallScreenMode.CALL_ENDED) {
   3223             // The "Call ended" state.
   3224             hintVisible = false;
   3225         } else if (hasRingingCall && !(hasActiveCall && !hasHoldingCall)) {
   3226             // An incoming call where you *don't* have the option to
   3227             // "answer & end" or "answer & hold".
   3228             hintVisible = false;
   3229         } else if (!phoneIsInUse()) {
   3230             // Or if the phone is totally idle (like if an error dialog
   3231             // is up, or an MMI is running.)
   3232             hintVisible = false;
   3233         }
   3234 
   3235         // The hint is also hidden on devices where we use onscreen
   3236         // touchable buttons instead.
   3237         if (isTouchUiEnabled()) {
   3238             hintVisible = false;
   3239         }
   3240 
   3241         // Also, if an incoming call is ringing, hide the hint if the
   3242         // "incoming call" touch UI is present (since the SlidingTab
   3243         // widget takes up a lot of space and the hint would collide with
   3244         // it.)
   3245         if (hasRingingCall && isIncomingCallTouchUiEnabled()) {
   3246             hintVisible = false;
   3247         }
   3248 
   3249         int hintVisibility = (hintVisible) ? View.VISIBLE : View.GONE;
   3250         mCallCard.getMenuButtonHint().setVisibility(hintVisibility);
   3251 
   3252         // TODO: Consider hiding the hint(s) whenever the menu is onscreen!
   3253         // (Currently, the menu is rendered on top of the hint, but the
   3254         // menu is semitransparent so you can still see the hint
   3255         // underneath, and the hint is *just* visible enough to be
   3256         // distracting.)
   3257     }
   3258 
   3259     /**
   3260      * Brings up UI to handle the various error conditions that
   3261      * can occur when first initializing the in-call UI.
   3262      * This is called from onResume() if we encountered
   3263      * an error while processing our initial Intent.
   3264      *
   3265      * @param status one of the InCallInitStatus error codes.
   3266      */
   3267     private void handleStartupError(InCallInitStatus status) {
   3268         if (DBG) log("handleStartupError(): status = " + status);
   3269 
   3270         // NOTE that the regular Phone UI is in an uninitialized state at
   3271         // this point, so we don't ever want the user to see it.
   3272         // That means:
   3273         // - Any cases here that need to go to some other activity should
   3274         //   call startActivity() AND immediately call endInCallScreenSession
   3275         //   on this one.
   3276         // - Any cases here that bring up a Dialog must ensure that the
   3277         //   Dialog handles both OK *and* cancel by calling endInCallScreenSession.
   3278         //   Activity.  (See showGenericErrorDialog() for an example.)
   3279 
   3280         switch (status) {
   3281 
   3282             case VOICEMAIL_NUMBER_MISSING:
   3283                 // Bring up the "Missing Voicemail Number" dialog, which
   3284                 // will ultimately take us to some other Activity (or else
   3285                 // just bail out of this activity.)
   3286                 handleMissingVoiceMailNumber();
   3287                 break;
   3288 
   3289             case POWER_OFF:
   3290                 // Radio is explictly powered off.
   3291 
   3292                 // TODO: This UI is ultra-simple for 1.0.  It would be nicer
   3293                 // to bring up a Dialog instead with the option "turn on radio
   3294                 // now".  If selected, we'd turn the radio on, wait for
   3295                 // network registration to complete, and then make the call.
   3296 
   3297                 showGenericErrorDialog(R.string.incall_error_power_off, true);
   3298                 break;
   3299 
   3300             case EMERGENCY_ONLY:
   3301                 // Only emergency numbers are allowed, but we tried to dial
   3302                 // a non-emergency number.
   3303                 // (This state is currently unused; see comments above.)
   3304                 showGenericErrorDialog(R.string.incall_error_emergency_only, true);
   3305                 break;
   3306 
   3307             case OUT_OF_SERVICE:
   3308                 // No network connection.
   3309                 showGenericErrorDialog(R.string.incall_error_out_of_service, true);
   3310                 break;
   3311 
   3312             case PHONE_NOT_IN_USE:
   3313                 // This error is handled directly in onResume() (by bailing
   3314                 // out of the activity.)  We should never see it here.
   3315                 Log.w(LOG_TAG,
   3316                       "handleStartupError: unexpected PHONE_NOT_IN_USE status");
   3317                 break;
   3318 
   3319             case NO_PHONE_NUMBER_SUPPLIED:
   3320                 // The supplied Intent didn't contain a valid phone number.
   3321                 // TODO: Need UI spec for this failure case; for now just
   3322                 // show a generic error.
   3323                 showGenericErrorDialog(R.string.incall_error_no_phone_number_supplied, true);
   3324                 break;
   3325 
   3326             case DIALED_MMI:
   3327                 // Our initial phone number was actually an MMI sequence.
   3328                 // There's no real "error" here, but we do bring up the
   3329                 // a Toast (as requested of the New UI paradigm).
   3330                 //
   3331                 // In-call MMIs do not trigger the normal MMI Initiate
   3332                 // Notifications, so we should notify the user here.
   3333                 // Otherwise, the code in PhoneUtils.java should handle
   3334                 // user notifications in the form of Toasts or Dialogs.
   3335                 if (mPhone.getState() == Phone.State.OFFHOOK) {
   3336                     Toast.makeText(this, R.string.incall_status_dialed_mmi, Toast.LENGTH_SHORT)
   3337                         .show();
   3338                 }
   3339                 break;
   3340 
   3341             case CALL_FAILED:
   3342                 // We couldn't successfully place the call; there was some
   3343                 // failure in the telephony layer.
   3344                 // TODO: Need UI spec for this failure case; for now just
   3345                 // show a generic error.
   3346                 showGenericErrorDialog(R.string.incall_error_call_failed, true);
   3347                 break;
   3348 
   3349             default:
   3350                 Log.w(LOG_TAG, "handleStartupError: unexpected status code " + status);
   3351                 showGenericErrorDialog(R.string.incall_error_call_failed, true);
   3352                 break;
   3353         }
   3354     }
   3355 
   3356     /**
   3357      * Utility function to bring up a generic "error" dialog, and then bail
   3358      * out of the in-call UI when the user hits OK (or the BACK button.)
   3359      */
   3360     private void showGenericErrorDialog(int resid, boolean isStartupError) {
   3361         CharSequence msg = getResources().getText(resid);
   3362         if (DBG) log("showGenericErrorDialog('" + msg + "')...");
   3363 
   3364         // create the clicklistener and cancel listener as needed.
   3365         DialogInterface.OnClickListener clickListener;
   3366         OnCancelListener cancelListener;
   3367         if (isStartupError) {
   3368             clickListener = new DialogInterface.OnClickListener() {
   3369                 public void onClick(DialogInterface dialog, int which) {
   3370                     bailOutAfterErrorDialog();
   3371                 }};
   3372             cancelListener = new OnCancelListener() {
   3373                 public void onCancel(DialogInterface dialog) {
   3374                     bailOutAfterErrorDialog();
   3375                 }};
   3376         } else {
   3377             clickListener = new DialogInterface.OnClickListener() {
   3378                 public void onClick(DialogInterface dialog, int which) {
   3379                     delayedCleanupAfterDisconnect();
   3380                 }};
   3381             cancelListener = new OnCancelListener() {
   3382                 public void onCancel(DialogInterface dialog) {
   3383                     delayedCleanupAfterDisconnect();
   3384                 }};
   3385         }
   3386 
   3387         // TODO: Consider adding a setTitle() call here (with some generic
   3388         // "failure" title?)
   3389         mGenericErrorDialog = new AlertDialog.Builder(this)
   3390                 .setMessage(msg)
   3391                 .setPositiveButton(R.string.ok, clickListener)
   3392                 .setOnCancelListener(cancelListener)
   3393                 .create();
   3394 
   3395         // When the dialog is up, completely hide the in-call UI
   3396         // underneath (which is in a partially-constructed state).
   3397         mGenericErrorDialog.getWindow().addFlags(
   3398                 WindowManager.LayoutParams.FLAG_DIM_BEHIND);
   3399 
   3400         mGenericErrorDialog.show();
   3401     }
   3402 
   3403     private void showCallLostDialog() {
   3404         if (DBG) log("showCallLostDialog()...");
   3405 
   3406         // Don't need to show the dialog if InCallScreen isn't in the forgeround
   3407         if (!mIsForegroundActivity) {
   3408             if (DBG) log("showCallLostDialog: not the foreground Activity! Bailing out...");
   3409             return;
   3410         }
   3411 
   3412         // Don't need to show the dialog again, if there is one already.
   3413         if (mCallLostDialog != null) {
   3414             if (DBG) log("showCallLostDialog: There is a mCallLostDialog already.");
   3415             return;
   3416         }
   3417 
   3418         mCallLostDialog = new AlertDialog.Builder(this)
   3419                 .setMessage(R.string.call_lost)
   3420                 .setIcon(android.R.drawable.ic_dialog_alert)
   3421                 .create();
   3422         mCallLostDialog.show();
   3423     }
   3424 
   3425     private void bailOutAfterErrorDialog() {
   3426         if (mGenericErrorDialog != null) {
   3427             if (DBG) log("bailOutAfterErrorDialog: DISMISSING mGenericErrorDialog.");
   3428             mGenericErrorDialog.dismiss();
   3429             mGenericErrorDialog = null;
   3430         }
   3431         if (DBG) log("bailOutAfterErrorDialog(): end InCallScreen session...");
   3432         endInCallScreenSession();
   3433     }
   3434 
   3435     /**
   3436      * Dismisses (and nulls out) all persistent Dialogs managed
   3437      * by the InCallScreen.  Useful if (a) we're about to bring up
   3438      * a dialog and want to pre-empt any currently visible dialogs,
   3439      * or (b) as a cleanup step when the Activity is going away.
   3440      */
   3441     private void dismissAllDialogs() {
   3442         if (DBG) log("dismissAllDialogs()...");
   3443 
   3444         // Note it's safe to dismiss() a dialog that's already dismissed.
   3445         // (Even if the AlertDialog object(s) below are still around, it's
   3446         // possible that the actual dialog(s) may have already been
   3447         // dismissed by the user.)
   3448 
   3449         if (mMissingVoicemailDialog != null) {
   3450             if (VDBG) log("- DISMISSING mMissingVoicemailDialog.");
   3451             mMissingVoicemailDialog.dismiss();
   3452             mMissingVoicemailDialog = null;
   3453         }
   3454         if (mMmiStartedDialog != null) {
   3455             if (VDBG) log("- DISMISSING mMmiStartedDialog.");
   3456             mMmiStartedDialog.dismiss();
   3457             mMmiStartedDialog = null;
   3458         }
   3459         if (mGenericErrorDialog != null) {
   3460             if (VDBG) log("- DISMISSING mGenericErrorDialog.");
   3461             mGenericErrorDialog.dismiss();
   3462             mGenericErrorDialog = null;
   3463         }
   3464         if (mSuppServiceFailureDialog != null) {
   3465             if (VDBG) log("- DISMISSING mSuppServiceFailureDialog.");
   3466             mSuppServiceFailureDialog.dismiss();
   3467             mSuppServiceFailureDialog = null;
   3468         }
   3469         if (mWaitPromptDialog != null) {
   3470             if (VDBG) log("- DISMISSING mWaitPromptDialog.");
   3471             mWaitPromptDialog.dismiss();
   3472             mWaitPromptDialog = null;
   3473         }
   3474         if (mWildPromptDialog != null) {
   3475             if (VDBG) log("- DISMISSING mWildPromptDialog.");
   3476             mWildPromptDialog.dismiss();
   3477             mWildPromptDialog = null;
   3478         }
   3479         if (mCallLostDialog != null) {
   3480             if (VDBG) log("- DISMISSING mCallLostDialog.");
   3481             mCallLostDialog.dismiss();
   3482             mCallLostDialog = null;
   3483         }
   3484         if ((mInCallScreenMode == InCallScreenMode.OTA_NORMAL
   3485                 || mInCallScreenMode == InCallScreenMode.OTA_ENDED)
   3486                 && otaUtils != null) {
   3487             otaUtils.dismissAllOtaDialogs();
   3488         }
   3489         if (mPausePromptDialog != null) {
   3490             if (DBG) log("- DISMISSING mPausePromptDialog.");
   3491             mPausePromptDialog.dismiss();
   3492             mPausePromptDialog = null;
   3493         }
   3494     }
   3495 
   3496 
   3497     //
   3498     // Helper functions for answering incoming calls.
   3499     //
   3500 
   3501     /**
   3502      * Answer a ringing call.  This method does nothing if there's no
   3503      * ringing or waiting call.
   3504      */
   3505     /* package */ void internalAnswerCall() {
   3506         // if (DBG) log("internalAnswerCall()...");
   3507         // if (DBG) PhoneUtils.dumpCallState(mPhone);
   3508 
   3509         final boolean hasRingingCall = !mRingingCall.isIdle();
   3510 
   3511         if (hasRingingCall) {
   3512             int phoneType = mPhone.getPhoneType();
   3513             if (phoneType == Phone.PHONE_TYPE_CDMA) {
   3514                 if (DBG) log("internalAnswerCall: answering (CDMA)...");
   3515                 // In CDMA this is simply a wrapper around PhoneUtils.answerCall().
   3516                 PhoneUtils.answerCall(mPhone);  // Automatically holds the current active call,
   3517                                                 // if there is one
   3518             } else if (phoneType == Phone.PHONE_TYPE_GSM) {
   3519                 // GSM: this is usually just a wrapper around
   3520                 // PhoneUtils.answerCall(), *but* we also need to do
   3521                 // something special for the "both lines in use" case.
   3522 
   3523                 final boolean hasActiveCall = !mForegroundCall.isIdle();
   3524                 final boolean hasHoldingCall = !mBackgroundCall.isIdle();
   3525 
   3526                 if (hasActiveCall && hasHoldingCall) {
   3527                     if (DBG) log("internalAnswerCall: answering (both lines in use!)...");
   3528                     // The relatively rare case where both lines are
   3529                     // already in use.  We "answer incoming, end ongoing"
   3530                     // in this case, according to the current UI spec.
   3531                     PhoneUtils.answerAndEndActive(mPhone);
   3532 
   3533                     // Alternatively, we could use
   3534                     //    PhoneUtils.answerAndEndHolding(mPhone);
   3535                     // here to end the on-hold call instead.
   3536                 } else {
   3537                     if (DBG) log("internalAnswerCall: answering...");
   3538                     PhoneUtils.answerCall(mPhone);  // Automatically holds the current active call,
   3539                                                     // if there is one
   3540                 }
   3541             } else {
   3542                 throw new IllegalStateException("Unexpected phone type: " + phoneType);
   3543             }
   3544         }
   3545     }
   3546 
   3547     /**
   3548      * Answer the ringing call *and* hang up the ongoing call.
   3549      */
   3550     /* package */ void internalAnswerAndEnd() {
   3551         if (DBG) log("internalAnswerAndEnd()...");
   3552         // if (DBG) PhoneUtils.dumpCallState(mPhone);
   3553         PhoneUtils.answerAndEndActive(mPhone);
   3554     }
   3555 
   3556     /**
   3557      * Hang up the ringing call (aka "Don't answer").
   3558      */
   3559     /* package */ void internalHangupRingingCall() {
   3560         if (DBG) log("internalHangupRingingCall()...");
   3561         PhoneUtils.hangupRingingCall(mPhone);
   3562     }
   3563 
   3564     /**
   3565      * Hang up the current active call.
   3566      */
   3567     /* package */ void internalHangup() {
   3568         if (DBG) log("internalHangup()...");
   3569         PhoneUtils.hangup(mPhone);
   3570     }
   3571 
   3572     /**
   3573      * InCallScreen-specific wrapper around PhoneUtils.switchHoldingAndActive().
   3574      */
   3575     private void internalSwapCalls() {
   3576         if (DBG) log("internalSwapCalls()...");
   3577 
   3578         // Any time we swap calls, force the DTMF dialpad to close.
   3579         // (We want the regular in-call UI to be visible right now, so the
   3580         // user can clearly see which call is now in the foreground.)
   3581         mDialer.closeDialer(true);  // do the "closing" animation
   3582 
   3583         // Also, clear out the "history" of DTMF digits you typed, to make
   3584         // sure you don't see digits from call #1 while call #2 is active.
   3585         // (Yes, this does mean that swapping calls twice will cause you
   3586         // to lose any previous digits from the current call; see the TODO
   3587         // comment on DTMFTwelvKeyDialer.clearDigits() for more info.)
   3588         mDialer.clearDigits();
   3589 
   3590         // Swap the fg and bg calls.
   3591         PhoneUtils.switchHoldingAndActive(mPhone);
   3592 
   3593         // If we have a valid BluetoothHandsfree then since CDMA network or
   3594         // Telephony FW does not send us information on which caller got swapped
   3595         // we need to update the second call active state in BluetoothHandsfree internally
   3596         if (mPhone.getPhoneType() == Phone.PHONE_TYPE_CDMA) {
   3597             BluetoothHandsfree bthf = PhoneApp.getInstance().getBluetoothHandsfree();
   3598             if (bthf != null) {
   3599                 bthf.cdmaSwapSecondCallState();
   3600             }
   3601         }
   3602 
   3603     }
   3604 
   3605     /**
   3606      * Sets the current high-level "mode" of the in-call UI.
   3607      *
   3608      * NOTE: if newMode is CALL_ENDED, the caller is responsible for
   3609      * posting a delayed DELAYED_CLEANUP_AFTER_DISCONNECT message, to make
   3610      * sure the "call ended" state goes away after a couple of seconds.
   3611      */
   3612     private void setInCallScreenMode(InCallScreenMode newMode) {
   3613         if (DBG) log("setInCallScreenMode: " + newMode);
   3614         mInCallScreenMode = newMode;
   3615         switch (mInCallScreenMode) {
   3616             case MANAGE_CONFERENCE:
   3617                 if (!PhoneUtils.isConferenceCall(mForegroundCall)) {
   3618                     Log.w(LOG_TAG, "MANAGE_CONFERENCE: no active conference call!");
   3619                     // Hide the Manage Conference panel, return to NORMAL mode.
   3620                     setInCallScreenMode(InCallScreenMode.NORMAL);
   3621                     return;
   3622                 }
   3623                 List<Connection> connections = mForegroundCall.getConnections();
   3624                 // There almost certainly will be > 1 connection,
   3625                 // since isConferenceCall() just returned true.
   3626                 if ((connections == null) || (connections.size() <= 1)) {
   3627                     Log.w(LOG_TAG,
   3628                           "MANAGE_CONFERENCE: Bogus TRUE from isConferenceCall(); connections = "
   3629                           + connections);
   3630                     // Hide the Manage Conference panel, return to NORMAL mode.
   3631                     setInCallScreenMode(InCallScreenMode.NORMAL);
   3632                     return;
   3633                 }
   3634 
   3635                 // TODO: Don't do this here. The call to
   3636                 // initManageConferencePanel() should instead happen
   3637                 // automagically in ManageConferenceUtils the very first
   3638                 // time you call updateManageConferencePanel() or
   3639                 // setPanelVisible(true).
   3640                 mManageConferenceUtils.initManageConferencePanel();  // if necessary
   3641 
   3642                 mManageConferenceUtils.updateManageConferencePanel(connections);
   3643 
   3644                 // The "Manage conference" UI takes up the full main frame,
   3645                 // replacing the inCallPanel and CallCard PopupWindow.
   3646                 mManageConferenceUtils.setPanelVisible(true);
   3647 
   3648                 // Start the chronometer.
   3649                 // TODO: Similarly, we shouldn't expose startConferenceTime()
   3650                 // and stopConferenceTime(); the ManageConferenceUtils
   3651                 // class ought to manage the conferenceTime widget itself
   3652                 // based on setPanelVisible() calls.
   3653                 long callDuration = mForegroundCall.getEarliestConnection().getDurationMillis();
   3654                 mManageConferenceUtils.startConferenceTime(
   3655                         SystemClock.elapsedRealtime() - callDuration);
   3656 
   3657                 mInCallPanel.setVisibility(View.GONE);
   3658 
   3659                 // No need to close the dialer here, since the Manage
   3660                 // Conference UI will just cover it up anyway.
   3661 
   3662                 break;
   3663 
   3664             case CALL_ENDED:
   3665                 // Display the CallCard (in the "Call ended" state)
   3666                 // and hide all other UI.
   3667 
   3668                 mManageConferenceUtils.setPanelVisible(false);
   3669                 mManageConferenceUtils.stopConferenceTime();
   3670 
   3671                 updateMenuButtonHint();  // Hide the Menu button hint
   3672 
   3673                 // Make sure the CallCard (which is a child of mInCallPanel) is visible.
   3674                 mInCallPanel.setVisibility(View.VISIBLE);
   3675 
   3676                 break;
   3677 
   3678             case NORMAL:
   3679                 mInCallPanel.setVisibility(View.VISIBLE);
   3680                 mManageConferenceUtils.setPanelVisible(false);
   3681                 mManageConferenceUtils.stopConferenceTime();
   3682                 break;
   3683 
   3684             case OTA_NORMAL:
   3685                 otaUtils.setCdmaOtaInCallScreenUiState(
   3686                         OtaUtils.CdmaOtaInCallScreenUiState.State.NORMAL);
   3687                 mInCallPanel.setVisibility(View.GONE);
   3688                 break;
   3689 
   3690             case OTA_ENDED:
   3691                 otaUtils.setCdmaOtaInCallScreenUiState(
   3692                         OtaUtils.CdmaOtaInCallScreenUiState.State.ENDED);
   3693                 mInCallPanel.setVisibility(View.GONE);
   3694                 break;
   3695 
   3696             case UNDEFINED:
   3697                 // Set our Activities intent to ACTION_UNDEFINED so
   3698                 // that if we get resumed after we've completed a call
   3699                 // the next call will not cause checkIsOtaCall to
   3700                 // return true.
   3701                 //
   3702                 // With the framework as of October 2009 the sequence below
   3703                 // causes the framework to call onResume, onPause, onNewIntent,
   3704                 // onResume. If we don't call setIntent below then when the
   3705                 // first onResume calls checkIsOtaCall via initOtaState it will
   3706                 // return true and the Activity will be confused.
   3707                 //
   3708                 //  1) Power up Phone A
   3709                 //  2) Place *22899 call and activate Phone A
   3710                 //  3) Press the power key on Phone A to turn off the display
   3711                 //  4) Call Phone A from Phone B answering Phone A
   3712                 //  5) The screen will be blank (Should be normal InCallScreen)
   3713                 //  6) Hang up the Phone B
   3714                 //  7) Phone A displays the activation screen.
   3715                 //
   3716                 // Step 3 is the critical step to cause the onResume, onPause
   3717                 // onNewIntent, onResume sequence. If step 3 is skipped the
   3718                 // sequence will be onNewIntent, onResume and all will be well.
   3719                 setIntent(new Intent(ACTION_UNDEFINED));
   3720 
   3721                 // Cleanup Ota Screen if necessary and set the panel
   3722                 // to VISIBLE.
   3723                 if (mPhone.getState() != Phone.State.OFFHOOK) {
   3724                     if (otaUtils != null) {
   3725                         otaUtils.cleanOtaScreen(true);
   3726                     }
   3727                 } else {
   3728                     log("WARNING: Setting mode to UNDEFINED but phone is OFFHOOK,"
   3729                             + " skip cleanOtaScreen.");
   3730                 }
   3731                 mInCallPanel.setVisibility(View.VISIBLE);
   3732                 break;
   3733         }
   3734 
   3735         // Update the visibility of the DTMF dialer tab on any state
   3736         // change.
   3737         updateDialpadVisibility();
   3738 
   3739         // Update the in-call touch UI on any state change (since it may
   3740         // need to hide or re-show itself.)
   3741         updateInCallTouchUi();
   3742     }
   3743 
   3744     /**
   3745      * @return true if the "Manage conference" UI is currently visible.
   3746      */
   3747     /* package */ boolean isManageConferenceMode() {
   3748         return (mInCallScreenMode == InCallScreenMode.MANAGE_CONFERENCE);
   3749     }
   3750 
   3751     /**
   3752      * Checks if the "Manage conference" UI needs to be updated.
   3753      * If the state of the current conference call has changed
   3754      * since our previous call to updateManageConferencePanel()),
   3755      * do a fresh update.  Also, if the current call is no longer a
   3756      * conference call at all, bail out of the "Manage conference" UI and
   3757      * return to InCallScreenMode.NORMAL mode.
   3758      */
   3759     private void updateManageConferencePanelIfNecessary() {
   3760         if (VDBG) log("updateManageConferencePanelIfNecessary: " + mForegroundCall + "...");
   3761 
   3762         List<Connection> connections = mForegroundCall.getConnections();
   3763         if (connections == null) {
   3764             if (VDBG) log("==> no connections on foreground call!");
   3765             // Hide the Manage Conference panel, return to NORMAL mode.
   3766             setInCallScreenMode(InCallScreenMode.NORMAL);
   3767             InCallInitStatus status = syncWithPhoneState();
   3768             if (status != InCallInitStatus.SUCCESS) {
   3769                 Log.w(LOG_TAG, "- syncWithPhoneState failed! status = " + status);
   3770                 // We shouldn't even be in the in-call UI in the first
   3771                 // place, so bail out:
   3772                 if (DBG) log("updateManageConferencePanelIfNecessary: endInCallScreenSession... 1");
   3773                 endInCallScreenSession();
   3774                 return;
   3775             }
   3776             return;
   3777         }
   3778 
   3779         int numConnections = connections.size();
   3780         if (numConnections <= 1) {
   3781             if (VDBG) log("==> foreground call no longer a conference!");
   3782             // Hide the Manage Conference panel, return to NORMAL mode.
   3783             setInCallScreenMode(InCallScreenMode.NORMAL);
   3784             InCallInitStatus status = syncWithPhoneState();
   3785             if (status != InCallInitStatus.SUCCESS) {
   3786                 Log.w(LOG_TAG, "- syncWithPhoneState failed! status = " + status);
   3787                 // We shouldn't even be in the in-call UI in the first
   3788                 // place, so bail out:
   3789                 if (DBG) log("updateManageConferencePanelIfNecessary: endInCallScreenSession... 2");
   3790                 endInCallScreenSession();
   3791                 return;
   3792             }
   3793             return;
   3794         }
   3795 
   3796         // TODO: the test to see if numConnections has changed can go in
   3797         // updateManageConferencePanel(), rather than here.
   3798         if (numConnections != mManageConferenceUtils.getNumCallersInConference()) {
   3799             if (VDBG) log("==> Conference size has changed; need to rebuild UI!");
   3800             mManageConferenceUtils.updateManageConferencePanel(connections);
   3801         }
   3802     }
   3803 
   3804     /**
   3805      * Updates the visibility of the DTMF dialpad (and its onscreen
   3806      * "handle", if applicable), based on the current state of the phone
   3807      * and/or the current InCallScreenMode.
   3808      */
   3809     private void updateDialpadVisibility() {
   3810         //
   3811         // (1) The dialpad itself:
   3812         //
   3813         // If an incoming call is ringing, make sure the dialpad is
   3814         // closed.  (We do this to make sure we're not covering up the
   3815         // "incoming call" UI, and especially to make sure that the "touch
   3816         // lock" overlay won't appear.)
   3817         if (mPhone.getState() == Phone.State.RINGING) {
   3818             mDialer.closeDialer(false);  // don't do the "closing" animation
   3819 
   3820             // Also, clear out the "history" of DTMF digits you may have typed
   3821             // into the previous call (so you don't see the previous call's
   3822             // digits if you answer this call and then bring up the dialpad.)
   3823             //
   3824             // TODO: it would be more precise to do this when you *answer* the
   3825             // incoming call, rather than as soon as it starts ringing, but
   3826             // the InCallScreen doesn't keep enough state right now to notice
   3827             // that specific transition in onPhoneStateChanged().
   3828             mDialer.clearDigits();
   3829         }
   3830 
   3831         //
   3832         // (2) The onscreen "handle":
   3833         //
   3834         // The handle is visible only if it's OK to actually open the
   3835         // dialpad.  (Note this is meaningful only on platforms that use a
   3836         // SlidingDrawer as a container for the dialpad.)
   3837         mDialer.setHandleVisible(okToShowDialpad());
   3838 
   3839         //
   3840         // (3) The main in-call panel (containing the CallCard):
   3841         //
   3842         // On some platforms(*) we need to hide the CallCard (which is a
   3843         // child of mInCallPanel) while the dialpad is visible.
   3844         //
   3845         // (*) We need to do this when using the dialpad from the
   3846         //     InCallTouchUi widget, but not when using the
   3847         //     SlidingDrawer-based dialpad, because the SlidingDrawer itself
   3848         //     is opaque.)
   3849         if (!mDialer.usingSlidingDrawer()) {
   3850             if (mDialerView != null) {
   3851                 mDialerView.setKeysBackgroundResource(
   3852                         isBluetoothAudioConnected() ? R.drawable.btn_dial_blue
   3853                         : R.drawable.btn_dial_green);
   3854             }
   3855 
   3856             if (isDialerOpened()) {
   3857                 mInCallPanel.setVisibility(View.GONE);
   3858             } else {
   3859                 // Dialpad is dismissed; bring back the CallCard if
   3860                 // it's supposed to be visible.
   3861                 if ((mInCallScreenMode == InCallScreenMode.NORMAL)
   3862                     || (mInCallScreenMode == InCallScreenMode.CALL_ENDED)) {
   3863                     mInCallPanel.setVisibility(View.VISIBLE);
   3864                 }
   3865             }
   3866         }
   3867     }
   3868 
   3869     /**
   3870      * @return true if the DTMF dialpad is currently visible.
   3871      */
   3872     /* package */ boolean isDialerOpened() {
   3873         return (mDialer != null && mDialer.isOpened());
   3874     }
   3875 
   3876     /**
   3877      * Called any time the DTMF dialpad is opened.
   3878      * @see DTMFTwelveKeyDialer.onDialerOpen()
   3879      */
   3880     /* package */ void onDialerOpen() {
   3881         if (DBG) log("onDialerOpen()...");
   3882 
   3883         // ANY time the dialpad becomes visible, start the timer that will
   3884         // eventually bring up the "touch lock" overlay.
   3885         resetTouchLockTimer();
   3886 
   3887         // Update the in-call touch UI (which may need to hide itself, if
   3888         // it's enabled.)
   3889         updateInCallTouchUi();
   3890 
   3891         // Update any other onscreen UI elements that depend on the dialpad.
   3892         updateDialpadVisibility();
   3893 
   3894         // This counts as explicit "user activity".
   3895         PhoneApp.getInstance().pokeUserActivity();
   3896 
   3897         //If on OTA Call, hide OTA Screen
   3898         // TODO: This may not be necessary, now that the dialpad is
   3899         // always visible in OTA mode.
   3900         if  ((mInCallScreenMode == InCallScreenMode.OTA_NORMAL
   3901                 || mInCallScreenMode == InCallScreenMode.OTA_ENDED)
   3902                 && otaUtils != null) {
   3903             otaUtils.hideOtaScreen();
   3904         }
   3905     }
   3906 
   3907     /**
   3908      * Called any time the DTMF dialpad is closed.
   3909      * @see DTMFTwelveKeyDialer.onDialerClose()
   3910      */
   3911     /* package */ void onDialerClose() {
   3912         if (DBG) log("onDialerClose()...");
   3913 
   3914         final PhoneApp app = PhoneApp.getInstance();
   3915 
   3916         // OTA-specific cleanup upon closing the dialpad.
   3917         if ((mInCallScreenMode == InCallScreenMode.OTA_NORMAL)
   3918             || (mInCallScreenMode == InCallScreenMode.OTA_ENDED)
   3919             || ((app.cdmaOtaScreenState != null)
   3920                 && (app.cdmaOtaScreenState.otaScreenState ==
   3921                     CdmaOtaScreenState.OtaScreenState.OTA_STATUS_ACTIVATION))) {
   3922             mDialer.setHandleVisible(false);
   3923             if (otaUtils != null) {
   3924                 otaUtils.otaShowProperScreen();
   3925             }
   3926         }
   3927 
   3928         // Dismiss the "touch lock" overlay if it was visible.
   3929         // (The overlay is only ever used on top of the dialpad).
   3930         enableTouchLock(false);
   3931 
   3932         // Update the in-call touch UI (which may need to re-show itself.)
   3933         updateInCallTouchUi();
   3934 
   3935         // Update the visibility of the dialpad itself (and any other
   3936         // onscreen UI elements that depend on it.)
   3937         updateDialpadVisibility();
   3938 
   3939         // This counts as explicit "user activity".
   3940         app.getInstance().pokeUserActivity();
   3941     }
   3942 
   3943     /**
   3944      * Determines when we can dial DTMF tones.
   3945      */
   3946     private boolean okToDialDTMFTones() {
   3947         final boolean hasRingingCall = !mRingingCall.isIdle();
   3948         final Call.State fgCallState = mForegroundCall.getState();
   3949 
   3950         // We're allowed to send DTMF tones when there's an ACTIVE
   3951         // foreground call, and not when an incoming call is ringing
   3952         // (since DTMF tones are useless in that state), or if the
   3953         // Manage Conference UI is visible (since the tab interferes
   3954         // with the "Back to call" button.)
   3955 
   3956         // We can also dial while in ALERTING state because there are
   3957         // some connections that never update to an ACTIVE state (no
   3958         // indication from the network).
   3959         boolean canDial =
   3960             (fgCallState == Call.State.ACTIVE || fgCallState == Call.State.ALERTING)
   3961             && !hasRingingCall
   3962             && (mInCallScreenMode != InCallScreenMode.MANAGE_CONFERENCE);
   3963 
   3964         if (VDBG) log ("[okToDialDTMFTones] foreground state: " + fgCallState +
   3965                 ", ringing state: " + hasRingingCall +
   3966                 ", call screen mode: " + mInCallScreenMode +
   3967                 ", result: " + canDial);
   3968 
   3969         return canDial;
   3970     }
   3971 
   3972     /**
   3973      * @return true if the in-call DTMF dialpad should be available to the
   3974      *      user, given the current state of the phone and the in-call UI.
   3975      *      (This is used to control the visibility of the dialer's
   3976      *      onscreen handle, if applicable, and the enabledness of the "Show
   3977      *      dialpad" onscreen button or menu item.)
   3978      */
   3979     /* package */ boolean okToShowDialpad() {
   3980         // The dialpad is available only when it's OK to dial DTMF
   3981         // tones given the current state of the current call.
   3982         return okToDialDTMFTones();
   3983     }
   3984 
   3985     /**
   3986      * Initializes the in-call touch UI on devices that need it.
   3987      */
   3988     private void initInCallTouchUi() {
   3989         if (DBG) log("initInCallTouchUi()...");
   3990         // TODO: we currently use the InCallTouchUi widget in at least
   3991         // some states on ALL platforms.  But if some devices ultimately
   3992         // end up not using *any* onscreen touch UI, we should make sure
   3993         // to not even inflate the InCallTouchUi widget on those devices.
   3994         mInCallTouchUi = (InCallTouchUi) findViewById(R.id.inCallTouchUi);
   3995         mInCallTouchUi.setInCallScreenInstance(this);
   3996     }
   3997 
   3998     /**
   3999      * Updates the state of the in-call touch UI.
   4000      */
   4001     private void updateInCallTouchUi() {
   4002         if (mInCallTouchUi != null) {
   4003             mInCallTouchUi.updateState(mPhone);
   4004         }
   4005     }
   4006 
   4007     /**
   4008      * @return true if the onscreen touch UI is enabled (for regular
   4009      * "ongoing call" states) on the current device.
   4010      */
   4011     public boolean isTouchUiEnabled() {
   4012         return (mInCallTouchUi != null) && mInCallTouchUi.isTouchUiEnabled();
   4013     }
   4014 
   4015     /**
   4016      * @return true if the onscreen touch UI is enabled for
   4017      * the "incoming call" state on the current device.
   4018      */
   4019     public boolean isIncomingCallTouchUiEnabled() {
   4020         return (mInCallTouchUi != null) && mInCallTouchUi.isIncomingCallTouchUiEnabled();
   4021     }
   4022 
   4023     /**
   4024      * Posts a handler message telling the InCallScreen to update the
   4025      * onscreen in-call touch UI.
   4026      *
   4027      * This is just a wrapper around updateInCallTouchUi(), for use by the
   4028      * rest of the phone app or from a thread other than the UI thread.
   4029      */
   4030     /* package */ void requestUpdateTouchUi() {
   4031         if (DBG) log("requestUpdateTouchUi()...");
   4032 
   4033         mHandler.removeMessages(REQUEST_UPDATE_TOUCH_UI);
   4034         mHandler.sendEmptyMessage(REQUEST_UPDATE_TOUCH_UI);
   4035     }
   4036 
   4037     /**
   4038      * @return true if it's OK to display the in-call touch UI, given the
   4039      * current state of the InCallScreen.
   4040      */
   4041     /* package */ boolean okToShowInCallTouchUi() {
   4042         // Note that this method is concerned only with the internal state
   4043         // of the InCallScreen.  (The InCallTouchUi widget has separate
   4044         // logic to make sure it's OK to display the touch UI given the
   4045         // current telephony state, and that it's allowed on the current
   4046         // device in the first place.)
   4047 
   4048         // The touch UI is NOT available if:
   4049         // - we're in some InCallScreenMode other than NORMAL
   4050         //   (like CALL_ENDED or one of the OTA modes)
   4051         return (mInCallScreenMode == InCallScreenMode.NORMAL);
   4052     }
   4053 
   4054     /**
   4055      * @return true if we're in restricted / emergency dialing only mode.
   4056      */
   4057     public boolean isPhoneStateRestricted() {
   4058         // TODO:  This needs to work IN TANDEM with the KeyGuardViewMediator Code.
   4059         // Right now, it looks like the mInputRestricted flag is INTERNAL to the
   4060         // KeyGuardViewMediator and SPECIFICALLY set to be FALSE while the emergency
   4061         // phone call is being made, to allow for input into the InCallScreen.
   4062         // Having the InCallScreen judge the state of the device from this flag
   4063         // becomes meaningless since it is always false for us.  The mediator should
   4064         // have an additional API to let this app know that it should be restricted.
   4065         return ((mPhone.getServiceState().getState() == ServiceState.STATE_EMERGENCY_ONLY) ||
   4066                 (mPhone.getServiceState().getState() == ServiceState.STATE_OUT_OF_SERVICE) ||
   4067                 (PhoneApp.getInstance().getKeyguardManager().inKeyguardRestrictedInputMode()));
   4068     }
   4069 
   4070     //
   4071     // In-call menu UI
   4072     //
   4073 
   4074     /**
   4075      * Override onCreatePanelView(), in order to get complete control
   4076      * over the UI that comes up when the user presses MENU.
   4077      *
   4078      * This callback allows me to return a totally custom View hierarchy
   4079      * (with custom layout and custom "item" views) to be shown instead
   4080      * of a standard android.view.Menu hierarchy.
   4081      *
   4082      * This gets called (with featureId == FEATURE_OPTIONS_PANEL) every
   4083      * time we need to bring up the menu.  (And in cases where we return
   4084      * non-null, that means that the "standard" menu callbacks
   4085      * onCreateOptionsMenu() and onPrepareOptionsMenu() won't get called
   4086      * at all.)
   4087      */
   4088     @Override
   4089     public View onCreatePanelView(int featureId) {
   4090         if (VDBG) log("onCreatePanelView(featureId = " + featureId + ")...");
   4091 
   4092         // We only want this special behavior for the "options panel"
   4093         // feature (i.e. the standard menu triggered by the MENU button.)
   4094         if (featureId != Window.FEATURE_OPTIONS_PANEL) {
   4095             return null;
   4096         }
   4097 
   4098         // For now, totally disable the in-call menu on devices where we
   4099         // use onscreen touchable buttons instead.
   4100         // TODO: even on "full touch" devices we may still ultimately need
   4101         // a regular menu in some states.  Need UI spec.
   4102         if (isTouchUiEnabled()) {
   4103             return null;
   4104         }
   4105 
   4106         // TODO: May need to revisit the wake state here if this needs to be
   4107         // tweaked.
   4108 
   4109         // Make sure there are no pending messages to *dismiss* the menu.
   4110         mHandler.removeMessages(DISMISS_MENU);
   4111 
   4112         if (mInCallMenu == null) {
   4113             if (VDBG) log("onCreatePanelView: creating mInCallMenu (first time)...");
   4114             mInCallMenu = new InCallMenu(this);
   4115             mInCallMenu.initMenu();
   4116         }
   4117 
   4118         boolean okToShowMenu = mInCallMenu.updateItems(mPhone);
   4119         return okToShowMenu ? mInCallMenu.getView() : null;
   4120     }
   4121 
   4122     /**
   4123      * Dismisses the menu panel (see onCreatePanelView().)
   4124      *
   4125      * @param dismissImmediate If true, hide the panel immediately.
   4126      *            If false, leave the menu visible onscreen for
   4127      *            a brief interval before dismissing it (so the
   4128      *            user can see the state change resulting from
   4129      *            his original click.)
   4130      */
   4131     /* package */ void dismissMenu(boolean dismissImmediate) {
   4132         if (VDBG) log("dismissMenu(immediate = " + dismissImmediate + ")...");
   4133 
   4134         if (dismissImmediate) {
   4135             closeOptionsMenu();
   4136         } else {
   4137             mHandler.removeMessages(DISMISS_MENU);
   4138             mHandler.sendEmptyMessageDelayed(DISMISS_MENU, MENU_DISMISS_DELAY);
   4139             // This will result in a dismissMenu(true) call shortly.
   4140         }
   4141     }
   4142 
   4143     /**
   4144      * Override onPanelClosed() to capture the panel closing event,
   4145      * allowing us to set the poke lock correctly whenever the option
   4146      * menu panel goes away.
   4147      */
   4148     @Override
   4149     public void onPanelClosed(int featureId, Menu menu) {
   4150         if (VDBG) log("onPanelClosed(featureId = " + featureId + ")...");
   4151 
   4152         // We only want this special behavior for the "options panel"
   4153         // feature (i.e. the standard menu triggered by the MENU button.)
   4154         if (featureId == Window.FEATURE_OPTIONS_PANEL) {
   4155             // TODO: May need to return to the original wake state here
   4156             // if onCreatePanelView ends up changing the wake state.
   4157         }
   4158 
   4159         super.onPanelClosed(featureId, menu);
   4160     }
   4161 
   4162     //
   4163     // Bluetooth helper methods.
   4164     //
   4165     // - BluetoothAdapter is the Bluetooth system service.  If
   4166     //   getDefaultAdapter() returns null
   4167     //   then the device is not BT capable.  Use BluetoothDevice.isEnabled()
   4168     //   to see if BT is enabled on the device.
   4169     //
   4170     // - BluetoothHeadset is the API for the control connection to a
   4171     //   Bluetooth Headset.  This lets you completely connect/disconnect a
   4172     //   headset (which we don't do from the Phone UI!) but also lets you
   4173     //   get the address of the currently active headset and see whether
   4174     //   it's currently connected.
   4175     //
   4176     // - BluetoothHandsfree is the API to control the audio connection to
   4177     //   a bluetooth headset. We use this API to switch the headset on and
   4178     //   off when the user presses the "Bluetooth" button.
   4179     //   Our BluetoothHandsfree instance (mBluetoothHandsfree) is created
   4180     //   by the PhoneApp and will be null if the device is not BT capable.
   4181     //
   4182 
   4183     /**
   4184      * @return true if the Bluetooth on/off switch in the UI should be
   4185      *         available to the user (i.e. if the device is BT-capable
   4186      *         and a headset is connected.)
   4187      */
   4188     /* package */ boolean isBluetoothAvailable() {
   4189         if (VDBG) log("isBluetoothAvailable()...");
   4190         if (mBluetoothHandsfree == null) {
   4191             // Device is not BT capable.
   4192             if (VDBG) log("  ==> FALSE (not BT capable)");
   4193             return false;
   4194         }
   4195 
   4196         // There's no need to ask the Bluetooth system service if BT is enabled:
   4197         //
   4198         //    BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
   4199         //    if ((adapter == null) || !adapter.isEnabled()) {
   4200         //        if (DBG) log("  ==> FALSE (BT not enabled)");
   4201         //        return false;
   4202         //    }
   4203         //    if (DBG) log("  - BT enabled!  device name " + adapter.getName()
   4204         //                 + ", address " + adapter.getAddress());
   4205         //
   4206         // ...since we already have a BluetoothHeadset instance.  We can just
   4207         // call isConnected() on that, and assume it'll be false if BT isn't
   4208         // enabled at all.
   4209 
   4210         // Check if there's a connected headset, using the BluetoothHeadset API.
   4211         boolean isConnected = false;
   4212         if (mBluetoothHeadset != null) {
   4213             if (VDBG) log("  - headset state = " + mBluetoothHeadset.getState());
   4214             BluetoothDevice headset = mBluetoothHeadset.getCurrentHeadset();
   4215             if (VDBG) log("  - headset address: " + headset);
   4216             if (headset != null) {
   4217                 isConnected = mBluetoothHeadset.isConnected(headset);
   4218                 if (VDBG) log("  - isConnected: " + isConnected);
   4219             }
   4220         }
   4221 
   4222         if (VDBG) log("  ==> " + isConnected);
   4223         return isConnected;
   4224     }
   4225 
   4226     /**
   4227      * @return true if a BT device is available, and its audio is currently connected.
   4228      */
   4229     /* package */ boolean isBluetoothAudioConnected() {
   4230         if (mBluetoothHandsfree == null) {
   4231             if (VDBG) log("isBluetoothAudioConnected: ==> FALSE (null mBluetoothHandsfree)");
   4232             return false;
   4233         }
   4234         boolean isAudioOn = mBluetoothHandsfree.isAudioOn();
   4235         if (VDBG) log("isBluetoothAudioConnected: ==> isAudioOn = " + isAudioOn);
   4236         return isAudioOn;
   4237     }
   4238 
   4239     /**
   4240      * Helper method used to control the state of the green LED in the
   4241      * "Bluetooth" menu item.
   4242      *
   4243      * @return true if a BT device is available and its audio is currently connected,
   4244      *              <b>or</b> if we issued a BluetoothHandsfree.userWantsAudioOn()
   4245      *              call within the last 5 seconds (which presumably means
   4246      *              that the BT audio connection is currently being set
   4247      *              up, and will be connected soon.)
   4248      */
   4249     /* package */ boolean isBluetoothAudioConnectedOrPending() {
   4250         if (isBluetoothAudioConnected()) {
   4251             if (VDBG) log("isBluetoothAudioConnectedOrPending: ==> TRUE (really connected)");
   4252             return true;
   4253         }
   4254 
   4255         // If we issued a userWantsAudioOn() call "recently enough", even
   4256         // if BT isn't actually connected yet, let's still pretend BT is
   4257         // on.  This is how we make the green LED in the menu item turn on
   4258         // right away.
   4259         if (mBluetoothConnectionPending) {
   4260             long timeSinceRequest =
   4261                     SystemClock.elapsedRealtime() - mBluetoothConnectionRequestTime;
   4262             if (timeSinceRequest < 5000 /* 5 seconds */) {
   4263                 if (VDBG) log("isBluetoothAudioConnectedOrPending: ==> TRUE (requested "
   4264                              + timeSinceRequest + " msec ago)");
   4265                 return true;
   4266             } else {
   4267                 if (VDBG) log("isBluetoothAudioConnectedOrPending: ==> FALSE (request too old: "
   4268                              + timeSinceRequest + " msec ago)");
   4269                 mBluetoothConnectionPending = false;
   4270                 return false;
   4271             }
   4272         }
   4273 
   4274         if (VDBG) log("isBluetoothAudioConnectedOrPending: ==> FALSE");
   4275         return false;
   4276     }
   4277 
   4278     /**
   4279      * Posts a message to our handler saying to update the onscreen UI
   4280      * based on a bluetooth headset state change.
   4281      */
   4282     /* package */ void requestUpdateBluetoothIndication() {
   4283         if (VDBG) log("requestUpdateBluetoothIndication()...");
   4284         // No need to look at the current state here; any UI elements that
   4285         // care about the bluetooth state (i.e. the CallCard) get
   4286         // the necessary state directly from PhoneApp.showBluetoothIndication().
   4287         mHandler.removeMessages(REQUEST_UPDATE_BLUETOOTH_INDICATION);
   4288         mHandler.sendEmptyMessage(REQUEST_UPDATE_BLUETOOTH_INDICATION);
   4289     }
   4290 
   4291     private void dumpBluetoothState() {
   4292         log("============== dumpBluetoothState() =============");
   4293         log("= isBluetoothAvailable: " + isBluetoothAvailable());
   4294         log("= isBluetoothAudioConnected: " + isBluetoothAudioConnected());
   4295         log("= isBluetoothAudioConnectedOrPending: " + isBluetoothAudioConnectedOrPending());
   4296         log("= PhoneApp.showBluetoothIndication: "
   4297             + PhoneApp.getInstance().showBluetoothIndication());
   4298         log("=");
   4299         if (mBluetoothHandsfree != null) {
   4300             log("= BluetoothHandsfree.isAudioOn: " + mBluetoothHandsfree.isAudioOn());
   4301             if (mBluetoothHeadset != null) {
   4302                 BluetoothDevice headset = mBluetoothHeadset.getCurrentHeadset();
   4303                 log("= BluetoothHeadset.getCurrentHeadset: " + headset);
   4304                 if (headset != null) {
   4305                     log("= BluetoothHeadset.isConnected: "
   4306                         + mBluetoothHeadset.isConnected(headset));
   4307                 }
   4308             } else {
   4309                 log("= mBluetoothHeadset is null");
   4310             }
   4311         } else {
   4312             log("= mBluetoothHandsfree is null; device is not BT capable");
   4313         }
   4314     }
   4315 
   4316     /* package */ void connectBluetoothAudio() {
   4317         if (VDBG) log("connectBluetoothAudio()...");
   4318         if (mBluetoothHandsfree != null) {
   4319             mBluetoothHandsfree.userWantsAudioOn();
   4320         }
   4321 
   4322         // Watch out: The bluetooth connection doesn't happen instantly;
   4323         // the userWantsAudioOn() call returns instantly but does its real
   4324         // work in another thread.  Also, in practice the BT connection
   4325         // takes longer than MENU_DISMISS_DELAY to complete(!) so we need
   4326         // a little trickery here to make the menu item's green LED update
   4327         // instantly.
   4328         // (See isBluetoothAudioConnectedOrPending() above.)
   4329         mBluetoothConnectionPending = true;
   4330         mBluetoothConnectionRequestTime = SystemClock.elapsedRealtime();
   4331     }
   4332 
   4333     /* package */ void disconnectBluetoothAudio() {
   4334         if (VDBG) log("disconnectBluetoothAudio()...");
   4335         if (mBluetoothHandsfree != null) {
   4336             mBluetoothHandsfree.userWantsAudioOff();
   4337         }
   4338         mBluetoothConnectionPending = false;
   4339     }
   4340 
   4341     //
   4342     // "Touch lock" UI.
   4343     //
   4344     // When the DTMF dialpad is up, after a certain amount of idle time we
   4345     // display an overlay graphic on top of the dialpad and "lock" the
   4346     // touch UI.  (UI Rationale: We need *some* sort of screen lock, with
   4347     // a fairly short timeout, to avoid false touches from the user's face
   4348     // while in-call.  But we *don't* want to do this by turning off the
   4349     // screen completely, since that's confusing (the user can't tell
   4350     // what's going on) *and* it's fairly cumbersome to have to hit MENU
   4351     // to bring the screen back, rather than using some gesture on the
   4352     // touch screen.)
   4353     //
   4354     // The user can dismiss the touch lock overlay by double-tapping on
   4355     // the central "lock" icon.  Also, the touch lock overlay will go away
   4356     // by itself if the DTMF dialpad is dismissed for any reason, such as
   4357     // the current call getting disconnected (see onDialerClose()).
   4358     //
   4359     // This entire feature is disabled on devices which use a proximity
   4360     // sensor to turn the screen off while in-call.
   4361     //
   4362 
   4363     /**
   4364      * Initializes the "touch lock" UI widgets.  We do this lazily
   4365      * to avoid slowing down the initial launch of the InCallScreen.
   4366      */
   4367     private void initTouchLock() {
   4368         if (VDBG) log("initTouchLock()...");
   4369         if (mTouchLockOverlay != null) {
   4370             Log.w(LOG_TAG, "initTouchLock: already initialized!");
   4371             return;
   4372         }
   4373 
   4374         if (!mUseTouchLockOverlay) {
   4375             Log.w(LOG_TAG, "initTouchLock: touch lock isn't used on this device!");
   4376             return;
   4377         }
   4378 
   4379         mTouchLockOverlay = (View) findViewById(R.id.touchLockOverlay);
   4380         // Note mTouchLockOverlay's visibility is initially GONE.
   4381         mTouchLockIcon = (View) findViewById(R.id.touchLockIcon);
   4382 
   4383         // Handle touch events.  (Basically mTouchLockOverlay consumes and
   4384         // discards any touch events it sees, and mTouchLockIcon listens
   4385         // for the "double-tap to unlock" gesture.)
   4386         mTouchLockOverlay.setOnTouchListener(this);
   4387         mTouchLockIcon.setOnTouchListener(this);
   4388 
   4389         mTouchLockFadeIn = AnimationUtils.loadAnimation(this, R.anim.touch_lock_fade_in);
   4390     }
   4391 
   4392     private boolean isTouchLocked() {
   4393         return mUseTouchLockOverlay
   4394                 && (mTouchLockOverlay != null)
   4395                 && (mTouchLockOverlay.getVisibility() == View.VISIBLE);
   4396     }
   4397 
   4398     /**
   4399      * Enables or disables the "touch lock" overlay on top of the DTMF dialpad.
   4400      *
   4401      * If enable=true, bring up the overlay immediately using an animated
   4402      * fade-in effect.  (Or do nothing if the overlay isn't appropriate
   4403      * right now, like if the dialpad isn't up, or the speaker is on.)
   4404      *
   4405      * If enable=false, immediately take down the overlay.  (Or do nothing
   4406      * if the overlay isn't actually up right now.)
   4407      *
   4408      * Note that with enable=false this method will *not* automatically
   4409      * start the touch lock timer.  (So when taking down the overlay while
   4410      * the dialer is still up, the caller is also responsible for calling
   4411      * resetTouchLockTimer(), to make sure the overlay will get
   4412      * (re-)enabled later.)
   4413      *
   4414      */
   4415     private void enableTouchLock(boolean enable) {
   4416         if (VDBG) log("enableTouchLock(" + enable + ")...");
   4417         if (enable) {
   4418             // We shouldn't have even gotten here if we don't use the
   4419             // touch lock overlay feature at all on this device.
   4420             if (!mUseTouchLockOverlay) {
   4421                 Log.w(LOG_TAG, "enableTouchLock: touch lock isn't used on this device!");
   4422                 return;
   4423             }
   4424 
   4425             // The "touch lock" overlay is only ever used on top of the
   4426             // DTMF dialpad.
   4427             if (!mDialer.isOpened()) {
   4428                 if (VDBG) log("enableTouchLock: dialpad isn't up, no need to lock screen.");
   4429                 return;
   4430             }
   4431 
   4432             // Also, the "touch lock" overlay NEVER appears if the speaker is in use.
   4433             if (PhoneUtils.isSpeakerOn(this)) {
   4434                 if (VDBG) log("enableTouchLock: speaker is on, no need to lock screen.");
   4435                 return;
   4436             }
   4437 
   4438             // Initialize the UI elements if necessary.
   4439             if (mTouchLockOverlay == null) {
   4440                 initTouchLock();
   4441             }
   4442 
   4443             // First take down the menu if it's up (since it's confusing
   4444             // to see a touchable menu *above* the touch lock overlay.)
   4445             // Note dismissMenu() has no effect if the menu is already closed.
   4446             dismissMenu(true);  // dismissImmediate = true
   4447 
   4448             // Bring up the touch lock overlay (with an animated fade)
   4449             mTouchLockOverlay.setVisibility(View.VISIBLE);
   4450             mTouchLockOverlay.startAnimation(mTouchLockFadeIn);
   4451         } else {
   4452             // TODO: it might be nice to immediately kill the animation if
   4453             // we're in the middle of fading-in:
   4454             //   if (mTouchLockFadeIn.hasStarted() && !mTouchLockFadeIn.hasEnded()) {
   4455             //      mTouchLockOverlay.clearAnimation();
   4456             //   }
   4457             // but the fade-in is so quick that this probably isn't necessary.
   4458 
   4459             // Take down the touch lock overlay (immediately)
   4460             if (mTouchLockOverlay != null) mTouchLockOverlay.setVisibility(View.GONE);
   4461         }
   4462     }
   4463 
   4464     /**
   4465      * Schedule the "touch lock" overlay to begin fading in after a short
   4466      * delay, but only if the DTMF dialpad is currently visible.
   4467      *
   4468      * (This is designed to be triggered on any user activity
   4469      * while the dialpad is up but not locked, and also
   4470      * whenever the user "unlocks" the touch lock overlay.)
   4471      *
   4472      * Calling this method supersedes any previous resetTouchLockTimer()
   4473      * calls (i.e. we first clear any pending TOUCH_LOCK_TIMER messages.)
   4474      */
   4475     private void resetTouchLockTimer() {
   4476         if (VDBG) log("resetTouchLockTimer()...");
   4477 
   4478         // This is a no-op if this device doesn't use the touch lock
   4479         // overlay feature at all.
   4480         if (!mUseTouchLockOverlay) return;
   4481 
   4482         mHandler.removeMessages(TOUCH_LOCK_TIMER);
   4483         if (mDialer.isOpened() && !isTouchLocked()) {
   4484             // The touch lock delay value comes from Gservices; we use
   4485             // the same value that's used for the PowerManager's
   4486             // POKE_LOCK_SHORT_TIMEOUT flag (i.e. the fastest possible
   4487             // screen timeout behavior.)
   4488 
   4489             // Do a fresh lookup each time, since settings values can
   4490             // change on the fly.  (The Settings.Secure helper class
   4491             // caches these values so this call is usually cheap.)
   4492             int touchLockDelay = Settings.Secure.getInt(
   4493                     getContentResolver(),
   4494                     Settings.Secure.SHORT_KEYLIGHT_DELAY_MS,
   4495                     TOUCH_LOCK_DELAY_DEFAULT);
   4496             mHandler.sendEmptyMessageDelayed(TOUCH_LOCK_TIMER, touchLockDelay);
   4497         }
   4498     }
   4499 
   4500     /**
   4501      * Handles the TOUCH_LOCK_TIMER event.
   4502      * @see resetTouchLockTimer
   4503      */
   4504     private void touchLockTimerExpired() {
   4505         // Ok, it's been long enough since we had any user activity with
   4506         // the DTMF dialpad up.  If the dialpad is still up, start fading
   4507         // in the "touch lock" overlay.
   4508         enableTouchLock(true);
   4509     }
   4510 
   4511     // View.OnTouchListener implementation
   4512     public boolean onTouch(View v, MotionEvent event) {
   4513         if (VDBG) log ("onTouch(View " + v + ")...");
   4514 
   4515         // Handle touch events on the "touch lock" overlay.
   4516         if ((v == mTouchLockIcon) || (v == mTouchLockOverlay)) {
   4517 
   4518             // TODO: move this big hunk of code to a helper function, or
   4519             // even better out to a separate helper class containing all
   4520             // the touch lock overlay code.
   4521 
   4522             // We only care about these touches while the touch lock UI is
   4523             // visible (including the time during the fade-in animation.)
   4524             if (!isTouchLocked()) {
   4525                 // Got an event from the touch lock UI, but we're not locked!
   4526                 // (This was probably a touch-UP right after we unlocked.
   4527                 // Ignore it.)
   4528                 return false;
   4529             }
   4530 
   4531             // (v == mTouchLockIcon) means the user hit the lock icon in the
   4532             // middle of the screen, and (v == mTouchLockOverlay) is a touch
   4533             // anywhere else on the overlay.
   4534 
   4535             if (v == mTouchLockIcon) {
   4536                 // Direct hit on the "lock" icon.  Handle the double-tap gesture.
   4537                 if (event.getAction() == MotionEvent.ACTION_DOWN) {
   4538                     long now = SystemClock.uptimeMillis();
   4539                     if (VDBG) log("- touch lock icon: handling a DOWN event, t = " + now);
   4540 
   4541                     // Look for the double-tap gesture:
   4542                     if (now < mTouchLockLastTouchTime + ViewConfiguration.getDoubleTapTimeout()) {
   4543                         if (VDBG) log("==> touch lock icon: DOUBLE-TAP!");
   4544                         // This was the 2nd tap of a double-tap gesture.
   4545                         // Take down the touch lock overlay, but post a
   4546                         // message in the future to bring it back later.
   4547                         enableTouchLock(false);
   4548                         resetTouchLockTimer();
   4549                         // This counts as explicit "user activity".
   4550                         PhoneApp.getInstance().pokeUserActivity();
   4551                     }
   4552                 } else if (event.getAction() == MotionEvent.ACTION_UP) {
   4553                     // Stash away the current time in case this is the first
   4554                     // tap of a double-tap gesture.  (We measure the time from
   4555                     // the first tap's UP to the second tap's DOWN.)
   4556                     mTouchLockLastTouchTime = SystemClock.uptimeMillis();
   4557                 }
   4558 
   4559                 // And regardless of what just happened, we *always* consume
   4560                 // touch events while the touch lock UI is (or was) visible.
   4561                 return true;
   4562 
   4563             } else {  // (v == mTouchLockOverlay)
   4564                 // User touched the "background" area of the touch lock overlay.
   4565 
   4566                 // TODO: If we're in the middle of the fade-in animation,
   4567                 // consider making a touch *anywhere* immediately unlock the
   4568                 // UI.  This could be risky, though, if the user tries to
   4569                 // *double-tap* during the fade-in (in which case the 2nd tap
   4570                 // might 't become a false touch on the dialpad!)
   4571                 //
   4572                 //if (event.getAction() == MotionEvent.ACTION_DOWN) {
   4573                 //    if (DBG) log("- touch lock overlay background: handling a DOWN event.");
   4574                 //
   4575                 //    if (mTouchLockFadeIn.hasStarted() && !mTouchLockFadeIn.hasEnded()) {
   4576                 //        // If we're still fading-in, a touch *anywhere* onscreen
   4577                 //        // immediately unlocks.
   4578                 //        if (DBG) log("==> touch lock: tap during fade-in!");
   4579                 //
   4580                 //        mTouchLockOverlay.clearAnimation();
   4581                 //        enableTouchLock(false);
   4582                 //        // ...but post a message in the future to bring it
   4583                 //        // back later.
   4584                 //        resetTouchLockTimer();
   4585                 //    }
   4586                 //}
   4587 
   4588                 // And regardless of what just happened, we *always* consume
   4589                 // touch events while the touch lock UI is (or was) visible.
   4590                 return true;
   4591             }
   4592         } else {
   4593             Log.w(LOG_TAG, "onTouch: event from unexpected View: " + v);
   4594             return false;
   4595         }
   4596     }
   4597 
   4598     // Any user activity while the dialpad is up, but not locked, should
   4599     // reset the touch lock timer back to the full delay amount.
   4600     @Override
   4601     public void onUserInteraction() {
   4602         if (mDialer.isOpened() && !isTouchLocked()) {
   4603             resetTouchLockTimer();
   4604         }
   4605     }
   4606 
   4607     /**
   4608      * Posts a handler message telling the InCallScreen to close
   4609      * the OTA failure notice after the specified delay.
   4610      * @see OtaUtils.otaShowProgramFailureNotice
   4611      */
   4612     /* package */ void requestCloseOtaFailureNotice(long timeout) {
   4613         if (DBG) log("requestCloseOtaFailureNotice() with timeout: " + timeout);
   4614         mHandler.sendEmptyMessageDelayed(REQUEST_CLOSE_OTA_FAILURE_NOTICE, timeout);
   4615 
   4616         // TODO: we probably ought to call removeMessages() for this
   4617         // message code in either onPause or onResume, just to be 100%
   4618         // sure that the message we just posted has no way to affect a
   4619         // *different* call if the user quickly backs out and restarts.
   4620         // (This is also true for requestCloseSpcErrorNotice() below, and
   4621         // probably anywhere else we use mHandler.sendEmptyMessageDelayed().)
   4622     }
   4623 
   4624     /**
   4625      * Posts a handler message telling the InCallScreen to close
   4626      * the SPC error notice after the specified delay.
   4627      * @see OtaUtils.otaShowSpcErrorNotice
   4628      */
   4629     /* package */ void requestCloseSpcErrorNotice(long timeout) {
   4630         if (DBG) log("requestCloseSpcErrorNotice() with timeout: " + timeout);
   4631         mHandler.sendEmptyMessageDelayed(REQUEST_CLOSE_SPC_ERROR_NOTICE, timeout);
   4632     }
   4633 
   4634     public boolean isOtaCallInActiveState() {
   4635         final PhoneApp app = PhoneApp.getInstance();
   4636         if ((mInCallScreenMode == InCallScreenMode.OTA_NORMAL)
   4637                 || ((app.cdmaOtaScreenState != null)
   4638                     && (app.cdmaOtaScreenState.otaScreenState ==
   4639                         CdmaOtaScreenState.OtaScreenState.OTA_STATUS_ACTIVATION))) {
   4640             return true;
   4641         } else {
   4642             return false;
   4643         }
   4644     }
   4645 
   4646     /**
   4647      * Handle OTA Call End scenario when display becomes dark during OTA Call
   4648      * and InCallScreen is in pause mode.  CallNotifier will listen for call
   4649      * end indication and call this api to handle OTA Call end scenario
   4650      */
   4651     public void handleOtaCallEnd() {
   4652         final PhoneApp app = PhoneApp.getInstance();
   4653 
   4654         if (DBG) log("handleOtaCallEnd entering");
   4655         if (((mInCallScreenMode == InCallScreenMode.OTA_NORMAL)
   4656                 || ((app.cdmaOtaScreenState != null)
   4657                 && (app.cdmaOtaScreenState.otaScreenState !=
   4658                     CdmaOtaScreenState.OtaScreenState.OTA_STATUS_UNDEFINED)))
   4659                 && ((app.cdmaOtaProvisionData != null)
   4660                 && (!app.cdmaOtaProvisionData.inOtaSpcState))) {
   4661             if (DBG) log("handleOtaCallEnd - Set OTA Call End stater");
   4662             setInCallScreenMode(InCallScreenMode.OTA_ENDED);
   4663             updateScreen();
   4664         }
   4665     }
   4666 
   4667     public boolean isOtaCallInEndState() {
   4668         return (mInCallScreenMode == InCallScreenMode.OTA_ENDED);
   4669     }
   4670 
   4671    /**
   4672     * Checks to see if the current call is a CDMA OTA Call, based on the
   4673     * action of the specified intent and OTA Screen state information.
   4674     *
   4675     * The OTA call is a CDMA-specific concept, so this method will
   4676     * always return false on a GSM phone.
   4677     */
   4678     private boolean checkIsOtaCall(Intent intent) {
   4679         if (VDBG) log("checkIsOtaCall...");
   4680 
   4681         if (intent == null || intent.getAction() == null) {
   4682             return false;
   4683         }
   4684 
   4685         if (mPhone.getPhoneType() != Phone.PHONE_TYPE_CDMA) {
   4686             return false;
   4687         }
   4688 
   4689         final PhoneApp app = PhoneApp.getInstance();
   4690 
   4691         if ((app.cdmaOtaScreenState == null)
   4692                 || (app.cdmaOtaProvisionData == null)) {
   4693             if (DBG) log("checkIsOtaCall: OtaUtils.CdmaOtaScreenState not initialized");
   4694             return false;
   4695         }
   4696 
   4697         String action = intent.getAction();
   4698         boolean isOtaCall = false;
   4699         if (action.equals(ACTION_SHOW_ACTIVATION)) {
   4700             if (DBG) log("checkIsOtaCall action = ACTION_SHOW_ACTIVATION");
   4701             if (!app.cdmaOtaProvisionData.isOtaCallIntentProcessed) {
   4702                 if (DBG) log("checkIsOtaCall: ACTION_SHOW_ACTIVATION is not handled before");
   4703                 app.cdmaOtaProvisionData.isOtaCallIntentProcessed = true;
   4704                 app.cdmaOtaScreenState.otaScreenState =
   4705                         CdmaOtaScreenState.OtaScreenState.OTA_STATUS_ACTIVATION;
   4706             }
   4707             isOtaCall = true;
   4708         } else if (action.equals(Intent.ACTION_CALL)
   4709                 || action.equals(Intent.ACTION_CALL_EMERGENCY)) {
   4710             String number;
   4711             try {
   4712                 number = getInitialNumber(intent);
   4713             } catch (PhoneUtils.VoiceMailNumberMissingException ex) {
   4714                 if (DBG) log("Error retrieving number using the api getInitialNumber()");
   4715                 return false;
   4716             }
   4717             if (mPhone.isOtaSpNumber(number)) {
   4718                 if (DBG) log("checkIsOtaCall action ACTION_CALL, it is valid OTA number");
   4719                 isOtaCall = true;
   4720             }
   4721         } else if (action.equals(intent.ACTION_MAIN)) {
   4722             if (DBG) log("checkIsOtaCall action ACTION_MAIN");
   4723             boolean isRingingCall = !mRingingCall.isIdle();
   4724             if (isRingingCall) {
   4725                 if (DBG) log("checkIsOtaCall isRingingCall: " + isRingingCall);
   4726                 return false;
   4727             } else if ((app.cdmaOtaInCallScreenUiState.state
   4728                             == CdmaOtaInCallScreenUiState.State.NORMAL)
   4729                     || (app.cdmaOtaInCallScreenUiState.state
   4730                             == CdmaOtaInCallScreenUiState.State.ENDED)) {
   4731                 if (DBG) log("checkIsOtaCall action ACTION_MAIN, OTA call already in progress");
   4732                 isOtaCall = true;
   4733             } else {
   4734                 if (app.cdmaOtaScreenState.otaScreenState !=
   4735                         CdmaOtaScreenState.OtaScreenState.OTA_STATUS_UNDEFINED) {
   4736                     if (DBG) log("checkIsOtaCall action ACTION_MAIN, "
   4737                                  + "OTA call in progress with UNDEFINED");
   4738                     isOtaCall = true;
   4739                 }
   4740             }
   4741         }
   4742 
   4743         if (DBG) log("checkIsOtaCall: isOtaCall =" + isOtaCall);
   4744         if (isOtaCall && (otaUtils == null)) {
   4745             if (DBG) log("checkIsOtaCall: creating OtaUtils...");
   4746             otaUtils = new OtaUtils(getApplicationContext(),
   4747                                     this, mInCallPanel, mCallCard, mDialer);
   4748         }
   4749         return isOtaCall;
   4750     }
   4751 
   4752     /**
   4753      * Initialize the OTA State and UI.
   4754      *
   4755      * On Resume, this function is called to check if current call is
   4756      * OTA Call and if it is OTA Call, create OtaUtil object and set
   4757      * InCallScreenMode to OTA Call mode (OTA_NORMAL or OTA_ENDED).
   4758      * As part of initialization, OTA Call Card is inflated.
   4759      * OtaUtil object provides utility apis that InCallScreen calls for OTA Call UI
   4760      * rendering, handling of touck/key events on OTA Screens and handling of
   4761      * Framework events that result in OTA State change
   4762      *
   4763      * @return: true if we are in an OtaCall
   4764      */
   4765     private boolean initOtaState() {
   4766         boolean inOtaCall = false;
   4767 
   4768         if (mPhone.getPhoneType() == Phone.PHONE_TYPE_CDMA) {
   4769             final PhoneApp app = PhoneApp.getInstance();
   4770 
   4771             if ((app.cdmaOtaScreenState == null) || (app.cdmaOtaProvisionData == null)) {
   4772                 if (DBG) log("initOtaState func - All CdmaOTA utility classes not initialized");
   4773                 return false;
   4774             }
   4775 
   4776             inOtaCall = checkIsOtaCall(getIntent());
   4777             if (inOtaCall) {
   4778                 OtaUtils.CdmaOtaInCallScreenUiState.State cdmaOtaInCallScreenState =
   4779                         otaUtils.getCdmaOtaInCallScreenUiState();
   4780                 if (cdmaOtaInCallScreenState == OtaUtils.CdmaOtaInCallScreenUiState.State.NORMAL) {
   4781                     if (DBG) log("initOtaState - in OTA Normal mode");
   4782                     setInCallScreenMode(InCallScreenMode.OTA_NORMAL);
   4783                 } else if (cdmaOtaInCallScreenState ==
   4784                                 OtaUtils.CdmaOtaInCallScreenUiState.State.ENDED) {
   4785                     if (DBG) log("initOtaState - in OTA END mode");
   4786                     setInCallScreenMode(InCallScreenMode.OTA_ENDED);
   4787                 } else if (app.cdmaOtaScreenState.otaScreenState ==
   4788                                 CdmaOtaScreenState.OtaScreenState.OTA_STATUS_SUCCESS_FAILURE_DLG) {
   4789                     if (DBG) log("initOtaState - set OTA END Mode");
   4790                     setInCallScreenMode(InCallScreenMode.OTA_ENDED);
   4791                 } else {
   4792                     if (DBG) log("initOtaState - Set OTA NORMAL Mode");
   4793                     setInCallScreenMode(InCallScreenMode.OTA_NORMAL);
   4794                 }
   4795             } else {
   4796                 if (otaUtils != null) {
   4797                     otaUtils.cleanOtaScreen(false);
   4798                 }
   4799             }
   4800         }
   4801         return inOtaCall;
   4802     }
   4803 
   4804     public void updateMenuItems() {
   4805         if (mInCallMenu != null) {
   4806             boolean okToShowMenu =  mInCallMenu.updateItems(PhoneApp.getInstance().phone);
   4807             if (!okToShowMenu) {
   4808                 dismissMenu(true);
   4809             }
   4810         }
   4811     }
   4812 
   4813     /**
   4814      * Updates and returns the InCallControlState instance.
   4815      */
   4816     public InCallControlState getUpdatedInCallControlState() {
   4817         mInCallControlState.update();
   4818         return mInCallControlState;
   4819     }
   4820 
   4821     /**
   4822      * Updates the background of the InCallScreen to indicate the state of
   4823      * the current call(s).
   4824      */
   4825     private void updateInCallBackground() {
   4826         final boolean hasRingingCall = !mRingingCall.isIdle();
   4827         final boolean hasActiveCall = !mForegroundCall.isIdle();
   4828         final boolean hasHoldingCall = !mPhone.getBackgroundCall().isIdle();
   4829         final PhoneApp app = PhoneApp.getInstance();
   4830         final boolean bluetoothActive = app.showBluetoothIndication();
   4831 
   4832         int backgroundResId = R.drawable.bg_in_call_gradient_unidentified;
   4833 
   4834         // Possible states of the background are:
   4835         // - bg_in_call_gradient_bluetooth.9.png     // blue
   4836         // - bg_in_call_gradient_connected.9.png     // green
   4837         // - bg_in_call_gradient_ended.9.png         // red
   4838         // - bg_in_call_gradient_on_hold.9.png       // orange
   4839         // - bg_in_call_gradient_unidentified.9.png  // gray
   4840 
   4841         if (hasRingingCall) {
   4842             // There's an INCOMING (or WAITING) call.
   4843             if (bluetoothActive) {
   4844                 backgroundResId = R.drawable.bg_in_call_gradient_bluetooth;
   4845             } else {
   4846                 backgroundResId = R.drawable.bg_in_call_gradient_unidentified;
   4847             }
   4848         } else if (hasHoldingCall && !hasActiveCall) {
   4849             // No foreground call, but there is a call on hold.
   4850             backgroundResId = R.drawable.bg_in_call_gradient_on_hold;
   4851         } else {
   4852             // In all cases other than "ringing" and "on hold", the state
   4853             // of the foreground call determines the background.
   4854             final Call.State fgState = mForegroundCall.getState();
   4855             switch (fgState) {
   4856                 case ACTIVE:
   4857                 case DISCONNECTING:  // Call will disconnect soon, but keep showing
   4858                                      // the normal "connected" background for now.
   4859                     if (bluetoothActive) {
   4860                         backgroundResId = R.drawable.bg_in_call_gradient_bluetooth;
   4861                     } else {
   4862                         backgroundResId = R.drawable.bg_in_call_gradient_connected;
   4863                     }
   4864                     break;
   4865 
   4866                 case DISCONNECTED:
   4867                     backgroundResId = R.drawable.bg_in_call_gradient_ended;
   4868                     break;
   4869 
   4870                 case DIALING:
   4871                 case ALERTING:
   4872                     if (bluetoothActive) {
   4873                         backgroundResId = R.drawable.bg_in_call_gradient_bluetooth;
   4874                     } else {
   4875                         backgroundResId = R.drawable.bg_in_call_gradient_unidentified;
   4876                     }
   4877                     break;
   4878 
   4879                 default:
   4880                     // Foreground call is (presumably) IDLE.
   4881                     // We're not usually here at all in this state, but
   4882                     // this *does* happen in some unusual cases (like
   4883                     // while displaying an MMI result).
   4884                     // Use the most generic background.
   4885                     backgroundResId = R.drawable.bg_in_call_gradient_unidentified;
   4886                     break;
   4887             }
   4888         }
   4889         mMainFrame.setBackgroundResource(backgroundResId);
   4890     }
   4891 
   4892     public void resetInCallScreenMode() {
   4893         if (DBG) log("resetInCallScreenMode - InCallScreenMode set to UNDEFINED");
   4894         setInCallScreenMode(InCallScreenMode.UNDEFINED);
   4895     }
   4896 
   4897     /**
   4898      * Clear all the fields related to the provider support.
   4899      */
   4900     private void clearProvider() {
   4901         mProviderOverlayVisible = false;
   4902         mProviderLabel = null;
   4903         mProviderIcon = null;
   4904         mProviderGatewayUri = null;
   4905         mProviderAddress = null;
   4906     }
   4907 
   4908     /**
   4909      * Updates the onscreen hint displayed while the user is dragging one
   4910      * of the handles of the RotarySelector widget used for incoming
   4911      * calls.
   4912      *
   4913      * @param hintTextResId resource ID of the hint text to display,
   4914      *        or 0 if no hint should be visible.
   4915      * @param hintColorResId resource ID for the color of the hint text
   4916      */
   4917     /* package */ void updateSlidingTabHint(int hintTextResId, int hintColorResId) {
   4918         if (VDBG) log("updateRotarySelectorHint(" + hintTextResId + ")...");
   4919         if (mCallCard != null) {
   4920             mCallCard.setRotarySelectorHint(hintTextResId, hintColorResId);
   4921             mCallCard.updateState(mPhone);
   4922             // TODO: if hintTextResId == 0, consider NOT clearing the onscreen
   4923             // hint right away, but instead post a delayed handler message to
   4924             // keep it onscreen for an extra second or two.  (This might make
   4925             // the hint more helpful if the user quickly taps one of the
   4926             // handles without dragging at all...)
   4927             // (Or, maybe this should happen completely within the RotarySelector
   4928             // widget, since the widget itself probably wants to keep the colored
   4929             // arrow visible for some extra time also...)
   4930         }
   4931     }
   4932 
   4933     @Override
   4934     public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
   4935         super.dispatchPopulateAccessibilityEvent(event);
   4936         mCallCard.dispatchPopulateAccessibilityEvent(event);
   4937         return true;
   4938     }
   4939 
   4940     private void log(String msg) {
   4941         Log.d(LOG_TAG, msg);
   4942     }
   4943 }
   4944