1 /*
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 
17 package com.android.phone;
18 
19 import android.app.PendingIntent;
20 import android.app.PendingIntent.CanceledException;
21 import android.content.BroadcastReceiver;
22 import android.content.Context;
23 import android.content.Intent;
24 import android.content.IntentFilter;
25 import android.os.AsyncResult;
26 import android.os.Handler;
27 import android.os.Message;
28 import android.telephony.ServiceState;
29 import android.util.Log;
30 
31 import com.android.internal.telephony.Phone;
32 import 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  */
49 public class HfaLogic {
50     private static final String TAG = HfaLogic.class.getSimpleName();
51 
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 
63     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 
68     private int mPhoneMonitorState = NOT_WAITING;
69     private BroadcastReceiver mReceiver;
70     private HfaLogicCallback mCallback;
71     private PendingIntent mResponseIntent;
72     private Context mContext;
73 
74     // No retry at the moment. Increase later if necessary.
75     private static final int DEFAULT_RETRY_COUNT = 0;
76     private int mRetryCount;
77 
78     public interface HfaLogicCallback {
onSuccess()79         public void onSuccess();
onError(String errorMsg)80         public void onError(String errorMsg);
81     }
82 
HfaLogic(Context context, HfaLogicCallback callback, PendingIntent intent)83     public HfaLogic(Context context, HfaLogicCallback callback, PendingIntent intent) {
84         mCallback = Preconditions.checkNotNull(callback);
85         mContext = Preconditions.checkNotNull(context);
86         mResponseIntent = intent;
87     }
88 
start()89     public void start() {
90         Log.i(TAG, "start:");
91         mRetryCount = DEFAULT_RETRY_COUNT;
92         startHfaIntentReceiver();
93         startProvisioning();
94     }
95 
startProvisioning()96     private void startProvisioning() {
97         Log.i(TAG, "startProvisioning:");
98         sendHfaCommand(ACTION_START);
99     }
100 
sendHfaCommand(String action)101     private void sendHfaCommand(String action) {
102         Log.i(TAG, "sendHfaCommand: command=" + action);
103         mContext.sendBroadcast(new Intent(action));
104     }
105 
onHfaError(String errorMsg)106     private void onHfaError(String errorMsg) {
107         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         }
120     }
121 
onHfaSuccess()122     private void onHfaSuccess() {
123         Log.i(TAG, "onHfaSuccess: NOT bouncing radio call onTotalSuccess");
124         stopHfaIntentReceiver();
125         // bounceRadio();
126         onTotalSuccess();
127     }
128 
onTotalSuccess()129     private void onTotalSuccess() {
130         Log.i(TAG, "onTotalSuccess: call mCallBack.onSuccess");
131         sendFinalResponse(OTASP_SUCCESS, null);
132         mCallback.onSuccess();
133     }
134 
bounceRadio()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 
onServiceStateChange(ServiceState state)144     private void onServiceStateChange(ServiceState state) {
145         final boolean radioIsOff = state.getVoiceRegState() == ServiceState.STATE_POWER_OFF;
146         final Phone phone = PhoneGlobals.getInstance().getPhone();
147 
148         Log.i(TAG, "Radio is on: " + !radioIsOff);
149 
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 
startHfaIntentReceiver()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)) {
176                     Log.i(TAG, "Hfa Successful");
177                     onHfaSuccess();
178                 }
179             }
180         };
181 
182         mContext.registerReceiver(mReceiver, filter);
183     }
184 
stopHfaIntentReceiver()185     private void stopHfaIntentReceiver() {
186         if (mReceiver != null) {
187             mContext.unregisterReceiver(mReceiver);
188             mReceiver = null;
189         }
190     }
191 
sendFinalResponse(int responseCode, String errorCode)192     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 
211     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 }
226