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