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 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