HeadsetPhoneState.java revision 9cc2dbd938b51007b8d6214672d257dc49f6b618
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 17package com.android.bluetooth.hfp; 18 19import android.bluetooth.BluetoothDevice; 20import android.content.BroadcastReceiver; 21import android.content.Context; 22import android.content.Intent; 23import android.content.IntentFilter; 24import android.telephony.PhoneStateListener; 25import android.telephony.ServiceState; 26import android.telephony.SignalStrength; 27import android.telephony.TelephonyManager; 28import android.telephony.SubscriptionManager; 29import android.telephony.SubscriptionManager.OnSubscriptionsChangedListener; 30import android.util.Log; 31 32import com.android.internal.telephony.IccCardConstants; 33import com.android.internal.telephony.TelephonyIntents; 34 35 36// Note: 37// All methods in this class are not thread safe, donot call them from 38// multiple threads. Call them from the HeadsetPhoneStateMachine message 39// handler only. 40class HeadsetPhoneState { 41 private static final String TAG = "HeadsetPhoneState"; 42 43 private HeadsetStateMachine mStateMachine; 44 private TelephonyManager mTelephonyManager; 45 private ServiceState mServiceState; 46 47 // HFP 1.6 CIND service 48 private int mService = HeadsetHalConstants.NETWORK_STATE_NOT_AVAILABLE; 49 50 // Number of active (foreground) calls 51 private int mNumActive = 0; 52 53 // Current Call Setup State 54 private int mCallState = HeadsetHalConstants.CALL_STATE_IDLE; 55 56 // Number of held (background) calls 57 private int mNumHeld = 0; 58 59 // HFP 1.6 CIND signal 60 private int mSignal = 0; 61 62 // HFP 1.6 CIND roam 63 private int mRoam = HeadsetHalConstants.SERVICE_TYPE_HOME; 64 65 // HFP 1.6 CIND battchg 66 private int mBatteryCharge = 0; 67 68 private int mSpeakerVolume = 0; 69 70 private int mMicVolume = 0; 71 72 private boolean mListening = false; 73 74 // when HFP Service Level Connection is established 75 private boolean mSlcReady = false; 76 77 private Context mContext = null; 78 79 private PhoneStateListener mPhoneStateListener = null; 80 81 private SubscriptionManager mSubMgr; 82 83 private OnSubscriptionsChangedListener mOnSubscriptionsChangedListener = 84 new OnSubscriptionsChangedListener() { 85 @Override 86 public void onSubscriptionsChanged() { 87 listenForPhoneState(false); 88 listenForPhoneState(true); 89 } 90 }; 91 92 93 HeadsetPhoneState(Context context, HeadsetStateMachine stateMachine) { 94 mStateMachine = stateMachine; 95 mTelephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE); 96 if (mTelephonyManager == null) { 97 Log.e(TAG, "getSystemService(Context.TELEPHONY_SERVICE) failed, " 98 + "cannot register for SubscriptionInfo changes"); 99 } 100 mContext = context; 101 102 // Register for SubscriptionInfo list changes which is guaranteed 103 // to invoke onSubscriptionInfoChanged and which in turns calls 104 // loadInBackgroud. 105 mSubMgr = SubscriptionManager.from(mContext); 106 mSubMgr.addOnSubscriptionsChangedListener(mOnSubscriptionsChangedListener); 107 } 108 109 public void cleanup() { 110 listenForPhoneState(false); 111 mSubMgr.removeOnSubscriptionsChangedListener(mOnSubscriptionsChangedListener); 112 113 mTelephonyManager = null; 114 mStateMachine = null; 115 } 116 117 void listenForPhoneState(boolean start) { 118 119 mSlcReady = start; 120 121 if (start) { 122 startListenForPhoneState(); 123 } else { 124 stopListenForPhoneState(); 125 } 126 127 } 128 129 private void startListenForPhoneState() { 130 if (!mListening && mSlcReady && mTelephonyManager != null) { 131 132 int subId = SubscriptionManager.getDefaultSubscriptionId(); 133 134 if (SubscriptionManager.isValidSubscriptionId(subId)) { 135 mPhoneStateListener = getPhoneStateListener(subId); 136 if (mTelephonyManager == null) { 137 Log.e(TAG, "mTelephonyManager is null, " 138 + "cannot start listening for phone state changes"); 139 } else { 140 mTelephonyManager.listen(mPhoneStateListener, 141 PhoneStateListener.LISTEN_SERVICE_STATE | 142 PhoneStateListener.LISTEN_SIGNAL_STRENGTHS); 143 mListening = true; 144 } 145 } 146 } 147 } 148 149 private void stopListenForPhoneState() { 150 if (mListening && mTelephonyManager != null) { 151 152 if (mTelephonyManager == null) { 153 Log.e(TAG, "mTelephonyManager is null, " 154 + "cannot send request to stop listening for phone state changes"); 155 } else { 156 mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_NONE); 157 mListening = false; 158 } 159 } 160 } 161 162 int getService() { 163 return mService; 164 } 165 166 int getNumActiveCall() { 167 return mNumActive; 168 } 169 170 void setNumActiveCall(int numActive) { 171 mNumActive = numActive; 172 } 173 174 int getCallState() { 175 return mCallState; 176 } 177 178 void setCallState(int callState) { 179 mCallState = callState; 180 } 181 182 int getNumHeldCall() { 183 return mNumHeld; 184 } 185 186 void setNumHeldCall(int numHeldCall) { 187 mNumHeld = numHeldCall; 188 } 189 190 int getSignal() { 191 return mSignal; 192 } 193 194 int getRoam() { 195 return mRoam; 196 } 197 198 void setRoam(int roam) { 199 if (mRoam != roam) { 200 mRoam = roam; 201 sendDeviceStateChanged(); 202 } 203 } 204 205 void setBatteryCharge(int batteryLevel) { 206 if (mBatteryCharge != batteryLevel) { 207 mBatteryCharge = batteryLevel; 208 sendDeviceStateChanged(); 209 } 210 } 211 212 int getBatteryCharge() { 213 return mBatteryCharge; 214 } 215 216 void setSpeakerVolume(int volume) { 217 mSpeakerVolume = volume; 218 } 219 220 int getSpeakerVolume() { 221 return mSpeakerVolume; 222 } 223 224 void setMicVolume(int volume) { 225 mMicVolume = volume; 226 } 227 228 int getMicVolume() { 229 return mMicVolume; 230 } 231 232 boolean isInCall() { 233 return (mNumActive >= 1); 234 } 235 236 void sendDeviceStateChanged() 237 { 238 // When out of service, send signal strength as 0. Some devices don't 239 // use the service indicator, but only the signal indicator 240 int signal = mService == HeadsetHalConstants.NETWORK_STATE_AVAILABLE ? mSignal : 0; 241 242 Log.d(TAG, "sendDeviceStateChanged. mService="+ mService + 243 " mSignal=" + signal +" mRoam="+ mRoam + 244 " mBatteryCharge=" + mBatteryCharge); 245 HeadsetStateMachine sm = mStateMachine; 246 if (sm != null) { 247 sm.sendMessage(HeadsetStateMachine.DEVICE_STATE_CHANGED, 248 new HeadsetDeviceState(mService, mRoam, signal, mBatteryCharge)); 249 } 250 } 251 252 private PhoneStateListener getPhoneStateListener(int subId) { 253 PhoneStateListener mPhoneStateListener = new PhoneStateListener(subId) { 254 @Override 255 public void onServiceStateChanged(ServiceState serviceState) { 256 mServiceState = serviceState; 257 int newService = (serviceState.getState() == ServiceState.STATE_IN_SERVICE) ? 258 HeadsetHalConstants.NETWORK_STATE_AVAILABLE : 259 HeadsetHalConstants.NETWORK_STATE_NOT_AVAILABLE; 260 int newRoam = serviceState.getRoaming() ? HeadsetHalConstants.SERVICE_TYPE_ROAMING 261 : HeadsetHalConstants.SERVICE_TYPE_HOME; 262 263 if (newService == mService && newRoam == mRoam) { 264 // Debounce the state change 265 return; 266 } 267 mService = newService; 268 mRoam = newRoam; 269 270 // If this is due to a SIM insertion, we want to defer sending device state changed 271 // until all the SIM config is loaded. 272 if (newService == HeadsetHalConstants.NETWORK_STATE_NOT_AVAILABLE) { 273 sendDeviceStateChanged(); 274 return; 275 } 276 IntentFilter simStateChangedFilter = 277 new IntentFilter(TelephonyIntents.ACTION_SIM_STATE_CHANGED); 278 mContext.registerReceiver(new BroadcastReceiver() { 279 @Override 280 public void onReceive(Context context, Intent intent) { 281 if (TelephonyIntents.ACTION_SIM_STATE_CHANGED.equals(intent.getAction())) { 282 // This is a sticky broadcast, so if it's already been loaded, 283 // this'll execute immediately. 284 if (IccCardConstants.INTENT_VALUE_ICC_LOADED.equals( 285 intent.getStringExtra(IccCardConstants.INTENT_KEY_ICC_STATE))) { 286 sendDeviceStateChanged(); 287 mContext.unregisterReceiver(this); 288 } 289 } 290 } 291 }, simStateChangedFilter); 292 } 293 294 @Override 295 public void onSignalStrengthsChanged(SignalStrength signalStrength) { 296 297 int prevSignal = mSignal; 298 if (mService == HeadsetHalConstants.NETWORK_STATE_NOT_AVAILABLE) { 299 mSignal = 0; 300 } else if (signalStrength.isGsm()) { 301 mSignal = signalStrength.getLteLevel(); 302 if (mSignal == SignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN) { 303 mSignal = gsmAsuToSignal(signalStrength); 304 } else { 305 // SignalStrength#getLteLevel returns the scale from 0-4 306 // Bluetooth signal scales at 0-5 307 // Let's match up the larger side 308 mSignal++; 309 } 310 } else { 311 mSignal = cdmaDbmEcioToSignal(signalStrength); 312 } 313 314 // network signal strength is scaled to BT 1-5 levels. 315 // This results in a lot of duplicate messages, hence this check 316 if (prevSignal != mSignal) { 317 sendDeviceStateChanged(); 318 } 319 } 320 321 /* convert [0,31] ASU signal strength to the [0,5] expected by 322 * bluetooth devices. Scale is similar to status bar policy 323 */ 324 private int gsmAsuToSignal(SignalStrength signalStrength) { 325 int asu = signalStrength.getGsmSignalStrength(); 326 if (asu == 99) return 0; 327 else if (asu >= 16) return 5; 328 else if (asu >= 8) return 4; 329 else if (asu >= 4) return 3; 330 else if (asu >= 2) return 2; 331 else if (asu >= 1) return 1; 332 else return 0; 333 } 334 335 /** 336 * Convert the cdma / evdo db levels to appropriate icon level. 337 * The scale is similar to the one used in status bar policy. 338 * 339 * @param signalStrength 340 * @return the icon level 341 */ 342 private int cdmaDbmEcioToSignal(SignalStrength signalStrength) { 343 int levelDbm = 0; 344 int levelEcio = 0; 345 int cdmaIconLevel = 0; 346 int evdoIconLevel = 0; 347 int cdmaDbm = signalStrength.getCdmaDbm(); 348 int cdmaEcio = signalStrength.getCdmaEcio(); 349 350 if (cdmaDbm >= -75) levelDbm = 4; 351 else if (cdmaDbm >= -85) levelDbm = 3; 352 else if (cdmaDbm >= -95) levelDbm = 2; 353 else if (cdmaDbm >= -100) levelDbm = 1; 354 else levelDbm = 0; 355 356 // Ec/Io are in dB*10 357 if (cdmaEcio >= -90) levelEcio = 4; 358 else if (cdmaEcio >= -110) levelEcio = 3; 359 else if (cdmaEcio >= -130) levelEcio = 2; 360 else if (cdmaEcio >= -150) levelEcio = 1; 361 else levelEcio = 0; 362 363 cdmaIconLevel = (levelDbm < levelEcio) ? levelDbm : levelEcio; 364 365 // STOPSHIP: Change back to getRilVoiceRadioTechnology 366 if (mServiceState != null && 367 (mServiceState.getRadioTechnology() == 368 ServiceState.RIL_RADIO_TECHNOLOGY_EVDO_0 || 369 mServiceState.getRadioTechnology() == 370 ServiceState.RIL_RADIO_TECHNOLOGY_EVDO_A)) { 371 int evdoEcio = signalStrength.getEvdoEcio(); 372 int evdoSnr = signalStrength.getEvdoSnr(); 373 int levelEvdoEcio = 0; 374 int levelEvdoSnr = 0; 375 376 // Ec/Io are in dB*10 377 if (evdoEcio >= -650) levelEvdoEcio = 4; 378 else if (evdoEcio >= -750) levelEvdoEcio = 3; 379 else if (evdoEcio >= -900) levelEvdoEcio = 2; 380 else if (evdoEcio >= -1050) levelEvdoEcio = 1; 381 else levelEvdoEcio = 0; 382 383 if (evdoSnr > 7) levelEvdoSnr = 4; 384 else if (evdoSnr > 5) levelEvdoSnr = 3; 385 else if (evdoSnr > 3) levelEvdoSnr = 2; 386 else if (evdoSnr > 1) levelEvdoSnr = 1; 387 else levelEvdoSnr = 0; 388 389 evdoIconLevel = (levelEvdoEcio < levelEvdoSnr) ? levelEvdoEcio : levelEvdoSnr; 390 } 391 // TODO(): There is a bug open regarding what should be sent. 392 return (cdmaIconLevel > evdoIconLevel) ? cdmaIconLevel : evdoIconLevel; 393 } 394 }; 395 return mPhoneStateListener; 396 } 397 398} 399 400class HeadsetDeviceState { 401 int mService; 402 int mRoam; 403 int mSignal; 404 int mBatteryCharge; 405 406 HeadsetDeviceState(int service, int roam, int signal, int batteryCharge) { 407 mService = service; 408 mRoam = roam; 409 mSignal = signal; 410 mBatteryCharge = batteryCharge; 411 } 412} 413 414class HeadsetCallState { 415 int mNumActive; 416 int mNumHeld; 417 int mCallState; 418 String mNumber; 419 int mType; 420 421 public HeadsetCallState(int numActive, int numHeld, int callState, String number, int type) { 422 mNumActive = numActive; 423 mNumHeld = numHeld; 424 mCallState = callState; 425 mNumber = number; 426 mType = type; 427 } 428} 429 430class HeadsetClccResponse { 431 int mIndex; 432 int mDirection; 433 int mStatus; 434 int mMode; 435 boolean mMpty; 436 String mNumber; 437 int mType; 438 439 public HeadsetClccResponse(int index, int direction, int status, int mode, boolean mpty, 440 String number, int type) { 441 mIndex = index; 442 mDirection = direction; 443 mStatus = status; 444 mMode = mode; 445 mMpty = mpty; 446 mNumber = number; 447 mType = type; 448 } 449} 450 451class HeadsetVendorSpecificResultCode { 452 BluetoothDevice mDevice; 453 String mCommand; 454 String mArg; 455 456 public HeadsetVendorSpecificResultCode(BluetoothDevice device, String command, String arg) { 457 mDevice = device; 458 mCommand = command; 459 mArg = arg; 460 } 461} 462