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