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