Merge "(2/N)[MediaProjection] Show dialog when token is reused" into udc-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
1d535bc391
@@ -1495,6 +1495,12 @@
|
||||
"group": "WM_DEBUG_CONFIGURATION",
|
||||
"at": "com\/android\/server\/wm\/ActivityRecord.java"
|
||||
},
|
||||
"-741766551": {
|
||||
"message": "Content Recording: Ignoring session on invalid virtual display",
|
||||
"level": "VERBOSE",
|
||||
"group": "WM_DEBUG_CONTENT_RECORDING",
|
||||
"at": "com\/android\/server\/wm\/ContentRecordingController.java"
|
||||
},
|
||||
"-732715767": {
|
||||
"message": "Unable to retrieve window container to start recording for display %d",
|
||||
"level": "VERBOSE",
|
||||
|
||||
@@ -20,11 +20,23 @@ import android.media.projection.IMediaProjection;
|
||||
import android.media.projection.IMediaProjectionCallback;
|
||||
import android.media.projection.IMediaProjectionWatcherCallback;
|
||||
import android.media.projection.MediaProjectionInfo;
|
||||
import android.media.projection.ReviewGrantedConsentResult;
|
||||
import android.os.IBinder;
|
||||
import android.view.ContentRecordingSession;
|
||||
|
||||
/** {@hide} */
|
||||
interface IMediaProjectionManager {
|
||||
/**
|
||||
* Intent extra indicating if user must review access to the consent token already granted.
|
||||
*/
|
||||
const String EXTRA_USER_REVIEW_GRANTED_CONSENT = "extra_media_projection_user_consent_required";
|
||||
|
||||
/**
|
||||
* Intent extra indicating the package attempting to re-use granted consent.
|
||||
*/
|
||||
const String EXTRA_PACKAGE_REUSING_GRANTED_CONSENT =
|
||||
"extra_media_projection_package_reusing_consent";
|
||||
|
||||
@UnsupportedAppUsage
|
||||
boolean hasProjectionPermission(int uid, String packageName);
|
||||
|
||||
@@ -36,6 +48,21 @@ interface IMediaProjectionManager {
|
||||
IMediaProjection createProjection(int uid, String packageName, int type,
|
||||
boolean permanentGrant);
|
||||
|
||||
/**
|
||||
* Returns the current {@link IMediaProjection} instance associated with the given
|
||||
* package, or {@code null} if it is not possible to re-use the current projection.
|
||||
*
|
||||
* <p>Should only be invoked when the user has reviewed consent for a re-used projection token.
|
||||
* Requires that there is a prior session waiting for the user to review consent, and the given
|
||||
* package details match those on the current projection.
|
||||
*
|
||||
* @see {@link #isCurrentProjection}
|
||||
*/
|
||||
@EnforcePermission("android.Manifest.permission.MANAGE_MEDIA_PROJECTION")
|
||||
@JavaPassthrough(annotation = "@android.annotation.RequiresPermission(android.Manifest"
|
||||
+ ".permission.MANAGE_MEDIA_PROJECTION)")
|
||||
IMediaProjection getProjection(int uid, String packageName);
|
||||
|
||||
/**
|
||||
* Returns {@code true} if the given {@link IMediaProjection} corresponds to the current
|
||||
* projection, or {@code false} otherwise.
|
||||
@@ -58,7 +85,7 @@ interface IMediaProjectionManager {
|
||||
*/
|
||||
@JavaPassthrough(annotation = "@android.annotation.RequiresPermission(android.Manifest"
|
||||
+ ".permission.MANAGE_MEDIA_PROJECTION)")
|
||||
void requestConsentForInvalidProjection(IMediaProjection projection);
|
||||
void requestConsentForInvalidProjection(in IMediaProjection projection);
|
||||
|
||||
@JavaPassthrough(annotation = "@android.annotation.RequiresPermission(android.Manifest"
|
||||
+ ".permission.MANAGE_MEDIA_PROJECTION)")
|
||||
@@ -94,9 +121,32 @@ interface IMediaProjectionManager {
|
||||
*
|
||||
* @param incomingSession the nullable incoming content recording session
|
||||
* @param projection the non-null projection the session describes
|
||||
* @throws SecurityException If the provided projection is not current.
|
||||
*/
|
||||
@JavaPassthrough(annotation = "@android.annotation.RequiresPermission(android.Manifest"
|
||||
+ ".permission.MANAGE_MEDIA_PROJECTION)")
|
||||
boolean setContentRecordingSession(in ContentRecordingSession incomingSession,
|
||||
in IMediaProjection projection);
|
||||
|
||||
/**
|
||||
* Sets the result of the user reviewing the recording permission, when the host app is re-using
|
||||
* the consent token.
|
||||
*
|
||||
* <p>Ignores the provided result if the given projection is not the current projection.
|
||||
*
|
||||
* <p>Based on the given result:
|
||||
* <ul>
|
||||
* <li>If UNKNOWN or RECORD_CANCEL, then tear down the recording.</li>
|
||||
* <li>If RECORD_CONTENT_DISPLAY, then record the default display.</li>
|
||||
* <li>If RECORD_CONTENT_TASK, record the task indicated by
|
||||
* {@link IMediaProjection#getLaunchCookie}.</li>
|
||||
* </ul>
|
||||
* @param projection The projection associated with the consent result. Must be the current
|
||||
* projection instance, unless the given result is RECORD_CANCEL.
|
||||
*/
|
||||
@EnforcePermission("android.Manifest.permission.MANAGE_MEDIA_PROJECTION")
|
||||
@JavaPassthrough(annotation = "@android.annotation.RequiresPermission(android.Manifest"
|
||||
+ ".permission.MANAGE_MEDIA_PROJECTION)")
|
||||
void setUserReviewGrantedConsentResult(ReviewGrantedConsentResult consentResult,
|
||||
in @nullable IMediaProjection projection);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright (C) 2023 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 android.media.projection;
|
||||
|
||||
/**
|
||||
* Indicates result of user interacting with consent dialog, when their review is required due to
|
||||
* app re-using the token.
|
||||
|
||||
* @hide
|
||||
*/
|
||||
@Backing(type="int")
|
||||
enum ReviewGrantedConsentResult {
|
||||
UNKNOWN = -1,
|
||||
RECORD_CANCEL = 0,
|
||||
RECORD_CONTENT_DISPLAY = 1,
|
||||
RECORD_CONTENT_TASK = 2,
|
||||
}
|
||||
@@ -19,9 +19,19 @@ package com.android.server.media.projection;
|
||||
import static android.Manifest.permission.MANAGE_MEDIA_PROJECTION;
|
||||
import static android.app.ActivityManagerInternal.MEDIA_PROJECTION_TOKEN_EVENT_CREATED;
|
||||
import static android.app.ActivityManagerInternal.MEDIA_PROJECTION_TOKEN_EVENT_DESTROYED;
|
||||
import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
|
||||
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
|
||||
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.ReviewGrantedConsentResult.RECORD_CANCEL;
|
||||
import static android.media.projection.ReviewGrantedConsentResult.RECORD_CONTENT_DISPLAY;
|
||||
import static android.media.projection.ReviewGrantedConsentResult.RECORD_CONTENT_TASK;
|
||||
import static android.media.projection.ReviewGrantedConsentResult.UNKNOWN;
|
||||
import static android.view.Display.DEFAULT_DISPLAY;
|
||||
import static android.view.Display.INVALID_DISPLAY;
|
||||
|
||||
import android.Manifest;
|
||||
import android.annotation.EnforcePermission;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.app.ActivityManagerInternal;
|
||||
@@ -30,7 +40,9 @@ import android.app.IProcessObserver;
|
||||
import android.app.compat.CompatChanges;
|
||||
import android.compat.annotation.ChangeId;
|
||||
import android.compat.annotation.EnabledSince;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
@@ -45,11 +57,13 @@ import android.media.projection.IMediaProjectionManager;
|
||||
import android.media.projection.IMediaProjectionWatcherCallback;
|
||||
import android.media.projection.MediaProjectionInfo;
|
||||
import android.media.projection.MediaProjectionManager;
|
||||
import android.media.projection.ReviewGrantedConsentResult;
|
||||
import android.os.Binder;
|
||||
import android.os.Build;
|
||||
import android.os.Handler;
|
||||
import android.os.IBinder;
|
||||
import android.os.Looper;
|
||||
import android.os.PermissionEnforcer;
|
||||
import android.os.RemoteException;
|
||||
import android.os.SystemClock;
|
||||
import android.os.UserHandle;
|
||||
@@ -57,6 +71,7 @@ import android.util.ArrayMap;
|
||||
import android.util.Slog;
|
||||
import android.view.ContentRecordingSession;
|
||||
|
||||
import com.android.internal.R;
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.internal.util.ArrayUtils;
|
||||
import com.android.internal.util.DumpUtils;
|
||||
@@ -69,6 +84,7 @@ import java.io.FileDescriptor;
|
||||
import java.io.PrintWriter;
|
||||
import java.time.Duration;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Manages MediaProjection sessions.
|
||||
@@ -161,10 +177,9 @@ public final class MediaProjectionManagerService extends SystemService
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onStart() {
|
||||
publishBinderService(Context.MEDIA_PROJECTION_SERVICE, new BinderService(),
|
||||
publishBinderService(Context.MEDIA_PROJECTION_SERVICE, new BinderService(mContext),
|
||||
false /*allowIsolated*/);
|
||||
mMediaRouter.addCallback(MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY, mMediaRouterCallback,
|
||||
MediaRouter.CALLBACK_FLAG_PASSIVE_DISCOVERY);
|
||||
@@ -305,6 +320,10 @@ public final class MediaProjectionManagerService extends SystemService
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (mProjectionGrant != null) {
|
||||
// Cache the session details.
|
||||
mProjectionGrant.mSession = incomingSession;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -323,9 +342,8 @@ public final class MediaProjectionManagerService extends SystemService
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Reshows the permisison dialog for the user to review consent they've already granted in
|
||||
* Re-shows the permission dialog for the user to review consent they've already granted in
|
||||
* the given projection instance.
|
||||
*
|
||||
* <p>Preconditions:
|
||||
@@ -337,18 +355,111 @@ public final class MediaProjectionManagerService extends SystemService
|
||||
* <p>Returns immediately but waits to start recording until user has reviewed their consent.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
void requestConsentForInvalidProjection(IMediaProjection projection) {
|
||||
void requestConsentForInvalidProjection() {
|
||||
synchronized (mLock) {
|
||||
Slog.v(TAG, "Reusing token: Reshow dialog for due to invalid projection.");
|
||||
// TODO(b/274790702): Trigger the permission dialog again in SysUI.
|
||||
// Trigger the permission dialog again in SysUI
|
||||
// Do not handle the result; SysUI will update us when the user has consented.
|
||||
mContext.startActivityAsUser(buildReviewGrantedConsentIntent(),
|
||||
UserHandle.getUserHandleForUid(mProjectionGrant.uid));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an intent to re-show the consent dialog in SysUI. Should only be used for the
|
||||
* scenario where the host app has re-used the consent token.
|
||||
*
|
||||
* <p>Consent dialog result handled in
|
||||
* {@link BinderService#setUserReviewGrantedConsentResult(int)}.
|
||||
*/
|
||||
private Intent buildReviewGrantedConsentIntent() {
|
||||
final String permissionDialogString = mContext.getResources().getString(
|
||||
R.string.config_mediaProjectionPermissionDialogComponent);
|
||||
final ComponentName mediaProjectionPermissionDialogComponent =
|
||||
ComponentName.unflattenFromString(permissionDialogString);
|
||||
// We can use mProjectionGrant since we already checked that it matches the given token.
|
||||
return new Intent().setComponent(mediaProjectionPermissionDialogComponent)
|
||||
.putExtra(EXTRA_USER_REVIEW_GRANTED_CONSENT, true)
|
||||
.putExtra(EXTRA_PACKAGE_REUSING_GRANTED_CONSENT, mProjectionGrant.packageName)
|
||||
.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles result of dialog shown from {@link BinderService#buildReviewGrantedConsentIntent()}.
|
||||
*
|
||||
* <p>Tears down session if user did not consent, or starts mirroring if user did consent.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
void setUserReviewGrantedConsentResult(@ReviewGrantedConsentResult int consentResult,
|
||||
@Nullable IMediaProjection projection) {
|
||||
synchronized (mLock) {
|
||||
final boolean consentGranted =
|
||||
consentResult == RECORD_CONTENT_DISPLAY || consentResult == RECORD_CONTENT_TASK;
|
||||
if (consentGranted && projection == null || !isCurrentProjection(
|
||||
projection.asBinder())) {
|
||||
Slog.v(TAG, "Reusing token: Ignore consent result of " + consentResult + " for a "
|
||||
+ "token that isn't current");
|
||||
return;
|
||||
}
|
||||
if (mProjectionGrant == null) {
|
||||
Slog.w(TAG, "Reusing token: Can't review consent with no ongoing projection.");
|
||||
return;
|
||||
}
|
||||
if (mProjectionGrant.mSession == null
|
||||
|| !mProjectionGrant.mSession.isWaitingToRecord()) {
|
||||
Slog.w(TAG, "Reusing token: Ignore consent result " + consentResult
|
||||
+ " if not waiting for the result.");
|
||||
return;
|
||||
}
|
||||
Slog.v(TAG, "Reusing token: Handling user consent result " + consentResult);
|
||||
switch (consentResult) {
|
||||
case UNKNOWN:
|
||||
case RECORD_CANCEL:
|
||||
// Pass in null to stop mirroring.
|
||||
setReviewedConsentSessionLocked(/* session= */ null);
|
||||
// The grant may now be null if setting the session failed.
|
||||
if (mProjectionGrant != null) {
|
||||
// Always stop the projection.
|
||||
mProjectionGrant.stop();
|
||||
}
|
||||
break;
|
||||
case RECORD_CONTENT_DISPLAY:
|
||||
// TODO(270118861) The app may have specified a particular id in the virtual
|
||||
// display config. However - below will always return INVALID since it checks
|
||||
// that window manager mirroring is not enabled (it is always enabled for MP).
|
||||
setReviewedConsentSessionLocked(ContentRecordingSession.createDisplaySession(
|
||||
DEFAULT_DISPLAY));
|
||||
break;
|
||||
case RECORD_CONTENT_TASK:
|
||||
setReviewedConsentSessionLocked(ContentRecordingSession.createTaskSession(
|
||||
mProjectionGrant.getLaunchCookie()));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the session after the user has reviewed consent. There must be a current session.
|
||||
*
|
||||
* @param session The new session details, or {@code null} to stop recording.
|
||||
*/
|
||||
private void setReviewedConsentSessionLocked(@Nullable ContentRecordingSession session) {
|
||||
if (session != null) {
|
||||
session.setWaitingToRecord(false);
|
||||
session.setVirtualDisplayId(mProjectionGrant.mVirtualDisplayId);
|
||||
}
|
||||
|
||||
Slog.v(TAG, "Reusing token: Processed consent so set the session " + session);
|
||||
if (!setContentRecordingSession(session)) {
|
||||
Slog.e(TAG, "Reusing token: Failed to set session for reused consent, so stop");
|
||||
// Do not need to invoke stop; updating the session does it for us.
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(b/261563516): Remove internal method and test aidl directly, here and elsewhere.
|
||||
@VisibleForTesting
|
||||
MediaProjection createProjectionInternal(int uid, String packageName, int type,
|
||||
boolean isPermanentGrant, UserHandle callingUser,
|
||||
boolean packageAttemptedReusingGrantedConsent) {
|
||||
boolean isPermanentGrant, UserHandle callingUser) {
|
||||
MediaProjection projection;
|
||||
ApplicationInfo ai;
|
||||
try {
|
||||
@@ -371,6 +482,34 @@ public final class MediaProjectionManagerService extends SystemService
|
||||
return projection;
|
||||
}
|
||||
|
||||
// TODO(b/261563516): Remove internal method and test aidl directly, here and elsewhere.
|
||||
@VisibleForTesting
|
||||
MediaProjection getProjectionInternal(int uid, String packageName) {
|
||||
final long callingToken = Binder.clearCallingIdentity();
|
||||
try {
|
||||
// Supposedly the package has re-used the user's consent; confirm the provided details
|
||||
// against the current projection token before re-using the current projection.
|
||||
if (mProjectionGrant == null || mProjectionGrant.mSession == null
|
||||
|| !mProjectionGrant.mSession.isWaitingToRecord()) {
|
||||
Slog.e(TAG, "Reusing token: Not possible to reuse the current projection "
|
||||
+ "instance");
|
||||
return null;
|
||||
}
|
||||
// The package matches, go ahead and re-use the token for this request.
|
||||
if (mProjectionGrant.uid == uid
|
||||
&& Objects.equals(mProjectionGrant.packageName, packageName)) {
|
||||
Slog.v(TAG, "Reusing token: getProjection can reuse the current projection");
|
||||
return mProjectionGrant;
|
||||
} else {
|
||||
Slog.e(TAG, "Reusing token: Not possible to reuse the current projection "
|
||||
+ "instance due to package details mismatching");
|
||||
return null;
|
||||
}
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(callingToken);
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
MediaProjectionInfo getActiveProjectionInfo() {
|
||||
synchronized (mLock) {
|
||||
@@ -395,6 +534,10 @@ public final class MediaProjectionManagerService extends SystemService
|
||||
|
||||
private final class BinderService extends IMediaProjectionManager.Stub {
|
||||
|
||||
BinderService(Context context) {
|
||||
super(PermissionEnforcer.fromContext(context));
|
||||
}
|
||||
|
||||
@Override // Binder call
|
||||
public boolean hasProjectionPermission(int uid, String packageName) {
|
||||
final long token = Binder.clearCallingIdentity();
|
||||
@@ -424,7 +567,25 @@ public final class MediaProjectionManagerService extends SystemService
|
||||
}
|
||||
final UserHandle callingUser = Binder.getCallingUserHandle();
|
||||
return createProjectionInternal(uid, packageName, type, isPermanentGrant,
|
||||
callingUser, false);
|
||||
callingUser);
|
||||
}
|
||||
|
||||
@Override // Binder call
|
||||
@EnforcePermission(MANAGE_MEDIA_PROJECTION)
|
||||
public IMediaProjection getProjection(int uid, String packageName) {
|
||||
getProjection_enforcePermission();
|
||||
if (packageName == null || packageName.isEmpty()) {
|
||||
throw new IllegalArgumentException("package name must not be empty");
|
||||
}
|
||||
|
||||
MediaProjection projection;
|
||||
final long callingToken = Binder.clearCallingIdentity();
|
||||
try {
|
||||
projection = getProjectionInternal(uid, packageName);
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(callingToken);
|
||||
}
|
||||
return projection;
|
||||
}
|
||||
|
||||
@Override // Binder call
|
||||
@@ -562,7 +723,7 @@ public final class MediaProjectionManagerService extends SystemService
|
||||
}
|
||||
|
||||
@Override
|
||||
public void requestConsentForInvalidProjection(IMediaProjection projection) {
|
||||
public void requestConsentForInvalidProjection(@NonNull IMediaProjection projection) {
|
||||
if (mContext.checkCallingOrSelfPermission(Manifest.permission.MANAGE_MEDIA_PROJECTION)
|
||||
!= PackageManager.PERMISSION_GRANTED) {
|
||||
throw new SecurityException("Requires MANAGE_MEDIA_PROJECTION to check if the given"
|
||||
@@ -577,7 +738,22 @@ public final class MediaProjectionManagerService extends SystemService
|
||||
// Remove calling app identity before performing any privileged operations.
|
||||
final long token = Binder.clearCallingIdentity();
|
||||
try {
|
||||
MediaProjectionManagerService.this.requestConsentForInvalidProjection(projection);
|
||||
MediaProjectionManagerService.this.requestConsentForInvalidProjection();
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(token);
|
||||
}
|
||||
}
|
||||
|
||||
@Override // Binder call
|
||||
@EnforcePermission(MANAGE_MEDIA_PROJECTION)
|
||||
public void setUserReviewGrantedConsentResult(@ReviewGrantedConsentResult int consentResult,
|
||||
@Nullable IMediaProjection projection) {
|
||||
setUserReviewGrantedConsentResult_enforcePermission();
|
||||
// Remove calling app identity before performing any privileged operations.
|
||||
final long token = Binder.clearCallingIdentity();
|
||||
try {
|
||||
MediaProjectionManagerService.this.setUserReviewGrantedConsentResult(consentResult,
|
||||
projection);
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(token);
|
||||
}
|
||||
@@ -594,7 +770,6 @@ public final class MediaProjectionManagerService extends SystemService
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private boolean checkPermission(String packageName, String permission) {
|
||||
return mContext.getPackageManager().checkPermission(permission, packageName)
|
||||
== PackageManager.PERMISSION_GRANTED;
|
||||
@@ -630,6 +805,8 @@ public final class MediaProjectionManagerService extends SystemService
|
||||
// Set if MediaProjection#createVirtualDisplay has been invoked previously (it
|
||||
// should only be called once).
|
||||
private int mVirtualDisplayId = INVALID_DISPLAY;
|
||||
// The associated session details already sent to WindowManager.
|
||||
private ContentRecordingSession mSession;
|
||||
|
||||
MediaProjection(int type, int uid, String packageName, int targetSdkVersion,
|
||||
boolean isPrivileged) {
|
||||
@@ -883,6 +1060,18 @@ public final class MediaProjectionManagerService extends SystemService
|
||||
}
|
||||
synchronized (mLock) {
|
||||
mVirtualDisplayId = displayId;
|
||||
|
||||
// If prior session was does not have a valid display id, then update the display
|
||||
// so recording can start.
|
||||
if (mSession != null && mSession.getVirtualDisplayId() == INVALID_DISPLAY) {
|
||||
Slog.v(TAG, "Virtual display now created, so update session with the virtual "
|
||||
+ "display id");
|
||||
mSession.setVirtualDisplayId(mVirtualDisplayId);
|
||||
if (!setContentRecordingSession(mSession)) {
|
||||
Slog.e(TAG, "Failed to set session for virtual display id");
|
||||
// Do not need to invoke stop; updating the session does it for us.
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -80,7 +80,7 @@ final class ContentRecordingController {
|
||||
}
|
||||
// Invalid scenario: ignore identical incoming session.
|
||||
if (ContentRecordingSession.isProjectionOnSameDisplay(mSession, incomingSession)) {
|
||||
// TODO(242833866) if incoming session is no longer waiting to record, allow
|
||||
// TODO(242833866): if incoming session is no longer waiting to record, allow
|
||||
// the update through.
|
||||
|
||||
ProtoLog.v(WM_DEBUG_CONTENT_RECORDING,
|
||||
@@ -99,7 +99,7 @@ final class ContentRecordingController {
|
||||
incomingDisplayContent = wmService.mRoot.getDisplayContentOrCreate(
|
||||
incomingSession.getVirtualDisplayId());
|
||||
incomingDisplayContent.setContentRecordingSession(incomingSession);
|
||||
// TODO(b/270118861) When user grants consent to re-use, explicitly ask ContentRecorder
|
||||
// TODO(b/270118861): When user grants consent to re-use, explicitly ask ContentRecorder
|
||||
// to update, since no config/display change arrives. Mark recording as black.
|
||||
}
|
||||
// Takeover and stopping scenario: stop recording on the pre-existing session.
|
||||
|
||||
@@ -19,15 +19,25 @@ package com.android.server.media.projection;
|
||||
|
||||
import static android.content.pm.ApplicationInfo.PRIVATE_FLAG_PRIVILEGED;
|
||||
import static android.media.projection.MediaProjectionManager.TYPE_MIRRORING;
|
||||
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_TASK;
|
||||
import static android.media.projection.ReviewGrantedConsentResult.UNKNOWN;
|
||||
import static android.view.Display.DEFAULT_DISPLAY;
|
||||
import static android.view.Display.INVALID_DISPLAY;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.atLeastOnce;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.testng.Assert.assertThrows;
|
||||
|
||||
import android.app.ActivityManagerInternal;
|
||||
@@ -37,7 +47,9 @@ import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.PackageManager.ApplicationInfoFlags;
|
||||
import android.content.pm.PackageManager.NameNotFoundException;
|
||||
import android.media.projection.IMediaProjection;
|
||||
import android.media.projection.IMediaProjectionCallback;
|
||||
import android.media.projection.ReviewGrantedConsentResult;
|
||||
import android.os.IBinder;
|
||||
import android.os.RemoteException;
|
||||
import android.os.UserHandle;
|
||||
@@ -56,6 +68,8 @@ import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Captor;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
@@ -108,6 +122,8 @@ public class MediaProjectionManagerServiceTest {
|
||||
private WindowManagerInternal mWindowManagerInternal;
|
||||
@Mock
|
||||
private PackageManager mPackageManager;
|
||||
@Captor
|
||||
private ArgumentCaptor<ContentRecordingSession> mSessionCaptor;
|
||||
|
||||
@Before
|
||||
public void setup() throws Exception {
|
||||
@@ -154,12 +170,15 @@ public class MediaProjectionManagerServiceTest {
|
||||
|
||||
@Test
|
||||
public void testCreateProjection() throws NameNotFoundException {
|
||||
MediaProjectionManagerService.MediaProjection projection =
|
||||
startProjectionPreconditions(/* packageAttemptedReusingGrantedConsent= */ false);
|
||||
// Create a first projection.
|
||||
MediaProjectionManagerService.MediaProjection projection = startProjectionPreconditions();
|
||||
projection.start(mIMediaProjectionCallback);
|
||||
|
||||
// We are allowed to create a new projection.
|
||||
MediaProjectionManagerService.MediaProjection secondProjection =
|
||||
startProjectionPreconditions(/* packageAttemptedReusingGrantedConsent= */ false);
|
||||
startProjectionPreconditions();
|
||||
|
||||
// This is a new projection.
|
||||
assertThat(secondProjection).isNotNull();
|
||||
assertThat(secondProjection).isNotEqualTo(projection);
|
||||
}
|
||||
@@ -167,44 +186,58 @@ public class MediaProjectionManagerServiceTest {
|
||||
@Test
|
||||
public void testCreateProjection_attemptReuse_noPriorProjectionGrant()
|
||||
throws NameNotFoundException {
|
||||
MediaProjectionManagerService.MediaProjection projection =
|
||||
startProjectionPreconditions(/* packageAttemptedReusingGrantedConsent= */ false);
|
||||
// Create a first projection.
|
||||
MediaProjectionManagerService.MediaProjection projection = startProjectionPreconditions();
|
||||
projection.start(mIMediaProjectionCallback);
|
||||
|
||||
MediaProjectionManagerService.MediaProjection secondProjection =
|
||||
startProjectionPreconditions(/* packageAttemptedReusingGrantedConsent= */ true);
|
||||
|
||||
assertThat(secondProjection).isNotNull();
|
||||
assertThat(secondProjection).isNotEqualTo(projection);
|
||||
// We are not allowed to retrieve the prior projection, since we are not waiting for the
|
||||
// user's consent.
|
||||
assertThat(startReusedProjectionPreconditions()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateProjection_attemptReuse_priorProjectionGrant_notWaiting()
|
||||
throws NameNotFoundException {
|
||||
MediaProjectionManagerService.MediaProjection projection =
|
||||
startProjectionPreconditions(/* packageAttemptedReusingGrantedConsent= */ false);
|
||||
// Create a first projection.
|
||||
MediaProjectionManagerService.MediaProjection projection = startProjectionPreconditions();
|
||||
projection.start(mIMediaProjectionCallback);
|
||||
|
||||
// Mark this projection as not waiting.
|
||||
// Mark this projection as not waiting for the user to review consent.
|
||||
doReturn(true).when(mWindowManagerInternal).setContentRecordingSession(
|
||||
any(ContentRecordingSession.class));
|
||||
mService.setContentRecordingSession(DISPLAY_SESSION);
|
||||
|
||||
// We are allowed to create another projection.
|
||||
// We are not allowed to retrieve the prior projection, since we are not waiting for the
|
||||
// user's consent.
|
||||
assertThat(startReusedProjectionPreconditions()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateProjection_attemptReuse_priorProjectionGrant_waiting()
|
||||
throws NameNotFoundException {
|
||||
// Create a first projection.
|
||||
MediaProjectionManagerService.MediaProjection projection = startProjectionPreconditions();
|
||||
projection.start(mIMediaProjectionCallback);
|
||||
|
||||
// Mark this projection as waiting for the user to review consent.
|
||||
doReturn(true).when(mWindowManagerInternal).setContentRecordingSession(
|
||||
any(ContentRecordingSession.class));
|
||||
mService.setContentRecordingSession(mWaitingDisplaySession);
|
||||
|
||||
// We are allowed to create another projection, reusing a prior grant if necessary.
|
||||
MediaProjectionManagerService.MediaProjection secondProjection =
|
||||
startProjectionPreconditions(/* packageAttemptedReusingGrantedConsent= */ true);
|
||||
startReusedProjectionPreconditions();
|
||||
|
||||
// This is a new projection, since we are waiting for the user's consent; simply provide
|
||||
// the projection grant from before.
|
||||
assertThat(secondProjection).isNotNull();
|
||||
|
||||
// But this is a new projection.
|
||||
assertThat(secondProjection).isNotEqualTo(projection);
|
||||
assertThat(secondProjection).isEqualTo(projection);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateProjection_attemptReuse_priorProjectionGrant_waiting_differentPackage()
|
||||
throws NameNotFoundException {
|
||||
MediaProjectionManagerService.MediaProjection projection =
|
||||
startProjectionPreconditions(/* packageAttemptedReusingGrantedConsent= */ false);
|
||||
MediaProjectionManagerService.MediaProjection projection = startProjectionPreconditions();
|
||||
projection.start(mIMediaProjectionCallback);
|
||||
|
||||
// Mark this projection as not waiting.
|
||||
@@ -213,8 +246,7 @@ public class MediaProjectionManagerServiceTest {
|
||||
// We are allowed to create another projection.
|
||||
MediaProjectionManagerService.MediaProjection secondProjection =
|
||||
mService.createProjectionInternal(UID + 10, PACKAGE_NAME + "foo",
|
||||
TYPE_MIRRORING, /* isPermanentGrant= */ true,
|
||||
UserHandle.CURRENT, /* packageAttemptedReusingGrantedConsent= */ true);
|
||||
TYPE_MIRRORING, /* isPermanentGrant= */ true, UserHandle.CURRENT);
|
||||
|
||||
assertThat(secondProjection).isNotNull();
|
||||
|
||||
@@ -366,6 +398,267 @@ public class MediaProjectionManagerServiceTest {
|
||||
assertThat(mService.isCurrentProjection(projection.asBinder())).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetUserReviewGrantedConsentResult_noCurrentProjection() {
|
||||
// Gracefully handle invocation without a current projection.
|
||||
mService.setUserReviewGrantedConsentResult(RECORD_CONTENT_DISPLAY,
|
||||
mock(IMediaProjection.class));
|
||||
assertThat(mService.getActiveProjectionInfo()).isNull();
|
||||
verify(mWindowManagerInternal, never()).setContentRecordingSession(any(
|
||||
ContentRecordingSession.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetUserReviewGrantedConsentResult_projectionNotCurrent() throws Exception {
|
||||
MediaProjectionManagerService.MediaProjection projection = startProjectionPreconditions();
|
||||
projection.start(mIMediaProjectionCallback);
|
||||
assertThat(mService.isCurrentProjection(projection)).isTrue();
|
||||
doReturn(true).when(mWindowManagerInternal).setContentRecordingSession(
|
||||
any(ContentRecordingSession.class));
|
||||
// Some other token.
|
||||
final IMediaProjection otherProjection = mock(IMediaProjection.class);
|
||||
doReturn(mock(IBinder.class)).when(otherProjection).asBinder();
|
||||
// Waiting for user to review consent.
|
||||
mService.setContentRecordingSession(mWaitingDisplaySession);
|
||||
mService.setUserReviewGrantedConsentResult(RECORD_CONTENT_DISPLAY, otherProjection);
|
||||
|
||||
// Display result is ignored; only the first session is set.
|
||||
verify(mWindowManagerInternal, times(1)).setContentRecordingSession(
|
||||
eq(mWaitingDisplaySession));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetUserReviewGrantedConsentResult_projectionNull() throws Exception {
|
||||
MediaProjectionManagerService.MediaProjection projection = startProjectionPreconditions();
|
||||
projection.start(mIMediaProjectionCallback);
|
||||
assertThat(mService.isCurrentProjection(projection)).isTrue();
|
||||
doReturn(true).when(mWindowManagerInternal).setContentRecordingSession(
|
||||
any(ContentRecordingSession.class));
|
||||
// Some other token.
|
||||
final IMediaProjection otherProjection = null;
|
||||
// Waiting for user to review consent.
|
||||
mService.setContentRecordingSession(mWaitingDisplaySession);
|
||||
mService.setUserReviewGrantedConsentResult(RECORD_CONTENT_DISPLAY, otherProjection);
|
||||
|
||||
// Display result is ignored; only the first session is set.
|
||||
verify(mWindowManagerInternal, times(1)).setContentRecordingSession(
|
||||
eq(mWaitingDisplaySession));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetUserReviewGrantedConsentResult_noVirtualDisplay() throws Exception {
|
||||
MediaProjectionManagerService.MediaProjection projection = startProjectionPreconditions();
|
||||
projection.start(mIMediaProjectionCallback);
|
||||
// Do not indicate that the virtual display was created.
|
||||
ContentRecordingSession session = mWaitingDisplaySession;
|
||||
session.setVirtualDisplayId(INVALID_DISPLAY);
|
||||
doReturn(true).when(mWindowManagerInternal).setContentRecordingSession(
|
||||
any(ContentRecordingSession.class));
|
||||
// Waiting for user to review consent.
|
||||
assertThat(mService.isCurrentProjection(projection)).isTrue();
|
||||
mService.setContentRecordingSession(session);
|
||||
|
||||
mService.setUserReviewGrantedConsentResult(RECORD_CONTENT_DISPLAY, projection);
|
||||
// A session is sent, indicating consent is granted to record but the virtual display isn't
|
||||
// ready yet.
|
||||
verify(mWindowManagerInternal, times(2)).setContentRecordingSession(
|
||||
mSessionCaptor.capture());
|
||||
// Examine latest value.
|
||||
final ContentRecordingSession capturedSession = mSessionCaptor.getValue();
|
||||
assertThat(capturedSession.isWaitingToRecord()).isFalse();
|
||||
assertThat(capturedSession.getVirtualDisplayId()).isEqualTo(INVALID_DISPLAY);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetUserReviewGrantedConsentResult_thenVirtualDisplayCreated() throws Exception {
|
||||
MediaProjectionManagerService.MediaProjection projection = startProjectionPreconditions();
|
||||
projection.start(mIMediaProjectionCallback);
|
||||
assertThat(mService.isCurrentProjection(projection)).isTrue();
|
||||
doReturn(true).when(mWindowManagerInternal).setContentRecordingSession(
|
||||
any(ContentRecordingSession.class));
|
||||
// Waiting for user to review consent.
|
||||
mService.setContentRecordingSession(mWaitingDisplaySession);
|
||||
mService.setUserReviewGrantedConsentResult(RECORD_CONTENT_DISPLAY, projection);
|
||||
|
||||
// Virtual Display is finally created.
|
||||
projection.notifyVirtualDisplayCreated(10);
|
||||
verifySetSessionWithContent(ContentRecordingSession.RECORD_CONTENT_DISPLAY);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetUserReviewGrantedConsentResult_unknown_updatedSession() throws Exception {
|
||||
testSetUserReviewGrantedConsentResult_userCancelsSession(
|
||||
/* isSetSessionSuccessful= */ true, UNKNOWN);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetUserReviewGrantedConsentResult_unknown_failedToUpdateSession()
|
||||
throws Exception {
|
||||
testSetUserReviewGrantedConsentResult_userCancelsSession(
|
||||
/* isSetSessionSuccessful= */ false, UNKNOWN);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetUserReviewGrantedConsentResult_cancel_updatedSession() throws Exception {
|
||||
testSetUserReviewGrantedConsentResult_userCancelsSession(
|
||||
/* isSetSessionSuccessful= */ true, RECORD_CANCEL);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetUserReviewGrantedConsentResult_cancel_failedToUpdateSession()
|
||||
throws Exception {
|
||||
testSetUserReviewGrantedConsentResult_userCancelsSession(
|
||||
/* isSetSessionSuccessful= */ false, RECORD_CANCEL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes and validates scenario where the consent result indicates the projection ends.
|
||||
*/
|
||||
private void testSetUserReviewGrantedConsentResult_userCancelsSession(
|
||||
boolean isSetSessionSuccessful, @ReviewGrantedConsentResult int consentResult)
|
||||
throws Exception {
|
||||
MediaProjectionManagerService.MediaProjection projection = startProjectionPreconditions();
|
||||
projection.start(mIMediaProjectionCallback);
|
||||
projection.notifyVirtualDisplayCreated(10);
|
||||
// Waiting for user to review consent.
|
||||
assertThat(mService.isCurrentProjection(projection)).isTrue();
|
||||
doReturn(true).when(mWindowManagerInternal).setContentRecordingSession(
|
||||
any(ContentRecordingSession.class));
|
||||
mService.setContentRecordingSession(mWaitingDisplaySession);
|
||||
|
||||
doReturn(isSetSessionSuccessful).when(mWindowManagerInternal).setContentRecordingSession(
|
||||
any(ContentRecordingSession.class));
|
||||
|
||||
mService.setUserReviewGrantedConsentResult(consentResult, projection);
|
||||
verify(mWindowManagerInternal, atLeastOnce()).setContentRecordingSession(
|
||||
mSessionCaptor.capture());
|
||||
// Null value to stop session.
|
||||
assertThat(mSessionCaptor.getValue()).isNull();
|
||||
assertThat(mService.isCurrentProjection(projection.asBinder())).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetUserReviewGrantedConsentResult_displayMirroring_startedSession()
|
||||
throws NameNotFoundException {
|
||||
testSetUserReviewGrantedConsentResult_startedSession(RECORD_CONTENT_DISPLAY,
|
||||
ContentRecordingSession.RECORD_CONTENT_DISPLAY);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetUserReviewGrantedConsentResult_displayMirroring_failedToStartSession()
|
||||
throws NameNotFoundException {
|
||||
testSetUserReviewGrantedConsentResult_failedToStartSession(RECORD_CONTENT_DISPLAY,
|
||||
ContentRecordingSession.RECORD_CONTENT_DISPLAY);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetUserReviewGrantedConsentResult_taskMirroring_startedSession()
|
||||
throws NameNotFoundException {
|
||||
testSetUserReviewGrantedConsentResult_startedSession(RECORD_CONTENT_TASK,
|
||||
ContentRecordingSession.RECORD_CONTENT_TASK);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetUserReviewGrantedConsentResult_taskMirroring_failedToStartSession()
|
||||
throws NameNotFoundException {
|
||||
testSetUserReviewGrantedConsentResult_failedToStartSession(RECORD_CONTENT_TASK,
|
||||
ContentRecordingSession.RECORD_CONTENT_TASK);
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes and validates scenario where the consent result indicates the projection continues,
|
||||
* and successfully started projection.
|
||||
*/
|
||||
private void testSetUserReviewGrantedConsentResult_startedSession(
|
||||
@ReviewGrantedConsentResult int consentResult,
|
||||
@ContentRecordingSession.RecordContent int recordedContent)
|
||||
throws NameNotFoundException {
|
||||
MediaProjectionManagerService.MediaProjection projection = startProjectionPreconditions();
|
||||
projection.setLaunchCookie(mock(IBinder.class));
|
||||
projection.start(mIMediaProjectionCallback);
|
||||
projection.notifyVirtualDisplayCreated(10);
|
||||
// Waiting for user to review consent.
|
||||
doReturn(true).when(mWindowManagerInternal).setContentRecordingSession(
|
||||
any(ContentRecordingSession.class));
|
||||
mService.setContentRecordingSession(mWaitingDisplaySession);
|
||||
|
||||
mService.setUserReviewGrantedConsentResult(consentResult, projection);
|
||||
verifySetSessionWithContent(recordedContent);
|
||||
assertThat(mService.isCurrentProjection(projection)).isTrue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes and validates scenario where the consent result indicates the projection continues,
|
||||
* but unable to continue projection.
|
||||
*/
|
||||
private void testSetUserReviewGrantedConsentResult_failedToStartSession(
|
||||
@ReviewGrantedConsentResult int consentResult,
|
||||
@ContentRecordingSession.RecordContent int recordedContent)
|
||||
throws NameNotFoundException {
|
||||
MediaProjectionManagerService.MediaProjection projection = startProjectionPreconditions();
|
||||
projection.start(mIMediaProjectionCallback);
|
||||
projection.notifyVirtualDisplayCreated(10);
|
||||
// Waiting for user to review consent.
|
||||
doReturn(true).when(mWindowManagerInternal).setContentRecordingSession(
|
||||
eq(mWaitingDisplaySession));
|
||||
mService.setContentRecordingSession(mWaitingDisplaySession);
|
||||
|
||||
doReturn(false).when(mWindowManagerInternal).setContentRecordingSession(
|
||||
any(ContentRecordingSession.class));
|
||||
|
||||
mService.setUserReviewGrantedConsentResult(consentResult, projection);
|
||||
verifySetSessionWithContent(recordedContent);
|
||||
assertThat(mService.isCurrentProjection(projection.asBinder())).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetUserReviewGrantedConsentResult_displayMirroring_noPriorSession()
|
||||
throws NameNotFoundException {
|
||||
MediaProjectionManagerService.MediaProjection projection = startProjectionPreconditions();
|
||||
projection.setLaunchCookie(mock(IBinder.class));
|
||||
projection.start(mIMediaProjectionCallback);
|
||||
// Skip setting the prior session details.
|
||||
|
||||
doReturn(true).when(mWindowManagerInternal).setContentRecordingSession(
|
||||
any(ContentRecordingSession.class));
|
||||
|
||||
mService.setUserReviewGrantedConsentResult(RECORD_CONTENT_DISPLAY, projection);
|
||||
// Result is ignored & session not updated.
|
||||
verify(mWindowManagerInternal, never()).setContentRecordingSession(any(
|
||||
ContentRecordingSession.class));
|
||||
// Current session continues.
|
||||
assertThat(mService.isCurrentProjection(projection)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetUserReviewGrantedConsentResult_displayMirroring_sessionNotWaiting()
|
||||
throws NameNotFoundException {
|
||||
MediaProjectionManagerService.MediaProjection projection = startProjectionPreconditions();
|
||||
projection.setLaunchCookie(mock(IBinder.class));
|
||||
projection.start(mIMediaProjectionCallback);
|
||||
// Session is not waiting for user's consent.
|
||||
doReturn(true).when(mWindowManagerInternal).setContentRecordingSession(
|
||||
any(ContentRecordingSession.class));
|
||||
mService.setContentRecordingSession(DISPLAY_SESSION);
|
||||
|
||||
doReturn(true).when(mWindowManagerInternal).setContentRecordingSession(
|
||||
any(ContentRecordingSession.class));
|
||||
|
||||
mService.setUserReviewGrantedConsentResult(RECORD_CONTENT_DISPLAY, projection);
|
||||
// Result is ignored; only the original session was ever sent.
|
||||
verify(mWindowManagerInternal).setContentRecordingSession(eq(
|
||||
DISPLAY_SESSION));
|
||||
// Current session continues.
|
||||
assertThat(mService.isCurrentProjection(projection)).isTrue();
|
||||
}
|
||||
|
||||
private void verifySetSessionWithContent(@ContentRecordingSession.RecordContent int content) {
|
||||
verify(mWindowManagerInternal, atLeastOnce()).setContentRecordingSession(
|
||||
mSessionCaptor.capture());
|
||||
assertThat(mSessionCaptor.getValue()).isNotNull();
|
||||
assertThat(mSessionCaptor.getValue().getContentToRecord()).isEqualTo(content);
|
||||
}
|
||||
|
||||
// Set up preconditions for creating a projection.
|
||||
private MediaProjectionManagerService.MediaProjection createProjectionPreconditions(
|
||||
MediaProjectionManagerService service)
|
||||
@@ -373,14 +666,7 @@ public class MediaProjectionManagerServiceTest {
|
||||
doReturn(mAppInfo).when(mPackageManager).getApplicationInfoAsUser(anyString(),
|
||||
any(ApplicationInfoFlags.class), any(UserHandle.class));
|
||||
return service.createProjectionInternal(UID, PACKAGE_NAME,
|
||||
TYPE_MIRRORING, /* isPermanentGrant= */ true, UserHandle.CURRENT,
|
||||
/* packageAttemptedReusingGrantedConsent= */ false);
|
||||
}
|
||||
|
||||
// Set up preconditions for creating a projection.
|
||||
private MediaProjectionManagerService.MediaProjection createProjectionPreconditions()
|
||||
throws NameNotFoundException {
|
||||
return createProjectionPreconditions(mService);
|
||||
TYPE_MIRRORING, /* isPermanentGrant= */ true, UserHandle.CURRENT);
|
||||
}
|
||||
|
||||
// Set up preconditions for starting a projection, with no foreground service requirements.
|
||||
@@ -391,19 +677,6 @@ public class MediaProjectionManagerServiceTest {
|
||||
return createProjectionPreconditions(service);
|
||||
}
|
||||
|
||||
// Set up preconditions for starting a projection, specifying if it is possible to reuse the
|
||||
// the current projection.
|
||||
private MediaProjectionManagerService.MediaProjection startProjectionPreconditions(
|
||||
boolean packageAttemptedReusingGrantedConsent)
|
||||
throws NameNotFoundException {
|
||||
mAppInfo.privateFlags |= PRIVATE_FLAG_PRIVILEGED;
|
||||
doReturn(mAppInfo).when(mPackageManager).getApplicationInfoAsUser(anyString(),
|
||||
any(ApplicationInfoFlags.class), any(UserHandle.class));
|
||||
return mService.createProjectionInternal(UID, PACKAGE_NAME,
|
||||
TYPE_MIRRORING, /* isPermanentGrant= */ true, UserHandle.CURRENT,
|
||||
packageAttemptedReusingGrantedConsent);
|
||||
}
|
||||
|
||||
// Set up preconditions for starting a projection, with no foreground service requirements.
|
||||
private MediaProjectionManagerService.MediaProjection startProjectionPreconditions()
|
||||
throws NameNotFoundException {
|
||||
@@ -411,6 +684,15 @@ public class MediaProjectionManagerServiceTest {
|
||||
return createProjectionPreconditions(mService);
|
||||
}
|
||||
|
||||
// Set up preconditions for starting a projection, retrieving a pre-existing projection.
|
||||
private MediaProjectionManagerService.MediaProjection startReusedProjectionPreconditions()
|
||||
throws NameNotFoundException {
|
||||
mAppInfo.privateFlags |= PRIVATE_FLAG_PRIVILEGED;
|
||||
doReturn(mAppInfo).when(mPackageManager).getApplicationInfoAsUser(anyString(),
|
||||
any(ApplicationInfoFlags.class), any(UserHandle.class));
|
||||
return mService.getProjectionInternal(UID, PACKAGE_NAME);
|
||||
}
|
||||
|
||||
private static class FakeIMediaProjectionCallback extends IMediaProjectionCallback.Stub {
|
||||
@Override
|
||||
public void onStop() throws RemoteException {
|
||||
|
||||
Reference in New Issue
Block a user