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

Commit 7b35eb95 authored by Sam Cackett's avatar Sam Cackett Committed by Android (Google) Code Review
Browse files

Merge "[PartialScreenShare] Add tracking for media projection initiated" into main

parents 57cb73f2 485fa22d
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -175,5 +175,5 @@ interface IMediaProjectionManager {
    @EnforcePermission("android.Manifest.permission.MANAGE_MEDIA_PROJECTION")
    @JavaPassthrough(annotation = "@android.annotation.RequiresPermission(android.Manifest"
            + ".permission.MANAGE_MEDIA_PROJECTION)")
    void notifyPermissionRequestStateChange(int hostUid, int state, int sessionCreationSource);
    oneway void notifyPermissionRequestStateChange(int hostUid, int state, int sessionCreationSource);
}
+38 −12
Original line number Diff line number Diff line
@@ -19,7 +19,11 @@ import android.media.projection.IMediaProjectionManager
import android.os.Process
import android.os.RemoteException
import android.util.Log
import com.android.internal.util.FrameworkStatsLog
import com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__CREATION_SOURCE__CREATION_SOURCE_APP as METRICS_CREATION_SOURCE_APP
import com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__CREATION_SOURCE__CREATION_SOURCE_CAST as METRICS_CREATION_SOURCE_CAST
import com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__CREATION_SOURCE__CREATION_SOURCE_SYSTEM_UI_SCREEN_RECORDER as METRICS_CREATION_SOURCE_SYSTEM_UI_SCREEN_RECORDER
import com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__CREATION_SOURCE__CREATION_SOURCE_UNKNOWN as METRICS_CREATION_SOURCE_UNKNOWN
import com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_INITIATED
import com.android.systemui.dagger.SysUISingleton
import javax.inject.Inject

@@ -36,21 +40,23 @@ constructor(private val service: IMediaProjectionManager) {
     *
     * @param sessionCreationSource The entry point requesting permission to capture.
     */
    fun notifyPermissionProgress(state: Int, sessionCreationSource: Int) {
        // TODO check that state & SessionCreationSource matches expected values
        notifyToServer(state, sessionCreationSource)
    fun notifyProjectionInitiated(sessionCreationSource: SessionCreationSource) {
        notifyToServer(
            MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_INITIATED,
            sessionCreationSource
        )
    }

    /**
     * Request to log that the permission request moved to the given state.
     *
     * Should not be used for the initialization state, since that
     * Should not be used for the initialization state, since that should use {@link
     * MediaProjectionMetricsLogger#notifyProjectionInitiated(Int)} and pass the
     * sessionCreationSource.
     */
    fun notifyPermissionProgress(state: Int) {
        // TODO validate state is valid
        notifyToServer(
            state,
            FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_UNKNOWN)
        notifyToServer(state, SessionCreationSource.UNKNOWN)
    }

    /**
@@ -64,16 +70,21 @@ constructor(private val service: IMediaProjectionManager) {
     *   Indicates the entry point for requesting the permission. Must be a valid state defined in
     *   the SessionCreationSource enum.
     */
    private fun notifyToServer(state: Int, sessionCreationSource: Int) {
    private fun notifyToServer(state: Int, sessionCreationSource: SessionCreationSource) {
        Log.v(TAG, "FOO notifyToServer of state $state and source $sessionCreationSource")
        try {
            service.notifyPermissionRequestStateChange(
                Process.myUid(), state, sessionCreationSource)
                Process.myUid(),
                state,
                sessionCreationSource.toMetricsConstant()
            )
        } catch (e: RemoteException) {
            Log.e(
                TAG,
                "Error notifying server of permission flow state $state from source $sessionCreationSource",
                e)
                "Error notifying server of permission flow state $state from source " +
                    "$sessionCreationSource",
                e
            )
        }
    }

@@ -81,3 +92,18 @@ constructor(private val service: IMediaProjectionManager) {
        const val TAG = "MediaProjectionMetricsLogger"
    }
}

enum class SessionCreationSource {
    APP,
    CAST,
    SYSTEM_UI_SCREEN_RECORDER,
    UNKNOWN;

    fun toMetricsConstant(): Int =
        when (this) {
            APP -> METRICS_CREATION_SOURCE_APP
            CAST -> METRICS_CREATION_SOURCE_CAST
            SYSTEM_UI_SCREEN_RECORDER -> METRICS_CREATION_SOURCE_SYSTEM_UI_SCREEN_RECORDER
            UNKNOWN -> METRICS_CREATION_SOURCE_UNKNOWN
        }
}
+19 −3
Original line number Diff line number Diff line
@@ -53,7 +53,9 @@ import android.view.Window;

import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
import com.android.systemui.mediaprojection.MediaProjectionMetricsLogger;
import com.android.systemui.mediaprojection.MediaProjectionServiceHelper;
import com.android.systemui.mediaprojection.SessionCreationSource;
import com.android.systemui.mediaprojection.appselector.MediaProjectionAppSelectorActivity;
import com.android.systemui.mediaprojection.devicepolicy.ScreenCaptureDevicePolicyResolver;
import com.android.systemui.mediaprojection.devicepolicy.ScreenCaptureDisabledDialog;
@@ -74,6 +76,7 @@ public class MediaProjectionPermissionActivity extends Activity
    private final FeatureFlags mFeatureFlags;
    private final Lazy<ScreenCaptureDevicePolicyResolver> mScreenCaptureDevicePolicyResolver;
    private final StatusBarManager mStatusBarManager;
    private final MediaProjectionMetricsLogger mMediaProjectionMetricsLogger;

    private String mPackageName;
    private int mUid;
@@ -90,15 +93,17 @@ public class MediaProjectionPermissionActivity extends Activity
    @Inject
    public MediaProjectionPermissionActivity(FeatureFlags featureFlags,
            Lazy<ScreenCaptureDevicePolicyResolver> screenCaptureDevicePolicyResolver,
            StatusBarManager statusBarManager) {
            StatusBarManager statusBarManager,
            MediaProjectionMetricsLogger mediaProjectionMetricsLogger) {
        mFeatureFlags = featureFlags;
        mScreenCaptureDevicePolicyResolver = screenCaptureDevicePolicyResolver;
        mStatusBarManager = statusBarManager;
        mMediaProjectionMetricsLogger = mediaProjectionMetricsLogger;
    }

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

        final Intent launchingIntent = getIntent();
        mReviewGrantedConsentRequired = launchingIntent.getBooleanExtra(
@@ -133,6 +138,10 @@ public class MediaProjectionPermissionActivity extends Activity

        try {
            if (MediaProjectionServiceHelper.hasProjectionPermission(mUid, mPackageName)) {
                if (savedInstanceState == null) {
                    mMediaProjectionMetricsLogger.notifyProjectionInitiated(
                            SessionCreationSource.APP);
                }
                final IMediaProjection projection =
                        MediaProjectionServiceHelper.createOrReuseProjection(mUid, mPackageName,
                                mReviewGrantedConsentRequired);
@@ -231,6 +240,13 @@ public class MediaProjectionPermissionActivity extends Activity
            mDialog = dialogBuilder.create();
        }

        if (savedInstanceState == null) {
            mMediaProjectionMetricsLogger.notifyProjectionInitiated(
                    appName == null
                            ? SessionCreationSource.CAST
                            : SessionCreationSource.APP);
        }

        setUpDialog(mDialog);
        mDialog.show();
    }
+11 −3
Original line number Diff line number Diff line
@@ -38,6 +38,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.MediaProjectionMetricsLogger;
import com.android.systemui.mediaprojection.SessionCreationSource;
import com.android.systemui.mediaprojection.devicepolicy.ScreenCaptureDevicePolicyResolver;
import com.android.systemui.mediaprojection.devicepolicy.ScreenCaptureDisabledDialog;
import com.android.systemui.plugins.ActivityStarter;
@@ -45,13 +47,13 @@ import com.android.systemui.settings.UserContextProvider;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.policy.CallbackController;

import dagger.Lazy;

import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.Executor;

import javax.inject.Inject;

import dagger.Lazy;

/**
 * Helper class to initiate a screen recording
 */
@@ -71,6 +73,7 @@ public class RecordingController
    private final FeatureFlags mFlags;
    private final UserContextProvider mUserContextProvider;
    private final UserTracker mUserTracker;
    private final MediaProjectionMetricsLogger mMediaProjectionMetricsLogger;

    protected static final String INTENT_UPDATE_STATE =
            "com.android.systemui.screenrecord.UPDATE_STATE";
@@ -115,7 +118,8 @@ public class RecordingController
            FeatureFlags flags,
            UserContextProvider userContextProvider,
            Lazy<ScreenCaptureDevicePolicyResolver> devicePolicyResolver,
            UserTracker userTracker) {
            UserTracker userTracker,
            MediaProjectionMetricsLogger mediaProjectionMetricsLogger) {
        mMainExecutor = mainExecutor;
        mContext = context;
        mFlags = flags;
@@ -123,6 +127,7 @@ public class RecordingController
        mBroadcastDispatcher = broadcastDispatcher;
        mUserContextProvider = userContextProvider;
        mUserTracker = userTracker;
        mMediaProjectionMetricsLogger = mediaProjectionMetricsLogger;

        BroadcastOptions options = BroadcastOptions.makeBasic();
        options.setInteractive(true);
@@ -149,6 +154,9 @@ public class RecordingController
            return new ScreenCaptureDisabledDialog(mContext);
        }

        mMediaProjectionMetricsLogger.notifyProjectionInitiated(
                SessionCreationSource.SYSTEM_UI_SCREEN_RECORDER);

        return flags.isEnabled(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING)
                ? new ScreenRecordPermissionDialog(context,  getHostUserHandle(), this,
                    activityStarter, mUserContextProvider, onStartRecordingClicked)
+32 −2
Original line number Diff line number Diff line
@@ -17,8 +17,10 @@
package com.android.systemui.screenrecord;

import static com.google.common.truth.Truth.assertThat;

import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;

import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.verify;
@@ -37,6 +39,8 @@ import com.android.systemui.animation.DialogLaunchAnimator;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.flags.FakeFeatureFlags;
import com.android.systemui.flags.Flags;
import com.android.systemui.mediaprojection.MediaProjectionMetricsLogger;
import com.android.systemui.mediaprojection.SessionCreationSource;
import com.android.systemui.mediaprojection.devicepolicy.ScreenCaptureDevicePolicyResolver;
import com.android.systemui.mediaprojection.devicepolicy.ScreenCaptureDisabledDialog;
import com.android.systemui.plugins.ActivityStarter;
@@ -76,6 +80,8 @@ public class RecordingControllerTest extends SysuiTestCase {
    private ActivityStarter mActivityStarter;
    @Mock
    private UserTracker mUserTracker;
    @Mock
    private MediaProjectionMetricsLogger mMediaProjectionMetricsLogger;

    private FakeFeatureFlags mFeatureFlags;
    private RecordingController mController;
@@ -86,8 +92,15 @@ public class RecordingControllerTest extends SysuiTestCase {
    public void setUp() {
        MockitoAnnotations.initMocks(this);
        mFeatureFlags = new FakeFeatureFlags();
        mController = new RecordingController(mMainExecutor, mBroadcastDispatcher, mContext,
                mFeatureFlags, mUserContextProvider, () -> mDevicePolicyResolver, mUserTracker);
        mController = new RecordingController(
                mMainExecutor,
                mBroadcastDispatcher,
                mContext,
                mFeatureFlags,
                mUserContextProvider,
                () -> mDevicePolicyResolver,
                mUserTracker,
                mMediaProjectionMetricsLogger);
        mController.addCallback(mCallback);
    }

@@ -269,4 +282,21 @@ public class RecordingControllerTest extends SysuiTestCase {

        assertThat(dialog).isInstanceOf(ScreenRecordPermissionDialog.class);
    }

    @Test
    public void testPoliciesFlagEnabled_screenCapturingAllowed_logsProjectionInitiated() {
        if (Looper.myLooper() == null) {
            Looper.prepare();
        }

        mFeatureFlags.set(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING, true);
        mFeatureFlags.set(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING_ENTERPRISE_POLICIES, true);
        when(mDevicePolicyResolver.isScreenCaptureCompletelyDisabled((any()))).thenReturn(false);

        mController.createScreenRecordDialog(mContext, mFeatureFlags,
                mDialogLaunchAnimator, mActivityStarter, /* onStartRecordingClicked= */ null);

        verify(mMediaProjectionMetricsLogger)
                .notifyProjectionInitiated(SessionCreationSource.SYSTEM_UI_SCREEN_RECORDER);
    }
}