Loading services/core/java/com/android/server/app/GameServiceProviderInstanceImpl.java +20 −6 Original line number Diff line number Diff line Loading @@ -43,6 +43,7 @@ import android.service.games.IGameSession; import android.service.games.IGameSessionController; import android.service.games.IGameSessionService; import android.util.Slog; import android.view.SurfaceControl; import android.view.SurfaceControlViewHost.SurfacePackage; import com.android.internal.annotations.GuardedBy; Loading Loading @@ -237,7 +238,7 @@ final class GameServiceProviderInstanceImpl implements GameServiceProviderInstan mTaskSystemBarsVisibilityListener); for (GameSessionRecord gameSessionRecord : mGameSessions.values()) { destroyGameSessionFromRecord(gameSessionRecord); destroyGameSessionFromRecordLocked(gameSessionRecord); } mGameSessions.clear(); Loading Loading @@ -510,10 +511,11 @@ final class GameServiceProviderInstanceImpl implements GameServiceProviderInstan } return; } destroyGameSessionFromRecord(gameSessionRecord); destroyGameSessionFromRecordLocked(gameSessionRecord); } private void destroyGameSessionFromRecord(@NonNull GameSessionRecord gameSessionRecord) { @GuardedBy("mLock") private void destroyGameSessionFromRecordLocked(@NonNull GameSessionRecord gameSessionRecord) { SurfacePackage surfacePackage = gameSessionRecord.getSurfacePackage(); if (surfacePackage != null) { try { Loading Loading @@ -586,17 +588,29 @@ final class GameServiceProviderInstanceImpl implements GameServiceProviderInstan @VisibleForTesting void takeScreenshot(int taskId, @NonNull AndroidFuture callback) { GameSessionRecord gameSessionRecord; synchronized (mLock) { boolean isTaskAssociatedWithGameSession = mGameSessions.containsKey(taskId); if (!isTaskAssociatedWithGameSession) { gameSessionRecord = mGameSessions.get(taskId); if (gameSessionRecord == null) { Slog.w(TAG, "No game session found for id: " + taskId); callback.complete(GameScreenshotResult.createInternalErrorResult()); return; } } final SurfacePackage overlaySurfacePackage = gameSessionRecord.getSurfacePackage(); final SurfaceControl overlaySurfaceControl = overlaySurfacePackage != null ? overlaySurfacePackage.getSurfaceControl() : null; mBackgroundExecutor.execute(() -> { final Bitmap bitmap = mWindowManagerService.captureTaskBitmap(taskId); final SurfaceControl.LayerCaptureArgs.Builder layerCaptureArgsBuilder = new SurfaceControl.LayerCaptureArgs.Builder(/* layer */ null); if (overlaySurfaceControl != null) { SurfaceControl[] excludeLayers = new SurfaceControl[1]; excludeLayers[0] = overlaySurfaceControl; layerCaptureArgsBuilder.setExcludeLayers(excludeLayers); } final Bitmap bitmap = mWindowManagerService.captureTaskBitmap(taskId, layerCaptureArgsBuilder); if (bitmap == null) { Slog.w(TAG, "Could not get bitmap for id: " + taskId); callback.complete(GameScreenshotResult.createInternalErrorResult()); Loading services/core/java/com/android/server/wm/WindowManagerService.java +11 −7 Original line number Diff line number Diff line Loading @@ -3855,14 +3855,20 @@ public class WindowManagerService extends IWindowManager.Stub } /** * Generates and returns an up-to-date {@link Bitmap} for the specified taskId. The returned * bitmap will be full size and will not include any secure content. * Generates and returns an up-to-date {@link Bitmap} for the specified taskId. * * @param taskId The task ID of the task for which a snapshot is requested. * @param taskId The task ID of the task for which a Bitmap is requested. * @param layerCaptureArgsBuilder A {@link SurfaceControl.LayerCaptureArgs.Builder} with * arguments for how to capture the Bitmap. The caller can * specify any arguments, but this method will ensure that the * specified task's SurfaceControl is used and the crop is set to * the bounds of that task. * @return The Bitmap, or null if no task with the specified ID can be found or the bitmap could * not be generated. */ @Nullable public Bitmap captureTaskBitmap(int taskId) { @Nullable public Bitmap captureTaskBitmap(int taskId, @NonNull SurfaceControl.LayerCaptureArgs.Builder layerCaptureArgsBuilder) { if (mTaskSnapshotController.shouldDisableSnapshots()) { return null; } Loading @@ -3876,9 +3882,7 @@ public class WindowManagerService extends IWindowManager.Stub task.getBounds(mTmpRect); final SurfaceControl sc = task.getSurfaceControl(); final SurfaceControl.ScreenshotHardwareBuffer buffer = SurfaceControl.captureLayers( new SurfaceControl.LayerCaptureArgs.Builder(sc) .setSourceCrop(mTmpRect) .build()); layerCaptureArgsBuilder.setLayer(sc).setSourceCrop(mTmpRect).build()); if (buffer == null) { Slog.w(TAG, "Could not get screenshot buffer for taskId: " + taskId); return null; Loading services/tests/mockingservicestests/src/com/android/server/app/GameServiceProviderInstanceImplTest.java +18 −2 Original line number Diff line number Diff line Loading @@ -60,6 +60,7 @@ import android.service.games.IGameServiceController; import android.service.games.IGameSession; import android.service.games.IGameSessionController; import android.service.games.IGameSessionService; import android.view.SurfaceControl; import android.view.SurfaceControlViewHost.SurfacePackage; import androidx.test.filters.SmallTest; Loading Loading @@ -746,6 +747,11 @@ public final class GameServiceProviderInstanceImplTest { mockPermissionGranted(Manifest.permission.MANAGE_GAME_ACTIVITY); mFakeGameService.requestCreateGameSession(10); FakeGameSession gameSession10 = new FakeGameSession(); SurfacePackage mockOverlaySurfacePackage = Mockito.mock(SurfacePackage.class); mFakeGameSessionService.removePendingFutureForTaskId(10) .complete(new CreateGameSessionResult(gameSession10, mockOverlaySurfacePackage)); IGameSessionController gameSessionController = getOnlyElement( mFakeGameSessionService.getCapturedCreateInvocations()).mGameSessionController; AndroidFuture<GameScreenshotResult> resultFuture = new AndroidFuture<>(); Loading @@ -754,18 +760,28 @@ public final class GameServiceProviderInstanceImplTest { GameScreenshotResult result = resultFuture.get(); assertEquals(GameScreenshotResult.GAME_SCREENSHOT_ERROR_INTERNAL_ERROR, result.getStatus()); verify(mMockWindowManagerService).captureTaskBitmap(10); verify(mMockWindowManagerService).captureTaskBitmap(eq(10), any()); } @Test public void takeScreenshot_success() throws Exception { when(mMockWindowManagerService.captureTaskBitmap(10)).thenReturn(TEST_BITMAP); SurfaceControl mockOverlaySurfaceControl = Mockito.mock(SurfaceControl.class); SurfaceControl[] excludeLayers = new SurfaceControl[1]; excludeLayers[0] = mockOverlaySurfaceControl; when(mMockWindowManagerService.captureTaskBitmap(eq(10), any())).thenReturn(TEST_BITMAP); mGameServiceProviderInstance.start(); startTask(10, GAME_A_MAIN_ACTIVITY); mockPermissionGranted(Manifest.permission.MANAGE_GAME_ACTIVITY); mFakeGameService.requestCreateGameSession(10); FakeGameSession gameSession10 = new FakeGameSession(); SurfacePackage mockOverlaySurfacePackage = Mockito.mock(SurfacePackage.class); when(mockOverlaySurfacePackage.getSurfaceControl()).thenReturn(mockOverlaySurfaceControl); mFakeGameSessionService.removePendingFutureForTaskId(10) .complete(new CreateGameSessionResult(gameSession10, mockOverlaySurfacePackage)); IGameSessionController gameSessionController = getOnlyElement( mFakeGameSessionService.getCapturedCreateInvocations()).mGameSessionController; AndroidFuture<GameScreenshotResult> resultFuture = new AndroidFuture<>(); Loading Loading
services/core/java/com/android/server/app/GameServiceProviderInstanceImpl.java +20 −6 Original line number Diff line number Diff line Loading @@ -43,6 +43,7 @@ import android.service.games.IGameSession; import android.service.games.IGameSessionController; import android.service.games.IGameSessionService; import android.util.Slog; import android.view.SurfaceControl; import android.view.SurfaceControlViewHost.SurfacePackage; import com.android.internal.annotations.GuardedBy; Loading Loading @@ -237,7 +238,7 @@ final class GameServiceProviderInstanceImpl implements GameServiceProviderInstan mTaskSystemBarsVisibilityListener); for (GameSessionRecord gameSessionRecord : mGameSessions.values()) { destroyGameSessionFromRecord(gameSessionRecord); destroyGameSessionFromRecordLocked(gameSessionRecord); } mGameSessions.clear(); Loading Loading @@ -510,10 +511,11 @@ final class GameServiceProviderInstanceImpl implements GameServiceProviderInstan } return; } destroyGameSessionFromRecord(gameSessionRecord); destroyGameSessionFromRecordLocked(gameSessionRecord); } private void destroyGameSessionFromRecord(@NonNull GameSessionRecord gameSessionRecord) { @GuardedBy("mLock") private void destroyGameSessionFromRecordLocked(@NonNull GameSessionRecord gameSessionRecord) { SurfacePackage surfacePackage = gameSessionRecord.getSurfacePackage(); if (surfacePackage != null) { try { Loading Loading @@ -586,17 +588,29 @@ final class GameServiceProviderInstanceImpl implements GameServiceProviderInstan @VisibleForTesting void takeScreenshot(int taskId, @NonNull AndroidFuture callback) { GameSessionRecord gameSessionRecord; synchronized (mLock) { boolean isTaskAssociatedWithGameSession = mGameSessions.containsKey(taskId); if (!isTaskAssociatedWithGameSession) { gameSessionRecord = mGameSessions.get(taskId); if (gameSessionRecord == null) { Slog.w(TAG, "No game session found for id: " + taskId); callback.complete(GameScreenshotResult.createInternalErrorResult()); return; } } final SurfacePackage overlaySurfacePackage = gameSessionRecord.getSurfacePackage(); final SurfaceControl overlaySurfaceControl = overlaySurfacePackage != null ? overlaySurfacePackage.getSurfaceControl() : null; mBackgroundExecutor.execute(() -> { final Bitmap bitmap = mWindowManagerService.captureTaskBitmap(taskId); final SurfaceControl.LayerCaptureArgs.Builder layerCaptureArgsBuilder = new SurfaceControl.LayerCaptureArgs.Builder(/* layer */ null); if (overlaySurfaceControl != null) { SurfaceControl[] excludeLayers = new SurfaceControl[1]; excludeLayers[0] = overlaySurfaceControl; layerCaptureArgsBuilder.setExcludeLayers(excludeLayers); } final Bitmap bitmap = mWindowManagerService.captureTaskBitmap(taskId, layerCaptureArgsBuilder); if (bitmap == null) { Slog.w(TAG, "Could not get bitmap for id: " + taskId); callback.complete(GameScreenshotResult.createInternalErrorResult()); Loading
services/core/java/com/android/server/wm/WindowManagerService.java +11 −7 Original line number Diff line number Diff line Loading @@ -3855,14 +3855,20 @@ public class WindowManagerService extends IWindowManager.Stub } /** * Generates and returns an up-to-date {@link Bitmap} for the specified taskId. The returned * bitmap will be full size and will not include any secure content. * Generates and returns an up-to-date {@link Bitmap} for the specified taskId. * * @param taskId The task ID of the task for which a snapshot is requested. * @param taskId The task ID of the task for which a Bitmap is requested. * @param layerCaptureArgsBuilder A {@link SurfaceControl.LayerCaptureArgs.Builder} with * arguments for how to capture the Bitmap. The caller can * specify any arguments, but this method will ensure that the * specified task's SurfaceControl is used and the crop is set to * the bounds of that task. * @return The Bitmap, or null if no task with the specified ID can be found or the bitmap could * not be generated. */ @Nullable public Bitmap captureTaskBitmap(int taskId) { @Nullable public Bitmap captureTaskBitmap(int taskId, @NonNull SurfaceControl.LayerCaptureArgs.Builder layerCaptureArgsBuilder) { if (mTaskSnapshotController.shouldDisableSnapshots()) { return null; } Loading @@ -3876,9 +3882,7 @@ public class WindowManagerService extends IWindowManager.Stub task.getBounds(mTmpRect); final SurfaceControl sc = task.getSurfaceControl(); final SurfaceControl.ScreenshotHardwareBuffer buffer = SurfaceControl.captureLayers( new SurfaceControl.LayerCaptureArgs.Builder(sc) .setSourceCrop(mTmpRect) .build()); layerCaptureArgsBuilder.setLayer(sc).setSourceCrop(mTmpRect).build()); if (buffer == null) { Slog.w(TAG, "Could not get screenshot buffer for taskId: " + taskId); return null; Loading
services/tests/mockingservicestests/src/com/android/server/app/GameServiceProviderInstanceImplTest.java +18 −2 Original line number Diff line number Diff line Loading @@ -60,6 +60,7 @@ import android.service.games.IGameServiceController; import android.service.games.IGameSession; import android.service.games.IGameSessionController; import android.service.games.IGameSessionService; import android.view.SurfaceControl; import android.view.SurfaceControlViewHost.SurfacePackage; import androidx.test.filters.SmallTest; Loading Loading @@ -746,6 +747,11 @@ public final class GameServiceProviderInstanceImplTest { mockPermissionGranted(Manifest.permission.MANAGE_GAME_ACTIVITY); mFakeGameService.requestCreateGameSession(10); FakeGameSession gameSession10 = new FakeGameSession(); SurfacePackage mockOverlaySurfacePackage = Mockito.mock(SurfacePackage.class); mFakeGameSessionService.removePendingFutureForTaskId(10) .complete(new CreateGameSessionResult(gameSession10, mockOverlaySurfacePackage)); IGameSessionController gameSessionController = getOnlyElement( mFakeGameSessionService.getCapturedCreateInvocations()).mGameSessionController; AndroidFuture<GameScreenshotResult> resultFuture = new AndroidFuture<>(); Loading @@ -754,18 +760,28 @@ public final class GameServiceProviderInstanceImplTest { GameScreenshotResult result = resultFuture.get(); assertEquals(GameScreenshotResult.GAME_SCREENSHOT_ERROR_INTERNAL_ERROR, result.getStatus()); verify(mMockWindowManagerService).captureTaskBitmap(10); verify(mMockWindowManagerService).captureTaskBitmap(eq(10), any()); } @Test public void takeScreenshot_success() throws Exception { when(mMockWindowManagerService.captureTaskBitmap(10)).thenReturn(TEST_BITMAP); SurfaceControl mockOverlaySurfaceControl = Mockito.mock(SurfaceControl.class); SurfaceControl[] excludeLayers = new SurfaceControl[1]; excludeLayers[0] = mockOverlaySurfaceControl; when(mMockWindowManagerService.captureTaskBitmap(eq(10), any())).thenReturn(TEST_BITMAP); mGameServiceProviderInstance.start(); startTask(10, GAME_A_MAIN_ACTIVITY); mockPermissionGranted(Manifest.permission.MANAGE_GAME_ACTIVITY); mFakeGameService.requestCreateGameSession(10); FakeGameSession gameSession10 = new FakeGameSession(); SurfacePackage mockOverlaySurfacePackage = Mockito.mock(SurfacePackage.class); when(mockOverlaySurfacePackage.getSurfaceControl()).thenReturn(mockOverlaySurfaceControl); mFakeGameSessionService.removePendingFutureForTaskId(10) .complete(new CreateGameSessionResult(gameSession10, mockOverlaySurfacePackage)); IGameSessionController gameSessionController = getOnlyElement( mFakeGameSessionService.getCapturedCreateInvocations()).mGameSessionController; AndroidFuture<GameScreenshotResult> resultFuture = new AndroidFuture<>(); Loading