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.util.Log; 25 26 // Note: 27 // All methods in this class are not thread safe, donot call them from 28 // multiple threads. Call them from the HeadsetPhoneStateMachine message 29 // handler only. 30 class HeadsetPhoneState { 31 private static final String TAG = "HeadsetPhoneState"; 32 33 private HeadsetStateMachine mStateMachine; 34 private TelephonyManager mTelephonyManager; 35 private ServiceState mServiceState; 36 37 // HFP 1.6 CIND service 38 private int mService = HeadsetHalConstants.NETWORK_STATE_NOT_AVAILABLE; 39 40 // Number of active (foreground) calls 41 private int mNumActive = 0; 42 43 // Current Call Setup State 44 private int mCallState = HeadsetHalConstants.CALL_STATE_IDLE; 45 46 // Number of held (background) calls 47 private int mNumHeld = 0; 48 49 // HFP 1.6 CIND signal 50 private int mSignal = 0; 51 52 // HFP 1.6 CIND roam 53 private int mRoam = HeadsetHalConstants.SERVICE_TYPE_HOME; 54 55 // HFP 1.6 CIND battchg 56 private int mBatteryCharge = 0; 57 58 private int mSpeakerVolume = 0; 59 60 private int mMicVolume = 0; 61 62 private boolean mListening = false; 63 64 HeadsetPhoneState(Context context, HeadsetStateMachine stateMachine) { 65 mStateMachine = stateMachine; 66 mTelephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE); 67 } 68 69 public void cleanup() { 70 listenForPhoneState(false); 71 mTelephonyManager = null; 72 mStateMachine = null; 73 } 74 75 void listenForPhoneState(boolean start) { 76 if (start) { 77 if (!mListening) { 78 mTelephonyManager.listen(mPhoneStateListener, 79 PhoneStateListener.LISTEN_SERVICE_STATE | 80 PhoneStateListener.LISTEN_SIGNAL_STRENGTHS); 81 mListening = true; 82 } 83 } else { 84 if (mListening) { 85 mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_NONE); 86 mListening = false; 87 } 88 } 89 } 90 91 int getService() { 92 return mService; 93 } 94 95 int getNumActiveCall() { 96 return mNumActive; 97 } 98 99 void setNumActiveCall(int numActive) { 100 mNumActive = numActive; 101 } 102 103 int getCallState() { 104 return mCallState; 105 } 106 107 void setCallState(int callState) { 108 mCallState = callState; 109 } 110 111 int getNumHeldCall() { 112 return mNumHeld; 113 } 114 115 void setNumHeldCall(int numHeldCall) { 116 mNumHeld = numHeldCall; 117 } 118 119 int getSignal() { 120 return mSignal; 121 } 122 123 int getRoam() { 124 return mRoam; 125 } 126 127 void setRoam(int roam) { 128 mRoam = roam; 129 } 130 131 void setBatteryCharge(int batteryLevel) { 132 if (mBatteryCharge != batteryLevel) { 133 mBatteryCharge = batteryLevel; 134 sendDeviceStateChanged(); 135 } 136 } 137 138 int getBatteryCharge() { 139 return mBatteryCharge; 140 } 141 142 void setSpeakerVolume(int volume) { 143 mSpeakerVolume = volume; 144 } 145 146 int getSpeakerVolume() { 147 return mSpeakerVolume; 148 } 149 150 void setMicVolume(int volume) { 151 mMicVolume = volume; 152 } 153 154 int getMicVolume() { 155 return mMicVolume; 156 } 157 158 boolean isInCall() { 159 return (mNumActive >= 1); 160 } 161 162 void sendDeviceStateChanged() 163 { 164 Log.d(TAG, "sendDeviceStateChanged. mService="+ mService + 165 " mSignal="+mSignal +" mRoam="+mRoam + 166 " mBatteryCharge=" + mBatteryCharge); 167 HeadsetStateMachine sm = mStateMachine; 168 if (sm != null) { 169 sm.sendMessage(HeadsetStateMachine.DEVICE_STATE_CHANGED, 170 new HeadsetDeviceState(mService, mRoam, mSignal, mBatteryCharge)); 171 } 172 } 173 174 private PhoneStateListener mPhoneStateListener = new PhoneStateListener() { 175 @Override 176 public void onServiceStateChanged(ServiceState serviceState) { 177 mServiceState = serviceState; 178 mService = (serviceState.getState() == ServiceState.STATE_IN_SERVICE) ? 179 HeadsetHalConstants.NETWORK_STATE_AVAILABLE : 180 HeadsetHalConstants.NETWORK_STATE_NOT_AVAILABLE; 181 sendDeviceStateChanged(); 182 } 183 184 @Override 185 public void onSignalStrengthsChanged(SignalStrength signalStrength) { 186 int prevSignal = mSignal; 187 if (signalStrength.isGsm()) { 188 mSignal = gsmAsuToSignal(signalStrength); 189 } else { 190 mSignal = cdmaDbmEcioToSignal(signalStrength); 191 } 192 // network signal strength is scaled to BT 1-5 levels. 193 // This results in a lot of duplicate messages, hence this check 194 if (prevSignal != mSignal) 195 sendDeviceStateChanged(); 196 } 197 198 /* convert [0,31] ASU signal strength to the [0,5] expected by 199 * bluetooth devices. Scale is similar to status bar policy 200 */ 201 private int gsmAsuToSignal(SignalStrength signalStrength) { 202 int asu = signalStrength.getGsmSignalStrength(); 203 if (asu >= 16) return 5; 204 else if (asu >= 8) return 4; 205 else if (asu >= 4) return 3; 206 else if (asu >= 2) return 2; 207 else if (asu >= 1) return 1; 208 else return 0; 209 } 210 211 /** 212 * Convert the cdma / evdo db levels to appropriate icon level. 213 * The scale is similar to the one used in status bar policy. 214 * 215 * @param signalStrength 216 * @return the icon level 217 */ 218 private int cdmaDbmEcioToSignal(SignalStrength signalStrength) { 219 int levelDbm = 0; 220 int levelEcio = 0; 221 int cdmaIconLevel = 0; 222 int evdoIconLevel = 0; 223 int cdmaDbm = signalStrength.getCdmaDbm(); 224 int cdmaEcio = signalStrength.getCdmaEcio(); 225 226 if (cdmaDbm >= -75) levelDbm = 4; 227 else if (cdmaDbm >= -85) levelDbm = 3; 228 else if (cdmaDbm >= -95) levelDbm = 2; 229 else if (cdmaDbm >= -100) levelDbm = 1; 230 else levelDbm = 0; 231 232 // Ec/Io are in dB*10 233 if (cdmaEcio >= -90) levelEcio = 4; 234 else if (cdmaEcio >= -110) levelEcio = 3; 235 else if (cdmaEcio >= -130) levelEcio = 2; 236 else if (cdmaEcio >= -150) levelEcio = 1; 237 else levelEcio = 0; 238 239 cdmaIconLevel = (levelDbm < levelEcio) ? levelDbm : levelEcio; 240 241 // STOPSHIP: Change back to getRilVoiceRadioTechnology 242 if (mServiceState != null && 243 (mServiceState.getRadioTechnology() == 244 ServiceState.RIL_RADIO_TECHNOLOGY_EVDO_0 || 245 mServiceState.getRadioTechnology() == 246 ServiceState.RIL_RADIO_TECHNOLOGY_EVDO_A)) { 247 int evdoEcio = signalStrength.getEvdoEcio(); 248 int evdoSnr = signalStrength.getEvdoSnr(); 249 int levelEvdoEcio = 0; 250 int levelEvdoSnr = 0; 251 252 // Ec/Io are in dB*10 253 if (evdoEcio >= -650) levelEvdoEcio = 4; 254 else if (evdoEcio >= -750) levelEvdoEcio = 3; 255 else if (evdoEcio >= -900) levelEvdoEcio = 2; 256 else if (evdoEcio >= -1050) levelEvdoEcio = 1; 257 else levelEvdoEcio = 0; 258 259 if (evdoSnr > 7) levelEvdoSnr = 4; 260 else if (evdoSnr > 5) levelEvdoSnr = 3; 261 else if (evdoSnr > 3) levelEvdoSnr = 2; 262 else if (evdoSnr > 1) levelEvdoSnr = 1; 263 else levelEvdoSnr = 0; 264 265 evdoIconLevel = (levelEvdoEcio < levelEvdoSnr) ? levelEvdoEcio : levelEvdoSnr; 266 } 267 // TODO(): There is a bug open regarding what should be sent. 268 return (cdmaIconLevel > evdoIconLevel) ? cdmaIconLevel : evdoIconLevel; 269 } 270 }; 271 272 } 273 274 class HeadsetDeviceState { 275 int mService; 276 int mRoam; 277 int mSignal; 278 int mBatteryCharge; 279 280 HeadsetDeviceState(int service, int roam, int signal, int batteryCharge) { 281 mService = service; 282 mRoam = roam; 283 mSignal = signal; 284 mBatteryCharge = batteryCharge; 285 } 286 } 287 288 class HeadsetCallState { 289 int mNumActive; 290 int mNumHeld; 291 int mCallState; 292 String mNumber; 293 int mType; 294 295 public HeadsetCallState(int numActive, int numHeld, int callState, String number, int type) { 296 mNumActive = numActive; 297 mNumHeld = numHeld; 298 mCallState = callState; 299 mNumber = number; 300 mType = type; 301 } 302 } 303 304 class HeadsetClccResponse { 305 int mIndex; 306 int mDirection; 307 int mStatus; 308 int mMode; 309 boolean mMpty; 310 String mNumber; 311 int mType; 312 313 public HeadsetClccResponse(int index, int direction, int status, int mode, boolean mpty, 314 String number, int type) { 315 mIndex = index; 316 mDirection = direction; 317 mStatus = status; 318 mMode = mode; 319 mMpty = mpty; 320 mNumber = number; 321 mType = type; 322 } 323 } 324