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

Commit 0f654a9f authored by Tyler Lacey's avatar Tyler Lacey Committed by Android (Google) Code Review
Browse files

Merge "Exclude game session overlay from game screenshots"

parents 62b2beb6 be78c088
Loading
Loading
Loading
Loading
+20 −6
Original line number Diff line number Diff line
@@ -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;
@@ -237,7 +238,7 @@ final class GameServiceProviderInstanceImpl implements GameServiceProviderInstan
                mTaskSystemBarsVisibilityListener);

        for (GameSessionRecord gameSessionRecord : mGameSessions.values()) {
            destroyGameSessionFromRecord(gameSessionRecord);
            destroyGameSessionFromRecordLocked(gameSessionRecord);
        }
        mGameSessions.clear();

@@ -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 {
@@ -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());
+11 −7
Original line number Diff line number Diff line
@@ -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;
        }
@@ -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;
+18 −2
Original line number Diff line number Diff line
@@ -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;
@@ -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<>();
@@ -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<>();