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