Merge "Add Settings switch to disable Game default frame rate" into main
This commit is contained in:
@@ -139,6 +139,7 @@
|
||||
<uses-permission android:name="android.permission.REMAP_MODIFIER_KEYS" />
|
||||
<uses-permission android:name="android.permission.ACCESS_GPU_SERVICE" />
|
||||
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
|
||||
<uses-permission android:name="android.permission.MANAGE_GAME_MODE" />
|
||||
|
||||
<application
|
||||
android:name=".SettingsApplication"
|
||||
|
@@ -5,6 +5,13 @@ package: "com.android.settings.flags"
|
||||
# NOTE: All Settings flags share the same Flags class, so prefix our
|
||||
# flags with 'development' to prevent naming collision.
|
||||
|
||||
flag {
|
||||
name: "development_game_default_frame_rate"
|
||||
namespace: "game"
|
||||
description: "This flag guards the new behavior with the addition of Game Default Frame Rate feature."
|
||||
bug: "286084594"
|
||||
}
|
||||
|
||||
flag {
|
||||
name: "development_hdr_sdr_ratio"
|
||||
namespace: "core_graphics"
|
||||
|
@@ -2639,8 +2639,12 @@
|
||||
<string name="display_white_balance_summary"></string>
|
||||
<!-- Display settings screen, setting option name to change Fold setting -->
|
||||
<string name="fold_lock_behavior_title">Continue using apps on fold</string>
|
||||
<!-- Display settings screen, game default frame rate settings title [CHAR LIMIT=30] -->
|
||||
<string name="disable_game_default_frame_rate_title">Disable default frame rate for games</string>
|
||||
<!-- Display settings screen, game default frame rate settings summary [CHAR LIMIT=NONE] -->
|
||||
<string name="disable_game_default_frame_rate_summary">Disable limiting the maximum frame rate for games at <xliff:g id="frame_rate" example="60">%1$d</xliff:g> Hz.</string>
|
||||
<!-- Display settings screen, peak refresh rate settings title [CHAR LIMIT=30] -->
|
||||
<string name="peak_refresh_rate_title">Smooth Display</string>
|
||||
<string name="peak_refresh_rate_title">Smooth display</string>
|
||||
<!-- Display settings screen, peak refresh rate settings summary [CHAR LIMIT=NONE] -->
|
||||
<string name="peak_refresh_rate_summary">Automatically raises the refresh rate up to <xliff:g name="refresh_rate" example="120">%1$d</xliff:g> Hz for some content. Increases battery usage.</string>
|
||||
<!-- Display developer settings: Force to the highest refresh rate [CHAR LIMIT=NONE] -->
|
||||
|
@@ -255,6 +255,11 @@
|
||||
android:title="@string/enable_angle_as_system_driver"
|
||||
android:summary="@string/enable_angle_as_system_driver_summary" />
|
||||
|
||||
<SwitchPreferenceCompat
|
||||
android:key="disable_game_default_frame_rate"
|
||||
android:title="@string/disable_game_default_frame_rate_title"
|
||||
android:summary="@string/disable_game_default_frame_rate_summary"/>
|
||||
|
||||
<Preference
|
||||
android:key="graphics_driver_dashboard"
|
||||
android:title="@string/graphics_driver_dashboard_title"
|
||||
|
@@ -688,6 +688,7 @@ public class DevelopmentSettingsDashboardFragment extends RestrictedDashboardFra
|
||||
controllers.add(new HardwareLayersUpdatesPreferenceController(context));
|
||||
controllers.add(new DebugGpuOverdrawPreferenceController(context));
|
||||
controllers.add(new DebugNonRectClipOperationsPreferenceController(context));
|
||||
controllers.add(new GameDefaultFrameRatePreferenceController(context));
|
||||
controllers.add(new ForceDarkPreferenceController(context));
|
||||
controllers.add(new EnableBlursPreferenceController(context));
|
||||
controllers.add(new ForceMSAAPreferenceController(context));
|
||||
|
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* 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.settings.development;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.os.SystemProperties;
|
||||
/**
|
||||
* Wrapper interface to access {@link SystemProperties}.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
|
||||
public interface DevelopmentSystemPropertiesWrapper {
|
||||
/**
|
||||
* 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, @NonNull String 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, @NonNull String val);
|
||||
|
||||
/**
|
||||
* 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, @NonNull int 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}.
|
||||
*/
|
||||
boolean getBoolean(@NonNull String key, @NonNull boolean def);
|
||||
}
|
@@ -0,0 +1,146 @@
|
||||
/*
|
||||
* 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.settings.development;
|
||||
|
||||
|
||||
import android.app.IGameManagerService;
|
||||
import android.content.Context;
|
||||
import android.os.RemoteException;
|
||||
import android.os.ServiceManager;
|
||||
import android.os.SystemProperties;
|
||||
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.TwoStatePreference;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.core.PreferenceControllerMixin;
|
||||
import com.android.settings.flags.Flags;
|
||||
import com.android.settingslib.development.DeveloperOptionsPreferenceController;
|
||||
|
||||
public class GameDefaultFrameRatePreferenceController extends DeveloperOptionsPreferenceController
|
||||
implements Preference.OnPreferenceChangeListener, PreferenceControllerMixin {
|
||||
private static final String TAG = "GameDefFrameRatePrefCtr";
|
||||
private static final String DISABLE_GAME_DEFAULT_FRAME_RATE_KEY =
|
||||
"disable_game_default_frame_rate";
|
||||
private final IGameManagerService mGameManagerService;
|
||||
static final String PROPERTY_DEBUG_GFX_GAME_DEFAULT_FRAME_RATE_DISABLED =
|
||||
"debug.graphics.game_default_frame_rate.disabled";
|
||||
|
||||
private final DevelopmentSystemPropertiesWrapper mSysProps;
|
||||
private int mGameDefaultFrameRateValue;
|
||||
|
||||
@VisibleForTesting
|
||||
static class Injector {
|
||||
public DevelopmentSystemPropertiesWrapper createSystemPropertiesWrapper() {
|
||||
return new DevelopmentSystemPropertiesWrapper() {
|
||||
@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 GameDefaultFrameRatePreferenceController(Context context) {
|
||||
super(context);
|
||||
mGameManagerService = IGameManagerService.Stub.asInterface(
|
||||
ServiceManager.getService(Context.GAME_SERVICE));
|
||||
|
||||
mSysProps = new Injector().createSystemPropertiesWrapper();
|
||||
|
||||
mGameDefaultFrameRateValue = mSysProps.getInt(
|
||||
"ro.surface_flinger.game_default_frame_rate_override", 60);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
GameDefaultFrameRatePreferenceController(Context context,
|
||||
IGameManagerService gameManagerService,
|
||||
Injector injector) {
|
||||
super(context);
|
||||
mGameManagerService = gameManagerService;
|
||||
mSysProps = injector.createSystemPropertiesWrapper();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPreferenceKey() {
|
||||
return DISABLE_GAME_DEFAULT_FRAME_RATE_KEY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
||||
final boolean isDisabled = (Boolean) newValue;
|
||||
try {
|
||||
mGameManagerService.toggleGameDefaultFrameRate(!isDisabled);
|
||||
updateGameDefaultPreferenceSetting();
|
||||
} catch (RemoteException e) {
|
||||
// intentional no-op
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void updateGameDefaultPreferenceSetting() {
|
||||
final boolean isDisabled =
|
||||
mSysProps.getBoolean(PROPERTY_DEBUG_GFX_GAME_DEFAULT_FRAME_RATE_DISABLED,
|
||||
false);
|
||||
((TwoStatePreference) mPreference).setChecked(isDisabled);
|
||||
mPreference.setSummary(mContext.getString(
|
||||
R.string.disable_game_default_frame_rate_summary,
|
||||
mGameDefaultFrameRateValue));
|
||||
}
|
||||
@Override
|
||||
public void updateState(Preference preference) {
|
||||
super.updateState(preference);
|
||||
updateGameDefaultPreferenceSetting();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAvailable() {
|
||||
return Flags.developmentGameDefaultFrameRate();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDeveloperOptionsSwitchDisabled() {
|
||||
super.onDeveloperOptionsSwitchDisabled();
|
||||
final TwoStatePreference preference = (TwoStatePreference) mPreference;
|
||||
if (preference.isChecked()) {
|
||||
// When the developer option is disabled, we should set everything
|
||||
// to off, that is, enabling game default frame rate.
|
||||
try {
|
||||
mGameManagerService.toggleGameDefaultFrameRate(true);
|
||||
} catch (RemoteException e) {
|
||||
// intentional no-op
|
||||
}
|
||||
}
|
||||
preference.setChecked(false);
|
||||
}
|
||||
|
||||
}
|
@@ -1,3 +1,6 @@
|
||||
# GameDefaultFrameRatePreferenceController
|
||||
per-file GameDefaultFrameRatePreferenceController.java=file:platform/frameworks/base:/GAME_MANAGER_OWNERS
|
||||
|
||||
# ShowHdrSdrRatioPreferenceController
|
||||
per-file ShowHdrSdrRatioPreferenceController.java=file:platform/frameworks/native:/services/surfaceflinger/OWNERS
|
||||
|
||||
|
@@ -0,0 +1,177 @@
|
||||
/*
|
||||
* 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.settings.development;
|
||||
|
||||
import static com.android.settings.development.GameDefaultFrameRatePreferenceController.Injector;
|
||||
import static com.android.settings.development.GameDefaultFrameRatePreferenceController.PROPERTY_DEBUG_GFX_GAME_DEFAULT_FRAME_RATE_DISABLED;
|
||||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.app.IGameManagerService;
|
||||
import android.content.Context;
|
||||
import android.os.RemoteException;
|
||||
import android.platform.test.flag.junit.SetFlagsRule;
|
||||
|
||||
import androidx.preference.PreferenceScreen;
|
||||
import androidx.preference.TwoStatePreference;
|
||||
|
||||
import com.android.settings.flags.Flags;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.ArgumentMatchers;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.RuntimeEnvironment;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
public class GameDefaultFrameRatePreferenceControllerTest {
|
||||
@Mock
|
||||
private Context mContext;
|
||||
@Mock
|
||||
private PreferenceScreen mScreen;
|
||||
@Mock
|
||||
private TwoStatePreference mPreference;
|
||||
@Mock
|
||||
private IGameManagerService mGameManagerService;
|
||||
@Mock
|
||||
private DevelopmentSystemPropertiesWrapper mSysPropsMock;
|
||||
|
||||
private GameDefaultFrameRatePreferenceController mController;
|
||||
|
||||
@Rule
|
||||
public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
mContext = RuntimeEnvironment.application;
|
||||
mController = new GameDefaultFrameRatePreferenceController(mContext, mGameManagerService,
|
||||
new Injector(){
|
||||
@Override
|
||||
public DevelopmentSystemPropertiesWrapper createSystemPropertiesWrapper() {
|
||||
return mSysPropsMock;
|
||||
}
|
||||
});
|
||||
when(mScreen.findPreference(mController.getPreferenceKey())).thenReturn(mPreference);
|
||||
mController.displayPreference(mScreen);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onPreferenceChange_settingEnabled_shouldChecked() throws RemoteException {
|
||||
mSetFlagsRule.enableFlags(Flags.FLAG_DEVELOPMENT_GAME_DEFAULT_FRAME_RATE);
|
||||
assertTrue(mController.isAvailable());
|
||||
when(mSysPropsMock.getBoolean(
|
||||
ArgumentMatchers.eq(PROPERTY_DEBUG_GFX_GAME_DEFAULT_FRAME_RATE_DISABLED),
|
||||
ArgumentMatchers.eq(false)))
|
||||
.thenReturn(true);
|
||||
|
||||
mController.onPreferenceChange(mPreference, true /* new value */);
|
||||
verify(mPreference).setChecked(true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onPreferenceChange_settingDisabled_shouldUnchecked() throws RemoteException {
|
||||
mSetFlagsRule.enableFlags(Flags.FLAG_DEVELOPMENT_GAME_DEFAULT_FRAME_RATE);
|
||||
assertTrue(mController.isAvailable());
|
||||
when(mSysPropsMock.getBoolean(
|
||||
ArgumentMatchers.eq(PROPERTY_DEBUG_GFX_GAME_DEFAULT_FRAME_RATE_DISABLED),
|
||||
ArgumentMatchers.eq(false)))
|
||||
.thenReturn(false);
|
||||
mController.onPreferenceChange(mPreference, false /* new value */);
|
||||
verify(mPreference).setChecked(false);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateState_settingEnabled_shouldChecked() throws RemoteException {
|
||||
mSetFlagsRule.enableFlags(Flags.FLAG_DEVELOPMENT_GAME_DEFAULT_FRAME_RATE);
|
||||
assertTrue(mController.isAvailable());
|
||||
when(mSysPropsMock.getBoolean(
|
||||
ArgumentMatchers.eq(PROPERTY_DEBUG_GFX_GAME_DEFAULT_FRAME_RATE_DISABLED),
|
||||
ArgumentMatchers.eq(false)))
|
||||
.thenReturn(true);
|
||||
mController.updateState(mPreference);
|
||||
verify(mPreference).setChecked(true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateState_settingDisabled_shouldUnchecked() throws RemoteException {
|
||||
mSetFlagsRule.enableFlags(Flags.FLAG_DEVELOPMENT_GAME_DEFAULT_FRAME_RATE);
|
||||
assertTrue(mController.isAvailable());
|
||||
when(mSysPropsMock.getBoolean(
|
||||
ArgumentMatchers.eq(PROPERTY_DEBUG_GFX_GAME_DEFAULT_FRAME_RATE_DISABLED),
|
||||
ArgumentMatchers.eq(false)))
|
||||
.thenReturn(false);
|
||||
mController.updateState(mPreference);
|
||||
verify(mPreference).setChecked(false);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void settingNotAvailable_flagsOff() {
|
||||
mSetFlagsRule.disableFlags(Flags.FLAG_DEVELOPMENT_GAME_DEFAULT_FRAME_RATE);
|
||||
mController = new GameDefaultFrameRatePreferenceController(
|
||||
mContext, mGameManagerService, new Injector());
|
||||
assertFalse(mController.isAvailable());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void settingAvailable_flagsOn() {
|
||||
mSetFlagsRule.enableFlags(Flags.FLAG_DEVELOPMENT_GAME_DEFAULT_FRAME_RATE);
|
||||
mController = new GameDefaultFrameRatePreferenceController(
|
||||
mContext, mGameManagerService, new Injector());
|
||||
assertTrue(mController.isAvailable());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onDeveloperOptionsSwitchDisabled_preferenceUnchecked_shouldNotTurnOffPreference()
|
||||
throws RemoteException {
|
||||
mSetFlagsRule.enableFlags(Flags.FLAG_DEVELOPMENT_GAME_DEFAULT_FRAME_RATE);
|
||||
when(mSysPropsMock.getBoolean(
|
||||
ArgumentMatchers.eq(PROPERTY_DEBUG_GFX_GAME_DEFAULT_FRAME_RATE_DISABLED),
|
||||
ArgumentMatchers.eq(false)))
|
||||
.thenReturn(false);
|
||||
assertTrue(mController.isAvailable());
|
||||
when(mPreference.isChecked()).thenReturn(false);
|
||||
mController.onDeveloperOptionsSwitchDisabled();
|
||||
|
||||
verify(mPreference).setChecked(false);
|
||||
verify(mPreference).setEnabled(false);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onDeveloperOptionsSwitchDisabled_preferenceChecked_shouldTurnOffPreference()
|
||||
throws RemoteException {
|
||||
mSetFlagsRule.enableFlags(Flags.FLAG_DEVELOPMENT_GAME_DEFAULT_FRAME_RATE);
|
||||
when(mSysPropsMock.getBoolean(
|
||||
ArgumentMatchers.eq(PROPERTY_DEBUG_GFX_GAME_DEFAULT_FRAME_RATE_DISABLED),
|
||||
ArgumentMatchers.eq(false)))
|
||||
.thenReturn(true);
|
||||
assertTrue(mController.isAvailable());
|
||||
|
||||
when(mPreference.isChecked()).thenReturn(true);
|
||||
mController.onDeveloperOptionsSwitchDisabled();
|
||||
|
||||
verify(mPreference).setChecked(false);
|
||||
verify(mPreference).setEnabled(false);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user