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) { 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 stopListenForPhoneState()135 private void stopListenForPhoneState() { 136 if (mListening) { 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 mRoam = roam; 181 } 182 setBatteryCharge(int batteryLevel)183 void setBatteryCharge(int batteryLevel) { 184 if (mBatteryCharge != batteryLevel) { 185 mBatteryCharge = batteryLevel; 186 sendDeviceStateChanged(); 187 } 188 } 189 getBatteryCharge()190 int getBatteryCharge() { 191 return mBatteryCharge; 192 } 193 setSpeakerVolume(int volume)194 void setSpeakerVolume(int volume) { 195 mSpeakerVolume = volume; 196 } 197 getSpeakerVolume()198 int getSpeakerVolume() { 199 return mSpeakerVolume; 200 } 201 setMicVolume(int volume)202 void setMicVolume(int volume) { 203 mMicVolume = volume; 204 } 205 getMicVolume()206 int getMicVolume() { 207 return mMicVolume; 208 } 209 isInCall()210 boolean isInCall() { 211 return (mNumActive >= 1); 212 } 213 sendDeviceStateChanged()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 getPhoneStateListener(int subId)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 HeadsetDeviceState(int service, int roam, int signal, int batteryCharge)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 HeadsetCallState(int numActive, int numHeld, int callState, String number, int type)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 HeadsetClccResponse(int index, int direction, int status, int mode, boolean mpty, String number, int type)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 HeadsetVendorSpecificResultCode(BluetoothDevice device, String command, String arg)406 public HeadsetVendorSpecificResultCode(BluetoothDevice device, String command, String arg) { 407 mDevice = device; 408 mCommand = command; 409 mArg = arg; 410 } 411 } 412