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.phone.OtaUtils.CdmaOtaInCallScreenUiState.State;
21
22import android.app.AlertDialog;
23import android.app.PendingIntent;
24import android.app.PendingIntent.CanceledException;
25import android.content.ActivityNotFoundException;
26import android.content.Context;
27import android.content.DialogInterface;
28import android.content.Intent;
29import android.content.pm.ResolveInfo;
30import android.os.AsyncResult;
31import android.os.Handler;
32import android.os.Message;
33import android.os.SystemClock;
34import android.os.SystemProperties;
35import android.provider.Settings;
36
37import android.util.Log;
38import android.view.KeyEvent;
39import android.view.View;
40import android.view.ViewGroup;
41import android.view.ViewStub;
42import android.view.WindowManager;
43
44import android.widget.Button;
45import android.widget.ScrollView;
46import android.widget.ToggleButton;
47import android.widget.ProgressBar;
48import android.widget.TextView;
49
50/**
51 * Handles all OTA Call related logic and UI functionality.
52 * The InCallScreen interacts with this class to perform an OTA Call.
53 *
54 * OTA is a CDMA-specific feature:
55 *   OTA or OTASP == Over The Air service provisioning
56 *   SPC == Service Programming Code
57 *   TODO: Include pointer to more detailed documentation.
58 */
59public class OtaUtils {
60    private static final String LOG_TAG = "OtaUtils";
61    private static final String UNACTIVATED_MIN2_VALUE = "000000";
62    private static final String UNACTIVATED_MIN_VALUE = "1111110111";
63    private static final boolean DBG = (PhoneApp.DBG_LEVEL >= 1);
64
65    public static final int OTA_SHOW_ACTIVATION_SCREEN_OFF = 0;
66    public static final int OTA_SHOW_ACTIVATION_SCREEN_ON = 1;
67    public static final int OTA_SHOW_LISTENING_SCREEN_OFF =0;
68    public static final int OTA_SHOW_LISTENING_SCREEN_ON =1;
69    public static final int OTA_SHOW_ACTIVATE_FAIL_COUNT_OFF = 0;
70    public static final int OTA_SHOW_ACTIVATE_FAIL_COUNT_THREE = 3;
71    public static final int OTA_PLAY_SUCCESS_FAILURE_TONE_OFF = 0;
72    public static final int OTA_PLAY_SUCCESS_FAILURE_TONE_ON = 1;
73
74    // SPC Timeout is 60 seconds
75    public final int OTA_SPC_TIMEOUT = 60;
76    public final int OTA_FAILURE_DIALOG_TIMEOUT = 2;
77
78    private InCallScreen mInCallScreen;
79    private Context mContext;
80    private PhoneApp mApplication;
81    private OtaWidgetData mOtaWidgetData;
82    private ViewGroup mInCallPanel;
83    private CallCard mCallCard;
84
85    // The DTMFTwelveKeyDialer instance owned by the InCallScreen, which
86    // the InCallScreen passes in to our constructor.
87    private DTMFTwelveKeyDialer mDialer;
88    //
89    // The DTMFTwelveKeyDialer instance that we create ourselves in
90    // initOtaInCallScreen(), and attach to the DTMFTwelveKeyDialerView
91    // ("otaDtmfDialerView") that comes from otacall_card.xml.
92    private DTMFTwelveKeyDialer mOtaCallCardDtmfDialer;
93    // TODO: we ought to share a single DTMFTwelveKeyDialer instance for
94    // both these uses, but see bug 2432289 for related issues.
95
96    private static boolean mIsWizardMode = true;
97
98    /**
99     * OtaWidgetData class represent all OTA UI elements
100     */
101    private class OtaWidgetData {
102        public Button otaEndButton;
103        public Button otaActivateButton;
104        public Button otaSkipButton;
105        public Button otaNextButton;
106        public ToggleButton otaSpeakerButton;
107        public View otaCallCardBase;
108        public View callCardOtaButtonsFailSuccess;
109        public ProgressBar otaTextProgressBar;
110        public TextView otaTextSuccessFail;
111        public View callCardOtaButtonsActivate;
112        public View callCardOtaButtonsListenProgress;
113        public TextView otaTextActivate;
114        public TextView otaTextListenProgress;
115        public ScrollView otaTextListenProgressContainer;
116        public AlertDialog spcErrorDialog;
117        public AlertDialog otaFailureDialog;
118        public AlertDialog otaSkipConfirmationDialog;
119        public TextView otaTitle;
120        public DTMFTwelveKeyDialerView otaDtmfDialerView;
121        public Button otaTryAgainButton;
122    }
123
124    public OtaUtils(Context context,
125                    InCallScreen inCallScreen,
126                    ViewGroup inCallPanel,
127                    CallCard callCard,
128                    DTMFTwelveKeyDialer dialer) {
129
130        if (DBG) log("Enter OtaUtil constructor");
131
132        mInCallScreen = inCallScreen;
133        mContext = context;
134        mInCallPanel = inCallPanel;
135        mCallCard = callCard;
136        mDialer = dialer;
137        mApplication = PhoneApp.getInstance();
138        mOtaWidgetData = new OtaWidgetData();
139
140        // inflate OTA Call card and footers
141        ViewStub otaCallCardStub = (ViewStub) mInCallScreen.findViewById(R.id.otaCallCardStub);
142        otaCallCardStub.inflate();
143        readXmlSettings();
144        initOtaInCallScreen();
145    }
146
147    /**
148     * Returns true if the phone needs activation.
149     *
150     * @param minString the phone's MIN configuration string
151     * @return true if phone needs activation
152     * @throws OtaConfigurationException if the string is invalid
153     */
154    public static boolean needsActivation(String minString) throws IllegalArgumentException {
155        if (minString == null || (minString.length() < 6)) {
156            throw new IllegalArgumentException();
157        }
158        return (minString.equals(UNACTIVATED_MIN_VALUE)
159                || minString.substring(0,6).equals(UNACTIVATED_MIN2_VALUE))
160                || SystemProperties.getBoolean("test_cdma_setup", false);
161    }
162
163    /**
164     * Starts the OTA provisioning call.  If the MIN isn't available yet, it returns false and adds
165     * an event to return the request to the calling app when it becomes available.
166     *
167     * @param context
168     * @param handler
169     * @param request
170     * @return true if we were able to launch Ota activity or it's not required; false otherwise
171     */
172    public static boolean maybeDoOtaCall(Context context, Handler handler, int request) {
173
174        PhoneApp app = PhoneApp.getInstance();
175        Phone phone = app.phone;
176
177        if (!isCdmaPhone()) {
178            if (DBG) Log.v("OtaUtils", "Can't run provisioning on a non-CDMA phone");
179            return true; // sanity check - a non-cdma phone doesn't need to run this
180        }
181
182        if (!phone.isMinInfoReady()) {
183            if (DBG) log("MIN is not ready. Registering to receive notification.");
184            phone.registerForSubscriptionInfoReady(handler, request, null);
185            return false;
186        }
187
188        phone.unregisterForSubscriptionInfoReady(handler);
189        String min = phone.getCdmaMin();
190
191        if (DBG) log("min_string: " + min);
192
193        boolean phoneNeedsActivation = false;
194        try {
195            phoneNeedsActivation = needsActivation(min);
196        } catch (IllegalArgumentException e) {
197            if (DBG) log("invalid MIN string, exit");
198            return true; // If the MIN string is wrong, there's nothing else we can do.
199        }
200
201        if (DBG) log("phoneNeedsActivation is set to " + phoneNeedsActivation);
202
203        int otaShowActivationScreen = context.getResources().getInteger(
204                R.integer.OtaShowActivationScreen);
205
206        if (DBG) log("otaShowActivationScreen: " + otaShowActivationScreen);
207
208        if (phoneNeedsActivation && (otaShowActivationScreen == OTA_SHOW_ACTIVATION_SCREEN_ON)) {
209            app.cdmaOtaProvisionData.isOtaCallIntentProcessed = false;
210            Intent newIntent = new Intent(InCallScreen.ACTION_SHOW_ACTIVATION);
211            newIntent.setClass(context, InCallScreen.class);
212            newIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
213            mIsWizardMode = false;
214            context.startActivity(newIntent);
215            if (DBG) log("activation intent sent.");
216        } else {
217            if (DBG) log("activation intent NOT sent.");
218        }
219        return true;
220    }
221
222    private void setSpeaker(boolean state) {
223        if (DBG) log("setSpeaker : " + state );
224        if (state == PhoneUtils.isSpeakerOn(mContext)) {
225            if (DBG) log("no change. returning");
226            return;
227        }
228
229        if (state && mInCallScreen.isBluetoothAvailable()
230                && mInCallScreen.isBluetoothAudioConnected()) {
231            mInCallScreen.disconnectBluetoothAudio();
232        }
233        PhoneUtils.turnOnSpeaker(mContext, state, true);
234    }
235
236    /**
237     * Handle OTA Provision events from Framework. Possible events are:
238     * OTA Commit Event - OTA provisioning was successful
239     * SPC retries exceeded - SPC failure retries has exceeded, and Phone needs to
240     *    power down.
241     */
242    public void onOtaProvisionStatusChanged(AsyncResult r) {
243        int OtaStatus[] = (int[]) r.result;
244        if (DBG) log("onOtaProvisionStatusChanged(): OtaStatus[0]" + OtaStatus[0]);
245
246        switch(OtaStatus[0]) {
247            case Phone.CDMA_OTA_PROVISION_STATUS_SPC_RETRIES_EXCEEDED:
248                otaShowInProgressScreen();
249                mApplication.cdmaOtaProvisionData.otaSpcUptime = SystemClock.elapsedRealtime();
250                otaShowSpcErrorNotice(OTA_SPC_TIMEOUT);
251                if (DBG) log("onOtaProvisionStatusChanged(): RETRIES EXCEEDED");
252                // Power.shutdown();
253                break;
254
255            case Phone.CDMA_OTA_PROVISION_STATUS_COMMITTED:
256                otaShowInProgressScreen();
257                mApplication.cdmaOtaProvisionData.isOtaCallCommitted = true;
258                if (DBG) log("onOtaProvisionStatusChanged(): DONE, isOtaCallCommitted set to true");
259                break;
260
261            case Phone.CDMA_OTA_PROVISION_STATUS_SPL_UNLOCKED:
262            case Phone.CDMA_OTA_PROVISION_STATUS_A_KEY_EXCHANGED:
263            case Phone.CDMA_OTA_PROVISION_STATUS_SSD_UPDATED:
264            case Phone.CDMA_OTA_PROVISION_STATUS_NAM_DOWNLOADED:
265            case Phone.CDMA_OTA_PROVISION_STATUS_MDN_DOWNLOADED:
266            case Phone.CDMA_OTA_PROVISION_STATUS_IMSI_DOWNLOADED:
267            case Phone.CDMA_OTA_PROVISION_STATUS_PRL_DOWNLOADED:
268            case Phone.CDMA_OTA_PROVISION_STATUS_OTAPA_STARTED:
269            case Phone.CDMA_OTA_PROVISION_STATUS_OTAPA_STOPPED:
270            case Phone.CDMA_OTA_PROVISION_STATUS_OTAPA_ABORTED:
271                if (DBG) log("onOtaProvisionStatusChanged(): change to ProgressScreen");
272                otaShowInProgressScreen();
273                break;
274
275            default:
276                if (DBG) log("onOtaProvisionStatusChanged(): Ignoring OtaStatus " + OtaStatus[0]);
277                break;
278        }
279    }
280
281    private void otaShowHome() {
282        if (DBG) log("OtaShowHome()...");
283        mApplication.cdmaOtaScreenState.otaScreenState =
284                CdmaOtaScreenState.OtaScreenState.OTA_STATUS_UNDEFINED;
285        mInCallScreen.endInCallScreenSession();
286        Intent intent = new Intent(Intent.ACTION_MAIN);
287        intent.addCategory (Intent.CATEGORY_HOME);
288        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
289        mContext.startActivity(intent);
290        return;
291    }
292
293    private void otaSkipActivation() {
294        if (DBG) log("otaSkipActivation()...");
295
296        PhoneApp app = PhoneApp.getInstance();
297        if (app != null && app.cdmaOtaInCallScreenUiState.reportSkipPendingIntent != null) {
298            try {
299                app.cdmaOtaInCallScreenUiState.reportSkipPendingIntent.send();
300            } catch (CanceledException e) {
301                // should never happen because no code cancels the pending intent right now,
302                // but if it does, the user will simply be returned to the initial setup screen
303            }
304        }
305
306        mInCallScreen.finish();
307        return;
308    }
309
310    private void otaPerformActivation() {
311        if (DBG) log("otaPerformActivation()...");
312        if (!mApplication.cdmaOtaProvisionData.inOtaSpcState) {
313            Intent newIntent = new Intent(Intent.ACTION_CALL);
314            newIntent.putExtra(Intent.EXTRA_PHONE_NUMBER, InCallScreen.OTA_NUMBER);
315            mInCallScreen.internalResolveIntent(newIntent);
316            otaShowListeningScreen();
317        }
318        return;
319    }
320
321    /**
322     * Show Activation Screen when phone powers up and OTA provision is
323     * required. Also shown when activation fails and user needs
324     * to re-attempt it. Contains ACTIVATE and SKIP buttons
325     * which allow user to start OTA activation or skip the activation process.
326     */
327    public void otaShowActivateScreen() {
328        if (DBG) log("OtaShowActivationScreen()...");
329        if (mApplication.cdmaOtaConfigData.otaShowActivationScreen
330                == OTA_SHOW_ACTIVATION_SCREEN_ON) {
331            if (DBG) log("OtaShowActivationScreen(): show activation screen");
332            if (!isDialerOpened()) {
333                otaScreenInitialize();
334                mOtaWidgetData.otaSkipButton.setVisibility(mIsWizardMode ?
335                        View.VISIBLE : View.INVISIBLE);
336                mOtaWidgetData.otaTextActivate.setVisibility(View.VISIBLE);
337                mOtaWidgetData.callCardOtaButtonsActivate.setVisibility(View.VISIBLE);
338            } else {
339                mDialer.setHandleVisible(true);
340            }
341            mApplication.cdmaOtaScreenState.otaScreenState =
342                    CdmaOtaScreenState.OtaScreenState.OTA_STATUS_ACTIVATION;
343        } else {
344            if (DBG) log("OtaShowActivationScreen(): show home screen");
345            otaShowHome();
346        }
347     }
348
349    /**
350     * Show "Listen for Instruction" screen during OTA call. Shown when OTA Call
351     * is initiated and user needs to listen for network instructions and press
352     * appropriate DTMF digits to proceed to the "Programming in Progress" phase.
353     */
354    private void otaShowListeningScreen() {
355        if (DBG) log("OtaShowListeningScreen()...");
356        if (mApplication.cdmaOtaConfigData.otaShowListeningScreen
357                == OTA_SHOW_LISTENING_SCREEN_ON) {
358            if (DBG) log("OtaShowListeningScreen(): show listening screen");
359            if (!isDialerOpened()) {
360                otaScreenInitialize();
361                mOtaWidgetData.otaTextListenProgressContainer.setVisibility(View.VISIBLE);
362                mOtaWidgetData.otaTextListenProgress.setText(R.string.ota_listen);
363                mOtaWidgetData.otaDtmfDialerView.setVisibility(View.VISIBLE);
364                mOtaWidgetData.callCardOtaButtonsListenProgress.setVisibility(View.VISIBLE);
365                mOtaWidgetData.otaSpeakerButton.setVisibility(View.VISIBLE);
366                boolean speakerOn = PhoneUtils.isSpeakerOn(mContext);
367                mOtaWidgetData.otaSpeakerButton.setChecked(speakerOn);
368            } else {
369                mDialer.setHandleVisible(true);
370            }
371            mApplication.cdmaOtaScreenState.otaScreenState =
372                    CdmaOtaScreenState.OtaScreenState.OTA_STATUS_LISTENING;
373
374            // Update the state of the in-call menu items.
375            mInCallScreen.updateMenuItems();
376        } else {
377            if (DBG) log("OtaShowListeningScreen(): show progress screen");
378            otaShowInProgressScreen();
379        }
380    }
381
382    /**
383     * Show "Programming In Progress" screen during OTA call. Shown when OTA
384     * provisioning is in progress after user has selected an option.
385     */
386    private void otaShowInProgressScreen() {
387        if (DBG) log("OtaShowInProgressScreen()...");
388        if (!isDialerOpened()) {
389            otaScreenInitialize();
390            mOtaWidgetData.otaTextListenProgressContainer.setVisibility(View.VISIBLE);
391            mOtaWidgetData.otaTextListenProgress.setText(R.string.ota_progress);
392            mOtaWidgetData.otaTextProgressBar.setVisibility(View.VISIBLE);
393            mOtaWidgetData.callCardOtaButtonsListenProgress.setVisibility(View.VISIBLE);
394            mOtaWidgetData.otaSpeakerButton.setVisibility(View.VISIBLE);
395            boolean speakerOn = PhoneUtils.isSpeakerOn(mContext);
396            mOtaWidgetData.otaSpeakerButton.setChecked(speakerOn);
397        } else {
398            mDialer.setHandleVisible(true);
399        }
400        mApplication.cdmaOtaScreenState.otaScreenState =
401            CdmaOtaScreenState.OtaScreenState.OTA_STATUS_PROGRESS;
402
403        // Update the state of the in-call menu items.
404        mInCallScreen.updateMenuItems();
405    }
406
407    /**
408     * Show programming failure dialog when OTA provisioning fails.
409     * If OTA provisioning attempts fail more than 3 times, then unsuccessful
410     * dialog is shown. Otherwise a two-second notice is shown with unsuccessful
411     * information. When notice expires, phone returns to activation screen.
412     */
413    private void otaShowProgramFailure(int length) {
414        if (DBG) log("OtaShowProgramFailure()...");
415        mApplication.cdmaOtaProvisionData.activationCount++;
416        if ((mApplication.cdmaOtaProvisionData.activationCount <
417                mApplication.cdmaOtaConfigData.otaShowActivateFailTimes)
418                && (mApplication.cdmaOtaConfigData.otaShowActivationScreen ==
419                OTA_SHOW_ACTIVATION_SCREEN_ON)) {
420            if (DBG) log("OtaShowProgramFailure(): activationCount"
421                    + mApplication.cdmaOtaProvisionData.activationCount);
422            if (DBG) log("OtaShowProgramFailure(): show failure notice");
423            otaShowProgramFailureNotice(length);
424        } else {
425            if (DBG) log("OtaShowProgramFailure(): show failure dialog");
426            otaShowProgramFailureDialog();
427        }
428    }
429
430    /**
431     * Show either programming success dialog when OTA provisioning succeeds, or
432     * programming failure dialog when it fails. See {@link otaShowProgramFailure}
433     * for more details.
434     */
435    public void otaShowSuccessFailure() {
436        if (DBG) log("OtaShowSuccessFailure()...");
437        otaScreenInitialize();
438        if (DBG) log("OtaShowSuccessFailure(): isOtaCallCommitted"
439                + mApplication.cdmaOtaProvisionData.isOtaCallCommitted);
440        if (mApplication.cdmaOtaProvisionData.isOtaCallCommitted) {
441            if (DBG) log("OtaShowSuccessFailure(), show success dialog");
442            otaShowProgramSuccessDialog();
443        } else {
444            if (DBG) log("OtaShowSuccessFailure(), show failure dialog");
445            otaShowProgramFailure(OTA_FAILURE_DIALOG_TIMEOUT);
446        }
447        return;
448    }
449
450    /**
451     * Show programming failure dialog when OTA provisioning fails more than 3
452     * times.
453     */
454    private void otaShowProgramFailureDialog() {
455        if (DBG) log("OtaShowProgramFailureDialog()...");
456        mApplication.cdmaOtaScreenState.otaScreenState =
457                CdmaOtaScreenState.OtaScreenState.OTA_STATUS_SUCCESS_FAILURE_DLG;
458        mOtaWidgetData.otaTitle.setText(R.string.ota_title_problem_with_activation);
459        mOtaWidgetData.otaTextSuccessFail.setVisibility(View.VISIBLE);
460        mOtaWidgetData.otaTextSuccessFail.setText(R.string.ota_unsuccessful);
461        mOtaWidgetData.callCardOtaButtonsFailSuccess.setVisibility(View.VISIBLE);
462        mOtaWidgetData.otaTryAgainButton.setVisibility(View.VISIBLE);
463        //close the dialer if open
464        if (isDialerOpened()) {
465            mDialer.closeDialer(false);
466        }
467    }
468
469    /**
470     * Show programming success dialog when OTA provisioning succeeds.
471     */
472    private void otaShowProgramSuccessDialog() {
473        if (DBG) log("OtaShowProgramSuccessDialog()...");
474        mApplication.cdmaOtaScreenState.otaScreenState =
475                CdmaOtaScreenState.OtaScreenState.OTA_STATUS_SUCCESS_FAILURE_DLG;
476        mOtaWidgetData.otaTitle.setText(R.string.ota_title_activate_success);
477        mOtaWidgetData.otaTextSuccessFail.setVisibility(View.VISIBLE);
478        mOtaWidgetData.otaTextSuccessFail.setText(R.string.ota_successful);
479        mOtaWidgetData.callCardOtaButtonsFailSuccess.setVisibility(View.VISIBLE);
480        mOtaWidgetData.otaNextButton.setVisibility(View.VISIBLE);
481        //close the dialer if open
482        if (isDialerOpened()) {
483            mDialer.closeDialer(false);
484        }
485    }
486
487    /**
488     * Show SPC failure notice when SPC attempts exceed 15 times.
489     * During OTA provisioning, if SPC code is incorrect OTA provisioning will
490     * fail. When SPC attempts are over 15, it shows SPC failure notice for one minute and
491     * then phone will power down.
492     */
493    private void otaShowSpcErrorNotice(int length) {
494        if (DBG) log("OtaShowSpcErrorNotice()...");
495        if (mOtaWidgetData.spcErrorDialog == null) {
496            mApplication.cdmaOtaProvisionData.inOtaSpcState = true;
497            DialogInterface.OnKeyListener keyListener;
498            keyListener = new DialogInterface.OnKeyListener() {
499                public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event) {
500                    log("Ignoring key events...");
501                    return true;
502                }};
503            mOtaWidgetData.spcErrorDialog = new AlertDialog.Builder(mInCallScreen)
504                    .setMessage(R.string.ota_spc_failure)
505                    .setOnKeyListener(keyListener)
506                    .create();
507            mOtaWidgetData.spcErrorDialog.getWindow().addFlags(
508                    WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
509                    | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
510            mOtaWidgetData.spcErrorDialog.show();
511            //close the dialer if open
512            if (isDialerOpened()) {
513                mDialer.closeDialer(false);
514            }
515            long noticeTime = length*1000;
516            if (DBG) log("OtaShowSpcErrorNotice(), remaining SPC noticeTime" + noticeTime);
517            mInCallScreen.requestCloseSpcErrorNotice(noticeTime);
518        }
519    }
520
521    /**
522     * When SPC notice times out, force phone to power down.
523     */
524    public void onOtaCloseSpcNotice() {
525        if (DBG) log("onOtaCloseSpcNotice(), send shutdown intent");
526        Intent shutdown = new Intent(Intent.ACTION_REQUEST_SHUTDOWN);
527        shutdown.putExtra(Intent.EXTRA_KEY_CONFIRM, false);
528        shutdown.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
529        mContext.startActivity(shutdown);
530    }
531
532    /**
533     * Show two-second notice when OTA provisioning fails and number of failed attempts
534     * is less then 3.
535     */
536    private void otaShowProgramFailureNotice(int length) {
537        if (DBG) log("OtaShowProgramFailureNotice()...");
538        if (mOtaWidgetData.otaFailureDialog == null) {
539            mOtaWidgetData.otaFailureDialog = new AlertDialog.Builder(mInCallScreen)
540                    .setMessage(R.string.ota_failure)
541                    .create();
542            mOtaWidgetData.otaFailureDialog.getWindow().addFlags(
543                    WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
544                    | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
545            mOtaWidgetData.otaFailureDialog.show();
546
547            long noticeTime = length*1000;
548            mInCallScreen.requestCloseOtaFailureNotice(noticeTime);
549        }
550    }
551
552    /**
553     * Handle OTA unsuccessful notice expiry. Dismisses the
554     * two-second notice and shows the activation screen.
555     */
556    public void onOtaCloseFailureNotice() {
557        if (DBG) log("onOtaCloseFailureNotice()...");
558        if (mOtaWidgetData.otaFailureDialog != null) {
559            mOtaWidgetData.otaFailureDialog.dismiss();
560            mOtaWidgetData.otaFailureDialog = null;
561        }
562        otaShowActivateScreen();
563    }
564
565    /**
566     * Initialize all OTA UI elements to be gone. Also set inCallPanel,
567     * callCard and the dialpad handle to be gone. This is called before any OTA screen
568     * gets drawn.
569     */
570    private void otaScreenInitialize() {
571        if (DBG) log("OtaScreenInitialize()...");
572
573        if (mInCallPanel != null) mInCallPanel.setVisibility(View.GONE);
574        if (mCallCard != null) mCallCard.hideCallCardElements();
575        mDialer.setHandleVisible(false);
576
577        mOtaWidgetData.otaTitle.setText(R.string.ota_title_activate);
578        mOtaWidgetData.otaTextActivate.setVisibility(View.GONE);
579        mOtaWidgetData.otaTextListenProgressContainer.setVisibility(View.GONE);
580        mOtaWidgetData.otaTextProgressBar.setVisibility(View.GONE);
581        mOtaWidgetData.otaTextSuccessFail.setVisibility(View.GONE);
582        mOtaWidgetData.callCardOtaButtonsActivate.setVisibility(View.GONE);
583        mOtaWidgetData.callCardOtaButtonsListenProgress.setVisibility(View.GONE);
584        mOtaWidgetData.callCardOtaButtonsFailSuccess.setVisibility(View.GONE);
585        mOtaWidgetData.otaDtmfDialerView.setVisibility(View.GONE);
586        mOtaWidgetData.otaSpeakerButton.setVisibility(View.GONE);
587        mOtaWidgetData.otaTryAgainButton.setVisibility(View.GONE);
588        mOtaWidgetData.otaNextButton.setVisibility(View.GONE);
589        mOtaWidgetData.otaCallCardBase.setVisibility(View.VISIBLE);
590        mOtaWidgetData.otaSkipButton.setVisibility(View.VISIBLE);
591    }
592
593    public void hideOtaScreen() {
594        if (DBG) log("hideOtaScreen()...");
595
596        mOtaWidgetData.callCardOtaButtonsActivate.setVisibility(View.GONE);
597        mOtaWidgetData.callCardOtaButtonsListenProgress.setVisibility(View.GONE);
598        mOtaWidgetData.callCardOtaButtonsFailSuccess.setVisibility(View.GONE);
599        mOtaWidgetData.otaCallCardBase.setVisibility(View.GONE);
600    }
601
602    public boolean isDialerOpened() {
603        return (mDialer != null && mDialer.isOpened());
604    }
605
606    /**
607     * Show the appropriate OTA screen based on the current state of OTA call.
608     * Shown whenever calling screen is resumed.
609     */
610    public void otaShowProperScreen() {
611        if (DBG) log("otaShowProperScreen()...");
612        if (mInCallScreen.isForegroundActivity()) {
613            if (DBG) log("otaShowProperScreen(), OTA is foreground activity, currentstate ="
614                    + mApplication.cdmaOtaScreenState.otaScreenState);
615            if (mInCallPanel != null) {
616                mInCallPanel.setVisibility(View.GONE);
617            }
618            if (mApplication.cdmaOtaScreenState.otaScreenState
619                    == CdmaOtaScreenState.OtaScreenState.OTA_STATUS_ACTIVATION) {
620                otaShowActivateScreen();
621            } else if (mApplication.cdmaOtaScreenState.otaScreenState
622                    == CdmaOtaScreenState.OtaScreenState.OTA_STATUS_LISTENING) {
623                otaShowListeningScreen();
624            } else if (mApplication.cdmaOtaScreenState.otaScreenState
625                    == CdmaOtaScreenState.OtaScreenState.OTA_STATUS_PROGRESS) {
626                otaShowInProgressScreen();
627            }
628
629            if (mApplication.cdmaOtaProvisionData.inOtaSpcState) {
630                otaShowSpcErrorNotice(getOtaSpcDisplayTime());
631            }
632        }
633    }
634
635    /**
636     * Read configuration values for each OTA screen from config.xml.
637     * These configuration values control visibility of each screen.
638     */
639    private void readXmlSettings() {
640        if (DBG) log("readXmlSettings()...");
641        if (mApplication.cdmaOtaConfigData.configComplete) {
642            return;
643        }
644
645        mApplication.cdmaOtaConfigData.configComplete = true;
646        int tmpOtaShowActivationScreen =
647                mContext.getResources().getInteger(R.integer.OtaShowActivationScreen);
648        mApplication.cdmaOtaConfigData.otaShowActivationScreen = tmpOtaShowActivationScreen;
649        if (DBG) log("readXmlSettings(), otaShowActivationScreen"
650                + mApplication.cdmaOtaConfigData.otaShowActivationScreen);
651
652        int tmpOtaShowListeningScreen =
653                mContext.getResources().getInteger(R.integer.OtaShowListeningScreen);
654        mApplication.cdmaOtaConfigData.otaShowListeningScreen = tmpOtaShowListeningScreen;
655        if (DBG) log("readXmlSettings(), otaShowListeningScreen"
656                + mApplication.cdmaOtaConfigData.otaShowListeningScreen);
657
658        int tmpOtaShowActivateFailTimes =
659                mContext.getResources().getInteger(R.integer.OtaShowActivateFailTimes);
660        mApplication.cdmaOtaConfigData.otaShowActivateFailTimes = tmpOtaShowActivateFailTimes;
661        if (DBG) log("readXmlSettings(), otaShowActivateFailTimes"
662                + mApplication.cdmaOtaConfigData.otaShowActivateFailTimes);
663
664        int tmpOtaPlaySuccessFailureTone =
665                mContext.getResources().getInteger(R.integer.OtaPlaySuccessFailureTone);
666        mApplication.cdmaOtaConfigData.otaPlaySuccessFailureTone = tmpOtaPlaySuccessFailureTone;
667        if (DBG) log("readXmlSettings(), otaPlaySuccessFailureTone"
668                + mApplication.cdmaOtaConfigData.otaPlaySuccessFailureTone);
669    }
670
671    /**
672     * Handle the click events for OTA buttons.
673     */
674    public void onClickHandler(int id) {
675        switch (id) {
676            case R.id.otaEndButton:
677                onClickOtaEndButton();
678                break;
679
680            case R.id.otaSpeakerButton:
681                onClickOtaSpeakerButton();
682                break;
683
684            case R.id.otaActivateButton:
685                onClickOtaActivateButton();
686                break;
687
688            case R.id.otaSkipButton:
689                onClickOtaActivateSkipButton();
690                break;
691
692            case R.id.otaNextButton:
693                onClickOtaActivateNextButton();
694                break;
695
696            case R.id.otaTryAgainButton:
697                onClickOtaTryAgainButton();
698                break;
699
700            default:
701                if (DBG) log ("onClickHandler: received a click event for unrecognized id");
702                break;
703        }
704    }
705
706    private void onClickOtaTryAgainButton() {
707        if (DBG) log("Activation Try Again Clicked!");
708        if (!mApplication.cdmaOtaProvisionData.inOtaSpcState) {
709            otaShowActivateScreen();
710        }
711    }
712
713    private void onClickOtaEndButton() {
714        if (DBG) log("Activation End Call Button Clicked!");
715        if (!mApplication.cdmaOtaProvisionData.inOtaSpcState) {
716            if (PhoneUtils.hangup(mApplication.mCM) == false) {
717                // If something went wrong when placing the OTA call,
718                // the screen is not updated by the call disconnect
719                // handler and we have to do it here
720                setSpeaker(false);
721                mInCallScreen.handleOtaCallEnd();
722            }
723        }
724    }
725
726    private void onClickOtaSpeakerButton() {
727        if (DBG) log("OTA Speaker button Clicked!");
728        if (!mApplication.cdmaOtaProvisionData.inOtaSpcState) {
729            boolean isChecked = !PhoneUtils.isSpeakerOn(mContext);
730            setSpeaker(isChecked);
731        }
732    }
733
734    private void onClickOtaActivateButton() {
735        if (DBG) log("Call Activation Clicked!");
736        otaPerformActivation();
737    }
738
739    private void onClickOtaActivateSkipButton() {
740        if (DBG) log("Activation Skip Clicked!");
741        DialogInterface.OnKeyListener keyListener;
742        keyListener = new DialogInterface.OnKeyListener() {
743            public boolean onKey(DialogInterface dialog, int keyCode,
744                    KeyEvent event) {
745                if (DBG) log("Ignoring key events...");
746                return true;
747            }
748        };
749        mOtaWidgetData.otaSkipConfirmationDialog = new AlertDialog.Builder(mInCallScreen)
750                .setTitle(R.string.ota_skip_activation_dialog_title)
751                .setMessage(R.string.ota_skip_activation_dialog_message)
752                .setPositiveButton(
753                    R.string.ota_skip_activation_dialog_skip_label,
754                    new AlertDialog.OnClickListener() {
755                        public void onClick(DialogInterface dialog, int which) {
756                            otaSkipActivation();
757                        }
758                    })
759                .setNegativeButton(
760                    R.string.ota_skip_activation_dialog_continue_label,
761                    new AlertDialog.OnClickListener() {
762                        public void onClick(DialogInterface dialog, int which) {
763                            otaPerformActivation();
764                        }
765                    })
766                .setOnKeyListener(keyListener)
767                .create();
768        mOtaWidgetData.otaSkipConfirmationDialog.show();
769    }
770
771    private void onClickOtaActivateNextButton() {
772        if (DBG) log("Dialog Next Clicked!");
773        if (!mApplication.cdmaOtaProvisionData.inOtaSpcState) {
774            mApplication.cdmaOtaScreenState.otaScreenState =
775                    CdmaOtaScreenState.OtaScreenState.OTA_STATUS_UNDEFINED;
776            otaShowHome();
777        }
778    }
779
780    public void dismissAllOtaDialogs() {
781        if (mOtaWidgetData.spcErrorDialog != null) {
782            if (DBG) log("- DISMISSING mSpcErrorDialog.");
783            mOtaWidgetData.spcErrorDialog.dismiss();
784            mOtaWidgetData.spcErrorDialog = null;
785        }
786        if (mOtaWidgetData.otaFailureDialog != null) {
787            if (DBG) log("- DISMISSING mOtaFailureDialog.");
788            mOtaWidgetData.otaFailureDialog.dismiss();
789            mOtaWidgetData.otaFailureDialog = null;
790        }
791    }
792
793    private int getOtaSpcDisplayTime() {
794        if (DBG) log("getOtaSpcDisplayTime()...");
795        int tmpSpcTime = 1;
796        if (mApplication.cdmaOtaProvisionData.inOtaSpcState) {
797            long tmpOtaSpcRunningTime = 0;
798            long tmpOtaSpcLeftTime = 0;
799            tmpOtaSpcRunningTime = SystemClock.elapsedRealtime();
800            tmpOtaSpcLeftTime =
801                tmpOtaSpcRunningTime - mApplication.cdmaOtaProvisionData.otaSpcUptime;
802            if (tmpOtaSpcLeftTime >= OTA_SPC_TIMEOUT*1000) {
803                tmpSpcTime = 1;
804            } else {
805                tmpSpcTime = OTA_SPC_TIMEOUT - (int)tmpOtaSpcLeftTime/1000;
806            }
807        }
808        if (DBG) log("getOtaSpcDisplayTime(), time for SPC error notice: " + tmpSpcTime);
809        return tmpSpcTime;
810    }
811
812    /**
813     * Initialize the OTA widgets for all OTA screens.
814     */
815    private void initOtaInCallScreen() {
816        if (DBG) log("initOtaInCallScreen()...");
817        mOtaWidgetData.otaTitle = (TextView) mInCallScreen.findViewById(R.id.otaTitle);
818        mOtaWidgetData.otaTextActivate = (TextView) mInCallScreen.findViewById(R.id.otaActivate);
819        mOtaWidgetData.otaTextActivate.setVisibility(View.GONE);
820        mOtaWidgetData.otaTextListenProgressContainer =
821                (ScrollView) mInCallScreen.findViewById(R.id.otaListenProgressContainer);
822        mOtaWidgetData.otaTextListenProgress =
823                (TextView) mInCallScreen.findViewById(R.id.otaListenProgress);
824        mOtaWidgetData.otaTextProgressBar =
825                (ProgressBar) mInCallScreen.findViewById(R.id.progress_large);
826        mOtaWidgetData.otaTextProgressBar.setIndeterminate(true);
827        mOtaWidgetData.otaTextSuccessFail =
828                (TextView) mInCallScreen.findViewById(R.id.otaSuccessFailStatus);
829
830        mOtaWidgetData.otaCallCardBase = (View) mInCallScreen.findViewById(R.id.otaBase);
831        mOtaWidgetData.callCardOtaButtonsListenProgress =
832                (View) mInCallScreen.findViewById(R.id.callCardOtaListenProgress);
833        mOtaWidgetData.callCardOtaButtonsActivate =
834                (View) mInCallScreen.findViewById(R.id.callCardOtaActivate);
835        mOtaWidgetData.callCardOtaButtonsFailSuccess =
836                (View) mInCallScreen.findViewById(R.id.callCardOtaFailOrSuccessful);
837
838        mOtaWidgetData.otaEndButton = (Button) mInCallScreen.findViewById(R.id.otaEndButton);
839        mOtaWidgetData.otaEndButton.setOnClickListener(mInCallScreen);
840        mOtaWidgetData.otaSpeakerButton =
841                (ToggleButton) mInCallScreen.findViewById(R.id.otaSpeakerButton);
842        mOtaWidgetData.otaSpeakerButton.setOnClickListener(mInCallScreen);
843        mOtaWidgetData.otaActivateButton =
844                (Button) mInCallScreen.findViewById(R.id.otaActivateButton);
845        mOtaWidgetData.otaActivateButton.setOnClickListener(mInCallScreen);
846        mOtaWidgetData.otaSkipButton = (Button) mInCallScreen.findViewById(R.id.otaSkipButton);
847        mOtaWidgetData.otaSkipButton.setOnClickListener(mInCallScreen);
848        mOtaWidgetData.otaNextButton = (Button) mInCallScreen.findViewById(R.id.otaNextButton);
849        mOtaWidgetData.otaNextButton.setOnClickListener(mInCallScreen);
850        mOtaWidgetData.otaTryAgainButton =
851                (Button) mInCallScreen.findViewById(R.id.otaTryAgainButton);
852        mOtaWidgetData.otaTryAgainButton.setOnClickListener(mInCallScreen);
853
854        mOtaWidgetData.otaDtmfDialerView =
855                (DTMFTwelveKeyDialerView) mInCallScreen.findViewById(R.id.otaDtmfDialer);
856        // Sanity-check: the otaDtmfDialer widget should *always* be present.
857        if (mOtaWidgetData.otaDtmfDialerView == null) {
858            Log.e(LOG_TAG, "onCreate: couldn't find otaDtmfDialer", new IllegalStateException());
859        }
860
861
862        // Create a new DTMFTwelveKeyDialer instance purely for use by the
863        // DTMFTwelveKeyDialerView ("otaDtmfDialerView") that comes from
864        // otacall_card.xml.
865        // (But note that mDialer is a separate DTMFTwelveKeyDialer
866        // instance, that belongs to the InCallScreen.  This is confusing;
867        // see the TODO comment above.)
868        mOtaCallCardDtmfDialer = new DTMFTwelveKeyDialer(mInCallScreen,
869                                                         mOtaWidgetData.otaDtmfDialerView,
870                                                         null /* no SlidingDrawer used here */);
871
872        // Initialize the new DTMFTwelveKeyDialer instance.  This is
873        // needed to play local DTMF tones.
874        mOtaCallCardDtmfDialer.startDialerSession();
875
876        mOtaWidgetData.otaDtmfDialerView.setDialer(mOtaCallCardDtmfDialer);
877    }
878
879    /**
880     * Clear out all OTA UI widget elements. Needs to get called
881     * when OTA call ends or InCallScreen is destroyed.
882     * @param disableSpeaker parameter control whether Speaker should be turned off.
883     */
884    public void cleanOtaScreen(boolean disableSpeaker) {
885        if (DBG) log("OTA ends, cleanOtaScreen!");
886
887        mApplication.cdmaOtaScreenState.otaScreenState =
888                CdmaOtaScreenState.OtaScreenState.OTA_STATUS_UNDEFINED;
889        mApplication.cdmaOtaProvisionData.isOtaCallCommitted = false;
890        mApplication.cdmaOtaProvisionData.isOtaCallIntentProcessed = false;
891        mApplication.cdmaOtaProvisionData.inOtaSpcState = false;
892        mApplication.cdmaOtaProvisionData.activationCount = 0;
893        mApplication.cdmaOtaProvisionData.otaSpcUptime = 0;
894        mApplication.cdmaOtaInCallScreenUiState.state = State.UNDEFINED;
895
896        if (mInCallPanel != null) mInCallPanel.setVisibility(View.VISIBLE);
897        if (mCallCard != null) mCallCard.hideCallCardElements();
898        mDialer.setHandleVisible(true);
899
900        // Free resources from the DTMFTwelveKeyDialer instance we created
901        // in initOtaInCallScreen().
902        if (mOtaCallCardDtmfDialer != null) {
903            mOtaCallCardDtmfDialer.stopDialerSession();
904        }
905
906        mOtaWidgetData.otaTextActivate.setVisibility(View.GONE);
907        mOtaWidgetData.otaTextListenProgressContainer.setVisibility(View.GONE);
908        mOtaWidgetData.otaTextProgressBar.setVisibility(View.GONE);
909        mOtaWidgetData.otaTextSuccessFail.setVisibility(View.GONE);
910        mOtaWidgetData.callCardOtaButtonsActivate.setVisibility(View.GONE);
911        mOtaWidgetData.callCardOtaButtonsListenProgress.setVisibility(View.GONE);
912        mOtaWidgetData.callCardOtaButtonsFailSuccess.setVisibility(View.GONE);
913        mOtaWidgetData.otaCallCardBase.setVisibility(View.GONE);
914        mOtaWidgetData.otaDtmfDialerView.setVisibility(View.GONE);
915        mOtaWidgetData.otaNextButton.setVisibility(View.GONE);
916        mOtaWidgetData.otaTryAgainButton.setVisibility(View.GONE);
917
918        // turn off the speaker in case it was turned on
919        // but the OTA call could not be completed
920        if (disableSpeaker) {
921            setSpeaker(false);
922        }
923    }
924
925    /**
926     * Defines OTA information that needs to be maintained during
927     * an OTA call when display orientation changes.
928     */
929    public static class CdmaOtaProvisionData {
930        public boolean isOtaCallCommitted;
931        public boolean isOtaCallIntentProcessed;
932        public boolean inOtaSpcState;
933        public int activationCount;
934        public long otaSpcUptime;
935    }
936
937    /**
938     * Defines OTA screen configuration items read from config.xml
939     * and used to control OTA display.
940     */
941    public static class CdmaOtaConfigData {
942        public int otaShowActivationScreen;
943        public int otaShowListeningScreen;
944        public int otaShowActivateFailTimes;
945        public int otaPlaySuccessFailureTone;
946        public boolean configComplete;
947        public CdmaOtaConfigData() {
948            if (DBG) log("CdmaOtaConfigData constructor!");
949            otaShowActivationScreen = OTA_SHOW_ACTIVATION_SCREEN_OFF;
950            otaShowListeningScreen = OTA_SHOW_LISTENING_SCREEN_OFF;
951            otaShowActivateFailTimes = OTA_SHOW_ACTIVATE_FAIL_COUNT_OFF;
952            otaPlaySuccessFailureTone = OTA_PLAY_SUCCESS_FAILURE_TONE_OFF;
953        }
954    }
955
956    /**
957     * The state of the OTA InCallScreen UI.
958     */
959    public static class CdmaOtaInCallScreenUiState {
960        public enum State {
961            UNDEFINED,
962            NORMAL,
963            ENDED
964        }
965
966        public State state;
967
968        public CdmaOtaInCallScreenUiState() {
969            if (DBG) log("CdmaOtaInCallScreenState: constructor init to UNDEFINED");
970            state = CdmaOtaInCallScreenUiState.State.UNDEFINED;
971        }
972
973        // the pending intent used to report when the user skips ota provisioning
974        public PendingIntent reportSkipPendingIntent;
975    }
976
977    /**
978     * Save the Ota InCallScreen UI state
979     */
980    public void setCdmaOtaInCallScreenUiState(CdmaOtaInCallScreenUiState.State state) {
981        if (DBG) log("setCdmaOtaInCallScreenState: " + state);
982        mApplication.cdmaOtaInCallScreenUiState.state = state;
983    }
984
985    /**
986     * Get the Ota InCallScreen UI state
987     */
988    public CdmaOtaInCallScreenUiState.State getCdmaOtaInCallScreenUiState() {
989        if (DBG) log("getCdmaOtaInCallScreenState: " + mApplication.cdmaOtaInCallScreenUiState.state);
990        return mApplication.cdmaOtaInCallScreenUiState.state;
991    }
992
993    /**
994     * The OTA screen state machine.
995     */
996    public static class CdmaOtaScreenState {
997        public enum OtaScreenState {
998            OTA_STATUS_UNDEFINED,
999            OTA_STATUS_ACTIVATION,
1000            OTA_STATUS_LISTENING,
1001            OTA_STATUS_PROGRESS,
1002            OTA_STATUS_SUCCESS_FAILURE_DLG
1003        }
1004
1005        public OtaScreenState otaScreenState;
1006
1007        public CdmaOtaScreenState() {
1008            otaScreenState = OtaScreenState.OTA_STATUS_UNDEFINED;
1009        }
1010    }
1011
1012    private static void log(String msg) {
1013        Log.d(LOG_TAG, msg);
1014    }
1015
1016    public static boolean isCdmaPhone() {
1017        return (PhoneApp.getInstance().phone.getPhoneType() == Phone.PHONE_TYPE_CDMA);
1018    }
1019}
1020