diff options
5 files changed, 1232 insertions, 182 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java index 2147510bbde5..f32f1c2dcd25 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java @@ -16,9 +16,16 @@ package com.android.systemui.statusbar; import static android.app.admin.DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED; +import static android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_SECURE_NOTIFICATIONS; +import static android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS; +import static android.os.UserHandle.USER_NULL; +import static android.provider.Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS; +import static android.provider.Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS; import static com.android.systemui.DejankUtils.whitelistIpcs; +import android.annotation.SuppressLint; +import android.annotation.UserIdInt; import android.app.ActivityOptions; import android.app.KeyguardManager; import android.app.Notification; @@ -30,8 +37,10 @@ import android.content.IntentFilter; import android.content.IntentSender; import android.content.pm.UserInfo; import android.database.ContentObserver; +import android.net.Uri; import android.os.Handler; import android.os.HandlerExecutor; +import android.os.Looper; import android.os.UserHandle; import android.os.UserManager; import android.provider.Settings; @@ -41,14 +50,18 @@ import android.util.SparseBooleanArray; import androidx.annotation.NonNull; import androidx.annotation.VisibleForTesting; +import androidx.annotation.WorkerThread; import com.android.internal.statusbar.NotificationVisibility; import com.android.internal.widget.LockPatternUtils; import com.android.systemui.Dumpable; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.dagger.SysUISingleton; +import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dump.DumpManager; +import com.android.systemui.flags.FeatureFlagsClassic; +import com.android.systemui.flags.Flags; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener; import com.android.systemui.recents.OverviewProxyService; @@ -63,7 +76,9 @@ import com.android.systemui.util.settings.SecureSettings; import java.io.PrintWriter; import java.util.ArrayList; +import java.util.Collection; import java.util.List; +import java.util.concurrent.Executor; import javax.inject.Inject; @@ -84,6 +99,11 @@ public class NotificationLockscreenUserManagerImpl implements private final SecureSettings mSecureSettings; private final Object mLock = new Object(); + private static final Uri SHOW_LOCKSCREEN = + Settings.Secure.getUriFor(LOCK_SCREEN_SHOW_NOTIFICATIONS); + private static final Uri SHOW_PRIVATE_LOCKSCREEN = + Settings.Secure.getUriFor(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS); + private final Lazy<NotificationVisibilityProvider> mVisibilityProviderLazy; private final Lazy<CommonNotifCollection> mCommonNotifCollectionLazy; private final DevicePolicyManager mDevicePolicyManager; @@ -91,6 +111,23 @@ public class NotificationLockscreenUserManagerImpl implements private final SparseBooleanArray mUsersWithSeparateWorkChallenge = new SparseBooleanArray(); private final SparseBooleanArray mUsersAllowingPrivateNotifications = new SparseBooleanArray(); private final SparseBooleanArray mUsersAllowingNotifications = new SparseBooleanArray(); + + // The variables between mUsersDpcAllowingNotifications and + // mUsersUsersAllowingPrivateNotifications (inclusive) are written on a background thread + // and read on the main thread. Because the pipeline needs these values, adding locks would + // introduce too much jank. This means that some pipeline runs could get incorrect values, that + // would be fixed on the next pipeline run. We think this will be rare since a pipeline run + // would have to overlap with a DPM sync or a user changing a value in Settings, and we run the + // pipeline frequently enough that it should be corrected by the next time it matters for the + // user. + private final SparseBooleanArray mUsersDpcAllowingNotifications = new SparseBooleanArray(); + private final SparseBooleanArray mUsersUsersAllowingNotifications = new SparseBooleanArray(); + private boolean mKeyguardAllowingNotifications = true; + private final SparseBooleanArray mUsersDpcAllowingPrivateNotifications + = new SparseBooleanArray(); + private final SparseBooleanArray mUsersUsersAllowingPrivateNotifications + = new SparseBooleanArray(); + private final SparseBooleanArray mUsersInLockdownLatestResult = new SparseBooleanArray(); private final SparseBooleanArray mShouldHideNotifsLatestResult = new SparseBooleanArray(); private final UserManager mUserManager; @@ -99,24 +136,39 @@ public class NotificationLockscreenUserManagerImpl implements private final BroadcastDispatcher mBroadcastDispatcher; private final NotificationClickNotifier mClickNotifier; private final Lazy<OverviewProxyService> mOverviewProxyServiceLazy; + private final FeatureFlagsClassic mFeatureFlags; private boolean mShowLockscreenNotifications; private LockPatternUtils mLockPatternUtils; protected KeyguardManager mKeyguardManager; private int mState = StatusBarState.SHADE; private final ListenerSet<NotificationStateChangedListener> mNotifStateChangedListeners = new ListenerSet<>(); + private final Collection<Uri> mLockScreenUris = new ArrayList<>(); + protected final BroadcastReceiver mAllUsersReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { final String action = intent.getAction(); - if (ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED.equals(action) && - isCurrentProfile(getSendingUserId())) { - mUsersAllowingPrivateNotifications.clear(); - updateLockscreenNotificationSetting(); - // TODO(b/231976036): Consolidate pipeline invalidations related to this event - // notifyNotificationStateChanged(); + if (ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED.equals(action)) { + if (mFeatureFlags.isEnabled(Flags.NOTIF_LS_BACKGROUND_THREAD)) { + boolean changed = updateDpcSettings(getSendingUserId()); + if (mCurrentUserId == getSendingUserId()) { + changed |= updateLockscreenNotificationSetting(); + } + if (changed) { + notifyNotificationStateChanged(); + } + } else { + if (isCurrentProfile(getSendingUserId())) { + mUsersAllowingPrivateNotifications.clear(); + updateLockscreenNotificationSetting(); + // TODO(b/231976036): Consolidate pipeline invalidations related to this + // event + // notifyNotificationStateChanged(); + } + } } } }; @@ -136,6 +188,14 @@ public class NotificationLockscreenUserManagerImpl implements updateCurrentProfilesCache(); break; case Intent.ACTION_USER_ADDED: + updateCurrentProfilesCache(); + if (mFeatureFlags.isEnabled(Flags.NOTIF_LS_BACKGROUND_THREAD)) { + final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, USER_NULL); + mBackgroundHandler.post(() -> { + initValuesForUser(userId); + }); + } + break; case Intent.ACTION_MANAGED_PROFILE_AVAILABLE: case Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE: updateCurrentProfilesCache(); @@ -193,6 +253,8 @@ public class NotificationLockscreenUserManagerImpl implements protected final Context mContext; private final Handler mMainHandler; + private final Handler mBackgroundHandler; + private final Executor mBackgroundExecutor; protected final SparseArray<UserInfo> mCurrentProfiles = new SparseArray<>(); protected final SparseArray<UserInfo> mCurrentManagedProfiles = new SparseArray<>(); @@ -214,13 +276,18 @@ public class NotificationLockscreenUserManagerImpl implements KeyguardManager keyguardManager, StatusBarStateController statusBarStateController, @Main Handler mainHandler, + @Background Handler backgroundHandler, + @Background Executor backgroundExecutor, DeviceProvisionedController deviceProvisionedController, KeyguardStateController keyguardStateController, SecureSettings secureSettings, DumpManager dumpManager, - LockPatternUtils lockPatternUtils) { + LockPatternUtils lockPatternUtils, + FeatureFlagsClassic featureFlags) { mContext = context; mMainHandler = mainHandler; + mBackgroundHandler = backgroundHandler; + mBackgroundExecutor = backgroundExecutor; mDevicePolicyManager = devicePolicyManager; mUserManager = userManager; mUserTracker = userTracker; @@ -236,6 +303,10 @@ public class NotificationLockscreenUserManagerImpl implements mDeviceProvisionedController = deviceProvisionedController; mSecureSettings = secureSettings; mKeyguardStateController = keyguardStateController; + mFeatureFlags = featureFlags; + + mLockScreenUris.add(SHOW_LOCKSCREEN); + mLockScreenUris.add(SHOW_PRIVATE_LOCKSCREEN); dumpManager.registerDumpable(this); } @@ -243,16 +314,53 @@ public class NotificationLockscreenUserManagerImpl implements public void setUpWithPresenter(NotificationPresenter presenter) { mPresenter = presenter; - mLockscreenSettingsObserver = new ContentObserver(mMainHandler) { + mLockscreenSettingsObserver = new ContentObserver( + mFeatureFlags.isEnabled(Flags.NOTIF_LS_BACKGROUND_THREAD) + ? mBackgroundHandler + : mMainHandler) { + @Override - public void onChange(boolean selfChange) { - // We don't know which user changed LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS or - // LOCK_SCREEN_SHOW_NOTIFICATIONS, so we just dump our cache ... - mUsersAllowingPrivateNotifications.clear(); - mUsersAllowingNotifications.clear(); - // ... and refresh all the notifications - updateLockscreenNotificationSetting(); - notifyNotificationStateChanged(); + public void onChange(boolean selfChange, Collection<Uri> uris, int flags) { + if (mFeatureFlags.isEnabled(Flags.NOTIF_LS_BACKGROUND_THREAD)) { + @SuppressLint("MissingPermission") + List<UserInfo> users = mUserManager.getUsers(); + for (int i = users.size() - 1; i >= 0; i--) { + onChange(selfChange, uris, flags,users.get(i).getUserHandle()); + } + } else { + // We don't know which user changed LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS or + // LOCK_SCREEN_SHOW_NOTIFICATIONS, so we just dump our cache ... + mUsersAllowingPrivateNotifications.clear(); + mUsersAllowingNotifications.clear(); + // ... and refresh all the notifications + updateLockscreenNotificationSetting(); + notifyNotificationStateChanged(); + } + } + + // Note: even though this is an override, this method is not called by the OS + // since we're not in system_server. We are using it internally for cases when + // we have a single user id available (e.g. from USER_ADDED). + @Override + public void onChange(boolean selfChange, Collection<Uri> uris, + int flags, UserHandle user) { + if (mFeatureFlags.isEnabled(Flags.NOTIF_LS_BACKGROUND_THREAD)) { + boolean changed = false; + for (Uri uri: uris) { + if (SHOW_LOCKSCREEN.equals(uri)) { + changed |= updateUserShowSettings(user.getIdentifier()); + } else if (SHOW_PRIVATE_LOCKSCREEN.equals(uri)) { + changed |= updateUserShowPrivateSettings(user.getIdentifier()); + } + } + + if (mCurrentUserId == user.getIdentifier()) { + changed |= updateLockscreenNotificationSetting(); + } + if (changed) { + notifyNotificationStateChanged(); + } + } } }; @@ -268,23 +376,26 @@ public class NotificationLockscreenUserManagerImpl implements }; mContext.getContentResolver().registerContentObserver( - mSecureSettings.getUriFor(Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS), false, + SHOW_LOCKSCREEN, false, mLockscreenSettingsObserver, UserHandle.USER_ALL); mContext.getContentResolver().registerContentObserver( - mSecureSettings.getUriFor(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS), + SHOW_PRIVATE_LOCKSCREEN, true, mLockscreenSettingsObserver, UserHandle.USER_ALL); - mContext.getContentResolver().registerContentObserver( - Settings.Global.getUriFor(Settings.Global.ZEN_MODE), false, - mSettingsObserver); + if (!mFeatureFlags.isEnabled(Flags.NOTIF_LS_BACKGROUND_THREAD)) { + mContext.getContentResolver().registerContentObserver( + Settings.Global.getUriFor(Settings.Global.ZEN_MODE), false, + mSettingsObserver); + } mBroadcastDispatcher.registerReceiver(mAllUsersReceiver, new IntentFilter(ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED), - null /* handler */, UserHandle.ALL); + mFeatureFlags.isEnabled(Flags.NOTIF_LS_BACKGROUND_THREAD) + ? mBackgroundExecutor : null, UserHandle.ALL); IntentFilter filter = new IntentFilter(); filter.addAction(Intent.ACTION_USER_ADDED); @@ -305,7 +416,23 @@ public class NotificationLockscreenUserManagerImpl implements mCurrentUserId = mUserTracker.getUserId(); // in case we reg'd receiver too late updateCurrentProfilesCache(); - mSettingsObserver.onChange(false); // set up + if (mFeatureFlags.isEnabled(Flags.NOTIF_LS_BACKGROUND_THREAD)) { + // Set up + mBackgroundHandler.post(() -> { + @SuppressLint("MissingPermission") List<UserInfo> users = mUserManager.getUsers(); + for (int i = users.size() - 1; i >= 0; i--) { + initValuesForUser(users.get(i).id); + } + }); + } else { + mSettingsObserver.onChange(false); // set up + } + } + + private void initValuesForUser(@UserIdInt int userId) { + mLockscreenSettingsObserver.onChange( + false, mLockScreenUris, 0, UserHandle.of(userId)); + updateDpcSettings(userId); } public boolean shouldShowLockscreenNotifications() { @@ -322,17 +449,75 @@ public class NotificationLockscreenUserManagerImpl implements mShowLockscreenNotifications = show; } - protected void updateLockscreenNotificationSetting() { - final boolean show = mSecureSettings.getIntForUser( - Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, - 1, - mCurrentUserId) != 0; - final int dpmFlags = mDevicePolicyManager.getKeyguardDisabledFeatures( - null /* admin */, mCurrentUserId); - final boolean allowedByDpm = (dpmFlags - & DevicePolicyManager.KEYGUARD_DISABLE_SECURE_NOTIFICATIONS) == 0; + protected boolean updateLockscreenNotificationSetting() { + boolean show; + boolean allowedByDpm; + + if (mFeatureFlags.isEnabled(Flags.NOTIF_LS_BACKGROUND_THREAD)) { + show = mUsersUsersAllowingNotifications.get(mCurrentUserId) + && mKeyguardAllowingNotifications; + // If DPC never notified us about a user, that means they have no policy for the user, + // and they allow the behavior + allowedByDpm = mUsersDpcAllowingNotifications.get(mCurrentUserId, true); + } else { + show = mSecureSettings.getIntForUser( + LOCK_SCREEN_SHOW_NOTIFICATIONS, + 1, + mCurrentUserId) != 0; + final int dpmFlags = mDevicePolicyManager.getKeyguardDisabledFeatures( + null /* admin */, mCurrentUserId); + allowedByDpm = (dpmFlags + & KEYGUARD_DISABLE_SECURE_NOTIFICATIONS) == 0; + } + final boolean oldValue = mShowLockscreenNotifications; setShowLockscreenNotifications(show && allowedByDpm); + + return oldValue != mShowLockscreenNotifications; + } + + @WorkerThread + protected boolean updateDpcSettings(int userId) { + boolean originalAllowLockscreen = mUsersDpcAllowingNotifications.get(userId); + boolean originalAllowPrivate = mUsersDpcAllowingPrivateNotifications.get(userId); + final int dpmFlags = mDevicePolicyManager.getKeyguardDisabledFeatures( + null /* admin */, userId); + final boolean allowedLockscreen = (dpmFlags & KEYGUARD_DISABLE_SECURE_NOTIFICATIONS) == 0; + final boolean allowedPrivate = (dpmFlags & KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS) == 0; + mUsersDpcAllowingNotifications.put(userId, allowedLockscreen); + mUsersDpcAllowingPrivateNotifications.put(userId, allowedPrivate); + return (originalAllowLockscreen != allowedLockscreen) + || (originalAllowPrivate != allowedPrivate); + } + + @WorkerThread + private boolean updateUserShowSettings(int userId) { + boolean originalAllowLockscreen = mUsersUsersAllowingNotifications.get(userId); + boolean newAllowLockscreen = mSecureSettings.getIntForUser( + LOCK_SCREEN_SHOW_NOTIFICATIONS, + 1, + userId) != 0; + mUsersUsersAllowingNotifications.put(userId, newAllowLockscreen); + boolean keyguardChanged = updateGlobalKeyguardSettings(); + return (newAllowLockscreen != originalAllowLockscreen) || keyguardChanged; + } + + @WorkerThread + private boolean updateUserShowPrivateSettings(int userId) { + boolean originalValue = mUsersUsersAllowingPrivateNotifications.get(userId); + boolean newValue = mSecureSettings.getIntForUser( + LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, + 0, + userId) != 0; + mUsersUsersAllowingPrivateNotifications.put(userId, newValue); + return (newValue != originalValue); + } + + @WorkerThread + private boolean updateGlobalKeyguardSettings() { + final boolean oldValue = mKeyguardAllowingNotifications; + mKeyguardAllowingNotifications = mKeyguardManager.getPrivateNotificationsAllowed(); + return oldValue != mKeyguardAllowingNotifications; } /** @@ -340,21 +525,41 @@ public class NotificationLockscreenUserManagerImpl implements * when the lockscreen is in "public" (secure & locked) mode? */ public boolean userAllowsPrivateNotificationsInPublic(int userHandle) { - if (userHandle == UserHandle.USER_ALL) { - return true; - } + if (mFeatureFlags.isEnabled(Flags.NOTIF_LS_BACKGROUND_THREAD)) { + if (userHandle == UserHandle.USER_ALL) { + userHandle = mCurrentUserId; + } + if (mUsersUsersAllowingPrivateNotifications.indexOfKey(userHandle) < 0) { + // TODO(b/301955929): STOP_SHIP (stop flag flip): remove this read and use a safe + // default value before moving to 'released' + Log.wtf(TAG, "Asking for redact notifs setting too early", new Throwable()); + updateUserShowPrivateSettings(userHandle); + } + if (mUsersDpcAllowingPrivateNotifications.indexOfKey(userHandle) < 0) { + // TODO(b/301955929): STOP_SHIP (stop flag flip): remove this read and use a safe + // default value before moving to 'released' + Log.wtf(TAG, "Asking for redact notifs dpm override too early", new Throwable()); + updateDpcSettings(userHandle); + } + return mUsersUsersAllowingPrivateNotifications.get(userHandle) + && mUsersDpcAllowingPrivateNotifications.get(userHandle); + } else { + if (userHandle == UserHandle.USER_ALL) { + return true; + } - if (mUsersAllowingPrivateNotifications.indexOfKey(userHandle) < 0) { - final boolean allowedByUser = 0 != mSecureSettings.getIntForUser( - Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0, userHandle); - final boolean allowedByDpm = adminAllowsKeyguardFeature(userHandle, - DevicePolicyManager.KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS); - final boolean allowed = allowedByUser && allowedByDpm; - mUsersAllowingPrivateNotifications.append(userHandle, allowed); - return allowed; - } + if (mUsersAllowingPrivateNotifications.indexOfKey(userHandle) < 0) { + final boolean allowedByUser = 0 != mSecureSettings.getIntForUser( + LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0, userHandle); + final boolean allowedByDpm = adminAllowsKeyguardFeature(userHandle, + KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS); + final boolean allowed = allowedByUser && allowedByDpm; + mUsersAllowingPrivateNotifications.append(userHandle, allowed); + return allowed; + } - return mUsersAllowingPrivateNotifications.get(userHandle); + return mUsersAllowingPrivateNotifications.get(userHandle); + } } /** @@ -406,21 +611,44 @@ public class NotificationLockscreenUserManagerImpl implements * "public" (secure & locked) mode? */ public boolean userAllowsNotificationsInPublic(int userHandle) { - if (isCurrentProfile(userHandle) && userHandle != mCurrentUserId) { - return true; - } + if (mFeatureFlags.isEnabled(Flags.NOTIF_LS_BACKGROUND_THREAD)) { + // Unlike 'show private', settings does not show a copy of this setting for each + // profile, so it inherits from the parent user. + if (userHandle == UserHandle.USER_ALL || mCurrentManagedProfiles.contains(userHandle)) { + userHandle = mCurrentUserId; + } + if (mUsersUsersAllowingNotifications.indexOfKey(userHandle) < 0) { + // TODO(b/301955929): STOP_SHIP (stop flag flip): remove this read and use a safe + // default value before moving to 'released' + Log.wtf(TAG, "Asking for show notifs setting too early", new Throwable()); + updateUserShowSettings(userHandle); + } + if (mUsersDpcAllowingNotifications.indexOfKey(userHandle) < 0) { + // TODO(b/301955929): STOP_SHIP (stop flag flip): remove this read and use a safe + // default value before moving to 'released' + Log.wtf(TAG, "Asking for show notifs dpm override too early", new Throwable()); + updateDpcSettings(userHandle); + } + return mUsersUsersAllowingNotifications.get(userHandle) + && mUsersDpcAllowingNotifications.get(userHandle) + && mKeyguardAllowingNotifications; + } else { + if (isCurrentProfile(userHandle) && userHandle != mCurrentUserId) { + return true; + } - if (mUsersAllowingNotifications.indexOfKey(userHandle) < 0) { - final boolean allowedByUser = 0 != mSecureSettings.getIntForUser( - Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 0, userHandle); - final boolean allowedByDpm = adminAllowsKeyguardFeature(userHandle, - DevicePolicyManager.KEYGUARD_DISABLE_SECURE_NOTIFICATIONS); - final boolean allowedBySystem = mKeyguardManager.getPrivateNotificationsAllowed(); - final boolean allowed = allowedByUser && allowedByDpm && allowedBySystem; - mUsersAllowingNotifications.append(userHandle, allowed); - return allowed; + if (mUsersAllowingNotifications.indexOfKey(userHandle) < 0) { + final boolean allowedByUser = 0 != mSecureSettings.getIntForUser( + LOCK_SCREEN_SHOW_NOTIFICATIONS, 0, userHandle); + final boolean allowedByDpm = adminAllowsKeyguardFeature(userHandle, + KEYGUARD_DISABLE_SECURE_NOTIFICATIONS); + final boolean allowedBySystem = mKeyguardManager.getPrivateNotificationsAllowed(); + final boolean allowed = allowedByUser && allowedByDpm && allowedBySystem; + mUsersAllowingNotifications.append(userHandle, allowed); + return allowed; + } + return mUsersAllowingNotifications.get(userHandle); } - return mUsersAllowingNotifications.get(userHandle); } /** @return true if the entry needs redaction when on the lockscreen. */ @@ -451,9 +679,15 @@ public class NotificationLockscreenUserManagerImpl implements return true; } NotificationEntry entry = mCommonNotifCollectionLazy.get().getEntry(key); - return entry != null - && entry.getRanking().getLockscreenVisibilityOverride() - == Notification.VISIBILITY_PRIVATE; + if (mFeatureFlags.isEnabled(Flags.NOTIF_LS_BACKGROUND_THREAD)) { + return entry != null + && entry.getRanking().getChannel().getLockscreenVisibility() + == Notification.VISIBILITY_PRIVATE; + } else { + return entry != null + && entry.getRanking().getLockscreenVisibilityOverride() + == Notification.VISIBILITY_PRIVATE; + } } private void updateCurrentProfilesCache() { @@ -491,20 +725,6 @@ public class NotificationLockscreenUserManagerImpl implements } /** - * If any managed/work profiles are in public mode. - */ - public boolean isAnyManagedProfilePublicMode() { - synchronized (mLock) { - for (int i = mCurrentManagedProfiles.size() - 1; i >= 0; i--) { - if (isLockscreenPublicMode(mCurrentManagedProfiles.valueAt(i).id)) { - return true; - } - } - } - return false; - } - - /** * Returns the current user id. This can change if the user is switched. */ public int getCurrentUserId() { @@ -581,8 +801,16 @@ public class NotificationLockscreenUserManagerImpl implements } private void notifyNotificationStateChanged() { - for (NotificationStateChangedListener listener : mNotifStateChangedListeners) { - listener.onNotificationStateChanged(); + if (!Looper.getMainLooper().isCurrentThread()) { + mMainHandler.post(() -> { + for (NotificationStateChangedListener listener : mNotifStateChangedListeners) { + listener.onNotificationStateChanged(); + } + }); + } else { + for (NotificationStateChangedListener listener : mNotifStateChangedListeners) { + listener.onNotificationStateChanged(); + } } } @@ -620,5 +848,15 @@ public class NotificationLockscreenUserManagerImpl implements pw.println(mUsersInLockdownLatestResult); pw.print(" mShouldHideNotifsLatestResult="); pw.println(mShouldHideNotifsLatestResult); + pw.print(" mUsersDpcAllowingNotifications="); + pw.println(mUsersDpcAllowingNotifications); + pw.print(" mUsersUsersAllowingNotifications="); + pw.println(mUsersUsersAllowingNotifications); + pw.print(" mKeyguardAllowingNotifications="); + pw.println(mKeyguardAllowingNotifications); + pw.print(" mUsersDpcAllowingPrivateNotifications="); + pw.println(mUsersDpcAllowingPrivateNotifications); + pw.print(" mUsersUsersAllowingPrivateNotifications="); + pw.println(mUsersUsersAllowingPrivateNotifications); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProvider.kt index b2c32cd42d1d..db7f46eb28f2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProvider.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProvider.kt @@ -1,6 +1,5 @@ package com.android.systemui.statusbar.notification.interruption -import android.app.Notification import android.app.Notification.VISIBILITY_SECRET import android.content.Context import android.database.ContentObserver @@ -14,6 +13,8 @@ import com.android.keyguard.KeyguardUpdateMonitorCallback import com.android.systemui.CoreStartable import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Main +import com.android.systemui.flags.FeatureFlagsClassic +import com.android.systemui.flags.Flags import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.settings.UserTracker import com.android.systemui.statusbar.NotificationLockscreenUserManager @@ -78,7 +79,8 @@ class KeyguardNotificationVisibilityProviderImpl @Inject constructor( private val statusBarStateController: SysuiStatusBarStateController, private val userTracker: UserTracker, private val secureSettings: SecureSettings, - private val globalSettings: GlobalSettings + private val globalSettings: GlobalSettings, + private val featureFlags: FeatureFlagsClassic ) : CoreStartable, KeyguardNotificationVisibilityProvider { private val showSilentNotifsUri = secureSettings.getUriFor(Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS) @@ -201,7 +203,7 @@ class KeyguardNotificationVisibilityProviderImpl @Inject constructor( // device isn't public, no need to check public-related settings, so allow !lockscreenUserManager.isLockscreenPublicMode(user) -> false // entry is meant to be secret on the lockscreen, disallow - entry.ranking.lockscreenVisibilityOverride == Notification.VISIBILITY_SECRET -> true + isRankingVisibilitySecret(entry) -> true // disallow if user disallows notifications in public else -> !lockscreenUserManager.userAllowsNotificationsInPublic(user) } @@ -215,6 +217,17 @@ class KeyguardNotificationVisibilityProviderImpl @Inject constructor( } } + private fun isRankingVisibilitySecret(entry: NotificationEntry): Boolean { + return if (featureFlags.isEnabled(Flags.NOTIF_LS_BACKGROUND_THREAD)) { + // ranking.lockscreenVisibilityOverride contains possibly out of date DPC and Setting + // info, and NotificationLockscreenUserManagerImpl is already listening for updates + // to those + entry.ranking.channel.lockscreenVisibility == VISIBILITY_SECRET + } else { + entry.ranking.lockscreenVisibilityOverride == VISIBILITY_SECRET + } + } + override fun dump(pw: PrintWriter, args: Array<out String>) = pw.asIndenting().run { println("isLockedOrLocking=$isLockedOrLocking") withIncreasedIndent { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerMainThreadTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerMainThreadTest.java new file mode 100644 index 000000000000..3a9c24a7109c --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerMainThreadTest.java @@ -0,0 +1,597 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar; + +import static android.app.admin.DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED; +import static android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_SECURE_NOTIFICATIONS; +import static android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS; +import static android.provider.Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS; +import static android.provider.Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS; + +import static junit.framework.Assert.assertFalse; +import static junit.framework.Assert.assertTrue; + +import static org.mockito.Mockito.clearInvocations; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.app.ActivityManager; +import android.app.KeyguardManager; +import android.app.Notification; +import android.app.admin.DevicePolicyManager; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.pm.UserInfo; +import android.database.ContentObserver; +import android.net.Uri; +import android.os.Handler; +import android.os.Looper; +import android.os.UserHandle; +import android.os.UserManager; +import android.provider.Settings; +import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper; + +import androidx.test.filters.SmallTest; + +import com.android.internal.widget.LockPatternUtils; +import com.android.systemui.Dependency; +import com.android.systemui.SysuiTestCase; +import com.android.systemui.broadcast.BroadcastDispatcher; +import com.android.systemui.dump.DumpManager; +import com.android.systemui.flags.FakeFeatureFlagsClassic; +import com.android.systemui.flags.Flags; +import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.recents.OverviewProxyService; +import com.android.systemui.settings.UserTracker; +import com.android.systemui.statusbar.NotificationLockscreenUserManager.NotificationStateChangedListener; +import com.android.systemui.statusbar.notification.collection.NotificationEntry; +import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder; +import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection; +import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider; +import com.android.systemui.statusbar.policy.DeviceProvisionedController; +import com.android.systemui.statusbar.policy.KeyguardStateController; +import com.android.systemui.util.settings.FakeSettings; + +import com.google.android.collect.Lists; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.concurrent.Executor; + +@SmallTest +@RunWith(AndroidTestingRunner.class) +@TestableLooper.RunWithLooper +public class NotificationLockscreenUserManagerMainThreadTest extends SysuiTestCase { + @Mock + private NotificationPresenter mPresenter; + @Mock + private UserManager mUserManager; + @Mock + private UserTracker mUserTracker; + + // Dependency mocks: + @Mock + private NotificationVisibilityProvider mVisibilityProvider; + @Mock + private CommonNotifCollection mNotifCollection; + @Mock + private DevicePolicyManager mDevicePolicyManager; + @Mock + private NotificationClickNotifier mClickNotifier; + @Mock + private OverviewProxyService mOverviewProxyService; + @Mock + private KeyguardManager mKeyguardManager; + @Mock + private DeviceProvisionedController mDeviceProvisionedController; + @Mock + private StatusBarStateController mStatusBarStateController; + @Mock + private BroadcastDispatcher mBroadcastDispatcher; + @Mock + private KeyguardStateController mKeyguardStateController; + + private UserInfo mCurrentUser; + private UserInfo mSecondaryUser; + private UserInfo mWorkUser; + private FakeSettings mSettings; + private TestNotificationLockscreenUserManager mLockscreenUserManager; + private NotificationEntry mCurrentUserNotif; + private NotificationEntry mSecondaryUserNotif; + private NotificationEntry mWorkProfileNotif; + private final FakeFeatureFlagsClassic mFakeFeatureFlags = new FakeFeatureFlagsClassic(); + private Executor mBackgroundExecutor = Runnable::run; // Direct executor + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + + mFakeFeatureFlags.set(Flags.NOTIF_LS_BACKGROUND_THREAD, false); + + int currentUserId = ActivityManager.getCurrentUser(); + when(mUserTracker.getUserId()).thenReturn(currentUserId); + mSettings = new FakeSettings(); + mSettings.setUserId(ActivityManager.getCurrentUser()); + mCurrentUser = new UserInfo(currentUserId, "", 0); + mSecondaryUser = new UserInfo(currentUserId + 1, "", 0); + mWorkUser = new UserInfo(currentUserId + 2, "" /* name */, null /* iconPath */, 0, + UserManager.USER_TYPE_PROFILE_MANAGED); + + when(mKeyguardManager.getPrivateNotificationsAllowed()).thenReturn(true); + when(mUserManager.getProfiles(currentUserId)).thenReturn(Lists.newArrayList( + mCurrentUser, mWorkUser)); + when(mUserManager.getProfiles(mSecondaryUser.id)).thenReturn(Lists.newArrayList( + mSecondaryUser)); + mDependency.injectTestDependency(Dependency.MAIN_HANDLER, + Handler.createAsync(Looper.myLooper())); + + Notification notifWithPrivateVisibility = new Notification(); + notifWithPrivateVisibility.visibility = Notification.VISIBILITY_PRIVATE; + mCurrentUserNotif = new NotificationEntryBuilder() + .setNotification(notifWithPrivateVisibility) + .setUser(new UserHandle(mCurrentUser.id)) + .build(); + mSecondaryUserNotif = new NotificationEntryBuilder() + .setNotification(notifWithPrivateVisibility) + .setUser(new UserHandle(mSecondaryUser.id)) + .build(); + mWorkProfileNotif = new NotificationEntryBuilder() + .setNotification(notifWithPrivateVisibility) + .setUser(new UserHandle(mWorkUser.id)) + .build(); + + mLockscreenUserManager = new TestNotificationLockscreenUserManager(mContext); + mLockscreenUserManager.setUpWithPresenter(mPresenter); + } + + private void changeSetting(String setting) { + final Collection<Uri> lockScreenUris = new ArrayList<>(); + lockScreenUris.add(Settings.Secure.getUriFor(setting)); + mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false, + lockScreenUris, 0); + } + + @Test + public void testLockScreenShowNotificationsFalse() { + mSettings.putInt(LOCK_SCREEN_SHOW_NOTIFICATIONS, 0); + changeSetting(LOCK_SCREEN_SHOW_NOTIFICATIONS); + assertFalse(mLockscreenUserManager.shouldShowLockscreenNotifications()); + } + + @Test + public void testLockScreenShowNotificationsTrue() { + mSettings.putInt(LOCK_SCREEN_SHOW_NOTIFICATIONS, 1); + changeSetting(LOCK_SCREEN_SHOW_NOTIFICATIONS); + assertTrue(mLockscreenUserManager.shouldShowLockscreenNotifications()); + } + + @Test + public void testLockScreenAllowPrivateNotificationsTrue() { + mSettings.putInt(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1); + changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS); + assertTrue(mLockscreenUserManager.userAllowsPrivateNotificationsInPublic(mCurrentUser.id)); + } + + @Test + public void testLockScreenAllowPrivateNotificationsFalse() { + mSettings.putIntForUser(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0, + mCurrentUser.id); + changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS); + assertFalse(mLockscreenUserManager.userAllowsPrivateNotificationsInPublic(mCurrentUser.id)); + } + + @Test + public void testLockScreenAllowsWorkPrivateNotificationsFalse() { + mSettings.putIntForUser(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0, + mWorkUser.id); + changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS); + assertFalse(mLockscreenUserManager.userAllowsPrivateNotificationsInPublic(mWorkUser.id)); + } + + @Test + public void testLockScreenAllowsWorkPrivateNotificationsTrue() { + mSettings.putIntForUser(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1, + mWorkUser.id); + changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS); + assertTrue(mLockscreenUserManager.userAllowsPrivateNotificationsInPublic(mWorkUser.id)); + } + + @Test + public void testCurrentUserPrivateNotificationsNotRedacted() { + // GIVEN current user doesn't allow private notifications to show + mSettings.putIntForUser(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0, + mCurrentUser.id); + changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS); + + // THEN current user's notification is redacted + assertTrue(mLockscreenUserManager.needsRedaction(mCurrentUserNotif)); + } + + @Test + public void testCurrentUserPrivateNotificationsRedacted() { + // GIVEN current user allows private notifications to show + mSettings.putIntForUser(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1, + mCurrentUser.id); + changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS); + + // THEN current user's notification isn't redacted + assertFalse(mLockscreenUserManager.needsRedaction(mCurrentUserNotif)); + } + + @Test + public void testWorkPrivateNotificationsRedacted() { + // GIVEN work profile doesn't private notifications to show + mSettings.putIntForUser(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0, + mWorkUser.id); + changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS); + + // THEN work profile notification is redacted + assertTrue(mLockscreenUserManager.needsRedaction(mWorkProfileNotif)); + assertFalse(mLockscreenUserManager.allowsManagedPrivateNotificationsInPublic()); + } + + @Test + public void testWorkPrivateNotificationsNotRedacted() { + // GIVEN work profile allows private notifications to show + mSettings.putIntForUser(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1, + mWorkUser.id); + changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS); + + // THEN work profile notification isn't redacted + assertFalse(mLockscreenUserManager.needsRedaction(mWorkProfileNotif)); + assertTrue(mLockscreenUserManager.allowsManagedPrivateNotificationsInPublic()); + } + + @Test + public void testWorkPrivateNotificationsNotRedacted_otherUsersRedacted() { + // GIVEN work profile allows private notifications to show but the other users don't + mSettings.putIntForUser(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1, + mWorkUser.id); + mSettings.putIntForUser(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0, + mCurrentUser.id); + mSettings.putIntForUser(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0, + mSecondaryUser.id); + changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS); + changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS); + changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS); + + // THEN the work profile notification doesn't need to be redacted + assertFalse(mLockscreenUserManager.needsRedaction(mWorkProfileNotif)); + + // THEN the current user and secondary user notifications do need to be redacted + assertTrue(mLockscreenUserManager.needsRedaction(mCurrentUserNotif)); + assertTrue(mLockscreenUserManager.needsRedaction(mSecondaryUserNotif)); + } + + @Test + public void testWorkProfileRedacted_otherUsersNotRedacted() { + // GIVEN work profile doesn't allow private notifications to show but the other users do + mSettings.putIntForUser(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0, + mWorkUser.id); + mSettings.putIntForUser(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1, + mCurrentUser.id); + mSettings.putIntForUser(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1, + mSecondaryUser.id); + changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS); + changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS); + changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS); + + // THEN the work profile notification needs to be redacted + assertTrue(mLockscreenUserManager.needsRedaction(mWorkProfileNotif)); + + // THEN the current user and secondary user notifications don't need to be redacted + assertFalse(mLockscreenUserManager.needsRedaction(mCurrentUserNotif)); + assertFalse(mLockscreenUserManager.needsRedaction(mSecondaryUserNotif)); + } + + @Test + public void testSecondaryUserNotRedacted_currentUserRedacted() { + // GIVEN secondary profile allows private notifications to show but the current user + // doesn't allow private notifications to show + mSettings.putIntForUser(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0, + mCurrentUser.id); + mSettings.putIntForUser(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1, + mSecondaryUser.id); + changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS); + changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS); + + // THEN the secondary profile notification still needs to be redacted because the current + // user's setting takes precedence + assertTrue(mLockscreenUserManager.needsRedaction(mSecondaryUserNotif)); + } + + @Test + public void testUserSwitchedCallsOnUserSwitching() { + mLockscreenUserManager.getUserTrackerCallbackForTest().onUserChanging(mSecondaryUser.id, + mContext); + verify(mPresenter, times(1)).onUserSwitched(mSecondaryUser.id); + } + + @Test + public void testIsLockscreenPublicMode() { + assertFalse(mLockscreenUserManager.isLockscreenPublicMode(mCurrentUser.id)); + mLockscreenUserManager.setLockscreenPublicMode(true, mCurrentUser.id); + assertTrue(mLockscreenUserManager.isLockscreenPublicMode(mCurrentUser.id)); + } + + @Test + public void testUpdateIsPublicMode() { + when(mKeyguardStateController.isMethodSecure()).thenReturn(true); + + NotificationStateChangedListener listener = mock(NotificationStateChangedListener.class); + mLockscreenUserManager.addNotificationStateChangedListener(listener); + mLockscreenUserManager.mCurrentProfiles.append(0, mock(UserInfo.class)); + + // first call explicitly sets user 0 to not public; notifies + mLockscreenUserManager.updatePublicMode(); + TestableLooper.get(this).processAllMessages(); + assertFalse(mLockscreenUserManager.isLockscreenPublicMode(0)); + verify(listener).onNotificationStateChanged(); + clearInvocations(listener); + + // calling again has no changes; does not notify + mLockscreenUserManager.updatePublicMode(); + TestableLooper.get(this).processAllMessages(); + assertFalse(mLockscreenUserManager.isLockscreenPublicMode(0)); + verify(listener, never()).onNotificationStateChanged(); + + // Calling again with keyguard now showing makes user 0 public; notifies + when(mKeyguardStateController.isShowing()).thenReturn(true); + mLockscreenUserManager.updatePublicMode(); + TestableLooper.get(this).processAllMessages(); + assertTrue(mLockscreenUserManager.isLockscreenPublicMode(0)); + verify(listener).onNotificationStateChanged(); + clearInvocations(listener); + + // calling again has no changes; does not notify + mLockscreenUserManager.updatePublicMode(); + TestableLooper.get(this).processAllMessages(); + assertTrue(mLockscreenUserManager.isLockscreenPublicMode(0)); + verify(listener, never()).onNotificationStateChanged(); + } + + @Test + public void testDevicePolicyDoesNotAllowNotifications() { + // User allows them + mSettings.putIntForUser(LOCK_SCREEN_SHOW_NOTIFICATIONS, 1, mCurrentUser.id); + changeSetting(LOCK_SCREEN_SHOW_NOTIFICATIONS); + + // DevicePolicy hides notifs on lockscreen + when(mDevicePolicyManager.getKeyguardDisabledFeatures(null, mCurrentUser.id)) + .thenReturn(KEYGUARD_DISABLE_SECURE_NOTIFICATIONS); + + BroadcastReceiver.PendingResult pr = new BroadcastReceiver.PendingResult( + 0, null, null, 0, true, false, null, mCurrentUser.id, 0); + mLockscreenUserManager.mAllUsersReceiver.setPendingResult(pr); + mLockscreenUserManager.mAllUsersReceiver.onReceive(mContext, + new Intent(ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED)); + + assertFalse(mLockscreenUserManager.userAllowsNotificationsInPublic(mCurrentUser.id)); + } + + @Test + public void testDevicePolicyDoesNotAllowNotifications_secondary() { + Mockito.clearInvocations(mDevicePolicyManager); + // User allows notifications + mSettings.putIntForUser(LOCK_SCREEN_SHOW_NOTIFICATIONS, 1, mSecondaryUser.id); + changeSetting(LOCK_SCREEN_SHOW_NOTIFICATIONS); + + // DevicePolicy hides notifications + when(mDevicePolicyManager.getKeyguardDisabledFeatures(null, mSecondaryUser.id)) + .thenReturn(KEYGUARD_DISABLE_SECURE_NOTIFICATIONS); + + BroadcastReceiver.PendingResult pr = new BroadcastReceiver.PendingResult( + 0, null, null, 0, true, false, null, mSecondaryUser.id, 0); + mLockscreenUserManager.mAllUsersReceiver.setPendingResult(pr); + mLockscreenUserManager.mAllUsersReceiver.onReceive(mContext, + new Intent(ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED)); + + assertFalse(mLockscreenUserManager.userAllowsNotificationsInPublic(mSecondaryUser.id)); + } + + @Test + public void testDevicePolicy_noPrivateNotifications() { + Mockito.clearInvocations(mDevicePolicyManager); + // User allows notifications + mSettings.putIntForUser(LOCK_SCREEN_SHOW_NOTIFICATIONS, 1, mCurrentUser.id); + changeSetting(LOCK_SCREEN_SHOW_NOTIFICATIONS); + + // DevicePolicy hides sensitive content + when(mDevicePolicyManager.getKeyguardDisabledFeatures(null, mCurrentUser.id)) + .thenReturn(KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS); + + BroadcastReceiver.PendingResult pr = new BroadcastReceiver.PendingResult( + 0, null, null, 0, true, false, null, mCurrentUser.id, 0); + mLockscreenUserManager.mAllUsersReceiver.setPendingResult(pr); + mLockscreenUserManager.mAllUsersReceiver.onReceive(mContext, + new Intent(ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED)); + + assertTrue(mLockscreenUserManager.needsRedaction(mCurrentUserNotif)); + } + + @Test + public void testDevicePolicy_noPrivateNotifications_userAll() { + // User allows notifications + mSettings.putIntForUser(LOCK_SCREEN_SHOW_NOTIFICATIONS, 1, mCurrentUser.id); + changeSetting(LOCK_SCREEN_SHOW_NOTIFICATIONS); + + // DevicePolicy hides sensitive content + when(mDevicePolicyManager.getKeyguardDisabledFeatures(null, mCurrentUser.id)) + .thenReturn(KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS); + + BroadcastReceiver.PendingResult pr = new BroadcastReceiver.PendingResult( + 0, null, null, 0, true, false, null, mCurrentUser.id, 0); + mLockscreenUserManager.mAllUsersReceiver.setPendingResult(pr); + mLockscreenUserManager.mAllUsersReceiver.onReceive(mContext, + new Intent(ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED)); + + assertTrue(mLockscreenUserManager.needsRedaction(new NotificationEntryBuilder() + .setNotification(new Notification()) + .setUser(UserHandle.ALL) + .build())); + } + + @Test + public void testDevicePolicyPrivateNotifications_secondary() { + Mockito.clearInvocations(mDevicePolicyManager); + // User allows notifications + mSettings.putIntForUser(LOCK_SCREEN_SHOW_NOTIFICATIONS, 1, mSecondaryUser.id); + changeSetting(LOCK_SCREEN_SHOW_NOTIFICATIONS); + + // DevicePolicy hides sensitive content + when(mDevicePolicyManager.getKeyguardDisabledFeatures(null, mSecondaryUser.id)) + .thenReturn(KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS); + + BroadcastReceiver.PendingResult pr = new BroadcastReceiver.PendingResult( + 0, null, null, 0, true, false, null, mSecondaryUser.id, 0); + mLockscreenUserManager.mAllUsersReceiver.setPendingResult(pr); + mLockscreenUserManager.mAllUsersReceiver.onReceive(mContext, + new Intent(ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED)); + + mLockscreenUserManager.mUserChangedCallback.onUserChanging(mSecondaryUser.id, mContext); + assertTrue(mLockscreenUserManager.needsRedaction(mSecondaryUserNotif)); + } + + @Test + public void testHideNotifications_primary() { + mSettings.putIntForUser(LOCK_SCREEN_SHOW_NOTIFICATIONS, 0, mCurrentUser.id); + changeSetting(LOCK_SCREEN_SHOW_NOTIFICATIONS); + + assertFalse(mLockscreenUserManager.userAllowsNotificationsInPublic(mCurrentUser.id)); + } + + @Test + public void testHideNotifications_secondary() { + mSettings.putIntForUser(LOCK_SCREEN_SHOW_NOTIFICATIONS, 0, mSecondaryUser.id); + changeSetting(LOCK_SCREEN_SHOW_NOTIFICATIONS); + + assertFalse(mLockscreenUserManager.userAllowsNotificationsInPublic(mSecondaryUser.id)); + } + + @Test + public void testHideNotifications_secondary_userSwitch() { + mSettings.putIntForUser(LOCK_SCREEN_SHOW_NOTIFICATIONS, 0, mSecondaryUser.id); + changeSetting(LOCK_SCREEN_SHOW_NOTIFICATIONS); + + mLockscreenUserManager.mUserChangedCallback.onUserChanging(mSecondaryUser.id, mContext); + + assertFalse(mLockscreenUserManager.userAllowsNotificationsInPublic(mSecondaryUser.id)); + } + + @Test + public void testShowNotifications_secondary_userSwitch() { + mSettings.putIntForUser(LOCK_SCREEN_SHOW_NOTIFICATIONS, 1, mSecondaryUser.id); + changeSetting(LOCK_SCREEN_SHOW_NOTIFICATIONS); + + mLockscreenUserManager.mUserChangedCallback.onUserChanging(mSecondaryUser.id, mContext); + + assertTrue(mLockscreenUserManager.userAllowsNotificationsInPublic(mSecondaryUser.id)); + } + + @Test + public void testUserAllowsNotificationsInPublic_keyguardManagerNoPrivateNotifications() { + // DevicePolicy allows notifications + when(mDevicePolicyManager.getKeyguardDisabledFeatures(null, mCurrentUser.id)) + .thenReturn(0); + BroadcastReceiver.PendingResult pr = new BroadcastReceiver.PendingResult( + 0, null, null, 0, true, false, null, mCurrentUser.id, 0); + mLockscreenUserManager.mAllUsersReceiver.setPendingResult(pr); + mLockscreenUserManager.mAllUsersReceiver.onReceive(mContext, + new Intent(ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED)); + + // KeyguardManager does not allow notifications + when(mKeyguardManager.getPrivateNotificationsAllowed()).thenReturn(false); + + // User allows notifications + mSettings.putIntForUser(LOCK_SCREEN_SHOW_NOTIFICATIONS, 1, mCurrentUser.id); + // We shouldn't need to call this method, but getPrivateNotificationsAllowed has no + // callback, so it's only updated when the setting is + changeSetting(LOCK_SCREEN_SHOW_NOTIFICATIONS); + + assertFalse(mLockscreenUserManager.userAllowsNotificationsInPublic(mCurrentUser.id)); + } + + @Test + public void testUserAllowsNotificationsInPublic_settingsChange() { + // User allows notifications + mSettings.putIntForUser(LOCK_SCREEN_SHOW_NOTIFICATIONS, 1, mCurrentUser.id); + changeSetting(LOCK_SCREEN_SHOW_NOTIFICATIONS); + + assertTrue(mLockscreenUserManager.userAllowsNotificationsInPublic(mCurrentUser.id)); + + // User disables + mSettings.putIntForUser(LOCK_SCREEN_SHOW_NOTIFICATIONS, 0, mCurrentUser.id); + changeSetting(LOCK_SCREEN_SHOW_NOTIFICATIONS); + + assertFalse(mLockscreenUserManager.userAllowsNotificationsInPublic(mCurrentUser.id)); + } + + private class TestNotificationLockscreenUserManager + extends NotificationLockscreenUserManagerImpl { + public TestNotificationLockscreenUserManager(Context context) { + super( + context, + mBroadcastDispatcher, + mDevicePolicyManager, + mUserManager, + mUserTracker, + (() -> mVisibilityProvider), + (() -> mNotifCollection), + mClickNotifier, + (() -> mOverviewProxyService), + NotificationLockscreenUserManagerMainThreadTest.this.mKeyguardManager, + mStatusBarStateController, + Handler.createAsync(Looper.myLooper()), + Handler.createAsync(Looper.myLooper()), + mBackgroundExecutor, + mDeviceProvisionedController, + mKeyguardStateController, + mSettings, + mock(DumpManager.class), + mock(LockPatternUtils.class), + mFakeFeatureFlags); + } + + public BroadcastReceiver getBaseBroadcastReceiverForTest() { + return mBaseBroadcastReceiver; + } + + public UserTracker.Callback getUserTrackerCallbackForTest() { + return mUserChangedCallback; + } + + public ContentObserver getLockscreenSettingsObserverForTest() { + return mLockscreenSettingsObserver; + } + + public ContentObserver getSettingsObserverForTest() { + return mSettingsObserver; + } + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java index 19863ecaf723..a5f5fc7e36f2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java @@ -16,17 +16,23 @@ package com.android.systemui.statusbar; +import static android.app.Notification.VISIBILITY_PRIVATE; +import static android.app.NotificationManager.IMPORTANCE_HIGH; +import static android.app.NotificationManager.VISIBILITY_NO_OVERRIDE; import static android.app.admin.DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED; import static android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_SECURE_NOTIFICATIONS; import static android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS; import static android.os.UserHandle.USER_ALL; +import static android.provider.Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS; import static android.provider.Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS; import static junit.framework.Assert.assertFalse; +import static junit.framework.Assert.assertNotNull; import static junit.framework.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.atMost; import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.mock; @@ -38,12 +44,15 @@ import static org.mockito.Mockito.when; import android.app.ActivityManager; import android.app.KeyguardManager; import android.app.Notification; +import android.app.NotificationChannel; import android.app.admin.DevicePolicyManager; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.pm.UserInfo; import android.database.ContentObserver; +import android.net.Uri; +import android.os.Bundle; import android.os.Handler; import android.os.Looper; import android.os.UserHandle; @@ -59,6 +68,8 @@ import com.android.systemui.Dependency; import com.android.systemui.SysuiTestCase; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.dump.DumpManager; +import com.android.systemui.flags.FakeFeatureFlagsClassic; +import com.android.systemui.flags.Flags; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.recents.OverviewProxyService; import com.android.systemui.settings.UserTracker; @@ -74,12 +85,16 @@ import com.android.systemui.util.settings.FakeSettings; import com.google.android.collect.Lists; import org.junit.Before; -import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; +import org.mockito.Mockito; import org.mockito.MockitoAnnotations; +import java.util.ArrayList; +import java.util.Collection; +import java.util.concurrent.Executor; + @SmallTest @RunWith(AndroidTestingRunner.class) @TestableLooper.RunWithLooper @@ -121,11 +136,15 @@ public class NotificationLockscreenUserManagerTest extends SysuiTestCase { private NotificationEntry mCurrentUserNotif; private NotificationEntry mSecondaryUserNotif; private NotificationEntry mWorkProfileNotif; + private final FakeFeatureFlagsClassic mFakeFeatureFlags = new FakeFeatureFlagsClassic(); + private Executor mBackgroundExecutor = Runnable::run; // Direct executor @Before public void setUp() { MockitoAnnotations.initMocks(this); + mFakeFeatureFlags.set(Flags.NOTIF_LS_BACKGROUND_THREAD, true); + int currentUserId = ActivityManager.getCurrentUser(); when(mUserTracker.getUserId()).thenReturn(currentUserId); mSettings = new FakeSettings(); @@ -138,103 +157,144 @@ public class NotificationLockscreenUserManagerTest extends SysuiTestCase { when(mKeyguardManager.getPrivateNotificationsAllowed()).thenReturn(true); when(mUserManager.getProfiles(currentUserId)).thenReturn(Lists.newArrayList( mCurrentUser, mWorkUser)); + when(mUserManager.getUsers()).thenReturn(Lists.newArrayList( + mCurrentUser, mWorkUser, mSecondaryUser)); when(mUserManager.getProfiles(mSecondaryUser.id)).thenReturn(Lists.newArrayList( mSecondaryUser)); mDependency.injectTestDependency(Dependency.MAIN_HANDLER, Handler.createAsync(Looper.myLooper())); Notification notifWithPrivateVisibility = new Notification(); - notifWithPrivateVisibility.visibility = Notification.VISIBILITY_PRIVATE; + notifWithPrivateVisibility.visibility = VISIBILITY_PRIVATE; mCurrentUserNotif = new NotificationEntryBuilder() .setNotification(notifWithPrivateVisibility) .setUser(new UserHandle(mCurrentUser.id)) .build(); + NotificationChannel channel = new NotificationChannel("1", "1", IMPORTANCE_HIGH); + channel.setLockscreenVisibility(VISIBILITY_NO_OVERRIDE); + mCurrentUserNotif.setRanking(new RankingBuilder(mCurrentUserNotif.getRanking()) + .setChannel(channel) + .setVisibilityOverride(VISIBILITY_NO_OVERRIDE).build()); + when(mNotifCollection.getEntry(mCurrentUserNotif.getKey())).thenReturn(mCurrentUserNotif); mSecondaryUserNotif = new NotificationEntryBuilder() .setNotification(notifWithPrivateVisibility) .setUser(new UserHandle(mSecondaryUser.id)) .build(); + mSecondaryUserNotif.setRanking(new RankingBuilder(mSecondaryUserNotif.getRanking()) + .setChannel(channel) + .setVisibilityOverride(VISIBILITY_NO_OVERRIDE).build()); + when(mNotifCollection.getEntry( + mSecondaryUserNotif.getKey())).thenReturn(mSecondaryUserNotif); mWorkProfileNotif = new NotificationEntryBuilder() .setNotification(notifWithPrivateVisibility) .setUser(new UserHandle(mWorkUser.id)) .build(); + mWorkProfileNotif.setRanking(new RankingBuilder(mWorkProfileNotif.getRanking()) + .setChannel(channel) + .setVisibilityOverride(VISIBILITY_NO_OVERRIDE).build()); + when(mNotifCollection.getEntry(mWorkProfileNotif.getKey())).thenReturn(mWorkProfileNotif); mLockscreenUserManager = new TestNotificationLockscreenUserManager(mContext); mLockscreenUserManager.setUpWithPresenter(mPresenter); } + private void changeSetting(String setting) { + final Collection<Uri> lockScreenUris = new ArrayList<>(); + lockScreenUris.add(Settings.Secure.getUriFor(setting)); + mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false, + lockScreenUris, 0); + } + @Test public void testLockScreenShowNotificationsFalse() { mSettings.putInt(LOCK_SCREEN_SHOW_NOTIFICATIONS, 0); - mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false); + changeSetting(LOCK_SCREEN_SHOW_NOTIFICATIONS); assertFalse(mLockscreenUserManager.shouldShowLockscreenNotifications()); } @Test public void testLockScreenShowNotificationsTrue() { mSettings.putInt(LOCK_SCREEN_SHOW_NOTIFICATIONS, 1); - mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false); + changeSetting(LOCK_SCREEN_SHOW_NOTIFICATIONS); assertTrue(mLockscreenUserManager.shouldShowLockscreenNotifications()); } @Test public void testLockScreenAllowPrivateNotificationsTrue() { - mSettings.putInt(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1); - mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false); + mSettings.putInt(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1); + changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS); assertTrue(mLockscreenUserManager.userAllowsPrivateNotificationsInPublic(mCurrentUser.id)); } @Test public void testLockScreenAllowPrivateNotificationsFalse() { - mSettings.putIntForUser(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0, + mSettings.putIntForUser(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0, mCurrentUser.id); - mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false); + changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS); assertFalse(mLockscreenUserManager.userAllowsPrivateNotificationsInPublic(mCurrentUser.id)); } @Test public void testLockScreenAllowsWorkPrivateNotificationsFalse() { - mSettings.putIntForUser(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0, + mSettings.putIntForUser(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0, mWorkUser.id); - mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false); + changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS); assertFalse(mLockscreenUserManager.userAllowsPrivateNotificationsInPublic(mWorkUser.id)); } @Test public void testLockScreenAllowsWorkPrivateNotificationsTrue() { - mSettings.putIntForUser(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1, + mSettings.putIntForUser(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1, mWorkUser.id); - mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false); + changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS); assertTrue(mLockscreenUserManager.userAllowsPrivateNotificationsInPublic(mWorkUser.id)); } @Test - public void testCurrentUserPrivateNotificationsNotRedacted() { + public void testCurrentUserPrivateNotificationsRedacted() { // GIVEN current user doesn't allow private notifications to show - mSettings.putIntForUser(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0, + mSettings.putIntForUser(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0, mCurrentUser.id); - mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false); + changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS); // THEN current user's notification is redacted assertTrue(mLockscreenUserManager.needsRedaction(mCurrentUserNotif)); } @Test - public void testCurrentUserPrivateNotificationsRedacted() { + public void testCurrentUserPrivateNotificationsNotRedacted() { // GIVEN current user allows private notifications to show - mSettings.putIntForUser(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1, + mSettings.putIntForUser(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1, mCurrentUser.id); - mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false); + changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS); // THEN current user's notification isn't redacted assertFalse(mLockscreenUserManager.needsRedaction(mCurrentUserNotif)); } @Test + public void testCurrentUserPrivateNotificationsRedactedChannel() { + // GIVEN current user allows private notifications to show + mSettings.putIntForUser(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1, + mCurrentUser.id); + changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS); + + // but doesn't allow it at the channel level + NotificationChannel channel = new NotificationChannel("1", "1", IMPORTANCE_HIGH); + channel.setLockscreenVisibility(VISIBILITY_PRIVATE); + mCurrentUserNotif.setRanking(new RankingBuilder(mCurrentUserNotif.getRanking()) + .setChannel(channel) + .setVisibilityOverride(VISIBILITY_NO_OVERRIDE).build()); + // THEN the notification is redacted + assertTrue(mLockscreenUserManager.needsRedaction(mCurrentUserNotif)); + } + + @Test public void testWorkPrivateNotificationsRedacted() { // GIVEN work profile doesn't private notifications to show - mSettings.putIntForUser(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0, + mSettings.putIntForUser(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0, mWorkUser.id); - mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false); + changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS); // THEN work profile notification is redacted assertTrue(mLockscreenUserManager.needsRedaction(mWorkProfileNotif)); @@ -244,9 +304,9 @@ public class NotificationLockscreenUserManagerTest extends SysuiTestCase { @Test public void testWorkPrivateNotificationsNotRedacted() { // GIVEN work profile allows private notifications to show - mSettings.putIntForUser(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1, + mSettings.putIntForUser(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1, mWorkUser.id); - mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false); + changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS); // THEN work profile notification isn't redacted assertFalse(mLockscreenUserManager.needsRedaction(mWorkProfileNotif)); @@ -256,13 +316,15 @@ public class NotificationLockscreenUserManagerTest extends SysuiTestCase { @Test public void testWorkPrivateNotificationsNotRedacted_otherUsersRedacted() { // GIVEN work profile allows private notifications to show but the other users don't - mSettings.putIntForUser(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1, + mSettings.putIntForUser(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1, mWorkUser.id); - mSettings.putIntForUser(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0, + mSettings.putIntForUser(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0, mCurrentUser.id); - mSettings.putIntForUser(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0, + mSettings.putIntForUser(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0, mSecondaryUser.id); - mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false); + changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS); + changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS); + changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS); // THEN the work profile notification doesn't need to be redacted assertFalse(mLockscreenUserManager.needsRedaction(mWorkProfileNotif)); @@ -275,13 +337,15 @@ public class NotificationLockscreenUserManagerTest extends SysuiTestCase { @Test public void testWorkProfileRedacted_otherUsersNotRedacted() { // GIVEN work profile doesn't allow private notifications to show but the other users do - mSettings.putIntForUser(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0, + mSettings.putIntForUser(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0, mWorkUser.id); - mSettings.putIntForUser(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1, + mSettings.putIntForUser(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1, mCurrentUser.id); - mSettings.putIntForUser(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1, + mSettings.putIntForUser(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1, mSecondaryUser.id); - mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false); + changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS); + changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS); + changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS); // THEN the work profile notification needs to be redacted assertTrue(mLockscreenUserManager.needsRedaction(mWorkProfileNotif)); @@ -295,11 +359,12 @@ public class NotificationLockscreenUserManagerTest extends SysuiTestCase { public void testSecondaryUserNotRedacted_currentUserRedacted() { // GIVEN secondary profile allows private notifications to show but the current user // doesn't allow private notifications to show - mSettings.putIntForUser(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0, + mSettings.putIntForUser(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0, mCurrentUser.id); - mSettings.putIntForUser(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1, + mSettings.putIntForUser(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1, mSecondaryUser.id); - mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false); + changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS); + changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS); // THEN the secondary profile notification still needs to be redacted because the current // user's setting takes precedence @@ -323,6 +388,7 @@ public class NotificationLockscreenUserManagerTest extends SysuiTestCase { @Test public void testUpdateIsPublicMode() { when(mKeyguardStateController.isMethodSecure()).thenReturn(true); + when(mKeyguardStateController.isShowing()).thenReturn(false); NotificationStateChangedListener listener = mock(NotificationStateChangedListener.class); mLockscreenUserManager.addNotificationStateChangedListener(listener); @@ -330,24 +396,28 @@ public class NotificationLockscreenUserManagerTest extends SysuiTestCase { // first call explicitly sets user 0 to not public; notifies mLockscreenUserManager.updatePublicMode(); + TestableLooper.get(this).processAllMessages(); assertFalse(mLockscreenUserManager.isLockscreenPublicMode(0)); verify(listener).onNotificationStateChanged(); clearInvocations(listener); // calling again has no changes; does not notify mLockscreenUserManager.updatePublicMode(); + TestableLooper.get(this).processAllMessages(); assertFalse(mLockscreenUserManager.isLockscreenPublicMode(0)); verify(listener, never()).onNotificationStateChanged(); // Calling again with keyguard now showing makes user 0 public; notifies when(mKeyguardStateController.isShowing()).thenReturn(true); mLockscreenUserManager.updatePublicMode(); + TestableLooper.get(this).processAllMessages(); assertTrue(mLockscreenUserManager.isLockscreenPublicMode(0)); verify(listener).onNotificationStateChanged(); clearInvocations(listener); // calling again has no changes; does not notify mLockscreenUserManager.updatePublicMode(); + TestableLooper.get(this).processAllMessages(); assertTrue(mLockscreenUserManager.isLockscreenPublicMode(0)); verify(listener, never()).onNotificationStateChanged(); } @@ -356,14 +426,14 @@ public class NotificationLockscreenUserManagerTest extends SysuiTestCase { public void testDevicePolicyDoesNotAllowNotifications() { // User allows them mSettings.putIntForUser(LOCK_SCREEN_SHOW_NOTIFICATIONS, 1, mCurrentUser.id); - mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false); + changeSetting(LOCK_SCREEN_SHOW_NOTIFICATIONS); // DevicePolicy hides notifs on lockscreen when(mDevicePolicyManager.getKeyguardDisabledFeatures(null, mCurrentUser.id)) .thenReturn(KEYGUARD_DISABLE_SECURE_NOTIFICATIONS); - BroadcastReceiver.PendingResult pr = mock(BroadcastReceiver.PendingResult.class); - when(pr.getSendingUserId()).thenReturn(mCurrentUser.id); + BroadcastReceiver.PendingResult pr = new BroadcastReceiver.PendingResult( + 0, null, null, 0, true, false, null, mCurrentUser.id, 0); mLockscreenUserManager.mAllUsersReceiver.setPendingResult(pr); mLockscreenUserManager.mAllUsersReceiver.onReceive(mContext, new Intent(ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED)); @@ -371,19 +441,18 @@ public class NotificationLockscreenUserManagerTest extends SysuiTestCase { assertFalse(mLockscreenUserManager.userAllowsNotificationsInPublic(mCurrentUser.id)); } - @Ignore("b/286230167") @Test public void testDevicePolicyDoesNotAllowNotifications_userAll() { // User allows them mSettings.putIntForUser(LOCK_SCREEN_SHOW_NOTIFICATIONS, 1, mCurrentUser.id); - mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false); + changeSetting(LOCK_SCREEN_SHOW_NOTIFICATIONS); // DevicePolicy hides notifications when(mDevicePolicyManager.getKeyguardDisabledFeatures(null, mCurrentUser.id)) .thenReturn(KEYGUARD_DISABLE_SECURE_NOTIFICATIONS); - BroadcastReceiver.PendingResult pr = mock(BroadcastReceiver.PendingResult.class); - when(pr.getSendingUserId()).thenReturn(mCurrentUser.id); + BroadcastReceiver.PendingResult pr = new BroadcastReceiver.PendingResult( + 0, null, null, 0, true, false, null, mCurrentUser.id, 0); mLockscreenUserManager.mAllUsersReceiver.setPendingResult(pr); mLockscreenUserManager.mAllUsersReceiver.onReceive(mContext, new Intent(ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED)); @@ -392,98 +461,105 @@ public class NotificationLockscreenUserManagerTest extends SysuiTestCase { } @Test - @Ignore("b/286230167") public void testDevicePolicyDoesNotAllowNotifications_secondary() { + Mockito.clearInvocations(mDevicePolicyManager); // User allows notifications mSettings.putIntForUser(LOCK_SCREEN_SHOW_NOTIFICATIONS, 1, mSecondaryUser.id); - mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false); + changeSetting(LOCK_SCREEN_SHOW_NOTIFICATIONS); // DevicePolicy hides notifications when(mDevicePolicyManager.getKeyguardDisabledFeatures(null, mSecondaryUser.id)) .thenReturn(KEYGUARD_DISABLE_SECURE_NOTIFICATIONS); - BroadcastReceiver.PendingResult pr = mock(BroadcastReceiver.PendingResult.class); - when(pr.getSendingUserId()).thenReturn(mSecondaryUser.id); + BroadcastReceiver.PendingResult pr = new BroadcastReceiver.PendingResult( + 0, null, null, 0, true, false, null, mSecondaryUser.id, 0); mLockscreenUserManager.mAllUsersReceiver.setPendingResult(pr); mLockscreenUserManager.mAllUsersReceiver.onReceive(mContext, new Intent(ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED)); assertFalse(mLockscreenUserManager.userAllowsNotificationsInPublic(mSecondaryUser.id)); - // TODO (b/286230167): enable assertion verify(mDevicePolicyManager, atMost(1)).getKeyguardDisabledFeatures(any(), anyInt()); } @Test public void testDevicePolicy_noPrivateNotifications() { + Mockito.clearInvocations(mDevicePolicyManager); // User allows notifications mSettings.putIntForUser(LOCK_SCREEN_SHOW_NOTIFICATIONS, 1, mCurrentUser.id); - mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false); + changeSetting(LOCK_SCREEN_SHOW_NOTIFICATIONS); // DevicePolicy hides sensitive content when(mDevicePolicyManager.getKeyguardDisabledFeatures(null, mCurrentUser.id)) .thenReturn(KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS); - BroadcastReceiver.PendingResult pr = mock(BroadcastReceiver.PendingResult.class); - when(pr.getSendingUserId()).thenReturn(mCurrentUser.id); + BroadcastReceiver.PendingResult pr = new BroadcastReceiver.PendingResult( + 0, null, null, 0, true, false, null, mCurrentUser.id, 0); mLockscreenUserManager.mAllUsersReceiver.setPendingResult(pr); mLockscreenUserManager.mAllUsersReceiver.onReceive(mContext, new Intent(ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED)); assertTrue(mLockscreenUserManager.needsRedaction(mCurrentUserNotif)); - // TODO (b/286230167): enable assertion. It's currently called 4 times. - //verify(mDevicePolicyManager, atMost(1)).getKeyguardDisabledFeatures(any(), anyInt()); + verify(mDevicePolicyManager, atMost(1)).getKeyguardDisabledFeatures(any(), anyInt()); } @Test public void testDevicePolicy_noPrivateNotifications_userAll() { + NotificationChannel channel = new NotificationChannel("1", "1", IMPORTANCE_HIGH); + channel.setLockscreenVisibility(VISIBILITY_NO_OVERRIDE); + NotificationEntry notifEntry = new NotificationEntryBuilder() + .setNotification(new Notification()) + .setUser(UserHandle.ALL) + .build(); + notifEntry.setRanking(new RankingBuilder(notifEntry.getRanking()) + .setChannel(channel) + .setVisibilityOverride(VISIBILITY_NO_OVERRIDE).build()); + when(mNotifCollection.getEntry(notifEntry.getKey())).thenReturn(notifEntry); // User allows notifications mSettings.putIntForUser(LOCK_SCREEN_SHOW_NOTIFICATIONS, 1, mCurrentUser.id); - mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false); + changeSetting(LOCK_SCREEN_SHOW_NOTIFICATIONS); // DevicePolicy hides sensitive content when(mDevicePolicyManager.getKeyguardDisabledFeatures(null, mCurrentUser.id)) .thenReturn(KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS); - BroadcastReceiver.PendingResult pr = mock(BroadcastReceiver.PendingResult.class); - when(pr.getSendingUserId()).thenReturn(mCurrentUser.id); + BroadcastReceiver.PendingResult pr = new BroadcastReceiver.PendingResult( + 0, null, null, 0, true, false, null, mCurrentUser.id, 0); mLockscreenUserManager.mAllUsersReceiver.setPendingResult(pr); mLockscreenUserManager.mAllUsersReceiver.onReceive(mContext, new Intent(ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED)); - assertTrue(mLockscreenUserManager.needsRedaction(new NotificationEntryBuilder() - .setNotification(new Notification()) - .setUser(UserHandle.ALL) - .build())); + assertTrue(mLockscreenUserManager.needsRedaction(notifEntry)); } @Test public void testDevicePolicyPrivateNotifications_secondary() { + Mockito.clearInvocations(mDevicePolicyManager); // User allows notifications mSettings.putIntForUser(LOCK_SCREEN_SHOW_NOTIFICATIONS, 1, mSecondaryUser.id); - mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false); + changeSetting(LOCK_SCREEN_SHOW_NOTIFICATIONS); // DevicePolicy hides sensitive content when(mDevicePolicyManager.getKeyguardDisabledFeatures(null, mSecondaryUser.id)) .thenReturn(KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS); - BroadcastReceiver.PendingResult pr = mock(BroadcastReceiver.PendingResult.class); - when(pr.getSendingUserId()).thenReturn(mSecondaryUser.id); + BroadcastReceiver.PendingResult pr = new BroadcastReceiver.PendingResult( + 0, null, null, 0, true, false, null, mSecondaryUser.id, 0); mLockscreenUserManager.mAllUsersReceiver.setPendingResult(pr); mLockscreenUserManager.mAllUsersReceiver.onReceive(mContext, new Intent(ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED)); + mLockscreenUserManager.mUserChangedCallback.onUserChanging(mSecondaryUser.id, mContext); assertTrue(mLockscreenUserManager.needsRedaction(mSecondaryUserNotif)); - // TODO (b/286230167): enable assertion. It's currently called 5 times. - //verify(mDevicePolicyManager, atMost(1)).getKeyguardDisabledFeatures(any(), anyInt()); + verify(mDevicePolicyManager, atMost(1)).getKeyguardDisabledFeatures(any(), anyInt()); } @Test public void testHideNotifications_primary() { mSettings.putIntForUser(LOCK_SCREEN_SHOW_NOTIFICATIONS, 0, mCurrentUser.id); - mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false); + changeSetting(LOCK_SCREEN_SHOW_NOTIFICATIONS); assertFalse(mLockscreenUserManager.userAllowsNotificationsInPublic(mCurrentUser.id)); } @@ -491,16 +567,17 @@ public class NotificationLockscreenUserManagerTest extends SysuiTestCase { @Test public void testHideNotifications_secondary() { mSettings.putIntForUser(LOCK_SCREEN_SHOW_NOTIFICATIONS, 0, mSecondaryUser.id); - mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false); + changeSetting(LOCK_SCREEN_SHOW_NOTIFICATIONS); assertFalse(mLockscreenUserManager.userAllowsNotificationsInPublic(mSecondaryUser.id)); } - @Ignore("b/286230167") @Test public void testHideNotifications_workProfile() { - mSettings.putIntForUser(LOCK_SCREEN_SHOW_NOTIFICATIONS, 0, mWorkUser.id); - mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false); + mSettings.putIntForUser(LOCK_SCREEN_SHOW_NOTIFICATIONS, 0, mCurrentUser.id); + mSettings.putIntForUser(LOCK_SCREEN_SHOW_NOTIFICATIONS, 1, mWorkUser.id); + changeSetting(LOCK_SCREEN_SHOW_NOTIFICATIONS); + changeSetting(LOCK_SCREEN_SHOW_NOTIFICATIONS); assertFalse(mLockscreenUserManager.userAllowsNotificationsInPublic(mWorkUser.id)); } @@ -508,67 +585,62 @@ public class NotificationLockscreenUserManagerTest extends SysuiTestCase { @Test public void testHideNotifications_secondary_userSwitch() { mSettings.putIntForUser(LOCK_SCREEN_SHOW_NOTIFICATIONS, 0, mSecondaryUser.id); + changeSetting(LOCK_SCREEN_SHOW_NOTIFICATIONS); - TestNotificationLockscreenUserManager lockscreenUserManager - = new TestNotificationLockscreenUserManager(mContext); - lockscreenUserManager.setUpWithPresenter(mPresenter); - - lockscreenUserManager.mUserChangedCallback.onUserChanging(mSecondaryUser.id, mContext); + mLockscreenUserManager.mUserChangedCallback.onUserChanging(mSecondaryUser.id, mContext); - assertFalse(lockscreenUserManager.userAllowsNotificationsInPublic(mSecondaryUser.id)); + assertFalse(mLockscreenUserManager.userAllowsNotificationsInPublic(mSecondaryUser.id)); } @Test public void testShowNotifications_secondary_userSwitch() { mSettings.putIntForUser(LOCK_SCREEN_SHOW_NOTIFICATIONS, 1, mSecondaryUser.id); + changeSetting(LOCK_SCREEN_SHOW_NOTIFICATIONS); - TestNotificationLockscreenUserManager lockscreenUserManager - = new TestNotificationLockscreenUserManager(mContext); - lockscreenUserManager.setUpWithPresenter(mPresenter); - - lockscreenUserManager.mUserChangedCallback.onUserChanging(mSecondaryUser.id, mContext); + mLockscreenUserManager.mUserChangedCallback.onUserChanging(mSecondaryUser.id, mContext); - assertTrue(lockscreenUserManager.userAllowsNotificationsInPublic(mSecondaryUser.id)); + assertTrue(mLockscreenUserManager.userAllowsNotificationsInPublic(mSecondaryUser.id)); } - @Ignore("b/286230167") @Test public void testShouldShowLockscreenNotifications_keyguardManagerNoPrivateNotifications() { + // KeyguardManager does not allow notifications + when(mKeyguardManager.getPrivateNotificationsAllowed()).thenReturn(false); // User allows notifications mSettings.putIntForUser(LOCK_SCREEN_SHOW_NOTIFICATIONS, 1, mCurrentUser.id); - mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false); + changeSetting(LOCK_SCREEN_SHOW_NOTIFICATIONS); // DevicePolicy allows notifications when(mDevicePolicyManager.getKeyguardDisabledFeatures(null, mCurrentUser.id)) .thenReturn(0); - BroadcastReceiver.PendingResult pr = mock(BroadcastReceiver.PendingResult.class); - when(pr.getSendingUserId()).thenReturn(mCurrentUser.id); + BroadcastReceiver.PendingResult pr = new BroadcastReceiver.PendingResult( + 0, null, null, 0, true, false, null, mCurrentUser.id, 0); mLockscreenUserManager.mAllUsersReceiver.setPendingResult(pr); mLockscreenUserManager.mAllUsersReceiver.onReceive(mContext, new Intent(ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED)); - // KeyguardManager does not - when(mKeyguardManager.getPrivateNotificationsAllowed()).thenReturn(false); - assertFalse(mLockscreenUserManager.shouldShowLockscreenNotifications()); } @Test public void testUserAllowsNotificationsInPublic_keyguardManagerNoPrivateNotifications() { - // User allows notifications - mSettings.putIntForUser(LOCK_SCREEN_SHOW_NOTIFICATIONS, 1, mCurrentUser.id); - mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false); // DevicePolicy allows notifications when(mDevicePolicyManager.getKeyguardDisabledFeatures(null, mCurrentUser.id)) .thenReturn(0); - BroadcastReceiver.PendingResult pr = mock(BroadcastReceiver.PendingResult.class); - when(pr.getSendingUserId()).thenReturn(mCurrentUser.id); + BroadcastReceiver.PendingResult pr = new BroadcastReceiver.PendingResult( + 0, null, null, 0, true, false, null, mCurrentUser.id, 0); mLockscreenUserManager.mAllUsersReceiver.setPendingResult(pr); mLockscreenUserManager.mAllUsersReceiver.onReceive(mContext, new Intent(ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED)); - // KeyguardManager does not + // KeyguardManager does not allow notifications when(mKeyguardManager.getPrivateNotificationsAllowed()).thenReturn(false); + // User allows notifications + mSettings.putIntForUser(LOCK_SCREEN_SHOW_NOTIFICATIONS, 1, mCurrentUser.id); + // We shouldn't need to call this method, but getPrivateNotificationsAllowed has no + // callback, so it's only updated when the setting is + changeSetting(LOCK_SCREEN_SHOW_NOTIFICATIONS); + assertFalse(mLockscreenUserManager.userAllowsNotificationsInPublic(mCurrentUser.id)); } @@ -576,31 +648,30 @@ public class NotificationLockscreenUserManagerTest extends SysuiTestCase { public void testUserAllowsNotificationsInPublic_settingsChange() { // User allows notifications mSettings.putIntForUser(LOCK_SCREEN_SHOW_NOTIFICATIONS, 1, mCurrentUser.id); - mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false); + changeSetting(LOCK_SCREEN_SHOW_NOTIFICATIONS); assertTrue(mLockscreenUserManager.userAllowsNotificationsInPublic(mCurrentUser.id)); // User disables mSettings.putIntForUser(LOCK_SCREEN_SHOW_NOTIFICATIONS, 0, mCurrentUser.id); - mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false); + changeSetting(LOCK_SCREEN_SHOW_NOTIFICATIONS); assertFalse(mLockscreenUserManager.userAllowsNotificationsInPublic(mCurrentUser.id)); } - @Ignore("b/286230167") @Test public void testUserAllowsNotificationsInPublic_devicePolicyChange() { // User allows notifications mSettings.putIntForUser(LOCK_SCREEN_SHOW_NOTIFICATIONS, 1, mCurrentUser.id); - mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false); + changeSetting(LOCK_SCREEN_SHOW_NOTIFICATIONS); assertTrue(mLockscreenUserManager.userAllowsNotificationsInPublic(mCurrentUser.id)); // DevicePolicy disables notifications when(mDevicePolicyManager.getKeyguardDisabledFeatures(null, mCurrentUser.id)) .thenReturn(KEYGUARD_DISABLE_SECURE_NOTIFICATIONS); - BroadcastReceiver.PendingResult pr = mock(BroadcastReceiver.PendingResult.class); - when(pr.getSendingUserId()).thenReturn(mCurrentUser.id); + BroadcastReceiver.PendingResult pr = new BroadcastReceiver.PendingResult( + 0, null, null, 0, true, false, null, mCurrentUser.id, 0); mLockscreenUserManager.mAllUsersReceiver.setPendingResult(pr); mLockscreenUserManager.mAllUsersReceiver.onReceive(mContext, new Intent(ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED)); @@ -608,6 +679,28 @@ public class NotificationLockscreenUserManagerTest extends SysuiTestCase { assertFalse(mLockscreenUserManager.userAllowsNotificationsInPublic(mCurrentUser.id)); } + @Test + public void testNewUserAdded() { + int newUserId = 14; + mSettings.putIntForUser(LOCK_SCREEN_SHOW_NOTIFICATIONS, 1, newUserId); + mSettings.putIntForUser(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1, newUserId); + when(mDevicePolicyManager.getKeyguardDisabledFeatures(null, newUserId)) + .thenReturn(KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS); + + BroadcastReceiver broadcastReceiver = + mLockscreenUserManager.getBaseBroadcastReceiverForTest(); + final Bundle extras = new Bundle(); + final Intent intent = new Intent(Intent.ACTION_USER_ADDED); + intent.putExtras(extras); + intent.putExtra(Intent.EXTRA_USER_HANDLE, newUserId); + broadcastReceiver.onReceive(mContext, intent); + + verify(mDevicePolicyManager, atMost(1)).getKeyguardDisabledFeatures(any(), eq(newUserId)); + + assertTrue(mLockscreenUserManager.userAllowsNotificationsInPublic(newUserId)); + assertFalse(mLockscreenUserManager.userAllowsPrivateNotificationsInPublic(newUserId)); + } + private class TestNotificationLockscreenUserManager extends NotificationLockscreenUserManagerImpl { public TestNotificationLockscreenUserManager(Context context) { @@ -623,12 +716,17 @@ public class NotificationLockscreenUserManagerTest extends SysuiTestCase { (() -> mOverviewProxyService), NotificationLockscreenUserManagerTest.this.mKeyguardManager, mStatusBarStateController, - Handler.createAsync(Looper.myLooper()), + Handler.createAsync(TestableLooper.get( + NotificationLockscreenUserManagerTest.this).getLooper()), + Handler.createAsync(TestableLooper.get( + NotificationLockscreenUserManagerTest.this).getLooper()), + mBackgroundExecutor, mDeviceProvisionedController, mKeyguardStateController, mSettings, mock(DumpManager.class), - mock(LockPatternUtils.class)); + mock(LockPatternUtils.class), + mFakeFeatureFlags); } public BroadcastReceiver getBaseBroadcastReceiverForTest() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProviderTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProviderTest.java index 83f2a5d4fbdf..b922ab39912b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProviderTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProviderTest.java @@ -22,6 +22,7 @@ import static android.app.Notification.VISIBILITY_SECRET; import static android.app.NotificationManager.IMPORTANCE_HIGH; import static android.app.NotificationManager.IMPORTANCE_LOW; import static android.app.NotificationManager.IMPORTANCE_MIN; +import static android.app.NotificationManager.VISIBILITY_NO_OVERRIDE; import static com.android.systemui.statusbar.StatusBarState.KEYGUARD; import static com.android.systemui.statusbar.StatusBarState.SHADE; @@ -36,6 +37,7 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import android.app.NotificationChannel; import android.content.Context; import android.os.Handler; import android.os.UserHandle; @@ -51,6 +53,9 @@ import com.android.systemui.CoreStartable; import com.android.systemui.SysuiTestCase; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; +import com.android.systemui.flags.FakeFeatureFlagsClassic; +import com.android.systemui.flags.FeatureFlagsClassic; +import com.android.systemui.flags.Flags; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.settings.UserTracker; import com.android.systemui.statusbar.NotificationLockscreenUserManager; @@ -96,6 +101,7 @@ public class KeyguardNotificationVisibilityProviderTest extends SysuiTestCase { @Mock private UserTracker mUserTracker; private final FakeSettings mSecureSettings = new FakeSettings(); private final FakeGlobalSettings mGlobalSettings = new FakeGlobalSettings(); + private final FakeFeatureFlagsClassic mFeatureFlags = new FakeFeatureFlagsClassic(); private KeyguardNotificationVisibilityProvider mKeyguardNotificationVisibilityProvider; private NotificationEntry mEntry; @@ -116,7 +122,8 @@ public class KeyguardNotificationVisibilityProviderTest extends SysuiTestCase { mStatusBarStateController, mUserTracker, mSecureSettings, - mGlobalSettings); + mGlobalSettings, + mFeatureFlags); mKeyguardNotificationVisibilityProvider = component.getProvider(); for (CoreStartable startable : component.getCoreStartables().values()) { startable.start(); @@ -424,6 +431,7 @@ public class KeyguardNotificationVisibilityProviderTest extends SysuiTestCase { @Test public void publicMode_settingsDisallow() { + mFeatureFlags.set(Flags.NOTIF_LS_BACKGROUND_THREAD, true); // GIVEN an 'unfiltered-keyguard-showing' state setupUnfilteredState(mEntry); @@ -433,12 +441,59 @@ public class KeyguardNotificationVisibilityProviderTest extends SysuiTestCase { when(mLockscreenUserManager.userAllowsNotificationsInPublic(NOTIF_USER_ID)) .thenReturn(false); + mEntry.setRanking(new RankingBuilder() + .setChannel(new NotificationChannel("1", "1", 4)) + .setVisibilityOverride(VISIBILITY_NO_OVERRIDE) + .setKey(mEntry.getKey()).build()); + + // THEN filter out the entry + assertTrue(mKeyguardNotificationVisibilityProvider.shouldHideNotification(mEntry)); + } + + @Test + public void publicMode_settingsDisallow_mainThread() { + mFeatureFlags.set(Flags.NOTIF_LS_BACKGROUND_THREAD, false); + // GIVEN an 'unfiltered-keyguard-showing' state + setupUnfilteredState(mEntry); + + // WHEN the notification's user is in public mode and settings are configured to disallow + // notifications in public mode + when(mLockscreenUserManager.isLockscreenPublicMode(NOTIF_USER_ID)).thenReturn(true); + when(mLockscreenUserManager.userAllowsNotificationsInPublic(NOTIF_USER_ID)) + .thenReturn(false); + + mEntry.setRanking(new RankingBuilder() + .setChannel(new NotificationChannel("1", "1", 4)) + .setVisibilityOverride(VISIBILITY_NO_OVERRIDE) + .setKey(mEntry.getKey()).build()); + // THEN filter out the entry assertTrue(mKeyguardNotificationVisibilityProvider.shouldHideNotification(mEntry)); } @Test public void publicMode_notifDisallowed() { + mFeatureFlags.set(Flags.NOTIF_LS_BACKGROUND_THREAD, true); + NotificationChannel channel = new NotificationChannel("1", "1", IMPORTANCE_HIGH); + channel.setLockscreenVisibility(VISIBILITY_SECRET); + // GIVEN an 'unfiltered-keyguard-showing' state + setupUnfilteredState(mEntry); + + // WHEN the notification's user is in public mode and settings are configured to disallow + // notifications in public mode + when(mLockscreenUserManager.isLockscreenPublicMode(CURR_USER_ID)).thenReturn(true); + mEntry.setRanking(new RankingBuilder() + .setKey(mEntry.getKey()) + .setChannel(channel) + .setVisibilityOverride(VISIBILITY_SECRET).build()); + + // THEN filter out the entry + assertTrue(mKeyguardNotificationVisibilityProvider.shouldHideNotification(mEntry)); + } + + @Test + public void publicMode_notifDisallowed_mainThread() { + mFeatureFlags.set(Flags.NOTIF_LS_BACKGROUND_THREAD, false); // GIVEN an 'unfiltered-keyguard-showing' state setupUnfilteredState(mEntry); @@ -506,6 +561,54 @@ public class KeyguardNotificationVisibilityProviderTest extends SysuiTestCase { } @Test + public void notificationChannelVisibilityNoOverride() { + mFeatureFlags.set(Flags.NOTIF_LS_BACKGROUND_THREAD, true); + // GIVEN a VISIBILITY_PRIVATE notification + NotificationEntryBuilder entryBuilder = new NotificationEntryBuilder() + .setUser(new UserHandle(NOTIF_USER_ID)); + entryBuilder.modifyNotification(mContext) + .setVisibility(VISIBILITY_PRIVATE); + mEntry = entryBuilder.build(); + // ranking says secret because of DPC or Setting + mEntry.setRanking(new RankingBuilder() + .setKey(mEntry.getKey()) + .setVisibilityOverride(VISIBILITY_SECRET) + .setImportance(IMPORTANCE_HIGH) + .build()); + + // WHEN we're in an 'unfiltered-keyguard-showing' state + setupUnfilteredState(mEntry); + + // THEN don't hide the entry based on visibility. (Redaction is handled elsewhere.) + assertFalse(mKeyguardNotificationVisibilityProvider.shouldHideNotification(mEntry)); + } + + @Test + public void notificationChannelVisibilitySecret() { + mFeatureFlags.set(Flags.NOTIF_LS_BACKGROUND_THREAD, true); + // GIVEN a VISIBILITY_PRIVATE notification + NotificationEntryBuilder entryBuilder = new NotificationEntryBuilder() + .setUser(new UserHandle(NOTIF_USER_ID)); + entryBuilder.modifyNotification(mContext) + .setVisibility(VISIBILITY_PRIVATE); + // And a VISIBILITY_SECRET NotificationChannel + NotificationChannel channel = new NotificationChannel("id", "name", IMPORTANCE_HIGH); + channel.setLockscreenVisibility(VISIBILITY_SECRET); + mEntry = entryBuilder.build(); + // WHEN we're in an 'unfiltered-keyguard-showing' state + setupUnfilteredState(mEntry); + when(mLockscreenUserManager.isLockscreenPublicMode(CURR_USER_ID)).thenReturn(true); + when(mLockscreenUserManager.isLockscreenPublicMode(NOTIF_USER_ID)).thenReturn(true); + + mEntry.setRanking(new RankingBuilder(mEntry.getRanking()) + .setChannel(channel) + .build()); + + // THEN hide the entry based on visibility. + assertTrue(mKeyguardNotificationVisibilityProvider.shouldHideNotification(mEntry)); + } + + @Test public void notificationVisibilityPrivate() { // GIVEN a VISIBILITY_PRIVATE notification NotificationEntryBuilder entryBuilder = new NotificationEntryBuilder() @@ -635,7 +738,8 @@ public class KeyguardNotificationVisibilityProviderTest extends SysuiTestCase { @BindsInstance SysuiStatusBarStateController statusBarStateController, @BindsInstance UserTracker userTracker, @BindsInstance SecureSettings secureSettings, - @BindsInstance GlobalSettings globalSettings + @BindsInstance GlobalSettings globalSettings, + @BindsInstance FeatureFlagsClassic featureFlags ); } } |