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

Commit d4a68c6f authored by Vladimir Komsiyski's avatar Vladimir Komsiyski Committed by Android (Google) Code Review
Browse files

Merge "ComputerControl display creation and screenshot API" into main

parents cbb0de5b 712beced
Loading
Loading
Loading
Loading
+56 −4
Original line number Diff line number Diff line
@@ -25,12 +25,21 @@ import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.IntentSender;
import android.graphics.PixelFormat;
import android.hardware.display.DisplayManagerGlobal;
import android.hardware.display.IVirtualDisplayCallback;
import android.hardware.input.VirtualKeyEvent;
import android.hardware.input.VirtualTouchEvent;
import android.media.Image;
import android.media.ImageReader;
import android.os.Binder;
import android.os.RemoteException;
import android.view.Display;
import android.view.DisplayInfo;
import android.view.Surface;

import com.android.internal.annotations.VisibleForTesting;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -85,11 +94,53 @@ public final class ComputerControlSession implements AutoCloseable {
    public @interface SessionCreationError {
    }

    @NonNull
    private final IComputerControlSession mSession;
    // TODO(b/439774796): Make this non-nullable.
    @Nullable
    private final ImageReader mImageReader;

    /** @hide */
    public ComputerControlSession(int displayId, @NonNull IVirtualDisplayCallback displayToken,
            @NonNull IComputerControlSession session) {
        this(displayId, displayToken, session, DisplayManagerGlobal.getInstance());
    }

    /** @hide */
    public ComputerControlSession(@NonNull IComputerControlSession session) {
    @VisibleForTesting
    public ComputerControlSession(int displayId, @NonNull IVirtualDisplayCallback displayToken,
            @NonNull IComputerControlSession session,
            @NonNull DisplayManagerGlobal displayManagerGlobal) {
        mSession = Objects.requireNonNull(session);

        // TODO(b/439774796): Require a valid display id.
        if (displayId != Display.INVALID_DISPLAY) {
            final Display display = displayManagerGlobal.getRealDisplay(displayId);
            Objects.requireNonNull(display);
            final DisplayInfo displayInfo = new DisplayInfo();
            display.getDisplayInfo(displayInfo);

            mImageReader = ImageReader.newInstance(displayInfo.logicalWidth,
                    displayInfo.logicalHeight,
                    PixelFormat.RGBA_8888, /* maxImages= */ 2);
            displayManagerGlobal.setVirtualDisplaySurface(displayToken, mImageReader.getSurface());
        } else {
            mImageReader = null;
        }
    }

    /**
     * Screenshot the current display content.
     *
     * <p>The behavior is similar to {@link ImageReader#acquireLatestImage}, meaning that any
     * previously acquired images should be released before attempting to acquire new ones.</p>
     *
     * @return A screenshot of the current display content, or {@code null} if no screenshot is
     *   currently available.
     */
    @Nullable
    public Image getScreenshot() {
        return mImageReader == null ? null : mImageReader.acquireLatestImage();
    }

    /** Returns the ID of the single trusted virtual display for this session. */
@@ -201,10 +252,11 @@ public final class ComputerControlSession implements AutoCloseable {
        }

        @Override
        public void onSessionCreated(IComputerControlSession session) {
        public void onSessionCreated(int displayId, IVirtualDisplayCallback displayToken,
                IComputerControlSession session) {
            Binder.withCleanCallingIdentity(() ->
                    mExecutor.execute(() ->
                            mCallback.onSessionCreated(new ComputerControlSession(session))));
                    mExecutor.execute(() -> mCallback.onSessionCreated(
                            new ComputerControlSession(displayId, displayToken, session))));
        }

        @Override
+16 −13
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ package android.companion.virtual.computercontrol;

import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.Parcel;
import android.os.Parcelable;
import android.view.Surface;
@@ -43,7 +44,7 @@ public final class ComputerControlSessionParams implements Parcelable {
            int displayWidthPx,
            int displayHeightPx,
            int displayDpi,
            @NonNull Surface displaySurface,
            @Nullable Surface displaySurface,
            boolean isDisplayAlwaysUnlocked) {
        mName = name;
        mDisplayWidthPx = displayWidthPx;
@@ -84,7 +85,7 @@ public final class ComputerControlSessionParams implements Parcelable {
    }

    /** Returns the surface to which the display content should be rendered. */
    @NonNull
    @Nullable
    public Surface getDisplaySurface() {
        return mDisplaySurface;
    }
@@ -229,17 +230,19 @@ public final class ComputerControlSessionParams implements Parcelable {
            if (mName == null || mName.isEmpty()) {
                throw new IllegalArgumentException("Name must be set");
            }
            if (mDisplaySurface == null) {
                throw new IllegalArgumentException("Surface must be set");
            }
            if (mDisplaySurface != null) {
                if (mDisplayWidthPx <= 0) {
                throw new IllegalArgumentException("Display width must be positive");
                    throw new IllegalArgumentException(
                            "Display width must be positive if surface is set");
                }
                if (mDisplayHeightPx <= 0) {
                throw new IllegalArgumentException("Display height must be positive");
                    throw new IllegalArgumentException(
                            "Display height must be positive if surface is set");
                }
                if (mDisplayDpi <= 0) {
                throw new IllegalArgumentException("Display DPI must be positive");
                    throw new IllegalArgumentException(
                            "Display DPI must be positive if surface is set");
                }
            }
            return new ComputerControlSessionParams(
                    mName,
+3 −1
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ package android.companion.virtual.computercontrol;

import android.app.PendingIntent;
import android.companion.virtual.computercontrol.IComputerControlSession;
import android.hardware.display.IVirtualDisplayCallback;

/**
 * Callback for computer control session events.
@@ -30,7 +31,8 @@ oneway interface IComputerControlSessionCallback {
    void onSessionPending(in PendingIntent pendingIntent);

    /** Called when the session has been successfully created. */
    void onSessionCreated(in IComputerControlSession session);
    void onSessionCreated(int displayId, in IVirtualDisplayCallback displayToken,
            in IComputerControlSession session);

    /** Called when the session failed to be created. */
    void onSessionCreationFailed(int errorCode);
+2 −13
Original line number Diff line number Diff line
@@ -66,17 +66,6 @@ public class ComputerControlSessionParamsTest {
        assertThat(params.getDisplayDpi()).isEqualTo(DISPLAY_DPI);
    }

    @Test
    public void build_unsetSurface_throwsException() {
        assertThrows(IllegalArgumentException.class,
                () -> new ComputerControlSessionParams.Builder()
                        .setName(COMPUTER_CONTROL_SESSION_NAME)
                        .setDisplayWidthPx(DISPLAY_WIDTH)
                        .setDisplayHeightPx(DISPLAY_HEIGHT)
                        .setDisplayDpi(DISPLAY_DPI)
                        .build());
    }

    @Test
    public void build_unsetName_throwsException() {
        assertThrows(IllegalArgumentException.class,
@@ -89,7 +78,7 @@ public class ComputerControlSessionParamsTest {
    }

    @Test
    public void build_nonPositiveDisplayDimensions_throwsException() {
    public void build_setSurface_nonPositiveDisplayDimensions_throwsException() {
        assertThrows(IllegalArgumentException.class,
                () -> new ComputerControlSessionParams.Builder()
                        .setName(COMPUTER_CONTROL_SESSION_NAME)
@@ -109,7 +98,7 @@ public class ComputerControlSessionParamsTest {
    }

    @Test
    public void build_nonPositiveDisplayDpi_throwsException() {
    public void build_setSurface_nonPositiveDisplayDpi_throwsException() {
        assertThrows(IllegalArgumentException.class,
                () -> new ComputerControlSessionParams.Builder()
                        .setName(COMPUTER_CONTROL_SESSION_NAME)
+20 −2
Original line number Diff line number Diff line
@@ -23,9 +23,13 @@ import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import android.hardware.display.DisplayManagerGlobal;
import android.hardware.display.IDisplayManager;
import android.hardware.display.IVirtualDisplayCallback;
import android.hardware.input.VirtualKeyEvent;
import android.hardware.input.VirtualTouchEvent;
import android.os.RemoteException;
import android.view.DisplayInfo;
import android.view.KeyEvent;
import android.view.Surface;

@@ -48,6 +52,10 @@ public class ComputerControlSessionTest {
    @Mock
    private IComputerControlSession mMockSession;
    @Mock
    private IDisplayManager mDisplayManager;
    @Mock
    private IVirtualDisplayCallback mVirtualDisplayCallback;
    @Mock
    private IInteractiveMirrorDisplay mMockInteractiveMirrorDisplay;

    private ComputerControlSession mSession;
@@ -55,9 +63,14 @@ public class ComputerControlSessionTest {
    private AutoCloseable mMockitoSession;

    @Before
    public void setUp() {
    public void setUp() throws RemoteException {
        mMockitoSession = MockitoAnnotations.openMocks(this);
        mSession = new ComputerControlSession(mMockSession);
        final DisplayInfo displayInfo = new DisplayInfo();
        displayInfo.logicalWidth = WIDTH;
        displayInfo.logicalHeight = HEIGHT;
        when(mDisplayManager.getDisplayInfo(DISPLAY_ID)).thenReturn(displayInfo);
        mSession = new ComputerControlSession(DISPLAY_ID, mVirtualDisplayCallback, mMockSession,
                new DisplayManagerGlobal(mDisplayManager));
    }

    @After
@@ -65,6 +78,11 @@ public class ComputerControlSessionTest {
        mMockitoSession.close();
    }

    @Test
    public void setsVirtualDisplaySurface() throws RemoteException {
        verify(mDisplayManager).setVirtualDisplaySurface(eq(mVirtualDisplayCallback), any());
    }

    @Test
    public void getVirtualDisplayId_returnsId() throws RemoteException {
        when(mMockSession.getVirtualDisplayId()).thenReturn(DISPLAY_ID);
Loading