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