1 /*
2  * Copyright (c) 2016 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 /**
18  * Bluetooth Headset Client StateMachine
19  *                      (Disconnected)
20  *                           | ^  ^
21  *                   CONNECT | |  | DISCONNECTED
22  *                           V |  |
23  *                   (Connecting) |
24  *                           |    |
25  *                 CONNECTED |    | DISCONNECT
26  *                           V    |
27  *                        (Connected)
28  *                           |    ^
29  *             CONNECT_AUDIO |    | DISCONNECT_AUDIO
30  *                           V    |
31  *                         (AudioOn)
32  */
33 
34 package com.android.bluetooth.hfpclient;
35 
36 import android.bluetooth.BluetoothAdapter;
37 import android.bluetooth.BluetoothDevice;
38 import android.bluetooth.BluetoothHeadsetClient;
39 import android.bluetooth.BluetoothHeadsetClientCall;
40 import android.bluetooth.BluetoothProfile;
41 import android.bluetooth.BluetoothUuid;
42 import android.bluetooth.hfp.BluetoothHfpProtoEnums;
43 import android.content.Intent;
44 import android.media.AudioAttributes;
45 import android.media.AudioFocusRequest;
46 import android.media.AudioManager;
47 import android.os.Bundle;
48 import android.os.Looper;
49 import android.os.Message;
50 import android.os.ParcelUuid;
51 import android.os.SystemClock;
52 import android.util.Log;
53 import android.util.Pair;
54 
55 import com.android.bluetooth.BluetoothMetricsProto;
56 import com.android.bluetooth.BluetoothStatsLog;
57 import com.android.bluetooth.R;
58 import com.android.bluetooth.Utils;
59 import com.android.bluetooth.btservice.AdapterService;
60 import com.android.bluetooth.btservice.MetricsLogger;
61 import com.android.bluetooth.btservice.ProfileService;
62 import com.android.bluetooth.statemachine.IState;
63 import com.android.bluetooth.statemachine.State;
64 import com.android.bluetooth.statemachine.StateMachine;
65 import com.android.internal.annotations.VisibleForTesting;
66 import com.android.internal.util.ArrayUtils;
67 
68 import java.util.ArrayList;
69 import java.util.Arrays;
70 import java.util.HashSet;
71 import java.util.Hashtable;
72 import java.util.LinkedList;
73 import java.util.List;
74 import java.util.Queue;
75 import java.util.Set;
76 
77 public class HeadsetClientStateMachine extends StateMachine {
78     private static final String TAG = "HeadsetClientStateMachine";
79     private static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
80 
81     static final int NO_ACTION = 0;
82     static final int IN_BAND_RING_ENABLED = 1;
83 
84     // external actions
85     public static final int AT_OK = 0;
86     public static final int CONNECT = 1;
87     public static final int DISCONNECT = 2;
88     public static final int CONNECT_AUDIO = 3;
89     public static final int DISCONNECT_AUDIO = 4;
90     public static final int VOICE_RECOGNITION_START = 5;
91     public static final int VOICE_RECOGNITION_STOP = 6;
92     public static final int SET_MIC_VOLUME = 7;
93     public static final int SET_SPEAKER_VOLUME = 8;
94     public static final int DIAL_NUMBER = 10;
95     public static final int ACCEPT_CALL = 12;
96     public static final int REJECT_CALL = 13;
97     public static final int HOLD_CALL = 14;
98     public static final int TERMINATE_CALL = 15;
99     public static final int ENTER_PRIVATE_MODE = 16;
100     public static final int SEND_DTMF = 17;
101     public static final int EXPLICIT_CALL_TRANSFER = 18;
102     public static final int DISABLE_NREC = 20;
103     public static final int SEND_VENDOR_AT_COMMAND = 21;
104 
105     // internal actions
106     private static final int QUERY_CURRENT_CALLS = 50;
107     private static final int QUERY_OPERATOR_NAME = 51;
108     private static final int SUBSCRIBER_INFO = 52;
109     private static final int CONNECTING_TIMEOUT = 53;
110 
111     // special action to handle terminating specific call from multiparty call
112     static final int TERMINATE_SPECIFIC_CALL = 53;
113 
114     // Timeouts.
115     @VisibleForTesting
116     static final int CONNECTING_TIMEOUT_MS = 10000;  // 10s
117     private static final int ROUTING_DELAY_MS = 250;
118 
119     private static final int MAX_HFP_SCO_VOICE_CALL_VOLUME = 15; // HFP 1.5 spec.
120     private static final int MIN_HFP_SCO_VOICE_CALL_VOLUME = 1; // HFP 1.5 spec.
121 
122     static final int HF_ORIGINATED_CALL_ID = -1;
123     private static final long OUTGOING_TIMEOUT_MILLI = 10 * 1000; // 10 seconds
124     private static final long QUERY_CURRENT_CALLS_WAIT_MILLIS = 2 * 1000; // 2 seconds
125 
126     // Keep track of audio routing across all devices.
127     private static boolean sAudioIsRouted = false;
128 
129     private final Disconnected mDisconnected;
130     private final Connecting mConnecting;
131     private final Connected mConnected;
132     private final AudioOn mAudioOn;
133     private State mPrevState;
134     private long mClccTimer = 0;
135 
136     private final HeadsetClientService mService;
137 
138     // Set of calls that represent the accurate state of calls that exists on AG and the calls that
139     // are currently in process of being notified to the AG from HF.
140     private final Hashtable<Integer, BluetoothHeadsetClientCall> mCalls = new Hashtable<>();
141     // Set of calls received from AG via the AT+CLCC command. We use this map to update the mCalls
142     // which is eventually used to inform the telephony stack of any changes to call on HF.
143     private final Hashtable<Integer, BluetoothHeadsetClientCall> mCallsUpdate = new Hashtable<>();
144 
145     private int mIndicatorNetworkState;
146     private int mIndicatorNetworkType;
147     private int mIndicatorNetworkSignal;
148     private int mIndicatorBatteryLevel;
149     private boolean mInBandRing;
150 
151     private String mOperatorName;
152     private String mSubscriberInfo;
153 
154     private static int sMaxAmVcVol;
155     private static int sMinAmVcVol;
156 
157     // queue of send actions (pair action, action_data)
158     private Queue<Pair<Integer, Object>> mQueuedActions;
159 
160     // last executed command, before action is complete e.g. waiting for some
161     // indicator
162     private Pair<Integer, Object> mPendingAction;
163 
164     private int mAudioState;
165     private boolean mAudioWbs;
166     private int mVoiceRecognitionActive;
167     private final BluetoothAdapter mAdapter;
168 
169     // currently connected device
170     private BluetoothDevice mCurrentDevice = null;
171 
172     // general peer features and call handling features
173     private int mPeerFeatures;
174     private int mChldFeatures;
175 
176     // This is returned when requesting focus from AudioManager
177     private AudioFocusRequest mAudioFocusRequest;
178 
179     private final AudioManager mAudioManager;
180     private final NativeInterface mNativeInterface;
181     private final VendorCommandResponseProcessor mVendorProcessor;
182 
183     // Accessor for the states, useful for reusing the state machines
getDisconnectedState()184     public IState getDisconnectedState() {
185         return mDisconnected;
186     }
187 
188     // Get if in band ring is currently enabled on device.
getInBandRing()189     public boolean getInBandRing() {
190         return mInBandRing;
191     }
192 
dump(StringBuilder sb)193     public void dump(StringBuilder sb) {
194         if (mCurrentDevice == null) return;
195         ProfileService.println(sb, "mCurrentDevice: " + mCurrentDevice.getAddress() + "("
196                 + mCurrentDevice.getName() + ") " + this.toString());
197         ProfileService.println(sb, "mAudioState: " + mAudioState);
198         ProfileService.println(sb, "mAudioWbs: " + mAudioWbs);
199         ProfileService.println(sb, "mIndicatorNetworkState: " + mIndicatorNetworkState);
200         ProfileService.println(sb, "mIndicatorNetworkType: " + mIndicatorNetworkType);
201         ProfileService.println(sb, "mIndicatorNetworkSignal: " + mIndicatorNetworkSignal);
202         ProfileService.println(sb, "mIndicatorBatteryLevel: " + mIndicatorBatteryLevel);
203         ProfileService.println(sb, "mOperatorName: " + mOperatorName);
204         ProfileService.println(sb, "mSubscriberInfo: " + mSubscriberInfo);
205 
206         ProfileService.println(sb, "mCalls:");
207         if (mCalls != null) {
208             for (BluetoothHeadsetClientCall call : mCalls.values()) {
209                 ProfileService.println(sb, "  " + call);
210             }
211         }
212 
213         ProfileService.println(sb, "mCallsUpdate:");
214         if (mCallsUpdate != null) {
215             for (BluetoothHeadsetClientCall call : mCallsUpdate.values()) {
216                 ProfileService.println(sb, "  " + call);
217             }
218         }
219     }
220 
clearPendingAction()221     private void clearPendingAction() {
222         mPendingAction = new Pair<Integer, Object>(NO_ACTION, 0);
223     }
224 
addQueuedAction(int action)225     private void addQueuedAction(int action) {
226         addQueuedAction(action, 0);
227     }
228 
addQueuedAction(int action, Object data)229     private void addQueuedAction(int action, Object data) {
230         mQueuedActions.add(new Pair<Integer, Object>(action, data));
231     }
232 
addQueuedAction(int action, int data)233     private void addQueuedAction(int action, int data) {
234         mQueuedActions.add(new Pair<Integer, Object>(action, data));
235     }
236 
getCall(int... states)237     private BluetoothHeadsetClientCall getCall(int... states) {
238         logD("getFromCallsWithStates states:" + Arrays.toString(states));
239         for (BluetoothHeadsetClientCall c : mCalls.values()) {
240             for (int s : states) {
241                 if (c.getState() == s) {
242                     return c;
243                 }
244             }
245         }
246         return null;
247     }
248 
callsInState(int state)249     private int callsInState(int state) {
250         int i = 0;
251         for (BluetoothHeadsetClientCall c : mCalls.values()) {
252             if (c.getState() == state) {
253                 i++;
254             }
255         }
256 
257         return i;
258     }
259 
sendCallChangedIntent(BluetoothHeadsetClientCall c)260     private void sendCallChangedIntent(BluetoothHeadsetClientCall c) {
261         logD("sendCallChangedIntent " + c);
262         Intent intent = new Intent(BluetoothHeadsetClient.ACTION_CALL_CHANGED);
263         intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
264         intent.putExtra(BluetoothHeadsetClient.EXTRA_CALL, c);
265         mService.sendBroadcast(intent, ProfileService.BLUETOOTH_PERM);
266     }
267 
queryCallsStart()268     private boolean queryCallsStart() {
269         logD("queryCallsStart");
270         clearPendingAction();
271         mNativeInterface.queryCurrentCalls(getByteAddress(mCurrentDevice));
272         addQueuedAction(QUERY_CURRENT_CALLS, 0);
273         return true;
274     }
275 
queryCallsDone()276     private void queryCallsDone() {
277         logD("queryCallsDone");
278         // mCalls has two types of calls:
279         // (a) Calls that are received from AG of a previous iteration of queryCallsStart()
280         // (b) Calls that are outgoing initiated from HF
281         // mCallsUpdate has all calls received from queryCallsUpdate() in current iteration of
282         // queryCallsStart().
283         //
284         // We use the following steps to make sure that calls are update correctly.
285         //
286         // If there are no calls initiated from HF (i.e. ID = -1) then:
287         // 1. All IDs which are common in mCalls & mCallsUpdate are updated and the upper layers are
288         // informed of the change calls (if any changes).
289         // 2. All IDs that are in mCalls but *not* in mCallsUpdate will be removed from mCalls and
290         // the calls should be terminated
291         // 3. All IDs that are new in mCallsUpdated should be added as new calls to mCalls.
292         //
293         // If there is an outgoing HF call, it is important to associate that call with one of the
294         // mCallsUpdated calls hence,
295         // 1. If from the above procedure we get N extra calls (i.e. {3}):
296         // choose the first call as the one to associate with the HF call.
297 
298         // Create set of IDs for added calls, removed calls and consitent calls.
299         // WARN!!! Java Map -> Set has association hence changes to Set are reflected in the Map
300         // itself (i.e. removing an element from Set removes it from the Map hence use copy).
301         Set<Integer> currCallIdSet = new HashSet<Integer>();
302         currCallIdSet.addAll(mCalls.keySet());
303         // Remove the entry for unassigned call.
304         currCallIdSet.remove(HF_ORIGINATED_CALL_ID);
305 
306         Set<Integer> newCallIdSet = new HashSet<Integer>();
307         newCallIdSet.addAll(mCallsUpdate.keySet());
308 
309         // Added.
310         Set<Integer> callAddedIds = new HashSet<Integer>();
311         callAddedIds.addAll(newCallIdSet);
312         callAddedIds.removeAll(currCallIdSet);
313 
314         // Removed.
315         Set<Integer> callRemovedIds = new HashSet<Integer>();
316         callRemovedIds.addAll(currCallIdSet);
317         callRemovedIds.removeAll(newCallIdSet);
318 
319         // Retained.
320         Set<Integer> callRetainedIds = new HashSet<Integer>();
321         callRetainedIds.addAll(currCallIdSet);
322         callRetainedIds.retainAll(newCallIdSet);
323 
324         logD("currCallIdSet " + mCalls.keySet() + " newCallIdSet " + newCallIdSet
325                 + " callAddedIds " + callAddedIds + " callRemovedIds " + callRemovedIds
326                 + " callRetainedIds " + callRetainedIds);
327 
328         // First thing is to try to associate the outgoing HF with a valid call.
329         Integer hfOriginatedAssoc = -1;
330         if (mCalls.containsKey(HF_ORIGINATED_CALL_ID)) {
331             BluetoothHeadsetClientCall c = mCalls.get(HF_ORIGINATED_CALL_ID);
332             long cCreationElapsed = c.getCreationElapsedMilli();
333             if (callAddedIds.size() > 0) {
334                 logD("Associating the first call with HF originated call");
335                 hfOriginatedAssoc = (Integer) callAddedIds.toArray()[0];
336                 mCalls.put(hfOriginatedAssoc, mCalls.get(HF_ORIGINATED_CALL_ID));
337                 mCalls.remove(HF_ORIGINATED_CALL_ID);
338 
339                 // Adjust this call in above sets.
340                 callAddedIds.remove(hfOriginatedAssoc);
341                 callRetainedIds.add(hfOriginatedAssoc);
342             } else if (SystemClock.elapsedRealtime() - cCreationElapsed > OUTGOING_TIMEOUT_MILLI) {
343                 Log.w(TAG, "Outgoing call did not see a response, clear the calls and send CHUP");
344                 // We send a terminate because we are in a bad state and trying to
345                 // recover.
346                 terminateCall();
347 
348                 // Clean out the state for outgoing call.
349                 for (Integer idx : mCalls.keySet()) {
350                     BluetoothHeadsetClientCall c1 = mCalls.get(idx);
351                     c1.setState(BluetoothHeadsetClientCall.CALL_STATE_TERMINATED);
352                     sendCallChangedIntent(c1);
353                 }
354                 mCalls.clear();
355 
356                 // We return here, if there's any update to the phone we should get a
357                 // follow up by getting some call indicators and hence update the calls.
358                 return;
359             }
360         }
361 
362         logD("ADJUST: currCallIdSet " + mCalls.keySet() + " newCallIdSet " + newCallIdSet
363                 + " callAddedIds " + callAddedIds + " callRemovedIds " + callRemovedIds
364                 + " callRetainedIds " + callRetainedIds);
365 
366         // Terminate & remove the calls that are done.
367         for (Integer idx : callRemovedIds) {
368             BluetoothHeadsetClientCall c = mCalls.remove(idx);
369             c.setState(BluetoothHeadsetClientCall.CALL_STATE_TERMINATED);
370             sendCallChangedIntent(c);
371         }
372 
373         // Add the new calls.
374         for (Integer idx : callAddedIds) {
375             BluetoothHeadsetClientCall c = mCallsUpdate.get(idx);
376             mCalls.put(idx, c);
377             sendCallChangedIntent(c);
378         }
379 
380         // Update the existing calls.
381         for (Integer idx : callRetainedIds) {
382             BluetoothHeadsetClientCall cOrig = mCalls.get(idx);
383             BluetoothHeadsetClientCall cUpdate = mCallsUpdate.get(idx);
384 
385             // Update the necessary fields.
386             cOrig.setNumber(cUpdate.getNumber());
387             cOrig.setState(cUpdate.getState());
388             cOrig.setMultiParty(cUpdate.isMultiParty());
389 
390             // Send update with original object (UUID, idx).
391             sendCallChangedIntent(cOrig);
392         }
393 
394         if (mCalls.size() > 0) {
395             if (mService.getResources().getBoolean(R.bool.hfp_clcc_poll_during_call)) {
396                 sendMessageDelayed(QUERY_CURRENT_CALLS, QUERY_CURRENT_CALLS_WAIT_MILLIS);
397             } else {
398                 if (getCall(BluetoothHeadsetClientCall.CALL_STATE_INCOMING) != null) {
399                     logD("Still have incoming call; polling");
400                     sendMessageDelayed(QUERY_CURRENT_CALLS, QUERY_CURRENT_CALLS_WAIT_MILLIS);
401                 } else {
402                     removeMessages(QUERY_CURRENT_CALLS);
403                 }
404             }
405         }
406 
407         mCallsUpdate.clear();
408     }
409 
queryCallsUpdate(int id, int state, String number, boolean multiParty, boolean outgoing)410     private void queryCallsUpdate(int id, int state, String number, boolean multiParty,
411             boolean outgoing) {
412         logD("queryCallsUpdate: " + id);
413         mCallsUpdate.put(id,
414                 new BluetoothHeadsetClientCall(mCurrentDevice, id, state, number, multiParty,
415                         outgoing, mInBandRing));
416     }
417 
acceptCall(int flag)418     private void acceptCall(int flag) {
419         int action = -1;
420 
421         logD("acceptCall: (" + flag + ")");
422 
423         BluetoothHeadsetClientCall c = getCall(BluetoothHeadsetClientCall.CALL_STATE_INCOMING,
424                 BluetoothHeadsetClientCall.CALL_STATE_WAITING);
425         if (c == null) {
426             c = getCall(BluetoothHeadsetClientCall.CALL_STATE_HELD_BY_RESPONSE_AND_HOLD,
427                     BluetoothHeadsetClientCall.CALL_STATE_HELD);
428 
429             if (c == null) {
430                 return;
431             }
432         }
433 
434         logD("Call to accept: " + c);
435         switch (c.getState()) {
436             case BluetoothHeadsetClientCall.CALL_STATE_INCOMING:
437                 if (flag != BluetoothHeadsetClient.CALL_ACCEPT_NONE) {
438                     return;
439                 }
440                 action = HeadsetClientHalConstants.CALL_ACTION_ATA;
441                 break;
442             case BluetoothHeadsetClientCall.CALL_STATE_WAITING:
443                 if (callsInState(BluetoothHeadsetClientCall.CALL_STATE_ACTIVE) == 0) {
444                     // if no active calls present only plain accept is allowed
445                     if (flag != BluetoothHeadsetClient.CALL_ACCEPT_NONE) {
446                         return;
447                     }
448                     action = HeadsetClientHalConstants.CALL_ACTION_CHLD_2;
449                     break;
450                 }
451 
452                 // if active calls are present then we have the option to either terminate the
453                 // existing call or hold the existing call. We hold the other call by default.
454                 if (flag == BluetoothHeadsetClient.CALL_ACCEPT_HOLD
455                         || flag == BluetoothHeadsetClient.CALL_ACCEPT_NONE) {
456                     logD("Accepting call with accept and hold");
457                     action = HeadsetClientHalConstants.CALL_ACTION_CHLD_2;
458                 } else if (flag == BluetoothHeadsetClient.CALL_ACCEPT_TERMINATE) {
459                     logD("Accepting call with accept and reject");
460                     action = HeadsetClientHalConstants.CALL_ACTION_CHLD_1;
461                 } else {
462                     Log.e(TAG, "Aceept call with invalid flag: " + flag);
463                     return;
464                 }
465                 break;
466             case BluetoothHeadsetClientCall.CALL_STATE_HELD:
467                 if (flag == BluetoothHeadsetClient.CALL_ACCEPT_HOLD) {
468                     action = HeadsetClientHalConstants.CALL_ACTION_CHLD_2;
469                 } else if (flag == BluetoothHeadsetClient.CALL_ACCEPT_TERMINATE) {
470                     action = HeadsetClientHalConstants.CALL_ACTION_CHLD_1;
471                 } else if (getCall(BluetoothHeadsetClientCall.CALL_STATE_ACTIVE) != null) {
472                     action = HeadsetClientHalConstants.CALL_ACTION_CHLD_3;
473                 } else if (flag == BluetoothHeadsetClient.CALL_ACCEPT_NONE) {
474                     action = HeadsetClientHalConstants.CALL_ACTION_CHLD_2;
475                 } else {
476                     action = HeadsetClientHalConstants.CALL_ACTION_CHLD_2;
477                 }
478                 break;
479             case BluetoothHeadsetClientCall.CALL_STATE_HELD_BY_RESPONSE_AND_HOLD:
480                 action = HeadsetClientHalConstants.CALL_ACTION_BTRH_1;
481                 break;
482             case BluetoothHeadsetClientCall.CALL_STATE_ALERTING:
483             case BluetoothHeadsetClientCall.CALL_STATE_ACTIVE:
484             case BluetoothHeadsetClientCall.CALL_STATE_DIALING:
485             default:
486                 return;
487         }
488 
489         if (flag == BluetoothHeadsetClient.CALL_ACCEPT_HOLD) {
490             // When unholding a call over Bluetooth make sure to route audio.
491             routeHfpAudio(true);
492         }
493 
494         if (mNativeInterface.handleCallAction(getByteAddress(mCurrentDevice), action, 0)) {
495             addQueuedAction(ACCEPT_CALL, action);
496         } else {
497             Log.e(TAG, "ERROR: Couldn't accept a call, action:" + action);
498         }
499     }
500 
rejectCall()501     private void rejectCall() {
502         int action;
503 
504         logD("rejectCall");
505 
506         BluetoothHeadsetClientCall c = getCall(BluetoothHeadsetClientCall.CALL_STATE_INCOMING,
507                 BluetoothHeadsetClientCall.CALL_STATE_WAITING,
508                 BluetoothHeadsetClientCall.CALL_STATE_HELD_BY_RESPONSE_AND_HOLD,
509                 BluetoothHeadsetClientCall.CALL_STATE_HELD);
510         if (c == null) {
511             logD("No call to reject, returning.");
512             return;
513         }
514 
515         switch (c.getState()) {
516             case BluetoothHeadsetClientCall.CALL_STATE_INCOMING:
517                 action = HeadsetClientHalConstants.CALL_ACTION_CHUP;
518                 break;
519             case BluetoothHeadsetClientCall.CALL_STATE_WAITING:
520             case BluetoothHeadsetClientCall.CALL_STATE_HELD:
521                 action = HeadsetClientHalConstants.CALL_ACTION_CHLD_0;
522                 break;
523             case BluetoothHeadsetClientCall.CALL_STATE_HELD_BY_RESPONSE_AND_HOLD:
524                 action = HeadsetClientHalConstants.CALL_ACTION_BTRH_2;
525                 break;
526             case BluetoothHeadsetClientCall.CALL_STATE_ACTIVE:
527             case BluetoothHeadsetClientCall.CALL_STATE_DIALING:
528             case BluetoothHeadsetClientCall.CALL_STATE_ALERTING:
529             default:
530                 return;
531         }
532 
533         if (mNativeInterface.handleCallAction(getByteAddress(mCurrentDevice), action, 0)) {
534             logD("Reject call action " + action);
535             addQueuedAction(REJECT_CALL, action);
536         } else {
537             Log.e(TAG, "ERROR: Couldn't reject a call, action:" + action);
538         }
539     }
540 
holdCall()541     private void holdCall() {
542         int action;
543 
544         logD("holdCall");
545 
546         BluetoothHeadsetClientCall c = getCall(BluetoothHeadsetClientCall.CALL_STATE_INCOMING);
547         if (c != null) {
548             action = HeadsetClientHalConstants.CALL_ACTION_BTRH_0;
549         } else {
550             c = getCall(BluetoothHeadsetClientCall.CALL_STATE_ACTIVE);
551             if (c == null) {
552                 return;
553             }
554 
555             action = HeadsetClientHalConstants.CALL_ACTION_CHLD_2;
556         }
557 
558         if (mNativeInterface.handleCallAction(getByteAddress(mCurrentDevice), action, 0)) {
559             addQueuedAction(HOLD_CALL, action);
560         } else {
561             Log.e(TAG, "ERROR: Couldn't hold a call, action:" + action);
562         }
563     }
564 
terminateCall()565     private void terminateCall() {
566         logD("terminateCall");
567 
568         int action = HeadsetClientHalConstants.CALL_ACTION_CHUP;
569 
570         BluetoothHeadsetClientCall c = getCall(BluetoothHeadsetClientCall.CALL_STATE_DIALING,
571                 BluetoothHeadsetClientCall.CALL_STATE_ALERTING,
572                 BluetoothHeadsetClientCall.CALL_STATE_ACTIVE);
573         if (c == null) {
574             // If the call being terminated is currently held, switch the action to CHLD_0
575             c = getCall(BluetoothHeadsetClientCall.CALL_STATE_HELD);
576             action = HeadsetClientHalConstants.CALL_ACTION_CHLD_0;
577         }
578         if (c != null) {
579             if (mNativeInterface.handleCallAction(getByteAddress(mCurrentDevice), action, 0)) {
580                 addQueuedAction(TERMINATE_CALL, action);
581             } else {
582                 Log.e(TAG, "ERROR: Couldn't terminate outgoing call");
583             }
584         }
585     }
586 
enterPrivateMode(int idx)587     private void enterPrivateMode(int idx) {
588         logD("enterPrivateMode: " + idx);
589 
590         BluetoothHeadsetClientCall c = mCalls.get(idx);
591 
592         if (c == null || c.getState() != BluetoothHeadsetClientCall.CALL_STATE_ACTIVE
593                 || !c.isMultiParty()) {
594             return;
595         }
596 
597         if (mNativeInterface.handleCallAction(getByteAddress(mCurrentDevice),
598                 HeadsetClientHalConstants.CALL_ACTION_CHLD_2X, idx)) {
599             addQueuedAction(ENTER_PRIVATE_MODE, c);
600         } else {
601             Log.e(TAG, "ERROR: Couldn't enter private " + " id:" + idx);
602         }
603     }
604 
explicitCallTransfer()605     private void explicitCallTransfer() {
606         logD("explicitCallTransfer");
607 
608         // can't transfer call if there is not enough call parties
609         if (mCalls.size() < 2) {
610             return;
611         }
612 
613         if (mNativeInterface.handleCallAction(getByteAddress(mCurrentDevice),
614                 HeadsetClientHalConstants.CALL_ACTION_CHLD_4, -1)) {
615             addQueuedAction(EXPLICIT_CALL_TRANSFER);
616         } else {
617             Log.e(TAG, "ERROR: Couldn't transfer call");
618         }
619     }
620 
getCurrentAgFeatures()621     public Bundle getCurrentAgFeatures() {
622         Bundle b = new Bundle();
623         if ((mPeerFeatures & HeadsetClientHalConstants.PEER_FEAT_3WAY)
624                 == HeadsetClientHalConstants.PEER_FEAT_3WAY) {
625             b.putBoolean(BluetoothHeadsetClient.EXTRA_AG_FEATURE_3WAY_CALLING, true);
626         }
627         if ((mPeerFeatures & HeadsetClientHalConstants.PEER_FEAT_VREC)
628                 == HeadsetClientHalConstants.PEER_FEAT_VREC) {
629             b.putBoolean(BluetoothHeadsetClient.EXTRA_AG_FEATURE_VOICE_RECOGNITION, true);
630         }
631         if ((mPeerFeatures & HeadsetClientHalConstants.PEER_FEAT_REJECT)
632                 == HeadsetClientHalConstants.PEER_FEAT_REJECT) {
633             b.putBoolean(BluetoothHeadsetClient.EXTRA_AG_FEATURE_REJECT_CALL, true);
634         }
635         if ((mPeerFeatures & HeadsetClientHalConstants.PEER_FEAT_ECC)
636                 == HeadsetClientHalConstants.PEER_FEAT_ECC) {
637             b.putBoolean(BluetoothHeadsetClient.EXTRA_AG_FEATURE_ECC, true);
638         }
639 
640         // add individual CHLD support extras
641         if ((mChldFeatures & HeadsetClientHalConstants.CHLD_FEAT_HOLD_ACC)
642                 == HeadsetClientHalConstants.CHLD_FEAT_HOLD_ACC) {
643             b.putBoolean(BluetoothHeadsetClient.EXTRA_AG_FEATURE_ACCEPT_HELD_OR_WAITING_CALL, true);
644         }
645         if ((mChldFeatures & HeadsetClientHalConstants.CHLD_FEAT_REL)
646                 == HeadsetClientHalConstants.CHLD_FEAT_REL) {
647             b.putBoolean(BluetoothHeadsetClient.EXTRA_AG_FEATURE_RELEASE_HELD_OR_WAITING_CALL,
648                     true);
649         }
650         if ((mChldFeatures & HeadsetClientHalConstants.CHLD_FEAT_REL_ACC)
651                 == HeadsetClientHalConstants.CHLD_FEAT_REL_ACC) {
652             b.putBoolean(BluetoothHeadsetClient.EXTRA_AG_FEATURE_RELEASE_AND_ACCEPT, true);
653         }
654         if ((mChldFeatures & HeadsetClientHalConstants.CHLD_FEAT_MERGE)
655                 == HeadsetClientHalConstants.CHLD_FEAT_MERGE) {
656             b.putBoolean(BluetoothHeadsetClient.EXTRA_AG_FEATURE_MERGE, true);
657         }
658         if ((mChldFeatures & HeadsetClientHalConstants.CHLD_FEAT_MERGE_DETACH)
659                 == HeadsetClientHalConstants.CHLD_FEAT_MERGE_DETACH) {
660             b.putBoolean(BluetoothHeadsetClient.EXTRA_AG_FEATURE_MERGE_AND_DETACH, true);
661         }
662 
663         return b;
664     }
665 
HeadsetClientStateMachine(HeadsetClientService context, Looper looper, NativeInterface nativeInterface)666     HeadsetClientStateMachine(HeadsetClientService context, Looper looper,
667                               NativeInterface nativeInterface) {
668         super(TAG, looper);
669         mService = context;
670         mNativeInterface = nativeInterface;
671         mAudioManager = mService.getAudioManager();
672 
673         mVendorProcessor = new VendorCommandResponseProcessor(mService, mNativeInterface);
674 
675         mAdapter = BluetoothAdapter.getDefaultAdapter();
676         mAudioState = BluetoothHeadsetClient.STATE_AUDIO_DISCONNECTED;
677         mAudioWbs = false;
678         mVoiceRecognitionActive = HeadsetClientHalConstants.VR_STATE_STOPPED;
679 
680         mIndicatorNetworkState = HeadsetClientHalConstants.NETWORK_STATE_NOT_AVAILABLE;
681         mIndicatorNetworkType = HeadsetClientHalConstants.SERVICE_TYPE_HOME;
682         mIndicatorNetworkSignal = 0;
683         mIndicatorBatteryLevel = 0;
684 
685         sMaxAmVcVol = mAudioManager.getStreamMaxVolume(AudioManager.STREAM_VOICE_CALL);
686         sMinAmVcVol = mAudioManager.getStreamMinVolume(AudioManager.STREAM_VOICE_CALL);
687 
688         mOperatorName = null;
689         mSubscriberInfo = null;
690 
691         mQueuedActions = new LinkedList<Pair<Integer, Object>>();
692         clearPendingAction();
693 
694         mCalls.clear();
695         mCallsUpdate.clear();
696 
697         mDisconnected = new Disconnected();
698         mConnecting = new Connecting();
699         mConnected = new Connected();
700         mAudioOn = new AudioOn();
701 
702         addState(mDisconnected);
703         addState(mConnecting);
704         addState(mConnected);
705         addState(mAudioOn, mConnected);
706 
707         setInitialState(mDisconnected);
708     }
709 
make(HeadsetClientService context, Looper looper, NativeInterface nativeInterface)710     static HeadsetClientStateMachine make(HeadsetClientService context, Looper looper,
711                                           NativeInterface nativeInterface) {
712         logD("make");
713         HeadsetClientStateMachine hfcsm = new HeadsetClientStateMachine(context, looper,
714                                                                         nativeInterface);
715         hfcsm.start();
716         return hfcsm;
717     }
718 
routeHfpAudio(boolean enable)719     synchronized void routeHfpAudio(boolean enable) {
720         if (mAudioManager == null) {
721             Log.e(TAG, "AudioManager is null!");
722             return;
723         }
724         logD("hfp_enable=" + enable);
725         if (enable && !sAudioIsRouted) {
726             mAudioManager.setParameters("hfp_enable=true");
727         } else if (!enable) {
728             mAudioManager.setParameters("hfp_enable=false");
729         }
730         sAudioIsRouted = enable;
731     }
732 
requestAudioFocus()733     private AudioFocusRequest requestAudioFocus() {
734         AudioAttributes streamAttributes =
735                 new AudioAttributes.Builder().setUsage(AudioAttributes.USAGE_VOICE_COMMUNICATION)
736                         .setContentType(AudioAttributes.CONTENT_TYPE_SPEECH)
737                         .build();
738         AudioFocusRequest focusRequest =
739                 new AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN_TRANSIENT)
740                         .setAudioAttributes(streamAttributes)
741                         .build();
742         int focusRequestStatus = mAudioManager.requestAudioFocus(focusRequest);
743         String s = (focusRequestStatus == AudioManager.AUDIOFOCUS_REQUEST_GRANTED)
744                 ? "AudioFocus granted" : "AudioFocus NOT granted";
745         logD("AudioManager requestAudioFocus returned: " + s);
746         return focusRequest;
747     }
748 
doQuit()749     public void doQuit() {
750         logD("doQuit");
751         if (mCurrentDevice != null) {
752             mNativeInterface.disconnect(getByteAddress(mCurrentDevice));
753         }
754         routeHfpAudio(false);
755         returnAudioFocusIfNecessary();
756         quitNow();
757     }
758 
returnAudioFocusIfNecessary()759     private void returnAudioFocusIfNecessary() {
760         if (mAudioFocusRequest == null) return;
761         mAudioManager.abandonAudioFocusRequest(mAudioFocusRequest);
762         mAudioFocusRequest = null;
763     }
764 
hfToAmVol(int hfVol)765     static int hfToAmVol(int hfVol) {
766         int amRange = sMaxAmVcVol - sMinAmVcVol;
767         int hfRange = MAX_HFP_SCO_VOICE_CALL_VOLUME - MIN_HFP_SCO_VOICE_CALL_VOLUME;
768         int amOffset = (amRange * (hfVol - MIN_HFP_SCO_VOICE_CALL_VOLUME)) / hfRange;
769         int amVol = sMinAmVcVol + amOffset;
770         logD("HF -> AM " + hfVol + " " + amVol);
771         return amVol;
772     }
773 
amToHfVol(int amVol)774     static int amToHfVol(int amVol) {
775         int amRange = (sMaxAmVcVol > sMinAmVcVol) ? (sMaxAmVcVol - sMinAmVcVol) : 1;
776         int hfRange = MAX_HFP_SCO_VOICE_CALL_VOLUME - MIN_HFP_SCO_VOICE_CALL_VOLUME;
777         int hfOffset = (hfRange * (amVol - sMinAmVcVol)) / amRange;
778         int hfVol = MIN_HFP_SCO_VOICE_CALL_VOLUME + hfOffset;
779         logD("AM -> HF " + amVol + " " + hfVol);
780         return hfVol;
781     }
782 
783     class Disconnected extends State {
784         @Override
enter()785         public void enter() {
786             logD("Enter Disconnected: " + getCurrentMessage().what);
787 
788             // cleanup
789             mIndicatorNetworkState = HeadsetClientHalConstants.NETWORK_STATE_NOT_AVAILABLE;
790             mIndicatorNetworkType = HeadsetClientHalConstants.SERVICE_TYPE_HOME;
791             mIndicatorNetworkSignal = 0;
792             mIndicatorBatteryLevel = 0;
793             mInBandRing = false;
794 
795             mAudioWbs = false;
796 
797             // will be set on connect
798 
799             mOperatorName = null;
800             mSubscriberInfo = null;
801 
802             mQueuedActions = new LinkedList<Pair<Integer, Object>>();
803             clearPendingAction();
804 
805             mCalls.clear();
806             mCallsUpdate.clear();
807 
808             mPeerFeatures = 0;
809             mChldFeatures = 0;
810 
811             removeMessages(QUERY_CURRENT_CALLS);
812 
813             if (mPrevState == mConnecting) {
814                 broadcastConnectionState(mCurrentDevice, BluetoothProfile.STATE_DISCONNECTED,
815                         BluetoothProfile.STATE_CONNECTING);
816             } else if (mPrevState == mConnected || mPrevState == mAudioOn) {
817                 broadcastConnectionState(mCurrentDevice, BluetoothProfile.STATE_DISCONNECTED,
818                         BluetoothProfile.STATE_CONNECTED);
819             } else if (mPrevState != null) { // null is the default state before Disconnected
820                 Log.e(TAG, "Connected: Illegal state transition from " + mPrevState.getName()
821                         + " to Connecting, mCurrentDevice=" + mCurrentDevice);
822             }
823             mCurrentDevice = null;
824         }
825 
826         @Override
processMessage(Message message)827         public synchronized boolean processMessage(Message message) {
828             logD("Disconnected process message: " + message.what);
829 
830             if (mCurrentDevice != null) {
831                 Log.e(TAG, "ERROR: current device not null in Disconnected");
832                 return NOT_HANDLED;
833             }
834 
835             switch (message.what) {
836                 case CONNECT:
837                     BluetoothDevice device = (BluetoothDevice) message.obj;
838                     if (!mNativeInterface.connect(getByteAddress(device))) {
839                         // No state transition is involved, fire broadcast immediately
840                         broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED,
841                                 BluetoothProfile.STATE_DISCONNECTED);
842                         break;
843                     }
844                     mCurrentDevice = device;
845                     transitionTo(mConnecting);
846                     break;
847                 case DISCONNECT:
848                     // ignore
849                     break;
850                 case StackEvent.STACK_EVENT:
851                     StackEvent event = (StackEvent) message.obj;
852                     logD("Stack event type: " + event.type);
853                     switch (event.type) {
854                         case StackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED:
855                             logD("Disconnected: Connection " + event.device
856                                     + " state changed:" + event.valueInt);
857                             processConnectionEvent(event.valueInt, event.device);
858                             break;
859                         default:
860                             Log.e(TAG, "Disconnected: Unexpected stack event: " + event.type);
861                             break;
862                     }
863                     break;
864                 default:
865                     return NOT_HANDLED;
866             }
867             return HANDLED;
868         }
869 
870         // in Disconnected state
processConnectionEvent(int state, BluetoothDevice device)871         private void processConnectionEvent(int state, BluetoothDevice device) {
872             switch (state) {
873                 case HeadsetClientHalConstants.CONNECTION_STATE_CONNECTED:
874                     Log.w(TAG, "HFPClient Connecting from Disconnected state");
875                     if (okToConnect(device)) {
876                         Log.i(TAG, "Incoming AG accepted");
877                         mCurrentDevice = device;
878                         transitionTo(mConnecting);
879                     } else {
880                         Log.i(TAG, "Incoming AG rejected. connectionPolicy="
881                                 + mService.getConnectionPolicy(device) + " bondState="
882                                 + device.getBondState());
883                         // reject the connection and stay in Disconnected state
884                         // itself
885                         mNativeInterface.disconnect(getByteAddress(device));
886                         // the other profile connection should be initiated
887                         AdapterService adapterService = AdapterService.getAdapterService();
888                         // No state transition is involved, fire broadcast immediately
889                         broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED,
890                                 BluetoothProfile.STATE_DISCONNECTED);
891                     }
892                     break;
893                 case HeadsetClientHalConstants.CONNECTION_STATE_CONNECTING:
894                 case HeadsetClientHalConstants.CONNECTION_STATE_DISCONNECTED:
895                 case HeadsetClientHalConstants.CONNECTION_STATE_DISCONNECTING:
896                 default:
897                     Log.i(TAG, "ignoring state: " + state);
898                     break;
899             }
900         }
901 
902         @Override
exit()903         public void exit() {
904             logD("Exit Disconnected: " + getCurrentMessage().what);
905             mPrevState = this;
906         }
907     }
908 
909     class Connecting extends State {
910         @Override
enter()911         public void enter() {
912             logD("Enter Connecting: " + getCurrentMessage().what);
913             // This message is either consumed in processMessage or
914             // removed in exit. It is safe to send a CONNECTING_TIMEOUT here since
915             // the only transition is when connection attempt is initiated.
916             sendMessageDelayed(CONNECTING_TIMEOUT, CONNECTING_TIMEOUT_MS);
917             if (mPrevState == mDisconnected) {
918                 broadcastConnectionState(mCurrentDevice, BluetoothProfile.STATE_CONNECTING,
919                         BluetoothProfile.STATE_DISCONNECTED);
920             } else {
921                 String prevStateName = mPrevState == null ? "null" : mPrevState.getName();
922                 Log.e(TAG, "Connected: Illegal state transition from " + prevStateName
923                         + " to Connecting, mCurrentDevice=" + mCurrentDevice);
924             }
925         }
926 
927         @Override
processMessage(Message message)928         public synchronized boolean processMessage(Message message) {
929             logD("Connecting process message: " + message.what);
930 
931             switch (message.what) {
932                 case CONNECT:
933                 case CONNECT_AUDIO:
934                 case DISCONNECT:
935                     deferMessage(message);
936                     break;
937                 case StackEvent.STACK_EVENT:
938                     StackEvent event = (StackEvent) message.obj;
939                     logD("Connecting: event type: " + event.type);
940                     switch (event.type) {
941                         case StackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED:
942                             logD("Connecting: Connection " + event.device + " state changed:"
943                                     + event.valueInt);
944                             processConnectionEvent(event.valueInt, event.valueInt2, event.valueInt3,
945                                     event.device);
946                             break;
947                         case StackEvent.EVENT_TYPE_AUDIO_STATE_CHANGED:
948                         case StackEvent.EVENT_TYPE_NETWORK_STATE:
949                         case StackEvent.EVENT_TYPE_ROAMING_STATE:
950                         case StackEvent.EVENT_TYPE_NETWORK_SIGNAL:
951                         case StackEvent.EVENT_TYPE_BATTERY_LEVEL:
952                         case StackEvent.EVENT_TYPE_CALL:
953                         case StackEvent.EVENT_TYPE_CALLSETUP:
954                         case StackEvent.EVENT_TYPE_CALLHELD:
955                         case StackEvent.EVENT_TYPE_RESP_AND_HOLD:
956                         case StackEvent.EVENT_TYPE_CLIP:
957                         case StackEvent.EVENT_TYPE_CALL_WAITING:
958                         case StackEvent.EVENT_TYPE_VOLUME_CHANGED:
959                             deferMessage(message);
960                             break;
961                         case StackEvent.EVENT_TYPE_CMD_RESULT:
962                         case StackEvent.EVENT_TYPE_SUBSCRIBER_INFO:
963                         case StackEvent.EVENT_TYPE_CURRENT_CALLS:
964                         case StackEvent.EVENT_TYPE_OPERATOR_NAME:
965                         default:
966                             Log.e(TAG, "Connecting: ignoring stack event: " + event.type);
967                             break;
968                     }
969                     break;
970                 case CONNECTING_TIMEOUT:
971                     // We timed out trying to connect, transition to disconnected.
972                     Log.w(TAG, "Connection timeout for " + mCurrentDevice);
973                     transitionTo(mDisconnected);
974                     break;
975 
976                 default:
977                     Log.w(TAG, "Message not handled " + message);
978                     return NOT_HANDLED;
979             }
980             return HANDLED;
981         }
982 
983         // in Connecting state
processConnectionEvent(int state, int peerFeat, int chldFeat, BluetoothDevice device)984         private void processConnectionEvent(int state, int peerFeat, int chldFeat,
985                 BluetoothDevice device) {
986             switch (state) {
987                 case HeadsetClientHalConstants.CONNECTION_STATE_DISCONNECTED:
988                     transitionTo(mDisconnected);
989                     break;
990 
991                 case HeadsetClientHalConstants.CONNECTION_STATE_SLC_CONNECTED:
992                     logD("HFPClient Connected from Connecting state");
993 
994                     mPeerFeatures = peerFeat;
995                     mChldFeatures = chldFeat;
996 
997                     // We do not support devices which do not support enhanced call status (ECS).
998                     if ((mPeerFeatures & HeadsetClientHalConstants.PEER_FEAT_ECS) == 0) {
999                         mNativeInterface.disconnect(getByteAddress(device));
1000                         return;
1001                     }
1002 
1003                     // Send AT+NREC to remote if supported by audio
1004                     if (HeadsetClientHalConstants.HANDSFREECLIENT_NREC_SUPPORTED && (
1005                             (mPeerFeatures & HeadsetClientHalConstants.PEER_FEAT_ECNR)
1006                                     == HeadsetClientHalConstants.PEER_FEAT_ECNR)) {
1007                         if (mNativeInterface.sendATCmd(getByteAddress(mCurrentDevice),
1008                                 HeadsetClientHalConstants.HANDSFREECLIENT_AT_CMD_NREC, 1, 0,
1009                                 null)) {
1010                             addQueuedAction(DISABLE_NREC);
1011                         } else {
1012                             Log.e(TAG, "Failed to send NREC");
1013                         }
1014                     }
1015 
1016                     int amVol = mAudioManager.getStreamVolume(AudioManager.STREAM_VOICE_CALL);
1017                     deferMessage(
1018                             obtainMessage(HeadsetClientStateMachine.SET_SPEAKER_VOLUME, amVol, 0));
1019                     // Mic is either in ON state (full volume) or OFF state. There is no way in
1020                     // Android to change the MIC volume.
1021                     deferMessage(obtainMessage(HeadsetClientStateMachine.SET_MIC_VOLUME,
1022                             mAudioManager.isMicrophoneMute() ? 0 : 15, 0));
1023                     // query subscriber info
1024                     deferMessage(obtainMessage(HeadsetClientStateMachine.SUBSCRIBER_INFO));
1025                     transitionTo(mConnected);
1026                     break;
1027 
1028                 case HeadsetClientHalConstants.CONNECTION_STATE_CONNECTED:
1029                     if (!mCurrentDevice.equals(device)) {
1030                         Log.w(TAG, "incoming connection event, device: " + device);
1031                         // No state transition is involved, fire broadcast immediately
1032                         broadcastConnectionState(mCurrentDevice,
1033                                 BluetoothProfile.STATE_DISCONNECTED,
1034                                 BluetoothProfile.STATE_CONNECTING);
1035                         broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTING,
1036                                 BluetoothProfile.STATE_DISCONNECTED);
1037 
1038                         mCurrentDevice = device;
1039                     }
1040                     break;
1041                 case HeadsetClientHalConstants.CONNECTION_STATE_CONNECTING:
1042                     /* outgoing connecting started */
1043                     logD("outgoing connection started, ignore");
1044                     break;
1045                 case HeadsetClientHalConstants.CONNECTION_STATE_DISCONNECTING:
1046                 default:
1047                     Log.e(TAG, "Incorrect state: " + state);
1048                     break;
1049             }
1050         }
1051 
1052         @Override
exit()1053         public void exit() {
1054             logD("Exit Connecting: " + getCurrentMessage().what);
1055             removeMessages(CONNECTING_TIMEOUT);
1056             mPrevState = this;
1057         }
1058     }
1059 
1060     class Connected extends State {
1061         int mCommandedSpeakerVolume = -1;
1062 
1063         @Override
enter()1064         public void enter() {
1065             logD("Enter Connected: " + getCurrentMessage().what);
1066             mAudioWbs = false;
1067             mCommandedSpeakerVolume = -1;
1068             if (mPrevState == mConnecting) {
1069                 broadcastConnectionState(mCurrentDevice, BluetoothProfile.STATE_CONNECTED,
1070                         BluetoothProfile.STATE_CONNECTING);
1071                 MetricsLogger.logProfileConnectionEvent(
1072                         BluetoothMetricsProto.ProfileId.HEADSET_CLIENT);
1073             } else if (mPrevState != mAudioOn) {
1074                 String prevStateName = mPrevState == null ? "null" : mPrevState.getName();
1075                 Log.e(TAG, "Connected: Illegal state transition from " + prevStateName
1076                         + " to Connecting, mCurrentDevice=" + mCurrentDevice);
1077             }
1078         }
1079 
1080         @Override
processMessage(Message message)1081         public synchronized boolean processMessage(Message message) {
1082             logD("Connected process message: " + message.what);
1083             if (DBG) {
1084                 if (mCurrentDevice == null) {
1085                     Log.e(TAG, "ERROR: mCurrentDevice is null in Connected");
1086                     return NOT_HANDLED;
1087                 }
1088             }
1089 
1090             switch (message.what) {
1091                 case CONNECT:
1092                     BluetoothDevice device = (BluetoothDevice) message.obj;
1093                     if (mCurrentDevice.equals(device)) {
1094                         // already connected to this device, do nothing
1095                         break;
1096                     }
1097                     mNativeInterface.connect(getByteAddress(device));
1098                     break;
1099                 case DISCONNECT:
1100                     BluetoothDevice dev = (BluetoothDevice) message.obj;
1101                     if (!mCurrentDevice.equals(dev)) {
1102                         break;
1103                     }
1104                     if (!mNativeInterface.disconnect(getByteAddress(dev))) {
1105                         Log.e(TAG, "disconnectNative failed for " + dev);
1106                     }
1107                     break;
1108 
1109                 case CONNECT_AUDIO:
1110                     if (!mNativeInterface.connectAudio(getByteAddress(mCurrentDevice))) {
1111                         Log.e(TAG, "ERROR: Couldn't connect Audio for device " + mCurrentDevice);
1112                         // No state transition is involved, fire broadcast immediately
1113                         broadcastAudioState(mCurrentDevice,
1114                                 BluetoothHeadsetClient.STATE_AUDIO_DISCONNECTED,
1115                                 BluetoothHeadsetClient.STATE_AUDIO_DISCONNECTED);
1116                     } else { // We have successfully sent a connect request!
1117                         mAudioState = BluetoothHeadsetClient.STATE_AUDIO_CONNECTING;
1118                     }
1119                     break;
1120 
1121                 case DISCONNECT_AUDIO:
1122                     if (!mNativeInterface.disconnectAudio(getByteAddress(mCurrentDevice))) {
1123                         Log.e(TAG, "ERROR: Couldn't disconnect Audio for device " + mCurrentDevice);
1124                     }
1125                     break;
1126 
1127                 case VOICE_RECOGNITION_START:
1128                     if (mVoiceRecognitionActive == HeadsetClientHalConstants.VR_STATE_STOPPED) {
1129                         if (mNativeInterface.startVoiceRecognition(
1130                                     getByteAddress(mCurrentDevice))) {
1131                             addQueuedAction(VOICE_RECOGNITION_START);
1132                         } else {
1133                             Log.e(TAG, "ERROR: Couldn't start voice recognition");
1134                         }
1135                     }
1136                     break;
1137 
1138                 case VOICE_RECOGNITION_STOP:
1139                     if (mVoiceRecognitionActive == HeadsetClientHalConstants.VR_STATE_STARTED) {
1140                         if (mNativeInterface.stopVoiceRecognition(
1141                                     getByteAddress(mCurrentDevice))) {
1142                             addQueuedAction(VOICE_RECOGNITION_STOP);
1143                         } else {
1144                             Log.e(TAG, "ERROR: Couldn't stop voice recognition");
1145                         }
1146                     }
1147                     break;
1148 
1149                 case SEND_VENDOR_AT_COMMAND: {
1150                     int vendorId = message.arg1;
1151                     String atCommand = (String) (message.obj);
1152                     mVendorProcessor.sendCommand(vendorId, atCommand, mCurrentDevice);
1153                     break;
1154                 }
1155 
1156                 // Called only for Mute/Un-mute - Mic volume change is not allowed.
1157                 case SET_MIC_VOLUME:
1158                     break;
1159                 case SET_SPEAKER_VOLUME:
1160                     // This message should always contain the volume in AudioManager max normalized.
1161                     int amVol = message.arg1;
1162                     int hfVol = amToHfVol(amVol);
1163                     if (amVol != mCommandedSpeakerVolume) {
1164                         logD("Volume" + amVol + ":" + mCommandedSpeakerVolume);
1165                         // Volume was changed by a 3rd party
1166                         mCommandedSpeakerVolume = -1;
1167                         if (mNativeInterface.setVolume(getByteAddress(mCurrentDevice),
1168                                 HeadsetClientHalConstants.VOLUME_TYPE_SPK, hfVol)) {
1169                             addQueuedAction(SET_SPEAKER_VOLUME);
1170                         }
1171                     }
1172                     break;
1173                 case DIAL_NUMBER:
1174                     // Add the call as an outgoing call.
1175                     BluetoothHeadsetClientCall c = (BluetoothHeadsetClientCall) message.obj;
1176                     mCalls.put(HF_ORIGINATED_CALL_ID, c);
1177 
1178                     if (mNativeInterface.dial(getByteAddress(mCurrentDevice), c.getNumber())) {
1179                         addQueuedAction(DIAL_NUMBER, c.getNumber());
1180                         // Start looping on calling current calls.
1181                         sendMessage(QUERY_CURRENT_CALLS);
1182                     } else {
1183                         Log.e(TAG,
1184                                 "ERROR: Cannot dial with a given number:" + c.toString(true));
1185                         // Set the call to terminated remove.
1186                         c.setState(BluetoothHeadsetClientCall.CALL_STATE_TERMINATED);
1187                         sendCallChangedIntent(c);
1188                         mCalls.remove(HF_ORIGINATED_CALL_ID);
1189                     }
1190                     break;
1191                 case ACCEPT_CALL:
1192                     acceptCall(message.arg1);
1193                     break;
1194                 case REJECT_CALL:
1195                     rejectCall();
1196                     break;
1197                 case HOLD_CALL:
1198                     holdCall();
1199                     break;
1200                 case TERMINATE_CALL:
1201                     terminateCall();
1202                     break;
1203                 case ENTER_PRIVATE_MODE:
1204                     enterPrivateMode(message.arg1);
1205                     break;
1206                 case EXPLICIT_CALL_TRANSFER:
1207                     explicitCallTransfer();
1208                     break;
1209                 case SEND_DTMF:
1210                     if (mNativeInterface.sendDtmf(getByteAddress(mCurrentDevice),
1211                             (byte) message.arg1)) {
1212                         addQueuedAction(SEND_DTMF);
1213                     } else {
1214                         Log.e(TAG, "ERROR: Couldn't send DTMF");
1215                     }
1216                     break;
1217                 case SUBSCRIBER_INFO:
1218                     if (mNativeInterface.retrieveSubscriberInfo(
1219                             getByteAddress(mCurrentDevice))) {
1220                         addQueuedAction(SUBSCRIBER_INFO);
1221                     } else {
1222                         Log.e(TAG, "ERROR: Couldn't retrieve subscriber info");
1223                     }
1224                     break;
1225                 case QUERY_CURRENT_CALLS:
1226                     removeMessages(QUERY_CURRENT_CALLS);
1227                     if (mCalls.size() > 0) {
1228                         // If there are ongoing calls periodically check their status.
1229                         sendMessageDelayed(QUERY_CURRENT_CALLS, QUERY_CURRENT_CALLS_WAIT_MILLIS);
1230                     }
1231                     queryCallsStart();
1232                     break;
1233                 case StackEvent.STACK_EVENT:
1234                     Intent intent = null;
1235                     StackEvent event = (StackEvent) message.obj;
1236                     logD("Connected: event type: " + event.type);
1237 
1238                     switch (event.type) {
1239                         case StackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED:
1240                             logD("Connected: Connection state changed: " + event.device
1241                                     + ": " + event.valueInt);
1242                             processConnectionEvent(event.valueInt, event.device);
1243                             break;
1244                         case StackEvent.EVENT_TYPE_AUDIO_STATE_CHANGED:
1245                             logD("Connected: Audio state changed: " + event.device + ": "
1246                                     + event.valueInt);
1247                             processAudioEvent(event.valueInt, event.device);
1248                             break;
1249                         case StackEvent.EVENT_TYPE_NETWORK_STATE:
1250                             logD("Connected: Network state: " + event.valueInt);
1251                             mIndicatorNetworkState = event.valueInt;
1252 
1253                             intent = new Intent(BluetoothHeadsetClient.ACTION_AG_EVENT);
1254                             intent.putExtra(BluetoothHeadsetClient.EXTRA_NETWORK_STATUS,
1255                                     event.valueInt);
1256 
1257                             if (mIndicatorNetworkState
1258                                     == HeadsetClientHalConstants.NETWORK_STATE_NOT_AVAILABLE) {
1259                                 mOperatorName = null;
1260                                 intent.putExtra(BluetoothHeadsetClient.EXTRA_OPERATOR_NAME,
1261                                         mOperatorName);
1262                             }
1263 
1264                             intent.putExtra(BluetoothDevice.EXTRA_DEVICE, event.device);
1265                             mService.sendBroadcast(intent, ProfileService.BLUETOOTH_PERM);
1266 
1267                             if (mIndicatorNetworkState
1268                                     == HeadsetClientHalConstants.NETWORK_STATE_AVAILABLE) {
1269                                 if (mNativeInterface.queryCurrentOperatorName(
1270                                         getByteAddress(mCurrentDevice))) {
1271                                     addQueuedAction(QUERY_OPERATOR_NAME);
1272                                 } else {
1273                                     Log.e(TAG, "ERROR: Couldn't querry operator name");
1274                                 }
1275                             }
1276                             break;
1277                         case StackEvent.EVENT_TYPE_ROAMING_STATE:
1278                             mIndicatorNetworkType = event.valueInt;
1279 
1280                             intent = new Intent(BluetoothHeadsetClient.ACTION_AG_EVENT);
1281                             intent.putExtra(BluetoothHeadsetClient.EXTRA_NETWORK_ROAMING,
1282                                     event.valueInt);
1283                             intent.putExtra(BluetoothDevice.EXTRA_DEVICE, event.device);
1284                             mService.sendBroadcast(intent, ProfileService.BLUETOOTH_PERM);
1285                             break;
1286                         case StackEvent.EVENT_TYPE_NETWORK_SIGNAL:
1287                             mIndicatorNetworkSignal = event.valueInt;
1288 
1289                             intent = new Intent(BluetoothHeadsetClient.ACTION_AG_EVENT);
1290                             intent.putExtra(BluetoothHeadsetClient.EXTRA_NETWORK_SIGNAL_STRENGTH,
1291                                     event.valueInt);
1292                             intent.putExtra(BluetoothDevice.EXTRA_DEVICE, event.device);
1293                             mService.sendBroadcast(intent, ProfileService.BLUETOOTH_PERM);
1294                             break;
1295                         case StackEvent.EVENT_TYPE_BATTERY_LEVEL:
1296                             mIndicatorBatteryLevel = event.valueInt;
1297 
1298                             intent = new Intent(BluetoothHeadsetClient.ACTION_AG_EVENT);
1299                             intent.putExtra(BluetoothHeadsetClient.EXTRA_BATTERY_LEVEL,
1300                                     event.valueInt);
1301                             intent.putExtra(BluetoothDevice.EXTRA_DEVICE, event.device);
1302                             mService.sendBroadcast(intent, ProfileService.BLUETOOTH_PERM);
1303                             break;
1304                         case StackEvent.EVENT_TYPE_OPERATOR_NAME:
1305                             mOperatorName = event.valueString;
1306 
1307                             intent = new Intent(BluetoothHeadsetClient.ACTION_AG_EVENT);
1308                             intent.putExtra(BluetoothHeadsetClient.EXTRA_OPERATOR_NAME,
1309                                     event.valueString);
1310                             intent.putExtra(BluetoothDevice.EXTRA_DEVICE, event.device);
1311                             mService.sendBroadcast(intent, ProfileService.BLUETOOTH_PERM);
1312                             break;
1313                         case StackEvent.EVENT_TYPE_VR_STATE_CHANGED:
1314                             int oldState = mVoiceRecognitionActive;
1315                             mVoiceRecognitionActive = event.valueInt;
1316                             broadcastVoiceRecognitionStateChanged(event.device, oldState,
1317                                     mVoiceRecognitionActive);
1318                             break;
1319                         case StackEvent.EVENT_TYPE_CALL:
1320                         case StackEvent.EVENT_TYPE_CALLSETUP:
1321                         case StackEvent.EVENT_TYPE_CALLHELD:
1322                         case StackEvent.EVENT_TYPE_RESP_AND_HOLD:
1323                         case StackEvent.EVENT_TYPE_CLIP:
1324                         case StackEvent.EVENT_TYPE_CALL_WAITING:
1325                             sendMessage(QUERY_CURRENT_CALLS);
1326                             break;
1327                         case StackEvent.EVENT_TYPE_CURRENT_CALLS:
1328                             queryCallsUpdate(event.valueInt, event.valueInt3, event.valueString,
1329                                     event.valueInt4
1330                                             == HeadsetClientHalConstants.CALL_MPTY_TYPE_MULTI,
1331                                     event.valueInt2
1332                                             == HeadsetClientHalConstants.CALL_DIRECTION_OUTGOING);
1333                             break;
1334                         case StackEvent.EVENT_TYPE_VOLUME_CHANGED:
1335                             if (event.valueInt == HeadsetClientHalConstants.VOLUME_TYPE_SPK) {
1336                                 mCommandedSpeakerVolume = hfToAmVol(event.valueInt2);
1337                                 logD("AM volume set to " + mCommandedSpeakerVolume);
1338                                 mAudioManager.setStreamVolume(AudioManager.STREAM_VOICE_CALL,
1339                                         +mCommandedSpeakerVolume, AudioManager.FLAG_SHOW_UI);
1340                             } else if (event.valueInt
1341                                     == HeadsetClientHalConstants.VOLUME_TYPE_MIC) {
1342                                 mAudioManager.setMicrophoneMute(event.valueInt2 == 0);
1343                             }
1344                             break;
1345                         case StackEvent.EVENT_TYPE_CMD_RESULT:
1346                             Pair<Integer, Object> queuedAction = mQueuedActions.poll();
1347 
1348                             // should not happen but...
1349                             if (queuedAction == null || queuedAction.first == NO_ACTION) {
1350                                 clearPendingAction();
1351                                 break;
1352                             }
1353 
1354                             logD("Connected: command result: " + event.valueInt
1355                                     + " queuedAction: " + queuedAction.first);
1356 
1357                             switch (queuedAction.first) {
1358                                 case QUERY_CURRENT_CALLS:
1359                                     queryCallsDone();
1360                                     break;
1361                                 case VOICE_RECOGNITION_START:
1362                                     if (event.valueInt == AT_OK) {
1363                                         oldState = mVoiceRecognitionActive;
1364                                         mVoiceRecognitionActive =
1365                                                 HeadsetClientHalConstants.VR_STATE_STARTED;
1366                                         broadcastVoiceRecognitionStateChanged(event.device,
1367                                                 oldState, mVoiceRecognitionActive);
1368                                     }
1369                                     break;
1370                                 case VOICE_RECOGNITION_STOP:
1371                                     if (event.valueInt == AT_OK) {
1372                                         oldState = mVoiceRecognitionActive;
1373                                         mVoiceRecognitionActive =
1374                                                 HeadsetClientHalConstants.VR_STATE_STOPPED;
1375                                         broadcastVoiceRecognitionStateChanged(event.device,
1376                                                 oldState, mVoiceRecognitionActive);
1377                                     }
1378                                     break;
1379                                 default:
1380                                     Log.w(TAG, "Unhandled AT OK " + event);
1381                                     break;
1382                             }
1383 
1384                             break;
1385                         case StackEvent.EVENT_TYPE_SUBSCRIBER_INFO:
1386                             mSubscriberInfo = event.valueString;
1387                             intent = new Intent(BluetoothHeadsetClient.ACTION_AG_EVENT);
1388                             intent.putExtra(BluetoothHeadsetClient.EXTRA_SUBSCRIBER_INFO,
1389                                     mSubscriberInfo);
1390                             intent.putExtra(BluetoothDevice.EXTRA_DEVICE, event.device);
1391                             mService.sendBroadcast(intent, ProfileService.BLUETOOTH_PERM);
1392                             break;
1393                         case StackEvent.EVENT_TYPE_IN_BAND_RINGTONE:
1394                             intent = new Intent(BluetoothHeadsetClient.ACTION_AG_EVENT);
1395                             mInBandRing = event.valueInt == IN_BAND_RING_ENABLED;
1396                             intent.putExtra(BluetoothHeadsetClient.EXTRA_IN_BAND_RING,
1397                                     event.valueInt);
1398                             intent.putExtra(BluetoothDevice.EXTRA_DEVICE, event.device);
1399                             mService.sendBroadcast(intent, ProfileService.BLUETOOTH_PERM);
1400                             logD(event.device.toString() + "onInBandRing" + event.valueInt);
1401                             break;
1402                         case StackEvent.EVENT_TYPE_RING_INDICATION:
1403                             // Ringing is not handled at this indication and rather should be
1404                             // implemented (by the client of this service). Use the
1405                             // CALL_STATE_INCOMING (and similar) handle ringing.
1406                             break;
1407                         case StackEvent.EVENT_TYPE_UNKNOWN_EVENT:
1408                             if (!mVendorProcessor.processEvent(event.valueString, event.device)) {
1409                                 Log.e(TAG, "Unknown event :" + event.valueString
1410                                         + " for device " + event.device);
1411                             }
1412                             break;
1413                         default:
1414                             Log.e(TAG, "Unknown stack event: " + event.type);
1415                             break;
1416                     }
1417 
1418                     break;
1419                 default:
1420                     return NOT_HANDLED;
1421             }
1422             return HANDLED;
1423         }
1424 
broadcastVoiceRecognitionStateChanged(BluetoothDevice device, int oldState, int newState)1425         private void broadcastVoiceRecognitionStateChanged(BluetoothDevice device, int oldState,
1426                 int newState) {
1427             if (oldState == newState) {
1428                 return;
1429             }
1430             Intent intent = new Intent(BluetoothHeadsetClient.ACTION_AG_EVENT);
1431             intent.putExtra(BluetoothHeadsetClient.EXTRA_VOICE_RECOGNITION, newState);
1432             intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
1433             mService.sendBroadcast(intent, ProfileService.BLUETOOTH_PERM);
1434         }
1435 
1436         // in Connected state
processConnectionEvent(int state, BluetoothDevice device)1437         private void processConnectionEvent(int state, BluetoothDevice device) {
1438             switch (state) {
1439                 case HeadsetClientHalConstants.CONNECTION_STATE_DISCONNECTED:
1440                     logD("Connected disconnects.");
1441                     // AG disconnects
1442                     if (mCurrentDevice.equals(device)) {
1443                         transitionTo(mDisconnected);
1444                     } else {
1445                         Log.e(TAG, "Disconnected from unknown device: " + device);
1446                     }
1447                     break;
1448                 default:
1449                     Log.e(TAG, "Connection State Device: " + device + " bad state: " + state);
1450                     break;
1451             }
1452         }
1453 
1454         // in Connected state
processAudioEvent(int state, BluetoothDevice device)1455         private void processAudioEvent(int state, BluetoothDevice device) {
1456             // message from old device
1457             if (!mCurrentDevice.equals(device)) {
1458                 Log.e(TAG, "Audio changed on disconnected device: " + device);
1459                 return;
1460             }
1461 
1462             switch (state) {
1463                 case HeadsetClientHalConstants.AUDIO_STATE_CONNECTED_MSBC:
1464                     mAudioWbs = true;
1465                     // fall through
1466                 case HeadsetClientHalConstants.AUDIO_STATE_CONNECTED:
1467                     // Audio state is split in two parts, the audio focus is maintained by the
1468                     // entity exercising this service (typically the Telecom stack) and audio
1469                     // routing is handled by the bluetooth stack itself. The only reason to do so is
1470                     // because Bluetooth SCO connection from the HF role is not entirely supported
1471                     // for routing and volume purposes.
1472                     // NOTE: All calls here are routed via the setParameters which changes the
1473                     // routing at the Audio HAL level.
1474 
1475                     if (mService.isScoRouted()) {
1476                         StackEvent event =
1477                                 new StackEvent(StackEvent.EVENT_TYPE_AUDIO_STATE_CHANGED);
1478                         event.valueInt = state;
1479                         event.device = device;
1480                         sendMessageDelayed(StackEvent.STACK_EVENT, event, ROUTING_DELAY_MS);
1481                         break;
1482                     }
1483 
1484                     mAudioState = BluetoothHeadsetClient.STATE_AUDIO_CONNECTED;
1485 
1486                     // We need to set the volume after switching into HFP mode as some Audio HALs
1487                     // reset the volume to a known-default on mode switch.
1488                     final int amVol = mAudioManager.getStreamVolume(AudioManager.STREAM_VOICE_CALL);
1489                     final int hfVol = amToHfVol(amVol);
1490 
1491                     logD("hfp_enable=true mAudioWbs is " + mAudioWbs);
1492                     if (mAudioWbs) {
1493                         logD("Setting sampling rate as 16000");
1494                         mAudioManager.setParameters("hfp_set_sampling_rate=16000");
1495                     } else {
1496                         logD("Setting sampling rate as 8000");
1497                         mAudioManager.setParameters("hfp_set_sampling_rate=8000");
1498                     }
1499                     logD("hf_volume " + hfVol);
1500                     routeHfpAudio(true);
1501                     mAudioFocusRequest = requestAudioFocus();
1502                     mAudioManager.setParameters("hfp_volume=" + hfVol);
1503                     transitionTo(mAudioOn);
1504                     break;
1505 
1506                 case HeadsetClientHalConstants.AUDIO_STATE_CONNECTING:
1507                     // No state transition is involved, fire broadcast immediately
1508                     broadcastAudioState(device, BluetoothHeadsetClient.STATE_AUDIO_CONNECTING,
1509                             mAudioState);
1510                     mAudioState = BluetoothHeadsetClient.STATE_AUDIO_CONNECTING;
1511                     break;
1512 
1513                 case HeadsetClientHalConstants.AUDIO_STATE_DISCONNECTED:
1514                     // No state transition is involved, fire broadcast immediately
1515                     broadcastAudioState(device, BluetoothHeadsetClient.STATE_AUDIO_DISCONNECTED,
1516                             mAudioState);
1517                     mAudioState = BluetoothHeadsetClient.STATE_AUDIO_DISCONNECTED;
1518                     break;
1519 
1520                 default:
1521                     Log.e(TAG, "Audio State Device: " + device + " bad state: " + state);
1522                     break;
1523             }
1524         }
1525 
1526         @Override
exit()1527         public void exit() {
1528             logD("Exit Connected: " + getCurrentMessage().what);
1529             mPrevState = this;
1530         }
1531     }
1532 
1533     class AudioOn extends State {
1534         @Override
enter()1535         public void enter() {
1536             logD("Enter AudioOn: " + getCurrentMessage().what);
1537             broadcastAudioState(mCurrentDevice, BluetoothHeadsetClient.STATE_AUDIO_CONNECTED,
1538                     BluetoothHeadsetClient.STATE_AUDIO_CONNECTING);
1539         }
1540 
1541         @Override
processMessage(Message message)1542         public synchronized boolean processMessage(Message message) {
1543             logD("AudioOn process message: " + message.what);
1544             if (DBG) {
1545                 if (mCurrentDevice == null) {
1546                     Log.e(TAG, "ERROR: mCurrentDevice is null in Connected");
1547                     return NOT_HANDLED;
1548                 }
1549             }
1550 
1551             switch (message.what) {
1552                 case DISCONNECT:
1553                     BluetoothDevice device = (BluetoothDevice) message.obj;
1554                     if (!mCurrentDevice.equals(device)) {
1555                         break;
1556                     }
1557                     deferMessage(message);
1558                     /*
1559                      * fall through - disconnect audio first then expect
1560                      * deferred DISCONNECT message in Connected state
1561                      */
1562                 case DISCONNECT_AUDIO:
1563                     /*
1564                      * just disconnect audio and wait for
1565                      * StackEvent.EVENT_TYPE_AUDIO_STATE_CHANGED, that triggers State
1566                      * Machines state changing
1567                      */
1568                     if (mNativeInterface.disconnectAudio(getByteAddress(mCurrentDevice))) {
1569                         routeHfpAudio(false);
1570                         returnAudioFocusIfNecessary();
1571                     }
1572                     break;
1573 
1574                 case HOLD_CALL:
1575                     holdCall();
1576                     break;
1577 
1578                 case StackEvent.STACK_EVENT:
1579                     StackEvent event = (StackEvent) message.obj;
1580                     logD("AudioOn: event type: " + event.type);
1581                     switch (event.type) {
1582                         case StackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED:
1583                             logD("AudioOn connection state changed" + event.device + ": "
1584                                     + event.valueInt);
1585                             processConnectionEvent(event.valueInt, event.device);
1586                             break;
1587                         case StackEvent.EVENT_TYPE_AUDIO_STATE_CHANGED:
1588                             logD("AudioOn audio state changed" + event.device + ": "
1589                                     + event.valueInt);
1590                             processAudioEvent(event.valueInt, event.device);
1591                             break;
1592                         default:
1593                             return NOT_HANDLED;
1594                     }
1595                     break;
1596                 default:
1597                     return NOT_HANDLED;
1598             }
1599             return HANDLED;
1600         }
1601 
1602         // in AudioOn state. Can AG disconnect RFCOMM prior to SCO? Handle this
processConnectionEvent(int state, BluetoothDevice device)1603         private void processConnectionEvent(int state, BluetoothDevice device) {
1604             switch (state) {
1605                 case HeadsetClientHalConstants.CONNECTION_STATE_DISCONNECTED:
1606                     if (mCurrentDevice.equals(device)) {
1607                         processAudioEvent(HeadsetClientHalConstants.AUDIO_STATE_DISCONNECTED,
1608                                 device);
1609                         transitionTo(mDisconnected);
1610                     } else {
1611                         Log.e(TAG, "Disconnected from unknown device: " + device);
1612                     }
1613                     break;
1614                 default:
1615                     Log.e(TAG, "Connection State Device: " + device + " bad state: " + state);
1616                     break;
1617             }
1618         }
1619 
1620         // in AudioOn state
processAudioEvent(int state, BluetoothDevice device)1621         private void processAudioEvent(int state, BluetoothDevice device) {
1622             if (!mCurrentDevice.equals(device)) {
1623                 Log.e(TAG, "Audio changed on disconnected device: " + device);
1624                 return;
1625             }
1626 
1627             switch (state) {
1628                 case HeadsetClientHalConstants.AUDIO_STATE_DISCONNECTED:
1629                     removeMessages(DISCONNECT_AUDIO);
1630                     mAudioState = BluetoothHeadsetClient.STATE_AUDIO_DISCONNECTED;
1631                     // Audio focus may still be held by the entity controlling the actual call
1632                     // (such as Telecom) and hence this will still keep the call around, there
1633                     // is not much we can do here since dropping the call without user consent
1634                     // even if the audio connection snapped may not be a good idea.
1635                     routeHfpAudio(false);
1636                     returnAudioFocusIfNecessary();
1637                     transitionTo(mConnected);
1638                     break;
1639 
1640                 default:
1641                     Log.e(TAG, "Audio State Device: " + device + " bad state: " + state);
1642                     break;
1643             }
1644         }
1645 
1646         @Override
exit()1647         public void exit() {
1648             logD("Exit AudioOn: " + getCurrentMessage().what);
1649             mPrevState = this;
1650             broadcastAudioState(mCurrentDevice, BluetoothHeadsetClient.STATE_AUDIO_DISCONNECTED,
1651                     BluetoothHeadsetClient.STATE_AUDIO_CONNECTED);
1652         }
1653     }
1654 
1655     /**
1656      * @hide
1657      */
getConnectionState(BluetoothDevice device)1658     public synchronized int getConnectionState(BluetoothDevice device) {
1659         if (mCurrentDevice == null) {
1660             return BluetoothProfile.STATE_DISCONNECTED;
1661         }
1662 
1663         if (!mCurrentDevice.equals(device)) {
1664             return BluetoothProfile.STATE_DISCONNECTED;
1665         }
1666 
1667         IState currentState = getCurrentState();
1668         if (currentState == mConnecting) {
1669             return BluetoothProfile.STATE_CONNECTING;
1670         }
1671 
1672         if (currentState == mConnected || currentState == mAudioOn) {
1673             return BluetoothProfile.STATE_CONNECTED;
1674         }
1675 
1676         Log.e(TAG, "Bad currentState: " + currentState);
1677         return BluetoothProfile.STATE_DISCONNECTED;
1678     }
1679 
broadcastAudioState(BluetoothDevice device, int newState, int prevState)1680     private void broadcastAudioState(BluetoothDevice device, int newState, int prevState) {
1681         BluetoothStatsLog.write(BluetoothStatsLog.BLUETOOTH_SCO_CONNECTION_STATE_CHANGED,
1682                 AdapterService.getAdapterService().obfuscateAddress(device),
1683                 getConnectionStateFromAudioState(newState), mAudioWbs
1684                         ? BluetoothHfpProtoEnums.SCO_CODEC_MSBC
1685                         : BluetoothHfpProtoEnums.SCO_CODEC_CVSD,
1686                 AdapterService.getAdapterService().getMetricId(device));
1687         Intent intent = new Intent(BluetoothHeadsetClient.ACTION_AUDIO_STATE_CHANGED);
1688         intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, prevState);
1689         intent.putExtra(BluetoothProfile.EXTRA_STATE, newState);
1690         if (newState == BluetoothHeadsetClient.STATE_AUDIO_CONNECTED) {
1691             intent.putExtra(BluetoothHeadsetClient.EXTRA_AUDIO_WBS, mAudioWbs);
1692         }
1693         intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
1694         mService.sendBroadcast(intent, ProfileService.BLUETOOTH_PERM);
1695         logD("Audio state " + device + ": " + prevState + "->" + newState);
1696     }
1697 
1698     // This method does not check for error condition (newState == prevState)
broadcastConnectionState(BluetoothDevice device, int newState, int prevState)1699     private void broadcastConnectionState(BluetoothDevice device, int newState, int prevState) {
1700         logD("Connection state " + device + ": " + prevState + "->" + newState);
1701         /*
1702          * Notifying the connection state change of the profile before sending
1703          * the intent for connection state change, as it was causing a race
1704          * condition, with the UI not being updated with the correct connection
1705          * state.
1706          */
1707         Intent intent = new Intent(BluetoothHeadsetClient.ACTION_CONNECTION_STATE_CHANGED);
1708         intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, prevState);
1709         intent.putExtra(BluetoothProfile.EXTRA_STATE, newState);
1710         intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
1711 
1712         // add feature extras when connected
1713         if (newState == BluetoothProfile.STATE_CONNECTED) {
1714             if ((mPeerFeatures & HeadsetClientHalConstants.PEER_FEAT_3WAY)
1715                     == HeadsetClientHalConstants.PEER_FEAT_3WAY) {
1716                 intent.putExtra(BluetoothHeadsetClient.EXTRA_AG_FEATURE_3WAY_CALLING, true);
1717             }
1718             if ((mPeerFeatures & HeadsetClientHalConstants.PEER_FEAT_VREC)
1719                     == HeadsetClientHalConstants.PEER_FEAT_VREC) {
1720                 intent.putExtra(BluetoothHeadsetClient.EXTRA_AG_FEATURE_VOICE_RECOGNITION, true);
1721             }
1722             if ((mPeerFeatures & HeadsetClientHalConstants.PEER_FEAT_REJECT)
1723                     == HeadsetClientHalConstants.PEER_FEAT_REJECT) {
1724                 intent.putExtra(BluetoothHeadsetClient.EXTRA_AG_FEATURE_REJECT_CALL, true);
1725             }
1726             if ((mPeerFeatures & HeadsetClientHalConstants.PEER_FEAT_ECC)
1727                     == HeadsetClientHalConstants.PEER_FEAT_ECC) {
1728                 intent.putExtra(BluetoothHeadsetClient.EXTRA_AG_FEATURE_ECC, true);
1729             }
1730 
1731             // add individual CHLD support extras
1732             if ((mChldFeatures & HeadsetClientHalConstants.CHLD_FEAT_HOLD_ACC)
1733                     == HeadsetClientHalConstants.CHLD_FEAT_HOLD_ACC) {
1734                 intent.putExtra(BluetoothHeadsetClient.EXTRA_AG_FEATURE_ACCEPT_HELD_OR_WAITING_CALL,
1735                         true);
1736             }
1737             if ((mChldFeatures & HeadsetClientHalConstants.CHLD_FEAT_REL)
1738                     == HeadsetClientHalConstants.CHLD_FEAT_REL) {
1739                 intent.putExtra(
1740                         BluetoothHeadsetClient.EXTRA_AG_FEATURE_RELEASE_HELD_OR_WAITING_CALL, true);
1741             }
1742             if ((mChldFeatures & HeadsetClientHalConstants.CHLD_FEAT_REL_ACC)
1743                     == HeadsetClientHalConstants.CHLD_FEAT_REL_ACC) {
1744                 intent.putExtra(BluetoothHeadsetClient.EXTRA_AG_FEATURE_RELEASE_AND_ACCEPT, true);
1745             }
1746             if ((mChldFeatures & HeadsetClientHalConstants.CHLD_FEAT_MERGE)
1747                     == HeadsetClientHalConstants.CHLD_FEAT_MERGE) {
1748                 intent.putExtra(BluetoothHeadsetClient.EXTRA_AG_FEATURE_MERGE, true);
1749             }
1750             if ((mChldFeatures & HeadsetClientHalConstants.CHLD_FEAT_MERGE_DETACH)
1751                     == HeadsetClientHalConstants.CHLD_FEAT_MERGE_DETACH) {
1752                 intent.putExtra(BluetoothHeadsetClient.EXTRA_AG_FEATURE_MERGE_AND_DETACH, true);
1753             }
1754         }
1755         mService.sendBroadcast(intent, ProfileService.BLUETOOTH_PERM);
1756     }
1757 
isConnected()1758     boolean isConnected() {
1759         IState currentState = getCurrentState();
1760         return (currentState == mConnected || currentState == mAudioOn);
1761     }
1762 
getDevicesMatchingConnectionStates(int[] states)1763     List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
1764         List<BluetoothDevice> deviceList = new ArrayList<BluetoothDevice>();
1765         Set<BluetoothDevice> bondedDevices = mAdapter.getBondedDevices();
1766         int connectionState;
1767         synchronized (this) {
1768             for (BluetoothDevice device : bondedDevices) {
1769                 ParcelUuid[] featureUuids = device.getUuids();
1770                 if (!ArrayUtils.contains(featureUuids, BluetoothUuid.HFP_AG)) {
1771                     continue;
1772                 }
1773                 connectionState = getConnectionState(device);
1774                 for (int state : states) {
1775                     if (connectionState == state) {
1776                         deviceList.add(device);
1777                     }
1778                 }
1779             }
1780         }
1781         return deviceList;
1782     }
1783 
okToConnect(BluetoothDevice device)1784     boolean okToConnect(BluetoothDevice device) {
1785         int connectionPolicy = mService.getConnectionPolicy(device);
1786         boolean ret = false;
1787         // check connection policy and accept or reject the connection. if connection policy is
1788         // undefined
1789         // it is likely that our SDP has not completed and peer is initiating
1790         // the
1791         // connection. Allow this connection, provided the device is bonded
1792         if ((BluetoothProfile.CONNECTION_POLICY_FORBIDDEN < connectionPolicy) || (
1793                 (BluetoothProfile.CONNECTION_POLICY_UNKNOWN == connectionPolicy)
1794                         && (device.getBondState() != BluetoothDevice.BOND_NONE))) {
1795             ret = true;
1796         }
1797         return ret;
1798     }
1799 
isAudioOn()1800     boolean isAudioOn() {
1801         return (getCurrentState() == mAudioOn);
1802     }
1803 
getAudioState(BluetoothDevice device)1804     synchronized int getAudioState(BluetoothDevice device) {
1805         if (mCurrentDevice == null || !mCurrentDevice.equals(device)) {
1806             return BluetoothHeadsetClient.STATE_AUDIO_DISCONNECTED;
1807         }
1808         return mAudioState;
1809     }
1810 
getConnectedDevices()1811     List<BluetoothDevice> getConnectedDevices() {
1812         List<BluetoothDevice> devices = new ArrayList<BluetoothDevice>();
1813         synchronized (this) {
1814             if (isConnected()) {
1815                 devices.add(mCurrentDevice);
1816             }
1817         }
1818         return devices;
1819     }
1820 
getByteAddress(BluetoothDevice device)1821     private byte[] getByteAddress(BluetoothDevice device) {
1822         return Utils.getBytesFromAddress(device.getAddress());
1823     }
1824 
getCurrentCalls()1825     public List<BluetoothHeadsetClientCall> getCurrentCalls() {
1826         return new ArrayList<BluetoothHeadsetClientCall>(mCalls.values());
1827     }
1828 
getCurrentAgEvents()1829     public Bundle getCurrentAgEvents() {
1830         Bundle b = new Bundle();
1831         b.putInt(BluetoothHeadsetClient.EXTRA_NETWORK_STATUS, mIndicatorNetworkState);
1832         b.putInt(BluetoothHeadsetClient.EXTRA_NETWORK_SIGNAL_STRENGTH, mIndicatorNetworkSignal);
1833         b.putInt(BluetoothHeadsetClient.EXTRA_NETWORK_ROAMING, mIndicatorNetworkType);
1834         b.putInt(BluetoothHeadsetClient.EXTRA_BATTERY_LEVEL, mIndicatorBatteryLevel);
1835         b.putString(BluetoothHeadsetClient.EXTRA_OPERATOR_NAME, mOperatorName);
1836         b.putString(BluetoothHeadsetClient.EXTRA_SUBSCRIBER_INFO, mSubscriberInfo);
1837         return b;
1838     }
1839 
getConnectionStateFromAudioState(int audioState)1840     private static int getConnectionStateFromAudioState(int audioState) {
1841         switch (audioState) {
1842             case BluetoothHeadsetClient.STATE_AUDIO_CONNECTED:
1843                 return BluetoothAdapter.STATE_CONNECTED;
1844             case BluetoothHeadsetClient.STATE_AUDIO_CONNECTING:
1845                 return BluetoothAdapter.STATE_CONNECTING;
1846             case BluetoothHeadsetClient.STATE_AUDIO_DISCONNECTED:
1847                 return BluetoothAdapter.STATE_DISCONNECTED;
1848         }
1849         return BluetoothAdapter.STATE_DISCONNECTED;
1850     }
1851 
logD(String message)1852     private static void logD(String message) {
1853         if (DBG) {
1854             Log.d(TAG, message);
1855         }
1856     }
1857 }
1858