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