Loading core/java/android/view/DisplayInfo.java +12 −0 Original line number Original line Diff line number Diff line Loading @@ -927,9 +927,21 @@ public final class DisplayInfo implements Parcelable { compatInfo.applyDisplayMetricsIfNeeded(outMetrics, applyToSize); compatInfo.applyDisplayMetricsIfNeeded(outMetrics, applyToSize); } } /** * The source of a change in the display info object. */ public enum DisplayInfoChangeSource { DISPLAY_SWAP, DISPLAY_MANAGER, WINDOW_MANAGER, OTHER } /** /** * Groups of related fields within a {@link DisplayInfo} object. * Groups of related fields within a {@link DisplayInfo} object. * Used to categorize changes between two instances. * Used to categorize changes between two instances. * Any changes to which fields belong to which groups need to update: * {@link com.android.server.wm.utils.DisplayInfoOverrides#WM_OVERRIDE_GROUPS}. */ */ public enum DisplayInfoGroup { public enum DisplayInfoGroup { /** Basic properties like IDs, flags, type, and ownership. */ /** Basic properties like IDs, flags, type, and ownership. */ Loading services/core/java/com/android/server/display/DisplayInfoProxy.java +68 −8 Original line number Original line Diff line number Diff line Loading @@ -16,29 +16,79 @@ package com.android.server.display; package com.android.server.display; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.Nullable; import android.hardware.display.DisplayManagerGlobal; import android.hardware.display.DisplayManagerGlobal; import android.view.DisplayInfo; import android.view.DisplayInfo; import androidx.annotation.IntRange; /** /** * Class for wrapping access of DisplayInfo objects by LogicalDisplay so that we can appropriately * A proxy class for {@link DisplayInfo} objects. * invalidate caches when they change. * This class wraps access to {@link DisplayInfo} objects by {@link LogicalDisplay} to allow * invalidating caches when the display information changes. */ */ public class DisplayInfoProxy { public class DisplayInfoProxy { private DisplayInfo mInfo; private DisplayInfo mInfo; /** * The bitmask of {@link DisplayInfo} groups that have changed in the last update. * Related to {@link DisplayInfo#getBasicChangedGroups(DisplayInfo)}. */ private int mDisplayInfoGroupsChanged = 0; /** The source of the last {@link DisplayInfo} change. */ private DisplayInfo.DisplayInfoChangeSource mDisplayInfoChangeSource; public DisplayInfoProxy(@Nullable DisplayInfo info) { public DisplayInfoProxy(@Nullable DisplayInfo info) { mInfo = info; mInfo = info; } } /** /** * Set the current {@link DisplayInfo}. * Sets the current {@link DisplayInfo} and invalidates system-wide caches. This method does * not track changes or the source of changes, and should only be used if these are recorded * elsewhere. To update the cache when something has triggered a change in * the {@link DisplayInfo} object use {@link #set(int, DisplayInfo.DisplayInfoChangeSource)} * or {@link #set(DisplayInfo, DisplayInfo, DisplayInfo.DisplayInfoChangeSource)}. * * * The also automatically invalidates the display info caches across the entire system. * @param newInfo The new {@link DisplayInfo} to apply. Must not be null. * @param info the new {@link DisplayInfo}. */ */ public void set(@Nullable DisplayInfo info) { public void set(@NonNull DisplayInfo newInfo) { mInfo = info; mInfo = newInfo; DisplayManagerGlobal.invalidateLocalDisplayInfoCaches(); } /** * Invalidates the current display information by computing the differences between an old and * new state, and then setting the new state. * * @param oldInfo The previous {@link DisplayInfo} state. Must not be null. * @param newInfo The new {@link DisplayInfo} state. Must not be null. If it is null, * use the other updateCache method with all groups marked as changed. * @param source The source of the change. */ public void set(@NonNull DisplayInfo oldInfo, @NonNull DisplayInfo newInfo, DisplayInfo.DisplayInfoChangeSource source) { mDisplayInfoGroupsChanged = oldInfo.getBasicChangedGroups(newInfo); mDisplayInfoChangeSource = source; set(newInfo); } /** * Invalidates the current display information using a pre-computed set of changed groups. * * This is an optimization for targeted updates where re-computing the full * {@link DisplayInfo} is unnecessary. * * @param changedGroups A positive integer bitmask representing the {@link DisplayInfo} groups * that have changed. * @param source The source of the change. */ public void set(@IntRange(from = 1) int changedGroups, DisplayInfo.DisplayInfoChangeSource source) { mDisplayInfoGroupsChanged = changedGroups; mDisplayInfoChangeSource = source; mInfo = null; DisplayManagerGlobal.invalidateLocalDisplayInfoCaches(); DisplayManagerGlobal.invalidateLocalDisplayInfoCaches(); } } Loading @@ -54,4 +104,14 @@ public class DisplayInfoProxy { public DisplayInfo get() { public DisplayInfo get() { return mInfo; return mInfo; } } /** Gets the display info groups that have changed. */ public int getDisplayInfoGroupsChanged() { return mDisplayInfoGroupsChanged; } /** Gets the source of the last update. */ public DisplayInfo.DisplayInfoChangeSource getDisplayInfoChangeSource() { return mDisplayInfoChangeSource; } } } No newline at end of file services/core/java/com/android/server/display/LogicalDisplay.java +46 −13 Original line number Original line Diff line number Diff line Loading @@ -21,6 +21,8 @@ import static android.view.Display.Mode.INVALID_MODE_ID; import static com.android.server.display.DisplayDeviceInfo.TOUCH_NONE; import static com.android.server.display.DisplayDeviceInfo.TOUCH_NONE; import static com.android.server.display.layout.Layout.Display.POSITION_REAR; import static com.android.server.display.layout.Layout.Display.POSITION_REAR; import static com.android.server.wm.utils.DisplayInfoOverrides.WM_OVERRIDE_FIELDS; import static com.android.server.wm.utils.DisplayInfoOverrides.WM_OVERRIDE_FIELDS; import static com.android.server.wm.utils.DisplayInfoOverrides.WM_OVERRIDE_GROUPS; import static com.android.server.wm.utils.DisplayInfoOverrides.WM_OVERRIDE_GROUPS_ENUMSET; import static com.android.server.wm.utils.DisplayInfoOverrides.copyDisplayInfoFields; import static com.android.server.wm.utils.DisplayInfoOverrides.copyDisplayInfoFields; import android.annotation.NonNull; import android.annotation.NonNull; Loading Loading @@ -294,12 +296,31 @@ final class LogicalDisplay { */ */ public DisplayInfo getDisplayInfoLocked() { public DisplayInfo getDisplayInfoLocked() { if (mInfo.get() == null) { if (mInfo.get() == null) { // We do not want to update displayInfoGroupsChanged // as no change has triggered this call mInfo.set(computeCurrentDisplayInfoLocked()); } return mInfo.get(); } /** * Computes the current display information based on the base and override info. * * Warning: this does not update the cache. */ private DisplayInfo computeCurrentDisplayInfoLocked() { DisplayInfo info = new DisplayInfo(); DisplayInfo info = new DisplayInfo(); copyDisplayInfoFields(info, mBaseDisplayInfo, mOverrideDisplayInfo, copyDisplayInfoFields(info, mBaseDisplayInfo, mOverrideDisplayInfo, WM_OVERRIDE_FIELDS); WM_OVERRIDE_FIELDS); mInfo.set(info); return info; } } return mInfo.get(); public int getDisplayInfoGroupsChangedLocked() { return mInfo.getDisplayInfoGroupsChanged(); } public DisplayInfo.DisplayInfoChangeSource getDisplayInfoChangeSource() { return mInfo.getDisplayInfoChangeSource(); } } /** /** Loading Loading @@ -341,16 +362,20 @@ final class LogicalDisplay { if (info != null) { if (info != null) { if (mOverrideDisplayInfo == null) { if (mOverrideDisplayInfo == null) { mOverrideDisplayInfo = new DisplayInfo(info); mOverrideDisplayInfo = new DisplayInfo(info); mInfo.set(null); mInfo.set(WM_OVERRIDE_GROUPS, DisplayInfo.DisplayInfoChangeSource.WINDOW_MANAGER); return true; return true; } else if (!mOverrideDisplayInfo.equals(info)) { } int groupsChanged = info.getBasicChangedGroups(mOverrideDisplayInfo, WM_OVERRIDE_GROUPS_ENUMSET); if (groupsChanged != 0) { mOverrideDisplayInfo.copyFrom(info); mOverrideDisplayInfo.copyFrom(info); mInfo.set(null); mInfo.set(groupsChanged, DisplayInfo.DisplayInfoChangeSource.WINDOW_MANAGER); return true; return true; } } } else if (mOverrideDisplayInfo != null) { } else if (mOverrideDisplayInfo != null) { mOverrideDisplayInfo = null; mOverrideDisplayInfo = null; mInfo.set(null); mInfo.set(WM_OVERRIDE_GROUPS, DisplayInfo.DisplayInfoChangeSource.WINDOW_MANAGER); return true; return true; } } return false; return false; Loading Loading @@ -440,6 +465,8 @@ final class LogicalDisplay { DisplayDeviceInfo deviceInfo = mPrimaryDisplayDevice.getDisplayDeviceInfoLocked(); DisplayDeviceInfo deviceInfo = mPrimaryDisplayDevice.getDisplayDeviceInfoLocked(); DisplayDeviceConfig config = mPrimaryDisplayDevice.getDisplayDeviceConfig(); DisplayDeviceConfig config = mPrimaryDisplayDevice.getDisplayDeviceConfig(); if (!Objects.equals(mPrimaryDisplayDeviceInfo, deviceInfo) || mDirty) { if (!Objects.equals(mPrimaryDisplayDeviceInfo, deviceInfo) || mDirty) { DisplayInfo oldDisplayInfo = getDisplayInfoLocked(); mBaseDisplayInfo.layerStack = mLayerStack; mBaseDisplayInfo.layerStack = mLayerStack; mBaseDisplayInfo.flags = 0; mBaseDisplayInfo.flags = 0; // Displays default to moving content to the primary display when removed // Displays default to moving content to the primary display when removed Loading Loading @@ -605,7 +632,8 @@ final class LogicalDisplay { mBaseDisplayInfo.canHostTasks = mCanHostTasks; mBaseDisplayInfo.canHostTasks = mCanHostTasks; mPrimaryDisplayDeviceInfo = deviceInfo; mPrimaryDisplayDeviceInfo = deviceInfo; mInfo.set(null); mInfo.set(oldDisplayInfo, computeCurrentDisplayInfoLocked(), DisplayInfo.DisplayInfoChangeSource.DISPLAY_MANAGER); mDirty = false; mDirty = false; } } } } Loading Loading @@ -992,7 +1020,8 @@ final class LogicalDisplay { if (mUserDisabledHdrTypes != userDisabledHdrTypes) { if (mUserDisabledHdrTypes != userDisabledHdrTypes) { mUserDisabledHdrTypes = userDisabledHdrTypes; mUserDisabledHdrTypes = userDisabledHdrTypes; mBaseDisplayInfo.userDisabledHdrTypes = userDisabledHdrTypes; mBaseDisplayInfo.userDisabledHdrTypes = userDisabledHdrTypes; mInfo.set(null); mInfo.set(DisplayInfo.DisplayInfoGroup.COLOR_AND_BRIGHTNESS.getMask(), DisplayInfo.DisplayInfoChangeSource.OTHER); } } } } Loading @@ -1013,7 +1042,8 @@ final class LogicalDisplay { boolean handleLogicalDisplayChangedLocked = false; boolean handleLogicalDisplayChangedLocked = false; if (isTargetDisplayType && mBaseDisplayInfo.isForceSdr != isForceSdr) { if (isTargetDisplayType && mBaseDisplayInfo.isForceSdr != isForceSdr) { mBaseDisplayInfo.isForceSdr = isForceSdr; mBaseDisplayInfo.isForceSdr = isForceSdr; mInfo.set(null); mInfo.set(DisplayInfo.DisplayInfoGroup.COLOR_AND_BRIGHTNESS.getMask(), DisplayInfo.DisplayInfoChangeSource.OTHER); handleLogicalDisplayChangedLocked = true; handleLogicalDisplayChangedLocked = true; } } return handleLogicalDisplayChangedLocked; return handleLogicalDisplayChangedLocked; Loading @@ -1039,7 +1069,8 @@ final class LogicalDisplay { Slog.i(TAG, "Set canHostTasks for display " + displayId + ": " + canHostTasks); Slog.i(TAG, "Set canHostTasks for display " + displayId + ": " + canHostTasks); mCanHostTasks = canHostTasks; mCanHostTasks = canHostTasks; mBaseDisplayInfo.canHostTasks = canHostTasks; mBaseDisplayInfo.canHostTasks = canHostTasks; mInfo.set(null); mInfo.set(DisplayInfo.DisplayInfoGroup.BASIC_PROPERTIES.getMask(), DisplayInfo.DisplayInfoChangeSource.OTHER); return true; return true; } } Loading Loading @@ -1110,13 +1141,15 @@ final class LogicalDisplay { * @return The previously set display device. * @return The previously set display device. */ */ public DisplayDevice setPrimaryDisplayDeviceLocked(@Nullable DisplayDevice device) { public DisplayDevice setPrimaryDisplayDeviceLocked(@Nullable DisplayDevice device) { DisplayInfo oldDisplayInfo = getDisplayInfoLocked(); final DisplayDevice old = mPrimaryDisplayDevice; final DisplayDevice old = mPrimaryDisplayDevice; mPrimaryDisplayDevice = device; mPrimaryDisplayDevice = device; // Reset all our display info data // Reset all our display info data mPrimaryDisplayDeviceInfo = null; mPrimaryDisplayDeviceInfo = null; mBaseDisplayInfo.copyFrom(EMPTY_DISPLAY_INFO); mBaseDisplayInfo.copyFrom(EMPTY_DISPLAY_INFO); mInfo.set(null); mInfo.set(oldDisplayInfo, computeCurrentDisplayInfoLocked(), DisplayInfo.DisplayInfoChangeSource.DISPLAY_SWAP); // Since mCanHostTasks depends on mPrimaryDisplayDevice, we should refresh mCanHostTasks // Since mCanHostTasks depends on mPrimaryDisplayDevice, we should refresh mCanHostTasks // when mPrimaryDisplayDevice changes. // when mPrimaryDisplayDevice changes. Loading services/core/java/com/android/server/wm/utils/DisplayInfoOverrides.java +21 −1 Original line number Original line Diff line number Diff line Loading @@ -20,6 +20,8 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.Nullable; import android.view.DisplayInfo; import android.view.DisplayInfo; import java.util.EnumSet; /** /** * Helper class to copy only subset of fields of DisplayInfo object or to perform * Helper class to copy only subset of fields of DisplayInfo object or to perform * comparison operation between DisplayInfo objects only with a subset of fields. * comparison operation between DisplayInfo objects only with a subset of fields. Loading @@ -28,7 +30,7 @@ public class DisplayInfoOverrides { /** /** * Set of DisplayInfo fields that are overridden in DisplayManager using values from * Set of DisplayInfo fields that are overridden in DisplayManager using values from * WindowManager * WindowManager. Any changes to the fields in here need to update {@link #WM_OVERRIDE_GROUPS}. */ */ public static final DisplayInfoFieldsUpdater WM_OVERRIDE_FIELDS = (out, source) -> { public static final DisplayInfoFieldsUpdater WM_OVERRIDE_FIELDS = (out, source) -> { out.appWidth = source.appWidth; out.appWidth = source.appWidth; Loading @@ -48,6 +50,24 @@ public class DisplayInfoOverrides { out.displayShape = source.displayShape; out.displayShape = source.displayShape; }; }; /** * A bitmask of all {@link DisplayInfo.DisplayInfoGroup}s that can be affected by * {@link #WM_OVERRIDE_FIELDS}. Must be in sync with {@link #WM_OVERRIDE_FIELDS} * and {@link #WM_OVERRIDE_GROUPS_ENUMSET}. */ public static final int WM_OVERRIDE_GROUPS = DisplayInfo.DisplayInfoGroup.ORIENTATION_AND_ROTATION.getMask() | DisplayInfo.DisplayInfoGroup.DIMENSIONS_AND_SHAPES.getMask(); /** * Same as {@link #WM_OVERRIDE_GROUPS}, but as an EnumSet. * This is used to optimize part of the code * in {@link LogicalDisplay#setDisplayInfoOverrideFromWindowManagerLocked}. */ public static final EnumSet<DisplayInfo.DisplayInfoGroup> WM_OVERRIDE_GROUPS_ENUMSET = EnumSet.of(DisplayInfo.DisplayInfoGroup.ORIENTATION_AND_ROTATION, DisplayInfo.DisplayInfoGroup.DIMENSIONS_AND_SHAPES); /** /** * Gets {@param base} DisplayInfo, overrides WindowManager-specific overrides using * Gets {@param base} DisplayInfo, overrides WindowManager-specific overrides using * {@param override} and writes the result to {@param out} * {@param override} and writes the result to {@param out} Loading services/tests/wmtests/src/com/android/server/wm/DeferredDisplayUpdaterDiffTest.java +71 −0 Original line number Original line Diff line number Diff line Loading @@ -24,6 +24,9 @@ import static com.android.server.wm.DeferredDisplayUpdater.DEFERRABLE_FIELDS; import static com.android.server.wm.DeferredDisplayUpdater.DIFF_NOT_WM_DEFERRABLE; import static com.android.server.wm.DeferredDisplayUpdater.DIFF_NOT_WM_DEFERRABLE; import static com.android.server.wm.DeferredDisplayUpdater.DIFF_WM_DEFERRABLE; import static com.android.server.wm.DeferredDisplayUpdater.DIFF_WM_DEFERRABLE; import static com.android.server.wm.DeferredDisplayUpdater.calculateDisplayInfoDiff; import static com.android.server.wm.DeferredDisplayUpdater.calculateDisplayInfoDiff; import static com.android.server.wm.utils.DisplayInfoOverrides.WM_OVERRIDE_FIELDS; import static com.android.server.wm.utils.DisplayInfoOverrides.WM_OVERRIDE_GROUPS; import static com.android.server.wm.utils.DisplayInfoOverrides.WM_OVERRIDE_GROUPS_ENUMSET; import static com.android.server.wm.utils.DisplayInfoOverrides.copyDisplayInfoFields; import static com.android.server.wm.utils.DisplayInfoOverrides.copyDisplayInfoFields; import static com.google.common.truth.Truth.assertWithMessage; import static com.google.common.truth.Truth.assertWithMessage; Loading @@ -46,6 +49,7 @@ import android.view.SurfaceControl.RefreshRateRange; import androidx.test.filters.SmallTest; import androidx.test.filters.SmallTest; import org.junit.Assert; import org.junit.Test; import org.junit.Test; import java.lang.reflect.Field; import java.lang.reflect.Field; Loading @@ -53,6 +57,7 @@ import java.lang.reflect.Modifier; import java.lang.reflect.ParameterizedType; import java.lang.reflect.ParameterizedType; import java.util.Arrays; import java.util.Arrays; import java.util.HashSet; import java.util.HashSet; import java.util.Objects; import java.util.Set; import java.util.Set; import java.util.function.Consumer; import java.util.function.Consumer; Loading Loading @@ -113,6 +118,72 @@ public class DeferredDisplayUpdaterDiffTest { }); }); } } /** * Verify that {@link com.android.server.wm.utils.DisplayInfoOverrides#WM_OVERRIDE_FIELDS} * and {@link com.android.server.wm.utils.DisplayInfoOverrides#WM_OVERRIDE_GROUPS} * and {@link com.android.server.wm.utils.DisplayInfoOverrides#WM_OVERRIDE_GROUPS} are kept in * sync. Works by applying the override and dynamically discovering which fields were * changed. It then checks that the set of {@link DisplayInfo.DisplayInfoGroup}s for those * fields matches the statically defined group constants. */ @Test public void testWmOverrideFieldsAndGroupsAreInSync() { final DisplayInfo info1 = new DisplayInfo(); final DisplayInfo info2 = new DisplayInfo(); makeAllFieldsDifferent(info1, info2); final DisplayInfo result = new DisplayInfo(); result.copyFrom(info1); WM_OVERRIDE_FIELDS.setFields(result, info2); final Set<DisplayInfo.DisplayInfoGroup> changedGroups = new HashSet<>(); forEachDisplayInfoField(field -> { try { final Object val1 = field.get(info1); final Object val2 = field.get(info2); final Object resVal = field.get(result); // If the result's field value matches info2's value, and that's different // from info1's original value, then this field was updated by WM_OVERRIDE_FIELDS. if (Objects.equals(resVal, val2) && !Objects.equals(val1, val2)) { // This field was changed by the updater. To find which group(s) it belongs to, // create two DisplayInfo objects that differ only in this one field and check // for changes. final DisplayInfo temp1 = new DisplayInfo(); final DisplayInfo temp2 = new DisplayInfo(); setDifferentFieldValues(temp1, temp2, field); int groupsMask = temp1.getBasicChangedGroups(temp2); // If groupsMask is 0, the field is not part of any group, and we can ignore it for (DisplayInfo.DisplayInfoGroup group : DisplayInfo.DisplayInfoGroup.values()) { if ((groupsMask & group.getMask()) != 0) { changedGroups.add(group); } } } } catch (IllegalAccessException e) { throw new RuntimeException(e); } }); final String errorMessage = "If you've changed the fields in WM_OVERRIDE_FIELDS, " + "you must update WM_OVERRIDE_GROUPS and WM_OVERRIDE_GROUPS_ENUMSET " + "accordingly and visa versa."; Assert.assertEquals("The set of changed groups does not match WM_OVERRIDE_GROUPS_ENUMSET. " + errorMessage, WM_OVERRIDE_GROUPS_ENUMSET, changedGroups); int changedGroupsMask = 0; for (DisplayInfo.DisplayInfoGroup group : changedGroups) { changedGroupsMask |= group.getMask(); } Assert.assertEquals("The mask of changed groups does not match WM_OVERRIDE_GROUPS. " + errorMessage, WM_OVERRIDE_GROUPS, changedGroupsMask); } /** /** * Sets each field of the objects to different values using reflection * Sets each field of the objects to different values using reflection */ */ Loading Loading
core/java/android/view/DisplayInfo.java +12 −0 Original line number Original line Diff line number Diff line Loading @@ -927,9 +927,21 @@ public final class DisplayInfo implements Parcelable { compatInfo.applyDisplayMetricsIfNeeded(outMetrics, applyToSize); compatInfo.applyDisplayMetricsIfNeeded(outMetrics, applyToSize); } } /** * The source of a change in the display info object. */ public enum DisplayInfoChangeSource { DISPLAY_SWAP, DISPLAY_MANAGER, WINDOW_MANAGER, OTHER } /** /** * Groups of related fields within a {@link DisplayInfo} object. * Groups of related fields within a {@link DisplayInfo} object. * Used to categorize changes between two instances. * Used to categorize changes between two instances. * Any changes to which fields belong to which groups need to update: * {@link com.android.server.wm.utils.DisplayInfoOverrides#WM_OVERRIDE_GROUPS}. */ */ public enum DisplayInfoGroup { public enum DisplayInfoGroup { /** Basic properties like IDs, flags, type, and ownership. */ /** Basic properties like IDs, flags, type, and ownership. */ Loading
services/core/java/com/android/server/display/DisplayInfoProxy.java +68 −8 Original line number Original line Diff line number Diff line Loading @@ -16,29 +16,79 @@ package com.android.server.display; package com.android.server.display; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.Nullable; import android.hardware.display.DisplayManagerGlobal; import android.hardware.display.DisplayManagerGlobal; import android.view.DisplayInfo; import android.view.DisplayInfo; import androidx.annotation.IntRange; /** /** * Class for wrapping access of DisplayInfo objects by LogicalDisplay so that we can appropriately * A proxy class for {@link DisplayInfo} objects. * invalidate caches when they change. * This class wraps access to {@link DisplayInfo} objects by {@link LogicalDisplay} to allow * invalidating caches when the display information changes. */ */ public class DisplayInfoProxy { public class DisplayInfoProxy { private DisplayInfo mInfo; private DisplayInfo mInfo; /** * The bitmask of {@link DisplayInfo} groups that have changed in the last update. * Related to {@link DisplayInfo#getBasicChangedGroups(DisplayInfo)}. */ private int mDisplayInfoGroupsChanged = 0; /** The source of the last {@link DisplayInfo} change. */ private DisplayInfo.DisplayInfoChangeSource mDisplayInfoChangeSource; public DisplayInfoProxy(@Nullable DisplayInfo info) { public DisplayInfoProxy(@Nullable DisplayInfo info) { mInfo = info; mInfo = info; } } /** /** * Set the current {@link DisplayInfo}. * Sets the current {@link DisplayInfo} and invalidates system-wide caches. This method does * not track changes or the source of changes, and should only be used if these are recorded * elsewhere. To update the cache when something has triggered a change in * the {@link DisplayInfo} object use {@link #set(int, DisplayInfo.DisplayInfoChangeSource)} * or {@link #set(DisplayInfo, DisplayInfo, DisplayInfo.DisplayInfoChangeSource)}. * * * The also automatically invalidates the display info caches across the entire system. * @param newInfo The new {@link DisplayInfo} to apply. Must not be null. * @param info the new {@link DisplayInfo}. */ */ public void set(@Nullable DisplayInfo info) { public void set(@NonNull DisplayInfo newInfo) { mInfo = info; mInfo = newInfo; DisplayManagerGlobal.invalidateLocalDisplayInfoCaches(); } /** * Invalidates the current display information by computing the differences between an old and * new state, and then setting the new state. * * @param oldInfo The previous {@link DisplayInfo} state. Must not be null. * @param newInfo The new {@link DisplayInfo} state. Must not be null. If it is null, * use the other updateCache method with all groups marked as changed. * @param source The source of the change. */ public void set(@NonNull DisplayInfo oldInfo, @NonNull DisplayInfo newInfo, DisplayInfo.DisplayInfoChangeSource source) { mDisplayInfoGroupsChanged = oldInfo.getBasicChangedGroups(newInfo); mDisplayInfoChangeSource = source; set(newInfo); } /** * Invalidates the current display information using a pre-computed set of changed groups. * * This is an optimization for targeted updates where re-computing the full * {@link DisplayInfo} is unnecessary. * * @param changedGroups A positive integer bitmask representing the {@link DisplayInfo} groups * that have changed. * @param source The source of the change. */ public void set(@IntRange(from = 1) int changedGroups, DisplayInfo.DisplayInfoChangeSource source) { mDisplayInfoGroupsChanged = changedGroups; mDisplayInfoChangeSource = source; mInfo = null; DisplayManagerGlobal.invalidateLocalDisplayInfoCaches(); DisplayManagerGlobal.invalidateLocalDisplayInfoCaches(); } } Loading @@ -54,4 +104,14 @@ public class DisplayInfoProxy { public DisplayInfo get() { public DisplayInfo get() { return mInfo; return mInfo; } } /** Gets the display info groups that have changed. */ public int getDisplayInfoGroupsChanged() { return mDisplayInfoGroupsChanged; } /** Gets the source of the last update. */ public DisplayInfo.DisplayInfoChangeSource getDisplayInfoChangeSource() { return mDisplayInfoChangeSource; } } } No newline at end of file
services/core/java/com/android/server/display/LogicalDisplay.java +46 −13 Original line number Original line Diff line number Diff line Loading @@ -21,6 +21,8 @@ import static android.view.Display.Mode.INVALID_MODE_ID; import static com.android.server.display.DisplayDeviceInfo.TOUCH_NONE; import static com.android.server.display.DisplayDeviceInfo.TOUCH_NONE; import static com.android.server.display.layout.Layout.Display.POSITION_REAR; import static com.android.server.display.layout.Layout.Display.POSITION_REAR; import static com.android.server.wm.utils.DisplayInfoOverrides.WM_OVERRIDE_FIELDS; import static com.android.server.wm.utils.DisplayInfoOverrides.WM_OVERRIDE_FIELDS; import static com.android.server.wm.utils.DisplayInfoOverrides.WM_OVERRIDE_GROUPS; import static com.android.server.wm.utils.DisplayInfoOverrides.WM_OVERRIDE_GROUPS_ENUMSET; import static com.android.server.wm.utils.DisplayInfoOverrides.copyDisplayInfoFields; import static com.android.server.wm.utils.DisplayInfoOverrides.copyDisplayInfoFields; import android.annotation.NonNull; import android.annotation.NonNull; Loading Loading @@ -294,12 +296,31 @@ final class LogicalDisplay { */ */ public DisplayInfo getDisplayInfoLocked() { public DisplayInfo getDisplayInfoLocked() { if (mInfo.get() == null) { if (mInfo.get() == null) { // We do not want to update displayInfoGroupsChanged // as no change has triggered this call mInfo.set(computeCurrentDisplayInfoLocked()); } return mInfo.get(); } /** * Computes the current display information based on the base and override info. * * Warning: this does not update the cache. */ private DisplayInfo computeCurrentDisplayInfoLocked() { DisplayInfo info = new DisplayInfo(); DisplayInfo info = new DisplayInfo(); copyDisplayInfoFields(info, mBaseDisplayInfo, mOverrideDisplayInfo, copyDisplayInfoFields(info, mBaseDisplayInfo, mOverrideDisplayInfo, WM_OVERRIDE_FIELDS); WM_OVERRIDE_FIELDS); mInfo.set(info); return info; } } return mInfo.get(); public int getDisplayInfoGroupsChangedLocked() { return mInfo.getDisplayInfoGroupsChanged(); } public DisplayInfo.DisplayInfoChangeSource getDisplayInfoChangeSource() { return mInfo.getDisplayInfoChangeSource(); } } /** /** Loading Loading @@ -341,16 +362,20 @@ final class LogicalDisplay { if (info != null) { if (info != null) { if (mOverrideDisplayInfo == null) { if (mOverrideDisplayInfo == null) { mOverrideDisplayInfo = new DisplayInfo(info); mOverrideDisplayInfo = new DisplayInfo(info); mInfo.set(null); mInfo.set(WM_OVERRIDE_GROUPS, DisplayInfo.DisplayInfoChangeSource.WINDOW_MANAGER); return true; return true; } else if (!mOverrideDisplayInfo.equals(info)) { } int groupsChanged = info.getBasicChangedGroups(mOverrideDisplayInfo, WM_OVERRIDE_GROUPS_ENUMSET); if (groupsChanged != 0) { mOverrideDisplayInfo.copyFrom(info); mOverrideDisplayInfo.copyFrom(info); mInfo.set(null); mInfo.set(groupsChanged, DisplayInfo.DisplayInfoChangeSource.WINDOW_MANAGER); return true; return true; } } } else if (mOverrideDisplayInfo != null) { } else if (mOverrideDisplayInfo != null) { mOverrideDisplayInfo = null; mOverrideDisplayInfo = null; mInfo.set(null); mInfo.set(WM_OVERRIDE_GROUPS, DisplayInfo.DisplayInfoChangeSource.WINDOW_MANAGER); return true; return true; } } return false; return false; Loading Loading @@ -440,6 +465,8 @@ final class LogicalDisplay { DisplayDeviceInfo deviceInfo = mPrimaryDisplayDevice.getDisplayDeviceInfoLocked(); DisplayDeviceInfo deviceInfo = mPrimaryDisplayDevice.getDisplayDeviceInfoLocked(); DisplayDeviceConfig config = mPrimaryDisplayDevice.getDisplayDeviceConfig(); DisplayDeviceConfig config = mPrimaryDisplayDevice.getDisplayDeviceConfig(); if (!Objects.equals(mPrimaryDisplayDeviceInfo, deviceInfo) || mDirty) { if (!Objects.equals(mPrimaryDisplayDeviceInfo, deviceInfo) || mDirty) { DisplayInfo oldDisplayInfo = getDisplayInfoLocked(); mBaseDisplayInfo.layerStack = mLayerStack; mBaseDisplayInfo.layerStack = mLayerStack; mBaseDisplayInfo.flags = 0; mBaseDisplayInfo.flags = 0; // Displays default to moving content to the primary display when removed // Displays default to moving content to the primary display when removed Loading Loading @@ -605,7 +632,8 @@ final class LogicalDisplay { mBaseDisplayInfo.canHostTasks = mCanHostTasks; mBaseDisplayInfo.canHostTasks = mCanHostTasks; mPrimaryDisplayDeviceInfo = deviceInfo; mPrimaryDisplayDeviceInfo = deviceInfo; mInfo.set(null); mInfo.set(oldDisplayInfo, computeCurrentDisplayInfoLocked(), DisplayInfo.DisplayInfoChangeSource.DISPLAY_MANAGER); mDirty = false; mDirty = false; } } } } Loading Loading @@ -992,7 +1020,8 @@ final class LogicalDisplay { if (mUserDisabledHdrTypes != userDisabledHdrTypes) { if (mUserDisabledHdrTypes != userDisabledHdrTypes) { mUserDisabledHdrTypes = userDisabledHdrTypes; mUserDisabledHdrTypes = userDisabledHdrTypes; mBaseDisplayInfo.userDisabledHdrTypes = userDisabledHdrTypes; mBaseDisplayInfo.userDisabledHdrTypes = userDisabledHdrTypes; mInfo.set(null); mInfo.set(DisplayInfo.DisplayInfoGroup.COLOR_AND_BRIGHTNESS.getMask(), DisplayInfo.DisplayInfoChangeSource.OTHER); } } } } Loading @@ -1013,7 +1042,8 @@ final class LogicalDisplay { boolean handleLogicalDisplayChangedLocked = false; boolean handleLogicalDisplayChangedLocked = false; if (isTargetDisplayType && mBaseDisplayInfo.isForceSdr != isForceSdr) { if (isTargetDisplayType && mBaseDisplayInfo.isForceSdr != isForceSdr) { mBaseDisplayInfo.isForceSdr = isForceSdr; mBaseDisplayInfo.isForceSdr = isForceSdr; mInfo.set(null); mInfo.set(DisplayInfo.DisplayInfoGroup.COLOR_AND_BRIGHTNESS.getMask(), DisplayInfo.DisplayInfoChangeSource.OTHER); handleLogicalDisplayChangedLocked = true; handleLogicalDisplayChangedLocked = true; } } return handleLogicalDisplayChangedLocked; return handleLogicalDisplayChangedLocked; Loading @@ -1039,7 +1069,8 @@ final class LogicalDisplay { Slog.i(TAG, "Set canHostTasks for display " + displayId + ": " + canHostTasks); Slog.i(TAG, "Set canHostTasks for display " + displayId + ": " + canHostTasks); mCanHostTasks = canHostTasks; mCanHostTasks = canHostTasks; mBaseDisplayInfo.canHostTasks = canHostTasks; mBaseDisplayInfo.canHostTasks = canHostTasks; mInfo.set(null); mInfo.set(DisplayInfo.DisplayInfoGroup.BASIC_PROPERTIES.getMask(), DisplayInfo.DisplayInfoChangeSource.OTHER); return true; return true; } } Loading Loading @@ -1110,13 +1141,15 @@ final class LogicalDisplay { * @return The previously set display device. * @return The previously set display device. */ */ public DisplayDevice setPrimaryDisplayDeviceLocked(@Nullable DisplayDevice device) { public DisplayDevice setPrimaryDisplayDeviceLocked(@Nullable DisplayDevice device) { DisplayInfo oldDisplayInfo = getDisplayInfoLocked(); final DisplayDevice old = mPrimaryDisplayDevice; final DisplayDevice old = mPrimaryDisplayDevice; mPrimaryDisplayDevice = device; mPrimaryDisplayDevice = device; // Reset all our display info data // Reset all our display info data mPrimaryDisplayDeviceInfo = null; mPrimaryDisplayDeviceInfo = null; mBaseDisplayInfo.copyFrom(EMPTY_DISPLAY_INFO); mBaseDisplayInfo.copyFrom(EMPTY_DISPLAY_INFO); mInfo.set(null); mInfo.set(oldDisplayInfo, computeCurrentDisplayInfoLocked(), DisplayInfo.DisplayInfoChangeSource.DISPLAY_SWAP); // Since mCanHostTasks depends on mPrimaryDisplayDevice, we should refresh mCanHostTasks // Since mCanHostTasks depends on mPrimaryDisplayDevice, we should refresh mCanHostTasks // when mPrimaryDisplayDevice changes. // when mPrimaryDisplayDevice changes. Loading
services/core/java/com/android/server/wm/utils/DisplayInfoOverrides.java +21 −1 Original line number Original line Diff line number Diff line Loading @@ -20,6 +20,8 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.Nullable; import android.view.DisplayInfo; import android.view.DisplayInfo; import java.util.EnumSet; /** /** * Helper class to copy only subset of fields of DisplayInfo object or to perform * Helper class to copy only subset of fields of DisplayInfo object or to perform * comparison operation between DisplayInfo objects only with a subset of fields. * comparison operation between DisplayInfo objects only with a subset of fields. Loading @@ -28,7 +30,7 @@ public class DisplayInfoOverrides { /** /** * Set of DisplayInfo fields that are overridden in DisplayManager using values from * Set of DisplayInfo fields that are overridden in DisplayManager using values from * WindowManager * WindowManager. Any changes to the fields in here need to update {@link #WM_OVERRIDE_GROUPS}. */ */ public static final DisplayInfoFieldsUpdater WM_OVERRIDE_FIELDS = (out, source) -> { public static final DisplayInfoFieldsUpdater WM_OVERRIDE_FIELDS = (out, source) -> { out.appWidth = source.appWidth; out.appWidth = source.appWidth; Loading @@ -48,6 +50,24 @@ public class DisplayInfoOverrides { out.displayShape = source.displayShape; out.displayShape = source.displayShape; }; }; /** * A bitmask of all {@link DisplayInfo.DisplayInfoGroup}s that can be affected by * {@link #WM_OVERRIDE_FIELDS}. Must be in sync with {@link #WM_OVERRIDE_FIELDS} * and {@link #WM_OVERRIDE_GROUPS_ENUMSET}. */ public static final int WM_OVERRIDE_GROUPS = DisplayInfo.DisplayInfoGroup.ORIENTATION_AND_ROTATION.getMask() | DisplayInfo.DisplayInfoGroup.DIMENSIONS_AND_SHAPES.getMask(); /** * Same as {@link #WM_OVERRIDE_GROUPS}, but as an EnumSet. * This is used to optimize part of the code * in {@link LogicalDisplay#setDisplayInfoOverrideFromWindowManagerLocked}. */ public static final EnumSet<DisplayInfo.DisplayInfoGroup> WM_OVERRIDE_GROUPS_ENUMSET = EnumSet.of(DisplayInfo.DisplayInfoGroup.ORIENTATION_AND_ROTATION, DisplayInfo.DisplayInfoGroup.DIMENSIONS_AND_SHAPES); /** /** * Gets {@param base} DisplayInfo, overrides WindowManager-specific overrides using * Gets {@param base} DisplayInfo, overrides WindowManager-specific overrides using * {@param override} and writes the result to {@param out} * {@param override} and writes the result to {@param out} Loading
services/tests/wmtests/src/com/android/server/wm/DeferredDisplayUpdaterDiffTest.java +71 −0 Original line number Original line Diff line number Diff line Loading @@ -24,6 +24,9 @@ import static com.android.server.wm.DeferredDisplayUpdater.DEFERRABLE_FIELDS; import static com.android.server.wm.DeferredDisplayUpdater.DIFF_NOT_WM_DEFERRABLE; import static com.android.server.wm.DeferredDisplayUpdater.DIFF_NOT_WM_DEFERRABLE; import static com.android.server.wm.DeferredDisplayUpdater.DIFF_WM_DEFERRABLE; import static com.android.server.wm.DeferredDisplayUpdater.DIFF_WM_DEFERRABLE; import static com.android.server.wm.DeferredDisplayUpdater.calculateDisplayInfoDiff; import static com.android.server.wm.DeferredDisplayUpdater.calculateDisplayInfoDiff; import static com.android.server.wm.utils.DisplayInfoOverrides.WM_OVERRIDE_FIELDS; import static com.android.server.wm.utils.DisplayInfoOverrides.WM_OVERRIDE_GROUPS; import static com.android.server.wm.utils.DisplayInfoOverrides.WM_OVERRIDE_GROUPS_ENUMSET; import static com.android.server.wm.utils.DisplayInfoOverrides.copyDisplayInfoFields; import static com.android.server.wm.utils.DisplayInfoOverrides.copyDisplayInfoFields; import static com.google.common.truth.Truth.assertWithMessage; import static com.google.common.truth.Truth.assertWithMessage; Loading @@ -46,6 +49,7 @@ import android.view.SurfaceControl.RefreshRateRange; import androidx.test.filters.SmallTest; import androidx.test.filters.SmallTest; import org.junit.Assert; import org.junit.Test; import org.junit.Test; import java.lang.reflect.Field; import java.lang.reflect.Field; Loading @@ -53,6 +57,7 @@ import java.lang.reflect.Modifier; import java.lang.reflect.ParameterizedType; import java.lang.reflect.ParameterizedType; import java.util.Arrays; import java.util.Arrays; import java.util.HashSet; import java.util.HashSet; import java.util.Objects; import java.util.Set; import java.util.Set; import java.util.function.Consumer; import java.util.function.Consumer; Loading Loading @@ -113,6 +118,72 @@ public class DeferredDisplayUpdaterDiffTest { }); }); } } /** * Verify that {@link com.android.server.wm.utils.DisplayInfoOverrides#WM_OVERRIDE_FIELDS} * and {@link com.android.server.wm.utils.DisplayInfoOverrides#WM_OVERRIDE_GROUPS} * and {@link com.android.server.wm.utils.DisplayInfoOverrides#WM_OVERRIDE_GROUPS} are kept in * sync. Works by applying the override and dynamically discovering which fields were * changed. It then checks that the set of {@link DisplayInfo.DisplayInfoGroup}s for those * fields matches the statically defined group constants. */ @Test public void testWmOverrideFieldsAndGroupsAreInSync() { final DisplayInfo info1 = new DisplayInfo(); final DisplayInfo info2 = new DisplayInfo(); makeAllFieldsDifferent(info1, info2); final DisplayInfo result = new DisplayInfo(); result.copyFrom(info1); WM_OVERRIDE_FIELDS.setFields(result, info2); final Set<DisplayInfo.DisplayInfoGroup> changedGroups = new HashSet<>(); forEachDisplayInfoField(field -> { try { final Object val1 = field.get(info1); final Object val2 = field.get(info2); final Object resVal = field.get(result); // If the result's field value matches info2's value, and that's different // from info1's original value, then this field was updated by WM_OVERRIDE_FIELDS. if (Objects.equals(resVal, val2) && !Objects.equals(val1, val2)) { // This field was changed by the updater. To find which group(s) it belongs to, // create two DisplayInfo objects that differ only in this one field and check // for changes. final DisplayInfo temp1 = new DisplayInfo(); final DisplayInfo temp2 = new DisplayInfo(); setDifferentFieldValues(temp1, temp2, field); int groupsMask = temp1.getBasicChangedGroups(temp2); // If groupsMask is 0, the field is not part of any group, and we can ignore it for (DisplayInfo.DisplayInfoGroup group : DisplayInfo.DisplayInfoGroup.values()) { if ((groupsMask & group.getMask()) != 0) { changedGroups.add(group); } } } } catch (IllegalAccessException e) { throw new RuntimeException(e); } }); final String errorMessage = "If you've changed the fields in WM_OVERRIDE_FIELDS, " + "you must update WM_OVERRIDE_GROUPS and WM_OVERRIDE_GROUPS_ENUMSET " + "accordingly and visa versa."; Assert.assertEquals("The set of changed groups does not match WM_OVERRIDE_GROUPS_ENUMSET. " + errorMessage, WM_OVERRIDE_GROUPS_ENUMSET, changedGroups); int changedGroupsMask = 0; for (DisplayInfo.DisplayInfoGroup group : changedGroups) { changedGroupsMask |= group.getMask(); } Assert.assertEquals("The mask of changed groups does not match WM_OVERRIDE_GROUPS. " + errorMessage, WM_OVERRIDE_GROUPS, changedGroupsMask); } /** /** * Sets each field of the objects to different values using reflection * Sets each field of the objects to different values using reflection */ */ Loading