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