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

Commit be78c088 authored by Tyler Lacey's avatar Tyler Lacey
Browse files

Exclude game session overlay from game screenshots

Test: Unit and manual testing
Bug: 215583568
Bug: 202414447
Bug: 202417255
CTS-Coverage-Bug: 206128693
Change-Id: Ifc809772616688c790445f68fe31165395ee5e8b
parent 0cd1d1d4
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<>();