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