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
This commit is contained in:
@@ -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());
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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<>();
|
||||
|
||||
Reference in New Issue
Block a user