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 android.app.Activity;
20 import android.app.PendingIntent;
21 import android.content.Context;
22 import android.content.Intent;
23 import android.content.pm.PackageManager;
24 import android.content.pm.ResolveInfo;
25 import android.os.Bundle;
26 import android.os.PersistableBundle;
27 import android.os.SystemProperties;
28 import android.provider.Settings;
29 import android.telephony.CarrierConfigManager;
30 import android.util.Log;
31 
32 import com.android.internal.telephony.Phone;
33 import 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  */
48 public 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
onCreate(Bundle icicle)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      */
isWizardRunning(Context context)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      */
startHfa()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