Loading services/core/java/com/android/server/pm/BackgroundUserSoundNotifier.java +55 −14 Original line number Diff line number Diff line Loading @@ -34,16 +34,19 @@ import android.media.AudioFocusInfo; import android.media.AudioManager; import android.media.AudioPlaybackConfiguration; import android.media.audiopolicy.AudioPolicy; import android.multiuser.Flags; import android.os.Looper; import android.os.RemoteException; import android.os.UserHandle; import android.os.UserManager; import android.util.ArraySet; import android.util.Log; import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; import java.util.List; import java.util.Set; public class BackgroundUserSoundNotifier { Loading @@ -65,6 +68,10 @@ public class BackgroundUserSoundNotifier { */ @VisibleForTesting int mNotificationClientUid = -1; /** * UIDs of audio focus infos with active notifications. */ Set<Integer> mNotificationClientUids = new ArraySet<>(); @VisibleForTesting AudioPolicy mFocusControlAudioPolicy; @VisibleForTesting Loading Loading @@ -149,27 +156,43 @@ public class BackgroundUserSoundNotifier { @SuppressLint("MissingPermission") @Override public void onReceive(Context context, Intent intent) { if (Flags.multipleAlarmNotificationsSupport()) { if (!intent.hasExtra(EXTRA_NOTIFICATION_CLIENT_UID)) { return; } } else { if (mNotificationClientUid == -1) { return; } dismissNotification(mNotificationClientUid); } int clientUid; if (Flags.multipleAlarmNotificationsSupport()) { clientUid = intent.getIntExtra(EXTRA_NOTIFICATION_CLIENT_UID, -1); } else { clientUid = mNotificationClientUid; } dismissNotification(clientUid); if (DEBUG) { final int actionIndex = intent.getAction().lastIndexOf(".") + 1; final String action = intent.getAction().substring(actionIndex); Log.d(LOG_TAG, "Action requested: " + action + ", by userId " + ActivityManager.getCurrentUser() + " for alarm on user " + UserHandle.getUserHandleForUid(mNotificationClientUid)); + UserHandle.getUserHandleForUid(clientUid)); } if (ACTION_MUTE_SOUND.equals(intent.getAction())) { muteAlarmSounds(mNotificationClientUid); muteAlarmSounds(clientUid); } else if (ACTION_SWITCH_USER.equals(intent.getAction())) { activityManager.switchUser(UserHandle.getUserId(mNotificationClientUid)); activityManager.switchUser(UserHandle.getUserId(clientUid)); } if (Flags.multipleAlarmNotificationsSupport()) { mNotificationClientUids.remove(clientUid); } else { mNotificationClientUid = -1; } } }; IntentFilter filter = new IntentFilter(); Loading Loading @@ -215,10 +238,11 @@ public class BackgroundUserSoundNotifier { final int userId = UserHandle.getUserId(afi.getClientUid()); final int usage = afi.getAttributes().getUsage(); UserInfo userInfo = mUserManager.getUserInfo(userId); // Only show notification if the sound is coming from background user and the notification // is not already shown. // for this UID is not already shown. if (userInfo != null && userId != foregroundContext.getUserId() && mNotificationClientUid == -1) { && !isNotificationShown(afi.getClientUid())) { //TODO: b/349138482 - Add handling of cases when usage == USAGE_NOTIFICATION_RINGTONE if (usage == USAGE_ALARM) { if (DEBUG) { Loading @@ -226,8 +250,11 @@ public class BackgroundUserSoundNotifier { + ", displaying notification for current user " + foregroundContext.getUserId()); } if (Flags.multipleAlarmNotificationsSupport()) { mNotificationClientUids.add(afi.getClientUid()); } else { mNotificationClientUid = afi.getClientUid(); } mNotificationManager.notifyAsUser(LOG_TAG, afi.getClientUid(), createNotification(userInfo.name, foregroundContext, afi.getClientUid()), Loading @@ -243,17 +270,23 @@ public class BackgroundUserSoundNotifier { */ @VisibleForTesting void dismissNotificationIfNecessary(int notificationClientUid) { if (getAudioFocusInfoForNotification(notificationClientUid) == null && mNotificationClientUid >= 0) { && isNotificationShown(notificationClientUid)) { if (DEBUG) { Log.d(LOG_TAG, "Alarm ringing on background user " + UserHandle.getUserHandleForUid(notificationClientUid).getIdentifier() + " left focus stack, dismissing notification"); } dismissNotification(notificationClientUid); if (Flags.multipleAlarmNotificationsSupport()) { mNotificationClientUids.remove(notificationClientUid); } else { mNotificationClientUid = -1; } } } /** * Dismisses notification for all users in case user switch occurred after notification was Loading Loading @@ -331,4 +364,12 @@ public class BackgroundUserSoundNotifier { return notificationBuilder.build(); } private boolean isNotificationShown(int notificationClientUid) { if (Flags.multipleAlarmNotificationsSupport()) { return mNotificationClientUids.contains(notificationClientUid); } else { return mNotificationClientUid != -1; } } } services/tests/mockingservicestests/src/com/android/server/pm/BackgroundUserSoundNotifierTest.java +80 −3 Original line number Diff line number Diff line Loading @@ -41,14 +41,19 @@ import android.media.AudioManager; import android.media.AudioPlaybackConfiguration; import android.media.PlayerProxy; import android.media.audiopolicy.AudioPolicy; import android.multiuser.Flags; import android.os.Build; import android.os.RemoteException; import android.os.UserHandle; import android.os.UserManager; import android.platform.test.annotations.RequiresFlagsEnabled; import android.platform.test.flag.junit.CheckFlagsRule; import android.platform.test.flag.junit.DeviceFlagsValueProvider; import android.util.ArraySet; import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; Loading @@ -60,7 +65,6 @@ import java.util.List; import java.util.Stack; @RunWith(JUnit4.class) public class BackgroundUserSoundNotifierTest { private final Context mRealContext = androidx.test.InstrumentationRegistry.getInstrumentation() .getTargetContext(); Loading @@ -72,6 +76,10 @@ public class BackgroundUserSoundNotifierTest { @Mock private NotificationManager mNotificationManager; @Rule public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule(); @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); Loading Loading @@ -142,8 +150,11 @@ public class BackgroundUserSoundNotifierTest { final int fgUserId = mSpiedContext.getUserId(); int bgUserId = fgUserId + 1; int bgUserUid = bgUserId * 100000; if (Flags.multipleAlarmNotificationsSupport()) { mBackgroundUserSoundNotifier.mNotificationClientUids.add(bgUserUid); } else { mBackgroundUserSoundNotifier.mNotificationClientUid = bgUserUid; } AudioManager mockAudioManager = mock(AudioManager.class); when(mSpiedContext.getSystemService(AudioManager.class)).thenReturn(mockAudioManager); Loading Loading @@ -243,6 +254,72 @@ public class BackgroundUserSoundNotifierTest { notification.actions[0].title); } @RequiresFlagsEnabled({Flags.FLAG_MULTIPLE_ALARM_NOTIFICATIONS_SUPPORT}) @Test public void testMultipleAlarmsSameUid_OneNotificationCreated() throws RemoteException { assumeTrue(UserManager.supportsMultipleUsers()); UserInfo user = createUser("User", UserManager.USER_TYPE_FULL_SECONDARY, 0); final int fgUserId = mSpiedContext.getUserId(); final int bgUserUid = user.id * 100000; doReturn(UserHandle.of(fgUserId)).when(mSpiedContext).getUser(); AudioAttributes aa = new AudioAttributes.Builder().setUsage(USAGE_ALARM).build(); AudioFocusInfo afi1 = new AudioFocusInfo(aa, bgUserUid, "", /* packageName= */ "com.android.car.audio", AudioManager.AUDIOFOCUS_GAIN, AudioManager.AUDIOFOCUS_NONE, /* flags= */ 0, Build.VERSION.SDK_INT); mBackgroundUserSoundNotifier.notifyForegroundUserAboutSoundIfNecessary(afi1); verify(mNotificationManager) .notifyAsUser(eq(BackgroundUserSoundNotifier.class.getSimpleName()), eq(afi1.getClientUid()), any(Notification.class), eq(UserHandle.of(fgUserId))); AudioFocusInfo afi2 = new AudioFocusInfo(aa, bgUserUid, "", /* packageName= */ "com.android.car.audio", AudioManager.AUDIOFOCUS_GAIN, AudioManager.AUDIOFOCUS_NONE, /* flags= */ 0, Build.VERSION.SDK_INT); clearInvocations(mNotificationManager); mBackgroundUserSoundNotifier.notifyForegroundUserAboutSoundIfNecessary(afi2); verify(mNotificationManager, never()) .notifyAsUser(eq(BackgroundUserSoundNotifier.class.getSimpleName()), eq(afi2.getClientUid()), any(Notification.class), eq(UserHandle.of(fgUserId))); } @RequiresFlagsEnabled({Flags.FLAG_MULTIPLE_ALARM_NOTIFICATIONS_SUPPORT}) @Test public void testMultipleAlarmsDifferentUsers_multipleNotificationsCreated() throws RemoteException { assumeTrue(UserManager.supportsMultipleUsers()); UserInfo user1 = createUser("User1", UserManager.USER_TYPE_FULL_SECONDARY, 0); UserInfo user2 = createUser("User2", UserManager.USER_TYPE_FULL_SECONDARY, 0); final int fgUserId = mSpiedContext.getUserId(); final int bgUserUid1 = user1.id * 100000; final int bgUserUid2 = user2.id * 100000; doReturn(UserHandle.of(fgUserId)).when(mSpiedContext).getUser(); AudioAttributes aa = new AudioAttributes.Builder().setUsage(USAGE_ALARM).build(); AudioFocusInfo afi1 = new AudioFocusInfo(aa, bgUserUid1, "", /* packageName= */ "com.android.car.audio", AudioManager.AUDIOFOCUS_GAIN, AudioManager.AUDIOFOCUS_NONE, /* flags= */ 0, Build.VERSION.SDK_INT); mBackgroundUserSoundNotifier.notifyForegroundUserAboutSoundIfNecessary(afi1); verify(mNotificationManager) .notifyAsUser(eq(BackgroundUserSoundNotifier.class.getSimpleName()), eq(afi1.getClientUid()), any(Notification.class), eq(UserHandle.of(fgUserId))); AudioFocusInfo afi2 = new AudioFocusInfo(aa, bgUserUid2, "", /* packageName= */ "com.android.car.audio", AudioManager.AUDIOFOCUS_GAIN, AudioManager.AUDIOFOCUS_NONE, /* flags= */ 0, Build.VERSION.SDK_INT); clearInvocations(mNotificationManager); mBackgroundUserSoundNotifier.notifyForegroundUserAboutSoundIfNecessary(afi2); verify(mNotificationManager) .notifyAsUser(eq(BackgroundUserSoundNotifier.class.getSimpleName()), eq(afi2.getClientUid()), any(Notification.class), eq(UserHandle.of(fgUserId))); } private UserInfo createUser(String name, String userType, int flags) { UserInfo user = mUserManager.createUser(name, userType, flags); if (user != null) { Loading Loading
services/core/java/com/android/server/pm/BackgroundUserSoundNotifier.java +55 −14 Original line number Diff line number Diff line Loading @@ -34,16 +34,19 @@ import android.media.AudioFocusInfo; import android.media.AudioManager; import android.media.AudioPlaybackConfiguration; import android.media.audiopolicy.AudioPolicy; import android.multiuser.Flags; import android.os.Looper; import android.os.RemoteException; import android.os.UserHandle; import android.os.UserManager; import android.util.ArraySet; import android.util.Log; import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; import java.util.List; import java.util.Set; public class BackgroundUserSoundNotifier { Loading @@ -65,6 +68,10 @@ public class BackgroundUserSoundNotifier { */ @VisibleForTesting int mNotificationClientUid = -1; /** * UIDs of audio focus infos with active notifications. */ Set<Integer> mNotificationClientUids = new ArraySet<>(); @VisibleForTesting AudioPolicy mFocusControlAudioPolicy; @VisibleForTesting Loading Loading @@ -149,27 +156,43 @@ public class BackgroundUserSoundNotifier { @SuppressLint("MissingPermission") @Override public void onReceive(Context context, Intent intent) { if (Flags.multipleAlarmNotificationsSupport()) { if (!intent.hasExtra(EXTRA_NOTIFICATION_CLIENT_UID)) { return; } } else { if (mNotificationClientUid == -1) { return; } dismissNotification(mNotificationClientUid); } int clientUid; if (Flags.multipleAlarmNotificationsSupport()) { clientUid = intent.getIntExtra(EXTRA_NOTIFICATION_CLIENT_UID, -1); } else { clientUid = mNotificationClientUid; } dismissNotification(clientUid); if (DEBUG) { final int actionIndex = intent.getAction().lastIndexOf(".") + 1; final String action = intent.getAction().substring(actionIndex); Log.d(LOG_TAG, "Action requested: " + action + ", by userId " + ActivityManager.getCurrentUser() + " for alarm on user " + UserHandle.getUserHandleForUid(mNotificationClientUid)); + UserHandle.getUserHandleForUid(clientUid)); } if (ACTION_MUTE_SOUND.equals(intent.getAction())) { muteAlarmSounds(mNotificationClientUid); muteAlarmSounds(clientUid); } else if (ACTION_SWITCH_USER.equals(intent.getAction())) { activityManager.switchUser(UserHandle.getUserId(mNotificationClientUid)); activityManager.switchUser(UserHandle.getUserId(clientUid)); } if (Flags.multipleAlarmNotificationsSupport()) { mNotificationClientUids.remove(clientUid); } else { mNotificationClientUid = -1; } } }; IntentFilter filter = new IntentFilter(); Loading Loading @@ -215,10 +238,11 @@ public class BackgroundUserSoundNotifier { final int userId = UserHandle.getUserId(afi.getClientUid()); final int usage = afi.getAttributes().getUsage(); UserInfo userInfo = mUserManager.getUserInfo(userId); // Only show notification if the sound is coming from background user and the notification // is not already shown. // for this UID is not already shown. if (userInfo != null && userId != foregroundContext.getUserId() && mNotificationClientUid == -1) { && !isNotificationShown(afi.getClientUid())) { //TODO: b/349138482 - Add handling of cases when usage == USAGE_NOTIFICATION_RINGTONE if (usage == USAGE_ALARM) { if (DEBUG) { Loading @@ -226,8 +250,11 @@ public class BackgroundUserSoundNotifier { + ", displaying notification for current user " + foregroundContext.getUserId()); } if (Flags.multipleAlarmNotificationsSupport()) { mNotificationClientUids.add(afi.getClientUid()); } else { mNotificationClientUid = afi.getClientUid(); } mNotificationManager.notifyAsUser(LOG_TAG, afi.getClientUid(), createNotification(userInfo.name, foregroundContext, afi.getClientUid()), Loading @@ -243,17 +270,23 @@ public class BackgroundUserSoundNotifier { */ @VisibleForTesting void dismissNotificationIfNecessary(int notificationClientUid) { if (getAudioFocusInfoForNotification(notificationClientUid) == null && mNotificationClientUid >= 0) { && isNotificationShown(notificationClientUid)) { if (DEBUG) { Log.d(LOG_TAG, "Alarm ringing on background user " + UserHandle.getUserHandleForUid(notificationClientUid).getIdentifier() + " left focus stack, dismissing notification"); } dismissNotification(notificationClientUid); if (Flags.multipleAlarmNotificationsSupport()) { mNotificationClientUids.remove(notificationClientUid); } else { mNotificationClientUid = -1; } } } /** * Dismisses notification for all users in case user switch occurred after notification was Loading Loading @@ -331,4 +364,12 @@ public class BackgroundUserSoundNotifier { return notificationBuilder.build(); } private boolean isNotificationShown(int notificationClientUid) { if (Flags.multipleAlarmNotificationsSupport()) { return mNotificationClientUids.contains(notificationClientUid); } else { return mNotificationClientUid != -1; } } }
services/tests/mockingservicestests/src/com/android/server/pm/BackgroundUserSoundNotifierTest.java +80 −3 Original line number Diff line number Diff line Loading @@ -41,14 +41,19 @@ import android.media.AudioManager; import android.media.AudioPlaybackConfiguration; import android.media.PlayerProxy; import android.media.audiopolicy.AudioPolicy; import android.multiuser.Flags; import android.os.Build; import android.os.RemoteException; import android.os.UserHandle; import android.os.UserManager; import android.platform.test.annotations.RequiresFlagsEnabled; import android.platform.test.flag.junit.CheckFlagsRule; import android.platform.test.flag.junit.DeviceFlagsValueProvider; import android.util.ArraySet; import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; Loading @@ -60,7 +65,6 @@ import java.util.List; import java.util.Stack; @RunWith(JUnit4.class) public class BackgroundUserSoundNotifierTest { private final Context mRealContext = androidx.test.InstrumentationRegistry.getInstrumentation() .getTargetContext(); Loading @@ -72,6 +76,10 @@ public class BackgroundUserSoundNotifierTest { @Mock private NotificationManager mNotificationManager; @Rule public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule(); @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); Loading Loading @@ -142,8 +150,11 @@ public class BackgroundUserSoundNotifierTest { final int fgUserId = mSpiedContext.getUserId(); int bgUserId = fgUserId + 1; int bgUserUid = bgUserId * 100000; if (Flags.multipleAlarmNotificationsSupport()) { mBackgroundUserSoundNotifier.mNotificationClientUids.add(bgUserUid); } else { mBackgroundUserSoundNotifier.mNotificationClientUid = bgUserUid; } AudioManager mockAudioManager = mock(AudioManager.class); when(mSpiedContext.getSystemService(AudioManager.class)).thenReturn(mockAudioManager); Loading Loading @@ -243,6 +254,72 @@ public class BackgroundUserSoundNotifierTest { notification.actions[0].title); } @RequiresFlagsEnabled({Flags.FLAG_MULTIPLE_ALARM_NOTIFICATIONS_SUPPORT}) @Test public void testMultipleAlarmsSameUid_OneNotificationCreated() throws RemoteException { assumeTrue(UserManager.supportsMultipleUsers()); UserInfo user = createUser("User", UserManager.USER_TYPE_FULL_SECONDARY, 0); final int fgUserId = mSpiedContext.getUserId(); final int bgUserUid = user.id * 100000; doReturn(UserHandle.of(fgUserId)).when(mSpiedContext).getUser(); AudioAttributes aa = new AudioAttributes.Builder().setUsage(USAGE_ALARM).build(); AudioFocusInfo afi1 = new AudioFocusInfo(aa, bgUserUid, "", /* packageName= */ "com.android.car.audio", AudioManager.AUDIOFOCUS_GAIN, AudioManager.AUDIOFOCUS_NONE, /* flags= */ 0, Build.VERSION.SDK_INT); mBackgroundUserSoundNotifier.notifyForegroundUserAboutSoundIfNecessary(afi1); verify(mNotificationManager) .notifyAsUser(eq(BackgroundUserSoundNotifier.class.getSimpleName()), eq(afi1.getClientUid()), any(Notification.class), eq(UserHandle.of(fgUserId))); AudioFocusInfo afi2 = new AudioFocusInfo(aa, bgUserUid, "", /* packageName= */ "com.android.car.audio", AudioManager.AUDIOFOCUS_GAIN, AudioManager.AUDIOFOCUS_NONE, /* flags= */ 0, Build.VERSION.SDK_INT); clearInvocations(mNotificationManager); mBackgroundUserSoundNotifier.notifyForegroundUserAboutSoundIfNecessary(afi2); verify(mNotificationManager, never()) .notifyAsUser(eq(BackgroundUserSoundNotifier.class.getSimpleName()), eq(afi2.getClientUid()), any(Notification.class), eq(UserHandle.of(fgUserId))); } @RequiresFlagsEnabled({Flags.FLAG_MULTIPLE_ALARM_NOTIFICATIONS_SUPPORT}) @Test public void testMultipleAlarmsDifferentUsers_multipleNotificationsCreated() throws RemoteException { assumeTrue(UserManager.supportsMultipleUsers()); UserInfo user1 = createUser("User1", UserManager.USER_TYPE_FULL_SECONDARY, 0); UserInfo user2 = createUser("User2", UserManager.USER_TYPE_FULL_SECONDARY, 0); final int fgUserId = mSpiedContext.getUserId(); final int bgUserUid1 = user1.id * 100000; final int bgUserUid2 = user2.id * 100000; doReturn(UserHandle.of(fgUserId)).when(mSpiedContext).getUser(); AudioAttributes aa = new AudioAttributes.Builder().setUsage(USAGE_ALARM).build(); AudioFocusInfo afi1 = new AudioFocusInfo(aa, bgUserUid1, "", /* packageName= */ "com.android.car.audio", AudioManager.AUDIOFOCUS_GAIN, AudioManager.AUDIOFOCUS_NONE, /* flags= */ 0, Build.VERSION.SDK_INT); mBackgroundUserSoundNotifier.notifyForegroundUserAboutSoundIfNecessary(afi1); verify(mNotificationManager) .notifyAsUser(eq(BackgroundUserSoundNotifier.class.getSimpleName()), eq(afi1.getClientUid()), any(Notification.class), eq(UserHandle.of(fgUserId))); AudioFocusInfo afi2 = new AudioFocusInfo(aa, bgUserUid2, "", /* packageName= */ "com.android.car.audio", AudioManager.AUDIOFOCUS_GAIN, AudioManager.AUDIOFOCUS_NONE, /* flags= */ 0, Build.VERSION.SDK_INT); clearInvocations(mNotificationManager); mBackgroundUserSoundNotifier.notifyForegroundUserAboutSoundIfNecessary(afi2); verify(mNotificationManager) .notifyAsUser(eq(BackgroundUserSoundNotifier.class.getSimpleName()), eq(afi2.getClientUid()), any(Notification.class), eq(UserHandle.of(fgUserId))); } private UserInfo createUser(String name, String userType, int flags) { UserInfo user = mUserManager.createUser(name, userType, flags); if (user != null) { Loading