Loading core/java/android/app/GameManager.java +16 −0 Original line number Diff line number Diff line Loading @@ -135,6 +135,7 @@ public final class GameManager { throw e.rethrowFromSystemServer(); } } /** * Returns a list of supported game modes for a given package. * <p> Loading @@ -151,4 +152,19 @@ public final class GameManager { } } /** * Returns if ANGLE is enabled for a given package and user ID. * <p> * The caller must have {@link android.Manifest.permission#MANAGE_GAME_MODE}. * * @hide */ @RequiresPermission(Manifest.permission.MANAGE_GAME_MODE) public @GameMode boolean getAngleEnabled(@NonNull String packageName) { try { return mService.getAngleEnabled(packageName, mContext.getUserId()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } } core/java/android/app/IGameManagerService.aidl +1 −0 Original line number Diff line number Diff line Loading @@ -23,4 +23,5 @@ 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); } No newline at end of file core/java/android/os/GraphicsEnvironment.java +36 −9 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ package android.os; import android.app.Activity; import android.app.GameManager; import android.content.BroadcastReceiver; import android.content.ContentResolver; import android.content.Context; Loading Loading @@ -113,6 +114,7 @@ public class GraphicsEnvironment { private ClassLoader mClassLoader; private String mLibrarySearchPaths; private String mLibraryPermittedPaths; private GameManager mGameManager; private int mAngleOptInIndex = -1; Loading @@ -125,6 +127,8 @@ public class GraphicsEnvironment { final ApplicationInfo appInfoWithMetaData = getAppInfoWithMetadata(context, pm, packageName); mGameManager = context.getSystemService(GameManager.class); Trace.traceBegin(Trace.TRACE_TAG_GRAPHICS, "setupGpuLayers"); setupGpuLayers(context, coreSettings, pm, packageName, appInfoWithMetaData); Trace.traceEnd(Trace.TRACE_TAG_GRAPHICS); Loading @@ -142,6 +146,23 @@ public class GraphicsEnvironment { Trace.traceEnd(Trace.TRACE_TAG_GRAPHICS); } /** * Query to determine if the Game Mode has enabled ANGLE. */ private boolean isAngleEnabledByGameMode(Context context, String packageName) { try { final boolean gameModeEnabledAngle = (mGameManager != null) && mGameManager.getAngleEnabled(packageName); Log.v(TAG, "ANGLE GameManagerService for " + packageName + ": " + gameModeEnabledAngle); return gameModeEnabledAngle; } catch (SecurityException e) { Log.e(TAG, "Caught exception while querying GameManagerService if ANGLE is enabled " + "for package: " + packageName); } return false; } /** * Query to determine if ANGLE should be used */ Loading @@ -163,7 +184,9 @@ public class GraphicsEnvironment { Log.v(TAG, "ANGLE developer option for " + packageName + ": " + devOptIn); } return requested; final boolean gameModeEnabledAngle = isAngleEnabledByGameMode(context, packageName); return requested || gameModeEnabledAngle; } private int getVulkanVersion(PackageManager pm) { Loading Loading @@ -521,16 +544,20 @@ public class GraphicsEnvironment { if (DEBUG) Log.v(TAG, "ANGLE package libs: " + paths); // If the user has set the developer option to something other than default, // we need to call setAngleInfo() with the package name and the developer // option value (native/angle/other). Then later when we are actually trying to // load a driver, GraphicsEnv::getShouldUseAngle() has seen the package name before // and can confidently answer yes/no based on the previously set developer // option value. final String devOptIn = getDriverForPackage(context, bundle, packageName); // We need to call setAngleInfo() with the package name and the developer option value //(native/angle/other). Then later when we are actually trying to load a driver, //GraphicsEnv::getShouldUseAngle() has seen the package name before and can confidently //answer yes/no based on the previously set developer option value. final String devOptIn; final String[] features = getAngleEglFeatures(context, bundle); final boolean gameModeEnabledAngle = isAngleEnabledByGameMode(context, packageName); if (gameModeEnabledAngle) { devOptIn = ANGLE_GL_DRIVER_CHOICE_ANGLE; } else { devOptIn = getDriverForPackage(context, bundle, packageName); } setAngleInfo(paths, packageName, devOptIn, features); return true; } Loading services/core/java/com/android/server/app/GameManagerService.java +75 −9 Original line number Diff line number Diff line Loading @@ -271,6 +271,13 @@ public final class GameManagerService extends IGameManagerService.Stub { public static final String METADATA_WM_ALLOW_DOWNSCALE = "com.android.graphics.intervention.wm.allowDownscale"; /** * Metadata that can be included in the app manifest to allow/disallow any ANGLE * interventions. Default value is TRUE. */ public static final String METADATA_ANGLE_ALLOW_ANGLE = "com.android.graphics.intervention.angle.allowAngle"; /** * 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 Loading @@ -294,6 +301,7 @@ public final class GameManagerService extends IGameManagerService.Stub { private boolean mPerfModeOptedIn; private boolean mBatteryModeOptedIn; private boolean mAllowDownscale; private boolean mAllowAngle; GamePackageConfiguration(String packageName, int userId) { mPackageName = packageName; Loading @@ -305,10 +313,12 @@ public final class GameManagerService extends IGameManagerService.Stub { 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; } } catch (PackageManager.NameNotFoundException e) { // Not all packages are installed, hence ignore those that are not installed yet. Loading Loading @@ -340,14 +350,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 DEFAULT_SCALING = "1.0"; public static final String ANGLE_KEY = "useAngle"; private final @GameMode int mGameMode; private final String mScaling; private final boolean mUseAngle; GameModeConfiguration(KeyValueListParser parser) { mGameMode = parser.getInt(MODE_KEY, GameManager.GAME_MODE_UNSUPPORTED); mScaling = !mAllowDownscale || isGameModeOptedIn(mGameMode) // isGameModeOptedIn() returns if an app will handle all of the changes necessary // for a particular game mode. If so, the Android framework (i.e. // 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); // 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 // - The Phenotype config has enabled it. mUseAngle = mAllowAngle && !willGamePerformOptimizations(mGameMode) && parser.getBoolean(ANGLE_KEY, false); } public int getGameMode() { Loading @@ -358,6 +380,10 @@ public final class GameManagerService extends IGameManagerService.Stub { return mScaling; } public boolean getUseAngle() { return mUseAngle; } public boolean isValid() { return (mGameMode == GameManager.GAME_MODE_PERFORMANCE || mGameMode == GameManager.GAME_MODE_BATTERY) Loading @@ -368,7 +394,8 @@ public final class GameManagerService extends IGameManagerService.Stub { * @hide */ public String toString() { return "[Game Mode:" + mGameMode + ",Scaling:" + mScaling + "]"; return "[Game Mode:" + mGameMode + ",Scaling:" + mScaling + ",Use Angle:" + mUseAngle + "]"; } /** Loading @@ -384,13 +411,14 @@ public final class GameManagerService extends IGameManagerService.Stub { } /** * Gets whether a package has opted into a game mode via its manifest. * Returns if the app will assume full responsibility for the experience provided by this * mode. If True, the system will not perform any interventions for the app. * * @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) { public boolean willGamePerformOptimizations(@GameMode int gameMode) { return (mBatteryModeOptedIn && gameMode == GameManager.GAME_MODE_BATTERY) || (mPerfModeOptedIn && gameMode == GameManager.GAME_MODE_PERFORMANCE); } Loading Loading @@ -631,7 +659,34 @@ public final class GameManagerService extends IGameManagerService.Stub { mHandler.sendMessageDelayed(msg, WRITE_SETTINGS_DELAY); } } updateCompatModeDownscale(packageName, gameMode); updateInterventions(packageName, gameMode); } /** * Get if ANGLE is enabled for the package for the currently enabled game mode. * Checks that the caller has {@link android.Manifest.permission#MANAGE_GAME_MODE}. */ @Override @RequiresPermission(Manifest.permission.MANAGE_GAME_MODE) public @GameMode boolean getAngleEnabled(String packageName, int userId) throws SecurityException { final int gameMode = getGameMode(packageName, userId); if (gameMode == GameManager.GAME_MODE_UNSUPPORTED) { return false; } synchronized (mDeviceConfigLock) { final GamePackageConfiguration config = mConfigs.get(packageName); if (config == null) { return false; } GamePackageConfiguration.GameModeConfiguration gameModeConfiguration = config.getGameModeConfiguration(gameMode); if (gameModeConfiguration == null) { return false; } return gameModeConfiguration.getUseAngle(); } } /** Loading Loading @@ -753,7 +808,7 @@ public final class GameManagerService extends IGameManagerService.Stub { if (DEBUG) { Slog.v(TAG, dumpDeviceConfigs()); } if (packageConfig.isGameModeOptedIn(gameMode)) { if (packageConfig.willGamePerformOptimizations(gameMode)) { disableCompatScale(packageName); return; } Loading Loading @@ -782,6 +837,17 @@ public final class GameManagerService extends IGameManagerService.Stub { return (bitField & modeToBitmask(gameMode)) != 0; } @RequiresPermission(Manifest.permission.WRITE_SECURE_SETTINGS) private void updateUseAngle(String packageName, @GameMode int gameMode) { // TODO (b/188475576): Nothing to do yet. Remove if it's still empty when we're ready to // ship. } private void updateInterventions(String packageName, @GameMode int gameMode) { updateCompatModeDownscale(packageName, gameMode); updateUseAngle(packageName, gameMode); } /** * @hide */ Loading Loading @@ -839,11 +905,11 @@ public final class GameManagerService extends IGameManagerService.Stub { if (newGameMode != gameMode) { setGameMode(packageName, newGameMode, userId); } updateCompatModeDownscale(packageName, gameMode); updateInterventions(packageName, gameMode); } } } catch (Exception e) { Slog.e(TAG, "Failed to update compat modes for user: " + userId); Slog.e(TAG, "Failed to update compat modes for user " + userId + ": " + e); } } Loading services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java +96 −6 Original line number Diff line number Diff line Loading @@ -182,7 +182,14 @@ public class GameManagerServiceTests { } private void mockDeviceConfigPerformance() { String configString = "mode=2,downscaleFactor=0.5"; String configString = "mode=2,downscaleFactor=0.5,useAngle=false"; when(DeviceConfig.getProperty(anyString(), anyString())) .thenReturn(configString); } // ANGLE will be disabled for most apps, so treat enabling ANGLE as a special case. private void mockDeviceConfigPerformanceEnableAngle() { String configString = "mode=2,downscaleFactor=0.5,useAngle=true"; when(DeviceConfig.getProperty(anyString(), anyString())) .thenReturn(configString); } Loading Loading @@ -212,7 +219,8 @@ public class GameManagerServiceTests { } private void mockGameModeOptInAll() throws Exception { final ApplicationInfo applicationInfo = new ApplicationInfo(); final ApplicationInfo applicationInfo = mMockPackageManager.getApplicationInfoAsUser( mPackageName, PackageManager.GET_META_DATA, USER_ID_1); Bundle metaDataBundle = new Bundle(); metaDataBundle.putBoolean( GameManagerService.GamePackageConfiguration.METADATA_PERFORMANCE_MODE_ENABLE, true); Loading @@ -224,7 +232,8 @@ public class GameManagerServiceTests { } private void mockGameModeOptInPerformance() throws Exception { final ApplicationInfo applicationInfo = new ApplicationInfo(); final ApplicationInfo applicationInfo = mMockPackageManager.getApplicationInfoAsUser( mPackageName, PackageManager.GET_META_DATA, USER_ID_1); Bundle metaDataBundle = new Bundle(); metaDataBundle.putBoolean( GameManagerService.GamePackageConfiguration.METADATA_PERFORMANCE_MODE_ENABLE, true); Loading @@ -234,7 +243,8 @@ public class GameManagerServiceTests { } private void mockGameModeOptInBattery() throws Exception { final ApplicationInfo applicationInfo = new ApplicationInfo(); final ApplicationInfo applicationInfo = mMockPackageManager.getApplicationInfoAsUser( mPackageName, PackageManager.GET_META_DATA, USER_ID_1); Bundle metaDataBundle = new Bundle(); metaDataBundle.putBoolean( GameManagerService.GamePackageConfiguration.METADATA_BATTERY_MODE_ENABLE, true); Loading @@ -244,7 +254,8 @@ public class GameManagerServiceTests { } private void mockInterventionAllowDownscaleTrue() throws Exception { final ApplicationInfo applicationInfo = new ApplicationInfo(); final ApplicationInfo applicationInfo = mMockPackageManager.getApplicationInfoAsUser( mPackageName, PackageManager.GET_META_DATA, USER_ID_1); Bundle metaDataBundle = new Bundle(); metaDataBundle.putBoolean( GameManagerService.GamePackageConfiguration.METADATA_WM_ALLOW_DOWNSCALE, true); Loading @@ -254,7 +265,8 @@ public class GameManagerServiceTests { } private void mockInterventionAllowDownscaleFalse() throws Exception { final ApplicationInfo applicationInfo = new ApplicationInfo(); final ApplicationInfo applicationInfo = mMockPackageManager.getApplicationInfoAsUser( mPackageName, PackageManager.GET_META_DATA, USER_ID_1); Bundle metaDataBundle = new Bundle(); metaDataBundle.putBoolean( GameManagerService.GamePackageConfiguration.METADATA_WM_ALLOW_DOWNSCALE, false); Loading @@ -263,6 +275,27 @@ public class GameManagerServiceTests { .thenReturn(applicationInfo); } private void mockInterventionAllowAngleTrue() throws Exception { final ApplicationInfo applicationInfo = mMockPackageManager.getApplicationInfoAsUser( mPackageName, PackageManager.GET_META_DATA, USER_ID_1); Bundle metaDataBundle = new Bundle(); metaDataBundle.putBoolean( GameManagerService.GamePackageConfiguration.METADATA_ANGLE_ALLOW_ANGLE, true); when(mMockPackageManager.getApplicationInfoAsUser(anyString(), anyInt(), anyInt())) .thenReturn(applicationInfo); } private void mockInterventionAllowAngleFalse() throws Exception { final ApplicationInfo applicationInfo = mMockPackageManager.getApplicationInfoAsUser( mPackageName, PackageManager.GET_META_DATA, USER_ID_1); Bundle metaDataBundle = new Bundle(); metaDataBundle.putBoolean( GameManagerService.GamePackageConfiguration.METADATA_ANGLE_ALLOW_ANGLE, false); applicationInfo.metaData = metaDataBundle; when(mMockPackageManager.getApplicationInfoAsUser(anyString(), anyInt(), anyInt())) .thenReturn(applicationInfo); } /** * By default game mode is not supported. */ Loading Loading @@ -427,6 +460,19 @@ public class GameManagerServiceTests { assertEquals(config.getGameModeConfiguration(gameMode).getScaling(), scaling); } private void checkAngleEnabled(GameManagerService gameManagerService, int gameMode, boolean angleEnabled) { gameManagerService.updateConfigsForUser(USER_ID_1, mPackageName); // Validate GamePackageConfiguration returns the correct value. GameManagerService.GamePackageConfiguration config = gameManagerService.getConfig(mPackageName); assertEquals(config.getGameModeConfiguration(gameMode).getUseAngle(), angleEnabled); // Validate GameManagerService.getAngleEnabled() returns the correct value. assertEquals(gameManagerService.getAngleEnabled(mPackageName, USER_ID_1), angleEnabled); } /** * Phenotype device config exists, but is only propagating the default value. */ Loading Loading @@ -591,6 +637,50 @@ public class GameManagerServiceTests { checkDownscaling(GameManager.GAME_MODE_PERFORMANCE, "0.5"); } /** * PERFORMANCE game mode is configured through Phenotype. The app hasn't specified any metadata. */ @Test public void testInterventionAllowAngleDefault() throws Exception { GameManagerService gameManagerService = new GameManagerService(mMockContext); gameManagerService.onUserStarting(USER_ID_1); mockDeviceConfigPerformance(); mockModifyGameModeGranted(); checkAngleEnabled(gameManagerService, GameManager.GAME_MODE_PERFORMANCE, false); } /** * PERFORMANCE game mode is configured through Phenotype. The app has opted-out of ANGLE. */ @Test public void testInterventionAllowAngleFalse() throws Exception { GameManagerService gameManagerService = new GameManagerService(mMockContext); gameManagerService.onUserStarting(USER_ID_1); mockDeviceConfigPerformanceEnableAngle(); mockInterventionAllowAngleFalse(); mockModifyGameModeGranted(); checkAngleEnabled(gameManagerService, GameManager.GAME_MODE_PERFORMANCE, false); } /** * PERFORMANCE game mode is configured through Phenotype. The app has redundantly specified * the ANGLE metadata default value of "true". */ @Test public void testInterventionAllowAngleTrue() throws Exception { mockDeviceConfigPerformanceEnableAngle(); mockInterventionAllowAngleTrue(); GameManagerService gameManagerService = new GameManagerService(mMockContext); gameManagerService.onUserStarting(USER_ID_1); mockModifyGameModeGranted(); gameManagerService.setGameMode(mPackageName, GameManager.GAME_MODE_PERFORMANCE, USER_ID_1); assertEquals(GameManager.GAME_MODE_PERFORMANCE, gameManagerService.getGameMode(mPackageName, USER_ID_1)); checkAngleEnabled(gameManagerService, GameManager.GAME_MODE_PERFORMANCE, true); } /** * 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. Loading Loading
core/java/android/app/GameManager.java +16 −0 Original line number Diff line number Diff line Loading @@ -135,6 +135,7 @@ public final class GameManager { throw e.rethrowFromSystemServer(); } } /** * Returns a list of supported game modes for a given package. * <p> Loading @@ -151,4 +152,19 @@ public final class GameManager { } } /** * Returns if ANGLE is enabled for a given package and user ID. * <p> * The caller must have {@link android.Manifest.permission#MANAGE_GAME_MODE}. * * @hide */ @RequiresPermission(Manifest.permission.MANAGE_GAME_MODE) public @GameMode boolean getAngleEnabled(@NonNull String packageName) { try { return mService.getAngleEnabled(packageName, mContext.getUserId()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } }
core/java/android/app/IGameManagerService.aidl +1 −0 Original line number Diff line number Diff line Loading @@ -23,4 +23,5 @@ 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); } No newline at end of file
core/java/android/os/GraphicsEnvironment.java +36 −9 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ package android.os; import android.app.Activity; import android.app.GameManager; import android.content.BroadcastReceiver; import android.content.ContentResolver; import android.content.Context; Loading Loading @@ -113,6 +114,7 @@ public class GraphicsEnvironment { private ClassLoader mClassLoader; private String mLibrarySearchPaths; private String mLibraryPermittedPaths; private GameManager mGameManager; private int mAngleOptInIndex = -1; Loading @@ -125,6 +127,8 @@ public class GraphicsEnvironment { final ApplicationInfo appInfoWithMetaData = getAppInfoWithMetadata(context, pm, packageName); mGameManager = context.getSystemService(GameManager.class); Trace.traceBegin(Trace.TRACE_TAG_GRAPHICS, "setupGpuLayers"); setupGpuLayers(context, coreSettings, pm, packageName, appInfoWithMetaData); Trace.traceEnd(Trace.TRACE_TAG_GRAPHICS); Loading @@ -142,6 +146,23 @@ public class GraphicsEnvironment { Trace.traceEnd(Trace.TRACE_TAG_GRAPHICS); } /** * Query to determine if the Game Mode has enabled ANGLE. */ private boolean isAngleEnabledByGameMode(Context context, String packageName) { try { final boolean gameModeEnabledAngle = (mGameManager != null) && mGameManager.getAngleEnabled(packageName); Log.v(TAG, "ANGLE GameManagerService for " + packageName + ": " + gameModeEnabledAngle); return gameModeEnabledAngle; } catch (SecurityException e) { Log.e(TAG, "Caught exception while querying GameManagerService if ANGLE is enabled " + "for package: " + packageName); } return false; } /** * Query to determine if ANGLE should be used */ Loading @@ -163,7 +184,9 @@ public class GraphicsEnvironment { Log.v(TAG, "ANGLE developer option for " + packageName + ": " + devOptIn); } return requested; final boolean gameModeEnabledAngle = isAngleEnabledByGameMode(context, packageName); return requested || gameModeEnabledAngle; } private int getVulkanVersion(PackageManager pm) { Loading Loading @@ -521,16 +544,20 @@ public class GraphicsEnvironment { if (DEBUG) Log.v(TAG, "ANGLE package libs: " + paths); // If the user has set the developer option to something other than default, // we need to call setAngleInfo() with the package name and the developer // option value (native/angle/other). Then later when we are actually trying to // load a driver, GraphicsEnv::getShouldUseAngle() has seen the package name before // and can confidently answer yes/no based on the previously set developer // option value. final String devOptIn = getDriverForPackage(context, bundle, packageName); // We need to call setAngleInfo() with the package name and the developer option value //(native/angle/other). Then later when we are actually trying to load a driver, //GraphicsEnv::getShouldUseAngle() has seen the package name before and can confidently //answer yes/no based on the previously set developer option value. final String devOptIn; final String[] features = getAngleEglFeatures(context, bundle); final boolean gameModeEnabledAngle = isAngleEnabledByGameMode(context, packageName); if (gameModeEnabledAngle) { devOptIn = ANGLE_GL_DRIVER_CHOICE_ANGLE; } else { devOptIn = getDriverForPackage(context, bundle, packageName); } setAngleInfo(paths, packageName, devOptIn, features); return true; } Loading
services/core/java/com/android/server/app/GameManagerService.java +75 −9 Original line number Diff line number Diff line Loading @@ -271,6 +271,13 @@ public final class GameManagerService extends IGameManagerService.Stub { public static final String METADATA_WM_ALLOW_DOWNSCALE = "com.android.graphics.intervention.wm.allowDownscale"; /** * Metadata that can be included in the app manifest to allow/disallow any ANGLE * interventions. Default value is TRUE. */ public static final String METADATA_ANGLE_ALLOW_ANGLE = "com.android.graphics.intervention.angle.allowAngle"; /** * 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 Loading @@ -294,6 +301,7 @@ public final class GameManagerService extends IGameManagerService.Stub { private boolean mPerfModeOptedIn; private boolean mBatteryModeOptedIn; private boolean mAllowDownscale; private boolean mAllowAngle; GamePackageConfiguration(String packageName, int userId) { mPackageName = packageName; Loading @@ -305,10 +313,12 @@ public final class GameManagerService extends IGameManagerService.Stub { 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; } } catch (PackageManager.NameNotFoundException e) { // Not all packages are installed, hence ignore those that are not installed yet. Loading Loading @@ -340,14 +350,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 DEFAULT_SCALING = "1.0"; public static final String ANGLE_KEY = "useAngle"; private final @GameMode int mGameMode; private final String mScaling; private final boolean mUseAngle; GameModeConfiguration(KeyValueListParser parser) { mGameMode = parser.getInt(MODE_KEY, GameManager.GAME_MODE_UNSUPPORTED); mScaling = !mAllowDownscale || isGameModeOptedIn(mGameMode) // isGameModeOptedIn() returns if an app will handle all of the changes necessary // for a particular game mode. If so, the Android framework (i.e. // 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); // 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 // - The Phenotype config has enabled it. mUseAngle = mAllowAngle && !willGamePerformOptimizations(mGameMode) && parser.getBoolean(ANGLE_KEY, false); } public int getGameMode() { Loading @@ -358,6 +380,10 @@ public final class GameManagerService extends IGameManagerService.Stub { return mScaling; } public boolean getUseAngle() { return mUseAngle; } public boolean isValid() { return (mGameMode == GameManager.GAME_MODE_PERFORMANCE || mGameMode == GameManager.GAME_MODE_BATTERY) Loading @@ -368,7 +394,8 @@ public final class GameManagerService extends IGameManagerService.Stub { * @hide */ public String toString() { return "[Game Mode:" + mGameMode + ",Scaling:" + mScaling + "]"; return "[Game Mode:" + mGameMode + ",Scaling:" + mScaling + ",Use Angle:" + mUseAngle + "]"; } /** Loading @@ -384,13 +411,14 @@ public final class GameManagerService extends IGameManagerService.Stub { } /** * Gets whether a package has opted into a game mode via its manifest. * Returns if the app will assume full responsibility for the experience provided by this * mode. If True, the system will not perform any interventions for the app. * * @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) { public boolean willGamePerformOptimizations(@GameMode int gameMode) { return (mBatteryModeOptedIn && gameMode == GameManager.GAME_MODE_BATTERY) || (mPerfModeOptedIn && gameMode == GameManager.GAME_MODE_PERFORMANCE); } Loading Loading @@ -631,7 +659,34 @@ public final class GameManagerService extends IGameManagerService.Stub { mHandler.sendMessageDelayed(msg, WRITE_SETTINGS_DELAY); } } updateCompatModeDownscale(packageName, gameMode); updateInterventions(packageName, gameMode); } /** * Get if ANGLE is enabled for the package for the currently enabled game mode. * Checks that the caller has {@link android.Manifest.permission#MANAGE_GAME_MODE}. */ @Override @RequiresPermission(Manifest.permission.MANAGE_GAME_MODE) public @GameMode boolean getAngleEnabled(String packageName, int userId) throws SecurityException { final int gameMode = getGameMode(packageName, userId); if (gameMode == GameManager.GAME_MODE_UNSUPPORTED) { return false; } synchronized (mDeviceConfigLock) { final GamePackageConfiguration config = mConfigs.get(packageName); if (config == null) { return false; } GamePackageConfiguration.GameModeConfiguration gameModeConfiguration = config.getGameModeConfiguration(gameMode); if (gameModeConfiguration == null) { return false; } return gameModeConfiguration.getUseAngle(); } } /** Loading Loading @@ -753,7 +808,7 @@ public final class GameManagerService extends IGameManagerService.Stub { if (DEBUG) { Slog.v(TAG, dumpDeviceConfigs()); } if (packageConfig.isGameModeOptedIn(gameMode)) { if (packageConfig.willGamePerformOptimizations(gameMode)) { disableCompatScale(packageName); return; } Loading Loading @@ -782,6 +837,17 @@ public final class GameManagerService extends IGameManagerService.Stub { return (bitField & modeToBitmask(gameMode)) != 0; } @RequiresPermission(Manifest.permission.WRITE_SECURE_SETTINGS) private void updateUseAngle(String packageName, @GameMode int gameMode) { // TODO (b/188475576): Nothing to do yet. Remove if it's still empty when we're ready to // ship. } private void updateInterventions(String packageName, @GameMode int gameMode) { updateCompatModeDownscale(packageName, gameMode); updateUseAngle(packageName, gameMode); } /** * @hide */ Loading Loading @@ -839,11 +905,11 @@ public final class GameManagerService extends IGameManagerService.Stub { if (newGameMode != gameMode) { setGameMode(packageName, newGameMode, userId); } updateCompatModeDownscale(packageName, gameMode); updateInterventions(packageName, gameMode); } } } catch (Exception e) { Slog.e(TAG, "Failed to update compat modes for user: " + userId); Slog.e(TAG, "Failed to update compat modes for user " + userId + ": " + e); } } Loading
services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java +96 −6 Original line number Diff line number Diff line Loading @@ -182,7 +182,14 @@ public class GameManagerServiceTests { } private void mockDeviceConfigPerformance() { String configString = "mode=2,downscaleFactor=0.5"; String configString = "mode=2,downscaleFactor=0.5,useAngle=false"; when(DeviceConfig.getProperty(anyString(), anyString())) .thenReturn(configString); } // ANGLE will be disabled for most apps, so treat enabling ANGLE as a special case. private void mockDeviceConfigPerformanceEnableAngle() { String configString = "mode=2,downscaleFactor=0.5,useAngle=true"; when(DeviceConfig.getProperty(anyString(), anyString())) .thenReturn(configString); } Loading Loading @@ -212,7 +219,8 @@ public class GameManagerServiceTests { } private void mockGameModeOptInAll() throws Exception { final ApplicationInfo applicationInfo = new ApplicationInfo(); final ApplicationInfo applicationInfo = mMockPackageManager.getApplicationInfoAsUser( mPackageName, PackageManager.GET_META_DATA, USER_ID_1); Bundle metaDataBundle = new Bundle(); metaDataBundle.putBoolean( GameManagerService.GamePackageConfiguration.METADATA_PERFORMANCE_MODE_ENABLE, true); Loading @@ -224,7 +232,8 @@ public class GameManagerServiceTests { } private void mockGameModeOptInPerformance() throws Exception { final ApplicationInfo applicationInfo = new ApplicationInfo(); final ApplicationInfo applicationInfo = mMockPackageManager.getApplicationInfoAsUser( mPackageName, PackageManager.GET_META_DATA, USER_ID_1); Bundle metaDataBundle = new Bundle(); metaDataBundle.putBoolean( GameManagerService.GamePackageConfiguration.METADATA_PERFORMANCE_MODE_ENABLE, true); Loading @@ -234,7 +243,8 @@ public class GameManagerServiceTests { } private void mockGameModeOptInBattery() throws Exception { final ApplicationInfo applicationInfo = new ApplicationInfo(); final ApplicationInfo applicationInfo = mMockPackageManager.getApplicationInfoAsUser( mPackageName, PackageManager.GET_META_DATA, USER_ID_1); Bundle metaDataBundle = new Bundle(); metaDataBundle.putBoolean( GameManagerService.GamePackageConfiguration.METADATA_BATTERY_MODE_ENABLE, true); Loading @@ -244,7 +254,8 @@ public class GameManagerServiceTests { } private void mockInterventionAllowDownscaleTrue() throws Exception { final ApplicationInfo applicationInfo = new ApplicationInfo(); final ApplicationInfo applicationInfo = mMockPackageManager.getApplicationInfoAsUser( mPackageName, PackageManager.GET_META_DATA, USER_ID_1); Bundle metaDataBundle = new Bundle(); metaDataBundle.putBoolean( GameManagerService.GamePackageConfiguration.METADATA_WM_ALLOW_DOWNSCALE, true); Loading @@ -254,7 +265,8 @@ public class GameManagerServiceTests { } private void mockInterventionAllowDownscaleFalse() throws Exception { final ApplicationInfo applicationInfo = new ApplicationInfo(); final ApplicationInfo applicationInfo = mMockPackageManager.getApplicationInfoAsUser( mPackageName, PackageManager.GET_META_DATA, USER_ID_1); Bundle metaDataBundle = new Bundle(); metaDataBundle.putBoolean( GameManagerService.GamePackageConfiguration.METADATA_WM_ALLOW_DOWNSCALE, false); Loading @@ -263,6 +275,27 @@ public class GameManagerServiceTests { .thenReturn(applicationInfo); } private void mockInterventionAllowAngleTrue() throws Exception { final ApplicationInfo applicationInfo = mMockPackageManager.getApplicationInfoAsUser( mPackageName, PackageManager.GET_META_DATA, USER_ID_1); Bundle metaDataBundle = new Bundle(); metaDataBundle.putBoolean( GameManagerService.GamePackageConfiguration.METADATA_ANGLE_ALLOW_ANGLE, true); when(mMockPackageManager.getApplicationInfoAsUser(anyString(), anyInt(), anyInt())) .thenReturn(applicationInfo); } private void mockInterventionAllowAngleFalse() throws Exception { final ApplicationInfo applicationInfo = mMockPackageManager.getApplicationInfoAsUser( mPackageName, PackageManager.GET_META_DATA, USER_ID_1); Bundle metaDataBundle = new Bundle(); metaDataBundle.putBoolean( GameManagerService.GamePackageConfiguration.METADATA_ANGLE_ALLOW_ANGLE, false); applicationInfo.metaData = metaDataBundle; when(mMockPackageManager.getApplicationInfoAsUser(anyString(), anyInt(), anyInt())) .thenReturn(applicationInfo); } /** * By default game mode is not supported. */ Loading Loading @@ -427,6 +460,19 @@ public class GameManagerServiceTests { assertEquals(config.getGameModeConfiguration(gameMode).getScaling(), scaling); } private void checkAngleEnabled(GameManagerService gameManagerService, int gameMode, boolean angleEnabled) { gameManagerService.updateConfigsForUser(USER_ID_1, mPackageName); // Validate GamePackageConfiguration returns the correct value. GameManagerService.GamePackageConfiguration config = gameManagerService.getConfig(mPackageName); assertEquals(config.getGameModeConfiguration(gameMode).getUseAngle(), angleEnabled); // Validate GameManagerService.getAngleEnabled() returns the correct value. assertEquals(gameManagerService.getAngleEnabled(mPackageName, USER_ID_1), angleEnabled); } /** * Phenotype device config exists, but is only propagating the default value. */ Loading Loading @@ -591,6 +637,50 @@ public class GameManagerServiceTests { checkDownscaling(GameManager.GAME_MODE_PERFORMANCE, "0.5"); } /** * PERFORMANCE game mode is configured through Phenotype. The app hasn't specified any metadata. */ @Test public void testInterventionAllowAngleDefault() throws Exception { GameManagerService gameManagerService = new GameManagerService(mMockContext); gameManagerService.onUserStarting(USER_ID_1); mockDeviceConfigPerformance(); mockModifyGameModeGranted(); checkAngleEnabled(gameManagerService, GameManager.GAME_MODE_PERFORMANCE, false); } /** * PERFORMANCE game mode is configured through Phenotype. The app has opted-out of ANGLE. */ @Test public void testInterventionAllowAngleFalse() throws Exception { GameManagerService gameManagerService = new GameManagerService(mMockContext); gameManagerService.onUserStarting(USER_ID_1); mockDeviceConfigPerformanceEnableAngle(); mockInterventionAllowAngleFalse(); mockModifyGameModeGranted(); checkAngleEnabled(gameManagerService, GameManager.GAME_MODE_PERFORMANCE, false); } /** * PERFORMANCE game mode is configured through Phenotype. The app has redundantly specified * the ANGLE metadata default value of "true". */ @Test public void testInterventionAllowAngleTrue() throws Exception { mockDeviceConfigPerformanceEnableAngle(); mockInterventionAllowAngleTrue(); GameManagerService gameManagerService = new GameManagerService(mMockContext); gameManagerService.onUserStarting(USER_ID_1); mockModifyGameModeGranted(); gameManagerService.setGameMode(mPackageName, GameManager.GAME_MODE_PERFORMANCE, USER_ID_1); assertEquals(GameManager.GAME_MODE_PERFORMANCE, gameManagerService.getGameMode(mPackageName, USER_ID_1)); checkAngleEnabled(gameManagerService, GameManager.GAME_MODE_PERFORMANCE, true); } /** * 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. Loading