Merge changes I95e0ac4a,Ie29ac45d

* changes:
  Support dynamic game resolution scaling factor
  Add new GameManagerService methods to update and get scaling factor
This commit is contained in:
Xiang Wang
2022-08-08 17:38:20 +00:00
committed by Android (Google) Code Review
7 changed files with 378 additions and 264 deletions

View File

@@ -0,0 +1,30 @@
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.app;
/**
* Game manager local system service interface.
*
* @hide Only for use within the system server.
*/
public abstract class GameManagerInternal {
/**
* Used by the CompatModePackages to read game's compat scaling override.
*/
public abstract float getResolutionScalingFactor(String packageName, int userId);
}

View File

@@ -31,4 +31,6 @@ interface IGameManagerService {
void setGameState(String packageName, in GameState gameState, int userId);
GameModeInfo getGameModeInfo(String packageName, int userId);
void setGameServiceProvider(String packageName);
void updateResolutionScalingFactor(String packageName, int gameMode, float scalingFactor, int userId);
float getResolutionScalingFactor(String packageName, int gameMode, int userId);
}

View File

@@ -25,20 +25,6 @@ import static com.android.internal.R.styleable.GameModeConfig_allowGameDownscali
import static com.android.internal.R.styleable.GameModeConfig_allowGameFpsOverride;
import static com.android.internal.R.styleable.GameModeConfig_supportsBatteryGameMode;
import static com.android.internal.R.styleable.GameModeConfig_supportsPerformanceGameMode;
import static com.android.server.wm.CompatModePackages.DOWNSCALED;
import static com.android.server.wm.CompatModePackages.DOWNSCALE_30;
import static com.android.server.wm.CompatModePackages.DOWNSCALE_35;
import static com.android.server.wm.CompatModePackages.DOWNSCALE_40;
import static com.android.server.wm.CompatModePackages.DOWNSCALE_45;
import static com.android.server.wm.CompatModePackages.DOWNSCALE_50;
import static com.android.server.wm.CompatModePackages.DOWNSCALE_55;
import static com.android.server.wm.CompatModePackages.DOWNSCALE_60;
import static com.android.server.wm.CompatModePackages.DOWNSCALE_65;
import static com.android.server.wm.CompatModePackages.DOWNSCALE_70;
import static com.android.server.wm.CompatModePackages.DOWNSCALE_75;
import static com.android.server.wm.CompatModePackages.DOWNSCALE_80;
import static com.android.server.wm.CompatModePackages.DOWNSCALE_85;
import static com.android.server.wm.CompatModePackages.DOWNSCALE_90;
import android.Manifest;
import android.annotation.NonNull;
@@ -48,10 +34,10 @@ import android.annotation.UserIdInt;
import android.app.ActivityManager;
import android.app.GameManager;
import android.app.GameManager.GameMode;
import android.app.GameManagerInternal;
import android.app.GameModeInfo;
import android.app.GameState;
import android.app.IGameManagerService;
import android.app.compat.PackageOverride;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -75,9 +61,7 @@ import android.os.Looper;
import android.os.Message;
import android.os.PowerManagerInternal;
import android.os.Process;
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.ServiceManager;
import android.os.ShellCallback;
import android.os.UserManager;
import android.provider.DeviceConfig;
@@ -92,8 +76,6 @@ import android.util.Xml;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.compat.CompatibilityOverrideConfig;
import com.android.internal.compat.IPlatformCompat;
import com.android.internal.os.BackgroundThread;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.FrameworkStatsLog;
@@ -140,10 +122,6 @@ public final class GameManagerService extends IGameManagerService.Stub {
static final int WRITE_SETTINGS_DELAY = 10 * 1000; // 10 seconds
static final int LOADING_BOOST_MAX_DURATION = 5 * 1000; // 5 seconds
static final PackageOverride COMPAT_ENABLED = new PackageOverride.Builder().setEnabled(true)
.build();
static final PackageOverride COMPAT_DISABLED = new PackageOverride.Builder().setEnabled(false)
.build();
private static final String PACKAGE_NAME_MSG_KEY = "packageName";
private static final String USER_ID_MSG_KEY = "userId";
private static final String GAME_MODE_INTERVENTION_LIST_FILE_NAME =
@@ -156,7 +134,6 @@ public final class GameManagerService extends IGameManagerService.Stub {
private final Handler mHandler;
private final PackageManager mPackageManager;
private final UserManager mUserManager;
private final IPlatformCompat mPlatformCompat;
private final PowerManagerInternal mPowerManagerInternal;
private final File mSystemDir;
@VisibleForTesting
@@ -180,8 +157,6 @@ public final class GameManagerService extends IGameManagerService.Stub {
mHandler = new SettingsHandler(looper);
mPackageManager = mContext.getPackageManager();
mUserManager = mContext.getSystemService(UserManager.class);
mPlatformCompat = IPlatformCompat.Stub.asInterface(
ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE));
mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class);
mSystemDir = new File(Environment.getDataDirectory(), "system");
mSystemDir.mkdirs();
@@ -212,8 +187,6 @@ public final class GameManagerService extends IGameManagerService.Stub {
mHandler = new SettingsHandler(looper);
mPackageManager = mContext.getPackageManager();
mUserManager = mContext.getSystemService(UserManager.class);
mPlatformCompat = IPlatformCompat.Stub.asInterface(
ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE));
mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class);
mSystemDir = new File(dataDir, "system");
mSystemDir.mkdirs();
@@ -326,10 +299,6 @@ 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);
final int userId = (int) msg.obj;
final String[] packageNames = getInstalledGamePackageNames(userId);
@@ -412,39 +381,6 @@ public final class GameManagerService extends IGameManagerService.Stub {
}
}
// Turn the raw string to the corresponding CompatChange id.
static long getCompatChangeId(String raw) {
switch (raw) {
case "0.3":
return DOWNSCALE_30;
case "0.35":
return DOWNSCALE_35;
case "0.4":
return DOWNSCALE_40;
case "0.45":
return DOWNSCALE_45;
case "0.5":
return DOWNSCALE_50;
case "0.55":
return DOWNSCALE_55;
case "0.6":
return DOWNSCALE_60;
case "0.65":
return DOWNSCALE_65;
case "0.7":
return DOWNSCALE_70;
case "0.75":
return DOWNSCALE_75;
case "0.8":
return DOWNSCALE_80;
case "0.85":
return DOWNSCALE_85;
case "0.9":
return DOWNSCALE_90;
}
return 0;
}
public enum FrameRate {
FPS_DEFAULT(0),
FPS_30(30),
@@ -649,6 +585,13 @@ public final class GameManagerService extends IGameManagerService.Stub {
return xmlFound;
}
GameModeConfiguration getOrAddDefaultGameModeConfiguration(int gameMode) {
synchronized (mModeConfigLock) {
mModeConfigs.putIfAbsent(gameMode, new GameModeConfiguration(gameMode));
return mModeConfigs.get(gameMode);
}
}
/**
* GameModeConfiguration contains all the values for all the interventions associated with
* a game mode.
@@ -659,17 +602,26 @@ public final class GameManagerService extends IGameManagerService.Stub {
public static final String MODE_KEY = "mode";
public static final String SCALING_KEY = "downscaleFactor";
public static final String FPS_KEY = "fps";
public static final String DEFAULT_SCALING = "1.0";
public static final String DEFAULT_FPS = "";
public static final String ANGLE_KEY = "useAngle";
public static final String LOADING_BOOST_KEY = "loadingBoost";
public static final float DEFAULT_SCALING = -1f;
public static final String DEFAULT_FPS = "";
public static final boolean DEFAULT_USE_ANGLE = false;
public static final int DEFAULT_LOADING_BOOST_DURATION = -1;
private final @GameMode int mGameMode;
private String mScaling;
private String mFps;
private float mScaling = DEFAULT_SCALING;
private String mFps = DEFAULT_FPS;
private final boolean mUseAngle;
private final int mLoadingBoostDuration;
GameModeConfiguration(int gameMode) {
mGameMode = gameMode;
mUseAngle = DEFAULT_USE_ANGLE;
mLoadingBoostDuration = DEFAULT_LOADING_BOOST_DURATION;
}
GameModeConfiguration(KeyValueListParser parser) {
mGameMode = parser.getInt(MODE_KEY, GameManager.GAME_MODE_UNSUPPORTED);
// isGameModeOptedIn() returns if an app will handle all of the changes necessary
@@ -677,7 +629,7 @@ public final class GameManagerService extends IGameManagerService.Stub {
// GameManagerService) will not do anything for the app (like window scaling or
// using ANGLE).
mScaling = !mAllowDownscale || willGamePerformOptimizations(mGameMode)
? DEFAULT_SCALING : parser.getString(SCALING_KEY, DEFAULT_SCALING);
? DEFAULT_SCALING : parser.getFloat(SCALING_KEY, DEFAULT_SCALING);
mFps = mAllowFpsOverride && !willGamePerformOptimizations(mGameMode)
? parser.getString(FPS_KEY, DEFAULT_FPS) : DEFAULT_FPS;
@@ -686,17 +638,18 @@ public final class GameManagerService extends IGameManagerService.Stub {
// - The app has not opted in to performing the work itself AND
// - The Phenotype config has enabled it.
mUseAngle = mAllowAngle && !willGamePerformOptimizations(mGameMode)
&& parser.getBoolean(ANGLE_KEY, false);
&& parser.getBoolean(ANGLE_KEY, DEFAULT_USE_ANGLE);
mLoadingBoostDuration = willGamePerformOptimizations(mGameMode) ? -1
: parser.getInt(LOADING_BOOST_KEY, -1);
mLoadingBoostDuration = willGamePerformOptimizations(mGameMode)
? DEFAULT_LOADING_BOOST_DURATION
: parser.getInt(LOADING_BOOST_KEY, DEFAULT_LOADING_BOOST_DURATION);
}
public int getGameMode() {
return mGameMode;
}
public synchronized String getScaling() {
public synchronized float getScaling() {
return mScaling;
}
@@ -712,7 +665,7 @@ public final class GameManagerService extends IGameManagerService.Stub {
return mLoadingBoostDuration;
}
public synchronized void setScaling(String scaling) {
public synchronized void setScaling(float scaling) {
mScaling = scaling;
}
@@ -735,13 +688,6 @@ public final class GameManagerService extends IGameManagerService.Stub {
+ mUseAngle + ",Fps:" + mFps + ",Loading Boost Duration:"
+ mLoadingBoostDuration + "]";
}
/**
* Get the corresponding compat change id for the current scaling string.
*/
public long getCompatChangeId() {
return GameManagerService.getCompatChangeId(mScaling);
}
}
public String getPackageName() {
@@ -813,7 +759,7 @@ public final class GameManagerService extends IGameManagerService.Stub {
}
/**
* Insert a new GameModeConfiguration
* Inserts a new GameModeConfiguration
*/
public void addModeConfig(GameModeConfiguration config) {
if (config.isActive()) {
@@ -839,6 +785,14 @@ public final class GameManagerService extends IGameManagerService.Stub {
}
}
private final class LocalService extends GameManagerInternal {
@Override
public float getResolutionScalingFactor(String packageName, int userId) {
final int gameMode = getGameModeFromSettings(packageName, userId);
return getResolutionScalingFactorInternal(packageName, gameMode, userId);
}
}
/**
* SystemService lifecycle for GameService.
*
@@ -849,13 +803,13 @@ public final class GameManagerService extends IGameManagerService.Stub {
public Lifecycle(Context context) {
super(context);
mService = new GameManagerService(context);
}
@Override
public void onStart() {
final Context context = getContext();
mService = new GameManagerService(context);
publishBinderService(Context.GAME_SERVICE, mService);
mService.publishLocalService();
mService.registerDeviceConfigListener();
mService.registerPackageReceiver();
}
@@ -937,8 +891,8 @@ public final class GameManagerService extends IGameManagerService.Stub {
private @GameMode int getGameModeFromSettings(String packageName, @UserIdInt int userId) {
synchronized (mLock) {
if (!mSettings.containsKey(userId)) {
Slog.w(TAG, "User ID '" + userId + "' does not have a Game Mode"
+ " selected for package: '" + packageName + "'");
Slog.d(TAG, "User ID '" + userId + "' does not have a Game Mode"
+ " selected for package: '" + packageName + "'");
return GameManager.GAME_MODE_UNSUPPORTED;
}
@@ -1163,6 +1117,66 @@ public final class GameManagerService extends IGameManagerService.Stub {
mGameServiceController.setGameServiceProvider(packageName);
}
/**
* Updates the resolution scaling factor for the package's target game mode and activates it.
*
* @param scalingFactor enable scaling override over any other compat scaling if positive,
* or disable the override otherwise
* @throws SecurityException if caller doesn't have
* {@link android.Manifest.permission#MANAGE_GAME_MODE}
* permission.
* @throws IllegalArgumentException if the user ID provided doesn't exist.
*/
@Override
@RequiresPermission(Manifest.permission.MANAGE_GAME_MODE)
public void updateResolutionScalingFactor(String packageName, int gameMode, float scalingFactor,
int userId) throws SecurityException, IllegalArgumentException {
checkPermission(Manifest.permission.MANAGE_GAME_MODE);
synchronized (mLock) {
if (!mSettings.containsKey(userId)) {
throw new IllegalArgumentException("User " + userId + " wasn't started");
}
}
setGameModeConfigOverride(packageName, userId, gameMode, null /*fpsStr*/,
Float.toString(scalingFactor));
}
/**
* Gets the resolution scaling factor for the package's target game mode.
*
* @return scaling factor for the game mode if exists or negative value otherwise.
* @throws SecurityException if caller doesn't have
* {@link android.Manifest.permission#MANAGE_GAME_MODE}
* permission.
* @throws IllegalArgumentException if the user ID provided doesn't exist.
*/
@Override
@RequiresPermission(Manifest.permission.MANAGE_GAME_MODE)
public float getResolutionScalingFactor(String packageName, int gameMode, int userId)
throws SecurityException, IllegalArgumentException {
checkPermission(Manifest.permission.MANAGE_GAME_MODE);
synchronized (mLock) {
if (!mSettings.containsKey(userId)) {
throw new IllegalArgumentException("User " + userId + " wasn't started");
}
}
return getResolutionScalingFactorInternal(packageName, gameMode, userId);
}
float getResolutionScalingFactorInternal(String packageName, int gameMode, int userId) {
final GamePackageConfiguration packageConfig = getConfig(packageName);
if (packageConfig == null) {
return GamePackageConfiguration.GameModeConfiguration.DEFAULT_SCALING;
}
final GamePackageConfiguration.GameModeConfiguration modeConfig =
packageConfig.getGameModeConfiguration(gameMode);
if (modeConfig != null) {
return modeConfig.getScaling();
}
return GamePackageConfiguration.GameModeConfiguration.DEFAULT_SCALING;
}
/**
* Notified when boot is completed.
*/
@@ -1238,28 +1252,6 @@ public final class GameManagerService extends IGameManagerService.Stub {
}
}
/**
* @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(
overrides);
try {
mPlatformCompat.putOverridesOnReleaseBuilds(changeConfig, packageName);
} catch (RemoteException e) {
Slog.e(TAG, "Failed to call IPlatformCompat#putOverridesOnReleaseBuilds", e);
}
} finally {
Binder.restoreCallingIdentity(uid);
}
}
/**
* Remove frame rate override due to mode switch
*/
@@ -1273,60 +1265,6 @@ 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_30, COMPAT_DISABLED);
overrides.put(DOWNSCALE_35, COMPAT_DISABLED);
overrides.put(DOWNSCALE_40, COMPAT_DISABLED);
overrides.put(DOWNSCALE_45, COMPAT_DISABLED);
overrides.put(DOWNSCALE_50, COMPAT_DISABLED);
overrides.put(DOWNSCALE_55, COMPAT_DISABLED);
overrides.put(DOWNSCALE_60, COMPAT_DISABLED);
overrides.put(DOWNSCALE_65, COMPAT_DISABLED);
overrides.put(DOWNSCALE_70, COMPAT_DISABLED);
overrides.put(DOWNSCALE_75, COMPAT_DISABLED);
overrides.put(DOWNSCALE_80, COMPAT_DISABLED);
overrides.put(DOWNSCALE_85, COMPAT_DISABLED);
overrides.put(DOWNSCALE_90, COMPAT_DISABLED);
overrides.put(scaleId, COMPAT_ENABLED);
final CompatibilityOverrideConfig changeConfig = new CompatibilityOverrideConfig(
overrides);
try {
mPlatformCompat.putOverridesOnReleaseBuilds(changeConfig, packageName);
} catch (RemoteException e) {
Slog.e(TAG, "Failed to call IPlatformCompat#putOverridesOnReleaseBuilds", e);
}
} finally {
Binder.restoreCallingIdentity(uid);
}
}
private void updateCompatModeDownscale(GamePackageConfiguration packageConfig,
String packageName, @GameMode int gameMode) {
if (DEBUG) {
Slog.v(TAG, dumpDeviceConfigs());
}
final GamePackageConfiguration.GameModeConfiguration modeConfig =
packageConfig.getGameModeConfiguration(gameMode);
if (modeConfig == null) {
Slog.i(TAG, "Game mode " + gameMode + " not found for " + packageName);
return;
}
long scaleId = modeConfig.getCompatChangeId();
if (scaleId == 0) {
Slog.w(TAG, "Invalid downscaling change id " + scaleId + " for "
+ packageName);
return;
}
enableCompatScale(packageName, scaleId);
}
private int modeToBitmask(@GameMode int gameMode) {
return (1 << gameMode);
}
@@ -1364,20 +1302,17 @@ public final class GameManagerService extends IGameManagerService.Stub {
@GameMode int gameMode, @UserIdInt int userId) {
if (gameMode == GameManager.GAME_MODE_STANDARD
|| gameMode == GameManager.GAME_MODE_UNSUPPORTED) {
disableCompatScale(packageName);
resetFps(packageName, userId);
return;
}
final GamePackageConfiguration packageConfig = getConfig(packageName);
if (packageConfig == null) {
disableCompatScale(packageName);
Slog.v(TAG, "Package configuration not found for " + packageName);
return;
}
if (packageConfig.willGamePerformOptimizations(gameMode)) {
return;
}
updateCompatModeDownscale(packageConfig, packageName, gameMode);
updateFps(packageConfig, packageName, gameMode, userId);
updateUseAngle(packageName, gameMode);
}
@@ -1408,7 +1343,7 @@ public final class GameManagerService extends IGameManagerService.Stub {
}
// modify GameModeConfiguration intervention settings
GamePackageConfiguration.GameModeConfiguration overrideModeConfig =
overrideConfig.getGameModeConfiguration(gameMode);
overrideConfig.getOrAddDefaultGameModeConfiguration(gameMode);
if (fpsStr != null) {
overrideModeConfig.setFpsStr(fpsStr);
@@ -1417,10 +1352,7 @@ public final class GameManagerService extends IGameManagerService.Stub {
GamePackageConfiguration.GameModeConfiguration.DEFAULT_FPS);
}
if (scaling != null) {
overrideModeConfig.setScaling(scaling);
} else {
overrideModeConfig.setScaling(
GamePackageConfiguration.GameModeConfiguration.DEFAULT_SCALING);
overrideModeConfig.setScaling(Float.parseFloat(scaling));
}
Slog.i(TAG, "Package Name: " + packageName
+ " FPS: " + String.valueOf(overrideModeConfig.getFps())
@@ -1470,7 +1402,7 @@ public final class GameManagerService extends IGameManagerService.Stub {
}
// If the game mode to reset is the only mode other than standard mode,
// The override config is removed.
// the override config is removed.
if (modes.length <= 2) {
synchronized (mOverrideConfigLock) {
mOverrideConfigs.remove(packageName);
@@ -1595,7 +1527,7 @@ public final class GameManagerService extends IGameManagerService.Stub {
}
}
} catch (Exception e) {
Slog.e(TAG, "Failed to update compat modes for user " + userId + ": " + e);
Slog.e(TAG, "Failed to update configs for user " + userId + ": " + e);
}
final Message msg = mHandler.obtainMessage(WRITE_GAME_MODE_INTERVENTION_LIST_FILE);
@@ -1646,7 +1578,7 @@ public final class GameManagerService extends IGameManagerService.Stub {
final int useAngle = gameModeConfiguration.getUseAngle() ? 1 : 0;
sb.append(TextUtils.formatSimple("angle=%d", useAngle));
sb.append(",");
final String scaling = gameModeConfiguration.getScaling();
final float scaling = gameModeConfiguration.getScaling();
sb.append("scaling=");
sb.append(scaling);
sb.append(",");
@@ -1746,14 +1678,6 @@ public final class GameManagerService extends IGameManagerService.Stub {
updateConfigsForUser(userId, true /*checkGamePackage*/, packageName);
break;
case ACTION_PACKAGE_REMOVED:
disableCompatScale(packageName);
// If EXTRA_REPLACING is true, it means there will be an
// ACTION_PACKAGE_ADDED triggered after this because this
// is an updated package that gets installed. Hence, disable
// resolution downscaling effort but avoid removing the server
// or commandline overriding configurations because those will
// not change but the package game mode configurations may change
// which may opt in and/or opt out some game mode configurations.
if (!intent.getBooleanExtra(EXTRA_REPLACING, false)) {
synchronized (mOverrideConfigLock) {
mOverrideConfigs.remove(packageName);
@@ -1785,6 +1709,10 @@ public final class GameManagerService extends IGameManagerService.Stub {
mDeviceConfigListener = new DeviceConfigListener();
}
private void publishLocalService() {
LocalServices.addService(GameManagerInternal.class, new LocalService());
}
private String dumpDeviceConfigs() {
StringBuilder out = new StringBuilder();
for (String key : mConfigs.keySet()) {

View File

@@ -16,21 +16,6 @@
package com.android.server.app;
import static com.android.server.wm.CompatModePackages.DOWNSCALED;
import static com.android.server.wm.CompatModePackages.DOWNSCALE_30;
import static com.android.server.wm.CompatModePackages.DOWNSCALE_35;
import static com.android.server.wm.CompatModePackages.DOWNSCALE_40;
import static com.android.server.wm.CompatModePackages.DOWNSCALE_45;
import static com.android.server.wm.CompatModePackages.DOWNSCALE_50;
import static com.android.server.wm.CompatModePackages.DOWNSCALE_55;
import static com.android.server.wm.CompatModePackages.DOWNSCALE_60;
import static com.android.server.wm.CompatModePackages.DOWNSCALE_65;
import static com.android.server.wm.CompatModePackages.DOWNSCALE_70;
import static com.android.server.wm.CompatModePackages.DOWNSCALE_75;
import static com.android.server.wm.CompatModePackages.DOWNSCALE_80;
import static com.android.server.wm.CompatModePackages.DOWNSCALE_85;
import static com.android.server.wm.CompatModePackages.DOWNSCALE_90;
import android.app.ActivityManager;
import android.app.GameManager;
import android.app.IGameManagerService;
@@ -39,7 +24,6 @@ import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.ServiceManager.ServiceNotFoundException;
import android.os.ShellCommand;
import android.util.ArraySet;
import java.io.PrintWriter;
import java.util.Locale;
@@ -53,23 +37,6 @@ public class GameManagerShellCommand extends ShellCommand {
public GameManagerShellCommand() {}
private static final ArraySet<Long> DOWNSCALE_CHANGE_IDS = new ArraySet<>(new Long[]{
DOWNSCALED,
DOWNSCALE_90,
DOWNSCALE_85,
DOWNSCALE_80,
DOWNSCALE_75,
DOWNSCALE_70,
DOWNSCALE_65,
DOWNSCALE_60,
DOWNSCALE_55,
DOWNSCALE_50,
DOWNSCALE_45,
DOWNSCALE_40,
DOWNSCALE_35,
DOWNSCALE_30,
});
@Override
public int onCommand(String cmd) {
if (cmd == null) {
@@ -212,11 +179,15 @@ public class GameManagerShellCommand extends ShellCommand {
case "--downscale":
if (downscaleRatio == null) {
downscaleRatio = getNextArgRequired();
if (downscaleRatio != null
&& GameManagerService.getCompatChangeId(downscaleRatio) == 0
&& !downscaleRatio.equals("disable")) {
pw.println("Invalid scaling ratio '" + downscaleRatio + "'");
return -1;
if ("disable".equals(downscaleRatio)) {
downscaleRatio = "-1";
} else {
try {
Float.parseFloat(downscaleRatio);
} catch (NumberFormatException e) {
pw.println("Invalid scaling ratio '" + downscaleRatio + "'");
return -1;
}
}
} else {
pw.println("Duplicate option '" + option + "'");
@@ -249,40 +220,16 @@ public class GameManagerShellCommand extends ShellCommand {
final GameManagerService gameManagerService = (GameManagerService)
ServiceManager.getService(Context.GAME_SERVICE);
boolean batteryModeSupported = false;
boolean perfModeSupported = false;
int [] modes = gameManagerService.getAvailableGameModes(packageName);
for (int mode : modes) {
if (mode == GameManager.GAME_MODE_PERFORMANCE) {
perfModeSupported = true;
} else if (mode == GameManager.GAME_MODE_BATTERY) {
batteryModeSupported = true;
}
}
switch (gameMode.toLowerCase(Locale.getDefault())) {
case "2":
case "performance":
if (perfModeSupported) {
gameManagerService.setGameModeConfigOverride(packageName, userId,
GameManager.GAME_MODE_PERFORMANCE, fpsStr, downscaleRatio);
} else {
pw.println("Game mode: " + gameMode + " not supported by "
+ packageName);
return -1;
}
gameManagerService.setGameModeConfigOverride(packageName, userId,
GameManager.GAME_MODE_PERFORMANCE, fpsStr, downscaleRatio);
break;
case "3":
case "battery":
if (batteryModeSupported) {
gameManagerService.setGameModeConfigOverride(packageName, userId,
GameManager.GAME_MODE_BATTERY, fpsStr, downscaleRatio);
} else {
pw.println("Game mode: " + gameMode + " not supported by "
+ packageName);
return -1;
}
gameManagerService.setGameModeConfigOverride(packageName, userId,
GameManager.GAME_MODE_BATTERY, fpsStr, downscaleRatio);
break;
default:
pw.println("Invalid game mode: " + gameMode);

View File

@@ -24,6 +24,7 @@ import static com.android.server.wm.ActivityTaskSupervisor.PRESERVE_WINDOWS;
import android.app.ActivityManager;
import android.app.AppGlobals;
import android.app.GameManagerInternal;
import android.app.compat.CompatChanges;
import android.compat.annotation.ChangeId;
import android.compat.annotation.Disabled;
@@ -48,6 +49,7 @@ import android.util.TypedXmlSerializer;
import android.util.Xml;
import com.android.internal.protolog.common.ProtoLog;
import com.android.server.LocalServices;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -64,6 +66,7 @@ public final class CompatModePackages {
private static final String TAG_CONFIGURATION = TAG + POSTFIX_CONFIGURATION;
private final ActivityTaskManagerService mService;
private GameManagerInternal mGameManager;
private final AtomicFile mFile;
// Compatibility state: no longer ask user to select the mode.
@@ -375,6 +378,18 @@ public final class CompatModePackages {
float getCompatScale(String packageName, int uid) {
final UserHandle userHandle = UserHandle.getUserHandleForUid(uid);
if (mGameManager == null) {
mGameManager = LocalServices.getService(GameManagerInternal.class);
}
if (mGameManager != null) {
final int userId = userHandle.getIdentifier();
final float scalingFactor = mGameManager.getResolutionScalingFactor(packageName,
userId);
if (scalingFactor > 0) {
return 1f / scalingFactor;
}
}
if (CompatChanges.isChangeEnabled(DOWNSCALED, packageName, userHandle)) {
if (CompatChanges.isChangeEnabled(DOWNSCALE_90, packageName, userHandle)) {
return 1f / 0.9f;

View File

@@ -201,7 +201,6 @@ public class GameManagerServiceTests {
public void tearDown() throws Exception {
LocalServices.removeServiceForTest(PowerManagerInternal.class);
GameManagerService gameManagerService = new GameManagerService(mMockContext);
gameManagerService.disableCompatScale(mPackageName);
if (mMockingSession != null) {
mMockingSession.finishMocking();
}
@@ -578,7 +577,7 @@ public class GameManagerServiceTests {
}
private void checkDownscaling(GameManagerService gameManagerService,
int gameMode, String scaling) {
int gameMode, float scaling) {
if (gameManagerService == null) {
gameManagerService = new GameManagerService(mMockContext, mTestLooper.getLooper());
startUser(gameManagerService, USER_ID_1);
@@ -586,7 +585,7 @@ public class GameManagerServiceTests {
}
GameManagerService.GamePackageConfiguration config =
gameManagerService.getConfig(mPackageName);
assertEquals(config.getGameModeConfiguration(gameMode).getScaling(), scaling);
assertEquals(scaling, config.getGameModeConfiguration(gameMode).getScaling(), 0.01f);
}
private void checkAngleEnabled(GameManagerService gameManagerService, int gameMode,
@@ -715,7 +714,7 @@ public class GameManagerServiceTests {
checkReportedModes(gameManagerService, GameManager.GAME_MODE_PERFORMANCE,
GameManager.GAME_MODE_STANDARD);
checkDownscaling(gameManagerService, GameManager.GAME_MODE_PERFORMANCE, "0.3");
checkDownscaling(gameManagerService, GameManager.GAME_MODE_PERFORMANCE, 0.3f);
checkFps(gameManagerService, GameManager.GAME_MODE_PERFORMANCE, 120);
}
@@ -735,7 +734,7 @@ public class GameManagerServiceTests {
checkReportedModes(gameManagerService, GameManager.GAME_MODE_BATTERY,
GameManager.GAME_MODE_STANDARD);
checkDownscaling(gameManagerService, GameManager.GAME_MODE_BATTERY, "0.5");
checkDownscaling(gameManagerService, GameManager.GAME_MODE_BATTERY, 0.5f);
checkFps(gameManagerService, GameManager.GAME_MODE_BATTERY, 60);
}
@@ -757,9 +756,9 @@ public class GameManagerServiceTests {
checkReportedModes(gameManagerService, GameManager.GAME_MODE_PERFORMANCE,
GameManager.GAME_MODE_BATTERY, GameManager.GAME_MODE_STANDARD);
checkDownscaling(gameManagerService, GameManager.GAME_MODE_PERFORMANCE, "0.3");
checkDownscaling(gameManagerService, GameManager.GAME_MODE_PERFORMANCE, 0.3f);
checkFps(gameManagerService, GameManager.GAME_MODE_PERFORMANCE, 120);
checkDownscaling(gameManagerService, GameManager.GAME_MODE_BATTERY, "0.5");
checkDownscaling(gameManagerService, GameManager.GAME_MODE_BATTERY, 0.5f);
checkFps(gameManagerService, GameManager.GAME_MODE_BATTERY, 60);
}
@@ -782,7 +781,7 @@ public class GameManagerServiceTests {
checkReportedModes(gameManagerService, GameManager.GAME_MODE_PERFORMANCE,
GameManager.GAME_MODE_STANDARD);
checkDownscaling(gameManagerService, GameManager.GAME_MODE_PERFORMANCE, "0.5");
checkDownscaling(gameManagerService, GameManager.GAME_MODE_PERFORMANCE, 0.5f);
checkFps(gameManagerService, GameManager.GAME_MODE_PERFORMANCE, 90);
}
@@ -805,7 +804,7 @@ public class GameManagerServiceTests {
checkReportedModes(gameManagerService, GameManager.GAME_MODE_BATTERY,
GameManager.GAME_MODE_STANDARD);
checkDownscaling(gameManagerService, GameManager.GAME_MODE_BATTERY, "0.7");
checkDownscaling(gameManagerService, GameManager.GAME_MODE_BATTERY, 0.7f);
checkFps(gameManagerService, GameManager.GAME_MODE_BATTERY, 30);
}
@@ -829,9 +828,9 @@ public class GameManagerServiceTests {
checkReportedModes(gameManagerService, GameManager.GAME_MODE_PERFORMANCE,
GameManager.GAME_MODE_BATTERY, GameManager.GAME_MODE_STANDARD);
checkDownscaling(gameManagerService, GameManager.GAME_MODE_PERFORMANCE, "0.5");
checkDownscaling(gameManagerService, GameManager.GAME_MODE_PERFORMANCE, 0.5f);
checkFps(gameManagerService, GameManager.GAME_MODE_PERFORMANCE, 90);
checkDownscaling(gameManagerService, GameManager.GAME_MODE_BATTERY, "0.7");
checkDownscaling(gameManagerService, GameManager.GAME_MODE_BATTERY, 0.7f);
checkFps(gameManagerService, GameManager.GAME_MODE_BATTERY, 30);
}
@@ -858,9 +857,9 @@ public class GameManagerServiceTests {
checkReportedModes(gameManagerService, GameManager.GAME_MODE_PERFORMANCE,
GameManager.GAME_MODE_BATTERY, GameManager.GAME_MODE_STANDARD);
checkDownscaling(gameManagerService, GameManager.GAME_MODE_PERFORMANCE, "0.3");
checkDownscaling(gameManagerService, GameManager.GAME_MODE_PERFORMANCE, 0.3f);
checkFps(gameManagerService, GameManager.GAME_MODE_PERFORMANCE, 120);
checkDownscaling(gameManagerService, GameManager.GAME_MODE_BATTERY, "0.7");
checkDownscaling(gameManagerService, GameManager.GAME_MODE_BATTERY, 0.7f);
checkFps(gameManagerService, GameManager.GAME_MODE_BATTERY, 30);
}
@@ -933,7 +932,7 @@ public class GameManagerServiceTests {
public void testInterventionAllowScalingDefault() throws Exception {
mockDeviceConfigPerformance();
mockModifyGameModeGranted();
checkDownscaling(null, GameManager.GAME_MODE_PERFORMANCE, "0.5");
checkDownscaling(null, GameManager.GAME_MODE_PERFORMANCE, 0.5f);
}
/**
@@ -944,7 +943,7 @@ public class GameManagerServiceTests {
mockDeviceConfigPerformance();
mockInterventionAllowDownscaleFalse();
mockModifyGameModeGranted();
checkDownscaling(null, GameManager.GAME_MODE_PERFORMANCE, "1.0");
checkDownscaling(null, GameManager.GAME_MODE_PERFORMANCE, -1.0f);
}
/**
@@ -956,7 +955,7 @@ public class GameManagerServiceTests {
mockDeviceConfigPerformance();
mockInterventionAllowDownscaleTrue();
mockModifyGameModeGranted();
checkDownscaling(null, GameManager.GAME_MODE_PERFORMANCE, "0.5");
checkDownscaling(null, GameManager.GAME_MODE_PERFORMANCE, 0.5f);
}
/**
@@ -1404,4 +1403,102 @@ public class GameManagerServiceTests {
assertEquals(splitLine[6], "angle=0,scaling=0.7,fps=30");
}
@Test
public void testUpdateResolutionScalingFactor() {
mockModifyGameModeGranted();
mockDeviceConfigBattery();
GameManagerService gameManagerService =
new GameManagerService(mMockContext, mTestLooper.getLooper());
startUser(gameManagerService, USER_ID_1);
float scalingFactor = 0.123f;
gameManagerService.updateResolutionScalingFactor(mPackageName,
GameManager.GAME_MODE_BATTERY, scalingFactor,
USER_ID_1);
assertEquals(scalingFactor, gameManagerService.getResolutionScalingFactor(mPackageName,
GameManager.GAME_MODE_BATTERY, USER_ID_1), 0.001f);
scalingFactor = 0.321f;
gameManagerService.updateResolutionScalingFactor(mPackageName,
GameManager.GAME_MODE_BATTERY, scalingFactor,
USER_ID_1);
assertEquals(scalingFactor, gameManagerService.getResolutionScalingFactor(mPackageName,
GameManager.GAME_MODE_BATTERY, USER_ID_1), 0.001f);
}
@Test
public void testUpdateResolutionScalingFactor_noDeviceConfig() {
mockModifyGameModeGranted();
GameManagerService gameManagerService =
new GameManagerService(mMockContext, mTestLooper.getLooper());
startUser(gameManagerService, USER_ID_1);
float scalingFactor = 0.123f;
gameManagerService.updateResolutionScalingFactor(mPackageName,
GameManager.GAME_MODE_BATTERY, scalingFactor,
USER_ID_1);
assertEquals(scalingFactor, gameManagerService.getResolutionScalingFactor(mPackageName,
GameManager.GAME_MODE_BATTERY, USER_ID_1), 0.001f);
scalingFactor = 0.321f;
gameManagerService.updateResolutionScalingFactor(mPackageName,
GameManager.GAME_MODE_BATTERY, scalingFactor,
USER_ID_1);
assertEquals(scalingFactor, gameManagerService.getResolutionScalingFactor(mPackageName,
GameManager.GAME_MODE_BATTERY,
USER_ID_1), 0.001f);
}
@Test
public void testUpdateResolutionScalingFactor_permissionDenied() {
mockModifyGameModeDenied();
mockDeviceConfigAll();
GameManagerService gameManagerService =
new GameManagerService(mMockContext, mTestLooper.getLooper());
startUser(gameManagerService, USER_ID_1);
float scalingFactor = 0.123f;
assertThrows(SecurityException.class, () -> {
gameManagerService.updateResolutionScalingFactor(mPackageName,
GameManager.GAME_MODE_BATTERY, scalingFactor,
USER_ID_1);
});
mockModifyGameModeGranted();
assertEquals(0.7f, gameManagerService.getResolutionScalingFactor(mPackageName,
GameManager.GAME_MODE_BATTERY, USER_ID_1), 0.001f);
}
@Test
public void testUpdateResolutionScalingFactor_noUserId() {
mockModifyGameModeGranted();
GameManagerService gameManagerService =
new GameManagerService(mMockContext, mTestLooper.getLooper());
startUser(gameManagerService, USER_ID_2);
final float scalingFactor = 0.123f;
assertThrows(IllegalArgumentException.class, () -> {
gameManagerService.updateResolutionScalingFactor(mPackageName,
GameManager.GAME_MODE_BATTERY, scalingFactor,
USER_ID_1);
});
}
@Test
public void testGetResolutionScalingFactor_permissionDenied() {
mockModifyGameModeDenied();
mockDeviceConfigAll();
GameManagerService gameManagerService =
new GameManagerService(mMockContext, mTestLooper.getLooper());
startUser(gameManagerService, USER_ID_1);
assertThrows(SecurityException.class, () -> {
gameManagerService.getResolutionScalingFactor(mPackageName,
GameManager.GAME_MODE_BATTERY, USER_ID_1);
});
}
@Test
public void testGetResolutionScalingFactor_noUserId() {
mockModifyGameModeDenied();
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);
}
}

View File

@@ -0,0 +1,95 @@
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.wm;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import android.app.GameManagerInternal;
import android.platform.test.annotations.Presubmit;
import androidx.test.filters.SmallTest;
import com.android.server.LocalServices;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
/**
* Tests for the {@link CompatModePackages} class.
*
* Build/Install/Run:
* atest WmTests:CompatModePackagesTests
*/
@SmallTest
@Presubmit
public class CompatModePackagesTests extends SystemServiceTestsBase {
ActivityTaskManagerService mAtm;
GameManagerInternal mGm;
static final String TEST_PACKAGE = "compat.mode.packages";
static final int TEST_USER_ID = 1;
@Before
public void setUp() {
mAtm = mSystemServicesTestRule.getActivityTaskManagerService();
mGm = mock(GameManagerInternal.class);
}
@After
public void tearDown() {
LocalServices.removeServiceForTest(GameManagerInternal.class);
}
@Test
public void testGetCompatScale_gameManagerReturnsPositive() {
LocalServices.addService(GameManagerInternal.class, mGm);
float scale = 0.25f;
doReturn(scale).when(mGm).getResolutionScalingFactor(anyString(), anyInt());
assertEquals(mAtm.mCompatModePackages.getCompatScale(TEST_PACKAGE, TEST_USER_ID), 1 / scale,
0.01f);
}
@Test
public void testGetCompatScale_gameManagerReturnsZero() {
LocalServices.addService(GameManagerInternal.class, mGm);
float scale = 0f;
doReturn(scale).when(mGm).getResolutionScalingFactor(anyString(), anyInt());
assertEquals(mAtm.mCompatModePackages.getCompatScale(TEST_PACKAGE, TEST_USER_ID), 1f,
0.01f);
}
@Test
public void testGetCompatScale_gameManagerReturnsNegative() {
LocalServices.addService(GameManagerInternal.class, mGm);
float scale = -1f;
doReturn(scale).when(mGm).getResolutionScalingFactor(anyString(), anyInt());
assertEquals(mAtm.mCompatModePackages.getCompatScale(TEST_PACKAGE, TEST_USER_ID), 1f,
0.01f);
}
@Test
public void testGetCompatScale_noGameManager() {
assertEquals(mAtm.mCompatModePackages.getCompatScale(TEST_PACKAGE, TEST_USER_ID), 1f,
0.01f);
}
}