Add support for manifest based opt-in for game modes
Also add support for games to opt-out of specific interventions via manifest. Manifest metadata tag name attributes need to be one of: "com.android.graphics.intervention.wm.allowDownscale" "com.android.app.gamemode.performance.enabled" "com.android.app.gamemode.battery.enabled" and the value attribute must be "true" Bug: b/184849707, b/186582669 Test: atest com.android.server.app.GameManagerServiceTests Change-Id: I0d09c1e1fb9ac704bb92cf65000d6f78a67e42fd
This commit is contained in:
@@ -56,6 +56,7 @@ import android.os.ShellCallback;
|
||||
import android.provider.DeviceConfig;
|
||||
import android.provider.DeviceConfig.Properties;
|
||||
import android.util.ArrayMap;
|
||||
import android.util.ArraySet;
|
||||
import android.util.KeyValueListParser;
|
||||
import android.util.Slog;
|
||||
|
||||
@@ -180,9 +181,14 @@ public final class GameManagerService extends IGameManagerService.Stub {
|
||||
break;
|
||||
}
|
||||
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);
|
||||
loadDeviceConfigLocked();
|
||||
break;
|
||||
final int userId = (int) msg.obj;
|
||||
final String[] packageNames = getInstalledGamePackageNames(userId);
|
||||
updateConfigsForUser(userId, packageNames);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -198,28 +204,8 @@ public final class GameManagerService extends IGameManagerService.Stub {
|
||||
|
||||
@Override
|
||||
public void onPropertiesChanged(Properties properties) {
|
||||
synchronized (mDeviceConfigLock) {
|
||||
for (final String packageName : properties.getKeyset()) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
final String[] packageNames = properties.getKeyset().toArray(new String[0]);
|
||||
updateConfigsForUser(mContext.getUserId(), packageNames);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -228,80 +214,166 @@ public final class GameManagerService extends IGameManagerService.Stub {
|
||||
}
|
||||
}
|
||||
|
||||
private static class GameModeConfiguration {
|
||||
public static final String TAG = "GameManagerService_GameModeConfiguration";
|
||||
public static final String MODE_KEY = "mode";
|
||||
public static final String SCALING_KEY = "downscaleFactor";
|
||||
|
||||
private final @GameMode int mGameMode;
|
||||
private final String mScaling;
|
||||
|
||||
private GameModeConfiguration(@NonNull int gameMode,
|
||||
@NonNull String scaling) {
|
||||
mGameMode = gameMode;
|
||||
mScaling = 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() {
|
||||
return mGameMode;
|
||||
}
|
||||
|
||||
public String getScaling() {
|
||||
return mScaling;
|
||||
}
|
||||
|
||||
public boolean isValid() {
|
||||
return (mGameMode == GameManager.GAME_MODE_PERFORMANCE
|
||||
|| mGameMode == GameManager.GAME_MODE_BATTERY) && getCompatChangeId() != 0;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "[Game Mode:" + mGameMode + ",Scaling:" + mScaling + "]";
|
||||
}
|
||||
|
||||
public long getCompatChangeId() {
|
||||
switch (mScaling) {
|
||||
case "0.5":
|
||||
return DOWNSCALE_50;
|
||||
case "0.6":
|
||||
return DOWNSCALE_60;
|
||||
case "0.7":
|
||||
return DOWNSCALE_70;
|
||||
case "0.8":
|
||||
return DOWNSCALE_80;
|
||||
case "0.9":
|
||||
return DOWNSCALE_90;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
private static class GamePackageConfiguration {
|
||||
/**
|
||||
* 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;
|
||||
|
||||
private GamePackageConfiguration(String packageName) {
|
||||
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 MODE_KEY = "mode";
|
||||
public static final String SCALING_KEY = "downscaleFactor";
|
||||
public static final String DEFAULT_SCALING = "1.0";
|
||||
|
||||
private final @GameMode int mGameMode;
|
||||
private final String mScaling;
|
||||
|
||||
GameModeConfiguration(KeyValueListParser parser) {
|
||||
mGameMode = parser.getInt(MODE_KEY, GameManager.GAME_MODE_UNSUPPORTED);
|
||||
mScaling = !mAllowDownscale || isGameModeOptedIn(mGameMode)
|
||||
? DEFAULT_SCALING : parser.getString(SCALING_KEY, DEFAULT_SCALING);
|
||||
}
|
||||
|
||||
public int getGameMode() {
|
||||
return mGameMode;
|
||||
}
|
||||
|
||||
public String getScaling() {
|
||||
return mScaling;
|
||||
}
|
||||
|
||||
public boolean isValid() {
|
||||
return (mGameMode == GameManager.GAME_MODE_PERFORMANCE
|
||||
|| mGameMode == GameManager.GAME_MODE_BATTERY)
|
||||
&& (!mAllowDownscale || getCompatChangeId() != 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
public String toString() {
|
||||
return "[Game Mode:" + mGameMode + ",Scaling:" + mScaling + "]";
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the corresponding compat change id for the current scaling string.
|
||||
*/
|
||||
public long getCompatChangeId() {
|
||||
switch (mScaling) {
|
||||
case "0.5":
|
||||
return DOWNSCALE_50;
|
||||
case "0.6":
|
||||
return DOWNSCALE_60;
|
||||
case "0.7":
|
||||
return DOWNSCALE_70;
|
||||
case "0.8":
|
||||
return DOWNSCALE_80;
|
||||
case "0.9":
|
||||
return DOWNSCALE_90;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
public String getPackageName() {
|
||||
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() {
|
||||
if (mModeConfigs.keySet().size() > 0) {
|
||||
return mModeConfigs.keySet().stream()
|
||||
.mapToInt(Integer::intValue).toArray();
|
||||
ArraySet<Integer> modeSet = new ArraySet<>(mModeConfigs.keySet());
|
||||
if (mBatteryModeOptedIn) {
|
||||
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};
|
||||
}
|
||||
@@ -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() {
|
||||
return mModeConfigs.size() > 0;
|
||||
return mModeConfigs.size() > 0 || mBatteryModeOptedIn || mPerfModeOptedIn;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
@@ -534,8 +584,6 @@ public final class GameManagerService extends IGameManagerService.Stub {
|
||||
@VisibleForTesting
|
||||
void onBootCompleted() {
|
||||
Slog.d(TAG, "onBootCompleted");
|
||||
final Message msg = mHandler.obtainMessage(POPULATE_GAME_MODE_SETTINGS);
|
||||
mHandler.sendMessage(msg);
|
||||
}
|
||||
|
||||
void onUserStarting(int userId) {
|
||||
@@ -549,6 +597,9 @@ public final class GameManagerService extends IGameManagerService.Stub {
|
||||
mSettings.put(userId, userSettings);
|
||||
userSettings.readPersistentDataLocked();
|
||||
}
|
||||
final Message msg = mHandler.obtainMessage(POPULATE_GAME_MODE_SETTINGS);
|
||||
msg.obj = userId;
|
||||
mHandler.sendMessage(msg);
|
||||
}
|
||||
|
||||
void onUserStopping(int userId) {
|
||||
@@ -562,24 +613,14 @@ public final class GameManagerService extends IGameManagerService.Stub {
|
||||
}
|
||||
}
|
||||
|
||||
void loadDeviceConfigLocked() {
|
||||
final List<PackageInfo> packages = mPackageManager.getInstalledPackages(0);
|
||||
final String[] packageNames = packages.stream().map(e -> e.packageName)
|
||||
.toArray(String[]::new);
|
||||
synchronized (mDeviceConfigLock) {
|
||||
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) {
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
@VisibleForTesting
|
||||
public void disableCompatScale(String packageName) {
|
||||
final long uid = Binder.clearCallingIdentity();
|
||||
try {
|
||||
Slog.i(TAG, "Disabling downscale for " + packageName);
|
||||
final ArrayMap<Long, PackageOverride> overrides = new ArrayMap<>();
|
||||
overrides.put(DOWNSCALED, COMPAT_DISABLED);
|
||||
final CompatibilityOverrideConfig changeConfig = new CompatibilityOverrideConfig(
|
||||
@@ -597,6 +638,7 @@ public final class GameManagerService extends IGameManagerService.Stub {
|
||||
private void enableCompatScale(String packageName, long scaleId) {
|
||||
final long uid = Binder.clearCallingIdentity();
|
||||
try {
|
||||
Slog.i(TAG, "Enabling downscale: " + scaleId + " for " + packageName);
|
||||
final ArrayMap<Long, PackageOverride> overrides = new ArrayMap<>();
|
||||
overrides.put(DOWNSCALED, COMPAT_ENABLED);
|
||||
overrides.put(DOWNSCALE_50, COMPAT_DISABLED);
|
||||
@@ -622,21 +664,25 @@ public final class GameManagerService extends IGameManagerService.Stub {
|
||||
if (gameMode == GameManager.GAME_MODE_STANDARD
|
||||
|| gameMode == GameManager.GAME_MODE_UNSUPPORTED) {
|
||||
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;
|
||||
}
|
||||
if (DEBUG) {
|
||||
Slog.v(TAG, dumpDeviceConfigs());
|
||||
}
|
||||
final GamePackageConfiguration packageConfig = mConfigs.get(packageName);
|
||||
if (packageConfig == null) {
|
||||
Slog.w(TAG, "Package configuration not found for " + packageName);
|
||||
if (packageConfig.isGameModeOptedIn(gameMode)) {
|
||||
disableCompatScale(packageName);
|
||||
return;
|
||||
}
|
||||
final GameModeConfiguration modeConfig = packageConfig.getGameModeConfiguration(
|
||||
gameMode);
|
||||
final GamePackageConfiguration.GameModeConfiguration modeConfig =
|
||||
packageConfig.getGameModeConfiguration(gameMode);
|
||||
if (modeConfig == null) {
|
||||
Slog.w(TAG, "Game mode " + gameMode + " not found for " + packageName);
|
||||
Slog.i(TAG, "Game mode " + gameMode + " not found for " + packageName);
|
||||
return;
|
||||
}
|
||||
long scaleId = modeConfig.getCompatChangeId();
|
||||
@@ -645,23 +691,64 @@ public final class GameManagerService extends IGameManagerService.Stub {
|
||||
+ packageName);
|
||||
return;
|
||||
}
|
||||
Slog.i(TAG, "Enabling downscale: " + scaleId + " for " + packageName);
|
||||
|
||||
enableCompatScale(packageName, scaleId);
|
||||
}
|
||||
}
|
||||
|
||||
private void putConfig(GamePackageConfiguration config) {
|
||||
if (config.isValid()) {
|
||||
if (DEBUG) {
|
||||
Slog.i(TAG, "Adding config: " + config.toString());
|
||||
/**
|
||||
* @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 (DEBUG) {
|
||||
Slog.i(TAG, "Adding config: " + config.toString());
|
||||
}
|
||||
mConfigs.put(packageName, config);
|
||||
} else {
|
||||
Slog.w(TAG, "Invalid package config for "
|
||||
+ config.getPackageName() + ":" + config.toString());
|
||||
mConfigs.remove(packageName);
|
||||
}
|
||||
}
|
||||
}
|
||||
mConfigs.put(config.getPackageName(), config);
|
||||
} else {
|
||||
Slog.w(TAG, "Invalid package config for "
|
||||
+ config.getPackageName() + ":" + config.toString());
|
||||
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() {
|
||||
final IntentFilter packageFilter = new IntentFilter();
|
||||
packageFilter.addAction(ACTION_PACKAGE_ADDED);
|
||||
@@ -677,16 +764,7 @@ public final class GameManagerService extends IGameManagerService.Stub {
|
||||
switch (intent.getAction()) {
|
||||
case ACTION_PACKAGE_ADDED:
|
||||
case ACTION_PACKAGE_CHANGED:
|
||||
synchronized (mDeviceConfigLock) {
|
||||
Properties properties = DeviceConfig.getProperties(
|
||||
DeviceConfig.NAMESPACE_GAME_OVERLAY, packageName);
|
||||
for (String key : properties.getKeyset()) {
|
||||
GamePackageConfiguration config =
|
||||
GamePackageConfiguration.fromProperties(key,
|
||||
properties);
|
||||
putConfig(config);
|
||||
}
|
||||
}
|
||||
updateConfigsForUser(mContext.getUserId(), packageName);
|
||||
break;
|
||||
case ACTION_PACKAGE_REMOVED:
|
||||
disableCompatScale(packageName);
|
||||
|
||||
@@ -19,6 +19,7 @@ package com.android.server.app;
|
||||
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
@@ -32,8 +33,10 @@ import android.content.ContextWrapper;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Bundle;
|
||||
import android.platform.test.annotations.Presubmit;
|
||||
import android.provider.DeviceConfig;
|
||||
import android.util.ArraySet;
|
||||
|
||||
import androidx.test.InstrumentationRegistry;
|
||||
import androidx.test.filters.SmallTest;
|
||||
@@ -140,15 +143,19 @@ public class GameManagerServiceTests {
|
||||
applicationInfo.category = ApplicationInfo.CATEGORY_GAME;
|
||||
final PackageInfo pi = new PackageInfo();
|
||||
pi.packageName = mPackageName;
|
||||
pi.applicationInfo = applicationInfo;
|
||||
final List<PackageInfo> packages = new ArrayList<>();
|
||||
packages.add(pi);
|
||||
when(mMockPackageManager.getInstalledPackages(anyInt())).thenReturn(packages);
|
||||
when(mMockPackageManager.getInstalledPackagesAsUser(anyInt(), anyInt()))
|
||||
.thenReturn(packages);
|
||||
when(mMockPackageManager.getApplicationInfoAsUser(anyString(), anyInt(), anyInt()))
|
||||
.thenReturn(applicationInfo);
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() throws Exception {
|
||||
GameManagerService gameManagerService = new GameManagerService(mMockContext);
|
||||
gameManagerService.disableCompatScale(mPackageName);
|
||||
if (mMockingSession != null) {
|
||||
mMockingSession.finishMocking();
|
||||
}
|
||||
@@ -165,57 +172,95 @@ public class GameManagerServiceTests {
|
||||
}
|
||||
|
||||
private void mockDeviceConfigDefault() {
|
||||
DeviceConfig.Properties properties = new DeviceConfig.Properties.Builder(
|
||||
DeviceConfig.NAMESPACE_GAME_OVERLAY).setString(mPackageName, "").build();
|
||||
when(DeviceConfig.getProperties(anyString(), anyString()))
|
||||
.thenReturn(properties);
|
||||
when(DeviceConfig.getProperty(anyString(), anyString()))
|
||||
.thenReturn("");
|
||||
}
|
||||
|
||||
private void mockDeviceConfigNone() {
|
||||
DeviceConfig.Properties properties = new DeviceConfig.Properties.Builder(
|
||||
DeviceConfig.NAMESPACE_GAME_OVERLAY).build();
|
||||
when(DeviceConfig.getProperties(anyString(), anyString()))
|
||||
.thenReturn(properties);
|
||||
when(DeviceConfig.getProperty(anyString(), anyString()))
|
||||
.thenReturn(null);
|
||||
}
|
||||
|
||||
private void mockDeviceConfigPerformance() {
|
||||
String configString = "mode=2,downscaleFactor=0.5";
|
||||
DeviceConfig.Properties properties = new DeviceConfig.Properties.Builder(
|
||||
DeviceConfig.NAMESPACE_GAME_OVERLAY).setString(mPackageName, configString).build();
|
||||
when(DeviceConfig.getProperties(anyString(), anyString()))
|
||||
.thenReturn(properties);
|
||||
when(DeviceConfig.getProperty(anyString(), anyString()))
|
||||
.thenReturn(configString);
|
||||
}
|
||||
|
||||
private void mockDeviceConfigBattery() {
|
||||
String configString = "mode=3,downscaleFactor=0.7";
|
||||
DeviceConfig.Properties properties = new DeviceConfig.Properties.Builder(
|
||||
DeviceConfig.NAMESPACE_GAME_OVERLAY).setString(mPackageName, configString).build();
|
||||
when(DeviceConfig.getProperties(anyString(), anyString()))
|
||||
.thenReturn(properties);
|
||||
when(DeviceConfig.getProperty(anyString(), anyString()))
|
||||
.thenReturn(configString);
|
||||
}
|
||||
|
||||
private void mockDeviceConfigAll() {
|
||||
String configString = "mode=3,downscaleFactor=0.7:mode=2,downscaleFactor=0.5";
|
||||
DeviceConfig.Properties properties = new DeviceConfig.Properties.Builder(
|
||||
DeviceConfig.NAMESPACE_GAME_OVERLAY).setString(mPackageName, configString).build();
|
||||
when(DeviceConfig.getProperties(anyString(), anyString()))
|
||||
.thenReturn(properties);
|
||||
when(DeviceConfig.getProperty(anyString(), anyString()))
|
||||
.thenReturn(configString);
|
||||
}
|
||||
|
||||
private void mockDeviceConfigInvalid() {
|
||||
String configString = "mode=2,downscaleFactor=0.55";
|
||||
DeviceConfig.Properties properties = new DeviceConfig.Properties.Builder(
|
||||
DeviceConfig.NAMESPACE_GAME_OVERLAY).setString(mPackageName, configString).build();
|
||||
when(DeviceConfig.getProperties(anyString(), anyString()))
|
||||
.thenReturn(properties);
|
||||
when(DeviceConfig.getProperty(anyString(), anyString()))
|
||||
.thenReturn(configString);
|
||||
}
|
||||
|
||||
private void mockDeviceConfigMalformed() {
|
||||
String configString = "adsljckv=nin3rn9hn1231245:8795tq=21ewuydg";
|
||||
DeviceConfig.Properties properties = new DeviceConfig.Properties.Builder(
|
||||
DeviceConfig.NAMESPACE_GAME_OVERLAY).setString(mPackageName, configString).build();
|
||||
when(DeviceConfig.getProperties(anyString(), anyString()))
|
||||
.thenReturn(properties);
|
||||
when(DeviceConfig.getProperty(anyString(), anyString()))
|
||||
.thenReturn(configString);
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
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
|
||||
public void testDeviceConfigDefault() {
|
||||
mockDeviceConfigDefault();
|
||||
mockModifyGameModeGranted();
|
||||
GameManagerService gameManagerService = new GameManagerService(mMockContext);
|
||||
gameManagerService.onUserStarting(USER_ID_1);
|
||||
gameManagerService.loadDeviceConfigLocked();
|
||||
|
||||
int[] modes = gameManagerService.getAvailableGameModes(mPackageName);
|
||||
assertEquals(modes.length, 1);
|
||||
assertEquals(modes[0], GameManager.GAME_MODE_UNSUPPORTED);
|
||||
checkReportedModes(GameManager.GAME_MODE_UNSUPPORTED);
|
||||
}
|
||||
|
||||
/**
|
||||
* Phonesky device config does not exists.
|
||||
* Phenotype device config does not exists.
|
||||
*/
|
||||
@Test
|
||||
public void testDeviceConfigNone() {
|
||||
mockDeviceConfigNone();
|
||||
mockModifyGameModeGranted();
|
||||
GameManagerService gameManagerService = new GameManagerService(mMockContext);
|
||||
gameManagerService.onUserStarting(USER_ID_1);
|
||||
gameManagerService.loadDeviceConfigLocked();
|
||||
|
||||
int[] modes = gameManagerService.getAvailableGameModes(mPackageName);
|
||||
assertEquals(modes.length, 1);
|
||||
assertEquals(modes[0], GameManager.GAME_MODE_UNSUPPORTED);
|
||||
checkReportedModes(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
|
||||
public void testDeviceConfigPerformance() {
|
||||
mockDeviceConfigPerformance();
|
||||
mockModifyGameModeGranted();
|
||||
GameManagerService gameManagerService = new GameManagerService(mMockContext);
|
||||
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);
|
||||
checkReportedModes(GameManager.GAME_MODE_PERFORMANCE, GameManager.GAME_MODE_STANDARD);
|
||||
}
|
||||
|
||||
/**
|
||||
* Phonesky device config for battery mode exists and is valid.
|
||||
* Phenotype device config for battery mode exists and is valid.
|
||||
*/
|
||||
@Test
|
||||
public void testDeviceConfigBattery() {
|
||||
mockDeviceConfigBattery();
|
||||
mockModifyGameModeGranted();
|
||||
GameManagerService gameManagerService = new GameManagerService(mMockContext);
|
||||
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);
|
||||
checkReportedModes(GameManager.GAME_MODE_BATTERY, GameManager.GAME_MODE_STANDARD);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
public void testDeviceConfigAll() {
|
||||
mockDeviceConfigAll();
|
||||
mockModifyGameModeGranted();
|
||||
GameManagerService gameManagerService = new GameManagerService(mMockContext);
|
||||
gameManagerService.onUserStarting(USER_ID_1);
|
||||
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);
|
||||
checkReportedModes(GameManager.GAME_MODE_PERFORMANCE, GameManager.GAME_MODE_BATTERY,
|
||||
GameManager.GAME_MODE_STANDARD);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
public void testDeviceConfigInvalid() {
|
||||
mockDeviceConfigInvalid();
|
||||
mockModifyGameModeGranted();
|
||||
GameManagerService gameManagerService = new GameManagerService(mMockContext);
|
||||
gameManagerService.onUserStarting(USER_ID_1);
|
||||
gameManagerService.loadDeviceConfigLocked();
|
||||
|
||||
int[] modes = gameManagerService.getAvailableGameModes(mPackageName);
|
||||
assertEquals(modes.length, 1);
|
||||
assertEquals(modes[0], GameManager.GAME_MODE_UNSUPPORTED);
|
||||
checkReportedModes(GameManager.GAME_MODE_UNSUPPORTED);
|
||||
}
|
||||
|
||||
/**
|
||||
* Phonesky device config is garbage.
|
||||
* Phenotype device config is garbage.
|
||||
*/
|
||||
@Test
|
||||
public void testDeviceConfigMalformed() {
|
||||
mockDeviceConfigMalformed();
|
||||
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.onUserStarting(USER_ID_1);
|
||||
gameManagerService.loadDeviceConfigLocked();
|
||||
|
||||
int[] modes = gameManagerService.getAvailableGameModes(mPackageName);
|
||||
assertEquals(modes.length, 1);
|
||||
assertEquals(modes[0], GameManager.GAME_MODE_UNSUPPORTED);
|
||||
gameManagerService.updateConfigsForUser(USER_ID_1, mPackageName);
|
||||
GameManagerService.GamePackageConfiguration config =
|
||||
gameManagerService.getConfig(mPackageName);
|
||||
assertNull(config.getGameModeConfiguration(GameManager.GAME_MODE_PERFORMANCE));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user