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 HeadsetPhoneState(Context context, HeadsetStateMachine stateMachine)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 cleanup()99 public void cleanup() { 100 listenForPhoneState(false); 101 mSubMgr.removeOnSubscriptionsChangedListener(mOnSubscriptionsChangedListener); 102 103 mTelephonyManager = null; 104 mStateMachine = null; 105 } 106 listenForPhoneState(boolean start)107 void listenForPhoneState(boolean start) { 108 109 mSlcReady = start; 110 111 if (start) { 112 startListenForPhoneState(); 113 } else { 114 stopListenForPhoneState(); 115 } 116 117 } 118 startListenForPhoneState()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 stopListenForPhoneState()135 private void stopListenForPhoneState() { 136 if (mListening && mTelephonyManager != null) { 137 138 mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_NONE); 139 mListening = false; 140 } 141 } 142 getService()143 int getService() { 144 return mService; 145 } 146 getNumActiveCall()147 int getNumActiveCall() { 148 return mNumActive; 149 } 150 setNumActiveCall(int numActive)151 void setNumActiveCall(int numActive) { 152 mNumActive = numActive; 153 } 154 getCallState()155 int getCallState() { 156 return mCallState; 157 } 158 setCallState(int callState)159 void setCallState(int callState) { 160 mCallState = callState; 161 } 162 getNumHeldCall()163 int getNumHeldCall() { 164 return mNumHeld; 165 } 166 setNumHeldCall(int numHeldCall)167 void setNumHeldCall(int numHeldCall) { 168 mNumHeld = numHeldCall; 169 } 170 getSignal()171 int getSignal() { 172 return mSignal; 173 } 174 getRoam()175 int getRoam() { 176 return mRoam; 177 } 178 setRoam(int roam)179 void setRoam(int roam) { 180 if (mRoam != roam) { 181 mRoam = roam; 182 sendDeviceStateChanged(); 183 } 184 } 185 setBatteryCharge(int batteryLevel)186 void setBatteryCharge(int batteryLevel) { 187 if (mBatteryCharge != batteryLevel) { 188 mBatteryCharge = batteryLevel; 189 sendDeviceStateChanged(); 190 } 191 } 192 getBatteryCharge()193 int getBatteryCharge() { 194 return mBatteryCharge; 195 } 196 setSpeakerVolume(int volume)197 void setSpeakerVolume(int volume) { 198 mSpeakerVolume = volume; 199 } 200 getSpeakerVolume()201 int getSpeakerVolume() { 202 return mSpeakerVolume; 203 } 204 setMicVolume(int volume)205 void setMicVolume(int volume) { 206 mMicVolume = volume; 207 } 208 getMicVolume()209 int getMicVolume() { 210 return mMicVolume; 211 } 212 isInCall()213 boolean isInCall() { 214 return (mNumActive >= 1); 215 } 216 sendDeviceStateChanged()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 getPhoneStateListener(int subId)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 HeadsetDeviceState(int service, int roam, int signal, int batteryCharge)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 HeadsetCallState(int numActive, int numHeld, int callState, String number, int type)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 HeadsetClccResponse(int index, int direction, int status, int mode, boolean mpty, String number, int type)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 HeadsetVendorSpecificResultCode(BluetoothDevice device, String command, String arg)409 public HeadsetVendorSpecificResultCode(BluetoothDevice device, String command, String arg) { 410 mDevice = device; 411 mCommand = command; 412 mArg = arg; 413 } 414 } 415