Add MANAGE_GAME_ACTIVITY permission

Add permission enforcement to GameSessionController#takeScreenshot

Bug: 216344350
Test: atest GameServiceProviderInstanceImplTest
Change-Id: Ibf0a267f96b02a98d77887766d8bd0867c25eb53
This commit is contained in:
Xiang Wang
2022-01-26 17:15:31 -08:00
committed by Peiyong Lin
parent 9c6a92819b
commit e7b9879b4b
5 changed files with 70 additions and 14 deletions

View File

@@ -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);
}

View File

@@ -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);

View File

@@ -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 -->

View File

@@ -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,

View File

@@ -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() {