Add LowPowerStandby allowed Reason: Temp PowerSave Allowlist

When ALLOWED_REASON_TEMP_POWER_SAVE_ALLOWLIST is set in the Low Power
Standby policy, apps on the temporary power-save allowlist will be
exempt from Low Power Standby restrictions.

Bug: 234002812
Test: atest LowPowerStandbyControllerTest LowPowerStandbyTest
Change-Id: I5575e38d5c6ede861f2543326f9135895dce2a23
diff --git a/core/api/current.txt b/core/api/current.txt
index d5be322..0db4569 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -32859,6 +32859,7 @@
     field public static final int LOCATION_MODE_GPS_DISABLED_WHEN_SCREEN_OFF = 1; // 0x1
     field public static final int LOCATION_MODE_NO_CHANGE = 0; // 0x0
     field public static final int LOCATION_MODE_THROTTLE_REQUESTS_WHEN_SCREEN_OFF = 4; // 0x4
+    field public static final int LOW_POWER_STANDBY_ALLOWED_REASON_TEMP_POWER_SAVE_ALLOWLIST = 2; // 0x2
     field public static final int LOW_POWER_STANDBY_ALLOWED_REASON_VOICE_INTERACTION = 1; // 0x1
     field public static final String LOW_POWER_STANDBY_FEATURE_WAKE_ON_LAN = "com.android.lowpowerstandby.WAKE_ON_LAN";
     field public static final int ON_AFTER_RELEASE = 536870912; // 0x20000000
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index b675587..30df189 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -2957,6 +2957,7 @@
      */
     @IntDef(prefix = { "LOW_POWER_STANDBY_ALLOWED_REASON_" }, flag = true, value = {
             LOW_POWER_STANDBY_ALLOWED_REASON_VOICE_INTERACTION,
+            LOW_POWER_STANDBY_ALLOWED_REASON_TEMP_POWER_SAVE_ALLOWLIST,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface LowPowerStandbyAllowedReason {
@@ -2969,6 +2970,13 @@
      */
     public static final int LOW_POWER_STANDBY_ALLOWED_REASON_VOICE_INTERACTION = 1 << 0;
 
+    /**
+     * Exempts apps on the temporary powersave allowlist.
+     *
+     * @see #isAllowedInLowPowerStandby(int)
+     */
+    public static final int LOW_POWER_STANDBY_ALLOWED_REASON_TEMP_POWER_SAVE_ALLOWLIST = 1 << 1;
+
     /** @hide */
     public static String lowPowerStandbyAllowedReasonsToString(
             @LowPowerStandbyAllowedReason int allowedReasons) {
@@ -2977,6 +2985,10 @@
             allowedStrings.add("ALLOWED_REASON_VOICE_INTERACTION");
             allowedReasons &= ~LOW_POWER_STANDBY_ALLOWED_REASON_VOICE_INTERACTION;
         }
+        if ((allowedReasons & LOW_POWER_STANDBY_ALLOWED_REASON_TEMP_POWER_SAVE_ALLOWLIST) != 0) {
+            allowedStrings.add("ALLOWED_REASON_TEMP_POWER_SAVE_ALLOWLIST");
+            allowedReasons &= ~LOW_POWER_STANDBY_ALLOWED_REASON_TEMP_POWER_SAVE_ALLOWLIST;
+        }
         if (allowedReasons != 0) {
             allowedStrings.add(String.valueOf(allowedReasons));
         }
diff --git a/services/core/java/com/android/server/power/LowPowerStandbyController.java b/services/core/java/com/android/server/power/LowPowerStandbyController.java
index 223bd55..0dc5f76 100644
--- a/services/core/java/com/android/server/power/LowPowerStandbyController.java
+++ b/services/core/java/com/android/server/power/LowPowerStandbyController.java
@@ -16,6 +16,7 @@
 
 package com.android.server.power;
 
+import static android.os.PowerManager.LOW_POWER_STANDBY_ALLOWED_REASON_TEMP_POWER_SAVE_ALLOWLIST;
 import static android.os.PowerManager.lowPowerStandbyAllowedReasonsToString;
 
 import android.Manifest;
@@ -59,6 +60,7 @@
 import com.android.modules.utils.TypedXmlPullParser;
 import com.android.modules.utils.TypedXmlSerializer;
 import com.android.server.LocalServices;
+import com.android.server.PowerAllowlistInternal;
 import com.android.server.net.NetworkPolicyManagerInternal;
 
 import org.xmlpull.v1.XmlPullParser;
@@ -134,7 +136,7 @@
     @GuardedBy("mLock")
     private boolean mEnableCustomPolicy;
 
-    private final BroadcastReceiver mIdleBroadcastReceiver = new BroadcastReceiver() {
+    private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
             switch (intent.getAction()) {
@@ -150,6 +152,8 @@
             }
         }
     };
+    private final TempAllowlistChangeListener mTempAllowlistChangeListener =
+            new TempAllowlistChangeListener();
 
     private final BroadcastReceiver mPackageBroadcastReceiver = new BroadcastReceiver() {
         @Override
@@ -318,7 +322,7 @@
             updateSettingsLocked();
 
             if (mIsEnabled) {
-                registerBroadcastReceiver();
+                registerListeners();
             }
         }
 
@@ -594,7 +598,7 @@
             onNonInteractive();
         }
 
-        registerBroadcastReceiver();
+        registerListeners();
     }
 
     @GuardedBy("mLock")
@@ -604,7 +608,7 @@
         }
 
         cancelStandbyTimeoutAlarmLocked();
-        unregisterBroadcastReceiver();
+        unregisterListeners();
         updateActiveLocked();
     }
 
@@ -629,13 +633,13 @@
         }
     }
 
-    private void registerBroadcastReceiver() {
+    private void registerListeners() {
         IntentFilter intentFilter = new IntentFilter();
         intentFilter.addAction(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED);
         intentFilter.addAction(Intent.ACTION_SCREEN_ON);
         intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
 
-        mContext.registerReceiver(mIdleBroadcastReceiver, intentFilter);
+        mContext.registerReceiver(mBroadcastReceiver, intentFilter);
 
         IntentFilter packageFilter = new IntentFilter();
         packageFilter.addDataScheme(IntentFilter.SCHEME_PACKAGE);
@@ -648,12 +652,18 @@
         userFilter.addAction(Intent.ACTION_USER_ADDED);
         userFilter.addAction(Intent.ACTION_USER_REMOVED);
         mContext.registerReceiver(mUserReceiver, userFilter, null, mHandler);
+
+        PowerAllowlistInternal pai = LocalServices.getService(PowerAllowlistInternal.class);
+        pai.registerTempAllowlistChangeListener(mTempAllowlistChangeListener);
     }
 
-    private void unregisterBroadcastReceiver() {
-        mContext.unregisterReceiver(mIdleBroadcastReceiver);
+    private void unregisterListeners() {
+        mContext.unregisterReceiver(mBroadcastReceiver);
         mContext.unregisterReceiver(mPackageBroadcastReceiver);
         mContext.unregisterReceiver(mUserReceiver);
+
+        PowerAllowlistInternal pai = LocalServices.getService(PowerAllowlistInternal.class);
+        pai.unregisterTempAllowlistChangeListener(mTempAllowlistChangeListener);
     }
 
     @GuardedBy("mLock")
@@ -1197,4 +1207,18 @@
             onSettingsChanged();
         }
     }
+
+    final class TempAllowlistChangeListener implements
+            PowerAllowlistInternal.TempAllowlistChangeListener {
+        @Override
+        public void onAppAdded(int uid) {
+            addToAllowlistInternal(uid, LOW_POWER_STANDBY_ALLOWED_REASON_TEMP_POWER_SAVE_ALLOWLIST);
+        }
+
+        @Override
+        public void onAppRemoved(int uid) {
+            removeFromAllowlistInternal(uid,
+                    LOW_POWER_STANDBY_ALLOWED_REASON_TEMP_POWER_SAVE_ALLOWLIST);
+        }
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/power/LowPowerStandbyControllerTest.java b/services/tests/servicestests/src/com/android/server/power/LowPowerStandbyControllerTest.java
index 6553ea9..454d3f3 100644
--- a/services/tests/servicestests/src/com/android/server/power/LowPowerStandbyControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/LowPowerStandbyControllerTest.java
@@ -16,6 +16,7 @@
 
 package com.android.server.power;
 
+import static android.os.PowerManager.LOW_POWER_STANDBY_ALLOWED_REASON_TEMP_POWER_SAVE_ALLOWLIST;
 import static android.os.PowerManager.LOW_POWER_STANDBY_ALLOWED_REASON_VOICE_INTERACTION;
 import static android.os.PowerManager.LOW_POWER_STANDBY_FEATURE_WAKE_ON_LAN;
 
@@ -62,6 +63,8 @@
 import com.android.internal.util.test.BroadcastInterceptingContext;
 import com.android.internal.util.test.FakeSettingsProvider;
 import com.android.server.LocalServices;
+import com.android.server.PowerAllowlistInternal;
+import com.android.server.PowerAllowlistInternal.TempAllowlistChangeListener;
 import com.android.server.net.NetworkPolicyManagerInternal;
 import com.android.server.power.LowPowerStandbyController.DeviceConfigWrapper;
 import com.android.server.testutils.OffsettableClock;
@@ -117,6 +120,8 @@
     private PowerManagerInternal mPowerManagerInternalMock;
     @Mock
     private NetworkPolicyManagerInternal mNetworkPolicyManagerInternalMock;
+    @Mock
+    private PowerAllowlistInternal mPowerAllowlistInternalMock;
 
     @Before
     public void setUp() throws Exception {
@@ -130,6 +135,7 @@
         when(mContextSpy.getSystemService(PowerManager.class)).thenReturn(powerManager);
         addLocalServiceMock(PowerManagerInternal.class, mPowerManagerInternalMock);
         addLocalServiceMock(NetworkPolicyManagerInternal.class, mNetworkPolicyManagerInternalMock);
+        addLocalServiceMock(PowerAllowlistInternal.class, mPowerAllowlistInternalMock);
 
         when(mIPowerManagerMock.isInteractive()).thenReturn(true);
 
@@ -169,6 +175,7 @@
         LocalServices.removeServiceForTest(PowerManagerInternal.class);
         LocalServices.removeServiceForTest(LowPowerStandbyControllerInternal.class);
         LocalServices.removeServiceForTest(NetworkPolicyManagerInternal.class);
+        LocalServices.removeServiceForTest(PowerAllowlistInternal.class);
         mTestPolicyFile.delete();
     }
 
@@ -614,7 +621,7 @@
 
         InOrder inOrder = inOrder(mPowerManagerInternalMock);
 
-        inOrder.verify(mPowerManagerInternalMock).setLowPowerStandbyAllowlist(new int[] {
+        inOrder.verify(mPowerManagerInternalMock).setLowPowerStandbyAllowlist(new int[]{
                 UserHandle.getUid(USER_ID_1, TEST_PKG1_APP_ID),
                 UserHandle.getUid(USER_ID_2, TEST_PKG1_APP_ID),
         });
@@ -626,7 +633,7 @@
         mContextSpy.sendBroadcast(intent);
         mTestLooper.dispatchAll();
 
-        inOrder.verify(mPowerManagerInternalMock).setLowPowerStandbyAllowlist(new int[] {
+        inOrder.verify(mPowerManagerInternalMock).setLowPowerStandbyAllowlist(new int[]{
                 UserHandle.getUid(USER_ID_1, TEST_PKG1_APP_ID)
         });
         inOrder.verifyNoMoreInteractions();
@@ -663,6 +670,39 @@
         assertFalse(mController.isPackageExempt(TEST_PKG1_APP_ID));
     }
 
+    @Test
+    public void testAllowReason_tempPowerSaveAllowlist() throws Exception {
+        mController.systemReady();
+        mController.setEnabled(true);
+        mController.setPolicy(policyWithAllowedReasons(
+                LOW_POWER_STANDBY_ALLOWED_REASON_TEMP_POWER_SAVE_ALLOWLIST));
+        mTestLooper.dispatchAll();
+
+        ArgumentCaptor<TempAllowlistChangeListener> tempAllowlistChangeListenerArgumentCaptor =
+                ArgumentCaptor.forClass(TempAllowlistChangeListener.class);
+        verify(mPowerAllowlistInternalMock).registerTempAllowlistChangeListener(
+                tempAllowlistChangeListenerArgumentCaptor.capture());
+        TempAllowlistChangeListener tempAllowlistChangeListener =
+                tempAllowlistChangeListenerArgumentCaptor.getValue();
+
+        tempAllowlistChangeListener.onAppAdded(TEST_PKG1_APP_ID);
+        mTestLooper.dispatchAll();
+        verify(mPowerManagerInternalMock).setLowPowerStandbyAllowlist(new int[]{TEST_PKG1_APP_ID});
+
+        tempAllowlistChangeListener.onAppAdded(TEST_PKG2_APP_ID);
+        mTestLooper.dispatchAll();
+        verify(mPowerManagerInternalMock).setLowPowerStandbyAllowlist(
+                new int[]{TEST_PKG1_APP_ID, TEST_PKG2_APP_ID});
+
+        tempAllowlistChangeListener.onAppRemoved(TEST_PKG1_APP_ID);
+        mTestLooper.dispatchAll();
+        verify(mPowerManagerInternalMock).setLowPowerStandbyAllowlist(new int[]{TEST_PKG2_APP_ID});
+
+        mController.setPolicy(EMPTY_POLICY);
+        mTestLooper.dispatchAll();
+        verify(mPowerManagerInternalMock).setLowPowerStandbyAllowlist(new int[0]);
+    }
+
     private void setInteractive() throws Exception {
         when(mIPowerManagerMock.isInteractive()).thenReturn(true);
         mContextSpy.sendBroadcast(new Intent(Intent.ACTION_SCREEN_ON));