HeadsetPhoneState.java revision 13e1a71bf0cf955ac39390fd96fd3f04e1954ef6
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.registerOnSubscriptionsChangedListener(mOnSubscriptionsChangedListener);
97    }
98
99    public void cleanup() {
100        listenForPhoneState(false);
101        mSubMgr.unregisterOnSubscriptionsChangedListener(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) {
121
122            int subId = SubscriptionManager.getDefaultSubId();
123
124            if (SubscriptionManager.isValidSubId(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) {
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        mRoam = roam;
181    }
182
183    void setBatteryCharge(int batteryLevel) {
184        if (mBatteryCharge != batteryLevel) {
185            mBatteryCharge = batteryLevel;
186            sendDeviceStateChanged();
187        }
188    }
189
190    int getBatteryCharge() {
191        return mBatteryCharge;
192    }
193
194    void setSpeakerVolume(int volume) {
195        mSpeakerVolume = volume;
196    }
197
198    int getSpeakerVolume() {
199        return mSpeakerVolume;
200    }
201
202    void setMicVolume(int volume) {
203        mMicVolume = volume;
204    }
205
206    int getMicVolume() {
207        return mMicVolume;
208    }
209
210    boolean isInCall() {
211        return (mNumActive >= 1);
212    }
213
214    void sendDeviceStateChanged()
215    {
216        // When out of service, send signal strength as 0. Some devices don't
217        // use the service indicator, but only the signal indicator
218        int signal = mService == HeadsetHalConstants.NETWORK_STATE_AVAILABLE ? mSignal : 0;
219
220        Log.d(TAG, "sendDeviceStateChanged. mService="+ mService +
221                   " mSignal=" + signal +" mRoam="+ mRoam +
222                   " mBatteryCharge=" + mBatteryCharge);
223        HeadsetStateMachine sm = mStateMachine;
224        if (sm != null) {
225            sm.sendMessage(HeadsetStateMachine.DEVICE_STATE_CHANGED,
226                new HeadsetDeviceState(mService, mRoam, signal, mBatteryCharge));
227        }
228    }
229
230    private PhoneStateListener getPhoneStateListener(int subId) {
231        PhoneStateListener mPhoneStateListener = new PhoneStateListener(subId) {
232            @Override
233            public void onServiceStateChanged(ServiceState serviceState) {
234
235                mServiceState = serviceState;
236                mService = (serviceState.getState() == ServiceState.STATE_IN_SERVICE) ?
237                    HeadsetHalConstants.NETWORK_STATE_AVAILABLE :
238                    HeadsetHalConstants.NETWORK_STATE_NOT_AVAILABLE;
239                setRoam(serviceState.getRoaming() ? HeadsetHalConstants.SERVICE_TYPE_ROAMING
240                                                  : HeadsetHalConstants.SERVICE_TYPE_HOME);
241
242                sendDeviceStateChanged();
243            }
244
245            @Override
246            public void onSignalStrengthsChanged(SignalStrength signalStrength) {
247
248                int prevSignal = mSignal;
249                if (mService == HeadsetHalConstants.NETWORK_STATE_NOT_AVAILABLE) {
250                    mSignal = 0;
251                } else if (signalStrength.isGsm()) {
252                    mSignal = gsmAsuToSignal(signalStrength);
253                } else {
254                    mSignal = cdmaDbmEcioToSignal(signalStrength);
255                }
256
257                // network signal strength is scaled to BT 1-5 levels.
258                // This results in a lot of duplicate messages, hence this check
259                if (prevSignal != mSignal) {
260                    sendDeviceStateChanged();
261                }
262            }
263
264            /* convert [0,31] ASU signal strength to the [0,5] expected by
265             * bluetooth devices. Scale is similar to status bar policy
266             */
267            private int gsmAsuToSignal(SignalStrength signalStrength) {
268                int asu = signalStrength.getGsmSignalStrength();
269                if      (asu >= 16) return 5;
270                else if (asu >= 8)  return 4;
271                else if (asu >= 4)  return 3;
272                else if (asu >= 2)  return 2;
273                else if (asu >= 1)  return 1;
274                else                return 0;
275            }
276
277            /**
278             * Convert the cdma / evdo db levels to appropriate icon level.
279             * The scale is similar to the one used in status bar policy.
280             *
281             * @param signalStrength
282             * @return the icon level
283             */
284            private int cdmaDbmEcioToSignal(SignalStrength signalStrength) {
285                int levelDbm = 0;
286                int levelEcio = 0;
287                int cdmaIconLevel = 0;
288                int evdoIconLevel = 0;
289                int cdmaDbm = signalStrength.getCdmaDbm();
290                int cdmaEcio = signalStrength.getCdmaEcio();
291
292                if (cdmaDbm >= -75) levelDbm = 4;
293                else if (cdmaDbm >= -85) levelDbm = 3;
294                else if (cdmaDbm >= -95) levelDbm = 2;
295                else if (cdmaDbm >= -100) levelDbm = 1;
296                else levelDbm = 0;
297
298                // Ec/Io are in dB*10
299                if (cdmaEcio >= -90) levelEcio = 4;
300                else if (cdmaEcio >= -110) levelEcio = 3;
301                else if (cdmaEcio >= -130) levelEcio = 2;
302                else if (cdmaEcio >= -150) levelEcio = 1;
303                else levelEcio = 0;
304
305                cdmaIconLevel = (levelDbm < levelEcio) ? levelDbm : levelEcio;
306
307                // STOPSHIP: Change back to getRilVoiceRadioTechnology
308                if (mServiceState != null &&
309                      (mServiceState.getRadioTechnology() ==
310                          ServiceState.RIL_RADIO_TECHNOLOGY_EVDO_0 ||
311                       mServiceState.getRadioTechnology() ==
312                           ServiceState.RIL_RADIO_TECHNOLOGY_EVDO_A)) {
313                      int evdoEcio = signalStrength.getEvdoEcio();
314                      int evdoSnr = signalStrength.getEvdoSnr();
315                      int levelEvdoEcio = 0;
316                      int levelEvdoSnr = 0;
317
318                      // Ec/Io are in dB*10
319                      if (evdoEcio >= -650) levelEvdoEcio = 4;
320                      else if (evdoEcio >= -750) levelEvdoEcio = 3;
321                      else if (evdoEcio >= -900) levelEvdoEcio = 2;
322                      else if (evdoEcio >= -1050) levelEvdoEcio = 1;
323                      else levelEvdoEcio = 0;
324
325                      if (evdoSnr > 7) levelEvdoSnr = 4;
326                      else if (evdoSnr > 5) levelEvdoSnr = 3;
327                      else if (evdoSnr > 3) levelEvdoSnr = 2;
328                      else if (evdoSnr > 1) levelEvdoSnr = 1;
329                      else levelEvdoSnr = 0;
330
331                      evdoIconLevel = (levelEvdoEcio < levelEvdoSnr) ? levelEvdoEcio : levelEvdoSnr;
332                }
333                // TODO(): There is a bug open regarding what should be sent.
334                return (cdmaIconLevel > evdoIconLevel) ?  cdmaIconLevel : evdoIconLevel;
335            }
336        };
337        return mPhoneStateListener;
338    }
339
340}
341
342class HeadsetDeviceState {
343    int mService;
344    int mRoam;
345    int mSignal;
346    int mBatteryCharge;
347
348    HeadsetDeviceState(int service, int roam, int signal, int batteryCharge) {
349        mService = service;
350        mRoam = roam;
351        mSignal = signal;
352        mBatteryCharge = batteryCharge;
353    }
354}
355
356class HeadsetCallState {
357    int mNumActive;
358    int mNumHeld;
359    int mCallState;
360    String mNumber;
361    int mType;
362
363    public HeadsetCallState(int numActive, int numHeld, int callState, String number, int type) {
364        mNumActive = numActive;
365        mNumHeld = numHeld;
366        mCallState = callState;
367        mNumber = number;
368        mType = type;
369    }
370}
371
372class HeadsetClccResponse {
373    int mIndex;
374    int mDirection;
375    int mStatus;
376    int mMode;
377    boolean mMpty;
378    String mNumber;
379    int mType;
380
381    public HeadsetClccResponse(int index, int direction, int status, int mode, boolean mpty,
382                               String number, int type) {
383        mIndex = index;
384        mDirection = direction;
385        mStatus = status;
386        mMode = mode;
387        mMpty = mpty;
388        mNumber = number;
389        mType = type;
390    }
391}
392
393class HeadsetVendorSpecificResultCode {
394    BluetoothDevice mDevice;
395    String mCommand;
396    String mArg;
397
398    public HeadsetVendorSpecificResultCode(BluetoothDevice device, String command, String arg) {
399        mDevice = device;
400        mCommand = command;
401        mArg = arg;
402    }
403}
404