blob: 3bc047d02e5fdf27a06927ff219ab4bf62545e2c [file] [log] [blame]
Santos Cordon83570472013-09-06 15:45:10 -07001/*
2 * Copyright (C) 2013 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.AlertDialog;
21import android.app.PendingIntent;
22import android.app.PendingIntent.CanceledException;
23import android.content.BroadcastReceiver;
24import android.content.Context;
25import android.content.DialogInterface;
26import android.content.Intent;
27import android.content.IntentFilter;
28import android.os.AsyncResult;
29import android.os.Bundle;
30import android.os.Handler;
31import android.os.Message;
32import android.telephony.ServiceState;
33import android.util.Log;
34
35import com.android.internal.telephony.CallManager;
36import com.android.internal.telephony.Phone;
37import com.google.common.base.Objects;
38import com.google.common.base.Objects.ToStringHelper;
39
40/**
41 * Starts and displays status for Hands Free Activation (HFA).
42 *
43 * This class operates with Hands Free Activation apps.
44 * It starts by broadcasting the intent com.android.action.START_HFA.
45 * An HFA app will pick that up and start the HFA process.
46 * If it fails it return ERROR_HFA Intent and upon success returns COMPLETE_HFA.
47 *
48 * If successful, we bounce the radio so that the service picks up the new number. This is also
49 * necessary for the setup wizard to pick up the successful activation so that it can continue
50 * past the welcome screen. Once the radio is back on we send back the pendingIntent to setup
51 * wizard and destroy the activity.
52 *
53 * If there is an error, we do not bounce the radio but still send the pending intent back to
54 * the wizard (with a failure code).
55 *
56 * The user has an option to skip activation. If that happens, we go back to the setup
57 * wizard.
58 *
59 * TODO(klp): We need system-only permissions for the HFA intents.
60 * TODO(klp): Should be full screen activity instead of dialogs.
61 * TODO(klp): Currently display the error code instead of the error string resource.
62 * TODO(klp): Need to check the system to ensure someone is listening for the intent
63 * before we send it. Should there be a timeout? 5 minutes?
64 */
65public class HfaActivity extends Activity {
66 private static final String TAG = HfaActivity.class.getSimpleName();
67
68 private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
69
70 private static final String ACTION_START = "com.android.action.START_HFA";
71 private static final String ACTION_ERROR = "com.android.action.ERROR_HFA";
72 private static final String ACTION_CANCEL = "com.android.action.CANCEL_HFA";
73 private static final String ACTION_COMPLETE = "com.android.action.COMPLETE_HFA";
74
75 private static final int SERVICE_STATE_CHANGED = 1;
76
77 public static final int OTASP_UNKNOWN = 0;
78 public static final int OTASP_USER_SKIPPED = 1;
79 public static final int OTASP_SUCCESS = 2;
80 public static final int OTASP_FAILURE = 3;
81
82 public static final int NOT_WAITING = 0;
83 public static final int WAITING_FOR_RADIO_OFF = 1;
84 public static final int WAITING_FOR_RADIO_ON = 2;
85
86 private int mPhoneMonitorState = NOT_WAITING;
87 private boolean mCanSkip;
88 private AlertDialog mDialog;
89 private BroadcastReceiver mReceiver;
90
91 @Override
92 protected void onCreate(Bundle savedInstanceState) {
93 super.onCreate(savedInstanceState);
94
95 if (VERBOSE) Log.v(TAG, "onCreate");
96
97 startHfaIntentReceiver();
98 startProvisioning();
99 }
100
101 @Override
102 protected void onDestroy() {
103 super.onDestroy();
104
105 if (VERBOSE) Log.v(TAG, "onDestroy");
106
107 if (mDialog != null && mDialog.isShowing()) {
108 mDialog.dismiss();
109 mDialog = null;
110 }
111
112 if (mReceiver != null) {
113 unregisterReceiver(mReceiver);
114 mReceiver = null;
115 }
116 }
117
118 private void startProvisioning() {
119 buildAndShowDialog();
120
121 sendHfaCommand(ACTION_START);
122 }
123
124 private void buildAndShowDialog() {
125 mCanSkip = true;
126
127 mDialog = new AlertDialog.Builder(this)
128 .setTitle(R.string.ota_hfa_activation_title)
129 .setMessage(R.string.ota_hfa_activation_dialog_message)
130 .setPositiveButton(R.string.ota_skip_activation_dialog_skip_label,
131 new DialogInterface.OnClickListener() {
132 @Override
133 public void onClick(DialogInterface di, int which) {
134 if (mCanSkip) {
135 sendHfaCommand(ACTION_CANCEL);
136 sendResponseToSetupWizard(OTASP_USER_SKIPPED);
137 }
138 }})
139 /*.setOnCancelListener(new DialogInterface.OnCancelListener() {
140 @Override
141 public void onCancel(DialogInterface di) {
142 sendResponseToSetupWizard(OTASP_USER_SKIPPED);
143 }})*/
144 .create();
145
146 if (VERBOSE) Log.v(TAG, "showing dialog");
147 mDialog.show();
148 }
149
150 private void sendHfaCommand(String action) {
151 if (VERBOSE) Log.v(TAG, "Sending command: " + action);
152 sendBroadcast(new Intent(action));
153 }
154
155 private void onHfaError(String errorMsg) {
156 mDialog.dismiss();
157
158 AlertDialog errorDialog = new AlertDialog.Builder(this)
159 .setMessage(errorMsg)
160 .setPositiveButton(R.string.ota_skip_activation_dialog_skip_label,
161 new DialogInterface.OnClickListener() {
162 @Override
163 public void onClick(DialogInterface di, int which) {
164 di.dismiss();
165 sendResponseToSetupWizard(OTASP_USER_SKIPPED);
166 }
167 })
168 .setNegativeButton(R.string.ota_try_again,
169 new DialogInterface.OnClickListener() {
170 @Override
171 public void onClick(DialogInterface di, int which) {
172 di.dismiss();
173 startProvisioning();
174 }
175 })
176 .create();
177
178 errorDialog.show();
179 }
180
181 private void onHfaSuccess() {
182 // User can no longer skip after success.
183 mCanSkip = false;
184
185 // We need to restart the modem upon successful activation
186 // so that it can acquire a number and ensure setupwizard will
187 // know about this success through phone state changes.
188
189 bounceRadio();
190 }
191
192 private void bounceRadio() {
193 final Phone phone = PhoneGlobals.getInstance().getPhone();
194 phone.registerForServiceStateChanged(mHandler, SERVICE_STATE_CHANGED, null);
195
196 mPhoneMonitorState = WAITING_FOR_RADIO_OFF;
197 phone.setRadioPower(false);
198 onServiceStateChange(phone.getServiceState());
199 }
200
201 private void onServiceStateChange(ServiceState state) {
202 final boolean radioIsOff = state.getVoiceRegState() == ServiceState.STATE_POWER_OFF;
203 final Phone phone = PhoneGlobals.getInstance().getPhone();
204
205 if (VERBOSE) Log.v(TAG, "Radio is off: " + radioIsOff);
206
207 if (mPhoneMonitorState == WAITING_FOR_RADIO_OFF) {
208 if (radioIsOff) {
209 mPhoneMonitorState = WAITING_FOR_RADIO_ON;
210 phone.setRadioPower(true);
211 }
212 } else if (mPhoneMonitorState == WAITING_FOR_RADIO_ON) {
213 if (!radioIsOff) {
214 mPhoneMonitorState = NOT_WAITING;
215 phone.unregisterForServiceStateChanged(mHandler);
216
217 // We have successfully bounced the radio.
218 // Time to go back to the setup wizard.
219 sendResponseToSetupWizard(OTASP_SUCCESS);
220 }
221 }
222 }
223
224 private void sendResponseToSetupWizard(int responseCode) {
225 final PendingIntent otaResponseIntent = getIntent().getParcelableExtra(
226 OtaUtils.EXTRA_OTASP_RESULT_CODE_PENDING_INTENT);
227
228 final Intent extraStuff = new Intent();
229 extraStuff.putExtra(OtaUtils.EXTRA_OTASP_RESULT_CODE, responseCode);
230
231 try {
232 if (VERBOSE) Log.v(TAG, "Sending OTASP confirmation with result code: " + responseCode);
233 otaResponseIntent.send(this, 0 /* resultCode (not used) */, extraStuff);
234 } catch (CanceledException e) {
235 Log.e(TAG, "Pending Intent canceled");
236 }
237
238 finish();
239 }
240
241 public void startHfaIntentReceiver() {
242 final IntentFilter filter = new IntentFilter(ACTION_COMPLETE);
243 filter.addAction(ACTION_ERROR);
244
245 mReceiver = new BroadcastReceiver() {
246 @Override
247 public void onReceive(Context context, Intent intent) {
248 final String action = intent.getAction();
249 if (action.equals(ACTION_ERROR)) {
250 onHfaError(intent.getStringExtra("errorCode"));
251 } else if (action.equals(ACTION_COMPLETE)) {
252 if (VERBOSE) Log.v(TAG, "Hfa Successful");
253 onHfaSuccess();
254 }
255 }
256 };
257
258 registerReceiver(mReceiver, filter);
259 }
260
261 private Handler mHandler = new Handler() {
262 @Override
263 public void handleMessage(Message msg) {
264 switch (msg.what) {
265 case SERVICE_STATE_CHANGED:
266 ServiceState state = (ServiceState) ((AsyncResult) msg.obj).result;
267 onServiceStateChange(state);
268 break;
269 default:
270 break;
271 }
272 }
273 };
274
275}