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