• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
17 package com.android.phone;
18 
19 import com.android.internal.telephony.Phone;
20 import com.android.phone.OtaUtils.CdmaOtaInCallScreenUiState.State;
21 
22 import android.app.AlertDialog;
23 import android.app.PendingIntent;
24 import android.app.PendingIntent.CanceledException;
25 import android.content.ActivityNotFoundException;
26 import android.content.Context;
27 import android.content.DialogInterface;
28 import android.content.Intent;
29 import android.content.pm.ResolveInfo;
30 import android.os.AsyncResult;
31 import android.os.Handler;
32 import android.os.Message;
33 import android.os.SystemClock;
34 import android.os.SystemProperties;
35 import android.provider.Settings;
36 
37 import android.util.Log;
38 import android.view.KeyEvent;
39 import android.view.View;
40 import android.view.ViewGroup;
41 import android.view.ViewStub;
42 import android.view.WindowManager;
43 
44 import android.widget.Button;
45 import android.widget.ScrollView;
46 import android.widget.ToggleButton;
47 import android.widget.ProgressBar;
48 import 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  */
59 public 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 
OtaUtils(Context context, InCallScreen inCallScreen, ViewGroup inCallPanel, CallCard callCard, DTMFTwelveKeyDialer dialer)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      */
needsActivation(String minString)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      */
maybeDoOtaCall(Context context, Handler handler, int request)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 
setSpeaker(boolean state)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      */
onOtaProvisionStatusChanged(AsyncResult r)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 
otaShowHome()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 
otaSkipActivation()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 
otaPerformActivation()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      */
otaShowActivateScreen()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      */
otaShowListeningScreen()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      */
otaShowInProgressScreen()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      */
otaShowProgramFailure(int length)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      */
otaShowSuccessFailure()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      */
otaShowProgramFailureDialog()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      */
otaShowProgramSuccessDialog()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      */
otaShowSpcErrorNotice(int length)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      */
onOtaCloseSpcNotice()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      */
otaShowProgramFailureNotice(int length)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      */
onOtaCloseFailureNotice()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      */
otaScreenInitialize()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 
hideOtaScreen()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 
isDialerOpened()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      */
otaShowProperScreen()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      */
readXmlSettings()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      */
onClickHandler(int id)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 
onClickOtaTryAgainButton()706     private void onClickOtaTryAgainButton() {
707         if (DBG) log("Activation Try Again Clicked!");
708         if (!mApplication.cdmaOtaProvisionData.inOtaSpcState) {
709             otaShowActivateScreen();
710         }
711     }
712 
onClickOtaEndButton()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 
onClickOtaSpeakerButton()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 
onClickOtaActivateButton()734     private void onClickOtaActivateButton() {
735         if (DBG) log("Call Activation Clicked!");
736         otaPerformActivation();
737     }
738 
onClickOtaActivateSkipButton()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 
onClickOtaActivateNextButton()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 
dismissAllOtaDialogs()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 
getOtaSpcDisplayTime()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      */
initOtaInCallScreen()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      */
cleanOtaScreen(boolean disableSpeaker)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;
CdmaOtaConfigData()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 
CdmaOtaInCallScreenUiState()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      */
setCdmaOtaInCallScreenUiState(CdmaOtaInCallScreenUiState.State state)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      */
getCdmaOtaInCallScreenUiState()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 
CdmaOtaScreenState()1007         public CdmaOtaScreenState() {
1008             otaScreenState = OtaScreenState.OTA_STATUS_UNDEFINED;
1009         }
1010     }
1011 
log(String msg)1012     private static void log(String msg) {
1013         Log.d(LOG_TAG, msg);
1014     }
1015 
isCdmaPhone()1016     public static boolean isCdmaPhone() {
1017         return (PhoneApp.getInstance().phone.getPhoneType() == Phone.PHONE_TYPE_CDMA);
1018     }
1019 }
1020