Add ability to disable wakelocks in light idle.

Make it possible to disable wakelocks for background apps while the
device is in light device idle mode.

Bug: 299329948
Test: atest PowerServiceTests:PowerManagerServiceTest
Change-Id: Ice07f936f6a2ac329825564cb4d8950a92e65fc3
diff --git a/AconfigFlags.bp b/AconfigFlags.bp
index f234166..76dd5ec 100644
--- a/AconfigFlags.bp
+++ b/AconfigFlags.bp
@@ -58,6 +58,7 @@
     ":android.service.autofill.flags-aconfig-java{.generated_srcjars}",
     ":com.android.net.flags-aconfig-java{.generated_srcjars}",
     ":device_policy_aconfig_flags_lib{.generated_srcjars}",
+    ":service-jobscheduler-deviceidle.flags-aconfig-java{.generated_srcjars}",
     ":surfaceflinger_flags_java_lib{.generated_srcjars}",
 ]
 
diff --git a/apex/jobscheduler/service/aconfig/Android.bp b/apex/jobscheduler/service/aconfig/Android.bp
index 24ecd3d..7d8a363 100644
--- a/apex/jobscheduler/service/aconfig/Android.bp
+++ b/apex/jobscheduler/service/aconfig/Android.bp
@@ -1,3 +1,19 @@
+// Device Idle
+aconfig_declarations {
+    name: "service-deviceidle.flags-aconfig",
+    package: "com.android.server.deviceidle",
+    srcs: [
+        "device_idle.aconfig",
+    ],
+}
+
+java_aconfig_library {
+    name: "service-jobscheduler-deviceidle.flags-aconfig-java",
+    aconfig_declarations: "service-deviceidle.flags-aconfig",
+    defaults: ["framework-minus-apex-aconfig-java-defaults"],
+    visibility: ["//frameworks/base:__subpackages__"],
+}
+
 // JobScheduler
 aconfig_declarations {
     name: "service-job.flags-aconfig",
@@ -15,6 +31,7 @@
 }
 
 service_jobscheduler_aconfig_srcjars = [
+    ":service-jobscheduler-deviceidle.flags-aconfig-java{.generated_srcjars}",
     ":service-jobscheduler-job.flags-aconfig-java{.generated_srcjars}",
 ]
 
diff --git a/apex/jobscheduler/service/aconfig/device_idle.aconfig b/apex/jobscheduler/service/aconfig/device_idle.aconfig
new file mode 100644
index 0000000..fc24b30
--- /dev/null
+++ b/apex/jobscheduler/service/aconfig/device_idle.aconfig
@@ -0,0 +1,8 @@
+package: "com.android.server.deviceidle"
+
+flag {
+    name: "disable_wakelocks_in_light_idle"
+    namespace: "backstage_power"
+    description: "Disable wakelocks for background apps while Light Device Idle is active"
+    bug: "299329948"
+}
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 4a4214f..8ce0c72 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -32,6 +32,7 @@
 import static android.os.PowerManagerInternal.wakefulnessToString;
 
 import static com.android.internal.util.LatencyTracker.ACTION_TURN_ON_SCREEN;
+import static com.android.server.deviceidle.Flags.disableWakelocksInLightIdle;
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
@@ -644,9 +645,11 @@
     private boolean mBatteryLevelLow;
 
     // True if we are currently in device idle mode.
+    @GuardedBy("mLock")
     private boolean mDeviceIdleMode;
 
     // True if we are currently in light device idle mode.
+    @GuardedBy("mLock")
     private boolean mLightDeviceIdleMode;
 
     // Set of app ids that we will respect the wake locks for while in device idle mode.
@@ -4035,6 +4038,9 @@
         synchronized (mLock) {
             if (mLightDeviceIdleMode != enabled) {
                 mLightDeviceIdleMode = enabled;
+                if (!mDeviceIdleMode && disableWakelocksInLightIdle()) {
+                    updateWakeLockDisabledStatesLocked();
+                }
                 setPowerModeInternal(MODE_DEVICE_IDLE, mDeviceIdleMode || mLightDeviceIdleMode);
                 return true;
             }
@@ -4045,7 +4051,7 @@
     void setDeviceIdleWhitelistInternal(int[] appids) {
         synchronized (mLock) {
             mDeviceIdleWhitelist = appids;
-            if (mDeviceIdleMode) {
+            if (doesIdleStateBlockWakeLocksLocked()) {
                 updateWakeLockDisabledStatesLocked();
             }
         }
@@ -4054,7 +4060,7 @@
     void setDeviceIdleTempWhitelistInternal(int[] appids) {
         synchronized (mLock) {
             mDeviceIdleTempWhitelist = appids;
-            if (mDeviceIdleMode) {
+            if (doesIdleStateBlockWakeLocksLocked()) {
                 updateWakeLockDisabledStatesLocked();
             }
         }
@@ -4114,7 +4120,7 @@
                     <= ActivityManager.PROCESS_STATE_RECEIVER;
             state.mProcState = procState;
             if (state.mNumWakeLocks > 0) {
-                if (mDeviceIdleMode || mLowPowerStandbyActive) {
+                if (doesIdleStateBlockWakeLocksLocked() || mLowPowerStandbyActive) {
                     handleUidStateChangeLocked();
                 } else if (!state.mActive && oldShouldAllow !=
                         (procState <= ActivityManager.PROCESS_STATE_RECEIVER)) {
@@ -4134,7 +4140,8 @@
                 state.mProcState = ActivityManager.PROCESS_STATE_NONEXISTENT;
                 state.mActive = false;
                 mUidState.removeAt(index);
-                if ((mDeviceIdleMode || mLowPowerStandbyActive) && state.mNumWakeLocks > 0) {
+                if ((doesIdleStateBlockWakeLocksLocked() || mLowPowerStandbyActive)
+                        && state.mNumWakeLocks > 0) {
                     handleUidStateChangeLocked();
                 }
             }
@@ -4169,6 +4176,11 @@
     }
 
     @GuardedBy("mLock")
+    private boolean doesIdleStateBlockWakeLocksLocked() {
+        return mDeviceIdleMode || (mLightDeviceIdleMode && disableWakelocksInLightIdle());
+    }
+
+    @GuardedBy("mLock")
     private void updateWakeLockDisabledStatesLocked() {
         boolean changed = false;
         final int numWakeLocks = mWakeLocks.size();
@@ -4207,7 +4219,7 @@
                                     != ActivityManager.PROCESS_STATE_NONEXISTENT &&
                             wakeLock.mUidState.mProcState > ActivityManager.PROCESS_STATE_RECEIVER);
                 }
-                if (mDeviceIdleMode) {
+                if (doesIdleStateBlockWakeLocksLocked()) {
                     // If we are in idle mode, we will also ignore all partial wake locks that are
                     // for application uids that are not allowlisted.
                     final UidState state = wakeLock.mUidState;
diff --git a/services/tests/powerservicetests/Android.bp b/services/tests/powerservicetests/Android.bp
index 7351fc5f..8d455fe 100644
--- a/services/tests/powerservicetests/Android.bp
+++ b/services/tests/powerservicetests/Android.bp
@@ -11,6 +11,7 @@
     ],
 
     static_libs: [
+        "flag-junit",
         "frameworks-base-testutils",
         "platform-compat-test-rules",
         "platform-test-annotations",
diff --git a/services/tests/powerservicetests/src/com/android/server/power/PowerManagerServiceTest.java b/services/tests/powerservicetests/src/com/android/server/power/PowerManagerServiceTest.java
index 8e1d8ab..d752ae4 100644
--- a/services/tests/powerservicetests/src/com/android/server/power/PowerManagerServiceTest.java
+++ b/services/tests/powerservicetests/src/com/android/server/power/PowerManagerServiceTest.java
@@ -26,6 +26,8 @@
 import static android.os.PowerManagerInternal.WAKEFULNESS_DOZING;
 import static android.os.PowerManagerInternal.WAKEFULNESS_DREAMING;
 
+import static com.android.server.deviceidle.Flags.FLAG_DISABLE_WAKELOCKS_IN_LIGHT_IDLE;
+
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.junit.Assert.assertFalse;
@@ -82,6 +84,7 @@
 import android.os.PowerSaveState;
 import android.os.UserHandle;
 import android.os.test.TestLooper;
+import android.platform.test.flag.junit.SetFlagsRule;
 import android.provider.DeviceConfig;
 import android.provider.Settings;
 import android.service.dreams.DreamManagerInternal;
@@ -175,6 +178,8 @@
 
     @Rule public TestRule compatChangeRule = new PlatformCompatChangeRule();
 
+    @Rule public SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
     private PowerManagerService mService;
     private ContextWrapper mContextSpy;
     private BatteryReceiver mBatteryReceiver;
@@ -2567,6 +2572,58 @@
     }
 
     @Test
+    public void testDisableWakelocksInLightDeviceIdle_FlagDisabled_BgApp() {
+        mSetFlagsRule.disableFlags(FLAG_DISABLE_WAKELOCKS_IN_LIGHT_IDLE);
+        createService();
+        startSystem();
+        WakeLock wakeLock = acquireWakeLock("fgsWakeLock", PowerManager.PARTIAL_WAKE_LOCK);
+        mService.updateUidProcStateInternal(wakeLock.mOwnerUid, PROCESS_STATE_RECEIVER);
+        mService.setDeviceIdleModeInternal(false);
+        mService.setLightDeviceIdleModeInternal(true);
+
+        assertThat(wakeLock.mDisabled).isFalse();
+    }
+
+    @Test
+    public void testDisableWakelocksInLightDeviceIdle_FlagDisabled_FgApp() {
+        mSetFlagsRule.disableFlags(FLAG_DISABLE_WAKELOCKS_IN_LIGHT_IDLE);
+        createService();
+        startSystem();
+        WakeLock wakeLock = acquireWakeLock("fgsWakeLock", PowerManager.PARTIAL_WAKE_LOCK);
+        mService.updateUidProcStateInternal(wakeLock.mOwnerUid, PROCESS_STATE_FOREGROUND_SERVICE);
+        mService.setDeviceIdleModeInternal(false);
+        mService.setLightDeviceIdleModeInternal(true);
+
+        assertThat(wakeLock.mDisabled).isFalse();
+    }
+
+    @Test
+    public void testDisableWakelocksInLightDeviceIdle_FlagEnabled_BgApp() {
+        mSetFlagsRule.enableFlags(FLAG_DISABLE_WAKELOCKS_IN_LIGHT_IDLE);
+        createService();
+        startSystem();
+        WakeLock wakeLock = acquireWakeLock("fgsWakeLock", PowerManager.PARTIAL_WAKE_LOCK);
+        mService.updateUidProcStateInternal(wakeLock.mOwnerUid, PROCESS_STATE_RECEIVER);
+        mService.setDeviceIdleModeInternal(false);
+        mService.setLightDeviceIdleModeInternal(true);
+
+        assertThat(wakeLock.mDisabled).isTrue();
+    }
+
+    @Test
+    public void testDisableWakelocksInLightDeviceIdle_FlagEnabled_FgApp() {
+        mSetFlagsRule.enableFlags(FLAG_DISABLE_WAKELOCKS_IN_LIGHT_IDLE);
+        createService();
+        startSystem();
+        WakeLock wakeLock = acquireWakeLock("fgsWakeLock", PowerManager.PARTIAL_WAKE_LOCK);
+        mService.updateUidProcStateInternal(wakeLock.mOwnerUid, PROCESS_STATE_FOREGROUND_SERVICE);
+        mService.setDeviceIdleModeInternal(false);
+        mService.setLightDeviceIdleModeInternal(true);
+
+        assertThat(wakeLock.mDisabled).isFalse();
+    }
+
+    @Test
     public void testLowPowerStandby_whenInactive_FgsWakeLockEnabled() {
         createService();
         startSystem();