Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 3815e9c2 authored by Xiang Wang's avatar Xiang Wang
Browse files

Support per user game config override

* Note that only supported game modes of a package are allowed to
  override through shell commands, while the resolution scaling APIs
  support all game modes at least until we have new game mode
  GAME_MODE_CUSTOM

Bug: b/240335717
Test: atest GameManagerServiceTests
Change-Id: Id145fe3a4af7f05812aa8fb3fd5d27be6211502c
parent 15798369
Loading
Loading
Loading
Loading
+73 −74
Original line number Diff line number Diff line
@@ -130,7 +130,6 @@ public final class GameManagerService extends IGameManagerService.Stub {
    private final Context mContext;
    private final Object mLock = new Object();
    private final Object mDeviceConfigLock = new Object();
    private final Object mOverrideConfigLock = new Object();
    private final Handler mHandler;
    private final PackageManager mPackageManager;
    private final UserManager mUserManager;
@@ -143,8 +142,6 @@ public final class GameManagerService extends IGameManagerService.Stub {
    private final ArrayMap<Integer, GameManagerSettings> mSettings = new ArrayMap<>();
    @GuardedBy("mDeviceConfigLock")
    private final ArrayMap<String, GamePackageConfiguration> mConfigs = new ArrayMap<>();
    @GuardedBy("mOverrideConfigLock")
    private final ArrayMap<String, GamePackageConfiguration> mOverrideConfigs = new ArrayMap<>();
    @Nullable
    private final GameServiceController mGameServiceController;

@@ -236,7 +233,7 @@ public final class GameManagerService extends IGameManagerService.Stub {
        final int userId = ActivityManager.getCurrentUser();
        String[] packageList = getInstalledGamePackageNames(userId);
        for (final String packageName : packageList) {
            pw.println(getInterventionList(packageName));
            pw.println(getInterventionList(packageName, userId));
        }
    }

@@ -759,7 +756,7 @@ public final class GameManagerService extends IGameManagerService.Stub {
        }

        /**
         * Inserts a new GameModeConfiguration
         * Inserts a new GameModeConfiguration.
         */
        public void addModeConfig(GameModeConfiguration config) {
            if (config.isActive()) {
@@ -772,6 +769,15 @@ public final class GameManagerService extends IGameManagerService.Stub {
            }
        }

        /**
         * Removes the GameModeConfiguration.
         */
        public void removeModeConfig(int mode) {
            synchronized (mModeConfigLock) {
                mModeConfigs.remove(mode);
            }
        }

        public boolean isActive() {
            synchronized (mModeConfigLock) {
                return mModeConfigs.size() > 0 || mBatteryModeOptedIn || mPerfModeOptedIn;
@@ -860,7 +866,10 @@ public final class GameManagerService extends IGameManagerService.Stub {
    }

    private @GameMode int[] getAvailableGameModesUnchecked(String packageName) {
        final GamePackageConfiguration config = getConfig(packageName);
        final GamePackageConfiguration config;
        synchronized (mDeviceConfigLock) {
            config = mConfigs.get(packageName);
        }
        if (config == null) {
            return new int[]{};
        }
@@ -1165,7 +1174,7 @@ public final class GameManagerService extends IGameManagerService.Stub {
    }

    float getResolutionScalingFactorInternal(String packageName, int gameMode, int userId) {
        final GamePackageConfiguration packageConfig = getConfig(packageName);
        final GamePackageConfiguration packageConfig = getConfig(packageName, userId);
        if (packageConfig == null) {
            return GamePackageConfiguration.GameModeConfiguration.DEFAULT_SCALING;
        }
@@ -1305,7 +1314,7 @@ public final class GameManagerService extends IGameManagerService.Stub {
            resetFps(packageName, userId);
            return;
        }
        final GamePackageConfiguration packageConfig = getConfig(packageName);
        final GamePackageConfiguration packageConfig = getConfig(packageName, userId);
        if (packageConfig == null) {
            Slog.v(TAG, "Package configuration not found for " + packageName);
            return;
@@ -1318,7 +1327,7 @@ public final class GameManagerService extends IGameManagerService.Stub {
    }

    /**
     * Set the override Game Mode Configuration.
     * Set the Game Mode Configuration override.
     * Update the config if exists, create one if not.
     */
    @VisibleForTesting
@@ -1326,95 +1335,86 @@ public final class GameManagerService extends IGameManagerService.Stub {
    public void setGameModeConfigOverride(String packageName, @UserIdInt int userId,
            @GameMode int gameMode, String fpsStr, String scaling) throws SecurityException {
        checkPermission(Manifest.permission.MANAGE_GAME_MODE);
        // Adding game mode config override of the given package name
        GamePackageConfiguration configOverride;
        synchronized (mLock) {
            if (!mSettings.containsKey(userId)) {
                return;
            }
        }
        // Adding override game mode configuration of the given package name
        GamePackageConfiguration overrideConfig;
        synchronized (mOverrideConfigLock) {
            // look for the existing override GamePackageConfiguration
            overrideConfig = mOverrideConfigs.get(packageName);
            if (overrideConfig == null) {
                overrideConfig = new GamePackageConfiguration(packageName, userId);
                mOverrideConfigs.put(packageName, overrideConfig);
            final GameManagerSettings settings = mSettings.get(userId);
            // look for the existing GamePackageConfiguration override
            configOverride = settings.getConfigOverride(packageName);
            if (configOverride == null) {
                configOverride = new GamePackageConfiguration(packageName, userId);
                settings.setConfigOverride(packageName, configOverride);
            }
        }
        // modify GameModeConfiguration intervention settings
        GamePackageConfiguration.GameModeConfiguration overrideModeConfig =
                overrideConfig.getOrAddDefaultGameModeConfiguration(gameMode);
        GamePackageConfiguration.GameModeConfiguration modeConfigOverride =
                configOverride.getOrAddDefaultGameModeConfiguration(gameMode);

        if (fpsStr != null) {
            overrideModeConfig.setFpsStr(fpsStr);
            modeConfigOverride.setFpsStr(fpsStr);
        } else {
            overrideModeConfig.setFpsStr(
            modeConfigOverride.setFpsStr(
                    GamePackageConfiguration.GameModeConfiguration.DEFAULT_FPS);
        }
        if (scaling != null) {
            overrideModeConfig.setScaling(Float.parseFloat(scaling));
            modeConfigOverride.setScaling(Float.parseFloat(scaling));
        }
        Slog.i(TAG, "Package Name: " + packageName
                + " FPS: " + String.valueOf(overrideModeConfig.getFps())
                + " Scaling: " + overrideModeConfig.getScaling());
                + " FPS: " + String.valueOf(modeConfigOverride.getFps())
                + " Scaling: " + modeConfigOverride.getScaling());
        setGameMode(packageName, gameMode, userId);
    }

    /**
     * Reset the overridden gameModeConfiguration of the given mode.
     * Remove the override config if game mode is not specified.
     * Remove the config override if game mode is not specified.
     */
    @VisibleForTesting
    @RequiresPermission(Manifest.permission.MANAGE_GAME_MODE)
    public void resetGameModeConfigOverride(String packageName, @UserIdInt int userId,
            @GameMode int gameModeToReset) throws SecurityException {
        checkPermission(Manifest.permission.MANAGE_GAME_MODE);
        synchronized (mLock) {
            if (!mSettings.containsKey(userId)) {
                return;
            }
        final GamePackageConfiguration deviceConfig;
        synchronized (mDeviceConfigLock) {
            deviceConfig = mConfigs.get(packageName);
        }

        // resets GamePackageConfiguration of a given packageName.
        // If a gameMode is specified, only reset the GameModeConfiguration of the gameMode.
        if (gameModeToReset != -1) {
            GamePackageConfiguration overrideConfig = null;
            synchronized (mOverrideConfigLock) {
                overrideConfig = mOverrideConfigs.get(packageName);
            }

            GamePackageConfiguration config = null;
            synchronized (mDeviceConfigLock) {
                config = mConfigs.get(packageName);
            }

            int[] modes = overrideConfig.getAvailableGameModes();

            // First check if the mode to reset exists
            boolean isGameModeExist = false;
            for (int mode : modes) {
                if (gameModeToReset == mode) {
                    isGameModeExist = true;
                }
        synchronized (mLock) {
            if (!mSettings.containsKey(userId)) {
                return;
            }
            if (!isGameModeExist) {
            final GameManagerSettings settings = mSettings.get(userId);
            if (gameModeToReset != -1) {
                final GamePackageConfiguration configOverride = settings.getConfigOverride(
                        packageName);
                if (configOverride == null) {
                    return;
                }

            // If the game mode to reset is the only mode other than standard mode,
            // the override config is removed.
            if (modes.length <= 2) {
                synchronized (mOverrideConfigLock) {
                    mOverrideConfigs.remove(packageName);
                final int modesBitfield = configOverride.getAvailableGameModesBitfield();
                if (!bitFieldContainsModeBitmask(modesBitfield, gameModeToReset)) {
                    return;
                }
                // if the game mode to reset is the only mode other than standard mode or there
                // is device config, the config override is removed.
                if (Integer.bitCount(modesBitfield) <= 2 || deviceConfig == null) {
                    settings.removeConfigOverride(packageName);
                } else {
                    final GamePackageConfiguration.GameModeConfiguration defaultModeConfig =
                            deviceConfig.getGameModeConfiguration(gameModeToReset);
                    // otherwise we reset the mode by copying the original config.
                overrideConfig.addModeConfig(config.getGameModeConfiguration(gameModeToReset));
                    if (defaultModeConfig == null) {
                        configOverride.removeModeConfig(gameModeToReset);
                    } else {
                        configOverride.addModeConfig(defaultModeConfig);
                    }
                }
            } else {
            synchronized (mOverrideConfigLock) {
                // remove override config if there is one
                mOverrideConfigs.remove(packageName);
                settings.removeConfigOverride(packageName);
            }
        }

@@ -1422,7 +1422,7 @@ public final class GameManagerService extends IGameManagerService.Stub {
        // If not, set the game mode to standard
        int gameMode = getGameMode(packageName, userId);

        final GamePackageConfiguration config = getConfig(packageName);
        final GamePackageConfiguration config = getConfig(packageName, userId);
        final int newGameMode = getNewGameMode(gameMode, config);
        if (gameMode != newGameMode) {
            setGameMode(packageName, GameManager.GAME_MODE_STANDARD, userId);
@@ -1460,8 +1460,8 @@ public final class GameManagerService extends IGameManagerService.Stub {
    /**
     * Returns the string listing all the interventions currently set to a game.
     */
    public String getInterventionList(String packageName) {
        final GamePackageConfiguration packageConfig = getConfig(packageName);
    public String getInterventionList(String packageName, int userId) {
        final GamePackageConfiguration packageConfig = getConfig(packageName, userId);
        final StringBuilder listStrSb = new StringBuilder();
        if (packageConfig == null) {
            listStrSb.append("\n No intervention found for package ")
@@ -1556,7 +1556,7 @@ public final class GameManagerService extends IGameManagerService.Stub {
            final StringBuilder sb = new StringBuilder();
            final List<String> installedGamesList = getInstalledGamePackageNamesByAllUsers(userId);
            for (final String packageName : installedGamesList) {
                GamePackageConfiguration packageConfig = getConfig(packageName);
                GamePackageConfiguration packageConfig = getConfig(packageName, userId);
                if (packageConfig == null) {
                    continue;
                }
@@ -1635,10 +1635,12 @@ public final class GameManagerService extends IGameManagerService.Stub {
     * @hide
     */
    @VisibleForTesting
    public GamePackageConfiguration getConfig(String packageName) {
    public GamePackageConfiguration getConfig(String packageName, int userId) {
        GamePackageConfiguration packageConfig = null;
        synchronized (mOverrideConfigLock) {
            packageConfig = mOverrideConfigs.get(packageName);
        synchronized (mLock) {
            if (mSettings.containsKey(userId)) {
                packageConfig = mSettings.get(userId).getConfigOverride(packageName);
            }
        }
        if (packageConfig == null) {
            synchronized (mDeviceConfigLock) {
@@ -1679,9 +1681,6 @@ public final class GameManagerService extends IGameManagerService.Stub {
                            break;
                        case ACTION_PACKAGE_REMOVED:
                            if (!intent.getBooleanExtra(EXTRA_REPLACING, false)) {
                                synchronized (mOverrideConfigLock) {
                                    mOverrideConfigs.remove(packageName);
                                }
                                synchronized (mDeviceConfigLock) {
                                    mConfigs.remove(packageName);
                                }
+33 −5
Original line number Diff line number Diff line
@@ -27,6 +27,7 @@ import android.util.Xml;

import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.XmlUtils;
import com.android.server.app.GameManagerService.GamePackageConfiguration;

import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -62,6 +63,8 @@ public class GameManagerSettings {

    // PackageName -> GameMode
    private final ArrayMap<String, Integer> mGameModes = new ArrayMap<>();
    // PackageName -> GamePackageConfiguration
    private final ArrayMap<String, GamePackageConfiguration> mConfigOverrides = new ArrayMap<>();

    GameManagerSettings(File dataDir) {
        mSystemDir = new File(dataDir, "system");
@@ -74,7 +77,7 @@ public class GameManagerSettings {
    }

    /**
     * Return the game mode of a given package.
     * Returns the game mode of a given package.
     * This operation must be synced with an external lock.
     */
    int getGameModeLocked(String packageName) {
@@ -85,7 +88,7 @@ public class GameManagerSettings {
    }

    /**
     * Set the game mode of a given package.
     * Sets the game mode of a given package.
     * This operation must be synced with an external lock.
     */
    void setGameModeLocked(String packageName, int gameMode) {
@@ -93,15 +96,40 @@ public class GameManagerSettings {
    }

    /**
     * Remove the game mode of a given package.
     * Removes all game settings of a given package.
     * This operation must be synced with an external lock.
     */
    void removeGame(String packageName) {
        mGameModes.remove(packageName);
        mConfigOverrides.remove(packageName);
    }

    /**
     * Write all current game service settings into disk.
     * Returns the game config override of a given package or null if absent.
     * This operation must be synced with an external lock.
     */
    GamePackageConfiguration getConfigOverride(String packageName) {
        return mConfigOverrides.get(packageName);
    }

    /**
     * Sets the game config override of a given package.
     * This operation must be synced with an external lock.
     */
    void setConfigOverride(String packageName, GamePackageConfiguration configOverride) {
        mConfigOverrides.put(packageName, configOverride);
    }

    /**
     * Removes the game mode config override of a given package.
     * This operation must be synced with an external lock.
     */
    void removeConfigOverride(String packageName) {
        mConfigOverrides.remove(packageName);
    }

    /**
     * Writes all current game service settings into disk.
     * This operation must be synced with an external lock.
     */
    void writePersistentDataLocked() {
@@ -139,7 +167,7 @@ public class GameManagerSettings {
    }

    /**
     * Read game service settings from the disk.
     * Reads game service settings from the disk.
     * This operation must be synced with an external lock.
     */
    boolean readPersistentDataLocked() {
+2 −1
Original line number Diff line number Diff line
@@ -81,7 +81,8 @@ public class GameManagerShellCommand extends ShellCommand {
        final GameManagerService gameManagerService = (GameManagerService)
                ServiceManager.getService(Context.GAME_SERVICE);

        final String listStr = gameManagerService.getInterventionList(packageName);
        final String listStr = gameManagerService.getInterventionList(packageName,
                ActivityManager.getCurrentUser());

        if (listStr == null) {
            pw.println("No interventions found for " + packageName);
+19 −12
Original line number Diff line number Diff line
@@ -584,7 +584,7 @@ public class GameManagerServiceTests {
            gameManagerService.updateConfigsForUser(USER_ID_1, true, mPackageName);
        }
        GameManagerService.GamePackageConfiguration config =
                gameManagerService.getConfig(mPackageName);
                gameManagerService.getConfig(mPackageName, USER_ID_1);
        assertEquals(scaling, config.getGameModeConfiguration(gameMode).getScaling(), 0.01f);
    }

@@ -594,7 +594,7 @@ public class GameManagerServiceTests {

        // Validate GamePackageConfiguration returns the correct value.
        GameManagerService.GamePackageConfiguration config =
                gameManagerService.getConfig(mPackageName);
                gameManagerService.getConfig(mPackageName, USER_ID_1);
        assertEquals(config.getGameModeConfiguration(gameMode).getUseAngle(), angleEnabled);

        // Validate GameManagerService.isAngleEnabled() returns the correct value.
@@ -607,7 +607,7 @@ public class GameManagerServiceTests {

        // Validate GamePackageConfiguration returns the correct value.
        GameManagerService.GamePackageConfiguration config =
                gameManagerService.getConfig(mPackageName);
                gameManagerService.getConfig(mPackageName, USER_ID_1);
        assertEquals(
                loadingBoost, config.getGameModeConfiguration(gameMode).getLoadingBoostDuration());

@@ -623,7 +623,7 @@ public class GameManagerServiceTests {
            gameManagerService.updateConfigsForUser(USER_ID_1, true, mPackageName);
        }
        GameManagerService.GamePackageConfiguration config =
                gameManagerService.getConfig(mPackageName);
                gameManagerService.getConfig(mPackageName, USER_ID_1);
        assertEquals(fps, config.getGameModeConfiguration(gameMode).getFps());
    }

@@ -1049,7 +1049,7 @@ public class GameManagerServiceTests {
                mTestLooper.getLooper());
        startUser(gameManagerService, USER_ID_1);
        GameManagerService.GamePackageConfiguration config =
                gameManagerService.getConfig(mPackageName);
                gameManagerService.getConfig(mPackageName, USER_ID_1);
        assertEquals(90,
                config.getGameModeConfiguration(GameManager.GAME_MODE_PERFORMANCE).getFps());
        assertEquals(30, config.getGameModeConfiguration(GameManager.GAME_MODE_BATTERY).getFps());
@@ -1064,7 +1064,7 @@ public class GameManagerServiceTests {
                mTestLooper.getLooper());
        startUser(gameManagerService, USER_ID_1);
        GameManagerService.GamePackageConfiguration config =
                gameManagerService.getConfig(mPackageName);
                gameManagerService.getConfig(mPackageName, USER_ID_1);
        assertEquals(0,
                config.getGameModeConfiguration(GameManager.GAME_MODE_PERFORMANCE).getFps());
        assertEquals(0, config.getGameModeConfiguration(GameManager.GAME_MODE_BATTERY).getFps());
@@ -1092,7 +1092,7 @@ public class GameManagerServiceTests {
        startUser(gameManagerService, USER_ID_1);
        gameManagerService.updateConfigsForUser(USER_ID_1, true, mPackageName);
        GameManagerService.GamePackageConfiguration config =
                gameManagerService.getConfig(mPackageName);
                gameManagerService.getConfig(mPackageName, USER_ID_1);
        assertNull(config.getGameModeConfiguration(GameManager.GAME_MODE_PERFORMANCE));
    }

@@ -1339,10 +1339,15 @@ public class GameManagerServiceTests {
        mTestLooper.dispatchAll();

        /* Expected fileOutput (order may vary)
         # user 1001:
         com.android.app2 <UID>   0   2   angle=0,scaling=0.5,fps=90  3   angle=0,scaling=0.5,fps=60
         com.android.app1 <UID>   1   2   angle=0,scaling=0.5,fps=90  3   angle=0,scaling=0.7,fps=30
         com.android.app0 <UID>   0   2   angle=0,scaling=0.6,fps=120 3   angle=0,scaling=0.7,fps=30

         # user 1002:
         com.android.app2 <UID>   0   2   angle=0,scaling=0.5,fps=90  3   angle=0,scaling=0.7,fps=30
         com.android.app1 <UID>   1   2   angle=0,scaling=0.5,fps=90  3   angle=0,scaling=0.7,fps=30
         com.android.app0 <UID>   0   2   angle=0,scaling=0.5,fps=90  3   angle=0,scaling=0.7,fps=30
         The current game mode would only be set to non-zero if the current user have that game
         installed.
        */
@@ -1386,7 +1391,7 @@ public class GameManagerServiceTests {
        assertEquals(splitLine[3], "2");
        assertEquals(splitLine[4], "angle=0,scaling=0.5,fps=90");
        assertEquals(splitLine[5], "3");
        assertEquals(splitLine[6], "angle=0,scaling=0.5,fps=60");
        assertEquals(splitLine[6], "angle=0,scaling=0.7,fps=30");
        splitLine = fileOutput.get(1).split("\\s+");
        assertEquals(splitLine[0], "com.android.app1");
        assertEquals(splitLine[2], "3");
@@ -1398,7 +1403,7 @@ public class GameManagerServiceTests {
        assertEquals(splitLine[0], "com.android.app0");
        assertEquals(splitLine[2], "0");
        assertEquals(splitLine[3], "2");
        assertEquals(splitLine[4], "angle=0,scaling=0.6,fps=120");
        assertEquals(splitLine[4], "angle=0,scaling=0.5,fps=90");
        assertEquals(splitLine[5], "3");
        assertEquals(splitLine[6], "angle=0,scaling=0.7,fps=30");

@@ -1493,12 +1498,14 @@ public class GameManagerServiceTests {

    @Test
    public void testGetResolutionScalingFactor_noUserId() {
        mockModifyGameModeDenied();
        mockModifyGameModeGranted();
        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);
        assertThrows(IllegalArgumentException.class, () -> {
            gameManagerService.getResolutionScalingFactor(mPackageName,
                    GameManager.GAME_MODE_BATTERY, USER_ID_1);
        });
    }
}