Blanket copy of PhoneApp to services/Telephony.

First phase of splitting out InCallUI from PhoneApp.

Change-Id: I237341c4ff00e96c677caa4580b251ef3432931b
diff --git a/src/com/android/phone/OtaUtils.java b/src/com/android/phone/OtaUtils.java
new file mode 100644
index 0000000..495df27
--- /dev/null
+++ b/src/com/android/phone/OtaUtils.java
@@ -0,0 +1,1645 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.phone;
+
+import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.PhoneConstants;
+import com.android.internal.telephony.TelephonyCapabilities;
+import com.android.internal.telephony.TelephonyProperties;
+import com.android.phone.OtaUtils.CdmaOtaInCallScreenUiState.State;
+
+import android.app.Activity;
+import android.app.ActivityManager;
+import android.app.AlertDialog;
+import android.app.PendingIntent;
+import android.app.PendingIntent.CanceledException;
+import android.content.ActivityNotFoundException;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.AsyncResult;
+import android.os.Handler;
+import android.os.SystemClock;
+import android.os.SystemProperties;
+import android.os.UserHandle;
+import android.telephony.TelephonyManager;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewStub;
+import android.view.WindowManager;
+import android.widget.Button;
+import android.widget.ProgressBar;
+import android.widget.ScrollView;
+import android.widget.TextView;
+import android.widget.ToggleButton;
+
+/**
+ * Handles all OTASP Call related logic and UI functionality.
+ * The InCallScreen interacts with this class to perform an OTASP Call.
+ *
+ * OTASP is a CDMA-specific feature:
+ *   OTA or OTASP == Over The Air service provisioning
+ *   SPC == Service Programming Code
+ *   TODO: Include pointer to more detailed documentation.
+ *
+ * TODO: This is Over The Air Service Provisioning (OTASP)
+ *       A better name would be OtaspUtils.java.
+ */
+public class OtaUtils {
+    private static final String LOG_TAG = "OtaUtils";
+    private static final boolean DBG = false;
+
+    public static final int OTA_SHOW_ACTIVATION_SCREEN_OFF = 0;
+    public static final int OTA_SHOW_ACTIVATION_SCREEN_ON = 1;
+    public static final int OTA_SHOW_LISTENING_SCREEN_OFF =0;
+    public static final int OTA_SHOW_LISTENING_SCREEN_ON =1;
+    public static final int OTA_SHOW_ACTIVATE_FAIL_COUNT_OFF = 0;
+    public static final int OTA_SHOW_ACTIVATE_FAIL_COUNT_THREE = 3;
+    public static final int OTA_PLAY_SUCCESS_FAILURE_TONE_OFF = 0;
+    public static final int OTA_PLAY_SUCCESS_FAILURE_TONE_ON = 1;
+
+    // SPC Timeout is 60 seconds
+    public final int OTA_SPC_TIMEOUT = 60;
+    public final int OTA_FAILURE_DIALOG_TIMEOUT = 2;
+
+    // Constants for OTASP-related Intents and intent extras.
+    // Watch out: these must agree with the corresponding constants in
+    // apps/SetupWizard!
+
+    // Intent action to launch an OTASP call.
+    public static final String ACTION_PERFORM_CDMA_PROVISIONING =
+           "com.android.phone.PERFORM_CDMA_PROVISIONING";
+
+    // Intent action to launch activation on a non-voice capable device
+    public static final String ACTION_PERFORM_VOICELESS_CDMA_PROVISIONING =
+            "com.android.phone.PERFORM_VOICELESS_CDMA_PROVISIONING";
+
+    // Intent action to display the InCallScreen in the OTASP "activation" state.
+    public static final String ACTION_DISPLAY_ACTIVATION_SCREEN =
+            "com.android.phone.DISPLAY_ACTIVATION_SCREEN";
+
+    // boolean voiceless provisioning extra that enables a "don't show this again" checkbox
+    // the user can check to never see the activity upon bootup again
+    public static final String EXTRA_VOICELESS_PROVISIONING_OFFER_DONTSHOW =
+            "com.android.phone.VOICELESS_PROVISIONING_OFFER_DONTSHOW";
+
+    // Activity result codes for the ACTION_PERFORM_CDMA_PROVISIONING intent
+    // (see the InCallScreenShowActivation activity.)
+    //
+    // Note: currently, our caller won't ever actually receive the
+    // RESULT_INTERACTIVE_OTASP_STARTED result code; see comments in
+    // InCallScreenShowActivation.onCreate() for details.
+
+    public static final int RESULT_INTERACTIVE_OTASP_STARTED = Activity.RESULT_FIRST_USER;
+    public static final int RESULT_NONINTERACTIVE_OTASP_STARTED = Activity.RESULT_FIRST_USER + 1;
+    public static final int RESULT_NONINTERACTIVE_OTASP_FAILED = Activity.RESULT_FIRST_USER + 2;
+
+    // Testing: Extra for the ACTION_PERFORM_CDMA_PROVISIONING intent that
+    // allows the caller to manually enable/disable "interactive mode" for
+    // the OTASP call.   Only available in userdebug or eng builds.
+    public static final String EXTRA_OVERRIDE_INTERACTIVE_MODE =
+            "ota_override_interactive_mode";
+
+    // Extra for the ACTION_PERFORM_CDMA_PROVISIONING intent, holding a
+    // PendingIntent which the phone app can use to send a result code
+    // back to the caller.
+    public static final String EXTRA_OTASP_RESULT_CODE_PENDING_INTENT =
+            "otasp_result_code_pending_intent";
+
+    // Extra attached to the above PendingIntent that indicates
+    // success or failure.
+    public static final String EXTRA_OTASP_RESULT_CODE =
+            "otasp_result_code";
+    public static final int OTASP_UNKNOWN = 0;
+    public static final int OTASP_USER_SKIPPED = 1;  // Only meaningful with interactive OTASP
+    public static final int OTASP_SUCCESS = 2;
+    public static final int OTASP_FAILURE = 3;
+    // failed due to CDMA_OTA_PROVISION_STATUS_SPC_RETRIES_EXCEEDED
+    public static final int OTASP_FAILURE_SPC_RETRIES = 4;
+    // TODO: Distinguish between interactive and non-interactive success
+    // and failure.  Then, have the PendingIntent be sent after
+    // interactive OTASP as well (so the caller can find out definitively
+    // when interactive OTASP completes.)
+
+    private static final String OTASP_NUMBER = "*228";
+    private static final String OTASP_NUMBER_NON_INTERACTIVE = "*22899";
+
+    private InCallScreen mInCallScreen;
+    private Context mContext;
+    private PhoneGlobals mApplication;
+    private OtaWidgetData mOtaWidgetData;
+    private ViewGroup mInCallTouchUi;  // UI controls for regular calls
+    private CallCard mCallCard;
+
+    // The DTMFTwelveKeyDialer instance.   We create this in
+    // initOtaInCallScreen(), and attach it to the DTMFTwelveKeyDialerView
+    // ("otaDtmfDialerView") that comes from otacall_card.xml.
+    private DTMFTwelveKeyDialer mOtaCallCardDtmfDialer;
+
+    private static boolean sIsWizardMode = true;
+
+    // How many times do we retry maybeDoOtaCall() if the LTE state is not known yet,
+    // and how long do we wait between retries
+    private static final int OTA_CALL_LTE_RETRIES_MAX = 5;
+    private static final int OTA_CALL_LTE_RETRY_PERIOD = 3000;
+    private static int sOtaCallLteRetries = 0;
+
+    // In "interactive mode", the OtaUtils object is tied to an
+    // InCallScreen instance, where we display a bunch of UI specific to
+    // the OTASP call.  But on devices that are not "voice capable", the
+    // OTASP call runs in a non-interactive mode, and we don't have
+    // an InCallScreen or CallCard or any OTASP UI elements at all.
+    private boolean mInteractive = true;
+
+
+    /**
+     * OtaWidgetData class represent all OTA UI elements
+     *
+     * TODO(OTASP): It's really ugly for the OtaUtils object to reach into the
+     *     InCallScreen like this and directly manipulate its widgets.
+     *
+     *     Instead, the model/view separation should be more clear: OtaUtils
+     *     should only know about a higher-level abstraction of the
+     *     OTASP-specific UI state (just like how the CallController uses the
+     *     InCallUiState object), and the InCallScreen itself should translate
+     *     that higher-level abstraction into actual onscreen views and widgets.
+     */
+    private class OtaWidgetData {
+        public Button otaEndButton;
+        public Button otaActivateButton;
+        public Button otaSkipButton;
+        public Button otaNextButton;
+        public ToggleButton otaSpeakerButton;
+        public ViewGroup otaUpperWidgets;
+        public View callCardOtaButtonsFailSuccess;
+        public ProgressBar otaTextProgressBar;
+        public TextView otaTextSuccessFail;
+        public View callCardOtaButtonsActivate;
+        public View callCardOtaButtonsListenProgress;
+        public TextView otaTextActivate;
+        public TextView otaTextListenProgress;
+        public AlertDialog spcErrorDialog;
+        public AlertDialog otaFailureDialog;
+        public AlertDialog otaSkipConfirmationDialog;
+        public TextView otaTitle;
+        public DTMFTwelveKeyDialerView otaDtmfDialerView;
+        public Button otaTryAgainButton;
+    }
+
+    /**
+     * OtaUtils constructor.
+     *
+     * @param context the Context of the calling Activity or Application
+     * @param interactive if true, use the InCallScreen to display the progress
+     *                    and result of the OTASP call.  In practice this is
+     *                    true IFF the current device is a voice-capable phone.
+     *
+     * Note if interactive is true, you must also call updateUiWidgets() as soon
+     * as the InCallScreen instance is ready.
+     */
+    public OtaUtils(Context context, boolean interactive) {
+        if (DBG) log("OtaUtils constructor...");
+        mApplication = PhoneGlobals.getInstance();
+        mContext = context;
+        mInteractive = interactive;
+    }
+
+    /**
+     * Updates the OtaUtils object's references to some UI elements belonging to
+     * the InCallScreen.  This is used only in interactive mode.
+     *
+     * Use clearUiWidgets() to clear out these references.  (The InCallScreen
+     * is responsible for doing this from its onDestroy() method.)
+     *
+     * This method has no effect if the UI widgets have already been set up.
+     * (In other words, it's safe to call this every time through
+     * InCallScreen.onResume().)
+     */
+    public void updateUiWidgets(InCallScreen inCallScreen,
+            ViewGroup inCallTouchUi, CallCard callCard) {
+        if (DBG) log("updateUiWidgets()...  mInCallScreen = " + mInCallScreen);
+
+        if (!mInteractive) {
+            throw new IllegalStateException("updateUiWidgets() called in non-interactive mode");
+        }
+
+        if (mInCallScreen != null) {
+            if (DBG) log("updateUiWidgets(): widgets already set up, nothing to do...");
+            return;
+        }
+
+        mInCallScreen = inCallScreen;
+        mInCallTouchUi = inCallTouchUi;
+        mCallCard = callCard;
+        mOtaWidgetData = new OtaWidgetData();
+
+        // Inflate OTASP-specific UI elements:
+        ViewStub otaCallCardStub = (ViewStub) mInCallScreen.findViewById(R.id.otaCallCardStub);
+        if (otaCallCardStub != null) {
+            // If otaCallCardStub is null here, that means it's already been
+            // inflated (which could have happened in the current InCallScreen
+            // instance for a *prior* OTASP call.)
+            otaCallCardStub.inflate();
+        }
+
+        readXmlSettings();
+        initOtaInCallScreen();
+    }
+
+    /**
+     * Clear out the OtaUtils object's references to any InCallScreen UI
+     * elements.  This is the opposite of updateUiWidgets().
+     */
+    public void clearUiWidgets() {
+        mInCallScreen = null;
+        mInCallTouchUi = null;
+        mCallCard = null;
+        mOtaWidgetData = null;
+    }
+
+    /**
+     * Starts the OTA provisioning call.  If the MIN isn't available yet, it returns false and adds
+     * an event to return the request to the calling app when it becomes available.
+     *
+     * @param context
+     * @param handler
+     * @param request
+     * @return true if we were able to launch Ota activity or it's not required; false otherwise
+     */
+    public static boolean maybeDoOtaCall(Context context, Handler handler, int request) {
+        PhoneGlobals app = PhoneGlobals.getInstance();
+        Phone phone = app.phone;
+
+        if (ActivityManager.isRunningInTestHarness()) {
+            Log.i(LOG_TAG, "Don't run provisioning when in test harness");
+            return true;
+        }
+
+        if (!TelephonyCapabilities.supportsOtasp(phone)) {
+            // Presumably not a CDMA phone.
+            if (DBG) log("maybeDoOtaCall: OTASP not supported on this device");
+            return true;  // Nothing to do here.
+        }
+
+        if (!phone.isMinInfoReady()) {
+            if (DBG) log("MIN is not ready. Registering to receive notification.");
+            phone.registerForSubscriptionInfoReady(handler, request, null);
+            return false;
+        }
+        phone.unregisterForSubscriptionInfoReady(handler);
+
+        if (getLteOnCdmaMode(context) == PhoneConstants.LTE_ON_CDMA_UNKNOWN) {
+            if (sOtaCallLteRetries < OTA_CALL_LTE_RETRIES_MAX) {
+                if (DBG) log("maybeDoOtaCall: LTE state still unknown: retrying");
+                handler.sendEmptyMessageDelayed(request, OTA_CALL_LTE_RETRY_PERIOD);
+                sOtaCallLteRetries++;
+                return false;
+            } else {
+                Log.w(LOG_TAG, "maybeDoOtaCall: LTE state still unknown: giving up");
+                return true;
+            }
+        }
+
+        boolean phoneNeedsActivation = phone.needsOtaServiceProvisioning();
+        if (DBG) log("phoneNeedsActivation is set to " + phoneNeedsActivation);
+
+        int otaShowActivationScreen = context.getResources().getInteger(
+                R.integer.OtaShowActivationScreen);
+        if (DBG) log("otaShowActivationScreen: " + otaShowActivationScreen);
+
+        // Run the OTASP call in "interactive" mode only if
+        // this is a non-LTE "voice capable" device.
+        if (PhoneGlobals.sVoiceCapable && getLteOnCdmaMode(context) == PhoneConstants.LTE_ON_CDMA_FALSE) {
+            if (phoneNeedsActivation
+                    && (otaShowActivationScreen == OTA_SHOW_ACTIVATION_SCREEN_ON)) {
+                app.cdmaOtaProvisionData.isOtaCallIntentProcessed = false;
+                sIsWizardMode = false;
+
+                if (DBG) Log.d(LOG_TAG, "==> Starting interactive CDMA provisioning...");
+                OtaUtils.startInteractiveOtasp(context);
+
+                if (DBG) log("maybeDoOtaCall: voice capable; activation started.");
+            } else {
+                if (DBG) log("maybeDoOtaCall: voice capable; activation NOT started.");
+            }
+        } else {
+            if (phoneNeedsActivation) {
+                app.cdmaOtaProvisionData.isOtaCallIntentProcessed = false;
+                Intent newIntent = new Intent(ACTION_PERFORM_VOICELESS_CDMA_PROVISIONING);
+                newIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+                newIntent.putExtra(EXTRA_VOICELESS_PROVISIONING_OFFER_DONTSHOW, true);
+                try {
+                    context.startActivity(newIntent);
+                } catch (ActivityNotFoundException e) {
+                    loge("No activity Handling PERFORM_VOICELESS_CDMA_PROVISIONING!");
+                    return false;
+                }
+                if (DBG) log("maybeDoOtaCall: non-interactive; activation intent sent.");
+            } else {
+                if (DBG) log("maybeDoOtaCall: non-interactive, no need for OTASP.");
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Starts a normal "interactive" OTASP call (i.e. CDMA activation
+     * for regular voice-capable phone devices.)
+     *
+     * This method is called from the InCallScreenShowActivation activity when
+     * handling the ACTION_PERFORM_CDMA_PROVISIONING intent.
+     */
+    public static void startInteractiveOtasp(Context context) {
+        if (DBG) log("startInteractiveOtasp()...");
+        PhoneGlobals app = PhoneGlobals.getInstance();
+
+        // There are two ways to start OTASP on voice-capable devices:
+        //
+        // (1) via the PERFORM_CDMA_PROVISIONING intent
+        //     - this is triggered by the "Activate device" button in settings,
+        //       or can be launched automatically upon boot if the device
+        //       thinks it needs to be provisioned.
+        //     - the intent is handled by InCallScreenShowActivation.onCreate(),
+        //       which calls this method
+        //     - we prepare for OTASP by initializing the OtaUtils object
+        //     - we bring up the InCallScreen in the ready-to-activate state
+        //     - when the user presses the "Activate" button we launch the
+        //       call by calling CallController.placeCall() via the
+        //       otaPerformActivation() method.
+        //
+        // (2) by manually making an outgoing call to a special OTASP number
+        //     like "*228" or "*22899".
+        //     - That sequence does NOT involve this method (OtaUtils.startInteractiveOtasp()).
+        //       Instead, the outgoing call request goes straight to CallController.placeCall().
+        //     - CallController.placeCall() notices that it's an OTASP
+        //       call, and initializes the OtaUtils object.
+        //     - The InCallScreen is launched (as the last step of
+        //       CallController.placeCall()).  The InCallScreen notices that
+        //       OTASP is active and shows the correct UI.
+
+        // Here, we start sequence (1):
+        // Do NOT immediately start the call.  Instead, bring up the InCallScreen
+        // in the special "activate" state (see OtaUtils.otaShowActivateScreen()).
+        // We won't actually make the call until the user presses the "Activate"
+        // button.
+
+        Intent activationScreenIntent = new Intent().setClass(context, InCallScreen.class)
+                .setAction(ACTION_DISPLAY_ACTIVATION_SCREEN);
+
+        // Watch out: in the scenario where OTASP gets triggered from the
+        // BOOT_COMPLETED broadcast (see OtaStartupReceiver.java), we might be
+        // running in the PhoneApp's context right now.
+        // So the FLAG_ACTIVITY_NEW_TASK flag is required here.
+        activationScreenIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+
+        // We're about to start the OTASP sequence, so create and initialize the
+        // OtaUtils instance.  (This needs to happen before bringing up the
+        // InCallScreen.)
+        OtaUtils.setupOtaspCall(activationScreenIntent);
+
+        // And bring up the InCallScreen...
+        Log.i(LOG_TAG, "startInteractiveOtasp: launching InCallScreen in 'activate' state: "
+              + activationScreenIntent);
+        context.startActivity(activationScreenIntent);
+    }
+
+    /**
+     * Starts the OTASP call *without* involving the InCallScreen or
+     * displaying any UI.
+     *
+     * This is used on data-only devices, which don't support any kind of
+     * in-call phone UI.
+     *
+     * @return PhoneUtils.CALL_STATUS_DIALED if we successfully
+     *         dialed the OTASP number, or one of the other
+     *         CALL_STATUS_* constants if there was a failure.
+     */
+    public static int startNonInteractiveOtasp(Context context) {
+        if (DBG) log("startNonInteractiveOtasp()...");
+        PhoneGlobals app = PhoneGlobals.getInstance();
+
+        if (app.otaUtils != null) {
+            // An OtaUtils instance already exists, presumably from a previous OTASP call.
+            Log.i(LOG_TAG, "startNonInteractiveOtasp: "
+                  + "OtaUtils already exists; nuking the old one and starting again...");
+        }
+
+        // Create the OtaUtils instance.
+        app.otaUtils = new OtaUtils(context, false /* non-interactive mode */);
+        if (DBG) log("- created OtaUtils: " + app.otaUtils);
+
+        // ... and kick off the OTASP call.
+        // TODO(InCallScreen redesign): This should probably go through
+        // the CallController, rather than directly calling
+        // PhoneUtils.placeCall().
+        Phone phone = PhoneGlobals.getPhone();
+        String number = OTASP_NUMBER_NON_INTERACTIVE;
+        Log.i(LOG_TAG, "startNonInteractiveOtasp: placing call to '" + number + "'...");
+        int callStatus = PhoneUtils.placeCall(context,
+                                              phone,
+                                              number,
+                                              null,  // contactRef
+                                              false,  //isEmergencyCall
+                                              null);  // gatewayUri
+
+        if (callStatus == PhoneUtils.CALL_STATUS_DIALED) {
+            if (DBG) log("  ==> successful return from placeCall(): callStatus = " + callStatus);
+        } else {
+            Log.w(LOG_TAG, "Failure from placeCall() for OTA number '"
+                  + number + "': code " + callStatus);
+            return callStatus;
+        }
+
+        // TODO: Any other special work to do here?
+        // Such as:
+        //
+        // - manually kick off progress updates, either using TelephonyRegistry
+        //   or else by sending PendingIntents directly to our caller?
+        //
+        // - manually silence the in-call audio?  (Probably unnecessary
+        //   if Stingray truly has no audio path from phone baseband
+        //   to the device's speakers.)
+        //
+
+        return callStatus;
+    }
+
+    /**
+     * @return true if the specified Intent is a CALL action that's an attempt
+     * to initate an OTASP call.
+     *
+     * OTASP is a CDMA-specific concept, so this method will always return false
+     * on GSM phones.
+     *
+     * This code was originally part of the InCallScreen.checkIsOtaCall() method.
+     */
+    public static boolean isOtaspCallIntent(Intent intent) {
+        if (DBG) log("isOtaspCallIntent(" + intent + ")...");
+        PhoneGlobals app = PhoneGlobals.getInstance();
+        Phone phone = app.mCM.getDefaultPhone();
+
+        if (intent == null) {
+            return false;
+        }
+        if (!TelephonyCapabilities.supportsOtasp(phone)) {
+            return false;
+        }
+
+        String action = intent.getAction();
+        if (action == null) {
+            return false;
+        }
+        if (!action.equals(Intent.ACTION_CALL)) {
+            if (DBG) log("isOtaspCallIntent: not a CALL action: '" + action + "' ==> not OTASP");
+            return false;
+        }
+
+        if ((app.cdmaOtaScreenState == null) || (app.cdmaOtaProvisionData == null)) {
+            // Uh oh -- something wrong with our internal OTASP state.
+            // (Since this is an OTASP-capable device, these objects
+            // *should* have already been created by PhoneApp.onCreate().)
+            throw new IllegalStateException("isOtaspCallIntent: "
+                                            + "app.cdmaOta* objects(s) not initialized");
+        }
+
+        // This is an OTASP call iff the number we're trying to dial is one of
+        // the magic OTASP numbers.
+        String number;
+        try {
+            number = PhoneUtils.getInitialNumber(intent);
+        } catch (PhoneUtils.VoiceMailNumberMissingException ex) {
+            // This was presumably a "voicemail:" intent, so it's
+            // obviously not an OTASP number.
+            if (DBG) log("isOtaspCallIntent: VoiceMailNumberMissingException => not OTASP");
+            return false;
+        }
+        if (phone.isOtaSpNumber(number)) {
+            if (DBG) log("isOtaSpNumber: ACTION_CALL to '" + number + "' ==> OTASP call!");
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Set up for an OTASP call.
+     *
+     * This method is called as part of the CallController placeCall() sequence
+     * before initiating an outgoing OTASP call.
+     *
+     * The purpose of this method is mainly to create and initialize the
+     * OtaUtils instance, along with some other misc pre-OTASP cleanup.
+     */
+    public static void setupOtaspCall(Intent intent) {
+        if (DBG) log("setupOtaspCall(): preparing for OTASP call to " + intent);
+        PhoneGlobals app = PhoneGlobals.getInstance();
+
+        if (app.otaUtils != null) {
+            // An OtaUtils instance already exists, presumably from a prior OTASP call.
+            // Nuke the old one and start this call with a fresh instance.
+            Log.i(LOG_TAG, "setupOtaspCall: "
+                  + "OtaUtils already exists; replacing with new instance...");
+        }
+
+        // Create the OtaUtils instance.
+        app.otaUtils = new OtaUtils(app.getApplicationContext(), true /* interactive */);
+        if (DBG) log("- created OtaUtils: " + app.otaUtils);
+
+        // NOTE we still need to call OtaUtils.updateUiWidgets() once the
+        // InCallScreen instance is ready; see InCallScreen.checkOtaspStateOnResume()
+
+        // Make sure the InCallScreen knows that it needs to switch into OTASP mode.
+        //
+        // NOTE in gingerbread and earlier, we used to do
+        //     setInCallScreenMode(InCallScreenMode.OTA_NORMAL);
+        // directly in the InCallScreen, back when this check happened inside the InCallScreen.
+        //
+        // But now, set the global CdmaOtaInCallScreenUiState object into
+        // NORMAL mode, which will then cause the InCallScreen (when it
+        // comes up) to realize that an OTA call is active.
+
+        app.otaUtils.setCdmaOtaInCallScreenUiState(
+            OtaUtils.CdmaOtaInCallScreenUiState.State.NORMAL);
+
+        // TODO(OTASP): note app.inCallUiState.inCallScreenMode and
+        // app.cdmaOtaInCallScreenUiState.state are mostly redundant.  Combine them.
+        app.inCallUiState.inCallScreenMode = InCallUiState.InCallScreenMode.OTA_NORMAL;
+
+        // TODO(OTASP / bug 5092031): we ideally should call
+        // otaShowListeningScreen() here to make sure that the DTMF dialpad
+        // becomes visible at the start of the "*228" call:
+        //
+        //  // ...and get the OTASP-specific UI into the right state.
+        //  app.otaUtils.otaShowListeningScreen();
+        //  if (app.otaUtils.mInCallScreen != null) {
+        //      app.otaUtils.mInCallScreen.requestUpdateScreen();
+        //  }
+        //
+        // But this doesn't actually work; the call to otaShowListeningScreen()
+        // *doesn't* actually bring up the listening screen, since the
+        // cdmaOtaConfigData.otaShowListeningScreen config parameter hasn't been
+        // initialized (we haven't run readXmlSettings() yet at this point!)
+
+        // Also, since the OTA call is now just starting, clear out
+        // the "committed" flag in app.cdmaOtaProvisionData.
+        if (app.cdmaOtaProvisionData != null) {
+            app.cdmaOtaProvisionData.isOtaCallCommitted = false;
+        }
+    }
+
+    private void setSpeaker(boolean state) {
+        if (DBG) log("setSpeaker : " + state );
+
+        if (!mInteractive) {
+            if (DBG) log("non-interactive mode, ignoring setSpeaker.");
+            return;
+        }
+
+        if (state == PhoneUtils.isSpeakerOn(mContext)) {
+            if (DBG) log("no change. returning");
+            return;
+        }
+
+        if (state && mInCallScreen.isBluetoothAvailable()
+                && mInCallScreen.isBluetoothAudioConnected()) {
+            mInCallScreen.disconnectBluetoothAudio();
+        }
+        PhoneUtils.turnOnSpeaker(mContext, state, true);
+    }
+
+    /**
+     * Handles OTA Provision events from the telephony layer.
+     * These events come in to this method whether or not
+     * the InCallScreen is visible.
+     *
+     * Possible events are:
+     * OTA Commit Event - OTA provisioning was successful
+     * SPC retries exceeded - SPC failure retries has exceeded, and Phone needs to
+     *    power down.
+     */
+    public void onOtaProvisionStatusChanged(AsyncResult r) {
+        int OtaStatus[] = (int[]) r.result;
+        if (DBG) log("Provision status event!");
+        if (DBG) log("onOtaProvisionStatusChanged(): status = "
+                     + OtaStatus[0] + " ==> " + otaProvisionStatusToString(OtaStatus[0]));
+
+        // In practice, in a normal successful OTASP call, events come in as follows:
+        //   - SPL_UNLOCKED within a couple of seconds after the call starts
+        //   - then a delay of around 45 seconds
+        //   - then PRL_DOWNLOADED and MDN_DOWNLOADED and COMMITTED within a span of 2 seconds
+
+        switch(OtaStatus[0]) {
+            case Phone.CDMA_OTA_PROVISION_STATUS_SPC_RETRIES_EXCEEDED:
+                if (DBG) log("onOtaProvisionStatusChanged(): RETRIES EXCEEDED");
+                updateOtaspProgress();
+                mApplication.cdmaOtaProvisionData.otaSpcUptime = SystemClock.elapsedRealtime();
+                if (mInteractive) {
+                    otaShowSpcErrorNotice(OTA_SPC_TIMEOUT);
+                } else {
+                    sendOtaspResult(OTASP_FAILURE_SPC_RETRIES);
+                }
+                // Power.shutdown();
+                break;
+
+            case Phone.CDMA_OTA_PROVISION_STATUS_COMMITTED:
+                if (DBG) {
+                    log("onOtaProvisionStatusChanged(): DONE, isOtaCallCommitted set to true");
+                }
+                mApplication.cdmaOtaProvisionData.isOtaCallCommitted = true;
+                if (mApplication.cdmaOtaScreenState.otaScreenState !=
+                    CdmaOtaScreenState.OtaScreenState.OTA_STATUS_UNDEFINED) {
+                    updateOtaspProgress();
+                }
+
+                break;
+
+            case Phone.CDMA_OTA_PROVISION_STATUS_SPL_UNLOCKED:
+            case Phone.CDMA_OTA_PROVISION_STATUS_A_KEY_EXCHANGED:
+            case Phone.CDMA_OTA_PROVISION_STATUS_SSD_UPDATED:
+            case Phone.CDMA_OTA_PROVISION_STATUS_NAM_DOWNLOADED:
+            case Phone.CDMA_OTA_PROVISION_STATUS_MDN_DOWNLOADED:
+            case Phone.CDMA_OTA_PROVISION_STATUS_IMSI_DOWNLOADED:
+            case Phone.CDMA_OTA_PROVISION_STATUS_PRL_DOWNLOADED:
+            case Phone.CDMA_OTA_PROVISION_STATUS_OTAPA_STARTED:
+            case Phone.CDMA_OTA_PROVISION_STATUS_OTAPA_STOPPED:
+            case Phone.CDMA_OTA_PROVISION_STATUS_OTAPA_ABORTED:
+                // Only update progress when OTA call is in normal state
+                if (getCdmaOtaInCallScreenUiState() == CdmaOtaInCallScreenUiState.State.NORMAL) {
+                    if (DBG) log("onOtaProvisionStatusChanged(): change to ProgressScreen");
+                    updateOtaspProgress();
+                }
+                break;
+
+            default:
+                if (DBG) log("onOtaProvisionStatusChanged(): Ignoring OtaStatus " + OtaStatus[0]);
+                break;
+        }
+    }
+
+    /**
+     * Handle a disconnect event from the OTASP call.
+     */
+    public void onOtaspDisconnect() {
+        if (DBG) log("onOtaspDisconnect()...");
+        // We only handle this event explicitly in non-interactive mode.
+        // (In interactive mode, the InCallScreen does any post-disconnect
+        // cleanup.)
+        if (!mInteractive) {
+            // Send a success or failure indication back to our caller.
+            updateNonInteractiveOtaSuccessFailure();
+        }
+    }
+
+    private void otaShowHome() {
+        if (DBG) log("otaShowHome()...");
+        mApplication.cdmaOtaScreenState.otaScreenState =
+                CdmaOtaScreenState.OtaScreenState.OTA_STATUS_UNDEFINED;
+        mInCallScreen.endInCallScreenSession();
+        Intent intent = new Intent(Intent.ACTION_MAIN);
+        intent.addCategory (Intent.CATEGORY_HOME);
+        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        mContext.startActivityAsUser(intent, UserHandle.CURRENT);
+        return;
+    }
+
+    private void otaSkipActivation() {
+        if (DBG) log("otaSkipActivation()...");
+
+        sendOtaspResult(OTASP_USER_SKIPPED);
+
+        if (mInteractive) mInCallScreen.finish();
+        return;
+    }
+
+    /**
+     * Actually initiate the OTASP call.  This method is triggered by the
+     * onscreen "Activate" button, and is only used in interactive mode.
+     */
+    private void otaPerformActivation() {
+        if (DBG) log("otaPerformActivation()...");
+        if (!mInteractive) {
+            // We shouldn't ever get here in non-interactive mode!
+            Log.w(LOG_TAG, "otaPerformActivation: not interactive!");
+            return;
+        }
+
+        if (!mApplication.cdmaOtaProvisionData.inOtaSpcState) {
+            // Place an outgoing call to the special OTASP number:
+            Intent newIntent = new Intent(Intent.ACTION_CALL);
+            newIntent.setData(Uri.fromParts(Constants.SCHEME_TEL, OTASP_NUMBER, null));
+
+            // Initiate the outgoing call:
+            mApplication.callController.placeCall(newIntent);
+
+            // ...and get the OTASP-specific UI into the right state.
+            otaShowListeningScreen();
+            mInCallScreen.requestUpdateScreen();
+        }
+        return;
+    }
+
+    /**
+     * Show Activation Screen when phone powers up and OTA provision is
+     * required. Also shown when activation fails and user needs
+     * to re-attempt it. Contains ACTIVATE and SKIP buttons
+     * which allow user to start OTA activation or skip the activation process.
+     */
+    public void otaShowActivateScreen() {
+        if (DBG) log("otaShowActivateScreen()...");
+        if (mApplication.cdmaOtaConfigData.otaShowActivationScreen
+                == OTA_SHOW_ACTIVATION_SCREEN_ON) {
+            if (DBG) log("otaShowActivateScreen(): show activation screen");
+            if (!isDialerOpened()) {
+                otaScreenInitialize();
+                mOtaWidgetData.otaSkipButton.setVisibility(sIsWizardMode ?
+                        View.VISIBLE : View.INVISIBLE);
+                mOtaWidgetData.otaTextActivate.setVisibility(View.VISIBLE);
+                mOtaWidgetData.callCardOtaButtonsActivate.setVisibility(View.VISIBLE);
+            }
+            mApplication.cdmaOtaScreenState.otaScreenState =
+                    CdmaOtaScreenState.OtaScreenState.OTA_STATUS_ACTIVATION;
+        } else {
+            if (DBG) log("otaShowActivateScreen(): show home screen");
+            otaShowHome();
+        }
+     }
+
+    /**
+     * Show "Listen for Instruction" screen during OTA call. Shown when OTA Call
+     * is initiated and user needs to listen for network instructions and press
+     * appropriate DTMF digits to proceed to the "Programming in Progress" phase.
+     */
+    private void otaShowListeningScreen() {
+        if (DBG) log("otaShowListeningScreen()...");
+        if (!mInteractive) {
+            // We shouldn't ever get here in non-interactive mode!
+            Log.w(LOG_TAG, "otaShowListeningScreen: not interactive!");
+            return;
+        }
+
+        if (mApplication.cdmaOtaConfigData.otaShowListeningScreen
+                == OTA_SHOW_LISTENING_SCREEN_ON) {
+            if (DBG) log("otaShowListeningScreen(): show listening screen");
+            if (!isDialerOpened()) {
+                otaScreenInitialize();
+                mOtaWidgetData.otaTextListenProgress.setVisibility(View.VISIBLE);
+                mOtaWidgetData.otaTextListenProgress.setText(R.string.ota_listen);
+                mOtaWidgetData.otaDtmfDialerView.setVisibility(View.VISIBLE);
+                mOtaWidgetData.callCardOtaButtonsListenProgress.setVisibility(View.VISIBLE);
+                mOtaWidgetData.otaSpeakerButton.setVisibility(View.VISIBLE);
+                boolean speakerOn = PhoneUtils.isSpeakerOn(mContext);
+                mOtaWidgetData.otaSpeakerButton.setChecked(speakerOn);
+            }
+            mApplication.cdmaOtaScreenState.otaScreenState =
+                    CdmaOtaScreenState.OtaScreenState.OTA_STATUS_LISTENING;
+        } else {
+            if (DBG) log("otaShowListeningScreen(): show progress screen");
+            otaShowInProgressScreen();
+        }
+    }
+
+    /**
+     * Do any necessary updates (of onscreen UI, for example)
+     * based on the latest status of the OTASP call.
+     */
+    private void updateOtaspProgress() {
+        if (DBG) log("updateOtaspProgress()...  mInteractive = " + mInteractive);
+        if (mInteractive) {
+            // On regular phones we just call through to
+            // otaShowInProgressScreen(), which updates the
+            // InCallScreen's onscreen UI.
+            otaShowInProgressScreen();
+        } else {
+            // We're not using the InCallScreen to show OTA progress.
+
+            // For now, at least, there's nothing to do here.
+            // The overall "success" or "failure" indication we send back
+            // (to our caller) is triggered by the DISCONNECT event;
+            // see updateNonInteractiveOtaSuccessFailure().
+
+            // But if we ever need to send *intermediate* progress updates back
+            // to our caller, we'd do that here, possbily using the same
+            // PendingIntent that we already use to indicate success or failure.
+        }
+    }
+
+    /**
+     * When a non-interactive OTASP call completes, send a success or
+     * failure indication back to our caller.
+     *
+     * This is basically the non-interactive equivalent of
+     * otaShowSuccessFailure().
+     */
+    private void updateNonInteractiveOtaSuccessFailure() {
+        // This is basically the same logic as otaShowSuccessFailure(): we
+        // check the isOtaCallCommitted bit, and if that's true it means
+        // that activation was successful.
+
+        if (DBG) log("updateNonInteractiveOtaSuccessFailure(): isOtaCallCommitted = "
+                     + mApplication.cdmaOtaProvisionData.isOtaCallCommitted);
+        int resultCode =
+                mApplication.cdmaOtaProvisionData.isOtaCallCommitted
+                ? OTASP_SUCCESS : OTASP_FAILURE;
+        sendOtaspResult(resultCode);
+    }
+
+    /**
+     * Sends the specified OTASP result code back to our caller (presumably
+     * SetupWizard) via the PendingIntent that they originally sent along with
+     * the ACTION_PERFORM_CDMA_PROVISIONING intent.
+     */
+    private void sendOtaspResult(int resultCode) {
+        if (DBG) log("sendOtaspResult: resultCode = " + resultCode);
+
+        // Pass the success or failure indication back to our caller by
+        // adding an additional extra to the PendingIntent we already
+        // have.
+        // (NB: there's a PendingIntent send() method that takes a resultCode
+        // directly, but we can't use that here since that call is only
+        // meaningful for pending intents that are actually used as activity
+        // results.)
+
+        Intent extraStuff = new Intent();
+        extraStuff.putExtra(EXTRA_OTASP_RESULT_CODE, resultCode);
+        // When we call PendingIntent.send() below, the extras from this
+        // intent will get merged with any extras already present in
+        // cdmaOtaScreenState.otaspResultCodePendingIntent.
+
+        if (mApplication.cdmaOtaScreenState == null) {
+            Log.e(LOG_TAG, "updateNonInteractiveOtaSuccessFailure: no cdmaOtaScreenState object!");
+            return;
+        }
+        if (mApplication.cdmaOtaScreenState.otaspResultCodePendingIntent == null) {
+            Log.w(LOG_TAG, "updateNonInteractiveOtaSuccessFailure: "
+                  + "null otaspResultCodePendingIntent!");
+            return;
+        }
+
+        try {
+            if (DBG) log("- sendOtaspResult:  SENDING PENDING INTENT: " +
+                         mApplication.cdmaOtaScreenState.otaspResultCodePendingIntent);
+            mApplication.cdmaOtaScreenState.otaspResultCodePendingIntent.send(
+                    mContext,
+                    0, /* resultCode (unused) */
+                    extraStuff);
+        } catch (CanceledException e) {
+            // should never happen because no code cancels the pending intent right now,
+            Log.e(LOG_TAG, "PendingIntent send() failed: " + e);
+        }
+    }
+
+    /**
+     * Show "Programming In Progress" screen during OTA call. Shown when OTA
+     * provisioning is in progress after user has selected an option.
+     */
+    private void otaShowInProgressScreen() {
+        if (DBG) log("otaShowInProgressScreen()...");
+        if (!mInteractive) {
+            // We shouldn't ever get here in non-interactive mode!
+            Log.w(LOG_TAG, "otaShowInProgressScreen: not interactive!");
+            return;
+        }
+
+        mApplication.cdmaOtaScreenState.otaScreenState =
+            CdmaOtaScreenState.OtaScreenState.OTA_STATUS_PROGRESS;
+
+        if ((mOtaWidgetData == null) || (mInCallScreen == null)) {
+            Log.w(LOG_TAG, "otaShowInProgressScreen: UI widgets not set up yet!");
+
+            // TODO(OTASP): our CdmaOtaScreenState is now correct; we just set
+            // it to OTA_STATUS_PROGRESS.  But we still need to make sure that
+            // when the InCallScreen eventually comes to the foreground, it
+            // notices that state and does all the same UI updating we do below.
+            return;
+        }
+
+        if (!isDialerOpened()) {
+            otaScreenInitialize();
+            mOtaWidgetData.otaTextListenProgress.setVisibility(View.VISIBLE);
+            mOtaWidgetData.otaTextListenProgress.setText(R.string.ota_progress);
+            mOtaWidgetData.otaTextProgressBar.setVisibility(View.VISIBLE);
+            mOtaWidgetData.callCardOtaButtonsListenProgress.setVisibility(View.VISIBLE);
+            mOtaWidgetData.otaSpeakerButton.setVisibility(View.VISIBLE);
+            boolean speakerOn = PhoneUtils.isSpeakerOn(mContext);
+            mOtaWidgetData.otaSpeakerButton.setChecked(speakerOn);
+        }
+    }
+
+    /**
+     * Show programming failure dialog when OTA provisioning fails.
+     * If OTA provisioning attempts fail more than 3 times, then unsuccessful
+     * dialog is shown. Otherwise a two-second notice is shown with unsuccessful
+     * information. When notice expires, phone returns to activation screen.
+     */
+    private void otaShowProgramFailure(int length) {
+        if (DBG) log("otaShowProgramFailure()...");
+        mApplication.cdmaOtaProvisionData.activationCount++;
+        if ((mApplication.cdmaOtaProvisionData.activationCount <
+                mApplication.cdmaOtaConfigData.otaShowActivateFailTimes)
+                && (mApplication.cdmaOtaConfigData.otaShowActivationScreen ==
+                OTA_SHOW_ACTIVATION_SCREEN_ON)) {
+            if (DBG) log("otaShowProgramFailure(): activationCount"
+                    + mApplication.cdmaOtaProvisionData.activationCount);
+            if (DBG) log("otaShowProgramFailure(): show failure notice");
+            otaShowProgramFailureNotice(length);
+        } else {
+            if (DBG) log("otaShowProgramFailure(): show failure dialog");
+            otaShowProgramFailureDialog();
+        }
+    }
+
+    /**
+     * Show either programming success dialog when OTA provisioning succeeds, or
+     * programming failure dialog when it fails. See {@link #otaShowProgramFailure}
+     * for more details.
+     */
+    public void otaShowSuccessFailure() {
+        if (DBG) log("otaShowSuccessFailure()...");
+        if (!mInteractive) {
+            // We shouldn't ever get here in non-interactive mode!
+            Log.w(LOG_TAG, "otaShowSuccessFailure: not interactive!");
+            return;
+        }
+
+        otaScreenInitialize();
+        if (DBG) log("otaShowSuccessFailure(): isOtaCallCommitted"
+                + mApplication.cdmaOtaProvisionData.isOtaCallCommitted);
+        if (mApplication.cdmaOtaProvisionData.isOtaCallCommitted) {
+            if (DBG) log("otaShowSuccessFailure(), show success dialog");
+            otaShowProgramSuccessDialog();
+        } else {
+            if (DBG) log("otaShowSuccessFailure(), show failure dialog");
+            otaShowProgramFailure(OTA_FAILURE_DIALOG_TIMEOUT);
+        }
+        return;
+    }
+
+    /**
+     * Show programming failure dialog when OTA provisioning fails more than 3
+     * times.
+     */
+    private void otaShowProgramFailureDialog() {
+        if (DBG) log("otaShowProgramFailureDialog()...");
+        mApplication.cdmaOtaScreenState.otaScreenState =
+                CdmaOtaScreenState.OtaScreenState.OTA_STATUS_SUCCESS_FAILURE_DLG;
+        mOtaWidgetData.otaTitle.setText(R.string.ota_title_problem_with_activation);
+        mOtaWidgetData.otaTextSuccessFail.setVisibility(View.VISIBLE);
+        mOtaWidgetData.otaTextSuccessFail.setText(R.string.ota_unsuccessful);
+        mOtaWidgetData.callCardOtaButtonsFailSuccess.setVisibility(View.VISIBLE);
+        mOtaWidgetData.otaTryAgainButton.setVisibility(View.VISIBLE);
+        //close the dialer if open
+        if (isDialerOpened()) {
+            mOtaCallCardDtmfDialer.closeDialer(false);
+        }
+    }
+
+    /**
+     * Show programming success dialog when OTA provisioning succeeds.
+     */
+    private void otaShowProgramSuccessDialog() {
+        if (DBG) log("otaShowProgramSuccessDialog()...");
+        mApplication.cdmaOtaScreenState.otaScreenState =
+                CdmaOtaScreenState.OtaScreenState.OTA_STATUS_SUCCESS_FAILURE_DLG;
+        mOtaWidgetData.otaTitle.setText(R.string.ota_title_activate_success);
+        mOtaWidgetData.otaTextSuccessFail.setVisibility(View.VISIBLE);
+        mOtaWidgetData.otaTextSuccessFail.setText(R.string.ota_successful);
+        mOtaWidgetData.callCardOtaButtonsFailSuccess.setVisibility(View.VISIBLE);
+        mOtaWidgetData.otaNextButton.setVisibility(View.VISIBLE);
+        //close the dialer if open
+        if (isDialerOpened()) {
+            mOtaCallCardDtmfDialer.closeDialer(false);
+        }
+    }
+
+    /**
+     * Show SPC failure notice when SPC attempts exceed 15 times.
+     * During OTA provisioning, if SPC code is incorrect OTA provisioning will
+     * fail. When SPC attempts are over 15, it shows SPC failure notice for one minute and
+     * then phone will power down.
+     */
+    private void otaShowSpcErrorNotice(int length) {
+        if (DBG) log("otaShowSpcErrorNotice()...");
+        if (mOtaWidgetData.spcErrorDialog == null) {
+            mApplication.cdmaOtaProvisionData.inOtaSpcState = true;
+            DialogInterface.OnKeyListener keyListener;
+            keyListener = new DialogInterface.OnKeyListener() {
+                public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event) {
+                    log("Ignoring key events...");
+                    return true;
+                }};
+            mOtaWidgetData.spcErrorDialog = new AlertDialog.Builder(mInCallScreen)
+                    .setMessage(R.string.ota_spc_failure)
+                    .setOnKeyListener(keyListener)
+                    .create();
+            mOtaWidgetData.spcErrorDialog.getWindow().addFlags(
+                    WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
+                    | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
+            mOtaWidgetData.spcErrorDialog.show();
+            //close the dialer if open
+            if (isDialerOpened()) {
+                mOtaCallCardDtmfDialer.closeDialer(false);
+            }
+            long noticeTime = length*1000;
+            if (DBG) log("otaShowSpcErrorNotice(), remaining SPC noticeTime" + noticeTime);
+            mInCallScreen.requestCloseSpcErrorNotice(noticeTime);
+        }
+    }
+
+    /**
+     * When SPC notice times out, force phone to power down.
+     */
+    public void onOtaCloseSpcNotice() {
+        if (DBG) log("onOtaCloseSpcNotice(), send shutdown intent");
+        Intent shutdown = new Intent(Intent.ACTION_REQUEST_SHUTDOWN);
+        shutdown.putExtra(Intent.EXTRA_KEY_CONFIRM, false);
+        shutdown.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        mContext.startActivity(shutdown);
+    }
+
+    /**
+     * Show two-second notice when OTA provisioning fails and number of failed attempts
+     * is less then 3.
+     */
+    private void otaShowProgramFailureNotice(int length) {
+        if (DBG) log("otaShowProgramFailureNotice()...");
+        if (mOtaWidgetData.otaFailureDialog == null) {
+            mOtaWidgetData.otaFailureDialog = new AlertDialog.Builder(mInCallScreen)
+                    .setMessage(R.string.ota_failure)
+                    .create();
+            mOtaWidgetData.otaFailureDialog.getWindow().addFlags(
+                    WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
+                    | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
+            mOtaWidgetData.otaFailureDialog.show();
+
+            long noticeTime = length*1000;
+            mInCallScreen.requestCloseOtaFailureNotice(noticeTime);
+        }
+    }
+
+    /**
+     * Handle OTA unsuccessful notice expiry. Dismisses the
+     * two-second notice and shows the activation screen.
+     */
+    public void onOtaCloseFailureNotice() {
+        if (DBG) log("onOtaCloseFailureNotice()...");
+        if (mOtaWidgetData.otaFailureDialog != null) {
+            mOtaWidgetData.otaFailureDialog.dismiss();
+            mOtaWidgetData.otaFailureDialog = null;
+        }
+        otaShowActivateScreen();
+    }
+
+    /**
+     * Initialize all OTA UI elements to be gone. Also set inCallPanel,
+     * callCard and the dialpad handle to be gone. This is called before any OTA screen
+     * gets drawn.
+     */
+    private void otaScreenInitialize() {
+        if (DBG) log("otaScreenInitialize()...");
+
+        if (!mInteractive) {
+            // We should never be doing anything with UI elements in
+            // non-interactive mode.
+            Log.w(LOG_TAG, "otaScreenInitialize: not interactive!");
+            return;
+        }
+
+        if (mInCallTouchUi != null) mInCallTouchUi.setVisibility(View.GONE);
+        if (mCallCard != null) {
+            mCallCard.setVisibility(View.GONE);
+            // TODO: try removing this.
+            mCallCard.hideCallCardElements();
+        }
+
+        mOtaWidgetData.otaTitle.setText(R.string.ota_title_activate);
+        mOtaWidgetData.otaTextActivate.setVisibility(View.GONE);
+        mOtaWidgetData.otaTextListenProgress.setVisibility(View.GONE);
+        mOtaWidgetData.otaTextProgressBar.setVisibility(View.GONE);
+        mOtaWidgetData.otaTextSuccessFail.setVisibility(View.GONE);
+        mOtaWidgetData.callCardOtaButtonsActivate.setVisibility(View.GONE);
+        mOtaWidgetData.callCardOtaButtonsListenProgress.setVisibility(View.GONE);
+        mOtaWidgetData.callCardOtaButtonsFailSuccess.setVisibility(View.GONE);
+        mOtaWidgetData.otaDtmfDialerView.setVisibility(View.GONE);
+        mOtaWidgetData.otaSpeakerButton.setVisibility(View.GONE);
+        mOtaWidgetData.otaTryAgainButton.setVisibility(View.GONE);
+        mOtaWidgetData.otaNextButton.setVisibility(View.GONE);
+        mOtaWidgetData.otaUpperWidgets.setVisibility(View.VISIBLE);
+        mOtaWidgetData.otaSkipButton.setVisibility(View.VISIBLE);
+    }
+
+    public void hideOtaScreen() {
+        if (DBG) log("hideOtaScreen()...");
+
+        mOtaWidgetData.callCardOtaButtonsActivate.setVisibility(View.GONE);
+        mOtaWidgetData.callCardOtaButtonsListenProgress.setVisibility(View.GONE);
+        mOtaWidgetData.callCardOtaButtonsFailSuccess.setVisibility(View.GONE);
+        mOtaWidgetData.otaUpperWidgets.setVisibility(View.GONE);
+    }
+
+    public boolean isDialerOpened() {
+        boolean retval = (mOtaCallCardDtmfDialer != null && mOtaCallCardDtmfDialer.isOpened());
+        if (DBG) log("- isDialerOpened() ==> " + retval);
+        return retval;
+    }
+
+    /**
+     * Show the appropriate OTA screen based on the current state of OTA call.
+     *
+     * This is called from the InCallScreen when the screen needs to be
+     * refreshed (and thus is only ever used in interactive mode.)
+     *
+     * Since this is called as part of the InCallScreen.updateScreen() sequence,
+     * this method does *not* post an mInCallScreen.requestUpdateScreen()
+     * request.
+     */
+    public void otaShowProperScreen() {
+        if (DBG) log("otaShowProperScreen()...");
+        if (!mInteractive) {
+            // We shouldn't ever get here in non-interactive mode!
+            Log.w(LOG_TAG, "otaShowProperScreen: not interactive!");
+            return;
+        }
+
+        if ((mInCallScreen != null) && mInCallScreen.isForegroundActivity()) {
+            if (DBG) log("otaShowProperScreen(): InCallScreen in foreground, currentstate = "
+                    + mApplication.cdmaOtaScreenState.otaScreenState);
+            if (mInCallTouchUi != null) {
+                mInCallTouchUi.setVisibility(View.GONE);
+            }
+            if (mCallCard != null) {
+                mCallCard.setVisibility(View.GONE);
+            }
+            if (mApplication.cdmaOtaScreenState.otaScreenState
+                    == CdmaOtaScreenState.OtaScreenState.OTA_STATUS_ACTIVATION) {
+                otaShowActivateScreen();
+            } else if (mApplication.cdmaOtaScreenState.otaScreenState
+                    == CdmaOtaScreenState.OtaScreenState.OTA_STATUS_LISTENING) {
+                otaShowListeningScreen();
+            } else if (mApplication.cdmaOtaScreenState.otaScreenState
+                    == CdmaOtaScreenState.OtaScreenState.OTA_STATUS_PROGRESS) {
+                otaShowInProgressScreen();
+            }
+
+            if (mApplication.cdmaOtaProvisionData.inOtaSpcState) {
+                otaShowSpcErrorNotice(getOtaSpcDisplayTime());
+            }
+        }
+    }
+
+    /**
+     * Read configuration values for each OTA screen from config.xml.
+     * These configuration values control visibility of each screen.
+     */
+    private void readXmlSettings() {
+        if (DBG) log("readXmlSettings()...");
+        if (mApplication.cdmaOtaConfigData.configComplete) {
+            return;
+        }
+
+        mApplication.cdmaOtaConfigData.configComplete = true;
+        int tmpOtaShowActivationScreen =
+                mContext.getResources().getInteger(R.integer.OtaShowActivationScreen);
+        mApplication.cdmaOtaConfigData.otaShowActivationScreen = tmpOtaShowActivationScreen;
+        if (DBG) log("readXmlSettings(), otaShowActivationScreen = "
+                + mApplication.cdmaOtaConfigData.otaShowActivationScreen);
+
+        int tmpOtaShowListeningScreen =
+                mContext.getResources().getInteger(R.integer.OtaShowListeningScreen);
+        mApplication.cdmaOtaConfigData.otaShowListeningScreen = tmpOtaShowListeningScreen;
+        if (DBG) log("readXmlSettings(), otaShowListeningScreen = "
+                + mApplication.cdmaOtaConfigData.otaShowListeningScreen);
+
+        int tmpOtaShowActivateFailTimes =
+                mContext.getResources().getInteger(R.integer.OtaShowActivateFailTimes);
+        mApplication.cdmaOtaConfigData.otaShowActivateFailTimes = tmpOtaShowActivateFailTimes;
+        if (DBG) log("readXmlSettings(), otaShowActivateFailTimes = "
+                + mApplication.cdmaOtaConfigData.otaShowActivateFailTimes);
+
+        int tmpOtaPlaySuccessFailureTone =
+                mContext.getResources().getInteger(R.integer.OtaPlaySuccessFailureTone);
+        mApplication.cdmaOtaConfigData.otaPlaySuccessFailureTone = tmpOtaPlaySuccessFailureTone;
+        if (DBG) log("readXmlSettings(), otaPlaySuccessFailureTone = "
+                + mApplication.cdmaOtaConfigData.otaPlaySuccessFailureTone);
+    }
+
+    /**
+     * Handle the click events for OTA buttons.
+     */
+    public void onClickHandler(int id) {
+        switch (id) {
+            case R.id.otaEndButton:
+                onClickOtaEndButton();
+                break;
+
+            case R.id.otaSpeakerButton:
+                onClickOtaSpeakerButton();
+                break;
+
+            case R.id.otaActivateButton:
+                onClickOtaActivateButton();
+                break;
+
+            case R.id.otaSkipButton:
+                onClickOtaActivateSkipButton();
+                break;
+
+            case R.id.otaNextButton:
+                onClickOtaActivateNextButton();
+                break;
+
+            case R.id.otaTryAgainButton:
+                onClickOtaTryAgainButton();
+                break;
+
+            default:
+                if (DBG) log ("onClickHandler: received a click event for unrecognized id");
+                break;
+        }
+    }
+
+    private void onClickOtaTryAgainButton() {
+        if (DBG) log("Activation Try Again Clicked!");
+        if (!mApplication.cdmaOtaProvisionData.inOtaSpcState) {
+            otaShowActivateScreen();
+        }
+    }
+
+    private void onClickOtaEndButton() {
+        if (DBG) log("Activation End Call Button Clicked!");
+        if (!mApplication.cdmaOtaProvisionData.inOtaSpcState) {
+            if (PhoneUtils.hangup(mApplication.mCM) == false) {
+                // If something went wrong when placing the OTA call,
+                // the screen is not updated by the call disconnect
+                // handler and we have to do it here
+                setSpeaker(false);
+                mInCallScreen.handleOtaCallEnd();
+            }
+        }
+    }
+
+    private void onClickOtaSpeakerButton() {
+        if (DBG) log("OTA Speaker button Clicked!");
+        if (!mApplication.cdmaOtaProvisionData.inOtaSpcState) {
+            boolean isChecked = !PhoneUtils.isSpeakerOn(mContext);
+            setSpeaker(isChecked);
+        }
+    }
+
+    private void onClickOtaActivateButton() {
+        if (DBG) log("Call Activation Clicked!");
+        otaPerformActivation();
+    }
+
+    private void onClickOtaActivateSkipButton() {
+        if (DBG) log("Activation Skip Clicked!");
+        DialogInterface.OnKeyListener keyListener;
+        keyListener = new DialogInterface.OnKeyListener() {
+            public boolean onKey(DialogInterface dialog, int keyCode,
+                    KeyEvent event) {
+                if (DBG) log("Ignoring key events...");
+                return true;
+            }
+        };
+        mOtaWidgetData.otaSkipConfirmationDialog = new AlertDialog.Builder(mInCallScreen)
+                .setTitle(R.string.ota_skip_activation_dialog_title)
+                .setMessage(R.string.ota_skip_activation_dialog_message)
+                .setPositiveButton(
+                    android.R.string.ok,
+                    // "OK" means "skip activation".
+                    new AlertDialog.OnClickListener() {
+                        public void onClick(DialogInterface dialog, int which) {
+                            otaSkipActivation();
+                        }
+                    })
+                .setNegativeButton(
+                    android.R.string.cancel,
+                    // "Cancel" means just dismiss the dialog.
+                    // Don't actually start an activation call.
+                    null)
+                .setOnKeyListener(keyListener)
+                .create();
+        mOtaWidgetData.otaSkipConfirmationDialog.show();
+    }
+
+    private void onClickOtaActivateNextButton() {
+        if (DBG) log("Dialog Next Clicked!");
+        if (!mApplication.cdmaOtaProvisionData.inOtaSpcState) {
+            mApplication.cdmaOtaScreenState.otaScreenState =
+                    CdmaOtaScreenState.OtaScreenState.OTA_STATUS_UNDEFINED;
+            otaShowHome();
+        }
+    }
+
+    public void dismissAllOtaDialogs() {
+        if (mOtaWidgetData != null) {
+            if (mOtaWidgetData.spcErrorDialog != null) {
+                if (DBG) log("- DISMISSING mSpcErrorDialog.");
+                mOtaWidgetData.spcErrorDialog.dismiss();
+                mOtaWidgetData.spcErrorDialog = null;
+            }
+            if (mOtaWidgetData.otaFailureDialog != null) {
+                if (DBG) log("- DISMISSING mOtaFailureDialog.");
+                mOtaWidgetData.otaFailureDialog.dismiss();
+                mOtaWidgetData.otaFailureDialog = null;
+            }
+        }
+    }
+
+    private int getOtaSpcDisplayTime() {
+        if (DBG) log("getOtaSpcDisplayTime()...");
+        int tmpSpcTime = 1;
+        if (mApplication.cdmaOtaProvisionData.inOtaSpcState) {
+            long tmpOtaSpcRunningTime = 0;
+            long tmpOtaSpcLeftTime = 0;
+            tmpOtaSpcRunningTime = SystemClock.elapsedRealtime();
+            tmpOtaSpcLeftTime =
+                tmpOtaSpcRunningTime - mApplication.cdmaOtaProvisionData.otaSpcUptime;
+            if (tmpOtaSpcLeftTime >= OTA_SPC_TIMEOUT*1000) {
+                tmpSpcTime = 1;
+            } else {
+                tmpSpcTime = OTA_SPC_TIMEOUT - (int)tmpOtaSpcLeftTime/1000;
+            }
+        }
+        if (DBG) log("getOtaSpcDisplayTime(), time for SPC error notice: " + tmpSpcTime);
+        return tmpSpcTime;
+    }
+
+    /**
+     * Initialize the OTA widgets for all OTA screens.
+     */
+    private void initOtaInCallScreen() {
+        if (DBG) log("initOtaInCallScreen()...");
+        mOtaWidgetData.otaTitle = (TextView) mInCallScreen.findViewById(R.id.otaTitle);
+        mOtaWidgetData.otaTextActivate = (TextView) mInCallScreen.findViewById(R.id.otaActivate);
+        mOtaWidgetData.otaTextActivate.setVisibility(View.GONE);
+        mOtaWidgetData.otaTextListenProgress =
+                (TextView) mInCallScreen.findViewById(R.id.otaListenProgress);
+        mOtaWidgetData.otaTextProgressBar =
+                (ProgressBar) mInCallScreen.findViewById(R.id.progress_large);
+        mOtaWidgetData.otaTextProgressBar.setIndeterminate(true);
+        mOtaWidgetData.otaTextSuccessFail =
+                (TextView) mInCallScreen.findViewById(R.id.otaSuccessFailStatus);
+
+        mOtaWidgetData.otaUpperWidgets =
+                (ViewGroup) mInCallScreen.findViewById(R.id.otaUpperWidgets);
+        mOtaWidgetData.callCardOtaButtonsListenProgress =
+                (View) mInCallScreen.findViewById(R.id.callCardOtaListenProgress);
+        mOtaWidgetData.callCardOtaButtonsActivate =
+                (View) mInCallScreen.findViewById(R.id.callCardOtaActivate);
+        mOtaWidgetData.callCardOtaButtonsFailSuccess =
+                (View) mInCallScreen.findViewById(R.id.callCardOtaFailOrSuccessful);
+
+        mOtaWidgetData.otaEndButton = (Button) mInCallScreen.findViewById(R.id.otaEndButton);
+        mOtaWidgetData.otaEndButton.setOnClickListener(mInCallScreen);
+        mOtaWidgetData.otaSpeakerButton =
+                (ToggleButton) mInCallScreen.findViewById(R.id.otaSpeakerButton);
+        mOtaWidgetData.otaSpeakerButton.setOnClickListener(mInCallScreen);
+        mOtaWidgetData.otaActivateButton =
+                (Button) mInCallScreen.findViewById(R.id.otaActivateButton);
+        mOtaWidgetData.otaActivateButton.setOnClickListener(mInCallScreen);
+        mOtaWidgetData.otaSkipButton = (Button) mInCallScreen.findViewById(R.id.otaSkipButton);
+        mOtaWidgetData.otaSkipButton.setOnClickListener(mInCallScreen);
+        mOtaWidgetData.otaNextButton = (Button) mInCallScreen.findViewById(R.id.otaNextButton);
+        mOtaWidgetData.otaNextButton.setOnClickListener(mInCallScreen);
+        mOtaWidgetData.otaTryAgainButton =
+                (Button) mInCallScreen.findViewById(R.id.otaTryAgainButton);
+        mOtaWidgetData.otaTryAgainButton.setOnClickListener(mInCallScreen);
+
+        mOtaWidgetData.otaDtmfDialerView =
+                (DTMFTwelveKeyDialerView) mInCallScreen.findViewById(R.id.otaDtmfDialerView);
+        // Sanity-check: the otaDtmfDialerView widget should *always* be present.
+        if (mOtaWidgetData.otaDtmfDialerView == null) {
+            throw new IllegalStateException("initOtaInCallScreen: couldn't find otaDtmfDialerView");
+        }
+
+        // Create a new DTMFTwelveKeyDialer instance purely for use by the
+        // DTMFTwelveKeyDialerView ("otaDtmfDialerView") that comes from
+        // otacall_card.xml.
+        mOtaCallCardDtmfDialer = new DTMFTwelveKeyDialer(mInCallScreen,
+                                                         mOtaWidgetData.otaDtmfDialerView);
+
+        // Initialize the new DTMFTwelveKeyDialer instance.  This is
+        // needed to play local DTMF tones.
+        mOtaCallCardDtmfDialer.startDialerSession();
+
+        mOtaWidgetData.otaDtmfDialerView.setDialer(mOtaCallCardDtmfDialer);
+    }
+
+    /**
+     * Clear out all OTA UI widget elements. Needs to get called
+     * when OTA call ends or InCallScreen is destroyed.
+     * @param disableSpeaker parameter control whether Speaker should be turned off.
+     */
+    public void cleanOtaScreen(boolean disableSpeaker) {
+        if (DBG) log("OTA ends, cleanOtaScreen!");
+
+        mApplication.cdmaOtaScreenState.otaScreenState =
+                CdmaOtaScreenState.OtaScreenState.OTA_STATUS_UNDEFINED;
+        mApplication.cdmaOtaProvisionData.isOtaCallCommitted = false;
+        mApplication.cdmaOtaProvisionData.isOtaCallIntentProcessed = false;
+        mApplication.cdmaOtaProvisionData.inOtaSpcState = false;
+        mApplication.cdmaOtaProvisionData.activationCount = 0;
+        mApplication.cdmaOtaProvisionData.otaSpcUptime = 0;
+        mApplication.cdmaOtaInCallScreenUiState.state = State.UNDEFINED;
+
+        if (mInteractive && (mOtaWidgetData != null)) {
+            if (mInCallTouchUi != null) mInCallTouchUi.setVisibility(View.VISIBLE);
+            if (mCallCard != null) {
+                mCallCard.setVisibility(View.VISIBLE);
+                mCallCard.hideCallCardElements();
+            }
+
+            // Free resources from the DTMFTwelveKeyDialer instance we created
+            // in initOtaInCallScreen().
+            if (mOtaCallCardDtmfDialer != null) {
+                mOtaCallCardDtmfDialer.stopDialerSession();
+            }
+
+            mOtaWidgetData.otaTextActivate.setVisibility(View.GONE);
+            mOtaWidgetData.otaTextListenProgress.setVisibility(View.GONE);
+            mOtaWidgetData.otaTextProgressBar.setVisibility(View.GONE);
+            mOtaWidgetData.otaTextSuccessFail.setVisibility(View.GONE);
+            mOtaWidgetData.callCardOtaButtonsActivate.setVisibility(View.GONE);
+            mOtaWidgetData.callCardOtaButtonsListenProgress.setVisibility(View.GONE);
+            mOtaWidgetData.callCardOtaButtonsFailSuccess.setVisibility(View.GONE);
+            mOtaWidgetData.otaUpperWidgets.setVisibility(View.GONE);
+            mOtaWidgetData.otaDtmfDialerView.setVisibility(View.GONE);
+            mOtaWidgetData.otaNextButton.setVisibility(View.GONE);
+            mOtaWidgetData.otaTryAgainButton.setVisibility(View.GONE);
+        }
+
+        // turn off the speaker in case it was turned on
+        // but the OTA call could not be completed
+        if (disableSpeaker) {
+            setSpeaker(false);
+        }
+    }
+
+    /**
+     * Defines OTA information that needs to be maintained during
+     * an OTA call when display orientation changes.
+     */
+    public static class CdmaOtaProvisionData {
+        public boolean isOtaCallCommitted;
+        public boolean isOtaCallIntentProcessed;
+        public boolean inOtaSpcState;
+        public int activationCount;
+        public long otaSpcUptime;
+    }
+
+    /**
+     * Defines OTA screen configuration items read from config.xml
+     * and used to control OTA display.
+     */
+    public static class CdmaOtaConfigData {
+        public int otaShowActivationScreen;
+        public int otaShowListeningScreen;
+        public int otaShowActivateFailTimes;
+        public int otaPlaySuccessFailureTone;
+        public boolean configComplete;
+        public CdmaOtaConfigData() {
+            if (DBG) log("CdmaOtaConfigData constructor!");
+            otaShowActivationScreen = OTA_SHOW_ACTIVATION_SCREEN_OFF;
+            otaShowListeningScreen = OTA_SHOW_LISTENING_SCREEN_OFF;
+            otaShowActivateFailTimes = OTA_SHOW_ACTIVATE_FAIL_COUNT_OFF;
+            otaPlaySuccessFailureTone = OTA_PLAY_SUCCESS_FAILURE_TONE_OFF;
+        }
+    }
+
+    /**
+     * The state of the OTA InCallScreen UI.
+     */
+    public static class CdmaOtaInCallScreenUiState {
+        public enum State {
+            UNDEFINED,
+            NORMAL,
+            ENDED
+        }
+
+        public State state;
+
+        public CdmaOtaInCallScreenUiState() {
+            if (DBG) log("CdmaOtaInCallScreenState: constructor init to UNDEFINED");
+            state = CdmaOtaInCallScreenUiState.State.UNDEFINED;
+        }
+    }
+
+    /**
+     * Save the Ota InCallScreen UI state
+     */
+    public void setCdmaOtaInCallScreenUiState(CdmaOtaInCallScreenUiState.State state) {
+        if (DBG) log("setCdmaOtaInCallScreenState: " + state);
+        mApplication.cdmaOtaInCallScreenUiState.state = state;
+    }
+
+    /**
+     * Get the Ota InCallScreen UI state
+     */
+    public CdmaOtaInCallScreenUiState.State getCdmaOtaInCallScreenUiState() {
+        if (DBG) log("getCdmaOtaInCallScreenState: "
+                     + mApplication.cdmaOtaInCallScreenUiState.state);
+        return mApplication.cdmaOtaInCallScreenUiState.state;
+    }
+
+    /**
+     * The OTA screen state machine.
+     */
+    public static class CdmaOtaScreenState {
+        public enum OtaScreenState {
+            OTA_STATUS_UNDEFINED,
+            OTA_STATUS_ACTIVATION,
+            OTA_STATUS_LISTENING,
+            OTA_STATUS_PROGRESS,
+            OTA_STATUS_SUCCESS_FAILURE_DLG
+        }
+
+        public OtaScreenState otaScreenState;
+
+        public CdmaOtaScreenState() {
+            otaScreenState = OtaScreenState.OTA_STATUS_UNDEFINED;
+        }
+
+        /**
+         * {@link PendingIntent} used to report an OTASP result status code
+         * back to our caller. Can be null.
+         *
+         * Our caller (presumably SetupWizard) may create this PendingIntent,
+         * pointing back at itself, and passes it along as an extra with the
+         * ACTION_PERFORM_CDMA_PROVISIONING intent.  Then, when there's an
+         * OTASP result to report, we send that PendingIntent back, adding an
+         * extra called EXTRA_OTASP_RESULT_CODE to indicate the result.
+         *
+         * Possible result values are the OTASP_RESULT_* constants.
+         */
+        public PendingIntent otaspResultCodePendingIntent;
+    }
+
+    /** @see com.android.internal.telephony.Phone */
+    private static String otaProvisionStatusToString(int status) {
+        switch (status) {
+            case Phone.CDMA_OTA_PROVISION_STATUS_SPL_UNLOCKED:
+                return "SPL_UNLOCKED";
+            case Phone.CDMA_OTA_PROVISION_STATUS_SPC_RETRIES_EXCEEDED:
+                return "SPC_RETRIES_EXCEEDED";
+            case Phone.CDMA_OTA_PROVISION_STATUS_A_KEY_EXCHANGED:
+                return "A_KEY_EXCHANGED";
+            case Phone.CDMA_OTA_PROVISION_STATUS_SSD_UPDATED:
+                return "SSD_UPDATED";
+            case Phone.CDMA_OTA_PROVISION_STATUS_NAM_DOWNLOADED:
+                return "NAM_DOWNLOADED";
+            case Phone.CDMA_OTA_PROVISION_STATUS_MDN_DOWNLOADED:
+                return "MDN_DOWNLOADED";
+            case Phone.CDMA_OTA_PROVISION_STATUS_IMSI_DOWNLOADED:
+                return "IMSI_DOWNLOADED";
+            case Phone.CDMA_OTA_PROVISION_STATUS_PRL_DOWNLOADED:
+                return "PRL_DOWNLOADED";
+            case Phone.CDMA_OTA_PROVISION_STATUS_COMMITTED:
+                return "COMMITTED";
+            case Phone.CDMA_OTA_PROVISION_STATUS_OTAPA_STARTED:
+                return "OTAPA_STARTED";
+            case Phone.CDMA_OTA_PROVISION_STATUS_OTAPA_STOPPED:
+                return "OTAPA_STOPPED";
+            case Phone.CDMA_OTA_PROVISION_STATUS_OTAPA_ABORTED:
+                return "OTAPA_ABORTED";
+            default:
+                return "<unknown status" + status + ">";
+        }
+    }
+
+    private static int getLteOnCdmaMode(Context context) {
+        final TelephonyManager telephonyManager = (TelephonyManager) context.getSystemService(
+                Context.TELEPHONY_SERVICE);
+        // If the telephony manager is not available yet, or if it doesn't know the answer yet,
+        // try falling back on the system property that may or may not be there
+        if (telephonyManager == null
+                || telephonyManager.getLteOnCdmaMode() == PhoneConstants.LTE_ON_CDMA_UNKNOWN) {
+            return SystemProperties.getInt(TelephonyProperties.PROPERTY_LTE_ON_CDMA_DEVICE,
+                    PhoneConstants.LTE_ON_CDMA_UNKNOWN);
+        }
+        return telephonyManager.getLteOnCdmaMode();
+    }
+
+    private static void log(String msg) {
+        Log.d(LOG_TAG, msg);
+    }
+
+    private static void loge(String msg) {
+        Log.e(LOG_TAG, msg);
+    }
+}