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