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