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