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