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

Commit 8d22a50c authored by Treehugger Robot's avatar Treehugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Add additional logic to redacting sensitive content" into main

parents 95057c8a 04a6b7ef
Loading
Loading
Loading
Loading
+90 −2
Original line number Diff line number Diff line
@@ -81,6 +81,7 @@ import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.DisableSceneContainer;
import com.android.systemui.flags.EnableSceneContainer;
import com.android.systemui.flags.FakeFeatureFlagsClassic;
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
import com.android.systemui.log.LogWtfHandlerRule;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.recents.OverviewProxyService;
@@ -90,6 +91,7 @@ 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.notification.row.shared.LockscreenOtpRedaction;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.util.concurrency.FakeExecutor;
@@ -115,6 +117,7 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;

import platform.test.runner.parameterized.ParameterizedAndroidJunit4;
import platform.test.runner.parameterized.Parameters;
@@ -169,6 +172,10 @@ public class NotificationLockscreenUserManagerTest extends SysuiTestCase {
    @Mock
    private DeviceUnlockedInteractor mDeviceUnlockedInteractor;
    @Mock
    private Lazy<KeyguardInteractor> mKeyguardInteractorLazy;
    @Mock
    private KeyguardInteractor mKeyguardInteractor;
    @Mock
    private StateFlow<DeviceUnlockStatus> mDeviceUnlockStatusStateFlow;

    private UserInfo mCurrentUser;
@@ -181,6 +188,7 @@ public class NotificationLockscreenUserManagerTest extends SysuiTestCase {
    private NotificationEntry mSecondaryUserNotif;
    private NotificationEntry mWorkProfileNotif;
    private NotificationEntry mSensitiveContentNotif;
    private long mSensitiveNotifPostTime;
    private final FakeFeatureFlagsClassic mFakeFeatureFlags = new FakeFeatureFlagsClassic();
    private final FakeSystemClock mFakeSystemClock = new FakeSystemClock();
    private final FakeExecutor mBackgroundExecutor = new FakeExecutor(mFakeSystemClock);
@@ -246,13 +254,17 @@ public class NotificationLockscreenUserManagerTest extends SysuiTestCase {
        mSensitiveContentNotif = new NotificationEntryBuilder()
                .setNotification(notifWithPrivateVisibility)
                .setUser(new UserHandle(mCurrentUser.id))
                .setPostTime(System.currentTimeMillis())
                .build();
        mSensitiveContentNotif.setRanking(new RankingBuilder(mCurrentUserNotif.getRanking())
                .setChannel(channel)
                .setSensitiveContent(true)
                .setVisibilityOverride(VISIBILITY_NO_OVERRIDE).build());
        mSensitiveNotifPostTime = mSensitiveContentNotif.getSbn().getPostTime();
        when(mNotifCollection.getEntry(mWorkProfileNotif.getKey())).thenReturn(mWorkProfileNotif);

        when(mKeyguardInteractorLazy.get()).thenReturn(mKeyguardInteractor);
        when(mKeyguardInteractor.isKeyguardDismissible())
                .thenReturn(mock(StateFlow.class));
        mLockscreenUserManager = new TestNotificationLockscreenUserManager(mContext);
        mLockscreenUserManager.setUpWithPresenter(mPresenter);

@@ -504,11 +516,85 @@ public class NotificationLockscreenUserManagerTest extends SysuiTestCase {
    }

    @Test
    @EnableFlags(LockscreenOtpRedaction.FLAG_NAME)
    public void testHasSensitiveContent_notRedactedIfNotLocked() {
        // Allow private notifications for this user
        mSettings.putIntForUser(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1,
                mCurrentUser.id);
        changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS);
        // Claim the device was last locked 1 day ago
        mLockscreenUserManager.mLastLockTime
                .set(mSensitiveNotifPostTime - TimeUnit.DAYS.toMillis(1));
        // Device is not currently locked
        when(mKeyguardManager.isDeviceLocked()).thenReturn(false);

        // Sensitive Content notifications are always redacted
        assertEquals(REDACTION_TYPE_NONE,
                mLockscreenUserManager.getRedactionType(mSensitiveContentNotif));
    }

    @Test
    @EnableFlags(LockscreenOtpRedaction.FLAG_NAME)
    public void testHasSensitiveContent_notRedactedIfUnlockedSinceReceipt() {
        // Allow private notifications for this user
        mSettings.putIntForUser(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1,
                mCurrentUser.id);
        changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS);
        when(mKeyguardManager.isDeviceLocked()).thenReturn(true);
        // Device was locked after this notification arrived
        mLockscreenUserManager.mLastLockTime
                .set(mSensitiveNotifPostTime + TimeUnit.DAYS.toMillis(1));

        // Sensitive Content notifications are always redacted
        assertEquals(REDACTION_TYPE_NONE,
                mLockscreenUserManager.getRedactionType(mSensitiveContentNotif));
    }

    @Test
    @EnableFlags(LockscreenOtpRedaction.FLAG_NAME)
    public void testHasSensitiveContent_notRedactedIfNotLockedForLongEnough() {
        // Allow private notifications for this user
        mSettings.putIntForUser(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1,
                mCurrentUser.id);
        changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS);
        // Device has been locked for 1 second before the notification came in, which is too short
        mLockscreenUserManager.mLastLockTime
                .set(mSensitiveNotifPostTime - TimeUnit.SECONDS.toMillis(1));
        when(mKeyguardManager.isDeviceLocked()).thenReturn(true);

        // Sensitive Content notifications are always redacted
        assertEquals(REDACTION_TYPE_NONE,
                mLockscreenUserManager.getRedactionType(mSensitiveContentNotif));
    }

    @Test
    @DisableFlags(LockscreenOtpRedaction.FLAG_NAME)
    public void testHasSensitiveContent_notRedactedFlagDisabled() {
        // Allow private notifications for this user
        mSettings.putIntForUser(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1,
                mCurrentUser.id);
        changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS);
        // Claim the device was last locked 1 day ago
        mLockscreenUserManager.mLastLockTime
                .set(mSensitiveNotifPostTime - TimeUnit.DAYS.toMillis(1));
        when(mKeyguardManager.isDeviceLocked()).thenReturn(true);

        // Sensitive Content notifications are always redacted
        assertEquals(REDACTION_TYPE_NONE,
                mLockscreenUserManager.getRedactionType(mSensitiveContentNotif));
    }

    @Test
    @EnableFlags(LockscreenOtpRedaction.FLAG_NAME)
    public void testHasSensitiveContent_redacted() {
        // Allow private notifications for this user
        mSettings.putIntForUser(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1,
                mCurrentUser.id);
        changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS);
        when(mKeyguardManager.isDeviceLocked()).thenReturn(true);
        // Claim the device was last unlocked 1 day ago
        mLockscreenUserManager.mLastLockTime
                .set(mSensitiveNotifPostTime - TimeUnit.DAYS.toMillis(1));

        // Sensitive Content notifications are always redacted
        assertEquals(REDACTION_TYPE_SENSITIVE_CONTENT,
@@ -1066,7 +1152,9 @@ public class NotificationLockscreenUserManagerTest extends SysuiTestCase {
                    mock(DumpManager.class),
                    mock(LockPatternUtils.class),
                    mFakeFeatureFlags,
                    mDeviceUnlockedInteractorLazy
                    mDeviceUnlockedInteractorLazy,
                    mKeyguardInteractorLazy,
                    null //CoroutineScope
            );
        }

+66 −5
Original line number Diff line number Diff line
@@ -61,11 +61,13 @@ import com.android.systemui.Dumpable;
import com.android.systemui.Flags;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Application;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.deviceentry.domain.interactor.DeviceUnlockedInteractor;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlagsClassic;
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener;
import com.android.systemui.recents.OverviewProxyService;
@@ -78,6 +80,7 @@ import com.android.systemui.statusbar.notification.row.shared.LockscreenOtpRedac
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.util.ListenerSet;
import com.android.systemui.util.kotlin.JavaAdapterKt;
import com.android.systemui.util.settings.SecureSettings;

import dagger.Lazy;
@@ -88,9 +91,13 @@ import java.util.Collection;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;

import javax.inject.Inject;

import kotlinx.coroutines.CoroutineScope;

/**
 * Handles keeping track of the current user, profiles, and various things related to hiding
 * contents, redacting notifications, and the lockscreen.
@@ -111,6 +118,9 @@ public class NotificationLockscreenUserManagerImpl implements
    private static final Uri SHOW_PRIVATE_LOCKSCREEN =
            Settings.Secure.getUriFor(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS);

    private static final long LOCK_TIME_FOR_SENSITIVE_REDACTION_MS =
            TimeUnit.MINUTES.toMillis(10);

    private final Lazy<NotificationVisibilityProvider> mVisibilityProviderLazy;
    private final Lazy<CommonNotifCollection> mCommonNotifCollectionLazy;
    private final DevicePolicyManager mDevicePolicyManager;
@@ -284,7 +294,12 @@ public class NotificationLockscreenUserManagerImpl implements
    protected final SparseArray<UserInfo> mCurrentProfiles = new SparseArray<>();
    protected final SparseArray<UserInfo> mCurrentManagedProfiles = new SparseArray<>();

    // The last lock time. Uses currentTimeMillis
    @VisibleForTesting
    protected final AtomicLong mLastLockTime = new AtomicLong(-1);

    protected int mCurrentUserId = 0;

    protected NotificationPresenter mPresenter;
    protected ContentObserver mLockscreenSettingsObserver;
    protected ContentObserver mSettingsObserver;
@@ -311,7 +326,10 @@ public class NotificationLockscreenUserManagerImpl implements
            DumpManager dumpManager,
            LockPatternUtils lockPatternUtils,
            FeatureFlagsClassic featureFlags,
            Lazy<DeviceUnlockedInteractor> deviceUnlockedInteractorLazy) {
            Lazy<DeviceUnlockedInteractor> deviceUnlockedInteractorLazy,
            Lazy<KeyguardInteractor> keyguardInteractor,
            @Application CoroutineScope coroutineScope
    ) {
        mContext = context;
        mMainExecutor = mainExecutor;
        mBackgroundExecutor = backgroundExecutor;
@@ -341,6 +359,18 @@ public class NotificationLockscreenUserManagerImpl implements
        if (keyguardPrivateNotifications()) {
            init();
        }

        // To avoid dependency injection cycle, finish constructing this object before using the
        // KeyguardInteractor. The CoroutineScope will only be null in tests.
        if (LockscreenOtpRedaction.isEnabled() && coroutineScope != null) {
            mMainExecutor.execute(() -> JavaAdapterKt.collectFlow(coroutineScope,
                    keyguardInteractor.get().isKeyguardDismissible(),
                    unlocked -> {
                        if (!unlocked) {
                            mLastLockTime.set(System.currentTimeMillis());
                        }
                    }));
        }
    }

    public void setUpWithPresenter(NotificationPresenter presenter) {
@@ -667,8 +697,6 @@ public class NotificationLockscreenUserManagerImpl implements
                !userAllowsPrivateNotificationsInPublic(mCurrentUserId);
        boolean isNotifForManagedProfile = mCurrentManagedProfiles.contains(userId);
        boolean isNotifUserRedacted = !userAllowsPrivateNotificationsInPublic(userId);
        boolean isNotifSensitive = LockscreenOtpRedaction.isEnabled()
                && ent.getRanking() != null && ent.getRanking().hasSensitiveContent();

        // redact notifications if the current user is redacting notifications or the notification
        // contains sensitive content. However if the notification is associated with a managed
@@ -689,12 +717,45 @@ public class NotificationLockscreenUserManagerImpl implements
        if (keyguardPrivateNotifications() && !mKeyguardAllowingNotifications) {
            return REDACTION_TYPE_PUBLIC;
        }
        if (isNotifSensitive) {

        if (shouldShowSensitiveContentRedactedView(ent)) {
            return REDACTION_TYPE_SENSITIVE_CONTENT;
        }
        return REDACTION_TYPE_NONE;
    }

    /*
     * We show the sensitive content redaction view if
     * 1. The feature is enabled
     * 2. The device is locked
     * 3. The notification has the `hasSensitiveContent` ranking variable set to true
     * 4. The device has been locked for at least LOCK_TIME_FOR_SENSITIVE_REDACTION_MS
     * 5. The notification arrived since the last lock time
     */
    private boolean shouldShowSensitiveContentRedactedView(NotificationEntry ent) {
        if (!LockscreenOtpRedaction.isEnabled()) {
            return false;
        }

        if (!mKeyguardManager.isDeviceLocked()) {
            return false;
        }

        if (ent.getRanking() == null || !ent.getRanking().hasSensitiveContent()) {
            return false;
        }

        long lastLockedTime = mLastLockTime.get();
        if (ent.getSbn().getPostTime() < lastLockedTime) {
            return false;
        }

        if ((System.currentTimeMillis() - lastLockedTime) < LOCK_TIME_FOR_SENSITIVE_REDACTION_MS) {
            return false;
        }
        return true;
    }

    private boolean packageHasVisibilityOverride(String key) {
        if (mCommonNotifCollectionLazy.get() == null) {
            Log.wtf(TAG, "mEntryManager was null!", new Throwable());