/* * Copyright (C) 2013 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.phone; import android.app.PendingIntent; import android.app.PendingIntent.CanceledException; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.os.AsyncResult; import android.os.Handler; import android.os.Message; import android.telephony.ServiceState; import android.util.Log; import com.android.internal.telephony.Phone; import com.google.common.base.Preconditions; /** * Starts and displays status for Hands Free Activation (HFA). * * This class operates with Hands Free Activation apps. * It starts by broadcasting the intent com.android.action.START_HFA. * An HFA app will pick that up and start the HFA process. * If it fails it return ERROR_HFA Intent and upon success returns COMPLETE_HFA. * * If successful, we bounce the radio so that the service picks up the new number. * Once the radio is back on we callback the requestor. * * If there is an error, we do not bounce the radio but still callback with a failure. * * TODO(klp): We need system-only permissions for the HFA intents. */ public class HfaLogic { private static final String TAG = HfaLogic.class.getSimpleName(); private static final String ACTION_START = "com.android.action.START_HFA"; private static final String ACTION_ERROR = "com.android.action.ERROR_HFA"; private static final String ACTION_CANCEL = "com.android.action.CANCEL_HFA"; private static final String ACTION_COMPLETE = "com.android.action.COMPLETE_HFA"; private static final int SERVICE_STATE_CHANGED = 1; public static final int NOT_WAITING = 0; public static final int WAITING_FOR_RADIO_OFF = 1; public static final int WAITING_FOR_RADIO_ON = 2; public static final int OTASP_UNKNOWN = 0; public static final int OTASP_USER_SKIPPED = 1; public static final int OTASP_SUCCESS = 2; public static final int OTASP_FAILURE = 3; private int mPhoneMonitorState = NOT_WAITING; private BroadcastReceiver mReceiver; private HfaLogicCallback mCallback; private PendingIntent mResponseIntent; private Context mContext; // No retry at the moment. Increase later if necessary. private static final int DEFAULT_RETRY_COUNT = 0; private int mRetryCount; public interface HfaLogicCallback { public void onSuccess(); public void onError(String errorMsg); } public HfaLogic(Context context, HfaLogicCallback callback, PendingIntent intent) { mCallback = Preconditions.checkNotNull(callback); mContext = Preconditions.checkNotNull(context); mResponseIntent = intent; } public void start() { Log.i(TAG, "start:"); mRetryCount = DEFAULT_RETRY_COUNT; startHfaIntentReceiver(); startProvisioning(); } private void startProvisioning() { Log.i(TAG, "startProvisioning:"); sendHfaCommand(ACTION_START); } private void sendHfaCommand(String action) { Log.i(TAG, "sendHfaCommand: command=" + action); mContext.sendBroadcast(new Intent(action)); } private void onHfaError(String errorMsg) { Log.i(TAG, "onHfaError: call mCallBack.onError errorMsg=" + errorMsg + " mRetryCount=" + mRetryCount); mRetryCount -= 1; if (mRetryCount >= 0) { Log.i(TAG, "onHfaError: retry"); startProvisioning(); } else { Log.i(TAG, "onHfaError: Declare OTASP_FAILURE"); mRetryCount = 0; stopHfaIntentReceiver(); sendFinalResponse(OTASP_FAILURE, errorMsg); mCallback.onError(errorMsg); } } private void onHfaSuccess() { Log.i(TAG, "onHfaSuccess: NOT bouncing radio call onTotalSuccess"); stopHfaIntentReceiver(); // bounceRadio(); onTotalSuccess(); } private void onTotalSuccess() { Log.i(TAG, "onTotalSuccess: call mCallBack.onSuccess"); sendFinalResponse(OTASP_SUCCESS, null); mCallback.onSuccess(); } private void bounceRadio() { final Phone phone = PhoneGlobals.getInstance().getPhone(); phone.registerForServiceStateChanged(mHandler, SERVICE_STATE_CHANGED, null); mPhoneMonitorState = WAITING_FOR_RADIO_OFF; phone.setRadioPower(false); onServiceStateChange(phone.getServiceState()); } private void onServiceStateChange(ServiceState state) { final boolean radioIsOff = state.getVoiceRegState() == ServiceState.STATE_POWER_OFF; final Phone phone = PhoneGlobals.getInstance().getPhone(); Log.i(TAG, "Radio is on: " + !radioIsOff); if (mPhoneMonitorState == WAITING_FOR_RADIO_OFF) { if (radioIsOff) { mPhoneMonitorState = WAITING_FOR_RADIO_ON; phone.setRadioPower(true); } } else if (mPhoneMonitorState == WAITING_FOR_RADIO_ON) { if (!radioIsOff) { mPhoneMonitorState = NOT_WAITING; phone.unregisterForServiceStateChanged(mHandler); onTotalSuccess(); } } } private void startHfaIntentReceiver() { final IntentFilter filter = new IntentFilter(ACTION_COMPLETE); filter.addAction(ACTION_ERROR); mReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { final String action = intent.getAction(); if (action.equals(ACTION_ERROR)) { onHfaError(intent.getStringExtra("errorCode")); } else if (action.equals(ACTION_COMPLETE)) { Log.i(TAG, "Hfa Successful"); onHfaSuccess(); } } }; mContext.registerReceiver(mReceiver, filter); } private void stopHfaIntentReceiver() { if (mReceiver != null) { mContext.unregisterReceiver(mReceiver); mReceiver = null; } } private void sendFinalResponse(int responseCode, String errorCode) { if (mResponseIntent != null) { final Intent extraStuff = new Intent(); extraStuff.putExtra(OtaUtils.EXTRA_OTASP_RESULT_CODE, responseCode); if (responseCode == OTASP_FAILURE && errorCode != null) { extraStuff.putExtra(OtaUtils.EXTRA_OTASP_ERROR_CODE, errorCode); } try { Log.i(TAG, "Sending OTASP confirmation with result code: " + responseCode); mResponseIntent.send(mContext, 0 /* resultCode (not used) */, extraStuff); } catch (CanceledException e) { Log.e(TAG, "Pending Intent canceled"); } } } private Handler mHandler = new Handler() { @Override public void handleMessage(Message msg) { switch (msg.what) { case SERVICE_STATE_CHANGED: ServiceState state = (ServiceState) ((AsyncResult) msg.obj).result; onServiceStateChange(state); break; default: break; } } }; }