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

Commit 1314cf61 authored by Jay Aliomer's avatar Jay Aliomer
Browse files

Battery saver dark mode bug

When battery saver is on and the dark mode is in automatic
scheduling, the battery saver does not apply before the
screen turns off which is a bug.

some refactoring has been done also to speedup startup time
Test: atest UiModeManagerTest UiModeManagerServiceTest

Fixes: 148798405
Fixes: 149546098
Change-Id: I0bee6517b39216146681097262cf55c7192b0131
(cherry picked from commit 2a76f814)
parent 6f01c62d
Loading
Loading
Loading
Loading
+54 −37
Original line number Diff line number Diff line
@@ -98,7 +98,8 @@ final class UiModeManagerService extends SystemService {
    private int mLastBroadcastState = Intent.EXTRA_DOCK_STATE_UNDOCKED;
    private int mNightMode = UiModeManager.MODE_NIGHT_NO;
    // we use the override auto mode
    // for example: force night mode off in the night time while in auto mode
    // for example: force night mode off in the night time
    // while in auto mode
    private int mNightModeOverride = mNightMode;
    private final LocalTime DEFAULT_CUSTOM_NIGHT_START_TIME = LocalTime.of(22, 0);
    private final LocalTime DEFAULT_CUSTOM_NIGHT_END_TIME = LocalTime.of(6, 0);
@@ -152,6 +153,7 @@ final class UiModeManagerService extends SystemService {
    private PowerManager.WakeLock mWakeLock;

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

    public UiModeManagerService(Context context) {
        super(context);
@@ -160,6 +162,7 @@ final class UiModeManagerService extends SystemService {
    @VisibleForTesting
    protected UiModeManagerService(Context context, WindowManagerInternal wm, AlarmManager am,
                                   PowerManager pm, PowerManager.WakeLock wl, TwilightManager tm,
                                   PowerManagerInternal localPowerManager,
                                   boolean setupWizardComplete) {
        super(context);
        mWindowManager = wm;
@@ -168,6 +171,8 @@ final class UiModeManagerService extends SystemService {
        mSetupWizardComplete = setupWizardComplete;
        mAlarmManager = am;
        mPowerManager = pm;
        mLocalPowerManager = localPowerManager;
        initPowerSave();
    }

    private static Intent buildHomeIntent(String category) {
@@ -230,11 +235,11 @@ final class UiModeManagerService extends SystemService {
        @Override
        public void onTwilightStateChanged(@Nullable TwilightState state) {
            synchronized (mLock) {
                if (mNightMode == UiModeManager.MODE_NIGHT_AUTO) {
                if (mNightMode == UiModeManager.MODE_NIGHT_AUTO && mSystemReady) {
                    if (mCar) {
                        updateLocked(0, 0);
                    } else {
                        registerScreenOffEvent();
                        registerScreenOffEventLocked();
                    }
                }
            }
@@ -250,7 +255,7 @@ final class UiModeManagerService extends SystemService {
        public void onReceive(Context context, Intent intent) {
            synchronized (mLock) {
                // must unregister first before updating
                unregisterScreenOffEvent();
                unregisterScreenOffEventLocked();
                updateLocked(0, 0);
            }
        }
@@ -326,8 +331,7 @@ final class UiModeManagerService extends SystemService {
    public void onStart() {
        final Context context = getContext();

        mPowerManager =
                (PowerManager) context.getSystemService(Context.POWER_SERVICE);
        mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
        mWakeLock = mPowerManager.newWakeLock(PowerManager.FULL_WAKE_LOCK, TAG);
        mWindowManager = LocalServices.getService(WindowManagerInternal.class);
        mAlarmManager = (AlarmManager) getContext().getSystemService(Context.ALARM_SERVICE);
@@ -341,21 +345,11 @@ final class UiModeManagerService extends SystemService {
        IntentFilter batteryFilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
        context.registerReceiver(mBatteryReceiver, batteryFilter);

        PowerManagerInternal localPowerManager =
        mLocalPowerManager =
                LocalServices.getService(PowerManagerInternal.class);
        mPowerSave = localPowerManager.getLowPowerState(ServiceType.NIGHT_MODE).batterySaverEnabled;
        localPowerManager.registerLowPowerModeObserver(ServiceType.NIGHT_MODE,
                state -> {
                    synchronized (mLock) {
                        if (mPowerSave == state.batterySaverEnabled) {
                            return;
                        }
                        mPowerSave = state.batterySaverEnabled;
                        if (mSystemReady) {
                            updateLocked(0, 0);
                        }
                    }
                });
        initPowerSave();

        mTwilightManager = getLocalService(TwilightManager.class);

        mConfiguration.setToDefaults();

@@ -396,7 +390,24 @@ final class UiModeManagerService extends SystemService {

        context.getContentResolver().registerContentObserver(Secure.getUriFor(Secure.UI_NIGHT_MODE),
                false, mDarkThemeObserver, 0);
        updateSystemProperties();
        mHandler.post(() -> updateSystemProperties());
    }

    private void initPowerSave() {
        mPowerSave =
                mLocalPowerManager.getLowPowerState(ServiceType.NIGHT_MODE)
                        .batterySaverEnabled;
        mLocalPowerManager.registerLowPowerModeObserver(ServiceType.NIGHT_MODE, state -> {
            synchronized (mLock) {
                if (mPowerSave == state.batterySaverEnabled) {
                    return;
                }
                mPowerSave = state.batterySaverEnabled;
                if (mSystemReady) {
                    updateLocked(0, 0);
                }
            }
        });
    }

    @VisibleForTesting
@@ -433,7 +444,7 @@ final class UiModeManagerService extends SystemService {
        if (shouldApplyAutomaticChangesImmediately()) {
            updateLocked(0, 0);
        } else {
            registerScreenOffEvent();
            registerScreenOffEventLocked();
        }
        scheduleNextCustomTimeListener();
    }
@@ -473,7 +484,8 @@ final class UiModeManagerService extends SystemService {
        return oldNightMode != mNightMode;
    }

    private void registerScreenOffEvent() {
    private void registerScreenOffEventLocked() {
        if (mPowerSave) return;
        mWaitForScreenOff = true;
        final IntentFilter intentFilter =
                new IntentFilter(Intent.ACTION_SCREEN_OFF);
@@ -484,7 +496,7 @@ final class UiModeManagerService extends SystemService {
        mAlarmManager.cancel(mCustomTimeListener);
    }

    private void unregisterScreenOffEvent() {
    private void unregisterScreenOffEventLocked() {
        mWaitForScreenOff = false;
        try {
            getContext().unregisterReceiver(mOnScreenOffHandler);
@@ -630,7 +642,7 @@ final class UiModeManagerService extends SystemService {
                synchronized (mLock) {
                    if (mNightMode != mode) {
                        if (mNightMode == MODE_NIGHT_AUTO || mNightMode == MODE_NIGHT_CUSTOM) {
                            unregisterScreenOffEvent();
                            unregisterScreenOffEventLocked();
                            cancelCustomAlarm();
                        }

@@ -643,10 +655,10 @@ final class UiModeManagerService extends SystemService {
                        // on screen off will update configuration instead
                        if ((mNightMode != MODE_NIGHT_AUTO && mNightMode != MODE_NIGHT_CUSTOM)
                                || shouldApplyAutomaticChangesImmediately()) {
                            unregisterScreenOffEvent();
                            unregisterScreenOffEventLocked();
                            updateLocked(0, 0);
                        } else {
                            registerScreenOffEvent();
                            registerScreenOffEventLocked();
                        }
                    }
                }
@@ -695,7 +707,7 @@ final class UiModeManagerService extends SystemService {
                final long ident = Binder.clearCallingIdentity();
                try {
                    if (mNightMode == MODE_NIGHT_AUTO || mNightMode == MODE_NIGHT_CUSTOM) {
                        unregisterScreenOffEvent();
                        unregisterScreenOffEventLocked();
                        mNightModeOverride = active
                                ? UiModeManager.MODE_NIGHT_YES : UiModeManager.MODE_NIGHT_NO;
                    } else if (mNightMode == UiModeManager.MODE_NIGHT_NO
@@ -737,7 +749,7 @@ final class UiModeManagerService extends SystemService {
                persistNightMode(user);
                onCustomTimeUpdated(user);
            } catch (DateTimeException e) {
                unregisterScreenOffEvent();
                unregisterScreenOffEventLocked();
            } finally {
                Binder.restoreCallingIdentity(ident);
            }
@@ -764,7 +776,7 @@ final class UiModeManagerService extends SystemService {
                mCustomAutoNightModeEndMilliseconds = newTime;
                onCustomTimeUpdated(user);
            } catch (DateTimeException e) {
                unregisterScreenOffEvent();
                unregisterScreenOffEventLocked();
            } finally {
                Binder.restoreCallingIdentity(ident);
            }
@@ -775,10 +787,10 @@ final class UiModeManagerService extends SystemService {
        persistNightMode(user);
        if (mNightMode != MODE_NIGHT_CUSTOM) return;
        if (shouldApplyAutomaticChangesImmediately()) {
            unregisterScreenOffEvent();
            unregisterScreenOffEventLocked();
            updateLocked(0, 0);
        } else {
            registerScreenOffEvent();
            registerScreenOffEventLocked();
        }
    }

@@ -788,6 +800,7 @@ final class UiModeManagerService extends SystemService {
            pw.print("  mDockState="); pw.print(mDockState);
            pw.print(" mLastBroadcastState="); pw.println(mLastBroadcastState);

            pw.print("  mNightModeOverride="); pw.print(mNightModeOverride);
            pw.print("  mNightMode="); pw.print(mNightMode); pw.print(" (");
            pw.print(Shell.nightModeToStr(mNightMode)); pw.print(") ");
            pw.print(" mNightModeLocked="); pw.println(mNightModeLocked);
@@ -802,6 +815,8 @@ final class UiModeManagerService extends SystemService {
            }
            pw.println("");
            pw.print(" mComputedNightMode="); pw.print(mComputedNightMode);
            pw.print(" customStart="); pw.print(mCustomAutoNightModeStartMilliseconds);
            pw.print(" customEnd"); pw.print(mCustomAutoNightModeEndMilliseconds);
            pw.print(" mCarModeEnableFlags="); pw.print(mCarModeEnableFlags);
            pw.print(" mEnableCarDockLaunch="); pw.println(mEnableCarDockLaunch);

@@ -824,7 +839,6 @@ final class UiModeManagerService extends SystemService {
    public void onBootPhase(int phase) {
        if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
            synchronized (mLock) {
                mTwilightManager = getLocalService(TwilightManager.class);
                mSystemReady = true;
                mCarModeEnabled = mDockState == Intent.EXTRA_DOCK_STATE_CAR;
                registerVrStateListener();
@@ -1011,6 +1025,10 @@ final class UiModeManagerService extends SystemService {
            uiMode = Configuration.UI_MODE_TYPE_VR_HEADSET;
        }

        if (mNightMode == MODE_NIGHT_YES || mNightMode == UiModeManager.MODE_NIGHT_NO) {
            updateComputedNightModeLocked(mNightMode == MODE_NIGHT_YES);
        }

        if (mNightMode == MODE_NIGHT_AUTO) {
            boolean activateNightMode = mComputedNightMode;
            if (mTwilightManager != null) {
@@ -1020,12 +1038,10 @@ final class UiModeManagerService extends SystemService {
            }
            
            updateComputedNightModeLocked(activateNightMode);
            uiMode = getComputedUiModeConfiguration(uiMode);
        } else {
            if (mTwilightManager != null) {
                mTwilightManager.unregisterListener(mTwilightListener);
            }
            uiMode |= mNightMode << 4;
        }

        if (mNightMode == MODE_NIGHT_CUSTOM) {
@@ -1033,7 +1049,6 @@ final class UiModeManagerService extends SystemService {
            final boolean activate = computeCustomNightMode();
            updateComputedNightModeLocked(activate);
            scheduleNextCustomTimeListener();
            uiMode = getComputedUiModeConfiguration(uiMode);
        } else {
            unregisterTimeChangeEvent();
        }
@@ -1042,6 +1057,8 @@ final class UiModeManagerService extends SystemService {
        if (mPowerSave && !mCarModeEnabled) {
            uiMode &= ~Configuration.UI_MODE_NIGHT_NO;
            uiMode |= Configuration.UI_MODE_NIGHT_YES;
        } else {
            uiMode = getComputedUiModeConfiguration(uiMode);
        }

        if (LOG) {
@@ -1053,7 +1070,7 @@ final class UiModeManagerService extends SystemService {
        }

        mCurUiMode = uiMode;
        if (!mHoldingConfiguration && !mWaitForScreenOff) {
        if (!mHoldingConfiguration && (!mWaitForScreenOff || mPowerSave)) {
            mConfiguration.uiMode = uiMode;
        }
    }
+41 −9
Original line number Diff line number Diff line
@@ -28,6 +28,8 @@ import android.content.res.Configuration;
import android.content.res.Resources;
import android.os.Handler;
import android.os.PowerManager;
import android.os.PowerManagerInternal;
import android.os.PowerSaveState;
import android.os.RemoteException;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
@@ -42,6 +44,7 @@ import org.mockito.Mock;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneId;
import java.util.function.Consumer;

import static android.app.UiModeManager.MODE_NIGHT_AUTO;
import static android.app.UiModeManager.MODE_NIGHT_CUSTOM;
@@ -79,29 +82,34 @@ public class UiModeManagerServiceTest extends UiServiceTestCase {
    @Mock
    private Resources mResources;
    @Mock
    TwilightManager mTwilightManager;
    private TwilightManager mTwilightManager;
    @Mock
    PowerManager.WakeLock mWakeLock;
    private PowerManager.WakeLock mWakeLock;
    @Mock
    AlarmManager mAlarmManager;
    private AlarmManager mAlarmManager;
    @Mock
    PowerManager mPowerManager;
    private PowerManager mPowerManager;
    @Mock
    TwilightState mTwilightState;
    private TwilightState mTwilightState;
    @Mock
    PowerManagerInternal mLocalPowerManager;

    private BroadcastReceiver mScreenOffCallback;
    private BroadcastReceiver mTimeChangedCallback;
    private AlarmManager.OnAlarmListener mCustomListener;
    private Consumer<PowerSaveState> mPowerSaveConsumer;

    @Before
    public void setUp() {
        initMocks(this);
        mUiManagerService = new UiModeManagerService(mContext,
                mWindowManager, mAlarmManager, mPowerManager,
                mWakeLock, mTwilightManager, true);
        mService = mUiManagerService.getService();
        when(mContext.checkCallingOrSelfPermission(anyString()))
                .thenReturn(PackageManager.PERMISSION_GRANTED);
        doAnswer(inv -> {
            mPowerSaveConsumer = (Consumer<PowerSaveState>) inv.getArgument(1);
            return null;
        }).when(mLocalPowerManager).registerLowPowerModeObserver(anyInt(), any());
        when(mLocalPowerManager.getLowPowerState(anyInt()))
                .thenReturn(new PowerSaveState.Builder().setBatterySaverEnabled(false).build());
        when(mContext.getResources()).thenReturn(mResources);
        when(mContext.getContentResolver()).thenReturn(mContentResolver);
        when(mPowerManager.isInteractive()).thenReturn(true);
@@ -127,6 +135,14 @@ public class UiModeManagerServiceTest extends UiServiceTestCase {
            mCustomListener = () -> {};
            return null;
        }).when(mAlarmManager).cancel(eq(mCustomListener));

        mUiManagerService = new UiModeManagerService(mContext,
                mWindowManager, mAlarmManager, mPowerManager,
                mWakeLock, mTwilightManager, mLocalPowerManager, true);
        try {
            mUiManagerService.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY);
        } catch (SecurityException e) {/* ignore for permission denial */}
        mService = mUiManagerService.getService();
    }

    @Test
@@ -150,6 +166,22 @@ public class UiModeManagerServiceTest extends UiServiceTestCase {
        verify(mContext, atLeastOnce()).unregisterReceiver(any(BroadcastReceiver.class));
    }

    @Test
    public void autoNightModeSwitch_batterySaverOn() throws RemoteException {
        mService.setNightMode(MODE_NIGHT_NO);
        when(mTwilightState.isNight()).thenReturn(false);
        mService.setNightMode(MODE_NIGHT_AUTO);

        // night NO
        assertFalse(isNightModeActivated());

        mPowerSaveConsumer.accept(
                new PowerSaveState.Builder().setBatterySaverEnabled(true).build());

        // night YES
        assertTrue(isNightModeActivated());
    }

    @Test
    public void setAutoMode_clearCache() throws RemoteException {
        try {