Loading core/java/android/provider/DeviceConfig.java +9 −0 Original line number Original line Diff line number Diff line Loading @@ -336,6 +336,15 @@ public final class DeviceConfig { @TestApi @TestApi String KEY_SYSTEM_GESTURES_EXCLUDED_BY_PRE_Q_STICKY_IMMERSIVE = String KEY_SYSTEM_GESTURES_EXCLUDED_BY_PRE_Q_STICKY_IMMERSIVE = "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 60hz. * * @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(); private static final Object sLock = new Object(); Loading core/res/res/values/config.xml +4 −0 Original line number Original line Diff line number Diff line Loading @@ -4208,4 +4208,8 @@ <!-- Sharesheet: define a max number of targets per application for new shortcuts-based direct share introduced in Q --> <!-- Sharesheet: define a max number of targets per application for new shortcuts-based direct share introduced in Q --> <integer name="config_maxShortcutTargetsPerApp">3</integer> <integer name="config_maxShortcutTargetsPerApp">3</integer> <!-- 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> </resources> core/res/res/values/symbols.xml +2 −0 Original line number Original line Diff line number Diff line Loading @@ -3822,6 +3822,8 @@ <java-symbol type="string" name="config_defaultSupervisionProfileOwnerComponent" /> <java-symbol type="string" name="config_defaultSupervisionProfileOwnerComponent" /> <java-symbol type="bool" name="config_inflateSignalStrength" /> <java-symbol type="bool" name="config_inflateSignalStrength" /> <java-symbol type="array" name="config_highRefreshRateBlacklist" /> <java-symbol type="drawable" name="android_logotype" /> <java-symbol type="drawable" name="android_logotype" /> <java-symbol type="layout" name="platlogo_layout" /> <java-symbol type="layout" name="platlogo_layout" /> Loading services/core/java/com/android/server/wm/HighRefreshRateBlacklist.java +71 −29 Original line number Original line Diff line number Diff line Loading @@ -16,60 +16,102 @@ package com.android.server.wm; package com.android.server.wm; import static android.provider.DeviceConfig.WindowManager.KEY_HIGH_REFRESH_RATE_BLACKLIST; import android.annotation.NonNull; 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 android.util.ArraySet; import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; 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. * A Blacklist for packages that should force the display out of high refresh rate. */ */ class HighRefreshRateBlacklist { class HighRefreshRateBlacklist { private static final String SYSPROP_KEY = "ro.window_manager.high_refresh_rate_blacklist"; private final ArraySet<String> mBlacklistedPackages = new ArraySet<>(); private static final String SYSPROP_KEY_LENGTH_SUFFIX = "_length"; @NonNull private static final String SYSPROP_KEY_ENTRY_SUFFIX = "_entry"; private final String[] mDefaultBlacklist; private static final int MAX_ENTRIES = 50; private final Object mLock = new Object(); private ArraySet<String> mBlacklistedPackages = new ArraySet<>(); static HighRefreshRateBlacklist create() { static HighRefreshRateBlacklist create(@NonNull Resources r) { return new HighRefreshRateBlacklist(new SystemPropertyGetter() { return new HighRefreshRateBlacklist(r, new DeviceConfigInterface() { @Override @Override public int getInt(String key, int def) { public @Nullable String getProperty(@NonNull String namespace, @NonNull String name) { return SystemProperties.getInt(key, def); return DeviceConfig.getProperty(namespace, name); } } public void addOnPropertyChangedListener(@NonNull String namespace, @Override @NonNull Executor executor, public String get(String key) { @NonNull DeviceConfig.OnPropertyChangedListener listener) { return SystemProperties.get(key); DeviceConfig.addOnPropertyChangedListener(namespace, executor, listener); } } }); }); } } @VisibleForTesting @VisibleForTesting HighRefreshRateBlacklist(SystemPropertyGetter propertyGetter) { HighRefreshRateBlacklist(Resources r, DeviceConfigInterface deviceConfig) { mDefaultBlacklist = r.getStringArray(R.array.config_highRefreshRateBlacklist); deviceConfig.addOnPropertyChangedListener(DeviceConfig.NAMESPACE_WINDOW_MANAGER, BackgroundThread.getExecutor(), new OnPropertyChangedListener()); final String property = deviceConfig.getProperty(DeviceConfig.NAMESPACE_WINDOW_MANAGER, KEY_HIGH_REFRESH_RATE_BLACKLIST); updateBlacklist(property); } // Read and populate the blacklist private void updateBlacklist(@Nullable String property) { final int length = Math.min( synchronized (mLock) { propertyGetter.getInt(SYSPROP_KEY + SYSPROP_KEY_LENGTH_SUFFIX, 0), mBlacklistedPackages.clear(); MAX_ENTRIES); if (property != null) { for (int i = 1; i <= length; i++) { String[] packages = property.split(","); final String packageName = propertyGetter.get( for (String pkg : packages) { SYSPROP_KEY + SYSPROP_KEY_ENTRY_SUFFIX + i); String pkgName = pkg.trim(); if (!packageName.isEmpty()) { if (!pkgName.isEmpty()) { mBlacklistedPackages.add(packageName); 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) { boolean isBlacklisted(String packageName) { synchronized (mLock) { return mBlacklistedPackages.contains(packageName); 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 { interface DeviceConfigInterface { int getInt(String key, int def); @Nullable String getProperty(@NonNull String namespace, @NonNull String name); @NonNull String get(String key); void addOnPropertyChangedListener(@NonNull String namespace, @NonNull Executor executor, @NonNull DeviceConfig.OnPropertyChangedListener listener); } private class OnPropertyChangedListener implements DeviceConfig.OnPropertyChangedListener { public void onPropertyChanged(@NonNull String namespace, @NonNull String name, @Nullable String value) { updateBlacklist(value); } } } } } services/core/java/com/android/server/wm/WindowManagerService.java +16 −1 Original line number Original line Diff line number Diff line Loading @@ -854,7 +854,7 @@ public class WindowManagerService extends IWindowManager.Stub final Configuration mTempConfiguration = new Configuration(); 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 // 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. // is in a special boot mode, such as being encrypted or waiting for a decryption password. Loading Loading @@ -1141,6 +1141,8 @@ public class WindowManagerService extends IWindowManager.Stub this, mInputManager, mActivityTaskManager, mH.getLooper()); this, mInputManager, mActivityTaskManager, mH.getLooper()); mDragDropController = new DragDropController(this, mH.getLooper()); mDragDropController = new DragDropController(this, mH.getLooper()); mHighRefreshRateBlacklist = HighRefreshRateBlacklist.create(context.getResources()); mSystemGestureExclusionLimitDp = Math.max(MIN_GESTURE_EXCLUSION_LIMIT_DP, mSystemGestureExclusionLimitDp = Math.max(MIN_GESTURE_EXCLUSION_LIMIT_DP, DeviceConfig.getInt(DeviceConfig.NAMESPACE_WINDOW_MANAGER, DeviceConfig.getInt(DeviceConfig.NAMESPACE_WINDOW_MANAGER, KEY_SYSTEM_GESTURE_EXCLUSION_LIMIT_DP, 0)); KEY_SYSTEM_GESTURE_EXCLUSION_LIMIT_DP, 0)); Loading Loading @@ -5922,6 +5924,12 @@ public class WindowManagerService extends IWindowManager.Stub mRoot.dumpTokens(pw, dumpAll); 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) { private void dumpTraceStatus(PrintWriter pw) { pw.println("WINDOW MANAGER TRACE (dumpsys window trace)"); pw.println("WINDOW MANAGER TRACE (dumpsys window trace)"); pw.print(mWindowTracing.getStatus() + "\n"); pw.print(mWindowTracing.getStatus() + "\n"); Loading Loading @@ -6321,6 +6329,9 @@ public class WindowManagerService extends IWindowManager.Stub } else if ("trace".equals(cmd)) { } else if ("trace".equals(cmd)) { dumpTraceStatus(pw); dumpTraceStatus(pw); return; return; } else if ("refresh".equals(cmd)) { dumpHighRefreshRateBlacklist(pw); return; } else { } else { // Dumping a single name? // Dumping a single name? if (!dumpWindows(pw, cmd, args, opti, dumpAll)) { if (!dumpWindows(pw, cmd, args, opti, dumpAll)) { Loading Loading @@ -6376,6 +6387,10 @@ public class WindowManagerService extends IWindowManager.Stub pw.println(separator); pw.println(separator); } } dumpTraceStatus(pw); dumpTraceStatus(pw); if (dumpAll) { pw.println(separator); } dumpHighRefreshRateBlacklist(pw); } } } } Loading Loading
core/java/android/provider/DeviceConfig.java +9 −0 Original line number Original line Diff line number Diff line Loading @@ -336,6 +336,15 @@ public final class DeviceConfig { @TestApi @TestApi String KEY_SYSTEM_GESTURES_EXCLUDED_BY_PRE_Q_STICKY_IMMERSIVE = String KEY_SYSTEM_GESTURES_EXCLUDED_BY_PRE_Q_STICKY_IMMERSIVE = "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 60hz. * * @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(); private static final Object sLock = new Object(); Loading
core/res/res/values/config.xml +4 −0 Original line number Original line Diff line number Diff line Loading @@ -4208,4 +4208,8 @@ <!-- Sharesheet: define a max number of targets per application for new shortcuts-based direct share introduced in Q --> <!-- Sharesheet: define a max number of targets per application for new shortcuts-based direct share introduced in Q --> <integer name="config_maxShortcutTargetsPerApp">3</integer> <integer name="config_maxShortcutTargetsPerApp">3</integer> <!-- 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> </resources>
core/res/res/values/symbols.xml +2 −0 Original line number Original line Diff line number Diff line Loading @@ -3822,6 +3822,8 @@ <java-symbol type="string" name="config_defaultSupervisionProfileOwnerComponent" /> <java-symbol type="string" name="config_defaultSupervisionProfileOwnerComponent" /> <java-symbol type="bool" name="config_inflateSignalStrength" /> <java-symbol type="bool" name="config_inflateSignalStrength" /> <java-symbol type="array" name="config_highRefreshRateBlacklist" /> <java-symbol type="drawable" name="android_logotype" /> <java-symbol type="drawable" name="android_logotype" /> <java-symbol type="layout" name="platlogo_layout" /> <java-symbol type="layout" name="platlogo_layout" /> Loading
services/core/java/com/android/server/wm/HighRefreshRateBlacklist.java +71 −29 Original line number Original line Diff line number Diff line Loading @@ -16,60 +16,102 @@ package com.android.server.wm; package com.android.server.wm; import static android.provider.DeviceConfig.WindowManager.KEY_HIGH_REFRESH_RATE_BLACKLIST; import android.annotation.NonNull; 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 android.util.ArraySet; import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; 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. * A Blacklist for packages that should force the display out of high refresh rate. */ */ class HighRefreshRateBlacklist { class HighRefreshRateBlacklist { private static final String SYSPROP_KEY = "ro.window_manager.high_refresh_rate_blacklist"; private final ArraySet<String> mBlacklistedPackages = new ArraySet<>(); private static final String SYSPROP_KEY_LENGTH_SUFFIX = "_length"; @NonNull private static final String SYSPROP_KEY_ENTRY_SUFFIX = "_entry"; private final String[] mDefaultBlacklist; private static final int MAX_ENTRIES = 50; private final Object mLock = new Object(); private ArraySet<String> mBlacklistedPackages = new ArraySet<>(); static HighRefreshRateBlacklist create() { static HighRefreshRateBlacklist create(@NonNull Resources r) { return new HighRefreshRateBlacklist(new SystemPropertyGetter() { return new HighRefreshRateBlacklist(r, new DeviceConfigInterface() { @Override @Override public int getInt(String key, int def) { public @Nullable String getProperty(@NonNull String namespace, @NonNull String name) { return SystemProperties.getInt(key, def); return DeviceConfig.getProperty(namespace, name); } } public void addOnPropertyChangedListener(@NonNull String namespace, @Override @NonNull Executor executor, public String get(String key) { @NonNull DeviceConfig.OnPropertyChangedListener listener) { return SystemProperties.get(key); DeviceConfig.addOnPropertyChangedListener(namespace, executor, listener); } } }); }); } } @VisibleForTesting @VisibleForTesting HighRefreshRateBlacklist(SystemPropertyGetter propertyGetter) { HighRefreshRateBlacklist(Resources r, DeviceConfigInterface deviceConfig) { mDefaultBlacklist = r.getStringArray(R.array.config_highRefreshRateBlacklist); deviceConfig.addOnPropertyChangedListener(DeviceConfig.NAMESPACE_WINDOW_MANAGER, BackgroundThread.getExecutor(), new OnPropertyChangedListener()); final String property = deviceConfig.getProperty(DeviceConfig.NAMESPACE_WINDOW_MANAGER, KEY_HIGH_REFRESH_RATE_BLACKLIST); updateBlacklist(property); } // Read and populate the blacklist private void updateBlacklist(@Nullable String property) { final int length = Math.min( synchronized (mLock) { propertyGetter.getInt(SYSPROP_KEY + SYSPROP_KEY_LENGTH_SUFFIX, 0), mBlacklistedPackages.clear(); MAX_ENTRIES); if (property != null) { for (int i = 1; i <= length; i++) { String[] packages = property.split(","); final String packageName = propertyGetter.get( for (String pkg : packages) { SYSPROP_KEY + SYSPROP_KEY_ENTRY_SUFFIX + i); String pkgName = pkg.trim(); if (!packageName.isEmpty()) { if (!pkgName.isEmpty()) { mBlacklistedPackages.add(packageName); 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) { boolean isBlacklisted(String packageName) { synchronized (mLock) { return mBlacklistedPackages.contains(packageName); 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 { interface DeviceConfigInterface { int getInt(String key, int def); @Nullable String getProperty(@NonNull String namespace, @NonNull String name); @NonNull String get(String key); void addOnPropertyChangedListener(@NonNull String namespace, @NonNull Executor executor, @NonNull DeviceConfig.OnPropertyChangedListener listener); } private class OnPropertyChangedListener implements DeviceConfig.OnPropertyChangedListener { public void onPropertyChanged(@NonNull String namespace, @NonNull String name, @Nullable String value) { updateBlacklist(value); } } } } }
services/core/java/com/android/server/wm/WindowManagerService.java +16 −1 Original line number Original line Diff line number Diff line Loading @@ -854,7 +854,7 @@ public class WindowManagerService extends IWindowManager.Stub final Configuration mTempConfiguration = new Configuration(); 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 // 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. // is in a special boot mode, such as being encrypted or waiting for a decryption password. Loading Loading @@ -1141,6 +1141,8 @@ public class WindowManagerService extends IWindowManager.Stub this, mInputManager, mActivityTaskManager, mH.getLooper()); this, mInputManager, mActivityTaskManager, mH.getLooper()); mDragDropController = new DragDropController(this, mH.getLooper()); mDragDropController = new DragDropController(this, mH.getLooper()); mHighRefreshRateBlacklist = HighRefreshRateBlacklist.create(context.getResources()); mSystemGestureExclusionLimitDp = Math.max(MIN_GESTURE_EXCLUSION_LIMIT_DP, mSystemGestureExclusionLimitDp = Math.max(MIN_GESTURE_EXCLUSION_LIMIT_DP, DeviceConfig.getInt(DeviceConfig.NAMESPACE_WINDOW_MANAGER, DeviceConfig.getInt(DeviceConfig.NAMESPACE_WINDOW_MANAGER, KEY_SYSTEM_GESTURE_EXCLUSION_LIMIT_DP, 0)); KEY_SYSTEM_GESTURE_EXCLUSION_LIMIT_DP, 0)); Loading Loading @@ -5922,6 +5924,12 @@ public class WindowManagerService extends IWindowManager.Stub mRoot.dumpTokens(pw, dumpAll); 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) { private void dumpTraceStatus(PrintWriter pw) { pw.println("WINDOW MANAGER TRACE (dumpsys window trace)"); pw.println("WINDOW MANAGER TRACE (dumpsys window trace)"); pw.print(mWindowTracing.getStatus() + "\n"); pw.print(mWindowTracing.getStatus() + "\n"); Loading Loading @@ -6321,6 +6329,9 @@ public class WindowManagerService extends IWindowManager.Stub } else if ("trace".equals(cmd)) { } else if ("trace".equals(cmd)) { dumpTraceStatus(pw); dumpTraceStatus(pw); return; return; } else if ("refresh".equals(cmd)) { dumpHighRefreshRateBlacklist(pw); return; } else { } else { // Dumping a single name? // Dumping a single name? if (!dumpWindows(pw, cmd, args, opti, dumpAll)) { if (!dumpWindows(pw, cmd, args, opti, dumpAll)) { Loading Loading @@ -6376,6 +6387,10 @@ public class WindowManagerService extends IWindowManager.Stub pw.println(separator); pw.println(separator); } } dumpTraceStatus(pw); dumpTraceStatus(pw); if (dumpAll) { pw.println(separator); } dumpHighRefreshRateBlacklist(pw); } } } } Loading