diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java index 26f5c4ca1e802..9aea7c4c6dad0 100644 --- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java +++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java @@ -154,6 +154,8 @@ import java.util.concurrent.atomic.AtomicBoolean; mPreferredDeviceforComm = null; initCommunicationStrategyId(); + + mSystemServer.registerUserStartedReceiver(mContext); } /*package*/ Context getContext() { @@ -993,6 +995,10 @@ import java.util.concurrent.atomic.AtomicBoolean; } } + /*package*/ void broadcastStickyIntentToCurrentProfileGroup(Intent intent) { + mSystemServer.broadcastStickyIntentToCurrentProfileGroup(intent); + } + /*package*/ void dump(PrintWriter pw, String prefix) { if (mBrokerHandler != null) { pw.println(prefix + "Message handler (watch for unhandled messages):"); diff --git a/services/core/java/com/android/server/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java index 82586b8f9b235..076cbffc0a296 100644 --- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java +++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java @@ -16,7 +16,6 @@ package com.android.server.audio; import android.annotation.NonNull; -import android.app.ActivityManager; import android.bluetooth.BluetoothA2dp; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; @@ -37,7 +36,6 @@ import android.media.MediaMetrics; import android.os.Binder; import android.os.RemoteCallbackList; import android.os.RemoteException; -import android.os.UserHandle; import android.text.TextUtils; import android.util.ArrayMap; import android.util.ArraySet; @@ -1270,7 +1268,7 @@ public class AudioDeviceInventory { final long ident = Binder.clearCallingIdentity(); try { - ActivityManager.broadcastStickyIntent(intent, UserHandle.USER_CURRENT); + mDeviceBroker.broadcastStickyIntentToCurrentProfileGroup(intent); } finally { Binder.restoreCallingIdentity(ident); } diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index aa8a6514c8672..c94b48c13fe27 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -5591,7 +5591,9 @@ public class AudioService extends IAudioService.Stub profile, suppressNoisyIntent, a2dpVolume); } - /*package*/ void setMusicMute(boolean mute) { + /** only public for mocking/spying, do not call outside of AudioService */ + @VisibleForTesting + public void setMusicMute(boolean mute) { mStreamStates[AudioSystem.STREAM_MUSIC].muteInternally(mute); } @@ -7071,7 +7073,9 @@ public class AudioService extends IAudioService.Stub } } - /*package*/ void checkMusicActive(int deviceType, String caller) { + /** only public for mocking/spying, do not call outside of AudioService */ + @VisibleForTesting + public void checkMusicActive(int deviceType, String caller) { if (mSafeMediaVolumeDevices.contains(deviceType)) { sendMsg(mAudioHandler, MSG_CHECK_MUSIC_ACTIVE, diff --git a/services/core/java/com/android/server/audio/SystemServerAdapter.java b/services/core/java/com/android/server/audio/SystemServerAdapter.java index 68893f8ca6c7c..22456bcf3f661 100644 --- a/services/core/java/com/android/server/audio/SystemServerAdapter.java +++ b/services/core/java/com/android/server/audio/SystemServerAdapter.java @@ -18,11 +18,20 @@ package com.android.server.audio; import android.annotation.NonNull; import android.annotation.Nullable; +import android.app.ActivityManager; +import android.app.ActivityManagerInternal; +import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; +import android.content.IntentFilter; +import android.content.pm.UserInfo; import android.media.AudioManager; import android.os.Binder; import android.os.UserHandle; +import android.os.UserManager; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.server.LocalServices; import java.util.Objects; @@ -82,4 +91,58 @@ public class SystemServerAdapter { Binder.restoreCallingIdentity(ident); } } + + /** + * Send sticky broadcast to current user's profile group (including current user) + */ + @VisibleForTesting + public void broadcastStickyIntentToCurrentProfileGroup(Intent intent) { + int[] profileIds = LocalServices.getService( + ActivityManagerInternal.class).getCurrentProfileIds(); + for (int userId : profileIds) { + ActivityManager.broadcastStickyIntent(intent, userId); + } + } + + /** + * Broadcast sticky intents when a profile is started. This is needed because newly created + * profiles would not receive the intents until the next state change. + */ + /*package*/ void registerUserStartedReceiver(Context context) { + IntentFilter filter = new IntentFilter(); + filter.addAction(Intent.ACTION_USER_STARTED); + context.registerReceiverAsUser(new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + if (Intent.ACTION_USER_STARTED.equals(intent.getAction())) { + final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, + UserHandle.USER_NULL); + if (userId == UserHandle.USER_NULL) { + return; + } + + UserManager userManager = context.getSystemService(UserManager.class); + final UserInfo profileParent = userManager.getProfileParent(userId); + if (profileParent == null) { + return; + } + + // get sticky intents from parent and broadcast them to the started profile + broadcastProfileParentStickyIntent(context, AudioManager.ACTION_HDMI_AUDIO_PLUG, + userId, profileParent.id); + broadcastProfileParentStickyIntent(context, AudioManager.ACTION_HEADSET_PLUG, + userId, profileParent.id); + } + } + }, UserHandle.ALL, filter, null, null); + } + + private void broadcastProfileParentStickyIntent(Context context, String intentAction, + int profileId, int parentId) { + Intent intent = context.registerReceiverAsUser(/*receiver*/ null, UserHandle.of(parentId), + new IntentFilter(intentAction), /*broadcastPermission*/ null, /*scheduler*/ null); + if (intent != null) { + ActivityManager.broadcastStickyIntent(intent, profileId); + } + } } diff --git a/services/tests/servicestests/src/com/android/server/audio/AudioDeviceBrokerTest.java b/services/tests/servicestests/src/com/android/server/audio/AudioDeviceBrokerTest.java index 4ecaac55ce96d..79a5ed65b999b 100644 --- a/services/tests/servicestests/src/com/android/server/audio/AudioDeviceBrokerTest.java +++ b/services/tests/servicestests/src/com/android/server/audio/AudioDeviceBrokerTest.java @@ -17,6 +17,7 @@ package com.android.server.audio; import static org.mockito.Mockito.any; import static org.mockito.Mockito.anyInt; +import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; @@ -27,6 +28,7 @@ import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothProfile; import android.content.Context; +import android.content.Intent; import android.media.AudioManager; import android.media.AudioSystem; import android.util.Log; @@ -58,7 +60,7 @@ public class AudioDeviceBrokerTest { @Mock private AudioService mMockAudioService; @Spy private AudioDeviceInventory mSpyDevInventory; @Spy private AudioSystemAdapter mSpyAudioSystem; - private SystemServerAdapter mSystemServer; + @Spy private SystemServerAdapter mSpySystemServer; private BluetoothDevice mFakeBtDevice; @@ -69,9 +71,9 @@ public class AudioDeviceBrokerTest { mMockAudioService = mock(AudioService.class); mSpyAudioSystem = spy(new NoOpAudioSystemAdapter()); mSpyDevInventory = spy(new AudioDeviceInventory(mSpyAudioSystem)); - mSystemServer = new NoOpSystemServerAdapter(); + mSpySystemServer = spy(new NoOpSystemServerAdapter()); mAudioDeviceBroker = new AudioDeviceBroker(mContext, mMockAudioService, mSpyDevInventory, - mSystemServer); + mSpySystemServer); mSpyDevInventory.setDeviceBroker(mAudioDeviceBroker); BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); @@ -172,6 +174,30 @@ public class AudioDeviceBrokerTest { true); } + /** + * Test that device wired state intents are broadcasted on connection state change + * @throws Exception + */ + @Test + public void testSetWiredDeviceConnectionState() throws Exception { + Log.i(TAG, "starting postSetWiredDeviceConnectionState"); + + final String address = "testAddress"; + final String name = "testName"; + final String caller = "testCaller"; + + doNothing().when(mSpySystemServer).broadcastStickyIntentToCurrentProfileGroup( + any(Intent.class)); + + mSpyDevInventory.setWiredDeviceConnectionState(AudioSystem.DEVICE_OUT_WIRED_HEADSET, + AudioService.CONNECTION_STATE_CONNECTED, address, name, caller); + Thread.sleep(MAX_MESSAGE_HANDLING_DELAY_MS); + + // Verify that the sticky intent is broadcasted + verify(mSpySystemServer, times(1)).broadcastStickyIntentToCurrentProfileGroup( + any(Intent.class)); + } + private void doTestConnectionDisconnectionReconnection(int delayAfterDisconnection, boolean mockMediaPlayback, boolean guaranteeSingleConnection) throws Exception { when(mMockAudioService.getDeviceForStream(AudioManager.STREAM_MUSIC))