1/* 2 * Copyright (C) 2009 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.phone; 18 19import android.app.Activity; 20import android.app.PendingIntent; 21import android.content.Context; 22import android.content.Intent; 23import android.content.pm.PackageManager; 24import android.content.pm.ResolveInfo; 25import android.os.Bundle; 26import android.os.PersistableBundle; 27import android.os.SystemProperties; 28import android.provider.Settings; 29import android.telephony.CarrierConfigManager; 30import android.util.Log; 31 32import com.android.internal.telephony.Phone; 33import com.android.internal.telephony.TelephonyCapabilities; 34 35/** 36 * Invisible activity that handles the com.android.phone.PERFORM_CDMA_PROVISIONING intent. 37 * This activity is protected by the android.permission.PERFORM_CDMA_PROVISIONING permission. 38 * 39 * We handle the PERFORM_CDMA_PROVISIONING action by launching an OTASP 40 * call via one of the OtaUtils helper methods: startInteractiveOtasp() on 41 * regular phones, or startNonInteractiveOtasp() on data-only devices. 42 * 43 * TODO: The class name InCallScreenShowActivation is misleading, since 44 * this activity is totally unrelated to the InCallScreen (which 45 * implements the in-call UI.) Let's eventually rename this to something 46 * like CdmaProvisioningLauncher or CdmaProvisioningHandler... 47 */ 48public class InCallScreenShowActivation extends Activity { 49 private static final String LOG_TAG = "InCallScreenShowActivation"; 50 private static final boolean DBG = 51 (PhoneGlobals.DBG_LEVEL >= 1) && (SystemProperties.getInt("ro.debuggable", 0) == 1); 52 53 @Override 54 protected void onCreate(Bundle icicle) { 55 super.onCreate(icicle); 56 57 Intent intent = getIntent(); 58 if (DBG) Log.d(LOG_TAG, "onCreate: intent = " + intent); 59 Bundle extras = intent.getExtras(); 60 if (DBG && (extras != null)) { 61 Log.d(LOG_TAG, " - has extras: size = " + extras.size()); // forces an unparcel() 62 Log.d(LOG_TAG, " - extras = " + extras); 63 } 64 65 PhoneGlobals app = PhoneGlobals.getInstanceIfPrimary(); 66 if (app == null) { 67 // TODO: All CDMA provisioning code should move into a BroadcastReceiver that runs 68 // exclusively in the primary user's context. This is because the majority of the 69 // telephony logic -- and all of the important bits -- runs only as primary so we don't 70 // have access to the things we need. We still need to maintain an Activity to support 71 // legacy code which starts this using startActivity() but that Activity should be a 72 // simple intent-trampoline for the new BroadcastReceiver. 73 // 74 // Though this conditional protects this code from NPEs on a secondary user due to an 75 // uninitialized PhoneGlobals, there's not a good reason at the time of this writing as 76 // to why a secondary user context shouldn't trigger a CDMA provisioning, or at least 77 // nobody has expressed concern. 78 Log.i(LOG_TAG, "Being asked to provision CDMA SIM from secondary user, skipping."); 79 setResult(RESULT_CANCELED); 80 finish(); 81 return; 82 } 83 84 Phone phone = app.getPhone(); 85 if (!TelephonyCapabilities.supportsOtasp(phone)) { 86 Log.w(LOG_TAG, "CDMA Provisioning not supported on this device"); 87 setResult(RESULT_CANCELED); 88 finish(); 89 return; 90 } 91 92 if (intent.getAction().equals(OtaUtils.ACTION_PERFORM_CDMA_PROVISIONING)) { 93 94 PersistableBundle carrierConfig = app.getCarrierConfig(); 95 boolean usesHfa = carrierConfig.getBoolean( 96 CarrierConfigManager.KEY_USE_HFA_FOR_PROVISIONING_BOOL); 97 if (usesHfa) { 98 Log.i(LOG_TAG, "Starting Hfa from ACTION_PERFORM_CDMA_PROVISIONING"); 99 startHfa(); 100 finish(); 101 return; 102 } 103 104 boolean usesOtasp = carrierConfig.getBoolean( 105 CarrierConfigManager.KEY_USE_OTASP_FOR_PROVISIONING_BOOL); 106 if (usesOtasp) { 107 // On voice-capable devices, we perform CDMA provisioning in 108 // "interactive" mode by directly launching the InCallScreen. 109 // boolean interactiveMode = PhoneGlobals.sVoiceCapable; 110 // TODO: Renable interactive mode for device provisioning. 111 boolean interactiveMode = false; 112 Log.i(LOG_TAG, "ACTION_PERFORM_CDMA_PROVISIONING (interactiveMode = " 113 + interactiveMode + ")..."); 114 115 // Testing: this intent extra allows test apps manually 116 // enable/disable "interactive mode", regardless of whether 117 // the current device is voice-capable. This is allowed only 118 // in userdebug or eng builds. 119 if (intent.hasExtra(OtaUtils.EXTRA_OVERRIDE_INTERACTIVE_MODE) 120 && (SystemProperties.getInt("ro.debuggable", 0) == 1)) { 121 interactiveMode = 122 intent.getBooleanExtra(OtaUtils.EXTRA_OVERRIDE_INTERACTIVE_MODE, false); 123 Log.d(LOG_TAG, "==> MANUALLY OVERRIDING interactiveMode to " + interactiveMode); 124 } 125 126 // We allow the caller to pass a PendingIntent (as the 127 // EXTRA_NONINTERACTIVE_OTASP_RESULT_PENDING_INTENT extra) 128 // which we'll later use to notify them when the OTASP call 129 // fails or succeeds. 130 // 131 // Stash that away here, and we'll fire it off later in 132 // OtaUtils.sendOtaspResult(). 133 app.cdmaOtaScreenState.otaspResultCodePendingIntent = 134 (PendingIntent) intent.getParcelableExtra( 135 OtaUtils.EXTRA_OTASP_RESULT_CODE_PENDING_INTENT); 136 137 if (interactiveMode) { 138 // On voice-capable devices, launch an OTASP call and arrange 139 // for the in-call UI to come up. (The InCallScreen will 140 // notice that an OTASP call is active, and display the 141 // special OTASP UI instead of the usual in-call controls.) 142 143 if (DBG) Log.d(LOG_TAG, "==> Starting interactive CDMA provisioning..."); 144 OtaUtils.startInteractiveOtasp(this); 145 146 // The result we set here is actually irrelevant, since the 147 // InCallScreen's "interactive" OTASP sequence never actually 148 // finish()es; it ends by directly launching the Home 149 // activity. So our caller won't actually ever get an 150 // onActivityResult() call in this case. 151 setResult(OtaUtils.RESULT_INTERACTIVE_OTASP_STARTED); 152 } else { 153 // On data-only devices, manually launch the OTASP call 154 // *without* displaying any UI. (Our caller, presumably 155 // SetupWizardActivity, is responsible for displaying some 156 // sort of progress UI.) 157 158 if (DBG) Log.d(LOG_TAG, "==> Starting non-interactive CDMA provisioning..."); 159 int callStatus = OtaUtils.startNonInteractiveOtasp(this); 160 161 if (callStatus == PhoneUtils.CALL_STATUS_DIALED) { 162 if (DBG) Log.d(LOG_TAG, 163 " ==> successful result from startNonInteractiveOtasp(): " + 164 callStatus); 165 setResult(OtaUtils.RESULT_NONINTERACTIVE_OTASP_STARTED); 166 } else { 167 Log.w(LOG_TAG, "Failure code from startNonInteractiveOtasp(): " + 168 callStatus); 169 setResult(OtaUtils.RESULT_NONINTERACTIVE_OTASP_FAILED); 170 } 171 } 172 } else { 173 Log.i(LOG_TAG, "Skipping activation."); 174 } 175 } else { 176 Log.e(LOG_TAG, "Unexpected intent action: " + intent); 177 setResult(RESULT_CANCELED); 178 } 179 180 finish(); 181 } 182 183 /** 184 * On devices that provide a phone initialization wizard (such as Google Setup Wizard), 185 * the wizard displays it's own activation UI. The Hfa activation started by this class 186 * will show a UI or not depending on the status of the setup wizard. If the setup wizard 187 * is running, do not show a UI, otherwise show our own UI since setup wizard will not. 188 * 189 * The method checks two properties: 190 * 1. Does the device require a setup wizard (ro.setupwizard.mode == (REQUIRED|OPTIONAL)) 191 * 2. Is device_provisioned set to non-zero--a property that setup wizard sets at completion. 192 * @return true if wizard is running, false otherwise. 193 */ 194 private boolean isWizardRunning(Context context) { 195 Intent intent = new Intent("android.intent.action.DEVICE_INITIALIZATION_WIZARD"); 196 ResolveInfo resolveInfo = context.getPackageManager().resolveActivity(intent, 197 PackageManager.MATCH_DEFAULT_ONLY); 198 boolean provisioned = Settings.Global.getInt(context.getContentResolver(), 199 Settings.Global.DEVICE_PROVISIONED, 0) != 0; 200 String mode = SystemProperties.get("ro.setupwizard.mode", "REQUIRED"); 201 boolean runningSetupWizard = "REQUIRED".equals(mode) || "OPTIONAL".equals(mode); 202 if (DBG) { 203 Log.v(LOG_TAG, "resolvInfo = " + resolveInfo + ", provisioned = " + provisioned 204 + ", runningSetupWizard = " + runningSetupWizard); 205 } 206 return resolveInfo != null && !provisioned && runningSetupWizard; 207 } 208 209 /** 210 * Starts the HFA provisioning process by bringing up the HFA Activity. 211 */ 212 private void startHfa() { 213 boolean isWizardRunning = isWizardRunning(this); 214 // We always run our HFA logic if we're in setup wizard, but if we're outside of setup 215 // wizard then we have to check a config to see if we should still run HFA. 216 if (isWizardRunning || 217 getResources().getBoolean(R.bool.config_allow_hfa_outside_of_setup_wizard)) { 218 219 final Intent intent = new Intent(); 220 221 final PendingIntent otaResponseIntent = getIntent().getParcelableExtra( 222 OtaUtils.EXTRA_OTASP_RESULT_CODE_PENDING_INTENT); 223 224 final boolean showUi = !isWizardRunning; 225 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 226 227 if (otaResponseIntent != null) { 228 intent.putExtra(OtaUtils.EXTRA_OTASP_RESULT_CODE_PENDING_INTENT, otaResponseIntent); 229 } 230 231 Log.v(LOG_TAG, "Starting hfa activation activity"); 232 if (showUi) { 233 intent.setClassName(this, HfaActivity.class.getName()); 234 startActivity(intent); 235 } else { 236 intent.setClassName(this, HfaService.class.getName()); 237 startService(intent); 238 } 239 240 } 241 setResult(RESULT_OK); 242 } 243} 244