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