• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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