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