• 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.MODIFY_PHONE_STATE;
20 
21 import android.annotation.Nullable;
22 import android.bluetooth.BluetoothDevice;
23 import android.bluetooth.BluetoothHeadset;
24 import android.bluetooth.BluetoothProfile;
25 import android.bluetooth.BluetoothUuid;
26 import android.bluetooth.IBluetoothHeadset;
27 import android.content.BroadcastReceiver;
28 import android.content.Context;
29 import android.content.Intent;
30 import android.content.IntentFilter;
31 import android.media.AudioManager;
32 import android.net.Uri;
33 import android.os.BatteryManager;
34 import android.os.HandlerThread;
35 import android.os.IDeviceIdleController;
36 import android.os.Looper;
37 import android.os.ParcelUuid;
38 import android.os.RemoteException;
39 import android.os.ServiceManager;
40 import android.os.SystemProperties;
41 import android.os.UserHandle;
42 import android.provider.Settings;
43 import android.telecom.PhoneAccount;
44 import android.util.Log;
45 
46 import com.android.bluetooth.BluetoothMetricsProto;
47 import com.android.bluetooth.Utils;
48 import com.android.bluetooth.btservice.AdapterService;
49 import com.android.bluetooth.btservice.MetricsLogger;
50 import com.android.bluetooth.btservice.ProfileService;
51 import com.android.internal.annotations.VisibleForTesting;
52 
53 import java.util.ArrayList;
54 import java.util.Arrays;
55 import java.util.Comparator;
56 import java.util.HashMap;
57 import java.util.List;
58 import java.util.Objects;
59 
60 /**
61  * Provides Bluetooth Headset and Handsfree profile, as a service in the Bluetooth application.
62  *
63  * Three modes for SCO audio:
64  * Mode 1: Telecom call through {@link #phoneStateChanged(int, int, int, String, int, boolean)}
65  * Mode 2: Virtual call through {@link #startScoUsingVirtualVoiceCall()}
66  * Mode 3: Voice recognition through {@link #startVoiceRecognition(BluetoothDevice)}
67  *
68  * When one mode is active, other mode cannot be started. API user has to terminate existing modes
69  * using the correct API or just {@link #disconnectAudio()} if user is a system service, before
70  * starting a new mode.
71  *
72  * {@link #connectAudio()} will start SCO audio at one of the above modes, but won't change mode
73  * {@link #disconnectAudio()} can happen in any mode to disconnect SCO
74  *
75  * When audio is disconnected, only Mode 1 Telecom call will be persisted, both Mode 2 virtual call
76  * and Mode 3 voice call will be terminated upon SCO termination and client has to restart the mode.
77  *
78  * NOTE: SCO termination can either be initiated on the AG side or the HF side
79  * TODO(b/79660380): As a workaround, voice recognition will be terminated if virtual call or
80  * Telecom call is initiated while voice recognition is ongoing, in case calling app did not call
81  * {@link #stopVoiceRecognition(BluetoothDevice)}
82  *
83  * AG - Audio Gateway, device running this {@link HeadsetService}, e.g. Android Phone
84  * HF - Handsfree device, device running headset client, e.g. Wireless headphones or car kits
85  */
86 public class HeadsetService extends ProfileService {
87     private static final String TAG = "HeadsetService";
88     private static final boolean DBG = false;
89     private static final String DISABLE_INBAND_RINGING_PROPERTY =
90             "persist.bluetooth.disableinbandringing";
91     private static final ParcelUuid[] HEADSET_UUIDS = {BluetoothUuid.HSP, BluetoothUuid.Handsfree};
92     private static final int[] CONNECTING_CONNECTED_STATES =
93             {BluetoothProfile.STATE_CONNECTING, BluetoothProfile.STATE_CONNECTED};
94     private static final int DIALING_OUT_TIMEOUT_MS = 10000;
95 
96     private int mMaxHeadsetConnections = 1;
97     private BluetoothDevice mActiveDevice;
98     private AdapterService mAdapterService;
99     private HandlerThread mStateMachinesThread;
100     // This is also used as a lock for shared data in HeadsetService
101     private final HashMap<BluetoothDevice, HeadsetStateMachine> mStateMachines = new HashMap<>();
102     private HeadsetNativeInterface mNativeInterface;
103     private HeadsetSystemInterface mSystemInterface;
104     private boolean mAudioRouteAllowed = true;
105     // Indicates whether SCO audio needs to be forced to open regardless ANY OTHER restrictions
106     private boolean mForceScoAudio;
107     private boolean mInbandRingingRuntimeDisable;
108     private boolean mVirtualCallStarted;
109     // Non null value indicates a pending dialing out event is going on
110     private DialingOutTimeoutEvent mDialingOutTimeoutEvent;
111     private boolean mVoiceRecognitionStarted;
112     // Non null value indicates a pending voice recognition request from headset is going on
113     private VoiceRecognitionTimeoutEvent mVoiceRecognitionTimeoutEvent;
114     // Timeout when voice recognition is started by remote device
115     @VisibleForTesting static int sStartVrTimeoutMs = 5000;
116     private boolean mStarted;
117     private boolean mCreated;
118     private static HeadsetService sHeadsetService;
119 
120     @Override
initBinder()121     public IProfileServiceBinder initBinder() {
122         return new BluetoothHeadsetBinder(this);
123     }
124 
125     @Override
create()126     protected void create() {
127         Log.i(TAG, "create()");
128         if (mCreated) {
129             throw new IllegalStateException("create() called twice");
130         }
131         mCreated = true;
132     }
133 
134     @Override
start()135     protected boolean start() {
136         Log.i(TAG, "start()");
137         if (mStarted) {
138             throw new IllegalStateException("start() called twice");
139         }
140         // Step 1: Get adapter service, should never be null
141         mAdapterService = Objects.requireNonNull(AdapterService.getAdapterService(),
142                 "AdapterService cannot be null when HeadsetService starts");
143         // Step 2: Start handler thread for state machines
144         mStateMachinesThread = new HandlerThread("HeadsetService.StateMachines");
145         mStateMachinesThread.start();
146         // Step 3: Initialize system interface
147         mSystemInterface = HeadsetObjectsFactory.getInstance().makeSystemInterface(this);
148         mSystemInterface.init();
149         // Step 4: Initialize native interface
150         mMaxHeadsetConnections = mAdapterService.getMaxConnectedAudioDevices();
151         mNativeInterface = HeadsetObjectsFactory.getInstance().getNativeInterface();
152         // Add 1 to allow a pending device to be connecting or disconnecting
153         mNativeInterface.init(mMaxHeadsetConnections + 1, isInbandRingingEnabled());
154         // Step 5: Check if state machine table is empty, crash if not
155         if (mStateMachines.size() > 0) {
156             throw new IllegalStateException(
157                     "start(): mStateMachines is not empty, " + mStateMachines.size()
158                             + " is already created. Was stop() called properly?");
159         }
160         // Step 6: Setup broadcast receivers
161         IntentFilter filter = new IntentFilter();
162         filter.addAction(Intent.ACTION_BATTERY_CHANGED);
163         filter.addAction(AudioManager.VOLUME_CHANGED_ACTION);
164         filter.addAction(BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY);
165         filter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
166         registerReceiver(mHeadsetReceiver, filter);
167         // Step 7: Mark service as started
168         setHeadsetService(this);
169         mStarted = true;
170         return true;
171     }
172 
173     @Override
stop()174     protected boolean stop() {
175         Log.i(TAG, "stop()");
176         if (!mStarted) {
177             Log.w(TAG, "stop() called before start()");
178             // Still return true because it is considered "stopped" and doesn't have any functional
179             // impact on the user
180             return true;
181         }
182         // Step 7: Mark service as stopped
183         mStarted = false;
184         setHeadsetService(null);
185         // Step 6: Tear down broadcast receivers
186         unregisterReceiver(mHeadsetReceiver);
187         synchronized (mStateMachines) {
188             // Reset active device to null
189             mActiveDevice = null;
190             mInbandRingingRuntimeDisable = false;
191             mForceScoAudio = false;
192             mAudioRouteAllowed = true;
193             mMaxHeadsetConnections = 1;
194             mVoiceRecognitionStarted = false;
195             mVirtualCallStarted = false;
196             if (mDialingOutTimeoutEvent != null) {
197                 mStateMachinesThread.getThreadHandler().removeCallbacks(mDialingOutTimeoutEvent);
198                 mDialingOutTimeoutEvent = null;
199             }
200             if (mVoiceRecognitionTimeoutEvent != null) {
201                 mStateMachinesThread.getThreadHandler()
202                         .removeCallbacks(mVoiceRecognitionTimeoutEvent);
203                 mVoiceRecognitionTimeoutEvent = null;
204                 if (mSystemInterface.getVoiceRecognitionWakeLock().isHeld()) {
205                     mSystemInterface.getVoiceRecognitionWakeLock().release();
206                 }
207             }
208             // Step 5: Destroy state machines
209             for (HeadsetStateMachine stateMachine : mStateMachines.values()) {
210                 HeadsetObjectsFactory.getInstance().destroyStateMachine(stateMachine);
211             }
212             mStateMachines.clear();
213         }
214         // Step 4: Destroy native interface
215         mNativeInterface.cleanup();
216         // Step 3: Destroy system interface
217         mSystemInterface.stop();
218         // Step 2: Stop handler thread
219         mStateMachinesThread.quitSafely();
220         mStateMachinesThread = null;
221         // Step 1: Clear
222         mAdapterService = null;
223         return true;
224     }
225 
226     @Override
cleanup()227     protected void cleanup() {
228         Log.i(TAG, "cleanup");
229         if (!mCreated) {
230             Log.w(TAG, "cleanup() called before create()");
231         }
232         mCreated = false;
233     }
234 
235     /**
236      * Checks if this service object is able to accept binder calls
237      *
238      * @return True if the object can accept binder calls, False otherwise
239      */
isAlive()240     public boolean isAlive() {
241         return isAvailable() && mCreated && mStarted;
242     }
243 
244     /**
245      * Get the {@link Looper} for the state machine thread. This is used in testing and helper
246      * objects
247      *
248      * @return {@link Looper} for the state machine thread
249      */
250     @VisibleForTesting
getStateMachinesThreadLooper()251     public Looper getStateMachinesThreadLooper() {
252         return mStateMachinesThread.getLooper();
253     }
254 
255     interface StateMachineTask {
execute(HeadsetStateMachine stateMachine)256         void execute(HeadsetStateMachine stateMachine);
257     }
258 
doForStateMachine(BluetoothDevice device, StateMachineTask task)259     private boolean doForStateMachine(BluetoothDevice device, StateMachineTask task) {
260         synchronized (mStateMachines) {
261             HeadsetStateMachine stateMachine = mStateMachines.get(device);
262             if (stateMachine == null) {
263                 return false;
264             }
265             task.execute(stateMachine);
266         }
267         return true;
268     }
269 
doForEachConnectedStateMachine(StateMachineTask task)270     private void doForEachConnectedStateMachine(StateMachineTask task) {
271         synchronized (mStateMachines) {
272             for (BluetoothDevice device : getConnectedDevices()) {
273                 task.execute(mStateMachines.get(device));
274             }
275         }
276     }
277 
onDeviceStateChanged(HeadsetDeviceState deviceState)278     void onDeviceStateChanged(HeadsetDeviceState deviceState) {
279         doForEachConnectedStateMachine(
280                 stateMachine -> stateMachine.sendMessage(HeadsetStateMachine.DEVICE_STATE_CHANGED,
281                         deviceState));
282     }
283 
284     /**
285      * Handle messages from native (JNI) to Java. This needs to be synchronized to avoid posting
286      * messages to state machine before start() is done
287      *
288      * @param stackEvent event from native stack
289      */
messageFromNative(HeadsetStackEvent stackEvent)290     void messageFromNative(HeadsetStackEvent stackEvent) {
291         Objects.requireNonNull(stackEvent.device,
292                 "Device should never be null, event: " + stackEvent);
293         synchronized (mStateMachines) {
294             HeadsetStateMachine stateMachine = mStateMachines.get(stackEvent.device);
295             if (stackEvent.type == HeadsetStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED) {
296                 switch (stackEvent.valueInt) {
297                     case HeadsetHalConstants.CONNECTION_STATE_CONNECTED:
298                     case HeadsetHalConstants.CONNECTION_STATE_CONNECTING: {
299                         // Create new state machine if none is found
300                         if (stateMachine == null) {
301                             stateMachine = HeadsetObjectsFactory.getInstance()
302                                     .makeStateMachine(stackEvent.device,
303                                             mStateMachinesThread.getLooper(), this, mAdapterService,
304                                             mNativeInterface, mSystemInterface);
305                             mStateMachines.put(stackEvent.device, stateMachine);
306                         }
307                         break;
308                     }
309                 }
310             }
311             if (stateMachine == null) {
312                 throw new IllegalStateException(
313                         "State machine not found for stack event: " + stackEvent);
314             }
315             stateMachine.sendMessage(HeadsetStateMachine.STACK_EVENT, stackEvent);
316         }
317     }
318 
319     private final BroadcastReceiver mHeadsetReceiver = new BroadcastReceiver() {
320         @Override
321         public void onReceive(Context context, Intent intent) {
322             String action = intent.getAction();
323             if (action == null) {
324                 Log.w(TAG, "mHeadsetReceiver, action is null");
325                 return;
326             }
327             switch (action) {
328                 case Intent.ACTION_BATTERY_CHANGED: {
329                     int batteryLevel = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);
330                     int scale = intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1);
331                     if (batteryLevel < 0 || scale <= 0) {
332                         Log.e(TAG, "Bad Battery Changed intent: batteryLevel=" + batteryLevel
333                                 + ", scale=" + scale);
334                         return;
335                     }
336                     batteryLevel = batteryLevel * 5 / scale;
337                     mSystemInterface.getHeadsetPhoneState().setCindBatteryCharge(batteryLevel);
338                     break;
339                 }
340                 case AudioManager.VOLUME_CHANGED_ACTION: {
341                     int streamType = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, -1);
342                     if (streamType == AudioManager.STREAM_BLUETOOTH_SCO) {
343                         doForEachConnectedStateMachine(stateMachine -> stateMachine.sendMessage(
344                                 HeadsetStateMachine.INTENT_SCO_VOLUME_CHANGED, intent));
345                     }
346                     break;
347                 }
348                 case BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY: {
349                     int requestType = intent.getIntExtra(BluetoothDevice.EXTRA_ACCESS_REQUEST_TYPE,
350                             BluetoothDevice.REQUEST_TYPE_PHONEBOOK_ACCESS);
351                     BluetoothDevice device =
352                             intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
353                     logD("Received BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY, device=" + device
354                             + ", type=" + requestType);
355                     if (requestType == BluetoothDevice.REQUEST_TYPE_PHONEBOOK_ACCESS) {
356                         synchronized (mStateMachines) {
357                             final HeadsetStateMachine stateMachine = mStateMachines.get(device);
358                             if (stateMachine == null) {
359                                 Log.wtfStack(TAG, "Cannot find state machine for " + device);
360                                 return;
361                             }
362                             stateMachine.sendMessage(
363                                     HeadsetStateMachine.INTENT_CONNECTION_ACCESS_REPLY, intent);
364                         }
365                     }
366                     break;
367                 }
368                 case BluetoothDevice.ACTION_BOND_STATE_CHANGED: {
369                     int state = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE,
370                             BluetoothDevice.ERROR);
371                     BluetoothDevice device = Objects.requireNonNull(
372                             intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE),
373                             "ACTION_BOND_STATE_CHANGED with no EXTRA_DEVICE");
374                     logD("Bond state changed for device: " + device + " state: " + state);
375                     if (state != BluetoothDevice.BOND_NONE) {
376                         break;
377                     }
378                     synchronized (mStateMachines) {
379                         HeadsetStateMachine stateMachine = mStateMachines.get(device);
380                         if (stateMachine == null) {
381                             break;
382                         }
383                         if (stateMachine.getConnectionState()
384                                 != BluetoothProfile.STATE_DISCONNECTED) {
385                             break;
386                         }
387                         removeStateMachine(device);
388                     }
389                     break;
390                 }
391                 default:
392                     Log.w(TAG, "Unknown action " + action);
393             }
394         }
395     };
396 
397     /**
398      * Handlers for incoming service calls
399      */
400     private static class BluetoothHeadsetBinder extends IBluetoothHeadset.Stub
401             implements IProfileServiceBinder {
402         private volatile HeadsetService mService;
403 
BluetoothHeadsetBinder(HeadsetService svc)404         BluetoothHeadsetBinder(HeadsetService svc) {
405             mService = svc;
406         }
407 
408         @Override
cleanup()409         public void cleanup() {
410             mService = null;
411         }
412 
getService()413         private HeadsetService getService() {
414             final HeadsetService service = mService;
415             if (!Utils.checkCallerAllowManagedProfiles(service)) {
416                 Log.w(TAG, "Headset call not allowed for non-active user");
417                 return null;
418             }
419             if (service == null) {
420                 Log.w(TAG, "Service is null");
421                 return null;
422             }
423             if (!service.isAlive()) {
424                 Log.w(TAG, "Service is not alive");
425                 return null;
426             }
427             return service;
428         }
429 
430         @Override
connect(BluetoothDevice device)431         public boolean connect(BluetoothDevice device) {
432             HeadsetService service = getService();
433             if (service == null) {
434                 return false;
435             }
436             return service.connect(device);
437         }
438 
439         @Override
disconnect(BluetoothDevice device)440         public boolean disconnect(BluetoothDevice device) {
441             HeadsetService service = getService();
442             if (service == null) {
443                 return false;
444             }
445             return service.disconnect(device);
446         }
447 
448         @Override
getConnectedDevices()449         public List<BluetoothDevice> getConnectedDevices() {
450             HeadsetService service = getService();
451             if (service == null) {
452                 return new ArrayList<BluetoothDevice>(0);
453             }
454             return service.getConnectedDevices();
455         }
456 
457         @Override
getDevicesMatchingConnectionStates(int[] states)458         public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
459             HeadsetService service = getService();
460             if (service == null) {
461                 return new ArrayList<BluetoothDevice>(0);
462             }
463             return service.getDevicesMatchingConnectionStates(states);
464         }
465 
466         @Override
getConnectionState(BluetoothDevice device)467         public int getConnectionState(BluetoothDevice device) {
468             HeadsetService service = getService();
469             if (service == null) {
470                 return BluetoothProfile.STATE_DISCONNECTED;
471             }
472             return service.getConnectionState(device);
473         }
474 
475         @Override
setPriority(BluetoothDevice device, int priority)476         public boolean setPriority(BluetoothDevice device, int priority) {
477             HeadsetService service = getService();
478             if (service == null) {
479                 return false;
480             }
481             return service.setPriority(device, priority);
482         }
483 
484         @Override
getPriority(BluetoothDevice device)485         public int getPriority(BluetoothDevice device) {
486             HeadsetService service = getService();
487             if (service == null) {
488                 return BluetoothProfile.PRIORITY_UNDEFINED;
489             }
490             return service.getPriority(device);
491         }
492 
493         @Override
startVoiceRecognition(BluetoothDevice device)494         public boolean startVoiceRecognition(BluetoothDevice device) {
495             HeadsetService service = getService();
496             if (service == null) {
497                 return false;
498             }
499             return service.startVoiceRecognition(device);
500         }
501 
502         @Override
stopVoiceRecognition(BluetoothDevice device)503         public boolean stopVoiceRecognition(BluetoothDevice device) {
504             HeadsetService service = getService();
505             if (service == null) {
506                 return false;
507             }
508             return service.stopVoiceRecognition(device);
509         }
510 
511         @Override
isAudioOn()512         public boolean isAudioOn() {
513             HeadsetService service = getService();
514             if (service == null) {
515                 return false;
516             }
517             return service.isAudioOn();
518         }
519 
520         @Override
isAudioConnected(BluetoothDevice device)521         public boolean isAudioConnected(BluetoothDevice device) {
522             HeadsetService service = getService();
523             if (service == null) {
524                 return false;
525             }
526             return service.isAudioConnected(device);
527         }
528 
529         @Override
getAudioState(BluetoothDevice device)530         public int getAudioState(BluetoothDevice device) {
531             HeadsetService service = getService();
532             if (service == null) {
533                 return BluetoothHeadset.STATE_AUDIO_DISCONNECTED;
534             }
535             return service.getAudioState(device);
536         }
537 
538         @Override
connectAudio()539         public boolean connectAudio() {
540             HeadsetService service = getService();
541             if (service == null) {
542                 return false;
543             }
544             return service.connectAudio();
545         }
546 
547         @Override
disconnectAudio()548         public boolean disconnectAudio() {
549             HeadsetService service = getService();
550             if (service == null) {
551                 return false;
552             }
553             return service.disconnectAudio();
554         }
555 
556         @Override
setAudioRouteAllowed(boolean allowed)557         public void setAudioRouteAllowed(boolean allowed) {
558             HeadsetService service = getService();
559             if (service == null) {
560                 return;
561             }
562             service.setAudioRouteAllowed(allowed);
563         }
564 
565         @Override
getAudioRouteAllowed()566         public boolean getAudioRouteAllowed() {
567             HeadsetService service = getService();
568             if (service != null) {
569                 return service.getAudioRouteAllowed();
570             }
571             return false;
572         }
573 
574         @Override
setForceScoAudio(boolean forced)575         public void setForceScoAudio(boolean forced) {
576             HeadsetService service = getService();
577             if (service == null) {
578                 return;
579             }
580             service.setForceScoAudio(forced);
581         }
582 
583         @Override
startScoUsingVirtualVoiceCall()584         public boolean startScoUsingVirtualVoiceCall() {
585             HeadsetService service = getService();
586             if (service == null) {
587                 return false;
588             }
589             return service.startScoUsingVirtualVoiceCall();
590         }
591 
592         @Override
stopScoUsingVirtualVoiceCall()593         public boolean stopScoUsingVirtualVoiceCall() {
594             HeadsetService service = getService();
595             if (service == null) {
596                 return false;
597             }
598             return service.stopScoUsingVirtualVoiceCall();
599         }
600 
601         @Override
phoneStateChanged(int numActive, int numHeld, int callState, String number, int type)602         public void phoneStateChanged(int numActive, int numHeld, int callState, String number,
603                 int type) {
604             HeadsetService service = getService();
605             if (service == null) {
606                 return;
607             }
608             service.phoneStateChanged(numActive, numHeld, callState, number, type, false);
609         }
610 
611         @Override
clccResponse(int index, int direction, int status, int mode, boolean mpty, String number, int type)612         public void clccResponse(int index, int direction, int status, int mode, boolean mpty,
613                 String number, int type) {
614             HeadsetService service = getService();
615             if (service == null) {
616                 return;
617             }
618             service.clccResponse(index, direction, status, mode, mpty, number, type);
619         }
620 
621         @Override
sendVendorSpecificResultCode(BluetoothDevice device, String command, String arg)622         public boolean sendVendorSpecificResultCode(BluetoothDevice device, String command,
623                 String arg) {
624             HeadsetService service = getService();
625             if (service == null) {
626                 return false;
627             }
628             return service.sendVendorSpecificResultCode(device, command, arg);
629         }
630 
631         @Override
setActiveDevice(BluetoothDevice device)632         public boolean setActiveDevice(BluetoothDevice device) {
633             HeadsetService service = getService();
634             if (service == null) {
635                 return false;
636             }
637             return service.setActiveDevice(device);
638         }
639 
640         @Override
getActiveDevice()641         public BluetoothDevice getActiveDevice() {
642             HeadsetService service = getService();
643             if (service == null) {
644                 return null;
645             }
646             return service.getActiveDevice();
647         }
648 
649         @Override
isInbandRingingEnabled()650         public boolean isInbandRingingEnabled() {
651             HeadsetService service = getService();
652             if (service == null) {
653                 return false;
654             }
655             return service.isInbandRingingEnabled();
656         }
657     }
658 
659     // API methods
getHeadsetService()660     public static synchronized HeadsetService getHeadsetService() {
661         if (sHeadsetService == null) {
662             Log.w(TAG, "getHeadsetService(): service is NULL");
663             return null;
664         }
665         if (!sHeadsetService.isAvailable()) {
666             Log.w(TAG, "getHeadsetService(): service is not available");
667             return null;
668         }
669         logD("getHeadsetService(): returning " + sHeadsetService);
670         return sHeadsetService;
671     }
672 
setHeadsetService(HeadsetService instance)673     private static synchronized void setHeadsetService(HeadsetService instance) {
674         logD("setHeadsetService(): set to: " + instance);
675         sHeadsetService = instance;
676     }
677 
connect(BluetoothDevice device)678     public boolean connect(BluetoothDevice device) {
679         enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH ADMIN permission");
680         if (getPriority(device) == BluetoothProfile.PRIORITY_OFF) {
681             Log.w(TAG, "connect: PRIORITY_OFF, device=" + device + ", " + Utils.getUidPidString());
682             return false;
683         }
684         ParcelUuid[] featureUuids = mAdapterService.getRemoteUuids(device);
685         if (!BluetoothUuid.containsAnyUuid(featureUuids, HEADSET_UUIDS)) {
686             Log.e(TAG, "connect: Cannot connect to " + device + ": no headset UUID, "
687                     + Utils.getUidPidString());
688             return false;
689         }
690         synchronized (mStateMachines) {
691             Log.i(TAG, "connect: device=" + device + ", " + Utils.getUidPidString());
692             HeadsetStateMachine stateMachine = mStateMachines.get(device);
693             if (stateMachine == null) {
694                 stateMachine = HeadsetObjectsFactory.getInstance()
695                         .makeStateMachine(device, mStateMachinesThread.getLooper(), this,
696                                 mAdapterService, mNativeInterface, mSystemInterface);
697                 mStateMachines.put(device, stateMachine);
698             }
699             int connectionState = stateMachine.getConnectionState();
700             if (connectionState == BluetoothProfile.STATE_CONNECTED
701                     || connectionState == BluetoothProfile.STATE_CONNECTING) {
702                 Log.w(TAG, "connect: device " + device
703                         + " is already connected/connecting, connectionState=" + connectionState);
704                 return false;
705             }
706             List<BluetoothDevice> connectingConnectedDevices =
707                     getDevicesMatchingConnectionStates(CONNECTING_CONNECTED_STATES);
708             boolean disconnectExisting = false;
709             if (connectingConnectedDevices.size() >= mMaxHeadsetConnections) {
710                 // When there is maximum one device, we automatically disconnect the current one
711                 if (mMaxHeadsetConnections == 1) {
712                     disconnectExisting = true;
713                 } else {
714                     Log.w(TAG, "Max connection has reached, rejecting connection to " + device);
715                     return false;
716                 }
717             }
718             if (disconnectExisting) {
719                 for (BluetoothDevice connectingConnectedDevice : connectingConnectedDevices) {
720                     disconnect(connectingConnectedDevice);
721                 }
722                 setActiveDevice(null);
723             }
724             stateMachine.sendMessage(HeadsetStateMachine.CONNECT, device);
725         }
726         return true;
727     }
728 
disconnect(BluetoothDevice device)729     boolean disconnect(BluetoothDevice device) {
730         enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission");
731         Log.i(TAG, "disconnect: device=" + device + ", " + Utils.getUidPidString());
732         synchronized (mStateMachines) {
733             HeadsetStateMachine stateMachine = mStateMachines.get(device);
734             if (stateMachine == null) {
735                 Log.w(TAG, "disconnect: device " + device + " not ever connected/connecting");
736                 return false;
737             }
738             int connectionState = stateMachine.getConnectionState();
739             if (connectionState != BluetoothProfile.STATE_CONNECTED
740                     && connectionState != BluetoothProfile.STATE_CONNECTING) {
741                 Log.w(TAG, "disconnect: device " + device
742                         + " not connected/connecting, connectionState=" + connectionState);
743                 return false;
744             }
745             stateMachine.sendMessage(HeadsetStateMachine.DISCONNECT, device);
746         }
747         return true;
748     }
749 
getConnectedDevices()750     public List<BluetoothDevice> getConnectedDevices() {
751         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
752         ArrayList<BluetoothDevice> devices = new ArrayList<>();
753         synchronized (mStateMachines) {
754             for (HeadsetStateMachine stateMachine : mStateMachines.values()) {
755                 if (stateMachine.getConnectionState() == BluetoothProfile.STATE_CONNECTED) {
756                     devices.add(stateMachine.getDevice());
757                 }
758             }
759         }
760         return devices;
761     }
762 
763     /**
764      * Same as the API method {@link BluetoothHeadset#getDevicesMatchingConnectionStates(int[])}
765      *
766      * @param states an array of states from {@link BluetoothProfile}
767      * @return a list of devices matching the array of connection states
768      */
769     @VisibleForTesting
getDevicesMatchingConnectionStates(int[] states)770     public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
771         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
772         ArrayList<BluetoothDevice> devices = new ArrayList<>();
773         if (states == null) {
774             return devices;
775         }
776         final BluetoothDevice[] bondedDevices = mAdapterService.getBondedDevices();
777         if (bondedDevices == null) {
778             return devices;
779         }
780         synchronized (mStateMachines) {
781             for (BluetoothDevice device : bondedDevices) {
782                 final ParcelUuid[] featureUuids = mAdapterService.getRemoteUuids(device);
783                 if (!BluetoothUuid.containsAnyUuid(featureUuids, HEADSET_UUIDS)) {
784                     continue;
785                 }
786                 int connectionState = getConnectionState(device);
787                 for (int state : states) {
788                     if (connectionState == state) {
789                         devices.add(device);
790                         break;
791                     }
792                 }
793             }
794         }
795         return devices;
796     }
797 
getConnectionState(BluetoothDevice device)798     public int getConnectionState(BluetoothDevice device) {
799         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
800         synchronized (mStateMachines) {
801             final HeadsetStateMachine stateMachine = mStateMachines.get(device);
802             if (stateMachine == null) {
803                 return BluetoothProfile.STATE_DISCONNECTED;
804             }
805             return stateMachine.getConnectionState();
806         }
807     }
808 
setPriority(BluetoothDevice device, int priority)809     public boolean setPriority(BluetoothDevice device, int priority) {
810         enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission");
811         Settings.Global.putInt(getContentResolver(),
812                 Settings.Global.getBluetoothHeadsetPriorityKey(device.getAddress()), priority);
813         Log.i(TAG, "setPriority: device=" + device + ", priority=" + priority + ", "
814                 + Utils.getUidPidString());
815         return true;
816     }
817 
getPriority(BluetoothDevice device)818     public int getPriority(BluetoothDevice device) {
819         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
820         return Settings.Global.getInt(getContentResolver(),
821                 Settings.Global.getBluetoothHeadsetPriorityKey(device.getAddress()),
822                 BluetoothProfile.PRIORITY_UNDEFINED);
823     }
824 
startVoiceRecognition(BluetoothDevice device)825     boolean startVoiceRecognition(BluetoothDevice device) {
826         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
827         Log.i(TAG, "startVoiceRecognition: device=" + device + ", " + Utils.getUidPidString());
828         synchronized (mStateMachines) {
829             // TODO(b/79660380): Workaround in case voice recognition was not terminated properly
830             if (mVoiceRecognitionStarted) {
831                 boolean status = stopVoiceRecognition(mActiveDevice);
832                 Log.w(TAG, "startVoiceRecognition: voice recognition is still active, just called "
833                         + "stopVoiceRecognition, returned " + status + " on " + mActiveDevice
834                         + ", please try again");
835                 mVoiceRecognitionStarted = false;
836                 return false;
837             }
838             if (!isAudioModeIdle()) {
839                 Log.w(TAG, "startVoiceRecognition: audio mode not idle, active device is "
840                         + mActiveDevice);
841                 return false;
842             }
843             // Audio should not be on when no audio mode is active
844             if (isAudioOn()) {
845                 // Disconnect audio so that API user can try later
846                 boolean status = disconnectAudio();
847                 Log.w(TAG, "startVoiceRecognition: audio is still active, please wait for audio to"
848                         + " be disconnected, disconnectAudio() returned " + status
849                         + ", active device is " + mActiveDevice);
850                 return false;
851             }
852             if (device == null) {
853                 Log.i(TAG, "device is null, use active device " + mActiveDevice + " instead");
854                 device = mActiveDevice;
855             }
856             boolean pendingRequestByHeadset = false;
857             if (mVoiceRecognitionTimeoutEvent != null) {
858                 if (!mVoiceRecognitionTimeoutEvent.mVoiceRecognitionDevice.equals(device)) {
859                     // TODO(b/79660380): Workaround when target device != requesting device
860                     Log.w(TAG, "startVoiceRecognition: device " + device
861                             + " is not the same as requesting device "
862                             + mVoiceRecognitionTimeoutEvent.mVoiceRecognitionDevice
863                             + ", fall back to requesting device");
864                     device = mVoiceRecognitionTimeoutEvent.mVoiceRecognitionDevice;
865                 }
866                 mStateMachinesThread.getThreadHandler()
867                         .removeCallbacks(mVoiceRecognitionTimeoutEvent);
868                 mVoiceRecognitionTimeoutEvent = null;
869                 if (mSystemInterface.getVoiceRecognitionWakeLock().isHeld()) {
870                     mSystemInterface.getVoiceRecognitionWakeLock().release();
871                 }
872                 pendingRequestByHeadset = true;
873             }
874             if (!Objects.equals(device, mActiveDevice) && !setActiveDevice(device)) {
875                 Log.w(TAG, "startVoiceRecognition: failed to set " + device + " as active");
876                 return false;
877             }
878             final HeadsetStateMachine stateMachine = mStateMachines.get(device);
879             if (stateMachine == null) {
880                 Log.w(TAG, "startVoiceRecognition: " + device + " is never connected");
881                 return false;
882             }
883             int connectionState = stateMachine.getConnectionState();
884             if (connectionState != BluetoothProfile.STATE_CONNECTED
885                     && connectionState != BluetoothProfile.STATE_CONNECTING) {
886                 Log.w(TAG, "startVoiceRecognition: " + device + " is not connected or connecting");
887                 return false;
888             }
889             mVoiceRecognitionStarted = true;
890             if (pendingRequestByHeadset) {
891                 stateMachine.sendMessage(HeadsetStateMachine.VOICE_RECOGNITION_RESULT,
892                         1 /* success */, 0, device);
893             } else {
894                 stateMachine.sendMessage(HeadsetStateMachine.VOICE_RECOGNITION_START, device);
895             }
896             stateMachine.sendMessage(HeadsetStateMachine.CONNECT_AUDIO, device);
897         }
898         return true;
899     }
900 
stopVoiceRecognition(BluetoothDevice device)901     boolean stopVoiceRecognition(BluetoothDevice device) {
902         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
903         Log.i(TAG, "stopVoiceRecognition: device=" + device + ", " + Utils.getUidPidString());
904         synchronized (mStateMachines) {
905             if (!Objects.equals(mActiveDevice, device)) {
906                 Log.w(TAG, "startVoiceRecognition: requested device " + device
907                         + " is not active, use active device " + mActiveDevice + " instead");
908                 device = mActiveDevice;
909             }
910             final HeadsetStateMachine stateMachine = mStateMachines.get(device);
911             if (stateMachine == null) {
912                 Log.w(TAG, "stopVoiceRecognition: " + device + " is never connected");
913                 return false;
914             }
915             int connectionState = stateMachine.getConnectionState();
916             if (connectionState != BluetoothProfile.STATE_CONNECTED
917                     && connectionState != BluetoothProfile.STATE_CONNECTING) {
918                 Log.w(TAG, "stopVoiceRecognition: " + device + " is not connected or connecting");
919                 return false;
920             }
921             if (!mVoiceRecognitionStarted) {
922                 Log.w(TAG, "stopVoiceRecognition: voice recognition was not started");
923                 return false;
924             }
925             mVoiceRecognitionStarted = false;
926             stateMachine.sendMessage(HeadsetStateMachine.VOICE_RECOGNITION_STOP, device);
927             stateMachine.sendMessage(HeadsetStateMachine.DISCONNECT_AUDIO, device);
928         }
929         return true;
930     }
931 
isAudioOn()932     boolean isAudioOn() {
933         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
934         return getNonIdleAudioDevices().size() > 0;
935     }
936 
isAudioConnected(BluetoothDevice device)937     boolean isAudioConnected(BluetoothDevice device) {
938         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
939         synchronized (mStateMachines) {
940             final HeadsetStateMachine stateMachine = mStateMachines.get(device);
941             if (stateMachine == null) {
942                 return false;
943             }
944             return stateMachine.getAudioState() == BluetoothHeadset.STATE_AUDIO_CONNECTED;
945         }
946     }
947 
getAudioState(BluetoothDevice device)948     int getAudioState(BluetoothDevice device) {
949         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
950         synchronized (mStateMachines) {
951             final HeadsetStateMachine stateMachine = mStateMachines.get(device);
952             if (stateMachine == null) {
953                 return BluetoothHeadset.STATE_AUDIO_DISCONNECTED;
954             }
955             return stateMachine.getAudioState();
956         }
957     }
958 
setAudioRouteAllowed(boolean allowed)959     public void setAudioRouteAllowed(boolean allowed) {
960         enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission");
961         Log.i(TAG, "setAudioRouteAllowed: allowed=" + allowed + ", " + Utils.getUidPidString());
962         mAudioRouteAllowed = allowed;
963         mNativeInterface.setScoAllowed(allowed);
964     }
965 
getAudioRouteAllowed()966     public boolean getAudioRouteAllowed() {
967         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
968         return mAudioRouteAllowed;
969     }
970 
setForceScoAudio(boolean forced)971     public void setForceScoAudio(boolean forced) {
972         enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission");
973         Log.i(TAG, "setForceScoAudio: forced=" + forced + ", " + Utils.getUidPidString());
974         mForceScoAudio = forced;
975     }
976 
977     @VisibleForTesting
getForceScoAudio()978     public boolean getForceScoAudio() {
979         return mForceScoAudio;
980     }
981 
982     /**
983      * Get first available device for SCO audio
984      *
985      * @return first connected headset device
986      */
987     @VisibleForTesting
988     @Nullable
getFirstConnectedAudioDevice()989     public BluetoothDevice getFirstConnectedAudioDevice() {
990         ArrayList<HeadsetStateMachine> stateMachines = new ArrayList<>();
991         synchronized (mStateMachines) {
992             List<BluetoothDevice> availableDevices =
993                     getDevicesMatchingConnectionStates(CONNECTING_CONNECTED_STATES);
994             for (BluetoothDevice device : availableDevices) {
995                 final HeadsetStateMachine stateMachine = mStateMachines.get(device);
996                 if (stateMachine == null) {
997                     continue;
998                 }
999                 stateMachines.add(stateMachine);
1000             }
1001         }
1002         stateMachines.sort(Comparator.comparingLong(HeadsetStateMachine::getConnectingTimestampMs));
1003         if (stateMachines.size() > 0) {
1004             return stateMachines.get(0).getDevice();
1005         }
1006         return null;
1007     }
1008 
1009     /**
1010      * Set the active device.
1011      *
1012      * @param device the active device
1013      * @return true on success, otherwise false
1014      */
setActiveDevice(BluetoothDevice device)1015     public boolean setActiveDevice(BluetoothDevice device) {
1016         enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission");
1017         Log.i(TAG, "setActiveDevice: device=" + device + ", " + Utils.getUidPidString());
1018         synchronized (mStateMachines) {
1019             if (device == null) {
1020                 // Clear the active device
1021                 if (mVoiceRecognitionStarted) {
1022                     if (!stopVoiceRecognition(mActiveDevice)) {
1023                         Log.w(TAG, "setActiveDevice: fail to stopVoiceRecognition from "
1024                                 + mActiveDevice);
1025                     }
1026                 }
1027                 if (mVirtualCallStarted) {
1028                     if (!stopScoUsingVirtualVoiceCall()) {
1029                         Log.w(TAG, "setActiveDevice: fail to stopScoUsingVirtualVoiceCall from "
1030                                 + mActiveDevice);
1031                     }
1032                 }
1033                 if (getAudioState(mActiveDevice) != BluetoothHeadset.STATE_AUDIO_DISCONNECTED) {
1034                     if (!disconnectAudio(mActiveDevice)) {
1035                         Log.w(TAG, "setActiveDevice: disconnectAudio failed on " + mActiveDevice);
1036                     }
1037                 }
1038                 mActiveDevice = null;
1039                 broadcastActiveDevice(null);
1040                 return true;
1041             }
1042             if (device.equals(mActiveDevice)) {
1043                 Log.i(TAG, "setActiveDevice: device " + device + " is already active");
1044                 return true;
1045             }
1046             if (getConnectionState(device) != BluetoothProfile.STATE_CONNECTED) {
1047                 Log.e(TAG, "setActiveDevice: Cannot set " + device
1048                         + " as active, device is not connected");
1049                 return false;
1050             }
1051             if (!mNativeInterface.setActiveDevice(device)) {
1052                 Log.e(TAG, "setActiveDevice: Cannot set " + device + " as active in native layer");
1053                 return false;
1054             }
1055             BluetoothDevice previousActiveDevice = mActiveDevice;
1056             mActiveDevice = device;
1057             if (getAudioState(previousActiveDevice) != BluetoothHeadset.STATE_AUDIO_DISCONNECTED) {
1058                 if (!disconnectAudio(previousActiveDevice)) {
1059                     Log.e(TAG, "setActiveDevice: fail to disconnectAudio from "
1060                             + previousActiveDevice);
1061                     mActiveDevice = previousActiveDevice;
1062                     mNativeInterface.setActiveDevice(previousActiveDevice);
1063                     return false;
1064                 }
1065                 broadcastActiveDevice(mActiveDevice);
1066             } else if (shouldPersistAudio()) {
1067                 broadcastActiveDevice(mActiveDevice);
1068                 if (!connectAudio(mActiveDevice)) {
1069                     Log.e(TAG, "setActiveDevice: fail to connectAudio to " + mActiveDevice);
1070                     mActiveDevice = previousActiveDevice;
1071                     mNativeInterface.setActiveDevice(previousActiveDevice);
1072                     return false;
1073                 }
1074             } else {
1075                 broadcastActiveDevice(mActiveDevice);
1076             }
1077         }
1078         return true;
1079     }
1080 
1081     /**
1082      * Get the active device.
1083      *
1084      * @return the active device or null if no device is active
1085      */
getActiveDevice()1086     public BluetoothDevice getActiveDevice() {
1087         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
1088         synchronized (mStateMachines) {
1089             return mActiveDevice;
1090         }
1091     }
1092 
connectAudio()1093     boolean connectAudio() {
1094         enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission");
1095         synchronized (mStateMachines) {
1096             BluetoothDevice device = mActiveDevice;
1097             if (device == null) {
1098                 Log.w(TAG, "connectAudio: no active device, " + Utils.getUidPidString());
1099                 return false;
1100             }
1101             return connectAudio(device);
1102         }
1103     }
1104 
connectAudio(BluetoothDevice device)1105     boolean connectAudio(BluetoothDevice device) {
1106         enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission");
1107         Log.i(TAG, "connectAudio: device=" + device + ", " + Utils.getUidPidString());
1108         synchronized (mStateMachines) {
1109             if (!isScoAcceptable(device)) {
1110                 Log.w(TAG, "connectAudio, rejected SCO request to " + device);
1111                 return false;
1112             }
1113             final HeadsetStateMachine stateMachine = mStateMachines.get(device);
1114             if (stateMachine == null) {
1115                 Log.w(TAG, "connectAudio: device " + device + " was never connected/connecting");
1116                 return false;
1117             }
1118             if (stateMachine.getConnectionState() != BluetoothProfile.STATE_CONNECTED) {
1119                 Log.w(TAG, "connectAudio: profile not connected");
1120                 return false;
1121             }
1122             if (stateMachine.getAudioState() != BluetoothHeadset.STATE_AUDIO_DISCONNECTED) {
1123                 logD("connectAudio: audio is not idle for device " + device);
1124                 return true;
1125             }
1126             if (isAudioOn()) {
1127                 Log.w(TAG, "connectAudio: audio is not idle, current audio devices are "
1128                         + Arrays.toString(getNonIdleAudioDevices().toArray()));
1129                 return false;
1130             }
1131             stateMachine.sendMessage(HeadsetStateMachine.CONNECT_AUDIO, device);
1132         }
1133         return true;
1134     }
1135 
getNonIdleAudioDevices()1136     private List<BluetoothDevice> getNonIdleAudioDevices() {
1137         ArrayList<BluetoothDevice> devices = new ArrayList<>();
1138         synchronized (mStateMachines) {
1139             for (HeadsetStateMachine stateMachine : mStateMachines.values()) {
1140                 if (stateMachine.getAudioState() != BluetoothHeadset.STATE_AUDIO_DISCONNECTED) {
1141                     devices.add(stateMachine.getDevice());
1142                 }
1143             }
1144         }
1145         return devices;
1146     }
1147 
disconnectAudio()1148     boolean disconnectAudio() {
1149         enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission");
1150         boolean result = false;
1151         synchronized (mStateMachines) {
1152             for (BluetoothDevice device : getNonIdleAudioDevices()) {
1153                 if (disconnectAudio(device)) {
1154                     result = true;
1155                 } else {
1156                     Log.e(TAG, "disconnectAudio() from " + device + " failed");
1157                 }
1158             }
1159         }
1160         if (!result) {
1161             logD("disconnectAudio() no active audio connection");
1162         }
1163         return result;
1164     }
1165 
disconnectAudio(BluetoothDevice device)1166     boolean disconnectAudio(BluetoothDevice device) {
1167         enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission");
1168         synchronized (mStateMachines) {
1169             Log.i(TAG, "disconnectAudio: device=" + device + ", " + Utils.getUidPidString());
1170             final HeadsetStateMachine stateMachine = mStateMachines.get(device);
1171             if (stateMachine == null) {
1172                 Log.w(TAG, "disconnectAudio: device " + device + " was never connected/connecting");
1173                 return false;
1174             }
1175             if (stateMachine.getAudioState() == BluetoothHeadset.STATE_AUDIO_DISCONNECTED) {
1176                 Log.w(TAG, "disconnectAudio, audio is already disconnected for " + device);
1177                 return false;
1178             }
1179             stateMachine.sendMessage(HeadsetStateMachine.DISCONNECT_AUDIO, device);
1180         }
1181         return true;
1182     }
1183 
isVirtualCallStarted()1184     boolean isVirtualCallStarted() {
1185         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
1186         synchronized (mStateMachines) {
1187             return mVirtualCallStarted;
1188         }
1189     }
1190 
startScoUsingVirtualVoiceCall()1191     private boolean startScoUsingVirtualVoiceCall() {
1192         enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission");
1193         Log.i(TAG, "startScoUsingVirtualVoiceCall: " + Utils.getUidPidString());
1194         synchronized (mStateMachines) {
1195             // TODO(b/79660380): Workaround in case voice recognition was not terminated properly
1196             if (mVoiceRecognitionStarted) {
1197                 boolean status = stopVoiceRecognition(mActiveDevice);
1198                 Log.w(TAG, "startScoUsingVirtualVoiceCall: voice recognition is still active, "
1199                         + "just called stopVoiceRecognition, returned " + status + " on "
1200                         + mActiveDevice + ", please try again");
1201                 mVoiceRecognitionStarted = false;
1202                 return false;
1203             }
1204             if (!isAudioModeIdle()) {
1205                 Log.w(TAG, "startScoUsingVirtualVoiceCall: audio mode not idle, active device is "
1206                         + mActiveDevice);
1207                 return false;
1208             }
1209             // Audio should not be on when no audio mode is active
1210             if (isAudioOn()) {
1211                 // Disconnect audio so that API user can try later
1212                 boolean status = disconnectAudio();
1213                 Log.w(TAG, "startScoUsingVirtualVoiceCall: audio is still active, please wait for "
1214                         + "audio to be disconnected, disconnectAudio() returned " + status
1215                         + ", active device is " + mActiveDevice);
1216                 return false;
1217             }
1218             if (mActiveDevice == null) {
1219                 Log.w(TAG, "startScoUsingVirtualVoiceCall: no active device");
1220                 return false;
1221             }
1222             mVirtualCallStarted = true;
1223             // Send virtual phone state changed to initialize SCO
1224             phoneStateChanged(0, 0, HeadsetHalConstants.CALL_STATE_DIALING, "", 0, true);
1225             phoneStateChanged(0, 0, HeadsetHalConstants.CALL_STATE_ALERTING, "", 0, true);
1226             phoneStateChanged(1, 0, HeadsetHalConstants.CALL_STATE_IDLE, "", 0, true);
1227             return true;
1228         }
1229     }
1230 
stopScoUsingVirtualVoiceCall()1231     boolean stopScoUsingVirtualVoiceCall() {
1232         enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission");
1233         Log.i(TAG, "stopScoUsingVirtualVoiceCall: " + Utils.getUidPidString());
1234         synchronized (mStateMachines) {
1235             // 1. Check if virtual call has already started
1236             if (!mVirtualCallStarted) {
1237                 Log.w(TAG, "stopScoUsingVirtualVoiceCall: virtual call not started");
1238                 return false;
1239             }
1240             mVirtualCallStarted = false;
1241             // 2. Send virtual phone state changed to close SCO
1242             phoneStateChanged(0, 0, HeadsetHalConstants.CALL_STATE_IDLE, "", 0, true);
1243         }
1244         return true;
1245     }
1246 
1247     class DialingOutTimeoutEvent implements Runnable {
1248         BluetoothDevice mDialingOutDevice;
1249 
DialingOutTimeoutEvent(BluetoothDevice fromDevice)1250         DialingOutTimeoutEvent(BluetoothDevice fromDevice) {
1251             mDialingOutDevice = fromDevice;
1252         }
1253 
1254         @Override
run()1255         public void run() {
1256             synchronized (mStateMachines) {
1257                 mDialingOutTimeoutEvent = null;
1258                 doForStateMachine(mDialingOutDevice, stateMachine -> stateMachine.sendMessage(
1259                         HeadsetStateMachine.DIALING_OUT_RESULT, 0 /* fail */, 0,
1260                         mDialingOutDevice));
1261             }
1262         }
1263 
1264         @Override
toString()1265         public String toString() {
1266             return "DialingOutTimeoutEvent[" + mDialingOutDevice + "]";
1267         }
1268     }
1269 
1270     /**
1271      * Dial an outgoing call as requested by the remote device
1272      *
1273      * @param fromDevice remote device that initiated this dial out action
1274      * @param dialNumber number to dial
1275      * @return true on successful dial out
1276      */
1277     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
dialOutgoingCall(BluetoothDevice fromDevice, String dialNumber)1278     public boolean dialOutgoingCall(BluetoothDevice fromDevice, String dialNumber) {
1279         synchronized (mStateMachines) {
1280             Log.i(TAG, "dialOutgoingCall: from " + fromDevice);
1281             if (!isOnStateMachineThread()) {
1282                 Log.e(TAG, "dialOutgoingCall must be called from state machine thread");
1283                 return false;
1284             }
1285             if (mDialingOutTimeoutEvent != null) {
1286                 Log.e(TAG, "dialOutgoingCall, already dialing by " + mDialingOutTimeoutEvent);
1287                 return false;
1288             }
1289             if (isVirtualCallStarted()) {
1290                 if (!stopScoUsingVirtualVoiceCall()) {
1291                     Log.e(TAG, "dialOutgoingCall failed to stop current virtual call");
1292                     return false;
1293                 }
1294             }
1295             if (!setActiveDevice(fromDevice)) {
1296                 Log.e(TAG, "dialOutgoingCall failed to set active device to " + fromDevice);
1297                 return false;
1298             }
1299             Intent intent = new Intent(Intent.ACTION_CALL_PRIVILEGED,
1300                     Uri.fromParts(PhoneAccount.SCHEME_TEL, dialNumber, null));
1301             intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
1302             startActivity(intent);
1303             mDialingOutTimeoutEvent = new DialingOutTimeoutEvent(fromDevice);
1304             mStateMachinesThread.getThreadHandler()
1305                     .postDelayed(mDialingOutTimeoutEvent, DIALING_OUT_TIMEOUT_MS);
1306             return true;
1307         }
1308     }
1309 
1310     /**
1311      * Check if any connected headset has started dialing calls
1312      *
1313      * @return true if some device has started dialing calls
1314      */
1315     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
hasDeviceInitiatedDialingOut()1316     public boolean hasDeviceInitiatedDialingOut() {
1317         synchronized (mStateMachines) {
1318             return mDialingOutTimeoutEvent != null;
1319         }
1320     }
1321 
1322     class VoiceRecognitionTimeoutEvent implements Runnable {
1323         BluetoothDevice mVoiceRecognitionDevice;
1324 
VoiceRecognitionTimeoutEvent(BluetoothDevice device)1325         VoiceRecognitionTimeoutEvent(BluetoothDevice device) {
1326             mVoiceRecognitionDevice = device;
1327         }
1328 
1329         @Override
run()1330         public void run() {
1331             synchronized (mStateMachines) {
1332                 if (mSystemInterface.getVoiceRecognitionWakeLock().isHeld()) {
1333                     mSystemInterface.getVoiceRecognitionWakeLock().release();
1334                 }
1335                 mVoiceRecognitionTimeoutEvent = null;
1336                 doForStateMachine(mVoiceRecognitionDevice, stateMachine -> stateMachine.sendMessage(
1337                         HeadsetStateMachine.VOICE_RECOGNITION_RESULT, 0 /* fail */, 0,
1338                         mVoiceRecognitionDevice));
1339             }
1340         }
1341 
1342         @Override
toString()1343         public String toString() {
1344             return "VoiceRecognitionTimeoutEvent[" + mVoiceRecognitionDevice + "]";
1345         }
1346     }
1347 
startVoiceRecognitionByHeadset(BluetoothDevice fromDevice)1348     boolean startVoiceRecognitionByHeadset(BluetoothDevice fromDevice) {
1349         synchronized (mStateMachines) {
1350             Log.i(TAG, "startVoiceRecognitionByHeadset: from " + fromDevice);
1351             // TODO(b/79660380): Workaround in case voice recognition was not terminated properly
1352             if (mVoiceRecognitionStarted) {
1353                 boolean status = stopVoiceRecognition(mActiveDevice);
1354                 Log.w(TAG, "startVoiceRecognitionByHeadset: voice recognition is still active, "
1355                         + "just called stopVoiceRecognition, returned " + status + " on "
1356                         + mActiveDevice + ", please try again");
1357                 mVoiceRecognitionStarted = false;
1358                 return false;
1359             }
1360             if (fromDevice == null) {
1361                 Log.e(TAG, "startVoiceRecognitionByHeadset: fromDevice is null");
1362                 return false;
1363             }
1364             if (!isAudioModeIdle()) {
1365                 Log.w(TAG, "startVoiceRecognitionByHeadset: audio mode not idle, active device is "
1366                         + mActiveDevice);
1367                 return false;
1368             }
1369             // Audio should not be on when no audio mode is active
1370             if (isAudioOn()) {
1371                 // Disconnect audio so that user can try later
1372                 boolean status = disconnectAudio();
1373                 Log.w(TAG, "startVoiceRecognitionByHeadset: audio is still active, please wait for"
1374                         + " audio to be disconnected, disconnectAudio() returned " + status
1375                         + ", active device is " + mActiveDevice);
1376                 return false;
1377             }
1378             // Do not start new request until the current one is finished or timeout
1379             if (mVoiceRecognitionTimeoutEvent != null) {
1380                 Log.w(TAG, "startVoiceRecognitionByHeadset: failed request from " + fromDevice
1381                         + ", already pending by " + mVoiceRecognitionTimeoutEvent);
1382                 return false;
1383             }
1384             if (!setActiveDevice(fromDevice)) {
1385                 Log.w(TAG, "startVoiceRecognitionByHeadset: failed to set " + fromDevice
1386                         + " as active");
1387                 return false;
1388             }
1389             IDeviceIdleController deviceIdleController = IDeviceIdleController.Stub.asInterface(
1390                     ServiceManager.getService(Context.DEVICE_IDLE_CONTROLLER));
1391             if (deviceIdleController == null) {
1392                 Log.w(TAG, "startVoiceRecognitionByHeadset: deviceIdleController is null, device="
1393                         + fromDevice);
1394                 return false;
1395             }
1396             try {
1397                 deviceIdleController.exitIdle("voice-command");
1398             } catch (RemoteException e) {
1399                 Log.w(TAG,
1400                         "startVoiceRecognitionByHeadset: failed to exit idle, device=" + fromDevice
1401                                 + ", error=" + e.getMessage());
1402                 return false;
1403             }
1404             if (!mSystemInterface.activateVoiceRecognition()) {
1405                 Log.w(TAG, "startVoiceRecognitionByHeadset: failed request from " + fromDevice);
1406                 return false;
1407             }
1408             mVoiceRecognitionTimeoutEvent = new VoiceRecognitionTimeoutEvent(fromDevice);
1409             mStateMachinesThread.getThreadHandler()
1410                     .postDelayed(mVoiceRecognitionTimeoutEvent, sStartVrTimeoutMs);
1411             if (!mSystemInterface.getVoiceRecognitionWakeLock().isHeld()) {
1412                 mSystemInterface.getVoiceRecognitionWakeLock().acquire(sStartVrTimeoutMs);
1413             }
1414             return true;
1415         }
1416     }
1417 
stopVoiceRecognitionByHeadset(BluetoothDevice fromDevice)1418     boolean stopVoiceRecognitionByHeadset(BluetoothDevice fromDevice) {
1419         synchronized (mStateMachines) {
1420             Log.i(TAG, "stopVoiceRecognitionByHeadset: from " + fromDevice);
1421             if (!Objects.equals(fromDevice, mActiveDevice)) {
1422                 Log.w(TAG, "stopVoiceRecognitionByHeadset: " + fromDevice
1423                         + " is not active, active device is " + mActiveDevice);
1424                 return false;
1425             }
1426             if (!mVoiceRecognitionStarted && mVoiceRecognitionTimeoutEvent == null) {
1427                 Log.w(TAG, "stopVoiceRecognitionByHeadset: voice recognition not started, device="
1428                         + fromDevice);
1429                 return false;
1430             }
1431             if (mVoiceRecognitionTimeoutEvent != null) {
1432                 if (mSystemInterface.getVoiceRecognitionWakeLock().isHeld()) {
1433                     mSystemInterface.getVoiceRecognitionWakeLock().release();
1434                 }
1435                 mStateMachinesThread.getThreadHandler()
1436                         .removeCallbacks(mVoiceRecognitionTimeoutEvent);
1437                 mVoiceRecognitionTimeoutEvent = null;
1438             }
1439             if (mVoiceRecognitionStarted) {
1440                 if (!disconnectAudio()) {
1441                     Log.w(TAG, "stopVoiceRecognitionByHeadset: failed to disconnect audio from "
1442                             + fromDevice);
1443                 }
1444                 mVoiceRecognitionStarted = false;
1445             }
1446             if (!mSystemInterface.deactivateVoiceRecognition()) {
1447                 Log.w(TAG, "stopVoiceRecognitionByHeadset: failed request from " + fromDevice);
1448                 return false;
1449             }
1450             return true;
1451         }
1452     }
1453 
phoneStateChanged(int numActive, int numHeld, int callState, String number, int type, boolean isVirtualCall)1454     private void phoneStateChanged(int numActive, int numHeld, int callState, String number,
1455             int type, boolean isVirtualCall) {
1456         enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, "Need MODIFY_PHONE_STATE permission");
1457         synchronized (mStateMachines) {
1458             // Should stop all other audio mode in this case
1459             if ((numActive + numHeld) > 0 || callState != HeadsetHalConstants.CALL_STATE_IDLE) {
1460                 if (!isVirtualCall && mVirtualCallStarted) {
1461                     // stop virtual voice call if there is an incoming Telecom call update
1462                     stopScoUsingVirtualVoiceCall();
1463                 }
1464                 if (mVoiceRecognitionStarted) {
1465                     // stop voice recognition if there is any incoming call
1466                     stopVoiceRecognition(mActiveDevice);
1467                 }
1468             }
1469             if (mDialingOutTimeoutEvent != null) {
1470                 // Send result to state machine when dialing starts
1471                 if (callState == HeadsetHalConstants.CALL_STATE_DIALING) {
1472                     mStateMachinesThread.getThreadHandler()
1473                             .removeCallbacks(mDialingOutTimeoutEvent);
1474                     doForStateMachine(mDialingOutTimeoutEvent.mDialingOutDevice,
1475                             stateMachine -> stateMachine.sendMessage(
1476                                     HeadsetStateMachine.DIALING_OUT_RESULT, 1 /* success */, 0,
1477                                     mDialingOutTimeoutEvent.mDialingOutDevice));
1478                 } else if (callState == HeadsetHalConstants.CALL_STATE_ACTIVE
1479                         || callState == HeadsetHalConstants.CALL_STATE_IDLE) {
1480                     // Clear the timeout event when the call is connected or disconnected
1481                     if (!mStateMachinesThread.getThreadHandler()
1482                             .hasCallbacks(mDialingOutTimeoutEvent)) {
1483                         mDialingOutTimeoutEvent = null;
1484                     }
1485                 }
1486             }
1487         }
1488         mStateMachinesThread.getThreadHandler().post(() -> {
1489             boolean shouldCallAudioBeActiveBefore = shouldCallAudioBeActive();
1490             mSystemInterface.getHeadsetPhoneState().setNumActiveCall(numActive);
1491             mSystemInterface.getHeadsetPhoneState().setNumHeldCall(numHeld);
1492             mSystemInterface.getHeadsetPhoneState().setCallState(callState);
1493             // Suspend A2DP when call about is about to become active
1494             if (callState != HeadsetHalConstants.CALL_STATE_DISCONNECTED
1495                     && shouldCallAudioBeActive() && !shouldCallAudioBeActiveBefore) {
1496                 mSystemInterface.getAudioManager().setParameters("A2dpSuspended=true");
1497             }
1498         });
1499         doForEachConnectedStateMachine(
1500                 stateMachine -> stateMachine.sendMessage(HeadsetStateMachine.CALL_STATE_CHANGED,
1501                         new HeadsetCallState(numActive, numHeld, callState, number, type)));
1502         mStateMachinesThread.getThreadHandler().post(() -> {
1503             if (callState == HeadsetHalConstants.CALL_STATE_IDLE
1504                     && !shouldCallAudioBeActive() && !isAudioOn()) {
1505                 // Resume A2DP when call ended and SCO is not connected
1506                 mSystemInterface.getAudioManager().setParameters("A2dpSuspended=false");
1507             }
1508         });
1509 
1510     }
1511 
clccResponse(int index, int direction, int status, int mode, boolean mpty, String number, int type)1512     private void clccResponse(int index, int direction, int status, int mode, boolean mpty,
1513             String number, int type) {
1514         enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, "Need MODIFY_PHONE_STATE permission");
1515         doForEachConnectedStateMachine(
1516                 stateMachine -> stateMachine.sendMessage(HeadsetStateMachine.SEND_CCLC_RESPONSE,
1517                         new HeadsetClccResponse(index, direction, status, mode, mpty, number,
1518                                 type)));
1519     }
1520 
sendVendorSpecificResultCode(BluetoothDevice device, String command, String arg)1521     private boolean sendVendorSpecificResultCode(BluetoothDevice device, String command,
1522             String arg) {
1523         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
1524         synchronized (mStateMachines) {
1525             final HeadsetStateMachine stateMachine = mStateMachines.get(device);
1526             if (stateMachine == null) {
1527                 Log.w(TAG, "sendVendorSpecificResultCode: device " + device
1528                         + " was never connected/connecting");
1529                 return false;
1530             }
1531             int connectionState = stateMachine.getConnectionState();
1532             if (connectionState != BluetoothProfile.STATE_CONNECTED) {
1533                 return false;
1534             }
1535             // Currently we support only "+ANDROID".
1536             if (!command.equals(BluetoothHeadset.VENDOR_RESULT_CODE_COMMAND_ANDROID)) {
1537                 Log.w(TAG, "Disallowed unsolicited result code command: " + command);
1538                 return false;
1539             }
1540             stateMachine.sendMessage(HeadsetStateMachine.SEND_VENDOR_SPECIFIC_RESULT_CODE,
1541                     new HeadsetVendorSpecificResultCode(device, command, arg));
1542         }
1543         return true;
1544     }
1545 
isInbandRingingEnabled()1546     boolean isInbandRingingEnabled() {
1547         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
1548         return BluetoothHeadset.isInbandRingingSupported(this) && !SystemProperties.getBoolean(
1549                 DISABLE_INBAND_RINGING_PROPERTY, false) && !mInbandRingingRuntimeDisable;
1550     }
1551 
1552     /**
1553      * Called from {@link HeadsetStateMachine} in state machine thread when there is a connection
1554      * state change
1555      *
1556      * @param device remote device
1557      * @param fromState from which connection state is the change
1558      * @param toState to which connection state is the change
1559      */
1560     @VisibleForTesting
onConnectionStateChangedFromStateMachine(BluetoothDevice device, int fromState, int toState)1561     public void onConnectionStateChangedFromStateMachine(BluetoothDevice device, int fromState,
1562             int toState) {
1563         synchronized (mStateMachines) {
1564             List<BluetoothDevice> audioConnectableDevices =
1565                     getDevicesMatchingConnectionStates(CONNECTING_CONNECTED_STATES);
1566             if (fromState != BluetoothProfile.STATE_CONNECTED
1567                     && toState == BluetoothProfile.STATE_CONNECTED) {
1568                 if (audioConnectableDevices.size() > 1) {
1569                     mInbandRingingRuntimeDisable = true;
1570                     doForEachConnectedStateMachine(
1571                             stateMachine -> stateMachine.sendMessage(HeadsetStateMachine.SEND_BSIR,
1572                                     0));
1573                 }
1574                 MetricsLogger.logProfileConnectionEvent(BluetoothMetricsProto.ProfileId.HEADSET);
1575             }
1576             if (fromState != BluetoothProfile.STATE_DISCONNECTED
1577                     && toState == BluetoothProfile.STATE_DISCONNECTED) {
1578                 if (audioConnectableDevices.size() <= 1) {
1579                     mInbandRingingRuntimeDisable = false;
1580                     doForEachConnectedStateMachine(
1581                             stateMachine -> stateMachine.sendMessage(HeadsetStateMachine.SEND_BSIR,
1582                                     1));
1583                 }
1584                 if (device.equals(mActiveDevice)) {
1585                     setActiveDevice(null);
1586                 }
1587             }
1588         }
1589     }
1590 
1591     /**
1592      * Check if no audio mode is active
1593      *
1594      * @return false if virtual call, voice recognition, or Telecom call is active, true if all idle
1595      */
isAudioModeIdle()1596     private boolean isAudioModeIdle() {
1597         synchronized (mStateMachines) {
1598             if (mVoiceRecognitionStarted || mVirtualCallStarted || !mSystemInterface.isCallIdle()) {
1599                 Log.i(TAG, "isAudioModeIdle: not idle, mVoiceRecognitionStarted="
1600                         + mVoiceRecognitionStarted + ", mVirtualCallStarted=" + mVirtualCallStarted
1601                         + ", isCallIdle=" + mSystemInterface.isCallIdle());
1602                 return false;
1603             }
1604             return true;
1605         }
1606     }
1607 
shouldCallAudioBeActive()1608     private boolean shouldCallAudioBeActive() {
1609         return mSystemInterface.isInCall() || (mSystemInterface.isRinging()
1610                 && isInbandRingingEnabled());
1611     }
1612 
1613     /**
1614      * Only persist audio during active device switch when call audio is supposed to be active and
1615      * virtual call has not been started. Virtual call is ignored because AudioService and
1616      * applications should reconnect SCO during active device switch and forcing SCO connection
1617      * here will make AudioService think SCO is started externally instead of by one of its SCO
1618      * clients.
1619      *
1620      * @return true if call audio should be active and no virtual call is going on
1621      */
shouldPersistAudio()1622     private boolean shouldPersistAudio() {
1623         return !mVirtualCallStarted && shouldCallAudioBeActive();
1624     }
1625 
1626     /**
1627      * Called from {@link HeadsetStateMachine} in state machine thread when there is a audio
1628      * connection state change
1629      *
1630      * @param device remote device
1631      * @param fromState from which audio connection state is the change
1632      * @param toState to which audio connection state is the change
1633      */
1634     @VisibleForTesting
onAudioStateChangedFromStateMachine(BluetoothDevice device, int fromState, int toState)1635     public void onAudioStateChangedFromStateMachine(BluetoothDevice device, int fromState,
1636             int toState) {
1637         synchronized (mStateMachines) {
1638             if (toState == BluetoothHeadset.STATE_AUDIO_DISCONNECTED) {
1639                 if (fromState != BluetoothHeadset.STATE_AUDIO_DISCONNECTED) {
1640                     if (mActiveDevice != null && !mActiveDevice.equals(device)
1641                             && shouldPersistAudio()) {
1642                         if (!connectAudio(mActiveDevice)) {
1643                             Log.w(TAG, "onAudioStateChangedFromStateMachine, failed to connect"
1644                                     + " audio to new " + "active device " + mActiveDevice
1645                                     + ", after " + device + " is disconnected from SCO");
1646                         }
1647                     }
1648                 }
1649                 if (mVoiceRecognitionStarted) {
1650                     if (!stopVoiceRecognitionByHeadset(device)) {
1651                         Log.w(TAG, "onAudioStateChangedFromStateMachine: failed to stop voice "
1652                                 + "recognition");
1653                     }
1654                 }
1655                 if (mVirtualCallStarted) {
1656                     if (!stopScoUsingVirtualVoiceCall()) {
1657                         Log.w(TAG, "onAudioStateChangedFromStateMachine: failed to stop virtual "
1658                                 + "voice call");
1659                     }
1660                 }
1661                 // Unsuspend A2DP when SCO connection is gone and call state is idle
1662                 if (mSystemInterface.isCallIdle()) {
1663                     mSystemInterface.getAudioManager().setParameters("A2dpSuspended=false");
1664                 }
1665             }
1666         }
1667     }
1668 
broadcastActiveDevice(BluetoothDevice device)1669     private void broadcastActiveDevice(BluetoothDevice device) {
1670         logD("broadcastActiveDevice: " + device);
1671         Intent intent = new Intent(BluetoothHeadset.ACTION_ACTIVE_DEVICE_CHANGED);
1672         intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
1673         intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
1674                 | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
1675         sendBroadcastAsUser(intent, UserHandle.ALL, HeadsetService.BLUETOOTH_PERM);
1676     }
1677 
1678     /**
1679      * Check whether it is OK to accept a headset connection from a remote device
1680      *
1681      * @param device remote device that initiates the connection
1682      * @return true if the connection is acceptable
1683      */
okToAcceptConnection(BluetoothDevice device)1684     public boolean okToAcceptConnection(BluetoothDevice device) {
1685         // Check if this is an incoming connection in Quiet mode.
1686         if (mAdapterService.isQuietModeEnabled()) {
1687             Log.w(TAG, "okToAcceptConnection: return false as quiet mode enabled");
1688             return false;
1689         }
1690         // Check priority and accept or reject the connection.
1691         int priority = getPriority(device);
1692         int bondState = mAdapterService.getBondState(device);
1693         // Allow this connection only if the device is bonded. Any attempt to connect while
1694         // bonding would potentially lead to an unauthorized connection.
1695         if (bondState != BluetoothDevice.BOND_BONDED) {
1696             Log.w(TAG, "okToAcceptConnection: return false, bondState=" + bondState);
1697             return false;
1698         } else if (priority != BluetoothProfile.PRIORITY_UNDEFINED
1699                 && priority != BluetoothProfile.PRIORITY_ON
1700                 && priority != BluetoothProfile.PRIORITY_AUTO_CONNECT) {
1701             // Otherwise, reject the connection if priority is not valid.
1702             Log.w(TAG, "okToAcceptConnection: return false, priority=" + priority);
1703             return false;
1704         }
1705         List<BluetoothDevice> connectingConnectedDevices =
1706                 getDevicesMatchingConnectionStates(CONNECTING_CONNECTED_STATES);
1707         if (connectingConnectedDevices.size() >= mMaxHeadsetConnections) {
1708             Log.w(TAG, "Maximum number of connections " + mMaxHeadsetConnections
1709                     + " was reached, rejecting connection from " + device);
1710             return false;
1711         }
1712         return true;
1713     }
1714 
1715     /**
1716      * Checks if SCO should be connected at current system state
1717      *
1718      * @param device device for SCO to be connected
1719      * @return true if SCO is allowed to be connected
1720      */
isScoAcceptable(BluetoothDevice device)1721     public boolean isScoAcceptable(BluetoothDevice device) {
1722         synchronized (mStateMachines) {
1723             if (device == null || !device.equals(mActiveDevice)) {
1724                 Log.w(TAG, "isScoAcceptable: rejected SCO since " + device
1725                         + " is not the current active device " + mActiveDevice);
1726                 return false;
1727             }
1728             if (mForceScoAudio) {
1729                 return true;
1730             }
1731             if (!mAudioRouteAllowed) {
1732                 Log.w(TAG, "isScoAcceptable: rejected SCO since audio route is not allowed");
1733                 return false;
1734             }
1735             if (mVoiceRecognitionStarted || mVirtualCallStarted) {
1736                 return true;
1737             }
1738             if (shouldCallAudioBeActive()) {
1739                 return true;
1740             }
1741             Log.w(TAG, "isScoAcceptable: rejected SCO, inCall=" + mSystemInterface.isInCall()
1742                     + ", voiceRecognition=" + mVoiceRecognitionStarted + ", ringing="
1743                     + mSystemInterface.isRinging() + ", inbandRinging=" + isInbandRingingEnabled()
1744                     + ", isVirtualCallStarted=" + mVirtualCallStarted);
1745             return false;
1746         }
1747     }
1748 
1749     /**
1750      * Remove state machine in {@link #mStateMachines} for a {@link BluetoothDevice}
1751      *
1752      * @param device device whose state machine is to be removed.
1753      */
removeStateMachine(BluetoothDevice device)1754     void removeStateMachine(BluetoothDevice device) {
1755         synchronized (mStateMachines) {
1756             HeadsetStateMachine stateMachine = mStateMachines.get(device);
1757             if (stateMachine == null) {
1758                 Log.w(TAG, "removeStateMachine(), " + device + " does not have a state machine");
1759                 return;
1760             }
1761             Log.i(TAG, "removeStateMachine(), removing state machine for device: " + device);
1762             HeadsetObjectsFactory.getInstance().destroyStateMachine(stateMachine);
1763             mStateMachines.remove(device);
1764         }
1765     }
1766 
isOnStateMachineThread()1767     private boolean isOnStateMachineThread() {
1768         final Looper myLooper = Looper.myLooper();
1769         return myLooper != null && (mStateMachinesThread != null) && (myLooper.getThread().getId()
1770                 == mStateMachinesThread.getId());
1771     }
1772 
1773     @Override
dump(StringBuilder sb)1774     public void dump(StringBuilder sb) {
1775         synchronized (mStateMachines) {
1776             super.dump(sb);
1777             ProfileService.println(sb, "mMaxHeadsetConnections: " + mMaxHeadsetConnections);
1778             ProfileService.println(sb, "DefaultMaxHeadsetConnections: "
1779                     + mAdapterService.getMaxConnectedAudioDevices());
1780             ProfileService.println(sb, "mActiveDevice: " + mActiveDevice);
1781             ProfileService.println(sb, "isInbandRingingEnabled: " + isInbandRingingEnabled());
1782             ProfileService.println(sb,
1783                     "isInbandRingingSupported: " + BluetoothHeadset.isInbandRingingSupported(this));
1784             ProfileService.println(sb,
1785                     "mInbandRingingRuntimeDisable: " + mInbandRingingRuntimeDisable);
1786             ProfileService.println(sb, "mAudioRouteAllowed: " + mAudioRouteAllowed);
1787             ProfileService.println(sb, "mVoiceRecognitionStarted: " + mVoiceRecognitionStarted);
1788             ProfileService.println(sb,
1789                     "mVoiceRecognitionTimeoutEvent: " + mVoiceRecognitionTimeoutEvent);
1790             ProfileService.println(sb, "mVirtualCallStarted: " + mVirtualCallStarted);
1791             ProfileService.println(sb, "mDialingOutTimeoutEvent: " + mDialingOutTimeoutEvent);
1792             ProfileService.println(sb, "mForceScoAudio: " + mForceScoAudio);
1793             ProfileService.println(sb, "mCreated: " + mCreated);
1794             ProfileService.println(sb, "mStarted: " + mStarted);
1795             ProfileService.println(sb,
1796                     "AudioManager.isBluetoothScoOn(): " + mSystemInterface.getAudioManager()
1797                             .isBluetoothScoOn());
1798             ProfileService.println(sb, "Telecom.isInCall(): " + mSystemInterface.isInCall());
1799             ProfileService.println(sb, "Telecom.isRinging(): " + mSystemInterface.isRinging());
1800             for (HeadsetStateMachine stateMachine : mStateMachines.values()) {
1801                 ProfileService.println(sb,
1802                         "==== StateMachine for " + stateMachine.getDevice() + " ====");
1803                 stateMachine.dump(sb);
1804             }
1805         }
1806     }
1807 
logD(String message)1808     private static void logD(String message) {
1809         if (DBG) {
1810             Log.d(TAG, message);
1811         }
1812     }
1813 }
1814