Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 33010228 authored by Tetiana Meronyk's avatar Tetiana Meronyk Committed by Android (Google) Code Review
Browse files

Merge "Take AudioFocusStack state into account when showing/dismissing notification" into main

parents d5902b20 4d0b38e6
Loading
Loading
Loading
Loading
+139 −69
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ package com.android.server.pm;

import static android.media.AudioAttributes.USAGE_ALARM;

import android.annotation.Nullable;
import android.annotation.SuppressLint;
import android.app.ActivityManager;
import android.app.Notification;
@@ -42,6 +43,8 @@ import android.util.Log;
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;

import java.util.List;

public class BackgroundUserSoundNotifier {

    private static final boolean DEBUG = false;
@@ -49,11 +52,21 @@ public class BackgroundUserSoundNotifier {
    private static final String BUSN_CHANNEL_ID = "bg_user_sound_channel";
    private static final String BUSN_CHANNEL_NAME = "BackgroundUserSound";
    public static final String ACTION_MUTE_SOUND = "com.android.server.ACTION_MUTE_BG_USER";
    private static final String EXTRA_NOTIFICATION_ID = "com.android.server.EXTRA_CLIENT_UID";
    private static final String EXTRA_CURRENT_USER_ID = "com.android.server.EXTRA_CURRENT_USER_ID";
    private static final String ACTION_SWITCH_USER = "com.android.server.ACTION_SWITCH_TO_USER";
    /** ID of user with notification displayed, -1 if notification is not showing*/
    private int mUserWithNotification = -1;
    private static final String ACTION_DISMISS_NOTIFICATION =
            "com.android.server.ACTION_DISMISS_NOTIFICATION";
    /**
     * The clientUid from the AudioFocusInfo of the background user,
     * for which an active notification is currently displayed.
     * Set to -1 if no notification is being shown.
     * TODO: b/367615180 - add support for multiple simultaneous alarms
     */
    @VisibleForTesting
    int mNotificationClientUid = -1;
    @VisibleForTesting
    AudioPolicy mFocusControlAudioPolicy;
    @VisibleForTesting
    BackgroundUserListener mBgUserListener;
    private final Context mSystemUserContext;
    @VisibleForTesting
    final NotificationManager mNotificationManager;
@@ -67,11 +80,18 @@ public class BackgroundUserSoundNotifier {
        mSystemUserContext = context;
        mNotificationManager =  mSystemUserContext.getSystemService(NotificationManager.class);
        mUserManager = mSystemUserContext.getSystemService(UserManager.class);
        createNotificationChannel();
        setupFocusControlAudioPolicy();
    }

    /**
     * Creates a dedicated channel for background user related notifications.
     */
    private void createNotificationChannel() {
        NotificationChannel channel = new NotificationChannel(BUSN_CHANNEL_ID, BUSN_CHANNEL_NAME,
                NotificationManager.IMPORTANCE_HIGH);
        channel.setSound(null, null);
        mNotificationManager.createNotificationChannel(channel);
        setupFocusControlAudioPolicy();
    }

    private void setupFocusControlAudioPolicy() {
@@ -81,15 +101,16 @@ public class BackgroundUserSoundNotifier {
        ActivityManager am = mSystemUserContext.getSystemService(ActivityManager.class);

        registerReceiver(am);
        BackgroundUserListener bgUserListener = new BackgroundUserListener(mSystemUserContext);
        mBgUserListener = new BackgroundUserListener(mSystemUserContext);
        AudioPolicy.Builder focusControlPolicyBuilder = new AudioPolicy.Builder(mSystemUserContext);
        focusControlPolicyBuilder.setLooper(Looper.getMainLooper());

        focusControlPolicyBuilder.setAudioPolicyFocusListener(bgUserListener);
        focusControlPolicyBuilder.setAudioPolicyFocusListener(mBgUserListener);

        AudioPolicy mFocusControlAudioPolicy = focusControlPolicyBuilder.build();
        mFocusControlAudioPolicy = focusControlPolicyBuilder.build();
        int status = mSystemUserContext.getSystemService(AudioManager.class)
                .registerAudioPolicy(mFocusControlAudioPolicy);

        if (status != AudioManager.SUCCESS) {
            Log.w(LOG_TAG , "Could not register the service's focus"
                    + " control audio policy, error: " + status);
@@ -117,8 +138,13 @@ public class BackgroundUserSoundNotifier {

        @SuppressLint("MissingPermission")
        public void onAudioFocusLoss(AudioFocusInfo afi, boolean wasNotified) {
            BackgroundUserSoundNotifier.this.dismissNotificationIfNecessary(afi);
            BackgroundUserSoundNotifier.this.dismissNotificationIfNecessary();
        }
    }

    @VisibleForTesting
    BackgroundUserListener getAudioPolicyFocusListener() {
        return  mBgUserListener;
    }

    /**
@@ -126,114 +152,156 @@ public class BackgroundUserSoundNotifier {
     *  When ACTION_MUTE_SOUND is received, it mutes a background user's alarm sound.
     *  When ACTION_SWITCH_USER is received, a switch to the background user with alarm is started.
     */
    private void registerReceiver(ActivityManager service) {
    private void registerReceiver(ActivityManager activityManager) {
        BroadcastReceiver backgroundUserNotificationBroadcastReceiver = new BroadcastReceiver() {
            @SuppressLint("MissingPermission")
            @Override
            public void onReceive(Context context, Intent intent) {
                if (!(intent.hasExtra(EXTRA_NOTIFICATION_ID)
                        && intent.hasExtra(EXTRA_CURRENT_USER_ID)
                        && intent.hasExtra(Intent.EXTRA_USER_ID))) {
                if (mNotificationClientUid == -1) {
                    return;
                }
                final int notificationId = intent.getIntExtra(EXTRA_NOTIFICATION_ID, -1);
                dismissNotification();

                if (DEBUG) {
                    Log.d(LOG_TAG,
                            "User with alarm id   " + intent.getIntExtra(Intent.EXTRA_USER_ID,
                                    -1) + "  current user id " + intent.getIntExtra(
                                    EXTRA_CURRENT_USER_ID, -1));
                }
                mUserWithNotification = -1;
                mNotificationManager.cancelAsUser(LOG_TAG, notificationId,
                        UserHandle.of(intent.getIntExtra(EXTRA_CURRENT_USER_ID, -1)));
                if (ACTION_MUTE_SOUND.equals(intent.getAction())) {
                    final AudioManager audioManager =
                            mSystemUserContext.getSystemService(AudioManager.class);
                    if (audioManager != null) {
                        for (AudioPlaybackConfiguration apc :
                                audioManager.getActivePlaybackConfigurations()) {
                            if (apc.getAudioAttributes().getUsage() == USAGE_ALARM) {
                                if (apc.getPlayerProxy() != null) {
                                    apc.getPlayerProxy().stop();
                                }
                            }
                        }
                    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));
                }

                if (ACTION_MUTE_SOUND.equals(intent.getAction())) {
                    muteAlarmSounds(mSystemUserContext);
                } else if (ACTION_SWITCH_USER.equals(intent.getAction())) {
                    service.switchUser(intent.getIntExtra(Intent.EXTRA_USER_ID, -1));
                    activityManager.switchUser(UserHandle.getUserId(mNotificationClientUid));
                }

                mNotificationClientUid = -1;
            }
        };

        IntentFilter filter = new IntentFilter();
        filter.addAction(ACTION_MUTE_SOUND);
        filter.addAction(ACTION_SWITCH_USER);
        filter.addAction(ACTION_DISMISS_NOTIFICATION);
        mSystemUserContext.registerReceiver(backgroundUserNotificationBroadcastReceiver, filter,
                Context.RECEIVER_NOT_EXPORTED);
    }

    /**
     * Stop player proxy for the ongoing alarm and drop focus for its AudioFocusInfo.
     */
    @VisibleForTesting
    void muteAlarmSounds(Context context) {
        AudioManager audioManager = context.getSystemService(AudioManager.class);
        if (audioManager != null) {
            for (AudioPlaybackConfiguration apc : audioManager.getActivePlaybackConfigurations()) {
                if (apc.getClientUid() == mNotificationClientUid && apc.getPlayerProxy() != null) {
                    apc.getPlayerProxy().stop();
                }
            }
        }
    }

    /**
     * Check if sound is coming from background user and show notification is required.
     */
    @VisibleForTesting
    void notifyForegroundUserAboutSoundIfNecessary(AudioFocusInfo afi, Context
            foregroundContext) throws RemoteException {
    void notifyForegroundUserAboutSoundIfNecessary(AudioFocusInfo afi, Context foregroundContext)
            throws RemoteException {
        final int userId = UserHandle.getUserId(afi.getClientUid());
        final int usage = afi.getAttributes().getUsage();
        UserInfo userInfo = mUserManager.getUserInfo(userId);
        if (userInfo != null && userId != foregroundContext.getUserId()) {
        // Only show notification if the sound is coming from background user and the notification
        // is not already shown.
        if (userInfo != null && userId != foregroundContext.getUserId()
                && mNotificationClientUid == -1) {
            //TODO: b/349138482 - Add handling of cases when usage == USAGE_NOTIFICATION_RINGTONE
            if (usage == USAGE_ALARM) {
                Intent muteIntent = createIntent(ACTION_MUTE_SOUND, afi, foregroundContext, userId);
                PendingIntent mutePI = PendingIntent.getBroadcast(mSystemUserContext, 0,
                        muteIntent, PendingIntent.FLAG_UPDATE_CURRENT
                                | PendingIntent.FLAG_IMMUTABLE);
                Intent switchIntent = createIntent(ACTION_SWITCH_USER, afi, foregroundContext,
                        userId);
                PendingIntent switchPI = PendingIntent.getBroadcast(mSystemUserContext, 0,
                        switchIntent, PendingIntent.FLAG_UPDATE_CURRENT
                                | PendingIntent.FLAG_IMMUTABLE);

                mUserWithNotification = foregroundContext.getUserId();
                mNotificationManager.notifyAsUser(LOG_TAG, afi.getClientUid(),
                        createNotification(userInfo.name, mutePI, switchPI, foregroundContext),
                if (DEBUG) {
                    Log.d(LOG_TAG, "Alarm ringing on background user " + userId
                            + ", displaying notification for current user "
                            + foregroundContext.getUserId());
                }

                mNotificationClientUid = afi.getClientUid();

                mNotificationManager.notifyAsUser(LOG_TAG, mNotificationClientUid,
                        createNotification(userInfo.name, foregroundContext),
                        foregroundContext.getUser());
            }
        }
    }

    /**
     * If notification is present, dismisses it. To be called when the relevant sound loses focus.
     * Dismisses notification if the associated focus has been removed from the focus stack.
     * Notification remains if the focus is temporarily lost due to another client taking over the
     * focus ownership.
     */
    private void dismissNotificationIfNecessary(AudioFocusInfo afi) {
        if (mUserWithNotification >= 0) {
            mNotificationManager.cancelAsUser(LOG_TAG, afi.getClientUid(),
                    UserHandle.of(mUserWithNotification));
    @VisibleForTesting
    void dismissNotificationIfNecessary() {
        if (getAudioFocusInfoForNotification() == null && mNotificationClientUid >= 0) {
            if (DEBUG) {
                Log.d(LOG_TAG, "Alarm ringing on background user "
                        + UserHandle.getUserHandleForUid(mNotificationClientUid).getIdentifier()
                        + " left focus stack, dismissing notification");
            }
            dismissNotification();
            mNotificationClientUid = -1;
        }
        mUserWithNotification = -1;
    }

    private Intent createIntent(String intentAction, AudioFocusInfo afi, Context fgUserContext,
            int userId) {
    /**
     * Dismisses notification for all users in case user switch occurred after notification was
     * shown.
     */
    @SuppressLint("MissingPermission")
    private void dismissNotification() {
        mNotificationManager.cancelAsUser(LOG_TAG, mNotificationClientUid, UserHandle.ALL);
    }

    /**
     * Returns AudioFocusInfo associated with the current notification.
     */
    @SuppressLint("MissingPermission")
    @VisibleForTesting
    @Nullable
    AudioFocusInfo getAudioFocusInfoForNotification() {
        if (mNotificationClientUid >= 0) {
            List<AudioFocusInfo> stack = mFocusControlAudioPolicy.getFocusStack();
            for (int i = stack.size() - 1; i >= 0; i--) {
                if (stack.get(i).getClientUid() == mNotificationClientUid) {
                    return stack.get(i);
                }
            }
        }
        return null;
    }

    private PendingIntent createPendingIntent(String intentAction) {
        final Intent intent = new Intent(intentAction);
        intent.putExtra(EXTRA_CURRENT_USER_ID, fgUserContext.getUserId());
        intent.putExtra(EXTRA_NOTIFICATION_ID, afi.getClientUid());
        intent.putExtra(Intent.EXTRA_USER_ID, userId);
        return intent;
        PendingIntent resultPI =  PendingIntent.getBroadcast(mSystemUserContext, 0, intent,
                PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
        return resultPI;
    }

    private Notification createNotification(String userName, PendingIntent muteIntent,
            PendingIntent switchIntent, Context fgContext) {
    @VisibleForTesting
    Notification createNotification(String userName, Context fgContext) {
        final String title = fgContext.getString(R.string.bg_user_sound_notification_title_alarm,
                userName);
        final int icon = R.drawable.ic_audio_alarm;

        PendingIntent mutePI = createPendingIntent(ACTION_MUTE_SOUND);
        PendingIntent switchPI = createPendingIntent(ACTION_SWITCH_USER);
        PendingIntent dismissNotificationPI = createPendingIntent(ACTION_DISMISS_NOTIFICATION);

        final Notification.Action mute = new Notification.Action.Builder(null,
                fgContext.getString(R.string.bg_user_sound_notification_button_mute),
                muteIntent).build();
                mutePI).build();
        final Notification.Action switchUser = new Notification.Action.Builder(null,
                fgContext.getString(R.string.bg_user_sound_notification_button_switch_user),
                switchIntent).build();
                switchPI).build();

        Notification.Builder notificationBuilder = new Notification.Builder(mSystemUserContext,
                BUSN_CHANNEL_ID)
                .setSmallIcon(icon)
@@ -243,16 +311,18 @@ public class BackgroundUserSoundNotifier {
                .setOngoing(true)
                .setColor(fgContext.getColor(R.color.system_notification_accent_color))
                .setContentTitle(title)
                .setContentIntent(muteIntent)
                .setContentIntent(mutePI)
                .setAutoCancel(true)
                .setDeleteIntent(dismissNotificationPI)
                .setVisibility(Notification.VISIBILITY_PUBLIC);

        if (mUserManager.isUserSwitcherEnabled() && (mUserManager.getUserSwitchability(
                UserHandle.of(fgContext.getUserId())) == UserManager.SWITCHABILITY_STATUS_OK)) {
                fgContext.getUser()) == UserManager.SWITCHABILITY_STATUS_OK)) {
            notificationBuilder.setActions(mute, switchUser);
        } else {
            notificationBuilder.setActions(mute);
        }

        return notificationBuilder.build();
    }
}
+126 −11
Original line number Diff line number Diff line
@@ -16,13 +16,19 @@

package com.android.server.pm;

import static android.media.AudioAttributes.USAGE_ALARM;

import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
import static org.testng.AssertJUnit.assertEquals;

import android.app.Notification;
import android.app.NotificationManager;
@@ -31,6 +37,9 @@ import android.content.pm.UserInfo;
import android.media.AudioAttributes;
import android.media.AudioFocusInfo;
import android.media.AudioManager;
import android.media.AudioPlaybackConfiguration;
import android.media.PlayerProxy;
import android.media.audiopolicy.AudioPolicy;
import android.os.Build;
import android.os.RemoteException;
import android.os.UserHandle;
@@ -45,6 +54,10 @@ import org.junit.runners.JUnit4;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;

import java.util.ArrayList;
import java.util.List;
import java.util.Stack;

@RunWith(JUnit4.class)

public class BackgroundUserSoundNotifierTest {
@@ -63,7 +76,10 @@ public class BackgroundUserSoundNotifierTest {
        MockitoAnnotations.initMocks(this);
        mSpiedContext = spy(mRealContext);
        mUsersToRemove = new ArraySet<>();
        mUserManager = UserManager.get(mRealContext);

        mUserManager = spy(mSpiedContext.getSystemService(UserManager.class));
        doReturn(mUserManager)
                .when(mSpiedContext).getSystemService(UserManager.class);
        doReturn(mNotificationManager)
                .when(mSpiedContext).getSystemService(NotificationManager.class);
        mBackgroundUserSoundNotifier = new BackgroundUserSoundNotifier(mSpiedContext);
@@ -74,12 +90,9 @@ public class BackgroundUserSoundNotifierTest {
        mUsersToRemove.stream().toList().forEach(this::removeUser);
    }
    @Test
    public void testAlarmOnBackgroundUser_ForegroundUserNotified() throws RemoteException {
        AudioAttributes aa = new AudioAttributes.Builder()
                .setUsage(AudioAttributes.USAGE_ALARM).build();
        UserInfo user = createUser("User",
                UserManager.USER_TYPE_FULL_SECONDARY,
                0);
    public void testAlarmOnBackgroundUser_foregroundUserNotified() throws RemoteException {
        AudioAttributes aa = new AudioAttributes.Builder().setUsage(USAGE_ALARM).build();
        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();
@@ -95,10 +108,9 @@ public class BackgroundUserSoundNotifierTest {
    }

    @Test
    public void testMediaOnBackgroundUser_ForegroundUserNotNotified() throws RemoteException {
    public void testMediaOnBackgroundUser_foregroundUserNotNotified() throws RemoteException {
        AudioAttributes aa = new AudioAttributes.Builder()
                .setUsage(AudioAttributes.USAGE_MEDIA).build();
        UserInfo user = createUser("User", UserManager.USER_TYPE_FULL_SECONDARY, 0);
        final int bgUserUid = mSpiedContext.getUserId() * 100000;
        AudioFocusInfo afi = new AudioFocusInfo(aa, bgUserUid, "",
                /* packageName= */ "com.android.car.audio", AudioManager.AUDIOFOCUS_GAIN,
@@ -109,9 +121,9 @@ public class BackgroundUserSoundNotifierTest {
    }

    @Test
    public void testAlarmOnForegroundUser_ForegroundUserNotNotified() throws RemoteException {
    public void testAlarmOnForegroundUser_foregroundUserNotNotified() throws RemoteException {
        AudioAttributes aa = new AudioAttributes.Builder()
                .setUsage(AudioAttributes.USAGE_ALARM).build();
                .setUsage(USAGE_ALARM).build();
        final int fgUserId = mSpiedContext.getUserId();
        final int fgUserUid = fgUserId * 100000;
        doReturn(UserHandle.of(fgUserId)).when(mSpiedContext).getUser();
@@ -123,6 +135,109 @@ public class BackgroundUserSoundNotifierTest {
        verifyZeroInteractions(mNotificationManager);
    }

    @Test
    public void testMuteAlarmSounds() {
        final int fgUserId = mSpiedContext.getUserId();
        int bgUserId = fgUserId + 1;
        int bgUserUid = bgUserId * 100000;
        mBackgroundUserSoundNotifier.mNotificationClientUid = bgUserUid;

        AudioManager mockAudioManager = mock(AudioManager.class);
        when(mSpiedContext.getSystemService(AudioManager.class)).thenReturn(mockAudioManager);

        AudioPlaybackConfiguration apc1 = mock(AudioPlaybackConfiguration.class);
        when(apc1.getClientUid()).thenReturn(bgUserUid);
        when(apc1.getPlayerProxy()).thenReturn(mock(PlayerProxy.class));

        AudioPlaybackConfiguration apc2 = mock(AudioPlaybackConfiguration.class);
        when(apc2.getClientUid()).thenReturn(bgUserUid + 1);
        when(apc2.getPlayerProxy()).thenReturn(mock(PlayerProxy.class));

        List<AudioPlaybackConfiguration> configs = new ArrayList<>();
        configs.add(apc1);
        configs.add(apc2);
        when(mockAudioManager.getActivePlaybackConfigurations()).thenReturn(configs);

        AudioPolicy mockAudioPolicy = mock(AudioPolicy.class);

        AudioAttributes aa = new AudioAttributes.Builder().setUsage(USAGE_ALARM).build();
        AudioFocusInfo afi = new AudioFocusInfo(aa, bgUserUid, "", /* packageName= */ "",
                AudioManager.AUDIOFOCUS_GAIN, AudioManager.AUDIOFOCUS_NONE, /* flags= */ 0,
                Build.VERSION.SDK_INT);
        Stack<AudioFocusInfo> focusStack = new Stack<>();
        focusStack.add(afi);
        doReturn(focusStack).when(mockAudioPolicy).getFocusStack();
        mBackgroundUserSoundNotifier.mFocusControlAudioPolicy = mockAudioPolicy;

        mBackgroundUserSoundNotifier.muteAlarmSounds(mSpiedContext);

        verify(apc1.getPlayerProxy()).stop();
        verify(apc2.getPlayerProxy(), never()).stop();
    }

    @Test
    public void testOnAudioFocusGrant_alarmOnBackgroundUser_notifiesForegroundUser() {
        final int fgUserId = mSpiedContext.getUserId();
        UserInfo bgUser = createUser("Background User",  UserManager.USER_TYPE_FULL_SECONDARY, 0);
        int bgUserUid = bgUser.id * 100000;

        AudioAttributes aa = new AudioAttributes.Builder().setUsage(USAGE_ALARM).build();
        AudioFocusInfo afi = new AudioFocusInfo(aa, bgUserUid, "", "",
                AudioManager.AUDIOFOCUS_GAIN, 0, 0, Build.VERSION.SDK_INT);

        mBackgroundUserSoundNotifier.getAudioPolicyFocusListener()
                .onAudioFocusGrant(afi, AudioManager.AUDIOFOCUS_REQUEST_GRANTED);

        verify(mNotificationManager)
                .notifyAsUser(eq(BackgroundUserSoundNotifier.class.getSimpleName()),
                        eq(afi.getClientUid()), any(Notification.class),
                        eq(UserHandle.of(fgUserId)));
    }


    @Test
    public void testCreateNotification_UserSwitcherEnabled_bothActionsAvailable() {
        String userName = "BgUser";

        doReturn(true).when(mUserManager).isUserSwitcherEnabled();
        doReturn(UserManager.SWITCHABILITY_STATUS_OK)
                .when(mUserManager).getUserSwitchability(any());

        Notification notification = mBackgroundUserSoundNotifier.createNotification(userName,
                mSpiedContext);

        assertEquals("Alarm for BgUser", notification.extras.getString(
                Notification.EXTRA_TITLE));
        assertEquals(Notification.CATEGORY_REMINDER, notification.category);
        assertEquals(Notification.VISIBILITY_PUBLIC, notification.visibility);
        assertEquals(com.android.internal.R.drawable.ic_audio_alarm,
                notification.getSmallIcon().getResId());

        assertEquals(2, notification.actions.length);
        assertEquals(mSpiedContext.getString(
                com.android.internal.R.string.bg_user_sound_notification_button_mute),
                notification.actions[0].title);
        assertEquals(mSpiedContext.getString(
                com.android.internal.R.string.bg_user_sound_notification_button_switch_user),
                notification.actions[1].title);
    }

    @Test
    public void testCreateNotification_UserSwitcherDisabled_onlyMuteActionAvailable() {
        String userName = "BgUser";

        doReturn(false).when(mUserManager).isUserSwitcherEnabled();
        doReturn(UserManager.SWITCHABILITY_STATUS_USER_SWITCH_DISALLOWED)
                .when(mUserManager).getUserSwitchability(any());

        Notification notification = mBackgroundUserSoundNotifier.createNotification(userName,
                mSpiedContext);

        assertEquals(1, notification.actions.length);
        assertEquals(mSpiedContext.getString(
                com.android.internal.R.string.bg_user_sound_notification_button_mute),
                notification.actions[0].title);
    }

    private UserInfo createUser(String name, String userType, int flags) {
        UserInfo user = mUserManager.createUser(name, userType, flags);