1/*
2 * Copyright (C) 2009 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.phone;
18
19import com.android.internal.telephony.Phone;
20import com.android.internal.telephony.PhoneConstants;
21import com.android.internal.telephony.TelephonyCapabilities;
22import com.android.internal.telephony.TelephonyProperties;
23import com.android.phone.OtaUtils.CdmaOtaInCallScreenUiState.State;
24
25import android.app.Activity;
26import android.app.ActivityManager;
27import android.app.AlertDialog;
28import android.app.PendingIntent;
29import android.app.PendingIntent.CanceledException;
30import android.content.ActivityNotFoundException;
31import android.content.Context;
32import android.content.DialogInterface;
33import android.content.Intent;
34import android.net.Uri;
35import android.os.AsyncResult;
36import android.os.Handler;
37import android.os.SystemClock;
38import android.os.SystemProperties;
39import android.os.UserHandle;
40import android.telephony.TelephonyManager;
41import android.util.Log;
42import android.view.KeyEvent;
43import android.view.View;
44import android.view.ViewGroup;
45import android.view.ViewStub;
46import android.view.WindowManager;
47import android.widget.Button;
48import android.widget.ProgressBar;
49import android.widget.ScrollView;
50import android.widget.TextView;
51import android.widget.ToggleButton;
52
53/**
54 * Handles all OTASP Call related logic and UI functionality.
55 * The InCallScreen interacts with this class to perform an OTASP Call.
56 *
57 * OTASP is a CDMA-specific feature:
58 *   OTA or OTASP == Over The Air service provisioning
59 *   SPC == Service Programming Code
60 *   TODO: Include pointer to more detailed documentation.
61 *
62 * TODO: This is Over The Air Service Provisioning (OTASP)
63 *       A better name would be OtaspUtils.java.
64 */
65public class OtaUtils {
66    private static final String LOG_TAG = "OtaUtils";
67    private static final boolean DBG = false;
68
69    public static final int OTA_SHOW_ACTIVATION_SCREEN_OFF = 0;
70    public static final int OTA_SHOW_ACTIVATION_SCREEN_ON = 1;
71    public static final int OTA_SHOW_LISTENING_SCREEN_OFF =0;
72    public static final int OTA_SHOW_LISTENING_SCREEN_ON =1;
73    public static final int OTA_SHOW_ACTIVATE_FAIL_COUNT_OFF = 0;
74    public static final int OTA_SHOW_ACTIVATE_FAIL_COUNT_THREE = 3;
75    public static final int OTA_PLAY_SUCCESS_FAILURE_TONE_OFF = 0;
76    public static final int OTA_PLAY_SUCCESS_FAILURE_TONE_ON = 1;
77
78    // SPC Timeout is 60 seconds
79    public final int OTA_SPC_TIMEOUT = 60;
80    public final int OTA_FAILURE_DIALOG_TIMEOUT = 2;
81
82    // Constants for OTASP-related Intents and intent extras.
83    // Watch out: these must agree with the corresponding constants in
84    // apps/SetupWizard!
85
86    // Intent action to launch an OTASP call.
87    public static final String ACTION_PERFORM_CDMA_PROVISIONING =
88           "com.android.phone.PERFORM_CDMA_PROVISIONING";
89
90    // Intent action to launch activation on a non-voice capable device
91    public static final String ACTION_PERFORM_VOICELESS_CDMA_PROVISIONING =
92            "com.android.phone.PERFORM_VOICELESS_CDMA_PROVISIONING";
93
94    // Intent action to display the InCallScreen in the OTASP "activation" state.
95    public static final String ACTION_DISPLAY_ACTIVATION_SCREEN =
96            "com.android.phone.DISPLAY_ACTIVATION_SCREEN";
97
98    // boolean voiceless provisioning extra that enables a "don't show this again" checkbox
99    // the user can check to never see the activity upon bootup again
100    public static final String EXTRA_VOICELESS_PROVISIONING_OFFER_DONTSHOW =
101            "com.android.phone.VOICELESS_PROVISIONING_OFFER_DONTSHOW";
102
103    // Activity result codes for the ACTION_PERFORM_CDMA_PROVISIONING intent
104    // (see the InCallScreenShowActivation activity.)
105    //
106    // Note: currently, our caller won't ever actually receive the
107    // RESULT_INTERACTIVE_OTASP_STARTED result code; see comments in
108    // InCallScreenShowActivation.onCreate() for details.
109
110    public static final int RESULT_INTERACTIVE_OTASP_STARTED = Activity.RESULT_FIRST_USER;
111    public static final int RESULT_NONINTERACTIVE_OTASP_STARTED = Activity.RESULT_FIRST_USER + 1;
112    public static final int RESULT_NONINTERACTIVE_OTASP_FAILED = Activity.RESULT_FIRST_USER + 2;
113
114    // Testing: Extra for the ACTION_PERFORM_CDMA_PROVISIONING intent that
115    // allows the caller to manually enable/disable "interactive mode" for
116    // the OTASP call.   Only available in userdebug or eng builds.
117    public static final String EXTRA_OVERRIDE_INTERACTIVE_MODE =
118            "ota_override_interactive_mode";
119
120    // Extra for the ACTION_PERFORM_CDMA_PROVISIONING intent, holding a
121    // PendingIntent which the phone app can use to send a result code
122    // back to the caller.
123    public static final String EXTRA_OTASP_RESULT_CODE_PENDING_INTENT =
124            "otasp_result_code_pending_intent";
125
126    // Extra attached to the above PendingIntent that indicates
127    // success or failure.
128    public static final String EXTRA_OTASP_RESULT_CODE =
129            "otasp_result_code";
130    public static final int OTASP_UNKNOWN = 0;
131    public static final int OTASP_USER_SKIPPED = 1;  // Only meaningful with interactive OTASP
132    public static final int OTASP_SUCCESS = 2;
133    public static final int OTASP_FAILURE = 3;
134    // failed due to CDMA_OTA_PROVISION_STATUS_SPC_RETRIES_EXCEEDED
135    public static final int OTASP_FAILURE_SPC_RETRIES = 4;
136    // TODO: Distinguish between interactive and non-interactive success
137    // and failure.  Then, have the PendingIntent be sent after
138    // interactive OTASP as well (so the caller can find out definitively
139    // when interactive OTASP completes.)
140
141    private static final String OTASP_NUMBER = "*228";
142    private static final String OTASP_NUMBER_NON_INTERACTIVE = "*22899";
143
144    private InCallScreen mInCallScreen;
145    private Context mContext;
146    private PhoneGlobals mApplication;
147    private OtaWidgetData mOtaWidgetData;
148    private ViewGroup mInCallTouchUi;  // UI controls for regular calls
149    private CallCard mCallCard;
150
151    // The DTMFTwelveKeyDialer instance.   We create this in
152    // initOtaInCallScreen(), and attach it to the DTMFTwelveKeyDialerView
153    // ("otaDtmfDialerView") that comes from otacall_card.xml.
154    private DTMFTwelveKeyDialer mOtaCallCardDtmfDialer;
155
156    private static boolean sIsWizardMode = true;
157
158    // How many times do we retry maybeDoOtaCall() if the LTE state is not known yet,
159    // and how long do we wait between retries
160    private static final int OTA_CALL_LTE_RETRIES_MAX = 5;
161    private static final int OTA_CALL_LTE_RETRY_PERIOD = 3000;
162    private static int sOtaCallLteRetries = 0;
163
164    // In "interactive mode", the OtaUtils object is tied to an
165    // InCallScreen instance, where we display a bunch of UI specific to
166    // the OTASP call.  But on devices that are not "voice capable", the
167    // OTASP call runs in a non-interactive mode, and we don't have
168    // an InCallScreen or CallCard or any OTASP UI elements at all.
169    private boolean mInteractive = true;
170
171
172    /**
173     * OtaWidgetData class represent all OTA UI elements
174     *
175     * TODO(OTASP): It's really ugly for the OtaUtils object to reach into the
176     *     InCallScreen like this and directly manipulate its widgets.
177     *
178     *     Instead, the model/view separation should be more clear: OtaUtils
179     *     should only know about a higher-level abstraction of the
180     *     OTASP-specific UI state (just like how the CallController uses the
181     *     InCallUiState object), and the InCallScreen itself should translate
182     *     that higher-level abstraction into actual onscreen views and widgets.
183     */
184    private class OtaWidgetData {
185        public Button otaEndButton;
186        public Button otaActivateButton;
187        public Button otaSkipButton;
188        public Button otaNextButton;
189        public ToggleButton otaSpeakerButton;
190        public ViewGroup otaUpperWidgets;
191        public View callCardOtaButtonsFailSuccess;
192        public ProgressBar otaTextProgressBar;
193        public TextView otaTextSuccessFail;
194        public View callCardOtaButtonsActivate;
195        public View callCardOtaButtonsListenProgress;
196        public TextView otaTextActivate;
197        public TextView otaTextListenProgress;
198        public AlertDialog spcErrorDialog;
199        public AlertDialog otaFailureDialog;
200        public AlertDialog otaSkipConfirmationDialog;
201        public TextView otaTitle;
202        public DTMFTwelveKeyDialerView otaDtmfDialerView;
203        public Button otaTryAgainButton;
204    }
205
206    /**
207     * OtaUtils constructor.
208     *
209     * @param context the Context of the calling Activity or Application
210     * @param interactive if true, use the InCallScreen to display the progress
211     *                    and result of the OTASP call.  In practice this is
212     *                    true IFF the current device is a voice-capable phone.
213     *
214     * Note if interactive is true, you must also call updateUiWidgets() as soon
215     * as the InCallScreen instance is ready.
216     */
217    public OtaUtils(Context context, boolean interactive) {
218        if (DBG) log("OtaUtils constructor...");
219        mApplication = PhoneGlobals.getInstance();
220        mContext = context;
221        mInteractive = interactive;
222    }
223
224    /**
225     * Updates the OtaUtils object's references to some UI elements belonging to
226     * the InCallScreen.  This is used only in interactive mode.
227     *
228     * Use clearUiWidgets() to clear out these references.  (The InCallScreen
229     * is responsible for doing this from its onDestroy() method.)
230     *
231     * This method has no effect if the UI widgets have already been set up.
232     * (In other words, it's safe to call this every time through
233     * InCallScreen.onResume().)
234     */
235    public void updateUiWidgets(InCallScreen inCallScreen,
236            ViewGroup inCallTouchUi, CallCard callCard) {
237        if (DBG) log("updateUiWidgets()...  mInCallScreen = " + mInCallScreen);
238
239        if (!mInteractive) {
240            throw new IllegalStateException("updateUiWidgets() called in non-interactive mode");
241        }
242
243        if (mInCallScreen != null) {
244            if (DBG) log("updateUiWidgets(): widgets already set up, nothing to do...");
245            return;
246        }
247
248        mInCallScreen = inCallScreen;
249        mInCallTouchUi = inCallTouchUi;
250        mCallCard = callCard;
251        mOtaWidgetData = new OtaWidgetData();
252
253        // Inflate OTASP-specific UI elements:
254        ViewStub otaCallCardStub = (ViewStub) mInCallScreen.findViewById(R.id.otaCallCardStub);
255        if (otaCallCardStub != null) {
256            // If otaCallCardStub is null here, that means it's already been
257            // inflated (which could have happened in the current InCallScreen
258            // instance for a *prior* OTASP call.)
259            otaCallCardStub.inflate();
260        }
261
262        readXmlSettings();
263        initOtaInCallScreen();
264    }
265
266    /**
267     * Clear out the OtaUtils object's references to any InCallScreen UI
268     * elements.  This is the opposite of updateUiWidgets().
269     */
270    public void clearUiWidgets() {
271        mInCallScreen = null;
272        mInCallTouchUi = null;
273        mCallCard = null;
274        mOtaWidgetData = null;
275    }
276
277    /**
278     * Starts the OTA provisioning call.  If the MIN isn't available yet, it returns false and adds
279     * an event to return the request to the calling app when it becomes available.
280     *
281     * @param context
282     * @param handler
283     * @param request
284     * @return true if we were able to launch Ota activity or it's not required; false otherwise
285     */
286    public static boolean maybeDoOtaCall(Context context, Handler handler, int request) {
287        PhoneGlobals app = PhoneGlobals.getInstance();
288        Phone phone = app.phone;
289
290        if (ActivityManager.isRunningInTestHarness()) {
291            Log.i(LOG_TAG, "Don't run provisioning when in test harness");
292            return true;
293        }
294
295        if (!TelephonyCapabilities.supportsOtasp(phone)) {
296            // Presumably not a CDMA phone.
297            if (DBG) log("maybeDoOtaCall: OTASP not supported on this device");
298            return true;  // Nothing to do here.
299        }
300
301        if (!phone.isMinInfoReady()) {
302            if (DBG) log("MIN is not ready. Registering to receive notification.");
303            phone.registerForSubscriptionInfoReady(handler, request, null);
304            return false;
305        }
306        phone.unregisterForSubscriptionInfoReady(handler);
307
308        if (getLteOnCdmaMode(context) == PhoneConstants.LTE_ON_CDMA_UNKNOWN) {
309            if (sOtaCallLteRetries < OTA_CALL_LTE_RETRIES_MAX) {
310                if (DBG) log("maybeDoOtaCall: LTE state still unknown: retrying");
311                handler.sendEmptyMessageDelayed(request, OTA_CALL_LTE_RETRY_PERIOD);
312                sOtaCallLteRetries++;
313                return false;
314            } else {
315                Log.w(LOG_TAG, "maybeDoOtaCall: LTE state still unknown: giving up");
316                return true;
317            }
318        }
319
320        boolean phoneNeedsActivation = phone.needsOtaServiceProvisioning();
321        if (DBG) log("phoneNeedsActivation is set to " + phoneNeedsActivation);
322
323        int otaShowActivationScreen = context.getResources().getInteger(
324                R.integer.OtaShowActivationScreen);
325        if (DBG) log("otaShowActivationScreen: " + otaShowActivationScreen);
326
327        // Run the OTASP call in "interactive" mode only if
328        // this is a non-LTE "voice capable" device.
329        if (PhoneGlobals.sVoiceCapable && getLteOnCdmaMode(context) == PhoneConstants.LTE_ON_CDMA_FALSE) {
330            if (phoneNeedsActivation
331                    && (otaShowActivationScreen == OTA_SHOW_ACTIVATION_SCREEN_ON)) {
332                app.cdmaOtaProvisionData.isOtaCallIntentProcessed = false;
333                sIsWizardMode = false;
334
335                if (DBG) Log.d(LOG_TAG, "==> Starting interactive CDMA provisioning...");
336                OtaUtils.startInteractiveOtasp(context);
337
338                if (DBG) log("maybeDoOtaCall: voice capable; activation started.");
339            } else {
340                if (DBG) log("maybeDoOtaCall: voice capable; activation NOT started.");
341            }
342        } else {
343            if (phoneNeedsActivation) {
344                app.cdmaOtaProvisionData.isOtaCallIntentProcessed = false;
345                Intent newIntent = new Intent(ACTION_PERFORM_VOICELESS_CDMA_PROVISIONING);
346                newIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
347                newIntent.putExtra(EXTRA_VOICELESS_PROVISIONING_OFFER_DONTSHOW, true);
348                try {
349                    context.startActivity(newIntent);
350                } catch (ActivityNotFoundException e) {
351                    loge("No activity Handling PERFORM_VOICELESS_CDMA_PROVISIONING!");
352                    return false;
353                }
354                if (DBG) log("maybeDoOtaCall: non-interactive; activation intent sent.");
355            } else {
356                if (DBG) log("maybeDoOtaCall: non-interactive, no need for OTASP.");
357            }
358        }
359        return true;
360    }
361
362    /**
363     * Starts a normal "interactive" OTASP call (i.e. CDMA activation
364     * for regular voice-capable phone devices.)
365     *
366     * This method is called from the InCallScreenShowActivation activity when
367     * handling the ACTION_PERFORM_CDMA_PROVISIONING intent.
368     */
369    public static void startInteractiveOtasp(Context context) {
370        if (DBG) log("startInteractiveOtasp()...");
371        PhoneGlobals app = PhoneGlobals.getInstance();
372
373        // There are two ways to start OTASP on voice-capable devices:
374        //
375        // (1) via the PERFORM_CDMA_PROVISIONING intent
376        //     - this is triggered by the "Activate device" button in settings,
377        //       or can be launched automatically upon boot if the device
378        //       thinks it needs to be provisioned.
379        //     - the intent is handled by InCallScreenShowActivation.onCreate(),
380        //       which calls this method
381        //     - we prepare for OTASP by initializing the OtaUtils object
382        //     - we bring up the InCallScreen in the ready-to-activate state
383        //     - when the user presses the "Activate" button we launch the
384        //       call by calling CallController.placeCall() via the
385        //       otaPerformActivation() method.
386        //
387        // (2) by manually making an outgoing call to a special OTASP number
388        //     like "*228" or "*22899".
389        //     - That sequence does NOT involve this method (OtaUtils.startInteractiveOtasp()).
390        //       Instead, the outgoing call request goes straight to CallController.placeCall().
391        //     - CallController.placeCall() notices that it's an OTASP
392        //       call, and initializes the OtaUtils object.
393        //     - The InCallScreen is launched (as the last step of
394        //       CallController.placeCall()).  The InCallScreen notices that
395        //       OTASP is active and shows the correct UI.
396
397        // Here, we start sequence (1):
398        // Do NOT immediately start the call.  Instead, bring up the InCallScreen
399        // in the special "activate" state (see OtaUtils.otaShowActivateScreen()).
400        // We won't actually make the call until the user presses the "Activate"
401        // button.
402
403        Intent activationScreenIntent = new Intent().setClass(context, InCallScreen.class)
404                .setAction(ACTION_DISPLAY_ACTIVATION_SCREEN);
405
406        // Watch out: in the scenario where OTASP gets triggered from the
407        // BOOT_COMPLETED broadcast (see OtaStartupReceiver.java), we might be
408        // running in the PhoneApp's context right now.
409        // So the FLAG_ACTIVITY_NEW_TASK flag is required here.
410        activationScreenIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
411
412        // We're about to start the OTASP sequence, so create and initialize the
413        // OtaUtils instance.  (This needs to happen before bringing up the
414        // InCallScreen.)
415        OtaUtils.setupOtaspCall(activationScreenIntent);
416
417        // And bring up the InCallScreen...
418        Log.i(LOG_TAG, "startInteractiveOtasp: launching InCallScreen in 'activate' state: "
419              + activationScreenIntent);
420        context.startActivity(activationScreenIntent);
421    }
422
423    /**
424     * Starts the OTASP call *without* involving the InCallScreen or
425     * displaying any UI.
426     *
427     * This is used on data-only devices, which don't support any kind of
428     * in-call phone UI.
429     *
430     * @return PhoneUtils.CALL_STATUS_DIALED if we successfully
431     *         dialed the OTASP number, or one of the other
432     *         CALL_STATUS_* constants if there was a failure.
433     */
434    public static int startNonInteractiveOtasp(Context context) {
435        if (DBG) log("startNonInteractiveOtasp()...");
436        PhoneGlobals app = PhoneGlobals.getInstance();
437
438        if (app.otaUtils != null) {
439            // An OtaUtils instance already exists, presumably from a previous OTASP call.
440            Log.i(LOG_TAG, "startNonInteractiveOtasp: "
441                  + "OtaUtils already exists; nuking the old one and starting again...");
442        }
443
444        // Create the OtaUtils instance.
445        app.otaUtils = new OtaUtils(context, false /* non-interactive mode */);
446        if (DBG) log("- created OtaUtils: " + app.otaUtils);
447
448        // ... and kick off the OTASP call.
449        // TODO(InCallScreen redesign): This should probably go through
450        // the CallController, rather than directly calling
451        // PhoneUtils.placeCall().
452        Phone phone = PhoneGlobals.getPhone();
453        String number = OTASP_NUMBER_NON_INTERACTIVE;
454        Log.i(LOG_TAG, "startNonInteractiveOtasp: placing call to '" + number + "'...");
455        int callStatus = PhoneUtils.placeCall(context,
456                                              phone,
457                                              number,
458                                              null,  // contactRef
459                                              false,  //isEmergencyCall
460                                              null);  // gatewayUri
461
462        if (callStatus == PhoneUtils.CALL_STATUS_DIALED) {
463            if (DBG) log("  ==> successful return from placeCall(): callStatus = " + callStatus);
464        } else {
465            Log.w(LOG_TAG, "Failure from placeCall() for OTA number '"
466                  + number + "': code " + callStatus);
467            return callStatus;
468        }
469
470        // TODO: Any other special work to do here?
471        // Such as:
472        //
473        // - manually kick off progress updates, either using TelephonyRegistry
474        //   or else by sending PendingIntents directly to our caller?
475        //
476        // - manually silence the in-call audio?  (Probably unnecessary
477        //   if Stingray truly has no audio path from phone baseband
478        //   to the device's speakers.)
479        //
480
481        return callStatus;
482    }
483
484    /**
485     * @return true if the specified Intent is a CALL action that's an attempt
486     * to initate an OTASP call.
487     *
488     * OTASP is a CDMA-specific concept, so this method will always return false
489     * on GSM phones.
490     *
491     * This code was originally part of the InCallScreen.checkIsOtaCall() method.
492     */
493    public static boolean isOtaspCallIntent(Intent intent) {
494        if (DBG) log("isOtaspCallIntent(" + intent + ")...");
495        PhoneGlobals app = PhoneGlobals.getInstance();
496        Phone phone = app.mCM.getDefaultPhone();
497
498        if (intent == null) {
499            return false;
500        }
501        if (!TelephonyCapabilities.supportsOtasp(phone)) {
502            return false;
503        }
504
505        String action = intent.getAction();
506        if (action == null) {
507            return false;
508        }
509        if (!action.equals(Intent.ACTION_CALL)) {
510            if (DBG) log("isOtaspCallIntent: not a CALL action: '" + action + "' ==> not OTASP");
511            return false;
512        }
513
514        if ((app.cdmaOtaScreenState == null) || (app.cdmaOtaProvisionData == null)) {
515            // Uh oh -- something wrong with our internal OTASP state.
516            // (Since this is an OTASP-capable device, these objects
517            // *should* have already been created by PhoneApp.onCreate().)
518            throw new IllegalStateException("isOtaspCallIntent: "
519                                            + "app.cdmaOta* objects(s) not initialized");
520        }
521
522        // This is an OTASP call iff the number we're trying to dial is one of
523        // the magic OTASP numbers.
524        String number;
525        try {
526            number = PhoneUtils.getInitialNumber(intent);
527        } catch (PhoneUtils.VoiceMailNumberMissingException ex) {
528            // This was presumably a "voicemail:" intent, so it's
529            // obviously not an OTASP number.
530            if (DBG) log("isOtaspCallIntent: VoiceMailNumberMissingException => not OTASP");
531            return false;
532        }
533        if (phone.isOtaSpNumber(number)) {
534            if (DBG) log("isOtaSpNumber: ACTION_CALL to '" + number + "' ==> OTASP call!");
535            return true;
536        }
537        return false;
538    }
539
540    /**
541     * Set up for an OTASP call.
542     *
543     * This method is called as part of the CallController placeCall() sequence
544     * before initiating an outgoing OTASP call.
545     *
546     * The purpose of this method is mainly to create and initialize the
547     * OtaUtils instance, along with some other misc pre-OTASP cleanup.
548     */
549    public static void setupOtaspCall(Intent intent) {
550        if (DBG) log("setupOtaspCall(): preparing for OTASP call to " + intent);
551        PhoneGlobals app = PhoneGlobals.getInstance();
552
553        if (app.otaUtils != null) {
554            // An OtaUtils instance already exists, presumably from a prior OTASP call.
555            // Nuke the old one and start this call with a fresh instance.
556            Log.i(LOG_TAG, "setupOtaspCall: "
557                  + "OtaUtils already exists; replacing with new instance...");
558        }
559
560        // Create the OtaUtils instance.
561        app.otaUtils = new OtaUtils(app.getApplicationContext(), true /* interactive */);
562        if (DBG) log("- created OtaUtils: " + app.otaUtils);
563
564        // NOTE we still need to call OtaUtils.updateUiWidgets() once the
565        // InCallScreen instance is ready; see InCallScreen.checkOtaspStateOnResume()
566
567        // Make sure the InCallScreen knows that it needs to switch into OTASP mode.
568        //
569        // NOTE in gingerbread and earlier, we used to do
570        //     setInCallScreenMode(InCallScreenMode.OTA_NORMAL);
571        // directly in the InCallScreen, back when this check happened inside the InCallScreen.
572        //
573        // But now, set the global CdmaOtaInCallScreenUiState object into
574        // NORMAL mode, which will then cause the InCallScreen (when it
575        // comes up) to realize that an OTA call is active.
576
577        app.otaUtils.setCdmaOtaInCallScreenUiState(
578            OtaUtils.CdmaOtaInCallScreenUiState.State.NORMAL);
579
580        // TODO(OTASP): note app.inCallUiState.inCallScreenMode and
581        // app.cdmaOtaInCallScreenUiState.state are mostly redundant.  Combine them.
582        app.inCallUiState.inCallScreenMode = InCallUiState.InCallScreenMode.OTA_NORMAL;
583
584        // TODO(OTASP / bug 5092031): we ideally should call
585        // otaShowListeningScreen() here to make sure that the DTMF dialpad
586        // becomes visible at the start of the "*228" call:
587        //
588        //  // ...and get the OTASP-specific UI into the right state.
589        //  app.otaUtils.otaShowListeningScreen();
590        //  if (app.otaUtils.mInCallScreen != null) {
591        //      app.otaUtils.mInCallScreen.requestUpdateScreen();
592        //  }
593        //
594        // But this doesn't actually work; the call to otaShowListeningScreen()
595        // *doesn't* actually bring up the listening screen, since the
596        // cdmaOtaConfigData.otaShowListeningScreen config parameter hasn't been
597        // initialized (we haven't run readXmlSettings() yet at this point!)
598
599        // Also, since the OTA call is now just starting, clear out
600        // the "committed" flag in app.cdmaOtaProvisionData.
601        if (app.cdmaOtaProvisionData != null) {
602            app.cdmaOtaProvisionData.isOtaCallCommitted = false;
603        }
604    }
605
606    private void setSpeaker(boolean state) {
607        if (DBG) log("setSpeaker : " + state );
608
609        if (!mInteractive) {
610            if (DBG) log("non-interactive mode, ignoring setSpeaker.");
611            return;
612        }
613
614        if (state == PhoneUtils.isSpeakerOn(mContext)) {
615            if (DBG) log("no change. returning");
616            return;
617        }
618
619        if (state && mInCallScreen.isBluetoothAvailable()
620                && mInCallScreen.isBluetoothAudioConnected()) {
621            mInCallScreen.disconnectBluetoothAudio();
622        }
623        PhoneUtils.turnOnSpeaker(mContext, state, true);
624    }
625
626    /**
627     * Handles OTA Provision events from the telephony layer.
628     * These events come in to this method whether or not
629     * the InCallScreen is visible.
630     *
631     * Possible events are:
632     * OTA Commit Event - OTA provisioning was successful
633     * SPC retries exceeded - SPC failure retries has exceeded, and Phone needs to
634     *    power down.
635     */
636    public void onOtaProvisionStatusChanged(AsyncResult r) {
637        int OtaStatus[] = (int[]) r.result;
638        if (DBG) log("Provision status event!");
639        if (DBG) log("onOtaProvisionStatusChanged(): status = "
640                     + OtaStatus[0] + " ==> " + otaProvisionStatusToString(OtaStatus[0]));
641
642        // In practice, in a normal successful OTASP call, events come in as follows:
643        //   - SPL_UNLOCKED within a couple of seconds after the call starts
644        //   - then a delay of around 45 seconds
645        //   - then PRL_DOWNLOADED and MDN_DOWNLOADED and COMMITTED within a span of 2 seconds
646
647        switch(OtaStatus[0]) {
648            case Phone.CDMA_OTA_PROVISION_STATUS_SPC_RETRIES_EXCEEDED:
649                if (DBG) log("onOtaProvisionStatusChanged(): RETRIES EXCEEDED");
650                updateOtaspProgress();
651                mApplication.cdmaOtaProvisionData.otaSpcUptime = SystemClock.elapsedRealtime();
652                if (mInteractive) {
653                    otaShowSpcErrorNotice(OTA_SPC_TIMEOUT);
654                } else {
655                    sendOtaspResult(OTASP_FAILURE_SPC_RETRIES);
656                }
657                // Power.shutdown();
658                break;
659
660            case Phone.CDMA_OTA_PROVISION_STATUS_COMMITTED:
661                if (DBG) {
662                    log("onOtaProvisionStatusChanged(): DONE, isOtaCallCommitted set to true");
663                }
664                mApplication.cdmaOtaProvisionData.isOtaCallCommitted = true;
665                if (mApplication.cdmaOtaScreenState.otaScreenState !=
666                    CdmaOtaScreenState.OtaScreenState.OTA_STATUS_UNDEFINED) {
667                    updateOtaspProgress();
668                }
669
670                break;
671
672            case Phone.CDMA_OTA_PROVISION_STATUS_SPL_UNLOCKED:
673            case Phone.CDMA_OTA_PROVISION_STATUS_A_KEY_EXCHANGED:
674            case Phone.CDMA_OTA_PROVISION_STATUS_SSD_UPDATED:
675            case Phone.CDMA_OTA_PROVISION_STATUS_NAM_DOWNLOADED:
676            case Phone.CDMA_OTA_PROVISION_STATUS_MDN_DOWNLOADED:
677            case Phone.CDMA_OTA_PROVISION_STATUS_IMSI_DOWNLOADED:
678            case Phone.CDMA_OTA_PROVISION_STATUS_PRL_DOWNLOADED:
679            case Phone.CDMA_OTA_PROVISION_STATUS_OTAPA_STARTED:
680            case Phone.CDMA_OTA_PROVISION_STATUS_OTAPA_STOPPED:
681            case Phone.CDMA_OTA_PROVISION_STATUS_OTAPA_ABORTED:
682                // Only update progress when OTA call is in normal state
683                if (getCdmaOtaInCallScreenUiState() == CdmaOtaInCallScreenUiState.State.NORMAL) {
684                    if (DBG) log("onOtaProvisionStatusChanged(): change to ProgressScreen");
685                    updateOtaspProgress();
686                }
687                break;
688
689            default:
690                if (DBG) log("onOtaProvisionStatusChanged(): Ignoring OtaStatus " + OtaStatus[0]);
691                break;
692        }
693    }
694
695    /**
696     * Handle a disconnect event from the OTASP call.
697     */
698    public void onOtaspDisconnect() {
699        if (DBG) log("onOtaspDisconnect()...");
700        // We only handle this event explicitly in non-interactive mode.
701        // (In interactive mode, the InCallScreen does any post-disconnect
702        // cleanup.)
703        if (!mInteractive) {
704            // Send a success or failure indication back to our caller.
705            updateNonInteractiveOtaSuccessFailure();
706        }
707    }
708
709    private void otaShowHome() {
710        if (DBG) log("otaShowHome()...");
711        mApplication.cdmaOtaScreenState.otaScreenState =
712                CdmaOtaScreenState.OtaScreenState.OTA_STATUS_UNDEFINED;
713        mInCallScreen.endInCallScreenSession();
714        Intent intent = new Intent(Intent.ACTION_MAIN);
715        intent.addCategory (Intent.CATEGORY_HOME);
716        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
717        mContext.startActivityAsUser(intent, UserHandle.CURRENT);
718        return;
719    }
720
721    private void otaSkipActivation() {
722        if (DBG) log("otaSkipActivation()...");
723
724        sendOtaspResult(OTASP_USER_SKIPPED);
725
726        if (mInteractive) mInCallScreen.finish();
727        return;
728    }
729
730    /**
731     * Actually initiate the OTASP call.  This method is triggered by the
732     * onscreen "Activate" button, and is only used in interactive mode.
733     */
734    private void otaPerformActivation() {
735        if (DBG) log("otaPerformActivation()...");
736        if (!mInteractive) {
737            // We shouldn't ever get here in non-interactive mode!
738            Log.w(LOG_TAG, "otaPerformActivation: not interactive!");
739            return;
740        }
741
742        if (!mApplication.cdmaOtaProvisionData.inOtaSpcState) {
743            // Place an outgoing call to the special OTASP number:
744            Intent newIntent = new Intent(Intent.ACTION_CALL);
745            newIntent.setData(Uri.fromParts(Constants.SCHEME_TEL, OTASP_NUMBER, null));
746
747            // Initiate the outgoing call:
748            mApplication.callController.placeCall(newIntent);
749
750            // ...and get the OTASP-specific UI into the right state.
751            otaShowListeningScreen();
752            mInCallScreen.requestUpdateScreen();
753        }
754        return;
755    }
756
757    /**
758     * Show Activation Screen when phone powers up and OTA provision is
759     * required. Also shown when activation fails and user needs
760     * to re-attempt it. Contains ACTIVATE and SKIP buttons
761     * which allow user to start OTA activation or skip the activation process.
762     */
763    public void otaShowActivateScreen() {
764        if (DBG) log("otaShowActivateScreen()...");
765        if (mApplication.cdmaOtaConfigData.otaShowActivationScreen
766                == OTA_SHOW_ACTIVATION_SCREEN_ON) {
767            if (DBG) log("otaShowActivateScreen(): show activation screen");
768            if (!isDialerOpened()) {
769                otaScreenInitialize();
770                mOtaWidgetData.otaSkipButton.setVisibility(sIsWizardMode ?
771                        View.VISIBLE : View.INVISIBLE);
772                mOtaWidgetData.otaTextActivate.setVisibility(View.VISIBLE);
773                mOtaWidgetData.callCardOtaButtonsActivate.setVisibility(View.VISIBLE);
774            }
775            mApplication.cdmaOtaScreenState.otaScreenState =
776                    CdmaOtaScreenState.OtaScreenState.OTA_STATUS_ACTIVATION;
777        } else {
778            if (DBG) log("otaShowActivateScreen(): show home screen");
779            otaShowHome();
780        }
781     }
782
783    /**
784     * Show "Listen for Instruction" screen during OTA call. Shown when OTA Call
785     * is initiated and user needs to listen for network instructions and press
786     * appropriate DTMF digits to proceed to the "Programming in Progress" phase.
787     */
788    private void otaShowListeningScreen() {
789        if (DBG) log("otaShowListeningScreen()...");
790        if (!mInteractive) {
791            // We shouldn't ever get here in non-interactive mode!
792            Log.w(LOG_TAG, "otaShowListeningScreen: not interactive!");
793            return;
794        }
795
796        if (mApplication.cdmaOtaConfigData.otaShowListeningScreen
797                == OTA_SHOW_LISTENING_SCREEN_ON) {
798            if (DBG) log("otaShowListeningScreen(): show listening screen");
799            if (!isDialerOpened()) {
800                otaScreenInitialize();
801                mOtaWidgetData.otaTextListenProgress.setVisibility(View.VISIBLE);
802                mOtaWidgetData.otaTextListenProgress.setText(R.string.ota_listen);
803                mOtaWidgetData.otaDtmfDialerView.setVisibility(View.VISIBLE);
804                mOtaWidgetData.callCardOtaButtonsListenProgress.setVisibility(View.VISIBLE);
805                mOtaWidgetData.otaSpeakerButton.setVisibility(View.VISIBLE);
806                boolean speakerOn = PhoneUtils.isSpeakerOn(mContext);
807                mOtaWidgetData.otaSpeakerButton.setChecked(speakerOn);
808            }
809            mApplication.cdmaOtaScreenState.otaScreenState =
810                    CdmaOtaScreenState.OtaScreenState.OTA_STATUS_LISTENING;
811        } else {
812            if (DBG) log("otaShowListeningScreen(): show progress screen");
813            otaShowInProgressScreen();
814        }
815    }
816
817    /**
818     * Do any necessary updates (of onscreen UI, for example)
819     * based on the latest status of the OTASP call.
820     */
821    private void updateOtaspProgress() {
822        if (DBG) log("updateOtaspProgress()...  mInteractive = " + mInteractive);
823        if (mInteractive) {
824            // On regular phones we just call through to
825            // otaShowInProgressScreen(), which updates the
826            // InCallScreen's onscreen UI.
827            otaShowInProgressScreen();
828        } else {
829            // We're not using the InCallScreen to show OTA progress.
830
831            // For now, at least, there's nothing to do here.
832            // The overall "success" or "failure" indication we send back
833            // (to our caller) is triggered by the DISCONNECT event;
834            // see updateNonInteractiveOtaSuccessFailure().
835
836            // But if we ever need to send *intermediate* progress updates back
837            // to our caller, we'd do that here, possbily using the same
838            // PendingIntent that we already use to indicate success or failure.
839        }
840    }
841
842    /**
843     * When a non-interactive OTASP call completes, send a success or
844     * failure indication back to our caller.
845     *
846     * This is basically the non-interactive equivalent of
847     * otaShowSuccessFailure().
848     */
849    private void updateNonInteractiveOtaSuccessFailure() {
850        // This is basically the same logic as otaShowSuccessFailure(): we
851        // check the isOtaCallCommitted bit, and if that's true it means
852        // that activation was successful.
853
854        if (DBG) log("updateNonInteractiveOtaSuccessFailure(): isOtaCallCommitted = "
855                     + mApplication.cdmaOtaProvisionData.isOtaCallCommitted);
856        int resultCode =
857                mApplication.cdmaOtaProvisionData.isOtaCallCommitted
858                ? OTASP_SUCCESS : OTASP_FAILURE;
859        sendOtaspResult(resultCode);
860    }
861
862    /**
863     * Sends the specified OTASP result code back to our caller (presumably
864     * SetupWizard) via the PendingIntent that they originally sent along with
865     * the ACTION_PERFORM_CDMA_PROVISIONING intent.
866     */
867    private void sendOtaspResult(int resultCode) {
868        if (DBG) log("sendOtaspResult: resultCode = " + resultCode);
869
870        // Pass the success or failure indication back to our caller by
871        // adding an additional extra to the PendingIntent we already
872        // have.
873        // (NB: there's a PendingIntent send() method that takes a resultCode
874        // directly, but we can't use that here since that call is only
875        // meaningful for pending intents that are actually used as activity
876        // results.)
877
878        Intent extraStuff = new Intent();
879        extraStuff.putExtra(EXTRA_OTASP_RESULT_CODE, resultCode);
880        // When we call PendingIntent.send() below, the extras from this
881        // intent will get merged with any extras already present in
882        // cdmaOtaScreenState.otaspResultCodePendingIntent.
883
884        if (mApplication.cdmaOtaScreenState == null) {
885            Log.e(LOG_TAG, "updateNonInteractiveOtaSuccessFailure: no cdmaOtaScreenState object!");
886            return;
887        }
888        if (mApplication.cdmaOtaScreenState.otaspResultCodePendingIntent == null) {
889            Log.w(LOG_TAG, "updateNonInteractiveOtaSuccessFailure: "
890                  + "null otaspResultCodePendingIntent!");
891            return;
892        }
893
894        try {
895            if (DBG) log("- sendOtaspResult:  SENDING PENDING INTENT: " +
896                         mApplication.cdmaOtaScreenState.otaspResultCodePendingIntent);
897            mApplication.cdmaOtaScreenState.otaspResultCodePendingIntent.send(
898                    mContext,
899                    0, /* resultCode (unused) */
900                    extraStuff);
901        } catch (CanceledException e) {
902            // should never happen because no code cancels the pending intent right now,
903            Log.e(LOG_TAG, "PendingIntent send() failed: " + e);
904        }
905    }
906
907    /**
908     * Show "Programming In Progress" screen during OTA call. Shown when OTA
909     * provisioning is in progress after user has selected an option.
910     */
911    private void otaShowInProgressScreen() {
912        if (DBG) log("otaShowInProgressScreen()...");
913        if (!mInteractive) {
914            // We shouldn't ever get here in non-interactive mode!
915            Log.w(LOG_TAG, "otaShowInProgressScreen: not interactive!");
916            return;
917        }
918
919        mApplication.cdmaOtaScreenState.otaScreenState =
920            CdmaOtaScreenState.OtaScreenState.OTA_STATUS_PROGRESS;
921
922        if ((mOtaWidgetData == null) || (mInCallScreen == null)) {
923            Log.w(LOG_TAG, "otaShowInProgressScreen: UI widgets not set up yet!");
924
925            // TODO(OTASP): our CdmaOtaScreenState is now correct; we just set
926            // it to OTA_STATUS_PROGRESS.  But we still need to make sure that
927            // when the InCallScreen eventually comes to the foreground, it
928            // notices that state and does all the same UI updating we do below.
929            return;
930        }
931
932        if (!isDialerOpened()) {
933            otaScreenInitialize();
934            mOtaWidgetData.otaTextListenProgress.setVisibility(View.VISIBLE);
935            mOtaWidgetData.otaTextListenProgress.setText(R.string.ota_progress);
936            mOtaWidgetData.otaTextProgressBar.setVisibility(View.VISIBLE);
937            mOtaWidgetData.callCardOtaButtonsListenProgress.setVisibility(View.VISIBLE);
938            mOtaWidgetData.otaSpeakerButton.setVisibility(View.VISIBLE);
939            boolean speakerOn = PhoneUtils.isSpeakerOn(mContext);
940            mOtaWidgetData.otaSpeakerButton.setChecked(speakerOn);
941        }
942    }
943
944    /**
945     * Show programming failure dialog when OTA provisioning fails.
946     * If OTA provisioning attempts fail more than 3 times, then unsuccessful
947     * dialog is shown. Otherwise a two-second notice is shown with unsuccessful
948     * information. When notice expires, phone returns to activation screen.
949     */
950    private void otaShowProgramFailure(int length) {
951        if (DBG) log("otaShowProgramFailure()...");
952        mApplication.cdmaOtaProvisionData.activationCount++;
953        if ((mApplication.cdmaOtaProvisionData.activationCount <
954                mApplication.cdmaOtaConfigData.otaShowActivateFailTimes)
955                && (mApplication.cdmaOtaConfigData.otaShowActivationScreen ==
956                OTA_SHOW_ACTIVATION_SCREEN_ON)) {
957            if (DBG) log("otaShowProgramFailure(): activationCount"
958                    + mApplication.cdmaOtaProvisionData.activationCount);
959            if (DBG) log("otaShowProgramFailure(): show failure notice");
960            otaShowProgramFailureNotice(length);
961        } else {
962            if (DBG) log("otaShowProgramFailure(): show failure dialog");
963            otaShowProgramFailureDialog();
964        }
965    }
966
967    /**
968     * Show either programming success dialog when OTA provisioning succeeds, or
969     * programming failure dialog when it fails. See {@link #otaShowProgramFailure}
970     * for more details.
971     */
972    public void otaShowSuccessFailure() {
973        if (DBG) log("otaShowSuccessFailure()...");
974        if (!mInteractive) {
975            // We shouldn't ever get here in non-interactive mode!
976            Log.w(LOG_TAG, "otaShowSuccessFailure: not interactive!");
977            return;
978        }
979
980        otaScreenInitialize();
981        if (DBG) log("otaShowSuccessFailure(): isOtaCallCommitted"
982                + mApplication.cdmaOtaProvisionData.isOtaCallCommitted);
983        if (mApplication.cdmaOtaProvisionData.isOtaCallCommitted) {
984            if (DBG) log("otaShowSuccessFailure(), show success dialog");
985            otaShowProgramSuccessDialog();
986        } else {
987            if (DBG) log("otaShowSuccessFailure(), show failure dialog");
988            otaShowProgramFailure(OTA_FAILURE_DIALOG_TIMEOUT);
989        }
990        return;
991    }
992
993    /**
994     * Show programming failure dialog when OTA provisioning fails more than 3
995     * times.
996     */
997    private void otaShowProgramFailureDialog() {
998        if (DBG) log("otaShowProgramFailureDialog()...");
999        mApplication.cdmaOtaScreenState.otaScreenState =
1000                CdmaOtaScreenState.OtaScreenState.OTA_STATUS_SUCCESS_FAILURE_DLG;
1001        mOtaWidgetData.otaTitle.setText(R.string.ota_title_problem_with_activation);
1002        mOtaWidgetData.otaTextSuccessFail.setVisibility(View.VISIBLE);
1003        mOtaWidgetData.otaTextSuccessFail.setText(R.string.ota_unsuccessful);
1004        mOtaWidgetData.callCardOtaButtonsFailSuccess.setVisibility(View.VISIBLE);
1005        mOtaWidgetData.otaTryAgainButton.setVisibility(View.VISIBLE);
1006        //close the dialer if open
1007        if (isDialerOpened()) {
1008            mOtaCallCardDtmfDialer.closeDialer(false);
1009        }
1010    }
1011
1012    /**
1013     * Show programming success dialog when OTA provisioning succeeds.
1014     */
1015    private void otaShowProgramSuccessDialog() {
1016        if (DBG) log("otaShowProgramSuccessDialog()...");
1017        mApplication.cdmaOtaScreenState.otaScreenState =
1018                CdmaOtaScreenState.OtaScreenState.OTA_STATUS_SUCCESS_FAILURE_DLG;
1019        mOtaWidgetData.otaTitle.setText(R.string.ota_title_activate_success);
1020        mOtaWidgetData.otaTextSuccessFail.setVisibility(View.VISIBLE);
1021        mOtaWidgetData.otaTextSuccessFail.setText(R.string.ota_successful);
1022        mOtaWidgetData.callCardOtaButtonsFailSuccess.setVisibility(View.VISIBLE);
1023        mOtaWidgetData.otaNextButton.setVisibility(View.VISIBLE);
1024        //close the dialer if open
1025        if (isDialerOpened()) {
1026            mOtaCallCardDtmfDialer.closeDialer(false);
1027        }
1028    }
1029
1030    /**
1031     * Show SPC failure notice when SPC attempts exceed 15 times.
1032     * During OTA provisioning, if SPC code is incorrect OTA provisioning will
1033     * fail. When SPC attempts are over 15, it shows SPC failure notice for one minute and
1034     * then phone will power down.
1035     */
1036    private void otaShowSpcErrorNotice(int length) {
1037        if (DBG) log("otaShowSpcErrorNotice()...");
1038        if (mOtaWidgetData.spcErrorDialog == null) {
1039            mApplication.cdmaOtaProvisionData.inOtaSpcState = true;
1040            DialogInterface.OnKeyListener keyListener;
1041            keyListener = new DialogInterface.OnKeyListener() {
1042                public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event) {
1043                    log("Ignoring key events...");
1044                    return true;
1045                }};
1046            mOtaWidgetData.spcErrorDialog = new AlertDialog.Builder(mInCallScreen)
1047                    .setMessage(R.string.ota_spc_failure)
1048                    .setOnKeyListener(keyListener)
1049                    .create();
1050            mOtaWidgetData.spcErrorDialog.getWindow().addFlags(
1051                    WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
1052                    | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
1053            mOtaWidgetData.spcErrorDialog.show();
1054            //close the dialer if open
1055            if (isDialerOpened()) {
1056                mOtaCallCardDtmfDialer.closeDialer(false);
1057            }
1058            long noticeTime = length*1000;
1059            if (DBG) log("otaShowSpcErrorNotice(), remaining SPC noticeTime" + noticeTime);
1060            mInCallScreen.requestCloseSpcErrorNotice(noticeTime);
1061        }
1062    }
1063
1064    /**
1065     * When SPC notice times out, force phone to power down.
1066     */
1067    public void onOtaCloseSpcNotice() {
1068        if (DBG) log("onOtaCloseSpcNotice(), send shutdown intent");
1069        Intent shutdown = new Intent(Intent.ACTION_REQUEST_SHUTDOWN);
1070        shutdown.putExtra(Intent.EXTRA_KEY_CONFIRM, false);
1071        shutdown.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
1072        mContext.startActivity(shutdown);
1073    }
1074
1075    /**
1076     * Show two-second notice when OTA provisioning fails and number of failed attempts
1077     * is less then 3.
1078     */
1079    private void otaShowProgramFailureNotice(int length) {
1080        if (DBG) log("otaShowProgramFailureNotice()...");
1081        if (mOtaWidgetData.otaFailureDialog == null) {
1082            mOtaWidgetData.otaFailureDialog = new AlertDialog.Builder(mInCallScreen)
1083                    .setMessage(R.string.ota_failure)
1084                    .create();
1085            mOtaWidgetData.otaFailureDialog.getWindow().addFlags(
1086                    WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
1087                    | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
1088            mOtaWidgetData.otaFailureDialog.show();
1089
1090            long noticeTime = length*1000;
1091            mInCallScreen.requestCloseOtaFailureNotice(noticeTime);
1092        }
1093    }
1094
1095    /**
1096     * Handle OTA unsuccessful notice expiry. Dismisses the
1097     * two-second notice and shows the activation screen.
1098     */
1099    public void onOtaCloseFailureNotice() {
1100        if (DBG) log("onOtaCloseFailureNotice()...");
1101        if (mOtaWidgetData.otaFailureDialog != null) {
1102            mOtaWidgetData.otaFailureDialog.dismiss();
1103            mOtaWidgetData.otaFailureDialog = null;
1104        }
1105        otaShowActivateScreen();
1106    }
1107
1108    /**
1109     * Initialize all OTA UI elements to be gone. Also set inCallPanel,
1110     * callCard and the dialpad handle to be gone. This is called before any OTA screen
1111     * gets drawn.
1112     */
1113    private void otaScreenInitialize() {
1114        if (DBG) log("otaScreenInitialize()...");
1115
1116        if (!mInteractive) {
1117            // We should never be doing anything with UI elements in
1118            // non-interactive mode.
1119            Log.w(LOG_TAG, "otaScreenInitialize: not interactive!");
1120            return;
1121        }
1122
1123        if (mInCallTouchUi != null) mInCallTouchUi.setVisibility(View.GONE);
1124        if (mCallCard != null) {
1125            mCallCard.setVisibility(View.GONE);
1126            // TODO: try removing this.
1127            mCallCard.hideCallCardElements();
1128        }
1129
1130        mOtaWidgetData.otaTitle.setText(R.string.ota_title_activate);
1131        mOtaWidgetData.otaTextActivate.setVisibility(View.GONE);
1132        mOtaWidgetData.otaTextListenProgress.setVisibility(View.GONE);
1133        mOtaWidgetData.otaTextProgressBar.setVisibility(View.GONE);
1134        mOtaWidgetData.otaTextSuccessFail.setVisibility(View.GONE);
1135        mOtaWidgetData.callCardOtaButtonsActivate.setVisibility(View.GONE);
1136        mOtaWidgetData.callCardOtaButtonsListenProgress.setVisibility(View.GONE);
1137        mOtaWidgetData.callCardOtaButtonsFailSuccess.setVisibility(View.GONE);
1138        mOtaWidgetData.otaDtmfDialerView.setVisibility(View.GONE);
1139        mOtaWidgetData.otaSpeakerButton.setVisibility(View.GONE);
1140        mOtaWidgetData.otaTryAgainButton.setVisibility(View.GONE);
1141        mOtaWidgetData.otaNextButton.setVisibility(View.GONE);
1142        mOtaWidgetData.otaUpperWidgets.setVisibility(View.VISIBLE);
1143        mOtaWidgetData.otaSkipButton.setVisibility(View.VISIBLE);
1144    }
1145
1146    public void hideOtaScreen() {
1147        if (DBG) log("hideOtaScreen()...");
1148
1149        mOtaWidgetData.callCardOtaButtonsActivate.setVisibility(View.GONE);
1150        mOtaWidgetData.callCardOtaButtonsListenProgress.setVisibility(View.GONE);
1151        mOtaWidgetData.callCardOtaButtonsFailSuccess.setVisibility(View.GONE);
1152        mOtaWidgetData.otaUpperWidgets.setVisibility(View.GONE);
1153    }
1154
1155    public boolean isDialerOpened() {
1156        boolean retval = (mOtaCallCardDtmfDialer != null && mOtaCallCardDtmfDialer.isOpened());
1157        if (DBG) log("- isDialerOpened() ==> " + retval);
1158        return retval;
1159    }
1160
1161    /**
1162     * Show the appropriate OTA screen based on the current state of OTA call.
1163     *
1164     * This is called from the InCallScreen when the screen needs to be
1165     * refreshed (and thus is only ever used in interactive mode.)
1166     *
1167     * Since this is called as part of the InCallScreen.updateScreen() sequence,
1168     * this method does *not* post an mInCallScreen.requestUpdateScreen()
1169     * request.
1170     */
1171    public void otaShowProperScreen() {
1172        if (DBG) log("otaShowProperScreen()...");
1173        if (!mInteractive) {
1174            // We shouldn't ever get here in non-interactive mode!
1175            Log.w(LOG_TAG, "otaShowProperScreen: not interactive!");
1176            return;
1177        }
1178
1179        if ((mInCallScreen != null) && mInCallScreen.isForegroundActivity()) {
1180            if (DBG) log("otaShowProperScreen(): InCallScreen in foreground, currentstate = "
1181                    + mApplication.cdmaOtaScreenState.otaScreenState);
1182            if (mInCallTouchUi != null) {
1183                mInCallTouchUi.setVisibility(View.GONE);
1184            }
1185            if (mCallCard != null) {
1186                mCallCard.setVisibility(View.GONE);
1187            }
1188            if (mApplication.cdmaOtaScreenState.otaScreenState
1189                    == CdmaOtaScreenState.OtaScreenState.OTA_STATUS_ACTIVATION) {
1190                otaShowActivateScreen();
1191            } else if (mApplication.cdmaOtaScreenState.otaScreenState
1192                    == CdmaOtaScreenState.OtaScreenState.OTA_STATUS_LISTENING) {
1193                otaShowListeningScreen();
1194            } else if (mApplication.cdmaOtaScreenState.otaScreenState
1195                    == CdmaOtaScreenState.OtaScreenState.OTA_STATUS_PROGRESS) {
1196                otaShowInProgressScreen();
1197            }
1198
1199            if (mApplication.cdmaOtaProvisionData.inOtaSpcState) {
1200                otaShowSpcErrorNotice(getOtaSpcDisplayTime());
1201            }
1202        }
1203    }
1204
1205    /**
1206     * Read configuration values for each OTA screen from config.xml.
1207     * These configuration values control visibility of each screen.
1208     */
1209    private void readXmlSettings() {
1210        if (DBG) log("readXmlSettings()...");
1211        if (mApplication.cdmaOtaConfigData.configComplete) {
1212            return;
1213        }
1214
1215        mApplication.cdmaOtaConfigData.configComplete = true;
1216        int tmpOtaShowActivationScreen =
1217                mContext.getResources().getInteger(R.integer.OtaShowActivationScreen);
1218        mApplication.cdmaOtaConfigData.otaShowActivationScreen = tmpOtaShowActivationScreen;
1219        if (DBG) log("readXmlSettings(), otaShowActivationScreen = "
1220                + mApplication.cdmaOtaConfigData.otaShowActivationScreen);
1221
1222        int tmpOtaShowListeningScreen =
1223                mContext.getResources().getInteger(R.integer.OtaShowListeningScreen);
1224        mApplication.cdmaOtaConfigData.otaShowListeningScreen = tmpOtaShowListeningScreen;
1225        if (DBG) log("readXmlSettings(), otaShowListeningScreen = "
1226                + mApplication.cdmaOtaConfigData.otaShowListeningScreen);
1227
1228        int tmpOtaShowActivateFailTimes =
1229                mContext.getResources().getInteger(R.integer.OtaShowActivateFailTimes);
1230        mApplication.cdmaOtaConfigData.otaShowActivateFailTimes = tmpOtaShowActivateFailTimes;
1231        if (DBG) log("readXmlSettings(), otaShowActivateFailTimes = "
1232                + mApplication.cdmaOtaConfigData.otaShowActivateFailTimes);
1233
1234        int tmpOtaPlaySuccessFailureTone =
1235                mContext.getResources().getInteger(R.integer.OtaPlaySuccessFailureTone);
1236        mApplication.cdmaOtaConfigData.otaPlaySuccessFailureTone = tmpOtaPlaySuccessFailureTone;
1237        if (DBG) log("readXmlSettings(), otaPlaySuccessFailureTone = "
1238                + mApplication.cdmaOtaConfigData.otaPlaySuccessFailureTone);
1239    }
1240
1241    /**
1242     * Handle the click events for OTA buttons.
1243     */
1244    public void onClickHandler(int id) {
1245        switch (id) {
1246            case R.id.otaEndButton:
1247                onClickOtaEndButton();
1248                break;
1249
1250            case R.id.otaSpeakerButton:
1251                onClickOtaSpeakerButton();
1252                break;
1253
1254            case R.id.otaActivateButton:
1255                onClickOtaActivateButton();
1256                break;
1257
1258            case R.id.otaSkipButton:
1259                onClickOtaActivateSkipButton();
1260                break;
1261
1262            case R.id.otaNextButton:
1263                onClickOtaActivateNextButton();
1264                break;
1265
1266            case R.id.otaTryAgainButton:
1267                onClickOtaTryAgainButton();
1268                break;
1269
1270            default:
1271                if (DBG) log ("onClickHandler: received a click event for unrecognized id");
1272                break;
1273        }
1274    }
1275
1276    private void onClickOtaTryAgainButton() {
1277        if (DBG) log("Activation Try Again Clicked!");
1278        if (!mApplication.cdmaOtaProvisionData.inOtaSpcState) {
1279            otaShowActivateScreen();
1280        }
1281    }
1282
1283    private void onClickOtaEndButton() {
1284        if (DBG) log("Activation End Call Button Clicked!");
1285        if (!mApplication.cdmaOtaProvisionData.inOtaSpcState) {
1286            if (PhoneUtils.hangup(mApplication.mCM) == false) {
1287                // If something went wrong when placing the OTA call,
1288                // the screen is not updated by the call disconnect
1289                // handler and we have to do it here
1290                setSpeaker(false);
1291                mInCallScreen.handleOtaCallEnd();
1292            }
1293        }
1294    }
1295
1296    private void onClickOtaSpeakerButton() {
1297        if (DBG) log("OTA Speaker button Clicked!");
1298        if (!mApplication.cdmaOtaProvisionData.inOtaSpcState) {
1299            boolean isChecked = !PhoneUtils.isSpeakerOn(mContext);
1300            setSpeaker(isChecked);
1301        }
1302    }
1303
1304    private void onClickOtaActivateButton() {
1305        if (DBG) log("Call Activation Clicked!");
1306        otaPerformActivation();
1307    }
1308
1309    private void onClickOtaActivateSkipButton() {
1310        if (DBG) log("Activation Skip Clicked!");
1311        DialogInterface.OnKeyListener keyListener;
1312        keyListener = new DialogInterface.OnKeyListener() {
1313            public boolean onKey(DialogInterface dialog, int keyCode,
1314                    KeyEvent event) {
1315                if (DBG) log("Ignoring key events...");
1316                return true;
1317            }
1318        };
1319        mOtaWidgetData.otaSkipConfirmationDialog = new AlertDialog.Builder(mInCallScreen)
1320                .setTitle(R.string.ota_skip_activation_dialog_title)
1321                .setMessage(R.string.ota_skip_activation_dialog_message)
1322                .setPositiveButton(
1323                    android.R.string.ok,
1324                    // "OK" means "skip activation".
1325                    new AlertDialog.OnClickListener() {
1326                        public void onClick(DialogInterface dialog, int which) {
1327                            otaSkipActivation();
1328                        }
1329                    })
1330                .setNegativeButton(
1331                    android.R.string.cancel,
1332                    // "Cancel" means just dismiss the dialog.
1333                    // Don't actually start an activation call.
1334                    null)
1335                .setOnKeyListener(keyListener)
1336                .create();
1337        mOtaWidgetData.otaSkipConfirmationDialog.show();
1338    }
1339
1340    private void onClickOtaActivateNextButton() {
1341        if (DBG) log("Dialog Next Clicked!");
1342        if (!mApplication.cdmaOtaProvisionData.inOtaSpcState) {
1343            mApplication.cdmaOtaScreenState.otaScreenState =
1344                    CdmaOtaScreenState.OtaScreenState.OTA_STATUS_UNDEFINED;
1345            otaShowHome();
1346        }
1347    }
1348
1349    public void dismissAllOtaDialogs() {
1350        if (mOtaWidgetData != null) {
1351            if (mOtaWidgetData.spcErrorDialog != null) {
1352                if (DBG) log("- DISMISSING mSpcErrorDialog.");
1353                mOtaWidgetData.spcErrorDialog.dismiss();
1354                mOtaWidgetData.spcErrorDialog = null;
1355            }
1356            if (mOtaWidgetData.otaFailureDialog != null) {
1357                if (DBG) log("- DISMISSING mOtaFailureDialog.");
1358                mOtaWidgetData.otaFailureDialog.dismiss();
1359                mOtaWidgetData.otaFailureDialog = null;
1360            }
1361        }
1362    }
1363
1364    private int getOtaSpcDisplayTime() {
1365        if (DBG) log("getOtaSpcDisplayTime()...");
1366        int tmpSpcTime = 1;
1367        if (mApplication.cdmaOtaProvisionData.inOtaSpcState) {
1368            long tmpOtaSpcRunningTime = 0;
1369            long tmpOtaSpcLeftTime = 0;
1370            tmpOtaSpcRunningTime = SystemClock.elapsedRealtime();
1371            tmpOtaSpcLeftTime =
1372                tmpOtaSpcRunningTime - mApplication.cdmaOtaProvisionData.otaSpcUptime;
1373            if (tmpOtaSpcLeftTime >= OTA_SPC_TIMEOUT*1000) {
1374                tmpSpcTime = 1;
1375            } else {
1376                tmpSpcTime = OTA_SPC_TIMEOUT - (int)tmpOtaSpcLeftTime/1000;
1377            }
1378        }
1379        if (DBG) log("getOtaSpcDisplayTime(), time for SPC error notice: " + tmpSpcTime);
1380        return tmpSpcTime;
1381    }
1382
1383    /**
1384     * Initialize the OTA widgets for all OTA screens.
1385     */
1386    private void initOtaInCallScreen() {
1387        if (DBG) log("initOtaInCallScreen()...");
1388        mOtaWidgetData.otaTitle = (TextView) mInCallScreen.findViewById(R.id.otaTitle);
1389        mOtaWidgetData.otaTextActivate = (TextView) mInCallScreen.findViewById(R.id.otaActivate);
1390        mOtaWidgetData.otaTextActivate.setVisibility(View.GONE);
1391        mOtaWidgetData.otaTextListenProgress =
1392                (TextView) mInCallScreen.findViewById(R.id.otaListenProgress);
1393        mOtaWidgetData.otaTextProgressBar =
1394                (ProgressBar) mInCallScreen.findViewById(R.id.progress_large);
1395        mOtaWidgetData.otaTextProgressBar.setIndeterminate(true);
1396        mOtaWidgetData.otaTextSuccessFail =
1397                (TextView) mInCallScreen.findViewById(R.id.otaSuccessFailStatus);
1398
1399        mOtaWidgetData.otaUpperWidgets =
1400                (ViewGroup) mInCallScreen.findViewById(R.id.otaUpperWidgets);
1401        mOtaWidgetData.callCardOtaButtonsListenProgress =
1402                (View) mInCallScreen.findViewById(R.id.callCardOtaListenProgress);
1403        mOtaWidgetData.callCardOtaButtonsActivate =
1404                (View) mInCallScreen.findViewById(R.id.callCardOtaActivate);
1405        mOtaWidgetData.callCardOtaButtonsFailSuccess =
1406                (View) mInCallScreen.findViewById(R.id.callCardOtaFailOrSuccessful);
1407
1408        mOtaWidgetData.otaEndButton = (Button) mInCallScreen.findViewById(R.id.otaEndButton);
1409        mOtaWidgetData.otaEndButton.setOnClickListener(mInCallScreen);
1410        mOtaWidgetData.otaSpeakerButton =
1411                (ToggleButton) mInCallScreen.findViewById(R.id.otaSpeakerButton);
1412        mOtaWidgetData.otaSpeakerButton.setOnClickListener(mInCallScreen);
1413        mOtaWidgetData.otaActivateButton =
1414                (Button) mInCallScreen.findViewById(R.id.otaActivateButton);
1415        mOtaWidgetData.otaActivateButton.setOnClickListener(mInCallScreen);
1416        mOtaWidgetData.otaSkipButton = (Button) mInCallScreen.findViewById(R.id.otaSkipButton);
1417        mOtaWidgetData.otaSkipButton.setOnClickListener(mInCallScreen);
1418        mOtaWidgetData.otaNextButton = (Button) mInCallScreen.findViewById(R.id.otaNextButton);
1419        mOtaWidgetData.otaNextButton.setOnClickListener(mInCallScreen);
1420        mOtaWidgetData.otaTryAgainButton =
1421                (Button) mInCallScreen.findViewById(R.id.otaTryAgainButton);
1422        mOtaWidgetData.otaTryAgainButton.setOnClickListener(mInCallScreen);
1423
1424        mOtaWidgetData.otaDtmfDialerView =
1425                (DTMFTwelveKeyDialerView) mInCallScreen.findViewById(R.id.otaDtmfDialerView);
1426        // Sanity-check: the otaDtmfDialerView widget should *always* be present.
1427        if (mOtaWidgetData.otaDtmfDialerView == null) {
1428            throw new IllegalStateException("initOtaInCallScreen: couldn't find otaDtmfDialerView");
1429        }
1430
1431        // Create a new DTMFTwelveKeyDialer instance purely for use by the
1432        // DTMFTwelveKeyDialerView ("otaDtmfDialerView") that comes from
1433        // otacall_card.xml.
1434        mOtaCallCardDtmfDialer = new DTMFTwelveKeyDialer(mInCallScreen,
1435                                                         mOtaWidgetData.otaDtmfDialerView);
1436
1437        // Initialize the new DTMFTwelveKeyDialer instance.  This is
1438        // needed to play local DTMF tones.
1439        mOtaCallCardDtmfDialer.startDialerSession();
1440
1441        mOtaWidgetData.otaDtmfDialerView.setDialer(mOtaCallCardDtmfDialer);
1442    }
1443
1444    /**
1445     * Clear out all OTA UI widget elements. Needs to get called
1446     * when OTA call ends or InCallScreen is destroyed.
1447     * @param disableSpeaker parameter control whether Speaker should be turned off.
1448     */
1449    public void cleanOtaScreen(boolean disableSpeaker) {
1450        if (DBG) log("OTA ends, cleanOtaScreen!");
1451
1452        mApplication.cdmaOtaScreenState.otaScreenState =
1453                CdmaOtaScreenState.OtaScreenState.OTA_STATUS_UNDEFINED;
1454        mApplication.cdmaOtaProvisionData.isOtaCallCommitted = false;
1455        mApplication.cdmaOtaProvisionData.isOtaCallIntentProcessed = false;
1456        mApplication.cdmaOtaProvisionData.inOtaSpcState = false;
1457        mApplication.cdmaOtaProvisionData.activationCount = 0;
1458        mApplication.cdmaOtaProvisionData.otaSpcUptime = 0;
1459        mApplication.cdmaOtaInCallScreenUiState.state = State.UNDEFINED;
1460
1461        if (mInteractive && (mOtaWidgetData != null)) {
1462            if (mInCallTouchUi != null) mInCallTouchUi.setVisibility(View.VISIBLE);
1463            if (mCallCard != null) {
1464                mCallCard.setVisibility(View.VISIBLE);
1465                mCallCard.hideCallCardElements();
1466            }
1467
1468            // Free resources from the DTMFTwelveKeyDialer instance we created
1469            // in initOtaInCallScreen().
1470            if (mOtaCallCardDtmfDialer != null) {
1471                mOtaCallCardDtmfDialer.stopDialerSession();
1472            }
1473
1474            mOtaWidgetData.otaTextActivate.setVisibility(View.GONE);
1475            mOtaWidgetData.otaTextListenProgress.setVisibility(View.GONE);
1476            mOtaWidgetData.otaTextProgressBar.setVisibility(View.GONE);
1477            mOtaWidgetData.otaTextSuccessFail.setVisibility(View.GONE);
1478            mOtaWidgetData.callCardOtaButtonsActivate.setVisibility(View.GONE);
1479            mOtaWidgetData.callCardOtaButtonsListenProgress.setVisibility(View.GONE);
1480            mOtaWidgetData.callCardOtaButtonsFailSuccess.setVisibility(View.GONE);
1481            mOtaWidgetData.otaUpperWidgets.setVisibility(View.GONE);
1482            mOtaWidgetData.otaDtmfDialerView.setVisibility(View.GONE);
1483            mOtaWidgetData.otaNextButton.setVisibility(View.GONE);
1484            mOtaWidgetData.otaTryAgainButton.setVisibility(View.GONE);
1485        }
1486
1487        // turn off the speaker in case it was turned on
1488        // but the OTA call could not be completed
1489        if (disableSpeaker) {
1490            setSpeaker(false);
1491        }
1492    }
1493
1494    /**
1495     * Defines OTA information that needs to be maintained during
1496     * an OTA call when display orientation changes.
1497     */
1498    public static class CdmaOtaProvisionData {
1499        public boolean isOtaCallCommitted;
1500        public boolean isOtaCallIntentProcessed;
1501        public boolean inOtaSpcState;
1502        public int activationCount;
1503        public long otaSpcUptime;
1504    }
1505
1506    /**
1507     * Defines OTA screen configuration items read from config.xml
1508     * and used to control OTA display.
1509     */
1510    public static class CdmaOtaConfigData {
1511        public int otaShowActivationScreen;
1512        public int otaShowListeningScreen;
1513        public int otaShowActivateFailTimes;
1514        public int otaPlaySuccessFailureTone;
1515        public boolean configComplete;
1516        public CdmaOtaConfigData() {
1517            if (DBG) log("CdmaOtaConfigData constructor!");
1518            otaShowActivationScreen = OTA_SHOW_ACTIVATION_SCREEN_OFF;
1519            otaShowListeningScreen = OTA_SHOW_LISTENING_SCREEN_OFF;
1520            otaShowActivateFailTimes = OTA_SHOW_ACTIVATE_FAIL_COUNT_OFF;
1521            otaPlaySuccessFailureTone = OTA_PLAY_SUCCESS_FAILURE_TONE_OFF;
1522        }
1523    }
1524
1525    /**
1526     * The state of the OTA InCallScreen UI.
1527     */
1528    public static class CdmaOtaInCallScreenUiState {
1529        public enum State {
1530            UNDEFINED,
1531            NORMAL,
1532            ENDED
1533        }
1534
1535        public State state;
1536
1537        public CdmaOtaInCallScreenUiState() {
1538            if (DBG) log("CdmaOtaInCallScreenState: constructor init to UNDEFINED");
1539            state = CdmaOtaInCallScreenUiState.State.UNDEFINED;
1540        }
1541    }
1542
1543    /**
1544     * Save the Ota InCallScreen UI state
1545     */
1546    public void setCdmaOtaInCallScreenUiState(CdmaOtaInCallScreenUiState.State state) {
1547        if (DBG) log("setCdmaOtaInCallScreenState: " + state);
1548        mApplication.cdmaOtaInCallScreenUiState.state = state;
1549    }
1550
1551    /**
1552     * Get the Ota InCallScreen UI state
1553     */
1554    public CdmaOtaInCallScreenUiState.State getCdmaOtaInCallScreenUiState() {
1555        if (DBG) log("getCdmaOtaInCallScreenState: "
1556                     + mApplication.cdmaOtaInCallScreenUiState.state);
1557        return mApplication.cdmaOtaInCallScreenUiState.state;
1558    }
1559
1560    /**
1561     * The OTA screen state machine.
1562     */
1563    public static class CdmaOtaScreenState {
1564        public enum OtaScreenState {
1565            OTA_STATUS_UNDEFINED,
1566            OTA_STATUS_ACTIVATION,
1567            OTA_STATUS_LISTENING,
1568            OTA_STATUS_PROGRESS,
1569            OTA_STATUS_SUCCESS_FAILURE_DLG
1570        }
1571
1572        public OtaScreenState otaScreenState;
1573
1574        public CdmaOtaScreenState() {
1575            otaScreenState = OtaScreenState.OTA_STATUS_UNDEFINED;
1576        }
1577
1578        /**
1579         * {@link PendingIntent} used to report an OTASP result status code
1580         * back to our caller. Can be null.
1581         *
1582         * Our caller (presumably SetupWizard) may create this PendingIntent,
1583         * pointing back at itself, and passes it along as an extra with the
1584         * ACTION_PERFORM_CDMA_PROVISIONING intent.  Then, when there's an
1585         * OTASP result to report, we send that PendingIntent back, adding an
1586         * extra called EXTRA_OTASP_RESULT_CODE to indicate the result.
1587         *
1588         * Possible result values are the OTASP_RESULT_* constants.
1589         */
1590        public PendingIntent otaspResultCodePendingIntent;
1591    }
1592
1593    /** @see com.android.internal.telephony.Phone */
1594    private static String otaProvisionStatusToString(int status) {
1595        switch (status) {
1596            case Phone.CDMA_OTA_PROVISION_STATUS_SPL_UNLOCKED:
1597                return "SPL_UNLOCKED";
1598            case Phone.CDMA_OTA_PROVISION_STATUS_SPC_RETRIES_EXCEEDED:
1599                return "SPC_RETRIES_EXCEEDED";
1600            case Phone.CDMA_OTA_PROVISION_STATUS_A_KEY_EXCHANGED:
1601                return "A_KEY_EXCHANGED";
1602            case Phone.CDMA_OTA_PROVISION_STATUS_SSD_UPDATED:
1603                return "SSD_UPDATED";
1604            case Phone.CDMA_OTA_PROVISION_STATUS_NAM_DOWNLOADED:
1605                return "NAM_DOWNLOADED";
1606            case Phone.CDMA_OTA_PROVISION_STATUS_MDN_DOWNLOADED:
1607                return "MDN_DOWNLOADED";
1608            case Phone.CDMA_OTA_PROVISION_STATUS_IMSI_DOWNLOADED:
1609                return "IMSI_DOWNLOADED";
1610            case Phone.CDMA_OTA_PROVISION_STATUS_PRL_DOWNLOADED:
1611                return "PRL_DOWNLOADED";
1612            case Phone.CDMA_OTA_PROVISION_STATUS_COMMITTED:
1613                return "COMMITTED";
1614            case Phone.CDMA_OTA_PROVISION_STATUS_OTAPA_STARTED:
1615                return "OTAPA_STARTED";
1616            case Phone.CDMA_OTA_PROVISION_STATUS_OTAPA_STOPPED:
1617                return "OTAPA_STOPPED";
1618            case Phone.CDMA_OTA_PROVISION_STATUS_OTAPA_ABORTED:
1619                return "OTAPA_ABORTED";
1620            default:
1621                return "<unknown status" + status + ">";
1622        }
1623    }
1624
1625    private static int getLteOnCdmaMode(Context context) {
1626        final TelephonyManager telephonyManager = (TelephonyManager) context.getSystemService(
1627                Context.TELEPHONY_SERVICE);
1628        // If the telephony manager is not available yet, or if it doesn't know the answer yet,
1629        // try falling back on the system property that may or may not be there
1630        if (telephonyManager == null
1631                || telephonyManager.getLteOnCdmaMode() == PhoneConstants.LTE_ON_CDMA_UNKNOWN) {
1632            return SystemProperties.getInt(TelephonyProperties.PROPERTY_LTE_ON_CDMA_DEVICE,
1633                    PhoneConstants.LTE_ON_CDMA_UNKNOWN);
1634        }
1635        return telephonyManager.getLteOnCdmaMode();
1636    }
1637
1638    private static void log(String msg) {
1639        Log.d(LOG_TAG, msg);
1640    }
1641
1642    private static void loge(String msg) {
1643        Log.e(LOG_TAG, msg);
1644    }
1645}
1646