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