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

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

Merge changes Ic5f78244,Ie84e6441 into main

* changes:
  Move global settings lookup off main thread
  Use dev option to disable notification protections
parents c695aa4b 69e54bfd
Loading
Loading
Loading
Loading
+31 −0
Original line number Diff line number Diff line
@@ -16,11 +16,14 @@

package com.android.systemui.statusbar.policy;

import static android.provider.Settings.Global.DISABLE_SCREEN_SHARE_PROTECTIONS_FOR_APPS_AND_NOTIFICATIONS;

import static com.android.server.notification.Flags.screenshareNotificationHiding;

import android.annotation.MainThread;
import android.app.IActivityManager;
import android.content.Context;
import android.database.ExecutorContentObserver;
import android.media.projection.MediaProjectionInfo;
import android.media.projection.MediaProjectionManager;
import android.os.Handler;
@@ -37,6 +40,7 @@ import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.util.Assert;
import com.android.systemui.util.ListenerSet;
import com.android.systemui.util.settings.GlobalSettings;

import java.util.concurrent.Executor;

@@ -50,6 +54,7 @@ public class SensitiveNotificationProtectionControllerImpl
    private final ArraySet<String> mExemptPackages = new ArraySet<>();
    private final ListenerSet<Runnable> mListeners = new ListenerSet<>();
    private volatile MediaProjectionInfo mProjection;
    boolean mDisableScreenShareProtections = false;

    @VisibleForTesting
    final MediaProjectionManager.Callback mMediaProjectionCallback =
@@ -58,6 +63,12 @@ public class SensitiveNotificationProtectionControllerImpl
                public void onStart(MediaProjectionInfo info) {
                    Trace.beginSection("SNPC.onProjectionStart");
                    try {
                        if (mDisableScreenShareProtections) {
                            Log.w(LOG_TAG,
                                    "Screen share protections disabled, ignoring projectionstart");
                            return;
                        }

                        // Only enable sensitive content protection if sharing full screen
                        // Launch cookie only set (non-null) if sharing single app/task
                        updateProjectionStateAndNotifyListeners(
@@ -81,6 +92,7 @@ public class SensitiveNotificationProtectionControllerImpl
    @Inject
    public SensitiveNotificationProtectionControllerImpl(
            Context context,
            GlobalSettings settings,
            MediaProjectionManager mediaProjectionManager,
            IActivityManager activityManager,
            @Main Handler mainHandler,
@@ -89,6 +101,25 @@ public class SensitiveNotificationProtectionControllerImpl
            return;
        }

        ExecutorContentObserver developerOptionsObserver = new ExecutorContentObserver(bgExecutor) {
            @Override
            public void onChange(boolean selfChange) {
                super.onChange(selfChange);
                boolean disableScreenShareProtections = settings.getInt(
                        DISABLE_SCREEN_SHARE_PROTECTIONS_FOR_APPS_AND_NOTIFICATIONS,
                        0) != 0;
                mainHandler.post(() -> {
                    mDisableScreenShareProtections = disableScreenShareProtections;
                });
            }
        };
        settings.registerContentObserver(
                DISABLE_SCREEN_SHARE_PROTECTIONS_FOR_APPS_AND_NOTIFICATIONS,
                developerOptionsObserver);

        // Get current setting value
        bgExecutor.execute(() -> developerOptionsObserver.onChange(true));

        bgExecutor.execute(() -> {
            ArraySet<String> exemptPackages = new ArraySet<>();
            // Exempt SystemUI
+2 −0
Original line number Diff line number Diff line
@@ -25,6 +25,7 @@ import androidx.test.filters.SmallTest
import com.android.server.notification.Flags
import com.android.systemui.SysuiTestCase
import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.settings.FakeGlobalSettings
import com.android.systemui.util.time.FakeSystemClock
import org.junit.Before
import org.junit.Test
@@ -49,6 +50,7 @@ class SensitiveNotificationProtectionControllerFlagDisabledTest : SysuiTestCase(
        controller =
            SensitiveNotificationProtectionControllerImpl(
                mContext,
                FakeGlobalSettings(),
                mediaProjectionManager,
                activityManager,
                handler,
+33 −3
Original line number Diff line number Diff line
@@ -22,6 +22,7 @@ import android.app.Notification
import android.media.projection.MediaProjectionInfo
import android.media.projection.MediaProjectionManager
import android.platform.test.annotations.EnableFlags
import android.provider.Settings.Global.DISABLE_SCREEN_SHARE_PROTECTIONS_FOR_APPS_AND_NOTIFICATIONS
import android.service.notification.StatusBarNotification
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper.RunWithLooper
@@ -33,6 +34,7 @@ import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.concurrency.mockExecutorHandler
import com.android.systemui.util.mockito.whenever
import com.android.systemui.util.mockito.withArgCaptor
import com.android.systemui.util.settings.FakeGlobalSettings
import com.android.systemui.util.time.FakeSystemClock
import org.junit.Assert.assertFalse
import org.junit.Assert.assertNotNull
@@ -61,6 +63,8 @@ class SensitiveNotificationProtectionControllerTest : SysuiTestCase() {
    @Mock private lateinit var listener2: Runnable
    @Mock private lateinit var listener3: Runnable

    private lateinit var executor: FakeExecutor
    private lateinit var globalSettings: FakeGlobalSettings
    private lateinit var mediaProjectionCallback: MediaProjectionManager.Callback
    private lateinit var controller: SensitiveNotificationProtectionControllerImpl

@@ -73,18 +77,19 @@ class SensitiveNotificationProtectionControllerTest : SysuiTestCase() {
        whenever(activityManager.bugreportWhitelistedPackages)
            .thenReturn(listOf(BUGREPORT_PACKAGE_NAME))

        val executor = FakeExecutor(FakeSystemClock())

        executor = FakeExecutor(FakeSystemClock())
        globalSettings = FakeGlobalSettings()
        controller =
            SensitiveNotificationProtectionControllerImpl(
                mContext,
                globalSettings,
                mediaProjectionManager,
                activityManager,
                mockExecutorHandler(executor),
                executor
            )

        // Process exemption processing
        // Process pending work (getting global setting and list of exemptions)
        executor.runAllReady()

        // Obtain useful MediaProjectionCallback
@@ -228,6 +233,14 @@ class SensitiveNotificationProtectionControllerTest : SysuiTestCase() {
        assertFalse(controller.isSensitiveStateActive)
    }

    @Test
    fun isSensitiveStateActive_projectionActive_disabledViaDevOption_false() {
        setDisabledViaDeveloperOption()
        mediaProjectionCallback.onStart(mediaProjectionInfo)

        assertFalse(controller.isSensitiveStateActive)
    }

    @Test
    fun shouldProtectNotification_projectionInactive_false() {
        val notificationEntry = mock(NotificationEntry::class.java)
@@ -294,6 +307,23 @@ class SensitiveNotificationProtectionControllerTest : SysuiTestCase() {
        assertFalse(controller.shouldProtectNotification(notificationEntry))
    }

    @Test
    fun shouldProtectNotification_projectionActive_disabledViaDevOption_false() {
        setDisabledViaDeveloperOption()
        mediaProjectionCallback.onStart(mediaProjectionInfo)

        val notificationEntry = setupNotificationEntry(TEST_PROJECTION_PACKAGE_NAME)

        assertFalse(controller.shouldProtectNotification(notificationEntry))
    }

    private fun setDisabledViaDeveloperOption() {
        globalSettings.putInt(DISABLE_SCREEN_SHARE_PROTECTIONS_FOR_APPS_AND_NOTIFICATIONS, 1)

        // Process pending work that gets current developer option global setting
        executor.runAllReady()
    }

    private fun setShareFullScreen() {
        whenever(mediaProjectionInfo.packageName).thenReturn(TEST_PROJECTION_PACKAGE_NAME)
        whenever(mediaProjectionInfo.launchCookie).thenReturn(null)
+13 −3
Original line number Diff line number Diff line
@@ -16,6 +16,8 @@

package com.android.server;

import static android.provider.Settings.Global.DISABLE_SCREEN_SHARE_PROTECTIONS_FOR_APPS_AND_NOTIFICATIONS;

import static com.android.internal.util.Preconditions.checkNotNull;

import android.annotation.NonNull;
@@ -24,11 +26,10 @@ import android.content.ComponentName;
import android.content.Context;
import android.media.projection.MediaProjectionInfo;
import android.media.projection.MediaProjectionManager;
import android.os.Handler;
import android.os.Looper;
import android.os.RemoteException;
import android.os.Trace;
import android.os.UserHandle;
import android.provider.Settings;
import android.service.notification.NotificationListenerService;
import android.service.notification.NotificationListenerService.RankingMap;
import android.service.notification.StatusBarNotification;
@@ -117,7 +118,7 @@ public final class SensitiveContentProtectionManagerService extends SystemServic

        // TODO(b/317250444): use MediaProjectionManagerService directly, reduces unnecessary
        //  handler, delegate, and binder death recipient
        mProjectionManager.addCallback(mProjectionCallback, new Handler(Looper.getMainLooper()));
        mProjectionManager.addCallback(mProjectionCallback, getContext().getMainThreadHandler());

        try {
            mNotificationListener.registerAsSystemService(
@@ -148,6 +149,15 @@ public final class SensitiveContentProtectionManagerService extends SystemServic
    }

    private void onProjectionStart() {
        // TODO(b/324447419): move GlobalSettings lookup to background thread
        boolean disableScreenShareProtections =
                Settings.Global.getInt(getContext().getContentResolver(),
                        DISABLE_SCREEN_SHARE_PROTECTIONS_FOR_APPS_AND_NOTIFICATIONS, 0) != 0;
        if (disableScreenShareProtections) {
            Log.w(TAG, "Screen share protections disabled, ignoring projection start");
            return;
        }

        synchronized (mSensitiveContentProtectionLock) {
            mProjectionActive = true;
            updateAppsThatShouldBlockScreenCapture();
+58 −0
Original line number Diff line number Diff line
@@ -31,6 +31,7 @@ import static org.mockito.Mockito.when;

import android.media.projection.MediaProjectionInfo;
import android.media.projection.MediaProjectionManager;
import android.provider.Settings;
import android.service.notification.NotificationListenerService.Ranking;
import android.service.notification.NotificationListenerService.RankingMap;
import android.service.notification.StatusBarNotification;
@@ -390,6 +391,16 @@ public class SensitiveContentProtectionManagerServiceTest {
        verify(mWindowManager).addBlockScreenCaptureForApps(EMPTY_SET);
    }

    @Test
    public void mediaProjectionOnStart_disabledViaDevOption_noBlockedPackages() {
        mockDisabledViaDevelopOption();
        setupSensitiveNotification();

        mMediaProjectionCallbackCaptor.getValue().onStart(mock(MediaProjectionInfo.class));

        verifyZeroInteractions(mWindowManager);
    }

    @Test
    public void nlsOnListenerConnected_projectionNotStarted_noop() {
        // Sets up mNotification1 & mRankingMap to be a sensitive notification, and mNotification2
@@ -483,6 +494,18 @@ public class SensitiveContentProtectionManagerServiceTest {
        verify(mWindowManager).addBlockScreenCaptureForApps(EMPTY_SET);
    }

    @Test
    public void nlsOnListenerConnected_disabledViaDevOption_noBlockedPackages() {
        mockDisabledViaDevelopOption();
        // Sets up mNotification1 & mRankingMap to be a sensitive notification, and mNotification2
        // as non-sensitive
        setupSensitiveNotification();
        mMediaProjectionCallbackCaptor.getValue().onStart(mock(MediaProjectionInfo.class));
        mSensitiveContentProtectionManagerService.mNotificationListener.onListenerConnected();

        verifyZeroInteractions(mWindowManager);
    }

    @Test
    public void nlsOnNotificationRankingUpdate_projectionNotStarted_noop() {
        // Sets up mNotification1 & mRankingMap to be a sensitive notification, and mNotification2
@@ -598,6 +621,19 @@ public class SensitiveContentProtectionManagerServiceTest {
        verify(mWindowManager).addBlockScreenCaptureForApps(EMPTY_SET);
    }

    @Test
    public void nlsOnNotificationRankingUpdate_disabledViaDevOption_noBlockedPackages() {
        mockDisabledViaDevelopOption();
        // Sets up mNotification1 & mRankingMap to be a sensitive notification, and mNotification2
        // as non-sensitive
        setupSensitiveNotification();
        mMediaProjectionCallbackCaptor.getValue().onStart(mock(MediaProjectionInfo.class));
        mSensitiveContentProtectionManagerService.mNotificationListener
                .onNotificationRankingUpdate(mRankingMap);

        verifyZeroInteractions(mWindowManager);
    }

    @Test
    public void nlsOnNotificationPosted_projectionNotStarted_noop() {
        // Sets up mNotification1 & mRankingMap to be a sensitive notification, and mNotification2
@@ -697,4 +733,26 @@ public class SensitiveContentProtectionManagerServiceTest {

        verifyZeroInteractions(mWindowManager);
    }

    @Test
    public void nlsOnNotificationPosted_disabledViaDevOption_noBlockedPackages() {
        mockDisabledViaDevelopOption();
        // Sets up mNotification1 & mRankingMap to be a sensitive notification, and mNotification2
        // as non-sensitive
        setupSensitiveNotification();
        mMediaProjectionCallbackCaptor.getValue().onStart(mock(MediaProjectionInfo.class));
        mSensitiveContentProtectionManagerService.mNotificationListener
                .onNotificationPosted(mNotification1, mRankingMap);

        verifyZeroInteractions(mWindowManager);
    }

    private void mockDisabledViaDevelopOption() {
        // mContext (TestableContext) uses [TestableSettingsProvider] and it will be cleared after
        // the test
        Settings.Global.putInt(
                mContext.getContentResolver(),
                Settings.Global.DISABLE_SCREEN_SHARE_PROTECTIONS_FOR_APPS_AND_NOTIFICATIONS,
                1);
    }
}