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