Loading core/api/system-current.txt +2 −1 Original line number Diff line number Diff line Loading @@ -166,6 +166,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"; Loading Loading @@ -11060,7 +11061,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); } core/java/android/service/games/GameSession.java +1 −1 Original line number Diff line number Diff line Loading @@ -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); Loading core/res/AndroidManifest.xml +6 −0 Original line number Diff line number Diff line Loading @@ -6174,6 +6174,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 --> Loading services/core/java/com/android/server/app/GameServiceProviderInstanceImpl.java +5 −4 Original line number Diff line number Diff line Loading @@ -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); Loading Loading @@ -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, Loading services/tests/mockingservicestests/src/com/android/server/app/GameServiceProviderInstanceImplTest.java +56 −8 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading Loading @@ -83,6 +85,7 @@ import org.mockito.quality.Strictness; import java.util.ArrayList; import java.util.HashMap; import java.util.Objects; /** Loading Loading @@ -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; Loading @@ -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); Loading Loading @@ -169,7 +172,7 @@ public final class GameServiceProviderInstanceImplTest { mGameServiceProviderInstance = new GameServiceProviderInstanceImpl( new UserHandle(USER_ID), ConcurrentUtils.DIRECT_EXECUTOR, mFakeContext, mMockContext, mFakeGameClassifier, mMockActivityManager, mMockActivityTaskManager, Loading Loading @@ -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)) Loading Loading @@ -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); Loading @@ -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) { Loading Loading @@ -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; Loading Loading @@ -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; Loading @@ -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() { Loading Loading
core/api/system-current.txt +2 −1 Original line number Diff line number Diff line Loading @@ -166,6 +166,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"; Loading Loading @@ -11060,7 +11061,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); }
core/java/android/service/games/GameSession.java +1 −1 Original line number Diff line number Diff line Loading @@ -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); Loading
core/res/AndroidManifest.xml +6 −0 Original line number Diff line number Diff line Loading @@ -6174,6 +6174,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 --> Loading
services/core/java/com/android/server/app/GameServiceProviderInstanceImpl.java +5 −4 Original line number Diff line number Diff line Loading @@ -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); Loading Loading @@ -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, Loading
services/tests/mockingservicestests/src/com/android/server/app/GameServiceProviderInstanceImplTest.java +56 −8 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading Loading @@ -83,6 +85,7 @@ import org.mockito.quality.Strictness; import java.util.ArrayList; import java.util.HashMap; import java.util.Objects; /** Loading Loading @@ -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; Loading @@ -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); Loading Loading @@ -169,7 +172,7 @@ public final class GameServiceProviderInstanceImplTest { mGameServiceProviderInstance = new GameServiceProviderInstanceImpl( new UserHandle(USER_ID), ConcurrentUtils.DIRECT_EXECUTOR, mFakeContext, mMockContext, mFakeGameClassifier, mMockActivityManager, mMockActivityTaskManager, Loading Loading @@ -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)) Loading Loading @@ -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); Loading @@ -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) { Loading Loading @@ -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; Loading Loading @@ -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; Loading @@ -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() { Loading