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

Commit 68ed66dc authored by Lucas Silva's avatar Lucas Silva
Browse files

Allow dark mode to turn on/off automatically while dreaming.

Currently dark mode schedules will wait until the screen turns off prior
to switching between light/dark themes. This avoids interrupting the
user while they are using the device. However, on devices where
screensavers are prevalent, the screen may never turn off and therefore
we will never switch between modes. This change ensures that the switch
can occur while the device is dreaming.

Fixes: 306272618
Test: atest UiModeManagerServiceTest
Change-Id: Ide068813075fc410fc6c6b4531cb09b7d6e11a84
parent 9cbaf674
Loading
Loading
Loading
Loading
+28 −23
Original line number Diff line number Diff line
@@ -80,6 +80,7 @@ import android.os.SystemProperties;
import android.os.UserHandle;
import android.provider.Settings;
import android.provider.Settings.Secure;
import android.service.dreams.DreamManagerInternal;
import android.service.dreams.Sandman;
import android.service.vr.IVrManager;
import android.service.vr.IVrStateCallbacks;
@@ -142,10 +143,10 @@ final class UiModeManagerService extends SystemService {
    private boolean mCarModeEnabled = false;
    private boolean mCharging = false;
    private boolean mPowerSave = false;
    // Do not change configuration now. wait until screen turns off.
    // Do not change configuration now. wait until the device is inactive (eg screen off, dreaming)
    // This prevents jank and activity restart when the user
    // is actively using the device
    private boolean mWaitForScreenOff = false;
    private boolean mWaitForDeviceInactive = false;
    private int mDefaultUiModeType;
    private boolean mCarModeKeepsScreenOn;
    private boolean mDeskModeKeepsScreenOn;
@@ -199,6 +200,7 @@ final class UiModeManagerService extends SystemService {

    private final LocalService mLocalService = new LocalService();
    private PowerManagerInternal mLocalPowerManager;
    private DreamManagerInternal mDreamManagerInternal;

    private final IUiModeManager.Stub mService;

@@ -295,7 +297,7 @@ final class UiModeManagerService extends SystemService {
                    if (shouldApplyAutomaticChangesImmediately()) {
                        updateLocked(0, 0);
                    } else {
                        registerScreenOffEventLocked();
                        registerDeviceInactiveListenerLocked();
                    }
                }
            }
@@ -306,12 +308,12 @@ final class UiModeManagerService extends SystemService {
     * DO NOT USE DIRECTLY
     * see register registerScreenOffEvent and unregisterScreenOffEvent
     */
    private final BroadcastReceiver mOnScreenOffHandler = new BroadcastReceiver() {
    private final BroadcastReceiver mDeviceInactiveListener = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            synchronized (mLock) {
                // must unregister first before updating
                unregisterScreenOffEventLocked();
                unregisterDeviceInactiveListenerLocked();
                updateLocked(0, 0);
            }
        }
@@ -432,6 +434,7 @@ final class UiModeManagerService extends SystemService {
                if (twilightManager != null) mTwilightManager = twilightManager;
                mLocalPowerManager =
                        LocalServices.getService(PowerManagerInternal.class);
                mDreamManagerInternal = LocalServices.getService(DreamManagerInternal.class);
                initPowerSave();
                mCarModeEnabled = mDockState == Intent.EXTRA_DOCK_STATE_CAR;
                registerVrStateListener();
@@ -582,7 +585,7 @@ final class UiModeManagerService extends SystemService {
        if (shouldApplyAutomaticChangesImmediately()) {
            updateLocked(0, 0);
        } else {
            registerScreenOffEventLocked();
            registerDeviceInactiveListenerLocked();
        }
        scheduleNextCustomTimeListener();
    }
@@ -631,22 +634,23 @@ final class UiModeManagerService extends SystemService {
        return LocalTime.ofNanoOfDay(t * 1000);
    }

    private void registerScreenOffEventLocked() {
    private void registerDeviceInactiveListenerLocked() {
        if (mPowerSave) return;
        mWaitForScreenOff = true;
        mWaitForDeviceInactive = true;
        final IntentFilter intentFilter =
                new IntentFilter(Intent.ACTION_SCREEN_OFF);
        getContext().registerReceiver(mOnScreenOffHandler, intentFilter);
        intentFilter.addAction(Intent.ACTION_DREAMING_STARTED);
        getContext().registerReceiver(mDeviceInactiveListener, intentFilter);
    }

    private void cancelCustomAlarm() {
        mAlarmManager.cancel(mCustomTimeListener);
    }

    private void unregisterScreenOffEventLocked() {
        mWaitForScreenOff = false;
    private void unregisterDeviceInactiveListenerLocked() {
        mWaitForDeviceInactive = false;
        try {
            getContext().unregisterReceiver(mOnScreenOffHandler);
            getContext().unregisterReceiver(mDeviceInactiveListener);
        } catch (IllegalArgumentException e) {
            // we ignore this exception if the receiver is unregistered already.
        }
@@ -828,7 +832,7 @@ final class UiModeManagerService extends SystemService {
                synchronized (mLock) {
                    if (mNightMode != mode || mNightModeCustomType != customModeType) {
                        if (mNightMode == MODE_NIGHT_AUTO || mNightMode == MODE_NIGHT_CUSTOM) {
                            unregisterScreenOffEventLocked();
                            unregisterDeviceInactiveListenerLocked();
                            cancelCustomAlarm();
                        }
                        mNightModeCustomType = mode == MODE_NIGHT_CUSTOM
@@ -840,10 +844,10 @@ final class UiModeManagerService extends SystemService {
                        // on screen off will update configuration instead
                        if ((mNightMode != MODE_NIGHT_AUTO && mNightMode != MODE_NIGHT_CUSTOM)
                                || shouldApplyAutomaticChangesImmediately()) {
                            unregisterScreenOffEventLocked();
                            unregisterDeviceInactiveListenerLocked();
                            updateLocked(0, 0);
                        } else {
                            registerScreenOffEventLocked();
                            registerDeviceInactiveListenerLocked();
                        }
                    }
                }
@@ -967,7 +971,7 @@ final class UiModeManagerService extends SystemService {
                final long ident = Binder.clearCallingIdentity();
                try {
                    if (mNightMode == MODE_NIGHT_AUTO || mNightMode == MODE_NIGHT_CUSTOM) {
                        unregisterScreenOffEventLocked();
                        unregisterDeviceInactiveListenerLocked();
                        mOverrideNightModeOff = !active;
                        mOverrideNightModeOn = active;
                        mOverrideNightModeUser = mCurrentUser;
@@ -1011,7 +1015,7 @@ final class UiModeManagerService extends SystemService {
                persistNightMode(user);
                onCustomTimeUpdated(user);
            } catch (DateTimeException e) {
                unregisterScreenOffEventLocked();
                unregisterDeviceInactiveListenerLocked();
            } finally {
                Binder.restoreCallingIdentity(ident);
            }
@@ -1038,7 +1042,7 @@ final class UiModeManagerService extends SystemService {
                mCustomAutoNightModeEndMilliseconds = newTime;
                onCustomTimeUpdated(user);
            } catch (DateTimeException e) {
                unregisterScreenOffEventLocked();
                unregisterDeviceInactiveListenerLocked();
            } finally {
                Binder.restoreCallingIdentity(ident);
            }
@@ -1383,10 +1387,10 @@ final class UiModeManagerService extends SystemService {
        persistNightMode(user);
        if (mNightMode != MODE_NIGHT_CUSTOM) return;
        if (shouldApplyAutomaticChangesImmediately()) {
            unregisterScreenOffEventLocked();
            unregisterDeviceInactiveListenerLocked();
            updateLocked(0, 0);
        } else {
            registerScreenOffEventLocked();
            registerDeviceInactiveListenerLocked();
        }
    }

@@ -1414,7 +1418,7 @@ final class UiModeManagerService extends SystemService {
                pw.print(" ");
            }
            pw.println("");
            pw.print(" waitScreenOff="); pw.print(mWaitForScreenOff);
            pw.print(" mWaitForDeviceInactive="); pw.print(mWaitForDeviceInactive);
            pw.print(" mComputedNightMode="); pw.print(mComputedNightMode);
            pw.print(" customStart="); pw.print(mCustomAutoNightModeStartMilliseconds);
            pw.print(" customEnd"); pw.print(mCustomAutoNightModeEndMilliseconds);
@@ -1675,7 +1679,7 @@ final class UiModeManagerService extends SystemService {
        }

        mCurUiMode = uiMode;
        if (!mHoldingConfiguration && (!mWaitForScreenOff || mPowerSave)) {
        if (!mHoldingConfiguration && (!mWaitForDeviceInactive || mPowerSave)) {
            mConfiguration.uiMode = uiMode;
        }
    }
@@ -1712,7 +1716,8 @@ final class UiModeManagerService extends SystemService {

    private boolean shouldApplyAutomaticChangesImmediately() {
        return mCar || !mPowerManager.isInteractive()
                || mNightModeCustomType == MODE_NIGHT_CUSTOM_TYPE_BEDTIME;
                || mNightModeCustomType == MODE_NIGHT_CUSTOM_TYPE_BEDTIME
                || mDreamManagerInternal.isDreaming();
    }

    private void scheduleNextCustomTimeListener() {
+39 −4
Original line number Diff line number Diff line
@@ -27,10 +27,14 @@ import static android.app.UiModeManager.MODE_NIGHT_YES;
import static android.app.UiModeManager.PROJECTION_TYPE_ALL;
import static android.app.UiModeManager.PROJECTION_TYPE_AUTOMOTIVE;
import static android.app.UiModeManager.PROJECTION_TYPE_NONE;

import static com.android.server.UiModeManagerService.SUPPORTED_NIGHT_MODE_CUSTOM_TYPES;

import static com.google.common.truth.Truth.assertThat;

import static junit.framework.TestCase.assertFalse;
import static junit.framework.TestCase.assertTrue;

import static org.hamcrest.Matchers.contains;
import static org.hamcrest.Matchers.empty;
import static org.junit.Assert.assertEquals;
@@ -81,6 +85,7 @@ import android.os.RemoteException;
import android.os.UserHandle;
import android.os.test.FakePermissionEnforcer;
import android.provider.Settings;
import android.service.dreams.DreamManagerInternal;
import android.test.mock.MockContentResolver;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
@@ -131,6 +136,9 @@ public class UiModeManagerServiceTest extends UiServiceTestCase {
    private TwilightState mTwilightState;
    @Mock
    PowerManagerInternal mLocalPowerManager;

    @Mock
    DreamManagerInternal mDreamManagerInternal;
    @Mock
    private PackageManager mPackageManager;
    @Mock
@@ -143,6 +151,7 @@ public class UiModeManagerServiceTest extends UiServiceTestCase {
    private ArgumentCaptor<BroadcastReceiver> mOrderedBroadcastReceiver;

    private BroadcastReceiver mScreenOffCallback;
    private BroadcastReceiver mDreamingStartedCallback;
    private BroadcastReceiver mTimeChangedCallback;
    private BroadcastReceiver mDockStateChangedCallback;
    private AlarmManager.OnAlarmListener mCustomListener;
@@ -187,6 +196,9 @@ public class UiModeManagerServiceTest extends UiServiceTestCase {
            if (filter.hasAction(Intent.ACTION_SCREEN_OFF)) {
                mScreenOffCallback = inv.getArgument(0);
            }
            if (filter.hasAction(Intent.ACTION_DREAMING_STARTED)) {
                mDreamingStartedCallback = inv.getArgument(0);
            }
            if (filter.hasAction(Intent.ACTION_DOCK_EVENT)) {
                mDockStateChangedCallback = inv.getArgument(0);
            }
@@ -210,6 +222,7 @@ public class UiModeManagerServiceTest extends UiServiceTestCase {
        addLocalService(WindowManagerInternal.class, mWindowManager);
        addLocalService(PowerManagerInternal.class, mLocalPowerManager);
        addLocalService(TwilightManager.class, mTwilightManager);
        addLocalService(DreamManagerInternal.class, mDreamManagerInternal);

        mInjector = spy(new TestInjector());
        mUiManagerService = new UiModeManagerService(mContext, /* setupWizardComplete= */ true,
@@ -281,7 +294,7 @@ public class UiModeManagerServiceTest extends UiServiceTestCase {
    }

    @Test
    public void setAutoMode_screenOffRegistered() throws RemoteException {
    public void setAutoMode_deviceInactiveRegistered() throws RemoteException {
        try {
            mService.setNightMode(MODE_NIGHT_NO);
        } catch (SecurityException e) { /* we should ignore this update config exception*/ }
@@ -291,7 +304,7 @@ public class UiModeManagerServiceTest extends UiServiceTestCase {

    @Ignore // b/152719290 - Fails on stage-aosp-master
    @Test
    public void setAutoMode_screenOffUnRegistered() throws RemoteException {
    public void setAutoMode_deviceInactiveUnRegistered() throws RemoteException {
        try {
            mService.setNightMode(MODE_NIGHT_AUTO);
        } catch (SecurityException e) { /* we should ignore this update config exception*/ }
@@ -776,7 +789,7 @@ public class UiModeManagerServiceTest extends UiServiceTestCase {
    }

    @Test
    public void customTime_darkThemeOn() throws RemoteException {
    public void customTime_darkThemeOn_afterScreenOff() throws RemoteException {
        LocalTime now = LocalTime.now();
        mService.setNightMode(MODE_NIGHT_NO);
        mService.setCustomNightModeStart(now.minusHours(1L).toNanoOfDay() / 1000);
@@ -787,7 +800,7 @@ public class UiModeManagerServiceTest extends UiServiceTestCase {
    }

    @Test
    public void customTime_darkThemeOff() throws RemoteException {
    public void customTime_darkThemeOff_afterScreenOff() throws RemoteException {
        LocalTime now = LocalTime.now();
        mService.setNightMode(MODE_NIGHT_YES);
        mService.setCustomNightModeStart(now.plusHours(1L).toNanoOfDay() / 1000);
@@ -797,6 +810,28 @@ public class UiModeManagerServiceTest extends UiServiceTestCase {
        assertFalse(isNightModeActivated());
    }

    @Test
    public void customTime_darkThemeOn_afterDreamingStarted() throws RemoteException {
        LocalTime now = LocalTime.now();
        mService.setNightMode(MODE_NIGHT_NO);
        mService.setCustomNightModeStart(now.minusHours(1L).toNanoOfDay() / 1000);
        mService.setCustomNightModeEnd(now.plusHours(1L).toNanoOfDay() / 1000);
        mService.setNightMode(MODE_NIGHT_CUSTOM);
        mDreamingStartedCallback.onReceive(mContext, new Intent(Intent.ACTION_DREAMING_STARTED));
        assertTrue(isNightModeActivated());
    }

    @Test
    public void customTime_darkThemeOff_afterDreamingStarted() throws RemoteException {
        LocalTime now = LocalTime.now();
        mService.setNightMode(MODE_NIGHT_YES);
        mService.setCustomNightModeStart(now.plusHours(1L).toNanoOfDay() / 1000);
        mService.setCustomNightModeEnd(now.minusHours(1L).toNanoOfDay() / 1000);
        mService.setNightMode(MODE_NIGHT_CUSTOM);
        mDreamingStartedCallback.onReceive(mContext, new Intent(Intent.ACTION_DREAMING_STARTED));
        assertFalse(isNightModeActivated());
    }

    @Test
    public void customTime_darkThemeOff_afterStartEnd() throws RemoteException {
        LocalTime now = LocalTime.now();