Loading core/java/android/companion/virtual/computercontrol/ComputerControlSession.java +56 −4 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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. */ Loading Loading @@ -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 Loading core/java/android/companion/virtual/computercontrol/ComputerControlSessionParams.java +16 −13 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading Loading @@ -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; } Loading Loading @@ -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, Loading core/java/android/companion/virtual/computercontrol/IComputerControlSessionCallback.aidl +3 −1 Original line number Diff line number Diff line Loading @@ -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. Loading @@ -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); Loading core/tests/coretests/src/android/companion/virtual/computercontrol/ComputerControlSessionParamsTest.java +2 −13 Original line number Diff line number Diff line Loading @@ -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, Loading @@ -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) Loading @@ -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) Loading core/tests/coretests/src/android/companion/virtual/computercontrol/ComputerControlSessionTest.java +20 −2 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading @@ -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 Loading @@ -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 Loading
core/java/android/companion/virtual/computercontrol/ComputerControlSession.java +56 −4 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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. */ Loading Loading @@ -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 Loading
core/java/android/companion/virtual/computercontrol/ComputerControlSessionParams.java +16 −13 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading Loading @@ -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; } Loading Loading @@ -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, Loading
core/java/android/companion/virtual/computercontrol/IComputerControlSessionCallback.aidl +3 −1 Original line number Diff line number Diff line Loading @@ -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. Loading @@ -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); Loading
core/tests/coretests/src/android/companion/virtual/computercontrol/ComputerControlSessionParamsTest.java +2 −13 Original line number Diff line number Diff line Loading @@ -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, Loading @@ -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) Loading @@ -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) Loading
core/tests/coretests/src/android/companion/virtual/computercontrol/ComputerControlSessionTest.java +20 −2 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading @@ -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 Loading @@ -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