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

Commit cd7ae441 authored by Chris Göllner's avatar Chris Göllner Committed by Daniel Akinola
Browse files

PSS opt-out override: move logic to MediaProjectionManager

Relanding PSS opt-out override from ag/25896593. Initial implementation defined the ChangeId on SysUI side, but SysUI tests seemed to not recognise it being disabled by default, leading to SysUI test failures. Now instead defining the ChangeId in MediaaProjectionmanager, SysUI is able to properly to read the override as disabled by default, so shouldn't break existing SysUI E2Es.

Bug: 316897322
Flag: None
Test: atest packages/SystemUI/tests/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionDialogDelegateTest.kt
Test: atest packages/SystemUI/tests/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogDelegateTest.kt
Test: atest CtsMediaProjectionTestCases:MediaProjectionCompatChangeTests
Test: atest PlatformScenarioTests:android.platform.test.scenario.sysui.mediaprojection.MediaProjectionPermissionTest
Change-Id: Ibb9ec963a094ab384980bcbd58c68a39d6094ef2
parent 0b65a14b
Loading
Loading
Loading
Loading
+15 −0
Original line number Diff line number Diff line
@@ -23,6 +23,9 @@ import android.annotation.SystemService;
import android.annotation.TestApi;
import android.app.Activity;
import android.app.ActivityOptions.LaunchCookie;
import android.compat.annotation.ChangeId;
import android.compat.annotation.Disabled;
import android.compat.annotation.Overridable;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -65,6 +68,18 @@ import java.util.Map;
public final class MediaProjectionManager {
    private static final String TAG = "MediaProjectionManager";

    /**
     * This change id ensures that users are presented with a choice of capturing a single app
     * or the entire screen when initiating a MediaProjection session, overriding the usage of
     * MediaProjectionConfig#createConfigForDefaultDisplay.
     *
     * @hide
     */
    @ChangeId
    @Overridable
    @Disabled
    public static final long OVERRIDE_DISABLE_MEDIA_PROJECTION_SINGLE_APP_OPTION = 316897322L;

    /**
     * Intent extra to customize the permission dialog based on the host app's preferences.
     * @hide
+11 −0
Original line number Diff line number Diff line
@@ -16,8 +16,11 @@

package com.android.systemui.mediaprojection.permission;

import static android.Manifest.permission.LOG_COMPAT_CHANGE;
import static android.Manifest.permission.READ_COMPAT_CHANGE_CONFIG;
import static android.media.projection.IMediaProjectionManager.EXTRA_PACKAGE_REUSING_GRANTED_CONSENT;
import static android.media.projection.IMediaProjectionManager.EXTRA_USER_REVIEW_GRANTED_CONSENT;
import static android.media.projection.MediaProjectionManager.OVERRIDE_DISABLE_MEDIA_PROJECTION_SINGLE_APP_OPTION;
import static android.media.projection.ReviewGrantedConsentResult.RECORD_CANCEL;
import static android.media.projection.ReviewGrantedConsentResult.RECORD_CONTENT_DISPLAY;
import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
@@ -26,11 +29,13 @@ import static com.android.systemui.mediaprojection.permission.ScreenShareOptionK
import static com.android.systemui.mediaprojection.permission.ScreenShareOptionKt.SINGLE_APP;

import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.app.Activity;
import android.app.ActivityManager;
import android.app.ActivityOptions.LaunchCookie;
import android.app.AlertDialog;
import android.app.StatusBarManager;
import android.app.compat.CompatChanges;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
@@ -108,6 +113,7 @@ public class MediaProjectionPermissionActivity extends Activity
    }

    @Override
    @RequiresPermission(allOf = {READ_COMPAT_CHANGE_CONFIG, LOG_COMPAT_CHANGE})
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

@@ -235,6 +241,10 @@ public class MediaProjectionPermissionActivity extends Activity
        // the correct screen width when in split screen.
        Context dialogContext = getApplicationContext();
        if (isPartialScreenSharingEnabled()) {
            final boolean overrideDisableSingleAppOption =
                    CompatChanges.isChangeEnabled(
                            OVERRIDE_DISABLE_MEDIA_PROJECTION_SINGLE_APP_OPTION,
                            mPackageName, getHostUserHandle());
            MediaProjectionPermissionDialogDelegate delegate =
                    new MediaProjectionPermissionDialogDelegate(
                            dialogContext,
@@ -246,6 +256,7 @@ public class MediaProjectionPermissionActivity extends Activity
                            },
                            () -> finish(RECORD_CANCEL, /* projection= */ null),
                            appName,
                            overrideDisableSingleAppOption,
                            mUid,
                            mMediaProjectionMetricsLogger);
            mDialog =
+9 −2
Original line number Diff line number Diff line
@@ -30,11 +30,12 @@ class MediaProjectionPermissionDialogDelegate(
    private val onStartRecordingClicked: Consumer<MediaProjectionPermissionDialogDelegate>,
    private val onCancelClicked: Runnable,
    private val appName: String?,
    private val forceShowPartialScreenshare: Boolean,
    hostUid: Int,
    mediaProjectionMetricsLogger: MediaProjectionMetricsLogger,
) :
    BaseMediaProjectionPermissionDialogDelegate<AlertDialog>(
        createOptionList(context, appName, mediaProjectionConfig),
        createOptionList(context, appName, mediaProjectionConfig, forceShowPartialScreenshare),
        appName,
        hostUid,
        mediaProjectionMetricsLogger
@@ -65,7 +66,8 @@ class MediaProjectionPermissionDialogDelegate(
        private fun createOptionList(
            context: Context,
            appName: String?,
            mediaProjectionConfig: MediaProjectionConfig?
            mediaProjectionConfig: MediaProjectionConfig?,
            overrideDisableSingleAppOption: Boolean = false,
        ): List<ScreenShareOption> {
            val singleAppWarningText =
                if (appName == null) {
@@ -80,8 +82,13 @@ class MediaProjectionPermissionDialogDelegate(
                    R.string.media_projection_entry_app_permission_dialog_warning_entire_screen
                }

            // The single app option should only be disabled if there is an app name provided,
            // the client has setup a MediaProjection with
            // MediaProjectionConfig#createConfigForDefaultDisplay, AND it hasn't been overridden by
            // the OVERRIDE_DISABLE_SINGLE_APP_OPTION per-app override.
            val singleAppOptionDisabled =
                appName != null &&
                    !overrideDisableSingleAppOption &&
                    mediaProjectionConfig?.regionToCapture ==
                        MediaProjectionConfig.CAPTURE_REGION_FIXED_DISPLAY

+141 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2022 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.mediaprojection.permission

import android.app.AlertDialog
import android.media.projection.MediaProjectionConfig
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import android.view.WindowManager
import android.widget.Spinner
import android.widget.TextView
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.flags.FeatureFlagsClassic
import com.android.systemui.flags.Flags
import com.android.systemui.mediaprojection.MediaProjectionMetricsLogger
import com.android.systemui.res.R
import com.android.systemui.statusbar.phone.AlertDialogWithDelegate
import com.android.systemui.statusbar.phone.SystemUIDialog
import com.android.systemui.util.mockito.mock
import junit.framework.Assert.assertEquals
import org.junit.After
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mockito.`when` as whenever

@SmallTest
@RunWith(AndroidTestingRunner::class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
class MediaProjectionPermissionDialogDelegateTest : SysuiTestCase() {

    private lateinit var dialog: AlertDialog

    private val flags = mock<FeatureFlagsClassic>()
    private val onStartRecordingClicked = mock<Runnable>()
    private val mediaProjectionMetricsLogger = mock<MediaProjectionMetricsLogger>()

    private val mediaProjectionConfig: MediaProjectionConfig =
        MediaProjectionConfig.createConfigForDefaultDisplay()
    private val appName: String = "testApp"
    private val hostUid: Int = 12345

    private val resIdSingleApp = R.string.screen_share_permission_dialog_option_single_app
    private val resIdFullScreen = R.string.screen_share_permission_dialog_option_entire_screen
    private val resIdSingleAppDisabled =
        R.string.media_projection_entry_app_permission_dialog_single_app_disabled

    @Before
    fun setUp() {
        whenever(flags.isEnabled(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING)).thenReturn(true)
    }

    @After
    fun teardown() {
        if (::dialog.isInitialized) {
            dialog.dismiss()
        }
    }

    @Test
    fun showDialog_forceShowPartialScreenShareFalse() {
        // Set up dialog with MediaProjectionConfig.createConfigForDefaultDisplay() and
        // overrideDisableSingleAppOption = false
        val overrideDisableSingleAppOption = false
        setUpAndShowDialog(overrideDisableSingleAppOption)

        val spinner = dialog.requireViewById<Spinner>(R.id.screen_share_mode_spinner)
        val secondOptionText =
            spinner.adapter
                .getDropDownView(1, null, spinner)
                .findViewById<TextView>(android.R.id.text2)
                ?.text

        // check that the first option is full screen and enabled
        assertEquals(context.getString(resIdFullScreen), spinner.selectedItem)

        // check that the second option is single app and disabled
        assertEquals(context.getString(resIdSingleAppDisabled, appName), secondOptionText)
    }

    @Test
    fun showDialog_forceShowPartialScreenShareTrue() {
        // Set up dialog with MediaProjectionConfig.createConfigForDefaultDisplay() and
        // overrideDisableSingleAppOption = true
        val overrideDisableSingleAppOption = true
        setUpAndShowDialog(overrideDisableSingleAppOption)

        val spinner = dialog.requireViewById<Spinner>(R.id.screen_share_mode_spinner)
        val secondOptionText =
            spinner.adapter
                .getDropDownView(1, null, spinner)
                .findViewById<TextView>(android.R.id.text1)
                ?.text

        // check that the first option is single app and enabled
        assertEquals(context.getString(resIdSingleApp), spinner.selectedItem)

        // check that the second option is full screen and enabled
        assertEquals(context.getString(resIdFullScreen), secondOptionText)
    }

    private fun setUpAndShowDialog(overrideDisableSingleAppOption: Boolean) {
        val delegate =
            MediaProjectionPermissionDialogDelegate(
                context,
                mediaProjectionConfig,
                {},
                onStartRecordingClicked,
                appName,
                overrideDisableSingleAppOption,
                hostUid,
                mediaProjectionMetricsLogger
            )

        dialog = AlertDialogWithDelegate(context, R.style.Theme_SystemUI_Dialog, delegate)
        SystemUIDialog.applyFlags(dialog)
        SystemUIDialog.setDialogSize(dialog)

        dialog.window?.addSystemFlags(
            WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS
        )

        delegate.onCreate(dialog, savedInstanceState = null)
        dialog.show()
    }
}