Home | History | Annotate | Download | only in phone
      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 {
     79         public void onSuccess();
     80         public void onError(String errorMsg);
     81     }
     82 
     83     public HfaLogic(Context context, HfaLogicCallback callback, PendingIntent intent) {
     84         mCallback = Preconditions.checkNotNull(callback);
     85         mContext = Preconditions.checkNotNull(context);
     86         mResponseIntent = intent;
     87     }
     88 
     89     public void start() {
     90         Log.i(TAG, "start:");
     91         mRetryCount = DEFAULT_RETRY_COUNT;
     92         startHfaIntentReceiver();
     93         startProvisioning();
     94     }
     95 
     96     private void startProvisioning() {
     97         Log.i(TAG, "startProvisioning:");
     98         sendHfaCommand(ACTION_START);
     99     }
    100 
    101     private void sendHfaCommand(String action) {
    102         Log.i(TAG, "sendHfaCommand: command=" + action);
    103         mContext.sendBroadcast(new Intent(action));
    104     }
    105 
    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 
    122     private void onHfaSuccess() {
    123         Log.i(TAG, "onHfaSuccess: NOT bouncing radio call onTotalSuccess");
    124         stopHfaIntentReceiver();
    125         // bounceRadio();
    126         onTotalSuccess();
    127     }
    128 
    129     private void onTotalSuccess() {
    130         Log.i(TAG, "onTotalSuccess: call mCallBack.onSuccess");
    131         sendFinalResponse(OTASP_SUCCESS, null);
    132         mCallback.onSuccess();
    133     }
    134 
    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 
    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 
    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 
    185     private void stopHfaIntentReceiver() {
    186         if (mReceiver != null) {
    187             mContext.unregisterReceiver(mReceiver);
    188             mReceiver = null;
    189         }
    190     }
    191 
    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