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