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