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