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