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