diff --git a/services/core/java/com/android/server/app/GameManagerService.java b/services/core/java/com/android/server/app/GameManagerService.java index 854b818c095f3..1302e226eba31 100644 --- a/services/core/java/com/android/server/app/GameManagerService.java +++ b/services/core/java/com/android/server/app/GameManagerService.java @@ -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 mSettings = new ArrayMap<>(); @GuardedBy("mDeviceConfigLock") private final ArrayMap mConfigs = new ArrayMap<>(); - @GuardedBy("mOverrideConfigLock") - private final ArrayMap 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 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 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; diff --git a/services/core/java/com/android/server/app/GameManagerSettings.java b/services/core/java/com/android/server/app/GameManagerSettings.java index 1455a6119c7ea..116249895dcd8 100644 --- a/services/core/java/com/android/server/app/GameManagerSettings.java +++ b/services/core/java/com/android/server/app/GameManagerSettings.java @@ -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: // // @@ -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 mGameModes = new ArrayMap<>(); + // PackageName -> GamePackageConfiguration + private final ArrayMap 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 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); + } } } } diff --git a/services/core/java/com/android/server/app/GameManagerShellCommand.java b/services/core/java/com/android/server/app/GameManagerShellCommand.java index 6e289b10258ea..cdbffbeae80bf 100644 --- a/services/core/java/com/android/server/app/GameManagerShellCommand.java +++ b/services/core/java/com/android/server/app/GameManagerShellCommand.java @@ -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); 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 b53a2c69708d9..9022db83d5ce8 100644 --- a/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java +++ b/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java @@ -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 0 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 + # user 1002: + com.android.app2 0 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 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(); } } 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 5be05d8aa55a7..e8dd541b05313 100644 --- a/services/tests/servicestests/src/com/android/server/app/GameManagerServiceSettingsTests.java +++ b/services/tests/servicestests/src/com/android/server/app/GameManagerServiceSettingsTests.java @@ -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 { + " \n" + " \n" + " \n" + + " \n" + + " \n" + " \n" + " \n" + " \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"), + ("" + + "\n" + + " \n" + + " \n" + + " \n" + + "\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"), + ("" + + "\n" + + " \n" + + " \n" + + " \n" + + " " + + " \n" + + " foo bar" + + " \n" + + " \n" + + " foo bar\n" + + " \n" + + " \n" + + "\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()); } }