diff options
6 files changed, 143 insertions, 39 deletions
diff --git a/services/core/java/com/android/server/display/DisplayAdapter.java b/services/core/java/com/android/server/display/DisplayAdapter.java index 69bc66fea56c..155f82a421ae 100644 --- a/services/core/java/com/android/server/display/DisplayAdapter.java +++ b/services/core/java/com/android/server/display/DisplayAdapter.java @@ -19,6 +19,7 @@ package com.android.server.display; import android.content.Context; import android.os.Handler; import android.view.Display; +import android.view.SurfaceControl; import com.android.server.display.feature.DisplayManagerFlags; @@ -138,6 +139,21 @@ abstract class DisplayAdapter { vsyncRate, /* isSynthetic= */ false, alternativeRefreshRates, supportedHdrTypes); } + static int getPowerModeForState(int state) { + switch (state) { + case Display.STATE_OFF: + return SurfaceControl.POWER_MODE_OFF; + case Display.STATE_DOZE: + return SurfaceControl.POWER_MODE_DOZE; + case Display.STATE_DOZE_SUSPEND: + return SurfaceControl.POWER_MODE_DOZE_SUSPEND; + case Display.STATE_ON_SUSPEND: + return SurfaceControl.POWER_MODE_ON_SUSPEND; + default: + return SurfaceControl.POWER_MODE_NORMAL; + } + } + public interface Listener { void onDisplayDeviceEvent(DisplayDevice device, int event); void onTraversalRequested(); diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java index 258c95582e3a..d402f010281f 100644 --- a/services/core/java/com/android/server/display/DisplayManagerService.java +++ b/services/core/java/com/android/server/display/DisplayManagerService.java @@ -2618,8 +2618,7 @@ public final class DisplayManagerService extends SystemService { // Blank or unblank the display immediately to match the state requested // by the display power controller (if known). DisplayDeviceInfo info = device.getDisplayDeviceInfoLocked(); - if ((info.flags & DisplayDeviceInfo.FLAG_NEVER_BLANK) == 0 - || android.companion.virtualdevice.flags.Flags.correctVirtualDisplayPowerState()) { + if ((info.flags & DisplayDeviceInfo.FLAG_NEVER_BLANK) == 0) { final LogicalDisplay display = mLogicalDisplayMapper.getDisplayLocked(device); if (display == null) { return null; @@ -5580,9 +5579,7 @@ public final class DisplayManagerService extends SystemService { final DisplayDevice displayDevice = mLogicalDisplayMapper.getDisplayLocked( id).getPrimaryDisplayDeviceLocked(); final int flags = displayDevice.getDisplayDeviceInfoLocked().flags; - if ((flags & DisplayDeviceInfo.FLAG_NEVER_BLANK) == 0 - || android.companion.virtualdevice.flags.Flags - .correctVirtualDisplayPowerState()) { + if ((flags & DisplayDeviceInfo.FLAG_NEVER_BLANK) == 0) { final DisplayPowerController displayPowerController = mDisplayPowerControllers.get(id); if (displayPowerController != null) { diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java index 551202c20cbb..7b714ad2bd9e 100644 --- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java +++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java @@ -208,21 +208,6 @@ final class LocalDisplayAdapter extends DisplayAdapter { } } - static int getPowerModeForState(int state) { - switch (state) { - case Display.STATE_OFF: - return SurfaceControl.POWER_MODE_OFF; - case Display.STATE_DOZE: - return SurfaceControl.POWER_MODE_DOZE; - case Display.STATE_DOZE_SUSPEND: - return SurfaceControl.POWER_MODE_DOZE_SUSPEND; - case Display.STATE_ON_SUSPEND: - return SurfaceControl.POWER_MODE_ON_SUSPEND; - default: - return SurfaceControl.POWER_MODE_NORMAL; - } - } - private final class LocalDisplayDevice extends DisplayDevice { private final long mPhysicalDisplayId; private final SparseArray<DisplayModeRecord> mSupportedModes = new SparseArray<>(); diff --git a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java index e7939bb50ece..ac03a93ca9e1 100644 --- a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java +++ b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java @@ -113,6 +113,11 @@ public class VirtualDisplayAdapter extends DisplayAdapter { public void destroyDisplay(IBinder displayToken) { DisplayControl.destroyVirtualDisplay(displayToken); } + + @Override + public void setDisplayPowerMode(IBinder displayToken, int mode) { + SurfaceControl.setDisplayPowerMode(displayToken, mode); + } }, featureFlags); } @@ -340,6 +345,7 @@ public class VirtualDisplayAdapter extends DisplayAdapter { private Display.Mode mMode; private int mDisplayIdToMirror; private boolean mIsWindowManagerMirroring; + private final boolean mNeverBlank; private final DisplayCutout mDisplayCutout; private final float mDefaultBrightness; private final float mDimBrightness; @@ -371,7 +377,11 @@ public class VirtualDisplayAdapter extends DisplayAdapter { mCallback = callback; mProjection = projection; mMediaProjectionCallback = mediaProjectionCallback; - if (android.companion.virtualdevice.flags.Flags.correctVirtualDisplayPowerState()) { + // Private non-mirror displays are never blank and always on. + mNeverBlank = (flags & VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR) == 0 + && (flags & VIRTUAL_DISPLAY_FLAG_PUBLIC) == 0; + if (android.companion.virtualdevice.flags.Flags.correctVirtualDisplayPowerState() + && !mNeverBlank) { // The display's power state depends on the power state of the state of its // display / power group, which we don't know here. Initializing to UNKNOWN allows // the first call to requestDisplayStateLocked() to set the correct state. @@ -471,7 +481,15 @@ public class VirtualDisplayAdapter extends DisplayAdapter { @Override public Runnable requestDisplayStateLocked(int state, float brightnessState, float sdrBrightnessState, DisplayOffloadSessionImpl displayOffloadSession) { + Runnable runnable = null; if (state != mDisplayState) { + Slog.d(TAG, "Changing state of virtual display " + mName + " from " + + Display.stateToString(mDisplayState) + " to " + + Display.stateToString(state)); + if (state != Display.STATE_ON && state != Display.STATE_OFF) { + Slog.wtf(TAG, "Unexpected display state for Virtual Display: " + + Display.stateToString(state)); + } mDisplayState = state; mInfo = null; sendDisplayDeviceEventLocked(this, DISPLAY_DEVICE_EVENT_CHANGED); @@ -480,6 +498,15 @@ public class VirtualDisplayAdapter extends DisplayAdapter { } else { mCallback.dispatchDisplayResumed(); } + + if (android.companion.virtualdevice.flags.Flags.correctVirtualDisplayPowerState()) { + final IBinder token = getDisplayTokenLocked(); + runnable = () -> { + final int mode = getPowerModeForState(state); + Slog.d(TAG, "Requesting power mode for display " + mName + " to " + mode); + mSurfaceControlDisplayFactory.setDisplayPowerMode(token, mode); + }; + } } if (android.companion.virtualdevice.flags.Flags.deviceAwareDisplayPower() && mBrightnessListener != null @@ -488,7 +515,7 @@ public class VirtualDisplayAdapter extends DisplayAdapter { mCurrentBrightness = brightnessState; mCallback.dispatchRequestedBrightnessChanged(mCurrentBrightness); } - return null; + return runnable; } @Override @@ -572,23 +599,14 @@ public class VirtualDisplayAdapter extends DisplayAdapter { mInfo.yDpi = mDensityDpi; mInfo.presentationDeadlineNanos = 1000000000L / (int) getRefreshRate(); // 1 frame mInfo.flags = 0; - if (android.companion.virtualdevice.flags.Flags.correctVirtualDisplayPowerState()) { - if ((mFlags & VIRTUAL_DISPLAY_FLAG_PUBLIC) == 0) { - mInfo.flags |= DisplayDeviceInfo.FLAG_PRIVATE; - } - if ((mFlags & VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR) == 0) { - mInfo.flags |= DisplayDeviceInfo.FLAG_OWN_CONTENT_ONLY; - } - } else { - if ((mFlags & VIRTUAL_DISPLAY_FLAG_PUBLIC) == 0) { - mInfo.flags |= DisplayDeviceInfo.FLAG_PRIVATE - | DisplayDeviceInfo.FLAG_NEVER_BLANK; - } - if ((mFlags & VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR) != 0) { - mInfo.flags &= ~DisplayDeviceInfo.FLAG_NEVER_BLANK; - } else { - mInfo.flags |= DisplayDeviceInfo.FLAG_OWN_CONTENT_ONLY; - } + if ((mFlags & VIRTUAL_DISPLAY_FLAG_PUBLIC) == 0) { + mInfo.flags |= DisplayDeviceInfo.FLAG_PRIVATE; + } + if ((mFlags & VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR) == 0) { + mInfo.flags |= DisplayDeviceInfo.FLAG_OWN_CONTENT_ONLY; + } + if (mNeverBlank) { + mInfo.flags |= DisplayDeviceInfo.FLAG_NEVER_BLANK; } if ((mFlags & VIRTUAL_DISPLAY_FLAG_OWN_DISPLAY_GROUP) != 0) { mInfo.flags |= DisplayDeviceInfo.FLAG_OWN_DISPLAY_GROUP; @@ -782,5 +800,13 @@ public class VirtualDisplayAdapter extends DisplayAdapter { * @param displayToken The display token for the display to be destroyed. */ void destroyDisplay(IBinder displayToken); + + /** + * Set the display power mode in SurfaceFlinger. + * + * @param displayToken The display token for the display. + * @param mode the SurfaceControl power mode, e.g. {@link SurfaceControl#POWER_MODE_OFF}. + */ + void setDisplayPowerMode(IBinder displayToken, int mode); } } diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java index c151732cec66..65585d06ff06 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java +++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java @@ -332,6 +332,10 @@ public class DisplayManagerServiceTest { @Override public void destroyDisplay(IBinder displayToken) { } + + @Override + public void setDisplayPowerMode(IBinder displayToken, int mode) { + } }, flags); } diff --git a/services/tests/displayservicetests/src/com/android/server/display/VirtualDisplayAdapterTest.java b/services/tests/displayservicetests/src/com/android/server/display/VirtualDisplayAdapterTest.java index 9287b3004279..0bef3b89547f 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/VirtualDisplayAdapterTest.java +++ b/services/tests/displayservicetests/src/com/android/server/display/VirtualDisplayAdapterTest.java @@ -21,19 +21,29 @@ import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyFloat; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import android.hardware.display.DisplayManager; import android.hardware.display.IVirtualDisplayCallback; import android.hardware.display.VirtualDisplayConfig; import android.media.projection.IMediaProjection; +import android.os.Binder; import android.os.IBinder; import android.os.PowerManager; import android.os.Process; +import android.platform.test.annotations.EnableFlags; import android.platform.test.flag.junit.CheckFlagsRule; import android.platform.test.flag.junit.DeviceFlagsValueProvider; +import android.platform.test.flag.junit.SetFlagsRule; import android.testing.TestableContext; import android.view.Display; import android.view.Surface; +import android.view.SurfaceControl; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; @@ -67,6 +77,9 @@ public class VirtualDisplayAdapterTest { public final TestableContext mContext = new TestableContext( InstrumentationRegistry.getInstrumentation().getContext()); + @Rule + public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); + @Mock private VirtualDisplayAdapter.SurfaceControlDisplayFactory mMockSufaceControlDisplayFactory; @@ -380,6 +393,69 @@ public class VirtualDisplayAdapterTest { } } + @EnableFlags( + android.companion.virtualdevice.flags.Flags.FLAG_CORRECT_VIRTUAL_DISPLAY_POWER_STATE) + @Test + public void neverBlankDisplay_alwaysOn() { + // A non-public non-mirror display is considered never blank. + DisplayDevice device = mAdapter.createVirtualDisplayLocked(mMockCallback, + /* projection= */ null, /* ownerUid= */ 10, /* packageName= */ "testpackage", + "uniqueId", /* surface= */ mSurfaceMock, /* flags= */ 0, + mVirtualDisplayConfigMock); + + DisplayDeviceInfo info = device.getDisplayDeviceInfoLocked(); + assertThat(info.state).isEqualTo(Display.STATE_ON); + assertThat(info.flags & DisplayDeviceInfo.FLAG_NEVER_BLANK) + .isEqualTo(DisplayDeviceInfo.FLAG_NEVER_BLANK); + } + + @EnableFlags( + android.companion.virtualdevice.flags.Flags.FLAG_CORRECT_VIRTUAL_DISPLAY_POWER_STATE) + @Test + public void virtualDisplayStateChange_propagatesToSurfaceControl() throws Exception { + final String uniqueId = "uniqueId"; + final IBinder displayToken = new Binder(); + when(mMockSufaceControlDisplayFactory.createDisplay( + any(), anyBoolean(), eq(uniqueId), anyFloat())) + .thenReturn(displayToken); + + // The display needs to be public, otherwise it will be considered never blank. + DisplayDevice device = mAdapter.createVirtualDisplayLocked(mMockCallback, + /* projection= */ null, /* ownerUid= */ 10, /* packageName= */ "testpackage", + uniqueId, /* surface= */ mSurfaceMock, DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC, + mVirtualDisplayConfigMock); + + DisplayDeviceInfo info = device.getDisplayDeviceInfoLocked(); + assertThat(info.state).isEqualTo(Display.STATE_UNKNOWN); + assertThat(info.flags & DisplayDeviceInfo.FLAG_NEVER_BLANK).isEqualTo(0); + + // Any initial state change is processed because the display state is initially UNKNOWN + Runnable stateOnRunnable = device.requestDisplayStateLocked( + Display.STATE_ON, /* brightnessState= */ 1.0f, /* sdrBrightnessState= */ 1.0f, + /* displayOffloadSession= */ null); + assertThat(stateOnRunnable).isNotNull(); + stateOnRunnable.run(); + verify(mMockSufaceControlDisplayFactory) + .setDisplayPowerMode(displayToken, SurfaceControl.POWER_MODE_NORMAL); + verify(mMockCallback).onResumed(); + + // Requesting the same display state is a no-op + Runnable stateOnSecondRunnable = device.requestDisplayStateLocked( + Display.STATE_ON, /* brightnessState= */ 1.0f, /* sdrBrightnessState= */ 1.0f, + /* displayOffloadSession= */ null); + assertThat(stateOnSecondRunnable).isNull(); + + // A change to the display state is processed + Runnable stateOffRunnable = device.requestDisplayStateLocked( + Display.STATE_OFF, /* brightnessState= */ 1.0f, /* sdrBrightnessState= */ 1.0f, + /* displayOffloadSession= */ null); + assertThat(stateOffRunnable).isNotNull(); + stateOffRunnable.run(); + verify(mMockSufaceControlDisplayFactory) + .setDisplayPowerMode(displayToken, SurfaceControl.POWER_MODE_OFF); + verify(mMockCallback).onPaused(); + } + private IVirtualDisplayCallback createCallback() { return new IVirtualDisplayCallback.Stub() { |