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

Commit c5d66302 authored by Fiona Campbell's avatar Fiona Campbell Committed by Android (Google) Code Review
Browse files

Merge "Lock if device unplugged and sleeping" into main

parents 3a4eafdd 7aedd123
Loading
Loading
Loading
Loading
+35 −2
Original line number Diff line number Diff line
@@ -148,6 +148,7 @@ import com.android.server.power.batterysaver.BatterySaverPolicy;
import com.android.server.power.batterysaver.BatterySaverStateMachine;
import com.android.server.power.batterysaver.BatterySavingStats;
import com.android.server.power.feature.PowerManagerFlags;
import com.android.server.wm.WindowManagerInternal;

import dalvik.annotation.optimization.NeverCompile;

@@ -354,6 +355,8 @@ public final class PowerManagerService extends SystemService
    private SettingsObserver mSettingsObserver;
    private DreamManagerInternal mDreamManager;
    private LogicalLight mAttentionLight;
    @Nullable
    private WindowManagerInternal mWindowManagerInternal;

    private final InattentiveSleepWarningController mInattentiveSleepWarningOverlayController;
    private final AmbientDisplaySuppressionController mAmbientDisplaySuppressionController;
@@ -763,6 +766,34 @@ public final class PowerManagerService extends SystemService
        @Override
        public void onDisplayRemoved(int displayId) {
            mNotifier.clearScreenTimeoutPolicyListeners(displayId);

            if (com.android.server.display.feature.flags.Flags.separateTimeouts()
                    && !mFeatureFlags.isLockOnUnplugEnabled()) {
                return;
            }

            // if all the remaining devices are asleep, lock the default display.
            synchronized (mLock) {
                for (int i = 0; i < mPowerGroups.size(); i++) {
                    PowerGroup pg = mPowerGroups.valueAt(i);
                    // If a power group remains, that is an adjacent group
                    // and it is awake, then do not lock the device.
                    if (pg.isDefaultOrAdjacentGroup()
                            && pg.getWakefulnessLocked() == WAKEFULNESS_AWAKE) {
                        return;
                    }
                }
            }
            tryToLockNow();
        }

        private void tryToLockNow() {
            if (mWindowManagerInternal == null) {
                mWindowManagerInternal =  getLocalService(WindowManagerInternal.class);
            }
            if (mWindowManagerInternal != null) {
                mWindowManagerInternal.lockNow();
            }
        }

        @Override
@@ -771,7 +802,8 @@ public final class PowerManagerService extends SystemService
        }
    }

    private final class DisplayGroupPowerChangeListener implements
    @VisibleForTesting
    final class DisplayGroupPowerChangeListener implements
            DisplayManagerInternal.DisplayGroupListener {

        static final int DISPLAY_GROUP_ADDED = 0;
@@ -1376,6 +1408,7 @@ public final class PowerManagerService extends SystemService
            mPolicy = getLocalService(WindowManagerPolicy.class);
            mBatteryManagerInternal = getLocalService(BatteryManagerInternal.class);
            mDisplayManager = mContext.getSystemService(DisplayManager.class);
            mWindowManagerInternal =  getLocalService(WindowManagerInternal.class);
            mAttentionDetector.systemReady(mContext);

            SensorManager sensorManager = new SystemSensorManager(mContext, mHandler.getLooper());
+12 −0
Original line number Diff line number Diff line
@@ -60,6 +60,10 @@ public class PowerManagerFlags {
    private final FlagState mMoveWscLoggingToNotifier =
            new FlagState(Flags.FLAG_MOVE_WSC_LOGGING_TO_NOTIFIER, Flags::moveWscLoggingToNotifier);

    private final FlagState mLockOnUnplug =
            new FlagState(Flags.FLAG_LOCK_ON_UNPLUG,
                    Flags::lockOnUnplug);

    private final FlagState mWakelockAttributionViaWorkchain =
            new FlagState(Flags.FLAG_WAKELOCK_ATTRIBUTION_VIA_WORKCHAIN,
                    Flags::wakelockAttributionViaWorkchain);
@@ -126,6 +130,13 @@ public class PowerManagerFlags {
        return mWakelockAttributionViaWorkchain.isEnabled();
    }

    /**
     * @return Whether to lock when all remaining adjacent displays are asleep.
     */
    public boolean isLockOnUnplugEnabled() {
        return mLockOnUnplug.isEnabled();
    }

    /**
     * @return Whether the feature to disable the frozen process wakelocks is enabled
     */
@@ -165,6 +176,7 @@ public class PowerManagerFlags {
        pw.println(" " + mImproveWakelockLatency);
        pw.println(" " + mPerDisplayWakeByTouch);
        pw.println(" " + mMoveWscLoggingToNotifier);
        pw.println(" " + mLockOnUnplug);
        pw.println(" " + mWakelockAttributionViaWorkchain);
        pw.println(" " + mDisableFrozenProcessWakelocks);
        pw.println(" " + mForceDisableWakelocks);
+10 −0
Original line number Diff line number Diff line
@@ -49,6 +49,16 @@ flag {
    is_fixed_read_only: true
}

flag {
    name: "lock_on_unplug"
    namespace: "lse_desktop_experience"
    description: "Fixes device locking when all remaining external displays are unplugged"
    bug: "414558760"
    metadata {
      purpose: PURPOSE_BUGFIX
    }
}

flag {
    name: "policy_reason_in_display_power_request"
    namespace: "wear_frameworks"
+155 −1
Original line number Diff line number Diff line
@@ -38,6 +38,8 @@ import static android.service.dreams.Flags.FLAG_DREAMS_V2;

import static com.android.dx.mockito.inline.extended.ExtendedMockito.doThrow;
import static com.android.server.deviceidle.Flags.FLAG_DISABLE_WAKELOCKS_IN_LIGHT_IDLE;
import static com.android.server.display.feature.flags.Flags.FLAG_SEPARATE_TIMEOUTS;
import static com.android.server.power.PowerManagerService.DisplayGroupPowerChangeListener.DISPLAY_GROUP_REMOVED;

import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
@@ -142,6 +144,7 @@ import com.android.server.power.batterysaver.BatterySaverStateMachine;
import com.android.server.power.feature.PowerManagerFlags;
import com.android.server.power.feature.flags.Flags;
import com.android.server.testutils.OffsettableClock;
import com.android.server.wm.WindowManagerInternal;

import com.google.testing.junit.testparameterinjector.TestParameter;
import com.google.testing.junit.testparameterinjector.TestParameterInjector;
@@ -212,6 +215,7 @@ public class PowerManagerServiceTest {
    @Mock private PowerManagerService.PowerPropertiesWrapper mPowerPropertiesWrapper;
    @Mock private DeviceStateManager mDeviceStateManagerMock;
    @Mock private DeviceConfigParameterProvider mDeviceParameterProvider;
    @Mock private WindowManagerInternal mWindowManagerInternalMock;

    @Captor private ArgumentCaptor<DisplayManager.DisplayListener> mDisplayListenerArgumentCaptor;

@@ -283,6 +287,7 @@ public class PowerManagerServiceTest {
        addLocalServiceMock(AttentionManagerInternal.class, mAttentionManagerInternalMock);
        addLocalServiceMock(DreamManagerInternal.class, mDreamManagerInternalMock);
        addLocalServiceMock(VirtualDeviceManagerInternal.class, mVirtualDeviceManagerInternalMock);
        addLocalServiceMock(WindowManagerInternal.class, mWindowManagerInternalMock);

        mContextSpy = spy(new ContextWrapper(ApplicationProvider.getApplicationContext()));
        mResourcesSpy = spy(mContextSpy.getResources());
@@ -1185,7 +1190,7 @@ public class PowerManagerServiceTest {
    }

    @Test
    @RequiresFlagsEnabled(com.android.server.display.feature.flags.Flags.FLAG_SEPARATE_TIMEOUTS)
    @RequiresFlagsEnabled({com.android.server.display.feature.flags.Flags.FLAG_SEPARATE_TIMEOUTS})
    public void testDisplaySleepsIfOtherActiveGroupPresent() {
        doAnswer(inv -> {
            when(mDreamManagerInternalMock.isDreaming()).thenReturn(true);
@@ -4540,6 +4545,155 @@ public class PowerManagerServiceTest {
        verify(mNativeWrapperMock, never()).nativeSetAutoSuspend(true);
    }

    @RequiresFlagsEnabled({FLAG_SEPARATE_TIMEOUTS,
            com.android.server.power.feature.flags.Flags.FLAG_LOCK_ON_UNPLUG})
    @Test
    public void testLocksWhenAdjacentSleepingAndUnplug_shouldLock() {
        final int nonDefaultPowerGroupId = Display.DEFAULT_DISPLAY_GROUP + 1;
        when(mDisplayManagerInternalMock.getDisplayGroupFlags(nonDefaultPowerGroupId))
                .thenReturn(DisplayGroup.FLAG_DEFAULT_GROUP_ADJACENT);
        PowerGroup pg2 = new PowerGroup(/* groupId= */ nonDefaultPowerGroupId,
                /* wakefulnessListener= */ null, mNotifierMock,
                mDisplayManagerInternalMock, WAKEFULNESS_ASLEEP, /* ready= */ true,
                /* supportsSandman= */ true,
                /* eventTime= */ mClock.now(), /* featureFlags= */ null,
                /* isDefaultGroupAdjacent= */ true);
        int displayInNonDefaultGroup = 1;
        final AtomicReference<DisplayManagerInternal.DisplayGroupListener> listener =
                new AtomicReference<>();
        long eventTime1 = 10;
        doAnswer((Answer<Void>) invocation -> {
            listener.set(invocation.getArgument(0));
            return null;
        }).when(mDisplayManagerInternalMock).registerDisplayGroupListener(any());

        createService();
        startSystem();
        listener.get().onDisplayGroupAdded(nonDefaultPowerGroupId);
        verify(mDisplayManagerMock).registerDisplayListener(
                mDisplayListenerArgumentCaptor.capture(), any());

        // Verify the global wakefulness is AWAKE
        assertThat(mService.getGlobalWakefulnessLocked()).isEqualTo(WAKEFULNESS_AWAKE);

        // Transition default display to doze, and verify the global wakefulness
        mService.setWakefulnessLocked(Display.DEFAULT_DISPLAY_GROUP, WAKEFULNESS_DOZING,
                eventTime1, 0, PowerManager.GO_TO_SLEEP_REASON_INATTENTIVE, 0, null, null);
        assertThat(mService.getGlobalWakefulnessLocked()).isEqualTo(WAKEFULNESS_AWAKE);

        mService.onPowerGroupEventLocked(DISPLAY_GROUP_REMOVED, pg2);
        mDisplayListenerArgumentCaptor.getValue().onDisplayRemoved(displayInNonDefaultGroup);
        mDisplayListenerArgumentCaptor.getValue().onDisplayDisconnected(displayInNonDefaultGroup);

        advanceTime(500);
        verify(mNotifierMock).clearScreenTimeoutPolicyListeners(displayInNonDefaultGroup);
        verify(mWindowManagerInternalMock).lockNow();
    }

    @RequiresFlagsEnabled({FLAG_SEPARATE_TIMEOUTS,
            com.android.server.power.feature.flags.Flags.FLAG_LOCK_ON_UNPLUG})
    @Test
    public void testLocksWhenAwakeAndUnplug_shouldntLock() {
        final int nonDefaultPowerGroupId = Display.DEFAULT_DISPLAY_GROUP + 1;
        PowerGroup pg2 = new PowerGroup(/* groupId= */ nonDefaultPowerGroupId,
                /* wakefulnessListener= */ null, mNotifierMock,
                mDisplayManagerInternalMock, WAKEFULNESS_AWAKE, /* ready= */ true,
                /* supportsSandman= */ true,
                /* eventTime= */ mClock.now(), /* featureFlags= */ null,
                /* isDefaultGroupAdjacent= */ false);
        int displayInNonDefaultGroup = 1;
        final AtomicReference<DisplayManagerInternal.DisplayGroupListener> listener =
                new AtomicReference<>();
        long eventTime1 = 10;
        doAnswer((Answer<Void>) invocation -> {
            listener.set(invocation.getArgument(0));
            return null;
        }).when(mDisplayManagerInternalMock).registerDisplayGroupListener(any());

        createService();
        startSystem();
        listener.get().onDisplayGroupAdded(nonDefaultPowerGroupId);
        verify(mDisplayManagerMock).registerDisplayListener(
                mDisplayListenerArgumentCaptor.capture(), any());

        // Verify the global wakefulness is AWAKE
        assertThat(mService.getGlobalWakefulnessLocked()).isEqualTo(WAKEFULNESS_AWAKE);

        // Transition default display to awake, and verify the global wakefulness
        mService.setWakefulnessLocked(Display.DEFAULT_DISPLAY_GROUP, WAKEFULNESS_AWAKE, eventTime1,
                0, PowerManager.GO_TO_SLEEP_REASON_INATTENTIVE, 0, null, null);
        assertThat(mService.getGlobalWakefulnessLocked()).isEqualTo(WAKEFULNESS_AWAKE);

        mService.onPowerGroupEventLocked(DISPLAY_GROUP_REMOVED, pg2);
        mDisplayListenerArgumentCaptor.getValue().onDisplayRemoved(displayInNonDefaultGroup);
        mDisplayListenerArgumentCaptor.getValue().onDisplayDisconnected(displayInNonDefaultGroup);

        advanceTime(500);
        verify(mNotifierMock).clearScreenTimeoutPolicyListeners(displayInNonDefaultGroup);
        verify(mWindowManagerInternalMock, never()).lockNow();
    }


    // Default adjacent groups that are awake, should prevent the device from locking.
    @RequiresFlagsEnabled({FLAG_SEPARATE_TIMEOUTS,
            com.android.server.power.feature.flags.Flags.FLAG_LOCK_ON_UNPLUG})
    @Test
    public void testLocksWhenSleepingAndUnplug_butTheresAnAwakeAdjacentGroup_shouldntLock() {
        final int defaultGroupAdjacentPowerGroupId = Display.DEFAULT_DISPLAY_GROUP + 1;
        final int nonDefaultPowerGroupId = defaultGroupAdjacentPowerGroupId + 1;
        when(mDisplayManagerInternalMock.getDisplayGroupFlags(defaultGroupAdjacentPowerGroupId))
                .thenReturn(DisplayGroup.FLAG_DEFAULT_GROUP_ADJACENT);
        PowerGroup pg2 = new PowerGroup(/* groupId= */ defaultGroupAdjacentPowerGroupId,
                /* wakefulnessListener= */ null, mNotifierMock,
                mDisplayManagerInternalMock, WAKEFULNESS_AWAKE, /* ready= */ true,
                /* supportsSandman= */ true,
                /* eventTime= */ mClock.now(), /* featureFlags= */ null,
                /* isDefaultGroupAdjacent= */ true);
        PowerGroup pg3 = new PowerGroup(/* groupId= */ nonDefaultPowerGroupId,
                /* wakefulnessListener= */ null, mNotifierMock,
                mDisplayManagerInternalMock, WAKEFULNESS_AWAKE, /* ready= */ true,
                /* supportsSandman= */ true,
                /* eventTime= */ mClock.now(), /* featureFlags= */ null,
                /* isDefaultGroupAdjacent= */ false);
        int displayInAdjacentDefaultGroup = 1;
        final AtomicReference<DisplayManagerInternal.DisplayGroupListener> listener =
                new AtomicReference<>();
        long eventTime1 = 10;
        doAnswer((Answer<Void>) invocation -> {
            listener.set(invocation.getArgument(0));
            return null;
        }).when(mDisplayManagerInternalMock).registerDisplayGroupListener(any());

        createService();
        startSystem();
        listener.get().onDisplayGroupAdded(defaultGroupAdjacentPowerGroupId);
        listener.get().onDisplayGroupAdded(nonDefaultPowerGroupId);
        verify(mDisplayManagerMock).registerDisplayListener(
                mDisplayListenerArgumentCaptor.capture(), any());

        // Verify the global wakefulness is AWAKE
        assertThat(mService.getGlobalWakefulnessLocked()).isEqualTo(WAKEFULNESS_AWAKE);

        // Transition default display to doze
        // Transition adjacent display to awake, and verify the global wakefulness
        mService.setWakefulnessLocked(Display.DEFAULT_DISPLAY_GROUP, WAKEFULNESS_DOZING,
                eventTime1, 0, PowerManager.GO_TO_SLEEP_REASON_INATTENTIVE, 0, null, null);
        mService.setWakefulnessLocked(defaultGroupAdjacentPowerGroupId, WAKEFULNESS_AWAKE,
                eventTime1, 0, PowerManager.GO_TO_SLEEP_REASON_INATTENTIVE, 0, null, null);

        assertThat(mService.getGlobalWakefulnessLocked()).isEqualTo(WAKEFULNESS_AWAKE);

        mService.onPowerGroupEventLocked(DISPLAY_GROUP_REMOVED, pg3);
        mDisplayListenerArgumentCaptor.getValue()
                .onDisplayRemoved(displayInAdjacentDefaultGroup);
        mDisplayListenerArgumentCaptor.getValue()
                .onDisplayDisconnected(displayInAdjacentDefaultGroup);

        advanceTime(500);
        verify(mNotifierMock).clearScreenTimeoutPolicyListeners(displayInAdjacentDefaultGroup);
        verify(mWindowManagerInternalMock, never()).lockNow();
    }

    private void setCachedUidProcState(int uid) {
        mService.updateUidProcStateInternal(uid, PROCESS_STATE_TOP_SLEEPING);
    }