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

Commit 01af8ca1 authored by William Xiao's avatar William Xiao
Browse files

Allow dreaming when charge limit is active

When a charge limit is active and the battery reaches the level of the
limit, the device will stop charging entirely. We still want to dream
in these cases as battery usage is not an issue since the device will
start charging again if the battery level goes below the limit.

Bug: 397717022
Fixes: 397717022
Test: atest DreamManagerServiceTest
      also manually verified on phone
Flag: android.service.dreams.allow_dream_with_charge_limit
Change-Id: Id5d67e6f687335bccb9edfe3787412f8c59d8c86
parent 6d9cf535
Loading
Loading
Loading
Loading
+10 −0
Original line number Diff line number Diff line
@@ -100,3 +100,13 @@ flag {
  bug: "403579494"
  is_fixed_read_only: true
}

flag {
    name: "allow_dream_with_charge_limit"
    namespace: "systemui"
    description: "Allows dreaming even when a charge limit is active"
    bug: "397717022"
    metadata {
        purpose: PURPOSE_BUGFIX
    }
}
 No newline at end of file
+61 −14
Original line number Diff line number Diff line
@@ -20,7 +20,9 @@ import static android.Manifest.permission.BIND_DREAM_SERVICE;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_DREAM;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.os.BatteryManager.EXTRA_CHARGING_STATUS;
import static android.service.dreams.Flags.allowDreamWhenPostured;
import static android.service.dreams.Flags.allowDreamWithChargeLimit;
import static android.service.dreams.Flags.cleanupDreamSettingsOnUninstall;
import static android.service.dreams.Flags.dreamHandlesBeingObscured;
import static android.service.dreams.Flags.dreamsV2;
@@ -46,6 +48,7 @@ import android.content.pm.PackageManagerInternal;
import android.content.pm.ServiceInfo;
import android.database.ContentObserver;
import android.hardware.display.AmbientDisplayConfiguration;
import android.hardware.health.BatteryChargingState;
import android.net.Uri;
import android.os.BatteryManager;
import android.os.BatteryManagerInternal;
@@ -120,6 +123,13 @@ public final class DreamManagerService extends SystemService {
    private static final int DREAM_ON_CHARGE = 1 << 1;
    private static final int DREAM_ON_POSTURED = 1 << 2;

    /**
     * Battery percentage at which the device stops charging when the charge limit feature is
     * enabled.
     */
    @VisibleForTesting
    static final int CHARGE_LIMIT_PERCENTAGE = 80;

    private final Object mLock = new Object();

    private final Context mContext;
@@ -206,13 +216,20 @@ public final class DreamManagerService extends SystemService {
                }
            };

    private final BroadcastReceiver mChargingReceiver = new BroadcastReceiver() {
    /**
     * Receiver for the {@link Intent#ACTION_BATTERY_CHANGED} broadcast.
     */
    private final BroadcastReceiver mBatteryChangedReceived = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
        public void onReceive(Context context, Intent batteryChangedIntent) {
            if (allowDreamWithChargeLimit()) {
                updateChargingStatus(batteryChangedIntent);
            } else {
                mIsCharging = mBatteryManagerInternal.isPowered(BatteryManager.BATTERY_PLUGGED_ANY);
                mIsWirelessCharging = mBatteryManagerInternal.isPowered(
                        BatteryManager.BATTERY_PLUGGED_WIRELESS);
            }
        }
    };

    private final BroadcastReceiver mDockStateReceiver = new BroadcastReceiver() {
@@ -320,10 +337,11 @@ public final class DreamManagerService extends SystemService {
            mContext.registerReceiver(
                    mDockStateReceiver, new IntentFilter(Intent.ACTION_DOCK_EVENT));

            IntentFilter chargingIntentFilter = new IntentFilter();
            chargingIntentFilter.addAction(Intent.ACTION_BATTERY_CHANGED);
            chargingIntentFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
            mContext.registerReceiver(mChargingReceiver, chargingIntentFilter);
            // Broadcast is sticky so we don't need to query state directly.
            IntentFilter batteryChangedIntentFilter = new IntentFilter();
            batteryChangedIntentFilter.addAction(Intent.ACTION_BATTERY_CHANGED);
            batteryChangedIntentFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
            mContext.registerReceiver(mBatteryChangedReceived, batteryChangedIntentFilter);

            mSettingsObserver = new SettingsObserver(mHandler);
            mContext.getContentResolver().registerContentObserver(Settings.Secure.getUriFor(
@@ -342,11 +360,13 @@ public final class DreamManagerService extends SystemService {
                            Settings.Secure.SCREENSAVER_RESTRICT_TO_WIRELESS_CHARGING),
                    false, mSettingsObserver, UserHandle.USER_ALL);

            if (!allowDreamWithChargeLimit()) {
                // We don't get an initial broadcast for the battery state, so we have to initialize
                // directly from BatteryManager.
                mIsCharging = mBatteryManagerInternal.isPowered(BatteryManager.BATTERY_PLUGGED_ANY);
                mIsWirelessCharging = mBatteryManagerInternal.isPowered(
                        BatteryManager.BATTERY_PLUGGED_WIRELESS);
            }

            updateWhenToDreamSettings();
        }
@@ -424,6 +444,33 @@ public final class DreamManagerService extends SystemService {
        }
    }

    private void updateChargingStatus(Intent batteryChangedIntent) {
        mIsWirelessCharging = mBatteryManagerInternal.isPowered(
                BatteryManager.BATTERY_PLUGGED_WIRELESS);

        if (mBatteryManagerInternal.isPowered(BatteryManager.BATTERY_PLUGGED_ANY)) {
            mIsCharging = true;
        } else {
            // When charge limit is enabled and the device is at the charge limit battery %, the
            // device stops charging entirely and the plug type is reported as BATTERY_PLUGGED_NONE.
            // Check if the feature is enabled so that the device can still dream when charge limit
            // is active.

            final ContentResolver resolver = mContext.getContentResolver();
            final boolean isChargeLimitEnabled = Settings.Secure.getIntForUser(resolver,
                    Settings.Secure.CHARGE_OPTIMIZATION_MODE, /*default=*/ 0,
                    UserHandle.USER_CURRENT) != 0;

            int chargingStatus = batteryChangedIntent.getIntExtra(EXTRA_CHARGING_STATUS,
                    BatteryChargingState.NORMAL);
            final boolean isChargeLimitActive =
                    mBatteryManagerInternal.getBatteryLevel() >= CHARGE_LIMIT_PERCENTAGE
                            && chargingStatus == BatteryChargingState.LONG_LIFE;

            mIsCharging = isChargeLimitEnabled && isChargeLimitActive;
        }
    }

    private void updateWhenToDreamSettings() {
        synchronized (mLock) {
            final ContentResolver resolver = mContext.getContentResolver();
+42 −0
Original line number Diff line number Diff line
@@ -16,11 +16,14 @@

package com.android.server.dreams;

import static android.os.BatteryManager.EXTRA_CHARGING_STATUS;
import static android.service.dreams.Flags.FLAG_DREAMS_V2;
import static android.service.dreams.Flags.FLAG_ALLOW_DREAM_WITH_CHARGE_LIMIT;

import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;

import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static com.android.server.dreams.DreamManagerService.CHARGE_LIMIT_PERCENTAGE;

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

@@ -33,6 +36,7 @@ import android.app.ActivityManagerInternal;
import android.content.BroadcastReceiver;
import android.content.ContextWrapper;
import android.content.Intent;
import android.hardware.health.BatteryChargingState;
import android.os.BatteryManager;
import android.os.BatteryManagerInternal;
import android.os.PowerManagerInternal;
@@ -200,6 +204,44 @@ public class DreamManagerServiceTest {
        final DreamManagerService service = createService();
        service.onBootPhase(SystemService.PHASE_THIRD_PARTY_APPS_CAN_START);

        // Battery changed event is received.
        ArgumentCaptor<BroadcastReceiver> receiverCaptor = ArgumentCaptor.forClass(
                BroadcastReceiver.class);
        verify(mContextSpy).registerReceiver(receiverCaptor.capture(),
                argThat((arg) -> arg.hasAction(Intent.ACTION_BATTERY_CHANGED)));
        receiverCaptor.getValue().onReceive(mContext, new Intent());

        // Dream condition is active.
        assertThat(service.dreamConditionActiveInternal()).isTrue();
    }

    @EnableFlags(FLAG_ALLOW_DREAM_WITH_CHARGE_LIMIT)
    @Test
    public void testDreamConditionActive_chargeLimitActive() {
        // Enable dreaming while charging only.
        Settings.Secure.putIntForUser(mContextSpy.getContentResolver(),
                Settings.Secure.SCREENSAVER_ACTIVATE_ON_SLEEP, 1, UserHandle.USER_CURRENT);
        // Enable charge limit setting.
        Settings.Secure.putIntForUser(mContextSpy.getContentResolver(),
                Settings.Secure.CHARGE_OPTIMIZATION_MODE, 1, UserHandle.USER_CURRENT);

        // Device is not considered charging when charge limit is on.
        when(mBatteryManagerInternal.isPowered(anyInt())).thenReturn(false);
        when(mBatteryManagerInternal.getBatteryLevel()).thenReturn(CHARGE_LIMIT_PERCENTAGE);

        // Initialize service so settings are read.
        final DreamManagerService service = createService();
        service.onBootPhase(SystemService.PHASE_THIRD_PARTY_APPS_CAN_START);

        // Battery changed event is received.
        ArgumentCaptor<BroadcastReceiver> receiverCaptor = ArgumentCaptor.forClass(
                BroadcastReceiver.class);
        verify(mContextSpy).registerReceiver(receiverCaptor.capture(),
                argThat((arg) -> arg.hasAction(Intent.ACTION_BATTERY_CHANGED)));
        Intent intent = new Intent();
        intent.putExtra(EXTRA_CHARGING_STATUS, BatteryChargingState.LONG_LIFE);
        receiverCaptor.getValue().onReceive(mContext, intent);

        // Dream condition is active.
        assertThat(service.dreamConditionActiveInternal()).isTrue();
    }