[Media Projection] Require valid token to set session
When MediaProjection sets the session details in MediaProjectiondManagerService, clear and re-set the calling identity (since we have entered the system server across the aidl boundary). Additionaly, verify that the call originated from a valid MediaProjection session. In the current model for MediaProjection, signature-level permission MANAGE_MEDIA_PROJECTION is held by the component that shows the acceptance dialog to the user. The user allowing some app to capture with MediaProjection is represented by the IMediaProjection token (see MediaProjectionManagerService# isValidMediaProjection). Bug: 230748205 Test: Manual Change-Id: Iace8eb7eea6c7a99fba7ea726481461a11bd1c90
This commit is contained in:
@@ -32,7 +32,6 @@ import android.graphics.Region;
|
||||
import android.os.Bundle;
|
||||
import android.os.IRemoteCallback;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
import android.view.ContentRecordingSession;
|
||||
import android.view.DisplayCutout;
|
||||
import android.view.DisplayInfo;
|
||||
import android.view.IAppTransitionAnimationSpecsFuture;
|
||||
@@ -873,17 +872,6 @@ interface IWindowManager
|
||||
*/
|
||||
void detachWindowContextFromWindowContainer(IBinder clientToken);
|
||||
|
||||
/**
|
||||
* Updates the content recording session. If a different session is already in progress, then
|
||||
* the pre-existing session is stopped, and the new incoming session takes over.
|
||||
*
|
||||
* The DisplayContent for the new session will begin recording when
|
||||
* {@link RootWindowContainer#onDisplayChanged} is invoked for the new {@link VirtualDisplay}.
|
||||
*
|
||||
* @param incomingSession the nullable incoming content recording session
|
||||
*/
|
||||
void setContentRecordingSession(in ContentRecordingSession incomingSession);
|
||||
|
||||
/**
|
||||
* Registers a listener, which is to be called whenever cross-window blur is enabled/disabled.
|
||||
*
|
||||
|
||||
@@ -21,6 +21,7 @@ import android.media.projection.IMediaProjectionCallback;
|
||||
import android.media.projection.IMediaProjectionWatcherCallback;
|
||||
import android.media.projection.MediaProjectionInfo;
|
||||
import android.os.IBinder;
|
||||
import android.view.ContentRecordingSession;
|
||||
|
||||
/** {@hide} */
|
||||
interface IMediaProjectionManager {
|
||||
@@ -33,4 +34,15 @@ interface IMediaProjectionManager {
|
||||
void stopActiveProjection();
|
||||
void addCallback(IMediaProjectionWatcherCallback callback);
|
||||
void removeCallback(IMediaProjectionWatcherCallback callback);
|
||||
|
||||
/**
|
||||
* Updates the content recording session. If a different session is already in progress, then
|
||||
* the pre-existing session is stopped, and the new incoming session takes over. Only updates
|
||||
* the session if the given projection is valid.
|
||||
*
|
||||
* @param incomingSession the nullable incoming content recording session
|
||||
* @param projection the non-null projection the session describes
|
||||
*/
|
||||
void setContentRecordingSession(in ContentRecordingSession incomingSession,
|
||||
in IMediaProjection projection);
|
||||
}
|
||||
|
||||
@@ -26,12 +26,11 @@ import android.hardware.display.VirtualDisplay;
|
||||
import android.hardware.display.VirtualDisplayConfig;
|
||||
import android.os.Handler;
|
||||
import android.os.RemoteException;
|
||||
import android.os.ServiceManager;
|
||||
import android.util.ArrayMap;
|
||||
import android.util.Log;
|
||||
import android.view.ContentRecordingSession;
|
||||
import android.view.IWindowManager;
|
||||
import android.view.Surface;
|
||||
import android.view.WindowManagerGlobal;
|
||||
import android.window.WindowContainerToken;
|
||||
|
||||
import java.util.Map;
|
||||
@@ -53,6 +52,7 @@ public final class MediaProjection {
|
||||
private final IMediaProjection mImpl;
|
||||
private final Context mContext;
|
||||
private final Map<Callback, CallbackRecord> mCallbacks;
|
||||
@Nullable private IMediaProjectionManager mProjectionService = null;
|
||||
|
||||
/** @hide */
|
||||
public MediaProjection(Context context, IMediaProjection impl) {
|
||||
@@ -172,7 +172,6 @@ public final class MediaProjection {
|
||||
@NonNull VirtualDisplayConfig.Builder virtualDisplayConfig,
|
||||
@Nullable VirtualDisplay.Callback callback, @Nullable Handler handler) {
|
||||
try {
|
||||
final IWindowManager wmService = WindowManagerGlobal.getWindowManagerService();
|
||||
final WindowContainerToken taskWindowContainerToken =
|
||||
mImpl.getTaskRecordingWindowContainerToken();
|
||||
Context windowContext = null;
|
||||
@@ -199,7 +198,7 @@ public final class MediaProjection {
|
||||
}
|
||||
session.setDisplayId(virtualDisplay.getDisplay().getDisplayId());
|
||||
// Successfully set up, so save the current session details.
|
||||
wmService.setContentRecordingSession(session);
|
||||
getProjectionService().setContentRecordingSession(session, mImpl);
|
||||
return virtualDisplay;
|
||||
} catch (RemoteException e) {
|
||||
// Can not capture if WMS is not accessible, so bail out.
|
||||
@@ -207,6 +206,14 @@ public final class MediaProjection {
|
||||
}
|
||||
}
|
||||
|
||||
private IMediaProjectionManager getProjectionService() {
|
||||
if (mProjectionService == null) {
|
||||
mProjectionService = IMediaProjectionManager.Stub.asInterface(
|
||||
ServiceManager.getService(Context.MEDIA_PROJECTION_SERVICE));
|
||||
}
|
||||
return mProjectionService;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops projection.
|
||||
*/
|
||||
|
||||
@@ -45,14 +45,15 @@ import android.os.RemoteException;
|
||||
import android.os.UserHandle;
|
||||
import android.util.ArrayMap;
|
||||
import android.util.Slog;
|
||||
import android.view.ContentRecordingSession;
|
||||
import android.window.WindowContainerToken;
|
||||
|
||||
import com.android.internal.util.ArrayUtils;
|
||||
import com.android.internal.util.DumpUtils;
|
||||
import com.android.server.LocalServices;
|
||||
import com.android.server.SystemService;
|
||||
import com.android.server.SystemService.TargetUser;
|
||||
import com.android.server.Watchdog;
|
||||
import com.android.server.wm.WindowManagerInternal;
|
||||
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.PrintWriter;
|
||||
@@ -381,6 +382,27 @@ public final class MediaProjectionManagerService extends SystemService
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the current content mirroring session.
|
||||
*/
|
||||
@Override
|
||||
public void setContentRecordingSession(@Nullable ContentRecordingSession incomingSession,
|
||||
@NonNull IMediaProjection projection) {
|
||||
final long origId = Binder.clearCallingIdentity();
|
||||
try {
|
||||
synchronized (mLock) {
|
||||
if (!isValidMediaProjection(projection)) {
|
||||
throw new SecurityException("Invalid media projection");
|
||||
}
|
||||
LocalServices.getService(
|
||||
WindowManagerInternal.class).setContentRecordingSession(
|
||||
incomingSession);
|
||||
}
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(origId);
|
||||
}
|
||||
}
|
||||
|
||||
@Override // Binder call
|
||||
public void dump(FileDescriptor fd, final PrintWriter pw, String[] args) {
|
||||
if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
|
||||
|
||||
@@ -207,7 +207,8 @@ final class ContentRecorder {
|
||||
// Update the cached session state first, since updating the service will result in always
|
||||
// returning to this instance to update recording state.
|
||||
mContentRecordingSession = null;
|
||||
mDisplayContent.mWmService.setContentRecordingSession(null);
|
||||
mDisplayContent.mWmService.mContentRecordingController.setContentRecordingSessionLocked(
|
||||
null, mDisplayContent.mWmService);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -30,6 +30,7 @@ import android.hardware.display.DisplayManagerInternal;
|
||||
import android.os.Bundle;
|
||||
import android.os.IBinder;
|
||||
import android.util.Pair;
|
||||
import android.view.ContentRecordingSession;
|
||||
import android.view.Display;
|
||||
import android.view.IInputFilter;
|
||||
import android.view.IRemoteAnimationFinishedCallback;
|
||||
@@ -869,4 +870,16 @@ public abstract class WindowManagerInternal {
|
||||
*/
|
||||
public abstract boolean isPointInsideWindow(
|
||||
@NonNull IBinder windowToken, int displayId, float displayX, float displayY);
|
||||
|
||||
/**
|
||||
* Updates the content recording session. If a different session is already in progress, then
|
||||
* the pre-existing session is stopped, and the new incoming session takes over.
|
||||
*
|
||||
* The DisplayContent for the new session will begin recording when
|
||||
* {@link RootWindowContainer#onDisplayChanged} is invoked for the new {@link VirtualDisplay}.
|
||||
* Must be invoked for a valid MediaProjection session.
|
||||
*
|
||||
* @param incomingSession the nullable incoming content recording session
|
||||
*/
|
||||
public abstract void setContentRecordingSession(ContentRecordingSession incomingSession);
|
||||
}
|
||||
|
||||
@@ -2956,16 +2956,6 @@ public class WindowManagerService extends IWindowManager.Stub
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the current content mirroring session.
|
||||
*/
|
||||
@Override
|
||||
public void setContentRecordingSession(@Nullable ContentRecordingSession incomingSession) {
|
||||
synchronized (mGlobalLock) {
|
||||
mContentRecordingController.setContentRecordingSessionLocked(incomingSession, this);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(multi-display): remove when no default display use case.
|
||||
void prepareAppTransitionNone() {
|
||||
if (!checkCallingPermission(MANAGE_APP_TOKENS, "prepareAppTransition()")) {
|
||||
@@ -8242,6 +8232,14 @@ public class WindowManagerService extends IWindowManager.Stub
|
||||
return w.getBounds().contains((int) displayX, (int) displayY);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setContentRecordingSession(@Nullable ContentRecordingSession incomingSession) {
|
||||
synchronized (mGlobalLock) {
|
||||
mContentRecordingController.setContentRecordingSessionLocked(incomingSession,
|
||||
WindowManagerService.this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void registerAppFreezeListener(AppFreezeListener listener) {
|
||||
|
||||
@@ -2441,7 +2441,7 @@ public class DisplayContentTests extends WindowTestsBase {
|
||||
ContentRecordingSession session = ContentRecordingSession.createDisplaySession(
|
||||
tokenToMirror);
|
||||
session.setDisplayId(displayId);
|
||||
mWm.setContentRecordingSession(session);
|
||||
mWm.mContentRecordingController.setContentRecordingSessionLocked(session, mWm);
|
||||
actualDC.updateRecording();
|
||||
|
||||
// THEN mirroring is not started, since a null surface indicates the VirtualDisplay is off.
|
||||
@@ -2470,7 +2470,7 @@ public class DisplayContentTests extends WindowTestsBase {
|
||||
ContentRecordingSession session = ContentRecordingSession.createDisplaySession(
|
||||
tokenToMirror);
|
||||
session.setDisplayId(displayId);
|
||||
mWm.setContentRecordingSession(session);
|
||||
mWm.mContentRecordingController.setContentRecordingSessionLocked(session, mWm);
|
||||
mWm.mRoot.onDisplayAdded(displayId);
|
||||
|
||||
// WHEN getting the DisplayContent for the new virtual display.
|
||||
|
||||
Reference in New Issue
Block a user