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