Merge changes from topic "convert_metadata"

* changes:
  Add allowGameFpsOverride intervention flag
  Introduce new game mode config xml resource file
This commit is contained in:
Xiang Wang
2022-02-02 05:43:03 +00:00
committed by Android (Google) Code Review
11 changed files with 226 additions and 22 deletions

View File

@@ -326,6 +326,9 @@ package android {
field public static final int allowClearUserData = 16842757; // 0x1010005
field public static final int allowClickWhenDisabled = 16844312; // 0x1010618
field public static final int allowEmbedded = 16843765; // 0x10103f5
field public static final int allowGameAngleDriver;
field public static final int allowGameDownscaling;
field public static final int allowGameFpsOverride;
field public static final int allowNativeHeapPointerTagging = 16844306; // 0x1010612
field public static final int allowParallelSyncs = 16843570; // 0x1010332
field public static final int allowSingleTap = 16843353; // 0x1010259
@@ -1435,10 +1438,12 @@ package android {
field public static final int summaryOn = 16843247; // 0x10101ef
field public static final int supportedTypes;
field public static final int supportsAssist = 16844016; // 0x10104f0
field public static final int supportsBatteryGameMode;
field public static final int supportsInlineSuggestions = 16844301; // 0x101060d
field public static final int supportsLaunchVoiceAssistFromKeyguard = 16844017; // 0x10104f1
field public static final int supportsLocalInteraction = 16844047; // 0x101050f
field public static final int supportsMultipleDisplays = 16844182; // 0x1010596
field public static final int supportsPerformanceGameMode;
field public static final int supportsPictureInPicture = 16844023; // 0x10104f7
field public static final int supportsRtl = 16843695; // 0x10103af
field public static final int supportsStylusHandwriting;

View File

@@ -278,6 +278,7 @@ package android.app {
}
public final class GameManager {
method @RequiresPermission(android.Manifest.permission.MANAGE_GAME_MODE) public boolean isAngleEnabled(@NonNull String);
method public void setGameServiceProvider(@Nullable String);
}

View File

@@ -181,14 +181,18 @@ public final class GameManager {
/**
* Returns if ANGLE is enabled for a given package and user ID.
* <p>
* ANGLE (Almost Native Graphics Layer Engine) can translate OpenGL ES commands to Vulkan
* commands. Enabling ANGLE may improve the performance and/or reduce the power consumption of
* applications.
* The caller must have {@link android.Manifest.permission#MANAGE_GAME_MODE}.
*
* @hide
*/
@TestApi
@RequiresPermission(Manifest.permission.MANAGE_GAME_MODE)
public @GameMode boolean getAngleEnabled(@NonNull String packageName) {
public @GameMode boolean isAngleEnabled(@NonNull String packageName) {
try {
return mService.getAngleEnabled(packageName, mContext.getUserId());
return mService.isAngleEnabled(packageName, mContext.getUserId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}

View File

@@ -26,7 +26,7 @@ interface IGameManagerService {
int getGameMode(String packageName, int userId);
void setGameMode(String packageName, int gameMode, int userId);
int[] getAvailableGameModes(String packageName);
boolean getAngleEnabled(String packageName, int userId);
boolean isAngleEnabled(String packageName, int userId);
void setGameState(String packageName, in GameState gameState, int userId);
GameModeInfo getGameModeInfo(String packageName, int userId);
void setGameServiceProvider(String packageName);

View File

@@ -165,7 +165,7 @@ public class GraphicsEnvironment {
private boolean isAngleEnabledByGameMode(Context context, String packageName) {
try {
final boolean gameModeEnabledAngle =
(mGameManager != null) && mGameManager.getAngleEnabled(packageName);
(mGameManager != null) && mGameManager.isAngleEnabled(packageName);
Log.v(TAG, "ANGLE GameManagerService for " + packageName + ": " + gameModeEnabledAngle);
return gameModeEnabledAngle;
} catch (SecurityException e) {

View File

@@ -8875,6 +8875,22 @@
<attr name="gameSessionService" format="string" />
</declare-styleable>
<!-- Use <code>game-mode-config</code> as the root tag of the XML resource that
describes a GameModeConfig.
Described here are the attributes that can be included in that tag. -->
<declare-styleable name="GameModeConfig">
<!-- Set true to opt in BATTERY mode. -->
<attr name="supportsBatteryGameMode" format="boolean" />
<!-- Set true to opt in PERFORMANCE mode. -->
<attr name="supportsPerformanceGameMode" format="boolean" />
<!-- Set true to enable ANGLE. -->
<attr name="allowGameAngleDriver" format="boolean" />
<!-- Set true to allow resolution downscaling intervention. -->
<attr name="allowGameDownscaling" format="boolean" />
<!-- Set true to allow FPS override intervention. -->
<attr name="allowGameFpsOverride" format="boolean" />
</declare-styleable>
<!-- Use <code>voice-enrollment-application</code>
as the root tag of the XML resource that escribes the supported keyphrases (hotwords)
by the enrollment application.

View File

@@ -3255,6 +3255,11 @@
<public name="showClockAndComplications" />
<!-- @hide @SystemApi -->
<public name="gameSessionService" />
<public name="supportsBatteryGameMode" />
<public name="supportsPerformanceGameMode" />
<public name="allowGameAngleDriver" />
<public name="allowGameDownscaling" />
<public name="allowGameFpsOverride" />
<public name="localeConfig" />
<public name="showBackground" />
<public name="inheritKeyStoreKeys" />

View File

@@ -20,6 +20,11 @@ import static android.content.Intent.ACTION_PACKAGE_ADDED;
import static android.content.Intent.ACTION_PACKAGE_CHANGED;
import static android.content.Intent.ACTION_PACKAGE_REMOVED;
import static com.android.internal.R.styleable.GameModeConfig_allowGameAngleDriver;
import static com.android.internal.R.styleable.GameModeConfig_allowGameDownscaling;
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;
@@ -54,6 +59,10 @@ import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
import android.hardware.power.Mode;
import android.net.Uri;
import android.os.Binder;
@@ -71,8 +80,10 @@ import android.os.ShellCallback;
import android.provider.DeviceConfig;
import android.provider.DeviceConfig.Properties;
import android.util.ArrayMap;
import android.util.AttributeSet;
import android.util.KeyValueListParser;
import android.util.Slog;
import android.util.Xml;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
@@ -84,7 +95,11 @@ import com.android.server.ServiceThread;
import com.android.server.SystemService;
import com.android.server.SystemService.TargetUser;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import java.io.FileDescriptor;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.List;
@@ -425,12 +440,20 @@ public final class GameManagerService extends IGameManagerService.Stub {
public static final String METADATA_BATTERY_MODE_ENABLE =
"com.android.app.gamemode.battery.enabled";
/**
* Metadata that allows a game to specify all intervention information with an XML file in
* the application field.
*/
public static final String METADATA_GAME_MODE_CONFIG = "android.game_mode_config";
private static final String GAME_MODE_CONFIG_NODE_NAME = "game-mode-config";
private final String mPackageName;
private final ArrayMap<Integer, GameModeConfiguration> mModeConfigs;
private boolean mPerfModeOptedIn;
private boolean mBatteryModeOptedIn;
private boolean mAllowDownscale;
private boolean mAllowAngle;
private boolean mAllowFpsOverride;
GamePackageConfiguration(String packageName, int userId) {
mPackageName = packageName;
@@ -438,18 +461,21 @@ public final class GameManagerService extends IGameManagerService.Stub {
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);
mAllowAngle = ai.metaData.getBoolean(METADATA_ANGLE_ALLOW_ANGLE, true);
} else {
mPerfModeOptedIn = false;
mBatteryModeOptedIn = false;
mAllowDownscale = true;
mAllowAngle = true;
if (!parseInterventionFromXml(ai, packageName)) {
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);
mAllowAngle = ai.metaData.getBoolean(METADATA_ANGLE_ALLOW_ANGLE, true);
} else {
mPerfModeOptedIn = false;
mBatteryModeOptedIn = false;
mAllowDownscale = true;
mAllowAngle = true;
mAllowFpsOverride = true;
}
}
} catch (PackageManager.NameNotFoundException e) {
} catch (NameNotFoundException e) {
// Not all packages are installed, hence ignore those that are not installed yet.
Slog.v(TAG, "Failed to get package metadata");
}
@@ -469,6 +495,53 @@ public final class GameManagerService extends IGameManagerService.Stub {
}
}
private boolean parseInterventionFromXml(ApplicationInfo ai, String packageName) {
boolean xmlFound = false;
try (XmlResourceParser parser = ai.loadXmlMetaData(mPackageManager,
METADATA_GAME_MODE_CONFIG)) {
if (parser == null) {
Slog.v(TAG, "No " + METADATA_GAME_MODE_CONFIG
+ " meta-data found for package " + mPackageName);
} else {
xmlFound = true;
final Resources resources = mPackageManager.getResourcesForApplication(
packageName);
final AttributeSet attributeSet = Xml.asAttributeSet(parser);
int type;
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
&& type != XmlPullParser.START_TAG) {
// Do nothing
}
boolean isStartingTagGameModeConfig =
GAME_MODE_CONFIG_NODE_NAME.equals(parser.getName());
if (!isStartingTagGameModeConfig) {
Slog.w(TAG, "Meta-data does not start with "
+ GAME_MODE_CONFIG_NODE_NAME
+ " tag");
} else {
final TypedArray array = resources.obtainAttributes(attributeSet,
com.android.internal.R.styleable.GameModeConfig);
mPerfModeOptedIn = array.getBoolean(
GameModeConfig_supportsPerformanceGameMode, false);
mBatteryModeOptedIn = array.getBoolean(
GameModeConfig_supportsBatteryGameMode,
false);
mAllowDownscale = array.getBoolean(GameModeConfig_allowGameDownscaling,
true);
mAllowAngle = array.getBoolean(GameModeConfig_allowGameAngleDriver, true);
mAllowFpsOverride = array.getBoolean(GameModeConfig_allowGameFpsOverride,
true);
array.recycle();
}
}
} catch (NameNotFoundException | XmlPullParserException | IOException ex) {
Slog.e(TAG, "Error while parsing XML meta-data for "
+ METADATA_GAME_MODE_CONFIG);
}
return xmlFound;
}
/**
* GameModeConfiguration contains all the values for all the interventions associated with
* a game mode.
@@ -497,7 +570,8 @@ public final class GameManagerService extends IGameManagerService.Stub {
mScaling = !mAllowDownscale || willGamePerformOptimizations(mGameMode)
? DEFAULT_SCALING : parser.getString(SCALING_KEY, DEFAULT_SCALING);
mFps = parser.getString(FPS_KEY, DEFAULT_FPS);
mFps = mAllowFpsOverride && !willGamePerformOptimizations(mGameMode)
? parser.getString(FPS_KEY, DEFAULT_FPS) : DEFAULT_FPS;
// We only want to use ANGLE if:
// - We're allowed to use ANGLE (the app hasn't opted out via the manifest) AND
// - The app has not opted in to performing the work itself AND
@@ -691,7 +765,7 @@ public final class GameManagerService extends IGameManagerService.Stub {
try {
return mPackageManager.getPackageUidAsUser(packageName, userId)
== Binder.getCallingUid();
} catch (PackageManager.NameNotFoundException e) {
} catch (NameNotFoundException e) {
return false;
}
}
@@ -855,7 +929,7 @@ public final class GameManagerService extends IGameManagerService.Stub {
*/
@Override
@RequiresPermission(Manifest.permission.MANAGE_GAME_MODE)
public @GameMode boolean getAngleEnabled(String packageName, int userId)
public @GameMode boolean isAngleEnabled(String packageName, int userId)
throws SecurityException {
final int gameMode = getGameMode(packageName, userId);
if (gameMode == GameManager.GAME_MODE_UNSUPPORTED) {
@@ -1413,7 +1487,7 @@ public final class GameManagerService extends IGameManagerService.Stub {
if (applicationInfo.category != ApplicationInfo.CATEGORY_GAME) {
return;
}
} catch (PackageManager.NameNotFoundException e) {
} catch (NameNotFoundException e) {
// Ignore the exception.
}
switch (intent.getAction()) {

View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<game-mode-config
xmlns:android="http://schemas.android.com/apk/res/android"
android:supportsPerformanceGameMode="false"
android:supportsBatteryGameMode="false"
android:allowGameAngleDriver="false"
android:allowGameDownscaling="false"
android:allowGameFpsOverride="false"
/>

View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<game-mode-config
xmlns:android="http://schemas.android.com/apk/res/android"
android:supportsPerformanceGameMode="false"
android:supportsBatteryGameMode="false"
android:allowGameAngleDriver="true"
android:allowGameDownscaling="true"
android:allowGameFpsOverride="true"
/>

View File

@@ -26,6 +26,8 @@ import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@@ -41,6 +43,9 @@ import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
import android.content.res.AssetManager;
import android.content.res.Resources;
import android.content.res.XmlResourceParser;
import android.hardware.power.Mode;
import android.os.Bundle;
import android.os.PowerManagerInternal;
@@ -159,11 +164,17 @@ public class GameManagerServiceTests {
mPackageName = mMockContext.getPackageName();
final ApplicationInfo applicationInfo = new ApplicationInfo();
applicationInfo.category = ApplicationInfo.CATEGORY_GAME;
applicationInfo.packageName = mPackageName;
final PackageInfo pi = new PackageInfo();
pi.packageName = mPackageName;
pi.applicationInfo = applicationInfo;
final List<PackageInfo> packages = new ArrayList<>();
packages.add(pi);
final Resources resources =
InstrumentationRegistry.getInstrumentation().getContext().getResources();
when(mMockPackageManager.getResourcesForApplication(anyString()))
.thenReturn(resources);
when(mMockPackageManager.getInstalledPackagesAsUser(anyInt(), anyInt()))
.thenReturn(packages);
when(mMockPackageManager.getApplicationInfoAsUser(anyString(), anyInt(), anyInt()))
@@ -322,6 +333,46 @@ public class GameManagerServiceTests {
.thenReturn(applicationInfo);
}
private void mockInterventionsEnabledFromXml() throws Exception {
final ApplicationInfo applicationInfo = mMockPackageManager.getApplicationInfoAsUser(
mPackageName, PackageManager.GET_META_DATA, USER_ID_1);
Bundle metaDataBundle = new Bundle();
final int resId = 123;
metaDataBundle.putInt(
GameManagerService.GamePackageConfiguration.METADATA_GAME_MODE_CONFIG, resId);
applicationInfo.metaData = metaDataBundle;
when(mMockPackageManager.getApplicationInfoAsUser(anyString(), anyInt(), anyInt()))
.thenReturn(applicationInfo);
seedGameManagerServiceMetaDataFromFile(mPackageName, resId,
"res/xml/gama_manager_service_metadata_config_enabled.xml");
}
private void mockInterventionsDisabledFromXml() throws Exception {
final ApplicationInfo applicationInfo = mMockPackageManager.getApplicationInfoAsUser(
mPackageName, PackageManager.GET_META_DATA, USER_ID_1);
Bundle metaDataBundle = new Bundle();
final int resId = 123;
metaDataBundle.putInt(
GameManagerService.GamePackageConfiguration.METADATA_GAME_MODE_CONFIG, resId);
applicationInfo.metaData = metaDataBundle;
when(mMockPackageManager.getApplicationInfoAsUser(anyString(), anyInt(), anyInt()))
.thenReturn(applicationInfo);
seedGameManagerServiceMetaDataFromFile(mPackageName, resId,
"res/xml/gama_manager_service_metadata_config_disabled.xml");
}
private void seedGameManagerServiceMetaDataFromFile(String packageName, int resId,
String fileName)
throws Exception {
AssetManager assetManager =
InstrumentationRegistry.getInstrumentation().getContext().getAssets();
XmlResourceParser xmlResourceParser =
assetManager.openXmlResourceParser(fileName);
when(mMockPackageManager.getXml(eq(packageName), eq(resId), any()))
.thenReturn(xmlResourceParser);
}
/**
* By default game mode is not supported.
*/
@@ -511,8 +562,8 @@ public class GameManagerServiceTests {
gameManagerService.getConfig(mPackageName);
assertEquals(config.getGameModeConfiguration(gameMode).getUseAngle(), angleEnabled);
// Validate GameManagerService.getAngleEnabled() returns the correct value.
assertEquals(gameManagerService.getAngleEnabled(mPackageName, USER_ID_1), angleEnabled);
// Validate GameManagerService.isAngleEnabled() returns the correct value.
assertEquals(gameManagerService.isAngleEnabled(mPackageName, USER_ID_1), angleEnabled);
}
private void checkFps(GameManagerService gameManagerService, int gameMode, int fps) {
@@ -523,7 +574,7 @@ public class GameManagerServiceTests {
}
GameManagerService.GamePackageConfiguration config =
gameManagerService.getConfig(mPackageName);
assertEquals(config.getGameModeConfiguration(gameMode).getFps(), fps);
assertEquals(fps, config.getGameModeConfiguration(gameMode).getFps());
}
/**
@@ -904,6 +955,36 @@ public class GameManagerServiceTests {
checkAngleEnabled(gameManagerService, GameManager.GAME_MODE_PERFORMANCE, true);
}
@Test
public void testGameModeConfigAllowFpsTrue() throws Exception {
mockDeviceConfigAll();
mockModifyGameModeGranted();
mockInterventionsEnabledFromXml();
GameManagerService gameManagerService = new GameManagerService(mMockContext,
mTestLooper.getLooper());
startUser(gameManagerService, USER_ID_1);
GameManagerService.GamePackageConfiguration config =
gameManagerService.getConfig(mPackageName);
assertEquals(90,
config.getGameModeConfiguration(GameManager.GAME_MODE_PERFORMANCE).getFps());
assertEquals(30, config.getGameModeConfiguration(GameManager.GAME_MODE_BATTERY).getFps());
}
@Test
public void testGameModeConfigAllowFpsFalse() throws Exception {
mockDeviceConfigAll();
mockModifyGameModeGranted();
mockInterventionsDisabledFromXml();
GameManagerService gameManagerService = new GameManagerService(mMockContext,
mTestLooper.getLooper());
startUser(gameManagerService, USER_ID_1);
GameManagerService.GamePackageConfiguration config =
gameManagerService.getConfig(mPackageName);
assertEquals(0,
config.getGameModeConfiguration(GameManager.GAME_MODE_PERFORMANCE).getFps());
assertEquals(0, config.getGameModeConfiguration(GameManager.GAME_MODE_BATTERY).getFps());
}
@Test
public void testInterventionFps() throws Exception {
mockDeviceConfigAll();