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 private static final int DEFAULT_RETRY_COUNT = 1; 75 private int mRetryCount; 76 77 public interface HfaLogicCallback { 78 public void onSuccess(); 79 public void onError(String errorMsg); 80 } 81 82 public HfaLogic(Context context, HfaLogicCallback callback, PendingIntent intent) { 83 mCallback = Preconditions.checkNotNull(callback); 84 mContext = Preconditions.checkNotNull(context); 85 mResponseIntent = intent; 86 } 87 88 public void start() { 89 Log.i(TAG, "start:"); 90 mRetryCount = DEFAULT_RETRY_COUNT; 91 startHfaIntentReceiver(); 92 startProvisioning(); 93 } 94 95 private void startProvisioning() { 96 Log.i(TAG, "startProvisioning:"); 97 sendHfaCommand(ACTION_START); 98 } 99 100 private void sendHfaCommand(String action) { 101 Log.i(TAG, "sendHfaCommand: command=" + action); 102 mContext.sendBroadcast(new Intent(action)); 103 } 104 105 private void onHfaError(String errorMsg) { 106 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 } 119 } 120 121 private void onHfaSuccess() { 122 Log.i(TAG, "onHfaSuccess: NOT bouncing radio call onTotalSuccess"); 123 stopHfaIntentReceiver(); 124 // bounceRadio(); 125 onTotalSuccess(); 126 } 127 128 private void onTotalSuccess() { 129 Log.i(TAG, "onTotalSuccess: call mCallBack.onSuccess"); 130 sendFinalResponse(OTASP_SUCCESS, null); 131 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 147 Log.i(TAG, "Radio is on: " + !radioIsOff); 148 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)) { 175 Log.i(TAG, "Hfa Successful"); 176 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 191 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 210 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 } 225