Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 77e8b04b authored by Vladimir Komsiyski's avatar Vladimir Komsiyski
Browse files

Per-power group sleep and dim timeout

 - VDM API to set custom sleep and dim timeout
 - Only for trusted non-mirror displays
 - Single timeout per device, all VDM displays that are NOT in the
   default display group are trusted and non-mirror. Display groups
   cannot be shared across devices.
 - PowerGroup gets these from VDM - the power group creation is async
   and with the display creation in VDM, so ensure that the display is
   created entirely in the locked section, so the power group query
   will always succeed.
 - We can finally drop the WakeLock from VDM and rely on the infinite
   sleep timeout (which needs to be the default, unfortunately)

Fix: 365188956
Test: new CTS in topic
Test: presubmit
Test: atest PowerServiceTests
Flag: android.companion.virtualdevice.flags.device_aware_display_power

Change-Id: Ieb32e8a6fd9660080eec998430b0e20d21017464
parent 3f72bb5d
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -3558,10 +3558,12 @@ package android.companion.virtual {
    method @Deprecated public int getDefaultActivityPolicy();
    method @Deprecated public int getDefaultNavigationPolicy();
    method public int getDevicePolicy(int);
    method @FlaggedApi("android.companion.virtualdevice.flags.device_aware_display_power") @NonNull public java.time.Duration getDimDuration();
    method @FlaggedApi("android.companion.virtual.flags.vdm_custom_home") @Nullable public android.content.ComponentName getHomeComponent();
    method @FlaggedApi("android.companion.virtual.flags.vdm_custom_ime") @Nullable public android.content.ComponentName getInputMethodComponent();
    method public int getLockState();
    method @Nullable public String getName();
    method @FlaggedApi("android.companion.virtualdevice.flags.device_aware_display_power") @NonNull public java.time.Duration getScreenOffTimeout();
    method @NonNull public java.util.Set<android.os.UserHandle> getUsersWithMatchingAccounts();
    method @NonNull public java.util.List<android.companion.virtual.sensor.VirtualSensorConfig> getVirtualSensorConfigs();
    method public void writeToParcel(@NonNull android.os.Parcel, int);
@@ -3594,10 +3596,12 @@ package android.companion.virtual {
    method @Deprecated @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setBlockedActivities(@NonNull java.util.Set<android.content.ComponentName>);
    method @Deprecated @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setBlockedCrossTaskNavigations(@NonNull java.util.Set<android.content.ComponentName>);
    method @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setDevicePolicy(int, int);
    method @FlaggedApi("android.companion.virtualdevice.flags.device_aware_display_power") @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setDimDuration(@NonNull java.time.Duration);
    method @FlaggedApi("android.companion.virtual.flags.vdm_custom_home") @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setHomeComponent(@Nullable android.content.ComponentName);
    method @FlaggedApi("android.companion.virtual.flags.vdm_custom_ime") @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setInputMethodComponent(@Nullable android.content.ComponentName);
    method @NonNull @RequiresPermission(value=android.Manifest.permission.ADD_ALWAYS_UNLOCKED_DISPLAY, conditional=true) public android.companion.virtual.VirtualDeviceParams.Builder setLockState(int);
    method @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setName(@NonNull String);
    method @FlaggedApi("android.companion.virtualdevice.flags.device_aware_display_power") @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setScreenOffTimeout(@NonNull java.time.Duration);
    method @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setUsersWithMatchingAccounts(@NonNull java.util.Set<android.os.UserHandle>);
    method @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setVirtualSensorCallback(@NonNull java.util.concurrent.Executor, @NonNull android.companion.virtual.sensor.VirtualSensorCallback);
    method @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setVirtualSensorDirectChannelCallback(@NonNull java.util.concurrent.Executor, @NonNull android.companion.virtual.sensor.VirtualSensorDirectChannelCallback);
+105 −4
Original line number Diff line number Diff line
@@ -318,6 +318,8 @@ public final class VirtualDeviceParams implements Parcelable {
    @Nullable private final IVirtualSensorCallback mVirtualSensorCallback;
    private final int mAudioPlaybackSessionId;
    private final int mAudioRecordingSessionId;
    private final long mDimDuration;
    private final long mScreenOffTimeout;

    private VirtualDeviceParams(
            @LockState int lockState,
@@ -333,7 +335,9 @@ public final class VirtualDeviceParams implements Parcelable {
            @NonNull List<VirtualSensorConfig> virtualSensorConfigs,
            @Nullable IVirtualSensorCallback virtualSensorCallback,
            int audioPlaybackSessionId,
            int audioRecordingSessionId) {
            int audioRecordingSessionId,
            long dimDuration,
            long screenOffTimeout) {
        mLockState = lockState;
        mUsersWithMatchingAccounts =
                new ArraySet<>(Objects.requireNonNull(usersWithMatchingAccounts));
@@ -351,6 +355,8 @@ public final class VirtualDeviceParams implements Parcelable {
        mVirtualSensorCallback = virtualSensorCallback;
        mAudioPlaybackSessionId = audioPlaybackSessionId;
        mAudioRecordingSessionId = audioRecordingSessionId;
        mDimDuration = dimDuration;
        mScreenOffTimeout = screenOffTimeout;
    }

    @SuppressWarnings("unchecked")
@@ -371,6 +377,8 @@ public final class VirtualDeviceParams implements Parcelable {
        mAudioRecordingSessionId = parcel.readInt();
        mHomeComponent = parcel.readTypedObject(ComponentName.CREATOR);
        mInputMethodComponent = parcel.readTypedObject(ComponentName.CREATOR);
        mDimDuration = parcel.readLong();
        mScreenOffTimeout = parcel.readLong();
    }

    /**
@@ -381,6 +389,26 @@ public final class VirtualDeviceParams implements Parcelable {
        return mLockState;
    }

    /**
     * Returns the dim duration for the displays of this device.
     *
     * @see Builder#setDimDuration(Duration)
     */
    @FlaggedApi(android.companion.virtualdevice.flags.Flags.FLAG_DEVICE_AWARE_DISPLAY_POWER)
    public @NonNull Duration getDimDuration() {
        return Duration.ofMillis(mDimDuration);
    }

    /**
     * Returns the screen off timeout of the displays of this device.
     *
     * @see Builder#setDimDuration(Duration)
     */
    @FlaggedApi(android.companion.virtualdevice.flags.Flags.FLAG_DEVICE_AWARE_DISPLAY_POWER)
    public @NonNull Duration getScreenOffTimeout() {
        return Duration.ofMillis(mScreenOffTimeout);
    }

    /**
     * Returns the custom component used as home on all displays owned by this virtual device that
     * support home activities.
@@ -604,6 +632,8 @@ public final class VirtualDeviceParams implements Parcelable {
        dest.writeInt(mAudioRecordingSessionId);
        dest.writeTypedObject(mHomeComponent, flags);
        dest.writeTypedObject(mInputMethodComponent, flags);
        dest.writeLong(mDimDuration);
        dest.writeLong(mScreenOffTimeout);
    }

    @Override
@@ -638,7 +668,9 @@ public final class VirtualDeviceParams implements Parcelable {
                && Objects.equals(mHomeComponent, that.mHomeComponent)
                && Objects.equals(mInputMethodComponent, that.mInputMethodComponent)
                && mAudioPlaybackSessionId == that.mAudioPlaybackSessionId
                && mAudioRecordingSessionId == that.mAudioRecordingSessionId;
                && mAudioRecordingSessionId == that.mAudioRecordingSessionId
                && mDimDuration == that.mDimDuration
                && mScreenOffTimeout == that.mScreenOffTimeout;
    }

    @Override
@@ -647,7 +679,7 @@ public final class VirtualDeviceParams implements Parcelable {
                mLockState, mUsersWithMatchingAccounts, mCrossTaskNavigationExemptions,
                mDefaultNavigationPolicy, mActivityPolicyExemptions, mDefaultActivityPolicy, mName,
                mDevicePolicies, mHomeComponent, mInputMethodComponent, mAudioPlaybackSessionId,
                mAudioRecordingSessionId);
                mAudioRecordingSessionId, mDimDuration, mScreenOffTimeout);
        for (int i = 0; i < mDevicePolicies.size(); i++) {
            hashCode = 31 * hashCode + mDevicePolicies.keyAt(i);
            hashCode = 31 * hashCode + mDevicePolicies.valueAt(i);
@@ -671,6 +703,8 @@ public final class VirtualDeviceParams implements Parcelable {
                + " mInputMethodComponent=" + mInputMethodComponent
                + " mAudioPlaybackSessionId=" + mAudioPlaybackSessionId
                + " mAudioRecordingSessionId=" + mAudioRecordingSessionId
                + " mDimDuration=" + mDimDuration
                + " mScreenOffTimeout=" + mScreenOffTimeout
                + ")";
    }

@@ -692,6 +726,8 @@ public final class VirtualDeviceParams implements Parcelable {
        pw.println(prefix + "mInputMethodComponent=" + mInputMethodComponent);
        pw.println(prefix + "mAudioPlaybackSessionId=" + mAudioPlaybackSessionId);
        pw.println(prefix + "mAudioRecordingSessionId=" + mAudioRecordingSessionId);
        pw.println(prefix + "mDimDuration=" + mDimDuration);
        pw.println(prefix + "mScreenOffTimeout=" + mScreenOffTimeout);
    }

    @NonNull
@@ -711,6 +747,8 @@ public final class VirtualDeviceParams implements Parcelable {
     */
    public static final class Builder {

        private static final Duration INFINITE_TIMEOUT = Duration.ofDays(365 * 1000);

        private @LockState int mLockState = LOCK_STATE_DEFAULT;
        @NonNull private Set<UserHandle> mUsersWithMatchingAccounts = Collections.emptySet();
        @NonNull private Set<ComponentName> mCrossTaskNavigationExemptions = Collections.emptySet();
@@ -733,6 +771,8 @@ public final class VirtualDeviceParams implements Parcelable {
        @Nullable private VirtualSensorDirectChannelCallback mVirtualSensorDirectChannelCallback;
        @Nullable private ComponentName mHomeComponent;
        @Nullable private ComponentName mInputMethodComponent;
        private Duration mDimDuration = Duration.ZERO;
        private Duration mScreenOffTimeout = Duration.ZERO;

        private static class VirtualSensorCallbackDelegate extends IVirtualSensorCallback.Stub {
            @NonNull
@@ -809,6 +849,57 @@ public final class VirtualDeviceParams implements Parcelable {
            return this;
        }

        /**
         * Sets the dim duration for all trusted non-mirror displays of the device.
         *
         * <p>The system will reduce the display brightness for the specified duration if there
         * has been no interaction just before the displays turn off.</p>
         *
         * <p>If set, the screen off timeout must also be set to a value larger than the dim
         * duration. If left unset or set to zero, then the display brightness will not be reduced.
         * </p>
         *
         * @throws IllegalArgumentException if the dim duration is negative or if the dim duration
         *   is longer than the screen off timeout.
         * @see android.hardware.display.DisplayManager#VIRTUAL_DISPLAY_FLAG_TRUSTED
         * @see android.hardware.display.DisplayManager#VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR
         * @see #setScreenOffTimeout
         */
        @FlaggedApi(android.companion.virtualdevice.flags.Flags.FLAG_DEVICE_AWARE_DISPLAY_POWER)
        @NonNull
        public Builder setDimDuration(@NonNull Duration dimDuration) {
            if (Objects.requireNonNull(dimDuration).compareTo(Duration.ZERO) < 0) {
                throw new IllegalArgumentException("The dim duration cannot be negative");
            }
            mDimDuration = dimDuration;
            return this;
        }

        /**
         * Sets the timeout, after which all trusted non-mirror displays of the device will turn
         * off, if there has been no interaction with the device.
         *
         * <p>If dim duration is set, the screen off timeout must be set to a value larger than the
         * dim duration. If left unset or set to zero, then the displays will never be turned off
         * due to inactivity.</p>
         *
         * @throws IllegalArgumentException if the screen off timeout is negative or if the dim
         *   duration is longer than the screen off timeout.
         * @see android.hardware.display.DisplayManager#VIRTUAL_DISPLAY_FLAG_TRUSTED
         * @see android.hardware.display.DisplayManager#VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR
         * @see #setDimDuration
         * @see VirtualDeviceManager.VirtualDevice#goToSleep()
         */
        @FlaggedApi(android.companion.virtualdevice.flags.Flags.FLAG_DEVICE_AWARE_DISPLAY_POWER)
        @NonNull
        public Builder setScreenOffTimeout(@NonNull Duration screenOffTimeout) {
            if (Objects.requireNonNull(screenOffTimeout).compareTo(Duration.ZERO) < 0) {
                throw new IllegalArgumentException("The screen off timeout cannot be negative");
            }
            mScreenOffTimeout = screenOffTimeout;
            return this;
        }

        /**
         * Specifies a component to be used as home on all displays owned by this virtual device
         * that support home activities.
@@ -1205,6 +1296,14 @@ public final class VirtualDeviceParams implements Parcelable {
                }
            }

            if (mDimDuration.compareTo(mScreenOffTimeout) > 0) {
                throw new IllegalArgumentException(
                        "The dim duration cannot be greater than the screen off timeout.");
            }
            if (mScreenOffTimeout.compareTo(Duration.ZERO) == 0) {
                mScreenOffTimeout = INFINITE_TIMEOUT;
            }

            if (!Flags.crossDeviceClipboard()) {
                mDevicePolicies.delete(POLICY_TYPE_CLIPBOARD);
            }
@@ -1250,7 +1349,9 @@ public final class VirtualDeviceParams implements Parcelable {
                    mVirtualSensorConfigs,
                    virtualSensorCallbackDelegate,
                    mAudioPlaybackSessionId,
                    mAudioRecordingSessionId);
                    mAudioRecordingSessionId,
                    mDimDuration.toMillis(),
                    mScreenOffTimeout.toMillis());
        }
    }
}
+28 −17
Original line number Diff line number Diff line
@@ -1465,28 +1465,28 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
    public int createVirtualDisplay(@NonNull VirtualDisplayConfig virtualDisplayConfig,
            @NonNull IVirtualDisplayCallback callback) {
        checkCallerIsDeviceOwner();

        int displayId;
        boolean showPointer;
        boolean isTrustedDisplay;
        GenericWindowPolicyController gwpc;
        synchronized (mVirtualDeviceLock) {
            gwpc = createWindowPolicyControllerLocked(virtualDisplayConfig.getDisplayCategories());
        }
        int displayId;
        displayId = mDisplayManagerInternal.createVirtualDisplay(virtualDisplayConfig, callback,
                this, gwpc, mOwnerPackageName);
            displayId = mDisplayManagerInternal.createVirtualDisplay(virtualDisplayConfig,
                    callback, this, gwpc, mOwnerPackageName);
            boolean isMirrorDisplay =
                mDisplayManagerInternal.getDisplayIdToMirror(displayId) != Display.INVALID_DISPLAY;
                    mDisplayManagerInternal.getDisplayIdToMirror(displayId)
                            != Display.INVALID_DISPLAY;
            gwpc.setDisplayId(displayId, isMirrorDisplay);
        boolean isTrustedDisplay =
            isTrustedDisplay =
                    (mDisplayManagerInternal.getDisplayInfo(displayId).flags & Display.FLAG_TRUSTED)
                            == Display.FLAG_TRUSTED;
        if (!isTrustedDisplay) {
            if (getDevicePolicy(POLICY_TYPE_CLIPBOARD) != DEVICE_POLICY_DEFAULT) {
                throw new SecurityException("All displays must be trusted for devices with custom"
                        + "clipboard policy.");
            }
            if (!isTrustedDisplay
                    && getDevicePolicy(POLICY_TYPE_CLIPBOARD) != DEVICE_POLICY_DEFAULT) {
                throw new SecurityException("All displays must be trusted for devices with "
                        + "custom clipboard policy.");
            }

        boolean showPointer;
        synchronized (mVirtualDeviceLock) {
            if (mVirtualDisplays.contains(displayId)) {
                gwpc.unregisterRunningAppsChangedListener(this);
                throw new IllegalStateException(
@@ -1523,6 +1523,9 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
    }

    private PowerManager.WakeLock createAndAcquireWakeLockForDisplay(int displayId) {
        if (android.companion.virtualdevice.flags.Flags.deviceAwareDisplayPower()) {
            return null;
        }
        final long token = Binder.clearCallingIdentity();
        try {
            PowerManager powerManager = mContext.getSystemService(PowerManager.class);
@@ -1681,6 +1684,14 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
        return mOwnerUid;
    }

    long getDimDurationMillis() {
        return mParams.getDimDuration().toMillis();
    }

    long getScreenOffTimeoutMillis() {
        return mParams.getScreenOffTimeout().toMillis();
    }

    @Override  // Binder call
    public int[] getDisplayIds() {
        synchronized (mVirtualDeviceLock) {
+16 −1
Original line number Diff line number Diff line
@@ -576,7 +576,6 @@ public class VirtualDeviceManagerService extends SystemService {
            }
        }


        @Override // Binder call
        public int getDeviceIdForDisplayId(int displayId) {
            if (displayId == Display.INVALID_DISPLAY || displayId == Display.DEFAULT_DISPLAY) {
@@ -910,6 +909,22 @@ public class VirtualDeviceManagerService extends SystemService {
            return mImpl.getDeviceIdForDisplayId(displayId);
        }

        @Override
        public long getDimDurationMillisForDeviceId(int deviceId) {
            synchronized (mVirtualDeviceManagerLock) {
                VirtualDeviceImpl virtualDevice = mVirtualDevices.get(deviceId);
                return virtualDevice == null ? -1 : virtualDevice.getDimDurationMillis();
            }
        }

        @Override
        public long getScreenOffTimeoutMillisForDeviceId(int deviceId) {
            synchronized (mVirtualDeviceManagerLock) {
                VirtualDeviceImpl virtualDevice = mVirtualDevices.get(deviceId);
                return virtualDevice == null ? -1 : virtualDevice.getScreenOffTimeoutMillis();
            }
        }

        @Override
        public boolean isValidVirtualDeviceId(int deviceId) {
            return mImpl.isValidVirtualDeviceId(deviceId);
+6 −0
Original line number Diff line number Diff line
@@ -167,6 +167,12 @@ public abstract class VirtualDeviceManagerInternal {
     */
    public abstract int getDeviceIdForDisplayId(int displayId);

    /** Returns the dim duration for the displays of the device with the given ID. */
    public abstract long getDimDurationMillisForDeviceId(int deviceId);

    /** Returns the screen off timeout of the displays of the device with the given ID. */
    public abstract long getScreenOffTimeoutMillisForDeviceId(int deviceId);

    /**
     * Gets the persistent ID for the VirtualDevice with the given device ID.
     *
Loading