blob: a0a83b1031b79abdb735524fc7294178ae6057ff [file] [log] [blame]
/*
* Copyright (C) 2021 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.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;
import android.compat.Compatibility;
import android.content.Context;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.ServiceManager.ServiceNotFoundException;
import android.os.ShellCommand;
import android.util.ArraySet;
import com.android.internal.compat.CompatibilityChangeConfig;
import com.android.server.compat.PlatformCompat;
import java.io.PrintWriter;
import java.util.Set;
import java.util.stream.Collectors;
/**
* ShellCommands for GameManagerService.
*
* Use with {@code adb shell cmd game ...}.
*/
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) {
return handleDefaultCommands(cmd);
}
final PrintWriter pw = getOutPrintWriter();
try {
switch (cmd) {
case "downscale": {
final String ratio = getNextArgRequired();
final String packageName = getNextArgRequired();
final long changeId = GameManagerService.getCompatChangeId(ratio);
if (changeId == 0 && !ratio.equals("disable")) {
pw.println("Invalid scaling ratio '" + ratio + "'");
break;
}
Set<Long> enabled = new ArraySet<>();
Set<Long> disabled;
if (changeId == 0) {
disabled = DOWNSCALE_CHANGE_IDS;
} else {
enabled.add(DOWNSCALED);
enabled.add(changeId);
disabled = DOWNSCALE_CHANGE_IDS.stream()
.filter(it -> it != DOWNSCALED && it != changeId)
.collect(Collectors.toSet());
}
final PlatformCompat platformCompat = (PlatformCompat)
ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE);
final CompatibilityChangeConfig overrides =
new CompatibilityChangeConfig(
new Compatibility.ChangeConfig(enabled, disabled));
platformCompat.setOverrides(overrides, packageName);
if (changeId == 0) {
pw.println("Disable downscaling for " + packageName + ".");
} else {
pw.println("Enable downscaling ratio for " + packageName + " to " + ratio);
}
return 0;
}
case "mode": {
/** The "mode" command allows setting a package's current game mode outside of
* the game dashboard UI. This command requires that a mode already be supported
* by the package. Devs can forcibly support game modes via the manifest
* metadata flags: com.android.app.gamemode.performance.enabled,
* com.android.app.gamemode.battery.enabled
* OR by `adb shell device_config put game_overlay \
* <PACKAGE_NAME> <CONFIG_STRING>`
* see: {@link GameManagerServiceTests#mockDeviceConfigAll()}
*/
return runGameMode(pw);
}
default:
return handleDefaultCommands(cmd);
}
} catch (Exception e) {
pw.println("Error: " + e);
}
return -1;
}
private int runGameMode(PrintWriter pw) throws ServiceNotFoundException, RemoteException {
final String option = getNextOption();
String userIdStr = null;
if (option != null && option.equals("--user")) {
userIdStr = getNextArgRequired();
}
final String gameMode = getNextArgRequired();
final String packageName = getNextArgRequired();
final IGameManagerService service = IGameManagerService.Stub.asInterface(
ServiceManager.getServiceOrThrow(Context.GAME_SERVICE));
boolean batteryModeSupported = false;
boolean perfModeSupported = false;
int[] modes = service.getAvailableGameModes(packageName);
for (int mode : modes) {
if (mode == GameManager.GAME_MODE_PERFORMANCE) {
perfModeSupported = true;
} else if (mode == GameManager.GAME_MODE_BATTERY) {
batteryModeSupported = true;
}
}
int userId = userIdStr != null ? Integer.parseInt(userIdStr)
: ActivityManager.getCurrentUser();
switch (gameMode.toLowerCase()) {
case "1":
case "standard":
// Standard should only be available if other game modes are.
if (batteryModeSupported || perfModeSupported) {
service.setGameMode(packageName, GameManager.GAME_MODE_STANDARD,
userId);
} else {
pw.println("Game mode: " + gameMode + " not supported by "
+ packageName);
return -1;
}
break;
case "2":
case "performance":
if (perfModeSupported) {
service.setGameMode(packageName, GameManager.GAME_MODE_PERFORMANCE,
userId);
} else {
pw.println("Game mode: " + gameMode + " not supported by "
+ packageName);
return -1;
}
break;
case "3":
case "battery":
if (batteryModeSupported) {
service.setGameMode(packageName, GameManager.GAME_MODE_BATTERY,
userId);
} else {
pw.println("Game mode: " + gameMode + " not supported by "
+ packageName);
return -1;
}
break;
default:
pw.println("Invalid game mode: " + gameMode);
return -1;
}
return 0;
}
@Override
public void onHelp() {
PrintWriter pw = getOutPrintWriter();
pw.println("Game manager (game) commands:");
pw.println(" help");
pw.println(" Print this help text.");
pw.println(" downscale [0.3|0.35|0.4|0.45|0.5|0.55|0.6|0.65|0.7|0.75|0.8|0.85|0.9|disable] <PACKAGE_NAME>");
pw.println(" Force app to run at the specified scaling ratio.");
pw.println(" mode [--user <USER_ID>] [1|2|3|standard|performance|battery] <PACKAGE_NAME>");
pw.println(" Force app to run in the specified game mode, if supported.");
pw.println(" --user <USER_ID>: apply for the given user, the current user is used when unspecified.");
}
}