diff --git a/services/core/java/com/android/server/app/GameManagerService.java b/services/core/java/com/android/server/app/GameManagerService.java index d2c6c6c620dd9..1aeeda0c41882 100644 --- a/services/core/java/com/android/server/app/GameManagerService.java +++ b/services/core/java/com/android/server/app/GameManagerService.java @@ -56,7 +56,6 @@ import android.os.ShellCallback; import android.provider.DeviceConfig; import android.provider.DeviceConfig.Properties; import android.util.ArrayMap; -import android.util.ArraySet; import android.util.KeyValueListParser; import android.util.Slog; @@ -363,19 +362,42 @@ public final class GameManagerService extends IGameManagerService.Stub { || (mPerfModeOptedIn && gameMode == GameManager.GAME_MODE_PERFORMANCE); } - public @GameMode int[] getAvailableGameModes() { - ArraySet modeSet = new ArraySet<>(mModeConfigs.keySet()); + private int getAvailableGameModesBitfield() { + int field = 0; + for (final int mode : mModeConfigs.keySet()) { + field |= modeToBitmask(mode); + } if (mBatteryModeOptedIn) { - modeSet.add(GameManager.GAME_MODE_BATTERY); + field |= modeToBitmask(GameManager.GAME_MODE_BATTERY); } if (mPerfModeOptedIn) { - modeSet.add(GameManager.GAME_MODE_PERFORMANCE); + field |= modeToBitmask(GameManager.GAME_MODE_PERFORMANCE); } - if (modeSet.size() > 0) { - modeSet.add(GameManager.GAME_MODE_STANDARD); - return modeSet.stream().mapToInt(Integer::intValue).toArray(); + // 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 new int[]{GameManager.GAME_MODE_UNSUPPORTED}; + return field; + } + + /** + * Get an array of a package's available game modes. + */ + public @GameMode int[] getAvailableGameModes() { + + int modesBitfield = getAvailableGameModesBitfield(); + int sigBits = Integer.bitCount(modesBitfield); + int[] modes = new int[sigBits]; + int i = 0; + for (int mode = 0; mode < sigBits; ++mode) { + if (((modesBitfield >> mode) & 1) != 0) { + modes[i++] = mode; + } + } + return modes; } /** @@ -696,6 +718,14 @@ public final class GameManagerService extends IGameManagerService.Stub { } } + private int modeToBitmask(@GameMode int gameMode) { + return (1 << gameMode); + } + + private boolean bitFieldContainsModeBitmask(int bitField, @GameMode int gameMode) { + return (bitField & modeToBitmask(gameMode)) != 0; + } + /** * @hide */ @@ -703,8 +733,8 @@ public final class GameManagerService extends IGameManagerService.Stub { public void updateConfigsForUser(int userId, String ...packageNames) { try { synchronized (mDeviceConfigLock) { - for (String packageName : packageNames) { - GamePackageConfiguration config = + for (final String packageName : packageNames) { + final GamePackageConfiguration config = new GamePackageConfiguration(packageName, userId); if (config.isValid()) { if (DEBUG) { @@ -718,13 +748,42 @@ public final class GameManagerService extends IGameManagerService.Stub { } } } - for (String packageName : packageNames) { - synchronized (mLock) { - if (mSettings.containsKey(userId)) { - GameManagerSettings userSettings = mSettings.get(userId); - updateCompatModeDownscale(packageName, - userSettings.getGameModeLocked(packageName)); + for (final String packageName : packageNames) { + if (mSettings.containsKey(userId)) { + int gameMode = getGameMode(packageName, userId); + int newGameMode = gameMode; + // Make sure the user settings and package configs don't conflict. I.e. the + // user setting is set to a mode that no longer available due to config/manifest + // changes. Most of the time we won't have to change anything. + GamePackageConfiguration config; + synchronized (mDeviceConfigLock) { + config = mConfigs.get(packageName); } + if (config != null) { + int modesBitfield = config.getAvailableGameModesBitfield(); + // Remove UNSUPPORTED to simplify the logic here, since we really just + // 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; + } + } + } 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; + } + if (newGameMode != gameMode) { + setGameMode(packageName, newGameMode, userId); + } + updateCompatModeDownscale(packageName, gameMode); } } } catch (Exception e) { 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 96495701811eb..e7a4c92da15cd 100644 --- a/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java +++ b/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java @@ -299,11 +299,13 @@ public class GameManagerServiceTests { public void testGameMode() { GameManagerService gameManagerService = new GameManagerService(mMockContext); gameManagerService.onUserStarting(USER_ID_1); - + gameManagerService.updateConfigsForUser(USER_ID_1, mPackageName); mockModifyGameModeGranted(); - assertEquals(GameManager.GAME_MODE_UNSUPPORTED, gameManagerService.getGameMode(mPackageName, USER_ID_1)); + // We need to make sure the mode is supported before setting it. + mockDeviceConfigAll(); + gameManagerService.updateConfigsForUser(USER_ID_1, mPackageName); gameManagerService.setGameMode(mPackageName, GameManager.GAME_MODE_STANDARD, USER_ID_1); assertEquals(GameManager.GAME_MODE_STANDARD, gameManagerService.getGameMode(mPackageName, USER_ID_1)); @@ -370,11 +372,13 @@ public class GameManagerServiceTests { */ @Test public void testGameModeMultipleUsers() { + mockModifyGameModeGranted(); + mockDeviceConfigAll(); GameManagerService gameManagerService = new GameManagerService(mMockContext); gameManagerService.onUserStarting(USER_ID_1); gameManagerService.onUserStarting(USER_ID_2); - - mockModifyGameModeGranted(); + gameManagerService.updateConfigsForUser(USER_ID_1, mPackageName); + gameManagerService.updateConfigsForUser(USER_ID_2, mPackageName); // Set User 1 to Standard gameManagerService.setGameMode(mPackageName, GameManager.GAME_MODE_STANDARD, USER_ID_1); @@ -603,4 +607,52 @@ public class GameManagerServiceTests { gameManagerService.getConfig(mPackageName); assertNull(config.getGameModeConfiguration(GameManager.GAME_MODE_PERFORMANCE)); } + + /** + * Ensure that, if a game no longer supports any game modes, we set the game mode to + * UNSUPPORTED + */ + @Test + public void testUnsetInvalidGameMode() throws Exception { + mockDeviceConfigNone(); + mockModifyGameModeGranted(); + GameManagerService gameManagerService = new GameManagerService(mMockContext); + gameManagerService.onUserStarting(USER_ID_1); + gameManagerService.setGameMode(mPackageName, GameManager.GAME_MODE_PERFORMANCE, USER_ID_1); + gameManagerService.updateConfigsForUser(USER_ID_1, mPackageName); + assertEquals(GameManager.GAME_MODE_UNSUPPORTED, + gameManagerService.getGameMode(mPackageName, USER_ID_1)); + } + + /** + * Ensure that, if a game no longer supports a specific game mode, but supports STANDARD, we set + * the game mode to STANDARD. + */ + @Test + public void testResetInvalidGameMode() throws Exception { + mockDeviceConfigPerformance(); + mockModifyGameModeGranted(); + GameManagerService gameManagerService = new GameManagerService(mMockContext); + gameManagerService.onUserStarting(USER_ID_1); + gameManagerService.setGameMode(mPackageName, GameManager.GAME_MODE_BATTERY, USER_ID_1); + gameManagerService.updateConfigsForUser(USER_ID_1, mPackageName); + assertEquals(GameManager.GAME_MODE_STANDARD, + gameManagerService.getGameMode(mPackageName, USER_ID_1)); + } + + /** + * Ensure that if a game supports STANDARD, but is currently set to UNSUPPORTED, we set the game + * mode to STANDARD + */ + @Test + public void testSetValidGameMode() throws Exception { + mockDeviceConfigPerformance(); + mockModifyGameModeGranted(); + GameManagerService gameManagerService = new GameManagerService(mMockContext); + gameManagerService.onUserStarting(USER_ID_1); + gameManagerService.setGameMode(mPackageName, GameManager.GAME_MODE_UNSUPPORTED, USER_ID_1); + gameManagerService.updateConfigsForUser(USER_ID_1, mPackageName); + assertEquals(GameManager.GAME_MODE_STANDARD, + gameManagerService.getGameMode(mPackageName, USER_ID_1)); + } }