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