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