Merge changes Ia3905cd1,Ibcedd393,Id145fe3a

* changes:
  Send writing file messages on shutdown with no delay
  Persist game mode configs in settings file
  Support per user game config override
This commit is contained in:
Xiang Wang
2022-08-22 04:32:45 +00:00
committed by Android (Google) Code Review
5 changed files with 540 additions and 192 deletions

View File

@@ -99,6 +99,7 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
/**
* Service to manage game related features.
@@ -119,7 +120,7 @@ public final class GameManagerService extends IGameManagerService.Stub {
static final int SET_GAME_STATE = 4;
static final int CANCEL_GAME_LOADING_MODE = 5;
static final int WRITE_GAME_MODE_INTERVENTION_LIST_FILE = 6;
static final int WRITE_SETTINGS_DELAY = 10 * 1000; // 10 seconds
static final int WRITE_DELAY_MILLIS = 10 * 1000; // 10 seconds
static final int LOADING_BOOST_MAX_DURATION = 5 * 1000; // 5 seconds
private static final String PACKAGE_NAME_MSG_KEY = "packageName";
@@ -130,8 +131,8 @@ 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 mOverrideConfigLock = new Object();
private final Handler mHandler;
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
final Handler mHandler;
private final PackageManager mPackageManager;
private final UserManager mUserManager;
private final PowerManagerInternal mPowerManagerInternal;
@@ -143,8 +144,6 @@ public final class GameManagerService extends IGameManagerService.Stub {
private final ArrayMap<Integer, GameManagerSettings> mSettings = new ArrayMap<>();
@GuardedBy("mDeviceConfigLock")
private final ArrayMap<String, GamePackageConfiguration> mConfigs = new ArrayMap<>();
@GuardedBy("mOverrideConfigLock")
private final ArrayMap<String, GamePackageConfiguration> mOverrideConfigs = new ArrayMap<>();
@Nullable
private final GameServiceController mGameServiceController;
@@ -236,7 +235,7 @@ public final class GameManagerService extends IGameManagerService.Stub {
final int userId = ActivityManager.getCurrentUser();
String[] packageList = getInstalledGamePackageNames(userId);
for (final String packageName : packageList) {
pw.println(getInterventionList(packageName));
pw.println(getInterventionList(packageName, userId));
}
}
@@ -258,14 +257,13 @@ public final class GameManagerService extends IGameManagerService.Stub {
if (userId < 0) {
Slog.wtf(TAG, "Attempt to write settings for invalid user: " + userId);
synchronized (mLock) {
removeMessages(WRITE_SETTINGS, msg.obj);
removeEqualMessages(WRITE_SETTINGS, msg.obj);
}
break;
}
Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
synchronized (mLock) {
removeMessages(WRITE_SETTINGS, msg.obj);
removeEqualMessages(WRITE_SETTINGS, msg.obj);
if (mSettings.containsKey(userId)) {
GameManagerSettings userSettings = mSettings.get(userId);
userSettings.writePersistentDataLocked();
@@ -279,8 +277,8 @@ public final class GameManagerService extends IGameManagerService.Stub {
if (userId < 0) {
Slog.wtf(TAG, "Attempt to write settings for invalid user: " + userId);
synchronized (mLock) {
removeMessages(WRITE_SETTINGS, msg.obj);
removeMessages(REMOVE_SETTINGS, msg.obj);
removeEqualMessages(WRITE_SETTINGS, msg.obj);
removeEqualMessages(REMOVE_SETTINGS, msg.obj);
}
break;
}
@@ -288,8 +286,8 @@ public final class GameManagerService extends IGameManagerService.Stub {
synchronized (mLock) {
// Since the user was removed, ignore previous write message
// and do write here.
removeMessages(WRITE_SETTINGS, msg.obj);
removeMessages(REMOVE_SETTINGS, msg.obj);
removeEqualMessages(WRITE_SETTINGS, msg.obj);
removeEqualMessages(REMOVE_SETTINGS, msg.obj);
if (mSettings.containsKey(userId)) {
final GameManagerSettings userSettings = mSettings.get(userId);
mSettings.remove(userId);
@@ -299,7 +297,7 @@ public final class GameManagerService extends IGameManagerService.Stub {
break;
}
case POPULATE_GAME_MODE_SETTINGS: {
removeMessages(POPULATE_GAME_MODE_SETTINGS, msg.obj);
removeEqualMessages(POPULATE_GAME_MODE_SETTINGS, msg.obj);
final int userId = (int) msg.obj;
final String[] packageNames = getInstalledGamePackageNames(userId);
updateConfigsForUser(userId, false /*checkGamePackage*/, packageNames);
@@ -345,13 +343,13 @@ public final class GameManagerService extends IGameManagerService.Stub {
if (userId < 0) {
Slog.wtf(TAG, "Attempt to write setting for invalid user: " + userId);
synchronized (mLock) {
removeMessages(WRITE_GAME_MODE_INTERVENTION_LIST_FILE, null);
removeEqualMessages(WRITE_GAME_MODE_INTERVENTION_LIST_FILE, msg.obj);
}
break;
}
Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
removeMessages(WRITE_GAME_MODE_INTERVENTION_LIST_FILE, null);
removeEqualMessages(WRITE_GAME_MODE_INTERVENTION_LIST_FILE, msg.obj);
writeGameModeInterventionsToFile(userId);
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
break;
@@ -446,8 +444,7 @@ public final class GameManagerService extends IGameManagerService.Stub {
/**
* GamePackageConfiguration manages all game mode config details for its associated package.
*/
@VisibleForTesting
public class GamePackageConfiguration {
public static class GamePackageConfiguration {
public static final String TAG = "GameManagerService_GamePackageConfiguration";
/**
@@ -499,12 +496,16 @@ public final class GameManagerService extends IGameManagerService.Stub {
private boolean mAllowAngle;
private boolean mAllowFpsOverride;
GamePackageConfiguration(String packageName, int userId) {
GamePackageConfiguration(String packageName) {
mPackageName = packageName;
}
GamePackageConfiguration(PackageManager packageManager, String packageName, int userId) {
mPackageName = packageName;
try {
final ApplicationInfo ai = mPackageManager.getApplicationInfoAsUser(packageName,
final ApplicationInfo ai = packageManager.getApplicationInfoAsUser(packageName,
PackageManager.GET_META_DATA, userId);
if (!parseInterventionFromXml(ai, packageName)) {
if (!parseInterventionFromXml(packageManager, ai, packageName)) {
if (ai.metaData != null) {
mPerfModeOptedIn = ai.metaData.getBoolean(METADATA_PERFORMANCE_MODE_ENABLE);
mBatteryModeOptedIn = ai.metaData.getBoolean(METADATA_BATTERY_MODE_ENABLE);
@@ -538,16 +539,17 @@ public final class GameManagerService extends IGameManagerService.Stub {
}
}
private boolean parseInterventionFromXml(ApplicationInfo ai, String packageName) {
private boolean parseInterventionFromXml(PackageManager packageManager, ApplicationInfo ai,
String packageName) {
boolean xmlFound = false;
try (XmlResourceParser parser = ai.loadXmlMetaData(mPackageManager,
try (XmlResourceParser parser = ai.loadXmlMetaData(packageManager,
METADATA_GAME_MODE_CONFIG)) {
if (parser == null) {
Slog.v(TAG, "No " + METADATA_GAME_MODE_CONFIG
+ " meta-data found for package " + mPackageName);
} else {
xmlFound = true;
final Resources resources = mPackageManager.getResourcesForApplication(
final Resources resources = packageManager.getResourcesForApplication(
packageName);
final AttributeSet attributeSet = Xml.asAttributeSet(parser);
int type;
@@ -596,7 +598,6 @@ public final class GameManagerService extends IGameManagerService.Stub {
* GameModeConfiguration contains all the values for all the interventions associated with
* a game mode.
*/
@VisibleForTesting
public class GameModeConfiguration {
public static final String TAG = "GameManagerService_GameModeConfiguration";
public static final String MODE_KEY = "mode";
@@ -613,8 +614,8 @@ public final class GameManagerService extends IGameManagerService.Stub {
private final @GameMode int mGameMode;
private float mScaling = DEFAULT_SCALING;
private String mFps = DEFAULT_FPS;
private final boolean mUseAngle;
private final int mLoadingBoostDuration;
private boolean mUseAngle;
private int mLoadingBoostDuration;
GameModeConfiguration(int gameMode) {
mGameMode = gameMode;
@@ -657,11 +658,15 @@ public final class GameManagerService extends IGameManagerService.Stub {
return GameManagerService.getFpsInt(mFps);
}
public boolean getUseAngle() {
synchronized String getFpsStr() {
return mFps;
}
public synchronized boolean getUseAngle() {
return mUseAngle;
}
public int getLoadingBoostDuration() {
public synchronized int getLoadingBoostDuration() {
return mLoadingBoostDuration;
}
@@ -673,6 +678,14 @@ public final class GameManagerService extends IGameManagerService.Stub {
mFps = fpsStr;
}
public synchronized void setUseAngle(boolean useAngle) {
mUseAngle = useAngle;
}
public synchronized void setLoadingBoostDuration(int loadingBoostDuration) {
mLoadingBoostDuration = loadingBoostDuration;
}
public boolean isActive() {
return (mGameMode == GameManager.GAME_MODE_STANDARD
|| mGameMode == GameManager.GAME_MODE_PERFORMANCE
@@ -759,7 +772,7 @@ public final class GameManagerService extends IGameManagerService.Stub {
}
/**
* Inserts a new GameModeConfiguration
* Inserts a new GameModeConfiguration.
*/
public void addModeConfig(GameModeConfiguration config) {
if (config.isActive()) {
@@ -772,6 +785,15 @@ public final class GameManagerService extends IGameManagerService.Stub {
}
}
/**
* Removes the GameModeConfiguration.
*/
public void removeModeConfig(int mode) {
synchronized (mModeConfigLock) {
mModeConfigs.remove(mode);
}
}
public boolean isActive() {
synchronized (mModeConfigLock) {
return mModeConfigs.size() > 0 || mBatteryModeOptedIn || mPerfModeOptedIn;
@@ -823,7 +845,8 @@ public final class GameManagerService extends IGameManagerService.Stub {
@Override
public void onUserStarting(@NonNull TargetUser user) {
mService.onUserStarting(user);
mService.onUserStarting(user,
Environment.getDataSystemDeDirectory(user.getUserIdentifier()));
}
@Override
@@ -860,7 +883,10 @@ public final class GameManagerService extends IGameManagerService.Stub {
}
private @GameMode int[] getAvailableGameModesUnchecked(String packageName) {
final GamePackageConfiguration config = getConfig(packageName);
final GamePackageConfiguration config;
synchronized (mDeviceConfigLock) {
config = mConfigs.get(packageName);
}
if (config == null) {
return new int[]{};
}
@@ -987,18 +1013,11 @@ public final class GameManagerService extends IGameManagerService.Stub {
}
GameManagerSettings userSettings = mSettings.get(userId);
userSettings.setGameModeLocked(packageName, gameMode);
final Message msg = mHandler.obtainMessage(WRITE_SETTINGS);
msg.obj = userId;
if (!mHandler.hasEqualMessages(WRITE_SETTINGS, userId)) {
mHandler.sendMessageDelayed(msg, WRITE_SETTINGS_DELAY);
}
}
updateInterventions(packageName, gameMode, userId);
final Message msg = mHandler.obtainMessage(WRITE_GAME_MODE_INTERVENTION_LIST_FILE);
msg.obj = userId;
if (!mHandler.hasEqualMessages(WRITE_GAME_MODE_INTERVENTION_LIST_FILE, userId)) {
mHandler.sendMessage(msg);
}
sendUserMessage(userId, WRITE_SETTINGS, "SET_GAME_MODE", WRITE_DELAY_MILLIS);
sendUserMessage(userId, WRITE_GAME_MODE_INTERVENTION_LIST_FILE,
"SET_GAME_MODE", 0 /*delayMillis*/);
}
/**
@@ -1033,7 +1052,6 @@ public final class GameManagerService extends IGameManagerService.Stub {
* the boost duration. If no configuration is available for the selected package or mode, the
* default is returned.
*/
@VisibleForTesting
public int getLoadingBoostDuration(String packageName, int userId)
throws SecurityException {
final int gameMode = getGameMode(packageName, userId);
@@ -1165,7 +1183,7 @@ public final class GameManagerService extends IGameManagerService.Stub {
}
float getResolutionScalingFactorInternal(String packageName, int gameMode, int userId) {
final GamePackageConfiguration packageConfig = getConfig(packageName);
final GamePackageConfiguration packageConfig = getConfig(packageName, userId);
if (packageConfig == null) {
return GamePackageConfiguration.GameModeConfiguration.DEFAULT_SCALING;
}
@@ -1186,22 +1204,46 @@ public final class GameManagerService extends IGameManagerService.Stub {
if (mGameServiceController != null) {
mGameServiceController.onBootComplete();
}
mContext.registerReceiver(new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (Intent.ACTION_SHUTDOWN.equals(intent.getAction())) {
synchronized (mLock) {
// Note that the max wait time of broadcast is 10s (see
// {@ShutdownThread#MAX_BROADCAST_TIMEMAX_BROADCAST_TIME}) currently so
// this can be optional only if we have message delay plus processing
// time significant smaller to prevent data loss.
for (Map.Entry<Integer, GameManagerSettings> entry : mSettings.entrySet()) {
final int userId = entry.getKey();
sendUserMessage(userId, WRITE_SETTINGS,
Intent.ACTION_SHUTDOWN, 0 /*delayMillis*/);
sendUserMessage(userId,
WRITE_GAME_MODE_INTERVENTION_LIST_FILE, Intent.ACTION_SHUTDOWN,
0 /*delayMillis*/);
}
}
}
}
}, new IntentFilter(Intent.ACTION_SHUTDOWN));
}
void onUserStarting(@NonNull TargetUser user) {
final int userId = user.getUserIdentifier();
private void sendUserMessage(int userId, int what, String eventForLog, int delayMillis) {
Message msg = mHandler.obtainMessage(what, userId);
if (!mHandler.sendMessageDelayed(msg, delayMillis)) {
Slog.e(TAG, "Failed to send user message " + what + " on " + eventForLog);
}
}
void onUserStarting(@NonNull TargetUser user, File settingDataDir) {
final int userId = user.getUserIdentifier();
synchronized (mLock) {
if (!mSettings.containsKey(userId)) {
GameManagerSettings userSettings =
new GameManagerSettings(Environment.getDataSystemDeDirectory(userId));
GameManagerSettings userSettings = new GameManagerSettings(settingDataDir);
mSettings.put(userId, userSettings);
userSettings.readPersistentDataLocked();
}
}
final Message msg = mHandler.obtainMessage(POPULATE_GAME_MODE_SETTINGS);
msg.obj = userId;
mHandler.sendMessage(msg);
sendUserMessage(userId, POPULATE_GAME_MODE_SETTINGS, "ON_USER_STARTING", 0 /*delayMillis*/);
if (mGameServiceController != null) {
mGameServiceController.notifyUserStarted(user);
@@ -1221,9 +1263,7 @@ public final class GameManagerService extends IGameManagerService.Stub {
if (!mSettings.containsKey(userId)) {
return;
}
final Message msg = mHandler.obtainMessage(REMOVE_SETTINGS);
msg.obj = userId;
mHandler.sendMessage(msg);
sendUserMessage(userId, REMOVE_SETTINGS, "ON_USER_STOPPING", 0 /*delayMillis*/);
}
if (mGameServiceController != null) {
@@ -1237,15 +1277,14 @@ public final class GameManagerService extends IGameManagerService.Stub {
synchronized (mLock) {
final int fromUserId = from.getUserIdentifier();
if (mSettings.containsKey(fromUserId)) {
final Message msg = mHandler.obtainMessage(REMOVE_SETTINGS);
msg.obj = fromUserId;
mHandler.sendMessage(msg);
sendUserMessage(fromUserId, REMOVE_SETTINGS, "ON_USER_SWITCHING",
0 /*delayMillis*/);
}
}
}
final Message msg = mHandler.obtainMessage(POPULATE_GAME_MODE_SETTINGS);
msg.obj = toUserId;
mHandler.sendMessage(msg);
sendUserMessage(toUserId, POPULATE_GAME_MODE_SETTINGS, "ON_USER_SWITCHING",
0 /*delayMillis*/);
if (mGameServiceController != null) {
mGameServiceController.notifyNewForegroundUser(to);
@@ -1265,7 +1304,7 @@ public final class GameManagerService extends IGameManagerService.Stub {
}
}
private int modeToBitmask(@GameMode int gameMode) {
private static int modeToBitmask(@GameMode int gameMode) {
return (1 << gameMode);
}
@@ -1305,7 +1344,7 @@ public final class GameManagerService extends IGameManagerService.Stub {
resetFps(packageName, userId);
return;
}
final GamePackageConfiguration packageConfig = getConfig(packageName);
final GamePackageConfiguration packageConfig = getConfig(packageName, userId);
if (packageConfig == null) {
Slog.v(TAG, "Package configuration not found for " + packageName);
return;
@@ -1318,7 +1357,7 @@ public final class GameManagerService extends IGameManagerService.Stub {
}
/**
* Set the override Game Mode Configuration.
* Set the Game Mode Configuration override.
* Update the config if exists, create one if not.
*/
@VisibleForTesting
@@ -1326,95 +1365,86 @@ public final class GameManagerService extends IGameManagerService.Stub {
public void setGameModeConfigOverride(String packageName, @UserIdInt int userId,
@GameMode int gameMode, String fpsStr, String scaling) throws SecurityException {
checkPermission(Manifest.permission.MANAGE_GAME_MODE);
// Adding game mode config override of the given package name
GamePackageConfiguration configOverride;
synchronized (mLock) {
if (!mSettings.containsKey(userId)) {
return;
}
}
// Adding override game mode configuration of the given package name
GamePackageConfiguration overrideConfig;
synchronized (mOverrideConfigLock) {
// look for the existing override GamePackageConfiguration
overrideConfig = mOverrideConfigs.get(packageName);
if (overrideConfig == null) {
overrideConfig = new GamePackageConfiguration(packageName, userId);
mOverrideConfigs.put(packageName, overrideConfig);
final GameManagerSettings settings = mSettings.get(userId);
// look for the existing GamePackageConfiguration override
configOverride = settings.getConfigOverride(packageName);
if (configOverride == null) {
configOverride = new GamePackageConfiguration(mPackageManager, packageName, userId);
settings.setConfigOverride(packageName, configOverride);
}
}
// modify GameModeConfiguration intervention settings
GamePackageConfiguration.GameModeConfiguration overrideModeConfig =
overrideConfig.getOrAddDefaultGameModeConfiguration(gameMode);
GamePackageConfiguration.GameModeConfiguration modeConfigOverride =
configOverride.getOrAddDefaultGameModeConfiguration(gameMode);
if (fpsStr != null) {
overrideModeConfig.setFpsStr(fpsStr);
modeConfigOverride.setFpsStr(fpsStr);
} else {
overrideModeConfig.setFpsStr(
modeConfigOverride.setFpsStr(
GamePackageConfiguration.GameModeConfiguration.DEFAULT_FPS);
}
if (scaling != null) {
overrideModeConfig.setScaling(Float.parseFloat(scaling));
modeConfigOverride.setScaling(Float.parseFloat(scaling));
}
Slog.i(TAG, "Package Name: " + packageName
+ " FPS: " + String.valueOf(overrideModeConfig.getFps())
+ " Scaling: " + overrideModeConfig.getScaling());
+ " FPS: " + String.valueOf(modeConfigOverride.getFps())
+ " Scaling: " + modeConfigOverride.getScaling());
setGameMode(packageName, gameMode, userId);
}
/**
* Reset the overridden gameModeConfiguration of the given mode.
* Remove the override config if game mode is not specified.
* Remove the config override if game mode is not specified.
*/
@VisibleForTesting
@RequiresPermission(Manifest.permission.MANAGE_GAME_MODE)
public void resetGameModeConfigOverride(String packageName, @UserIdInt int userId,
@GameMode int gameModeToReset) throws SecurityException {
checkPermission(Manifest.permission.MANAGE_GAME_MODE);
synchronized (mLock) {
if (!mSettings.containsKey(userId)) {
return;
}
final GamePackageConfiguration deviceConfig;
synchronized (mDeviceConfigLock) {
deviceConfig = mConfigs.get(packageName);
}
// resets GamePackageConfiguration of a given packageName.
// If a gameMode is specified, only reset the GameModeConfiguration of the gameMode.
if (gameModeToReset != -1) {
GamePackageConfiguration overrideConfig = null;
synchronized (mOverrideConfigLock) {
overrideConfig = mOverrideConfigs.get(packageName);
}
GamePackageConfiguration config = null;
synchronized (mDeviceConfigLock) {
config = mConfigs.get(packageName);
}
int[] modes = overrideConfig.getAvailableGameModes();
// First check if the mode to reset exists
boolean isGameModeExist = false;
for (int mode : modes) {
if (gameModeToReset == mode) {
isGameModeExist = true;
}
}
if (!isGameModeExist) {
synchronized (mLock) {
if (!mSettings.containsKey(userId)) {
return;
}
// If the game mode to reset is the only mode other than standard mode,
// the override config is removed.
if (modes.length <= 2) {
synchronized (mOverrideConfigLock) {
mOverrideConfigs.remove(packageName);
final GameManagerSettings settings = mSettings.get(userId);
if (gameModeToReset != -1) {
final GamePackageConfiguration configOverride = settings.getConfigOverride(
packageName);
if (configOverride == null) {
return;
}
final int modesBitfield = configOverride.getAvailableGameModesBitfield();
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 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);
}
}
} else {
// otherwise we reset the mode by copying the original config.
overrideConfig.addModeConfig(config.getGameModeConfiguration(gameModeToReset));
}
} else {
synchronized (mOverrideConfigLock) {
// remove override config if there is one
mOverrideConfigs.remove(packageName);
settings.removeConfigOverride(packageName);
}
}
@@ -1422,7 +1452,7 @@ public final class GameManagerService extends IGameManagerService.Stub {
// If not, set the game mode to standard
int gameMode = getGameMode(packageName, userId);
final GamePackageConfiguration config = getConfig(packageName);
final GamePackageConfiguration config = getConfig(packageName, userId);
final int newGameMode = getNewGameMode(gameMode, config);
if (gameMode != newGameMode) {
setGameMode(packageName, GameManager.GAME_MODE_STANDARD, userId);
@@ -1460,8 +1490,8 @@ public final class GameManagerService extends IGameManagerService.Stub {
/**
* Returns the string listing all the interventions currently set to a game.
*/
public String getInterventionList(String packageName) {
final GamePackageConfiguration packageConfig = getConfig(packageName);
public String getInterventionList(String packageName, int userId) {
final GamePackageConfiguration packageConfig = getConfig(packageName, userId);
final StringBuilder listStrSb = new StringBuilder();
if (packageConfig == null) {
listStrSb.append("\n No intervention found for package ")
@@ -1487,7 +1517,7 @@ public final class GameManagerService extends IGameManagerService.Stub {
synchronized (mDeviceConfigLock) {
for (final String packageName : packageNames) {
final GamePackageConfiguration config =
new GamePackageConfiguration(packageName, userId);
new GamePackageConfiguration(mPackageManager, packageName, userId);
if (config.isActive()) {
if (DEBUG) {
Slog.i(TAG, "Adding config: " + config.toString());
@@ -1526,15 +1556,11 @@ public final class GameManagerService extends IGameManagerService.Stub {
updateInterventions(packageName, gameMode, userId);
}
}
sendUserMessage(userId, WRITE_GAME_MODE_INTERVENTION_LIST_FILE,
"UPDATE_CONFIGS_FOR_USERS", 0 /*delayMillis*/);
} catch (Exception e) {
Slog.e(TAG, "Failed to update configs for user " + userId + ": " + e);
}
final Message msg = mHandler.obtainMessage(WRITE_GAME_MODE_INTERVENTION_LIST_FILE);
msg.obj = userId;
if (!mHandler.hasEqualMessages(WRITE_GAME_MODE_INTERVENTION_LIST_FILE, userId)) {
mHandler.sendMessage(msg);
}
}
/*
@@ -1556,7 +1582,7 @@ public final class GameManagerService extends IGameManagerService.Stub {
final StringBuilder sb = new StringBuilder();
final List<String> installedGamesList = getInstalledGamePackageNamesByAllUsers(userId);
for (final String packageName : installedGamesList) {
GamePackageConfiguration packageConfig = getConfig(packageName);
GamePackageConfiguration packageConfig = getConfig(packageName, userId);
if (packageConfig == null) {
continue;
}
@@ -1634,11 +1660,12 @@ public final class GameManagerService extends IGameManagerService.Stub {
/**
* @hide
*/
@VisibleForTesting
public GamePackageConfiguration getConfig(String packageName) {
public GamePackageConfiguration getConfig(String packageName, int userId) {
GamePackageConfiguration packageConfig = null;
synchronized (mOverrideConfigLock) {
packageConfig = mOverrideConfigs.get(packageName);
synchronized (mLock) {
if (mSettings.containsKey(userId)) {
packageConfig = mSettings.get(userId).getConfigOverride(packageName);
}
}
if (packageConfig == null) {
synchronized (mDeviceConfigLock) {
@@ -1679,9 +1706,6 @@ public final class GameManagerService extends IGameManagerService.Stub {
break;
case ACTION_PACKAGE_REMOVED:
if (!intent.getBooleanExtra(EXTRA_REPLACING, false)) {
synchronized (mOverrideConfigLock) {
mOverrideConfigs.remove(packageName);
}
synchronized (mDeviceConfigLock) {
mConfigs.remove(packageName);
}
@@ -1689,6 +1713,11 @@ public final class GameManagerService extends IGameManagerService.Stub {
if (mSettings.containsKey(userId)) {
mSettings.get(userId).removeGame(packageName);
}
sendUserMessage(userId, WRITE_SETTINGS,
Intent.ACTION_PACKAGE_REMOVED, WRITE_DELAY_MILLIS);
sendUserMessage(userId,
WRITE_GAME_MODE_INTERVENTION_LIST_FILE,
Intent.ACTION_PACKAGE_REMOVED, WRITE_DELAY_MILLIS);
}
}
break;

View File

@@ -27,6 +27,8 @@ import android.util.Xml;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.XmlUtils;
import com.android.server.app.GameManagerService.GamePackageConfiguration;
import com.android.server.app.GameManagerService.GamePackageConfiguration.GameModeConfiguration;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -39,10 +41,11 @@ import java.util.Map;
/**
* Persists all GameService related settings.
*
* @hide
*/
public class GameManagerSettings {
public static final String TAG = "GameManagerService_GameManagerSettings";
// The XML file follows the below format:
// <?xml>
// <packages>
@@ -53,8 +56,14 @@ public class GameManagerSettings {
private static final String TAG_PACKAGE = "package";
private static final String TAG_PACKAGES = "packages";
private static final String TAG_GAME_MODE_CONFIG = "gameModeConfig";
private static final String ATTR_NAME = "name";
private static final String ATTR_GAME_MODE = "gameMode";
private static final String ATTR_SCALING = "scaling";
private static final String ATTR_FPS = "fps";
private static final String ATTR_USE_ANGLE = "useAngle";
private static final String ATTR_LOADING_BOOST_DURATION = "loadingBoost";
private final File mSystemDir;
@VisibleForTesting
@@ -62,6 +71,8 @@ public class GameManagerSettings {
// PackageName -> GameMode
private final ArrayMap<String, Integer> mGameModes = new ArrayMap<>();
// PackageName -> GamePackageConfiguration
private final ArrayMap<String, GamePackageConfiguration> mConfigOverrides = new ArrayMap<>();
GameManagerSettings(File dataDir) {
mSystemDir = new File(dataDir, "system");
@@ -74,7 +85,7 @@ public class GameManagerSettings {
}
/**
* Return the game mode of a given package.
* Returns the game mode of a given package.
* This operation must be synced with an external lock.
*/
int getGameModeLocked(String packageName) {
@@ -85,7 +96,7 @@ public class GameManagerSettings {
}
/**
* Set the game mode of a given package.
* Sets the game mode of a given package.
* This operation must be synced with an external lock.
*/
void setGameModeLocked(String packageName, int gameMode) {
@@ -93,15 +104,40 @@ public class GameManagerSettings {
}
/**
* Remove the game mode of a given package.
* Removes all game settings of a given package.
* This operation must be synced with an external lock.
*/
void removeGame(String packageName) {
mGameModes.remove(packageName);
mConfigOverrides.remove(packageName);
}
/**
* Write all current game service settings into disk.
* Returns the game config override of a given package or null if absent.
* This operation must be synced with an external lock.
*/
GamePackageConfiguration getConfigOverride(String packageName) {
return mConfigOverrides.get(packageName);
}
/**
* Sets the game config override of a given package.
* This operation must be synced with an external lock.
*/
void setConfigOverride(String packageName, GamePackageConfiguration configOverride) {
mConfigOverrides.put(packageName, configOverride);
}
/**
* Removes the game mode config override of a given package.
* This operation must be synced with an external lock.
*/
void removeConfigOverride(String packageName) {
mConfigOverrides.remove(packageName);
}
/**
* Writes all current game service settings into disk.
* This operation must be synced with an external lock.
*/
void writePersistentDataLocked() {
@@ -115,9 +151,11 @@ public class GameManagerSettings {
serializer.startTag(null, TAG_PACKAGES);
for (Map.Entry<String, Integer> entry : mGameModes.entrySet()) {
String packageName = entry.getKey();
serializer.startTag(null, TAG_PACKAGE);
serializer.attribute(null, ATTR_NAME, entry.getKey());
serializer.attribute(null, ATTR_NAME, packageName);
serializer.attributeInt(null, ATTR_GAME_MODE, entry.getValue());
writeGameModeConfigTags(serializer, mConfigOverrides.get(packageName));
serializer.endTag(null, TAG_PACKAGE);
}
serializer.endTag(null, TAG_PACKAGES);
@@ -133,20 +171,41 @@ public class GameManagerSettings {
return;
} catch (java.io.IOException e) {
mSettingsFile.failWrite(fstr);
Slog.wtf(GameManagerService.TAG, "Unable to write game manager service settings, "
Slog.wtf(TAG, "Unable to write game manager service settings, "
+ "current changes will be lost at reboot", e);
}
}
private void writeGameModeConfigTags(TypedXmlSerializer serializer,
GamePackageConfiguration config) throws IOException {
if (config == null) {
return;
}
final int[] gameModes = config.getAvailableGameModes();
for (final int mode : gameModes) {
final GameModeConfiguration modeConfig = config.getGameModeConfiguration(mode);
if (modeConfig != null) {
serializer.startTag(null, TAG_GAME_MODE_CONFIG);
serializer.attributeInt(null, ATTR_GAME_MODE, mode);
serializer.attributeBoolean(null, ATTR_USE_ANGLE, modeConfig.getUseAngle());
serializer.attribute(null, ATTR_FPS, modeConfig.getFpsStr());
serializer.attributeFloat(null, ATTR_SCALING, modeConfig.getScaling());
serializer.attributeInt(null, ATTR_LOADING_BOOST_DURATION,
modeConfig.getLoadingBoostDuration());
serializer.endTag(null, TAG_GAME_MODE_CONFIG);
}
}
}
/**
* Read game service settings from the disk.
* Reads game service settings from the disk.
* This operation must be synced with an external lock.
*/
boolean readPersistentDataLocked() {
mGameModes.clear();
if (!mSettingsFile.exists()) {
Slog.v(GameManagerService.TAG, "Settings file doesn't exists, skip reading");
Slog.v(TAG, "Settings file doesn't exist, skip reading");
return false;
}
@@ -160,8 +219,7 @@ public class GameManagerSettings {
// Do nothing
}
if (type != XmlPullParser.START_TAG) {
Slog.wtf(GameManagerService.TAG,
"No start tag found in package manager settings");
Slog.wtf(TAG, "No start tag found in package manager settings");
return false;
}
@@ -173,35 +231,107 @@ public class GameManagerSettings {
}
String tagName = parser.getName();
if (tagName.equals(TAG_PACKAGE)) {
if (type == XmlPullParser.START_TAG && TAG_PACKAGE.equals(tagName)) {
readPackage(parser);
} else {
Slog.w(GameManagerService.TAG, "Unknown element: " + parser.getName());
XmlUtils.skipCurrentTag(parser);
Slog.w(TAG, "Unknown element under packages tag: " + tagName + " with type: "
+ type);
}
}
} catch (XmlPullParserException | java.io.IOException e) {
Slog.wtf(GameManagerService.TAG, "Error reading package manager settings", e);
Slog.wtf(TAG, "Error reading package manager settings", e);
return false;
}
return true;
}
// this must be called on tag of type START_TAG.
private void readPackage(TypedXmlPullParser parser) throws XmlPullParserException,
IOException {
String name = null;
final String name = parser.getAttributeValue(null, ATTR_NAME);
if (name == null) {
Slog.wtf(TAG, "No package name found in package tag");
XmlUtils.skipCurrentTag(parser);
return;
}
int gameMode = GameManager.GAME_MODE_UNSUPPORTED;
try {
name = parser.getAttributeValue(null, ATTR_NAME);
gameMode = parser.getAttributeInt(null, ATTR_GAME_MODE);
} catch (XmlPullParserException e) {
Slog.wtf(GameManagerService.TAG, "Error reading game mode", e);
Slog.wtf(TAG, "Invalid game mode in package tag: "
+ parser.getAttributeValue(null, ATTR_GAME_MODE), e);
return;
}
if (name != null) {
mGameModes.put(name, gameMode);
} else {
XmlUtils.skipCurrentTag(parser);
mGameModes.put(name, gameMode);
final int packageTagDepth = parser.getDepth();
int type;
final GamePackageConfiguration config = new GamePackageConfiguration(name);
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
&& (type != XmlPullParser.END_TAG
|| parser.getDepth() > packageTagDepth)) {
if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
continue;
}
final String tagName = parser.getName();
if (type == XmlPullParser.START_TAG && TAG_GAME_MODE_CONFIG.equals(tagName)) {
readGameModeConfig(parser, config);
} else {
XmlUtils.skipCurrentTag(parser);
Slog.w(TAG, "Unknown element under package tag: " + tagName + " with type: "
+ type);
}
}
if (config.getAvailableGameModes().length > 1) {
mConfigOverrides.put(name, config);
}
}
// this must be called on tag of type START_TAG.
private void readGameModeConfig(TypedXmlPullParser parser, GamePackageConfiguration config) {
final int gameMode;
try {
gameMode = parser.getAttributeInt(null, ATTR_GAME_MODE);
} catch (XmlPullParserException e) {
Slog.wtf(TAG, "Invalid game mode value in config tag: " + parser.getAttributeValue(null,
ATTR_GAME_MODE), e);
return;
}
final GameModeConfiguration modeConfig = config.getOrAddDefaultGameModeConfiguration(
gameMode);
try {
final float scaling = parser.getAttributeFloat(null, ATTR_SCALING);
modeConfig.setScaling(scaling);
} catch (XmlPullParserException e) {
final String rawScaling = parser.getAttributeValue(null, ATTR_SCALING);
if (rawScaling != null) {
Slog.wtf(TAG, "Invalid scaling value in config tag: " + rawScaling, e);
}
}
final String fps = parser.getAttributeValue(null, ATTR_FPS);
modeConfig.setFpsStr(fps != null ? fps : GameModeConfiguration.DEFAULT_FPS);
try {
final boolean useAngle = parser.getAttributeBoolean(null, ATTR_USE_ANGLE);
modeConfig.setUseAngle(useAngle);
} catch (XmlPullParserException e) {
final String rawUseAngle = parser.getAttributeValue(null, ATTR_USE_ANGLE);
if (rawUseAngle != null) {
Slog.wtf(TAG, "Invalid useAngle value in config tag: " + rawUseAngle, e);
}
}
try {
final int loadingBoostDuration = parser.getAttributeInt(null,
ATTR_LOADING_BOOST_DURATION);
modeConfig.setLoadingBoostDuration(loadingBoostDuration);
} catch (XmlPullParserException e) {
final String rawLoadingBoost = parser.getAttributeValue(null,
ATTR_LOADING_BOOST_DURATION);
if (rawLoadingBoost != null) {
Slog.wtf(TAG, "Invalid loading boost in config tag: " + rawLoadingBoost, e);
}
}
}
}

View File

@@ -81,7 +81,8 @@ public class GameManagerShellCommand extends ShellCommand {
final GameManagerService gameManagerService = (GameManagerService)
ServiceManager.getService(Context.GAME_SERVICE);
final String listStr = gameManagerService.getInterventionList(packageName);
final String listStr = gameManagerService.getInterventionList(packageName,
ActivityManager.getCurrentUser());
if (listStr == null) {
pw.println("No interventions found for " + packageName);

View File

@@ -17,8 +17,10 @@
package com.android.server.app;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
import static com.android.server.app.GameManagerService.WRITE_SETTINGS;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThrows;
@@ -35,11 +37,15 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.Manifest;
import android.annotation.Nullable;
import android.app.GameManager;
import android.app.GameModeInfo;
import android.app.GameState;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.ContextWrapper;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
@@ -97,6 +103,7 @@ public class GameManagerServiceTests {
private PowerManagerInternal mMockPowerManager;
@Mock
private UserManager mMockUserManager;
private BroadcastReceiver mShutDownActionReceiver;
// Stolen from ConnectivityServiceTest.MockContext
class MockContext extends ContextWrapper {
@@ -165,6 +172,12 @@ public class GameManagerServiceTests {
}
throw new UnsupportedOperationException("Couldn't find system service: " + name);
}
@Override
public Intent registerReceiver(@Nullable BroadcastReceiver receiver, IntentFilter filter) {
mShutDownActionReceiver = receiver;
return null;
}
}
@Before
@@ -200,15 +213,16 @@ public class GameManagerServiceTests {
@After
public void tearDown() throws Exception {
LocalServices.removeServiceForTest(PowerManagerInternal.class);
GameManagerService gameManagerService = new GameManagerService(mMockContext);
if (mMockingSession != null) {
mMockingSession.finishMocking();
}
deleteFolder(InstrumentationRegistry.getTargetContext().getFilesDir());
}
private void startUser(GameManagerService gameManagerService, int userId) {
UserInfo userInfo = new UserInfo(userId, "name", 0);
gameManagerService.onUserStarting(new SystemService.TargetUser(userInfo));
gameManagerService.onUserStarting(new SystemService.TargetUser(userInfo),
InstrumentationRegistry.getContext().getFilesDir());
mTestLooper.dispatchAll();
}
@@ -584,7 +598,7 @@ public class GameManagerServiceTests {
gameManagerService.updateConfigsForUser(USER_ID_1, true, mPackageName);
}
GameManagerService.GamePackageConfiguration config =
gameManagerService.getConfig(mPackageName);
gameManagerService.getConfig(mPackageName, USER_ID_1);
assertEquals(scaling, config.getGameModeConfiguration(gameMode).getScaling(), 0.01f);
}
@@ -594,7 +608,7 @@ public class GameManagerServiceTests {
// Validate GamePackageConfiguration returns the correct value.
GameManagerService.GamePackageConfiguration config =
gameManagerService.getConfig(mPackageName);
gameManagerService.getConfig(mPackageName, USER_ID_1);
assertEquals(config.getGameModeConfiguration(gameMode).getUseAngle(), angleEnabled);
// Validate GameManagerService.isAngleEnabled() returns the correct value.
@@ -607,7 +621,7 @@ public class GameManagerServiceTests {
// Validate GamePackageConfiguration returns the correct value.
GameManagerService.GamePackageConfiguration config =
gameManagerService.getConfig(mPackageName);
gameManagerService.getConfig(mPackageName, USER_ID_1);
assertEquals(
loadingBoost, config.getGameModeConfiguration(gameMode).getLoadingBoostDuration());
@@ -623,7 +637,7 @@ public class GameManagerServiceTests {
gameManagerService.updateConfigsForUser(USER_ID_1, true, mPackageName);
}
GameManagerService.GamePackageConfiguration config =
gameManagerService.getConfig(mPackageName);
gameManagerService.getConfig(mPackageName, USER_ID_1);
assertEquals(fps, config.getGameModeConfiguration(gameMode).getFps());
}
@@ -1049,7 +1063,7 @@ public class GameManagerServiceTests {
mTestLooper.getLooper());
startUser(gameManagerService, USER_ID_1);
GameManagerService.GamePackageConfiguration config =
gameManagerService.getConfig(mPackageName);
gameManagerService.getConfig(mPackageName, USER_ID_1);
assertEquals(90,
config.getGameModeConfiguration(GameManager.GAME_MODE_PERFORMANCE).getFps());
assertEquals(30, config.getGameModeConfiguration(GameManager.GAME_MODE_BATTERY).getFps());
@@ -1064,7 +1078,7 @@ public class GameManagerServiceTests {
mTestLooper.getLooper());
startUser(gameManagerService, USER_ID_1);
GameManagerService.GamePackageConfiguration config =
gameManagerService.getConfig(mPackageName);
gameManagerService.getConfig(mPackageName, USER_ID_1);
assertEquals(0,
config.getGameModeConfiguration(GameManager.GAME_MODE_PERFORMANCE).getFps());
assertEquals(0, config.getGameModeConfiguration(GameManager.GAME_MODE_BATTERY).getFps());
@@ -1092,7 +1106,7 @@ public class GameManagerServiceTests {
startUser(gameManagerService, USER_ID_1);
gameManagerService.updateConfigsForUser(USER_ID_1, true, mPackageName);
GameManagerService.GamePackageConfiguration config =
gameManagerService.getConfig(mPackageName);
gameManagerService.getConfig(mPackageName, USER_ID_1);
assertNull(config.getGameModeConfiguration(GameManager.GAME_MODE_PERFORMANCE));
}
@@ -1339,10 +1353,15 @@ public class GameManagerServiceTests {
mTestLooper.dispatchAll();
/* Expected fileOutput (order may vary)
# user 1001:
com.android.app2 <UID> 0 2 angle=0,scaling=0.5,fps=90 3 angle=0,scaling=0.5,fps=60
com.android.app1 <UID> 1 2 angle=0,scaling=0.5,fps=90 3 angle=0,scaling=0.7,fps=30
com.android.app0 <UID> 0 2 angle=0,scaling=0.6,fps=120 3 angle=0,scaling=0.7,fps=30
# user 1002:
com.android.app2 <UID> 0 2 angle=0,scaling=0.5,fps=90 3 angle=0,scaling=0.7,fps=30
com.android.app1 <UID> 1 2 angle=0,scaling=0.5,fps=90 3 angle=0,scaling=0.7,fps=30
com.android.app0 <UID> 0 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.
*/
@@ -1386,7 +1405,7 @@ public class GameManagerServiceTests {
assertEquals(splitLine[3], "2");
assertEquals(splitLine[4], "angle=0,scaling=0.5,fps=90");
assertEquals(splitLine[5], "3");
assertEquals(splitLine[6], "angle=0,scaling=0.5,fps=60");
assertEquals(splitLine[6], "angle=0,scaling=0.7,fps=30");
splitLine = fileOutput.get(1).split("\\s+");
assertEquals(splitLine[0], "com.android.app1");
assertEquals(splitLine[2], "3");
@@ -1398,7 +1417,7 @@ public class GameManagerServiceTests {
assertEquals(splitLine[0], "com.android.app0");
assertEquals(splitLine[2], "0");
assertEquals(splitLine[3], "2");
assertEquals(splitLine[4], "angle=0,scaling=0.6,fps=120");
assertEquals(splitLine[4], "angle=0,scaling=0.5,fps=90");
assertEquals(splitLine[5], "3");
assertEquals(splitLine[6], "angle=0,scaling=0.7,fps=30");
@@ -1493,12 +1512,52 @@ public class GameManagerServiceTests {
@Test
public void testGetResolutionScalingFactor_noUserId() {
mockModifyGameModeDenied();
mockModifyGameModeGranted();
mockDeviceConfigAll();
GameManagerService gameManagerService =
new GameManagerService(mMockContext, mTestLooper.getLooper());
startUser(gameManagerService, USER_ID_2);
assertEquals(-1f, gameManagerService.getResolutionScalingFactor(mPackageName,
GameManager.GAME_MODE_BATTERY, USER_ID_1), 0.001f);
assertThrows(IllegalArgumentException.class, () -> {
gameManagerService.getResolutionScalingFactor(mPackageName,
GameManager.GAME_MODE_BATTERY, USER_ID_1);
});
}
@Test
public void testWritingSettingFile_onShutdown() throws InterruptedException {
mockModifyGameModeGranted();
mockDeviceConfigAll();
GameManagerService gameManagerService = new GameManagerService(mMockContext);
gameManagerService.onBootCompleted();
startUser(gameManagerService, USER_ID_1);
Thread.sleep(500);
gameManagerService.setGameModeConfigOverride("com.android.app1", USER_ID_1,
GameManager.GAME_MODE_BATTERY, "60", "0.5");
gameManagerService.setGameMode("com.android.app1", USER_ID_1,
GameManager.GAME_MODE_PERFORMANCE);
GameManagerSettings settings = new GameManagerSettings(
InstrumentationRegistry.getContext().getFilesDir());
Thread.sleep(500);
// no data written as delayed messages are queued
assertFalse(settings.readPersistentDataLocked());
assertTrue(gameManagerService.mHandler.hasEqualMessages(WRITE_SETTINGS, USER_ID_1));
Intent shutdown = new Intent();
shutdown.setAction(Intent.ACTION_SHUTDOWN);
mShutDownActionReceiver.onReceive(mMockContext, shutdown);
Thread.sleep(500);
// data is written on processing new message with no delay on shutdown,
// and all queued messages should be removed
assertTrue(settings.readPersistentDataLocked());
assertFalse(gameManagerService.mHandler.hasEqualMessages(WRITE_SETTINGS, USER_ID_1));
}
private static void deleteFolder(File folder) {
File[] files = folder.listFiles();
if (files != null) {
for (File file : files) {
deleteFolder(file);
}
}
folder.delete();
}
}

View File

@@ -16,9 +16,13 @@
package com.android.server.app;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import android.app.GameManager;
import android.content.Context;
import android.platform.test.annotations.Presubmit;
import android.util.AtomicFile;
@@ -28,6 +32,9 @@ import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.server.app.GameManagerService.GamePackageConfiguration;
import com.android.server.app.GameManagerService.GamePackageConfiguration.GameModeConfiguration;
import org.junit.After;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -66,6 +73,9 @@ public class GameManagerServiceSettingsTests {
+ " <package name=\"com.android.app1\" gameMode=\"1\">\n"
+ " </package>\n"
+ " <package name=\"com.android.app2\" gameMode=\"2\">\n"
+ " <gameModeConfig gameMode=\"2\" scaling=\"0.99\" "
+ "useAngle=\"true\" fps=\"90\" loadingBoost=\"123\"></gameModeConfig>\n"
+ " <gameModeConfig gameMode=\"3\"></gameModeConfig>\n"
+ " </package>\n"
+ " <package name=\"com.android.app3\" gameMode=\"3\">\n"
+ " </package>\n"
@@ -92,40 +102,159 @@ public class GameManagerServiceSettingsTests {
writeGameServiceXml();
}
private void verifyGameServiceSettingsData(GameManagerSettings settings) {
assertThat(settings.getGameModeLocked(PACKAGE_NAME_1), is(1));
assertThat(settings.getGameModeLocked(PACKAGE_NAME_2), is(2));
assertThat(settings.getGameModeLocked(PACKAGE_NAME_3), is(3));
}
@After
public void tearDown() throws Exception {
deleteFolder(InstrumentationRegistry.getTargetContext().getFilesDir());
}
/** read in data and verify */
@Test
public void testReadGameServiceSettings() {
/* write out files and read */
writeOldFiles();
final Context context = InstrumentationRegistry.getContext();
GameManagerSettings settings = new GameManagerSettings(context.getFilesDir());
assertThat(settings.readPersistentDataLocked(), is(true));
verifyGameServiceSettingsData(settings);
assertTrue(settings.readPersistentDataLocked());
// test game modes
assertEquals(1, settings.getGameModeLocked(PACKAGE_NAME_1));
assertEquals(2, settings.getGameModeLocked(PACKAGE_NAME_2));
assertEquals(3, settings.getGameModeLocked(PACKAGE_NAME_3));
// test game mode configs
assertNull(settings.getConfigOverride(PACKAGE_NAME_1));
assertNull(settings.getConfigOverride(PACKAGE_NAME_3));
final GamePackageConfiguration config = settings.getConfigOverride(PACKAGE_NAME_2);
assertNotNull(config);
assertNull(config.getGameModeConfiguration(GameManager.GAME_MODE_STANDARD));
final GameModeConfiguration performanceConfig = config.getGameModeConfiguration(
GameManager.GAME_MODE_PERFORMANCE);
assertNotNull(performanceConfig);
assertEquals(performanceConfig.getScaling(), 0.99, 0.01f);
assertEquals(performanceConfig.getLoadingBoostDuration(), 123);
assertEquals(performanceConfig.getFpsStr(), "90");
assertTrue(performanceConfig.getUseAngle());
final GameModeConfiguration batteryConfig = config.getGameModeConfiguration(
GameManager.GAME_MODE_BATTERY);
assertNotNull(batteryConfig);
assertEquals(batteryConfig.getScaling(), GameModeConfiguration.DEFAULT_SCALING, 0.01f);
assertEquals(batteryConfig.getLoadingBoostDuration(),
GameModeConfiguration.DEFAULT_LOADING_BOOST_DURATION);
assertEquals(batteryConfig.getFpsStr(), GameModeConfiguration.DEFAULT_FPS);
assertFalse(batteryConfig.getUseAngle());
}
/** read in data, write it out, and read it back in. Verify same. */
@Test
public void testReadGameServiceSettings_invalidConfigAttributes() {
writeFile(new File(InstrumentationRegistry.getContext().getFilesDir(),
"system/game-manager-service.xml"),
("<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
+ "<packages>\n"
+ " <package name=\"com.android.app1\" gameMode=\"1\">\n"
+ " <gameModeConfig gameMode=\"3\" scaling=\"invalid\" "
+ "useAngle=\"invalid\" fps=\"invalid\" "
+ "loadingBoost=\"invalid\"></gameModeConfig>\n"
+ " </package>\n"
+ "</packages>\n").getBytes());
final Context context = InstrumentationRegistry.getContext();
GameManagerSettings settings = new GameManagerSettings(context.getFilesDir());
assertTrue(settings.readPersistentDataLocked());
final GamePackageConfiguration config = settings.getConfigOverride(PACKAGE_NAME_1);
assertNotNull(config);
final GameModeConfiguration batteryConfig = config.getGameModeConfiguration(
GameManager.GAME_MODE_BATTERY);
assertNotNull(batteryConfig);
assertEquals(batteryConfig.getScaling(), GameModeConfiguration.DEFAULT_SCALING, 0.01f);
assertEquals(batteryConfig.getLoadingBoostDuration(),
GameModeConfiguration.DEFAULT_LOADING_BOOST_DURATION);
assertEquals(batteryConfig.getFpsStr(), "invalid");
assertFalse(batteryConfig.getUseAngle());
}
@Test
public void testReadGameServiceSettings_invalidTags() {
writeFile(new File(InstrumentationRegistry.getContext().getFilesDir(),
"system/game-manager-service.xml"),
("<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
+ "<packages>\n"
+ " <package gameMode=\"1\">\n"
+ " </package>\n"
+ " <package name=\"com.android.app2\" gameMode=\"2\">\n"
+ " <unknown></unknown>"
+ " <gameModeConfig gameMode=\"3\" fps=\"90\"></gameModeConfig>\n"
+ " foo bar"
+ " </package>\n"
+ " <unknownTag></unknownTag>\n"
+ " foo bar\n"
+ " <package name=\"com.android.app3\" gameMode=\"3\">\n"
+ " </package>\n"
+ "</packages>\n").getBytes());
final Context context = InstrumentationRegistry.getContext();
GameManagerSettings settings = new GameManagerSettings(context.getFilesDir());
assertTrue(settings.readPersistentDataLocked());
assertEquals(0, settings.getGameModeLocked(PACKAGE_NAME_1));
assertEquals(2, settings.getGameModeLocked(PACKAGE_NAME_2));
assertEquals(3, settings.getGameModeLocked(PACKAGE_NAME_3));
final GamePackageConfiguration config = settings.getConfigOverride(PACKAGE_NAME_2);
assertNotNull(config);
final GameModeConfiguration batteryConfig = config.getGameModeConfiguration(
GameManager.GAME_MODE_BATTERY);
assertNotNull(batteryConfig);
assertEquals(batteryConfig.getFpsStr(), "90");
}
@Test
public void testWriteGameServiceSettings() {
// write out files and read
writeOldFiles();
final Context context = InstrumentationRegistry.getContext();
GameManagerSettings settings = new GameManagerSettings(context.getFilesDir());
assertThat(settings.readPersistentDataLocked(), is(true));
// write out, read back in and verify the same
// set package settings and write out to file
settings.setGameModeLocked(PACKAGE_NAME_1, GameManager.GAME_MODE_BATTERY);
settings.setGameModeLocked(PACKAGE_NAME_2, GameManager.GAME_MODE_PERFORMANCE);
settings.setGameModeLocked(PACKAGE_NAME_3, GameManager.GAME_MODE_STANDARD);
GamePackageConfiguration config = new GamePackageConfiguration(PACKAGE_NAME_2);
GameModeConfiguration performanceConfig = config.getOrAddDefaultGameModeConfiguration(
GameManager.GAME_MODE_PERFORMANCE);
performanceConfig.setLoadingBoostDuration(321);
performanceConfig.setScaling(0.66f);
performanceConfig.setUseAngle(true);
performanceConfig.setFpsStr("60");
GameModeConfiguration batteryConfig = config.getOrAddDefaultGameModeConfiguration(
GameManager.GAME_MODE_BATTERY);
batteryConfig.setScaling(0.77f);
settings.setConfigOverride(PACKAGE_NAME_2, config);
settings.writePersistentDataLocked();
assertThat(settings.readPersistentDataLocked(), is(true));
verifyGameServiceSettingsData(settings);
// clear the settings in memory
settings.removeGame(PACKAGE_NAME_1);
settings.removeGame(PACKAGE_NAME_2);
settings.removeGame(PACKAGE_NAME_3);
// read back in and verify
assertTrue(settings.readPersistentDataLocked());
assertEquals(3, settings.getGameModeLocked(PACKAGE_NAME_1));
assertEquals(2, settings.getGameModeLocked(PACKAGE_NAME_2));
assertEquals(1, settings.getGameModeLocked(PACKAGE_NAME_3));
config = settings.getConfigOverride(PACKAGE_NAME_1);
assertNull(config);
config = settings.getConfigOverride(PACKAGE_NAME_2);
assertNotNull(config);
batteryConfig = config.getGameModeConfiguration(GameManager.GAME_MODE_BATTERY);
assertNotNull(batteryConfig);
assertEquals(batteryConfig.getScaling(), 0.77f, 0.01f);
assertEquals(batteryConfig.getLoadingBoostDuration(),
GameModeConfiguration.DEFAULT_LOADING_BOOST_DURATION);
assertEquals(batteryConfig.getFpsStr(), GameModeConfiguration.DEFAULT_FPS);
assertFalse(batteryConfig.getUseAngle());
performanceConfig = config.getGameModeConfiguration(GameManager.GAME_MODE_PERFORMANCE);
assertNotNull(performanceConfig);
assertEquals(performanceConfig.getScaling(), 0.66f, 0.01f);
assertEquals(performanceConfig.getLoadingBoostDuration(), 321);
assertEquals(performanceConfig.getFpsStr(), "60");
assertTrue(performanceConfig.getUseAngle());
}
}