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 && mTelephonyManager != null) {
    121 
    122             int subId = SubscriptionManager.getDefaultSubscriptionId();
    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 && mTelephonyManager != null) {
    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         if (mRoam != roam) {
    181             mRoam = roam;
    182             sendDeviceStateChanged();
    183         }
    184     }
    185 
    186     void setBatteryCharge(int batteryLevel) {
    187         if (mBatteryCharge != batteryLevel) {
    188             mBatteryCharge = batteryLevel;
    189             sendDeviceStateChanged();
    190         }
    191     }
    192 
    193     int getBatteryCharge() {
    194         return mBatteryCharge;
    195     }
    196 
    197     void setSpeakerVolume(int volume) {
    198         mSpeakerVolume = volume;
    199     }
    200 
    201     int getSpeakerVolume() {
    202         return mSpeakerVolume;
    203     }
    204 
    205     void setMicVolume(int volume) {
    206         mMicVolume = volume;
    207     }
    208 
    209     int getMicVolume() {
    210         return mMicVolume;
    211     }
    212 
    213     boolean isInCall() {
    214         return (mNumActive >= 1);
    215     }
    216 
    217     void sendDeviceStateChanged()
    218     {
    219         // When out of service, send signal strength as 0. Some devices don't
    220         // use the service indicator, but only the signal indicator
    221         int signal = mService == HeadsetHalConstants.NETWORK_STATE_AVAILABLE ? mSignal : 0;
    222 
    223         Log.d(TAG, "sendDeviceStateChanged. mService="+ mService +
    224                    " mSignal=" + signal +" mRoam="+ mRoam +
    225                    " mBatteryCharge=" + mBatteryCharge);
    226         HeadsetStateMachine sm = mStateMachine;
    227         if (sm != null) {
    228             sm.sendMessage(HeadsetStateMachine.DEVICE_STATE_CHANGED,
    229                 new HeadsetDeviceState(mService, mRoam, signal, mBatteryCharge));
    230         }
    231     }
    232 
    233     private PhoneStateListener getPhoneStateListener(int subId) {
    234         PhoneStateListener mPhoneStateListener = new PhoneStateListener(subId) {
    235             @Override
    236             public void onServiceStateChanged(ServiceState serviceState) {
    237 
    238                 mServiceState = serviceState;
    239                 mService = (serviceState.getState() == ServiceState.STATE_IN_SERVICE) ?
    240                     HeadsetHalConstants.NETWORK_STATE_AVAILABLE :
    241                     HeadsetHalConstants.NETWORK_STATE_NOT_AVAILABLE;
    242                 setRoam(serviceState.getRoaming() ? HeadsetHalConstants.SERVICE_TYPE_ROAMING
    243                                                   : HeadsetHalConstants.SERVICE_TYPE_HOME);
    244 
    245                 sendDeviceStateChanged();
    246             }
    247 
    248             @Override
    249             public void onSignalStrengthsChanged(SignalStrength signalStrength) {
    250 
    251                 int prevSignal = mSignal;
    252                 if (mService == HeadsetHalConstants.NETWORK_STATE_NOT_AVAILABLE) {
    253                     mSignal = 0;
    254                 } else if (signalStrength.isGsm()) {
    255                     mSignal = signalStrength.getLteLevel();
    256                     if (mSignal == SignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN) {
    257                         mSignal = gsmAsuToSignal(signalStrength);
    258                     } else {
    259                         // SignalStrength#getLteLevel returns the scale from 0-4
    260                         // Bluetooth signal scales at 0-5
    261                         // Let's match up the larger side
    262                         mSignal++;
    263                     }
    264                 } else {
    265                     mSignal = cdmaDbmEcioToSignal(signalStrength);
    266                 }
    267 
    268                 // network signal strength is scaled to BT 1-5 levels.
    269                 // This results in a lot of duplicate messages, hence this check
    270                 if (prevSignal != mSignal) {
    271                     sendDeviceStateChanged();
    272                 }
    273             }
    274 
    275             /* convert [0,31] ASU signal strength to the [0,5] expected by
    276              * bluetooth devices. Scale is similar to status bar policy
    277              */
    278             private int gsmAsuToSignal(SignalStrength signalStrength) {
    279                 int asu = signalStrength.getGsmSignalStrength();
    280                 if      (asu >= 16) return 5;
    281                 else if (asu >= 8)  return 4;
    282                 else if (asu >= 4)  return 3;
    283                 else if (asu >= 2)  return 2;
    284                 else if (asu >= 1)  return 1;
    285                 else                return 0;
    286             }
    287 
    288             /**
    289              * Convert the cdma / evdo db levels to appropriate icon level.
    290              * The scale is similar to the one used in status bar policy.
    291              *
    292              * @param signalStrength
    293              * @return the icon level
    294              */
    295             private int cdmaDbmEcioToSignal(SignalStrength signalStrength) {
    296                 int levelDbm = 0;
    297                 int levelEcio = 0;
    298                 int cdmaIconLevel = 0;
    299                 int evdoIconLevel = 0;
    300                 int cdmaDbm = signalStrength.getCdmaDbm();
    301                 int cdmaEcio = signalStrength.getCdmaEcio();
    302 
    303                 if (cdmaDbm >= -75) levelDbm = 4;
    304                 else if (cdmaDbm >= -85) levelDbm = 3;
    305                 else if (cdmaDbm >= -95) levelDbm = 2;
    306                 else if (cdmaDbm >= -100) levelDbm = 1;
    307                 else levelDbm = 0;
    308 
    309                 // Ec/Io are in dB*10
    310                 if (cdmaEcio >= -90) levelEcio = 4;
    311                 else if (cdmaEcio >= -110) levelEcio = 3;
    312                 else if (cdmaEcio >= -130) levelEcio = 2;
    313                 else if (cdmaEcio >= -150) levelEcio = 1;
    314                 else levelEcio = 0;
    315 
    316                 cdmaIconLevel = (levelDbm < levelEcio) ? levelDbm : levelEcio;
    317 
    318                 // STOPSHIP: Change back to getRilVoiceRadioTechnology
    319                 if (mServiceState != null &&
    320                       (mServiceState.getRadioTechnology() ==
    321                           ServiceState.RIL_RADIO_TECHNOLOGY_EVDO_0 ||
    322                        mServiceState.getRadioTechnology() ==
    323                            ServiceState.RIL_RADIO_TECHNOLOGY_EVDO_A)) {
    324                       int evdoEcio = signalStrength.getEvdoEcio();
    325                       int evdoSnr = signalStrength.getEvdoSnr();
    326                       int levelEvdoEcio = 0;
    327                       int levelEvdoSnr = 0;
    328 
    329                       // Ec/Io are in dB*10
    330                       if (evdoEcio >= -650) levelEvdoEcio = 4;
    331                       else if (evdoEcio >= -750) levelEvdoEcio = 3;
    332                       else if (evdoEcio >= -900) levelEvdoEcio = 2;
    333                       else if (evdoEcio >= -1050) levelEvdoEcio = 1;
    334                       else levelEvdoEcio = 0;
    335 
    336                       if (evdoSnr > 7) levelEvdoSnr = 4;
    337                       else if (evdoSnr > 5) levelEvdoSnr = 3;
    338                       else if (evdoSnr > 3) levelEvdoSnr = 2;
    339                       else if (evdoSnr > 1) levelEvdoSnr = 1;
    340                       else levelEvdoSnr = 0;
    341 
    342                       evdoIconLevel = (levelEvdoEcio < levelEvdoSnr) ? levelEvdoEcio : levelEvdoSnr;
    343                 }
    344                 // TODO(): There is a bug open regarding what should be sent.
    345                 return (cdmaIconLevel > evdoIconLevel) ?  cdmaIconLevel : evdoIconLevel;
    346             }
    347         };
    348         return mPhoneStateListener;
    349     }
    350 
    351 }
    352 
    353 class HeadsetDeviceState {
    354     int mService;
    355     int mRoam;
    356     int mSignal;
    357     int mBatteryCharge;
    358 
    359     HeadsetDeviceState(int service, int roam, int signal, int batteryCharge) {
    360         mService = service;
    361         mRoam = roam;
    362         mSignal = signal;
    363         mBatteryCharge = batteryCharge;
    364     }
    365 }
    366 
    367 class HeadsetCallState {
    368     int mNumActive;
    369     int mNumHeld;
    370     int mCallState;
    371     String mNumber;
    372     int mType;
    373 
    374     public HeadsetCallState(int numActive, int numHeld, int callState, String number, int type) {
    375         mNumActive = numActive;
    376         mNumHeld = numHeld;
    377         mCallState = callState;
    378         mNumber = number;
    379         mType = type;
    380     }
    381 }
    382 
    383 class HeadsetClccResponse {
    384     int mIndex;
    385     int mDirection;
    386     int mStatus;
    387     int mMode;
    388     boolean mMpty;
    389     String mNumber;
    390     int mType;
    391 
    392     public HeadsetClccResponse(int index, int direction, int status, int mode, boolean mpty,
    393                                String number, int type) {
    394         mIndex = index;
    395         mDirection = direction;
    396         mStatus = status;
    397         mMode = mode;
    398         mMpty = mpty;
    399         mNumber = number;
    400         mType = type;
    401     }
    402 }
    403 
    404 class HeadsetVendorSpecificResultCode {
    405     BluetoothDevice mDevice;
    406     String mCommand;
    407     String mArg;
    408 
    409     public HeadsetVendorSpecificResultCode(BluetoothDevice device, String command, String arg) {
    410         mDevice = device;
    411         mCommand = command;
    412         mArg = arg;
    413     }
    414 }
    415