blob: b064b18b7ca478d1c1d128c529535b3c53ae6a1c [file] [log] [blame]
Santos Cordon00d7a432013-09-17 14:07:14 -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
Santos Cordoncd76f8b2013-09-23 17:59:50 -070019import android.app.PendingIntent;
20import android.app.PendingIntent.CanceledException;
Santos Cordon00d7a432013-09-17 14:07:14 -070021import android.content.BroadcastReceiver;
22import android.content.Context;
23import android.content.Intent;
24import android.content.IntentFilter;
25import android.os.AsyncResult;
26import android.os.Handler;
27import android.os.Message;
28import android.telephony.ServiceState;
29import android.util.Log;
30
31import com.android.internal.telephony.Phone;
32import com.google.common.base.Preconditions;
33
34/**
35 * Starts and displays status for Hands Free Activation (HFA).
36 *
37 * This class operates with Hands Free Activation apps.
38 * It starts by broadcasting the intent com.android.action.START_HFA.
39 * An HFA app will pick that up and start the HFA process.
40 * If it fails it return ERROR_HFA Intent and upon success returns COMPLETE_HFA.
41 *
42 * If successful, we bounce the radio so that the service picks up the new number.
43 * Once the radio is back on we callback the requestor.
44 *
45 * If there is an error, we do not bounce the radio but still callback with a failure.
46 *
47 * TODO(klp): We need system-only permissions for the HFA intents.
48 */
49public class HfaLogic {
50 private static final String TAG = HfaLogic.class.getSimpleName();
Santos Cordon00d7a432013-09-17 14:07:14 -070051
52 private static final String ACTION_START = "com.android.action.START_HFA";
53 private static final String ACTION_ERROR = "com.android.action.ERROR_HFA";
54 private static final String ACTION_CANCEL = "com.android.action.CANCEL_HFA";
55 private static final String ACTION_COMPLETE = "com.android.action.COMPLETE_HFA";
56
57 private static final int SERVICE_STATE_CHANGED = 1;
58
59 public static final int NOT_WAITING = 0;
60 public static final int WAITING_FOR_RADIO_OFF = 1;
61 public static final int WAITING_FOR_RADIO_ON = 2;
62
Santos Cordoncd76f8b2013-09-23 17:59:50 -070063 public static final int OTASP_UNKNOWN = 0;
64 public static final int OTASP_USER_SKIPPED = 1;
65 public static final int OTASP_SUCCESS = 2;
66 public static final int OTASP_FAILURE = 3;
67
Santos Cordon00d7a432013-09-17 14:07:14 -070068 private int mPhoneMonitorState = NOT_WAITING;
69 private BroadcastReceiver mReceiver;
70 private HfaLogicCallback mCallback;
Santos Cordoncd76f8b2013-09-23 17:59:50 -070071 private PendingIntent mResponseIntent;
Santos Cordon00d7a432013-09-17 14:07:14 -070072 private Context mContext;
73
Santos Cordond6883692014-10-06 09:58:19 -070074 // No retry at the moment. Increase later if necessary.
75 private static final int DEFAULT_RETRY_COUNT = 0;
Wink Savillefc578432013-09-30 10:48:17 -070076 private int mRetryCount;
77
Santos Cordon00d7a432013-09-17 14:07:14 -070078 public interface HfaLogicCallback {
79 public void onSuccess();
80 public void onError(String errorMsg);
81 }
82
Santos Cordoncd76f8b2013-09-23 17:59:50 -070083 public HfaLogic(Context context, HfaLogicCallback callback, PendingIntent intent) {
Santos Cordon00d7a432013-09-17 14:07:14 -070084 mCallback = Preconditions.checkNotNull(callback);
85 mContext = Preconditions.checkNotNull(context);
Santos Cordoncd76f8b2013-09-23 17:59:50 -070086 mResponseIntent = intent;
Santos Cordon00d7a432013-09-17 14:07:14 -070087 }
88
89 public void start() {
Wink Savillefc578432013-09-30 10:48:17 -070090 Log.i(TAG, "start:");
91 mRetryCount = DEFAULT_RETRY_COUNT;
Santos Cordon00d7a432013-09-17 14:07:14 -070092 startHfaIntentReceiver();
93 startProvisioning();
94 }
95
96 private void startProvisioning() {
Wink Savillefc578432013-09-30 10:48:17 -070097 Log.i(TAG, "startProvisioning:");
Santos Cordon00d7a432013-09-17 14:07:14 -070098 sendHfaCommand(ACTION_START);
99 }
100
101 private void sendHfaCommand(String action) {
Wink Savillefc578432013-09-30 10:48:17 -0700102 Log.i(TAG, "sendHfaCommand: command=" + action);
Santos Cordon00d7a432013-09-17 14:07:14 -0700103 mContext.sendBroadcast(new Intent(action));
104 }
105
106 private void onHfaError(String errorMsg) {
Wink Savillefc578432013-09-30 10:48:17 -0700107 Log.i(TAG, "onHfaError: call mCallBack.onError errorMsg=" + errorMsg
108 + " mRetryCount=" + mRetryCount);
109 mRetryCount -= 1;
110 if (mRetryCount >= 0) {
111 Log.i(TAG, "onHfaError: retry");
112 startProvisioning();
113 } else {
114 Log.i(TAG, "onHfaError: Declare OTASP_FAILURE");
115 mRetryCount = 0;
116 stopHfaIntentReceiver();
117 sendFinalResponse(OTASP_FAILURE, errorMsg);
118 mCallback.onError(errorMsg);
119 }
Santos Cordon00d7a432013-09-17 14:07:14 -0700120 }
121
122 private void onHfaSuccess() {
Wink Savillefc578432013-09-30 10:48:17 -0700123 Log.i(TAG, "onHfaSuccess: NOT bouncing radio call onTotalSuccess");
Santos Cordon00d7a432013-09-17 14:07:14 -0700124 stopHfaIntentReceiver();
Wink Savillefc578432013-09-30 10:48:17 -0700125 // bounceRadio();
126 onTotalSuccess();
Santos Cordon00d7a432013-09-17 14:07:14 -0700127 }
128
129 private void onTotalSuccess() {
Wink Savillefc578432013-09-30 10:48:17 -0700130 Log.i(TAG, "onTotalSuccess: call mCallBack.onSuccess");
Santos Cordoncd76f8b2013-09-23 17:59:50 -0700131 sendFinalResponse(OTASP_SUCCESS, null);
Santos Cordon00d7a432013-09-17 14:07:14 -0700132 mCallback.onSuccess();
133 }
134
135 private void bounceRadio() {
136 final Phone phone = PhoneGlobals.getInstance().getPhone();
137 phone.registerForServiceStateChanged(mHandler, SERVICE_STATE_CHANGED, null);
138
139 mPhoneMonitorState = WAITING_FOR_RADIO_OFF;
140 phone.setRadioPower(false);
141 onServiceStateChange(phone.getServiceState());
142 }
143
144 private void onServiceStateChange(ServiceState state) {
145 final boolean radioIsOff = state.getVoiceRegState() == ServiceState.STATE_POWER_OFF;
146 final Phone phone = PhoneGlobals.getInstance().getPhone();
147
Santos Cordoncd76f8b2013-09-23 17:59:50 -0700148 Log.i(TAG, "Radio is on: " + !radioIsOff);
Santos Cordon00d7a432013-09-17 14:07:14 -0700149
150 if (mPhoneMonitorState == WAITING_FOR_RADIO_OFF) {
151 if (radioIsOff) {
152 mPhoneMonitorState = WAITING_FOR_RADIO_ON;
153 phone.setRadioPower(true);
154 }
155 } else if (mPhoneMonitorState == WAITING_FOR_RADIO_ON) {
156 if (!radioIsOff) {
157 mPhoneMonitorState = NOT_WAITING;
158 phone.unregisterForServiceStateChanged(mHandler);
159
160 onTotalSuccess();
161 }
162 }
163 }
164
165 private void startHfaIntentReceiver() {
166 final IntentFilter filter = new IntentFilter(ACTION_COMPLETE);
167 filter.addAction(ACTION_ERROR);
168
169 mReceiver = new BroadcastReceiver() {
170 @Override
171 public void onReceive(Context context, Intent intent) {
172 final String action = intent.getAction();
173 if (action.equals(ACTION_ERROR)) {
174 onHfaError(intent.getStringExtra("errorCode"));
175 } else if (action.equals(ACTION_COMPLETE)) {
Santos Cordoncd76f8b2013-09-23 17:59:50 -0700176 Log.i(TAG, "Hfa Successful");
Santos Cordon00d7a432013-09-17 14:07:14 -0700177 onHfaSuccess();
178 }
179 }
180 };
181
182 mContext.registerReceiver(mReceiver, filter);
183 }
184
185 private void stopHfaIntentReceiver() {
186 if (mReceiver != null) {
187 mContext.unregisterReceiver(mReceiver);
188 mReceiver = null;
189 }
190 }
191
Santos Cordoncd76f8b2013-09-23 17:59:50 -0700192 private void sendFinalResponse(int responseCode, String errorCode) {
193 if (mResponseIntent != null) {
194 final Intent extraStuff = new Intent();
195 extraStuff.putExtra(OtaUtils.EXTRA_OTASP_RESULT_CODE, responseCode);
196
197 if (responseCode == OTASP_FAILURE && errorCode != null) {
198 extraStuff.putExtra(OtaUtils.EXTRA_OTASP_ERROR_CODE, errorCode);
199 }
200
201 try {
202 Log.i(TAG, "Sending OTASP confirmation with result code: "
203 + responseCode);
204 mResponseIntent.send(mContext, 0 /* resultCode (not used) */, extraStuff);
205 } catch (CanceledException e) {
206 Log.e(TAG, "Pending Intent canceled");
207 }
208 }
209 }
210
Santos Cordon00d7a432013-09-17 14:07:14 -0700211 private Handler mHandler = new Handler() {
212 @Override
213 public void handleMessage(Message msg) {
214 switch (msg.what) {
215 case SERVICE_STATE_CHANGED:
216 ServiceState state = (ServiceState) ((AsyncResult) msg.obj).result;
217 onServiceStateChange(state);
218 break;
219 default:
220 break;
221 }
222 }
223 };
224
225}