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

Commit 3733ec3b authored by Nick Chameyev's avatar Nick Chameyev
Browse files

[Partial Screensharing] Handle enterprise policies

Adds a logic that shows/hides work
profile or personal profile tabs
in partial screen sharing when it
is not allowed to capture the screen.

Adds a logic that shows screen capture
disabled dialog when we can't even
start the app picker dialog (when
we can't capture the screen completely).

Bug: 233348916
Test: manual testing using TestDPC with various
  device policies (disabled screen capture on
  work profile, on fully managed device,
  disallow sharing into managed profile policy),
  starting screen sharing from work or personal
  profile host app
Test: com.android.systemui.screenrecord.RecordingControllerTest
Test: com.android.systemui.mediaprojection.devicepolicy.ScreenCaptureDevicePolicyResolverTest
Change-Id: Ib732826a977c560318ff147d630d426c0828b3bd
parent 2319065b
Loading
Loading
Loading
Loading
+13 −1
Original line number Diff line number Diff line
@@ -28,12 +28,15 @@ import android.os.ResultReceiver
import android.os.UserHandle
import android.view.ViewGroup
import com.android.internal.annotations.VisibleForTesting
import com.android.internal.app.AbstractMultiProfilePagerAdapter.EmptyStateProvider
import com.android.internal.app.AbstractMultiProfilePagerAdapter.MyUserIdProvider
import com.android.internal.app.ChooserActivity
import com.android.internal.app.ResolverListController
import com.android.internal.app.chooser.NotSelectableTargetInfo
import com.android.internal.app.chooser.TargetInfo
import com.android.systemui.R
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.mediaprojection.appselector.MediaProjectionAppSelectorComponent
import com.android.systemui.mediaprojection.appselector.MediaProjectionAppSelectorController
import com.android.systemui.mediaprojection.appselector.MediaProjectionAppSelectorResultHandler
@@ -47,6 +50,7 @@ import javax.inject.Inject
class MediaProjectionAppSelectorActivity(
    private val componentFactory: MediaProjectionAppSelectorComponent.Factory,
    private val activityLauncher: AsyncActivityLauncher,
    private val featureFlags: FeatureFlags,
    /** This is used to override the dependency in a screenshot test */
    @VisibleForTesting
    private val listControllerFactory: ((userHandle: UserHandle) -> ResolverListController)?
@@ -56,7 +60,8 @@ class MediaProjectionAppSelectorActivity(
    constructor(
        componentFactory: MediaProjectionAppSelectorComponent.Factory,
        activityLauncher: AsyncActivityLauncher,
    ) : this(componentFactory, activityLauncher, null)
        featureFlags: FeatureFlags
    ) : this(componentFactory, activityLauncher, featureFlags, listControllerFactory = null)

    private lateinit var configurationController: ConfigurationController
    private lateinit var controller: MediaProjectionAppSelectorController
@@ -91,6 +96,13 @@ class MediaProjectionAppSelectorActivity(

    override fun appliedThemeResId(): Int = R.style.Theme_SystemUI_MediaProjectionAppSelector

    override fun createBlockerEmptyStateProvider(): EmptyStateProvider =
        if (featureFlags.isEnabled(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING_ENTERPRISE_POLICIES)) {
            component.emptyStateProvider
        } else {
            super.createBlockerEmptyStateProvider()
        }

    override fun createListController(userHandle: UserHandle): ResolverListController =
        listControllerFactory?.invoke(userHandle) ?: super.createListController(userHandle)

+54 −14
Original line number Diff line number Diff line
@@ -45,33 +45,46 @@ import android.text.style.StyleSpan;
import android.util.Log;
import android.view.Window;

import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
import com.android.systemui.mediaprojection.devicepolicy.ScreenCaptureDevicePolicyResolver;
import com.android.systemui.mediaprojection.devicepolicy.ScreenCaptureDisabledDialog;
import com.android.systemui.screenrecord.MediaProjectionPermissionDialog;
import com.android.systemui.screenrecord.ScreenShareOption;
import com.android.systemui.statusbar.phone.SystemUIDialog;
import com.android.systemui.util.Utils;

import javax.inject.Inject;

import dagger.Lazy;

public class MediaProjectionPermissionActivity extends Activity
        implements DialogInterface.OnClickListener, DialogInterface.OnCancelListener {
    private static final String TAG = "MediaProjectionPermissionActivity";
    private static final float MAX_APP_NAME_SIZE_PX = 500f;
    private static final String ELLIPSIS = "\u2026";

    private final FeatureFlags mFeatureFlags;
    private final Lazy<ScreenCaptureDevicePolicyResolver> mScreenCaptureDevicePolicyResolver;

    private String mPackageName;
    private int mUid;
    private IMediaProjectionManager mService;
    private FeatureFlags mFeatureFlags;

    private AlertDialog mDialog;

    @Inject
    public MediaProjectionPermissionActivity(FeatureFlags featureFlags,
            Lazy<ScreenCaptureDevicePolicyResolver> screenCaptureDevicePolicyResolver) {
        mFeatureFlags = featureFlags;
        mScreenCaptureDevicePolicyResolver = screenCaptureDevicePolicyResolver;
    }

    @Override
    public void onCreate(Bundle icicle) {
        super.onCreate(icicle);

        mFeatureFlags = Dependency.get(FeatureFlags.class);
        mPackageName = getCallingPackage();
        IBinder b = ServiceManager.getService(MEDIA_PROJECTION_SERVICE);
        mService = IMediaProjectionManager.Stub.asInterface(b);
@@ -104,6 +117,12 @@ public class MediaProjectionPermissionActivity extends Activity
            return;
        }

        if (mFeatureFlags.isEnabled(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING_ENTERPRISE_POLICIES)) {
            if (showScreenCaptureDisabledDialogIfNeeded()) {
                return;
            }
        }

        TextPaint paint = new TextPaint();
        paint.setTextSize(42);

@@ -171,16 +190,7 @@ public class MediaProjectionPermissionActivity extends Activity
            mDialog = dialogBuilder.create();
        }

        SystemUIDialog.registerDismissListener(mDialog);
        SystemUIDialog.applyFlags(mDialog);
        SystemUIDialog.setDialogSize(mDialog);

        mDialog.setOnCancelListener(this);
        mDialog.create();
        mDialog.getButton(DialogInterface.BUTTON_POSITIVE).setFilterTouchesWhenObscured(true);

        final Window w = mDialog.getWindow();
        w.addSystemFlags(SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
        setUpDialog(mDialog);

        mDialog.show();
    }
@@ -200,6 +210,32 @@ public class MediaProjectionPermissionActivity extends Activity
        }
    }

    private void setUpDialog(AlertDialog dialog) {
        SystemUIDialog.registerDismissListener(dialog);
        SystemUIDialog.applyFlags(dialog);
        SystemUIDialog.setDialogSize(dialog);

        dialog.setOnCancelListener(this);
        dialog.create();
        dialog.getButton(DialogInterface.BUTTON_POSITIVE).setFilterTouchesWhenObscured(true);

        final Window w = dialog.getWindow();
        w.addSystemFlags(SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
    }

    private boolean showScreenCaptureDisabledDialogIfNeeded() {
        final UserHandle hostUserHandle = getHostUserHandle();
        if (mScreenCaptureDevicePolicyResolver.get()
                .isScreenCaptureCompletelyDisabled(hostUserHandle)) {
            AlertDialog dialog = new ScreenCaptureDisabledDialog(this);
            setUpDialog(dialog);
            dialog.show();
            return true;
        }

        return false;
    }

    private void grantMediaProjectionPermission(int screenShareMode) {
        try {
            if (screenShareMode == ENTIRE_SCREEN) {
@@ -211,7 +247,7 @@ public class MediaProjectionPermissionActivity extends Activity
                intent.putExtra(MediaProjectionManager.EXTRA_MEDIA_PROJECTION,
                        projection.asBinder());
                intent.putExtra(MediaProjectionAppSelectorActivity.EXTRA_HOST_APP_USER_HANDLE,
                        UserHandle.getUserHandleForUid(getLaunchedFromUid()));
                        getHostUserHandle());
                intent.setFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);

                // Start activity from the current foreground user to avoid creating a separate
@@ -230,6 +266,10 @@ public class MediaProjectionPermissionActivity extends Activity
        }
    }

    private UserHandle getHostUserHandle() {
        return UserHandle.getUserHandleForUid(getLaunchedFromUid());
    }

    private IMediaProjection createProjection(int uid, String packageName) throws RemoteException {
        return mService.createProjection(uid, packageName,
                MediaProjectionManager.TYPE_SCREEN_CAPTURE, false /* permanentGrant */);
+9 −2
Original line number Diff line number Diff line
@@ -24,6 +24,7 @@ import com.android.launcher3.icons.IconFactory
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.media.MediaProjectionAppSelectorActivity
import com.android.systemui.media.MediaProjectionAppSelectorActivity.Companion.EXTRA_HOST_APP_USER_HANDLE
import com.android.systemui.media.MediaProjectionPermissionActivity
import com.android.systemui.mediaprojection.appselector.data.ActivityTaskManagerLabelLoader
import com.android.systemui.mediaprojection.appselector.data.ActivityTaskManagerThumbnailLoader
import com.android.systemui.mediaprojection.appselector.data.AppIconLoader
@@ -45,10 +46,10 @@ import dagger.Provides
import dagger.Subcomponent
import dagger.multibindings.ClassKey
import dagger.multibindings.IntoMap
import javax.inject.Qualifier
import javax.inject.Scope
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.SupervisorJob
import javax.inject.Qualifier
import javax.inject.Scope

@Qualifier @Retention(AnnotationRetention.BINARY) annotation class MediaProjectionAppSelector

@@ -67,6 +68,11 @@ interface MediaProjectionModule {
    fun provideMediaProjectionAppSelectorActivity(
        activity: MediaProjectionAppSelectorActivity
    ): Activity

    @Binds
    @IntoMap
    @ClassKey(MediaProjectionPermissionActivity::class)
    fun bindsMediaProjectionPermissionActivity(impl: MediaProjectionPermissionActivity): Activity
}

/**
@@ -149,6 +155,7 @@ interface MediaProjectionAppSelectorComponent {

    val controller: MediaProjectionAppSelectorController
    val recentsViewController: MediaProjectionRecentsViewController
    val emptyStateProvider: MediaProjectionBlockerEmptyStateProvider
    @get:HostUserHandle val hostUserHandle: UserHandle
    @get:PersonalProfile val personalProfileUserHandle: UserHandle

+2 −1
Original line number Diff line number Diff line
@@ -171,8 +171,9 @@ public class ScreenRecordTile extends QSTileImpl<QSTile.BooleanState>
            getHost().collapsePanels();
        };

        Dialog dialog = mController.createScreenRecordDialog(mContext, mFlags,
        final Dialog dialog = mController.createScreenRecordDialog(mContext, mFlags,
                mDialogLaunchAnimator, mActivityStarter, onStartRecordingClicked);

        ActivityStarter.OnDismissAction dismissAction = () -> {
            if (shouldAnimateFromView) {
                mDialogLaunchAnimator.showFromView(dialog, view, new DialogCuj(
+33 −3
Original line number Diff line number Diff line
@@ -36,6 +36,8 @@ import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
import com.android.systemui.mediaprojection.devicepolicy.ScreenCaptureDevicePolicyResolver;
import com.android.systemui.mediaprojection.devicepolicy.ScreenCaptureDisabledDialog;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.settings.UserContextProvider;
import com.android.systemui.settings.UserTracker;
@@ -46,6 +48,8 @@ import java.util.concurrent.Executor;

import javax.inject.Inject;

import dagger.Lazy;

/**
 * Helper class to initiate a screen recording
 */
@@ -60,6 +64,8 @@ public class RecordingController
    private CountDownTimer mCountDownTimer = null;
    private final Executor mMainExecutor;
    private final BroadcastDispatcher mBroadcastDispatcher;
    private final Context mContext;
    private final FeatureFlags mFlags;
    private final UserContextProvider mUserContextProvider;
    private final UserTracker mUserTracker;

@@ -70,6 +76,8 @@ public class RecordingController
    private CopyOnWriteArrayList<RecordingStateChangeCallback> mListeners =
            new CopyOnWriteArrayList<>();

    private final Lazy<ScreenCaptureDevicePolicyResolver> mDevicePolicyResolver;

    @VisibleForTesting
    final UserTracker.Callback mUserChangedCallback =
            new UserTracker.Callback() {
@@ -100,22 +108,44 @@ public class RecordingController
    @Inject
    public RecordingController(@Main Executor mainExecutor,
            BroadcastDispatcher broadcastDispatcher,
            Context context,
            FeatureFlags flags,
            UserContextProvider userContextProvider,
            Lazy<ScreenCaptureDevicePolicyResolver> devicePolicyResolver,
            UserTracker userTracker) {
        mMainExecutor = mainExecutor;
        mContext = context;
        mFlags = flags;
        mDevicePolicyResolver = devicePolicyResolver;
        mBroadcastDispatcher = broadcastDispatcher;
        mUserContextProvider = userContextProvider;
        mUserTracker = userTracker;
    }

    /** Create a dialog to show screen recording options to the user. */
    /**
     * MediaProjection host is SystemUI for the screen recorder, so return 'my user handle'
     */
    private UserHandle getHostUserHandle() {
        return UserHandle.of(UserHandle.myUserId());
    }

    /** Create a dialog to show screen recording options to the user.
     *  If screen capturing is currently not allowed it will return a dialog
     *  that warns users about it. */
    public Dialog createScreenRecordDialog(Context context, FeatureFlags flags,
                                           DialogLaunchAnimator dialogLaunchAnimator,
                                           ActivityStarter activityStarter,
                                           @Nullable Runnable onStartRecordingClicked) {
        if (mFlags.isEnabled(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING_ENTERPRISE_POLICIES)
                && mDevicePolicyResolver.get()
                        .isScreenCaptureCompletelyDisabled(getHostUserHandle())) {
            return new ScreenCaptureDisabledDialog(mContext);
        }

        return flags.isEnabled(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING)
                ? new ScreenRecordPermissionDialog(context, this, activityStarter,
                        dialogLaunchAnimator, mUserContextProvider, onStartRecordingClicked)
                ? new ScreenRecordPermissionDialog(context,  getHostUserHandle(), this,
                    activityStarter, dialogLaunchAnimator, mUserContextProvider,
                    onStartRecordingClicked)
                : new ScreenRecordDialog(context, this, activityStarter,
                mUserContextProvider, flags, dialogLaunchAnimator, onStartRecordingClicked);
    }
Loading