From d7cf4bcbecf0cd3dfa8ba9729fd95d5f47e43e35 Mon Sep 17 00:00:00 2001 From: Xiang Wang Date: Wed, 9 Nov 2022 17:19:52 -0800 Subject: [PATCH 1/3] Always return STANDARD & CUSTOM game modes as available modes Change the default game mode from UNSUPPORTED to STANDARD when config is missing etc. This will solve the ambiguity on UNSUPPORTED as it's publicly documented to always return from getGameMode when application is not a game instead. Bug: b/243448953 Test: atest GameManagerServiceTests Change-Id: I277735cccd0daf6148aa106cc31bf92f73db018a --- .../server/app/GameManagerService.java | 72 ++++++------ .../server/app/GameManagerSettings.java | 6 +- .../server/app/GameManagerServiceTests.java | 107 ++++++++++-------- 3 files changed, 98 insertions(+), 87 deletions(-) diff --git a/services/core/java/com/android/server/app/GameManagerService.java b/services/core/java/com/android/server/app/GameManagerService.java index 14601da634d99..a3413bfac49c1 100644 --- a/services/core/java/com/android/server/app/GameManagerService.java +++ b/services/core/java/com/android/server/app/GameManagerService.java @@ -598,6 +598,14 @@ public final class GameManagerService extends IGameManagerService.Stub { } } + // used to check if the override package config has any game mode config, if not, it's + // considered empty and safe to delete from settings + boolean hasActiveGameModeConfig() { + synchronized (mModeConfigLock) { + return !mModeConfigs.isEmpty(); + } + } + /** * GameModeConfiguration contains all the values for all the interventions associated with * a game mode. @@ -693,7 +701,8 @@ public final class GameManagerService extends IGameManagerService.Stub { public boolean isActive() { return (mGameMode == GameManager.GAME_MODE_STANDARD || mGameMode == GameManager.GAME_MODE_PERFORMANCE - || mGameMode == GameManager.GAME_MODE_BATTERY) + || mGameMode == GameManager.GAME_MODE_BATTERY + || mGameMode == GameManager.GAME_MODE_CUSTOM) && !willGamePerformOptimizations(mGameMode); } @@ -741,7 +750,8 @@ public final class GameManagerService extends IGameManagerService.Stub { } private int getAvailableGameModesBitfield() { - int field = 0; + int field = modeToBitmask(GameManager.GAME_MODE_CUSTOM) + | modeToBitmask(GameManager.GAME_MODE_STANDARD); synchronized (mModeConfigLock) { for (final int mode : mModeConfigs.keySet()) { field |= modeToBitmask(mode); @@ -753,13 +763,6 @@ public final class GameManagerService extends IGameManagerService.Stub { if (mPerfModeOptedIn) { field |= modeToBitmask(GameManager.GAME_MODE_PERFORMANCE); } - // The lowest bit is reserved for UNSUPPORTED, STANDARD is supported if we support any - // other mode. - if (field > 1) { - field |= modeToBitmask(GameManager.GAME_MODE_STANDARD); - } else { - field |= modeToBitmask(GameManager.GAME_MODE_UNSUPPORTED); - } return field; } @@ -881,7 +884,7 @@ public final class GameManagerService extends IGameManagerService.Stub { private final class LocalService extends GameManagerInternal { @Override public float getResolutionScalingFactor(String packageName, int userId) { - final int gameMode = getGameModeFromSettings(packageName, userId); + final int gameMode = getGameModeFromSettingsUnchecked(packageName, userId); return getResolutionScalingFactorInternal(packageName, gameMode, userId); } } @@ -960,7 +963,7 @@ public final class GameManagerService extends IGameManagerService.Stub { config = mConfigs.get(packageName); } if (config == null) { - return new int[]{}; + return new int[]{GameManager.GAME_MODE_STANDARD, GameManager.GAME_MODE_CUSTOM}; } return config.getAvailableGameModes(); } @@ -986,12 +989,13 @@ public final class GameManagerService extends IGameManagerService.Stub { return getAvailableGameModesUnchecked(packageName); } - private @GameMode int getGameModeFromSettings(String packageName, @UserIdInt int userId) { + private @GameMode int getGameModeFromSettingsUnchecked(String packageName, + @UserIdInt int userId) { synchronized (mLock) { if (!mSettings.containsKey(userId)) { Slog.d(TAG, "User ID '" + userId + "' does not have a Game Mode" - + " selected for package: '" + packageName + "'"); - return GameManager.GAME_MODE_UNSUPPORTED; + + " selected for package: '" + packageName + "'"); + return GameManager.GAME_MODE_STANDARD; } return mSettings.get(userId).getGameModeLocked(packageName); @@ -1024,12 +1028,12 @@ public final class GameManagerService extends IGameManagerService.Stub { // return a value if the package name is valid. Next, check if the caller has the necessary // permission and return a value. Do this check last, since it can throw an exception. if (isValidPackageName(packageName, userId)) { - return getGameModeFromSettings(packageName, userId); + return getGameModeFromSettingsUnchecked(packageName, userId); } // Since the package name doesn't match, check the caller has the necessary permission. checkPermission(Manifest.permission.MANAGE_GAME_MODE); - return getGameModeFromSettings(packageName, userId); + return getGameModeFromSettingsUnchecked(packageName, userId); } /** @@ -1054,7 +1058,7 @@ public final class GameManagerService extends IGameManagerService.Stub { return null; } - final @GameMode int activeGameMode = getGameModeFromSettings(packageName, userId); + final @GameMode int activeGameMode = getGameModeFromSettingsUnchecked(packageName, userId); final GamePackageConfiguration config = getConfig(packageName, userId); if (config != null) { final @GameMode int[] optedInGameModes = config.getOptedInGameModes(); @@ -1077,7 +1081,10 @@ public final class GameManagerService extends IGameManagerService.Stub { } return gameModeInfoBuilder.build(); } else { - return new GameModeInfo.Builder().setActiveGameMode(activeGameMode).build(); + return new GameModeInfo.Builder() + .setActiveGameMode(activeGameMode) + .setAvailableGameModes(getAvailableGameModesUnchecked(packageName)) + .build(); } } @@ -1474,7 +1481,8 @@ public final class GameManagerService extends IGameManagerService.Stub { final GamePackageConfiguration packageConfig = getConfig(packageName, userId); if (gameMode == GameManager.GAME_MODE_STANDARD || gameMode == GameManager.GAME_MODE_UNSUPPORTED || packageConfig == null - || packageConfig.willGamePerformOptimizations(gameMode)) { + || packageConfig.willGamePerformOptimizations(gameMode) + || packageConfig.getGameModeConfiguration(gameMode) == null) { resetFps(packageName, userId); // resolution scaling does not need to be reset as it's now read dynamically on game // restart, see #getResolutionScalingFactor and CompatModePackages#getCompatScale. @@ -1562,13 +1570,9 @@ public final class GameManagerService extends IGameManagerService.Stub { if (!bitFieldContainsModeBitmask(modesBitfield, gameModeToReset)) { return; } - // if the game mode to reset is the only mode other than standard mode or there - // is device config, the entire package config override is removed. - if (Integer.bitCount(modesBitfield) <= 2 || deviceConfig == null) { + configOverride.removeModeConfig(gameModeToReset); + if (!configOverride.hasActiveGameModeConfig()) { settings.removeConfigOverride(packageName); - } else { - // otherwise we reset the mode by removing the game mode config override - configOverride.removeModeConfig(gameModeToReset); } } else { settings.removeConfigOverride(packageName); @@ -1596,20 +1600,12 @@ public final class GameManagerService extends IGameManagerService.Stub { // want to check if we support selectable game modes modesBitfield &= ~modeToBitmask(GameManager.GAME_MODE_UNSUPPORTED); if (!bitFieldContainsModeBitmask(modesBitfield, gameMode)) { - if (bitFieldContainsModeBitmask(modesBitfield, - GameManager.GAME_MODE_STANDARD)) { - // If the current set mode isn't supported, - // but we support STANDARD, then set the mode to STANDARD. - newGameMode = GameManager.GAME_MODE_STANDARD; - } else { - // If we don't support any game modes, then set to UNSUPPORTED - newGameMode = GameManager.GAME_MODE_UNSUPPORTED; - } + // always default to STANDARD if there is no mode config + newGameMode = GameManager.GAME_MODE_STANDARD; } - } else if (gameMode != GameManager.GAME_MODE_UNSUPPORTED) { - // If we have no config for the package, but the configured mode is not - // UNSUPPORTED, then set to UNSUPPORTED - newGameMode = GameManager.GAME_MODE_UNSUPPORTED; + } else { + // always default to STANDARD if there is no package config + newGameMode = GameManager.GAME_MODE_STANDARD; } return newGameMode; } diff --git a/services/core/java/com/android/server/app/GameManagerSettings.java b/services/core/java/com/android/server/app/GameManagerSettings.java index 1e688371fa4d8..9fe0f3669d39b 100644 --- a/services/core/java/com/android/server/app/GameManagerSettings.java +++ b/services/core/java/com/android/server/app/GameManagerSettings.java @@ -92,7 +92,7 @@ public class GameManagerSettings { if (mGameModes.containsKey(packageName)) { return mGameModes.get(packageName); } - return GameManager.GAME_MODE_UNSUPPORTED; + return GameManager.GAME_MODE_STANDARD; } /** @@ -255,7 +255,7 @@ public class GameManagerSettings { XmlUtils.skipCurrentTag(parser); return; } - int gameMode = GameManager.GAME_MODE_UNSUPPORTED; + int gameMode; try { gameMode = parser.getAttributeInt(null, ATTR_GAME_MODE); } catch (XmlPullParserException e) { @@ -282,7 +282,7 @@ public class GameManagerSettings { + type); } } - if (config.getAvailableGameModes().length > 1) { + if (config.hasActiveGameModeConfig()) { mConfigOverrides.put(name, config); } } diff --git a/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java b/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java index 09d0683bdb4e5..17ef0379281f8 100644 --- a/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java +++ b/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java @@ -444,7 +444,7 @@ public class GameManagerServiceTests { } /** - * By default game mode is not supported. + * By default game mode is set to STANDARD */ @Test public void testGameModeDefaultValue() { @@ -454,7 +454,7 @@ public class GameManagerServiceTests { startUser(gameManagerService, USER_ID_1); mockModifyGameModeGranted(); - assertEquals(GameManager.GAME_MODE_UNSUPPORTED, + assertEquals(GameManager.GAME_MODE_STANDARD, gameManagerService.getGameMode(mPackageName, USER_ID_1)); } @@ -470,7 +470,7 @@ public class GameManagerServiceTests { mockModifyGameModeGranted(); gameManagerService.setGameMode(mPackageName, GameManager.GAME_MODE_STANDARD, USER_ID_2); - assertEquals(GameManager.GAME_MODE_UNSUPPORTED, + assertEquals(GameManager.GAME_MODE_STANDARD, gameManagerService.getGameMode(mPackageName, USER_ID_2)); } @@ -486,7 +486,7 @@ public class GameManagerServiceTests { startUser(gameManagerService, USER_ID_1); gameManagerService.updateConfigsForUser(USER_ID_1, true, mPackageName); mockModifyGameModeGranted(); - assertEquals(GameManager.GAME_MODE_UNSUPPORTED, + assertEquals(GameManager.GAME_MODE_STANDARD, gameManagerService.getGameMode(mPackageName, USER_ID_1)); // We need to make sure the mode is supported before setting it. mockDeviceConfigAll(); @@ -679,7 +679,8 @@ public class GameManagerServiceTests { public void testDeviceConfigDefault() { mockDeviceConfigDefault(); mockModifyGameModeGranted(); - checkReportedAvailableGameModes(createServiceAndStartUser(USER_ID_1)); + checkReportedAvailableGameModes(createServiceAndStartUser(USER_ID_1), + GameManager.GAME_MODE_STANDARD, GameManager.GAME_MODE_CUSTOM); } /** @@ -689,7 +690,8 @@ public class GameManagerServiceTests { public void testDeviceConfigNone() { mockDeviceConfigNone(); mockModifyGameModeGranted(); - checkReportedAvailableGameModes(createServiceAndStartUser(USER_ID_1)); + checkReportedAvailableGameModes(createServiceAndStartUser(USER_ID_1), + GameManager.GAME_MODE_STANDARD, GameManager.GAME_MODE_CUSTOM); } /** @@ -699,7 +701,8 @@ public class GameManagerServiceTests { public void testDeviceConfigInvalid() { mockDeviceConfigInvalid(); mockModifyGameModeGranted(); - checkReportedAvailableGameModes(createServiceAndStartUser(USER_ID_1)); + checkReportedAvailableGameModes(createServiceAndStartUser(USER_ID_1), + GameManager.GAME_MODE_STANDARD, GameManager.GAME_MODE_CUSTOM); } /** @@ -709,7 +712,8 @@ public class GameManagerServiceTests { public void testDeviceConfigMalformed() { mockDeviceConfigMalformed(); mockModifyGameModeGranted(); - checkReportedAvailableGameModes(createServiceAndStartUser(USER_ID_1)); + checkReportedAvailableGameModes(createServiceAndStartUser(USER_ID_1), + GameManager.GAME_MODE_STANDARD, GameManager.GAME_MODE_CUSTOM); } /** @@ -727,7 +731,7 @@ public class GameManagerServiceTests { GameManager.GAME_MODE_PERFORMANCE, "120", "0.3"); checkReportedAvailableGameModes(gameManagerService, GameManager.GAME_MODE_PERFORMANCE, - GameManager.GAME_MODE_STANDARD); + GameManager.GAME_MODE_STANDARD, GameManager.GAME_MODE_CUSTOM); checkDownscaling(gameManagerService, GameManager.GAME_MODE_PERFORMANCE, 0.3f); checkFps(gameManagerService, GameManager.GAME_MODE_PERFORMANCE, 120); } @@ -747,7 +751,7 @@ public class GameManagerServiceTests { GameManager.GAME_MODE_BATTERY, "60", "0.5"); checkReportedAvailableGameModes(gameManagerService, GameManager.GAME_MODE_BATTERY, - GameManager.GAME_MODE_STANDARD); + GameManager.GAME_MODE_STANDARD, GameManager.GAME_MODE_CUSTOM); checkDownscaling(gameManagerService, GameManager.GAME_MODE_BATTERY, 0.5f); checkFps(gameManagerService, GameManager.GAME_MODE_BATTERY, 60); } @@ -769,7 +773,8 @@ public class GameManagerServiceTests { GameManager.GAME_MODE_BATTERY, "60", "0.5"); checkReportedAvailableGameModes(gameManagerService, GameManager.GAME_MODE_PERFORMANCE, - GameManager.GAME_MODE_BATTERY, GameManager.GAME_MODE_STANDARD); + GameManager.GAME_MODE_BATTERY, GameManager.GAME_MODE_STANDARD, + GameManager.GAME_MODE_CUSTOM); checkDownscaling(gameManagerService, GameManager.GAME_MODE_PERFORMANCE, 0.3f); checkFps(gameManagerService, GameManager.GAME_MODE_PERFORMANCE, 120); checkDownscaling(gameManagerService, GameManager.GAME_MODE_BATTERY, 0.5f); @@ -863,7 +868,7 @@ public class GameManagerServiceTests { GameManager.GAME_MODE_PERFORMANCE); checkReportedAvailableGameModes(gameManagerService, GameManager.GAME_MODE_PERFORMANCE, - GameManager.GAME_MODE_STANDARD); + GameManager.GAME_MODE_STANDARD, GameManager.GAME_MODE_CUSTOM); checkDownscaling(gameManagerService, GameManager.GAME_MODE_PERFORMANCE, 0.5f); checkFps(gameManagerService, GameManager.GAME_MODE_PERFORMANCE, 90); } @@ -886,7 +891,7 @@ public class GameManagerServiceTests { GameManager.GAME_MODE_BATTERY); checkReportedAvailableGameModes(gameManagerService, GameManager.GAME_MODE_BATTERY, - GameManager.GAME_MODE_STANDARD); + GameManager.GAME_MODE_STANDARD, GameManager.GAME_MODE_CUSTOM); checkDownscaling(gameManagerService, GameManager.GAME_MODE_BATTERY, 0.7f); checkFps(gameManagerService, GameManager.GAME_MODE_BATTERY, 30); } @@ -910,7 +915,8 @@ public class GameManagerServiceTests { gameManagerService.resetGameModeConfigOverride(mPackageName, USER_ID_1, -1); checkReportedAvailableGameModes(gameManagerService, GameManager.GAME_MODE_PERFORMANCE, - GameManager.GAME_MODE_BATTERY, GameManager.GAME_MODE_STANDARD); + GameManager.GAME_MODE_BATTERY, GameManager.GAME_MODE_STANDARD, + GameManager.GAME_MODE_CUSTOM); checkDownscaling(gameManagerService, GameManager.GAME_MODE_PERFORMANCE, 0.5f); checkFps(gameManagerService, GameManager.GAME_MODE_PERFORMANCE, 90); checkDownscaling(gameManagerService, GameManager.GAME_MODE_BATTERY, 0.7f); @@ -939,7 +945,8 @@ public class GameManagerServiceTests { GameManager.GAME_MODE_BATTERY); checkReportedAvailableGameModes(gameManagerService, GameManager.GAME_MODE_PERFORMANCE, - GameManager.GAME_MODE_BATTERY, GameManager.GAME_MODE_STANDARD); + GameManager.GAME_MODE_BATTERY, GameManager.GAME_MODE_STANDARD, + GameManager.GAME_MODE_CUSTOM); checkDownscaling(gameManagerService, GameManager.GAME_MODE_PERFORMANCE, 0.3f); checkFps(gameManagerService, GameManager.GAME_MODE_PERFORMANCE, 120); checkDownscaling(gameManagerService, GameManager.GAME_MODE_BATTERY, 0.7f); @@ -956,7 +963,7 @@ public class GameManagerServiceTests { mockModifyGameModeGranted(); checkReportedAvailableGameModes(createServiceAndStartUser(USER_ID_1), GameManager.GAME_MODE_PERFORMANCE, GameManager.GAME_MODE_BATTERY, - GameManager.GAME_MODE_STANDARD); + GameManager.GAME_MODE_STANDARD, GameManager.GAME_MODE_CUSTOM); } @@ -969,7 +976,8 @@ public class GameManagerServiceTests { mockDeviceConfigNone(); mockModifyGameModeGranted(); checkReportedAvailableGameModes(createServiceAndStartUser(USER_ID_1), - GameManager.GAME_MODE_BATTERY, GameManager.GAME_MODE_STANDARD); + GameManager.GAME_MODE_BATTERY, GameManager.GAME_MODE_STANDARD, + GameManager.GAME_MODE_CUSTOM); } /** @@ -981,7 +989,8 @@ public class GameManagerServiceTests { mockDeviceConfigNone(); mockModifyGameModeGranted(); checkReportedAvailableGameModes(createServiceAndStartUser(USER_ID_1), - GameManager.GAME_MODE_PERFORMANCE, GameManager.GAME_MODE_STANDARD); + GameManager.GAME_MODE_PERFORMANCE, GameManager.GAME_MODE_STANDARD, + GameManager.GAME_MODE_CUSTOM); } /** @@ -995,7 +1004,7 @@ public class GameManagerServiceTests { mockModifyGameModeGranted(); checkReportedAvailableGameModes(createServiceAndStartUser(USER_ID_1), GameManager.GAME_MODE_PERFORMANCE, GameManager.GAME_MODE_BATTERY, - GameManager.GAME_MODE_STANDARD); + GameManager.GAME_MODE_STANDARD, GameManager.GAME_MODE_CUSTOM); } /** @@ -1009,7 +1018,7 @@ public class GameManagerServiceTests { mockModifyGameModeGranted(); checkReportedAvailableGameModes(createServiceAndStartUser(USER_ID_1), GameManager.GAME_MODE_PERFORMANCE, GameManager.GAME_MODE_BATTERY, - GameManager.GAME_MODE_STANDARD); + GameManager.GAME_MODE_STANDARD, GameManager.GAME_MODE_CUSTOM); } /** @@ -1181,7 +1190,7 @@ public class GameManagerServiceTests { /** * Ensure that, if a game no longer supports any game modes, we set the game mode to - * UNSUPPORTED + * STANDARD */ @Test public void testUnsetInvalidGameMode() throws Exception { @@ -1192,7 +1201,7 @@ public class GameManagerServiceTests { startUser(gameManagerService, USER_ID_1); gameManagerService.setGameMode(mPackageName, GameManager.GAME_MODE_PERFORMANCE, USER_ID_1); gameManagerService.updateConfigsForUser(USER_ID_1, true, mPackageName); - assertEquals(GameManager.GAME_MODE_UNSUPPORTED, + assertEquals(GameManager.GAME_MODE_STANDARD, gameManagerService.getGameMode(mPackageName, USER_ID_1)); } @@ -1260,7 +1269,8 @@ public class GameManagerServiceTests { assertTrue(gameModeInfo.isFpsOverrideAllowed()); checkReportedAvailableGameModes(gameManagerService, GameManager.GAME_MODE_PERFORMANCE, - GameManager.GAME_MODE_BATTERY, GameManager.GAME_MODE_STANDARD); + GameManager.GAME_MODE_BATTERY, GameManager.GAME_MODE_STANDARD, + GameManager.GAME_MODE_CUSTOM); checkReportedOptedInGameModes(gameManagerService); assertEquals(new GameModeConfiguration.Builder() @@ -1286,7 +1296,8 @@ public class GameManagerServiceTests { assertEquals(GameManager.GAME_MODE_BATTERY, gameModeInfo.getActiveGameMode()); checkReportedAvailableGameModes(gameManagerService, - GameManager.GAME_MODE_BATTERY, GameManager.GAME_MODE_STANDARD); + GameManager.GAME_MODE_BATTERY, GameManager.GAME_MODE_STANDARD, + GameManager.GAME_MODE_CUSTOM); checkReportedOptedInGameModes(gameManagerService); assertNotNull(gameModeInfo.getGameModeConfiguration(GameManager.GAME_MODE_BATTERY)); @@ -1305,7 +1316,8 @@ public class GameManagerServiceTests { assertEquals(GameManager.GAME_MODE_PERFORMANCE, gameModeInfo.getActiveGameMode()); checkReportedAvailableGameModes(gameManagerService, - GameManager.GAME_MODE_PERFORMANCE, GameManager.GAME_MODE_STANDARD); + GameManager.GAME_MODE_PERFORMANCE, GameManager.GAME_MODE_STANDARD, + GameManager.GAME_MODE_CUSTOM); checkReportedOptedInGameModes(gameManagerService); assertNotNull(gameModeInfo.getGameModeConfiguration(GameManager.GAME_MODE_PERFORMANCE)); @@ -1313,7 +1325,7 @@ public class GameManagerServiceTests { } @Test - public void testGetGameModeInfoWithUnsupportedGameMode() { + public void testGetGameModeInfoWithDefaultGameModes() { mockDeviceConfigNone(); mockModifyGameModeGranted(); GameManagerService gameManagerService = @@ -1321,9 +1333,11 @@ public class GameManagerServiceTests { startUser(gameManagerService, USER_ID_1); GameModeInfo gameModeInfo = gameManagerService.getGameModeInfo(mPackageName, USER_ID_1); - assertEquals(GameManager.GAME_MODE_UNSUPPORTED, gameModeInfo.getActiveGameMode()); - checkReportedAvailableGameModes(gameManagerService); - + assertEquals(GameManager.GAME_MODE_STANDARD, gameModeInfo.getActiveGameMode()); + checkReportedAvailableGameModes(gameManagerService, GameManager.GAME_MODE_STANDARD, + GameManager.GAME_MODE_CUSTOM); + assertNull(gameModeInfo.getGameModeConfiguration(GameManager.GAME_MODE_CUSTOM)); + assertNull(gameModeInfo.getGameModeConfiguration(GameManager.GAME_MODE_STANDARD)); assertNull(gameModeInfo.getGameModeConfiguration(GameManager.GAME_MODE_BATTERY)); assertNull(gameModeInfo.getGameModeConfiguration(GameManager.GAME_MODE_PERFORMANCE)); } @@ -1363,7 +1377,7 @@ public class GameManagerServiceTests { GameModeInfo gameModeInfo) { checkReportedAvailableGameModes(gameManagerService, GameManager.GAME_MODE_PERFORMANCE, GameManager.GAME_MODE_BATTERY, - GameManager.GAME_MODE_STANDARD); + GameManager.GAME_MODE_STANDARD, GameManager.GAME_MODE_CUSTOM); checkReportedOptedInGameModes(gameManagerService, GameManager.GAME_MODE_PERFORMANCE, GameManager.GAME_MODE_BATTERY); assertTrue(gameModeInfo.isFpsOverrideAllowed()); @@ -1381,7 +1395,7 @@ public class GameManagerServiceTests { assertEquals(GameManager.GAME_MODE_STANDARD, gameModeInfo.getActiveGameMode()); checkReportedAvailableGameModes(gameManagerService, GameManager.GAME_MODE_BATTERY, - GameManager.GAME_MODE_STANDARD); + GameManager.GAME_MODE_STANDARD, GameManager.GAME_MODE_CUSTOM); checkReportedOptedInGameModes(gameManagerService, GameManager.GAME_MODE_BATTERY); assertNull(gameModeInfo.getGameModeConfiguration(GameManager.GAME_MODE_BATTERY)); @@ -1399,7 +1413,8 @@ public class GameManagerServiceTests { assertEquals(GameManager.GAME_MODE_STANDARD, gameModeInfo.getActiveGameMode()); checkReportedAvailableGameModes(gameManagerService, GameManager.GAME_MODE_PERFORMANCE, - GameManager.GAME_MODE_BATTERY, GameManager.GAME_MODE_STANDARD); + GameManager.GAME_MODE_BATTERY, GameManager.GAME_MODE_STANDARD, + GameManager.GAME_MODE_CUSTOM); checkReportedOptedInGameModes(gameManagerService, GameManager.GAME_MODE_PERFORMANCE); assertNotNull(gameModeInfo.getGameModeConfiguration(GameManager.GAME_MODE_BATTERY)); @@ -1526,14 +1541,14 @@ public class GameManagerServiceTests { /* Expected fileOutput (order may vary) # user 1001: - com.android.app2 0 2 angle=0,scaling=0.5,fps=90 3 angle=0,scaling=0.5,fps=60 + com.android.app2 1 2 angle=0,scaling=0.5,fps=90 3 angle=0,scaling=0.5,fps=60 com.android.app1 1 2 angle=0,scaling=0.5,fps=90 3 angle=0,scaling=0.7,fps=30 - com.android.app0 0 2 angle=0,scaling=0.6,fps=120 3 angle=0,scaling=0.7,fps=30 + com.android.app0 1 2 angle=0,scaling=0.6,fps=120 3 angle=0,scaling=0.7,fps=30 # user 1002: - com.android.app2 0 2 angle=0,scaling=0.5,fps=90 3 angle=0,scaling=0.7,fps=30 + com.android.app2 1 2 angle=0,scaling=0.5,fps=90 3 angle=0,scaling=0.7,fps=30 com.android.app1 1 2 angle=0,scaling=0.5,fps=90 3 angle=0,scaling=0.7,fps=30 - com.android.app0 0 2 angle=0,scaling=0.5,fps=90 3 angle=0,scaling=0.7,fps=30 + com.android.app0 1 2 angle=0,scaling=0.5,fps=90 3 angle=0,scaling=0.7,fps=30 The current game mode would only be set to non-zero if the current user have that game installed. */ @@ -1550,7 +1565,7 @@ public class GameManagerServiceTests { assertEquals(splitLine[6], "angle=0,scaling=0.5,fps=60"); splitLine = fileOutput.get(1).split("\\s+"); assertEquals(splitLine[0], "com.android.app1"); - assertEquals(splitLine[2], "0"); + assertEquals(splitLine[2], "1"); assertEquals(splitLine[3], "2"); assertEquals(splitLine[4], "angle=0,scaling=0.5,fps=90"); assertEquals(splitLine[5], "3"); @@ -1573,7 +1588,7 @@ public class GameManagerServiceTests { splitLine = fileOutput.get(0).split("\\s+"); assertEquals(splitLine[0], "com.android.app2"); - assertEquals(splitLine[2], "0"); + assertEquals(splitLine[2], "1"); assertEquals(splitLine[3], "2"); assertEquals(splitLine[4], "angle=0,scaling=0.5,fps=90"); assertEquals(splitLine[5], "3"); @@ -1587,7 +1602,7 @@ public class GameManagerServiceTests { assertEquals(splitLine[6], "angle=0,scaling=0.7,fps=30"); splitLine = fileOutput.get(2).split("\\s+"); assertEquals(splitLine[0], "com.android.app0"); - assertEquals(splitLine[2], "0"); + assertEquals(splitLine[2], "1"); assertEquals(splitLine[3], "2"); assertEquals(splitLine[4], "angle=0,scaling=0.5,fps=90"); assertEquals(splitLine[5], "3"); @@ -1608,7 +1623,7 @@ public class GameManagerServiceTests { startUser(gameManagerService, USER_ID_2); gameManagerService.setGameMode(mPackageName, GameManager.GAME_MODE_BATTERY, USER_ID_1); checkReportedAvailableGameModes(gameManagerService, GameManager.GAME_MODE_STANDARD, - GameManager.GAME_MODE_BATTERY); + GameManager.GAME_MODE_BATTERY, GameManager.GAME_MODE_CUSTOM); assertEquals(gameManagerService.getGameMode(mPackageName, USER_ID_1), GameManager.GAME_MODE_BATTERY); @@ -1617,15 +1632,15 @@ public class GameManagerServiceTests { assertEquals(gameManagerService.getGameMode(mPackageName, USER_ID_2), GameManager.GAME_MODE_STANDARD); checkReportedAvailableGameModes(gameManagerService, GameManager.GAME_MODE_STANDARD, - GameManager.GAME_MODE_BATTERY, GameManager.GAME_MODE_PERFORMANCE); - gameManagerService.setGameMode(mPackageName, GameManager.GAME_MODE_PERFORMANCE, USER_ID_2); - gameManagerService.setGameMode(mPackageName, GameManager.GAME_MODE_BATTERY, USER_ID_1); + GameManager.GAME_MODE_BATTERY, GameManager.GAME_MODE_PERFORMANCE, + GameManager.GAME_MODE_CUSTOM); switchUser(gameManagerService, USER_ID_2, USER_ID_1); + assertEquals(gameManagerService.getGameMode(mPackageName, USER_ID_1), + GameManager.GAME_MODE_BATTERY); checkReportedAvailableGameModes(gameManagerService, GameManager.GAME_MODE_STANDARD, - GameManager.GAME_MODE_BATTERY, GameManager.GAME_MODE_PERFORMANCE); - gameManagerService.setGameMode(mPackageName, GameManager.GAME_MODE_PERFORMANCE, USER_ID_2); - gameManagerService.setGameMode(mPackageName, GameManager.GAME_MODE_BATTERY, USER_ID_1); + GameManager.GAME_MODE_BATTERY, GameManager.GAME_MODE_PERFORMANCE, + GameManager.GAME_MODE_CUSTOM); } @Test From db78635eef90224c62e5f78f8cbd04a4cdfb9011 Mon Sep 17 00:00:00 2001 From: Xiang Wang Date: Wed, 9 Nov 2022 17:54:24 -0800 Subject: [PATCH 2/3] Disallow setting UNSUPPORTED mode to avoid ambiguity Replace the UNSUPPORTED mode read from settings file with STANDARD. Bug: b/243448953 Test: atest GameManagerServiceTests GameManagerTest Change-Id: I503e98447b8f522e9df4fec87f179578d7a21f3e --- core/java/android/app/GameManager.java | 4 +++- .../src/android/app/GameManagerTests.java | 10 +--------- .../com/android/server/app/GameManagerService.java | 4 ++-- .../com/android/server/app/GameManagerSettings.java | 7 ++++++- .../server/app/GameManagerServiceSettingsTests.java | 2 +- 5 files changed, 13 insertions(+), 14 deletions(-) diff --git a/core/java/android/app/GameManager.java b/core/java/android/app/GameManager.java index 8768a02ba95e0..f92194d3ffac5 100644 --- a/core/java/android/app/GameManager.java +++ b/core/java/android/app/GameManager.java @@ -187,7 +187,9 @@ public final class GameManager { * Sets the game mode for the given package. *

* The caller must have {@link android.Manifest.permission#MANAGE_GAME_MODE}. - * + *

+ * Setting the game mode on a non-game application or setting a game to + * {@link #GAME_MODE_UNSUPPORTED} will have no effect. * @hide */ @SystemApi diff --git a/core/tests/GameManagerTests/src/android/app/GameManagerTests.java b/core/tests/GameManagerTests/src/android/app/GameManagerTests.java index 207ab2a271124..fac3a0ecdec2a 100644 --- a/core/tests/GameManagerTests/src/android/app/GameManagerTests.java +++ b/core/tests/GameManagerTests/src/android/app/GameManagerTests.java @@ -50,15 +50,11 @@ public final class GameManagerTests { mPackageName = mContext.getPackageName(); // Reset the Game Mode for the test app, since it persists across invocations. - mGameManager.setGameMode(mPackageName, GameManager.GAME_MODE_UNSUPPORTED); + mGameManager.setGameMode(mPackageName, GameManager.GAME_MODE_STANDARD); } @Test public void testPublicApiGameModeGetterSetter() { - assertEquals(GameManager.GAME_MODE_UNSUPPORTED, - mGameManager.getGameMode()); - - mGameManager.setGameMode(mPackageName, GameManager.GAME_MODE_STANDARD); assertEquals(GameManager.GAME_MODE_STANDARD, mGameManager.getGameMode()); @@ -73,10 +69,6 @@ public final class GameManagerTests { @Test public void testPrivilegedGameModeGetterSetter() { - assertEquals(GameManager.GAME_MODE_UNSUPPORTED, - mGameManager.getGameMode(mPackageName)); - - mGameManager.setGameMode(mPackageName, GameManager.GAME_MODE_STANDARD); assertEquals(GameManager.GAME_MODE_STANDARD, mGameManager.getGameMode(mPackageName)); diff --git a/services/core/java/com/android/server/app/GameManagerService.java b/services/core/java/com/android/server/app/GameManagerService.java index a3413bfac49c1..8725106aa76cc 100644 --- a/services/core/java/com/android/server/app/GameManagerService.java +++ b/services/core/java/com/android/server/app/GameManagerService.java @@ -1098,8 +1098,8 @@ public final class GameManagerService extends IGameManagerService.Stub { throws SecurityException { checkPermission(Manifest.permission.MANAGE_GAME_MODE); - if (!isPackageGame(packageName, userId)) { - // Restrict to games only. + if (!isPackageGame(packageName, userId) || gameMode == GameManager.GAME_MODE_UNSUPPORTED) { + // Restrict to games and valid game modes only. return; } diff --git a/services/core/java/com/android/server/app/GameManagerSettings.java b/services/core/java/com/android/server/app/GameManagerSettings.java index 9fe0f3669d39b..638bc4e7a0700 100644 --- a/services/core/java/com/android/server/app/GameManagerSettings.java +++ b/services/core/java/com/android/server/app/GameManagerSettings.java @@ -90,7 +90,12 @@ public class GameManagerSettings { */ int getGameModeLocked(String packageName) { if (mGameModes.containsKey(packageName)) { - return mGameModes.get(packageName); + final int gameMode = mGameModes.get(packageName); + if (gameMode == GameManager.GAME_MODE_UNSUPPORTED) { + // force replace cached UNSUPPORTED mode with STANDARD starting in U + return GameManager.GAME_MODE_STANDARD; + } + return gameMode; } return GameManager.GAME_MODE_STANDARD; } diff --git a/services/tests/servicestests/src/com/android/server/app/GameManagerServiceSettingsTests.java b/services/tests/servicestests/src/com/android/server/app/GameManagerServiceSettingsTests.java index e8dd541b05313..582c78bce54c7 100644 --- a/services/tests/servicestests/src/com/android/server/app/GameManagerServiceSettingsTests.java +++ b/services/tests/servicestests/src/com/android/server/app/GameManagerServiceSettingsTests.java @@ -192,7 +192,7 @@ public class GameManagerServiceSettingsTests { final Context context = InstrumentationRegistry.getContext(); GameManagerSettings settings = new GameManagerSettings(context.getFilesDir()); assertTrue(settings.readPersistentDataLocked()); - assertEquals(0, settings.getGameModeLocked(PACKAGE_NAME_1)); + assertEquals(1, settings.getGameModeLocked(PACKAGE_NAME_1)); assertEquals(2, settings.getGameModeLocked(PACKAGE_NAME_2)); assertEquals(3, settings.getGameModeLocked(PACKAGE_NAME_3)); From 8b9be354e12b72cad1bd67e73ab5b0d58f1e3d81 Mon Sep 17 00:00:00 2001 From: Xiang Wang Date: Fri, 11 Nov 2022 16:30:22 -0800 Subject: [PATCH 3/3] Add IGameModeListener and methods to add/remove in GameManagerService Bug: b/243448953 Test: atest GameManagerServiceTests Change-Id: I5e286ee46d118fe2e88782bc7751f4ec650323b3 --- .../java/android/app/IGameManagerService.aidl | 14 ++++ core/java/android/app/IGameModeListener.aidl | 25 +++++++ .../server/app/GameManagerService.java | 72 ++++++++++++++++++- .../server/app/GameManagerServiceTests.java | 56 +++++++++++++++ 4 files changed, 166 insertions(+), 1 deletion(-) create mode 100644 core/java/android/app/IGameModeListener.aidl diff --git a/core/java/android/app/IGameManagerService.aidl b/core/java/android/app/IGameManagerService.aidl index f695ed32e8dda..aea097d069dce 100644 --- a/core/java/android/app/IGameManagerService.aidl +++ b/core/java/android/app/IGameManagerService.aidl @@ -19,20 +19,34 @@ package android.app; import android.app.GameModeConfiguration; import android.app.GameModeInfo; import android.app.GameState; +import android.app.IGameModeListener; /** * @hide */ interface IGameManagerService { int getGameMode(String packageName, int userId); + @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.MANAGE_GAME_MODE)") void setGameMode(String packageName, int gameMode, int userId); + @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.MANAGE_GAME_MODE)") int[] getAvailableGameModes(String packageName); + @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.MANAGE_GAME_MODE)") boolean isAngleEnabled(String packageName, int userId); + @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.MANAGE_GAME_MODE)") void notifyGraphicsEnvironmentSetup(String packageName, int userId); void setGameState(String packageName, in GameState gameState, int userId); + @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.MANAGE_GAME_MODE)") GameModeInfo getGameModeInfo(String packageName, int userId); + @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.SET_GAME_SERVICE)") void setGameServiceProvider(String packageName); + @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.MANAGE_GAME_MODE)") void updateResolutionScalingFactor(String packageName, int gameMode, float scalingFactor, int userId); + @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.MANAGE_GAME_MODE)") float getResolutionScalingFactor(String packageName, int gameMode, int userId); + @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.MANAGE_GAME_MODE)") void updateCustomGameModeConfiguration(String packageName, in GameModeConfiguration gameModeConfig, int userId); + @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.MANAGE_GAME_MODE)") + void addGameModeListener(IGameModeListener gameModeListener); + @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.MANAGE_GAME_MODE)") + void removeGameModeListener(IGameModeListener gameModeListener); } diff --git a/core/java/android/app/IGameModeListener.aidl b/core/java/android/app/IGameModeListener.aidl new file mode 100644 index 0000000000000..77fcac07f7e74 --- /dev/null +++ b/core/java/android/app/IGameModeListener.aidl @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.app; + +/** @hide */ +interface IGameModeListener { + /** + * Called when the game mode of the user has changed. + */ + void onGameModeChanged(String packageName, int gameModeFrom, int gameModeTo, int userId); +} diff --git a/services/core/java/com/android/server/app/GameManagerService.java b/services/core/java/com/android/server/app/GameManagerService.java index 8725106aa76cc..b92c1635d7c61 100644 --- a/services/core/java/com/android/server/app/GameManagerService.java +++ b/services/core/java/com/android/server/app/GameManagerService.java @@ -39,6 +39,7 @@ import android.app.GameModeConfiguration; import android.app.GameModeInfo; import android.app.GameState; import android.app.IGameManagerService; +import android.app.IGameModeListener; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; @@ -58,10 +59,12 @@ import android.os.Bundle; import android.os.Environment; import android.os.FileUtils; import android.os.Handler; +import android.os.IBinder; import android.os.Looper; import android.os.Message; import android.os.PowerManagerInternal; import android.os.Process; +import android.os.RemoteException; import android.os.ResultReceiver; import android.os.ShellCallback; import android.os.UserManager; @@ -132,6 +135,7 @@ public final class GameManagerService extends IGameManagerService.Stub { private final Context mContext; private final Object mLock = new Object(); private final Object mDeviceConfigLock = new Object(); + private final Object mGameModeListenerLock = new Object(); @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) final Handler mHandler; private final PackageManager mPackageManager; @@ -145,6 +149,9 @@ public final class GameManagerService extends IGameManagerService.Stub { private final ArrayMap mSettings = new ArrayMap<>(); @GuardedBy("mDeviceConfigLock") private final ArrayMap mConfigs = new ArrayMap<>(); + // listener to caller uid map + @GuardedBy("mGameModeListenerLock") + private final ArrayMap mGameModeListeners = new ArrayMap<>(); @Nullable private final GameServiceController mGameServiceController; @@ -1102,7 +1109,7 @@ public final class GameManagerService extends IGameManagerService.Stub { // Restrict to games and valid game modes only. return; } - + int fromGameMode; synchronized (mLock) { userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(), userId, false, true, "setGameMode", @@ -1114,9 +1121,21 @@ public final class GameManagerService extends IGameManagerService.Stub { return; } GameManagerSettings userSettings = mSettings.get(userId); + fromGameMode = userSettings.getGameModeLocked(packageName); userSettings.setGameModeLocked(packageName, gameMode); } updateInterventions(packageName, gameMode, userId); + synchronized (mGameModeListenerLock) { + for (IGameModeListener listener : mGameModeListeners.keySet()) { + Binder.allowBlocking(listener.asBinder()); + try { + listener.onGameModeChanged(packageName, fromGameMode, gameMode, userId); + } catch (RemoteException ex) { + Slog.w(TAG, "Cannot notify game mode change for listener added by " + + mGameModeListeners.get(listener)); + } + } + } sendUserMessage(userId, WRITE_SETTINGS, "SET_GAME_MODE", WRITE_DELAY_MILLIS); sendUserMessage(userId, WRITE_GAME_MODE_INTERVENTION_LIST_FILE, "SET_GAME_MODE", 0 /*delayMillis*/); @@ -1341,6 +1360,57 @@ public final class GameManagerService extends IGameManagerService.Stub { + internalConfig.getScaling()); } + /** + * Adds a game mode listener. + * + * @throws SecurityException if caller doesn't have + * {@link android.Manifest.permission#MANAGE_GAME_MODE} + * permission. + */ + @Override + @RequiresPermission(Manifest.permission.MANAGE_GAME_MODE) + public void addGameModeListener(@NonNull IGameModeListener listener) { + checkPermission(Manifest.permission.MANAGE_GAME_MODE); + try { + final IBinder listenerBinder = listener.asBinder(); + listenerBinder.linkToDeath(new DeathRecipient() { + @Override public void binderDied() { + // TODO(b/258851194): add traces on binder death based listener removal + removeGameModeListenerUnchecked(listener); + listenerBinder.unlinkToDeath(this, 0 /*flags*/); + } + }, 0 /*flags*/); + synchronized (mGameModeListenerLock) { + mGameModeListeners.put(listener, Binder.getCallingUid()); + } + } catch (RemoteException ex) { + Slog.e(TAG, + "Failed to link death recipient for IGameModeListener from caller " + + Binder.getCallingUid() + ", abandoned its listener registration", ex); + } + } + + /** + * Removes a game mode listener. + * + * @throws SecurityException if caller doesn't have + * {@link android.Manifest.permission#MANAGE_GAME_MODE} + * permission. + */ + @Override + @RequiresPermission(Manifest.permission.MANAGE_GAME_MODE) + public void removeGameModeListener(@NonNull IGameModeListener listener) { + // TODO(b/258851194): add traces on manual listener removal + checkPermission(Manifest.permission.MANAGE_GAME_MODE); + removeGameModeListenerUnchecked(listener); + } + + private void removeGameModeListenerUnchecked(IGameModeListener listener) { + synchronized (mGameModeListenerLock) { + mGameModeListeners.remove(listener); + } + } + /** * Notified when boot is completed. */ diff --git a/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java b/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java index 17ef0379281f8..dc77762795c7e 100644 --- a/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java +++ b/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java @@ -33,6 +33,7 @@ import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.any; import static org.mockito.Mockito.eq; import static org.mockito.Mockito.never; +import static org.mockito.Mockito.reset; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -43,6 +44,7 @@ import android.app.GameManager; import android.app.GameModeConfiguration; import android.app.GameModeInfo; import android.app.GameState; +import android.app.IGameModeListener; import android.content.BroadcastReceiver; import android.content.Context; import android.content.ContextWrapper; @@ -57,7 +59,9 @@ import android.content.res.Resources; import android.content.res.XmlResourceParser; import android.hardware.power.Mode; import android.os.Bundle; +import android.os.IBinder; import android.os.PowerManagerInternal; +import android.os.RemoteException; import android.os.UserManager; import android.os.test.TestLooper; import android.platform.test.annotations.Presubmit; @@ -74,7 +78,9 @@ import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; import org.mockito.ArgumentMatchers; +import org.mockito.Captor; import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.MockitoSession; @@ -110,6 +116,9 @@ public class GameManagerServiceTests { private UserManager mMockUserManager; private BroadcastReceiver mShutDownActionReceiver; + @Captor + ArgumentCaptor mDeathRecipientCaptor; + // Stolen from ConnectivityServiceTest.MockContext class MockContext extends ContextWrapper { private static final String TAG = "MockContext"; @@ -1874,6 +1883,53 @@ public class GameManagerServiceTests { ArgumentMatchers.eq(0.0f)); } + @Test + public void testAddGameModeListener() throws RemoteException { + GameManagerService gameManagerService = + new GameManagerService(mMockContext, mTestLooper.getLooper()); + mockDeviceConfigAll(); + startUser(gameManagerService, USER_ID_1); + mockModifyGameModeGranted(); + + IGameModeListener mockListener = Mockito.mock(IGameModeListener.class); + IBinder binder = Mockito.mock(IBinder.class); + when(mockListener.asBinder()).thenReturn(binder); + gameManagerService.addGameModeListener(mockListener); + verify(binder).linkToDeath(mDeathRecipientCaptor.capture(), anyInt()); + + gameManagerService.setGameMode(mPackageName, GameManager.GAME_MODE_PERFORMANCE, USER_ID_1); + verify(mockListener).onGameModeChanged(mPackageName, GameManager.GAME_MODE_STANDARD, + GameManager.GAME_MODE_PERFORMANCE, USER_ID_1); + reset(mockListener); + gameManagerService.setGameMode(mPackageName, GameManager.GAME_MODE_BATTERY, USER_ID_1); + verify(mockListener).onGameModeChanged(mPackageName, GameManager.GAME_MODE_PERFORMANCE, + GameManager.GAME_MODE_BATTERY, USER_ID_1); + reset(mockListener); + + mDeathRecipientCaptor.getValue().binderDied(); + verify(binder).unlinkToDeath(eq(mDeathRecipientCaptor.getValue()), anyInt()); + gameManagerService.setGameMode(mPackageName, GameManager.GAME_MODE_CUSTOM, USER_ID_1); + verify(mockListener, never()).onGameModeChanged(anyString(), anyInt(), anyInt(), anyInt()); + } + + @Test + public void testRemoveGameModeListener() throws RemoteException { + GameManagerService gameManagerService = + new GameManagerService(mMockContext, mTestLooper.getLooper()); + mockDeviceConfigAll(); + startUser(gameManagerService, USER_ID_1); + mockModifyGameModeGranted(); + + IGameModeListener mockListener = Mockito.mock(IGameModeListener.class); + IBinder binder = Mockito.mock(IBinder.class); + when(mockListener.asBinder()).thenReturn(binder); + + gameManagerService.addGameModeListener(mockListener); + gameManagerService.removeGameModeListener(mockListener); + gameManagerService.setGameMode(mPackageName, GameManager.GAME_MODE_PERFORMANCE, USER_ID_1); + verify(mockListener, never()).onGameModeChanged(anyString(), anyInt(), anyInt(), anyInt()); + } + private static void deleteFolder(File folder) { File[] files = folder.listFiles(); if (files != null) {