diff --git a/android/app/src/com/android/bluetooth/a2dp/A2dpService.java b/android/app/src/com/android/bluetooth/a2dp/A2dpService.java index cc9853764b388081e59e92b52103023e70b5be6e..f22be4abae32643c5260220b99b7407bfca9d3cf 100644 --- a/android/app/src/com/android/bluetooth/a2dp/A2dpService.java +++ b/android/app/src/com/android/bluetooth/a2dp/A2dpService.java @@ -41,7 +41,6 @@ import android.bluetooth.BufferConstraints; import android.bluetooth.IBluetoothA2dp; import android.companion.CompanionDeviceManager; import android.content.AttributionSource; -import android.content.Context; import android.content.Intent; import android.media.AudioDeviceCallback; import android.media.AudioDeviceInfo; @@ -90,18 +89,18 @@ public class A2dpService extends ProfileService { private static A2dpService sA2dpService; - private AdapterService mAdapterService; - private DatabaseManager mDatabaseManager; + private final A2dpNativeInterface mNativeInterface; + private final AdapterService mAdapterService; + private final AudioManager mAudioManager; + private final DatabaseManager mDatabaseManager; + private final CompanionDeviceManager mCompanionDeviceManager; + private HandlerThread mStateMachinesThread; private Handler mHandler = null; - private final A2dpNativeInterface mNativeInterface; @VisibleForTesting ServiceFactory mFactory = new ServiceFactory(); - @VisibleForTesting - AudioManager mAudioManager; private A2dpCodecConfig mA2dpCodecConfig; - private CompanionDeviceManager mCompanionDeviceManager; @GuardedBy("mStateMachines") private BluetoothDevice mActiveDevice; @@ -126,15 +125,20 @@ public class A2dpService extends ProfileService { private final AudioManagerAudioDeviceCallback mAudioManagerAudioDeviceCallback = new AudioManagerAudioDeviceCallback(); - public A2dpService(Context ctx) { - super(ctx); - mNativeInterface = requireNonNull(A2dpNativeInterface.getInstance()); + public A2dpService(AdapterService adapterService) { + this(adapterService, A2dpNativeInterface.getInstance()); } @VisibleForTesting - A2dpService(Context ctx, A2dpNativeInterface nativeInterface) { - super(ctx); + A2dpService(AdapterService adapterService, A2dpNativeInterface nativeInterface) { + super(requireNonNull(adapterService)); + mAdapterService = adapterService; mNativeInterface = requireNonNull(nativeInterface); + mDatabaseManager = requireNonNull(mAdapterService.getDatabase()); + mAudioManager = requireNonNull(getSystemService(AudioManager.class)); + + // Some platform may not have the FEATURE_COMPANION_DEVICE_SETUP + mCompanionDeviceManager = getSystemService(CompanionDeviceManager.class); } public static boolean isEnabled() { @@ -157,20 +161,6 @@ public class A2dpService extends ProfileService { throw new IllegalStateException("start() called twice"); } - // Step 1: Get AdapterService, DatabaseManager, AudioManager. - // None of them can be null. - mAdapterService = - requireNonNull( - AdapterService.getAdapterService(), - "AdapterService cannot be null when A2dpService starts"); - mDatabaseManager = - requireNonNull( - mAdapterService.getDatabase(), - "DatabaseManager cannot be null when A2dpService starts"); - mAudioManager = getSystemService(AudioManager.class); - mCompanionDeviceManager = getSystemService(CompanionDeviceManager.class); - requireNonNull(mAudioManager, "AudioManager cannot be null when A2dpService starts"); - // Step 2: Get maximum number of connected audio devices mMaxConnectedAudioDevices = mAdapterService.getMaxConnectedAudioDevices(); Log.i(TAG, "Max connected audio devices set to " + mMaxConnectedAudioDevices); @@ -255,10 +245,6 @@ public class A2dpService extends ProfileService { // Step 2: Reset maximum number of connected audio devices mMaxConnectedAudioDevices = 1; - - // Step 1: Clear AdapterService, AudioManager - mAudioManager = null; - mAdapterService = null; } @Override @@ -1608,6 +1594,12 @@ public class A2dpService extends ProfileService { return; } if (!hasBluetoothPrivilegedPermission(service)) { + if (service.mCompanionDeviceManager == null) { + throw new SecurityException( + "Caller should have BLUETOOTH_PRIVILEGED in order to call" + + " setCodecConfigPreference without a CompanionDeviceManager" + + " service"); + } enforceCdmAssociation(service.mCompanionDeviceManager, service, source.getPackageName(), Binder.getCallingUid(), device); } diff --git a/android/app/tests/unit/src/com/android/bluetooth/a2dp/A2dpServiceTest.java b/android/app/tests/unit/src/com/android/bluetooth/a2dp/A2dpServiceTest.java index 6dacf8fe8c0d6e7e56c9e43a287c26863429a127..faa2833dfcefc09c64fcaae7371322a7a3a6dea7 100644 --- a/android/app/tests/unit/src/com/android/bluetooth/a2dp/A2dpServiceTest.java +++ b/android/app/tests/unit/src/com/android/bluetooth/a2dp/A2dpServiceTest.java @@ -76,7 +76,6 @@ public class A2dpServiceTest { @Mock private ActiveDeviceManager mActiveDeviceManager; @Mock private AdapterService mAdapterService; @Mock private AudioManager mAudioManager; - @Mock private Context mContext; @Mock private DatabaseManager mDatabaseManager; @Mock private SilenceDeviceManager mSilenceDeviceManager; private InOrder mInOrder = null; @@ -87,19 +86,18 @@ public class A2dpServiceTest { public void setUp() throws Exception { // Set up mocks and test assets MockitoAnnotations.initMocks(this); - mInOrder = inOrder(mContext); + mInOrder = inOrder(mAdapterService); TestUtils.mockGetSystemService( - mContext, Context.AUDIO_SERVICE, AudioManager.class, mAudioManager); + mAdapterService, Context.AUDIO_SERVICE, AudioManager.class, mAudioManager); doReturn(InstrumentationRegistry.getTargetContext().getResources()) - .when(mContext) + .when(mAdapterService) .getResources(); if (Looper.myLooper() == null) { Looper.prepare(); } - TestUtils.setAdapterService(mAdapterService); doReturn(true).when(mAdapterService).isA2dpOffloadEnabled(); doReturn(MAX_CONNECTED_AUDIO_DEVICES).when(mAdapterService).getMaxConnectedAudioDevices(); doReturn(false).when(mAdapterService).isQuietModeEnabled(); @@ -107,7 +105,7 @@ public class A2dpServiceTest { doReturn(mActiveDeviceManager).when(mAdapterService).getActiveDeviceManager(); doReturn(mSilenceDeviceManager).when(mAdapterService).getSilenceDeviceManager(); - mA2dpService = new A2dpService(mContext, mMockNativeInterface); + mA2dpService = new A2dpService(mAdapterService, mMockNativeInterface); mA2dpService.start(); mA2dpService.setAvailable(true); @@ -126,12 +124,11 @@ public class A2dpServiceTest { @After public void tearDown() throws Exception { mA2dpService.stop(); - TestUtils.clearAdapterService(mAdapterService); } @SafeVarargs private void verifyIntentSent(Matcher<Intent>... matchers) { - mInOrder.verify(mContext, timeout(TIMEOUT.toMillis() * 2)) + mInOrder.verify(mAdapterService, timeout(TIMEOUT.toMillis() * 2)) .sendBroadcast(MockitoHamcrest.argThat(AllOf.allOf(matchers)), any(), any()); } @@ -1061,7 +1058,7 @@ public class A2dpServiceTest { stackEvent.valueInt = newConnectionState; mA2dpService.messageFromNative(stackEvent); // Verify the connection state broadcast - mInOrder.verify(mContext, timeout(TIMEOUT.toMillis()).times(0)) + mInOrder.verify(mAdapterService, timeout(TIMEOUT.toMillis()).times(0)) .sendBroadcast(any(), any(), any()); } @@ -1088,7 +1085,7 @@ public class A2dpServiceTest { stackEvent.valueInt = audioStackEvent; mA2dpService.messageFromNative(stackEvent); // Verify the audio state broadcast - mInOrder.verify(mContext, timeout(TIMEOUT.toMillis()).times(0)) + mInOrder.verify(mAdapterService, timeout(TIMEOUT.toMillis()).times(0)) .sendBroadcast(any(), any(), any()); } @@ -1113,7 +1110,7 @@ public class A2dpServiceTest { stackEvent.codecStatus = codecStatus; mA2dpService.messageFromNative(stackEvent); // Verify the codec status broadcast - mInOrder.verify(mContext, timeout(TIMEOUT.toMillis()).times(0)) + mInOrder.verify(mAdapterService, timeout(TIMEOUT.toMillis()).times(0)) .sendBroadcast(any(), any(), any()); }