HeadsetPhoneState.java revision 2ca3331f43b767c108166dee9036b670cf126b88
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.content.Context; 20import android.telephony.PhoneStateListener; 21import android.telephony.ServiceState; 22import android.telephony.SignalStrength; 23import android.telephony.TelephonyManager; 24import android.telephony.SubscriptionManager; 25import android.content.IntentFilter; 26import android.content.Intent; 27import android.content.BroadcastReceiver; 28 29import com.android.internal.telephony.TelephonyIntents; 30import com.android.internal.telephony.PhoneConstants; 31 32import android.util.Log; 33import android.bluetooth.BluetoothDevice; 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 extends BroadcastReceiver{ 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 HeadsetPhoneState(Context context, HeadsetStateMachine stateMachine) { 82 mStateMachine = stateMachine; 83 mTelephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE); 84 85 IntentFilter filter = new IntentFilter(); 86 filter.addAction(TelephonyIntents.ACTION_SUBINFO_RECORD_UPDATED); 87 88 mContext = context; 89 mContext.registerReceiver(this, filter); 90 91 } 92 93 public void cleanup() { 94 mContext.unregisterReceiver(this); 95 listenForPhoneState(false); 96 mTelephonyManager = null; 97 mStateMachine = null; 98 } 99 100 @Override 101 public void onReceive(Context context, Intent intent) { 102 final String action = intent.getAction(); 103 Log.d(TAG, "onReceive, intent action = " + action); 104 105 if (action.equals(TelephonyIntents.ACTION_SUBINFO_RECORD_UPDATED)) { 106 listenForPhoneState(false); 107 listenForPhoneState(true); 108 } 109 } 110 111 void listenForPhoneState(boolean start) { 112 113 mSlcReady = start; 114 115 if (start) { 116 startListenForPhoneState(); 117 } else { 118 stopListenForPhoneState(); 119 } 120 121 } 122 123 private void startListenForPhoneState() { 124 if (!mListening && mSlcReady) { 125 126 // SUB selection, use sim1 always 127 long[] subs = SubscriptionManager.getSubId(PhoneConstants.SIM_ID_1); 128 129 if (subs != null && subs[0] >= 0) { 130 mPhoneStateListener = getPhoneStateListener(subs[0]); 131 132 mTelephonyManager.listen(mPhoneStateListener, 133 PhoneStateListener.LISTEN_SERVICE_STATE | 134 PhoneStateListener.LISTEN_SIGNAL_STRENGTHS); 135 mListening = true; 136 } 137 } 138 } 139 140 private void stopListenForPhoneState() { 141 if (mListening) { 142 143 mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_NONE); 144 mListening = false; 145 } 146 } 147 148 int getService() { 149 return mService; 150 } 151 152 int getNumActiveCall() { 153 return mNumActive; 154 } 155 156 void setNumActiveCall(int numActive) { 157 mNumActive = numActive; 158 } 159 160 int getCallState() { 161 return mCallState; 162 } 163 164 void setCallState(int callState) { 165 mCallState = callState; 166 } 167 168 int getNumHeldCall() { 169 return mNumHeld; 170 } 171 172 void setNumHeldCall(int numHeldCall) { 173 mNumHeld = numHeldCall; 174 } 175 176 int getSignal() { 177 return mSignal; 178 } 179 180 int getRoam() { 181 return mRoam; 182 } 183 184 void setRoam(int roam) { 185 mRoam = roam; 186 } 187 188 void setBatteryCharge(int batteryLevel) { 189 if (mBatteryCharge != batteryLevel) { 190 mBatteryCharge = batteryLevel; 191 sendDeviceStateChanged(); 192 } 193 } 194 195 int getBatteryCharge() { 196 return mBatteryCharge; 197 } 198 199 void setSpeakerVolume(int volume) { 200 mSpeakerVolume = volume; 201 } 202 203 int getSpeakerVolume() { 204 return mSpeakerVolume; 205 } 206 207 void setMicVolume(int volume) { 208 mMicVolume = volume; 209 } 210 211 int getMicVolume() { 212 return mMicVolume; 213 } 214 215 boolean isInCall() { 216 return (mNumActive >= 1); 217 } 218 219 void sendDeviceStateChanged() 220 { 221 // When out of service, send signal strength as 0. Some devices don't 222 // use the service indicator, but only the signal indicator 223 int signal = mService == HeadsetHalConstants.NETWORK_STATE_AVAILABLE ? mSignal : 0; 224 225 Log.d(TAG, "sendDeviceStateChanged. mService="+ mService + 226 " mSignal=" + signal +" mRoam="+ mRoam + 227 " mBatteryCharge=" + mBatteryCharge); 228 HeadsetStateMachine sm = mStateMachine; 229 if (sm != null) { 230 sm.sendMessage(HeadsetStateMachine.DEVICE_STATE_CHANGED, 231 new HeadsetDeviceState(mService, mRoam, signal, mBatteryCharge)); 232 } 233 } 234 235 private PhoneStateListener getPhoneStateListener(long subId) { 236 PhoneStateListener mPhoneStateListener = new PhoneStateListener(subId) { 237 @Override 238 public void onServiceStateChanged(ServiceState serviceState) { 239 240 mServiceState = serviceState; 241 mService = (serviceState.getState() == ServiceState.STATE_IN_SERVICE) ? 242 HeadsetHalConstants.NETWORK_STATE_AVAILABLE : 243 HeadsetHalConstants.NETWORK_STATE_NOT_AVAILABLE; 244 setRoam(serviceState.getRoaming() ? HeadsetHalConstants.SERVICE_TYPE_ROAMING 245 : HeadsetHalConstants.SERVICE_TYPE_HOME); 246 247 sendDeviceStateChanged(); 248 } 249 250 @Override 251 public void onSignalStrengthsChanged(SignalStrength signalStrength) { 252 253 int prevSignal = mSignal; 254 if (mService == HeadsetHalConstants.NETWORK_STATE_NOT_AVAILABLE) { 255 mSignal = 0; 256 } else if (signalStrength.isGsm()) { 257 mSignal = gsmAsuToSignal(signalStrength); 258 } else { 259 mSignal = cdmaDbmEcioToSignal(signalStrength); 260 } 261 262 // network signal strength is scaled to BT 1-5 levels. 263 // This results in a lot of duplicate messages, hence this check 264 if (prevSignal != mSignal) { 265 sendDeviceStateChanged(); 266 } 267 } 268 269 /* convert [0,31] ASU signal strength to the [0,5] expected by 270 * bluetooth devices. Scale is similar to status bar policy 271 */ 272 private int gsmAsuToSignal(SignalStrength signalStrength) { 273 int asu = signalStrength.getGsmSignalStrength(); 274 if (asu >= 16) return 5; 275 else if (asu >= 8) return 4; 276 else if (asu >= 4) return 3; 277 else if (asu >= 2) return 2; 278 else if (asu >= 1) return 1; 279 else return 0; 280 } 281 282 /** 283 * Convert the cdma / evdo db levels to appropriate icon level. 284 * The scale is similar to the one used in status bar policy. 285 * 286 * @param signalStrength 287 * @return the icon level 288 */ 289 private int cdmaDbmEcioToSignal(SignalStrength signalStrength) { 290 int levelDbm = 0; 291 int levelEcio = 0; 292 int cdmaIconLevel = 0; 293 int evdoIconLevel = 0; 294 int cdmaDbm = signalStrength.getCdmaDbm(); 295 int cdmaEcio = signalStrength.getCdmaEcio(); 296 297 if (cdmaDbm >= -75) levelDbm = 4; 298 else if (cdmaDbm >= -85) levelDbm = 3; 299 else if (cdmaDbm >= -95) levelDbm = 2; 300 else if (cdmaDbm >= -100) levelDbm = 1; 301 else levelDbm = 0; 302 303 // Ec/Io are in dB*10 304 if (cdmaEcio >= -90) levelEcio = 4; 305 else if (cdmaEcio >= -110) levelEcio = 3; 306 else if (cdmaEcio >= -130) levelEcio = 2; 307 else if (cdmaEcio >= -150) levelEcio = 1; 308 else levelEcio = 0; 309 310 cdmaIconLevel = (levelDbm < levelEcio) ? levelDbm : levelEcio; 311 312 // STOPSHIP: Change back to getRilVoiceRadioTechnology 313 if (mServiceState != null && 314 (mServiceState.getRadioTechnology() == 315 ServiceState.RIL_RADIO_TECHNOLOGY_EVDO_0 || 316 mServiceState.getRadioTechnology() == 317 ServiceState.RIL_RADIO_TECHNOLOGY_EVDO_A)) { 318 int evdoEcio = signalStrength.getEvdoEcio(); 319 int evdoSnr = signalStrength.getEvdoSnr(); 320 int levelEvdoEcio = 0; 321 int levelEvdoSnr = 0; 322 323 // Ec/Io are in dB*10 324 if (evdoEcio >= -650) levelEvdoEcio = 4; 325 else if (evdoEcio >= -750) levelEvdoEcio = 3; 326 else if (evdoEcio >= -900) levelEvdoEcio = 2; 327 else if (evdoEcio >= -1050) levelEvdoEcio = 1; 328 else levelEvdoEcio = 0; 329 330 if (evdoSnr > 7) levelEvdoSnr = 4; 331 else if (evdoSnr > 5) levelEvdoSnr = 3; 332 else if (evdoSnr > 3) levelEvdoSnr = 2; 333 else if (evdoSnr > 1) levelEvdoSnr = 1; 334 else levelEvdoSnr = 0; 335 336 evdoIconLevel = (levelEvdoEcio < levelEvdoSnr) ? levelEvdoEcio : levelEvdoSnr; 337 } 338 // TODO(): There is a bug open regarding what should be sent. 339 return (cdmaIconLevel > evdoIconLevel) ? cdmaIconLevel : evdoIconLevel; 340 } 341 }; 342 return mPhoneStateListener; 343 } 344 345} 346 347class HeadsetDeviceState { 348 int mService; 349 int mRoam; 350 int mSignal; 351 int mBatteryCharge; 352 353 HeadsetDeviceState(int service, int roam, int signal, int batteryCharge) { 354 mService = service; 355 mRoam = roam; 356 mSignal = signal; 357 mBatteryCharge = batteryCharge; 358 } 359} 360 361class HeadsetCallState { 362 int mNumActive; 363 int mNumHeld; 364 int mCallState; 365 String mNumber; 366 int mType; 367 368 public HeadsetCallState(int numActive, int numHeld, int callState, String number, int type) { 369 mNumActive = numActive; 370 mNumHeld = numHeld; 371 mCallState = callState; 372 mNumber = number; 373 mType = type; 374 } 375} 376 377class HeadsetClccResponse { 378 int mIndex; 379 int mDirection; 380 int mStatus; 381 int mMode; 382 boolean mMpty; 383 String mNumber; 384 int mType; 385 386 public HeadsetClccResponse(int index, int direction, int status, int mode, boolean mpty, 387 String number, int type) { 388 mIndex = index; 389 mDirection = direction; 390 mStatus = status; 391 mMode = mode; 392 mMpty = mpty; 393 mNumber = number; 394 mType = type; 395 } 396} 397 398class HeadsetVendorSpecificResultCode { 399 BluetoothDevice mDevice; 400 String mCommand; 401 String mArg; 402 403 public HeadsetVendorSpecificResultCode(BluetoothDevice device, String command, String arg) { 404 mDevice = device; 405 mCommand = command; 406 mArg = arg; 407 } 408} 409