A2dpService.java revision a75a48708dcdfd82079c64c7fbb5ad94f7783b83
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.a2dp;
18
19import android.bluetooth.BluetoothA2dp;
20import android.bluetooth.BluetoothAdapter;
21import android.bluetooth.BluetoothCodecConfig;
22import android.bluetooth.BluetoothCodecStatus;
23import android.bluetooth.BluetoothDevice;
24import android.bluetooth.BluetoothProfile;
25import android.bluetooth.BluetoothUuid;
26import android.bluetooth.IBluetoothA2dp;
27import android.content.BroadcastReceiver;
28import android.content.Context;
29import android.content.Intent;
30import android.content.IntentFilter;
31import android.media.AudioManager;
32import android.os.HandlerThread;
33import android.provider.Settings;
34import android.support.annotation.GuardedBy;
35import android.support.annotation.VisibleForTesting;
36import android.util.Log;
37
38import com.android.bluetooth.Utils;
39import com.android.bluetooth.avrcp.Avrcp;
40import com.android.bluetooth.btservice.AdapterService;
41import com.android.bluetooth.btservice.ProfileService;
42
43import java.util.ArrayList;
44import java.util.List;
45import java.util.Objects;
46import java.util.Set;
47import java.util.concurrent.ConcurrentHashMap;
48import java.util.concurrent.ConcurrentMap;
49
50/**
51 * Provides Bluetooth A2DP profile, as a service in the Bluetooth application.
52 * @hide
53 */
54public class A2dpService extends ProfileService {
55    private static final boolean DBG = true;
56    private static final String TAG = "A2dpService";
57
58    private static A2dpService sA2dpService;
59
60    private BluetoothAdapter mAdapter;
61    private AdapterService mAdapterService;
62    private HandlerThread mStateMachinesThread;
63    private Avrcp mAvrcp;
64
65    @VisibleForTesting
66    A2dpNativeInterface mA2dpNativeInterface;
67    private AudioManager mAudioManager;
68    private A2dpCodecConfig mA2dpCodecConfig;
69
70    @GuardedBy("mStateMachines")
71    private BluetoothDevice mActiveDevice;
72    private final ConcurrentMap<BluetoothDevice, A2dpStateMachine> mStateMachines =
73            new ConcurrentHashMap<>();
74
75    // Upper limit of all A2DP devices: Bonded or Connected
76    private static final int MAX_A2DP_STATE_MACHINES = 50;
77    // Upper limit of all A2DP devices that are Connected or Connecting
78    private int mMaxConnectedAudioDevices = 1;
79
80    private BroadcastReceiver mBondStateChangedReceiver;
81    private BroadcastReceiver mConnectionStateChangedReceiver;
82
83    @Override
84    protected IProfileServiceBinder initBinder() {
85        return new BluetoothA2dpBinder(this);
86    }
87
88    @Override
89    protected void create() {
90        Log.i(TAG, "create()");
91    }
92
93    @Override
94    protected boolean start() {
95        Log.i(TAG, "start()");
96        if (sA2dpService != null) {
97            throw new IllegalStateException("start() called twice");
98        }
99
100        // Step 1: Get BluetoothAdapter, AdapterService, A2dpNativeInterface, AudioManager.
101        // None of them can be null.
102        mAdapter = Objects.requireNonNull(BluetoothAdapter.getDefaultAdapter(),
103                "BluetoothAdapter cannot be null when A2dpService starts");
104        mAdapterService = Objects.requireNonNull(AdapterService.getAdapterService(),
105                "AdapterService cannot be null when A2dpService starts");
106        mA2dpNativeInterface = Objects.requireNonNull(A2dpNativeInterface.getInstance(),
107                "A2dpNativeInterface cannot be null when A2dpService starts");
108        mAudioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
109        Objects.requireNonNull(mAudioManager,
110                               "AudioManager cannot be null when A2dpService starts");
111
112        // Step 2: Get maximum number of connected audio devices
113        mMaxConnectedAudioDevices = mAdapterService.getMaxConnectedAudioDevices();
114        Log.i(TAG, "Max connected audio devices set to " + mMaxConnectedAudioDevices);
115
116        // Step 3: Start handler thread for state machines
117        mStateMachines.clear();
118        mStateMachinesThread = new HandlerThread("A2dpService.StateMachines");
119        mStateMachinesThread.start();
120
121        // Step 4: Setup codec config and clear active device
122        mA2dpCodecConfig = new A2dpCodecConfig(this, mA2dpNativeInterface);
123        mActiveDevice = null;
124
125        // Step 5: Setup AVRCP
126        mAvrcp = Avrcp.make(this);
127
128        // Step 6: Initialize native interface
129        mA2dpNativeInterface.init(mA2dpCodecConfig.codecConfigPriorities());
130
131        // Step 7: Setup broadcast receivers
132        IntentFilter filter = new IntentFilter();
133        filter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
134        mBondStateChangedReceiver = new BondStateChangedReceiver();
135        registerReceiver(mBondStateChangedReceiver, filter);
136        filter = new IntentFilter();
137        filter.addAction(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED);
138        mConnectionStateChangedReceiver = new ConnectionStateChangedReceiver();
139        registerReceiver(mConnectionStateChangedReceiver, filter);
140
141        // Step 8: Mark service as started
142        setA2dpService(this);
143
144        return true;
145    }
146
147    @Override
148    protected boolean stop() {
149        Log.i(TAG, "stop()");
150        if (sA2dpService == null) {
151            Log.w(TAG, "stop() called before start()");
152            return true;
153        }
154
155        // Step 8: Mark service as stopped
156        setA2dpService(null);
157
158        // Step 7: Unregister broadcast receivers
159        unregisterReceiver(mConnectionStateChangedReceiver);
160        mConnectionStateChangedReceiver = null;
161        unregisterReceiver(mBondStateChangedReceiver);
162        mBondStateChangedReceiver = null;
163
164        // Step 6: Cleanup native interface
165        mA2dpNativeInterface.cleanup();
166        mA2dpNativeInterface = null;
167
168        // Step 5: Cleanup AVRCP
169        mAvrcp.cleanup();
170        mAvrcp = null;
171
172        // Step 4: Clear codec config and active device
173        mA2dpCodecConfig = null;
174        mActiveDevice = null;
175
176        // Step 3: Destroy state machines and stop handler thread
177        synchronized (mStateMachines) {
178            for (A2dpStateMachine sm : mStateMachines.values()) {
179                sm.doQuit();
180                sm.cleanup();
181            }
182            mStateMachines.clear();
183        }
184        mStateMachinesThread.quitSafely();
185        mStateMachinesThread = null;
186
187        // Step 2: Reset maximum number of connected audio devices
188        mMaxConnectedAudioDevices = 1;
189
190        // Step 1: Clear BluetoothAdapter, AdapterService, A2dpNativeInterface, AudioManager
191        mAudioManager = null;
192        mA2dpNativeInterface = null;
193        mAdapterService = null;
194        mAdapter = null;
195
196        return true;
197    }
198
199    @Override
200    protected void cleanup() {
201        Log.i(TAG, "cleanup()");
202    }
203
204    public static synchronized A2dpService getA2dpService() {
205        if (sA2dpService == null) {
206            Log.w(TAG, "getA2dpService(): service is null");
207            return null;
208        }
209        if (!sA2dpService.isAvailable()) {
210            Log.w(TAG, "getA2dpService(): service is not available");
211            return null;
212        }
213        return sA2dpService;
214    }
215
216    private static synchronized void setA2dpService(A2dpService instance) {
217        if (DBG) {
218            Log.d(TAG, "setA2dpService(): set to: " + instance);
219        }
220        sA2dpService = instance;
221    }
222
223    public boolean connect(BluetoothDevice device) {
224        enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH ADMIN permission");
225        if (DBG) {
226            Log.d(TAG, "connect(): " + device);
227        }
228
229        if (getPriority(device) == BluetoothProfile.PRIORITY_OFF) {
230            Log.e(TAG, "Cannot connect to " + device + " : PRIORITY_OFF");
231            return false;
232        }
233        if (!BluetoothUuid.isUuidPresent(mAdapterService.getRemoteUuids(device),
234                                         BluetoothUuid.AudioSink)) {
235            Log.e(TAG, "Cannot connect to " + device + " : Remote does not have A2DP Sink UUID");
236            return false;
237        }
238
239        synchronized (mStateMachines) {
240            if (!connectionAllowedCheckMaxDevices(device)) {
241                Log.e(TAG, "Cannot connect to " + device + " : too many connected devices");
242                return false;
243            }
244            A2dpStateMachine smConnect = getOrCreateStateMachine(device);
245            if (smConnect == null) {
246                Log.e(TAG, "Cannot connect to " + device + " : no state machine");
247                return false;
248            }
249            smConnect.sendMessage(A2dpStateMachine.CONNECT);
250            return true;
251        }
252    }
253
254    boolean disconnect(BluetoothDevice device) {
255        enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH ADMIN permission");
256        if (DBG) {
257            Log.d(TAG, "disconnect(): " + device);
258        }
259
260        synchronized (mStateMachines) {
261            A2dpStateMachine sm = mStateMachines.get(device);
262            if (sm == null) {
263                Log.e(TAG, "Ignored disconnect request for " + device + " : no state machine");
264                return false;
265            }
266            sm.sendMessage(A2dpStateMachine.DISCONNECT);
267            return true;
268        }
269    }
270
271    public List<BluetoothDevice> getConnectedDevices() {
272        enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
273        synchronized (mStateMachines) {
274            List<BluetoothDevice> devices = new ArrayList<>();
275            for (A2dpStateMachine sm : mStateMachines.values()) {
276                if (sm.isConnected()) {
277                    devices.add(sm.getDevice());
278                }
279            }
280            return devices;
281        }
282    }
283
284    /**
285     * Check whether can connect to a peer device.
286     * The check considers the maximum number of connected peers.
287     *
288     * @param device the peer device to connect to
289     * @return true if connection is allowed, otherwise false
290     */
291    private boolean connectionAllowedCheckMaxDevices(BluetoothDevice device) {
292        int connected = 0;
293        // Count devices that are in the process of connecting or already connected
294        synchronized (mStateMachines) {
295            for (A2dpStateMachine sm : mStateMachines.values()) {
296                switch (sm.getConnectionState()) {
297                    case BluetoothProfile.STATE_CONNECTING:
298                    case BluetoothProfile.STATE_CONNECTED:
299                        if (Objects.equals(device, sm.getDevice())) {
300                            return true;    // Already connected or accounted for
301                        }
302                        connected++;
303                        break;
304                    default:
305                        break;
306                }
307            }
308        }
309        return (connected < mMaxConnectedAudioDevices);
310    }
311
312    /**
313     * Check whether can connect to a peer device.
314     * The check considers a number of factors during the evaluation.
315     *
316     * @param device the peer device to connect to
317     * @return true if connection is allowed, otherwise false
318     */
319    @VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
320    public boolean okToConnect(BluetoothDevice device) {
321        // Check if this is an incoming connection in Quiet mode.
322        if (mAdapterService.isQuietModeEnabled()) {
323            Log.e(TAG, "okToConnect: cannot connect to " + device + " : quiet mode enabled");
324            return false;
325        }
326        // Check if too many devices
327        if (!connectionAllowedCheckMaxDevices(device)) {
328            Log.e(TAG, "okToConnect: cannot connect to " + device
329                    + " : too many connected devices");
330            return false;
331        }
332        // Check priority and accept or reject the connection
333        int priority = getPriority(device);
334        int bondState = mAdapterService.getBondState(device);
335        // If priority is undefined, it is likely that our SDP has not completed and peer is
336        // initiating the connection. Allow the connection only if the device is bonded or bonding.
337        if ((priority == BluetoothProfile.PRIORITY_UNDEFINED)
338                && (bondState == BluetoothDevice.BOND_NONE)) {
339            Log.e(TAG, "okToConnect: cannot connect to " + device + " : priority=" + priority
340                    + " bondState=" + bondState);
341            return false;
342        }
343        if (priority <= BluetoothProfile.PRIORITY_OFF) {
344            Log.e(TAG, "okToConnect: cannot connect to " + device + " : priority=" + priority);
345            return false;
346        }
347        return true;
348    }
349
350    List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
351        enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
352        List<BluetoothDevice> devices = new ArrayList<>();
353        Set<BluetoothDevice> bondedDevices = mAdapter.getBondedDevices();
354        synchronized (mStateMachines) {
355            for (BluetoothDevice device : bondedDevices) {
356                if (!BluetoothUuid.isUuidPresent(mAdapterService.getRemoteUuids(device),
357                                                 BluetoothUuid.AudioSink)) {
358                    continue;
359                }
360                int connectionState = BluetoothProfile.STATE_DISCONNECTED;
361                A2dpStateMachine sm = mStateMachines.get(device);
362                if (sm != null) {
363                    connectionState = sm.getConnectionState();
364                }
365                for (int i = 0; i < states.length; i++) {
366                    if (connectionState == states[i]) {
367                        devices.add(device);
368                    }
369                }
370            }
371            return devices;
372        }
373    }
374
375    /**
376     * Get the list of devices that have state machines.
377     *
378     * @return the list of devices that have state machines
379     */
380    @VisibleForTesting
381    List<BluetoothDevice> getDevices() {
382        List<BluetoothDevice> devices = new ArrayList<>();
383        synchronized (mStateMachines) {
384            for (A2dpStateMachine sm : mStateMachines.values()) {
385                devices.add(sm.getDevice());
386            }
387            return devices;
388        }
389    }
390
391    public int getConnectionState(BluetoothDevice device) {
392        enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
393        synchronized (mStateMachines) {
394            A2dpStateMachine sm = mStateMachines.get(device);
395            if (sm == null) {
396                return BluetoothProfile.STATE_DISCONNECTED;
397            }
398            return sm.getConnectionState();
399        }
400    }
401
402    /**
403     * Set the active device.
404     *
405     * @param device the active device
406     * @return true on success, otherwise false
407     */
408    public boolean setActiveDevice(BluetoothDevice device) {
409        enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH ADMIN permission");
410        synchronized (mStateMachines) {
411            BluetoothDevice previousActiveDevice = mActiveDevice;
412            if (DBG) {
413                Log.d(TAG, "setActiveDevice(" + device + "): previous is " + previousActiveDevice);
414            }
415            if (device == null) {
416                // Clear the active device
417                mActiveDevice = null;
418                broadcastActiveDevice(null);
419                if (previousActiveDevice != null) {
420                    // Make sure the Audio Manager knows the previous Active device is disconnected
421                    mAudioManager.setBluetoothA2dpDeviceConnectionState(
422                            previousActiveDevice, BluetoothProfile.STATE_DISCONNECTED,
423                            BluetoothProfile.A2DP);
424                }
425                return true;
426            }
427
428            BluetoothCodecStatus codecStatus = null;
429            A2dpStateMachine sm = mStateMachines.get(device);
430            if (sm == null) {
431                Log.e(TAG, "setActiveDevice(" + device + "): Cannot set as active: "
432                          + "no state machine");
433                return false;
434            }
435            if (sm.getConnectionState() != BluetoothProfile.STATE_CONNECTED) {
436                Log.e(TAG, "setActiveDevice(" + device + "): Cannot set as active: "
437                          + "device is not connected");
438                return false;
439            }
440            if (!mA2dpNativeInterface.setActiveDevice(device)) {
441                Log.e(TAG, "setActiveDevice(" + device + "): Cannot set as active in native layer");
442                return false;
443            }
444            codecStatus = sm.getCodecStatus();
445
446            boolean deviceChanged = !Objects.equals(device, mActiveDevice);
447            mActiveDevice = device;
448            broadcastActiveDevice(mActiveDevice);
449            if (deviceChanged) {
450                // Send an intent with the active device codec config
451                if (codecStatus != null) {
452                    broadcastCodecConfig(mActiveDevice, codecStatus);
453                }
454                // Make sure the Audio Manager knows the previous Active device is disconnected,
455                // and the new Active device is connected.
456                if (previousActiveDevice != null) {
457                    mAudioManager.setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(
458                            previousActiveDevice, BluetoothProfile.STATE_DISCONNECTED,
459                            BluetoothProfile.A2DP, true);
460                }
461                mAudioManager.setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(
462                        mActiveDevice, BluetoothProfile.STATE_CONNECTED, BluetoothProfile.A2DP,
463                        true);
464                // Inform the Audio Service about the codec configuration
465                // change, so the Audio Service can reset accordingly the audio
466                // feeding parameters in the Audio HAL to the Bluetooth stack.
467                mAudioManager.handleBluetoothA2dpDeviceConfigChange(mActiveDevice);
468            }
469        }
470        return true;
471    }
472
473    /**
474     * Get the active device.
475     *
476     * @return the active device or null if no device is active
477     */
478    public BluetoothDevice getActiveDevice() {
479        enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
480        synchronized (mStateMachines) {
481            return mActiveDevice;
482        }
483    }
484
485    private boolean isActiveDevice(BluetoothDevice device) {
486        synchronized (mStateMachines) {
487            return (device != null) && Objects.equals(device, mActiveDevice);
488        }
489    }
490
491    public boolean setPriority(BluetoothDevice device, int priority) {
492        enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission");
493        Settings.Global.putInt(getContentResolver(),
494                Settings.Global.getBluetoothA2dpSinkPriorityKey(device.getAddress()), priority);
495        if (DBG) {
496            Log.d(TAG, "Saved priority " + device + " = " + priority);
497        }
498        return true;
499    }
500
501    public int getPriority(BluetoothDevice device) {
502        enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission");
503        int priority = Settings.Global.getInt(getContentResolver(),
504                Settings.Global.getBluetoothA2dpSinkPriorityKey(device.getAddress()),
505                BluetoothProfile.PRIORITY_UNDEFINED);
506        return priority;
507    }
508
509    /* Absolute volume implementation */
510    public boolean isAvrcpAbsoluteVolumeSupported() {
511        return mAvrcp.isAbsoluteVolumeSupported();
512    }
513
514    public void adjustAvrcpAbsoluteVolume(int direction) {
515        mAvrcp.adjustVolume(direction);
516    }
517
518    public void setAvrcpAbsoluteVolume(int volume) {
519        mAvrcp.setAbsoluteVolume(volume);
520    }
521
522    public void setAvrcpAudioState(int state) {
523        mAvrcp.setA2dpAudioState(state);
524    }
525
526    public void resetAvrcpBlacklist(BluetoothDevice device) {
527        if (mAvrcp != null) {
528            mAvrcp.resetBlackList(device.getAddress());
529        }
530    }
531
532    boolean isA2dpPlaying(BluetoothDevice device) {
533        enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
534        if (DBG) {
535            Log.d(TAG, "isA2dpPlaying(" + device + ")");
536        }
537        synchronized (mStateMachines) {
538            A2dpStateMachine sm = mStateMachines.get(device);
539            if (sm == null) {
540                return false;
541            }
542            return sm.isPlaying();
543        }
544    }
545
546    /**
547     * Gets the current codec status (configuration and capability).
548     *
549     * @param device the remote Bluetooth device. If null, use the currect
550     * active A2DP Bluetooth device.
551     * @return the current codec status
552     * @hide
553     */
554    public BluetoothCodecStatus getCodecStatus(BluetoothDevice device) {
555        enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
556        if (DBG) {
557            Log.d(TAG, "getCodecStatus(" + device + ")");
558        }
559        synchronized (mStateMachines) {
560            if (device == null) {
561                device = mActiveDevice;
562            }
563            if (device == null) {
564                return null;
565            }
566            A2dpStateMachine sm = mStateMachines.get(device);
567            if (sm != null) {
568                return sm.getCodecStatus();
569            }
570            return null;
571        }
572    }
573
574    /**
575     * Sets the codec configuration preference.
576     *
577     * @param device the remote Bluetooth device. If null, use the currect
578     * active A2DP Bluetooth device.
579     * @param codecConfig the codec configuration preference
580     * @hide
581     */
582    public void setCodecConfigPreference(BluetoothDevice device,
583                                         BluetoothCodecConfig codecConfig) {
584        enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
585        if (DBG) {
586            Log.d(TAG, "setCodecConfigPreference(" + device + "): "
587                    + Objects.toString(codecConfig));
588        }
589        if (device == null) {
590            device = mActiveDevice;
591        }
592        if (device == null) {
593            Log.e(TAG, "Cannot set codec config preference: no active A2DP device");
594            return;
595        }
596        mA2dpCodecConfig.setCodecConfigPreference(device, codecConfig);
597    }
598
599    /**
600     * Enables the optional codecs.
601     *
602     * @param device the remote Bluetooth device. If null, use the currect
603     * active A2DP Bluetooth device.
604     * @hide
605     */
606    public void enableOptionalCodecs(BluetoothDevice device) {
607        enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
608        if (DBG) {
609            Log.d(TAG, "enableOptionalCodecs(" + device + ")");
610        }
611        if (device == null) {
612            device = mActiveDevice;
613        }
614        if (device == null) {
615            Log.e(TAG, "Cannot enable optional codecs: no active A2DP device");
616            return;
617        }
618        mA2dpCodecConfig.enableOptionalCodecs(device);
619    }
620
621    /**
622     * Disables the optional codecs.
623     *
624     * @param device the remote Bluetooth device. If null, use the currect
625     * active A2DP Bluetooth device.
626     * @hide
627     */
628    public void disableOptionalCodecs(BluetoothDevice device) {
629        enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
630        if (DBG) {
631            Log.d(TAG, "disableOptionalCodecs(" + device + ")");
632        }
633        if (device == null) {
634            device = mActiveDevice;
635        }
636        if (device == null) {
637            Log.e(TAG, "Cannot disable optional codecs: no active A2DP device");
638            return;
639        }
640        mA2dpCodecConfig.disableOptionalCodecs(device);
641    }
642
643    public int getSupportsOptionalCodecs(BluetoothDevice device) {
644        enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission");
645        int support = Settings.Global.getInt(getContentResolver(),
646                Settings.Global.getBluetoothA2dpSupportsOptionalCodecsKey(device.getAddress()),
647                BluetoothA2dp.OPTIONAL_CODECS_SUPPORT_UNKNOWN);
648        return support;
649    }
650
651    public void setSupportsOptionalCodecs(BluetoothDevice device, boolean doesSupport) {
652        enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission");
653        int value = doesSupport ? BluetoothA2dp.OPTIONAL_CODECS_SUPPORTED
654                : BluetoothA2dp.OPTIONAL_CODECS_NOT_SUPPORTED;
655        Settings.Global.putInt(getContentResolver(),
656                Settings.Global.getBluetoothA2dpSupportsOptionalCodecsKey(device.getAddress()),
657                value);
658    }
659
660    public int getOptionalCodecsEnabled(BluetoothDevice device) {
661        enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission");
662        return Settings.Global.getInt(getContentResolver(),
663                Settings.Global.getBluetoothA2dpOptionalCodecsEnabledKey(device.getAddress()),
664                BluetoothA2dp.OPTIONAL_CODECS_PREF_UNKNOWN);
665    }
666
667    public void setOptionalCodecsEnabled(BluetoothDevice device, int value) {
668        enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission");
669        if (value != BluetoothA2dp.OPTIONAL_CODECS_PREF_UNKNOWN
670                && value != BluetoothA2dp.OPTIONAL_CODECS_PREF_DISABLED
671                && value != BluetoothA2dp.OPTIONAL_CODECS_PREF_ENABLED) {
672            Log.w(TAG, "Unexpected value passed to setOptionalCodecsEnabled:" + value);
673            return;
674        }
675        Settings.Global.putInt(getContentResolver(),
676                Settings.Global.getBluetoothA2dpOptionalCodecsEnabledKey(device.getAddress()),
677                value);
678    }
679
680    // Handle messages from native (JNI) to Java
681    void messageFromNative(A2dpStackEvent stackEvent) {
682        Objects.requireNonNull(stackEvent.device,
683                               "Device should never be null, event: " + stackEvent);
684        synchronized (mStateMachines) {
685            A2dpStateMachine sm = null;
686            BluetoothDevice device = stackEvent.device;
687            if (stackEvent.type == A2dpStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED) {
688                switch (stackEvent.valueInt) {
689                    case A2dpStackEvent.CONNECTION_STATE_CONNECTED:
690                    case A2dpStackEvent.CONNECTION_STATE_CONNECTING:
691                        // Create a new state machine only when connecting to a device
692                        if (!connectionAllowedCheckMaxDevices(device)) {
693                            Log.e(TAG, "Cannot connect to " + device
694                                    + " : too many connected devices");
695                            return;
696                        }
697                        sm = getOrCreateStateMachine(device);
698                        break;
699                    default:
700                        sm = mStateMachines.get(device);
701                        break;
702                }
703            }
704            if (sm == null) {
705                Log.e(TAG, "Cannot process stack event: no state machine: " + stackEvent);
706                return;
707            }
708            sm.sendMessage(A2dpStateMachine.STACK_EVENT, stackEvent);
709        }
710    }
711
712    /**
713     * The codec configuration for a device has been updated.
714     *
715     * @param device the remote device
716     * @param codecStatus the new codec status
717     * @param sameAudioFeedingParameters if true the audio feeding parameters
718     * haven't been changed
719     */
720    void codecConfigUpdated(BluetoothDevice device, BluetoothCodecStatus codecStatus,
721                            boolean sameAudioFeedingParameters) {
722        broadcastCodecConfig(device, codecStatus);
723
724        // Inform the Audio Service about the codec configuration change,
725        // so the Audio Service can reset accordingly the audio feeding
726        // parameters in the Audio HAL to the Bluetooth stack.
727        if (isActiveDevice(device) && !sameAudioFeedingParameters) {
728            mAudioManager.handleBluetoothA2dpDeviceConfigChange(device);
729        }
730    }
731
732    private A2dpStateMachine getOrCreateStateMachine(BluetoothDevice device) {
733        if (device == null) {
734            Log.e(TAG, "getOrCreateStateMachine failed: device cannot be null");
735            return null;
736        }
737        synchronized (mStateMachines) {
738            A2dpStateMachine sm = mStateMachines.get(device);
739            if (sm != null) {
740                return sm;
741            }
742            // Limit the maximum number of state machines to avoid DoS attack
743            if (mStateMachines.size() >= MAX_A2DP_STATE_MACHINES) {
744                Log.e(TAG, "Maximum number of A2DP state machines reached: "
745                        + MAX_A2DP_STATE_MACHINES);
746                return null;
747            }
748            if (DBG) {
749                Log.d(TAG, "Creating a new state machine for " + device);
750            }
751            sm = A2dpStateMachine.make(device, this, mA2dpNativeInterface,
752                                       mStateMachinesThread.getLooper());
753            mStateMachines.put(device, sm);
754            return sm;
755        }
756    }
757
758    private void broadcastActiveDevice(BluetoothDevice device) {
759        if (DBG) {
760            Log.d(TAG, "broadcastActiveDevice(" + device + ")");
761        }
762
763        Intent intent = new Intent(BluetoothA2dp.ACTION_ACTIVE_DEVICE_CHANGED);
764        intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
765        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
766                        | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
767        sendBroadcast(intent, ProfileService.BLUETOOTH_PERM);
768    }
769
770    private void broadcastCodecConfig(BluetoothDevice device, BluetoothCodecStatus codecStatus) {
771        if (DBG) {
772            Log.d(TAG, "broadcastCodecConfig(" + device + "): " + codecStatus);
773        }
774        Intent intent = new Intent(BluetoothA2dp.ACTION_CODEC_CONFIG_CHANGED);
775        intent.putExtra(BluetoothCodecStatus.EXTRA_CODEC_STATUS, codecStatus);
776        intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
777        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
778                        | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
779        sendBroadcast(intent, A2dpService.BLUETOOTH_PERM);
780    }
781
782    private class BondStateChangedReceiver extends BroadcastReceiver {
783        @Override
784        public void onReceive(Context context, Intent intent) {
785            if (!BluetoothDevice.ACTION_BOND_STATE_CHANGED.equals(intent.getAction())) {
786                return;
787            }
788            int state = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE,
789                                           BluetoothDevice.ERROR);
790            BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
791            Objects.requireNonNull(device, "ACTION_BOND_STATE_CHANGED with no EXTRA_DEVICE");
792            bondStateChanged(device, state);
793        }
794    }
795
796    /**
797     * Process a change in the bonding state for a device.
798     *
799     * @param device the device whose bonding state has changed
800     * @param bondState the new bond state for the device. Possible values are:
801     * {@link BluetoothDevice#BOND_NONE},
802     * {@link BluetoothDevice#BOND_BONDING},
803     * {@link BluetoothDevice#BOND_BONDED}.
804     */
805    @VisibleForTesting
806    void bondStateChanged(BluetoothDevice device, int bondState) {
807        if (DBG) {
808            Log.d(TAG, "Bond state changed for device: " + device + " state: " + bondState);
809        }
810        // Remove state machine if the bonding for a device is removed
811        if (bondState != BluetoothDevice.BOND_NONE) {
812            return;
813        }
814        synchronized (mStateMachines) {
815            A2dpStateMachine sm = mStateMachines.get(device);
816            if (sm == null) {
817                return;
818            }
819            if (sm.getConnectionState() != BluetoothProfile.STATE_DISCONNECTED) {
820                return;
821            }
822            removeStateMachine(device);
823        }
824    }
825
826    private void removeStateMachine(BluetoothDevice device) {
827        synchronized (mStateMachines) {
828            A2dpStateMachine sm = mStateMachines.get(device);
829            if (sm == null) {
830                Log.w(TAG, "removeStateMachine: device " + device
831                        + " does not have a state machine");
832                return;
833            }
834            Log.i(TAG, "removeStateMachine: removing state machine for device: " + device);
835            sm.doQuit();
836            sm.cleanup();
837            mStateMachines.remove(device);
838        }
839    }
840
841    private void updateOptionalCodecsSupport(BluetoothDevice device) {
842        int previousSupport = getSupportsOptionalCodecs(device);
843        boolean supportsOptional = false;
844
845        synchronized (mStateMachines) {
846            A2dpStateMachine sm = mStateMachines.get(device);
847            if (sm == null) {
848                return;
849            }
850            BluetoothCodecStatus codecStatus = sm.getCodecStatus();
851            if (codecStatus != null) {
852                for (BluetoothCodecConfig config : codecStatus.getCodecsSelectableCapabilities()) {
853                    if (!config.isMandatoryCodec()) {
854                        supportsOptional = true;
855                        break;
856                    }
857                }
858            }
859        }
860        if (previousSupport == BluetoothA2dp.OPTIONAL_CODECS_SUPPORT_UNKNOWN
861                || supportsOptional != (previousSupport
862                                    == BluetoothA2dp.OPTIONAL_CODECS_SUPPORTED)) {
863            setSupportsOptionalCodecs(device, supportsOptional);
864        }
865        if (supportsOptional) {
866            int enabled = getOptionalCodecsEnabled(device);
867            if (enabled == BluetoothA2dp.OPTIONAL_CODECS_PREF_ENABLED) {
868                enableOptionalCodecs(device);
869            } else if (enabled == BluetoothA2dp.OPTIONAL_CODECS_PREF_DISABLED) {
870                disableOptionalCodecs(device);
871            }
872        }
873    }
874
875    private void connectionStateChanged(BluetoothDevice device, int fromState, int toState) {
876        if ((device == null) || (fromState == toState)) {
877            return;
878        }
879        synchronized (mStateMachines) {
880            if (toState == BluetoothProfile.STATE_CONNECTED) {
881                // Each time a device connects, we want to re-check if it supports optional
882                // codecs (perhaps it's had a firmware update, etc.) and save that state if
883                // it differs from what we had saved before.
884                updateOptionalCodecsSupport(device);
885            }
886            // Set the active device if only one connected device is supported and it was connected
887            if (toState == BluetoothProfile.STATE_CONNECTED && (mMaxConnectedAudioDevices == 1)) {
888                setActiveDevice(device);
889            }
890            // Check if the active device is not connected anymore
891            if (isActiveDevice(device) && (fromState == BluetoothProfile.STATE_CONNECTED)) {
892                setActiveDevice(null);
893            }
894            // Check if the device is disconnected - if unbond, remove the state machine
895            if (toState == BluetoothProfile.STATE_DISCONNECTED) {
896                int bondState = mAdapterService.getBondState(device);
897                if (bondState == BluetoothDevice.BOND_NONE) {
898                    removeStateMachine(device);
899                }
900            }
901        }
902    }
903
904    /**
905     * Receiver for processing device connection state changes.
906     *
907     * <ul>
908     * <li> Update codec support per device when device is (re)connected
909     * <li> Delete the state machine instance if the device is disconnected and unbond
910     * </ul>
911     */
912    private class ConnectionStateChangedReceiver extends BroadcastReceiver {
913        @Override
914        public void onReceive(Context context, Intent intent) {
915            if (!BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED.equals(intent.getAction())) {
916                return;
917            }
918            BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
919            int toState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1);
920            int fromState = intent.getIntExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, -1);
921            connectionStateChanged(device, fromState, toState);
922        }
923    }
924
925    /**
926     * Binder object: must be a static class or memory leak may occur.
927     */
928    @VisibleForTesting
929    static class BluetoothA2dpBinder extends IBluetoothA2dp.Stub
930            implements IProfileServiceBinder {
931        private A2dpService mService;
932
933        private A2dpService getService() {
934            if (!Utils.checkCaller()) {
935                Log.w(TAG, "A2DP call not allowed for non-active user");
936                return null;
937            }
938
939            if (mService != null && mService.isAvailable()) {
940                return mService;
941            }
942            return null;
943        }
944
945        BluetoothA2dpBinder(A2dpService svc) {
946            mService = svc;
947        }
948
949        @Override
950        public void cleanup() {
951            mService = null;
952        }
953
954        @Override
955        public boolean connect(BluetoothDevice device) {
956            A2dpService service = getService();
957            if (service == null) {
958                return false;
959            }
960            return service.connect(device);
961        }
962
963        @Override
964        public boolean disconnect(BluetoothDevice device) {
965            A2dpService service = getService();
966            if (service == null) {
967                return false;
968            }
969            return service.disconnect(device);
970        }
971
972        @Override
973        public List<BluetoothDevice> getConnectedDevices() {
974            A2dpService service = getService();
975            if (service == null) {
976                return new ArrayList<>(0);
977            }
978            return service.getConnectedDevices();
979        }
980
981        @Override
982        public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
983            A2dpService service = getService();
984            if (service == null) {
985                return new ArrayList<>(0);
986            }
987            return service.getDevicesMatchingConnectionStates(states);
988        }
989
990        @Override
991        public int getConnectionState(BluetoothDevice device) {
992            A2dpService service = getService();
993            if (service == null) {
994                return BluetoothProfile.STATE_DISCONNECTED;
995            }
996            return service.getConnectionState(device);
997        }
998
999        @Override
1000        public boolean setActiveDevice(BluetoothDevice device) {
1001            A2dpService service = getService();
1002            if (service == null) {
1003                return false;
1004            }
1005            return service.setActiveDevice(device);
1006        }
1007
1008        @Override
1009        public BluetoothDevice getActiveDevice() {
1010            A2dpService service = getService();
1011            if (service == null) {
1012                return null;
1013            }
1014            return service.getActiveDevice();
1015        }
1016
1017        @Override
1018        public boolean setPriority(BluetoothDevice device, int priority) {
1019            A2dpService service = getService();
1020            if (service == null) {
1021                return false;
1022            }
1023            return service.setPriority(device, priority);
1024        }
1025
1026        @Override
1027        public int getPriority(BluetoothDevice device) {
1028            A2dpService service = getService();
1029            if (service == null) {
1030                return BluetoothProfile.PRIORITY_UNDEFINED;
1031            }
1032            return service.getPriority(device);
1033        }
1034
1035        @Override
1036        public boolean isAvrcpAbsoluteVolumeSupported() {
1037            A2dpService service = getService();
1038            if (service == null) {
1039                return false;
1040            }
1041            return service.isAvrcpAbsoluteVolumeSupported();
1042        }
1043
1044        @Override
1045        public void adjustAvrcpAbsoluteVolume(int direction) {
1046            A2dpService service = getService();
1047            if (service == null) {
1048                return;
1049            }
1050            service.adjustAvrcpAbsoluteVolume(direction);
1051        }
1052
1053        @Override
1054        public void setAvrcpAbsoluteVolume(int volume) {
1055            A2dpService service = getService();
1056            if (service == null) {
1057                return;
1058            }
1059            service.setAvrcpAbsoluteVolume(volume);
1060        }
1061
1062        @Override
1063        public boolean isA2dpPlaying(BluetoothDevice device) {
1064            A2dpService service = getService();
1065            if (service == null) {
1066                return false;
1067            }
1068            return service.isA2dpPlaying(device);
1069        }
1070
1071        @Override
1072        public BluetoothCodecStatus getCodecStatus(BluetoothDevice device) {
1073            A2dpService service = getService();
1074            if (service == null) {
1075                return null;
1076            }
1077            return service.getCodecStatus(device);
1078        }
1079
1080        @Override
1081        public void setCodecConfigPreference(BluetoothDevice device,
1082                                             BluetoothCodecConfig codecConfig) {
1083            A2dpService service = getService();
1084            if (service == null) {
1085                return;
1086            }
1087            service.setCodecConfigPreference(device, codecConfig);
1088        }
1089
1090        @Override
1091        public void enableOptionalCodecs(BluetoothDevice device) {
1092            A2dpService service = getService();
1093            if (service == null) {
1094                return;
1095            }
1096            service.enableOptionalCodecs(device);
1097        }
1098
1099        @Override
1100        public void disableOptionalCodecs(BluetoothDevice device) {
1101            A2dpService service = getService();
1102            if (service == null) {
1103                return;
1104            }
1105            service.disableOptionalCodecs(device);
1106        }
1107
1108        public int supportsOptionalCodecs(BluetoothDevice device) {
1109            A2dpService service = getService();
1110            if (service == null) {
1111                return BluetoothA2dp.OPTIONAL_CODECS_SUPPORT_UNKNOWN;
1112            }
1113            return service.getSupportsOptionalCodecs(device);
1114        }
1115
1116        public int getOptionalCodecsEnabled(BluetoothDevice device) {
1117            A2dpService service = getService();
1118            if (service == null) {
1119                return BluetoothA2dp.OPTIONAL_CODECS_PREF_UNKNOWN;
1120            }
1121            return service.getOptionalCodecsEnabled(device);
1122        }
1123
1124        public void setOptionalCodecsEnabled(BluetoothDevice device, int value) {
1125            A2dpService service = getService();
1126            if (service == null) {
1127                return;
1128            }
1129            service.setOptionalCodecsEnabled(device, value);
1130        }
1131    }
1132
1133    @Override
1134    public void dump(StringBuilder sb) {
1135        super.dump(sb);
1136        ProfileService.println(sb, "mActiveDevice: " + mActiveDevice);
1137        for (A2dpStateMachine sm : mStateMachines.values()) {
1138            sm.dump(sb);
1139        }
1140        if (mAvrcp != null) {
1141            mAvrcp.dump(sb);
1142        }
1143    }
1144}
1145