Loading core/java/android/provider/DeviceConfig.java +9 −0 Original line number Diff line number Diff line Loading @@ -344,6 +344,15 @@ public final class DeviceConfig { @TestApi String KEY_SYSTEM_GESTURES_EXCLUDED_BY_PRE_Q_STICKY_IMMERSIVE = "system_gestures_excluded_by_pre_q_sticky_immersive"; /** * Key for controlling which packages are explicitly blocked from running at refresh rates * higher than 90hz. * * @see android.provider.DeviceConfig#NAMESPACE_WINDOW_MANAGER * @hide */ String KEY_HIGH_REFRESH_RATE_BLACKLIST = "high_refresh_rate_blacklist"; } private static final Object sLock = new Object(); Loading core/res/res/values/config.xml +4 −0 Original line number Diff line number Diff line Loading @@ -4225,4 +4225,8 @@ the default implementation of ACTION_FACTORY_RESET does not work, so it is needed to re-route this intent to this package. This is being used in MasterClearReceiver.java. --> <string name="config_factoryResetPackage" translatable="false"></string> <!-- The list of packages to automatically opt out of refresh rates higher than 60hz because of known compatibility issues. --> <string-array name="config_highRefreshRateBlacklist"></string-array> </resources> core/res/res/values/symbols.xml +1 −0 Original line number Diff line number Diff line Loading @@ -3828,4 +3828,5 @@ <java-symbol type="integer" name="config_notificationStripRemoteViewSizeBytes" /> <java-symbol type="string" name="config_factoryResetPackage" /> <java-symbol type="array" name="config_highRefreshRateBlacklist" /> </resources> services/core/java/com/android/server/wm/HighRefreshRateBlacklist.java +71 −29 Original line number Diff line number Diff line Loading @@ -16,60 +16,102 @@ package com.android.server.wm; import static android.provider.DeviceConfig.WindowManager.KEY_HIGH_REFRESH_RATE_BLACKLIST; import android.annotation.NonNull; import android.os.SystemProperties; import android.annotation.Nullable; import android.content.res.Resources; import android.provider.DeviceConfig; import android.util.ArraySet; import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.os.BackgroundThread; import java.io.PrintWriter; import java.util.concurrent.Executor; /** * A Blacklist for packages that should force the display out of high refresh rate. */ class HighRefreshRateBlacklist { private static final String SYSPROP_KEY = "ro.window_manager.high_refresh_rate_blacklist"; private static final String SYSPROP_KEY_LENGTH_SUFFIX = "_length"; private static final String SYSPROP_KEY_ENTRY_SUFFIX = "_entry"; private static final int MAX_ENTRIES = 50; private ArraySet<String> mBlacklistedPackages = new ArraySet<>(); private final ArraySet<String> mBlacklistedPackages = new ArraySet<>(); @NonNull private final String[] mDefaultBlacklist; private final Object mLock = new Object(); static HighRefreshRateBlacklist create() { return new HighRefreshRateBlacklist(new SystemPropertyGetter() { static HighRefreshRateBlacklist create(@NonNull Resources r) { return new HighRefreshRateBlacklist(r, new DeviceConfigInterface() { @Override public int getInt(String key, int def) { return SystemProperties.getInt(key, def); public @Nullable String getProperty(@NonNull String namespace, @NonNull String name) { return DeviceConfig.getProperty(namespace, name); } @Override public String get(String key) { return SystemProperties.get(key); public void addOnPropertiesChangedListener(@NonNull String namespace, @NonNull Executor executor, @NonNull DeviceConfig.OnPropertiesChangedListener listener) { DeviceConfig.addOnPropertiesChangedListener(namespace, executor, listener); } }); } @VisibleForTesting HighRefreshRateBlacklist(SystemPropertyGetter propertyGetter) { HighRefreshRateBlacklist(Resources r, DeviceConfigInterface deviceConfig) { mDefaultBlacklist = r.getStringArray(R.array.config_highRefreshRateBlacklist); deviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_WINDOW_MANAGER, BackgroundThread.getExecutor(), new OnPropertiesChangedListener()); final String property = deviceConfig.getProperty(DeviceConfig.NAMESPACE_WINDOW_MANAGER, KEY_HIGH_REFRESH_RATE_BLACKLIST); updateBlacklist(property); } // Read and populate the blacklist final int length = Math.min( propertyGetter.getInt(SYSPROP_KEY + SYSPROP_KEY_LENGTH_SUFFIX, 0), MAX_ENTRIES); for (int i = 1; i <= length; i++) { final String packageName = propertyGetter.get( SYSPROP_KEY + SYSPROP_KEY_ENTRY_SUFFIX + i); if (!packageName.isEmpty()) { mBlacklistedPackages.add(packageName); private void updateBlacklist(@Nullable String property) { synchronized (mLock) { mBlacklistedPackages.clear(); if (property != null) { String[] packages = property.split(","); for (String pkg : packages) { String pkgName = pkg.trim(); if (!pkgName.isEmpty()) { mBlacklistedPackages.add(pkgName); } } } else { // If there's no config, or the config has been deleted, fallback to the device's // default blacklist for (String pkg : mDefaultBlacklist) { mBlacklistedPackages.add(pkg); } } } } boolean isBlacklisted(String packageName) { synchronized (mLock) { return mBlacklistedPackages.contains(packageName); } } void dump(PrintWriter pw) { pw.println("High Refresh Rate Blacklist"); pw.println(" Packages:"); synchronized (mLock) { for (String pkg : mBlacklistedPackages) { pw.println(" " + pkg); } } } interface SystemPropertyGetter { int getInt(String key, int def); @NonNull String get(String key); interface DeviceConfigInterface { @Nullable String getProperty(@NonNull String namespace, @NonNull String name); void addOnPropertiesChangedListener(@NonNull String namespace, @NonNull Executor executor, @NonNull DeviceConfig.OnPropertiesChangedListener listener); } private class OnPropertiesChangedListener implements DeviceConfig.OnPropertiesChangedListener { public void onPropertiesChanged(@NonNull DeviceConfig.Properties properties) { updateBlacklist( properties.getString(KEY_HIGH_REFRESH_RATE_BLACKLIST, null /*default*/)); } } } services/core/java/com/android/server/wm/WindowManagerService.java +16 −1 Original line number Diff line number Diff line Loading @@ -856,7 +856,7 @@ public class WindowManagerService extends IWindowManager.Stub final Configuration mTempConfiguration = new Configuration(); final HighRefreshRateBlacklist mHighRefreshRateBlacklist = HighRefreshRateBlacklist.create(); final HighRefreshRateBlacklist mHighRefreshRateBlacklist; // If true, only the core apps and services are being launched because the device // is in a special boot mode, such as being encrypted or waiting for a decryption password. Loading Loading @@ -1143,6 +1143,8 @@ public class WindowManagerService extends IWindowManager.Stub this, mInputManager, mActivityTaskManager, mH.getLooper()); mDragDropController = new DragDropController(this, mH.getLooper()); mHighRefreshRateBlacklist = HighRefreshRateBlacklist.create(context.getResources()); mSystemGestureExclusionLimitDp = Math.max(MIN_GESTURE_EXCLUSION_LIMIT_DP, DeviceConfig.getInt(DeviceConfig.NAMESPACE_WINDOW_MANAGER, KEY_SYSTEM_GESTURE_EXCLUSION_LIMIT_DP, 0)); Loading Loading @@ -5897,6 +5899,12 @@ public class WindowManagerService extends IWindowManager.Stub mRoot.dumpTokens(pw, dumpAll); } private void dumpHighRefreshRateBlacklist(PrintWriter pw) { pw.println("WINDOW MANAGER HIGH REFRESH RATE BLACKLIST (dumpsys window refresh)"); mHighRefreshRateBlacklist.dump(pw); } private void dumpTraceStatus(PrintWriter pw) { pw.println("WINDOW MANAGER TRACE (dumpsys window trace)"); pw.print(mWindowTracing.getStatus() + "\n"); Loading Loading @@ -6297,6 +6305,9 @@ public class WindowManagerService extends IWindowManager.Stub } else if ("trace".equals(cmd)) { dumpTraceStatus(pw); return; } else if ("refresh".equals(cmd)) { dumpHighRefreshRateBlacklist(pw); return; } else { // Dumping a single name? if (!dumpWindows(pw, cmd, args, opti, dumpAll)) { Loading Loading @@ -6352,6 +6363,10 @@ public class WindowManagerService extends IWindowManager.Stub pw.println(separator); } dumpTraceStatus(pw); if (dumpAll) { pw.println(separator); } dumpHighRefreshRateBlacklist(pw); } } Loading Loading
core/java/android/provider/DeviceConfig.java +9 −0 Original line number Diff line number Diff line Loading @@ -344,6 +344,15 @@ public final class DeviceConfig { @TestApi String KEY_SYSTEM_GESTURES_EXCLUDED_BY_PRE_Q_STICKY_IMMERSIVE = "system_gestures_excluded_by_pre_q_sticky_immersive"; /** * Key for controlling which packages are explicitly blocked from running at refresh rates * higher than 90hz. * * @see android.provider.DeviceConfig#NAMESPACE_WINDOW_MANAGER * @hide */ String KEY_HIGH_REFRESH_RATE_BLACKLIST = "high_refresh_rate_blacklist"; } private static final Object sLock = new Object(); Loading
core/res/res/values/config.xml +4 −0 Original line number Diff line number Diff line Loading @@ -4225,4 +4225,8 @@ the default implementation of ACTION_FACTORY_RESET does not work, so it is needed to re-route this intent to this package. This is being used in MasterClearReceiver.java. --> <string name="config_factoryResetPackage" translatable="false"></string> <!-- The list of packages to automatically opt out of refresh rates higher than 60hz because of known compatibility issues. --> <string-array name="config_highRefreshRateBlacklist"></string-array> </resources>
core/res/res/values/symbols.xml +1 −0 Original line number Diff line number Diff line Loading @@ -3828,4 +3828,5 @@ <java-symbol type="integer" name="config_notificationStripRemoteViewSizeBytes" /> <java-symbol type="string" name="config_factoryResetPackage" /> <java-symbol type="array" name="config_highRefreshRateBlacklist" /> </resources>
services/core/java/com/android/server/wm/HighRefreshRateBlacklist.java +71 −29 Original line number Diff line number Diff line Loading @@ -16,60 +16,102 @@ package com.android.server.wm; import static android.provider.DeviceConfig.WindowManager.KEY_HIGH_REFRESH_RATE_BLACKLIST; import android.annotation.NonNull; import android.os.SystemProperties; import android.annotation.Nullable; import android.content.res.Resources; import android.provider.DeviceConfig; import android.util.ArraySet; import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.os.BackgroundThread; import java.io.PrintWriter; import java.util.concurrent.Executor; /** * A Blacklist for packages that should force the display out of high refresh rate. */ class HighRefreshRateBlacklist { private static final String SYSPROP_KEY = "ro.window_manager.high_refresh_rate_blacklist"; private static final String SYSPROP_KEY_LENGTH_SUFFIX = "_length"; private static final String SYSPROP_KEY_ENTRY_SUFFIX = "_entry"; private static final int MAX_ENTRIES = 50; private ArraySet<String> mBlacklistedPackages = new ArraySet<>(); private final ArraySet<String> mBlacklistedPackages = new ArraySet<>(); @NonNull private final String[] mDefaultBlacklist; private final Object mLock = new Object(); static HighRefreshRateBlacklist create() { return new HighRefreshRateBlacklist(new SystemPropertyGetter() { static HighRefreshRateBlacklist create(@NonNull Resources r) { return new HighRefreshRateBlacklist(r, new DeviceConfigInterface() { @Override public int getInt(String key, int def) { return SystemProperties.getInt(key, def); public @Nullable String getProperty(@NonNull String namespace, @NonNull String name) { return DeviceConfig.getProperty(namespace, name); } @Override public String get(String key) { return SystemProperties.get(key); public void addOnPropertiesChangedListener(@NonNull String namespace, @NonNull Executor executor, @NonNull DeviceConfig.OnPropertiesChangedListener listener) { DeviceConfig.addOnPropertiesChangedListener(namespace, executor, listener); } }); } @VisibleForTesting HighRefreshRateBlacklist(SystemPropertyGetter propertyGetter) { HighRefreshRateBlacklist(Resources r, DeviceConfigInterface deviceConfig) { mDefaultBlacklist = r.getStringArray(R.array.config_highRefreshRateBlacklist); deviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_WINDOW_MANAGER, BackgroundThread.getExecutor(), new OnPropertiesChangedListener()); final String property = deviceConfig.getProperty(DeviceConfig.NAMESPACE_WINDOW_MANAGER, KEY_HIGH_REFRESH_RATE_BLACKLIST); updateBlacklist(property); } // Read and populate the blacklist final int length = Math.min( propertyGetter.getInt(SYSPROP_KEY + SYSPROP_KEY_LENGTH_SUFFIX, 0), MAX_ENTRIES); for (int i = 1; i <= length; i++) { final String packageName = propertyGetter.get( SYSPROP_KEY + SYSPROP_KEY_ENTRY_SUFFIX + i); if (!packageName.isEmpty()) { mBlacklistedPackages.add(packageName); private void updateBlacklist(@Nullable String property) { synchronized (mLock) { mBlacklistedPackages.clear(); if (property != null) { String[] packages = property.split(","); for (String pkg : packages) { String pkgName = pkg.trim(); if (!pkgName.isEmpty()) { mBlacklistedPackages.add(pkgName); } } } else { // If there's no config, or the config has been deleted, fallback to the device's // default blacklist for (String pkg : mDefaultBlacklist) { mBlacklistedPackages.add(pkg); } } } } boolean isBlacklisted(String packageName) { synchronized (mLock) { return mBlacklistedPackages.contains(packageName); } } void dump(PrintWriter pw) { pw.println("High Refresh Rate Blacklist"); pw.println(" Packages:"); synchronized (mLock) { for (String pkg : mBlacklistedPackages) { pw.println(" " + pkg); } } } interface SystemPropertyGetter { int getInt(String key, int def); @NonNull String get(String key); interface DeviceConfigInterface { @Nullable String getProperty(@NonNull String namespace, @NonNull String name); void addOnPropertiesChangedListener(@NonNull String namespace, @NonNull Executor executor, @NonNull DeviceConfig.OnPropertiesChangedListener listener); } private class OnPropertiesChangedListener implements DeviceConfig.OnPropertiesChangedListener { public void onPropertiesChanged(@NonNull DeviceConfig.Properties properties) { updateBlacklist( properties.getString(KEY_HIGH_REFRESH_RATE_BLACKLIST, null /*default*/)); } } }
services/core/java/com/android/server/wm/WindowManagerService.java +16 −1 Original line number Diff line number Diff line Loading @@ -856,7 +856,7 @@ public class WindowManagerService extends IWindowManager.Stub final Configuration mTempConfiguration = new Configuration(); final HighRefreshRateBlacklist mHighRefreshRateBlacklist = HighRefreshRateBlacklist.create(); final HighRefreshRateBlacklist mHighRefreshRateBlacklist; // If true, only the core apps and services are being launched because the device // is in a special boot mode, such as being encrypted or waiting for a decryption password. Loading Loading @@ -1143,6 +1143,8 @@ public class WindowManagerService extends IWindowManager.Stub this, mInputManager, mActivityTaskManager, mH.getLooper()); mDragDropController = new DragDropController(this, mH.getLooper()); mHighRefreshRateBlacklist = HighRefreshRateBlacklist.create(context.getResources()); mSystemGestureExclusionLimitDp = Math.max(MIN_GESTURE_EXCLUSION_LIMIT_DP, DeviceConfig.getInt(DeviceConfig.NAMESPACE_WINDOW_MANAGER, KEY_SYSTEM_GESTURE_EXCLUSION_LIMIT_DP, 0)); Loading Loading @@ -5897,6 +5899,12 @@ public class WindowManagerService extends IWindowManager.Stub mRoot.dumpTokens(pw, dumpAll); } private void dumpHighRefreshRateBlacklist(PrintWriter pw) { pw.println("WINDOW MANAGER HIGH REFRESH RATE BLACKLIST (dumpsys window refresh)"); mHighRefreshRateBlacklist.dump(pw); } private void dumpTraceStatus(PrintWriter pw) { pw.println("WINDOW MANAGER TRACE (dumpsys window trace)"); pw.print(mWindowTracing.getStatus() + "\n"); Loading Loading @@ -6297,6 +6305,9 @@ public class WindowManagerService extends IWindowManager.Stub } else if ("trace".equals(cmd)) { dumpTraceStatus(pw); return; } else if ("refresh".equals(cmd)) { dumpHighRefreshRateBlacklist(pw); return; } else { // Dumping a single name? if (!dumpWindows(pw, cmd, args, opti, dumpAll)) { Loading Loading @@ -6352,6 +6363,10 @@ public class WindowManagerService extends IWindowManager.Stub pw.println(separator); } dumpTraceStatus(pw); if (dumpAll) { pw.println(separator); } dumpHighRefreshRateBlacklist(pw); } } Loading