Merge "Read the high refresh rate blacklist from DeviceConfig." into qt-r1-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
6173ba7475
@@ -336,6 +336,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 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();
|
||||
|
||||
@@ -4208,4 +4208,8 @@
|
||||
|
||||
<!-- Sharesheet: define a max number of targets per application for new shortcuts-based direct share introduced in Q -->
|
||||
<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>
|
||||
|
||||
@@ -3822,6 +3822,8 @@
|
||||
<java-symbol type="string" name="config_defaultSupervisionProfileOwnerComponent" />
|
||||
<java-symbol type="bool" name="config_inflateSignalStrength" />
|
||||
|
||||
<java-symbol type="array" name="config_highRefreshRateBlacklist" />
|
||||
|
||||
<java-symbol type="drawable" name="android_logotype" />
|
||||
<java-symbol type="layout" name="platlogo_layout" />
|
||||
|
||||
|
||||
@@ -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 final ArraySet<String> mBlacklistedPackages = new ArraySet<>();
|
||||
@NonNull
|
||||
private final String[] mDefaultBlacklist;
|
||||
private final Object mLock = new Object();
|
||||
|
||||
private ArraySet<String> mBlacklistedPackages = new ArraySet<>();
|
||||
|
||||
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 addOnPropertyChangedListener(@NonNull String namespace,
|
||||
@NonNull Executor executor,
|
||||
@NonNull DeviceConfig.OnPropertyChangedListener listener) {
|
||||
DeviceConfig.addOnPropertyChangedListener(namespace, executor, listener);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@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
|
||||
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) {
|
||||
return mBlacklistedPackages.contains(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 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -854,7 +854,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.
|
||||
@@ -1141,6 +1141,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));
|
||||
@@ -5922,6 +5924,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");
|
||||
@@ -6321,6 +6329,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)) {
|
||||
@@ -6376,6 +6387,10 @@ public class WindowManagerService extends IWindowManager.Stub
|
||||
pw.println(separator);
|
||||
}
|
||||
dumpTraceStatus(pw);
|
||||
if (dumpAll) {
|
||||
pw.println(separator);
|
||||
}
|
||||
dumpHighRefreshRateBlacklist(pw);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,71 +16,153 @@
|
||||
|
||||
package com.android.server.wm;
|
||||
|
||||
import static android.provider.DeviceConfig.WindowManager.KEY_HIGH_REFRESH_RATE_BLACKLIST;
|
||||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.content.res.Resources;
|
||||
import android.platform.test.annotations.Presubmit;
|
||||
import android.provider.DeviceConfig;
|
||||
import android.util.Pair;
|
||||
|
||||
import androidx.test.filters.FlakyTest;
|
||||
import androidx.test.filters.SmallTest;
|
||||
|
||||
import com.android.server.wm.HighRefreshRateBlacklist.SystemPropertyGetter;
|
||||
import com.android.internal.R;
|
||||
import com.android.server.wm.HighRefreshRateBlacklist.DeviceConfigInterface;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* Build/Install/Run:
|
||||
* atest WmTests:HighRefreshRateBlacklistTest
|
||||
* atest WmTests:HighRefreshRateBlacklistTest
|
||||
*/
|
||||
@SmallTest
|
||||
@Presubmit
|
||||
@FlakyTest
|
||||
public class HighRefreshRateBlacklistTest {
|
||||
|
||||
@Test
|
||||
public void testBlacklist() {
|
||||
HighRefreshRateBlacklist blacklist = new HighRefreshRateBlacklist(
|
||||
new SystemPropertyGetter() {
|
||||
|
||||
@Override
|
||||
public int getInt(String key, int def) {
|
||||
if ("ro.window_manager.high_refresh_rate_blacklist_length".equals(key)) {
|
||||
return 2;
|
||||
}
|
||||
return def;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String get(String key) {
|
||||
if ("ro.window_manager.high_refresh_rate_blacklist_entry1".equals(key)) {
|
||||
return "com.android.sample1";
|
||||
}
|
||||
if ("ro.window_manager.high_refresh_rate_blacklist_entry2".equals(key)) {
|
||||
return "com.android.sample2";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
});
|
||||
public void testDefaultBlacklist() {
|
||||
final Resources r = createResources("com.android.sample1", "com.android.sample2");
|
||||
HighRefreshRateBlacklist blacklist =
|
||||
new HighRefreshRateBlacklist(r, new FakeDeviceConfigInterface());
|
||||
assertTrue(blacklist.isBlacklisted("com.android.sample1"));
|
||||
assertTrue(blacklist.isBlacklisted("com.android.sample2"));
|
||||
assertFalse(blacklist.isBlacklisted("com.android.sample3"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNoBlacklist() {
|
||||
HighRefreshRateBlacklist blacklist = new HighRefreshRateBlacklist(
|
||||
new SystemPropertyGetter() {
|
||||
|
||||
@Override
|
||||
public int getInt(String key, int def) {
|
||||
return def;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String get(String key) {
|
||||
return "";
|
||||
}
|
||||
});
|
||||
public void testNoDefaultBlacklist() {
|
||||
final Resources r = createResources();
|
||||
HighRefreshRateBlacklist blacklist =
|
||||
new HighRefreshRateBlacklist(r, new FakeDeviceConfigInterface());
|
||||
assertFalse(blacklist.isBlacklisted("com.android.sample1"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDefaultBlacklistIsOverriddenByDeviceConfig() {
|
||||
final Resources r = createResources("com.android.sample1");
|
||||
final FakeDeviceConfigInterface config = new FakeDeviceConfigInterface();
|
||||
config.setBlacklist("com.android.sample2,com.android.sample3");
|
||||
HighRefreshRateBlacklist blacklist = new HighRefreshRateBlacklist(r, config);
|
||||
assertFalse(blacklist.isBlacklisted("com.android.sample1"));
|
||||
assertTrue(blacklist.isBlacklisted("com.android.sample2"));
|
||||
assertTrue(blacklist.isBlacklisted("com.android.sample3"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDefaultBlacklistIsOverriddenByEmptyDeviceConfig() {
|
||||
final Resources r = createResources("com.android.sample1");
|
||||
final FakeDeviceConfigInterface config = new FakeDeviceConfigInterface();
|
||||
config.setBlacklist("");
|
||||
HighRefreshRateBlacklist blacklist = new HighRefreshRateBlacklist(r, config);
|
||||
assertFalse(blacklist.isBlacklisted("com.android.sample1"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDefaultBlacklistIsOverriddenByDeviceConfigUpdate() {
|
||||
final Resources r = createResources("com.android.sample1");
|
||||
final FakeDeviceConfigInterface config = new FakeDeviceConfigInterface();
|
||||
HighRefreshRateBlacklist blacklist = new HighRefreshRateBlacklist(r, config);
|
||||
|
||||
// First check that the default blacklist is in effect
|
||||
assertTrue(blacklist.isBlacklisted("com.android.sample1"));
|
||||
assertFalse(blacklist.isBlacklisted("com.android.sample2"));
|
||||
assertFalse(blacklist.isBlacklisted("com.android.sample3"));
|
||||
|
||||
// Then confirm that the DeviceConfig list has propagated and taken effect.
|
||||
config.setBlacklist("com.android.sample2,com.android.sample3");
|
||||
assertFalse(blacklist.isBlacklisted("com.android.sample1"));
|
||||
assertTrue(blacklist.isBlacklisted("com.android.sample2"));
|
||||
assertTrue(blacklist.isBlacklisted("com.android.sample3"));
|
||||
|
||||
// Finally make sure we go back to the default list if the DeviceConfig gets deleted.
|
||||
config.setBlacklist(null);
|
||||
assertTrue(blacklist.isBlacklisted("com.android.sample1"));
|
||||
assertFalse(blacklist.isBlacklisted("com.android.sample2"));
|
||||
assertFalse(blacklist.isBlacklisted("com.android.sample3"));
|
||||
}
|
||||
|
||||
private Resources createResources(String... defaultBlacklist) {
|
||||
Resources r = mock(Resources.class);
|
||||
when(r.getStringArray(R.array.config_highRefreshRateBlacklist))
|
||||
.thenReturn(defaultBlacklist);
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
class FakeDeviceConfigInterface implements DeviceConfigInterface {
|
||||
private List<Pair<DeviceConfig.OnPropertyChangedListener, Executor>> mListeners =
|
||||
new ArrayList<>();
|
||||
private String mBlacklist;
|
||||
|
||||
@Override
|
||||
public String getProperty(String namespace, String name) {
|
||||
if (!DeviceConfig.NAMESPACE_WINDOW_MANAGER.equals(namespace)
|
||||
|| !KEY_HIGH_REFRESH_RATE_BLACKLIST.equals(name)) {
|
||||
throw new IllegalArgumentException("Only things in NAMESPACE_WINDOW_MANAGER "
|
||||
+ "supported.");
|
||||
}
|
||||
return mBlacklist;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addOnPropertyChangedListener(String namespace, Executor executor,
|
||||
DeviceConfig.OnPropertyChangedListener listener) {
|
||||
|
||||
if (!DeviceConfig.NAMESPACE_WINDOW_MANAGER.equals(namespace)) {
|
||||
throw new IllegalArgumentException("Only things in NAMESPACE_WINDOW_MANAGER "
|
||||
+ "supported.");
|
||||
}
|
||||
mListeners.add(new Pair<>(listener, executor));
|
||||
}
|
||||
|
||||
void setBlacklist(String blacklist) {
|
||||
mBlacklist = blacklist;
|
||||
CountDownLatch latch = new CountDownLatch(mListeners.size());
|
||||
for (Pair<DeviceConfig.OnPropertyChangedListener, Executor> listenerInfo :
|
||||
mListeners) {
|
||||
final Executor executor = listenerInfo.second;
|
||||
final DeviceConfig.OnPropertyChangedListener listener = listenerInfo.first;
|
||||
executor.execute(() -> {
|
||||
listener.onPropertyChanged(DeviceConfig.NAMESPACE_WINDOW_MANAGER,
|
||||
KEY_HIGH_REFRESH_RATE_BLACKLIST, blacklist);
|
||||
latch.countDown();
|
||||
});
|
||||
}
|
||||
try {
|
||||
latch.await(10, TimeUnit.SECONDS);
|
||||
} catch (InterruptedException e) {
|
||||
throw new RuntimeException("Failed to notify all blacklist listeners in time.", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user