Override config should trigger intervention ignoring opt-in info
In the past we have an inconsistent behavior regarding override config
as it will ignore intervention setting such as `mAllowDownscale` but
still respect opt-in info as `mPerfModeOptedIn`. This will be confusing
and there is no way for OEM or game developers to test new interventions
for games without any pre-configured game mode device config.
Now they can instead first opt in the game modes temporarily if not
pre-configured (to make them available), then apply the override to
test. But they should reset the opt-in info and overrides after testing,
then communicate the interventions to OEMs.
This also fix the bug below where the override config used to contain
full information including opt-in info that can be stale. Now it's
lightweight as it only contains GameModeConfiguration(s) and will be
used together with default config in getConfig call.
Bug: b/253102835
Test: atest GameManagerServiceTests
Change-Id: Iee14d6eed07b16b6adb86b459edebdeca2b03fbc
diff --git a/services/core/java/com/android/server/app/GameManagerService.java b/services/core/java/com/android/server/app/GameManagerService.java
index e11d95a..558364f 100644
--- a/services/core/java/com/android/server/app/GameManagerService.java
+++ b/services/core/java/com/android/server/app/GameManagerService.java
@@ -490,6 +490,8 @@
private final Object mModeConfigLock = new Object();
@GuardedBy("mModeConfigLock")
private final ArrayMap<Integer, GameModeConfiguration> mModeConfigs = new ArrayMap<>();
+ // if adding new properties or make any of the below overridable, the method
+ // copyAndApplyOverride should be updated accordingly
private boolean mPerfModeOptedIn = false;
private boolean mBatteryModeOptedIn = false;
private boolean mAllowDownscale = true;
@@ -800,6 +802,42 @@
}
}
+ GamePackageConfiguration copyAndApplyOverride(GamePackageConfiguration overrideConfig) {
+ GamePackageConfiguration copy = new GamePackageConfiguration(mPackageName);
+ // if a game mode is overridden, we treat it with the highest priority and reset any
+ // opt-in game modes so that interventions are always executed.
+ copy.mPerfModeOptedIn = mPerfModeOptedIn && !(overrideConfig != null
+ && overrideConfig.getGameModeConfiguration(GameManager.GAME_MODE_PERFORMANCE)
+ != null);
+ copy.mBatteryModeOptedIn = mBatteryModeOptedIn && !(overrideConfig != null
+ && overrideConfig.getGameModeConfiguration(GameManager.GAME_MODE_BATTERY)
+ != null);
+
+ // if any game mode is overridden, we will consider all interventions forced-active,
+ // this can be done more granular by checking if a specific intervention is
+ // overridden under each game mode override, but only if necessary.
+ copy.mAllowDownscale = mAllowDownscale || overrideConfig != null;
+ copy.mAllowAngle = mAllowAngle || overrideConfig != null;
+ copy.mAllowFpsOverride = mAllowFpsOverride || overrideConfig != null;
+ if (overrideConfig != null) {
+ synchronized (copy.mModeConfigLock) {
+ synchronized (mModeConfigLock) {
+ for (Map.Entry<Integer, GameModeConfiguration> entry :
+ mModeConfigs.entrySet()) {
+ copy.mModeConfigs.put(entry.getKey(), entry.getValue());
+ }
+ }
+ synchronized (overrideConfig.mModeConfigLock) {
+ for (Map.Entry<Integer, GameModeConfiguration> entry :
+ overrideConfig.mModeConfigs.entrySet()) {
+ copy.mModeConfigs.put(entry.getKey(), entry.getValue());
+ }
+ }
+ }
+ }
+ return copy;
+ }
+
public String toString() {
synchronized (mModeConfigLock) {
return "[Name:" + mPackageName + " Modes: " + mModeConfigs.toString() + "]";
@@ -1375,7 +1413,7 @@
// look for the existing GamePackageConfiguration override
configOverride = settings.getConfigOverride(packageName);
if (configOverride == null) {
- configOverride = new GamePackageConfiguration(mPackageManager, packageName, userId);
+ configOverride = new GamePackageConfiguration(packageName);
settings.setConfigOverride(packageName, configOverride);
}
}
@@ -1430,18 +1468,12 @@
return;
}
// if the game mode to reset is the only mode other than standard mode or there
- // is device config, the config override is removed.
+ // is device config, the entire package config override is removed.
if (Integer.bitCount(modesBitfield) <= 2 || deviceConfig == null) {
settings.removeConfigOverride(packageName);
} else {
- final GamePackageConfiguration.GameModeConfiguration defaultModeConfig =
- deviceConfig.getGameModeConfiguration(gameModeToReset);
- // otherwise we reset the mode by copying the original config.
- if (defaultModeConfig == null) {
- configOverride.removeModeConfig(gameModeToReset);
- } else {
- configOverride.addModeConfig(defaultModeConfig);
- }
+ // otherwise we reset the mode by removing the game mode config override
+ configOverride.removeModeConfig(gameModeToReset);
}
} else {
settings.removeConfigOverride(packageName);
@@ -1661,18 +1693,21 @@
* @hide
*/
public GamePackageConfiguration getConfig(String packageName, int userId) {
- GamePackageConfiguration packageConfig = null;
+ GamePackageConfiguration overrideConfig = null;
+ GamePackageConfiguration config;
+ synchronized (mDeviceConfigLock) {
+ config = mConfigs.get(packageName);
+ }
+
synchronized (mLock) {
if (mSettings.containsKey(userId)) {
- packageConfig = mSettings.get(userId).getConfigOverride(packageName);
+ overrideConfig = mSettings.get(userId).getConfigOverride(packageName);
}
}
- if (packageConfig == null) {
- synchronized (mDeviceConfigLock) {
- packageConfig = mConfigs.get(packageName);
- }
+ if (overrideConfig == null || config == null) {
+ return overrideConfig == null ? config : overrideConfig;
}
- return packageConfig;
+ return config.copyAndApplyOverride(overrideConfig);
}
private void registerPackageReceiver() {
diff --git a/services/tests/mockingservicestests/res/xml/game_manager_service_metadata_config_interventions_disabled_all_opt_in.xml b/services/tests/mockingservicestests/res/xml/game_manager_service_metadata_config_interventions_disabled_all_opt_in.xml
new file mode 100644
index 0000000..77fe786
--- /dev/null
+++ b/services/tests/mockingservicestests/res/xml/game_manager_service_metadata_config_interventions_disabled_all_opt_in.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<game-mode-config
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:supportsPerformanceGameMode="true"
+ android:supportsBatteryGameMode="true"
+ android:allowGameAngleDriver="false"
+ android:allowGameDownscaling="false"
+ android:allowGameFpsOverride="false"
+/>
\ No newline at end of file
diff --git a/services/tests/mockingservicestests/res/xml/gama_manager_service_metadata_config_disabled.xml b/services/tests/mockingservicestests/res/xml/game_manager_service_metadata_config_interventions_disabled_no_opt_in.xml
similarity index 100%
rename from services/tests/mockingservicestests/res/xml/gama_manager_service_metadata_config_disabled.xml
rename to services/tests/mockingservicestests/res/xml/game_manager_service_metadata_config_interventions_disabled_no_opt_in.xml
diff --git a/services/tests/mockingservicestests/res/xml/gama_manager_service_metadata_config_enabled.xml b/services/tests/mockingservicestests/res/xml/game_manager_service_metadata_config_interventions_enabled_no_opt_in.xml
similarity index 100%
rename from services/tests/mockingservicestests/res/xml/gama_manager_service_metadata_config_enabled.xml
rename to services/tests/mockingservicestests/res/xml/game_manager_service_metadata_config_interventions_enabled_no_opt_in.xml
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 9022db8..c7f8e54b 100644
--- a/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java
+++ b/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java
@@ -382,38 +382,35 @@
.thenReturn(applicationInfo);
}
- private void mockInterventionsEnabledFromXml() throws Exception {
- final ApplicationInfo applicationInfo = mMockPackageManager.getApplicationInfoAsUser(
- mPackageName, PackageManager.GET_META_DATA, USER_ID_1);
- Bundle metaDataBundle = new Bundle();
- final int resId = 123;
- metaDataBundle.putInt(
- GameManagerService.GamePackageConfiguration.METADATA_GAME_MODE_CONFIG, resId);
- applicationInfo.metaData = metaDataBundle;
- when(mMockPackageManager.getApplicationInfoAsUser(anyString(), anyInt(), anyInt()))
- .thenReturn(applicationInfo);
- seedGameManagerServiceMetaDataFromFile(mPackageName, resId,
- "res/xml/gama_manager_service_metadata_config_enabled.xml");
+ private void mockInterventionsEnabledNoOptInFromXml() throws Exception {
+ seedGameManagerServiceMetaDataFromFile(mPackageName, 123,
+ "res/xml/game_manager_service_metadata_config_interventions_enabled_no_opt_in.xml");
}
- private void mockInterventionsDisabledFromXml() throws Exception {
- final ApplicationInfo applicationInfo = mMockPackageManager.getApplicationInfoAsUser(
- mPackageName, PackageManager.GET_META_DATA, USER_ID_1);
- Bundle metaDataBundle = new Bundle();
- final int resId = 123;
- metaDataBundle.putInt(
- GameManagerService.GamePackageConfiguration.METADATA_GAME_MODE_CONFIG, resId);
- applicationInfo.metaData = metaDataBundle;
- when(mMockPackageManager.getApplicationInfoAsUser(anyString(), anyInt(), anyInt()))
- .thenReturn(applicationInfo);
- seedGameManagerServiceMetaDataFromFile(mPackageName, resId,
- "res/xml/gama_manager_service_metadata_config_disabled.xml");
+ private void mockInterventionsDisabledNoOptInFromXml() throws Exception {
+ seedGameManagerServiceMetaDataFromFile(mPackageName, 123,
+ "res/xml/game_manager_service_metadata_config_interventions_disabled_no_opt_in"
+ + ".xml");
+ }
+
+ private void mockInterventionsDisabledAllOptInFromXml() throws Exception {
+ seedGameManagerServiceMetaDataFromFile(mPackageName, 123,
+ "res/xml/game_manager_service_metadata_config_interventions_disabled_all_opt_in"
+ + ".xml");
}
private void seedGameManagerServiceMetaDataFromFile(String packageName, int resId,
String fileName)
throws Exception {
+ final ApplicationInfo applicationInfo = mMockPackageManager.getApplicationInfoAsUser(
+ mPackageName, PackageManager.GET_META_DATA, USER_ID_1);
+ Bundle metaDataBundle = new Bundle();
+ metaDataBundle.putInt(
+ GameManagerService.GamePackageConfiguration.METADATA_GAME_MODE_CONFIG, resId);
+ applicationInfo.metaData = metaDataBundle;
+ when(mMockPackageManager.getApplicationInfoAsUser(anyString(), anyInt(), anyInt()))
+ .thenReturn(applicationInfo);
AssetManager assetManager =
InstrumentationRegistry.getInstrumentation().getContext().getAssets();
XmlResourceParser xmlResourceParser =
@@ -641,6 +638,12 @@
assertEquals(fps, config.getGameModeConfiguration(gameMode).getFps());
}
+ private boolean checkOptedIn(GameManagerService gameManagerService, int gameMode) {
+ GameManagerService.GamePackageConfiguration config =
+ gameManagerService.getConfig(mPackageName, USER_ID_1);
+ return config.willGamePerformOptimizations(gameMode);
+ }
+
/**
* Phenotype device config exists, but is only propagating the default value.
*/
@@ -756,7 +759,7 @@
* Override device configs for both battery and performance modes exists and are valid.
*/
@Test
- public void testSetDeviceOverrideConfigAll() {
+ public void testSetDeviceConfigOverrideAll() {
mockDeviceConfigAll();
mockModifyGameModeGranted();
@@ -776,6 +779,75 @@
checkFps(gameManagerService, GameManager.GAME_MODE_BATTERY, 60);
}
+ @Test
+ public void testSetBatteryModeConfigOverride_thenUpdateAllDeviceConfig() throws Exception {
+ mockModifyGameModeGranted();
+ String configStringBefore =
+ "mode=2,downscaleFactor=1.0,fps=90:mode=3,downscaleFactor=0.1,fps=30";
+ when(DeviceConfig.getProperty(anyString(), anyString()))
+ .thenReturn(configStringBefore);
+ mockInterventionsEnabledNoOptInFromXml();
+ GameManagerService gameManagerService = new GameManagerService(mMockContext,
+ mTestLooper.getLooper());
+ startUser(gameManagerService, USER_ID_1);
+
+ checkDownscaling(gameManagerService, GameManager.GAME_MODE_PERFORMANCE, 1.0f);
+ checkFps(gameManagerService, GameManager.GAME_MODE_PERFORMANCE, 90);
+ checkDownscaling(gameManagerService, GameManager.GAME_MODE_BATTERY, 0.1f);
+ checkFps(gameManagerService, GameManager.GAME_MODE_BATTERY, 30);
+
+ gameManagerService.setGameModeConfigOverride(mPackageName, USER_ID_1, 3, "40",
+ "0.2");
+
+ checkFps(gameManagerService, GameManager.GAME_MODE_BATTERY, 40);
+ checkDownscaling(gameManagerService, GameManager.GAME_MODE_BATTERY, 0.2f);
+
+ String configStringAfter =
+ "mode=2,downscaleFactor=0.9,fps=60:mode=3,downscaleFactor=0.3,fps=50";
+ when(DeviceConfig.getProperty(anyString(), anyString()))
+ .thenReturn(configStringAfter);
+ gameManagerService.updateConfigsForUser(USER_ID_1, false, mPackageName);
+
+ // performance mode was not overridden thus it should be updated
+ checkDownscaling(gameManagerService, GameManager.GAME_MODE_PERFORMANCE, 0.9f);
+ checkFps(gameManagerService, GameManager.GAME_MODE_PERFORMANCE, 60);
+
+ // battery mode was overridden thus it should be the same as the override
+ checkDownscaling(gameManagerService, GameManager.GAME_MODE_BATTERY, 0.2f);
+ checkFps(gameManagerService, GameManager.GAME_MODE_BATTERY, 40);
+ }
+
+ @Test
+ public void testSetBatteryModeConfigOverride_thenOptInBatteryMode() throws Exception {
+ mockModifyGameModeGranted();
+ String configStringBefore =
+ "mode=2,downscaleFactor=1.0,fps=90:mode=3,downscaleFactor=0.1,fps=30";
+ when(DeviceConfig.getProperty(anyString(), anyString()))
+ .thenReturn(configStringBefore);
+ mockInterventionsDisabledNoOptInFromXml();
+ GameManagerService gameManagerService = new GameManagerService(mMockContext,
+ mTestLooper.getLooper());
+ startUser(gameManagerService, USER_ID_1);
+
+ assertFalse(checkOptedIn(gameManagerService, GameManager.GAME_MODE_PERFORMANCE));
+ assertFalse(checkOptedIn(gameManagerService, GameManager.GAME_MODE_BATTERY));
+ checkFps(gameManagerService, GameManager.GAME_MODE_PERFORMANCE, 0);
+
+ gameManagerService.setGameModeConfigOverride(mPackageName, USER_ID_1, 3, "40",
+ "0.2");
+ checkFps(gameManagerService, GameManager.GAME_MODE_PERFORMANCE, 0);
+ // override will enable the interventions
+ checkDownscaling(gameManagerService, GameManager.GAME_MODE_BATTERY, 0.2f);
+ checkFps(gameManagerService, GameManager.GAME_MODE_BATTERY, 40);
+
+ mockInterventionsDisabledAllOptInFromXml();
+ gameManagerService.updateConfigsForUser(USER_ID_1, false, mPackageName);
+
+ assertTrue(checkOptedIn(gameManagerService, GameManager.GAME_MODE_PERFORMANCE));
+ // opt-in is still false for battery mode as override exists
+ assertFalse(checkOptedIn(gameManagerService, GameManager.GAME_MODE_BATTERY));
+ }
+
/**
* Override device config for performance mode exists and is valid.
*/
@@ -1050,7 +1122,7 @@
gameManagerService.setGameMode(mPackageName, GameManager.GAME_MODE_PERFORMANCE, USER_ID_1);
assertEquals(GameManager.GAME_MODE_PERFORMANCE,
gameManagerService.getGameMode(mPackageName, USER_ID_1));
- mockInterventionsEnabledFromXml();
+ mockInterventionsEnabledNoOptInFromXml();
checkLoadingBoost(gameManagerService, GameManager.GAME_MODE_PERFORMANCE, 0);
}
@@ -1058,7 +1130,7 @@
public void testGameModeConfigAllowFpsTrue() throws Exception {
mockDeviceConfigAll();
mockModifyGameModeGranted();
- mockInterventionsEnabledFromXml();
+ mockInterventionsEnabledNoOptInFromXml();
GameManagerService gameManagerService = new GameManagerService(mMockContext,
mTestLooper.getLooper());
startUser(gameManagerService, USER_ID_1);
@@ -1073,7 +1145,7 @@
public void testGameModeConfigAllowFpsFalse() throws Exception {
mockDeviceConfigAll();
mockModifyGameModeGranted();
- mockInterventionsDisabledFromXml();
+ mockInterventionsDisabledNoOptInFromXml();
GameManagerService gameManagerService = new GameManagerService(mMockContext,
mTestLooper.getLooper());
startUser(gameManagerService, USER_ID_1);