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

Commit 97a77117 authored by Daniel Akinola's avatar Daniel Akinola Committed by Android (Google) Code Review
Browse files

Merge "PSS Override" into main

parents a48b7ade d0cc94af
Loading
Loading
Loading
Loading
+15 −0
Original line number Original line Diff line number Diff line
@@ -23,6 +23,9 @@ import android.annotation.SystemService;
import android.annotation.TestApi;
import android.annotation.TestApi;
import android.app.Activity;
import android.app.Activity;
import android.app.ActivityOptions.LaunchCookie;
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.ComponentName;
import android.content.Context;
import android.content.Context;
import android.content.Intent;
import android.content.Intent;
@@ -65,6 +68,18 @@ import java.util.Map;
public final class MediaProjectionManager {
public final class MediaProjectionManager {
    private static final String TAG = "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.
     * Intent extra to customize the permission dialog based on the host app's preferences.
     * @hide
     * @hide
+11 −0
Original line number Original line Diff line number Diff line
@@ -16,8 +16,11 @@


package com.android.systemui.mediaprojection.permission;
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_PACKAGE_REUSING_GRANTED_CONSENT;
import static android.media.projection.IMediaProjectionManager.EXTRA_USER_REVIEW_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_CANCEL;
import static android.media.projection.ReviewGrantedConsentResult.RECORD_CONTENT_DISPLAY;
import static android.media.projection.ReviewGrantedConsentResult.RECORD_CONTENT_DISPLAY;
import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
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 static com.android.systemui.mediaprojection.permission.ScreenShareOptionKt.SINGLE_APP;


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


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


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


+141 −0
Original line number Original line 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()
    }
}