Enable bluetooth a2dpsink to request audio focus from source
- Allow source to initiate play on IoT devices without having audiofocus
first
- Added mockPackageManager and testSrcPlayIot to A2dpSinkStreamHandler
unit test
Test: Bluetooth a2dp sink works using the sample-audio-bluetooth
sample app
Bug: 38098044
Change-Id: I98472c857afa4922ae9ce118c1bcf0f801ffd304
diff --git a/android/app/src/com/android/bluetooth/a2dpsink/A2dpSinkStreamHandler.java b/android/app/src/com/android/bluetooth/a2dpsink/A2dpSinkStreamHandler.java
index 33fe1ec..eb3a827 100644
--- a/android/app/src/com/android/bluetooth/a2dpsink/A2dpSinkStreamHandler.java
+++ b/android/app/src/com/android/bluetooth/a2dpsink/A2dpSinkStreamHandler.java
@@ -17,6 +17,7 @@
package com.android.bluetooth.a2dpsink;
import android.content.Context;
+import android.content.pm.PackageManager;
import android.media.AudioAttributes;
import android.media.AudioManager;
import android.media.AudioManager.OnAudioFocusChangeListener;
@@ -131,7 +132,16 @@
break;
case SRC_PLAY:
- // Remote play command, if we have audio focus update avrcp, otherwise send pause.
+ // Remote play command.
+ // If is an iot device gain focus and start avrcp updates.
+ if (isIotDevice()) {
+ if (mAudioFocus == AudioManager.AUDIOFOCUS_NONE) {
+ requestAudioFocus();
+ }
+ startAvrcpUpdates();
+ break;
+ }
+ // Otherwise, pause if we don't have focus
if (mAudioFocus == AudioManager.AUDIOFOCUS_NONE) {
sendAvrcpPause();
} else {
@@ -311,4 +321,8 @@
Log.e(TAG, "Passthrough not sent, connection un-available.");
}
}
+
+ private boolean isIotDevice() {
+ return mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_EMBEDDED);
+ }
}
diff --git a/android/app/tests/src/com/android/bluetooth/a2dpsink/A2dpSinkStreamHandlerTest.java b/android/app/tests/src/com/android/bluetooth/a2dpsink/A2dpSinkStreamHandlerTest.java
index 2f18960..c611213 100644
--- a/android/app/tests/src/com/android/bluetooth/a2dpsink/A2dpSinkStreamHandlerTest.java
+++ b/android/app/tests/src/com/android/bluetooth/a2dpsink/A2dpSinkStreamHandlerTest.java
@@ -27,6 +27,7 @@
import static org.mockito.Mockito.when;
import android.content.Context;
+import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.media.AudioManager;
import android.media.AudioManager.OnAudioFocusChangeListener;
@@ -56,6 +57,8 @@
@Mock Resources mockResources;
+ @Mock PackageManager mockPackageManager;
+
@Before
public void setUp() {
// Mock the looper
@@ -77,6 +80,8 @@
when(mockAudioManager.abandonAudioFocus(any())).thenReturn(AudioManager.AUDIOFOCUS_GAIN);
doNothing().when(mockA2dpSink).informAudioTrackGainNative(anyFloat());
when(mockContext.getMainLooper()).thenReturn(mHandlerThread.getLooper());
+ when(mockContext.getPackageManager()).thenReturn(mockPackageManager);
+ when(mockPackageManager.hasSystemFeature(any())).thenReturn(false);
streamHandler = spy(new A2dpSinkStreamHandler(mockA2dpSink, mockContext));
}
@@ -138,6 +143,16 @@
}
@Test
+ public void testSrcPlayIot() {
+ // Play was pressed remotely for an iot device, expect streaming to start.
+ when(mockPackageManager.hasSystemFeature(any())).thenReturn(true);
+ streamHandler.handleMessage(streamHandler.obtainMessage(A2dpSinkStreamHandler.SRC_PLAY));
+ verify(mockAudioManager, times(1)).requestAudioFocus(any(), anyInt(), anyInt());
+ verify(mockA2dpSink, times(1)).informAudioFocusStateNative(1);
+ verify(mockA2dpSink, times(1)).informAudioTrackGainNative(1.0f);
+ }
+
+ @Test
public void testSrcPause() {
// Play was pressed locally, expect streaming to start.
streamHandler.handleMessage(streamHandler.obtainMessage(A2dpSinkStreamHandler.SRC_PLAY));