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