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

Commit e7b9879b authored by Xiang Wang's avatar Xiang Wang Committed by Peiyong Lin
Browse files

Add MANAGE_GAME_ACTIVITY permission

Add permission enforcement to GameSessionController#takeScreenshot

Bug: 216344350
Test: atest GameServiceProviderInstanceImplTest
Change-Id: Ibf0a267f96b02a98d77887766d8bd0867c25eb53
parent 9c6a9281
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -165,6 +165,7 @@ package android {
    field public static final String MANAGE_DEBUGGING = "android.permission.MANAGE_DEBUGGING";
    field public static final String MANAGE_DEVICE_ADMINS = "android.permission.MANAGE_DEVICE_ADMINS";
    field public static final String MANAGE_FACTORY_RESET_PROTECTION = "android.permission.MANAGE_FACTORY_RESET_PROTECTION";
    field public static final String MANAGE_GAME_ACTIVITY = "android.permission.MANAGE_GAME_ACTIVITY";
    field public static final String MANAGE_GAME_MODE = "android.permission.MANAGE_GAME_MODE";
    field public static final String MANAGE_HOTWORD_DETECTION = "android.permission.MANAGE_HOTWORD_DETECTION";
    field public static final String MANAGE_IPSEC_TUNNELS = "android.permission.MANAGE_IPSEC_TUNNELS";
@@ -10855,7 +10856,7 @@ package android.service.games {
    method public void onCreate();
    method public void onDestroy();
    method public void onGameTaskFocusChanged(boolean);
    method @RequiresPermission(android.Manifest.permission.FORCE_STOP_PACKAGES) public final boolean restartGame();
    method @RequiresPermission(android.Manifest.permission.MANAGE_GAME_ACTIVITY) public final boolean restartGame();
    method public void setTaskOverlayView(@NonNull android.view.View, @NonNull android.view.ViewGroup.LayoutParams);
    method public void takeScreenshot(@NonNull java.util.concurrent.Executor, @NonNull android.service.games.GameSession.ScreenshotCallback);
  }
+1 −1
Original line number Diff line number Diff line
@@ -278,7 +278,7 @@ public abstract class GameSession {
     *
     * @return {@code true} if the game was successfully restarted; otherwise, {@code false}.
     */
    @RequiresPermission(android.Manifest.permission.FORCE_STOP_PACKAGES)
    @RequiresPermission(android.Manifest.permission.MANAGE_GAME_ACTIVITY)
    public final boolean restartGame() {
        try {
            mGameSessionController.restartGame(mTaskId);
+6 −0
Original line number Diff line number Diff line
@@ -6150,6 +6150,12 @@
    <permission android:name="android.permission.ACCESS_FPS_COUNTER"
                android:protectionLevel="signature|privileged" />

    <!-- @SystemApi Allows the GameService provider to create GameSession and call GameSession
                    APIs and overlay a view on top of the game's Activity.
     @hide -->
    <permission android:name="android.permission.MANAGE_GAME_ACTIVITY"
                android:protectionLevel="signature|privileged" />

    <!-- @SystemApi Allows the holder to register callbacks to inform the RebootReadinessManager
         when they are performing reboot-blocking work.
         @hide -->
+5 −4
Original line number Diff line number Diff line
@@ -116,9 +116,10 @@ final class GameServiceProviderInstanceImpl implements GameServiceProviderInstan
                    });
                }

                @RequiresPermission(android.Manifest.permission.FORCE_STOP_PACKAGES)
                @Override
                @RequiresPermission(Manifest.permission.MANAGE_GAME_ACTIVITY)
                public void restartGame(int taskId) {
                    mContext.enforceCallingPermission(Manifest.permission.FORCE_STOP_PACKAGES,
                    mContext.enforceCallingPermission(Manifest.permission.MANAGE_GAME_ACTIVITY,
                            "restartGame()");
                    mBackgroundExecutor.execute(() -> {
                        GameServiceProviderInstanceImpl.this.restartGame(taskId);
@@ -372,12 +373,12 @@ final class GameServiceProviderInstanceImpl implements GameServiceProviderInstan
                        }, mBackgroundExecutor);

        AndroidFuture<Void> unusedPostCreateGameSessionFuture =
                mGameSessionServiceConnector.post(gameService -> {
                mGameSessionServiceConnector.post(gameSessionService -> {
                    CreateGameSessionRequest createGameSessionRequest =
                            new CreateGameSessionRequest(
                                    taskId,
                                    existingGameSessionRecord.getComponentName().getPackageName());
                    gameService.create(
                    gameSessionService.create(
                            mGameSessionController,
                            createGameSessionRequest,
                            gameSessionViewHostConfiguration,
+56 −8
Original line number Diff line number Diff line
@@ -26,6 +26,7 @@ import static com.google.common.collect.Iterables.getOnlyElement;
import static com.google.common.truth.Truth.assertThat;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThrows;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
@@ -33,6 +34,7 @@ import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.verifyZeroInteractions;

import android.Manifest;
import android.annotation.Nullable;
import android.app.ActivityManager.RunningTaskInfo;
import android.app.ActivityTaskManager;
@@ -83,6 +85,7 @@ import org.mockito.quality.Strictness;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Objects;


/**
@@ -121,7 +124,7 @@ public final class GameServiceProviderInstanceImplTest {
    private WindowManagerInternal mMockWindowManagerInternal;
    @Mock
    private IActivityManager mMockActivityManager;
    private FakeContext mFakeContext;
    private MockContext mMockContext;
    private FakeGameClassifier mFakeGameClassifier;
    private FakeGameService mFakeGameService;
    private FakeServiceConnector<IGameService> mFakeGameServiceConnector;
@@ -140,7 +143,7 @@ public final class GameServiceProviderInstanceImplTest {
                .strictness(Strictness.LENIENT)
                .startMocking();

        mFakeContext = new FakeContext(InstrumentationRegistry.getInstrumentation().getContext());
        mMockContext = new MockContext(InstrumentationRegistry.getInstrumentation().getContext());

        mFakeGameClassifier = new FakeGameClassifier();
        mFakeGameClassifier.recordGamePackage(GAME_A_PACKAGE);
@@ -169,7 +172,7 @@ public final class GameServiceProviderInstanceImplTest {
        mGameServiceProviderInstance = new GameServiceProviderInstanceImpl(
                new UserHandle(USER_ID),
                ConcurrentUtils.DIRECT_EXECUTOR,
                mFakeContext,
                mMockContext,
                mFakeGameClassifier,
                mMockActivityManager,
                mMockActivityTaskManager,
@@ -683,6 +686,7 @@ public final class GameServiceProviderInstanceImplTest {

    @Test
    public void restartGame_taskIdAssociatedWithGame_restartsTargetGame() throws Exception {
        mockPermissionGranted(Manifest.permission.MANAGE_GAME_ACTIVITY);
        Intent launchIntent = new Intent("com.test.ACTION_LAUNCH_GAME_PACKAGE")
                .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        when(mMockPackageManager.getLaunchIntentForPackage(GAME_A_PACKAGE))
@@ -710,11 +714,12 @@ public final class GameServiceProviderInstanceImplTest {
                .mGameSessionController.restartGame(10);

        verify(mMockActivityManager).forceStopPackage(GAME_A_PACKAGE, UserHandle.USER_CURRENT);
        assertThat(mFakeContext.getLastStartedIntent()).isEqualTo(launchIntent);
        assertThat(mMockContext.getLastStartedIntent()).isEqualTo(launchIntent);
    }

    @Test
    public void restartGame_taskIdNotAssociatedWithGame_noOp() throws Exception {
        mockPermissionGranted(Manifest.permission.MANAGE_GAME_ACTIVITY);
        mGameServiceProviderInstance.start();

        startTask(10, GAME_A_MAIN_ACTIVITY);
@@ -730,7 +735,19 @@ public final class GameServiceProviderInstanceImplTest {
                .mGameSessionController.restartGame(11);

        verifyZeroInteractions(mMockActivityManager);
        assertThat(mFakeContext.getLastStartedIntent()).isNull();
        assertThat(mMockContext.getLastStartedIntent()).isNull();
    }

    @Test
    public void restartGame_failurePermissionDenied() throws Exception {
        mGameServiceProviderInstance.start();
        startTask(10, GAME_A_MAIN_ACTIVITY);
        mFakeGameService.requestCreateGameSession(10);

        IGameSessionController gameSessionController = Objects.requireNonNull(getOnlyElement(
                mFakeGameSessionService.getCapturedCreateInvocations())).mGameSessionController;
        assertThrows(SecurityException.class,
                () -> gameSessionController.restartGame(10));
    }

    private void startTask(int taskId, ComponentName componentName) {
@@ -774,6 +791,14 @@ public final class GameServiceProviderInstanceImplTest {
        }
    }

    private void mockPermissionGranted(String permission) {
        mMockContext.setPermission(permission, PackageManager.PERMISSION_GRANTED);
    }

    private void mockPermissionDenied(String permission) {
        mMockContext.setPermission(permission, PackageManager.PERMISSION_DENIED);
    }

    static final class FakeGameService extends IGameService.Stub {
        private IGameServiceController mGameServiceController;

@@ -900,13 +925,28 @@ public final class GameServiceProviderInstanceImplTest {
        }
    }

    private final class FakeContext extends ContextWrapper {
    private final class MockContext extends ContextWrapper {
        private Intent mLastStartedIntent;
        // Map of permission name -> PermissionManager.Permission_{GRANTED|DENIED} constant
        private final HashMap<String, Integer> mMockedPermissions = new HashMap<>();

        FakeContext(Context base) {
        MockContext(Context base) {
            super(base);
        }

        /**
         * Mock checks for the specified permission, and have them behave as per {@code granted}.
         *
         * <p>Passing null reverts to default behavior, which does a real permission check on the
         * test package.
         *
         * @param granted One of {@link PackageManager#PERMISSION_GRANTED} or
         *                {@link PackageManager#PERMISSION_DENIED}.
         */
        public void setPermission(String permission, Integer granted) {
            mMockedPermissions.put(permission, granted);
        }

        @Override
        public PackageManager getPackageManager() {
            return mMockPackageManager;
@@ -919,7 +959,15 @@ public final class GameServiceProviderInstanceImplTest {

        @Override
        public void enforceCallingPermission(String permission, @Nullable String message) {
            // Do nothing.
            final Integer granted = mMockedPermissions.get(permission);
            if (granted == null) {
                super.enforceCallingOrSelfPermission(permission, message);
                return;
            }

            if (!granted.equals(PackageManager.PERMISSION_GRANTED)) {
                throw new SecurityException("[Test] permission denied: " + permission);
            }
        }

        Intent getLastStartedIntent() {