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

Commit 97d24320 authored by Richard MacGregor's avatar Richard MacGregor Committed by Android (Google) Code Review
Browse files

Merge changes I8c7b4685,Id53a55aa into main

* changes:
  Add traces for otp redaction for notifications
  Support hiding notifications during screenshare
parents 1e96c47e af42a5f6
Loading
Loading
Loading
Loading
+14 −3
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ package com.android.systemui.statusbar.notification.collection.coordinator

import android.os.UserHandle
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.systemui.Flags.screenshareNotificationHiding
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.statusbar.NotificationLockscreenUserManager
import com.android.systemui.statusbar.StatusBarState
@@ -30,6 +31,7 @@ import com.android.systemui.statusbar.notification.collection.coordinator.dagger
import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeRenderListListener
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.Invalidator
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.statusbar.policy.SensitiveNotificationProtectionController
import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import dagger.Binds
import dagger.Module
@@ -55,6 +57,8 @@ class SensitiveContentCoordinatorImpl @Inject constructor(
    private val statusBarStateController: StatusBarStateController,
    private val keyguardStateController: KeyguardStateController,
    private val selectedUserInteractor: SelectedUserInteractor,
    private val sensitiveNotificationProtectionController:
        SensitiveNotificationProtectionController,
) : Invalidator("SensitiveContentInvalidator"),
        SensitiveContentCoordinator,
        DynamicPrivacyController.Listener,
@@ -82,10 +86,13 @@ class SensitiveContentCoordinatorImpl @Inject constructor(
            return
        }

        val isSensitiveContentProtectionActive = screenshareNotificationHiding() &&
            sensitiveNotificationProtectionController.isSensitiveStateActive
        val currentUserId = lockscreenUserManager.currentUserId
        val devicePublic = lockscreenUserManager.isLockscreenPublicMode(currentUserId)
        val deviceSensitive = devicePublic &&
                !lockscreenUserManager.userAllowsPrivateNotificationsInPublic(currentUserId)
        val deviceSensitive = (devicePublic &&
                !lockscreenUserManager.userAllowsPrivateNotificationsInPublic(currentUserId)) ||
                isSensitiveContentProtectionActive
        val dynamicallyUnlocked = dynamicPrivacyController.isDynamicallyUnlocked
        for (entry in extractAllRepresentativeEntries(entries).filter { it.rowExists() }) {
            val notifUserId = entry.sbn.user.identifier
@@ -105,9 +112,13 @@ class SensitiveContentCoordinatorImpl @Inject constructor(
                    else -> lockscreenUserManager.needsSeparateWorkChallenge(notifUserId)
                }
            }

            val shouldProtectNotification = screenshareNotificationHiding() &&
                sensitiveNotificationProtectionController.shouldProtectNotification(entry)

            val needsRedaction = lockscreenUserManager.needsRedaction(entry)
            val isSensitive = userPublic && needsRedaction
            entry.setSensitive(isSensitive, deviceSensitive)
            entry.setSensitive(isSensitive || shouldProtectNotification, deviceSensitive)
        }
    }
}
+35 −2
Original line number Diff line number Diff line
@@ -22,6 +22,7 @@ import static android.service.notification.NotificationStats.DISMISS_SENTIMENT_N
import static com.android.app.animation.Interpolators.STANDARD;
import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_SCROLL_FLING;
import static com.android.systemui.Dependency.ALLOW_NOTIFICATION_LONG_PRESS_NAME;
import static com.android.systemui.Flags.screenshareNotificationHiding;
import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.OnEmptySpaceClickListener;
import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.OnOverscrollTopChangedListener;
@@ -135,6 +136,7 @@ import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
import com.android.systemui.statusbar.policy.SensitiveNotificationProtectionController;
import com.android.systemui.statusbar.policy.SplitShadeStateController;
import com.android.systemui.statusbar.policy.ZenModeController;
import com.android.systemui.tuner.TunerService;
@@ -218,6 +220,8 @@ public class NotificationStackScrollLayoutController implements Dumpable {
    private final SecureSettings mSecureSettings;
    private final NotificationDismissibilityProvider mDismissibilityProvider;
    private final ActivityStarter mActivityStarter;
    private final SensitiveNotificationProtectionController
            mSensitiveNotificationProtectionController;

    private View mLongPressedView;

@@ -295,6 +299,15 @@ public class NotificationStackScrollLayoutController implements Dumpable {
                }
            };

    private final Runnable mSensitiveStateChangedListener = new Runnable() {
        @Override
        public void run() {
            // Animate false to protect against screen recording capturing content
            // during the animation
            updateSensitivenessWithAnimation(false);
        }
    };

    private final DynamicPrivacyController.Listener mDynamicPrivacyControllerListener = () -> {
        if (mView.isExpanded()) {
            // The bottom might change because we're using the final actual height of the view
@@ -399,8 +412,21 @@ public class NotificationStackScrollLayoutController implements Dumpable {
    }

    private void updateSensitivenessWithAnimation(boolean animate) {
        Trace.beginSection("NSSLC.updateSensitivenessWithAnimation");
        if (screenshareNotificationHiding()) {
            boolean isAnyProfilePublic = mLockscreenUserManager.isAnyProfilePublicMode();
            boolean isSensitiveContentProtectionActive =
                    mSensitiveNotificationProtectionController.isSensitiveStateActive();
            boolean isSensitive = isAnyProfilePublic || isSensitiveContentProtectionActive;

            // Only animate if in a non-sensitive state (not screen sharing)
            boolean shouldAnimate = animate && !isSensitiveContentProtectionActive;
            mView.updateSensitiveness(shouldAnimate, isSensitive);
        } else {
            mView.updateSensitiveness(animate, mLockscreenUserManager.isAnyProfilePublicMode());
        }
        Trace.endSection();
    }

    /**
     * Set the overexpansion of the panel to be applied to the view.
@@ -708,7 +734,8 @@ public class NotificationStackScrollLayoutController implements Dumpable {
            SecureSettings secureSettings,
            NotificationDismissibilityProvider dismissibilityProvider,
            ActivityStarter activityStarter,
            SplitShadeStateController splitShadeStateController) {
            SplitShadeStateController splitShadeStateController,
            SensitiveNotificationProtectionController sensitiveNotificationProtectionController) {
        mView = view;
        mKeyguardTransitionRepo = keyguardTransitionRepo;
        mViewBinder = viewBinder;
@@ -756,6 +783,7 @@ public class NotificationStackScrollLayoutController implements Dumpable {
        mSecureSettings = secureSettings;
        mDismissibilityProvider = dismissibilityProvider;
        mActivityStarter = activityStarter;
        mSensitiveNotificationProtectionController = sensitiveNotificationProtectionController;
        mView.passSplitShadeStateController(splitShadeStateController);
        mDumpManager.registerDumpable(this);
        updateResources();
@@ -860,6 +888,11 @@ public class NotificationStackScrollLayoutController implements Dumpable {
        mDeviceProvisionedController.addCallback(mDeviceProvisionedListener);
        mDeviceProvisionedListener.onDeviceProvisionedChanged();

        if (screenshareNotificationHiding()) {
            mSensitiveNotificationProtectionController
                    .registerSensitiveStateListener(mSensitiveStateChangedListener);
        }

        if (mView.isAttachedToWindow()) {
            mOnAttachStateChangeListener.onViewAttachedToWindow(mView);
        }
+41 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 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.policy;

import com.android.systemui.statusbar.notification.collection.NotificationEntry;

/**
 * A controller which provides the current sensitive notification protections status as well as
 * to assist in feature usage and exemptions
 */
public interface SensitiveNotificationProtectionController {
    /**
     * Register a runnable that triggers on changes to protection state
     *
     * <p> onSensitiveStateChanged not invoked on registration
     */
    void registerSensitiveStateListener(Runnable onSensitiveStateChanged);

    /** Unregister a previously registered onSensitiveStateChanged runnable */
    void unregisterSensitiveStateListener(Runnable onSensitiveStateChanged);

    /** Return {@code true} if device in state in which notifications should be protected */
    boolean isSensitiveStateActive();

    /** Return {@code true} when notification should be protected */
    boolean shouldProtectNotification(NotificationEntry entry);
}
+104 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 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.policy;

import static com.android.systemui.Flags.screenshareNotificationHiding;

import android.media.projection.MediaProjectionInfo;
import android.media.projection.MediaProjectionManager;
import android.os.Handler;
import android.os.Trace;

import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.util.ListenerSet;

import javax.inject.Inject;

/** Implementation of SensitiveNotificationProtectionController. **/
@SysUISingleton
public class SensitiveNotificationProtectionControllerImpl
        implements SensitiveNotificationProtectionController {
    private final MediaProjectionManager mMediaProjectionManager;
    private final ListenerSet<Runnable> mListeners = new ListenerSet<>();
    private volatile MediaProjectionInfo mProjection;

    @VisibleForTesting
    final MediaProjectionManager.Callback mMediaProjectionCallback =
            new MediaProjectionManager.Callback() {
                @Override
                public void onStart(MediaProjectionInfo info) {
                    Trace.beginSection(
                            "SNPC.onProjectionStart");
                    mProjection = info;
                    mListeners.forEach(Runnable::run);
                    Trace.endSection();
                }

                @Override
                public void onStop(MediaProjectionInfo info) {
                    Trace.beginSection(
                            "SNPC.onProjectionStop");
                    mProjection = null;
                    mListeners.forEach(Runnable::run);
                    Trace.endSection();
                }
            };

    @Inject
    public SensitiveNotificationProtectionControllerImpl(
            MediaProjectionManager mediaProjectionManager,
            @Main Handler mainHandler) {
        mMediaProjectionManager = mediaProjectionManager;

        if (screenshareNotificationHiding()) {
            mMediaProjectionManager.addCallback(mMediaProjectionCallback, mainHandler);
        }
    }

    @Override
    public void registerSensitiveStateListener(Runnable onSensitiveStateChanged) {
        mListeners.addIfAbsent(onSensitiveStateChanged);
    }

    @Override
    public void unregisterSensitiveStateListener(Runnable onSensitiveStateChanged) {
        mListeners.remove(onSensitiveStateChanged);
    }

    @Override
    public boolean isSensitiveStateActive() {
        // TODO(b/316955558): Add disabled by developer option
        // TODO(b/316955306): Add feature exemption for sysui and bug handlers
        // TODO(b/316955346): Add feature exemption for single app screen sharing
        return mProjection != null;
    }

    @Override
    public boolean shouldProtectNotification(NotificationEntry entry) {
        if (!isSensitiveStateActive()) {
            return false;
        }

        // Exempt foreground service notifications from protection in effort to keep screen share
        // stop actions easily accessible
        // TODO(b/316955208): Exempt FGS notifications only for app that started projection
        return !entry.getSbn().getNotification().isFgsOrUij();
    }
}
+7 −0
Original line number Diff line number Diff line
@@ -60,6 +60,8 @@ import com.android.systemui.statusbar.policy.RotationLockController;
import com.android.systemui.statusbar.policy.RotationLockControllerImpl;
import com.android.systemui.statusbar.policy.SecurityController;
import com.android.systemui.statusbar.policy.SecurityControllerImpl;
import com.android.systemui.statusbar.policy.SensitiveNotificationProtectionController;
import com.android.systemui.statusbar.policy.SensitiveNotificationProtectionControllerImpl;
import com.android.systemui.statusbar.policy.SplitShadeStateController;
import com.android.systemui.statusbar.policy.SplitShadeStateControllerImpl;
import com.android.systemui.statusbar.policy.UserInfoController;
@@ -144,6 +146,11 @@ public interface StatusBarPolicyModule {
    @Binds
    SecurityController provideSecurityController(SecurityControllerImpl controllerImpl);

    /** */
    @Binds
    SensitiveNotificationProtectionController provideSensitiveNotificationProtectionController(
            SensitiveNotificationProtectionControllerImpl controllerImpl);

    /** */
    @Binds
    UserInfoController provideUserInfoContrller(UserInfoControllerImpl controllerImpl);
Loading