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 HeadsetPhoneState(Context context, HeadsetStateMachine stateMachine)64 HeadsetPhoneState(Context context, HeadsetStateMachine stateMachine) { 65 mStateMachine = stateMachine; 66 mTelephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE); 67 } 68 cleanup()69 public void cleanup() { 70 listenForPhoneState(false); 71 mTelephonyManager = null; 72 mStateMachine = null; 73 } 74 listenForPhoneState(boolean start)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 getService()91 int getService() { 92 return mService; 93 } 94 getNumActiveCall()95 int getNumActiveCall() { 96 return mNumActive; 97 } 98 setNumActiveCall(int numActive)99 void setNumActiveCall(int numActive) { 100 mNumActive = numActive; 101 } 102 getCallState()103 int getCallState() { 104 return mCallState; 105 } 106 setCallState(int callState)107 void setCallState(int callState) { 108 mCallState = callState; 109 } 110 getNumHeldCall()111 int getNumHeldCall() { 112 return mNumHeld; 113 } 114 setNumHeldCall(int numHeldCall)115 void setNumHeldCall(int numHeldCall) { 116 mNumHeld = numHeldCall; 117 } 118 getSignal()119 int getSignal() { 120 return mSignal; 121 } 122 getRoam()123 int getRoam() { 124 return mRoam; 125 } 126 setRoam(int roam)127 void setRoam(int roam) { 128 mRoam = roam; 129 } 130 setBatteryCharge(int batteryLevel)131 void setBatteryCharge(int batteryLevel) { 132 if (mBatteryCharge != batteryLevel) { 133 mBatteryCharge = batteryLevel; 134 sendDeviceStateChanged(); 135 } 136 } 137 getBatteryCharge()138 int getBatteryCharge() { 139 return mBatteryCharge; 140 } 141 setSpeakerVolume(int volume)142 void setSpeakerVolume(int volume) { 143 mSpeakerVolume = volume; 144 } 145 getSpeakerVolume()146 int getSpeakerVolume() { 147 return mSpeakerVolume; 148 } 149 setMicVolume(int volume)150 void setMicVolume(int volume) { 151 mMicVolume = volume; 152 } 153 getMicVolume()154 int getMicVolume() { 155 return mMicVolume; 156 } 157 isInCall()158 boolean isInCall() { 159 return (mNumActive >= 1); 160 } 161 sendDeviceStateChanged()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 HeadsetDeviceState(int service, int roam, int signal, int batteryCharge)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 HeadsetCallState(int numActive, int numHeld, int callState, String number, int type)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 HeadsetClccResponse(int index, int direction, int status, int mode, boolean mpty, String number, int type)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 HeadsetVendorSpecificResultCode(String command, String arg)331 public HeadsetVendorSpecificResultCode(String command, String arg) { 332 mCommand = command; 333 mArg = arg; 334 } 335 } 336