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

Commit 45dd2a32 authored by Naomi Musgrave's avatar Naomi Musgrave
Browse files

MediaProjection captures DisplayArea

MediaProjection is used by apps to capture the contents of
a display. Apps consume the content from a surface passed
in to MediaProjection#createVirtualDisplay. The images
written out to the surface can be used for saving
screenshots of the current display, screen recording,
or casting to a different display.

MediaProjection is currently tied to capturing a physical
display. This change instead captures the DisplayArea
the app is launched on. This is consistent with Display API
sandboxing, which returns the DisplayArea bounds rather
than the physical display bounds.

Note that apps use the Display APIs to size the
VirtualDisplay and output surface.

Verified: screenshots, screen recording, and casting

Bug: 180624777
Test: atest WmTests:DisplayContentTests
Change-Id: I909e9d32f573aec33869d9f49d6caffc8d7420aa
parent 9e0dd926
Loading
Loading
Loading
Loading
+23 −0
Original line number Original line Diff line number Diff line
@@ -21,6 +21,7 @@ import android.annotation.Nullable;
import android.graphics.Point;
import android.graphics.Point;
import android.hardware.SensorManager;
import android.hardware.SensorManager;
import android.os.Handler;
import android.os.Handler;
import android.os.IBinder;
import android.os.PowerManager;
import android.os.PowerManager;
import android.util.IntArray;
import android.util.IntArray;
import android.util.Slog;
import android.util.Slog;
@@ -339,6 +340,28 @@ public abstract class DisplayManagerInternal {
     */
     */
    public abstract List<RefreshRateLimitation> getRefreshRateLimitations(int displayId);
    public abstract List<RefreshRateLimitation> getRefreshRateLimitations(int displayId);


    /**
     * Returns the window token of the level of the WindowManager hierarchy to mirror. Returns null
     * if layer mirroring by SurfaceFlinger should not be performed for the given displayId.
     * For now, only used for mirroring started from MediaProjection.
     */
    public abstract IBinder getWindowTokenClientToMirror(int displayId);

    /**
     * For the given displayId, updates the window token of the level of the WindowManager hierarchy
     * to mirror. If windowToken is null, then SurfaceFlinger performs no layer mirroring to the
     * given display.
     * For now, only used for mirroring started from MediaProjection.
     */
    public abstract void setWindowTokenClientToMirror(int displayId, IBinder windowToken);

    /**
     * Returns the default size of the surface associated with the display, or null if the surface
     * is not provided for layer mirroring by SurfaceFlinger.
     * For now, only used for mirroring started from MediaProjection.
     */
    public abstract Point getDisplaySurfaceDefaultSize(int displayId);

    /**
    /**
     * Describes the requested power state of the display.
     * Describes the requested power state of the display.
     *
     *
+48 −8
Original line number Original line Diff line number Diff line
@@ -23,6 +23,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.Nullable;
import android.media.projection.MediaProjection;
import android.media.projection.MediaProjection;
import android.os.Handler;
import android.os.Handler;
import android.os.IBinder;
import android.os.Parcel;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.Parcelable;
import android.view.Surface;
import android.view.Surface;
@@ -91,9 +92,16 @@ public final class VirtualDisplayConfig implements Parcelable {
     */
     */
    private int mDisplayIdToMirror = DEFAULT_DISPLAY;
    private int mDisplayIdToMirror = DEFAULT_DISPLAY;


    /**
     * The window token of the level of the WindowManager hierarchy to mirror, or null if mirroring
     * should not be performed.
     */
    @Nullable
    private IBinder mWindowTokenClientToMirror = null;




    // Code below generated by codegen v1.0.20.

    // Code below generated by codegen v1.0.23.
    //
    //
    // DO NOT MODIFY!
    // DO NOT MODIFY!
    // CHECKSTYLE:OFF Generated code
    // CHECKSTYLE:OFF Generated code
@@ -115,7 +123,8 @@ public final class VirtualDisplayConfig implements Parcelable {
            int flags,
            int flags,
            @Nullable Surface surface,
            @Nullable Surface surface,
            @Nullable String uniqueId,
            @Nullable String uniqueId,
            int displayIdToMirror) {
            int displayIdToMirror,
            @Nullable IBinder windowTokenClientToMirror) {
        this.mName = name;
        this.mName = name;
        com.android.internal.util.AnnotationValidations.validate(
        com.android.internal.util.AnnotationValidations.validate(
                NonNull.class, null, mName);
                NonNull.class, null, mName);
@@ -135,6 +144,7 @@ public final class VirtualDisplayConfig implements Parcelable {
        this.mSurface = surface;
        this.mSurface = surface;
        this.mUniqueId = uniqueId;
        this.mUniqueId = uniqueId;
        this.mDisplayIdToMirror = displayIdToMirror;
        this.mDisplayIdToMirror = displayIdToMirror;
        this.mWindowTokenClientToMirror = windowTokenClientToMirror;


        // onConstructed(); // You can define this method to get a callback
        // onConstructed(); // You can define this method to get a callback
    }
    }
@@ -212,6 +222,15 @@ public final class VirtualDisplayConfig implements Parcelable {
        return mDisplayIdToMirror;
        return mDisplayIdToMirror;
    }
    }


    /**
     * The window token of the level of the WindowManager hierarchy to mirror, or null if mirroring
     * should not be performed.
     */
    @DataClass.Generated.Member
    public @Nullable IBinder getWindowTokenClientToMirror() {
        return mWindowTokenClientToMirror;
    }

    @Override
    @Override
    @DataClass.Generated.Member
    @DataClass.Generated.Member
    public void writeToParcel(@NonNull Parcel dest, int flags) {
    public void writeToParcel(@NonNull Parcel dest, int flags) {
@@ -221,6 +240,7 @@ public final class VirtualDisplayConfig implements Parcelable {
        int flg = 0;
        int flg = 0;
        if (mSurface != null) flg |= 0x20;
        if (mSurface != null) flg |= 0x20;
        if (mUniqueId != null) flg |= 0x40;
        if (mUniqueId != null) flg |= 0x40;
        if (mWindowTokenClientToMirror != null) flg |= 0x100;
        dest.writeInt(flg);
        dest.writeInt(flg);
        dest.writeString(mName);
        dest.writeString(mName);
        dest.writeInt(mWidth);
        dest.writeInt(mWidth);
@@ -230,6 +250,7 @@ public final class VirtualDisplayConfig implements Parcelable {
        if (mSurface != null) dest.writeTypedObject(mSurface, flags);
        if (mSurface != null) dest.writeTypedObject(mSurface, flags);
        if (mUniqueId != null) dest.writeString(mUniqueId);
        if (mUniqueId != null) dest.writeString(mUniqueId);
        dest.writeInt(mDisplayIdToMirror);
        dest.writeInt(mDisplayIdToMirror);
        if (mWindowTokenClientToMirror != null) dest.writeStrongBinder(mWindowTokenClientToMirror);
    }
    }


    @Override
    @Override
@@ -252,6 +273,7 @@ public final class VirtualDisplayConfig implements Parcelable {
        Surface surface = (flg & 0x20) == 0 ? null : (Surface) in.readTypedObject(Surface.CREATOR);
        Surface surface = (flg & 0x20) == 0 ? null : (Surface) in.readTypedObject(Surface.CREATOR);
        String uniqueId = (flg & 0x40) == 0 ? null : in.readString();
        String uniqueId = (flg & 0x40) == 0 ? null : in.readString();
        int displayIdToMirror = in.readInt();
        int displayIdToMirror = in.readInt();
        IBinder windowTokenClientToMirror = (flg & 0x100) == 0 ? null : (IBinder) in.readStrongBinder();


        this.mName = name;
        this.mName = name;
        com.android.internal.util.AnnotationValidations.validate(
        com.android.internal.util.AnnotationValidations.validate(
@@ -272,6 +294,7 @@ public final class VirtualDisplayConfig implements Parcelable {
        this.mSurface = surface;
        this.mSurface = surface;
        this.mUniqueId = uniqueId;
        this.mUniqueId = uniqueId;
        this.mDisplayIdToMirror = displayIdToMirror;
        this.mDisplayIdToMirror = displayIdToMirror;
        this.mWindowTokenClientToMirror = windowTokenClientToMirror;


        // onConstructed(); // You can define this method to get a callback
        // onConstructed(); // You can define this method to get a callback
    }
    }
@@ -305,6 +328,7 @@ public final class VirtualDisplayConfig implements Parcelable {
        private @Nullable Surface mSurface;
        private @Nullable Surface mSurface;
        private @Nullable String mUniqueId;
        private @Nullable String mUniqueId;
        private int mDisplayIdToMirror;
        private int mDisplayIdToMirror;
        private @Nullable IBinder mWindowTokenClientToMirror;


        private long mBuilderFieldsSet = 0L;
        private long mBuilderFieldsSet = 0L;


@@ -439,10 +463,22 @@ public final class VirtualDisplayConfig implements Parcelable {
            return this;
            return this;
        }
        }


        /**
         * The window token of the level of the WindowManager hierarchy to mirror, or null if mirroring
         * should not be performed.
         */
        @DataClass.Generated.Member
        public @NonNull Builder setWindowTokenClientToMirror(@NonNull IBinder value) {
            checkNotUsed();
            mBuilderFieldsSet |= 0x100;
            mWindowTokenClientToMirror = value;
            return this;
        }

        /** Builds the instance. This builder should not be touched after calling this! */
        /** Builds the instance. This builder should not be touched after calling this! */
        public @NonNull VirtualDisplayConfig build() {
        public @NonNull VirtualDisplayConfig build() {
            checkNotUsed();
            checkNotUsed();
            mBuilderFieldsSet |= 0x100; // Mark builder used
            mBuilderFieldsSet |= 0x200; // Mark builder used


            if ((mBuilderFieldsSet & 0x10) == 0) {
            if ((mBuilderFieldsSet & 0x10) == 0) {
                mFlags = 0;
                mFlags = 0;
@@ -456,6 +492,9 @@ public final class VirtualDisplayConfig implements Parcelable {
            if ((mBuilderFieldsSet & 0x80) == 0) {
            if ((mBuilderFieldsSet & 0x80) == 0) {
                mDisplayIdToMirror = DEFAULT_DISPLAY;
                mDisplayIdToMirror = DEFAULT_DISPLAY;
            }
            }
            if ((mBuilderFieldsSet & 0x100) == 0) {
                mWindowTokenClientToMirror = null;
            }
            VirtualDisplayConfig o = new VirtualDisplayConfig(
            VirtualDisplayConfig o = new VirtualDisplayConfig(
                    mName,
                    mName,
                    mWidth,
                    mWidth,
@@ -464,12 +503,13 @@ public final class VirtualDisplayConfig implements Parcelable {
                    mFlags,
                    mFlags,
                    mSurface,
                    mSurface,
                    mUniqueId,
                    mUniqueId,
                    mDisplayIdToMirror);
                    mDisplayIdToMirror,
                    mWindowTokenClientToMirror);
            return o;
            return o;
        }
        }


        private void checkNotUsed() {
        private void checkNotUsed() {
            if ((mBuilderFieldsSet & 0x100) != 0) {
            if ((mBuilderFieldsSet & 0x200) != 0) {
                throw new IllegalStateException(
                throw new IllegalStateException(
                        "This Builder should not be reused. Use a new Builder instance instead");
                        "This Builder should not be reused. Use a new Builder instance instead");
            }
            }
@@ -477,10 +517,10 @@ public final class VirtualDisplayConfig implements Parcelable {
    }
    }


    @DataClass.Generated(
    @DataClass.Generated(
            time = 1604456298440L,
            time = 1620657851981L,
            codegenVersion = "1.0.20",
            codegenVersion = "1.0.23",
            sourceFile = "frameworks/base/core/java/android/hardware/display/VirtualDisplayConfig.java",
            sourceFile = "frameworks/base/core/java/android/hardware/display/VirtualDisplayConfig.java",
            inputSignatures = "private @android.annotation.NonNull java.lang.String mName\nprivate @android.annotation.IntRange int mWidth\nprivate @android.annotation.IntRange int mHeight\nprivate @android.annotation.IntRange int mDensityDpi\nprivate  int mFlags\nprivate @android.annotation.Nullable android.view.Surface mSurface\nprivate @android.annotation.Nullable java.lang.String mUniqueId\nprivate  int mDisplayIdToMirror\nclass VirtualDisplayConfig extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genParcelable=true, genAidl=true, genBuilder=true)")
            inputSignatures = "private @android.annotation.NonNull java.lang.String mName\nprivate @android.annotation.IntRange int mWidth\nprivate @android.annotation.IntRange int mHeight\nprivate @android.annotation.IntRange int mDensityDpi\nprivate  int mFlags\nprivate @android.annotation.Nullable android.view.Surface mSurface\nprivate @android.annotation.Nullable java.lang.String mUniqueId\nprivate  int mDisplayIdToMirror\nprivate @android.annotation.Nullable android.os.IBinder mWindowTokenClientToMirror\nclass VirtualDisplayConfig extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genParcelable=true, genAidl=true, genBuilder=true)")
    @Deprecated
    @Deprecated
    private void __metadata() {}
    private void __metadata() {}


+15 −0
Original line number Original line Diff line number Diff line
@@ -29,6 +29,7 @@ import android.graphics.Canvas;
import android.graphics.ColorSpace;
import android.graphics.ColorSpace;
import android.graphics.HardwareRenderer;
import android.graphics.HardwareRenderer;
import android.graphics.Matrix;
import android.graphics.Matrix;
import android.graphics.Point;
import android.graphics.RecordingCanvas;
import android.graphics.RecordingCanvas;
import android.graphics.Rect;
import android.graphics.Rect;
import android.graphics.RenderNode;
import android.graphics.RenderNode;
@@ -407,6 +408,20 @@ public class Surface implements Parcelable {
        }
        }
    }
    }


    /**
     * Returns the default size of this Surface provided by the consumer of the surface.
     * Should only be used by the producer of the surface.
     *
     * @hide
     */
    @NonNull
    public Point getDefaultSize() {
        synchronized (mLock) {
            checkNotReleasedLocked();
            return new Point(nativeGetWidth(mNativeObject), nativeGetHeight(mNativeObject));
        }
    }

    /**
    /**
     * Gets a {@link Canvas} for drawing into this surface.
     * Gets a {@link Canvas} for drawing into this surface.
     *
     *
+24 −4
Original line number Original line Diff line number Diff line
@@ -16,14 +16,14 @@


package android.media.projection;
package android.media.projection;


import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;

import android.annotation.NonNull;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.Nullable;
import android.content.Context;
import android.content.Context;
import android.hardware.display.DisplayManager;
import android.hardware.display.DisplayManager;
import android.hardware.display.VirtualDisplay;
import android.hardware.display.VirtualDisplay;
import android.hardware.display.VirtualDisplayConfig;
import android.hardware.display.VirtualDisplayConfig;
import android.media.projection.IMediaProjection;
import android.media.projection.IMediaProjectionCallback;
import android.os.Handler;
import android.os.Handler;
import android.os.RemoteException;
import android.os.RemoteException;
import android.util.ArrayMap;
import android.util.ArrayMap;
@@ -106,7 +106,7 @@ public final class MediaProjection {
        if (isSecure) {
        if (isSecure) {
            flags |= DisplayManager.VIRTUAL_DISPLAY_FLAG_SECURE;
            flags |= DisplayManager.VIRTUAL_DISPLAY_FLAG_SECURE;
        }
        }
        final VirtualDisplayConfig.Builder builder = new VirtualDisplayConfig.Builder(name, width,
        final VirtualDisplayConfig.Builder builder = buildMirroredVirtualDisplay(name, width,
                height, dpi);
                height, dpi);
        builder.setFlags(flags);
        builder.setFlags(flags);
        if (surface != null) {
        if (surface != null) {
@@ -141,7 +141,7 @@ public final class MediaProjection {
    public VirtualDisplay createVirtualDisplay(@NonNull String name,
    public VirtualDisplay createVirtualDisplay(@NonNull String name,
            int width, int height, int dpi, int flags, @Nullable Surface surface,
            int width, int height, int dpi, int flags, @Nullable Surface surface,
            @Nullable VirtualDisplay.Callback callback, @Nullable Handler handler) {
            @Nullable VirtualDisplay.Callback callback, @Nullable Handler handler) {
        final VirtualDisplayConfig.Builder builder = new VirtualDisplayConfig.Builder(name, width,
        final VirtualDisplayConfig.Builder builder = buildMirroredVirtualDisplay(name, width,
                height, dpi);
                height, dpi);
        builder.setFlags(flags);
        builder.setFlags(flags);
        if (surface != null) {
        if (surface != null) {
@@ -150,6 +150,26 @@ public final class MediaProjection {
        return createVirtualDisplay(builder.build(), callback, handler);
        return createVirtualDisplay(builder.build(), callback, handler);
    }
    }


    /**
     * Constructs a {@link VirtualDisplayConfig.Builder}, which will mirror the contents of a
     * DisplayArea. The DisplayArea to mirror is from the DisplayArea the caller is launched on.
     *
     * @param name   The name of the virtual display, must be non-empty.
     * @param width  The width of the virtual display in pixels. Must be greater than 0.
     * @param height The height of the virtual display in pixels. Must be greater than 0.
     * @param dpi    The density of the virtual display in dpi. Must be greater than 0.
     * @return a config representing a VirtualDisplay
     */
    private VirtualDisplayConfig.Builder buildMirroredVirtualDisplay(@NonNull String name,
            int width, int height, int dpi) {
        Context windowContext = mContext.createWindowContext(mContext.getDisplayNoVerify(),
                TYPE_APPLICATION, null /* options */);
        final VirtualDisplayConfig.Builder builder = new VirtualDisplayConfig.Builder(name, width,
                height, dpi);
        builder.setWindowTokenClientToMirror(windowContext.getWindowContextToken());
        return builder;
    }

    /**
    /**
     * Creates a {@link android.hardware.display.VirtualDisplay} to capture the
     * Creates a {@link android.hardware.display.VirtualDisplay} to capture the
     * contents of the screen.
     * contents of the screen.
+30 −0
Original line number Original line Diff line number Diff line
@@ -16,7 +16,9 @@


package com.android.server.display;
package com.android.server.display;


import android.annotation.Nullable;
import android.content.Context;
import android.content.Context;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.Rect;
import android.hardware.display.DisplayViewport;
import android.hardware.display.DisplayViewport;
import android.os.IBinder;
import android.os.IBinder;
@@ -104,6 +106,34 @@ abstract class DisplayDevice {
        return Display.DEFAULT_DISPLAY;
        return Display.DEFAULT_DISPLAY;
    }
    }


    /**
     * Returns the window token of the level of the WindowManager hierarchy to mirror, or null
     * if layer mirroring by SurfaceFlinger should not be performed.
     * For now, only used for mirroring started from MediaProjection.
     */
    @Nullable
    public IBinder getWindowTokenClientToMirrorLocked() {
        return null;
    }

    /**
     * Updates the window token of the level of the level of the WindowManager hierarchy to mirror.
     * If windowToken is null, then no layer mirroring by SurfaceFlinger to should be performed.
     * For now, only used for mirroring started from MediaProjection.
     */
    public void setWindowTokenClientToMirrorLocked(IBinder windowToken) {
    }

    /**
     * Returns the default size of the surface associated with the display, or null if the surface
     * is not provided for layer mirroring by SurfaceFlinger.
     * For now, only used for mirroring started from MediaProjection.
     */
    @Nullable
    public Point getDisplaySurfaceDefaultSize() {
        return null;
    }

    /**
    /**
     * Gets the name of the display device.
     * Gets the name of the display device.
     *
     *
Loading