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

Commit 60e153c3 authored by Xiang Wang's avatar Xiang Wang
Browse files

Persist custom game mode configuration in settings

Allow storing <package> element with only config as user may only set
custom game mode configuration while never set game mode.

Bug: b/243448953
Test: atest GameManagerServiceTests GameManagerServiceSettingsTests
Change-Id: I17f973dd8ba8b27f2a4f0a4e6324eb56b8ad6345
parent 9cae44ac
Loading
Loading
Loading
Loading
+22 −7
Original line number Diff line number Diff line
@@ -118,6 +118,14 @@ import java.util.Set;
 */
public final class GameManagerService extends IGameManagerService.Stub {
    public static final String TAG = "GameManagerService";
    // event strings used for logging
    private static final String EVENT_SET_GAME_MODE = "SET_GAME_MODE";
    private static final String EVENT_UPDATE_CUSTOM_GAME_MODE_CONFIG =
            "UPDATE_CUSTOM_GAME_MODE_CONFIG";
    private static final String EVENT_RECEIVE_SHUTDOWN_INDENT = "RECEIVE_SHUTDOWN_INDENT";
    private static final String EVENT_ON_USER_STARTING = "ON_USER_STARTING";
    private static final String EVENT_ON_USER_SWITCHING = "ON_USER_SWITCHING";
    private static final String EVENT_ON_USER_STOPPING = "ON_USER_STOPPING";

    private static final boolean DEBUG = false;

@@ -1154,9 +1162,9 @@ public final class GameManagerService extends IGameManagerService.Stub {
                }
            }
        }
        sendUserMessage(userId, WRITE_SETTINGS, "SET_GAME_MODE", WRITE_DELAY_MILLIS);
        sendUserMessage(userId, WRITE_SETTINGS, EVENT_SET_GAME_MODE, WRITE_DELAY_MILLIS);
        sendUserMessage(userId, WRITE_GAME_MODE_INTERVENTION_LIST_FILE,
                "SET_GAME_MODE", 0 /*delayMillis*/);
                EVENT_SET_GAME_MODE, 0 /*delayMillis*/);
        int gameUid = -1;
        try {
            gameUid = mPackageManager.getPackageUidAsUser(packageName, userId);
@@ -1399,6 +1407,11 @@ public final class GameManagerService extends IGameManagerService.Stub {
        Slog.i(TAG, "Updated custom game mode config for package: " + packageName
                + " with FPS=" + internalConfig.getFps() + ";Scaling="
                + internalConfig.getScaling() + " under user " + userId);

        sendUserMessage(userId, WRITE_SETTINGS, EVENT_UPDATE_CUSTOM_GAME_MODE_CONFIG,
                WRITE_DELAY_MILLIS);
        sendUserMessage(userId, WRITE_GAME_MODE_INTERVENTION_LIST_FILE,
                EVENT_UPDATE_CUSTOM_GAME_MODE_CONFIG, WRITE_DELAY_MILLIS /*delayMillis*/);
    }

    /**
@@ -1473,9 +1486,10 @@ public final class GameManagerService extends IGameManagerService.Stub {
                        for (Map.Entry<Integer, GameManagerSettings> entry : mSettings.entrySet()) {
                            final int userId = entry.getKey();
                            sendUserMessage(userId, WRITE_SETTINGS,
                                    Intent.ACTION_SHUTDOWN, 0 /*delayMillis*/);
                                    EVENT_RECEIVE_SHUTDOWN_INDENT, 0 /*delayMillis*/);
                            sendUserMessage(userId,
                                    WRITE_GAME_MODE_INTERVENTION_LIST_FILE, Intent.ACTION_SHUTDOWN,
                                    WRITE_GAME_MODE_INTERVENTION_LIST_FILE,
                                    EVENT_RECEIVE_SHUTDOWN_INDENT,
                                    0 /*delayMillis*/);
                        }
                    }
@@ -1500,7 +1514,8 @@ public final class GameManagerService extends IGameManagerService.Stub {
                userSettings.readPersistentDataLocked();
            }
        }
        sendUserMessage(userId, POPULATE_GAME_MODE_SETTINGS, "ON_USER_STARTING", 0 /*delayMillis*/);
        sendUserMessage(userId, POPULATE_GAME_MODE_SETTINGS, EVENT_ON_USER_STARTING,
                0 /*delayMillis*/);

        if (mGameServiceController != null) {
            mGameServiceController.notifyUserStarted(user);
@@ -1520,7 +1535,7 @@ public final class GameManagerService extends IGameManagerService.Stub {
            if (!mSettings.containsKey(userId)) {
                return;
            }
            sendUserMessage(userId, REMOVE_SETTINGS, "ON_USER_STOPPING", 0 /*delayMillis*/);
            sendUserMessage(userId, REMOVE_SETTINGS, EVENT_ON_USER_STOPPING, 0 /*delayMillis*/);
        }

        if (mGameServiceController != null) {
@@ -1533,7 +1548,7 @@ public final class GameManagerService extends IGameManagerService.Stub {
        // we want to re-populate the setting when switching user as the device config may have
        // changed, which will only update for the previous user, see
        // DeviceConfigListener#onPropertiesChanged.
        sendUserMessage(toUserId, POPULATE_GAME_MODE_SETTINGS, "ON_USER_SWITCHING",
        sendUserMessage(toUserId, POPULATE_GAME_MODE_SETTINGS, EVENT_ON_USER_SWITCHING,
                0 /*delayMillis*/);

        if (mGameServiceController != null) {
+17 −13
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@ package com.android.server.app;
import android.app.GameManager;
import android.os.FileUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.AtomicFile;
import android.util.Slog;
import android.util.Xml;
@@ -37,7 +38,6 @@ import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Map;

/**
 * Persists all GameService related settings.
@@ -49,7 +49,11 @@ public class GameManagerSettings {
    // The XML file follows the below format:
    // <?xml>
    // <packages>
    //     <package></package>
    //     <package name="" gameMode="">
    //       <gameModeConfig gameMode="" fps="" scaling="" useAngle="" loadingBoost="">
    //       </gameModeConfig>
    //       ...
    //     </package>
    //     ...
    // </packages>
    private static final String GAME_SERVICE_FILE_NAME = "game-manager-service.xml";
@@ -155,11 +159,14 @@ public class GameManagerSettings {
            serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);

            serializer.startTag(null, TAG_PACKAGES);
            for (Map.Entry<String, Integer> entry : mGameModes.entrySet()) {
                String packageName = entry.getKey();
            final ArraySet<String> packageNames = new ArraySet<>(mGameModes.keySet());
            packageNames.addAll(mConfigOverrides.keySet());
            for (String packageName : packageNames) {
                serializer.startTag(null, TAG_PACKAGE);
                serializer.attribute(null, ATTR_NAME, packageName);
                serializer.attributeInt(null, ATTR_GAME_MODE, entry.getValue());
                if (mGameModes.containsKey(packageName)) {
                    serializer.attributeInt(null, ATTR_GAME_MODE, mGameModes.get(packageName));
                }
                writeGameModeConfigTags(serializer, mConfigOverrides.get(packageName));
                serializer.endTag(null, TAG_PACKAGE);
            }
@@ -224,7 +231,7 @@ public class GameManagerSettings {
                // Do nothing
            }
            if (type != XmlPullParser.START_TAG) {
                Slog.wtf(TAG, "No start tag found in package manager settings");
                Slog.wtf(TAG, "No start tag found in game manager settings");
                return false;
            }

@@ -245,7 +252,7 @@ public class GameManagerSettings {
                }
            }
        } catch (XmlPullParserException | java.io.IOException e) {
            Slog.wtf(TAG, "Error reading package manager settings", e);
            Slog.wtf(TAG, "Error reading game manager settings", e);
            return false;
        }
        return true;
@@ -260,15 +267,12 @@ public class GameManagerSettings {
            XmlUtils.skipCurrentTag(parser);
            return;
        }
        int gameMode;
        try {
            gameMode = parser.getAttributeInt(null, ATTR_GAME_MODE);
            final int gameMode = parser.getAttributeInt(null, ATTR_GAME_MODE);
            mGameModes.put(name, gameMode);
        } catch (XmlPullParserException e) {
            Slog.wtf(TAG, "Invalid game mode in package tag: "
                    + parser.getAttributeValue(null, ATTR_GAME_MODE), e);
            return;
            Slog.v(TAG, "No game mode selected by user for package" + name);
        }
        mGameModes.put(name, gameMode);
        final int packageTagDepth = parser.getDepth();
        int type;
        final GamePackageConfiguration config = new GamePackageConfiguration(name);
+46 −6
Original line number Diff line number Diff line
@@ -20,6 +20,8 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSess
import static com.android.server.app.GameManagerService.CANCEL_GAME_LOADING_MODE;
import static com.android.server.app.GameManagerService.LOADING_BOOST_MAX_DURATION;
import static com.android.server.app.GameManagerService.SET_GAME_STATE;
import static com.android.server.app.GameManagerService.WRITE_DELAY_MILLIS;
import static com.android.server.app.GameManagerService.WRITE_GAME_MODE_INTERVENTION_LIST_FILE;
import static com.android.server.app.GameManagerService.WRITE_SETTINGS;

import static org.junit.Assert.assertArrayEquals;
@@ -1835,9 +1837,7 @@ public class GameManagerServiceTests {
    public void testUpdateCustomGameModeConfiguration_permissionDenied() {
        mockModifyGameModeDenied();
        mockDeviceConfigAll();
        GameManagerService gameManagerService =
                new GameManagerService(mMockContext, mTestLooper.getLooper());
        startUser(gameManagerService, USER_ID_1);
        GameManagerService gameManagerService = createServiceAndStartUser(USER_ID_1);
        assertThrows(SecurityException.class, () -> {
            gameManagerService.updateCustomGameModeConfiguration(mPackageName,
                    new GameModeConfiguration.Builder().setScalingFactor(0.5f).build(),
@@ -1848,9 +1848,7 @@ public class GameManagerServiceTests {
    @Test
    public void testUpdateCustomGameModeConfiguration_noUserId() {
        mockModifyGameModeGranted();
        GameManagerService gameManagerService =
                new GameManagerService(mMockContext, mTestLooper.getLooper());
        startUser(gameManagerService, USER_ID_2);
        GameManagerService gameManagerService = createServiceAndStartUser(USER_ID_2);
        assertThrows(IllegalArgumentException.class, () -> {
            gameManagerService.updateCustomGameModeConfiguration(mPackageName,
                    new GameModeConfiguration.Builder().setScalingFactor(0.5f).build(),
@@ -1873,6 +1871,48 @@ public class GameManagerServiceTests {
        assertNull(pkgConfig);
    }

    @Test
    public void testUpdateCustomGameModeConfiguration() throws InterruptedException {
        mockModifyGameModeGranted();
        GameManagerService gameManagerService = createServiceAndStartUser(USER_ID_1);
        gameManagerService.updateCustomGameModeConfiguration(mPackageName,
                new GameModeConfiguration.Builder().setScalingFactor(0.35f).setFpsOverride(
                        60).build(),
                USER_ID_1);

        assertTrue(gameManagerService.mHandler.hasEqualMessages(WRITE_SETTINGS, USER_ID_1));
        assertTrue(
                gameManagerService.mHandler.hasEqualMessages(WRITE_GAME_MODE_INTERVENTION_LIST_FILE,
                        USER_ID_1));

        GameManagerService.GamePackageConfiguration pkgConfig = gameManagerService.getConfig(
                mPackageName, USER_ID_1);
        assertNotNull(pkgConfig);
        GameManagerService.GamePackageConfiguration.GameModeConfiguration modeConfig =
                pkgConfig.getGameModeConfiguration(GameManager.GAME_MODE_CUSTOM);
        assertNotNull(modeConfig);
        assertEquals(modeConfig.getScaling(), 0.35f, 0.01f);
        assertEquals(modeConfig.getFps(), 60);
        // creates a new service to check that no data has been stored
        mTestLooper.dispatchAll();
        gameManagerService = createServiceAndStartUser(USER_ID_1);
        pkgConfig = gameManagerService.getConfig(mPackageName, USER_ID_1);
        assertNull(pkgConfig);

        mTestLooper.moveTimeForward(WRITE_DELAY_MILLIS + 500);
        mTestLooper.dispatchAll();
        // creates a new service to check that data is persisted after delay
        gameManagerService = createServiceAndStartUser(USER_ID_1);
        assertEquals(GameManager.GAME_MODE_STANDARD,
                gameManagerService.getGameMode(mPackageName, USER_ID_1));
        pkgConfig = gameManagerService.getConfig(mPackageName, USER_ID_1);
        assertNotNull(pkgConfig);
        modeConfig = pkgConfig.getGameModeConfiguration(GameManager.GAME_MODE_CUSTOM);
        assertNotNull(modeConfig);
        assertEquals(modeConfig.getScaling(), 0.35f, 0.01f);
        assertEquals(modeConfig.getFps(), 60);
    }

    @Test
    public void testWritingSettingFile_onShutdown() throws InterruptedException {
        mockModifyGameModeGranted();
+57 −13
Original line number Diff line number Diff line
@@ -52,6 +52,8 @@ public class GameManagerServiceSettingsTests {
    private static final String PACKAGE_NAME_1 = "com.android.app1";
    private static final String PACKAGE_NAME_2 = "com.android.app2";
    private static final String PACKAGE_NAME_3 = "com.android.app3";
    private static final String PACKAGE_NAME_4 = "com.android.app4";


    private void writeFile(File file, byte[] data) {
        file.mkdirs();
@@ -69,16 +71,23 @@ public class GameManagerServiceSettingsTests {
        writeFile(new File(InstrumentationRegistry.getContext().getFilesDir(),
                        "system/game-manager-service.xml"),
                ("<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
                        + "<packages>\n"
                        + "  <package name=\"com.android.app1\" gameMode=\"1\">\n"
                        + "  </package>\n"
                        + "<packages>"
                        + "\n" // app1: no package config setting
                        + "\n" // app2: performance mode is selected with override
                        + "  <package name=\"com.android.app2\" gameMode=\"2\">\n"
                        + "     <gameModeConfig gameMode=\"2\" scaling=\"0.99\" "
                        + "useAngle=\"true\" fps=\"90\" loadingBoost=\"123\"></gameModeConfig>\n"
                        + "     <gameModeConfig gameMode=\"3\"></gameModeConfig>\n"
                        + "  </package>\n"
                        + "  </package>"
                        + "\n" // app3: only battery mode is selected
                        + "  <package name=\"com.android.app3\" gameMode=\"3\">\n"
                        + "  </package>\n"
                        + "  </package>"
                        + "\n" // app4: no game mode selected but custom game mode config
                        + "  <package name=\"com.android.app4\">\n"
                        + "     <gameModeConfig gameMode=\"4\" scaling=\"0.4\" "
                        + "fps=\"30\"></gameModeConfig>\n"
                        + "  </package>"
                        + "\n"
                        + "</packages>\n").getBytes());
    }

@@ -115,14 +124,15 @@ public class GameManagerServiceSettingsTests {
        assertTrue(settings.readPersistentDataLocked());

        // test game modes
        assertEquals(1, settings.getGameModeLocked(PACKAGE_NAME_1));
        assertEquals(2, settings.getGameModeLocked(PACKAGE_NAME_2));
        assertEquals(3, settings.getGameModeLocked(PACKAGE_NAME_3));
        assertEquals(GameManager.GAME_MODE_STANDARD, settings.getGameModeLocked(PACKAGE_NAME_1));
        assertEquals(GameManager.GAME_MODE_PERFORMANCE, settings.getGameModeLocked(PACKAGE_NAME_2));
        assertEquals(GameManager.GAME_MODE_BATTERY, settings.getGameModeLocked(PACKAGE_NAME_3));
        assertEquals(GameManager.GAME_MODE_STANDARD, settings.getGameModeLocked(PACKAGE_NAME_4));

        // test game mode configs
        assertNull(settings.getConfigOverride(PACKAGE_NAME_1));
        assertNull(settings.getConfigOverride(PACKAGE_NAME_3));
        final GamePackageConfiguration config = settings.getConfigOverride(PACKAGE_NAME_2);
        GamePackageConfiguration config = settings.getConfigOverride(PACKAGE_NAME_2);
        assertNotNull(config);

        assertNull(config.getGameModeConfiguration(GameManager.GAME_MODE_STANDARD));
@@ -141,6 +151,14 @@ public class GameManagerServiceSettingsTests {
                GameModeConfiguration.DEFAULT_LOADING_BOOST_DURATION);
        assertEquals(batteryConfig.getFpsStr(), GameModeConfiguration.DEFAULT_FPS);
        assertFalse(batteryConfig.getUseAngle());

        config = settings.getConfigOverride(PACKAGE_NAME_4);
        assertNotNull(config);
        GameModeConfiguration customConfig = config.getGameModeConfiguration(
                GameManager.GAME_MODE_CUSTOM);
        assertNotNull(customConfig);
        assertEquals(customConfig.getScaling(), 0.4f, 0.1f);
        assertEquals(customConfig.getFps(), 30);
    }

    @Test
@@ -176,16 +194,20 @@ public class GameManagerServiceSettingsTests {
        writeFile(new File(InstrumentationRegistry.getContext().getFilesDir(),
                        "system/game-manager-service.xml"),
                ("<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
                        + "<packages>\n"
                        + "<packages>"
                        + "\n" // missing package name
                        + "  <package gameMode=\"1\">\n"
                        + "  </package>\n"
                        + "  </package>"
                        + "\n" // app2 with unknown sub element
                        + "  <package name=\"com.android.app2\" gameMode=\"2\">\n"
                        + "     <unknown></unknown>"
                        + "     <gameModeConfig gameMode=\"3\" fps=\"90\"></gameModeConfig>\n"
                        + "     foo bar"
                        + "  </package>\n"
                        + "  </package>"
                        + "\n" // unknown package element
                        + "  <unknownTag></unknownTag>\n"
                        + "    foo bar\n"
                        + "    foo bar"
                        + "\n" // app3 after unknown element
                        + "  <package name=\"com.android.app3\" gameMode=\"3\">\n"
                        + "  </package>\n"
                        + "</packages>\n").getBytes());
@@ -214,6 +236,8 @@ public class GameManagerServiceSettingsTests {
        settings.setGameModeLocked(PACKAGE_NAME_1, GameManager.GAME_MODE_BATTERY);
        settings.setGameModeLocked(PACKAGE_NAME_2, GameManager.GAME_MODE_PERFORMANCE);
        settings.setGameModeLocked(PACKAGE_NAME_3, GameManager.GAME_MODE_STANDARD);

        // set config for app2
        GamePackageConfiguration config = new GamePackageConfiguration(PACKAGE_NAME_2);
        GameModeConfiguration performanceConfig = config.getOrAddDefaultGameModeConfiguration(
                GameManager.GAME_MODE_PERFORMANCE);
@@ -225,18 +249,29 @@ public class GameManagerServiceSettingsTests {
                GameManager.GAME_MODE_BATTERY);
        batteryConfig.setScaling(0.77f);
        settings.setConfigOverride(PACKAGE_NAME_2, config);

        // set config for app4
        config = new GamePackageConfiguration(PACKAGE_NAME_4);
        GameModeConfiguration customConfig = config.getOrAddDefaultGameModeConfiguration(
                GameManager.GAME_MODE_CUSTOM);
        customConfig.setScaling(0.4f);
        customConfig.setFpsStr("30");
        settings.setConfigOverride(PACKAGE_NAME_4, config);

        settings.writePersistentDataLocked();

        // clear the settings in memory
        settings.removeGame(PACKAGE_NAME_1);
        settings.removeGame(PACKAGE_NAME_2);
        settings.removeGame(PACKAGE_NAME_3);
        settings.removeGame(PACKAGE_NAME_4);

        // read back in and verify
        assertTrue(settings.readPersistentDataLocked());
        assertEquals(3, settings.getGameModeLocked(PACKAGE_NAME_1));
        assertEquals(2, settings.getGameModeLocked(PACKAGE_NAME_2));
        assertEquals(1, settings.getGameModeLocked(PACKAGE_NAME_3));
        assertEquals(1, settings.getGameModeLocked(PACKAGE_NAME_4));

        config = settings.getConfigOverride(PACKAGE_NAME_1);
        assertNull(config);
@@ -256,5 +291,14 @@ public class GameManagerServiceSettingsTests {
        assertEquals(performanceConfig.getLoadingBoostDuration(), 321);
        assertEquals(performanceConfig.getFpsStr(), "60");
        assertTrue(performanceConfig.getUseAngle());

        config = settings.getConfigOverride(PACKAGE_NAME_4);
        assertNotNull(config);
        customConfig = config.getGameModeConfiguration(GameManager.GAME_MODE_CUSTOM);
        assertNotNull(customConfig);
        assertEquals(customConfig.getScaling(), 0.4f, 0.1f);
        assertEquals(customConfig.getFps(), 30);
        assertNull(config.getGameModeConfiguration(GameManager.GAME_MODE_PERFORMANCE));
        assertNull(config.getGameModeConfiguration(GameManager.GAME_MODE_BATTERY));
    }
}