1 /*
2  * Copyright (C) 2012 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 package com.android.bluetooth.hfp;
18 
19 import static android.Manifest.permission.BLUETOOTH_CONNECT;
20 import static android.Manifest.permission.MODIFY_PHONE_STATE;
21 
22 import static com.android.bluetooth.Utils.enforceBluetoothPrivilegedPermission;
23 import static com.android.modules.utils.build.SdkLevel.isAtLeastU;
24 
25 import static java.util.Objects.requireNonNull;
26 
27 import android.annotation.NonNull;
28 import android.annotation.Nullable;
29 import android.annotation.RequiresPermission;
30 import android.bluetooth.BluetoothClass;
31 import android.bluetooth.BluetoothDevice;
32 import android.bluetooth.BluetoothHeadset;
33 import android.bluetooth.BluetoothProfile;
34 import android.bluetooth.BluetoothSinkAudioPolicy;
35 import android.bluetooth.BluetoothStatusCodes;
36 import android.bluetooth.BluetoothUuid;
37 import android.bluetooth.IBluetoothHeadset;
38 import android.content.AttributionSource;
39 import android.content.BroadcastReceiver;
40 import android.content.Context;
41 import android.content.Intent;
42 import android.content.IntentFilter;
43 import android.media.AudioDeviceInfo;
44 import android.media.AudioManager;
45 import android.media.BluetoothProfileConnectionInfo;
46 import android.net.Uri;
47 import android.os.BatteryManager;
48 import android.os.Handler;
49 import android.os.HandlerThread;
50 import android.os.Looper;
51 import android.os.ParcelUuid;
52 import android.os.SystemProperties;
53 import android.os.UserHandle;
54 import android.sysprop.BluetoothProperties;
55 import android.telecom.PhoneAccount;
56 import android.util.Log;
57 
58 import com.android.bluetooth.BluetoothMetricsProto;
59 import com.android.bluetooth.BluetoothStatsLog;
60 import com.android.bluetooth.Utils;
61 import com.android.bluetooth.a2dp.A2dpService;
62 import com.android.bluetooth.btservice.AdapterService;
63 import com.android.bluetooth.btservice.AudioRoutingManager;
64 import com.android.bluetooth.btservice.MetricsLogger;
65 import com.android.bluetooth.btservice.ProfileService;
66 import com.android.bluetooth.btservice.ServiceFactory;
67 import com.android.bluetooth.btservice.storage.DatabaseManager;
68 import com.android.bluetooth.flags.Flags;
69 import com.android.bluetooth.hfpclient.HeadsetClientService;
70 import com.android.bluetooth.hfpclient.HeadsetClientStateMachine;
71 import com.android.bluetooth.le_audio.LeAudioService;
72 import com.android.bluetooth.telephony.BluetoothInCallService;
73 import com.android.internal.annotations.VisibleForTesting;
74 
75 import java.util.ArrayList;
76 import java.util.Arrays;
77 import java.util.Collections;
78 import java.util.Comparator;
79 import java.util.HashMap;
80 import java.util.List;
81 import java.util.Objects;
82 import java.util.Optional;
83 import java.util.concurrent.FutureTask;
84 
85 /**
86  * Provides Bluetooth Headset and Handsfree profile, as a service in the Bluetooth application.
87  *
88  * <p>Three modes for SCO audio: Mode 1: Telecom call through {@link #phoneStateChanged(int, int,
89  * int, String, int, String, boolean)} Mode 2: Virtual call through {@link
90  * #startScoUsingVirtualVoiceCall()} Mode 3: Voice recognition through {@link
91  * #startVoiceRecognition(BluetoothDevice)}
92  *
93  * <p>When one mode is active, other mode cannot be started. API user has to terminate existing
94  * modes using the correct API or just {@link #disconnectAudio()} if user is a system service,
95  * before starting a new mode.
96  *
97  * <p>{@link #connectAudio()} will start SCO audio at one of the above modes, but won't change mode
98  * {@link #disconnectAudio()} can happen in any mode to disconnect SCO
99  *
100  * <p>When audio is disconnected, only Mode 1 Telecom call will be persisted, both Mode 2 virtual
101  * call and Mode 3 voice call will be terminated upon SCO termination and client has to restart the
102  * mode.
103  *
104  * <p>NOTE: SCO termination can either be initiated on the AG side or the HF side TODO(b/79660380):
105  * As a workaround, voice recognition will be terminated if virtual call or Telecom call is
106  * initiated while voice recognition is ongoing, in case calling app did not call {@link
107  * #stopVoiceRecognition(BluetoothDevice)}
108  *
109  * <p>AG - Audio Gateway, device running this {@link HeadsetService}, e.g. Android Phone HF -
110  * Handsfree device, device running headset client, e.g. Wireless headphones or car kits
111  */
112 public class HeadsetService extends ProfileService {
113     private static final String TAG = "HeadsetService";
114 
115     /** HFP AG owned/managed components */
116     private static final String HFP_AG_IN_CALL_SERVICE =
117             BluetoothInCallService.class.getCanonicalName();
118 
119     private static final String DISABLE_INBAND_RINGING_PROPERTY =
120             "persist.bluetooth.disableinbandringing";
121     private static final String REJECT_SCO_IF_HFPC_CONNECTED_PROPERTY =
122             "bluetooth.hfp.reject_sco_if_hfpc_connected";
123     private static final ParcelUuid[] HEADSET_UUIDS = {BluetoothUuid.HSP, BluetoothUuid.HFP};
124     private static final int[] CONNECTING_CONNECTED_STATES = {
125         BluetoothProfile.STATE_CONNECTING, BluetoothProfile.STATE_CONNECTED
126     };
127     private static final int DIALING_OUT_TIMEOUT_MS = 10000;
128     private static final int CLCC_END_MARK_INDEX = 0;
129 
130     // Timeout for state machine thread join, to prevent potential ANR.
131     private static final int SM_THREAD_JOIN_TIMEOUT_MS = 1000;
132 
133     private final AdapterService mAdapterService;
134     private final DatabaseManager mDatabaseManager;
135     private final HeadsetNativeInterface mNativeInterface;
136     private final HashMap<BluetoothDevice, HeadsetStateMachine> mStateMachines = new HashMap<>();
137     private final Handler mHandler;
138     private final Looper mStateMachinesLooper;
139     private final Handler mStateMachinesThreadHandler;
140     private final HandlerThread mStateMachinesThread;
141     // This is also used as a lock for shared data in HeadsetService
142     private final HeadsetSystemInterface mSystemInterface;
143 
144     private int mMaxHeadsetConnections = 1;
145     private BluetoothDevice mActiveDevice;
146     private boolean mAudioRouteAllowed = true;
147     // Indicates whether SCO audio needs to be forced to open regardless ANY OTHER restrictions
148     private boolean mForceScoAudio;
149     private boolean mInbandRingingRuntimeDisable;
150     private boolean mVirtualCallStarted;
151     // Non null value indicates a pending dialing out event is going on
152     private DialingOutTimeoutEvent mDialingOutTimeoutEvent;
153     private boolean mVoiceRecognitionStarted;
154     // Non null value indicates a pending voice recognition request from headset is going on
155     private VoiceRecognitionTimeoutEvent mVoiceRecognitionTimeoutEvent;
156     // Timeout when voice recognition is started by remote device
157     @VisibleForTesting static int sStartVrTimeoutMs = 5000;
158     private ArrayList<StateMachineTask> mPendingClccResponses = new ArrayList<>();
159     private static HeadsetService sHeadsetService;
160 
161     @VisibleForTesting boolean mIsAptXSwbEnabled = false;
162     @VisibleForTesting boolean mIsAptXSwbPmEnabled = false;
163 
164     @VisibleForTesting ServiceFactory mFactory = new ServiceFactory();
165 
HeadsetService(AdapterService adapterService)166     public HeadsetService(AdapterService adapterService) {
167         this(adapterService, HeadsetNativeInterface.getInstance(), null);
168     }
169 
170     @VisibleForTesting
HeadsetService(AdapterService adapterService, HeadsetNativeInterface nativeInterface)171     HeadsetService(AdapterService adapterService, HeadsetNativeInterface nativeInterface) {
172         this(adapterService, nativeInterface, null);
173     }
174 
175     @VisibleForTesting
HeadsetService( AdapterService adapterService, HeadsetNativeInterface nativeInterface, Looper looper)176     HeadsetService(
177             AdapterService adapterService, HeadsetNativeInterface nativeInterface, Looper looper) {
178         super(requireNonNull(adapterService));
179         mAdapterService = adapterService;
180         mDatabaseManager = requireNonNull(mAdapterService.getDatabase());
181         mNativeInterface = requireNonNull(nativeInterface);
182         if (looper != null) {
183             mHandler = new Handler(looper);
184             mStateMachinesThread = null;
185             mStateMachinesLooper = looper;
186         } else {
187             mHandler = new Handler(Looper.getMainLooper());
188             mStateMachinesThread = new HandlerThread("HeadsetService.StateMachines");
189             mStateMachinesThread.start();
190             mStateMachinesLooper = mStateMachinesThread.getLooper();
191         }
192         mStateMachinesThreadHandler = new Handler(mStateMachinesLooper);
193 
194         setComponentAvailable(HFP_AG_IN_CALL_SERVICE, true);
195 
196         // Step 3: Initialize system interface
197         mSystemInterface = HeadsetObjectsFactory.getInstance().makeSystemInterface(this);
198         // Step 4: Initialize native interface
199         if (Flags.hfpCodecAptxVoice()) {
200             mIsAptXSwbEnabled =
201                     SystemProperties.getBoolean("bluetooth.hfp.codec_aptx_voice.enabled", false);
202             Log.i(TAG, "mIsAptXSwbEnabled: " + mIsAptXSwbEnabled);
203             mIsAptXSwbPmEnabled =
204                     SystemProperties.getBoolean(
205                             "bluetooth.hfp.swb.aptx.power_management.enabled", false);
206             Log.i(TAG, "mIsAptXSwbPmEnabled: " + mIsAptXSwbPmEnabled);
207         }
208         setHeadsetService(this);
209         mMaxHeadsetConnections = mAdapterService.getMaxConnectedAudioDevices();
210         // Add 1 to allow a pending device to be connecting or disconnecting
211         mNativeInterface.init(mMaxHeadsetConnections + 1, isInbandRingingEnabled());
212         if (Flags.hfpCodecAptxVoice()) {
213             enableSwbCodec(
214                     HeadsetHalConstants.BTHF_SWB_CODEC_VENDOR_APTX,
215                     mIsAptXSwbEnabled,
216                     mActiveDevice);
217         }
218         // Step 6: Setup broadcast receivers
219         IntentFilter filter = new IntentFilter();
220         filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
221         filter.addAction(Intent.ACTION_BATTERY_CHANGED);
222         filter.addAction(AudioManager.ACTION_VOLUME_CHANGED);
223         filter.addAction(BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY);
224         registerReceiver(mHeadsetReceiver, filter);
225     }
226 
isEnabled()227     public static boolean isEnabled() {
228         return BluetoothProperties.isProfileHfpAgEnabled().orElse(false);
229     }
230 
231     @Override
initBinder()232     public IProfileServiceBinder initBinder() {
233         return new BluetoothHeadsetBinder(this);
234     }
235 
236     @Override
stop()237     public void stop() {
238         Log.i(TAG, "stop()");
239         // Step 6: Tear down broadcast receivers
240         unregisterReceiver(mHeadsetReceiver);
241         synchronized (mStateMachines) {
242             // Reset active device to null
243             mActiveDevice = null;
244             mInbandRingingRuntimeDisable = false;
245             mForceScoAudio = false;
246             mAudioRouteAllowed = true;
247             mMaxHeadsetConnections = 1;
248             mVoiceRecognitionStarted = false;
249             mVirtualCallStarted = false;
250             if (mDialingOutTimeoutEvent != null) {
251                 mStateMachinesThreadHandler.removeCallbacks(mDialingOutTimeoutEvent);
252                 mDialingOutTimeoutEvent = null;
253             }
254             if (mVoiceRecognitionTimeoutEvent != null) {
255                 mStateMachinesThreadHandler.removeCallbacks(mVoiceRecognitionTimeoutEvent);
256                 mVoiceRecognitionTimeoutEvent = null;
257                 if (mSystemInterface.getVoiceRecognitionWakeLock().isHeld()) {
258                     mSystemInterface.getVoiceRecognitionWakeLock().release();
259                 }
260             }
261             // Step 5: Destroy state machines
262             for (HeadsetStateMachine stateMachine : mStateMachines.values()) {
263                 HeadsetObjectsFactory.getInstance().destroyStateMachine(stateMachine);
264             }
265             mStateMachines.clear();
266         }
267         // Step 4: Destroy native interface
268         mNativeInterface.cleanup();
269         setHeadsetService(null);
270         // Step 3: Destroy system interface
271         mSystemInterface.stop();
272         // Step 2: Stop handler thread
273         if (mStateMachinesThread != null) {
274             try {
275                 mStateMachinesThread.quitSafely();
276                 mStateMachinesThread.join(SM_THREAD_JOIN_TIMEOUT_MS);
277             } catch (InterruptedException e) {
278                 // Do not rethrow as we are shutting down anyway
279             }
280         }
281 
282         // Unregister Handler and stop all queued messages.
283         mHandler.removeCallbacksAndMessages(null);
284 
285         // Step 1: Clear
286         setComponentAvailable(HFP_AG_IN_CALL_SERVICE, false);
287     }
288 
289     @Override
cleanup()290     public void cleanup() {
291         Log.i(TAG, "cleanup");
292     }
293 
294     /**
295      * Checks if this service object is able to accept binder calls
296      *
297      * @return True if the object can accept binder calls, False otherwise
298      */
isAlive()299     public boolean isAlive() {
300         return isAvailable();
301     }
302 
303     /**
304      * Get the {@link Looper} for the state machine thread. This is used in testing and helper
305      * objects
306      *
307      * @return {@link Looper} for the state machine thread
308      */
309     @VisibleForTesting
getStateMachinesThreadLooper()310     public Looper getStateMachinesThreadLooper() {
311         return mStateMachinesThread.getLooper();
312     }
313 
314     interface StateMachineTask {
execute(HeadsetStateMachine stateMachine)315         void execute(HeadsetStateMachine stateMachine);
316     }
317 
doForStateMachine(BluetoothDevice device, StateMachineTask task)318     private boolean doForStateMachine(BluetoothDevice device, StateMachineTask task) {
319         synchronized (mStateMachines) {
320             HeadsetStateMachine stateMachine = mStateMachines.get(device);
321             if (stateMachine == null) {
322                 return false;
323             }
324             task.execute(stateMachine);
325         }
326         return true;
327     }
328 
doForEachConnectedStateMachine(StateMachineTask task)329     private void doForEachConnectedStateMachine(StateMachineTask task) {
330         synchronized (mStateMachines) {
331             for (BluetoothDevice device : getConnectedDevices()) {
332                 task.execute(mStateMachines.get(device));
333             }
334         }
335     }
336 
doForEachConnectedStateMachine(List<StateMachineTask> tasks)337     private void doForEachConnectedStateMachine(List<StateMachineTask> tasks) {
338         synchronized (mStateMachines) {
339             for (BluetoothDevice device : getConnectedDevices()) {
340                 for (StateMachineTask task : tasks) {
341                     task.execute(mStateMachines.get(device));
342                 }
343             }
344         }
345     }
346 
onDeviceStateChanged(HeadsetDeviceState deviceState)347     void onDeviceStateChanged(HeadsetDeviceState deviceState) {
348         doForEachConnectedStateMachine(
349                 stateMachine ->
350                         stateMachine.sendMessage(
351                                 HeadsetStateMachine.DEVICE_STATE_CHANGED, deviceState));
352     }
353 
354     /**
355      * Handle messages from native (JNI) to Java. This needs to be synchronized to avoid posting
356      * messages to state machine before start() is done
357      *
358      * @param stackEvent event from native stack
359      */
messageFromNative(HeadsetStackEvent stackEvent)360     void messageFromNative(HeadsetStackEvent stackEvent) {
361         Objects.requireNonNull(
362                 stackEvent.device, "Device should never be null, event: " + stackEvent);
363         synchronized (mStateMachines) {
364             HeadsetStateMachine stateMachine = mStateMachines.get(stackEvent.device);
365             if (stackEvent.type == HeadsetStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED) {
366                 switch (stackEvent.valueInt) {
367                     case HeadsetHalConstants.CONNECTION_STATE_CONNECTED:
368                     case HeadsetHalConstants.CONNECTION_STATE_CONNECTING:
369                         {
370                             // Create new state machine if none is found
371                             if (stateMachine == null) {
372                                 stateMachine =
373                                         HeadsetObjectsFactory.getInstance()
374                                                 .makeStateMachine(
375                                                         stackEvent.device,
376                                                         mStateMachinesLooper,
377                                                         this,
378                                                         mAdapterService,
379                                                         mNativeInterface,
380                                                         mSystemInterface);
381                                 mStateMachines.put(stackEvent.device, stateMachine);
382                             }
383                             break;
384                         }
385                 }
386             }
387             if (stateMachine == null) {
388                 throw new IllegalStateException(
389                         "State machine not found for stack event: " + stackEvent);
390             }
391             stateMachine.sendMessage(HeadsetStateMachine.STACK_EVENT, stackEvent);
392         }
393     }
394 
395     private final BroadcastReceiver mHeadsetReceiver =
396             new BroadcastReceiver() {
397                 @Override
398                 public void onReceive(Context context, Intent intent) {
399                     String action = intent.getAction();
400                     if (action == null) {
401                         Log.w(TAG, "mHeadsetReceiver, action is null");
402                         return;
403                     }
404                     switch (action) {
405                         case Intent.ACTION_BATTERY_CHANGED:
406                             {
407                                 int batteryLevel =
408                                         intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);
409                                 int scale = intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1);
410                                 if (batteryLevel < 0 || scale <= 0) {
411                                     Log.e(
412                                             TAG,
413                                             "Bad Battery Changed intent: batteryLevel="
414                                                     + batteryLevel
415                                                     + ", scale="
416                                                     + scale);
417                                     return;
418                                 }
419                                 int cindBatteryLevel =
420                                         Math.round(batteryLevel * 5 / ((float) scale));
421                                 mSystemInterface
422                                         .getHeadsetPhoneState()
423                                         .setCindBatteryCharge(cindBatteryLevel);
424                                 break;
425                             }
426                         case AudioManager.ACTION_VOLUME_CHANGED:
427                             {
428                                 int streamType =
429                                         intent.getIntExtra(
430                                                 AudioManager.EXTRA_VOLUME_STREAM_TYPE, -1);
431                                 if (streamType == AudioManager.STREAM_BLUETOOTH_SCO) {
432                                     doForEachConnectedStateMachine(
433                                             stateMachine ->
434                                                     stateMachine.sendMessage(
435                                                             HeadsetStateMachine
436                                                                     .INTENT_SCO_VOLUME_CHANGED,
437                                                             intent));
438                                 }
439                                 break;
440                             }
441                         case BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY:
442                             {
443                                 int requestType =
444                                         intent.getIntExtra(
445                                                 BluetoothDevice.EXTRA_ACCESS_REQUEST_TYPE,
446                                                 BluetoothDevice.REQUEST_TYPE_PHONEBOOK_ACCESS);
447                                 BluetoothDevice device =
448                                         intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
449                                 logD(
450                                         "Received BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY,"
451                                                 + " device="
452                                                 + device
453                                                 + ", type="
454                                                 + requestType);
455                                 if (requestType == BluetoothDevice.REQUEST_TYPE_PHONEBOOK_ACCESS) {
456                                     synchronized (mStateMachines) {
457                                         final HeadsetStateMachine stateMachine =
458                                                 mStateMachines.get(device);
459                                         if (stateMachine == null) {
460                                             Log.wtf(TAG, "Cannot find state machine for " + device);
461                                             return;
462                                         }
463                                         stateMachine.sendMessage(
464                                                 HeadsetStateMachine.INTENT_CONNECTION_ACCESS_REPLY,
465                                                 intent);
466                                     }
467                                 }
468                                 break;
469                             }
470                         default:
471                             Log.w(TAG, "Unknown action " + action);
472                     }
473                 }
474             };
475 
handleBondStateChanged(BluetoothDevice device, int fromState, int toState)476     public void handleBondStateChanged(BluetoothDevice device, int fromState, int toState) {
477         mHandler.post(() -> bondStateChanged(device, toState));
478     }
479 
bondStateChanged(BluetoothDevice device, int state)480     private void bondStateChanged(BluetoothDevice device, int state) {
481         logD("Bond state changed for device: " + device + " state: " + state);
482         if (state != BluetoothDevice.BOND_NONE) {
483             return;
484         }
485         synchronized (mStateMachines) {
486             HeadsetStateMachine stateMachine = mStateMachines.get(device);
487             if (stateMachine == null) {
488                 return;
489             }
490             if (stateMachine.getConnectionState() != BluetoothProfile.STATE_DISCONNECTED) {
491                 return;
492             }
493             removeStateMachine(device);
494         }
495     }
496 
497     /** Handlers for incoming service calls */
498     @VisibleForTesting
499     static class BluetoothHeadsetBinder extends IBluetoothHeadset.Stub
500             implements IProfileServiceBinder {
501         private volatile HeadsetService mService;
502 
BluetoothHeadsetBinder(HeadsetService svc)503         BluetoothHeadsetBinder(HeadsetService svc) {
504             mService = svc;
505         }
506 
507         @Override
cleanup()508         public void cleanup() {
509             mService = null;
510         }
511 
512         @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
getService(AttributionSource source)513         private HeadsetService getService(AttributionSource source) {
514             if (Utils.isInstrumentationTestMode()) {
515                 return mService;
516             }
517             if (!Utils.checkCallerIsSystemOrActiveOrManagedUser(mService, TAG)
518                     || !Utils.checkServiceAvailable(mService, TAG)
519                     || !Utils.checkConnectPermissionForDataDelivery(mService, source, TAG)) {
520                 return null;
521             }
522             return mService;
523         }
524 
525         @Override
connect(BluetoothDevice device, AttributionSource source)526         public boolean connect(BluetoothDevice device, AttributionSource source) {
527             HeadsetService service = getService(source);
528             if (service == null) {
529                 return false;
530             }
531 
532             return service.connect(device);
533         }
534 
535         @Override
disconnect(BluetoothDevice device, AttributionSource source)536         public boolean disconnect(BluetoothDevice device, AttributionSource source) {
537             HeadsetService service = getService(source);
538             if (service == null) {
539                 return false;
540             }
541 
542             return service.disconnect(device);
543         }
544 
545         @Override
getConnectedDevices(AttributionSource source)546         public List<BluetoothDevice> getConnectedDevices(AttributionSource source) {
547             HeadsetService service = getService(source);
548             if (service == null) {
549                 return Collections.emptyList();
550             }
551 
552             return service.getConnectedDevices();
553         }
554 
555         @Override
getDevicesMatchingConnectionStates( int[] states, AttributionSource source)556         public List<BluetoothDevice> getDevicesMatchingConnectionStates(
557                 int[] states, AttributionSource source) {
558             HeadsetService service = getService(source);
559             if (service == null) {
560                 return Collections.emptyList();
561             }
562 
563             return service.getDevicesMatchingConnectionStates(states);
564         }
565 
566         @Override
getConnectionState(BluetoothDevice device, AttributionSource source)567         public int getConnectionState(BluetoothDevice device, AttributionSource source) {
568             HeadsetService service = getService(source);
569             if (service == null) {
570                 return BluetoothProfile.STATE_DISCONNECTED;
571             }
572 
573             return service.getConnectionState(device);
574         }
575 
576         @Override
setConnectionPolicy( BluetoothDevice device, int connectionPolicy, AttributionSource source)577         public boolean setConnectionPolicy(
578                 BluetoothDevice device, int connectionPolicy, AttributionSource source) {
579             HeadsetService service = getService(source);
580             if (service == null) {
581                 return false;
582             }
583 
584             enforceBluetoothPrivilegedPermission(service);
585             return service.setConnectionPolicy(device, connectionPolicy);
586         }
587 
588         @Override
getConnectionPolicy(BluetoothDevice device, AttributionSource source)589         public int getConnectionPolicy(BluetoothDevice device, AttributionSource source) {
590             HeadsetService service = getService(source);
591             if (service == null) {
592                 return BluetoothProfile.CONNECTION_POLICY_UNKNOWN;
593             }
594 
595             enforceBluetoothPrivilegedPermission(service);
596             return service.getConnectionPolicy(device);
597         }
598 
599         @Override
isNoiseReductionSupported(BluetoothDevice device, AttributionSource source)600         public boolean isNoiseReductionSupported(BluetoothDevice device, AttributionSource source) {
601             HeadsetService service = getService(source);
602             if (service == null) {
603                 return false;
604             }
605 
606             return service.isNoiseReductionSupported(device);
607         }
608 
609         @Override
isVoiceRecognitionSupported( BluetoothDevice device, AttributionSource source)610         public boolean isVoiceRecognitionSupported(
611                 BluetoothDevice device, AttributionSource source) {
612             HeadsetService service = getService(source);
613             if (service == null) {
614                 return false;
615             }
616 
617             return service.isVoiceRecognitionSupported(device);
618         }
619 
620         @Override
startVoiceRecognition(BluetoothDevice device, AttributionSource source)621         public boolean startVoiceRecognition(BluetoothDevice device, AttributionSource source) {
622             HeadsetService service = getService(source);
623             if (service == null) {
624                 return false;
625             }
626 
627             return service.startVoiceRecognition(device);
628         }
629 
630         @Override
stopVoiceRecognition(BluetoothDevice device, AttributionSource source)631         public boolean stopVoiceRecognition(BluetoothDevice device, AttributionSource source) {
632             HeadsetService service = getService(source);
633             if (service == null) {
634                 return false;
635             }
636 
637             return service.stopVoiceRecognition(device);
638         }
639 
640         @Override
isAudioOn(AttributionSource source)641         public boolean isAudioOn(AttributionSource source) {
642             HeadsetService service = getService(source);
643             if (service == null) {
644                 return false;
645             }
646 
647             return service.isAudioOn();
648         }
649 
650         @Override
isAudioConnected(BluetoothDevice device, AttributionSource source)651         public boolean isAudioConnected(BluetoothDevice device, AttributionSource source) {
652             HeadsetService service = getService(source);
653             if (service == null) {
654                 return false;
655             }
656 
657             return service.isAudioConnected(device);
658         }
659 
660         @Override
getAudioState(BluetoothDevice device, AttributionSource source)661         public int getAudioState(BluetoothDevice device, AttributionSource source) {
662             HeadsetService service = getService(source);
663             if (service == null) {
664                 return BluetoothHeadset.STATE_AUDIO_DISCONNECTED;
665             }
666 
667             enforceBluetoothPrivilegedPermission(service);
668             return service.getAudioState(device);
669         }
670 
671         @Override
connectAudio(AttributionSource source)672         public int connectAudio(AttributionSource source) {
673             HeadsetService service = getService(source);
674             if (service == null) {
675                 return BluetoothStatusCodes.ERROR_PROFILE_SERVICE_NOT_BOUND;
676             }
677 
678             enforceBluetoothPrivilegedPermission(service);
679             return service.connectAudio();
680         }
681 
682         @Override
disconnectAudio(AttributionSource source)683         public int disconnectAudio(AttributionSource source) {
684             HeadsetService service = getService(source);
685             if (service == null) {
686                 return BluetoothStatusCodes.ERROR_PROFILE_SERVICE_NOT_BOUND;
687             }
688 
689             enforceBluetoothPrivilegedPermission(service);
690             return service.disconnectAudio();
691         }
692 
693         @Override
setAudioRouteAllowed(boolean allowed, AttributionSource source)694         public void setAudioRouteAllowed(boolean allowed, AttributionSource source) {
695             HeadsetService service = getService(source);
696             if (service == null) {
697                 return;
698             }
699 
700             enforceBluetoothPrivilegedPermission(service);
701             service.setAudioRouteAllowed(allowed);
702         }
703 
704         @Override
getAudioRouteAllowed(AttributionSource source)705         public boolean getAudioRouteAllowed(AttributionSource source) {
706             HeadsetService service = getService(source);
707             if (service == null) {
708                 return false;
709             }
710 
711             enforceBluetoothPrivilegedPermission(service);
712             return service.getAudioRouteAllowed();
713         }
714 
715         @Override
setForceScoAudio(boolean forced, AttributionSource source)716         public void setForceScoAudio(boolean forced, AttributionSource source) {
717             HeadsetService service = getService(source);
718             if (service == null) {
719                 return;
720             }
721 
722             service.setForceScoAudio(forced);
723         }
724 
725         @Override
startScoUsingVirtualVoiceCall(AttributionSource source)726         public boolean startScoUsingVirtualVoiceCall(AttributionSource source) {
727             HeadsetService service = getService(source);
728             if (service == null) {
729                 return false;
730             }
731 
732             enforceBluetoothPrivilegedPermission(service);
733             return service.startScoUsingVirtualVoiceCall();
734         }
735 
736         @Override
stopScoUsingVirtualVoiceCall(AttributionSource source)737         public boolean stopScoUsingVirtualVoiceCall(AttributionSource source) {
738             HeadsetService service = getService(source);
739             if (service == null) {
740                 return false;
741             }
742 
743             enforceBluetoothPrivilegedPermission(service);
744             return service.stopScoUsingVirtualVoiceCall();
745         }
746 
747         @Override
phoneStateChanged( int numActive, int numHeld, int callState, String number, int type, String name, AttributionSource source)748         public void phoneStateChanged(
749                 int numActive,
750                 int numHeld,
751                 int callState,
752                 String number,
753                 int type,
754                 String name,
755                 AttributionSource source) {
756             HeadsetService service = getService(source);
757             if (service == null) {
758                 return;
759             }
760 
761             service.phoneStateChanged(numActive, numHeld, callState, number, type, name, false);
762         }
763 
764         @Override
clccResponse( int index, int direction, int status, int mode, boolean mpty, String number, int type, AttributionSource source)765         public void clccResponse(
766                 int index,
767                 int direction,
768                 int status,
769                 int mode,
770                 boolean mpty,
771                 String number,
772                 int type,
773                 AttributionSource source) {
774             HeadsetService service = getService(source);
775             if (service == null) {
776                 return;
777             }
778 
779             service.clccResponse(index, direction, status, mode, mpty, number, type);
780         }
781 
782         @Override
sendVendorSpecificResultCode( BluetoothDevice device, String command, String arg, AttributionSource source)783         public boolean sendVendorSpecificResultCode(
784                 BluetoothDevice device, String command, String arg, AttributionSource source) {
785             HeadsetService service = getService(source);
786             if (service == null) {
787                 return false;
788             }
789 
790             return service.sendVendorSpecificResultCode(device, command, arg);
791         }
792 
793         @Override
setActiveDevice(BluetoothDevice device, AttributionSource source)794         public boolean setActiveDevice(BluetoothDevice device, AttributionSource source) {
795             HeadsetService service = getService(source);
796             if (service == null) {
797                 return false;
798             }
799 
800             if (Flags.audioRoutingCentralization()) {
801                 return ((AudioRoutingManager) service.mAdapterService.getActiveDeviceManager())
802                         .activateDeviceProfile(device, BluetoothProfile.HEADSET)
803                         .join();
804             }
805 
806             return service.setActiveDevice(device);
807         }
808 
809         @Override
getActiveDevice(AttributionSource source)810         public BluetoothDevice getActiveDevice(AttributionSource source) {
811             HeadsetService service = getService(source);
812             if (service == null) {
813                 return null;
814             }
815 
816             return service.getActiveDevice();
817         }
818 
819         @Override
isInbandRingingEnabled(AttributionSource source)820         public boolean isInbandRingingEnabled(AttributionSource source) {
821             HeadsetService service = getService(source);
822             if (service == null) {
823                 return false;
824             }
825 
826             enforceBluetoothPrivilegedPermission(service);
827             return service.isInbandRingingEnabled();
828         }
829     }
830 
831     // API methods
getHeadsetService()832     public static synchronized HeadsetService getHeadsetService() {
833         if (sHeadsetService == null) {
834             Log.w(TAG, "getHeadsetService(): service is NULL");
835             return null;
836         }
837         if (!sHeadsetService.isAvailable()) {
838             Log.w(TAG, "getHeadsetService(): service is not available");
839             return null;
840         }
841         logD("getHeadsetService(): returning " + sHeadsetService);
842         return sHeadsetService;
843     }
844 
setHeadsetService(HeadsetService instance)845     private static synchronized void setHeadsetService(HeadsetService instance) {
846         logD("setHeadsetService(): set to: " + instance);
847         sHeadsetService = instance;
848     }
849 
850     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
connect(BluetoothDevice device)851     public boolean connect(BluetoothDevice device) {
852         if (getConnectionPolicy(device) == BluetoothProfile.CONNECTION_POLICY_FORBIDDEN) {
853             Log.w(
854                     TAG,
855                     "connect: CONNECTION_POLICY_FORBIDDEN, device="
856                             + device
857                             + ", "
858                             + Utils.getUidPidString());
859             return false;
860         }
861         ParcelUuid[] featureUuids = mAdapterService.getRemoteUuids(device);
862         if (!BluetoothUuid.containsAnyUuid(featureUuids, HEADSET_UUIDS)) {
863             Log.e(
864                     TAG,
865                     "connect: Cannot connect to "
866                             + device
867                             + ": no headset UUID, "
868                             + Utils.getUidPidString());
869             return false;
870         }
871         synchronized (mStateMachines) {
872             Log.i(TAG, "connect: device=" + device + ", " + Utils.getUidPidString());
873             HeadsetStateMachine stateMachine = mStateMachines.get(device);
874             if (stateMachine == null) {
875                 stateMachine =
876                         HeadsetObjectsFactory.getInstance()
877                                 .makeStateMachine(
878                                         device,
879                                         mStateMachinesLooper,
880                                         this,
881                                         mAdapterService,
882                                         mNativeInterface,
883                                         mSystemInterface);
884                 mStateMachines.put(device, stateMachine);
885             }
886             int connectionState = stateMachine.getConnectionState();
887             if (connectionState == BluetoothProfile.STATE_CONNECTED
888                     || connectionState == BluetoothProfile.STATE_CONNECTING) {
889                 Log.w(
890                         TAG,
891                         "connect: device "
892                                 + device
893                                 + " is already connected/connecting, connectionState="
894                                 + connectionState);
895                 return false;
896             }
897             List<BluetoothDevice> connectingConnectedDevices =
898                     getDevicesMatchingConnectionStates(CONNECTING_CONNECTED_STATES);
899             boolean disconnectExisting = false;
900             if (connectingConnectedDevices.size() >= mMaxHeadsetConnections) {
901                 // When there is maximum one device, we automatically disconnect the current one
902                 if (mMaxHeadsetConnections == 1) {
903                     disconnectExisting = true;
904                 } else {
905                     Log.w(TAG, "Max connection has reached, rejecting connection to " + device);
906                     return false;
907                 }
908             }
909             if (disconnectExisting) {
910                 for (BluetoothDevice connectingConnectedDevice : connectingConnectedDevices) {
911                     disconnect(connectingConnectedDevice);
912                 }
913                 setActiveDevice(null);
914             }
915             stateMachine.sendMessage(HeadsetStateMachine.CONNECT, device);
916         }
917         return true;
918     }
919 
920     /**
921      * Disconnects hfp from the passed in device
922      *
923      * @param device is the device with which we will disconnect hfp
924      * @return true if hfp is disconnected, false if the device is not connected
925      */
disconnect(BluetoothDevice device)926     public boolean disconnect(BluetoothDevice device) {
927         Log.i(TAG, "disconnect: device=" + device + ", " + Utils.getUidPidString());
928         synchronized (mStateMachines) {
929             HeadsetStateMachine stateMachine = mStateMachines.get(device);
930             if (stateMachine == null) {
931                 Log.w(TAG, "disconnect: device " + device + " not ever connected/connecting");
932                 return false;
933             }
934             int connectionState = stateMachine.getConnectionState();
935             if (connectionState != BluetoothProfile.STATE_CONNECTED
936                     && connectionState != BluetoothProfile.STATE_CONNECTING) {
937                 Log.w(
938                         TAG,
939                         "disconnect: device "
940                                 + device
941                                 + " not connected/connecting, connectionState="
942                                 + connectionState);
943                 return false;
944             }
945             stateMachine.sendMessage(HeadsetStateMachine.DISCONNECT, device);
946         }
947         return true;
948     }
949 
getConnectedDevices()950     public List<BluetoothDevice> getConnectedDevices() {
951         ArrayList<BluetoothDevice> devices = new ArrayList<>();
952         synchronized (mStateMachines) {
953             for (HeadsetStateMachine stateMachine : mStateMachines.values()) {
954                 if (stateMachine.getConnectionState() == BluetoothProfile.STATE_CONNECTED) {
955                     devices.add(stateMachine.getDevice());
956                 }
957             }
958         }
959         return devices;
960     }
961 
962     /**
963      * Same as the API method {@link BluetoothHeadset#getDevicesMatchingConnectionStates(int[])}
964      *
965      * @param states an array of states from {@link BluetoothProfile}
966      * @return a list of devices matching the array of connection states
967      */
968     @VisibleForTesting
getDevicesMatchingConnectionStates(int[] states)969     public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
970         ArrayList<BluetoothDevice> devices = new ArrayList<>();
971         synchronized (mStateMachines) {
972             if (states == null) {
973                 return devices;
974             }
975             final BluetoothDevice[] bondedDevices = mAdapterService.getBondedDevices();
976             if (bondedDevices == null) {
977                 return devices;
978             }
979             for (BluetoothDevice device : bondedDevices) {
980                 final ParcelUuid[] featureUuids = mAdapterService.getRemoteUuids(device);
981                 if (!BluetoothUuid.containsAnyUuid(featureUuids, HEADSET_UUIDS)) {
982                     continue;
983                 }
984                 int connectionState = getConnectionState(device);
985                 for (int state : states) {
986                     if (connectionState == state) {
987                         devices.add(device);
988                         break;
989                     }
990                 }
991             }
992         }
993         return devices;
994     }
995 
getConnectionState(BluetoothDevice device)996     public int getConnectionState(BluetoothDevice device) {
997         synchronized (mStateMachines) {
998             final HeadsetStateMachine stateMachine = mStateMachines.get(device);
999             if (stateMachine == null) {
1000                 return BluetoothProfile.STATE_DISCONNECTED;
1001             }
1002             return stateMachine.getConnectionState();
1003         }
1004     }
1005 
1006     /**
1007      * Set connection policy of the profile and connects it if connectionPolicy is {@link
1008      * BluetoothProfile#CONNECTION_POLICY_ALLOWED} or disconnects if connectionPolicy is {@link
1009      * BluetoothProfile#CONNECTION_POLICY_FORBIDDEN}
1010      *
1011      * <p>The device should already be paired. Connection policy can be one of: {@link
1012      * BluetoothProfile#CONNECTION_POLICY_ALLOWED}, {@link
1013      * BluetoothProfile#CONNECTION_POLICY_FORBIDDEN}, {@link
1014      * BluetoothProfile#CONNECTION_POLICY_UNKNOWN}
1015      *
1016      * @param device Paired bluetooth device
1017      * @param connectionPolicy is the connection policy to set to for this profile
1018      * @return true if connectionPolicy is set, false on error
1019      */
1020     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
setConnectionPolicy(BluetoothDevice device, int connectionPolicy)1021     public boolean setConnectionPolicy(BluetoothDevice device, int connectionPolicy) {
1022         Log.i(
1023                 TAG,
1024                 "setConnectionPolicy: device="
1025                         + device
1026                         + ", connectionPolicy="
1027                         + connectionPolicy
1028                         + ", "
1029                         + Utils.getUidPidString());
1030 
1031         if (!mDatabaseManager.setProfileConnectionPolicy(
1032                 device, BluetoothProfile.HEADSET, connectionPolicy)) {
1033             return false;
1034         }
1035         if (connectionPolicy == BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
1036             connect(device);
1037         } else if (connectionPolicy == BluetoothProfile.CONNECTION_POLICY_FORBIDDEN) {
1038             disconnect(device);
1039         }
1040         return true;
1041     }
1042 
1043     /**
1044      * Get the connection policy of the profile.
1045      *
1046      * <p>The connection policy can be any of: {@link BluetoothProfile#CONNECTION_POLICY_ALLOWED},
1047      * {@link BluetoothProfile#CONNECTION_POLICY_FORBIDDEN}, {@link
1048      * BluetoothProfile#CONNECTION_POLICY_UNKNOWN}
1049      *
1050      * @param device Bluetooth device
1051      * @return connection policy of the device
1052      */
getConnectionPolicy(BluetoothDevice device)1053     public int getConnectionPolicy(BluetoothDevice device) {
1054         return mDatabaseManager.getProfileConnectionPolicy(device, BluetoothProfile.HEADSET);
1055     }
1056 
isNoiseReductionSupported(BluetoothDevice device)1057     boolean isNoiseReductionSupported(BluetoothDevice device) {
1058         return mNativeInterface.isNoiseReductionSupported(device);
1059     }
1060 
isVoiceRecognitionSupported(BluetoothDevice device)1061     boolean isVoiceRecognitionSupported(BluetoothDevice device) {
1062         return mNativeInterface.isVoiceRecognitionSupported(device);
1063     }
1064 
1065     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
startVoiceRecognition(BluetoothDevice device)1066     boolean startVoiceRecognition(BluetoothDevice device) {
1067         Log.i(TAG, "startVoiceRecognition: device=" + device + ", " + Utils.getUidPidString());
1068         synchronized (mStateMachines) {
1069             // TODO(b/79660380): Workaround in case voice recognition was not terminated properly
1070             if (mVoiceRecognitionStarted) {
1071                 boolean status = stopVoiceRecognition(mActiveDevice);
1072                 Log.w(
1073                         TAG,
1074                         "startVoiceRecognition: voice recognition is still active, just called "
1075                                 + "stopVoiceRecognition, returned "
1076                                 + status
1077                                 + " on "
1078                                 + mActiveDevice
1079                                 + ", please try again");
1080                 mVoiceRecognitionStarted = false;
1081                 return false;
1082             }
1083             if (!isAudioModeIdle()) {
1084                 Log.w(
1085                         TAG,
1086                         "startVoiceRecognition: audio mode not idle, active device is "
1087                                 + mActiveDevice);
1088                 return false;
1089             }
1090             // Audio should not be on when no audio mode is active
1091             if (isAudioOn()) {
1092                 // Disconnect audio so that API user can try later
1093                 int status = disconnectAudio();
1094                 Log.w(
1095                         TAG,
1096                         "startVoiceRecognition: audio is still active, please wait for audio to"
1097                                 + " be disconnected, disconnectAudio() returned "
1098                                 + status
1099                                 + ", active device is "
1100                                 + mActiveDevice);
1101                 return false;
1102             }
1103             if (device == null) {
1104                 Log.i(TAG, "device is null, use active device " + mActiveDevice + " instead");
1105                 device = mActiveDevice;
1106             }
1107             boolean pendingRequestByHeadset = false;
1108             if (mVoiceRecognitionTimeoutEvent != null) {
1109                 if (!mVoiceRecognitionTimeoutEvent.mVoiceRecognitionDevice.equals(device)) {
1110                     // TODO(b/79660380): Workaround when target device != requesting device
1111                     Log.w(
1112                             TAG,
1113                             "startVoiceRecognition: device "
1114                                     + device
1115                                     + " is not the same as requesting device "
1116                                     + mVoiceRecognitionTimeoutEvent.mVoiceRecognitionDevice
1117                                     + ", fall back to requesting device");
1118                     device = mVoiceRecognitionTimeoutEvent.mVoiceRecognitionDevice;
1119                 }
1120                 mStateMachinesThreadHandler.removeCallbacks(mVoiceRecognitionTimeoutEvent);
1121                 mVoiceRecognitionTimeoutEvent = null;
1122                 if (mSystemInterface.getVoiceRecognitionWakeLock().isHeld()) {
1123                     mSystemInterface.getVoiceRecognitionWakeLock().release();
1124                 }
1125                 pendingRequestByHeadset = true;
1126             }
1127             if (!Objects.equals(device, mActiveDevice) && !setActiveDevice(device)) {
1128                 Log.w(TAG, "startVoiceRecognition: failed to set " + device + " as active");
1129                 return false;
1130             }
1131             final HeadsetStateMachine stateMachine = mStateMachines.get(device);
1132             if (stateMachine == null) {
1133                 Log.w(TAG, "startVoiceRecognition: " + device + " is never connected");
1134                 return false;
1135             }
1136             int connectionState = stateMachine.getConnectionState();
1137             if (connectionState != BluetoothProfile.STATE_CONNECTED
1138                     && connectionState != BluetoothProfile.STATE_CONNECTING) {
1139                 Log.w(TAG, "startVoiceRecognition: " + device + " is not connected or connecting");
1140                 return false;
1141             }
1142             if (SystemProperties.getBoolean(REJECT_SCO_IF_HFPC_CONNECTED_PROPERTY, false)
1143                     && isHeadsetClientConnected()) {
1144                 Log.w(TAG, "startVoiceRecognition: rejected SCO since HFPC is connected!");
1145                 return false;
1146             }
1147             mVoiceRecognitionStarted = true;
1148             if (pendingRequestByHeadset) {
1149                 stateMachine.sendMessage(
1150                         HeadsetStateMachine.VOICE_RECOGNITION_RESULT, 1 /* success */, 0, device);
1151             } else {
1152                 stateMachine.sendMessage(HeadsetStateMachine.VOICE_RECOGNITION_START, device);
1153             }
1154             if (Utils.isScoManagedByAudioEnabled()) {
1155                 // when isScoManagedByAudio is on, tell AudioManager to connect SCO
1156                 AudioManager am = mSystemInterface.getAudioManager();
1157                 BluetoothDevice finalDevice = device;
1158                 Optional<AudioDeviceInfo> audioDeviceInfo =
1159                         am.getAvailableCommunicationDevices().stream()
1160                                 .filter(
1161                                         x ->
1162                                                 x.getType() == AudioDeviceInfo.TYPE_BLUETOOTH_SCO
1163                                                         && x.getAddress()
1164                                                                 .equals(finalDevice.getAddress()))
1165                                 .findFirst();
1166                 if (audioDeviceInfo.isPresent()) {
1167                     am.setCommunicationDevice(audioDeviceInfo.get());
1168                     Log.i(TAG, "Audio Manager will initiate the SCO connection");
1169                     return true;
1170                 }
1171                 Log.w(
1172                         TAG,
1173                         "Cannot find audioDeviceInfo that matches device="
1174                                 + device
1175                                 + " to create the SCO");
1176                 return false;
1177             }
1178             stateMachine.sendMessage(HeadsetStateMachine.CONNECT_AUDIO, device);
1179         }
1180         if (Flags.hfpCodecAptxVoice()) {
1181             enableSwbCodec(HeadsetHalConstants.BTHF_SWB_CODEC_VENDOR_APTX, true, device);
1182         }
1183         return true;
1184     }
1185 
stopVoiceRecognition(BluetoothDevice device)1186     boolean stopVoiceRecognition(BluetoothDevice device) {
1187         Log.i(TAG, "stopVoiceRecognition: device=" + device + ", " + Utils.getUidPidString());
1188         synchronized (mStateMachines) {
1189             if (!Objects.equals(mActiveDevice, device)) {
1190                 Log.w(
1191                         TAG,
1192                         "startVoiceRecognition: requested device "
1193                                 + device
1194                                 + " is not active, use active device "
1195                                 + mActiveDevice
1196                                 + " instead");
1197                 device = mActiveDevice;
1198             }
1199             final HeadsetStateMachine stateMachine = mStateMachines.get(device);
1200             if (stateMachine == null) {
1201                 Log.w(TAG, "stopVoiceRecognition: " + device + " is never connected");
1202                 return false;
1203             }
1204             int connectionState = stateMachine.getConnectionState();
1205             if (connectionState != BluetoothProfile.STATE_CONNECTED
1206                     && connectionState != BluetoothProfile.STATE_CONNECTING) {
1207                 Log.w(TAG, "stopVoiceRecognition: " + device + " is not connected or connecting");
1208                 return false;
1209             }
1210             if (!mVoiceRecognitionStarted) {
1211                 Log.w(TAG, "stopVoiceRecognition: voice recognition was not started");
1212                 return false;
1213             }
1214             mVoiceRecognitionStarted = false;
1215             stateMachine.sendMessage(HeadsetStateMachine.VOICE_RECOGNITION_STOP, device);
1216             if (Utils.isScoManagedByAudioEnabled()) {
1217                 mSystemInterface.getAudioManager().clearCommunicationDevice();
1218                 return true;
1219             }
1220             stateMachine.sendMessage(HeadsetStateMachine.DISCONNECT_AUDIO, device);
1221         }
1222         if (Flags.hfpCodecAptxVoice()) {
1223             enableSwbCodec(HeadsetHalConstants.BTHF_SWB_CODEC_VENDOR_APTX, false, device);
1224         }
1225         return true;
1226     }
1227 
isAudioOn()1228     boolean isAudioOn() {
1229         return getNonIdleAudioDevices().size() > 0;
1230     }
1231 
isAudioConnected(BluetoothDevice device)1232     boolean isAudioConnected(BluetoothDevice device) {
1233         synchronized (mStateMachines) {
1234             final HeadsetStateMachine stateMachine = mStateMachines.get(device);
1235             if (stateMachine == null) {
1236                 return false;
1237             }
1238             return stateMachine.getAudioState() == BluetoothHeadset.STATE_AUDIO_CONNECTED;
1239         }
1240     }
1241 
getAudioState(BluetoothDevice device)1242     int getAudioState(BluetoothDevice device) {
1243         synchronized (mStateMachines) {
1244             final HeadsetStateMachine stateMachine = mStateMachines.get(device);
1245             if (stateMachine == null) {
1246                 return BluetoothHeadset.STATE_AUDIO_DISCONNECTED;
1247             }
1248             return stateMachine.getAudioState();
1249         }
1250     }
1251 
setAudioRouteAllowed(boolean allowed)1252     public void setAudioRouteAllowed(boolean allowed) {
1253         Log.i(TAG, "setAudioRouteAllowed: allowed=" + allowed + ", " + Utils.getUidPidString());
1254         mAudioRouteAllowed = allowed;
1255         mNativeInterface.setScoAllowed(allowed);
1256     }
1257 
getAudioRouteAllowed()1258     public boolean getAudioRouteAllowed() {
1259         return mAudioRouteAllowed;
1260     }
1261 
setForceScoAudio(boolean forced)1262     public void setForceScoAudio(boolean forced) {
1263         Log.i(TAG, "setForceScoAudio: forced=" + forced + ", " + Utils.getUidPidString());
1264         mForceScoAudio = forced;
1265     }
1266 
1267     @VisibleForTesting
getForceScoAudio()1268     public boolean getForceScoAudio() {
1269         return mForceScoAudio;
1270     }
1271 
1272     /**
1273      * Get first available device for SCO audio
1274      *
1275      * @return first connected headset device
1276      */
1277     @VisibleForTesting
1278     @Nullable
getFirstConnectedAudioDevice()1279     public BluetoothDevice getFirstConnectedAudioDevice() {
1280         ArrayList<HeadsetStateMachine> stateMachines = new ArrayList<>();
1281         synchronized (mStateMachines) {
1282             List<BluetoothDevice> availableDevices =
1283                     getDevicesMatchingConnectionStates(CONNECTING_CONNECTED_STATES);
1284             for (BluetoothDevice device : availableDevices) {
1285                 final HeadsetStateMachine stateMachine = mStateMachines.get(device);
1286                 if (stateMachine == null) {
1287                     continue;
1288                 }
1289                 stateMachines.add(stateMachine);
1290             }
1291         }
1292         stateMachines.sort(Comparator.comparingLong(HeadsetStateMachine::getConnectingTimestampMs));
1293         if (stateMachines.size() > 0) {
1294             return stateMachines.get(0).getDevice();
1295         }
1296         return null;
1297     }
1298 
1299     /**
1300      * Process a change in the silence mode for a {@link BluetoothDevice}.
1301      *
1302      * @param device the device to change silence mode
1303      * @param silence true to enable silence mode, false to disable.
1304      * @return true on success, false on error
1305      */
1306     @VisibleForTesting
1307     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
setSilenceMode(BluetoothDevice device, boolean silence)1308     public boolean setSilenceMode(BluetoothDevice device, boolean silence) {
1309         Log.d(TAG, "setSilenceMode(" + device + "): " + silence);
1310 
1311         if (silence && Objects.equals(mActiveDevice, device)) {
1312             setActiveDevice(null);
1313         } else if (!silence && mActiveDevice == null) {
1314             // Set the device as the active device if currently no active device.
1315             setActiveDevice(device);
1316         }
1317         synchronized (mStateMachines) {
1318             final HeadsetStateMachine stateMachine = mStateMachines.get(device);
1319             if (stateMachine == null) {
1320                 Log.w(TAG, "setSilenceMode: device " + device + " was never connected/connecting");
1321                 return false;
1322             }
1323             stateMachine.setSilenceDevice(silence);
1324         }
1325 
1326         return true;
1327     }
1328 
1329     /**
1330      * Get the Bluetooth Audio Policy stored in the state machine
1331      *
1332      * @param device the device to change silence mode
1333      * @return a {@link BluetoothSinkAudioPolicy} object
1334      */
getHfpCallAudioPolicy(BluetoothDevice device)1335     public BluetoothSinkAudioPolicy getHfpCallAudioPolicy(BluetoothDevice device) {
1336         synchronized (mStateMachines) {
1337             final HeadsetStateMachine stateMachine = mStateMachines.get(device);
1338             if (stateMachine == null) {
1339                 Log.e(TAG, "getHfpCallAudioPolicy(), " + device + " does not have a state machine");
1340                 return null;
1341             }
1342             return stateMachine.getHfpCallAudioPolicy();
1343         }
1344     }
1345 
1346     /** Remove the active device */
removeActiveDevice()1347     private void removeActiveDevice() {
1348         synchronized (mStateMachines) {
1349             // As per b/202602952, if we remove the active device due to a disconnection,
1350             // we need to check if another device is connected and set it active instead.
1351             // Calling this before any other active related calls has the same effect as
1352             // a classic active device switch.
1353             BluetoothDevice fallbackDevice = getFallbackDevice();
1354             if (fallbackDevice != null
1355                     && mActiveDevice != null
1356                     && getConnectionState(mActiveDevice) != BluetoothProfile.STATE_CONNECTED) {
1357                 setActiveDevice(fallbackDevice);
1358                 return;
1359             }
1360             // Clear the active device
1361             if (mVoiceRecognitionStarted) {
1362                 if (!stopVoiceRecognition(mActiveDevice)) {
1363                     Log.w(
1364                             TAG,
1365                             "removeActiveDevice: fail to stopVoiceRecognition from "
1366                                     + mActiveDevice);
1367                 }
1368             }
1369             if (mVirtualCallStarted) {
1370                 if (!stopScoUsingVirtualVoiceCall()) {
1371                     Log.w(
1372                             TAG,
1373                             "removeActiveDevice: fail to stopScoUsingVirtualVoiceCall from "
1374                                     + mActiveDevice);
1375                 }
1376             }
1377             if (getAudioState(mActiveDevice) != BluetoothHeadset.STATE_AUDIO_DISCONNECTED) {
1378                 int disconnectStatus = disconnectAudio(mActiveDevice);
1379                 if (disconnectStatus != BluetoothStatusCodes.SUCCESS) {
1380                     Log.w(
1381                             TAG,
1382                             "removeActiveDevice: disconnectAudio failed on "
1383                                     + mActiveDevice
1384                                     + " with status code "
1385                                     + disconnectStatus);
1386                 }
1387             }
1388             mActiveDevice = null;
1389             mNativeInterface.setActiveDevice(null);
1390             broadcastActiveDevice(null);
1391         }
1392     }
1393 
1394     /**
1395      * Set the active device.
1396      *
1397      * @param device the active device
1398      * @return true on success, otherwise false
1399      */
1400     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
setActiveDevice(BluetoothDevice device)1401     public boolean setActiveDevice(BluetoothDevice device) {
1402         Log.i(TAG, "setActiveDevice: device=" + device + ", " + Utils.getUidPidString());
1403         if (device == null) {
1404             removeActiveDevice();
1405             return true;
1406         }
1407         synchronized (mStateMachines) {
1408             if (device.equals(mActiveDevice)) {
1409                 Log.i(TAG, "setActiveDevice: device " + device + " is already active");
1410                 return true;
1411             }
1412             if (getConnectionState(device) != BluetoothProfile.STATE_CONNECTED) {
1413                 Log.e(
1414                         TAG,
1415                         "setActiveDevice: Cannot set "
1416                                 + device
1417                                 + " as active, device is not connected");
1418                 return false;
1419             }
1420             if (!mNativeInterface.setActiveDevice(device)) {
1421                 Log.e(TAG, "setActiveDevice: Cannot set " + device + " as active in native layer");
1422                 return false;
1423             }
1424             BluetoothDevice previousActiveDevice = mActiveDevice;
1425             mActiveDevice = device;
1426 
1427             /* If HFP is getting active for a phone call and there are active LE Audio devices,
1428              * Lets inactive LeAudio device as soon as possible so there is no CISes connected
1429              * when SCO is going to be created
1430              */
1431             if (mSystemInterface.isInCall() || mSystemInterface.isRinging()) {
1432                 LeAudioService leAudioService = mFactory.getLeAudioService();
1433                 if (leAudioService != null
1434                         && !leAudioService.getConnectedDevices().isEmpty()
1435                         && Flags.leaudioResumeActiveAfterHfpHandover()) {
1436                     Log.i(TAG, "Make sure no le audio device active for HFP handover.");
1437                     leAudioService.setInactiveForHfpHandover(mActiveDevice);
1438                 }
1439             }
1440 
1441             if (getAudioState(previousActiveDevice) != BluetoothHeadset.STATE_AUDIO_DISCONNECTED) {
1442                 int disconnectStatus = disconnectAudio(previousActiveDevice);
1443                 if (disconnectStatus != BluetoothStatusCodes.SUCCESS) {
1444                     Log.e(
1445                             TAG,
1446                             "setActiveDevice: fail to disconnectAudio from "
1447                                     + previousActiveDevice
1448                                     + " with status code "
1449                                     + disconnectStatus);
1450                     if (previousActiveDevice == null) {
1451                         removeActiveDevice();
1452                     } else {
1453                         mActiveDevice = previousActiveDevice;
1454                         mNativeInterface.setActiveDevice(previousActiveDevice);
1455                     }
1456                     return false;
1457                 }
1458                 if (Utils.isScoManagedByAudioEnabled()) {
1459                     // tell Audio Framework that active device changed
1460                     mSystemInterface
1461                             .getAudioManager()
1462                             .handleBluetoothActiveDeviceChanged(
1463                                     mActiveDevice,
1464                                     previousActiveDevice,
1465                                     BluetoothProfileConnectionInfo.createHfpInfo());
1466                 }
1467                 broadcastActiveDevice(mActiveDevice);
1468             } else if (shouldPersistAudio()) {
1469                 /* If HFP is getting active for a phonecall and there is LeAudio device active,
1470                  * Lets inactive LeAudio device as soon as possible so there is no CISes connected
1471                  * when SCO is created
1472                  */
1473                 LeAudioService leAudioService = mFactory.getLeAudioService();
1474                 if (leAudioService != null && !Flags.leaudioResumeActiveAfterHfpHandover()) {
1475                     Log.i(TAG, "Make sure there is no le audio device active.");
1476                     leAudioService.setInactiveForHfpHandover(mActiveDevice);
1477                 }
1478                 if (Utils.isScoManagedByAudioEnabled()) {
1479                     // tell Audio Framework that active device changed
1480                     mSystemInterface
1481                             .getAudioManager()
1482                             .handleBluetoothActiveDeviceChanged(
1483                                     mActiveDevice,
1484                                     previousActiveDevice,
1485                                     BluetoothProfileConnectionInfo.createHfpInfo());
1486                 }
1487                 broadcastActiveDevice(mActiveDevice);
1488                 if (Utils.isScoManagedByAudioEnabled()) {
1489                     // Audio Framework will handle audio transition
1490                     return true;
1491                 }
1492                 int connectStatus = connectAudio(mActiveDevice);
1493                 if (connectStatus != BluetoothStatusCodes.SUCCESS) {
1494                     Log.e(
1495                             TAG,
1496                             "setActiveDevice: fail to connectAudio to "
1497                                     + mActiveDevice
1498                                     + " with status code "
1499                                     + connectStatus);
1500                     if (previousActiveDevice == null) {
1501                         removeActiveDevice();
1502                     } else {
1503                         mActiveDevice = previousActiveDevice;
1504                         mNativeInterface.setActiveDevice(previousActiveDevice);
1505                     }
1506                     return false;
1507                 }
1508             } else {
1509                 if (Utils.isScoManagedByAudioEnabled()) {
1510                     // tell Audio Framework that active device changed
1511                     mSystemInterface
1512                             .getAudioManager()
1513                             .handleBluetoothActiveDeviceChanged(
1514                                     mActiveDevice,
1515                                     previousActiveDevice,
1516                                     BluetoothProfileConnectionInfo.createHfpInfo());
1517                 }
1518                 broadcastActiveDevice(mActiveDevice);
1519             }
1520         }
1521         return true;
1522     }
1523 
1524     /**
1525      * Get the active device.
1526      *
1527      * @return the active device or null if no device is active
1528      */
getActiveDevice()1529     public BluetoothDevice getActiveDevice() {
1530         synchronized (mStateMachines) {
1531             return mActiveDevice;
1532         }
1533     }
1534 
connectAudio()1535     public int connectAudio() {
1536         synchronized (mStateMachines) {
1537             BluetoothDevice device = mActiveDevice;
1538             if (device == null) {
1539                 Log.w(TAG, "connectAudio: no active device, " + Utils.getUidPidString());
1540                 return BluetoothStatusCodes.ERROR_NO_ACTIVE_DEVICES;
1541             }
1542             return connectAudio(device);
1543         }
1544     }
1545 
connectAudio(BluetoothDevice device)1546     int connectAudio(BluetoothDevice device) {
1547         Log.i(TAG, "connectAudio: device=" + device + ", " + Utils.getUidPidString());
1548         synchronized (mStateMachines) {
1549             final HeadsetStateMachine stateMachine = mStateMachines.get(device);
1550             if (stateMachine == null) {
1551                 Log.w(TAG, "connectAudio: device " + device + " was never connected/connecting");
1552                 return BluetoothStatusCodes.ERROR_PROFILE_NOT_CONNECTED;
1553             }
1554             int scoConnectionAllowedState = isScoAcceptable(device);
1555             if (scoConnectionAllowedState != BluetoothStatusCodes.SUCCESS) {
1556                 Log.w(TAG, "connectAudio, rejected SCO request to " + device);
1557                 return scoConnectionAllowedState;
1558             }
1559             if (stateMachine.getConnectionState() != BluetoothProfile.STATE_CONNECTED) {
1560                 Log.w(TAG, "connectAudio: profile not connected");
1561                 return BluetoothStatusCodes.ERROR_PROFILE_NOT_CONNECTED;
1562             }
1563             if (stateMachine.getAudioState() != BluetoothHeadset.STATE_AUDIO_DISCONNECTED) {
1564                 logD("connectAudio: audio is not idle for device " + device);
1565                 return BluetoothStatusCodes.SUCCESS;
1566             }
1567             if (isAudioOn()) {
1568                 Log.w(
1569                         TAG,
1570                         "connectAudio: audio is not idle, current audio devices are "
1571                                 + Arrays.toString(getNonIdleAudioDevices().toArray()));
1572                 return BluetoothStatusCodes.ERROR_AUDIO_DEVICE_ALREADY_CONNECTED;
1573             }
1574             stateMachine.sendMessage(HeadsetStateMachine.CONNECT_AUDIO, device);
1575         }
1576         return BluetoothStatusCodes.SUCCESS;
1577     }
1578 
getNonIdleAudioDevices()1579     private List<BluetoothDevice> getNonIdleAudioDevices() {
1580         ArrayList<BluetoothDevice> devices = new ArrayList<>();
1581         synchronized (mStateMachines) {
1582             for (HeadsetStateMachine stateMachine : mStateMachines.values()) {
1583                 if (stateMachine.getAudioState() != BluetoothHeadset.STATE_AUDIO_DISCONNECTED) {
1584                     devices.add(stateMachine.getDevice());
1585                 }
1586             }
1587         }
1588         return devices;
1589     }
1590 
disconnectAudio()1591     int disconnectAudio() {
1592         int disconnectResult = BluetoothStatusCodes.ERROR_NO_ACTIVE_DEVICES;
1593         synchronized (mStateMachines) {
1594             for (BluetoothDevice device : getNonIdleAudioDevices()) {
1595                 disconnectResult = disconnectAudio(device);
1596                 if (disconnectResult == BluetoothStatusCodes.SUCCESS) {
1597                     return disconnectResult;
1598                 } else {
1599                     Log.e(
1600                             TAG,
1601                             "disconnectAudio() from "
1602                                     + device
1603                                     + " failed with status code "
1604                                     + disconnectResult);
1605                 }
1606             }
1607         }
1608         logD("disconnectAudio() no active audio connection");
1609         return disconnectResult;
1610     }
1611 
disconnectAudio(BluetoothDevice device)1612     int disconnectAudio(BluetoothDevice device) {
1613         synchronized (mStateMachines) {
1614             Log.i(TAG, "disconnectAudio: device=" + device + ", " + Utils.getUidPidString());
1615             final HeadsetStateMachine stateMachine = mStateMachines.get(device);
1616             if (stateMachine == null) {
1617                 Log.w(TAG, "disconnectAudio: device " + device + " was never connected/connecting");
1618                 return BluetoothStatusCodes.ERROR_PROFILE_NOT_CONNECTED;
1619             }
1620             if (stateMachine.getAudioState() == BluetoothHeadset.STATE_AUDIO_DISCONNECTED) {
1621                 Log.w(TAG, "disconnectAudio, audio is already disconnected for " + device);
1622                 return BluetoothStatusCodes.ERROR_AUDIO_DEVICE_ALREADY_DISCONNECTED;
1623             }
1624             stateMachine.sendMessage(HeadsetStateMachine.DISCONNECT_AUDIO, device);
1625         }
1626         return BluetoothStatusCodes.SUCCESS;
1627     }
1628 
isVirtualCallStarted()1629     boolean isVirtualCallStarted() {
1630         synchronized (mStateMachines) {
1631             return mVirtualCallStarted;
1632         }
1633     }
1634 
1635     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
1636     @VisibleForTesting
startScoUsingVirtualVoiceCall()1637     boolean startScoUsingVirtualVoiceCall() {
1638         Log.i(TAG, "startScoUsingVirtualVoiceCall: " + Utils.getUidPidString());
1639         synchronized (mStateMachines) {
1640             // TODO(b/79660380): Workaround in case voice recognition was not terminated properly
1641             if (mVoiceRecognitionStarted) {
1642                 boolean status = stopVoiceRecognition(mActiveDevice);
1643                 Log.w(
1644                         TAG,
1645                         "startScoUsingVirtualVoiceCall: voice recognition is still active, "
1646                                 + "just called stopVoiceRecognition, returned "
1647                                 + status
1648                                 + " on "
1649                                 + mActiveDevice
1650                                 + ", please try again");
1651                 mVoiceRecognitionStarted = false;
1652                 return false;
1653             }
1654             if (!isAudioModeIdle()) {
1655                 Log.w(
1656                         TAG,
1657                         "startScoUsingVirtualVoiceCall: audio mode not idle, active device is "
1658                                 + mActiveDevice);
1659                 return false;
1660             }
1661             // Audio should not be on when no audio mode is active
1662             if (isAudioOn()) {
1663                 // Disconnect audio so that API user can try later
1664                 int status = disconnectAudio();
1665                 Log.w(
1666                         TAG,
1667                         "startScoUsingVirtualVoiceCall: audio is still active, please wait for "
1668                                 + "audio to be disconnected, disconnectAudio() returned "
1669                                 + status
1670                                 + ", active device is "
1671                                 + mActiveDevice);
1672                 return false;
1673             }
1674             if (mActiveDevice == null) {
1675                 Log.w(TAG, "startScoUsingVirtualVoiceCall: no active device");
1676                 return false;
1677             }
1678             if (SystemProperties.getBoolean(REJECT_SCO_IF_HFPC_CONNECTED_PROPERTY, false)
1679                     && isHeadsetClientConnected()) {
1680                 Log.w(TAG, "startScoUsingVirtualVoiceCall: rejected SCO since HFPC is connected!");
1681                 return false;
1682             }
1683             mVirtualCallStarted = true;
1684             // Send virtual phone state changed to initialize SCO
1685             phoneStateChanged(0, 0, HeadsetHalConstants.CALL_STATE_DIALING, "", 0, "", true);
1686             phoneStateChanged(0, 0, HeadsetHalConstants.CALL_STATE_ALERTING, "", 0, "", true);
1687             phoneStateChanged(1, 0, HeadsetHalConstants.CALL_STATE_IDLE, "", 0, "", true);
1688             return true;
1689         }
1690     }
1691 
1692     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
stopScoUsingVirtualVoiceCall()1693     boolean stopScoUsingVirtualVoiceCall() {
1694         Log.i(TAG, "stopScoUsingVirtualVoiceCall: " + Utils.getUidPidString());
1695         synchronized (mStateMachines) {
1696             // 1. Check if virtual call has already started
1697             if (!mVirtualCallStarted) {
1698                 Log.w(TAG, "stopScoUsingVirtualVoiceCall: virtual call not started");
1699                 return false;
1700             }
1701             mVirtualCallStarted = false;
1702             // 2. Send virtual phone state changed to close SCO
1703             phoneStateChanged(0, 0, HeadsetHalConstants.CALL_STATE_IDLE, "", 0, "", true);
1704         }
1705         return true;
1706     }
1707 
1708     class DialingOutTimeoutEvent implements Runnable {
1709         BluetoothDevice mDialingOutDevice;
1710 
DialingOutTimeoutEvent(BluetoothDevice fromDevice)1711         DialingOutTimeoutEvent(BluetoothDevice fromDevice) {
1712             mDialingOutDevice = fromDevice;
1713         }
1714 
1715         @Override
run()1716         public void run() {
1717             synchronized (mStateMachines) {
1718                 mDialingOutTimeoutEvent = null;
1719                 doForStateMachine(
1720                         mDialingOutDevice,
1721                         stateMachine ->
1722                                 stateMachine.sendMessage(
1723                                         HeadsetStateMachine.DIALING_OUT_RESULT,
1724                                         0 /* fail */,
1725                                         0,
1726                                         mDialingOutDevice));
1727             }
1728         }
1729 
1730         @Override
toString()1731         public String toString() {
1732             return "DialingOutTimeoutEvent[" + mDialingOutDevice + "]";
1733         }
1734     }
1735 
1736     /**
1737      * Dial an outgoing call as requested by the remote device
1738      *
1739      * @param fromDevice remote device that initiated this dial out action
1740      * @param dialNumber number to dial
1741      * @return true on successful dial out
1742      */
1743     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
1744     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
dialOutgoingCall(BluetoothDevice fromDevice, String dialNumber)1745     public boolean dialOutgoingCall(BluetoothDevice fromDevice, String dialNumber) {
1746         synchronized (mStateMachines) {
1747             Log.i(TAG, "dialOutgoingCall: from " + fromDevice);
1748             if (mDialingOutTimeoutEvent != null) {
1749                 Log.e(TAG, "dialOutgoingCall, already dialing by " + mDialingOutTimeoutEvent);
1750                 return false;
1751             }
1752             if (isVirtualCallStarted()) {
1753                 if (!stopScoUsingVirtualVoiceCall()) {
1754                     Log.e(TAG, "dialOutgoingCall failed to stop current virtual call");
1755                     return false;
1756                 }
1757             }
1758             if (!setActiveDevice(fromDevice)) {
1759                 Log.e(TAG, "dialOutgoingCall failed to set active device to " + fromDevice);
1760                 return false;
1761             }
1762             Intent intent =
1763                     new Intent(
1764                             Intent.ACTION_CALL_PRIVILEGED,
1765                             Uri.fromParts(PhoneAccount.SCHEME_TEL, dialNumber, null));
1766             intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
1767             startActivity(intent);
1768             mDialingOutTimeoutEvent = new DialingOutTimeoutEvent(fromDevice);
1769             mStateMachinesThreadHandler.postDelayed(
1770                     mDialingOutTimeoutEvent, DIALING_OUT_TIMEOUT_MS);
1771             return true;
1772         }
1773     }
1774 
1775     /**
1776      * Check if any connected headset has started dialing calls
1777      *
1778      * @return true if some device has started dialing calls
1779      */
1780     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
hasDeviceInitiatedDialingOut()1781     public boolean hasDeviceInitiatedDialingOut() {
1782         synchronized (mStateMachines) {
1783             return mDialingOutTimeoutEvent != null;
1784         }
1785     }
1786 
1787     class VoiceRecognitionTimeoutEvent implements Runnable {
1788         BluetoothDevice mVoiceRecognitionDevice;
1789 
VoiceRecognitionTimeoutEvent(BluetoothDevice device)1790         VoiceRecognitionTimeoutEvent(BluetoothDevice device) {
1791             mVoiceRecognitionDevice = device;
1792         }
1793 
1794         @Override
run()1795         public void run() {
1796             synchronized (mStateMachines) {
1797                 if (mSystemInterface.getVoiceRecognitionWakeLock().isHeld()) {
1798                     mSystemInterface.getVoiceRecognitionWakeLock().release();
1799                 }
1800                 mVoiceRecognitionTimeoutEvent = null;
1801                 doForStateMachine(
1802                         mVoiceRecognitionDevice,
1803                         stateMachine ->
1804                                 stateMachine.sendMessage(
1805                                         HeadsetStateMachine.VOICE_RECOGNITION_RESULT,
1806                                         0 /* fail */,
1807                                         0,
1808                                         mVoiceRecognitionDevice));
1809             }
1810         }
1811 
1812         @Override
toString()1813         public String toString() {
1814             return "VoiceRecognitionTimeoutEvent[" + mVoiceRecognitionDevice + "]";
1815         }
1816     }
1817 
1818     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
startVoiceRecognitionByHeadset(BluetoothDevice fromDevice)1819     boolean startVoiceRecognitionByHeadset(BluetoothDevice fromDevice) {
1820         synchronized (mStateMachines) {
1821             Log.i(TAG, "startVoiceRecognitionByHeadset: from " + fromDevice);
1822             // TODO(b/79660380): Workaround in case voice recognition was not terminated properly
1823             if (mVoiceRecognitionStarted) {
1824                 boolean status = stopVoiceRecognition(mActiveDevice);
1825                 Log.w(
1826                         TAG,
1827                         "startVoiceRecognitionByHeadset: voice recognition is still active, "
1828                                 + "just called stopVoiceRecognition, returned "
1829                                 + status
1830                                 + " on "
1831                                 + mActiveDevice
1832                                 + ", please try again");
1833                 mVoiceRecognitionStarted = false;
1834                 return false;
1835             }
1836             if (fromDevice == null) {
1837                 Log.e(TAG, "startVoiceRecognitionByHeadset: fromDevice is null");
1838                 return false;
1839             }
1840             if (!isAudioModeIdle()) {
1841                 Log.w(
1842                         TAG,
1843                         "startVoiceRecognitionByHeadset: audio mode not idle, active device is "
1844                                 + mActiveDevice);
1845                 return false;
1846             }
1847             // Audio should not be on when no audio mode is active
1848             if (isAudioOn()) {
1849                 // Disconnect audio so that user can try later
1850                 int status = disconnectAudio();
1851                 Log.w(
1852                         TAG,
1853                         "startVoiceRecognitionByHeadset: audio is still active, please wait for"
1854                                 + " audio to be disconnected, disconnectAudio() returned "
1855                                 + status
1856                                 + ", active device is "
1857                                 + mActiveDevice);
1858                 return false;
1859             }
1860             // Do not start new request until the current one is finished or timeout
1861             if (mVoiceRecognitionTimeoutEvent != null) {
1862                 Log.w(
1863                         TAG,
1864                         "startVoiceRecognitionByHeadset: failed request from "
1865                                 + fromDevice
1866                                 + ", already pending by "
1867                                 + mVoiceRecognitionTimeoutEvent);
1868                 return false;
1869             }
1870             if (!setActiveDevice(fromDevice)) {
1871                 Log.w(
1872                         TAG,
1873                         "startVoiceRecognitionByHeadset: failed to set "
1874                                 + fromDevice
1875                                 + " as active");
1876                 return false;
1877             }
1878             if (!mSystemInterface.activateVoiceRecognition()) {
1879                 Log.w(TAG, "startVoiceRecognitionByHeadset: failed request from " + fromDevice);
1880                 return false;
1881             }
1882             if (SystemProperties.getBoolean(REJECT_SCO_IF_HFPC_CONNECTED_PROPERTY, false)
1883                     && isHeadsetClientConnected()) {
1884                 Log.w(TAG, "startVoiceRecognitionByHeadset: rejected SCO since HFPC is connected!");
1885                 return false;
1886             }
1887             mVoiceRecognitionTimeoutEvent = new VoiceRecognitionTimeoutEvent(fromDevice);
1888             mStateMachinesThreadHandler.postDelayed(
1889                     mVoiceRecognitionTimeoutEvent, sStartVrTimeoutMs);
1890 
1891             if (!mSystemInterface.getVoiceRecognitionWakeLock().isHeld()) {
1892                 mSystemInterface.getVoiceRecognitionWakeLock().acquire(sStartVrTimeoutMs);
1893             }
1894             if (Flags.hfpCodecAptxVoice()) {
1895                 enableSwbCodec(HeadsetHalConstants.BTHF_SWB_CODEC_VENDOR_APTX, true, fromDevice);
1896             }
1897             return true;
1898         }
1899     }
1900 
stopVoiceRecognitionByHeadset(BluetoothDevice fromDevice)1901     boolean stopVoiceRecognitionByHeadset(BluetoothDevice fromDevice) {
1902         synchronized (mStateMachines) {
1903             Log.i(TAG, "stopVoiceRecognitionByHeadset: from " + fromDevice);
1904             if (!Objects.equals(fromDevice, mActiveDevice)) {
1905                 Log.w(
1906                         TAG,
1907                         "stopVoiceRecognitionByHeadset: "
1908                                 + fromDevice
1909                                 + " is not active, active device is "
1910                                 + mActiveDevice);
1911                 return false;
1912             }
1913             if (!mVoiceRecognitionStarted && mVoiceRecognitionTimeoutEvent == null) {
1914                 Log.w(
1915                         TAG,
1916                         "stopVoiceRecognitionByHeadset: voice recognition not started, device="
1917                                 + fromDevice);
1918                 return false;
1919             }
1920             if (mVoiceRecognitionTimeoutEvent != null) {
1921                 if (mSystemInterface.getVoiceRecognitionWakeLock().isHeld()) {
1922                     mSystemInterface.getVoiceRecognitionWakeLock().release();
1923                 }
1924                 mStateMachinesThreadHandler.removeCallbacks(mVoiceRecognitionTimeoutEvent);
1925 
1926                 mVoiceRecognitionTimeoutEvent = null;
1927             }
1928             if (mVoiceRecognitionStarted) {
1929                 int disconnectStatus = disconnectAudio();
1930                 if (disconnectStatus != BluetoothStatusCodes.SUCCESS) {
1931                     Log.w(
1932                             TAG,
1933                             "stopVoiceRecognitionByHeadset: failed to disconnect audio from "
1934                                     + fromDevice
1935                                     + " with status code "
1936                                     + disconnectStatus);
1937                 }
1938                 mVoiceRecognitionStarted = false;
1939             }
1940             if (!mSystemInterface.deactivateVoiceRecognition()) {
1941                 Log.w(TAG, "stopVoiceRecognitionByHeadset: failed request from " + fromDevice);
1942                 return false;
1943             }
1944             if (Flags.hfpCodecAptxVoice()) {
1945                 enableSwbCodec(HeadsetHalConstants.BTHF_SWB_CODEC_VENDOR_APTX, false, fromDevice);
1946             }
1947             return true;
1948         }
1949     }
1950 
1951     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
1952     @VisibleForTesting
phoneStateChanged( int numActive, int numHeld, int callState, String number, int type, String name, boolean isVirtualCall)1953     void phoneStateChanged(
1954             int numActive,
1955             int numHeld,
1956             int callState,
1957             String number,
1958             int type,
1959             String name,
1960             boolean isVirtualCall) {
1961         enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, "Need MODIFY_PHONE_STATE permission");
1962         synchronized (mStateMachines) {
1963             // Should stop all other audio mode in this case
1964             if ((numActive + numHeld) > 0 || callState != HeadsetHalConstants.CALL_STATE_IDLE) {
1965                 if (!isVirtualCall && mVirtualCallStarted) {
1966                     // stop virtual voice call if there is an incoming Telecom call update
1967                     stopScoUsingVirtualVoiceCall();
1968                 }
1969                 if (mVoiceRecognitionStarted) {
1970                     // stop voice recognition if there is any incoming call
1971                     stopVoiceRecognition(mActiveDevice);
1972                 }
1973             }
1974             if (mDialingOutTimeoutEvent != null) {
1975                 // Send result to state machine when dialing starts
1976                 if (callState == HeadsetHalConstants.CALL_STATE_DIALING) {
1977                     mStateMachinesThreadHandler.removeCallbacks(mDialingOutTimeoutEvent);
1978                     doForStateMachine(
1979                             mDialingOutTimeoutEvent.mDialingOutDevice,
1980                             stateMachine ->
1981                                     stateMachine.sendMessage(
1982                                             HeadsetStateMachine.DIALING_OUT_RESULT,
1983                                             1 /* success */,
1984                                             0,
1985                                             mDialingOutTimeoutEvent.mDialingOutDevice));
1986                 } else if (callState == HeadsetHalConstants.CALL_STATE_ACTIVE
1987                         || callState == HeadsetHalConstants.CALL_STATE_IDLE) {
1988                     // Clear the timeout event when the call is connected or disconnected
1989                     if (!mStateMachinesThreadHandler.hasCallbacks(mDialingOutTimeoutEvent)) {
1990                         mDialingOutTimeoutEvent = null;
1991                     }
1992                 }
1993             }
1994         }
1995         mStateMachinesThreadHandler.post(
1996                 () -> {
1997                     boolean isCallIdleBefore = mSystemInterface.isCallIdle();
1998                     mSystemInterface.getHeadsetPhoneState().setNumActiveCall(numActive);
1999                     mSystemInterface.getHeadsetPhoneState().setNumHeldCall(numHeld);
2000                     mSystemInterface.getHeadsetPhoneState().setCallState(callState);
2001                     // Suspend A2DP when call about is about to become active
2002                     if (mActiveDevice != null
2003                             && callState != HeadsetHalConstants.CALL_STATE_DISCONNECTED
2004                             && !mSystemInterface.isCallIdle()
2005                             && isCallIdleBefore
2006                             && !Utils.isScoManagedByAudioEnabled()) {
2007                         mSystemInterface.getAudioManager().setA2dpSuspended(true);
2008                         if (isAtLeastU()) {
2009                             mSystemInterface.getAudioManager().setLeAudioSuspended(true);
2010                         }
2011                     }
2012                 });
2013         doForEachConnectedStateMachine(
2014                 stateMachine ->
2015                         stateMachine.sendMessage(
2016                                 HeadsetStateMachine.CALL_STATE_CHANGED,
2017                                 new HeadsetCallState(
2018                                         numActive, numHeld, callState, number, type, name)));
2019         if (Utils.isScoManagedByAudioEnabled()) {
2020             if (mActiveDevice == null) {
2021                 Log.i(TAG, "HeadsetService's active device is null");
2022             } else {
2023                 // wait until mActiveDevice's state machine processed CALL_STATE_CHANGED message,
2024                 // then Audio Framework starts the SCO connection
2025                 FutureTask task = new FutureTask(() -> {}, null);
2026                 mStateMachines.get(mActiveDevice).getHandler().post(task);
2027                 try {
2028                     task.get();
2029                 } catch (Exception e) {
2030                     Log.e(
2031                             TAG,
2032                             "Exception when waiting for CALL_STATE_CHANGED message" + e.toString());
2033                 }
2034             }
2035         }
2036         mStateMachinesThreadHandler.post(
2037                 () -> {
2038                     if (callState == HeadsetHalConstants.CALL_STATE_IDLE
2039                             && mSystemInterface.isCallIdle()
2040                             && !isAudioOn()
2041                             && !Utils.isScoManagedByAudioEnabled()) {
2042                         // Resume A2DP when call ended and SCO is not connected
2043                         mSystemInterface.getAudioManager().setA2dpSuspended(false);
2044                         if (isAtLeastU()) {
2045                             mSystemInterface.getAudioManager().setLeAudioSuspended(false);
2046                         }
2047                     }
2048                 });
2049         if (callState == HeadsetHalConstants.CALL_STATE_IDLE) {
2050             final HeadsetStateMachine stateMachine = mStateMachines.get(mActiveDevice);
2051             if (stateMachine == null) {
2052                 Log.d(TAG, "phoneStateChanged: CALL_STATE_IDLE, mActiveDevice is Null");
2053             } else {
2054                 BluetoothSinkAudioPolicy currentPolicy = stateMachine.getHfpCallAudioPolicy();
2055                 if (currentPolicy != null
2056                         && currentPolicy.getActiveDevicePolicyAfterConnection()
2057                                 == BluetoothSinkAudioPolicy.POLICY_NOT_ALLOWED) {
2058                     /**
2059                      * If the active device was set because of the pick up audio policy and the
2060                      * connecting policy is NOT_ALLOWED, then after the call is terminated, we must
2061                      * de-activate this device. If there is a fallback mechanism, we should follow
2062                      * it to set fallback device be active.
2063                      */
2064                     removeActiveDevice();
2065                     if (Flags.sinkAudioPolicyHandover()) {
2066                         BluetoothDevice fallbackDevice = getFallbackDevice();
2067                         if (fallbackDevice != null
2068                                 && getConnectionState(fallbackDevice)
2069                                         == BluetoothProfile.STATE_CONNECTED) {
2070                             Log.d(
2071                                     TAG,
2072                                     "BluetoothSinkAudioPolicy set fallbackDevice="
2073                                             + fallbackDevice
2074                                             + " active");
2075                             setActiveDevice(fallbackDevice);
2076                         }
2077                     }
2078                 }
2079             }
2080         }
2081     }
2082 
2083     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
clccResponse( int index, int direction, int status, int mode, boolean mpty, String number, int type)2084     void clccResponse(
2085             int index, int direction, int status, int mode, boolean mpty, String number, int type) {
2086         enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, "Need MODIFY_PHONE_STATE permission");
2087         mPendingClccResponses.add(
2088                 stateMachine ->
2089                         stateMachine.sendMessage(
2090                                 HeadsetStateMachine.SEND_CLCC_RESPONSE,
2091                                 new HeadsetClccResponse(
2092                                         index, direction, status, mode, mpty, number, type)));
2093         if (index == CLCC_END_MARK_INDEX) {
2094             doForEachConnectedStateMachine(mPendingClccResponses);
2095             mPendingClccResponses.clear();
2096         }
2097     }
2098 
sendVendorSpecificResultCode( BluetoothDevice device, String command, String arg)2099     private boolean sendVendorSpecificResultCode(
2100             BluetoothDevice device, String command, String arg) {
2101         synchronized (mStateMachines) {
2102             final HeadsetStateMachine stateMachine = mStateMachines.get(device);
2103             if (stateMachine == null) {
2104                 Log.w(
2105                         TAG,
2106                         "sendVendorSpecificResultCode: device "
2107                                 + device
2108                                 + " was never connected/connecting");
2109                 return false;
2110             }
2111             int connectionState = stateMachine.getConnectionState();
2112             if (connectionState != BluetoothProfile.STATE_CONNECTED) {
2113                 return false;
2114             }
2115             // Currently we support only "+ANDROID".
2116             if (!command.equals(BluetoothHeadset.VENDOR_RESULT_CODE_COMMAND_ANDROID)) {
2117                 Log.w(TAG, "Disallowed unsolicited result code command: " + command);
2118                 return false;
2119             }
2120             stateMachine.sendMessage(
2121                     HeadsetStateMachine.SEND_VENDOR_SPECIFIC_RESULT_CODE,
2122                     new HeadsetVendorSpecificResultCode(device, command, arg));
2123         }
2124         return true;
2125     }
2126 
2127     /**
2128      * Checks if headset devices are able to get inband ringing.
2129      *
2130      * @return True if inband ringing is enabled.
2131      */
isInbandRingingEnabled()2132     public boolean isInbandRingingEnabled() {
2133         boolean isInbandRingingSupported =
2134                 getResources()
2135                         .getBoolean(
2136                                 com.android.bluetooth.R.bool
2137                                         .config_bluetooth_hfp_inband_ringing_support);
2138 
2139         boolean inbandRingtoneAllowedByPolicy = true;
2140         List<BluetoothDevice> audioConnectableDevices = getConnectedDevices();
2141         if (audioConnectableDevices.size() == 1) {
2142             BluetoothDevice connectedDevice = audioConnectableDevices.get(0);
2143             BluetoothSinkAudioPolicy callAudioPolicy = getHfpCallAudioPolicy(connectedDevice);
2144             if (callAudioPolicy != null
2145                     && callAudioPolicy.getInBandRingtonePolicy()
2146                             == BluetoothSinkAudioPolicy.POLICY_NOT_ALLOWED) {
2147                 inbandRingtoneAllowedByPolicy = false;
2148             }
2149         }
2150 
2151         return isInbandRingingSupported
2152                 && !SystemProperties.getBoolean(DISABLE_INBAND_RINGING_PROPERTY, false)
2153                 && !mInbandRingingRuntimeDisable
2154                 && inbandRingtoneAllowedByPolicy
2155                 && !isHeadsetClientConnected();
2156     }
2157 
isHeadsetClientConnected()2158     private boolean isHeadsetClientConnected() {
2159         HeadsetClientService headsetClientService = HeadsetClientService.getHeadsetClientService();
2160         if (headsetClientService == null) {
2161             return false;
2162         }
2163         return !(headsetClientService.getConnectedDevices().isEmpty());
2164     }
2165 
2166     /**
2167      * Called from {@link HeadsetStateMachine} in state machine thread when there is a connection
2168      * state change
2169      *
2170      * @param device remote device
2171      * @param fromState from which connection state is the change
2172      * @param toState to which connection state is the change
2173      */
2174     @VisibleForTesting
2175     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
onConnectionStateChangedFromStateMachine( BluetoothDevice device, int fromState, int toState)2176     public void onConnectionStateChangedFromStateMachine(
2177             BluetoothDevice device, int fromState, int toState) {
2178         if (fromState != BluetoothProfile.STATE_CONNECTED
2179                 && toState == BluetoothProfile.STATE_CONNECTED) {
2180             updateInbandRinging(device, true);
2181             MetricsLogger.logProfileConnectionEvent(BluetoothMetricsProto.ProfileId.HEADSET);
2182         }
2183         if (fromState != BluetoothProfile.STATE_DISCONNECTED
2184                 && toState == BluetoothProfile.STATE_DISCONNECTED) {
2185             updateInbandRinging(device, false);
2186             if (device.equals(mActiveDevice)) {
2187                 setActiveDevice(null);
2188             }
2189         }
2190 
2191         mAdapterService
2192                 .getActiveDeviceManager()
2193                 .profileConnectionStateChanged(
2194                         BluetoothProfile.HEADSET, device, fromState, toState);
2195         mAdapterService
2196                 .getSilenceDeviceManager()
2197                 .hfpConnectionStateChanged(device, fromState, toState);
2198         mAdapterService
2199                 .getRemoteDevices()
2200                 .handleHeadsetConnectionStateChanged(device, fromState, toState);
2201         mAdapterService.notifyProfileConnectionStateChangeToGatt(
2202                 BluetoothProfile.HEADSET, fromState, toState);
2203         mAdapterService.handleProfileConnectionStateChange(
2204                 BluetoothProfile.HEADSET, device, fromState, toState);
2205         mAdapterService.updateProfileConnectionAdapterProperties(
2206                 device, BluetoothProfile.HEADSET, toState, fromState);
2207     }
2208 
2209     /** Called from {@link HeadsetClientStateMachine} to update inband ringing status. */
updateInbandRinging(BluetoothDevice device, boolean connected)2210     public void updateInbandRinging(BluetoothDevice device, boolean connected) {
2211         synchronized (mStateMachines) {
2212             List<BluetoothDevice> audioConnectableDevices = getConnectedDevices();
2213             final int enabled;
2214             final boolean inbandRingingRuntimeDisable = mInbandRingingRuntimeDisable;
2215 
2216             if (audioConnectableDevices.size() > 1 || isHeadsetClientConnected()) {
2217                 mInbandRingingRuntimeDisable = true;
2218                 enabled = 0;
2219             } else {
2220                 mInbandRingingRuntimeDisable = false;
2221                 enabled = 1;
2222             }
2223 
2224             final boolean updateAll = inbandRingingRuntimeDisable != mInbandRingingRuntimeDisable;
2225 
2226             Log.i(
2227                     TAG,
2228                     "updateInbandRinging():"
2229                             + " Device="
2230                             + device
2231                             + " enabled="
2232                             + enabled
2233                             + " connected="
2234                             + connected
2235                             + " Update all="
2236                             + updateAll);
2237 
2238             StateMachineTask sendBsirTask =
2239                     stateMachine ->
2240                             stateMachine.sendMessage(HeadsetStateMachine.SEND_BSIR, enabled);
2241 
2242             if (updateAll) {
2243                 doForEachConnectedStateMachine(sendBsirTask);
2244             } else if (connected) {
2245                 // Same Inband ringing status, send +BSIR only to the new connected device
2246                 doForStateMachine(device, sendBsirTask);
2247             }
2248         }
2249     }
2250 
2251     /**
2252      * Check if no audio mode is active
2253      *
2254      * @return false if virtual call, voice recognition, or Telecom call is active, true if all idle
2255      */
isAudioModeIdle()2256     private boolean isAudioModeIdle() {
2257         synchronized (mStateMachines) {
2258             if (mVoiceRecognitionStarted || mVirtualCallStarted || !mSystemInterface.isCallIdle()) {
2259                 Log.i(
2260                         TAG,
2261                         "isAudioModeIdle: not idle, mVoiceRecognitionStarted="
2262                                 + mVoiceRecognitionStarted
2263                                 + ", mVirtualCallStarted="
2264                                 + mVirtualCallStarted
2265                                 + ", isCallIdle="
2266                                 + mSystemInterface.isCallIdle());
2267                 return false;
2268             }
2269             return true;
2270         }
2271     }
2272 
2273     /**
2274      * Check if the device only allows HFP profile as audio profile
2275      *
2276      * @param device Bluetooth device
2277      * @return true if it is a BluetoothDevice with only HFP profile connectable
2278      */
isHFPAudioOnly(@onNull BluetoothDevice device)2279     private boolean isHFPAudioOnly(@NonNull BluetoothDevice device) {
2280         int hfpPolicy =
2281                 mDatabaseManager.getProfileConnectionPolicy(device, BluetoothProfile.HEADSET);
2282         int a2dpPolicy = mDatabaseManager.getProfileConnectionPolicy(device, BluetoothProfile.A2DP);
2283         int leAudioPolicy =
2284                 mDatabaseManager.getProfileConnectionPolicy(device, BluetoothProfile.LE_AUDIO);
2285         int ashaPolicy =
2286                 mDatabaseManager.getProfileConnectionPolicy(device, BluetoothProfile.HEARING_AID);
2287         return hfpPolicy == BluetoothProfile.CONNECTION_POLICY_ALLOWED
2288                 && a2dpPolicy != BluetoothProfile.CONNECTION_POLICY_ALLOWED
2289                 && leAudioPolicy != BluetoothProfile.CONNECTION_POLICY_ALLOWED
2290                 && ashaPolicy != BluetoothProfile.CONNECTION_POLICY_ALLOWED;
2291     }
2292 
shouldCallAudioBeActive()2293     private boolean shouldCallAudioBeActive() {
2294         return mSystemInterface.isInCall()
2295                 || (mSystemInterface.isRinging() && isInbandRingingEnabled());
2296     }
2297 
2298     /**
2299      * Only persist audio during active device switch when call audio is supposed to be active and
2300      * virtual call has not been started. Virtual call is ignored because AudioService and
2301      * applications should reconnect SCO during active device switch and forcing SCO connection here
2302      * will make AudioService think SCO is started externally instead of by one of its SCO clients.
2303      *
2304      * @return true if call audio should be active and no virtual call is going on
2305      */
shouldPersistAudio()2306     private boolean shouldPersistAudio() {
2307         return !mVirtualCallStarted && shouldCallAudioBeActive();
2308     }
2309 
2310     /**
2311      * Called from {@link HeadsetStateMachine} in state machine thread when there is a audio
2312      * connection state change
2313      *
2314      * @param device remote device
2315      * @param fromState from which audio connection state is the change
2316      * @param toState to which audio connection state is the change
2317      */
2318     @VisibleForTesting
2319     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
onAudioStateChangedFromStateMachine( BluetoothDevice device, int fromState, int toState)2320     public void onAudioStateChangedFromStateMachine(
2321             BluetoothDevice device, int fromState, int toState) {
2322         synchronized (mStateMachines) {
2323             if (toState == BluetoothHeadset.STATE_AUDIO_DISCONNECTED) {
2324                 if (fromState != BluetoothHeadset.STATE_AUDIO_DISCONNECTED) {
2325                     if (mActiveDevice != null
2326                             && !mActiveDevice.equals(device)
2327                             && shouldPersistAudio()) {
2328                         int connectStatus = connectAudio(mActiveDevice);
2329                         if (connectStatus != BluetoothStatusCodes.SUCCESS) {
2330                             Log.w(
2331                                     TAG,
2332                                     "onAudioStateChangedFromStateMachine, failed to connect"
2333                                             + " audio to new "
2334                                             + "active device "
2335                                             + mActiveDevice
2336                                             + ", after "
2337                                             + device
2338                                             + " is disconnected from SCO due to"
2339                                             + " status code "
2340                                             + connectStatus);
2341                         }
2342                     }
2343                 }
2344                 if (mVoiceRecognitionStarted) {
2345                     if (!stopVoiceRecognitionByHeadset(device)) {
2346                         Log.w(
2347                                 TAG,
2348                                 "onAudioStateChangedFromStateMachine: failed to stop voice "
2349                                         + "recognition");
2350                     }
2351                 }
2352                 if (mVirtualCallStarted) {
2353                     if (!stopScoUsingVirtualVoiceCall()) {
2354                         Log.w(
2355                                 TAG,
2356                                 "onAudioStateChangedFromStateMachine: failed to stop virtual "
2357                                         + "voice call");
2358                     }
2359                 }
2360                 // Resumes LE audio previous active device if HFP handover happened before.
2361                 // Do it here because some controllers cannot handle SCO and CIS
2362                 // co-existence see {@link LeAudioService#setInactiveForHfpHandover}
2363                 if (Flags.leaudioResumeActiveAfterHfpHandover()) {
2364                     LeAudioService leAudioService = mFactory.getLeAudioService();
2365                     if (!Flags.keepHfpActiveDuringLeaudioHandover()
2366                             && leAudioService != null
2367                             && !leAudioService.getConnectedDevices().isEmpty()
2368                             && leAudioService.getActiveDevices().get(0) == null) {
2369                         leAudioService.setActiveAfterHfpHandover();
2370                     }
2371 
2372                     // usually controller limitation cause CONNECTING -> DISCONNECTED, so only
2373                     // resume LE audio active device if it is HFP audio only and SCO disconnected
2374                     if (Flags.keepHfpActiveDuringLeaudioHandover()
2375                             && fromState != BluetoothHeadset.STATE_AUDIO_CONNECTING
2376                             && isHFPAudioOnly(device)) {
2377 
2378                         if (leAudioService != null
2379                                 && !leAudioService.getConnectedDevices().isEmpty()
2380                                 && leAudioService.getActiveDevices().get(0) == null) {
2381                             leAudioService.setActiveAfterHfpHandover();
2382                         }
2383                     }
2384                 }
2385 
2386                 // Unsuspend A2DP when SCO connection is gone and call state is idle
2387                 if (mSystemInterface.isCallIdle() && !Utils.isScoManagedByAudioEnabled()) {
2388                     mSystemInterface.getAudioManager().setA2dpSuspended(false);
2389                     if (isAtLeastU()) {
2390                         mSystemInterface.getAudioManager().setLeAudioSuspended(false);
2391                     }
2392                 }
2393             }
2394         }
2395     }
2396 
broadcastActiveDevice(BluetoothDevice device)2397     private void broadcastActiveDevice(BluetoothDevice device) {
2398         logD("broadcastActiveDevice: " + device);
2399 
2400         mAdapterService.handleActiveDeviceChange(BluetoothProfile.HEADSET, device);
2401 
2402         BluetoothStatsLog.write(
2403                 BluetoothStatsLog.BLUETOOTH_ACTIVE_DEVICE_CHANGED,
2404                 BluetoothProfile.HEADSET,
2405                 mAdapterService.obfuscateAddress(device),
2406                 mAdapterService.getMetricId(device));
2407 
2408         Intent intent = new Intent(BluetoothHeadset.ACTION_ACTIVE_DEVICE_CHANGED);
2409         intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
2410         intent.addFlags(
2411                 Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
2412                         | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
2413         sendBroadcastAsUser(
2414                 intent,
2415                 UserHandle.ALL,
2416                 BLUETOOTH_CONNECT,
2417                 Utils.getTempBroadcastOptions().toBundle());
2418     }
2419 
2420     /**
2421      * Check whether it is OK to accept a headset connection from a remote device
2422      *
2423      * @param device remote device that initiates the connection
2424      * @return true if the connection is acceptable
2425      */
okToAcceptConnection(BluetoothDevice device, boolean isOutgoingRequest)2426     public boolean okToAcceptConnection(BluetoothDevice device, boolean isOutgoingRequest) {
2427         // Check if this is an incoming connection in Quiet mode.
2428         if (mAdapterService.isQuietModeEnabled()) {
2429             Log.w(TAG, "okToAcceptConnection: return false as quiet mode enabled");
2430             return false;
2431         }
2432         // Check connection policy and accept or reject the connection.
2433         int connectionPolicy = getConnectionPolicy(device);
2434         int bondState = mAdapterService.getBondState(device);
2435         // Allow this connection only if the device is bonded. Any attempt to connect while
2436         // bonding would potentially lead to an unauthorized connection.
2437         if (bondState != BluetoothDevice.BOND_BONDED) {
2438             Log.w(TAG, "okToAcceptConnection: return false, bondState=" + bondState);
2439             return false;
2440         } else if (connectionPolicy != BluetoothProfile.CONNECTION_POLICY_UNKNOWN
2441                 && connectionPolicy != BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
2442             // Otherwise, reject the connection if connection policy is not valid.
2443             if (!isOutgoingRequest) {
2444                 A2dpService a2dpService = A2dpService.getA2dpService();
2445                 if (a2dpService != null && a2dpService.okToConnect(device, true)) {
2446                     Log.d(
2447                             TAG,
2448                             "okToAcceptConnection: return false,"
2449                                     + " Fallback connection to allowed A2DP profile");
2450                     a2dpService.connect(device);
2451                     return false;
2452                 }
2453             }
2454             Log.w(TAG, "okToAcceptConnection: return false, connectionPolicy=" + connectionPolicy);
2455             return false;
2456         }
2457         List<BluetoothDevice> connectingConnectedDevices =
2458                 getDevicesMatchingConnectionStates(CONNECTING_CONNECTED_STATES);
2459         if (connectingConnectedDevices.size() >= mMaxHeadsetConnections) {
2460             Log.w(
2461                     TAG,
2462                     "Maximum number of connections "
2463                             + mMaxHeadsetConnections
2464                             + " was reached, rejecting connection from "
2465                             + device);
2466             return false;
2467         }
2468         return true;
2469     }
2470 
2471     /**
2472      * Checks if SCO should be connected at current system state. Returns {@link
2473      * BluetoothStatusCodes#SUCCESS} if SCO is allowed to be connected or an error code on failure.
2474      *
2475      * @param device device for SCO to be connected
2476      * @return whether SCO can be connected
2477      */
isScoAcceptable(BluetoothDevice device)2478     public int isScoAcceptable(BluetoothDevice device) {
2479         synchronized (mStateMachines) {
2480             if (device == null || !device.equals(mActiveDevice)) {
2481                 Log.w(
2482                         TAG,
2483                         "isScoAcceptable: rejected SCO since "
2484                                 + device
2485                                 + " is not the current active device "
2486                                 + mActiveDevice);
2487                 return BluetoothStatusCodes.ERROR_NOT_ACTIVE_DEVICE;
2488             }
2489             if (SystemProperties.getBoolean(REJECT_SCO_IF_HFPC_CONNECTED_PROPERTY, false)
2490                     && isHeadsetClientConnected()) {
2491                 Log.w(TAG, "isScoAcceptable: rejected SCO since HFPC is connected!");
2492                 return BluetoothStatusCodes.ERROR_AUDIO_ROUTE_BLOCKED;
2493             }
2494             if (mForceScoAudio) {
2495                 return BluetoothStatusCodes.SUCCESS;
2496             }
2497             if (!mAudioRouteAllowed) {
2498                 Log.w(TAG, "isScoAcceptable: rejected SCO since audio route is not allowed");
2499                 return BluetoothStatusCodes.ERROR_AUDIO_ROUTE_BLOCKED;
2500             }
2501             if (mVoiceRecognitionStarted || mVirtualCallStarted) {
2502                 return BluetoothStatusCodes.SUCCESS;
2503             }
2504             if (shouldCallAudioBeActive()) {
2505                 return BluetoothStatusCodes.SUCCESS;
2506             }
2507             Log.w(
2508                     TAG,
2509                     "isScoAcceptable: rejected SCO, inCall="
2510                             + mSystemInterface.isInCall()
2511                             + ", voiceRecognition="
2512                             + mVoiceRecognitionStarted
2513                             + ", ringing="
2514                             + mSystemInterface.isRinging()
2515                             + ", inbandRinging="
2516                             + isInbandRingingEnabled()
2517                             + ", isVirtualCallStarted="
2518                             + mVirtualCallStarted);
2519             return BluetoothStatusCodes.ERROR_CALL_ACTIVE;
2520         }
2521     }
2522 
2523     /**
2524      * Remove state machine in {@link #mStateMachines} for a {@link BluetoothDevice}
2525      *
2526      * @param device device whose state machine is to be removed.
2527      */
removeStateMachine(BluetoothDevice device)2528     void removeStateMachine(BluetoothDevice device) {
2529         synchronized (mStateMachines) {
2530             HeadsetStateMachine stateMachine = mStateMachines.get(device);
2531             if (stateMachine == null) {
2532                 Log.w(TAG, "removeStateMachine(), " + device + " does not have a state machine");
2533                 return;
2534             }
2535             Log.i(TAG, "removeStateMachine(), removing state machine for device: " + device);
2536             HeadsetObjectsFactory.getInstance().destroyStateMachine(stateMachine);
2537             mStateMachines.remove(device);
2538         }
2539     }
2540 
2541     /** Retrieves the most recently connected device in the A2DP connected devices list. */
getFallbackDevice()2542     public BluetoothDevice getFallbackDevice() {
2543         DatabaseManager dbManager = mAdapterService.getDatabase();
2544         return dbManager != null
2545                 ? dbManager.getMostRecentlyConnectedDevicesInList(getFallbackCandidates(dbManager))
2546                 : null;
2547     }
2548 
2549     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
getFallbackCandidates(DatabaseManager dbManager)2550     List<BluetoothDevice> getFallbackCandidates(DatabaseManager dbManager) {
2551         List<BluetoothDevice> fallbackCandidates = getConnectedDevices();
2552         List<BluetoothDevice> uninterestedCandidates = new ArrayList<>();
2553         for (BluetoothDevice device : fallbackCandidates) {
2554             byte[] deviceType =
2555                     dbManager.getCustomMeta(device, BluetoothDevice.METADATA_DEVICE_TYPE);
2556             BluetoothClass deviceClass = device.getBluetoothClass();
2557             if ((deviceClass != null
2558                             && deviceClass.getMajorDeviceClass()
2559                                     == BluetoothClass.Device.WEARABLE_WRIST_WATCH)
2560                     || (deviceType != null
2561                             && BluetoothDevice.DEVICE_TYPE_WATCH.equals(new String(deviceType)))) {
2562                 uninterestedCandidates.add(device);
2563             }
2564         }
2565         for (BluetoothDevice device : uninterestedCandidates) {
2566             fallbackCandidates.remove(device);
2567         }
2568         return fallbackCandidates;
2569     }
2570 
2571     @Override
dump(StringBuilder sb)2572     public void dump(StringBuilder sb) {
2573         boolean isScoOn = mSystemInterface.getAudioManager().isBluetoothScoOn();
2574         boolean isInbandRingingSupported =
2575                 getResources()
2576                         .getBoolean(
2577                                 com.android.bluetooth.R.bool
2578                                         .config_bluetooth_hfp_inband_ringing_support);
2579         synchronized (mStateMachines) {
2580             super.dump(sb);
2581             ProfileService.println(sb, "mMaxHeadsetConnections: " + mMaxHeadsetConnections);
2582             ProfileService.println(
2583                     sb,
2584                     "DefaultMaxHeadsetConnections: "
2585                             + mAdapterService.getMaxConnectedAudioDevices());
2586             ProfileService.println(sb, "mActiveDevice: " + mActiveDevice);
2587             ProfileService.println(sb, "isInbandRingingEnabled: " + isInbandRingingEnabled());
2588             ProfileService.println(sb, "isInbandRingingSupported: " + isInbandRingingSupported);
2589             ProfileService.println(
2590                     sb, "mInbandRingingRuntimeDisable: " + mInbandRingingRuntimeDisable);
2591             ProfileService.println(sb, "mAudioRouteAllowed: " + mAudioRouteAllowed);
2592             ProfileService.println(sb, "mVoiceRecognitionStarted: " + mVoiceRecognitionStarted);
2593             ProfileService.println(
2594                     sb, "mVoiceRecognitionTimeoutEvent: " + mVoiceRecognitionTimeoutEvent);
2595             ProfileService.println(sb, "mVirtualCallStarted: " + mVirtualCallStarted);
2596             ProfileService.println(sb, "mDialingOutTimeoutEvent: " + mDialingOutTimeoutEvent);
2597             ProfileService.println(sb, "mForceScoAudio: " + mForceScoAudio);
2598             ProfileService.println(sb, "AudioManager.isBluetoothScoOn(): " + isScoOn);
2599             ProfileService.println(sb, "Telecom.isInCall(): " + mSystemInterface.isInCall());
2600             ProfileService.println(sb, "Telecom.isRinging(): " + mSystemInterface.isRinging());
2601             for (HeadsetStateMachine stateMachine : mStateMachines.values()) {
2602                 ProfileService.println(
2603                         sb, "==== StateMachine for " + stateMachine.getDevice() + " ====");
2604                 stateMachine.dump(sb);
2605             }
2606         }
2607     }
2608 
2609     /** Enable SWB Codec. */
enableSwbCodec(int swbCodec, boolean enable, BluetoothDevice device)2610     void enableSwbCodec(int swbCodec, boolean enable, BluetoothDevice device) {
2611         logD("enableSwbCodec: swbCodec: " + swbCodec + " enable: " + enable + " device: " + device);
2612         boolean result = mNativeInterface.enableSwb(swbCodec, enable, device);
2613         logD("enableSwbCodec result: " + result);
2614     }
2615 
2616     /** Check whether AptX SWB Codec is enabled. */
isAptXSwbEnabled()2617     boolean isAptXSwbEnabled() {
2618         logD("mIsAptXSwbEnabled: " + mIsAptXSwbEnabled);
2619         return mIsAptXSwbEnabled;
2620     }
2621 
2622     /** Check whether AptX SWB Codec Power Management is enabled. */
isAptXSwbPmEnabled()2623     boolean isAptXSwbPmEnabled() {
2624         logD("isAptXSwbPmEnabled: " + mIsAptXSwbPmEnabled);
2625         return mIsAptXSwbPmEnabled;
2626     }
2627 
logD(String message)2628     private static void logD(String message) {
2629         Log.d(TAG, message);
2630     }
2631 }
2632