blob: e4a3205073dc74a98eb2fa6ec284838c8804fd60 [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
Wink Savillefc578432013-09-30 10:48:17 -070074 private static final int DEFAULT_RETRY_COUNT = 1;
75 private int mRetryCount;
76
Santos Cordon00d7a432013-09-17 14:07:14 -070077 public interface HfaLogicCallback {
78 public void onSuccess();
79 public void onError(String errorMsg);
80 }
81
Santos Cordoncd76f8b2013-09-23 17:59:50 -070082 public HfaLogic(Context context, HfaLogicCallback callback, PendingIntent intent) {
Santos Cordon00d7a432013-09-17 14:07:14 -070083 mCallback = Preconditions.checkNotNull(callback);
84 mContext = Preconditions.checkNotNull(context);
Santos Cordoncd76f8b2013-09-23 17:59:50 -070085 mResponseIntent = intent;
Santos Cordon00d7a432013-09-17 14:07:14 -070086 }
87
88 public void start() {
Wink Savillefc578432013-09-30 10:48:17 -070089 Log.i(TAG, "start:");
90 mRetryCount = DEFAULT_RETRY_COUNT;
Santos Cordon00d7a432013-09-17 14:07:14 -070091 startHfaIntentReceiver();
92 startProvisioning();
93 }
94
95 private void startProvisioning() {
Wink Savillefc578432013-09-30 10:48:17 -070096 Log.i(TAG, "startProvisioning:");
Santos Cordon00d7a432013-09-17 14:07:14 -070097 sendHfaCommand(ACTION_START);
98 }
99
100 private void sendHfaCommand(String action) {
Wink Savillefc578432013-09-30 10:48:17 -0700101 Log.i(TAG, "sendHfaCommand: command=" + action);
Santos Cordon00d7a432013-09-17 14:07:14 -0700102 mContext.sendBroadcast(new Intent(action));
103 }
104
105 private void onHfaError(String errorMsg) {
Wink Savillefc578432013-09-30 10:48:17 -0700106 Log.i(TAG, "onHfaError: call mCallBack.onError errorMsg=" + errorMsg
107 + " mRetryCount=" + mRetryCount);
108 mRetryCount -= 1;
109 if (mRetryCount >= 0) {
110 Log.i(TAG, "onHfaError: retry");
111 startProvisioning();
112 } else {
113 Log.i(TAG, "onHfaError: Declare OTASP_FAILURE");
114 mRetryCount = 0;
115 stopHfaIntentReceiver();
116 sendFinalResponse(OTASP_FAILURE, errorMsg);
117 mCallback.onError(errorMsg);
118 }
Santos Cordon00d7a432013-09-17 14:07:14 -0700119 }
120
121 private void onHfaSuccess() {
Wink Savillefc578432013-09-30 10:48:17 -0700122 Log.i(TAG, "onHfaSuccess: NOT bouncing radio call onTotalSuccess");
Santos Cordon00d7a432013-09-17 14:07:14 -0700123 stopHfaIntentReceiver();
Wink Savillefc578432013-09-30 10:48:17 -0700124 // bounceRadio();
125 onTotalSuccess();
Santos Cordon00d7a432013-09-17 14:07:14 -0700126 }
127
128 private void onTotalSuccess() {
Wink Savillefc578432013-09-30 10:48:17 -0700129 Log.i(TAG, "onTotalSuccess: call mCallBack.onSuccess");
Santos Cordoncd76f8b2013-09-23 17:59:50 -0700130 sendFinalResponse(OTASP_SUCCESS, null);
Santos Cordon00d7a432013-09-17 14:07:14 -0700131 mCallback.onSuccess();
132 }
133
134 private void bounceRadio() {
135 final Phone phone = PhoneGlobals.getInstance().getPhone();
136 phone.registerForServiceStateChanged(mHandler, SERVICE_STATE_CHANGED, null);
137
138 mPhoneMonitorState = WAITING_FOR_RADIO_OFF;
139 phone.setRadioPower(false);
140 onServiceStateChange(phone.getServiceState());
141 }
142
143 private void onServiceStateChange(ServiceState state) {
144 final boolean radioIsOff = state.getVoiceRegState() == ServiceState.STATE_POWER_OFF;
145 final Phone phone = PhoneGlobals.getInstance().getPhone();
146
Santos Cordoncd76f8b2013-09-23 17:59:50 -0700147 Log.i(TAG, "Radio is on: " + !radioIsOff);
Santos Cordon00d7a432013-09-17 14:07:14 -0700148
149 if (mPhoneMonitorState == WAITING_FOR_RADIO_OFF) {
150 if (radioIsOff) {
151 mPhoneMonitorState = WAITING_FOR_RADIO_ON;
152 phone.setRadioPower(true);
153 }
154 } else if (mPhoneMonitorState == WAITING_FOR_RADIO_ON) {
155 if (!radioIsOff) {
156 mPhoneMonitorState = NOT_WAITING;
157 phone.unregisterForServiceStateChanged(mHandler);
158
159 onTotalSuccess();
160 }
161 }
162 }
163
164 private void startHfaIntentReceiver() {
165 final IntentFilter filter = new IntentFilter(ACTION_COMPLETE);
166 filter.addAction(ACTION_ERROR);
167
168 mReceiver = new BroadcastReceiver() {
169 @Override
170 public void onReceive(Context context, Intent intent) {
171 final String action = intent.getAction();
172 if (action.equals(ACTION_ERROR)) {
173 onHfaError(intent.getStringExtra("errorCode"));
174 } else if (action.equals(ACTION_COMPLETE)) {
Santos Cordoncd76f8b2013-09-23 17:59:50 -0700175 Log.i(TAG, "Hfa Successful");
Santos Cordon00d7a432013-09-17 14:07:14 -0700176 onHfaSuccess();
177 }
178 }
179 };
180
181 mContext.registerReceiver(mReceiver, filter);
182 }
183
184 private void stopHfaIntentReceiver() {
185 if (mReceiver != null) {
186 mContext.unregisterReceiver(mReceiver);
187 mReceiver = null;
188 }
189 }
190
Santos Cordoncd76f8b2013-09-23 17:59:50 -0700191 private void sendFinalResponse(int responseCode, String errorCode) {
192 if (mResponseIntent != null) {
193 final Intent extraStuff = new Intent();
194 extraStuff.putExtra(OtaUtils.EXTRA_OTASP_RESULT_CODE, responseCode);
195
196 if (responseCode == OTASP_FAILURE && errorCode != null) {
197 extraStuff.putExtra(OtaUtils.EXTRA_OTASP_ERROR_CODE, errorCode);
198 }
199
200 try {
201 Log.i(TAG, "Sending OTASP confirmation with result code: "
202 + responseCode);
203 mResponseIntent.send(mContext, 0 /* resultCode (not used) */, extraStuff);
204 } catch (CanceledException e) {
205 Log.e(TAG, "Pending Intent canceled");
206 }
207 }
208 }
209
Santos Cordon00d7a432013-09-17 14:07:14 -0700210 private Handler mHandler = new Handler() {
211 @Override
212 public void handleMessage(Message msg) {
213 switch (msg.what) {
214 case SERVICE_STATE_CHANGED:
215 ServiceState state = (ServiceState) ((AsyncResult) msg.obj).result;
216 onServiceStateChange(state);
217 break;
218 default:
219 break;
220 }
221 }
222 };
223
224}