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

Commit 6e4cd761 authored by Andy Yu's avatar Andy Yu
Browse files

Add Game Default Frame Rate calls

Update Game mode intervention frame rate override
and add game default frame rate override JNI calls
to SurfaceFlinger. Game default frame rate depends on
two sysprops:

To determine if it's enabled:
1) persist.graphics.game_default_frame_rate.enabled
To determine the default frame rate value:
2) ro.surface_flinger.game_default_frame_rate_override

This change adds setGameDefaultFrameRateOverride to
call into SurfaceFlinger. Meanwhile it also changes the
original override call to setGameModeFrameRateOverride
to differenitate between those two calls.

Bug: 286084594
Test: atest GameManagerServiceTests
Change-Id: I68f93e79fda5457303b49d0bd8f0edbb14e01b3f
parent fce39f14
Loading
Loading
Loading
Loading
+14 −0
Original line number Diff line number Diff line
@@ -26,6 +26,7 @@ aconfig_srcjars = [
    ":android.os.flags-aconfig-java{.generated_srcjars}",
    ":android.os.vibrator.flags-aconfig-java{.generated_srcjars}",
    ":android.security.flags-aconfig-java{.generated_srcjars}",
    ":android.server.app.flags-aconfig-java{.generated_srcjars}",
    ":android.service.chooser.flags-aconfig-java{.generated_srcjars}",
    ":android.service.notification.flags-aconfig-java{.generated_srcjars}",
    ":android.view.flags-aconfig-java{.generated_srcjars}",
@@ -834,6 +835,19 @@ java_aconfig_library {
    defaults: ["framework-minus-apex-aconfig-java-defaults"],
}

// App
aconfig_declarations {
    name: "android.server.app.flags-aconfig",
    package: "android.server.app",
    srcs: ["services/core/java/com/android/server/app/flags.aconfig"],
}

java_aconfig_library {
    name: "android.server.app.flags-aconfig-java",
    aconfig_declarations: "android.server.app.flags-aconfig",
    defaults: ["framework-minus-apex-aconfig-java-defaults"],
}

// WebView
aconfig_declarations {
    name: "android.webkit.flags-aconfig",
+2 −0
Original line number Diff line number Diff line
@@ -52,4 +52,6 @@ interface IGameManagerService {
    void removeGameModeListener(IGameModeListener gameModeListener);
    void addGameStateListener(IGameStateListener gameStateListener);
    void removeGameStateListener(IGameStateListener gameStateListener);
    @EnforcePermission("MANAGE_GAME_MODE")
    void toggleGameDefaultFrameRate(boolean isEnabled);
}
+108 −12
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@ package com.android.server.app;
import static android.content.Intent.ACTION_PACKAGE_ADDED;
import static android.content.Intent.ACTION_PACKAGE_REMOVED;
import static android.content.Intent.EXTRA_REPLACING;
import static android.server.app.Flags.gameDefaultFrameRate;

import static com.android.internal.R.styleable.GameModeConfig_allowGameAngleDriver;
import static com.android.internal.R.styleable.GameModeConfig_allowGameDownscaling;
@@ -28,6 +29,7 @@ import static com.android.internal.R.styleable.GameModeConfig_supportsPerformanc
import static com.android.internal.util.ConcurrentUtils.DIRECT_EXECUTOR;

import android.Manifest;
import android.annotation.EnforcePermission;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
@@ -66,11 +68,13 @@ import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.PermissionEnforcer;
import android.os.PowerManagerInternal;
import android.os.Process;
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.ShellCallback;
import android.os.SystemProperties;
import android.os.UserManager;
import android.provider.DeviceConfig;
import android.provider.DeviceConfig.Properties;
@@ -138,12 +142,17 @@ public final class GameManagerService extends IGameManagerService.Stub {
    static final int WRITE_GAME_MODE_INTERVENTION_LIST_FILE = 6;
    static final int WRITE_DELAY_MILLIS = 10 * 1000;  // 10 seconds
    static final int LOADING_BOOST_MAX_DURATION = 5 * 1000;  // 5 seconds
    static final String PROPERTY_PERSISTENT_GFX_GAME_DEFAULT_FRAME_RATE_ENABLED =
            "persist.graphics.game_default_frame_rate.enabled";
    static final String PROPERTY_RO_SURFACEFLINGER_GAME_DEFAULT_FRAME_RATE =
            "ro.surface_flinger.game_default_frame_rate_override";

    private static final String PACKAGE_NAME_MSG_KEY = "packageName";
    private static final String USER_ID_MSG_KEY = "userId";
    private static final String GAME_MODE_INTERVENTION_LIST_FILE_NAME =
            "game_mode_intervention.list";


    private final Context mContext;
    private final Object mLock = new Object();
    private final Object mDeviceConfigLock = new Object();
@@ -154,7 +163,6 @@ public final class GameManagerService extends IGameManagerService.Stub {
    private final PackageManager mPackageManager;
    private final UserManager mUserManager;
    private final PowerManagerInternal mPowerManagerInternal;
    private final File mSystemDir;
    @VisibleForTesting
    final AtomicFile mGameModeInterventionListFile;
    private DeviceConfigListener mDeviceConfigListener;
@@ -175,28 +183,56 @@ public final class GameManagerService extends IGameManagerService.Stub {
    final MyUidObserver mUidObserver;
    @GuardedBy("mUidObserverLock")
    private final Set<Integer> mForegroundGameUids = new HashSet<>();
    private final GameManagerServiceSystemPropertiesWrapper mSysProps;

    @VisibleForTesting
    static class Injector {
        public GameManagerServiceSystemPropertiesWrapper createSystemPropertiesWrapper() {
            return new GameManagerServiceSystemPropertiesWrapper() {
                @Override
                public String get(String key, String def) {
                    return SystemProperties.get(key, def);
                }
                @Override
                public boolean getBoolean(String key, boolean def) {
                    return SystemProperties.getBoolean(key, def);
                }

                @Override
                public int getInt(String key, int def) {
                    return SystemProperties.getInt(key, def);
                }

                @Override
                public void set(String key, String val) {
                    SystemProperties.set(key, val);
                }
            };
        }
    }

    public GameManagerService(Context context) {
        this(context, createServiceThread().getLooper());
    }

    GameManagerService(Context context, Looper looper) {
        this(context, looper, Environment.getDataDirectory());
        this(context, looper, Environment.getDataDirectory(), new Injector());
    }

    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
    GameManagerService(Context context, Looper looper, File dataDir) {
    GameManagerService(Context context, Looper looper, File dataDir, Injector injector) {
        super(PermissionEnforcer.fromContext(context));
        mContext = context;
        mHandler = new SettingsHandler(looper);
        mPackageManager = mContext.getPackageManager();
        mUserManager = mContext.getSystemService(UserManager.class);
        mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class);
        mSystemDir = new File(dataDir, "system");
        mSystemDir.mkdirs();
        FileUtils.setPermissions(mSystemDir.toString(),
        File systemDir = new File(dataDir, "system");
        systemDir.mkdirs();
        FileUtils.setPermissions(systemDir.toString(),
                FileUtils.S_IRWXU | FileUtils.S_IRWXG | FileUtils.S_IROTH | FileUtils.S_IXOTH,
                -1, -1);
        mGameModeInterventionListFile = new AtomicFile(new File(mSystemDir,
        mGameModeInterventionListFile = new AtomicFile(new File(systemDir,
                GAME_MODE_INTERVENTION_LIST_FILE_NAME));
        FileUtils.setPermissions(mGameModeInterventionListFile.getBaseFile().getAbsolutePath(),
                FileUtils.S_IRUSR | FileUtils.S_IWUSR
@@ -220,6 +256,8 @@ public final class GameManagerService extends IGameManagerService.Stub {
        } catch (RemoteException e) {
            Slog.w(TAG, "Could not register UidObserver");
        }

        mSysProps = injector.createSystemPropertiesWrapper();
    }

    @Override
@@ -1588,7 +1626,7 @@ public final class GameManagerService extends IGameManagerService.Stub {
        try {
            final float fps = 0.0f;
            final int uid = mPackageManager.getPackageUidAsUser(packageName, userId);
            setOverrideFrameRate(uid, fps);
            setGameModeFrameRateOverride(uid, fps);
        } catch (PackageManager.NameNotFoundException e) {
            return;
        }
@@ -1620,7 +1658,7 @@ public final class GameManagerService extends IGameManagerService.Stub {
        try {
            final float fps = modeConfig.getFps();
            final int uid = mPackageManager.getPackageUidAsUser(packageName, userId);
            setOverrideFrameRate(uid, fps);
            setGameModeFrameRateOverride(uid, fps);
        } catch (PackageManager.NameNotFoundException e) {
            return;
        }
@@ -2159,14 +2197,70 @@ public final class GameManagerService extends IGameManagerService.Stub {
    }

    @VisibleForTesting
    void setOverrideFrameRate(int uid, float frameRate) {
        nativeSetOverrideFrameRate(uid, frameRate);
    void setGameModeFrameRateOverride(int uid, float frameRate) {
        nativeSetGameModeFrameRateOverride(uid, frameRate);
    }

    @VisibleForTesting
    void setGameDefaultFrameRateOverride(int uid, float frameRate) {
        Slog.v(TAG, "setDefaultFrameRateOverride : " + uid + " , " + frameRate);
        nativeSetGameDefaultFrameRateOverride(uid, frameRate);
    }

    private float getGameDefaultFrameRate() {
        final boolean isGameDefaultFrameRateEnabled;
        float gameDefaultFrameRate = 0.0f;
        synchronized (mLock) {
            isGameDefaultFrameRateEnabled =
                    mSysProps.getBoolean(
                            PROPERTY_PERSISTENT_GFX_GAME_DEFAULT_FRAME_RATE_ENABLED, true);
        }
        if (gameDefaultFrameRate()) {
            gameDefaultFrameRate = isGameDefaultFrameRateEnabled
                    ? (float) mSysProps.getInt(
                            PROPERTY_RO_SURFACEFLINGER_GAME_DEFAULT_FRAME_RATE, 0) : 0.0f;
        }
        return gameDefaultFrameRate;
    }

    @Override
    @EnforcePermission(Manifest.permission.MANAGE_GAME_MODE)
    public void toggleGameDefaultFrameRate(boolean isEnabled) {
        toggleGameDefaultFrameRate_enforcePermission();
        if (gameDefaultFrameRate()) {
            Slog.v(TAG, "toggleGameDefaultFrameRate : " + isEnabled);
            this.toggleGameDefaultFrameRateUnchecked(isEnabled);
        }
    }

    private void toggleGameDefaultFrameRateUnchecked(boolean isEnabled) {
        // Update system properties.
        // Here we only need to immediately update games that are in the foreground.
        // We will update game default frame rate when a game comes into foreground in
        // MyUidObserver.
        synchronized (mLock) {
            if (isEnabled) {
                mSysProps.set(
                        PROPERTY_PERSISTENT_GFX_GAME_DEFAULT_FRAME_RATE_ENABLED, "true");
            } else {
                mSysProps.set(
                        PROPERTY_PERSISTENT_GFX_GAME_DEFAULT_FRAME_RATE_ENABLED, "false");
            }
        }

        // Update all foreground games' frame rate.
        synchronized (mUidObserverLock) {
            for (int uid : mForegroundGameUids) {
                setGameDefaultFrameRateOverride(uid, getGameDefaultFrameRate());
            }
        }
    }

    /**
     * load dynamic library for frame rate overriding JNI calls
     */
    private static native void nativeSetOverrideFrameRate(int uid, float frameRate);
    private static native void nativeSetGameModeFrameRateOverride(int uid, float frameRate);
    private static native void nativeSetGameDefaultFrameRateOverride(int uid, float frameRate);

    final class MyUidObserver extends UidObserver {
        @Override
@@ -2179,6 +2273,7 @@ public final class GameManagerService extends IGameManagerService.Stub {
        @Override
        public void onUidStateChanged(int uid, int procState, long procStateSeq, int capability) {
            synchronized (mUidObserverLock) {

                if (procState != ActivityManager.PROCESS_STATE_TOP) {
                    disableGameMode(uid);
                    return;
@@ -2198,6 +2293,7 @@ public final class GameManagerService extends IGameManagerService.Stub {
                    Slog.v(TAG, "Game power mode ON (process state was changed to foreground)");
                    mPowerManagerInternal.setPowerMode(Mode.GAME, true);
                }
                setGameDefaultFrameRateOverride(uid, getGameDefaultFrameRate());
                mForegroundGameUids.add(uid);
            }
        }
+67 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2023 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.server.app;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.SystemProperties;
/**
 * Wrapper interface to access {@link SystemProperties}.
 *
 * @hide
 */
interface GameManagerServiceSystemPropertiesWrapper {
    /**
     * Get the String value for the given {@code key}.
     *
     * @param key the key to lookup
     * @param def the default value in case the property is not set or empty
     * @return if the {@code key} isn't found, return {@code def} if it isn't null, or an empty
     * string otherwise
     */
    @NonNull
    String get(@NonNull String key, @Nullable String def);
    /**
     * Get the Boolean value for the given {@code key}.
     *
     * @param key the key to lookup
     * @param def the default value in case the property is not set or empty
     * @return if the {@code key} isn't found, return {@code def} if it isn't null, not parsable
     * or an empty string otherwise
     */
    @NonNull
    boolean getBoolean(@NonNull String key, boolean def);

    /**
     * Get the Integer value for the given {@code key}.
     *
     * @param key the key to lookup
     * @param def the default value in case the property is not set or empty
     * @return if the {@code key} isn't found, return {@code def} if it isn't null, not parsable
     * or an empty string otherwise
     */
    @NonNull
    int getInt(@NonNull String key, int def);
    /**
     * Set the value for the given {@code key} to {@code val}.
     *
     * @throws IllegalArgumentException if the {@code val} exceeds 91 characters
     * @throws RuntimeException if the property cannot be set, for example, if it was blocked by
     * SELinux. libc will log the underlying reason.
     */
    void set(@NonNull String key, @Nullable String val);
}
+9 −0
Original line number Diff line number Diff line
package: "android.server.app"

flag {
    name: "game_default_frame_rate"
    namespace: "game"
    description: "This flag guards the new behavior with the addition of Game Default Frame Rate feature."
    bug: "286084594"
    is_fixed_read_only: true
}
 No newline at end of file
Loading