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