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