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

Commit 2445f858 authored by Chavi Weingarten's avatar Chavi Weingarten
Browse files

Add TestAPI to replace content on a display.

The current tests use MediaProjection to test that content is updated
frame by frame. However, this causes several issues, like having to
accept the permission dialog, having to wait for app launch animations
to complete, content overlaying the test area. Instead, the tests only
need to capture the window being tested. To do this, the test app
creates a VirtualDisplay and then swaps the content of the display with
a mirror of the window being tested. This way, the frames received in
the VD only contain the content in the window mirror and nothing else on
screen.

This reduces the test time since we don't need to wait for a foreground
service and the permission dialog to be clicked, nor does it have to
wait for any animations to complete. The test can start as soon as the
window is placed on screen and the VirtualDisplay contains the mirror.

It also reduces flakes becuase there's no chance of other content
showing over the test area so the pixels in the VD are deterministic.

Test: SurfaceSyncGroupContinuousTest
Test: ASurfaceControlBackPressureTest
Test: SurfacePackageFlickerTest
Test: AttachedSurfaceControlSyncTest
Test: SurfaceViewSyncTest
Test: SurfaceViewSyncContinuousTest

Bug: 282169297
Bug: 288339794
Change-Id: I311a30f8e16b99034b9a662fe2755630d68fcb80
parent 919cffbb
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -3622,6 +3622,8 @@ package android.view {
    method public default void holdLock(android.os.IBinder, int);
    method public default boolean isGlobalKey(int);
    method public default boolean isTaskSnapshotSupported();
    method @RequiresPermission(android.Manifest.permission.ACCESS_SURFACE_FLINGER) public default boolean replaceContentOnDisplayWithMirror(int, @NonNull android.view.Window);
    method @RequiresPermission(android.Manifest.permission.ACCESS_SURFACE_FLINGER) public default boolean replaceContentOnDisplayWithSc(int, @NonNull android.view.SurfaceControl);
    method public default void setDisplayImePolicy(int, int);
    method public default void setShouldShowSystemDecors(int, boolean);
    method public default void setShouldShowWithInsecureKeyguard(int, boolean);
+9 −0
Original line number Diff line number Diff line
@@ -1044,4 +1044,13 @@ interface IWindowManager
     * @return List of ComponentNames corresponding to the activities that were notified.
    */
    List<ComponentName> notifyScreenshotListeners(int displayId);

    /**
     * Replace the content of the displayId with the SurfaceControl passed in. This can be used for
     * tests when creating a VirtualDisplay, but only want to capture specific content and not
     * mirror the entire display.
     */
     @JavaPassthrough(annotation = "@android.annotation.RequiresPermission(android.Manifest"
             + ".permission.ACCESS_SURFACE_FLINGER)")
    boolean replaceContentOnDisplay(int displayId, in SurfaceControl sc);
}
+31 −0
Original line number Diff line number Diff line
@@ -5701,4 +5701,35 @@ public interface WindowManager extends ViewManager {
    default @NonNull List<ComponentName> notifyScreenshotListeners(int displayId) {
        throw new UnsupportedOperationException();
    }

    /**
     * @param displayId The displayId to that should have its content replaced.
     * @param window The window that should get mirrored and the mirrored content rendered on
     *               displayId passed in.
     *
     * @return Whether it successfully created a mirror SurfaceControl and replaced the display
     * content with the mirror of the Window.
     *
     * @hide
     */
    @TestApi
    @RequiresPermission(permission.ACCESS_SURFACE_FLINGER)
    default boolean replaceContentOnDisplayWithMirror(int displayId, @NonNull Window window) {
        throw new UnsupportedOperationException();
    }

    /**
     * @param displayId The displayId to that should have its content replaced.
     * @param sc The SurfaceControl that should get rendered onto the displayId passed in.
     *
     * @return Whether it successfully created a mirror SurfaceControl and replaced the display
     * content with the mirror of the Window.
     *
     * @hide
     */
    @TestApi
    @RequiresPermission(permission.ACCESS_SURFACE_FLINGER)
    default boolean replaceContentOnDisplayWithSc(int displayId, @NonNull SurfaceControl sc) {
        throw new UnsupportedOperationException();
    }
}
+41 −0
Original line number Diff line number Diff line
@@ -34,6 +34,7 @@ import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.StrictMode;
import android.util.Log;
import android.window.ITaskFpsCallback;
import android.window.TaskFpsCallback;
import android.window.WindowContext;
@@ -80,6 +81,8 @@ import java.util.function.IntConsumer;
 * @hide
 */
public final class WindowManagerImpl implements WindowManager {
    private static final String TAG = "WindowManager";

    @UnsupportedAppUsage
    private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
    @UiContext
@@ -467,4 +470,42 @@ public final class WindowManagerImpl implements WindowManager {
            throw e.rethrowFromSystemServer();
        }
    }

    @Override
    public boolean replaceContentOnDisplayWithMirror(int displayId, @NonNull Window window) {
        View decorView = window.peekDecorView();
        if (decorView == null) {
            Log.e(TAG, "replaceContentOnDisplayWithMirror: Window's decorView was null.");
            return false;
        }

        ViewRootImpl viewRoot = decorView.getViewRootImpl();
        if (viewRoot == null) {
            Log.e(TAG, "replaceContentOnDisplayWithMirror: Window's viewRootImpl was null.");
            return false;
        }

        SurfaceControl sc = viewRoot.getSurfaceControl();
        if (!sc.isValid()) {
            Log.e(TAG, "replaceContentOnDisplayWithMirror: Window's SC is invalid.");
            return false;
        }
        return replaceContentOnDisplayWithSc(displayId, SurfaceControl.mirrorSurface(sc));
    }

    @Override
    public boolean replaceContentOnDisplayWithSc(int displayId, @NonNull SurfaceControl sc) {
        if (!sc.isValid()) {
            Log.e(TAG, "replaceContentOnDisplayWithSc: Invalid SC.");
            return false;
        }

        try {
            return WindowManagerGlobal.getWindowManagerService()
                    .replaceContentOnDisplay(displayId, sc);
        } catch (RemoteException e) {
            e.rethrowAsRuntimeException();
        }
        return false;
    }
}
+8 −0
Original line number Diff line number Diff line
@@ -7032,4 +7032,12 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
        // Display is the root, so it's not rotated relative to anything.
        return Surface.ROTATION_0;
    }

    public void replaceContent(SurfaceControl sc) {
        new Transaction().reparent(sc, getSurfaceControl())
                .reparent(mWindowingLayer, null)
                .reparent(mOverlayLayer, null)
                .reparent(mA11yOverlayLayer, null)
                .apply();
    }
}
Loading