Merge "[Partial Screenshare] log projection permission cancelled" into main
diff --git a/media/java/android/media/projection/IMediaProjectionManager.aidl b/media/java/android/media/projection/IMediaProjectionManager.aidl
index 10c880d..24efbd1 100644
--- a/media/java/android/media/projection/IMediaProjectionManager.aidl
+++ b/media/java/android/media/projection/IMediaProjectionManager.aidl
@@ -158,26 +158,6 @@
in @nullable IMediaProjection projection);
/**
- * Notifies system server that we are handling a particular state during the consent flow.
- *
- * <p>Only used for emitting atoms.
- *
- * @param hostUid The uid of the process requesting consent to capture, may be an app or
- * SystemUI.
- * @param state The state that SystemUI is handling during the consent flow.
- * Must be a valid
- * state defined in the MediaProjectionState enum.
- * @param sessionCreationSource Only set if the state is MEDIA_PROJECTION_STATE_INITIATED.
- * Indicates the entry point for requesting the permission. Must be
- * a valid state defined
- * in the SessionCreationSource enum.
- */
- @EnforcePermission("android.Manifest.permission.MANAGE_MEDIA_PROJECTION")
- @JavaPassthrough(annotation = "@android.annotation.RequiresPermission(android.Manifest"
- + ".permission.MANAGE_MEDIA_PROJECTION)")
- oneway void notifyPermissionRequestStateChange(int hostUid, int state, int sessionCreationSource);
-
- /**
* Notifies system server that the permission request was initiated.
*
* <p>Only used for emitting atoms.
@@ -208,6 +188,19 @@
oneway void notifyPermissionRequestDisplayed(int hostUid);
/**
+ * Notifies system server that the permission request was cancelled.
+ *
+ * <p>Only used for emitting atoms.
+ *
+ * @param hostUid The uid of the process requesting consent to capture, may be an app or
+ * SystemUI.
+ */
+ @EnforcePermission("android.Manifest.permission.MANAGE_MEDIA_PROJECTION")
+ @JavaPassthrough(annotation = "@android.annotation.RequiresPermission(android.Manifest"
+ + ".permission.MANAGE_MEDIA_PROJECTION)")
+ oneway void notifyPermissionRequestCancelled(int hostUid);
+
+ /**
* Notifies system server that the app selector was displayed.
*
* <p>Only used for emitting atoms.
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/MediaProjectionMetricsLogger.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/MediaProjectionMetricsLogger.kt
index 1962119..98a3896 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/MediaProjectionMetricsLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/MediaProjectionMetricsLogger.kt
@@ -16,7 +16,6 @@
package com.android.systemui.mediaprojection
import android.media.projection.IMediaProjectionManager
-import android.os.Process
import android.os.RemoteException
import android.util.Log
import com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__CREATION_SOURCE__CREATION_SOURCE_APP as METRICS_CREATION_SOURCE_APP
@@ -66,6 +65,19 @@
}
/**
+ * Request to log that the permission request was cancelled.
+ *
+ * @param hostUid The UID of the package that initiates MediaProjection.
+ */
+ fun notifyProjectionRequestCancelled(hostUid: Int) {
+ try {
+ service.notifyPermissionRequestCancelled(hostUid)
+ } catch (e: RemoteException) {
+ Log.e(TAG, "Error notifying server of projection cancelled", e)
+ }
+ }
+
+ /**
* Request to log that the app selector was displayed.
*
* @param hostUid The UID of the package that initiates MediaProjection.
@@ -78,47 +90,6 @@
}
}
- /**
- * Request to log that the permission request moved to the given state.
- *
- * 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, SessionCreationSource.UNKNOWN)
- }
-
- /**
- * Notifies system server that we are handling a particular state during the consent flow.
- *
- * Only used for emitting atoms.
- *
- * @param state The state that SystemUI is handling during the consent flow. Must be a valid
- * state defined in the MediaProjectionState enum.
- * @param sessionCreationSource Only set if the state is MEDIA_PROJECTION_STATE_INITIATED.
- * Indicates the entry point for requesting the permission. Must be a valid state defined in
- * the SessionCreationSource enum.
- */
- 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.toMetricsConstant()
- )
- } catch (e: RemoteException) {
- Log.e(
- TAG,
- "Error notifying server of permission flow state $state from source " +
- "$sessionCreationSource",
- e
- )
- }
- }
-
companion object {
const val TAG = "MediaProjectionMetricsLogger"
}
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorActivity.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorActivity.kt
index 04d5566..50e9e751 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorActivity.kt
@@ -210,6 +210,10 @@
reviewGrantedConsentRequired,
/* projection= */ null
)
+ if (isFinishing) {
+ // Only log dismissed when actually finishing, and not when changing configuration.
+ controller.onSelectorDismissed()
+ }
}
activityLauncher.destroy()
controller.destroy()
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorController.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorController.kt
index 67ef119..575e198 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorController.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorController.kt
@@ -18,7 +18,6 @@
import android.content.ComponentName
import android.os.UserHandle
-import com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_APP_SELECTOR_DISPLAYED as STATE_APP_SELECTOR_DISPLAYED
import com.android.systemui.mediaprojection.MediaProjectionMetricsLogger
import com.android.systemui.mediaprojection.appselector.data.RecentTask
import com.android.systemui.mediaprojection.appselector.data.RecentTaskListProvider
@@ -76,6 +75,10 @@
scope.cancel()
}
+ fun onSelectorDismissed() {
+ logger.notifyProjectionRequestCancelled(hostUid)
+ }
+
private suspend fun refreshForegroundTaskThumbnails(tasks: List<RecentTask>) {
coroutineScope {
val thumbnails: List<Deferred<ThumbnailData?>> =
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/BaseScreenSharePermissionDialog.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/BaseScreenSharePermissionDialog.kt
index 8b437c3..eea369f 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/BaseScreenSharePermissionDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/BaseScreenSharePermissionDialog.kt
@@ -32,6 +32,7 @@
import androidx.annotation.DrawableRes
import androidx.annotation.LayoutRes
import androidx.annotation.StringRes
+import com.android.systemui.mediaprojection.MediaProjectionMetricsLogger
import com.android.systemui.res.R
import com.android.systemui.statusbar.phone.SystemUIDialog
@@ -40,16 +41,31 @@
context: Context,
private val screenShareOptions: List<ScreenShareOption>,
private val appName: String?,
+ private val hostUid: Int,
+ private val mediaProjectionMetricsLogger: MediaProjectionMetricsLogger,
@DrawableRes private val dialogIconDrawable: Int? = null,
- @ColorRes private val dialogIconTint: Int? = null
+ @ColorRes private val dialogIconTint: Int? = null,
) : SystemUIDialog(context), AdapterView.OnItemSelectedListener {
private lateinit var dialogTitle: TextView
private lateinit var startButton: TextView
private lateinit var cancelButton: TextView
private lateinit var warning: TextView
private lateinit var screenShareModeSpinner: Spinner
+ private var hasCancelBeenLogged: Boolean = false
var selectedScreenShareOption: ScreenShareOption = screenShareOptions.first()
+ override fun dismiss() {
+ super.dismiss()
+
+ // Dismiss can be called multiple times and we only want to log once.
+ if (hasCancelBeenLogged) {
+ return
+ }
+
+ mediaProjectionMetricsLogger.notifyProjectionRequestCancelled(hostUid)
+ hasCancelBeenLogged = true
+ }
+
public override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
window?.addPrivateFlags(WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS)
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java
index f7cc589..eacfa57 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java
@@ -222,13 +222,19 @@
// the correct screen width when in split screen.
Context dialogContext = getApplicationContext();
if (isPartialScreenSharingEnabled()) {
- mDialog = new MediaProjectionPermissionDialog(dialogContext, getMediaProjectionConfig(),
+ mDialog = new MediaProjectionPermissionDialog(
+ dialogContext,
+ getMediaProjectionConfig(),
() -> {
MediaProjectionPermissionDialog dialog =
(MediaProjectionPermissionDialog) mDialog;
ScreenShareOption selectedOption = dialog.getSelectedScreenShareOption();
grantMediaProjectionPermission(selectedOption.getMode());
- }, () -> finish(RECORD_CANCEL, /* projection= */ null), appName);
+ },
+ () -> finish(RECORD_CANCEL, /* projection= */ null),
+ appName,
+ mUid,
+ mMediaProjectionMetricsLogger);
} else {
AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(dialogContext,
R.style.Theme_SystemUI_Dialog)
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionDialog.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionDialog.kt
index b9bafd4..cff22b0 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionDialog.kt
@@ -18,6 +18,7 @@
import android.content.Context
import android.media.projection.MediaProjectionConfig
import android.os.Bundle
+import com.android.systemui.mediaprojection.MediaProjectionMetricsLogger
import com.android.systemui.res.R
/** Dialog to select screen recording options */
@@ -26,12 +27,16 @@
mediaProjectionConfig: MediaProjectionConfig?,
private val onStartRecordingClicked: Runnable,
private val onCancelClicked: Runnable,
- private val appName: String?
+ private val appName: String?,
+ hostUid: Int,
+ mediaProjectionMetricsLogger: MediaProjectionMetricsLogger,
) :
BaseScreenSharePermissionDialog(
context,
createOptionList(context, appName, mediaProjectionConfig),
- appName
+ appName,
+ hostUid,
+ mediaProjectionMetricsLogger
) {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java
index ea1205a..05f125f 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java
@@ -163,8 +163,7 @@
}
mMediaProjectionMetricsLogger.notifyProjectionInitiated(
- mUserContextProvider.getUserContext().getUserId(),
- SessionCreationSource.SYSTEM_UI_SCREEN_RECORDER);
+ getHostUid(), SessionCreationSource.SYSTEM_UI_SCREEN_RECORDER);
return flags.isEnabled(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING)
? new ScreenRecordPermissionDialog(
@@ -174,7 +173,8 @@
/* controller= */ this,
activityStarter,
mUserContextProvider,
- onStartRecordingClicked)
+ onStartRecordingClicked,
+ mMediaProjectionMetricsLogger)
: new ScreenRecordDialog(
context,
/* controller= */ this,
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialog.java b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialog.java
index f2e94e9..e7481cc 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialog.java
@@ -66,8 +66,10 @@
private Switch mAudioSwitch;
private Spinner mOptions;
- public ScreenRecordDialog(Context context, RecordingController controller,
- UserContextProvider userContextProvider, @Nullable Runnable onStartRecordingClicked) {
+ public ScreenRecordDialog(Context context,
+ RecordingController controller,
+ UserContextProvider userContextProvider,
+ @Nullable Runnable onStartRecordingClicked) {
super(context);
mController = controller;
mUserContextProvider = userContextProvider;
@@ -89,7 +91,6 @@
TextView cancelBtn = findViewById(R.id.button_cancel);
cancelBtn.setOnClickListener(v -> dismiss());
-
TextView startBtn = findViewById(R.id.button_start);
startBtn.setOnClickListener(v -> {
if (mOnStartRecordingClicked != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialog.kt b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialog.kt
index 3b3aa53..f74234b 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialog.kt
@@ -33,6 +33,7 @@
import android.widget.Switch
import androidx.annotation.LayoutRes
import com.android.systemui.mediaprojection.MediaProjectionCaptureTarget
+import com.android.systemui.mediaprojection.MediaProjectionMetricsLogger
import com.android.systemui.mediaprojection.appselector.MediaProjectionAppSelectorActivity
import com.android.systemui.mediaprojection.permission.BaseScreenSharePermissionDialog
import com.android.systemui.mediaprojection.permission.ENTIRE_SCREEN
@@ -50,12 +51,15 @@
private val controller: RecordingController,
private val activityStarter: ActivityStarter,
private val userContextProvider: UserContextProvider,
- private val onStartRecordingClicked: Runnable?
+ private val onStartRecordingClicked: Runnable?,
+ mediaProjectionMetricsLogger: MediaProjectionMetricsLogger,
) :
BaseScreenSharePermissionDialog(
context,
createOptionList(),
appName = null,
+ hostUid = hostUid,
+ mediaProjectionMetricsLogger,
R.drawable.ic_screenrecord,
R.color.screenrecord_icon_color
) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/MediaProjectionMetricsLoggerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/MediaProjectionMetricsLoggerTest.kt
index fd1e2c7..da448aa 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/MediaProjectionMetricsLoggerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/MediaProjectionMetricsLoggerTest.kt
@@ -74,6 +74,15 @@
}
@Test
+ fun notifyProjectionCancelled_forwardsToServiceWithMetricsValue() {
+ val hostUid = 123
+
+ logger.notifyProjectionRequestCancelled(hostUid)
+
+ verify(service).notifyPermissionRequestCancelled(hostUid)
+ }
+
+ @Test
fun notifyAppSelectorDisplayed_forwardsToService() {
val hostUid = 654
diff --git a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorControllerTest.kt
index 5255f71..44798ea 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorControllerTest.kt
@@ -4,7 +4,6 @@
import android.os.UserHandle
import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
-import com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_APP_SELECTOR_DISPLAYED as STATE_APP_SELECTOR_DISPLAYED
import com.android.systemui.SysuiTestCase
import com.android.systemui.mediaprojection.MediaProjectionMetricsLogger
import com.android.systemui.mediaprojection.appselector.data.RecentTask
@@ -214,7 +213,7 @@
@Test
fun init_firstStart_logsAppSelectorDisplayed() {
val hostUid = 123456789
- val controller = createController(isFirstStart = true, hostUid)
+ val controller = createController(isFirstStart = true, hostUid)
controller.init()
@@ -231,6 +230,15 @@
verify(logger, never()).notifyAppSelectorDisplayed(hostUid)
}
+ @Test
+ fun onSelectorDismissed_logsProjectionRequestCancelled() {
+ val hostUid = 123
+
+ createController(hostUid = hostUid).onSelectorDismissed()
+
+ verify(logger).notifyProjectionRequestCancelled(hostUid)
+ }
+
private fun givenCaptureAllowed(isAllow: Boolean) {
whenever(policyResolver.isScreenCaptureAllowed(any(), any())).thenReturn(isAllow)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingControllerTest.java
index c439cfe..49049bc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingControllerTest.java
@@ -16,6 +16,8 @@
package com.android.systemui.screenrecord;
+import static android.os.Process.myUid;
+
import static com.google.common.truth.Truth.assertThat;
import static junit.framework.Assert.assertFalse;
@@ -31,8 +33,8 @@
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
-import android.os.Looper;
import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
import androidx.test.filters.SmallTest;
@@ -60,6 +62,7 @@
@SmallTest
@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
/**
* Tests for exception handling and bitmap configuration in adding smart actions to Screenshot
* Notification.
@@ -117,10 +120,6 @@
// starting, and notifies listeners.
@Test
public void testCancelCountdown() {
- if (Looper.myLooper() == null) {
- Looper.prepare();
- }
-
mController.startCountdown(100, 10, null, null);
assertTrue(mController.isStarting());
@@ -137,10 +136,6 @@
// Test that when recording is started, the start intent is sent and listeners are notified.
@Test
public void testStartRecording() throws PendingIntent.CanceledException {
- if (Looper.myLooper() == null) {
- Looper.prepare();
- }
-
PendingIntent startIntent = Mockito.mock(PendingIntent.class);
mController.startCountdown(0, 0, startIntent, null);
@@ -151,10 +146,6 @@
// Test that when recording is stopped, the stop intent is sent and listeners are notified.
@Test
public void testStopRecording() throws PendingIntent.CanceledException {
- if (Looper.myLooper() == null) {
- Looper.prepare();
- }
-
PendingIntent startIntent = Mockito.mock(PendingIntent.class);
PendingIntent stopIntent = Mockito.mock(PendingIntent.class);
@@ -182,10 +173,6 @@
// Test that broadcast will update state
@Test
public void testUpdateStateBroadcast() {
- if (Looper.myLooper() == null) {
- Looper.prepare();
- }
-
// When a recording has started
PendingIntent startIntent = Mockito.mock(PendingIntent.class);
mController.startCountdown(0, 0, startIntent, null);
@@ -211,10 +198,6 @@
// Test that switching users will stop an ongoing recording
@Test
public void testUserChange() {
- if (Looper.myLooper() == null) {
- Looper.prepare();
- }
-
// If we are recording
PendingIntent startIntent = Mockito.mock(PendingIntent.class);
PendingIntent stopIntent = Mockito.mock(PendingIntent.class);
@@ -231,10 +214,6 @@
@Test
public void testPoliciesFlagDisabled_screenCapturingNotAllowed_returnsNullDevicePolicyDialog() {
- 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, false);
when(mDevicePolicyResolver.isScreenCaptureCompletelyDisabled((any()))).thenReturn(true);
@@ -247,10 +226,6 @@
@Test
public void testPartialScreenSharingDisabled_returnsLegacyDialog() {
- if (Looper.myLooper() == null) {
- Looper.prepare();
- }
-
mFeatureFlags.set(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING, false);
mFeatureFlags.set(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING_ENTERPRISE_POLICIES, false);
@@ -262,10 +237,6 @@
@Test
public void testPoliciesFlagEnabled_screenCapturingNotAllowed_returnsDevicePolicyDialog() {
- 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(true);
@@ -278,10 +249,6 @@
@Test
public void testPoliciesFlagEnabled_screenCapturingAllowed_returnsNullDevicePolicyDialog() {
- 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);
@@ -294,9 +261,6 @@
@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);
@@ -306,7 +270,7 @@
verify(mMediaProjectionMetricsLogger)
.notifyProjectionInitiated(
- TEST_USER_ID,
+ /* hostUid= */ myUid(),
SessionCreationSource.SYSTEM_UI_SCREEN_RECORDER);
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogTest.kt
index bf12d7d..fd38139 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogTest.kt
@@ -26,6 +26,7 @@
import com.android.systemui.SysuiTestCase
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
+import com.android.systemui.mediaprojection.MediaProjectionMetricsLogger
import com.android.systemui.mediaprojection.appselector.MediaProjectionAppSelectorActivity
import com.android.systemui.mediaprojection.permission.ENTIRE_SCREEN
import com.android.systemui.mediaprojection.permission.SINGLE_APP
@@ -41,7 +42,6 @@
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
-import org.mockito.Mockito
import org.mockito.Mockito.eq
import org.mockito.Mockito.verify
import org.mockito.Mockito.`when` as whenever
@@ -57,6 +57,7 @@
@Mock private lateinit var userContextProvider: UserContextProvider
@Mock private lateinit var flags: FeatureFlags
@Mock private lateinit var onStartRecordingClicked: Runnable
+ @Mock private lateinit var mediaProjectionMetricsLogger: MediaProjectionMetricsLogger
private lateinit var dialog: ScreenRecordPermissionDialog
@@ -72,7 +73,8 @@
controller,
starter,
userContextProvider,
- onStartRecordingClicked
+ onStartRecordingClicked,
+ mediaProjectionMetricsLogger,
)
dialog.onCreate(null)
whenever(flags.isEnabled(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING)).thenReturn(true)
@@ -149,6 +151,28 @@
assertThat(dialog.isShowing).isFalse()
}
+ @Test
+ fun showDialog_cancelClickedMultipleTimes_projectionRequestCancelledIsLoggedOnce() {
+ dialog.show()
+
+ clickOnCancel()
+ clickOnCancel()
+
+ verify(mediaProjectionMetricsLogger).notifyProjectionRequestCancelled(TEST_HOST_UID)
+ }
+
+ @Test
+ fun dismissDialog_dismissCalledMultipleTimes_projectionRequestCancelledIsLoggedOnce() {
+ dialog.show()
+
+ TestableLooper.get(this).runWithLooper {
+ dialog.dismiss()
+ dialog.dismiss()
+ }
+
+ verify(mediaProjectionMetricsLogger).notifyProjectionRequestCancelled(TEST_HOST_UID)
+ }
+
private fun clickOnCancel() {
dialog.requireViewById<View>(android.R.id.button2).performClick()
}
diff --git a/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java b/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java
index 58927d1..893ed61 100644
--- a/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java
+++ b/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java
@@ -470,6 +470,11 @@
}
@VisibleForTesting
+ void notifyPermissionRequestCancelled(int hostUid) {
+ mMediaProjectionMetricsLogger.logProjectionPermissionRequestCancelled(hostUid);
+ }
+
+ @VisibleForTesting
void notifyAppSelectorDisplayed(int hostUid) {
mMediaProjectionMetricsLogger.logAppSelectorDisplayed(hostUid);
}
@@ -852,19 +857,6 @@
@Override // Binder call
@EnforcePermission(MANAGE_MEDIA_PROJECTION)
- public void notifyPermissionRequestStateChange(int hostUid, int state,
- int sessionCreationSource) {
- notifyPermissionRequestStateChange_enforcePermission();
- final long token = Binder.clearCallingIdentity();
- try {
- mMediaProjectionMetricsLogger.notifyProjectionStateChange(hostUid, state, sessionCreationSource);
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- }
-
- @Override // Binder call
- @EnforcePermission(MANAGE_MEDIA_PROJECTION)
public void notifyPermissionRequestInitiated(int hostUid, int sessionCreationSource) {
notifyPermissionRequestInitiated_enforcePermission();
final long token = Binder.clearCallingIdentity();
@@ -890,6 +882,18 @@
@Override // Binder call
@EnforcePermission(MANAGE_MEDIA_PROJECTION)
+ public void notifyPermissionRequestCancelled(int hostUid) {
+ notifyPermissionRequestCancelled_enforcePermission();
+ final long token = Binder.clearCallingIdentity();
+ try {
+ MediaProjectionManagerService.this.notifyPermissionRequestCancelled(hostUid);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override // Binder call
+ @EnforcePermission(MANAGE_MEDIA_PROJECTION)
public void notifyAppSelectorDisplayed(int hostUid) {
notifyAppSelectorDisplayed_enforcePermission();
final long token = Binder.clearCallingIdentity();
diff --git a/services/core/java/com/android/server/media/projection/MediaProjectionMetricsLogger.java b/services/core/java/com/android/server/media/projection/MediaProjectionMetricsLogger.java
index 55a30bf..d7fefeb 100644
--- a/services/core/java/com/android/server/media/projection/MediaProjectionMetricsLogger.java
+++ b/services/core/java/com/android/server/media/projection/MediaProjectionMetricsLogger.java
@@ -118,6 +118,23 @@
}
/**
+ * Logs that requesting permission for media projection was cancelled by the user.
+ *
+ * @param hostUid UID of the package that initiates MediaProjection.
+ */
+ public void logProjectionPermissionRequestCancelled(int hostUid) {
+ write(
+ mSessionIdGenerator.getCurrentSessionId(),
+ FrameworkStatsLog
+ .MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_CANCELLED,
+ hostUid,
+ TARGET_UID_UNKNOWN,
+ TIME_SINCE_LAST_ACTIVE_UNKNOWN,
+ FrameworkStatsLog
+ .MEDIA_PROJECTION_STATE_CHANGED__CREATION_SOURCE__CREATION_SOURCE_UNKNOWN);
+ }
+
+ /**
* Logs that the app selector dialog is shown for the user.
*
* @param hostUid UID of the package that initiates MediaProjection.
@@ -174,23 +191,6 @@
}
}
- public void notifyProjectionStateChange(int hostUid, int state, int sessionCreationSource) {
- write(hostUid, state, sessionCreationSource);
- }
-
- private void write(int hostUid, int state, int sessionCreationSource) {
- mFrameworkStatsLogWrapper.write(
- /* code */ FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED,
- /* session_id */ 123,
- /* state */ state,
- /* previous_state */ FrameworkStatsLog
- .MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_UNKNOWN,
- /* host_uid */ hostUid,
- /* target_uid */ -1,
- /* time_since_last_active */ 0,
- /* creation_source */ sessionCreationSource);
- }
-
private void write(
int sessionId,
int state,
diff --git a/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java
index 4e6dd06..ece3dfe 100644
--- a/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java
@@ -674,6 +674,17 @@
}
@Test
+ public void notifyPermissionRequestCancelled_forwardsToLogger() {
+ int hostUid = 123;
+ mService =
+ new MediaProjectionManagerService(mContext, mMediaProjectionMetricsLoggerInjector);
+
+ mService.notifyPermissionRequestCancelled(hostUid);
+
+ verify(mMediaProjectionMetricsLogger).logProjectionPermissionRequestCancelled(hostUid);
+ }
+
+ @Test
public void notifyAppSelectorDisplayed_forwardsToLogger() {
int hostUid = 456;
mService =
diff --git a/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionMetricsLoggerTest.java b/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionMetricsLoggerTest.java
index 410604f..ad1cd6e 100644
--- a/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionMetricsLoggerTest.java
+++ b/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionMetricsLoggerTest.java
@@ -18,6 +18,7 @@
import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__CREATION_SOURCE__CREATION_SOURCE_UNKNOWN;
import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_APP_SELECTOR_DISPLAYED;
+import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_CANCELLED;
import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_CAPTURING_IN_PROGRESS;
import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_INITIATED;
import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_PERMISSION_REQUEST_DISPLAYED;
@@ -452,6 +453,63 @@
MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_STOPPED);
}
+ @Test
+ public void logProjectionPermissionRequestCancelled_logsStateChangedAtomId() {
+ mLogger.logProjectionPermissionRequestCancelled(TEST_HOST_UID);
+
+ verifyStateChangedAtomIdLogged();
+ }
+
+ @Test
+ public void logProjectionPermissionRequestCancelled_logsExistingSessionId() {
+ int existingSessionId = 456;
+ when(mSessionIdGenerator.getCurrentSessionId()).thenReturn(existingSessionId);
+
+ mLogger.logProjectionPermissionRequestCancelled(TEST_HOST_UID);
+
+ verifySessionIdLogged(existingSessionId);
+ }
+
+ @Test
+ public void logProjectionPermissionRequestCancelled_logsStateCancelled() {
+ mLogger.logProjectionPermissionRequestCancelled(TEST_HOST_UID);
+
+ verifyStateLogged(MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_CANCELLED);
+ }
+
+ @Test
+ public void logProjectionPermissionRequestCancelled_logsPreviousState() {
+ mLogger.logInitiated(TEST_HOST_UID, TEST_CREATION_SOURCE);
+ verifyPreviousStateLogged(
+ MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_UNKNOWN);
+
+ mLogger.logProjectionPermissionRequestCancelled(TEST_HOST_UID);
+ verifyPreviousStateLogged(
+ MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_INITIATED);
+ }
+
+ @Test
+ public void logProjectionPermissionRequestCancelled_logsHostUid() {
+ mLogger.logProjectionPermissionRequestCancelled(TEST_HOST_UID);
+
+ verifyHostUidLogged(TEST_HOST_UID);
+ }
+
+ @Test
+ public void logProjectionPermissionRequestCancelled_logsUnknownTargetUid() {
+ mLogger.logProjectionPermissionRequestCancelled(TEST_HOST_UID);
+
+ verifyTargetUidLogged(-2);
+ }
+
+ @Test
+ public void logProjectionPermissionRequestCancelled_logsUnknownCreationSource() {
+ mLogger.logProjectionPermissionRequestCancelled(TEST_HOST_UID);
+
+ verifyCreationSourceLogged(
+ MEDIA_PROJECTION_STATE_CHANGED__CREATION_SOURCE__CREATION_SOURCE_UNKNOWN);
+ }
+
private void verifyStateChangedAtomIdLogged() {
verify(mFrameworkStatsLogWrapper)
.write(