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.util.Log; 25 import android.bluetooth.BluetoothDevice; 26 27 28 // Note: 29 // All methods in this class are not thread safe, donot call them from 30 // multiple threads. Call them from the HeadsetPhoneStateMachine message 31 // handler only. 32 class HeadsetPhoneState { 33 private static final String TAG = "HeadsetPhoneState"; 34 35 private HeadsetStateMachine mStateMachine; 36 private TelephonyManager mTelephonyManager; 37 private ServiceState mServiceState; 38 39 // HFP 1.6 CIND service 40 private int mService = HeadsetHalConstants.NETWORK_STATE_NOT_AVAILABLE; 41 42 // Number of active (foreground) calls 43 private int mNumActive = 0; 44 45 // Current Call Setup State 46 private int mCallState = HeadsetHalConstants.CALL_STATE_IDLE; 47 48 // Number of held (background) calls 49 private int mNumHeld = 0; 50 51 // HFP 1.6 CIND signal 52 private int mSignal = 0; 53 54 // HFP 1.6 CIND roam 55 private int mRoam = HeadsetHalConstants.SERVICE_TYPE_HOME; 56 57 // HFP 1.6 CIND battchg 58 private int mBatteryCharge = 0; 59 60 private int mSpeakerVolume = 0; 61 62 private int mMicVolume = 0; 63 64 private boolean mListening = false; 65 66 HeadsetPhoneState(Context context, HeadsetStateMachine stateMachine) { 67 mStateMachine = stateMachine; 68 mTelephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE); 69 } 70 71 public void cleanup() { 72 listenForPhoneState(false); 73 mTelephonyManager = null; 74 mStateMachine = null; 75 } 76 77 void listenForPhoneState(boolean start) { 78 if (start) { 79 if (!mListening) { 80 mTelephonyManager.listen(mPhoneStateListener, 81 PhoneStateListener.LISTEN_SERVICE_STATE | 82 PhoneStateListener.LISTEN_SIGNAL_STRENGTHS); 83 mListening = true; 84 } 85 } else { 86 if (mListening) { 87 mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_NONE); 88 mListening = false; 89 } 90 } 91 } 92 93 int getService() { 94 return mService; 95 } 96 97 int getNumActiveCall() { 98 return mNumActive; 99 } 100 101 void setNumActiveCall(int numActive) { 102 mNumActive = numActive; 103 } 104 105 int getCallState() { 106 return mCallState; 107 } 108 109 void setCallState(int callState) { 110 mCallState = callState; 111 } 112 113 int getNumHeldCall() { 114 return mNumHeld; 115 } 116 117 void setNumHeldCall(int numHeldCall) { 118 mNumHeld = numHeldCall; 119 } 120 121 int getSignal() { 122 return mSignal; 123 } 124 125 int getRoam() { 126 return mRoam; 127 } 128 129 void setRoam(int roam) { 130 mRoam = roam; 131 } 132 133 void setBatteryCharge(int batteryLevel) { 134 if (mBatteryCharge != batteryLevel) { 135 mBatteryCharge = batteryLevel; 136 sendDeviceStateChanged(); 137 } 138 } 139 140 int getBatteryCharge() { 141 return mBatteryCharge; 142 } 143 144 void setSpeakerVolume(int volume) { 145 mSpeakerVolume = volume; 146 } 147 148 int getSpeakerVolume() { 149 return mSpeakerVolume; 150 } 151 152 void setMicVolume(int volume) { 153 mMicVolume = volume; 154 } 155 156 int getMicVolume() { 157 return mMicVolume; 158 } 159 160 boolean isInCall() { 161 return (mNumActive >= 1); 162 } 163 164 void sendDeviceStateChanged() 165 { 166 // When out of service, send signal strength as 0. Some devices don't 167 // use the service indicator, but only the signal indicator 168 int signal = mService == HeadsetHalConstants.NETWORK_STATE_AVAILABLE ? mSignal : 0; 169 170 Log.d(TAG, "sendDeviceStateChanged. mService="+ mService + 171 " mSignal=" + signal +" mRoam="+ mRoam + 172 " mBatteryCharge=" + mBatteryCharge); 173 HeadsetStateMachine sm = mStateMachine; 174 if (sm != null) { 175 sm.sendMessage(HeadsetStateMachine.DEVICE_STATE_CHANGED, 176 new HeadsetDeviceState(mService, mRoam, signal, mBatteryCharge)); 177 } 178 } 179 180 private PhoneStateListener mPhoneStateListener = new PhoneStateListener() { 181 @Override 182 public void onServiceStateChanged(ServiceState serviceState) { 183 mServiceState = serviceState; 184 mService = (serviceState.getState() == ServiceState.STATE_IN_SERVICE) ? 185 HeadsetHalConstants.NETWORK_STATE_AVAILABLE : 186 HeadsetHalConstants.NETWORK_STATE_NOT_AVAILABLE; 187 setRoam(serviceState.getRoaming() ? HeadsetHalConstants.SERVICE_TYPE_ROAMING 188 : HeadsetHalConstants.SERVICE_TYPE_HOME); 189 sendDeviceStateChanged(); 190 } 191 192 @Override 193 public void onSignalStrengthsChanged(SignalStrength signalStrength) { 194 int prevSignal = mSignal; 195 if (mService == HeadsetHalConstants.NETWORK_STATE_NOT_AVAILABLE) 196 mSignal = 0; 197 else if (signalStrength.isGsm()) { 198 mSignal = gsmAsuToSignal(signalStrength); 199 } else { 200 mSignal = cdmaDbmEcioToSignal(signalStrength); 201 } 202 // network signal strength is scaled to BT 1-5 levels. 203 // This results in a lot of duplicate messages, hence this check 204 if (prevSignal != mSignal) 205 sendDeviceStateChanged(); 206 } 207 208 /* convert [0,31] ASU signal strength to the [0,5] expected by 209 * bluetooth devices. Scale is similar to status bar policy 210 */ 211 private int gsmAsuToSignal(SignalStrength signalStrength) { 212 int asu = signalStrength.getGsmSignalStrength(); 213 if (asu >= 16) return 5; 214 else if (asu >= 8) return 4; 215 else if (asu >= 4) return 3; 216 else if (asu >= 2) return 2; 217 else if (asu >= 1) return 1; 218 else return 0; 219 } 220 221 /** 222 * Convert the cdma / evdo db levels to appropriate icon level. 223 * The scale is similar to the one used in status bar policy. 224 * 225 * @param signalStrength 226 * @return the icon level 227 */ 228 private int cdmaDbmEcioToSignal(SignalStrength signalStrength) { 229 int levelDbm = 0; 230 int levelEcio = 0; 231 int cdmaIconLevel = 0; 232 int evdoIconLevel = 0; 233 int cdmaDbm = signalStrength.getCdmaDbm(); 234 int cdmaEcio = signalStrength.getCdmaEcio(); 235 236 if (cdmaDbm >= -75) levelDbm = 4; 237 else if (cdmaDbm >= -85) levelDbm = 3; 238 else if (cdmaDbm >= -95) levelDbm = 2; 239 else if (cdmaDbm >= -100) levelDbm = 1; 240 else levelDbm = 0; 241 242 // Ec/Io are in dB*10 243 if (cdmaEcio >= -90) levelEcio = 4; 244 else if (cdmaEcio >= -110) levelEcio = 3; 245 else if (cdmaEcio >= -130) levelEcio = 2; 246 else if (cdmaEcio >= -150) levelEcio = 1; 247 else levelEcio = 0; 248 249 cdmaIconLevel = (levelDbm < levelEcio) ? levelDbm : levelEcio; 250 251 // STOPSHIP: Change back to getRilVoiceRadioTechnology 252 if (mServiceState != null && 253 (mServiceState.getRadioTechnology() == 254 ServiceState.RIL_RADIO_TECHNOLOGY_EVDO_0 || 255 mServiceState.getRadioTechnology() == 256 ServiceState.RIL_RADIO_TECHNOLOGY_EVDO_A)) { 257 int evdoEcio = signalStrength.getEvdoEcio(); 258 int evdoSnr = signalStrength.getEvdoSnr(); 259 int levelEvdoEcio = 0; 260 int levelEvdoSnr = 0; 261 262 // Ec/Io are in dB*10 263 if (evdoEcio >= -650) levelEvdoEcio = 4; 264 else if (evdoEcio >= -750) levelEvdoEcio = 3; 265 else if (evdoEcio >= -900) levelEvdoEcio = 2; 266 else if (evdoEcio >= -1050) levelEvdoEcio = 1; 267 else levelEvdoEcio = 0; 268 269 if (evdoSnr > 7) levelEvdoSnr = 4; 270 else if (evdoSnr > 5) levelEvdoSnr = 3; 271 else if (evdoSnr > 3) levelEvdoSnr = 2; 272 else if (evdoSnr > 1) levelEvdoSnr = 1; 273 else levelEvdoSnr = 0; 274 275 evdoIconLevel = (levelEvdoEcio < levelEvdoSnr) ? levelEvdoEcio : levelEvdoSnr; 276 } 277 // TODO(): There is a bug open regarding what should be sent. 278 return (cdmaIconLevel > evdoIconLevel) ? cdmaIconLevel : evdoIconLevel; 279 } 280 }; 281 282 } 283 284 class HeadsetDeviceState { 285 int mService; 286 int mRoam; 287 int mSignal; 288 int mBatteryCharge; 289 290 HeadsetDeviceState(int service, int roam, int signal, int batteryCharge) { 291 mService = service; 292 mRoam = roam; 293 mSignal = signal; 294 mBatteryCharge = batteryCharge; 295 } 296 } 297 298 class HeadsetCallState { 299 int mNumActive; 300 int mNumHeld; 301 int mCallState; 302 String mNumber; 303 int mType; 304 305 public HeadsetCallState(int numActive, int numHeld, int callState, String number, int type) { 306 mNumActive = numActive; 307 mNumHeld = numHeld; 308 mCallState = callState; 309 mNumber = number; 310 mType = type; 311 } 312 } 313 314 class HeadsetClccResponse { 315 int mIndex; 316 int mDirection; 317 int mStatus; 318 int mMode; 319 boolean mMpty; 320 String mNumber; 321 int mType; 322 323 public HeadsetClccResponse(int index, int direction, int status, int mode, boolean mpty, 324 String number, int type) { 325 mIndex = index; 326 mDirection = direction; 327 mStatus = status; 328 mMode = mode; 329 mMpty = mpty; 330 mNumber = number; 331 mType = type; 332 } 333 } 334 335 class HeadsetVendorSpecificResultCode { 336 BluetoothDevice mDevice; 337 String mCommand; 338 String mArg; 339 340 public HeadsetVendorSpecificResultCode(BluetoothDevice device, String command, String arg) { 341 mDevice = device; 342 mCommand = command; 343 mArg = arg; 344 } 345 } 346