Merge "Add support for manifest based opt-in for game modes" into sc-dev am: 6dbd56f8cf
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/14174058 Change-Id: I6ab4efa45bd12cca468b2d8f86206807a40c353a
This commit is contained in:
@@ -56,6 +56,7 @@ import android.os.ShellCallback;
|
|||||||
import android.provider.DeviceConfig;
|
import android.provider.DeviceConfig;
|
||||||
import android.provider.DeviceConfig.Properties;
|
import android.provider.DeviceConfig.Properties;
|
||||||
import android.util.ArrayMap;
|
import android.util.ArrayMap;
|
||||||
|
import android.util.ArraySet;
|
||||||
import android.util.KeyValueListParser;
|
import android.util.KeyValueListParser;
|
||||||
import android.util.Slog;
|
import android.util.Slog;
|
||||||
|
|
||||||
@@ -180,9 +181,14 @@ public final class GameManagerService extends IGameManagerService.Stub {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case POPULATE_GAME_MODE_SETTINGS: {
|
case POPULATE_GAME_MODE_SETTINGS: {
|
||||||
|
// Scan all game packages and re-enforce the configured compat mode overrides
|
||||||
|
// as the DeviceConfig may have be wiped/since last reboot and we can't risk
|
||||||
|
// having overrides configured for packages that no longer have any DeviceConfig
|
||||||
|
// and thus any way to escape compat mode.
|
||||||
removeMessages(POPULATE_GAME_MODE_SETTINGS, msg.obj);
|
removeMessages(POPULATE_GAME_MODE_SETTINGS, msg.obj);
|
||||||
loadDeviceConfigLocked();
|
final int userId = (int) msg.obj;
|
||||||
break;
|
final String[] packageNames = getInstalledGamePackageNames(userId);
|
||||||
|
updateConfigsForUser(userId, packageNames);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -198,28 +204,8 @@ public final class GameManagerService extends IGameManagerService.Stub {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onPropertiesChanged(Properties properties) {
|
public void onPropertiesChanged(Properties properties) {
|
||||||
synchronized (mDeviceConfigLock) {
|
final String[] packageNames = properties.getKeyset().toArray(new String[0]);
|
||||||
for (final String packageName : properties.getKeyset()) {
|
updateConfigsForUser(mContext.getUserId(), packageNames);
|
||||||
try {
|
|
||||||
// Check if the package is installed before caching it.
|
|
||||||
mPackageManager.getPackageInfo(packageName, 0);
|
|
||||||
final GamePackageConfiguration config =
|
|
||||||
GamePackageConfiguration.fromProperties(packageName, properties);
|
|
||||||
if (config.isValid()) {
|
|
||||||
putConfig(config);
|
|
||||||
} else {
|
|
||||||
// This means that we received a bad config, or the config was deleted.
|
|
||||||
Slog.i(TAG, "Removing config for: " + packageName);
|
|
||||||
mConfigs.remove(packageName);
|
|
||||||
disableCompatScale(packageName);
|
|
||||||
}
|
|
||||||
} catch (PackageManager.NameNotFoundException e) {
|
|
||||||
if (DEBUG) {
|
|
||||||
Slog.v(TAG, "Package name not found", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -228,25 +214,96 @@ public final class GameManagerService extends IGameManagerService.Stub {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class GameModeConfiguration {
|
/**
|
||||||
|
* GamePackageConfiguration manages all game mode config details for its associated package.
|
||||||
|
*/
|
||||||
|
@VisibleForTesting
|
||||||
|
public class GamePackageConfiguration {
|
||||||
|
public static final String TAG = "GameManagerService_GamePackageConfiguration";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Metadata that can be included in the app manifest to allow/disallow any window manager
|
||||||
|
* downscaling interventions. Default value is TRUE.
|
||||||
|
*/
|
||||||
|
public static final String METADATA_WM_ALLOW_DOWNSCALE =
|
||||||
|
"com.android.graphics.intervention.wm.allowDownscale";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Metadata that needs to be included in the app manifest to OPT-IN to PERFORMANCE mode.
|
||||||
|
* This means the app will assume full responsibility for the experience provided by this
|
||||||
|
* mode and the system will enable no window manager downscaling.
|
||||||
|
* Default value is FALSE
|
||||||
|
*/
|
||||||
|
public static final String METADATA_PERFORMANCE_MODE_ENABLE =
|
||||||
|
"com.android.app.gamemode.performance.enabled";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Metadata that needs to be included in the app manifest to OPT-IN to BATTERY mode.
|
||||||
|
* This means the app will assume full responsibility for the experience provided by this
|
||||||
|
* mode and the system will enable no window manager downscaling.
|
||||||
|
* Default value is FALSE
|
||||||
|
*/
|
||||||
|
public static final String METADATA_BATTERY_MODE_ENABLE =
|
||||||
|
"com.android.app.gamemode.battery.enabled";
|
||||||
|
|
||||||
|
private final String mPackageName;
|
||||||
|
private final ArrayMap<Integer, GameModeConfiguration> mModeConfigs;
|
||||||
|
private boolean mPerfModeOptedIn;
|
||||||
|
private boolean mBatteryModeOptedIn;
|
||||||
|
private boolean mAllowDownscale;
|
||||||
|
|
||||||
|
GamePackageConfiguration(String packageName, int userId) {
|
||||||
|
mPackageName = packageName;
|
||||||
|
mModeConfigs = new ArrayMap<>();
|
||||||
|
try {
|
||||||
|
final ApplicationInfo ai = mPackageManager.getApplicationInfoAsUser(packageName,
|
||||||
|
PackageManager.GET_META_DATA, userId);
|
||||||
|
if (ai.metaData != null) {
|
||||||
|
mPerfModeOptedIn = ai.metaData.getBoolean(METADATA_PERFORMANCE_MODE_ENABLE);
|
||||||
|
mBatteryModeOptedIn = ai.metaData.getBoolean(METADATA_BATTERY_MODE_ENABLE);
|
||||||
|
mAllowDownscale = ai.metaData.getBoolean(METADATA_WM_ALLOW_DOWNSCALE, true);
|
||||||
|
} else {
|
||||||
|
mPerfModeOptedIn = false;
|
||||||
|
mBatteryModeOptedIn = false;
|
||||||
|
mAllowDownscale = true;
|
||||||
|
}
|
||||||
|
} catch (PackageManager.NameNotFoundException e) {
|
||||||
|
Slog.e(TAG, "Failed to get package metadata", e);
|
||||||
|
}
|
||||||
|
final String configString = DeviceConfig.getProperty(
|
||||||
|
DeviceConfig.NAMESPACE_GAME_OVERLAY, packageName);
|
||||||
|
if (configString != null) {
|
||||||
|
final String[] gameModeConfigStrings = configString.split(":");
|
||||||
|
for (String gameModeConfigString : gameModeConfigStrings) {
|
||||||
|
try {
|
||||||
|
final KeyValueListParser parser = new KeyValueListParser(',');
|
||||||
|
parser.setString(gameModeConfigString);
|
||||||
|
addModeConfig(new GameModeConfiguration(parser));
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
Slog.e(TAG, "Invalid config string");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 TAG = "GameManagerService_GameModeConfiguration";
|
||||||
public static final String MODE_KEY = "mode";
|
public static final String MODE_KEY = "mode";
|
||||||
public static final String SCALING_KEY = "downscaleFactor";
|
public static final String SCALING_KEY = "downscaleFactor";
|
||||||
|
public static final String DEFAULT_SCALING = "1.0";
|
||||||
|
|
||||||
private final @GameMode int mGameMode;
|
private final @GameMode int mGameMode;
|
||||||
private final String mScaling;
|
private final String mScaling;
|
||||||
|
|
||||||
private GameModeConfiguration(@NonNull int gameMode,
|
GameModeConfiguration(KeyValueListParser parser) {
|
||||||
@NonNull String scaling) {
|
mGameMode = parser.getInt(MODE_KEY, GameManager.GAME_MODE_UNSUPPORTED);
|
||||||
mGameMode = gameMode;
|
mScaling = !mAllowDownscale || isGameModeOptedIn(mGameMode)
|
||||||
mScaling = scaling;
|
? DEFAULT_SCALING : parser.getString(SCALING_KEY, DEFAULT_SCALING);
|
||||||
}
|
|
||||||
|
|
||||||
public static GameModeConfiguration fromKeyValueListParser(KeyValueListParser parser) {
|
|
||||||
return new GameModeConfiguration(
|
|
||||||
parser.getInt(MODE_KEY, GameManager.GAME_MODE_UNSUPPORTED),
|
|
||||||
parser.getString(SCALING_KEY, "1.0")
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getGameMode() {
|
public int getGameMode() {
|
||||||
@@ -259,13 +316,20 @@ public final class GameManagerService extends IGameManagerService.Stub {
|
|||||||
|
|
||||||
public boolean isValid() {
|
public boolean isValid() {
|
||||||
return (mGameMode == GameManager.GAME_MODE_PERFORMANCE
|
return (mGameMode == GameManager.GAME_MODE_PERFORMANCE
|
||||||
|| mGameMode == GameManager.GAME_MODE_BATTERY) && getCompatChangeId() != 0;
|
|| mGameMode == GameManager.GAME_MODE_BATTERY)
|
||||||
|
&& (!mAllowDownscale || getCompatChangeId() != 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "[Game Mode:" + mGameMode + ",Scaling:" + mScaling + "]";
|
return "[Game Mode:" + mGameMode + ",Scaling:" + mScaling + "]";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the corresponding compat change id for the current scaling string.
|
||||||
|
*/
|
||||||
public long getCompatChangeId() {
|
public long getCompatChangeId() {
|
||||||
switch (mScaling) {
|
switch (mScaling) {
|
||||||
case "0.5":
|
case "0.5":
|
||||||
@@ -283,25 +347,33 @@ public final class GameManagerService extends IGameManagerService.Stub {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class GamePackageConfiguration {
|
|
||||||
public static final String TAG = "GameManagerService_GamePackageConfiguration";
|
|
||||||
|
|
||||||
private final String mPackageName;
|
|
||||||
private final ArrayMap<Integer, GameModeConfiguration> mModeConfigs;
|
|
||||||
|
|
||||||
private GamePackageConfiguration(String packageName) {
|
|
||||||
mPackageName = packageName;
|
|
||||||
mModeConfigs = new ArrayMap<>();
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getPackageName() {
|
public String getPackageName() {
|
||||||
return mPackageName;
|
return mPackageName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets whether a package has opted into a game mode via its manifest.
|
||||||
|
*
|
||||||
|
* @return True if the app package has specified in its metadata either:
|
||||||
|
* "com.android.app.gamemode.performance.enabled" or
|
||||||
|
* "com.android.app.gamemode.battery.enabled" with a value of "true"
|
||||||
|
*/
|
||||||
|
public boolean isGameModeOptedIn(@GameMode int gameMode) {
|
||||||
|
return (mBatteryModeOptedIn && gameMode == GameManager.GAME_MODE_BATTERY)
|
||||||
|
|| (mPerfModeOptedIn && gameMode == GameManager.GAME_MODE_PERFORMANCE);
|
||||||
|
}
|
||||||
|
|
||||||
public @GameMode int[] getAvailableGameModes() {
|
public @GameMode int[] getAvailableGameModes() {
|
||||||
if (mModeConfigs.keySet().size() > 0) {
|
ArraySet<Integer> modeSet = new ArraySet<>(mModeConfigs.keySet());
|
||||||
return mModeConfigs.keySet().stream()
|
if (mBatteryModeOptedIn) {
|
||||||
.mapToInt(Integer::intValue).toArray();
|
modeSet.add(GameManager.GAME_MODE_BATTERY);
|
||||||
|
}
|
||||||
|
if (mPerfModeOptedIn) {
|
||||||
|
modeSet.add(GameManager.GAME_MODE_PERFORMANCE);
|
||||||
|
}
|
||||||
|
if (modeSet.size() > 0) {
|
||||||
|
modeSet.add(GameManager.GAME_MODE_STANDARD);
|
||||||
|
return modeSet.stream().mapToInt(Integer::intValue).toArray();
|
||||||
}
|
}
|
||||||
return new int[]{GameManager.GAME_MODE_UNSUPPORTED};
|
return new int[]{GameManager.GAME_MODE_UNSUPPORTED};
|
||||||
}
|
}
|
||||||
@@ -327,30 +399,8 @@ public final class GameManagerService extends IGameManagerService.Stub {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new instance from a package name and DeviceConfig.Properties instance
|
|
||||||
*/
|
|
||||||
public static GamePackageConfiguration fromProperties(String key,
|
|
||||||
Properties properties) {
|
|
||||||
final GamePackageConfiguration packageConfig = new GamePackageConfiguration(key);
|
|
||||||
final String configString = properties.getString(key, "");
|
|
||||||
final String[] gameModeConfigStrings = configString.split(":");
|
|
||||||
for (String gameModeConfigString : gameModeConfigStrings) {
|
|
||||||
try {
|
|
||||||
final KeyValueListParser parser = new KeyValueListParser(',');
|
|
||||||
parser.setString(gameModeConfigString);
|
|
||||||
final GameModeConfiguration config =
|
|
||||||
GameModeConfiguration.fromKeyValueListParser(parser);
|
|
||||||
packageConfig.addModeConfig(config);
|
|
||||||
} catch (IllegalArgumentException e) {
|
|
||||||
Slog.e(TAG, "Invalid config string");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return packageConfig;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isValid() {
|
public boolean isValid() {
|
||||||
return mModeConfigs.size() > 0;
|
return mModeConfigs.size() > 0 || mBatteryModeOptedIn || mPerfModeOptedIn;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String toString() {
|
public String toString() {
|
||||||
@@ -534,8 +584,6 @@ public final class GameManagerService extends IGameManagerService.Stub {
|
|||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
void onBootCompleted() {
|
void onBootCompleted() {
|
||||||
Slog.d(TAG, "onBootCompleted");
|
Slog.d(TAG, "onBootCompleted");
|
||||||
final Message msg = mHandler.obtainMessage(POPULATE_GAME_MODE_SETTINGS);
|
|
||||||
mHandler.sendMessage(msg);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void onUserStarting(int userId) {
|
void onUserStarting(int userId) {
|
||||||
@@ -549,6 +597,9 @@ public final class GameManagerService extends IGameManagerService.Stub {
|
|||||||
mSettings.put(userId, userSettings);
|
mSettings.put(userId, userSettings);
|
||||||
userSettings.readPersistentDataLocked();
|
userSettings.readPersistentDataLocked();
|
||||||
}
|
}
|
||||||
|
final Message msg = mHandler.obtainMessage(POPULATE_GAME_MODE_SETTINGS);
|
||||||
|
msg.obj = userId;
|
||||||
|
mHandler.sendMessage(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
void onUserStopping(int userId) {
|
void onUserStopping(int userId) {
|
||||||
@@ -562,24 +613,14 @@ public final class GameManagerService extends IGameManagerService.Stub {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void loadDeviceConfigLocked() {
|
/**
|
||||||
final List<PackageInfo> packages = mPackageManager.getInstalledPackages(0);
|
* @hide
|
||||||
final String[] packageNames = packages.stream().map(e -> e.packageName)
|
*/
|
||||||
.toArray(String[]::new);
|
@VisibleForTesting
|
||||||
synchronized (mDeviceConfigLock) {
|
public void disableCompatScale(String packageName) {
|
||||||
final Properties properties = DeviceConfig.getProperties(
|
|
||||||
DeviceConfig.NAMESPACE_GAME_OVERLAY, packageNames);
|
|
||||||
for (String key : properties.getKeyset()) {
|
|
||||||
final GamePackageConfiguration config =
|
|
||||||
GamePackageConfiguration.fromProperties(key, properties);
|
|
||||||
putConfig(config);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void disableCompatScale(String packageName) {
|
|
||||||
final long uid = Binder.clearCallingIdentity();
|
final long uid = Binder.clearCallingIdentity();
|
||||||
try {
|
try {
|
||||||
|
Slog.i(TAG, "Disabling downscale for " + packageName);
|
||||||
final ArrayMap<Long, PackageOverride> overrides = new ArrayMap<>();
|
final ArrayMap<Long, PackageOverride> overrides = new ArrayMap<>();
|
||||||
overrides.put(DOWNSCALED, COMPAT_DISABLED);
|
overrides.put(DOWNSCALED, COMPAT_DISABLED);
|
||||||
final CompatibilityOverrideConfig changeConfig = new CompatibilityOverrideConfig(
|
final CompatibilityOverrideConfig changeConfig = new CompatibilityOverrideConfig(
|
||||||
@@ -597,6 +638,7 @@ public final class GameManagerService extends IGameManagerService.Stub {
|
|||||||
private void enableCompatScale(String packageName, long scaleId) {
|
private void enableCompatScale(String packageName, long scaleId) {
|
||||||
final long uid = Binder.clearCallingIdentity();
|
final long uid = Binder.clearCallingIdentity();
|
||||||
try {
|
try {
|
||||||
|
Slog.i(TAG, "Enabling downscale: " + scaleId + " for " + packageName);
|
||||||
final ArrayMap<Long, PackageOverride> overrides = new ArrayMap<>();
|
final ArrayMap<Long, PackageOverride> overrides = new ArrayMap<>();
|
||||||
overrides.put(DOWNSCALED, COMPAT_ENABLED);
|
overrides.put(DOWNSCALED, COMPAT_ENABLED);
|
||||||
overrides.put(DOWNSCALE_50, COMPAT_DISABLED);
|
overrides.put(DOWNSCALE_50, COMPAT_DISABLED);
|
||||||
@@ -622,21 +664,25 @@ public final class GameManagerService extends IGameManagerService.Stub {
|
|||||||
if (gameMode == GameManager.GAME_MODE_STANDARD
|
if (gameMode == GameManager.GAME_MODE_STANDARD
|
||||||
|| gameMode == GameManager.GAME_MODE_UNSUPPORTED) {
|
|| gameMode == GameManager.GAME_MODE_UNSUPPORTED) {
|
||||||
disableCompatScale(packageName);
|
disableCompatScale(packageName);
|
||||||
Slog.v(TAG, "Disabling downscale");
|
return;
|
||||||
|
}
|
||||||
|
final GamePackageConfiguration packageConfig = mConfigs.get(packageName);
|
||||||
|
if (packageConfig == null) {
|
||||||
|
disableCompatScale(packageName);
|
||||||
|
Slog.v(TAG, "Package configuration not found for " + packageName);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
Slog.v(TAG, dumpDeviceConfigs());
|
Slog.v(TAG, dumpDeviceConfigs());
|
||||||
}
|
}
|
||||||
final GamePackageConfiguration packageConfig = mConfigs.get(packageName);
|
if (packageConfig.isGameModeOptedIn(gameMode)) {
|
||||||
if (packageConfig == null) {
|
disableCompatScale(packageName);
|
||||||
Slog.w(TAG, "Package configuration not found for " + packageName);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
final GameModeConfiguration modeConfig = packageConfig.getGameModeConfiguration(
|
final GamePackageConfiguration.GameModeConfiguration modeConfig =
|
||||||
gameMode);
|
packageConfig.getGameModeConfiguration(gameMode);
|
||||||
if (modeConfig == null) {
|
if (modeConfig == null) {
|
||||||
Slog.w(TAG, "Game mode " + gameMode + " not found for " + packageName);
|
Slog.i(TAG, "Game mode " + gameMode + " not found for " + packageName);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
long scaleId = modeConfig.getCompatChangeId();
|
long scaleId = modeConfig.getCompatChangeId();
|
||||||
@@ -645,22 +691,63 @@ public final class GameManagerService extends IGameManagerService.Stub {
|
|||||||
+ packageName);
|
+ packageName);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Slog.i(TAG, "Enabling downscale: " + scaleId + " for " + packageName);
|
|
||||||
enableCompatScale(packageName, scaleId);
|
enableCompatScale(packageName, scaleId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void putConfig(GamePackageConfiguration config) {
|
/**
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
@VisibleForTesting
|
||||||
|
public void updateConfigsForUser(int userId, String ...packageNames) {
|
||||||
|
try {
|
||||||
|
synchronized (mDeviceConfigLock) {
|
||||||
|
for (String packageName : packageNames) {
|
||||||
|
GamePackageConfiguration config =
|
||||||
|
new GamePackageConfiguration(packageName, userId);
|
||||||
if (config.isValid()) {
|
if (config.isValid()) {
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
Slog.i(TAG, "Adding config: " + config.toString());
|
Slog.i(TAG, "Adding config: " + config.toString());
|
||||||
}
|
}
|
||||||
mConfigs.put(config.getPackageName(), config);
|
mConfigs.put(packageName, config);
|
||||||
} else {
|
} else {
|
||||||
Slog.w(TAG, "Invalid package config for "
|
Slog.w(TAG, "Invalid package config for "
|
||||||
+ config.getPackageName() + ":" + config.toString());
|
+ config.getPackageName() + ":" + config.toString());
|
||||||
|
mConfigs.remove(packageName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
for (String packageName : packageNames) {
|
||||||
|
synchronized (mLock) {
|
||||||
|
if (mSettings.containsKey(userId)) {
|
||||||
|
GameManagerSettings userSettings = mSettings.get(userId);
|
||||||
|
updateCompatModeDownscale(packageName,
|
||||||
|
userSettings.getGameModeLocked(packageName));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
Slog.e(TAG, "Failed to update compat modes for user: " + userId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String[] getInstalledGamePackageNames(int userId) {
|
||||||
|
final List<PackageInfo> packages =
|
||||||
|
mPackageManager.getInstalledPackagesAsUser(0, userId);
|
||||||
|
return packages.stream().filter(e -> e.applicationInfo != null && e.applicationInfo.category
|
||||||
|
== ApplicationInfo.CATEGORY_GAME)
|
||||||
|
.map(e -> e.packageName)
|
||||||
|
.toArray(String[]::new);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
@VisibleForTesting
|
||||||
|
public GamePackageConfiguration getConfig(String packageName) {
|
||||||
|
return mConfigs.get(packageName);
|
||||||
|
}
|
||||||
|
|
||||||
private void registerPackageReceiver() {
|
private void registerPackageReceiver() {
|
||||||
final IntentFilter packageFilter = new IntentFilter();
|
final IntentFilter packageFilter = new IntentFilter();
|
||||||
@@ -677,16 +764,7 @@ public final class GameManagerService extends IGameManagerService.Stub {
|
|||||||
switch (intent.getAction()) {
|
switch (intent.getAction()) {
|
||||||
case ACTION_PACKAGE_ADDED:
|
case ACTION_PACKAGE_ADDED:
|
||||||
case ACTION_PACKAGE_CHANGED:
|
case ACTION_PACKAGE_CHANGED:
|
||||||
synchronized (mDeviceConfigLock) {
|
updateConfigsForUser(mContext.getUserId(), packageName);
|
||||||
Properties properties = DeviceConfig.getProperties(
|
|
||||||
DeviceConfig.NAMESPACE_GAME_OVERLAY, packageName);
|
|
||||||
for (String key : properties.getKeyset()) {
|
|
||||||
GamePackageConfiguration config =
|
|
||||||
GamePackageConfiguration.fromProperties(key,
|
|
||||||
properties);
|
|
||||||
putConfig(config);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case ACTION_PACKAGE_REMOVED:
|
case ACTION_PACKAGE_REMOVED:
|
||||||
disableCompatScale(packageName);
|
disableCompatScale(packageName);
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ package com.android.server.app;
|
|||||||
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
|
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertNull;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
import static org.junit.Assert.fail;
|
import static org.junit.Assert.fail;
|
||||||
import static org.mockito.ArgumentMatchers.anyInt;
|
import static org.mockito.ArgumentMatchers.anyInt;
|
||||||
@@ -32,8 +33,10 @@ import android.content.ContextWrapper;
|
|||||||
import android.content.pm.ApplicationInfo;
|
import android.content.pm.ApplicationInfo;
|
||||||
import android.content.pm.PackageInfo;
|
import android.content.pm.PackageInfo;
|
||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
|
import android.os.Bundle;
|
||||||
import android.platform.test.annotations.Presubmit;
|
import android.platform.test.annotations.Presubmit;
|
||||||
import android.provider.DeviceConfig;
|
import android.provider.DeviceConfig;
|
||||||
|
import android.util.ArraySet;
|
||||||
|
|
||||||
import androidx.test.InstrumentationRegistry;
|
import androidx.test.InstrumentationRegistry;
|
||||||
import androidx.test.filters.SmallTest;
|
import androidx.test.filters.SmallTest;
|
||||||
@@ -140,15 +143,19 @@ public class GameManagerServiceTests {
|
|||||||
applicationInfo.category = ApplicationInfo.CATEGORY_GAME;
|
applicationInfo.category = ApplicationInfo.CATEGORY_GAME;
|
||||||
final PackageInfo pi = new PackageInfo();
|
final PackageInfo pi = new PackageInfo();
|
||||||
pi.packageName = mPackageName;
|
pi.packageName = mPackageName;
|
||||||
|
pi.applicationInfo = applicationInfo;
|
||||||
final List<PackageInfo> packages = new ArrayList<>();
|
final List<PackageInfo> packages = new ArrayList<>();
|
||||||
packages.add(pi);
|
packages.add(pi);
|
||||||
when(mMockPackageManager.getInstalledPackages(anyInt())).thenReturn(packages);
|
when(mMockPackageManager.getInstalledPackagesAsUser(anyInt(), anyInt()))
|
||||||
|
.thenReturn(packages);
|
||||||
when(mMockPackageManager.getApplicationInfoAsUser(anyString(), anyInt(), anyInt()))
|
when(mMockPackageManager.getApplicationInfoAsUser(anyString(), anyInt(), anyInt()))
|
||||||
.thenReturn(applicationInfo);
|
.thenReturn(applicationInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
@After
|
@After
|
||||||
public void tearDown() throws Exception {
|
public void tearDown() throws Exception {
|
||||||
|
GameManagerService gameManagerService = new GameManagerService(mMockContext);
|
||||||
|
gameManagerService.disableCompatScale(mPackageName);
|
||||||
if (mMockingSession != null) {
|
if (mMockingSession != null) {
|
||||||
mMockingSession.finishMocking();
|
mMockingSession.finishMocking();
|
||||||
}
|
}
|
||||||
@@ -165,57 +172,95 @@ public class GameManagerServiceTests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void mockDeviceConfigDefault() {
|
private void mockDeviceConfigDefault() {
|
||||||
DeviceConfig.Properties properties = new DeviceConfig.Properties.Builder(
|
when(DeviceConfig.getProperty(anyString(), anyString()))
|
||||||
DeviceConfig.NAMESPACE_GAME_OVERLAY).setString(mPackageName, "").build();
|
.thenReturn("");
|
||||||
when(DeviceConfig.getProperties(anyString(), anyString()))
|
|
||||||
.thenReturn(properties);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void mockDeviceConfigNone() {
|
private void mockDeviceConfigNone() {
|
||||||
DeviceConfig.Properties properties = new DeviceConfig.Properties.Builder(
|
when(DeviceConfig.getProperty(anyString(), anyString()))
|
||||||
DeviceConfig.NAMESPACE_GAME_OVERLAY).build();
|
.thenReturn(null);
|
||||||
when(DeviceConfig.getProperties(anyString(), anyString()))
|
|
||||||
.thenReturn(properties);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void mockDeviceConfigPerformance() {
|
private void mockDeviceConfigPerformance() {
|
||||||
String configString = "mode=2,downscaleFactor=0.5";
|
String configString = "mode=2,downscaleFactor=0.5";
|
||||||
DeviceConfig.Properties properties = new DeviceConfig.Properties.Builder(
|
when(DeviceConfig.getProperty(anyString(), anyString()))
|
||||||
DeviceConfig.NAMESPACE_GAME_OVERLAY).setString(mPackageName, configString).build();
|
.thenReturn(configString);
|
||||||
when(DeviceConfig.getProperties(anyString(), anyString()))
|
|
||||||
.thenReturn(properties);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void mockDeviceConfigBattery() {
|
private void mockDeviceConfigBattery() {
|
||||||
String configString = "mode=3,downscaleFactor=0.7";
|
String configString = "mode=3,downscaleFactor=0.7";
|
||||||
DeviceConfig.Properties properties = new DeviceConfig.Properties.Builder(
|
when(DeviceConfig.getProperty(anyString(), anyString()))
|
||||||
DeviceConfig.NAMESPACE_GAME_OVERLAY).setString(mPackageName, configString).build();
|
.thenReturn(configString);
|
||||||
when(DeviceConfig.getProperties(anyString(), anyString()))
|
|
||||||
.thenReturn(properties);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void mockDeviceConfigAll() {
|
private void mockDeviceConfigAll() {
|
||||||
String configString = "mode=3,downscaleFactor=0.7:mode=2,downscaleFactor=0.5";
|
String configString = "mode=3,downscaleFactor=0.7:mode=2,downscaleFactor=0.5";
|
||||||
DeviceConfig.Properties properties = new DeviceConfig.Properties.Builder(
|
when(DeviceConfig.getProperty(anyString(), anyString()))
|
||||||
DeviceConfig.NAMESPACE_GAME_OVERLAY).setString(mPackageName, configString).build();
|
.thenReturn(configString);
|
||||||
when(DeviceConfig.getProperties(anyString(), anyString()))
|
|
||||||
.thenReturn(properties);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void mockDeviceConfigInvalid() {
|
private void mockDeviceConfigInvalid() {
|
||||||
String configString = "mode=2,downscaleFactor=0.55";
|
String configString = "mode=2,downscaleFactor=0.55";
|
||||||
DeviceConfig.Properties properties = new DeviceConfig.Properties.Builder(
|
when(DeviceConfig.getProperty(anyString(), anyString()))
|
||||||
DeviceConfig.NAMESPACE_GAME_OVERLAY).setString(mPackageName, configString).build();
|
.thenReturn(configString);
|
||||||
when(DeviceConfig.getProperties(anyString(), anyString()))
|
|
||||||
.thenReturn(properties);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void mockDeviceConfigMalformed() {
|
private void mockDeviceConfigMalformed() {
|
||||||
String configString = "adsljckv=nin3rn9hn1231245:8795tq=21ewuydg";
|
String configString = "adsljckv=nin3rn9hn1231245:8795tq=21ewuydg";
|
||||||
DeviceConfig.Properties properties = new DeviceConfig.Properties.Builder(
|
when(DeviceConfig.getProperty(anyString(), anyString()))
|
||||||
DeviceConfig.NAMESPACE_GAME_OVERLAY).setString(mPackageName, configString).build();
|
.thenReturn(configString);
|
||||||
when(DeviceConfig.getProperties(anyString(), anyString()))
|
}
|
||||||
.thenReturn(properties);
|
|
||||||
|
private void mockGameModeOptInAll() throws Exception {
|
||||||
|
final ApplicationInfo applicationInfo = new ApplicationInfo();
|
||||||
|
Bundle metaDataBundle = new Bundle();
|
||||||
|
metaDataBundle.putBoolean(
|
||||||
|
GameManagerService.GamePackageConfiguration.METADATA_PERFORMANCE_MODE_ENABLE, true);
|
||||||
|
metaDataBundle.putBoolean(
|
||||||
|
GameManagerService.GamePackageConfiguration.METADATA_BATTERY_MODE_ENABLE, true);
|
||||||
|
applicationInfo.metaData = metaDataBundle;
|
||||||
|
when(mMockPackageManager.getApplicationInfoAsUser(anyString(), anyInt(), anyInt()))
|
||||||
|
.thenReturn(applicationInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void mockGameModeOptInPerformance() throws Exception {
|
||||||
|
final ApplicationInfo applicationInfo = new ApplicationInfo();
|
||||||
|
Bundle metaDataBundle = new Bundle();
|
||||||
|
metaDataBundle.putBoolean(
|
||||||
|
GameManagerService.GamePackageConfiguration.METADATA_PERFORMANCE_MODE_ENABLE, true);
|
||||||
|
applicationInfo.metaData = metaDataBundle;
|
||||||
|
when(mMockPackageManager.getApplicationInfoAsUser(anyString(), anyInt(), anyInt()))
|
||||||
|
.thenReturn(applicationInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void mockGameModeOptInBattery() throws Exception {
|
||||||
|
final ApplicationInfo applicationInfo = new ApplicationInfo();
|
||||||
|
Bundle metaDataBundle = new Bundle();
|
||||||
|
metaDataBundle.putBoolean(
|
||||||
|
GameManagerService.GamePackageConfiguration.METADATA_BATTERY_MODE_ENABLE, true);
|
||||||
|
applicationInfo.metaData = metaDataBundle;
|
||||||
|
when(mMockPackageManager.getApplicationInfoAsUser(anyString(), anyInt(), anyInt()))
|
||||||
|
.thenReturn(applicationInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void mockInterventionAllowDownscaleTrue() throws Exception {
|
||||||
|
final ApplicationInfo applicationInfo = new ApplicationInfo();
|
||||||
|
Bundle metaDataBundle = new Bundle();
|
||||||
|
metaDataBundle.putBoolean(
|
||||||
|
GameManagerService.GamePackageConfiguration.METADATA_WM_ALLOW_DOWNSCALE, true);
|
||||||
|
applicationInfo.metaData = metaDataBundle;
|
||||||
|
when(mMockPackageManager.getApplicationInfoAsUser(anyString(), anyInt(), anyInt()))
|
||||||
|
.thenReturn(applicationInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void mockInterventionAllowDownscaleFalse() throws Exception {
|
||||||
|
final ApplicationInfo applicationInfo = new ApplicationInfo();
|
||||||
|
Bundle metaDataBundle = new Bundle();
|
||||||
|
metaDataBundle.putBoolean(
|
||||||
|
GameManagerService.GamePackageConfiguration.METADATA_WM_ALLOW_DOWNSCALE, false);
|
||||||
|
applicationInfo.metaData = metaDataBundle;
|
||||||
|
when(mMockPackageManager.getApplicationInfoAsUser(anyString(), anyInt(), anyInt()))
|
||||||
|
.thenReturn(applicationInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -353,136 +398,209 @@ public class GameManagerServiceTests {
|
|||||||
gameManagerService.getGameMode(mPackageName, USER_ID_2));
|
gameManagerService.getGameMode(mPackageName, USER_ID_2));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void checkReportedModes(int ...requiredModes) {
|
||||||
|
GameManagerService gameManagerService = new GameManagerService(mMockContext);
|
||||||
|
gameManagerService.onUserStarting(USER_ID_1);
|
||||||
|
gameManagerService.updateConfigsForUser(USER_ID_1, mPackageName);
|
||||||
|
ArraySet<Integer> reportedModes = new ArraySet<>();
|
||||||
|
int[] modes = gameManagerService.getAvailableGameModes(mPackageName);
|
||||||
|
for (int mode : modes) {
|
||||||
|
reportedModes.add(mode);
|
||||||
|
}
|
||||||
|
assertEquals(requiredModes.length, reportedModes.size());
|
||||||
|
for (int requiredMode : reportedModes) {
|
||||||
|
assertTrue("Required game mode not supported: " + requiredMode,
|
||||||
|
reportedModes.contains(requiredMode));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkDownscaling(int gameMode, String scaling) {
|
||||||
|
GameManagerService gameManagerService = new GameManagerService(mMockContext);
|
||||||
|
gameManagerService.onUserStarting(USER_ID_1);
|
||||||
|
gameManagerService.updateConfigsForUser(USER_ID_1, mPackageName);
|
||||||
|
GameManagerService.GamePackageConfiguration config =
|
||||||
|
gameManagerService.getConfig(mPackageName);
|
||||||
|
assertEquals(config.getGameModeConfiguration(gameMode).getScaling(), scaling);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Phonesky device config exists, but is only propagating the default value.
|
* Phenotype device config exists, but is only propagating the default value.
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testDeviceConfigDefault() {
|
public void testDeviceConfigDefault() {
|
||||||
mockDeviceConfigDefault();
|
mockDeviceConfigDefault();
|
||||||
mockModifyGameModeGranted();
|
mockModifyGameModeGranted();
|
||||||
GameManagerService gameManagerService = new GameManagerService(mMockContext);
|
checkReportedModes(GameManager.GAME_MODE_UNSUPPORTED);
|
||||||
gameManagerService.onUserStarting(USER_ID_1);
|
|
||||||
gameManagerService.loadDeviceConfigLocked();
|
|
||||||
|
|
||||||
int[] modes = gameManagerService.getAvailableGameModes(mPackageName);
|
|
||||||
assertEquals(modes.length, 1);
|
|
||||||
assertEquals(modes[0], GameManager.GAME_MODE_UNSUPPORTED);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Phonesky device config does not exists.
|
* Phenotype device config does not exists.
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testDeviceConfigNone() {
|
public void testDeviceConfigNone() {
|
||||||
mockDeviceConfigNone();
|
mockDeviceConfigNone();
|
||||||
mockModifyGameModeGranted();
|
mockModifyGameModeGranted();
|
||||||
GameManagerService gameManagerService = new GameManagerService(mMockContext);
|
checkReportedModes(GameManager.GAME_MODE_UNSUPPORTED);
|
||||||
gameManagerService.onUserStarting(USER_ID_1);
|
|
||||||
gameManagerService.loadDeviceConfigLocked();
|
|
||||||
|
|
||||||
int[] modes = gameManagerService.getAvailableGameModes(mPackageName);
|
|
||||||
assertEquals(modes.length, 1);
|
|
||||||
assertEquals(modes[0], GameManager.GAME_MODE_UNSUPPORTED);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Phonesky device config for performance mode exists and is valid.
|
* Phenotype device config for performance mode exists and is valid.
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testDeviceConfigPerformance() {
|
public void testDeviceConfigPerformance() {
|
||||||
mockDeviceConfigPerformance();
|
mockDeviceConfigPerformance();
|
||||||
mockModifyGameModeGranted();
|
mockModifyGameModeGranted();
|
||||||
GameManagerService gameManagerService = new GameManagerService(mMockContext);
|
checkReportedModes(GameManager.GAME_MODE_PERFORMANCE, GameManager.GAME_MODE_STANDARD);
|
||||||
gameManagerService.onUserStarting(USER_ID_1);
|
|
||||||
gameManagerService.loadDeviceConfigLocked();
|
|
||||||
|
|
||||||
boolean perfModeExists = false;
|
|
||||||
int[] modes = gameManagerService.getAvailableGameModes(mPackageName);
|
|
||||||
for (int mode : modes) {
|
|
||||||
if (mode == GameManager.GAME_MODE_PERFORMANCE) {
|
|
||||||
perfModeExists = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
assertEquals(modes.length, 1);
|
|
||||||
assertTrue(perfModeExists);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Phonesky device config for battery mode exists and is valid.
|
* Phenotype device config for battery mode exists and is valid.
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testDeviceConfigBattery() {
|
public void testDeviceConfigBattery() {
|
||||||
mockDeviceConfigBattery();
|
mockDeviceConfigBattery();
|
||||||
mockModifyGameModeGranted();
|
mockModifyGameModeGranted();
|
||||||
GameManagerService gameManagerService = new GameManagerService(mMockContext);
|
checkReportedModes(GameManager.GAME_MODE_BATTERY, GameManager.GAME_MODE_STANDARD);
|
||||||
gameManagerService.onUserStarting(USER_ID_1);
|
|
||||||
gameManagerService.loadDeviceConfigLocked();
|
|
||||||
|
|
||||||
boolean batteryModeExists = false;
|
|
||||||
int[] modes = gameManagerService.getAvailableGameModes(mPackageName);
|
|
||||||
for (int mode : modes) {
|
|
||||||
if (mode == GameManager.GAME_MODE_BATTERY) {
|
|
||||||
batteryModeExists = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
assertEquals(modes.length, 1);
|
|
||||||
assertTrue(batteryModeExists);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Phonesky device configs for both battery and performance modes exists and are valid.
|
* Phenotype device configs for both battery and performance modes exists and are valid.
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testDeviceConfigAll() {
|
public void testDeviceConfigAll() {
|
||||||
mockDeviceConfigAll();
|
mockDeviceConfigAll();
|
||||||
mockModifyGameModeGranted();
|
mockModifyGameModeGranted();
|
||||||
GameManagerService gameManagerService = new GameManagerService(mMockContext);
|
checkReportedModes(GameManager.GAME_MODE_PERFORMANCE, GameManager.GAME_MODE_BATTERY,
|
||||||
gameManagerService.onUserStarting(USER_ID_1);
|
GameManager.GAME_MODE_STANDARD);
|
||||||
gameManagerService.loadDeviceConfigLocked();
|
|
||||||
|
|
||||||
boolean batteryModeExists = false;
|
|
||||||
boolean perfModeExists = false;
|
|
||||||
int[] modes = gameManagerService.getAvailableGameModes(mPackageName);
|
|
||||||
for (int mode : modes) {
|
|
||||||
if (mode == GameManager.GAME_MODE_BATTERY) {
|
|
||||||
batteryModeExists = true;
|
|
||||||
} else if (mode == GameManager.GAME_MODE_PERFORMANCE) {
|
|
||||||
perfModeExists = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
assertTrue(batteryModeExists);
|
|
||||||
assertTrue(perfModeExists);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Phonesky device config contains values that parse correctly but are not valid in game mode.
|
* Phenotype device config contains values that parse correctly but are not valid in game mode.
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testDeviceConfigInvalid() {
|
public void testDeviceConfigInvalid() {
|
||||||
mockDeviceConfigInvalid();
|
mockDeviceConfigInvalid();
|
||||||
mockModifyGameModeGranted();
|
mockModifyGameModeGranted();
|
||||||
GameManagerService gameManagerService = new GameManagerService(mMockContext);
|
checkReportedModes(GameManager.GAME_MODE_UNSUPPORTED);
|
||||||
gameManagerService.onUserStarting(USER_ID_1);
|
|
||||||
gameManagerService.loadDeviceConfigLocked();
|
|
||||||
|
|
||||||
int[] modes = gameManagerService.getAvailableGameModes(mPackageName);
|
|
||||||
assertEquals(modes.length, 1);
|
|
||||||
assertEquals(modes[0], GameManager.GAME_MODE_UNSUPPORTED);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Phonesky device config is garbage.
|
* Phenotype device config is garbage.
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testDeviceConfigMalformed() {
|
public void testDeviceConfigMalformed() {
|
||||||
mockDeviceConfigMalformed();
|
mockDeviceConfigMalformed();
|
||||||
mockModifyGameModeGranted();
|
mockModifyGameModeGranted();
|
||||||
|
checkReportedModes(GameManager.GAME_MODE_UNSUPPORTED);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Game modes are made available only through app manifest opt-in.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testGameModeOptInAll() throws Exception {
|
||||||
|
mockGameModeOptInAll();
|
||||||
|
mockDeviceConfigNone();
|
||||||
|
mockModifyGameModeGranted();
|
||||||
|
checkReportedModes(GameManager.GAME_MODE_PERFORMANCE, GameManager.GAME_MODE_BATTERY,
|
||||||
|
GameManager.GAME_MODE_STANDARD);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* BATTERY game mode is available through the app manifest opt-in.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testGameModeOptInBattery() throws Exception {
|
||||||
|
mockGameModeOptInBattery();
|
||||||
|
mockDeviceConfigNone();
|
||||||
|
mockModifyGameModeGranted();
|
||||||
|
checkReportedModes(GameManager.GAME_MODE_BATTERY, GameManager.GAME_MODE_STANDARD);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PERFORMANCE game mode is available through the app manifest opt-in.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testGameModeOptInPerformance() throws Exception {
|
||||||
|
mockGameModeOptInPerformance();
|
||||||
|
mockDeviceConfigNone();
|
||||||
|
mockModifyGameModeGranted();
|
||||||
|
checkReportedModes(GameManager.GAME_MODE_PERFORMANCE, GameManager.GAME_MODE_STANDARD);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* BATTERY game mode is available through the app manifest opt-in and PERFORMANCE game mode is
|
||||||
|
* available through Phenotype.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testGameModeOptInBatteryMixed() throws Exception {
|
||||||
|
mockGameModeOptInBattery();
|
||||||
|
mockDeviceConfigPerformance();
|
||||||
|
mockModifyGameModeGranted();
|
||||||
|
checkReportedModes(GameManager.GAME_MODE_PERFORMANCE, GameManager.GAME_MODE_BATTERY,
|
||||||
|
GameManager.GAME_MODE_STANDARD);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PERFORMANCE game mode is available through the app manifest opt-in and BATTERY game mode is
|
||||||
|
* available through Phenotype.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testGameModeOptInPerformanceMixed() throws Exception {
|
||||||
|
mockGameModeOptInPerformance();
|
||||||
|
mockDeviceConfigBattery();
|
||||||
|
mockModifyGameModeGranted();
|
||||||
|
checkReportedModes(GameManager.GAME_MODE_PERFORMANCE, GameManager.GAME_MODE_BATTERY,
|
||||||
|
GameManager.GAME_MODE_STANDARD);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PERFORMANCE game mode is configured through Phenotype. The app hasn't specified any metadata.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testInterventionAllowScalingDefault() throws Exception {
|
||||||
|
mockDeviceConfigPerformance();
|
||||||
|
mockModifyGameModeGranted();
|
||||||
|
checkDownscaling(GameManager.GAME_MODE_PERFORMANCE, "0.5");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PERFORMANCE game mode is configured through Phenotype. The app has opted-out of scaling.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testInterventionAllowDownscaleFalse() throws Exception {
|
||||||
|
mockDeviceConfigPerformance();
|
||||||
|
mockInterventionAllowDownscaleFalse();
|
||||||
|
mockModifyGameModeGranted();
|
||||||
|
checkDownscaling(GameManager.GAME_MODE_PERFORMANCE, "1.0");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PERFORMANCE game mode is configured through Phenotype. The app has redundantly specified
|
||||||
|
* the downscaling metadata default value of "true".
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testInterventionAllowDownscaleTrue() throws Exception {
|
||||||
|
mockDeviceConfigPerformance();
|
||||||
|
mockInterventionAllowDownscaleTrue();
|
||||||
|
mockModifyGameModeGranted();
|
||||||
|
checkDownscaling(GameManager.GAME_MODE_PERFORMANCE, "0.5");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PERFORMANCE game mode is configured through Phenotype, but the app has also opted into the
|
||||||
|
* same mode. No interventions for this game mode should be available in this case.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testDeviceConfigOptInOverlap() throws Exception {
|
||||||
|
mockDeviceConfigPerformance();
|
||||||
|
mockGameModeOptInPerformance();
|
||||||
|
mockModifyGameModeGranted();
|
||||||
GameManagerService gameManagerService = new GameManagerService(mMockContext);
|
GameManagerService gameManagerService = new GameManagerService(mMockContext);
|
||||||
gameManagerService.onUserStarting(USER_ID_1);
|
gameManagerService.onUserStarting(USER_ID_1);
|
||||||
gameManagerService.loadDeviceConfigLocked();
|
gameManagerService.updateConfigsForUser(USER_ID_1, mPackageName);
|
||||||
|
GameManagerService.GamePackageConfiguration config =
|
||||||
int[] modes = gameManagerService.getAvailableGameModes(mPackageName);
|
gameManagerService.getConfig(mPackageName);
|
||||||
assertEquals(modes.length, 1);
|
assertNull(config.getGameModeConfiguration(GameManager.GAME_MODE_PERFORMANCE));
|
||||||
assertEquals(modes[0], GameManager.GAME_MODE_UNSUPPORTED);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user