HeadsetPhoneState.java revision 69d4ca45a25e86823fbdb754ca6a3995f8131d59
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.util.Log;
25import 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.
32class 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        Log.d(TAG, "sendDeviceStateChanged. mService="+ mService +
167                   " mSignal="+mSignal +" mRoam="+mRoam +
168                   " mBatteryCharge=" + mBatteryCharge);
169        HeadsetStateMachine sm = mStateMachine;
170        if (sm != null) {
171            sm.sendMessage(HeadsetStateMachine.DEVICE_STATE_CHANGED,
172                new HeadsetDeviceState(mService, mRoam, mSignal, mBatteryCharge));
173        }
174    }
175
176    private PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
177        @Override
178        public void onServiceStateChanged(ServiceState serviceState) {
179            mServiceState = serviceState;
180            mService = (serviceState.getState() == ServiceState.STATE_IN_SERVICE) ?
181                HeadsetHalConstants.NETWORK_STATE_AVAILABLE :
182                HeadsetHalConstants.NETWORK_STATE_NOT_AVAILABLE;
183            setRoam(serviceState.getRoaming() ? HeadsetHalConstants.SERVICE_TYPE_ROAMING
184                                              : HeadsetHalConstants.SERVICE_TYPE_HOME);
185            sendDeviceStateChanged();
186        }
187
188        @Override
189        public void onSignalStrengthsChanged(SignalStrength signalStrength) {
190            int prevSignal = mSignal;
191            if (mService == HeadsetHalConstants.NETWORK_STATE_NOT_AVAILABLE)
192                mSignal = 0;
193            else if (signalStrength.isGsm()) {
194                mSignal = gsmAsuToSignal(signalStrength);
195            } else {
196                mSignal = cdmaDbmEcioToSignal(signalStrength);
197            }
198            // network signal strength is scaled to BT 1-5 levels.
199            // This results in a lot of duplicate messages, hence this check
200            if (prevSignal != mSignal)
201                sendDeviceStateChanged();
202        }
203
204        /* convert [0,31] ASU signal strength to the [0,5] expected by
205         * bluetooth devices. Scale is similar to status bar policy
206         */
207        private int gsmAsuToSignal(SignalStrength signalStrength) {
208            int asu = signalStrength.getGsmSignalStrength();
209            if      (asu >= 16) return 5;
210            else if (asu >= 8)  return 4;
211            else if (asu >= 4)  return 3;
212            else if (asu >= 2)  return 2;
213            else if (asu >= 1)  return 1;
214            else                return 0;
215        }
216
217        /**
218         * Convert the cdma / evdo db levels to appropriate icon level.
219         * The scale is similar to the one used in status bar policy.
220         *
221         * @param signalStrength
222         * @return the icon level
223         */
224        private int cdmaDbmEcioToSignal(SignalStrength signalStrength) {
225            int levelDbm = 0;
226            int levelEcio = 0;
227            int cdmaIconLevel = 0;
228            int evdoIconLevel = 0;
229            int cdmaDbm = signalStrength.getCdmaDbm();
230            int cdmaEcio = signalStrength.getCdmaEcio();
231
232            if (cdmaDbm >= -75) levelDbm = 4;
233            else if (cdmaDbm >= -85) levelDbm = 3;
234            else if (cdmaDbm >= -95) levelDbm = 2;
235            else if (cdmaDbm >= -100) levelDbm = 1;
236            else levelDbm = 0;
237
238            // Ec/Io are in dB*10
239            if (cdmaEcio >= -90) levelEcio = 4;
240            else if (cdmaEcio >= -110) levelEcio = 3;
241            else if (cdmaEcio >= -130) levelEcio = 2;
242            else if (cdmaEcio >= -150) levelEcio = 1;
243            else levelEcio = 0;
244
245            cdmaIconLevel = (levelDbm < levelEcio) ? levelDbm : levelEcio;
246
247            // STOPSHIP: Change back to getRilVoiceRadioTechnology
248            if (mServiceState != null &&
249                  (mServiceState.getRadioTechnology() ==
250                      ServiceState.RIL_RADIO_TECHNOLOGY_EVDO_0 ||
251                   mServiceState.getRadioTechnology() ==
252                       ServiceState.RIL_RADIO_TECHNOLOGY_EVDO_A)) {
253                  int evdoEcio = signalStrength.getEvdoEcio();
254                  int evdoSnr = signalStrength.getEvdoSnr();
255                  int levelEvdoEcio = 0;
256                  int levelEvdoSnr = 0;
257
258                  // Ec/Io are in dB*10
259                  if (evdoEcio >= -650) levelEvdoEcio = 4;
260                  else if (evdoEcio >= -750) levelEvdoEcio = 3;
261                  else if (evdoEcio >= -900) levelEvdoEcio = 2;
262                  else if (evdoEcio >= -1050) levelEvdoEcio = 1;
263                  else levelEvdoEcio = 0;
264
265                  if (evdoSnr > 7) levelEvdoSnr = 4;
266                  else if (evdoSnr > 5) levelEvdoSnr = 3;
267                  else if (evdoSnr > 3) levelEvdoSnr = 2;
268                  else if (evdoSnr > 1) levelEvdoSnr = 1;
269                  else levelEvdoSnr = 0;
270
271                  evdoIconLevel = (levelEvdoEcio < levelEvdoSnr) ? levelEvdoEcio : levelEvdoSnr;
272            }
273            // TODO(): There is a bug open regarding what should be sent.
274            return (cdmaIconLevel > evdoIconLevel) ?  cdmaIconLevel : evdoIconLevel;
275        }
276    };
277
278}
279
280class HeadsetDeviceState {
281    int mService;
282    int mRoam;
283    int mSignal;
284    int mBatteryCharge;
285
286    HeadsetDeviceState(int service, int roam, int signal, int batteryCharge) {
287        mService = service;
288        mRoam = roam;
289        mSignal = signal;
290        mBatteryCharge = batteryCharge;
291    }
292}
293
294class HeadsetCallState {
295    int mNumActive;
296    int mNumHeld;
297    int mCallState;
298    String mNumber;
299    int mType;
300
301    public HeadsetCallState(int numActive, int numHeld, int callState, String number, int type) {
302        mNumActive = numActive;
303        mNumHeld = numHeld;
304        mCallState = callState;
305        mNumber = number;
306        mType = type;
307    }
308}
309
310class HeadsetClccResponse {
311    int mIndex;
312    int mDirection;
313    int mStatus;
314    int mMode;
315    boolean mMpty;
316    String mNumber;
317    int mType;
318
319    public HeadsetClccResponse(int index, int direction, int status, int mode, boolean mpty,
320                               String number, int type) {
321        mIndex = index;
322        mDirection = direction;
323        mStatus = status;
324        mMode = mode;
325        mMpty = mpty;
326        mNumber = number;
327        mType = type;
328    }
329}
330
331class HeadsetVendorSpecificResultCode {
332    BluetoothDevice mDevice;
333    String mCommand;
334    String mArg;
335
336    public HeadsetVendorSpecificResultCode(BluetoothDevice device, String command, String arg) {
337        mDevice = device;
338        mCommand = command;
339        mArg = arg;
340    }
341}
342