Home | History | Annotate | Download | only in hfp
      1 /*
      2  * Copyright (C) 2012 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.bluetooth.hfp;
     18 
     19 import android.bluetooth.BluetoothDevice;
     20 import android.content.BroadcastReceiver;
     21 import android.content.Context;
     22 import android.content.Intent;
     23 import android.content.IntentFilter;
     24 import android.telephony.PhoneStateListener;
     25 import android.telephony.ServiceState;
     26 import android.telephony.SignalStrength;
     27 import android.telephony.TelephonyManager;
     28 import android.telephony.SubscriptionManager;
     29 import android.telephony.SubscriptionManager.OnSubscriptionsChangedListener;
     30 import android.util.Log;
     31 
     32 import com.android.internal.telephony.IccCardConstants;
     33 import com.android.internal.telephony.TelephonyIntents;
     34 
     35 
     36 // Note:
     37 // All methods in this class are not thread safe, donot call them from
     38 // multiple threads. Call them from the HeadsetPhoneStateMachine message
     39 // handler only.
     40 class HeadsetPhoneState {
     41     private static final String TAG = "HeadsetPhoneState";
     42 
     43     private HeadsetStateMachine mStateMachine;
     44     private TelephonyManager mTelephonyManager;
     45     private ServiceState mServiceState;
     46 
     47     // HFP 1.6 CIND service
     48     private int mService = HeadsetHalConstants.NETWORK_STATE_NOT_AVAILABLE;
     49 
     50     // Check this before sending out service state to the device -- if the SIM isn't fully
     51     // loaded, don't expose that the network is available.
     52     private boolean mIsSimStateLoaded = false;
     53 
     54     // Number of active (foreground) calls
     55     private int mNumActive = 0;
     56 
     57     // Current Call Setup State
     58     private int mCallState = HeadsetHalConstants.CALL_STATE_IDLE;
     59 
     60     // Number of held (background) calls
     61     private int mNumHeld = 0;
     62 
     63     // HFP 1.6 CIND signal
     64     private int mSignal = 0;
     65 
     66     // HFP 1.6 CIND roam
     67     private int mRoam = HeadsetHalConstants.SERVICE_TYPE_HOME;
     68 
     69     // HFP 1.6 CIND battchg
     70     private int mBatteryCharge = 0;
     71 
     72     private int mSpeakerVolume = 0;
     73 
     74     private int mMicVolume = 0;
     75 
     76     private boolean mListening = false;
     77 
     78     // when HFP Service Level Connection is established
     79     private boolean mSlcReady = false;
     80 
     81     private Context mContext = null;
     82 
     83     private PhoneStateListener mPhoneStateListener = null;
     84 
     85     private SubscriptionManager mSubMgr;
     86 
     87     private OnSubscriptionsChangedListener mOnSubscriptionsChangedListener =
     88             new OnSubscriptionsChangedListener() {
     89         @Override
     90         public void onSubscriptionsChanged() {
     91             listenForPhoneState(false);
     92             listenForPhoneState(true);
     93         }
     94     };
     95 
     96 
     97     HeadsetPhoneState(Context context, HeadsetStateMachine stateMachine) {
     98         mStateMachine = stateMachine;
     99         mTelephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
    100         if (mTelephonyManager == null) {
    101             Log.e(TAG, "getSystemService(Context.TELEPHONY_SERVICE) failed, "
    102                   + "cannot register for SubscriptionInfo changes");
    103         }
    104         mContext = context;
    105 
    106         // Register for SubscriptionInfo list changes which is guaranteed
    107         // to invoke onSubscriptionInfoChanged and which in turns calls
    108         // loadInBackgroud.
    109         mSubMgr = SubscriptionManager.from(mContext);
    110         mSubMgr.addOnSubscriptionsChangedListener(mOnSubscriptionsChangedListener);
    111     }
    112 
    113     public void cleanup() {
    114         listenForPhoneState(false);
    115         mSubMgr.removeOnSubscriptionsChangedListener(mOnSubscriptionsChangedListener);
    116 
    117         mTelephonyManager = null;
    118         mStateMachine = null;
    119     }
    120 
    121     void listenForPhoneState(boolean start) {
    122 
    123         mSlcReady = start;
    124 
    125         if (start) {
    126             startListenForPhoneState();
    127         } else {
    128             stopListenForPhoneState();
    129         }
    130 
    131     }
    132 
    133     private void startListenForPhoneState() {
    134         if (!mListening && mSlcReady && mTelephonyManager != null) {
    135 
    136             int subId = SubscriptionManager.getDefaultSubscriptionId();
    137 
    138             if (SubscriptionManager.isValidSubscriptionId(subId)) {
    139                 mPhoneStateListener = getPhoneStateListener(subId);
    140                 if (mTelephonyManager == null) {
    141                     Log.e(TAG, "mTelephonyManager is null, "
    142                          + "cannot start listening for phone state changes");
    143                 } else {
    144                     mTelephonyManager.listen(mPhoneStateListener,
    145                                              PhoneStateListener.LISTEN_SERVICE_STATE |
    146                                              PhoneStateListener.LISTEN_SIGNAL_STRENGTHS);
    147                     mListening = true;
    148                 }
    149             }
    150         }
    151     }
    152 
    153     private void stopListenForPhoneState() {
    154         if (mListening && mTelephonyManager != null) {
    155 
    156             if (mTelephonyManager == null) {
    157                 Log.e(TAG, "mTelephonyManager is null, "
    158                      + "cannot send request to stop listening for phone state changes");
    159             } else {
    160                 mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_NONE);
    161                 mListening = false;
    162             }
    163         }
    164     }
    165 
    166     int getService() {
    167         return mService;
    168     }
    169 
    170     int getNumActiveCall() {
    171         return mNumActive;
    172     }
    173 
    174     void setNumActiveCall(int numActive) {
    175         mNumActive = numActive;
    176     }
    177 
    178     int getCallState() {
    179         return mCallState;
    180     }
    181 
    182     void setCallState(int callState) {
    183         mCallState = callState;
    184     }
    185 
    186     int getNumHeldCall() {
    187         return mNumHeld;
    188     }
    189 
    190     void setNumHeldCall(int numHeldCall) {
    191         mNumHeld = numHeldCall;
    192     }
    193 
    194     int getSignal() {
    195         return mSignal;
    196     }
    197 
    198     int getRoam() {
    199         return mRoam;
    200     }
    201 
    202     void setRoam(int roam) {
    203         if (mRoam != roam) {
    204             mRoam = roam;
    205             sendDeviceStateChanged();
    206         }
    207     }
    208 
    209     void setBatteryCharge(int batteryLevel) {
    210         if (mBatteryCharge != batteryLevel) {
    211             mBatteryCharge = batteryLevel;
    212             sendDeviceStateChanged();
    213         }
    214     }
    215 
    216     int getBatteryCharge() {
    217         return mBatteryCharge;
    218     }
    219 
    220     void setSpeakerVolume(int volume) {
    221         mSpeakerVolume = volume;
    222     }
    223 
    224     int getSpeakerVolume() {
    225         return mSpeakerVolume;
    226     }
    227 
    228     void setMicVolume(int volume) {
    229         mMicVolume = volume;
    230     }
    231 
    232     int getMicVolume() {
    233         return mMicVolume;
    234     }
    235 
    236     boolean isInCall() {
    237         return (mNumActive >= 1);
    238     }
    239 
    240     void sendDeviceStateChanged()
    241     {
    242         int service =
    243                 mIsSimStateLoaded ? mService : HeadsetHalConstants.NETWORK_STATE_NOT_AVAILABLE;
    244         // When out of service, send signal strength as 0. Some devices don't
    245         // use the service indicator, but only the signal indicator
    246         int signal = service == HeadsetHalConstants.NETWORK_STATE_AVAILABLE ? mSignal : 0;
    247 
    248         Log.d(TAG, "sendDeviceStateChanged. mService="+ mService +
    249                    " mIsSimStateLoaded=" + mIsSimStateLoaded +
    250                    " mSignal=" + signal +" mRoam="+ mRoam +
    251                    " mBatteryCharge=" + mBatteryCharge);
    252         HeadsetStateMachine sm = mStateMachine;
    253         if (sm != null) {
    254             sm.sendMessage(HeadsetStateMachine.DEVICE_STATE_CHANGED,
    255                 new HeadsetDeviceState(service, mRoam, signal, mBatteryCharge));
    256         }
    257     }
    258 
    259     private PhoneStateListener getPhoneStateListener(int subId) {
    260         PhoneStateListener mPhoneStateListener = new PhoneStateListener(subId) {
    261             @Override
    262             public void onServiceStateChanged(ServiceState serviceState) {
    263                 mServiceState = serviceState;
    264                 int newService = (serviceState.getState() == ServiceState.STATE_IN_SERVICE) ?
    265                     HeadsetHalConstants.NETWORK_STATE_AVAILABLE :
    266                     HeadsetHalConstants.NETWORK_STATE_NOT_AVAILABLE;
    267                 int newRoam = serviceState.getRoaming() ? HeadsetHalConstants.SERVICE_TYPE_ROAMING
    268                                                   : HeadsetHalConstants.SERVICE_TYPE_HOME;
    269 
    270                 if (newService == mService && newRoam == mRoam) {
    271                     // Debounce the state change
    272                     return;
    273                 }
    274                 mService = newService;
    275                 mRoam = newRoam;
    276 
    277                 // If this is due to a SIM insertion, we want to defer sending device state changed
    278                 // until all the SIM config is loaded.
    279                 if (newService == HeadsetHalConstants.NETWORK_STATE_NOT_AVAILABLE) {
    280                     mIsSimStateLoaded = false;
    281                     sendDeviceStateChanged();
    282                     return;
    283                 }
    284                 IntentFilter simStateChangedFilter =
    285                         new IntentFilter(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
    286                 mContext.registerReceiver(new BroadcastReceiver() {
    287                     @Override
    288                     public void onReceive(Context context, Intent intent) {
    289                         if (TelephonyIntents.ACTION_SIM_STATE_CHANGED.equals(intent.getAction())) {
    290                             // This is a sticky broadcast, so if it's already been loaded,
    291                             // this'll execute immediately.
    292                             if (IccCardConstants.INTENT_VALUE_ICC_LOADED.equals(
    293                                     intent.getStringExtra(IccCardConstants.INTENT_KEY_ICC_STATE))) {
    294                                 mIsSimStateLoaded = true;
    295                                 sendDeviceStateChanged();
    296                                 mContext.unregisterReceiver(this);
    297                             }
    298                         }
    299                     }
    300                 }, simStateChangedFilter);
    301             }
    302 
    303             @Override
    304             public void onSignalStrengthsChanged(SignalStrength signalStrength) {
    305 
    306                 int prevSignal = mSignal;
    307                 if (mService == HeadsetHalConstants.NETWORK_STATE_NOT_AVAILABLE) {
    308                     mSignal = 0;
    309                 } else if (signalStrength.isGsm()) {
    310                     mSignal = signalStrength.getLteLevel();
    311                     if (mSignal == SignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN) {
    312                         mSignal = gsmAsuToSignal(signalStrength);
    313                     } else {
    314                         // SignalStrength#getLteLevel returns the scale from 0-4
    315                         // Bluetooth signal scales at 0-5
    316                         // Let's match up the larger side
    317                         mSignal++;
    318                     }
    319                 } else {
    320                     mSignal = cdmaDbmEcioToSignal(signalStrength);
    321                 }
    322 
    323                 // network signal strength is scaled to BT 1-5 levels.
    324                 // This results in a lot of duplicate messages, hence this check
    325                 if (prevSignal != mSignal) {
    326                     sendDeviceStateChanged();
    327                 }
    328             }
    329 
    330             /* convert [0,31] ASU signal strength to the [0,5] expected by
    331              * bluetooth devices. Scale is similar to status bar policy
    332              */
    333             private int gsmAsuToSignal(SignalStrength signalStrength) {
    334                 int asu = signalStrength.getGsmSignalStrength();
    335                 if      (asu == 99) return 0;
    336                 else if (asu >= 16) return 5;
    337                 else if (asu >= 8)  return 4;
    338                 else if (asu >= 4)  return 3;
    339                 else if (asu >= 2)  return 2;
    340                 else if (asu >= 1)  return 1;
    341                 else                return 0;
    342             }
    343 
    344             /**
    345              * Convert the cdma / evdo db levels to appropriate icon level.
    346              * The scale is similar to the one used in status bar policy.
    347              *
    348              * @param signalStrength
    349              * @return the icon level
    350              */
    351             private int cdmaDbmEcioToSignal(SignalStrength signalStrength) {
    352                 int levelDbm = 0;
    353                 int levelEcio = 0;
    354                 int cdmaIconLevel = 0;
    355                 int evdoIconLevel = 0;
    356                 int cdmaDbm = signalStrength.getCdmaDbm();
    357                 int cdmaEcio = signalStrength.getCdmaEcio();
    358 
    359                 if (cdmaDbm >= -75) levelDbm = 4;
    360                 else if (cdmaDbm >= -85) levelDbm = 3;
    361                 else if (cdmaDbm >= -95) levelDbm = 2;
    362                 else if (cdmaDbm >= -100) levelDbm = 1;
    363                 else levelDbm = 0;
    364 
    365                 // Ec/Io are in dB*10
    366                 if (cdmaEcio >= -90) levelEcio = 4;
    367                 else if (cdmaEcio >= -110) levelEcio = 3;
    368                 else if (cdmaEcio >= -130) levelEcio = 2;
    369                 else if (cdmaEcio >= -150) levelEcio = 1;
    370                 else levelEcio = 0;
    371 
    372                 cdmaIconLevel = (levelDbm < levelEcio) ? levelDbm : levelEcio;
    373 
    374                 // STOPSHIP: Change back to getRilVoiceRadioTechnology
    375                 if (mServiceState != null &&
    376                       (mServiceState.getRadioTechnology() ==
    377                           ServiceState.RIL_RADIO_TECHNOLOGY_EVDO_0 ||
    378                        mServiceState.getRadioTechnology() ==
    379                            ServiceState.RIL_RADIO_TECHNOLOGY_EVDO_A)) {
    380                       int evdoEcio = signalStrength.getEvdoEcio();
    381                       int evdoSnr = signalStrength.getEvdoSnr();
    382                       int levelEvdoEcio = 0;
    383                       int levelEvdoSnr = 0;
    384 
    385                       // Ec/Io are in dB*10
    386                       if (evdoEcio >= -650) levelEvdoEcio = 4;
    387                       else if (evdoEcio >= -750) levelEvdoEcio = 3;
    388                       else if (evdoEcio >= -900) levelEvdoEcio = 2;
    389                       else if (evdoEcio >= -1050) levelEvdoEcio = 1;
    390                       else levelEvdoEcio = 0;
    391 
    392                       if (evdoSnr > 7) levelEvdoSnr = 4;
    393                       else if (evdoSnr > 5) levelEvdoSnr = 3;
    394                       else if (evdoSnr > 3) levelEvdoSnr = 2;
    395                       else if (evdoSnr > 1) levelEvdoSnr = 1;
    396                       else levelEvdoSnr = 0;
    397 
    398                       evdoIconLevel = (levelEvdoEcio < levelEvdoSnr) ? levelEvdoEcio : levelEvdoSnr;
    399                 }
    400                 // TODO(): There is a bug open regarding what should be sent.
    401                 return (cdmaIconLevel > evdoIconLevel) ?  cdmaIconLevel : evdoIconLevel;
    402             }
    403         };
    404         return mPhoneStateListener;
    405     }
    406 
    407 }
    408 
    409 class HeadsetDeviceState {
    410     int mService;
    411     int mRoam;
    412     int mSignal;
    413     int mBatteryCharge;
    414 
    415     HeadsetDeviceState(int service, int roam, int signal, int batteryCharge) {
    416         mService = service;
    417         mRoam = roam;
    418         mSignal = signal;
    419         mBatteryCharge = batteryCharge;
    420     }
    421 }
    422 
    423 class HeadsetCallState {
    424     int mNumActive;
    425     int mNumHeld;
    426     int mCallState;
    427     String mNumber;
    428     int mType;
    429 
    430     public HeadsetCallState(int numActive, int numHeld, int callState, String number, int type) {
    431         mNumActive = numActive;
    432         mNumHeld = numHeld;
    433         mCallState = callState;
    434         mNumber = number;
    435         mType = type;
    436     }
    437 }
    438 
    439 class HeadsetClccResponse {
    440     int mIndex;
    441     int mDirection;
    442     int mStatus;
    443     int mMode;
    444     boolean mMpty;
    445     String mNumber;
    446     int mType;
    447 
    448     public HeadsetClccResponse(int index, int direction, int status, int mode, boolean mpty,
    449                                String number, int type) {
    450         mIndex = index;
    451         mDirection = direction;
    452         mStatus = status;
    453         mMode = mode;
    454         mMpty = mpty;
    455         mNumber = number;
    456         mType = type;
    457     }
    458 }
    459 
    460 class HeadsetVendorSpecificResultCode {
    461     BluetoothDevice mDevice;
    462     String mCommand;
    463     String mArg;
    464 
    465     public HeadsetVendorSpecificResultCode(BluetoothDevice device, String command, String arg) {
    466         mDevice = device;
    467         mCommand = command;
    468         mArg = arg;
    469     }
    470 }
    471