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     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