blob: 5a5e4b4c0b2ca78f3a156ca64f16a0725aa88853 [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
74 public interface HfaLogicCallback {
75 public void onSuccess();
76 public void onError(String errorMsg);
77 }
78
Santos Cordoncd76f8b2013-09-23 17:59:50 -070079 public HfaLogic(Context context, HfaLogicCallback callback, PendingIntent intent) {
Santos Cordon00d7a432013-09-17 14:07:14 -070080 mCallback = Preconditions.checkNotNull(callback);
81 mContext = Preconditions.checkNotNull(context);
Santos Cordoncd76f8b2013-09-23 17:59:50 -070082 mResponseIntent = intent;
Santos Cordon00d7a432013-09-17 14:07:14 -070083 }
84
85 public void start() {
86 Log.i(TAG, "Start Hfa Provisioning.");
87 startHfaIntentReceiver();
88 startProvisioning();
89 }
90
91 private void startProvisioning() {
92 sendHfaCommand(ACTION_START);
93 }
94
95 private void sendHfaCommand(String action) {
Santos Cordoncd76f8b2013-09-23 17:59:50 -070096 Log.i(TAG, "Sending command: " + action);
Santos Cordon00d7a432013-09-17 14:07:14 -070097 mContext.sendBroadcast(new Intent(action));
98 }
99
100 private void onHfaError(String errorMsg) {
Santos Cordoncd76f8b2013-09-23 17:59:50 -0700101 Log.i(TAG, "HfaError");
Santos Cordon00d7a432013-09-17 14:07:14 -0700102 stopHfaIntentReceiver();
Santos Cordoncd76f8b2013-09-23 17:59:50 -0700103 sendFinalResponse(OTASP_FAILURE, errorMsg);
Santos Cordon00d7a432013-09-17 14:07:14 -0700104 mCallback.onError(errorMsg);
105 }
106
107 private void onHfaSuccess() {
Santos Cordoncd76f8b2013-09-23 17:59:50 -0700108 Log.i(TAG, "HfaSuccess");
Santos Cordon00d7a432013-09-17 14:07:14 -0700109 stopHfaIntentReceiver();
110 bounceRadio();
111 }
112
113 private void onTotalSuccess() {
Santos Cordoncd76f8b2013-09-23 17:59:50 -0700114 sendFinalResponse(OTASP_SUCCESS, null);
Santos Cordon00d7a432013-09-17 14:07:14 -0700115 mCallback.onSuccess();
116 }
117
118 private void bounceRadio() {
119 final Phone phone = PhoneGlobals.getInstance().getPhone();
120 phone.registerForServiceStateChanged(mHandler, SERVICE_STATE_CHANGED, null);
121
122 mPhoneMonitorState = WAITING_FOR_RADIO_OFF;
123 phone.setRadioPower(false);
124 onServiceStateChange(phone.getServiceState());
125 }
126
127 private void onServiceStateChange(ServiceState state) {
128 final boolean radioIsOff = state.getVoiceRegState() == ServiceState.STATE_POWER_OFF;
129 final Phone phone = PhoneGlobals.getInstance().getPhone();
130
Santos Cordoncd76f8b2013-09-23 17:59:50 -0700131 Log.i(TAG, "Radio is on: " + !radioIsOff);
Santos Cordon00d7a432013-09-17 14:07:14 -0700132
133 if (mPhoneMonitorState == WAITING_FOR_RADIO_OFF) {
134 if (radioIsOff) {
135 mPhoneMonitorState = WAITING_FOR_RADIO_ON;
136 phone.setRadioPower(true);
137 }
138 } else if (mPhoneMonitorState == WAITING_FOR_RADIO_ON) {
139 if (!radioIsOff) {
140 mPhoneMonitorState = NOT_WAITING;
141 phone.unregisterForServiceStateChanged(mHandler);
142
143 onTotalSuccess();
144 }
145 }
146 }
147
148 private void startHfaIntentReceiver() {
149 final IntentFilter filter = new IntentFilter(ACTION_COMPLETE);
150 filter.addAction(ACTION_ERROR);
151
152 mReceiver = new BroadcastReceiver() {
153 @Override
154 public void onReceive(Context context, Intent intent) {
155 final String action = intent.getAction();
156 if (action.equals(ACTION_ERROR)) {
157 onHfaError(intent.getStringExtra("errorCode"));
158 } else if (action.equals(ACTION_COMPLETE)) {
Santos Cordoncd76f8b2013-09-23 17:59:50 -0700159 Log.i(TAG, "Hfa Successful");
Santos Cordon00d7a432013-09-17 14:07:14 -0700160 onHfaSuccess();
161 }
162 }
163 };
164
165 mContext.registerReceiver(mReceiver, filter);
166 }
167
168 private void stopHfaIntentReceiver() {
169 if (mReceiver != null) {
170 mContext.unregisterReceiver(mReceiver);
171 mReceiver = null;
172 }
173 }
174
Santos Cordoncd76f8b2013-09-23 17:59:50 -0700175 private void sendFinalResponse(int responseCode, String errorCode) {
176 if (mResponseIntent != null) {
177 final Intent extraStuff = new Intent();
178 extraStuff.putExtra(OtaUtils.EXTRA_OTASP_RESULT_CODE, responseCode);
179
180 if (responseCode == OTASP_FAILURE && errorCode != null) {
181 extraStuff.putExtra(OtaUtils.EXTRA_OTASP_ERROR_CODE, errorCode);
182 }
183
184 try {
185 Log.i(TAG, "Sending OTASP confirmation with result code: "
186 + responseCode);
187 mResponseIntent.send(mContext, 0 /* resultCode (not used) */, extraStuff);
188 } catch (CanceledException e) {
189 Log.e(TAG, "Pending Intent canceled");
190 }
191 }
192 }
193
Santos Cordon00d7a432013-09-17 14:07:14 -0700194 private Handler mHandler = new Handler() {
195 @Override
196 public void handleMessage(Message msg) {
197 switch (msg.what) {
198 case SERVICE_STATE_CHANGED:
199 ServiceState state = (ServiceState) ((AsyncResult) msg.obj).result;
200 onServiceStateChange(state);
201 break;
202 default:
203 break;
204 }
205 }
206 };
207
208}