• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2012 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.bluetooth.a2dp;
18 
19 import static android.Manifest.permission.BLUETOOTH_CONNECT;
20 
21 import static com.android.bluetooth.Utils.enforceBluetoothPrivilegedPermission;
22 
23 import android.annotation.RequiresPermission;
24 import android.bluetooth.BluetoothA2dp;
25 import android.bluetooth.BluetoothA2dp.OptionalCodecsPreferenceStatus;
26 import android.bluetooth.BluetoothA2dp.OptionalCodecsSupportStatus;
27 import android.bluetooth.BluetoothCodecConfig;
28 import android.bluetooth.BluetoothCodecStatus;
29 import android.bluetooth.BluetoothDevice;
30 import android.bluetooth.BluetoothProfile;
31 import android.bluetooth.BluetoothUuid;
32 import android.bluetooth.BufferConstraints;
33 import android.bluetooth.IBluetoothA2dp;
34 import android.content.Attributable;
35 import android.content.AttributionSource;
36 import android.content.BroadcastReceiver;
37 import android.content.Context;
38 import android.content.Intent;
39 import android.content.IntentFilter;
40 import android.media.AudioManager;
41 import android.os.HandlerThread;
42 import android.util.Log;
43 
44 import com.android.bluetooth.BluetoothMetricsProto;
45 import com.android.bluetooth.BluetoothStatsLog;
46 import com.android.bluetooth.Utils;
47 import com.android.bluetooth.btservice.AdapterService;
48 import com.android.bluetooth.btservice.MetricsLogger;
49 import com.android.bluetooth.btservice.ProfileService;
50 import com.android.bluetooth.btservice.ServiceFactory;
51 import com.android.bluetooth.btservice.storage.DatabaseManager;
52 import com.android.internal.annotations.GuardedBy;
53 import com.android.internal.annotations.VisibleForTesting;
54 import com.android.internal.util.ArrayUtils;
55 
56 import java.util.ArrayList;
57 import java.util.List;
58 import java.util.Objects;
59 import java.util.concurrent.ConcurrentHashMap;
60 import java.util.concurrent.ConcurrentMap;
61 
62 /**
63  * Provides Bluetooth A2DP profile, as a service in the Bluetooth application.
64  * @hide
65  */
66 public class A2dpService extends ProfileService {
67     private static final boolean DBG = true;
68     private static final String TAG = "A2dpService";
69 
70     private static A2dpService sA2dpService;
71 
72     private AdapterService mAdapterService;
73     private DatabaseManager mDatabaseManager;
74     private HandlerThread mStateMachinesThread;
75 
76     @VisibleForTesting
77     A2dpNativeInterface mA2dpNativeInterface;
78     @VisibleForTesting
79     ServiceFactory mFactory = new ServiceFactory();
80     private AudioManager mAudioManager;
81     private A2dpCodecConfig mA2dpCodecConfig;
82 
83     @GuardedBy("mStateMachines")
84     private BluetoothDevice mActiveDevice;
85     private final ConcurrentMap<BluetoothDevice, A2dpStateMachine> mStateMachines =
86             new ConcurrentHashMap<>();
87 
88     // Protect setActiveDevice() so all invoked is handled squentially
89     private final Object mActiveSwitchingGuard = new Object();
90 
91     // Upper limit of all A2DP devices: Bonded or Connected
92     private static final int MAX_A2DP_STATE_MACHINES = 50;
93     // Upper limit of all A2DP devices that are Connected or Connecting
94     private int mMaxConnectedAudioDevices = 1;
95     // A2DP Offload Enabled in platform
96     boolean mA2dpOffloadEnabled = false;
97 
98     private BroadcastReceiver mBondStateChangedReceiver;
99     private BroadcastReceiver mConnectionStateChangedReceiver;
100 
101     @Override
initBinder()102     protected IProfileServiceBinder initBinder() {
103         return new BluetoothA2dpBinder(this);
104     }
105 
106     @Override
create()107     protected void create() {
108         Log.i(TAG, "create()");
109     }
110 
111     @Override
start()112     protected boolean start() {
113         Log.i(TAG, "start()");
114         if (sA2dpService != null) {
115             throw new IllegalStateException("start() called twice");
116         }
117 
118         // Step 1: Get AdapterService, A2dpNativeInterface, DatabaseManager, AudioManager.
119         // None of them can be null.
120         mAdapterService = Objects.requireNonNull(AdapterService.getAdapterService(),
121                 "AdapterService cannot be null when A2dpService starts");
122         mA2dpNativeInterface = Objects.requireNonNull(A2dpNativeInterface.getInstance(),
123                 "A2dpNativeInterface cannot be null when A2dpService starts");
124         mDatabaseManager = Objects.requireNonNull(mAdapterService.getDatabase(),
125                 "DatabaseManager cannot be null when A2dpService starts");
126         mAudioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
127         Objects.requireNonNull(mAudioManager,
128                                "AudioManager cannot be null when A2dpService starts");
129 
130         // Step 2: Get maximum number of connected audio devices
131         mMaxConnectedAudioDevices = mAdapterService.getMaxConnectedAudioDevices();
132         Log.i(TAG, "Max connected audio devices set to " + mMaxConnectedAudioDevices);
133 
134         // Step 3: Start handler thread for state machines
135         mStateMachines.clear();
136         mStateMachinesThread = new HandlerThread("A2dpService.StateMachines");
137         mStateMachinesThread.start();
138 
139         // Step 4: Setup codec config
140         mA2dpCodecConfig = new A2dpCodecConfig(this, mA2dpNativeInterface);
141 
142         // Step 5: Initialize native interface
143         mA2dpNativeInterface.init(mMaxConnectedAudioDevices,
144                                   mA2dpCodecConfig.codecConfigPriorities(),
145                                   mA2dpCodecConfig.codecConfigOffloading());
146 
147         // Step 6: Check if A2DP is in offload mode
148         mA2dpOffloadEnabled = mAdapterService.isA2dpOffloadEnabled();
149         if (DBG) {
150             Log.d(TAG, "A2DP offload flag set to " + mA2dpOffloadEnabled);
151         }
152 
153         // Step 7: Setup broadcast receivers
154         IntentFilter filter = new IntentFilter();
155         filter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
156         mBondStateChangedReceiver = new BondStateChangedReceiver();
157         registerReceiver(mBondStateChangedReceiver, filter);
158         filter = new IntentFilter();
159         filter.addAction(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED);
160         mConnectionStateChangedReceiver = new ConnectionStateChangedReceiver();
161         registerReceiver(mConnectionStateChangedReceiver, filter);
162 
163         // Step 8: Mark service as started
164         setA2dpService(this);
165 
166         // Step 9: Clear active device
167         setActiveDevice(null);
168 
169         return true;
170     }
171 
172     @Override
stop()173     protected boolean stop() {
174         Log.i(TAG, "stop()");
175         if (sA2dpService == null) {
176             Log.w(TAG, "stop() called before start()");
177             return true;
178         }
179 
180         // Step 9: Clear active device and stop playing audio
181         removeActiveDevice(true);
182 
183         // Step 8: Mark service as stopped
184         setA2dpService(null);
185 
186         // Step 7: Unregister broadcast receivers
187         unregisterReceiver(mConnectionStateChangedReceiver);
188         mConnectionStateChangedReceiver = null;
189         unregisterReceiver(mBondStateChangedReceiver);
190         mBondStateChangedReceiver = null;
191 
192         // Step 6: Cleanup native interface
193         mA2dpNativeInterface.cleanup();
194         mA2dpNativeInterface = null;
195 
196         // Step 5: Clear codec config
197         mA2dpCodecConfig = null;
198 
199         // Step 4: Destroy state machines and stop handler thread
200         synchronized (mStateMachines) {
201             for (A2dpStateMachine sm : mStateMachines.values()) {
202                 sm.doQuit();
203                 sm.cleanup();
204             }
205             mStateMachines.clear();
206         }
207         mStateMachinesThread.quitSafely();
208         mStateMachinesThread = null;
209 
210         // Step 2: Reset maximum number of connected audio devices
211         mMaxConnectedAudioDevices = 1;
212 
213         // Step 1: Clear AdapterService, A2dpNativeInterface, AudioManager
214         mAudioManager = null;
215         mA2dpNativeInterface = null;
216         mAdapterService = null;
217 
218         return true;
219     }
220 
221     @Override
cleanup()222     protected void cleanup() {
223         Log.i(TAG, "cleanup()");
224     }
225 
getA2dpService()226     public static synchronized A2dpService getA2dpService() {
227         if (sA2dpService == null) {
228             Log.w(TAG, "getA2dpService(): service is null");
229             return null;
230         }
231         if (!sA2dpService.isAvailable()) {
232             Log.w(TAG, "getA2dpService(): service is not available");
233             return null;
234         }
235         return sA2dpService;
236     }
237 
setA2dpService(A2dpService instance)238     private static synchronized void setA2dpService(A2dpService instance) {
239         if (DBG) {
240             Log.d(TAG, "setA2dpService(): set to: " + instance);
241         }
242         sA2dpService = instance;
243     }
244 
connect(BluetoothDevice device)245     public boolean connect(BluetoothDevice device) {
246         if (DBG) {
247             Log.d(TAG, "connect(): " + device);
248         }
249 
250         if (getConnectionPolicy(device) == BluetoothProfile.CONNECTION_POLICY_FORBIDDEN) {
251             Log.e(TAG, "Cannot connect to " + device + " : CONNECTION_POLICY_FORBIDDEN");
252             return false;
253         }
254         if (!ArrayUtils.contains(mAdapterService.getRemoteUuids(device),
255                                          BluetoothUuid.A2DP_SINK)) {
256             Log.e(TAG, "Cannot connect to " + device + " : Remote does not have A2DP Sink UUID");
257             return false;
258         }
259 
260         synchronized (mStateMachines) {
261             if (!connectionAllowedCheckMaxDevices(device)) {
262                 // when mMaxConnectedAudioDevices is one, disconnect current device first.
263                 if (mMaxConnectedAudioDevices == 1) {
264                     List<BluetoothDevice> sinks = getDevicesMatchingConnectionStates(
265                             new int[] {BluetoothProfile.STATE_CONNECTED,
266                                     BluetoothProfile.STATE_CONNECTING,
267                                     BluetoothProfile.STATE_DISCONNECTING});
268                     for (BluetoothDevice sink : sinks) {
269                         if (sink.equals(device)) {
270                             Log.w(TAG, "Connecting to device " + device + " : disconnect skipped");
271                             continue;
272                         }
273                         disconnect(sink);
274                     }
275                 } else {
276                     Log.e(TAG, "Cannot connect to " + device + " : too many connected devices");
277                     return false;
278                 }
279             }
280             A2dpStateMachine smConnect = getOrCreateStateMachine(device);
281             if (smConnect == null) {
282                 Log.e(TAG, "Cannot connect to " + device + " : no state machine");
283                 return false;
284             }
285             smConnect.sendMessage(A2dpStateMachine.CONNECT);
286             return true;
287         }
288     }
289 
290     /**
291      * Disconnects A2dp for the remote bluetooth device
292      *
293      * @param device is the device with which we would like to disconnect a2dp
294      * @return true if profile disconnected, false if device not connected over a2dp
295      */
disconnect(BluetoothDevice device)296     public boolean disconnect(BluetoothDevice device) {
297         if (DBG) {
298             Log.d(TAG, "disconnect(): " + device);
299         }
300 
301         synchronized (mStateMachines) {
302             A2dpStateMachine sm = mStateMachines.get(device);
303             if (sm == null) {
304                 Log.e(TAG, "Ignored disconnect request for " + device + " : no state machine");
305                 return false;
306             }
307             sm.sendMessage(A2dpStateMachine.DISCONNECT);
308             return true;
309         }
310     }
311 
getConnectedDevices()312     public List<BluetoothDevice> getConnectedDevices() {
313         synchronized (mStateMachines) {
314             List<BluetoothDevice> devices = new ArrayList<>();
315             for (A2dpStateMachine sm : mStateMachines.values()) {
316                 if (sm.isConnected()) {
317                     devices.add(sm.getDevice());
318                 }
319             }
320             return devices;
321         }
322     }
323 
324     /**
325      * Check whether can connect to a peer device.
326      * The check considers the maximum number of connected peers.
327      *
328      * @param device the peer device to connect to
329      * @return true if connection is allowed, otherwise false
330      */
connectionAllowedCheckMaxDevices(BluetoothDevice device)331     private boolean connectionAllowedCheckMaxDevices(BluetoothDevice device) {
332         int connected = 0;
333         // Count devices that are in the process of connecting or already connected
334         synchronized (mStateMachines) {
335             for (A2dpStateMachine sm : mStateMachines.values()) {
336                 switch (sm.getConnectionState()) {
337                     case BluetoothProfile.STATE_CONNECTING:
338                     case BluetoothProfile.STATE_CONNECTED:
339                         if (Objects.equals(device, sm.getDevice())) {
340                             return true;    // Already connected or accounted for
341                         }
342                         connected++;
343                         break;
344                     default:
345                         break;
346                 }
347             }
348         }
349         return (connected < mMaxConnectedAudioDevices);
350     }
351 
352     /**
353      * Check whether can connect to a peer device.
354      * The check considers a number of factors during the evaluation.
355      *
356      * @param device the peer device to connect to
357      * @param isOutgoingRequest if true, the check is for outgoing connection
358      * request, otherwise is for incoming connection request
359      * @return true if connection is allowed, otherwise false
360      */
361     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
okToConnect(BluetoothDevice device, boolean isOutgoingRequest)362     public boolean okToConnect(BluetoothDevice device, boolean isOutgoingRequest) {
363         Log.i(TAG, "okToConnect: device " + device + " isOutgoingRequest: " + isOutgoingRequest);
364         // Check if this is an incoming connection in Quiet mode.
365         if (mAdapterService.isQuietModeEnabled() && !isOutgoingRequest) {
366             Log.e(TAG, "okToConnect: cannot connect to " + device + " : quiet mode enabled");
367             return false;
368         }
369         // Check if too many devices
370         if (!connectionAllowedCheckMaxDevices(device)) {
371             Log.e(TAG, "okToConnect: cannot connect to " + device
372                     + " : too many connected devices");
373             return false;
374         }
375         // Check connectionPolicy and accept or reject the connection.
376         int connectionPolicy = getConnectionPolicy(device);
377         int bondState = mAdapterService.getBondState(device);
378         // Allow this connection only if the device is bonded. Any attempt to connect while
379         // bonding would potentially lead to an unauthorized connection.
380         if (bondState != BluetoothDevice.BOND_BONDED) {
381             Log.w(TAG, "okToConnect: return false, bondState=" + bondState);
382             return false;
383         } else if (connectionPolicy != BluetoothProfile.CONNECTION_POLICY_UNKNOWN
384                 && connectionPolicy != BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
385             // Otherwise, reject the connection if connectionPolicy is not valid.
386             Log.w(TAG, "okToConnect: return false, connectionPolicy=" + connectionPolicy);
387             return false;
388         }
389         return true;
390     }
391 
getDevicesMatchingConnectionStates(int[] states)392     List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
393         List<BluetoothDevice> devices = new ArrayList<>();
394         if (states == null) {
395             return devices;
396         }
397         final BluetoothDevice[] bondedDevices = mAdapterService.getBondedDevices();
398         if (bondedDevices == null) {
399             return devices;
400         }
401         synchronized (mStateMachines) {
402             for (BluetoothDevice device : bondedDevices) {
403                 if (!ArrayUtils.contains(mAdapterService.getRemoteUuids(device),
404                                                  BluetoothUuid.A2DP_SINK)) {
405                     continue;
406                 }
407                 int connectionState = BluetoothProfile.STATE_DISCONNECTED;
408                 A2dpStateMachine sm = mStateMachines.get(device);
409                 if (sm != null) {
410                     connectionState = sm.getConnectionState();
411                 }
412                 for (int state : states) {
413                     if (connectionState == state) {
414                         devices.add(device);
415                         break;
416                     }
417                 }
418             }
419             return devices;
420         }
421     }
422 
423     /**
424      * Get the list of devices that have state machines.
425      *
426      * @return the list of devices that have state machines
427      */
428     @VisibleForTesting
getDevices()429     List<BluetoothDevice> getDevices() {
430         List<BluetoothDevice> devices = new ArrayList<>();
431         synchronized (mStateMachines) {
432             for (A2dpStateMachine sm : mStateMachines.values()) {
433                 devices.add(sm.getDevice());
434             }
435             return devices;
436         }
437     }
438 
getConnectionState(BluetoothDevice device)439     public int getConnectionState(BluetoothDevice device) {
440         synchronized (mStateMachines) {
441             A2dpStateMachine sm = mStateMachines.get(device);
442             if (sm == null) {
443                 return BluetoothProfile.STATE_DISCONNECTED;
444             }
445             return sm.getConnectionState();
446         }
447     }
448 
removeActiveDevice(boolean forceStopPlayingAudio)449     private void removeActiveDevice(boolean forceStopPlayingAudio) {
450         synchronized (mActiveSwitchingGuard) {
451             BluetoothDevice previousActiveDevice = null;
452             synchronized (mStateMachines) {
453                 if (mActiveDevice == null) return;
454                 previousActiveDevice = mActiveDevice;
455             }
456             // This needs to happen before we inform the audio manager that the device
457             // disconnected. Please see comment in updateAndBroadcastActiveDevice() for why.
458             updateAndBroadcastActiveDevice(null);
459 
460             // Make sure the Audio Manager knows the previous Active device is disconnected.
461             // However, if A2DP is still connected and not forcing stop audio for that remote
462             // device, the user has explicitly switched the output to the local device and music
463             // should continue playing. Otherwise, the remote device has been indeed disconnected
464             // and audio should be suspended before switching the output to the local device.
465             boolean suppressNoisyIntent = !forceStopPlayingAudio
466                     && (getConnectionState(previousActiveDevice)
467                     == BluetoothProfile.STATE_CONNECTED);
468             Log.i(TAG, "removeActiveDevice: suppressNoisyIntent=" + suppressNoisyIntent);
469             mAudioManager.setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(
470                     previousActiveDevice, BluetoothProfile.STATE_DISCONNECTED,
471                     BluetoothProfile.A2DP, suppressNoisyIntent, -1);
472 
473             synchronized (mStateMachines) {
474                 // Make sure the Active device in native layer is set to null and audio is off
475                 if (!mA2dpNativeInterface.setActiveDevice(null)) {
476                     Log.w(TAG, "setActiveDevice(null): Cannot remove active device in native "
477                             + "layer");
478                 }
479             }
480         }
481     }
482 
483     /**
484      * Process a change in the silence mode for a {@link BluetoothDevice}.
485      *
486      * @param device the device to change silence mode
487      * @param silence true to enable silence mode, false to disable.
488      * @return true on success, false on error
489      */
490     @VisibleForTesting
setSilenceMode(BluetoothDevice device, boolean silence)491     public boolean setSilenceMode(BluetoothDevice device, boolean silence) {
492         if (DBG) {
493             Log.d(TAG, "setSilenceMode(" + device + "): " + silence);
494         }
495         if (silence && Objects.equals(mActiveDevice, device)) {
496             removeActiveDevice(true);
497         } else if (!silence && mActiveDevice == null) {
498             // Set the device as the active device if currently no active device.
499             setActiveDevice(device);
500         }
501         if (!mA2dpNativeInterface.setSilenceDevice(device, silence)) {
502             Log.e(TAG, "Cannot set " + device + " silence mode " + silence + " in native layer");
503             return false;
504         }
505         return true;
506     }
507 
508     /**
509      * Set the active device.
510      *
511      * @param device the active device
512      * @return true on success, otherwise false
513      */
setActiveDevice(BluetoothDevice device)514     public boolean setActiveDevice(BluetoothDevice device) {
515         synchronized (mActiveSwitchingGuard) {
516             if (device == null) {
517                 // Remove active device and continue playing audio only if necessary.
518                 removeActiveDevice(false);
519                 return true;
520             }
521 
522             A2dpStateMachine sm = null;
523             BluetoothDevice previousActiveDevice = null;
524             synchronized (mStateMachines) {
525                 if (Objects.equals(device, mActiveDevice)) {
526                     Log.i(TAG, "setActiveDevice(" + device + "): current is " + mActiveDevice
527                             + " no changed");
528                     // returns true since the device is activated even double attempted
529                     return true;
530                 }
531                 if (DBG) {
532                     Log.d(TAG, "setActiveDevice(" + device + "): current is " + mActiveDevice);
533                 }
534                 sm = mStateMachines.get(device);
535                 if (sm == null) {
536                     Log.e(TAG, "setActiveDevice(" + device + "): Cannot set as active: "
537                               + "no state machine");
538                     return false;
539                 }
540                 if (sm.getConnectionState() != BluetoothProfile.STATE_CONNECTED) {
541                     Log.e(TAG, "setActiveDevice(" + device + "): Cannot set as active: "
542                               + "device is not connected");
543                     return false;
544                 }
545                 previousActiveDevice = mActiveDevice;
546             }
547 
548             // Switch from one A2DP to another A2DP device
549             if (DBG) {
550                 Log.d(TAG, "Switch A2DP devices to " + device + " from " + previousActiveDevice);
551             }
552             // This needs to happen before we inform the audio manager that the device
553             // disconnected. Please see comment in updateAndBroadcastActiveDevice() for why.
554             updateAndBroadcastActiveDevice(device);
555             // Make sure the Audio Manager knows the previous Active device is disconnected,
556             // and the new Active device is connected.
557             if (previousActiveDevice != null) {
558                 mAudioManager.setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(
559                         previousActiveDevice, BluetoothProfile.STATE_DISCONNECTED,
560                         BluetoothProfile.A2DP, true, -1);
561             }
562 
563             BluetoothDevice newActiveDevice = null;
564             synchronized (mStateMachines) {
565                 if (!mA2dpNativeInterface.setActiveDevice(device)) {
566                     Log.e(TAG, "setActiveDevice(" + device + "): Cannot set as active in native "
567                             + "layer");
568                     // Remove active device and stop playing audio.
569                     removeActiveDevice(true);
570                     return false;
571                 }
572                 // Send an intent with the active device codec config
573                 BluetoothCodecStatus codecStatus = sm.getCodecStatus();
574                 if (codecStatus != null) {
575                     broadcastCodecConfig(mActiveDevice, codecStatus);
576                 }
577                 newActiveDevice = mActiveDevice;
578             }
579 
580             // Tasks of Bluetooth are done, and now restore the AudioManager side.
581             int rememberedVolume = -1;
582             if (mFactory.getAvrcpTargetService() != null) {
583                 rememberedVolume = mFactory.getAvrcpTargetService()
584                         .getRememberedVolumeForDevice(newActiveDevice);
585             }
586             mAudioManager.setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(
587                     newActiveDevice, BluetoothProfile.STATE_CONNECTED, BluetoothProfile.A2DP,
588                     true, rememberedVolume);
589             // Inform the Audio Service about the codec configuration
590             // change, so the Audio Service can reset accordingly the audio
591             // feeding parameters in the Audio HAL to the Bluetooth stack.
592             mAudioManager.handleBluetoothA2dpDeviceConfigChange(newActiveDevice);
593         }
594         return true;
595     }
596 
597     /**
598      * Get the active device.
599      *
600      * @return the active device or null if no device is active
601      */
getActiveDevice()602     public BluetoothDevice getActiveDevice() {
603         synchronized (mStateMachines) {
604             return mActiveDevice;
605         }
606     }
607 
isActiveDevice(BluetoothDevice device)608     private boolean isActiveDevice(BluetoothDevice device) {
609         synchronized (mStateMachines) {
610             return (device != null) && Objects.equals(device, mActiveDevice);
611         }
612     }
613 
614     /**
615      * Set connection policy of the profile and connects it if connectionPolicy is
616      * {@link BluetoothProfile#CONNECTION_POLICY_ALLOWED} or disconnects if connectionPolicy is
617      * {@link BluetoothProfile#CONNECTION_POLICY_FORBIDDEN}
618      *
619      * <p> The device should already be paired.
620      * Connection policy can be one of:
621      * {@link BluetoothProfile#CONNECTION_POLICY_ALLOWED},
622      * {@link BluetoothProfile#CONNECTION_POLICY_FORBIDDEN},
623      * {@link BluetoothProfile#CONNECTION_POLICY_UNKNOWN}
624      *
625      * @param device Paired bluetooth device
626      * @param connectionPolicy is the connection policy to set to for this profile
627      * @return true if connectionPolicy is set, false on error
628      */
629     @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
setConnectionPolicy(BluetoothDevice device, int connectionPolicy)630     public boolean setConnectionPolicy(BluetoothDevice device, int connectionPolicy) {
631         enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED,
632                 "Need BLUETOOTH_PRIVILEGED permission");
633         if (DBG) {
634             Log.d(TAG, "Saved connectionPolicy " + device + " = " + connectionPolicy);
635         }
636 
637         if (!mDatabaseManager.setProfileConnectionPolicy(device, BluetoothProfile.A2DP,
638                   connectionPolicy)) {
639             return false;
640         }
641         if (connectionPolicy == BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
642             connect(device);
643         } else if (connectionPolicy == BluetoothProfile.CONNECTION_POLICY_FORBIDDEN) {
644             disconnect(device);
645         }
646         return true;
647     }
648 
649     /**
650      * Get the connection policy of the profile.
651      *
652      * <p> The connection policy can be any of:
653      * {@link BluetoothProfile#CONNECTION_POLICY_ALLOWED},
654      * {@link BluetoothProfile#CONNECTION_POLICY_FORBIDDEN},
655      * {@link BluetoothProfile#CONNECTION_POLICY_UNKNOWN}
656      *
657      * @param device Bluetooth device
658      * @return connection policy of the device
659      * @hide
660      */
getConnectionPolicy(BluetoothDevice device)661     public int getConnectionPolicy(BluetoothDevice device) {
662         return mDatabaseManager
663                 .getProfileConnectionPolicy(device, BluetoothProfile.A2DP);
664     }
665 
isAvrcpAbsoluteVolumeSupported()666     public boolean isAvrcpAbsoluteVolumeSupported() {
667         // TODO (apanicke): Add a hook here for the AvrcpTargetService.
668         return false;
669     }
670 
671 
setAvrcpAbsoluteVolume(int volume)672     public void setAvrcpAbsoluteVolume(int volume) {
673         // TODO (apanicke): Instead of using A2DP as a middleman for volume changes, add a binder
674         // service to the new AVRCP Profile and have the audio manager use that instead.
675         if (mFactory.getAvrcpTargetService() != null) {
676             mFactory.getAvrcpTargetService().sendVolumeChanged(volume);
677             return;
678         }
679     }
680 
isA2dpPlaying(BluetoothDevice device)681     boolean isA2dpPlaying(BluetoothDevice device) {
682         if (DBG) {
683             Log.d(TAG, "isA2dpPlaying(" + device + ")");
684         }
685         synchronized (mStateMachines) {
686             A2dpStateMachine sm = mStateMachines.get(device);
687             if (sm == null) {
688                 return false;
689             }
690             return sm.isPlaying();
691         }
692     }
693 
694     /**
695      * Gets the current codec status (configuration and capability).
696      *
697      * @param device the remote Bluetooth device. If null, use the current
698      * active A2DP Bluetooth device.
699      * @return the current codec status
700      * @hide
701      */
getCodecStatus(BluetoothDevice device)702     public BluetoothCodecStatus getCodecStatus(BluetoothDevice device) {
703         if (DBG) {
704             Log.d(TAG, "getCodecStatus(" + device + ")");
705         }
706         synchronized (mStateMachines) {
707             if (device == null) {
708                 device = mActiveDevice;
709             }
710             if (device == null) {
711                 return null;
712             }
713             A2dpStateMachine sm = mStateMachines.get(device);
714             if (sm != null) {
715                 return sm.getCodecStatus();
716             }
717             return null;
718         }
719     }
720 
721     /**
722      * Sets the codec configuration preference.
723      *
724      * @param device the remote Bluetooth device. If null, use the currect
725      * active A2DP Bluetooth device.
726      * @param codecConfig the codec configuration preference
727      * @hide
728      */
setCodecConfigPreference(BluetoothDevice device, BluetoothCodecConfig codecConfig)729     public void setCodecConfigPreference(BluetoothDevice device,
730                                          BluetoothCodecConfig codecConfig) {
731         if (DBG) {
732             Log.d(TAG, "setCodecConfigPreference(" + device + "): "
733                     + Objects.toString(codecConfig));
734         }
735         if (device == null) {
736             device = mActiveDevice;
737         }
738         if (device == null) {
739             Log.e(TAG, "setCodecConfigPreference: Invalid device");
740             return;
741         }
742         if (codecConfig == null) {
743             Log.e(TAG, "setCodecConfigPreference: Codec config can't be null");
744             return;
745         }
746         BluetoothCodecStatus codecStatus = getCodecStatus(device);
747         if (codecStatus == null) {
748             Log.e(TAG, "setCodecConfigPreference: Codec status is null");
749             return;
750         }
751         mA2dpCodecConfig.setCodecConfigPreference(device, codecStatus, codecConfig);
752     }
753 
754     /**
755      * Enables the optional codecs.
756      *
757      * @param device the remote Bluetooth device. If null, use the currect
758      * active A2DP Bluetooth device.
759      * @hide
760      */
enableOptionalCodecs(BluetoothDevice device)761     public void enableOptionalCodecs(BluetoothDevice device) {
762         if (DBG) {
763             Log.d(TAG, "enableOptionalCodecs(" + device + ")");
764         }
765         if (device == null) {
766             device = mActiveDevice;
767         }
768         if (device == null) {
769             Log.e(TAG, "enableOptionalCodecs: Invalid device");
770             return;
771         }
772         if (getSupportsOptionalCodecs(device) != BluetoothA2dp.OPTIONAL_CODECS_SUPPORTED) {
773             Log.e(TAG, "enableOptionalCodecs: No optional codecs");
774             return;
775         }
776         BluetoothCodecStatus codecStatus = getCodecStatus(device);
777         if (codecStatus == null) {
778             Log.e(TAG, "enableOptionalCodecs: Codec status is null");
779             return;
780         }
781         mA2dpCodecConfig.enableOptionalCodecs(device, codecStatus.getCodecConfig());
782     }
783 
784     /**
785      * Disables the optional codecs.
786      *
787      * @param device the remote Bluetooth device. If null, use the currect
788      * active A2DP Bluetooth device.
789      * @hide
790      */
disableOptionalCodecs(BluetoothDevice device)791     public void disableOptionalCodecs(BluetoothDevice device) {
792         if (DBG) {
793             Log.d(TAG, "disableOptionalCodecs(" + device + ")");
794         }
795         if (device == null) {
796             device = mActiveDevice;
797         }
798         if (device == null) {
799             Log.e(TAG, "disableOptionalCodecs: Invalid device");
800             return;
801         }
802         if (getSupportsOptionalCodecs(device) != BluetoothA2dp.OPTIONAL_CODECS_SUPPORTED) {
803             Log.e(TAG, "disableOptionalCodecs: No optional codecs");
804             return;
805         }
806         BluetoothCodecStatus codecStatus = getCodecStatus(device);
807         if (codecStatus == null) {
808             Log.e(TAG, "disableOptionalCodecs: Codec status is null");
809             return;
810         }
811         mA2dpCodecConfig.disableOptionalCodecs(device, codecStatus.getCodecConfig());
812     }
813 
814     /**
815      * Checks whether optional codecs are supported
816      *
817      * @param device is the remote bluetooth device.
818      * @return whether optional codecs are supported. Possible values are:
819      * {@link OptionalCodecsSupportStatus#OPTIONAL_CODECS_SUPPORTED},
820      * {@link OptionalCodecsSupportStatus#OPTIONAL_CODECS_NOT_SUPPORTED},
821      * {@link OptionalCodecsSupportStatus#OPTIONAL_CODECS_SUPPORT_UNKNOWN}.
822      */
getSupportsOptionalCodecs(BluetoothDevice device)823     public @OptionalCodecsSupportStatus int getSupportsOptionalCodecs(BluetoothDevice device) {
824         return mDatabaseManager.getA2dpSupportsOptionalCodecs(device);
825     }
826 
setSupportsOptionalCodecs(BluetoothDevice device, boolean doesSupport)827     public void setSupportsOptionalCodecs(BluetoothDevice device, boolean doesSupport) {
828         int value = doesSupport ? BluetoothA2dp.OPTIONAL_CODECS_SUPPORTED
829                 : BluetoothA2dp.OPTIONAL_CODECS_NOT_SUPPORTED;
830         mDatabaseManager.setA2dpSupportsOptionalCodecs(device, value);
831     }
832 
833     /**
834      * Checks whether optional codecs are enabled
835      *
836      * @param device is the remote bluetooth device
837      * @return whether the optional codecs are enabled. Possible values are:
838      * {@link OptionalCodecsPreferenceStatus#OPTIONAL_CODECS_PREF_ENABLED},
839      * {@link OptionalCodecsPreferenceStatus#OPTIONAL_CODECS_PREF_DISABLED},
840      * {@link OptionalCodecsPreferenceStatus#OPTIONAL_CODECS_PREF_UNKNOWN}.
841      */
getOptionalCodecsEnabled(BluetoothDevice device)842     public @OptionalCodecsPreferenceStatus int getOptionalCodecsEnabled(BluetoothDevice device) {
843         return mDatabaseManager.getA2dpOptionalCodecsEnabled(device);
844     }
845 
846     /**
847      * Sets the optional codecs to be set to the passed in value
848      *
849      * @param device is the remote bluetooth device
850      * @param value is the new status for the optional codecs. Possible values are:
851      * {@link OptionalCodecsPreferenceStatus#OPTIONAL_CODECS_PREF_ENABLED},
852      * {@link OptionalCodecsPreferenceStatus#OPTIONAL_CODECS_PREF_DISABLED},
853      * {@link OptionalCodecsPreferenceStatus#OPTIONAL_CODECS_PREF_UNKNOWN}.
854      */
setOptionalCodecsEnabled(BluetoothDevice device, @OptionalCodecsPreferenceStatus int value)855     public void setOptionalCodecsEnabled(BluetoothDevice device,
856             @OptionalCodecsPreferenceStatus int value) {
857         if (value != BluetoothA2dp.OPTIONAL_CODECS_PREF_UNKNOWN
858                 && value != BluetoothA2dp.OPTIONAL_CODECS_PREF_DISABLED
859                 && value != BluetoothA2dp.OPTIONAL_CODECS_PREF_ENABLED) {
860             Log.w(TAG, "Unexpected value passed to setOptionalCodecsEnabled:" + value);
861             return;
862         }
863         mDatabaseManager.setA2dpOptionalCodecsEnabled(device, value);
864     }
865 
866     /**
867      * Get dynamic audio buffer size supported type
868      *
869      * @return support <p>Possible values are
870      * {@link BluetoothA2dp#DYNAMIC_BUFFER_SUPPORT_NONE},
871      * {@link BluetoothA2dp#DYNAMIC_BUFFER_SUPPORT_A2DP_OFFLOAD},
872      * {@link BluetoothA2dp#DYNAMIC_BUFFER_SUPPORT_A2DP_SOFTWARE_ENCODING}.
873      */
874     @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
getDynamicBufferSupport()875     public int getDynamicBufferSupport() {
876         enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED,
877                 "Need BLUETOOTH_PRIVILEGED permission");
878         return mAdapterService.getDynamicBufferSupport();
879     }
880 
881     /**
882      * Get dynamic audio buffer size
883      *
884      * @return BufferConstraints
885      */
886     @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
getBufferConstraints()887     public BufferConstraints getBufferConstraints() {
888         enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED,
889                 "Need BLUETOOTH_PRIVILEGED permission");
890         return mAdapterService.getBufferConstraints();
891     }
892 
893     /**
894      * Set dynamic audio buffer size
895      *
896      * @param codec Audio codec
897      * @param value buffer millis
898      * @return true if the settings is successful, false otherwise
899      */
900     @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
setBufferLengthMillis(int codec, int value)901     public boolean setBufferLengthMillis(int codec, int value) {
902         enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED,
903                 "Need BLUETOOTH_PRIVILEGED permission");
904         return mAdapterService.setBufferLengthMillis(codec, value);
905     }
906 
907     // Handle messages from native (JNI) to Java
messageFromNative(A2dpStackEvent stackEvent)908     void messageFromNative(A2dpStackEvent stackEvent) {
909         Objects.requireNonNull(stackEvent.device,
910                                "Device should never be null, event: " + stackEvent);
911         synchronized (mStateMachines) {
912             BluetoothDevice device = stackEvent.device;
913             A2dpStateMachine sm = mStateMachines.get(device);
914             if (sm == null) {
915                 if (stackEvent.type == A2dpStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED) {
916                     switch (stackEvent.valueInt) {
917                         case A2dpStackEvent.CONNECTION_STATE_CONNECTED:
918                         case A2dpStackEvent.CONNECTION_STATE_CONNECTING:
919                             // Create a new state machine only when connecting to a device
920                             if (!connectionAllowedCheckMaxDevices(device)) {
921                                 Log.e(TAG, "Cannot connect to " + device
922                                         + " : too many connected devices");
923                                 return;
924                             }
925                             sm = getOrCreateStateMachine(device);
926                             break;
927                         default:
928                             break;
929                     }
930                 }
931             }
932             if (sm == null) {
933                 Log.e(TAG, "Cannot process stack event: no state machine: " + stackEvent);
934                 return;
935             }
936             sm.sendMessage(A2dpStateMachine.STACK_EVENT, stackEvent);
937         }
938     }
939 
940     /**
941      * The codec configuration for a device has been updated.
942      *
943      * @param device the remote device
944      * @param codecStatus the new codec status
945      * @param sameAudioFeedingParameters if true the audio feeding parameters
946      * haven't been changed
947      */
948     @VisibleForTesting
codecConfigUpdated(BluetoothDevice device, BluetoothCodecStatus codecStatus, boolean sameAudioFeedingParameters)949     public void codecConfigUpdated(BluetoothDevice device, BluetoothCodecStatus codecStatus,
950                             boolean sameAudioFeedingParameters) {
951         // Log codec config and capability metrics
952         BluetoothCodecConfig codecConfig = codecStatus.getCodecConfig();
953         int metricId = mAdapterService.getMetricId(device);
954         BluetoothStatsLog.write(BluetoothStatsLog.BLUETOOTH_A2DP_CODEC_CONFIG_CHANGED,
955                 mAdapterService.obfuscateAddress(device), codecConfig.getCodecType(),
956                 codecConfig.getCodecPriority(), codecConfig.getSampleRate(),
957                 codecConfig.getBitsPerSample(), codecConfig.getChannelMode(),
958                 codecConfig.getCodecSpecific1(), codecConfig.getCodecSpecific2(),
959                 codecConfig.getCodecSpecific3(), codecConfig.getCodecSpecific4(), metricId);
960         BluetoothCodecConfig[] codecCapabilities = codecStatus.getCodecsSelectableCapabilities();
961         for (BluetoothCodecConfig codecCapability : codecCapabilities) {
962             BluetoothStatsLog.write(BluetoothStatsLog.BLUETOOTH_A2DP_CODEC_CAPABILITY_CHANGED,
963                     mAdapterService.obfuscateAddress(device), codecCapability.getCodecType(),
964                     codecCapability.getCodecPriority(), codecCapability.getSampleRate(),
965                     codecCapability.getBitsPerSample(), codecCapability.getChannelMode(),
966                     codecConfig.getCodecSpecific1(), codecConfig.getCodecSpecific2(),
967                     codecConfig.getCodecSpecific3(), codecConfig.getCodecSpecific4(), metricId);
968         }
969 
970         broadcastCodecConfig(device, codecStatus);
971 
972         // Inform the Audio Service about the codec configuration change,
973         // so the Audio Service can reset accordingly the audio feeding
974         // parameters in the Audio HAL to the Bluetooth stack.
975         if (isActiveDevice(device) && !sameAudioFeedingParameters) {
976             mAudioManager.handleBluetoothA2dpDeviceConfigChange(device);
977         }
978     }
979 
getOrCreateStateMachine(BluetoothDevice device)980     private A2dpStateMachine getOrCreateStateMachine(BluetoothDevice device) {
981         if (device == null) {
982             Log.e(TAG, "getOrCreateStateMachine failed: device cannot be null");
983             return null;
984         }
985         synchronized (mStateMachines) {
986             A2dpStateMachine sm = mStateMachines.get(device);
987             if (sm != null) {
988                 return sm;
989             }
990             // Limit the maximum number of state machines to avoid DoS attack
991             if (mStateMachines.size() >= MAX_A2DP_STATE_MACHINES) {
992                 Log.e(TAG, "Maximum number of A2DP state machines reached: "
993                         + MAX_A2DP_STATE_MACHINES);
994                 return null;
995             }
996             if (DBG) {
997                 Log.d(TAG, "Creating a new state machine for " + device);
998             }
999             sm = A2dpStateMachine.make(device, this, mA2dpNativeInterface,
1000                                        mStateMachinesThread.getLooper());
1001             mStateMachines.put(device, sm);
1002             return sm;
1003         }
1004     }
1005 
1006     // This needs to run before any of the Audio Manager connection functions since
1007     // AVRCP needs to be aware that the audio device is changed before the Audio Manager
1008     // changes the volume of the output devices.
updateAndBroadcastActiveDevice(BluetoothDevice device)1009     private void updateAndBroadcastActiveDevice(BluetoothDevice device) {
1010         if (DBG) {
1011             Log.d(TAG, "updateAndBroadcastActiveDevice(" + device + ")");
1012         }
1013 
1014         // Make sure volume has been store before device been remove from active.
1015         if (mFactory.getAvrcpTargetService() != null) {
1016             mFactory.getAvrcpTargetService().volumeDeviceSwitched(device);
1017         }
1018         synchronized (mStateMachines) {
1019             mActiveDevice = device;
1020         }
1021 
1022         BluetoothStatsLog.write(BluetoothStatsLog.BLUETOOTH_ACTIVE_DEVICE_CHANGED,
1023                 BluetoothProfile.A2DP, mAdapterService.obfuscateAddress(device),
1024                 mAdapterService.getMetricId(device));
1025         Intent intent = new Intent(BluetoothA2dp.ACTION_ACTIVE_DEVICE_CHANGED);
1026         intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
1027         intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
1028                         | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
1029         sendBroadcast(intent, BLUETOOTH_CONNECT, Utils.getTempAllowlistBroadcastOptions());
1030     }
1031 
broadcastCodecConfig(BluetoothDevice device, BluetoothCodecStatus codecStatus)1032     private void broadcastCodecConfig(BluetoothDevice device, BluetoothCodecStatus codecStatus) {
1033         if (DBG) {
1034             Log.d(TAG, "broadcastCodecConfig(" + device + "): " + codecStatus);
1035         }
1036         Intent intent = new Intent(BluetoothA2dp.ACTION_CODEC_CONFIG_CHANGED);
1037         intent.putExtra(BluetoothCodecStatus.EXTRA_CODEC_STATUS, codecStatus);
1038         intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
1039         intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
1040                         | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
1041         sendBroadcast(intent, BLUETOOTH_CONNECT, Utils.getTempAllowlistBroadcastOptions());
1042     }
1043 
1044     private class BondStateChangedReceiver extends BroadcastReceiver {
1045         @Override
onReceive(Context context, Intent intent)1046         public void onReceive(Context context, Intent intent) {
1047             if (!BluetoothDevice.ACTION_BOND_STATE_CHANGED.equals(intent.getAction())) {
1048                 return;
1049             }
1050             int state = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE,
1051                                            BluetoothDevice.ERROR);
1052             BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
1053             Objects.requireNonNull(device, "ACTION_BOND_STATE_CHANGED with no EXTRA_DEVICE");
1054             bondStateChanged(device, state);
1055         }
1056     }
1057 
1058     /**
1059      * Process a change in the bonding state for a device.
1060      *
1061      * @param device the device whose bonding state has changed
1062      * @param bondState the new bond state for the device. Possible values are:
1063      * {@link BluetoothDevice#BOND_NONE},
1064      * {@link BluetoothDevice#BOND_BONDING},
1065      * {@link BluetoothDevice#BOND_BONDED}.
1066      */
1067     @VisibleForTesting
bondStateChanged(BluetoothDevice device, int bondState)1068     void bondStateChanged(BluetoothDevice device, int bondState) {
1069         if (DBG) {
1070             Log.d(TAG, "Bond state changed for device: " + device + " state: " + bondState);
1071         }
1072         // Remove state machine if the bonding for a device is removed
1073         if (bondState != BluetoothDevice.BOND_NONE) {
1074             return;
1075         }
1076         synchronized (mStateMachines) {
1077             A2dpStateMachine sm = mStateMachines.get(device);
1078             if (sm == null) {
1079                 return;
1080             }
1081             if (sm.getConnectionState() != BluetoothProfile.STATE_DISCONNECTED) {
1082                 return;
1083             }
1084         }
1085         if (mFactory.getAvrcpTargetService() != null) {
1086             mFactory.getAvrcpTargetService().removeStoredVolumeForDevice(device);
1087         }
1088         removeStateMachine(device);
1089     }
1090 
removeStateMachine(BluetoothDevice device)1091     private void removeStateMachine(BluetoothDevice device) {
1092         synchronized (mStateMachines) {
1093             A2dpStateMachine sm = mStateMachines.get(device);
1094             if (sm == null) {
1095                 Log.w(TAG, "removeStateMachine: device " + device
1096                         + " does not have a state machine");
1097                 return;
1098             }
1099             Log.i(TAG, "removeStateMachine: removing state machine for device: " + device);
1100             sm.doQuit();
1101             sm.cleanup();
1102             mStateMachines.remove(device);
1103         }
1104     }
1105 
1106 
1107     /**
1108      * Update and initiate optional codec status change to native.
1109      *
1110      * @param device the device to change optional codec status
1111      */
1112     @VisibleForTesting
updateOptionalCodecsSupport(BluetoothDevice device)1113     public void updateOptionalCodecsSupport(BluetoothDevice device) {
1114         int previousSupport = getSupportsOptionalCodecs(device);
1115         boolean supportsOptional = false;
1116         boolean hasMandatoryCodec = false;
1117 
1118         synchronized (mStateMachines) {
1119             A2dpStateMachine sm = mStateMachines.get(device);
1120             if (sm == null) {
1121                 return;
1122             }
1123             BluetoothCodecStatus codecStatus = sm.getCodecStatus();
1124             if (codecStatus != null) {
1125                 for (BluetoothCodecConfig config : codecStatus.getCodecsSelectableCapabilities()) {
1126                     if (config.isMandatoryCodec()) {
1127                         hasMandatoryCodec = true;
1128                     } else {
1129                         supportsOptional = true;
1130                     }
1131                 }
1132             }
1133         }
1134         if (!hasMandatoryCodec) {
1135             // Mandatory codec(SBC) is not selectable. It could be caused by the remote device
1136             // select codec before native finish get codec capabilities. Stop use this codec
1137             // status as the reference to support/enable optional codecs.
1138             Log.i(TAG, "updateOptionalCodecsSupport: Mandatory codec is not selectable.");
1139             return;
1140         }
1141 
1142         if (previousSupport == BluetoothA2dp.OPTIONAL_CODECS_SUPPORT_UNKNOWN
1143                 || supportsOptional != (previousSupport
1144                                     == BluetoothA2dp.OPTIONAL_CODECS_SUPPORTED)) {
1145             setSupportsOptionalCodecs(device, supportsOptional);
1146         }
1147         if (supportsOptional) {
1148             int enabled = getOptionalCodecsEnabled(device);
1149             switch (enabled) {
1150                 case BluetoothA2dp.OPTIONAL_CODECS_PREF_UNKNOWN:
1151                     // Enable optional codec by default.
1152                     setOptionalCodecsEnabled(device, BluetoothA2dp.OPTIONAL_CODECS_PREF_ENABLED);
1153                     // Fall through intended
1154                 case BluetoothA2dp.OPTIONAL_CODECS_PREF_ENABLED:
1155                     enableOptionalCodecs(device);
1156                     break;
1157                 case BluetoothA2dp.OPTIONAL_CODECS_PREF_DISABLED:
1158                     disableOptionalCodecs(device);
1159                     break;
1160             }
1161         }
1162     }
1163 
connectionStateChanged(BluetoothDevice device, int fromState, int toState)1164     private void connectionStateChanged(BluetoothDevice device, int fromState, int toState) {
1165         if ((device == null) || (fromState == toState)) {
1166             return;
1167         }
1168         if (toState == BluetoothProfile.STATE_CONNECTED) {
1169             MetricsLogger.logProfileConnectionEvent(BluetoothMetricsProto.ProfileId.A2DP);
1170         }
1171         // Set the active device if only one connected device is supported and it was connected
1172         if (toState == BluetoothProfile.STATE_CONNECTED && (mMaxConnectedAudioDevices == 1)) {
1173             setActiveDevice(device);
1174         }
1175         // Check if the active device is not connected anymore
1176         if (isActiveDevice(device) && (fromState == BluetoothProfile.STATE_CONNECTED)) {
1177             setActiveDevice(null);
1178         }
1179         // Check if the device is disconnected - if unbond, remove the state machine
1180         if (toState == BluetoothProfile.STATE_DISCONNECTED) {
1181             if (mAdapterService.getBondState(device) == BluetoothDevice.BOND_NONE) {
1182                 if (mFactory.getAvrcpTargetService() != null) {
1183                     mFactory.getAvrcpTargetService().removeStoredVolumeForDevice(device);
1184                 }
1185                 removeStateMachine(device);
1186             }
1187         }
1188     }
1189 
1190     /**
1191      * Receiver for processing device connection state changes.
1192      *
1193      * <ul>
1194      * <li> Update codec support per device when device is (re)connected
1195      * <li> Delete the state machine instance if the device is disconnected and unbond
1196      * </ul>
1197      */
1198     private class ConnectionStateChangedReceiver extends BroadcastReceiver {
1199         @Override
onReceive(Context context, Intent intent)1200         public void onReceive(Context context, Intent intent) {
1201             if (!BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED.equals(intent.getAction())) {
1202                 return;
1203             }
1204             BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
1205             int toState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1);
1206             int fromState = intent.getIntExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, -1);
1207             connectionStateChanged(device, fromState, toState);
1208         }
1209     }
1210 
1211     /**
1212      * Binder object: must be a static class or memory leak may occur.
1213      */
1214     @VisibleForTesting
1215     static class BluetoothA2dpBinder extends IBluetoothA2dp.Stub
1216             implements IProfileServiceBinder {
1217         private A2dpService mService;
1218 
1219         @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
getService(AttributionSource source)1220         private A2dpService getService(AttributionSource source) {
1221             if (!Utils.checkCallerIsSystemOrActiveUser(TAG)
1222                     || !Utils.checkServiceAvailable(mService, TAG)
1223                     || !Utils.checkConnectPermissionForDataDelivery(mService, source, TAG)) {
1224                 return null;
1225             }
1226             return mService;
1227         }
1228 
BluetoothA2dpBinder(A2dpService svc)1229         BluetoothA2dpBinder(A2dpService svc) {
1230             mService = svc;
1231         }
1232 
1233         @Override
cleanup()1234         public void cleanup() {
1235             mService = null;
1236         }
1237 
1238         @Override
connect(BluetoothDevice device)1239         public boolean connect(BluetoothDevice device) {
1240             return connectWithAttribution(device, Utils.getCallingAttributionSource());
1241         }
1242 
1243         @Override
connectWithAttribution(BluetoothDevice device, AttributionSource source)1244         public boolean connectWithAttribution(BluetoothDevice device, AttributionSource source) {
1245             Attributable.setAttributionSource(device, source);
1246             A2dpService service = getService(source);
1247             if (service == null) {
1248                 return false;
1249             }
1250             return service.connect(device);
1251         }
1252 
1253         @Override
disconnect(BluetoothDevice device)1254         public boolean disconnect(BluetoothDevice device) {
1255             return disconnectWithAttribution(device, Utils.getCallingAttributionSource());
1256         }
1257 
1258         @Override
disconnectWithAttribution(BluetoothDevice device, AttributionSource source)1259         public boolean disconnectWithAttribution(BluetoothDevice device, AttributionSource source) {
1260             Attributable.setAttributionSource(device, source);
1261             A2dpService service = getService(source);
1262             if (service == null) {
1263                 return false;
1264             }
1265             return service.disconnect(device);
1266         }
1267 
1268         @Override
getConnectedDevices()1269         public List<BluetoothDevice> getConnectedDevices() {
1270             return getConnectedDevicesWithAttribution(Utils.getCallingAttributionSource());
1271         }
1272 
1273         @Override
getConnectedDevicesWithAttribution(AttributionSource source)1274         public List<BluetoothDevice> getConnectedDevicesWithAttribution(AttributionSource source) {
1275             A2dpService service = getService(source);
1276             if (service == null) {
1277                 return new ArrayList<>(0);
1278             }
1279             return service.getConnectedDevices();
1280         }
1281 
1282         @Override
getDevicesMatchingConnectionStates(int[] states)1283         public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
1284             return getDevicesMatchingConnectionStatesWithAttribution(states,
1285                     Utils.getCallingAttributionSource());
1286         }
1287 
1288         @Override
getDevicesMatchingConnectionStatesWithAttribution(int[] states, AttributionSource source)1289         public List<BluetoothDevice> getDevicesMatchingConnectionStatesWithAttribution(int[] states,
1290                 AttributionSource source) {
1291             A2dpService service = getService(source);
1292             if (service == null) {
1293                 return new ArrayList<>(0);
1294             }
1295             return service.getDevicesMatchingConnectionStates(states);
1296         }
1297 
1298         @Override
getConnectionState(BluetoothDevice device)1299         public int getConnectionState(BluetoothDevice device) {
1300             return getConnectionStateWithAttribution(device, Utils.getCallingAttributionSource());
1301         }
1302 
1303         @Override
getConnectionStateWithAttribution(BluetoothDevice device, AttributionSource source)1304         public int getConnectionStateWithAttribution(BluetoothDevice device,
1305                 AttributionSource source) {
1306             Attributable.setAttributionSource(device, source);
1307             A2dpService service = getService(source);
1308             if (service == null) {
1309                 return BluetoothProfile.STATE_DISCONNECTED;
1310             }
1311             return service.getConnectionState(device);
1312         }
1313 
1314         @Override
setActiveDevice(BluetoothDevice device, AttributionSource source)1315         public boolean setActiveDevice(BluetoothDevice device, AttributionSource source) {
1316             Attributable.setAttributionSource(device, source);
1317             A2dpService service = getService(source);
1318             if (service == null) {
1319                 return false;
1320             }
1321             return service.setActiveDevice(device);
1322         }
1323 
1324         @Override
getActiveDevice(AttributionSource source)1325         public BluetoothDevice getActiveDevice(AttributionSource source) {
1326             A2dpService service = getService(source);
1327             if (service == null) {
1328                 return null;
1329             }
1330             return service.getActiveDevice();
1331         }
1332 
1333         @Override
setConnectionPolicy(BluetoothDevice device, int connectionPolicy, AttributionSource source)1334         public boolean setConnectionPolicy(BluetoothDevice device, int connectionPolicy,
1335                 AttributionSource source) {
1336             Attributable.setAttributionSource(device, source);
1337             A2dpService service = getService(source);
1338             if (service == null) {
1339                 return false;
1340             }
1341             return service.setConnectionPolicy(device, connectionPolicy);
1342         }
1343 
1344         @Override
getPriority(BluetoothDevice device, AttributionSource source)1345         public int getPriority(BluetoothDevice device, AttributionSource source) {
1346             Attributable.setAttributionSource(device, source);
1347             A2dpService service = getService(source);
1348             if (service == null) {
1349                 return BluetoothProfile.CONNECTION_POLICY_UNKNOWN;
1350             }
1351             return service.getConnectionPolicy(device);
1352         }
1353 
1354         @Override
getConnectionPolicy(BluetoothDevice device, AttributionSource source)1355         public int getConnectionPolicy(BluetoothDevice device, AttributionSource source) {
1356             Attributable.setAttributionSource(device, source);
1357             A2dpService service = getService(source);
1358             if (service == null) {
1359                 return BluetoothProfile.CONNECTION_POLICY_UNKNOWN;
1360             }
1361             enforceBluetoothPrivilegedPermission(service);
1362             return service.getConnectionPolicy(device);
1363         }
1364 
1365         @Override
isAvrcpAbsoluteVolumeSupported()1366         public boolean isAvrcpAbsoluteVolumeSupported() {
1367             // TODO (apanicke): Add a hook here for the AvrcpTargetService.
1368             return false;
1369         }
1370 
1371         @Override
setAvrcpAbsoluteVolume(int volume, AttributionSource source)1372         public void setAvrcpAbsoluteVolume(int volume, AttributionSource source) {
1373             A2dpService service = getService(source);
1374             if (service == null) {
1375                 return;
1376             }
1377             service.setAvrcpAbsoluteVolume(volume);
1378         }
1379 
1380         @Override
isA2dpPlaying(BluetoothDevice device, AttributionSource source)1381         public boolean isA2dpPlaying(BluetoothDevice device, AttributionSource source) {
1382             Attributable.setAttributionSource(device, source);
1383             A2dpService service = getService(source);
1384             if (service == null) {
1385                 return false;
1386             }
1387             return service.isA2dpPlaying(device);
1388         }
1389 
1390         @Override
getCodecStatus(BluetoothDevice device, AttributionSource source)1391         public BluetoothCodecStatus getCodecStatus(BluetoothDevice device,
1392                 AttributionSource source) {
1393             Attributable.setAttributionSource(device, source);
1394             A2dpService service = getService(source);
1395             if (service == null) {
1396                 return null;
1397             }
1398             return service.getCodecStatus(device);
1399         }
1400 
1401         @Override
setCodecConfigPreference(BluetoothDevice device, BluetoothCodecConfig codecConfig, AttributionSource source)1402         public void setCodecConfigPreference(BluetoothDevice device,
1403                 BluetoothCodecConfig codecConfig, AttributionSource source) {
1404             Attributable.setAttributionSource(device, source);
1405             A2dpService service = getService(source);
1406             if (service == null) {
1407                 return;
1408             }
1409             service.setCodecConfigPreference(device, codecConfig);
1410         }
1411 
1412         @Override
enableOptionalCodecs(BluetoothDevice device, AttributionSource source)1413         public void enableOptionalCodecs(BluetoothDevice device, AttributionSource source) {
1414             Attributable.setAttributionSource(device, source);
1415             A2dpService service = getService(source);
1416             if (service == null) {
1417                 return;
1418             }
1419             service.enableOptionalCodecs(device);
1420         }
1421 
1422         @Override
disableOptionalCodecs(BluetoothDevice device, AttributionSource source)1423         public void disableOptionalCodecs(BluetoothDevice device, AttributionSource source) {
1424             Attributable.setAttributionSource(device, source);
1425             A2dpService service = getService(source);
1426             if (service == null) {
1427                 return;
1428             }
1429             service.disableOptionalCodecs(device);
1430         }
1431 
1432         @Override
supportsOptionalCodecs(BluetoothDevice device, AttributionSource source)1433         public int supportsOptionalCodecs(BluetoothDevice device, AttributionSource source) {
1434             Attributable.setAttributionSource(device, source);
1435             A2dpService service = getService(source);
1436             if (service == null) {
1437                 return BluetoothA2dp.OPTIONAL_CODECS_SUPPORT_UNKNOWN;
1438             }
1439             return service.getSupportsOptionalCodecs(device);
1440         }
1441 
1442         @Override
getOptionalCodecsEnabled(BluetoothDevice device, AttributionSource source)1443         public int getOptionalCodecsEnabled(BluetoothDevice device, AttributionSource source) {
1444             Attributable.setAttributionSource(device, source);
1445             A2dpService service = getService(source);
1446             if (service == null) {
1447                 return BluetoothA2dp.OPTIONAL_CODECS_PREF_UNKNOWN;
1448             }
1449             return service.getOptionalCodecsEnabled(device);
1450         }
1451 
1452         @Override
setOptionalCodecsEnabled(BluetoothDevice device, int value, AttributionSource source)1453         public void setOptionalCodecsEnabled(BluetoothDevice device, int value,
1454                 AttributionSource source) {
1455             Attributable.setAttributionSource(device, source);
1456             A2dpService service = getService(source);
1457             if (service == null) {
1458                 return;
1459             }
1460             service.setOptionalCodecsEnabled(device, value);
1461         }
1462 
1463         @Override
getDynamicBufferSupport(AttributionSource source)1464         public int getDynamicBufferSupport(AttributionSource source) {
1465             A2dpService service = getService(source);
1466             if (service == null) {
1467                 return BluetoothA2dp.DYNAMIC_BUFFER_SUPPORT_NONE;
1468             }
1469             return service.getDynamicBufferSupport();
1470         }
1471 
1472         @Override
getBufferConstraints(AttributionSource source)1473         public BufferConstraints getBufferConstraints(AttributionSource source) {
1474             A2dpService service = getService(source);
1475             if (service == null) {
1476                 return null;
1477             }
1478             return service.getBufferConstraints();
1479         }
1480 
1481         @Override
setBufferLengthMillis(int codec, int value, AttributionSource source)1482         public boolean setBufferLengthMillis(int codec, int value, AttributionSource source) {
1483             A2dpService service = getService(source);
1484             if (service == null) {
1485                 return false;
1486             }
1487             return service.setBufferLengthMillis(codec, value);
1488         }
1489     }
1490 
1491     @Override
dump(StringBuilder sb)1492     public void dump(StringBuilder sb) {
1493         super.dump(sb);
1494         ProfileService.println(sb, "mActiveDevice: " + mActiveDevice);
1495         ProfileService.println(sb, "mMaxConnectedAudioDevices: " + mMaxConnectedAudioDevices);
1496         if (mA2dpCodecConfig != null) {
1497             ProfileService.println(sb, "codecConfigPriorities:");
1498             for (BluetoothCodecConfig codecConfig : mA2dpCodecConfig.codecConfigPriorities()) {
1499                 ProfileService.println(sb, "  " + codecConfig.getCodecName() + ": "
1500                         + codecConfig.getCodecPriority());
1501             }
1502             ProfileService.println(sb, "mA2dpOffloadEnabled: " + mA2dpOffloadEnabled);
1503             if (mA2dpOffloadEnabled) {
1504                 ProfileService.println(sb, "codecConfigOffloading:");
1505                 for (BluetoothCodecConfig codecConfig : mA2dpCodecConfig.codecConfigOffloading()) {
1506                     ProfileService.println(sb, "  " + codecConfig);
1507                 }
1508             }
1509         } else {
1510             ProfileService.println(sb, "mA2dpCodecConfig: null");
1511         }
1512         for (A2dpStateMachine sm : mStateMachines.values()) {
1513             sm.dump(sb);
1514         }
1515     }
1516 }
1517