HeadsetService.java revision bc5e652497d767d9a576d6b89cbacde4ed2881be
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
17package com.android.bluetooth.hfp;
18
19import static android.Manifest.permission.MODIFY_PHONE_STATE;
20
21import android.annotation.Nullable;
22import android.bluetooth.BluetoothDevice;
23import android.bluetooth.BluetoothHeadset;
24import android.bluetooth.BluetoothProfile;
25import android.bluetooth.BluetoothUuid;
26import android.bluetooth.IBluetoothHeadset;
27import android.content.BroadcastReceiver;
28import android.content.Context;
29import android.content.Intent;
30import android.content.IntentFilter;
31import android.media.AudioManager;
32import android.os.BatteryManager;
33import android.os.HandlerThread;
34import android.os.Looper;
35import android.os.ParcelUuid;
36import android.os.SystemProperties;
37import android.os.UserHandle;
38import android.provider.Settings;
39import android.util.Log;
40
41import com.android.bluetooth.BluetoothMetricsProto;
42import com.android.bluetooth.Utils;
43import com.android.bluetooth.btservice.AdapterService;
44import com.android.bluetooth.btservice.MetricsLogger;
45import com.android.bluetooth.btservice.ProfileService;
46import com.android.internal.annotations.VisibleForTesting;
47
48import java.util.ArrayList;
49import java.util.Arrays;
50import java.util.Comparator;
51import java.util.HashMap;
52import java.util.List;
53import java.util.Objects;
54
55/**
56 * Provides Bluetooth Headset and Handsfree profile, as a service in the Bluetooth application.
57 */
58public class HeadsetService extends ProfileService {
59    private static final String TAG = "HeadsetService";
60    private static final boolean DBG = false;
61    private static final String DISABLE_INBAND_RINGING_PROPERTY =
62            "persist.bluetooth.disableinbandringing";
63    private static final ParcelUuid[] HEADSET_UUIDS = {
64            BluetoothUuid.HSP, BluetoothUuid.Handsfree,
65    };
66    private static final int[] CONNECTING_CONNECTED_STATES = {
67            BluetoothProfile.STATE_CONNECTING, BluetoothProfile.STATE_CONNECTED
68    };
69
70    private int mMaxHeadsetConnections = 1;
71    private BluetoothDevice mActiveDevice;
72    private AdapterService mAdapterService;
73    private HandlerThread mStateMachinesThread;
74    // This is also used as a lock for shared data in HeadsetService
75    private final HashMap<BluetoothDevice, HeadsetStateMachine> mStateMachines = new HashMap<>();
76    private HeadsetNativeInterface mNativeInterface;
77    private HeadsetSystemInterface mSystemInterface;
78    private boolean mAudioRouteAllowed = true;
79    // Indicates whether SCO audio needs to be forced to open regardless ANY OTHER restrictions
80    private boolean mForceScoAudio;
81    private boolean mInbandRingingRuntimeDisable;
82    // TODO: Need better handling of voice recognition and virtual voice call events for Multi-HFP
83    private boolean mVoiceRecognitionStarted;
84    private boolean mStarted;
85    private boolean mCreated;
86    private static HeadsetService sHeadsetService;
87
88    @Override
89    public IProfileServiceBinder initBinder() {
90        return new BluetoothHeadsetBinder(this);
91    }
92
93    @Override
94    protected void create() {
95        Log.i(TAG, "create()");
96        if (mCreated) {
97            throw new IllegalStateException("create() called twice");
98        }
99        mCreated = true;
100    }
101
102    @Override
103    protected boolean start() {
104        Log.i(TAG, "start()");
105        if (mStarted) {
106            throw new IllegalStateException("start() called twice");
107        }
108        // Step 1: Get adapter service, should never be null
109        mAdapterService = Objects.requireNonNull(AdapterService.getAdapterService(),
110                "AdapterService cannot be null when HeadsetService starts");
111        // Step 2: Start handler thread for state machines
112        mStateMachinesThread = new HandlerThread("HeadsetService.StateMachines");
113        mStateMachinesThread.start();
114        // Step 3: Initialize system interface
115        mSystemInterface = HeadsetObjectsFactory.getInstance().makeSystemInterface(this);
116        mSystemInterface.init();
117        // Step 4: Initialize native interface
118        mMaxHeadsetConnections = mAdapterService.getMaxConnectedAudioDevices();
119        mNativeInterface = HeadsetObjectsFactory.getInstance().getNativeInterface();
120        // Add 1 to allow a pending device to be connecting or disconnecting
121        mNativeInterface.init(mMaxHeadsetConnections + 1, isInbandRingingEnabled());
122        // Step 5: Check if state machine table is empty, crash if not
123        if (mStateMachines.size() > 0) {
124            throw new IllegalStateException(
125                    "start(): mStateMachines is not empty, " + mStateMachines.size()
126                            + " is already created. Was stop() called properly?");
127        }
128        // Step 6: Setup broadcast receivers
129        IntentFilter filter = new IntentFilter();
130        filter.addAction(Intent.ACTION_BATTERY_CHANGED);
131        filter.addAction(AudioManager.VOLUME_CHANGED_ACTION);
132        filter.addAction(BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY);
133        filter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
134        registerReceiver(mHeadsetReceiver, filter);
135        // Step 7: Mark service as started
136        setHeadsetService(this);
137        mStarted = true;
138        return true;
139    }
140
141    @Override
142    protected boolean stop() {
143        Log.i(TAG, "stop()");
144        if (!mStarted) {
145            Log.w(TAG, "stop() called before start()");
146            // Still return true because it is considered "stopped" and doesn't have any functional
147            // impact on the user
148            return true;
149        }
150        // Step 7: Mark service as stopped
151        mStarted = false;
152        setHeadsetService(null);
153        // Reset active device to null
154        mActiveDevice = null;
155        mInbandRingingRuntimeDisable = false;
156        mForceScoAudio = false;
157        mAudioRouteAllowed = true;
158        mMaxHeadsetConnections = 1;
159        mVoiceRecognitionStarted = false;
160        // Step 6: Tear down broadcast receivers
161        unregisterReceiver(mHeadsetReceiver);
162        // Step 5: Destroy state machines
163        synchronized (mStateMachines) {
164            for (HeadsetStateMachine stateMachine : mStateMachines.values()) {
165                HeadsetObjectsFactory.getInstance().destroyStateMachine(stateMachine);
166            }
167            mStateMachines.clear();
168        }
169        // Step 4: Destroy native interface
170        mNativeInterface.cleanup();
171        // Step 3: Destroy system interface
172        mSystemInterface.stop();
173        // Step 2: Stop handler thread
174        mStateMachinesThread.quitSafely();
175        mStateMachinesThread = null;
176        // Step 1: Clear
177        mAdapterService = null;
178        return true;
179    }
180
181    @Override
182    protected void cleanup() {
183        Log.i(TAG, "cleanup");
184        if (!mCreated) {
185            Log.w(TAG, "cleanup() called before create()");
186        }
187        mCreated = false;
188    }
189
190    /**
191     * Checks if this service object is able to accept binder calls
192     *
193     * @return True if the object can accept binder calls, False otherwise
194     */
195    public boolean isAlive() {
196        return isAvailable() && mCreated && mStarted;
197    }
198
199    /**
200     * Get the {@link Looper} for the state machine thread. This is used in testing and helper
201     * objects
202     *
203     * @return {@link Looper} for the state machine thread
204     */
205    @VisibleForTesting
206    public Looper getStateMachinesThreadLooper() {
207        return mStateMachinesThread.getLooper();
208    }
209
210    interface StateMachineTask {
211        void execute(HeadsetStateMachine stateMachine);
212    }
213
214    private void doForEachConnectedStateMachine(StateMachineTask task) {
215        synchronized (mStateMachines) {
216            for (BluetoothDevice device : getConnectedDevices()) {
217                task.execute(mStateMachines.get(device));
218            }
219        }
220    }
221
222    void onDeviceStateChanged(HeadsetDeviceState deviceState) {
223        doForEachConnectedStateMachine(
224                stateMachine -> stateMachine.sendMessage(HeadsetStateMachine.DEVICE_STATE_CHANGED,
225                        deviceState));
226    }
227
228    /**
229     * Handle messages from native (JNI) to Java. This needs to be synchronized to avoid posting
230     * messages to state machine before start() is done
231     *
232     * @param stackEvent event from native stack
233     */
234    void messageFromNative(HeadsetStackEvent stackEvent) {
235        Objects.requireNonNull(stackEvent.device,
236                "Device should never be null, event: " + stackEvent);
237        synchronized (mStateMachines) {
238            HeadsetStateMachine stateMachine = mStateMachines.get(stackEvent.device);
239            if (stackEvent.type == HeadsetStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED) {
240                switch (stackEvent.valueInt) {
241                    case HeadsetHalConstants.CONNECTION_STATE_CONNECTED:
242                    case HeadsetHalConstants.CONNECTION_STATE_CONNECTING: {
243                        // Create new state machine if none is found
244                        if (stateMachine == null) {
245                            stateMachine = HeadsetObjectsFactory.getInstance()
246                                    .makeStateMachine(stackEvent.device,
247                                            mStateMachinesThread.getLooper(), this, mAdapterService,
248                                            mNativeInterface, mSystemInterface);
249                            mStateMachines.put(stackEvent.device, stateMachine);
250                        }
251                        break;
252                    }
253                }
254            }
255            if (stateMachine == null) {
256                throw new IllegalStateException(
257                        "State machine not found for stack event: " + stackEvent);
258            }
259            stateMachine.sendMessage(HeadsetStateMachine.STACK_EVENT, stackEvent);
260        }
261    }
262
263    private final BroadcastReceiver mHeadsetReceiver = new BroadcastReceiver() {
264        @Override
265        public void onReceive(Context context, Intent intent) {
266            String action = intent.getAction();
267            if (action == null) {
268                Log.w(TAG, "mHeadsetReceiver, action is null");
269                return;
270            }
271            switch (action) {
272                case Intent.ACTION_BATTERY_CHANGED: {
273                    int batteryLevel = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);
274                    int scale = intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1);
275                    if (batteryLevel < 0 || scale <= 0) {
276                        Log.e(TAG, "Bad Battery Changed intent: batteryLevel=" + batteryLevel
277                                + ", scale=" + scale);
278                        return;
279                    }
280                    batteryLevel = batteryLevel * 5 / scale;
281                    mSystemInterface.getHeadsetPhoneState().setCindBatteryCharge(batteryLevel);
282                    break;
283                }
284                case AudioManager.VOLUME_CHANGED_ACTION: {
285                    int streamType = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, -1);
286                    if (streamType == AudioManager.STREAM_BLUETOOTH_SCO) {
287                        doForEachConnectedStateMachine(stateMachine -> stateMachine.sendMessage(
288                                HeadsetStateMachine.INTENT_SCO_VOLUME_CHANGED, intent));
289                    }
290                    break;
291                }
292                case BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY: {
293                    int requestType = intent.getIntExtra(BluetoothDevice.EXTRA_ACCESS_REQUEST_TYPE,
294                            BluetoothDevice.REQUEST_TYPE_PHONEBOOK_ACCESS);
295                    BluetoothDevice device =
296                            intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
297                    logD("Received BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY, device=" + device
298                            + ", type=" + requestType);
299                    if (requestType == BluetoothDevice.REQUEST_TYPE_PHONEBOOK_ACCESS) {
300                        synchronized (mStateMachines) {
301                            final HeadsetStateMachine stateMachine = mStateMachines.get(device);
302                            if (stateMachine == null) {
303                                Log.wtfStack(TAG, "Cannot find state machine for " + device);
304                                return;
305                            }
306                            stateMachine.sendMessage(
307                                    HeadsetStateMachine.INTENT_CONNECTION_ACCESS_REPLY, intent);
308                        }
309                    }
310                    break;
311                }
312                case BluetoothDevice.ACTION_BOND_STATE_CHANGED: {
313                    int state = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE,
314                            BluetoothDevice.ERROR);
315                    BluetoothDevice device = Objects.requireNonNull(
316                            intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE),
317                            "ACTION_BOND_STATE_CHANGED with no EXTRA_DEVICE");
318                    logD("Bond state changed for device: " + device + " state: " + state);
319                    if (state != BluetoothDevice.BOND_NONE) {
320                        break;
321                    }
322                    synchronized (mStateMachines) {
323                        HeadsetStateMachine stateMachine = mStateMachines.get(device);
324                        if (stateMachine == null) {
325                            break;
326                        }
327                        if (stateMachine.getConnectionState()
328                                != BluetoothProfile.STATE_DISCONNECTED) {
329                            break;
330                        }
331                        removeStateMachine(device);
332                    }
333                    break;
334                }
335                default:
336                    Log.w(TAG, "Unknown action " + action);
337            }
338        }
339    };
340
341    /**
342     * Handlers for incoming service calls
343     */
344    private static class BluetoothHeadsetBinder extends IBluetoothHeadset.Stub
345            implements IProfileServiceBinder {
346        private volatile HeadsetService mService;
347
348        BluetoothHeadsetBinder(HeadsetService svc) {
349            mService = svc;
350        }
351
352        @Override
353        public void cleanup() {
354            mService = null;
355        }
356
357        private HeadsetService getService() {
358            final HeadsetService service = mService;
359            if (!Utils.checkCallerAllowManagedProfiles(service)) {
360                Log.w(TAG, "Headset call not allowed for non-active user");
361                return null;
362            }
363            if (service == null) {
364                Log.w(TAG, "Service is null");
365                return null;
366            }
367            if (!service.isAlive()) {
368                Log.w(TAG, "Service is not alive");
369                return null;
370            }
371            return service;
372        }
373
374        @Override
375        public boolean connect(BluetoothDevice device) {
376            HeadsetService service = getService();
377            if (service == null) {
378                return false;
379            }
380            return service.connect(device);
381        }
382
383        @Override
384        public boolean disconnect(BluetoothDevice device) {
385            HeadsetService service = getService();
386            if (service == null) {
387                return false;
388            }
389            logD("disconnect in HeadsetService");
390            return service.disconnect(device);
391        }
392
393        @Override
394        public List<BluetoothDevice> getConnectedDevices() {
395            HeadsetService service = getService();
396            if (service == null) {
397                return new ArrayList<BluetoothDevice>(0);
398            }
399            return service.getConnectedDevices();
400        }
401
402        @Override
403        public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
404            HeadsetService service = getService();
405            if (service == null) {
406                return new ArrayList<BluetoothDevice>(0);
407            }
408            return service.getDevicesMatchingConnectionStates(states);
409        }
410
411        @Override
412        public int getConnectionState(BluetoothDevice device) {
413            HeadsetService service = getService();
414            if (service == null) {
415                return BluetoothProfile.STATE_DISCONNECTED;
416            }
417            return service.getConnectionState(device);
418        }
419
420        @Override
421        public boolean setPriority(BluetoothDevice device, int priority) {
422            HeadsetService service = getService();
423            if (service == null) {
424                return false;
425            }
426            return service.setPriority(device, priority);
427        }
428
429        @Override
430        public int getPriority(BluetoothDevice device) {
431            HeadsetService service = getService();
432            if (service == null) {
433                return BluetoothProfile.PRIORITY_UNDEFINED;
434            }
435            return service.getPriority(device);
436        }
437
438        @Override
439        public boolean startVoiceRecognition(BluetoothDevice device) {
440            HeadsetService service = getService();
441            if (service == null) {
442                return false;
443            }
444            return service.startVoiceRecognition(device);
445        }
446
447        @Override
448        public boolean stopVoiceRecognition(BluetoothDevice device) {
449            HeadsetService service = getService();
450            if (service == null) {
451                return false;
452            }
453            return service.stopVoiceRecognition(device);
454        }
455
456        @Override
457        public boolean isAudioOn() {
458            HeadsetService service = getService();
459            if (service == null) {
460                return false;
461            }
462            return service.isAudioOn();
463        }
464
465        @Override
466        public boolean isAudioConnected(BluetoothDevice device) {
467            HeadsetService service = getService();
468            if (service == null) {
469                return false;
470            }
471            return service.isAudioConnected(device);
472        }
473
474        @Override
475        public int getAudioState(BluetoothDevice device) {
476            HeadsetService service = getService();
477            if (service == null) {
478                return BluetoothHeadset.STATE_AUDIO_DISCONNECTED;
479            }
480            return service.getAudioState(device);
481        }
482
483        @Override
484        public boolean connectAudio() {
485            HeadsetService service = getService();
486            if (service == null) {
487                return false;
488            }
489            return service.connectAudio();
490        }
491
492        @Override
493        public boolean disconnectAudio() {
494            HeadsetService service = getService();
495            if (service == null) {
496                return false;
497            }
498            return service.disconnectAudio();
499        }
500
501        @Override
502        public void setAudioRouteAllowed(boolean allowed) {
503            HeadsetService service = getService();
504            if (service == null) {
505                return;
506            }
507            service.setAudioRouteAllowed(allowed);
508        }
509
510        @Override
511        public boolean getAudioRouteAllowed() {
512            HeadsetService service = getService();
513            if (service != null) {
514                return service.getAudioRouteAllowed();
515            }
516            return false;
517        }
518
519        @Override
520        public void setForceScoAudio(boolean forced) {
521            HeadsetService service = getService();
522            if (service == null) {
523                return;
524            }
525            service.setForceScoAudio(forced);
526        }
527
528        @Override
529        public boolean startScoUsingVirtualVoiceCall(BluetoothDevice device) {
530            HeadsetService service = getService();
531            if (service == null) {
532                return false;
533            }
534            return service.startScoUsingVirtualVoiceCall(device);
535        }
536
537        @Override
538        public boolean stopScoUsingVirtualVoiceCall(BluetoothDevice device) {
539            HeadsetService service = getService();
540            if (service == null) {
541                return false;
542            }
543            return service.stopScoUsingVirtualVoiceCall(device);
544        }
545
546        @Override
547        public void phoneStateChanged(int numActive, int numHeld, int callState, String number,
548                int type) {
549            HeadsetService service = getService();
550            if (service == null) {
551                return;
552            }
553            service.phoneStateChanged(numActive, numHeld, callState, number, type);
554        }
555
556        @Override
557        public void clccResponse(int index, int direction, int status, int mode, boolean mpty,
558                String number, int type) {
559            HeadsetService service = getService();
560            if (service == null) {
561                return;
562            }
563            service.clccResponse(index, direction, status, mode, mpty, number, type);
564        }
565
566        @Override
567        public boolean sendVendorSpecificResultCode(BluetoothDevice device, String command,
568                String arg) {
569            HeadsetService service = getService();
570            if (service == null) {
571                return false;
572            }
573            return service.sendVendorSpecificResultCode(device, command, arg);
574        }
575
576        @Override
577        public boolean setActiveDevice(BluetoothDevice device) {
578            HeadsetService service = getService();
579            if (service == null) {
580                return false;
581            }
582            return service.setActiveDevice(device);
583        }
584
585        @Override
586        public BluetoothDevice getActiveDevice() {
587            HeadsetService service = getService();
588            if (service == null) {
589                return null;
590            }
591            return service.getActiveDevice();
592        }
593
594        @Override
595        public boolean isInbandRingingEnabled() {
596            HeadsetService service = getService();
597            if (service == null) {
598                return false;
599            }
600            return service.isInbandRingingEnabled();
601        }
602    }
603
604    // API methods
605    public static synchronized HeadsetService getHeadsetService() {
606        if (sHeadsetService == null) {
607            Log.w(TAG, "getHeadsetService(): service is NULL");
608            return null;
609        }
610        if (!sHeadsetService.isAvailable()) {
611            Log.w(TAG, "getHeadsetService(): service is not available");
612            return null;
613        }
614        logD("getHeadsetService(): returning " + sHeadsetService);
615        return sHeadsetService;
616    }
617
618    private static synchronized void setHeadsetService(HeadsetService instance) {
619        logD("setHeadsetService(): set to: " + instance);
620        sHeadsetService = instance;
621    }
622
623    public boolean connect(BluetoothDevice device) {
624        enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH ADMIN permission");
625        if (getPriority(device) == BluetoothProfile.PRIORITY_OFF) {
626            Log.w(TAG, "connect: PRIORITY_OFF, device=" + device);
627            return false;
628        }
629        ParcelUuid[] featureUuids = mAdapterService.getRemoteUuids(device);
630        if (!BluetoothUuid.containsAnyUuid(featureUuids, HEADSET_UUIDS)) {
631            Log.e(TAG, "connect: Cannot connect to " + device + ": no headset UUID");
632            return false;
633        }
634        synchronized (mStateMachines) {
635            Log.i(TAG, "connect: device=" + device);
636            HeadsetStateMachine stateMachine = mStateMachines.get(device);
637            if (stateMachine == null) {
638                stateMachine = HeadsetObjectsFactory.getInstance()
639                        .makeStateMachine(device, mStateMachinesThread.getLooper(), this,
640                                mAdapterService, mNativeInterface, mSystemInterface);
641                mStateMachines.put(device, stateMachine);
642            }
643            int connectionState = stateMachine.getConnectionState();
644            if (connectionState == BluetoothProfile.STATE_CONNECTED
645                    || connectionState == BluetoothProfile.STATE_CONNECTING) {
646                Log.w(TAG, "connect: device " + device
647                        + " is already connected/connecting, connectionState=" + connectionState);
648                return false;
649            }
650            List<BluetoothDevice> connectingConnectedDevices =
651                    getDevicesMatchingConnectionStates(CONNECTING_CONNECTED_STATES);
652            boolean disconnectExisting = false;
653            if (connectingConnectedDevices.size() >= mMaxHeadsetConnections) {
654                // When there is maximum one device, we automatically disconnect the current one
655                if (mMaxHeadsetConnections == 1) {
656                    disconnectExisting = true;
657                } else {
658                    Log.w(TAG, "Max connection has reached, rejecting connection to " + device);
659                    return false;
660                }
661            }
662            if (disconnectExisting) {
663                for (BluetoothDevice connectingConnectedDevice : connectingConnectedDevices) {
664                    disconnect(connectingConnectedDevice);
665                }
666                setActiveDevice(null);
667            }
668            stateMachine.sendMessage(HeadsetStateMachine.CONNECT, device);
669        }
670        return true;
671    }
672
673    boolean disconnect(BluetoothDevice device) {
674        enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission");
675        synchronized (mStateMachines) {
676            Log.i(TAG, "disconnect: device=" + device);
677            HeadsetStateMachine stateMachine = mStateMachines.get(device);
678            if (stateMachine == null) {
679                Log.w(TAG, "disconnect: device " + device + " not ever connected/connecting");
680                return false;
681            }
682            int connectionState = stateMachine.getConnectionState();
683            if (connectionState != BluetoothProfile.STATE_CONNECTED
684                    && connectionState != BluetoothProfile.STATE_CONNECTING) {
685                Log.w(TAG, "disconnect: device " + device
686                        + " not connected/connecting, connectionState=" + connectionState);
687                return false;
688            }
689            stateMachine.sendMessage(HeadsetStateMachine.DISCONNECT, device);
690        }
691        return true;
692    }
693
694    public List<BluetoothDevice> getConnectedDevices() {
695        enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
696        ArrayList<BluetoothDevice> devices = new ArrayList<>();
697        synchronized (mStateMachines) {
698            for (HeadsetStateMachine stateMachine : mStateMachines.values()) {
699                if (stateMachine.getConnectionState() == BluetoothProfile.STATE_CONNECTED) {
700                    devices.add(stateMachine.getDevice());
701                }
702            }
703        }
704        return devices;
705    }
706
707    /**
708     * Same as the API method {@link BluetoothHeadset#getDevicesMatchingConnectionStates(int[])}
709     *
710     * @param states an array of states from {@link BluetoothProfile}
711     * @return a list of devices matching the array of connection states
712     */
713    @VisibleForTesting
714    public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
715        enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
716        ArrayList<BluetoothDevice> devices = new ArrayList<>();
717        if (states == null) {
718            return devices;
719        }
720        final BluetoothDevice[] bondedDevices = mAdapterService.getBondedDevices();
721        if (bondedDevices == null) {
722            return devices;
723        }
724        synchronized (mStateMachines) {
725            for (BluetoothDevice device : bondedDevices) {
726                final ParcelUuid[] featureUuids = mAdapterService.getRemoteUuids(device);
727                if (!BluetoothUuid.containsAnyUuid(featureUuids, HEADSET_UUIDS)) {
728                    continue;
729                }
730                int connectionState = getConnectionState(device);
731                for (int state : states) {
732                    if (connectionState == state) {
733                        devices.add(device);
734                        break;
735                    }
736                }
737            }
738        }
739        return devices;
740    }
741
742    public int getConnectionState(BluetoothDevice device) {
743        enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
744        synchronized (mStateMachines) {
745            final HeadsetStateMachine stateMachine = mStateMachines.get(device);
746            if (stateMachine == null) {
747                return BluetoothProfile.STATE_DISCONNECTED;
748            }
749            return stateMachine.getConnectionState();
750        }
751    }
752
753    public boolean setPriority(BluetoothDevice device, int priority) {
754        enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission");
755        Settings.Global.putInt(getContentResolver(),
756                Settings.Global.getBluetoothHeadsetPriorityKey(device.getAddress()), priority);
757        logD("Saved priority " + device + " = " + priority);
758        return true;
759    }
760
761    public int getPriority(BluetoothDevice device) {
762        enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
763        return Settings.Global.getInt(getContentResolver(),
764                Settings.Global.getBluetoothHeadsetPriorityKey(device.getAddress()),
765                BluetoothProfile.PRIORITY_UNDEFINED);
766    }
767
768    boolean startVoiceRecognition(BluetoothDevice device) {
769        enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
770        synchronized (mStateMachines) {
771            final HeadsetStateMachine stateMachine = mStateMachines.get(device);
772            if (stateMachine == null) {
773                return false;
774            }
775            int connectionState = stateMachine.getConnectionState();
776            if (connectionState != BluetoothProfile.STATE_CONNECTED
777                    && connectionState != BluetoothProfile.STATE_CONNECTING) {
778                return false;
779            }
780            mVoiceRecognitionStarted = true;
781            stateMachine.sendMessage(HeadsetStateMachine.VOICE_RECOGNITION_START, device);
782        }
783        return true;
784    }
785
786    boolean stopVoiceRecognition(BluetoothDevice device) {
787        enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
788        synchronized (mStateMachines) {
789            final HeadsetStateMachine stateMachine = mStateMachines.get(device);
790            if (stateMachine == null) {
791                return false;
792            }
793            // It seem that we really need to check the AudioOn state.
794            // But since we allow startVoiceRecognition in STATE_CONNECTED and
795            // STATE_CONNECTING state, we do these 2 in this method
796            int connectionState = stateMachine.getConnectionState();
797            if (connectionState != BluetoothProfile.STATE_CONNECTED
798                    && connectionState != BluetoothProfile.STATE_CONNECTING) {
799                return false;
800            }
801            mVoiceRecognitionStarted = false;
802            stateMachine.sendMessage(HeadsetStateMachine.VOICE_RECOGNITION_STOP, device);
803        }
804        return true;
805    }
806
807    boolean isAudioOn() {
808        enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
809        return getNonIdleAudioDevices().size() > 0;
810    }
811
812    boolean isAudioConnected(BluetoothDevice device) {
813        enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
814        synchronized (mStateMachines) {
815            final HeadsetStateMachine stateMachine = mStateMachines.get(device);
816            if (stateMachine == null) {
817                return false;
818            }
819            return stateMachine.getAudioState() == BluetoothHeadset.STATE_AUDIO_CONNECTED;
820        }
821    }
822
823    int getAudioState(BluetoothDevice device) {
824        enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
825        synchronized (mStateMachines) {
826            final HeadsetStateMachine stateMachine = mStateMachines.get(device);
827            if (stateMachine == null) {
828                return BluetoothHeadset.STATE_AUDIO_DISCONNECTED;
829            }
830            return stateMachine.getAudioState();
831        }
832    }
833
834    public void setAudioRouteAllowed(boolean allowed) {
835        enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission");
836        mAudioRouteAllowed = allowed;
837        mNativeInterface.setScoAllowed(allowed);
838    }
839
840    public boolean getAudioRouteAllowed() {
841        enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
842        return mAudioRouteAllowed;
843    }
844
845    public void setForceScoAudio(boolean forced) {
846        enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission");
847        mForceScoAudio = forced;
848    }
849
850    @VisibleForTesting
851    public boolean getForceScoAudio() {
852        return mForceScoAudio;
853    }
854
855    /**
856     * Get first available device for SCO audio
857     *
858     * @return first connected headset device
859     */
860    @VisibleForTesting
861    @Nullable
862    public BluetoothDevice getFirstConnectedAudioDevice() {
863        ArrayList<HeadsetStateMachine> stateMachines = new ArrayList<>();
864        synchronized (mStateMachines) {
865            List<BluetoothDevice> availableDevices =
866                    getDevicesMatchingConnectionStates(CONNECTING_CONNECTED_STATES);
867            for (BluetoothDevice device : availableDevices) {
868                final HeadsetStateMachine stateMachine = mStateMachines.get(device);
869                if (stateMachine == null) {
870                    continue;
871                }
872                stateMachines.add(stateMachine);
873            }
874        }
875        stateMachines.sort(Comparator.comparingLong(HeadsetStateMachine::getConnectingTimestampMs));
876        if (stateMachines.size() > 0) {
877            return stateMachines.get(0).getDevice();
878        }
879        return null;
880    }
881
882    /**
883     * Set the active device.
884     *
885     * @param device the active device
886     * @return true on success, otherwise false
887     */
888    public boolean setActiveDevice(BluetoothDevice device) {
889        enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission");
890        synchronized (mStateMachines) {
891            logD("setActiveDevice: " + device);
892            if (device == null) {
893                // Clear the active device
894                if (getAudioState(mActiveDevice) != BluetoothHeadset.STATE_AUDIO_DISCONNECTED) {
895                    if (!disconnectAudio(mActiveDevice)) {
896                        Log.e(TAG,
897                                "setActiveDevice: fail to disconnectAudio from " + mActiveDevice);
898                        return false;
899                    }
900                }
901                mActiveDevice = null;
902                broadcastActiveDevice(null);
903                return true;
904            }
905            if (device.equals(mActiveDevice)) {
906                Log.w(TAG, "setActiveDevice: device " + device + " is already active");
907                return true;
908            }
909            if (getConnectionState(device) != BluetoothProfile.STATE_CONNECTED) {
910                Log.e(TAG, "setActiveDevice: Cannot set " + device
911                        + " as active, device is not connected");
912                return false;
913            }
914            if (!mNativeInterface.setActiveDevice(device)) {
915                Log.e(TAG, "setActiveDevice: Cannot set " + device + " as active in native layer");
916                return false;
917            }
918            BluetoothDevice previousActiveDevice = mActiveDevice;
919            mActiveDevice = device;
920            if (getAudioState(previousActiveDevice) != BluetoothHeadset.STATE_AUDIO_DISCONNECTED) {
921                mSystemInterface.getAudioManager().setBluetoothScoOn(false);
922                if (!disconnectAudio(previousActiveDevice)) {
923                    Log.e(TAG, "setActiveDevice: fail to disconnectAudio from "
924                            + previousActiveDevice);
925                    mActiveDevice = previousActiveDevice;
926                    mNativeInterface.setActiveDevice(previousActiveDevice);
927                    return false;
928                }
929                broadcastActiveDevice(mActiveDevice);
930            } else if (mSystemInterface.isInCall() || (mSystemInterface.isRinging()
931                    && isInbandRingingEnabled()) || mVoiceRecognitionStarted) {
932                broadcastActiveDevice(mActiveDevice);
933                if (!connectAudio(mActiveDevice)) {
934                    Log.e(TAG, "setActiveDevice: fail to connectAudio to " + mActiveDevice);
935                    mActiveDevice = previousActiveDevice;
936                    mNativeInterface.setActiveDevice(previousActiveDevice);
937                    return false;
938                }
939            } else {
940                broadcastActiveDevice(mActiveDevice);
941            }
942        }
943        return true;
944    }
945
946    /**
947     * Get the active device.
948     *
949     * @return the active device or null if no device is active
950     */
951    public BluetoothDevice getActiveDevice() {
952        enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
953        synchronized (mStateMachines) {
954            return mActiveDevice;
955        }
956    }
957
958    boolean connectAudio() {
959        enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission");
960        synchronized (mStateMachines) {
961            BluetoothDevice device = mActiveDevice;
962            if (device == null) {
963                Log.w(TAG, "connectAudio: no active device is selected");
964                return false;
965            }
966            return connectAudio(device);
967        }
968    }
969
970    boolean connectAudio(BluetoothDevice device) {
971        enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission");
972        if (device == null) {
973            Log.w(TAG, "connectAudio: device is null");
974            return false;
975        }
976        if (!device.equals(mActiveDevice)) {
977            Log.w(TAG, "connectAudio: device is not active, active device is " + mActiveDevice);
978            return false;
979        }
980        synchronized (mStateMachines) {
981            Log.i(TAG, "connectAudio, device=" + device);
982            final HeadsetStateMachine stateMachine = mStateMachines.get(device);
983            if (stateMachine == null) {
984                Log.w(TAG, "connectAudio: device " + device + " was never connected/connecting");
985                return false;
986            }
987            if (stateMachine.getConnectionState() != BluetoothProfile.STATE_CONNECTED) {
988                Log.w(TAG, "connectAudio: profile not connected");
989                return false;
990            }
991            if (stateMachine.getAudioState() != BluetoothHeadset.STATE_AUDIO_DISCONNECTED) {
992                logD("connectAudio: audio is not idle for device " + device);
993                return true;
994            }
995            if (isAudioOn()) {
996                Log.w(TAG, "connectAudio: audio is not idle, current audio devices are "
997                        + Arrays.toString(getNonIdleAudioDevices().toArray()));
998                return false;
999            }
1000            stateMachine.sendMessage(HeadsetStateMachine.CONNECT_AUDIO, device);
1001        }
1002
1003        return true;
1004    }
1005
1006    private List<BluetoothDevice> getNonIdleAudioDevices() {
1007        ArrayList<BluetoothDevice> devices = new ArrayList<>();
1008        synchronized (mStateMachines) {
1009            for (HeadsetStateMachine stateMachine : mStateMachines.values()) {
1010                if (stateMachine.getAudioState() != BluetoothHeadset.STATE_AUDIO_DISCONNECTED) {
1011                    devices.add(stateMachine.getDevice());
1012                }
1013            }
1014        }
1015        return devices;
1016    }
1017
1018    boolean disconnectAudio() {
1019        enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission");
1020        boolean result = false;
1021        synchronized (mStateMachines) {
1022            for (BluetoothDevice device : getNonIdleAudioDevices()) {
1023                if (!disconnectAudio(device)) {
1024                    Log.e(TAG, "disconnectAudio() from " + device + " failed");
1025                } else {
1026                    result = true;
1027                }
1028            }
1029        }
1030        if (!result) {
1031            logD("disconnectAudio() no active audio connection");
1032        }
1033        return result;
1034    }
1035
1036    boolean disconnectAudio(BluetoothDevice device) {
1037        enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission");
1038        synchronized (mStateMachines) {
1039            Log.i(TAG, "disconnectAudio, device=" + device);
1040            final HeadsetStateMachine stateMachine = mStateMachines.get(device);
1041            if (stateMachine == null) {
1042                Log.w(TAG, "disconnectAudio: device " + device + " was never connected/connecting");
1043                return false;
1044            }
1045            if (stateMachine.getAudioState() == BluetoothHeadset.STATE_AUDIO_DISCONNECTED) {
1046                Log.w(TAG, "disconnectAudio, audio is already disconnected for " + device);
1047                return false;
1048            }
1049            stateMachine.sendMessage(HeadsetStateMachine.DISCONNECT_AUDIO, device);
1050        }
1051        return true;
1052    }
1053
1054    boolean startScoUsingVirtualVoiceCall(BluetoothDevice device) {
1055        enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission");
1056        synchronized (mStateMachines) {
1057            final HeadsetStateMachine stateMachine = mStateMachines.get(device);
1058            if (stateMachine == null) {
1059                Log.w(TAG, "startScoUsingVirtualVoiceCall: device " + device
1060                        + " was never connected/connecting");
1061                return false;
1062            }
1063            stateMachine.sendMessage(HeadsetStateMachine.VIRTUAL_CALL_START, device);
1064        }
1065        return true;
1066    }
1067
1068    boolean stopScoUsingVirtualVoiceCall(BluetoothDevice device) {
1069        enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission");
1070        synchronized (mStateMachines) {
1071            final HeadsetStateMachine stateMachine = mStateMachines.get(device);
1072            if (stateMachine == null) {
1073                Log.w(TAG, "stopScoUsingVirtualVoiceCall: device " + device
1074                        + " was never connected/connecting");
1075                return false;
1076            }
1077            int connectionState = stateMachine.getConnectionState();
1078            if (connectionState != BluetoothProfile.STATE_CONNECTED
1079                    && connectionState != BluetoothProfile.STATE_CONNECTING) {
1080                Log.w(TAG, "stopScoUsingVirtualVoiceCall: device " + device
1081                        + " is neither connected/connecting");
1082                return false;
1083            }
1084            stateMachine.sendMessage(HeadsetStateMachine.VIRTUAL_CALL_STOP, device);
1085        }
1086        return true;
1087    }
1088
1089    private void phoneStateChanged(int numActive, int numHeld, int callState, String number,
1090            int type) {
1091        enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, null);
1092        doForEachConnectedStateMachine(
1093                stateMachine -> stateMachine.sendMessage(HeadsetStateMachine.CALL_STATE_CHANGED,
1094                        new HeadsetCallState(numActive, numHeld, callState, number, type)));
1095    }
1096
1097    private void clccResponse(int index, int direction, int status, int mode, boolean mpty,
1098            String number, int type) {
1099        enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, null);
1100        doForEachConnectedStateMachine(
1101                stateMachine -> stateMachine.sendMessage(HeadsetStateMachine.SEND_CCLC_RESPONSE,
1102                        new HeadsetClccResponse(index, direction, status, mode, mpty, number,
1103                                type)));
1104    }
1105
1106    private boolean sendVendorSpecificResultCode(BluetoothDevice device, String command,
1107            String arg) {
1108        enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
1109        synchronized (mStateMachines) {
1110            final HeadsetStateMachine stateMachine = mStateMachines.get(device);
1111            if (stateMachine == null) {
1112                Log.w(TAG, "sendVendorSpecificResultCode: device " + device
1113                        + " was never connected/connecting");
1114                return false;
1115            }
1116            int connectionState = stateMachine.getConnectionState();
1117            if (connectionState != BluetoothProfile.STATE_CONNECTED) {
1118                return false;
1119            }
1120            // Currently we support only "+ANDROID".
1121            if (!command.equals(BluetoothHeadset.VENDOR_RESULT_CODE_COMMAND_ANDROID)) {
1122                Log.w(TAG, "Disallowed unsolicited result code command: " + command);
1123                return false;
1124            }
1125            stateMachine.sendMessage(HeadsetStateMachine.SEND_VENDOR_SPECIFIC_RESULT_CODE,
1126                    new HeadsetVendorSpecificResultCode(device, command, arg));
1127        }
1128        return true;
1129    }
1130
1131    boolean isInbandRingingEnabled() {
1132        enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
1133        return BluetoothHeadset.isInbandRingingSupported(this) && !SystemProperties.getBoolean(
1134                DISABLE_INBAND_RINGING_PROPERTY, false) && !mInbandRingingRuntimeDisable;
1135    }
1136
1137    /**
1138     * Called from {@link HeadsetStateMachine} in state machine thread when there is a connection
1139     * state change
1140     *
1141     * @param device remote device
1142     * @param fromState from which connection state is the change
1143     * @param toState to which connection state is the change
1144     */
1145    @VisibleForTesting
1146    public void onConnectionStateChangedFromStateMachine(BluetoothDevice device, int fromState,
1147            int toState) {
1148        synchronized (mStateMachines) {
1149            List<BluetoothDevice> audioConnectableDevices =
1150                    getDevicesMatchingConnectionStates(CONNECTING_CONNECTED_STATES);
1151            if (fromState != BluetoothProfile.STATE_CONNECTED
1152                    && toState == BluetoothProfile.STATE_CONNECTED) {
1153                if (audioConnectableDevices.size() > 1) {
1154                    mInbandRingingRuntimeDisable = true;
1155                    doForEachConnectedStateMachine(
1156                            stateMachine -> stateMachine.sendMessage(HeadsetStateMachine.SEND_BSIR,
1157                                    0));
1158                }
1159                MetricsLogger.logProfileConnectionEvent(BluetoothMetricsProto.ProfileId.HEADSET);
1160            }
1161            if (fromState == BluetoothProfile.STATE_CONNECTED
1162                    && toState != BluetoothProfile.STATE_CONNECTED) {
1163                if (audioConnectableDevices.size() <= 1) {
1164                    mInbandRingingRuntimeDisable = false;
1165                    doForEachConnectedStateMachine(
1166                            stateMachine -> stateMachine.sendMessage(HeadsetStateMachine.SEND_BSIR,
1167                                    1));
1168                }
1169                if (device.equals(mActiveDevice)) {
1170                    setActiveDevice(null);
1171                }
1172            }
1173        }
1174    }
1175
1176    /**
1177     * Called from {@link HeadsetStateMachine} in state machine thread when there is a audio
1178     * connection state change
1179     *
1180     * @param device remote device
1181     * @param fromState from which audio connection state is the change
1182     * @param toState to which audio connection state is the change
1183     */
1184    @VisibleForTesting
1185    public void onAudioStateChangedFromStateMachine(BluetoothDevice device, int fromState,
1186            int toState) {
1187        synchronized (mStateMachines) {
1188            if (fromState != BluetoothHeadset.STATE_AUDIO_CONNECTED
1189                    && toState == BluetoothHeadset.STATE_AUDIO_CONNECTED) {
1190                // Do nothing
1191            }
1192            if (fromState != BluetoothHeadset.STATE_AUDIO_DISCONNECTED
1193                    && toState == BluetoothHeadset.STATE_AUDIO_DISCONNECTED) {
1194                if (mActiveDevice != null && !mActiveDevice.equals(device)) {
1195                    if (!connectAudio(mActiveDevice)) {
1196                        Log.w(TAG, "onAudioStateChangedFromStateMachine, failed to connect to new "
1197                                + "active device " + mActiveDevice + ", after " + device
1198                                + " is disconnected");
1199                        setActiveDevice(null);
1200                    }
1201                }
1202            }
1203        }
1204    }
1205
1206    private void broadcastActiveDevice(BluetoothDevice device) {
1207        logD("broadcastActiveDevice: " + device);
1208        Intent intent = new Intent(BluetoothHeadset.ACTION_ACTIVE_DEVICE_CHANGED);
1209        intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
1210        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
1211                | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
1212        sendBroadcastAsUser(intent, UserHandle.ALL, HeadsetService.BLUETOOTH_PERM);
1213    }
1214
1215    /**
1216     * Check whether it is OK to accept a headset connection from a remote device
1217     *
1218     * @param device remote device that initiates the connection
1219     * @return true if the connection is acceptable
1220     */
1221    public boolean okToAcceptConnection(BluetoothDevice device) {
1222        // Check if this is an incoming connection in Quiet mode.
1223        if (mAdapterService.isQuietModeEnabled()) {
1224            Log.w(TAG, "okToAcceptConnection: return false as quiet mode enabled");
1225            return false;
1226        }
1227        // Check priority and accept or reject the connection.
1228        // Note: Logic can be simplified, but keeping it this way for readability
1229        int priority = getPriority(device);
1230        int bondState = mAdapterService.getBondState(device);
1231        // If priority is undefined, it is likely that service discovery has not completed and peer
1232        // initiated the connection. Allow this connection only if the device is bonded or bonding
1233        boolean serviceDiscoveryPending = (priority == BluetoothProfile.PRIORITY_UNDEFINED)
1234                && (bondState == BluetoothDevice.BOND_BONDING
1235                || bondState == BluetoothDevice.BOND_BONDED);
1236        // Also allow connection when device is bonded/bonding and priority is ON/AUTO_CONNECT.
1237        boolean isEnabled = (priority == BluetoothProfile.PRIORITY_ON
1238                || priority == BluetoothProfile.PRIORITY_AUTO_CONNECT)
1239                && (bondState == BluetoothDevice.BOND_BONDED
1240                || bondState == BluetoothDevice.BOND_BONDING);
1241        if (!serviceDiscoveryPending && !isEnabled) {
1242            // Otherwise, reject the connection if no service discovery is pending and priority is
1243            // neither PRIORITY_ON nor PRIORITY_AUTO_CONNECT
1244            Log.w(TAG, "okToConnect: return false, priority=" + priority + ", bondState="
1245                    + bondState);
1246            return false;
1247        }
1248        List<BluetoothDevice> connectingConnectedDevices =
1249                getDevicesMatchingConnectionStates(CONNECTING_CONNECTED_STATES);
1250        if (connectingConnectedDevices.size() >= mMaxHeadsetConnections) {
1251            Log.w(TAG, "Maximum number of connections " + mMaxHeadsetConnections
1252                    + " was reached, rejecting connection from " + device);
1253            return false;
1254        }
1255        return true;
1256    }
1257
1258    /**
1259     * Remove state machine in {@link #mStateMachines} for a {@link BluetoothDevice}
1260     *
1261     * @param device device whose state machine is to be removed.
1262     */
1263    void removeStateMachine(BluetoothDevice device) {
1264        synchronized (mStateMachines) {
1265            HeadsetStateMachine stateMachine = mStateMachines.get(device);
1266            if (stateMachine == null) {
1267                Log.w(TAG, "removeStateMachine(), " + device + " does not have a state machine");
1268                return;
1269            }
1270            Log.i(TAG, "removeStateMachine(), removing state machine for device: " + device);
1271            HeadsetObjectsFactory.getInstance().destroyStateMachine(stateMachine);
1272            mStateMachines.remove(device);
1273        }
1274    }
1275
1276    @Override
1277    public void dump(StringBuilder sb) {
1278        synchronized (mStateMachines) {
1279            super.dump(sb);
1280            ProfileService.println(sb, "mMaxHeadsetConnections: " + mMaxHeadsetConnections);
1281            ProfileService.println(sb, "DefaultMaxHeadsetConnections: "
1282                    + mAdapterService.getMaxConnectedAudioDevices());
1283            ProfileService.println(sb, "mActiveDevice: " + mActiveDevice);
1284            ProfileService.println(sb, "isInbandRingingEnabled: " + isInbandRingingEnabled());
1285            ProfileService.println(sb,
1286                    "isInbandRingingSupported: " + BluetoothHeadset.isInbandRingingSupported(this));
1287            ProfileService.println(sb,
1288                    "mInbandRingingRuntimeDisable: " + mInbandRingingRuntimeDisable);
1289            ProfileService.println(sb, "mAudioRouteAllowed: " + mAudioRouteAllowed);
1290            ProfileService.println(sb, "mForceScoAudio: " + mForceScoAudio);
1291            ProfileService.println(sb, "mCreated: " + mCreated);
1292            ProfileService.println(sb, "mStarted: " + mStarted);
1293            ProfileService.println(sb,
1294                    "AudioManager.isBluetoothScoOn(): " + mSystemInterface.getAudioManager()
1295                            .isBluetoothScoOn());
1296            ProfileService.println(sb, "Telecom.isInCall(): " + mSystemInterface.isInCall());
1297            ProfileService.println(sb, "Telecom.isRinging(): " + mSystemInterface.isRinging());
1298            for (HeadsetStateMachine stateMachine : mStateMachines.values()) {
1299                ProfileService.println(sb,
1300                        "==== StateMachine for " + stateMachine.getDevice() + " ====");
1301                stateMachine.dump(sb);
1302            }
1303        }
1304    }
1305
1306    private static void logD(String message) {
1307        if (DBG) {
1308            Log.d(TAG, message);
1309        }
1310    }
1311}
1312