Merge "Update EconomicPolicy loading."
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/AlarmManagerEconomicPolicy.java b/apex/jobscheduler/service/java/com/android/server/tare/AlarmManagerEconomicPolicy.java
index d0f719b..a46430f 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/AlarmManagerEconomicPolicy.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/AlarmManagerEconomicPolicy.java
@@ -91,6 +91,7 @@
 import static android.app.tare.EconomyManager.KEY_AM_REWARD_WIDGET_INTERACTION_INSTANT;
 import static android.app.tare.EconomyManager.KEY_AM_REWARD_WIDGET_INTERACTION_MAX;
 import static android.app.tare.EconomyManager.KEY_AM_REWARD_WIDGET_INTERACTION_ONGOING;
+import static android.app.tare.EconomyManager.arcToCake;
 import static android.provider.Settings.Global.TARE_ALARM_MANAGER_CONSTANTS;
 
 import static com.android.server.tare.Modifier.COST_MODIFIER_CHARGING;
@@ -103,7 +104,6 @@
 import android.annotation.Nullable;
 import android.content.ContentResolver;
 import android.provider.DeviceConfig;
-import android.provider.Settings;
 import android.util.IndentingPrintWriter;
 import android.util.KeyValueListParser;
 import android.util.Slog;
@@ -150,13 +150,15 @@
 
     private final KeyValueListParser mParser = new KeyValueListParser(',');
     private final InternalResourceService mInternalResourceService;
+    private final Injector mInjector;
 
     private final SparseArray<Action> mActions = new SparseArray<>();
     private final SparseArray<Reward> mRewards = new SparseArray<>();
 
-    AlarmManagerEconomicPolicy(InternalResourceService irs) {
+    AlarmManagerEconomicPolicy(InternalResourceService irs, Injector injector) {
         super(irs);
         mInternalResourceService = irs;
+        mInjector = injector;
         loadConstants("", null);
     }
 
@@ -164,7 +166,7 @@
     void setup(@NonNull DeviceConfig.Properties properties) {
         super.setup(properties);
         ContentResolver resolver = mInternalResourceService.getContext().getContentResolver();
-        loadConstants(Settings.Global.getString(resolver, TARE_ALARM_MANAGER_CONSTANTS),
+        loadConstants(mInjector.getSettingsGlobalString(resolver, TARE_ALARM_MANAGER_CONSTANTS),
                 properties);
     }
 
@@ -226,20 +228,20 @@
             Slog.e(TAG, "Global setting key incorrect: ", e);
         }
 
-        mMinSatiatedBalanceExempted = getConstantAsCake(mParser, properties,
-                KEY_AM_MIN_SATIATED_BALANCE_EXEMPTED,
-                DEFAULT_AM_MIN_SATIATED_BALANCE_EXEMPTED_CAKES);
         mMinSatiatedBalanceOther = getConstantAsCake(mParser, properties,
-                KEY_AM_MIN_SATIATED_BALANCE_OTHER_APP,
-                DEFAULT_AM_MIN_SATIATED_BALANCE_OTHER_APP_CAKES);
+            KEY_AM_MIN_SATIATED_BALANCE_OTHER_APP, DEFAULT_AM_MIN_SATIATED_BALANCE_OTHER_APP_CAKES);
+        mMinSatiatedBalanceExempted = getConstantAsCake(mParser, properties,
+            KEY_AM_MIN_SATIATED_BALANCE_EXEMPTED, DEFAULT_AM_MIN_SATIATED_BALANCE_EXEMPTED_CAKES,
+            mMinSatiatedBalanceOther);
         mMaxSatiatedBalance = getConstantAsCake(mParser, properties,
-                KEY_AM_MAX_SATIATED_BALANCE,
-                DEFAULT_AM_MAX_SATIATED_BALANCE_CAKES);
+            KEY_AM_MAX_SATIATED_BALANCE, DEFAULT_AM_MAX_SATIATED_BALANCE_CAKES,
+            Math.max(arcToCake(1), mMinSatiatedBalanceExempted));
         mInitialSatiatedConsumptionLimit = getConstantAsCake(mParser, properties,
-                KEY_AM_INITIAL_CONSUMPTION_LIMIT, DEFAULT_AM_INITIAL_CONSUMPTION_LIMIT_CAKES);
-        mHardSatiatedConsumptionLimit = Math.max(mInitialSatiatedConsumptionLimit,
-                getConstantAsCake(mParser, properties,
-                        KEY_AM_HARD_CONSUMPTION_LIMIT, DEFAULT_AM_HARD_CONSUMPTION_LIMIT_CAKES));
+            KEY_AM_INITIAL_CONSUMPTION_LIMIT, DEFAULT_AM_INITIAL_CONSUMPTION_LIMIT_CAKES,
+            arcToCake(1));
+        mHardSatiatedConsumptionLimit = getConstantAsCake(mParser, properties,
+            KEY_AM_HARD_CONSUMPTION_LIMIT, DEFAULT_AM_HARD_CONSUMPTION_LIMIT_CAKES,
+            mInitialSatiatedConsumptionLimit);
 
         final long exactAllowWhileIdleWakeupBasePrice = getConstantAsCake(mParser, properties,
                 KEY_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_EXACT_WAKEUP_BASE_PRICE,
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/CompleteEconomicPolicy.java b/apex/jobscheduler/service/java/com/android/server/tare/CompleteEconomicPolicy.java
index c3eb5bf..5d9cce8 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/CompleteEconomicPolicy.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/CompleteEconomicPolicy.java
@@ -23,11 +23,13 @@
 import android.util.IndentingPrintWriter;
 import android.util.SparseArray;
 
-import libcore.util.EmptyArray;
+import com.android.internal.annotations.VisibleForTesting;
 
+import libcore.util.EmptyArray;
 
 /** Combines all enabled policies into one. */
 public class CompleteEconomicPolicy extends EconomicPolicy {
+
     private final ArraySet<EconomicPolicy> mEnabledEconomicPolicies = new ArraySet<>();
     /** Lazily populated set of actions covered by this policy. */
     private final SparseArray<Action> mActions = new SparseArray<>();
@@ -35,12 +37,23 @@
     private final SparseArray<Reward> mRewards = new SparseArray<>();
     private final int[] mCostModifiers;
     private long mMaxSatiatedBalance;
-    private long mConsumptionLimit;
+    private long mInitialConsumptionLimit;
+    private long mHardConsumptionLimit;
 
     CompleteEconomicPolicy(@NonNull InternalResourceService irs) {
+        this(irs, new CompleteInjector());
+    }
+
+    @VisibleForTesting
+    CompleteEconomicPolicy(@NonNull InternalResourceService irs,
+            @NonNull CompleteInjector injector) {
         super(irs);
-        mEnabledEconomicPolicies.add(new AlarmManagerEconomicPolicy(irs));
-        mEnabledEconomicPolicies.add(new JobSchedulerEconomicPolicy(irs));
+        if (injector.isPolicyEnabled(POLICY_AM)) {
+            mEnabledEconomicPolicies.add(new AlarmManagerEconomicPolicy(irs, injector));
+        }
+        if (injector.isPolicyEnabled(POLICY_JS)) {
+            mEnabledEconomicPolicies.add(new JobSchedulerEconomicPolicy(irs, injector));
+        }
 
         ArraySet<Integer> costModifiers = new ArraySet<>();
         for (int i = 0; i < mEnabledEconomicPolicies.size(); ++i) {
@@ -54,7 +67,7 @@
             mCostModifiers[i] = costModifiers.valueAt(i);
         }
 
-        updateMaxBalances();
+        updateLimits();
     }
 
     @Override
@@ -63,21 +76,22 @@
         for (int i = 0; i < mEnabledEconomicPolicies.size(); ++i) {
             mEnabledEconomicPolicies.valueAt(i).setup(properties);
         }
-        updateMaxBalances();
+        updateLimits();
     }
 
-    private void updateMaxBalances() {
-        long max = 0;
+    private void updateLimits() {
+        long maxSatiatedBalance = 0;
+        long initialConsumptionLimit = 0;
+        long hardConsumptionLimit = 0;
         for (int i = 0; i < mEnabledEconomicPolicies.size(); ++i) {
-            max += mEnabledEconomicPolicies.valueAt(i).getMaxSatiatedBalance();
+            final EconomicPolicy economicPolicy = mEnabledEconomicPolicies.valueAt(i);
+            maxSatiatedBalance += economicPolicy.getMaxSatiatedBalance();
+            initialConsumptionLimit += economicPolicy.getInitialSatiatedConsumptionLimit();
+            hardConsumptionLimit += economicPolicy.getHardSatiatedConsumptionLimit();
         }
-        mMaxSatiatedBalance = max;
-
-        max = 0;
-        for (int i = 0; i < mEnabledEconomicPolicies.size(); ++i) {
-            max += mEnabledEconomicPolicies.valueAt(i).getInitialSatiatedConsumptionLimit();
-        }
-        mConsumptionLimit = max;
+        mMaxSatiatedBalance = maxSatiatedBalance;
+        mInitialConsumptionLimit = initialConsumptionLimit;
+        mHardConsumptionLimit = hardConsumptionLimit;
     }
 
     @Override
@@ -96,12 +110,12 @@
 
     @Override
     long getInitialSatiatedConsumptionLimit() {
-        return mConsumptionLimit;
+        return mInitialConsumptionLimit;
     }
 
     @Override
     long getHardSatiatedConsumptionLimit() {
-        return mConsumptionLimit;
+        return mHardConsumptionLimit;
     }
 
     @NonNull
@@ -156,6 +170,14 @@
         return reward;
     }
 
+    @VisibleForTesting
+    static class CompleteInjector extends Injector {
+
+        boolean isPolicyEnabled(int policy) {
+            return true;
+        }
+    }
+
     @Override
     void dump(IndentingPrintWriter pw) {
         dumpActiveModifiers(pw);
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/EconomicPolicy.java b/apex/jobscheduler/service/java/com/android/server/tare/EconomicPolicy.java
index aeb6abc..0937e7b 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/EconomicPolicy.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/EconomicPolicy.java
@@ -29,10 +29,14 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.content.ContentResolver;
 import android.provider.DeviceConfig;
+import android.provider.Settings;
 import android.util.IndentingPrintWriter;
 import android.util.KeyValueListParser;
 
+import com.android.internal.annotations.VisibleForTesting;
+
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 
@@ -419,18 +423,34 @@
 
     protected long getConstantAsCake(@NonNull KeyValueListParser parser,
             @Nullable DeviceConfig.Properties properties, String key, long defaultValCake) {
+        return getConstantAsCake(parser, properties, key, defaultValCake, 0);
+    }
+
+    protected long getConstantAsCake(@NonNull KeyValueListParser parser,
+            @Nullable DeviceConfig.Properties properties, String key, long defaultValCake,
+            long minValCake) {
         // Don't cross the streams! Mixing Settings/local user config changes with DeviceConfig
         // config can cause issues since the scales may be different, so use one or the other.
         if (parser.size() > 0) {
             // User settings take precedence. Just stick with the Settings constants, even if there
             // are invalid values. It's not worth the time to evaluate all the key/value pairs to
             // make sure there are valid ones before deciding.
-            return parseCreditValue(parser.getString(key, null), defaultValCake);
+            return Math.max(minValCake,
+                parseCreditValue(parser.getString(key, null), defaultValCake));
         }
         if (properties != null) {
-            return parseCreditValue(properties.getString(key, null), defaultValCake);
+            return Math.max(minValCake,
+                parseCreditValue(properties.getString(key, null), defaultValCake));
         }
-        return defaultValCake;
+        return Math.max(minValCake, defaultValCake);
+    }
+
+    @VisibleForTesting
+    static class Injector {
+        @Nullable
+        String getSettingsGlobalString(@NonNull ContentResolver resolver, @NonNull String name) {
+            return Settings.Global.getString(resolver, name);
+        }
     }
 
     protected static void dumpActiveModifiers(IndentingPrintWriter pw) {
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/JobSchedulerEconomicPolicy.java b/apex/jobscheduler/service/java/com/android/server/tare/JobSchedulerEconomicPolicy.java
index 948f0a7..e7db1ad 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/JobSchedulerEconomicPolicy.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/JobSchedulerEconomicPolicy.java
@@ -100,6 +100,7 @@
 import static android.app.tare.EconomyManager.KEY_JS_REWARD_WIDGET_INTERACTION_INSTANT;
 import static android.app.tare.EconomyManager.KEY_JS_REWARD_WIDGET_INTERACTION_MAX;
 import static android.app.tare.EconomyManager.KEY_JS_REWARD_WIDGET_INTERACTION_ONGOING;
+import static android.app.tare.EconomyManager.arcToCake;
 import static android.provider.Settings.Global.TARE_JOB_SCHEDULER_CONSTANTS;
 
 import static com.android.server.tare.Modifier.COST_MODIFIER_CHARGING;
@@ -112,7 +113,6 @@
 import android.annotation.Nullable;
 import android.content.ContentResolver;
 import android.provider.DeviceConfig;
-import android.provider.Settings;
 import android.util.IndentingPrintWriter;
 import android.util.KeyValueListParser;
 import android.util.Slog;
@@ -152,13 +152,15 @@
 
     private final KeyValueListParser mParser = new KeyValueListParser(',');
     private final InternalResourceService mInternalResourceService;
+    private final Injector mInjector;
 
     private final SparseArray<Action> mActions = new SparseArray<>();
     private final SparseArray<Reward> mRewards = new SparseArray<>();
 
-    JobSchedulerEconomicPolicy(InternalResourceService irs) {
+    JobSchedulerEconomicPolicy(InternalResourceService irs, Injector injector) {
         super(irs);
         mInternalResourceService = irs;
+        mInjector = injector;
         loadConstants("", null);
     }
 
@@ -166,7 +168,7 @@
     void setup(@NonNull DeviceConfig.Properties properties) {
         super.setup(properties);
         ContentResolver resolver = mInternalResourceService.getContext().getContentResolver();
-        loadConstants(Settings.Global.getString(resolver, TARE_JOB_SCHEDULER_CONSTANTS),
+        loadConstants(mInjector.getSettingsGlobalString(resolver, TARE_JOB_SCHEDULER_CONSTANTS),
                 properties);
     }
 
@@ -223,22 +225,20 @@
             Slog.e(TAG, "Global setting key incorrect: ", e);
         }
 
-        mMinSatiatedBalanceExempted = getConstantAsCake(mParser, properties,
-                KEY_JS_MIN_SATIATED_BALANCE_EXEMPTED,
-                DEFAULT_JS_MIN_SATIATED_BALANCE_EXEMPTED_CAKES);
         mMinSatiatedBalanceOther = getConstantAsCake(mParser, properties,
-                KEY_JS_MIN_SATIATED_BALANCE_OTHER_APP,
-                DEFAULT_JS_MIN_SATIATED_BALANCE_OTHER_APP_CAKES);
+            KEY_JS_MIN_SATIATED_BALANCE_OTHER_APP, DEFAULT_JS_MIN_SATIATED_BALANCE_OTHER_APP_CAKES);
+        mMinSatiatedBalanceExempted = getConstantAsCake(mParser, properties,
+            KEY_JS_MIN_SATIATED_BALANCE_EXEMPTED, DEFAULT_JS_MIN_SATIATED_BALANCE_EXEMPTED_CAKES,
+            mMinSatiatedBalanceOther);
         mMaxSatiatedBalance = getConstantAsCake(mParser, properties,
-                KEY_JS_MAX_SATIATED_BALANCE,
-                DEFAULT_JS_MAX_SATIATED_BALANCE_CAKES);
+            KEY_JS_MAX_SATIATED_BALANCE, DEFAULT_JS_MAX_SATIATED_BALANCE_CAKES,
+            Math.max(arcToCake(1), mMinSatiatedBalanceExempted));
         mInitialSatiatedConsumptionLimit = getConstantAsCake(mParser, properties,
-                KEY_JS_INITIAL_CONSUMPTION_LIMIT,
-                DEFAULT_JS_INITIAL_CONSUMPTION_LIMIT_CAKES);
-        mHardSatiatedConsumptionLimit = Math.max(mInitialSatiatedConsumptionLimit,
-                getConstantAsCake(mParser, properties,
-                        KEY_JS_HARD_CONSUMPTION_LIMIT,
-                        DEFAULT_JS_HARD_CONSUMPTION_LIMIT_CAKES));
+            KEY_JS_INITIAL_CONSUMPTION_LIMIT, DEFAULT_JS_INITIAL_CONSUMPTION_LIMIT_CAKES,
+            arcToCake(1));
+        mHardSatiatedConsumptionLimit = getConstantAsCake(mParser, properties,
+            KEY_JS_HARD_CONSUMPTION_LIMIT, DEFAULT_JS_HARD_CONSUMPTION_LIMIT_CAKES,
+            mInitialSatiatedConsumptionLimit);
 
         mActions.put(ACTION_JOB_MAX_START, new Action(ACTION_JOB_MAX_START,
                 getConstantAsCake(mParser, properties,
diff --git a/services/tests/mockingservicestests/src/com/android/server/tare/AlarmManagerEconomicPolicyTest.java b/services/tests/mockingservicestests/src/com/android/server/tare/AlarmManagerEconomicPolicyTest.java
new file mode 100644
index 0000000..2e200c3
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/tare/AlarmManagerEconomicPolicyTest.java
@@ -0,0 +1,194 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.tare;
+
+import static android.app.tare.EconomyManager.arcToCake;
+import static android.provider.Settings.Global.TARE_ALARM_MANAGER_CONSTANTS;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.mock;
+
+import android.app.ActivityManager;
+import android.app.IActivityManager;
+import android.app.tare.EconomyManager;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.os.BatteryManager;
+import android.os.Looper;
+import android.os.PowerManager;
+import android.os.RemoteException;
+import android.provider.DeviceConfig;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentMatchers;
+import org.mockito.Mock;
+import org.mockito.MockitoSession;
+import org.mockito.quality.Strictness;
+import org.mockito.stubbing.Answer;
+
+@RunWith(AndroidJUnit4.class)
+public class AlarmManagerEconomicPolicyTest {
+    private AlarmManagerEconomicPolicy mEconomicPolicy;
+    private DeviceConfig.Properties.Builder mDeviceConfigPropertiesBuilder;
+    private EconomicPolicy.Injector mInjector = new InjectorForTest();
+
+    private MockitoSession mMockingSession;
+    @Mock
+    private Context mContext;
+    @Mock
+    private InternalResourceService mIrs;
+
+    private static class InjectorForTest extends EconomicPolicy.Injector {
+        public String settingsConstant;
+
+        @Nullable
+        @Override
+        String getSettingsGlobalString(@NonNull ContentResolver resolver, @NonNull String name) {
+            return TARE_ALARM_MANAGER_CONSTANTS.equals(name) ? settingsConstant : null;
+        }
+    }
+
+    @Before
+    public void setUp() {
+        mMockingSession = mockitoSession()
+            .initMocks(this)
+            .strictness(Strictness.LENIENT)
+            .spyStatic(DeviceConfig.class)
+            .startMocking();
+
+        when(mIrs.getContext()).thenReturn(mContext);
+        when(mContext.getMainLooper()).thenReturn(Looper.getMainLooper());
+        when(mContext.getContentResolver()).thenReturn(mock(ContentResolver.class));
+        // Called by Modifiers.
+        when(mContext.getSystemService(BatteryManager.class))
+            .thenReturn(mock(BatteryManager.class));
+        when(mContext.getSystemService(PowerManager.class))
+            .thenReturn(mock(PowerManager.class));
+        IActivityManager activityManager = ActivityManager.getService();
+        spyOn(activityManager);
+        try {
+            doNothing().when(activityManager).registerUidObserver(any(), anyInt(), anyInt(), any());
+        } catch (RemoteException e) {
+            fail("registerUidObserver threw exception: " + e.getMessage());
+        }
+
+        mDeviceConfigPropertiesBuilder =
+                new DeviceConfig.Properties.Builder(DeviceConfig.NAMESPACE_TARE);
+        doAnswer(
+                (Answer<DeviceConfig.Properties>) invocationOnMock
+                        -> mDeviceConfigPropertiesBuilder.build())
+                .when(() -> DeviceConfig.getProperties(
+                        eq(DeviceConfig.NAMESPACE_TARE), ArgumentMatchers.<String>any()));
+
+        // Initialize real objects.
+        // Capture the listeners.
+        mEconomicPolicy = new AlarmManagerEconomicPolicy(mIrs, mInjector);
+    }
+
+    @After
+    public void tearDown() {
+        if (mMockingSession != null) {
+            mMockingSession.finishMocking();
+        }
+    }
+
+    private void setDeviceConfigCakes(String key, long valCakes) {
+        mDeviceConfigPropertiesBuilder.setString(key, valCakes + "c");
+        mEconomicPolicy.setup(mDeviceConfigPropertiesBuilder.build());
+    }
+
+    @Test
+    public void testDefaults() {
+        assertEquals(EconomyManager.DEFAULT_AM_INITIAL_CONSUMPTION_LIMIT_CAKES,
+                mEconomicPolicy.getInitialSatiatedConsumptionLimit());
+        assertEquals(EconomyManager.DEFAULT_AM_HARD_CONSUMPTION_LIMIT_CAKES,
+                mEconomicPolicy.getHardSatiatedConsumptionLimit());
+        assertEquals(EconomyManager.DEFAULT_AM_MAX_SATIATED_BALANCE_CAKES,
+                mEconomicPolicy.getMaxSatiatedBalance());
+        final String pkgExempted = "com.pkg.exempted";
+        when(mIrs.isPackageExempted(anyInt(), eq(pkgExempted))).thenReturn(true);
+        assertEquals(EconomyManager.DEFAULT_AM_MIN_SATIATED_BALANCE_EXEMPTED_CAKES,
+                mEconomicPolicy.getMinSatiatedBalance(0, pkgExempted));
+        assertEquals(EconomyManager.DEFAULT_AM_MIN_SATIATED_BALANCE_OTHER_APP_CAKES,
+                mEconomicPolicy.getMinSatiatedBalance(0, "com.any.other.app"));
+    }
+
+    @Test
+    public void testConstantsUpdating_ValidValues() {
+        setDeviceConfigCakes(EconomyManager.KEY_AM_INITIAL_CONSUMPTION_LIMIT, arcToCake(5));
+        setDeviceConfigCakes(EconomyManager.KEY_AM_HARD_CONSUMPTION_LIMIT, arcToCake(25));
+        setDeviceConfigCakes(EconomyManager.KEY_AM_MAX_SATIATED_BALANCE, arcToCake(10));
+        setDeviceConfigCakes(EconomyManager.KEY_AM_MIN_SATIATED_BALANCE_EXEMPTED, arcToCake(9));
+        setDeviceConfigCakes(EconomyManager.KEY_AM_MIN_SATIATED_BALANCE_OTHER_APP, arcToCake(7));
+
+        assertEquals(arcToCake(5), mEconomicPolicy.getInitialSatiatedConsumptionLimit());
+        assertEquals(arcToCake(25), mEconomicPolicy.getHardSatiatedConsumptionLimit());
+        assertEquals(arcToCake(10), mEconomicPolicy.getMaxSatiatedBalance());
+        final String pkgExempted = "com.pkg.exempted";
+        when(mIrs.isPackageExempted(anyInt(), eq(pkgExempted))).thenReturn(true);
+        assertEquals(arcToCake(9), mEconomicPolicy.getMinSatiatedBalance(0, pkgExempted));
+        assertEquals(arcToCake(7), mEconomicPolicy.getMinSatiatedBalance(0, "com.any.other.app"));
+    }
+
+    @Test
+    public void testConstantsUpdating_InvalidValues() {
+        // Test negatives.
+        setDeviceConfigCakes(EconomyManager.KEY_AM_INITIAL_CONSUMPTION_LIMIT, arcToCake(-5));
+        setDeviceConfigCakes(EconomyManager.KEY_AM_HARD_CONSUMPTION_LIMIT, arcToCake(-5));
+        setDeviceConfigCakes(EconomyManager.KEY_AM_MAX_SATIATED_BALANCE, arcToCake(-1));
+        setDeviceConfigCakes(EconomyManager.KEY_AM_MIN_SATIATED_BALANCE_EXEMPTED, arcToCake(-2));
+        setDeviceConfigCakes(EconomyManager.KEY_AM_MIN_SATIATED_BALANCE_OTHER_APP, arcToCake(-3));
+
+        assertEquals(arcToCake(1), mEconomicPolicy.getInitialSatiatedConsumptionLimit());
+        assertEquals(arcToCake(1), mEconomicPolicy.getHardSatiatedConsumptionLimit());
+        assertEquals(arcToCake(1), mEconomicPolicy.getMaxSatiatedBalance());
+        final String pkgExempted = "com.pkg.exempted";
+        when(mIrs.isPackageExempted(anyInt(), eq(pkgExempted))).thenReturn(true);
+        assertEquals(arcToCake(0), mEconomicPolicy.getMinSatiatedBalance(0, pkgExempted));
+        assertEquals(arcToCake(0), mEconomicPolicy.getMinSatiatedBalance(0, "com.any.other.app"));
+
+        // Test min+max reversed.
+        setDeviceConfigCakes(EconomyManager.KEY_AM_INITIAL_CONSUMPTION_LIMIT, arcToCake(5));
+        setDeviceConfigCakes(EconomyManager.KEY_AM_HARD_CONSUMPTION_LIMIT, arcToCake(3));
+        setDeviceConfigCakes(EconomyManager.KEY_AM_MAX_SATIATED_BALANCE, arcToCake(10));
+        setDeviceConfigCakes(EconomyManager.KEY_AM_MIN_SATIATED_BALANCE_EXEMPTED, arcToCake(11));
+        setDeviceConfigCakes(EconomyManager.KEY_AM_MIN_SATIATED_BALANCE_OTHER_APP, arcToCake(13));
+
+        assertEquals(arcToCake(5), mEconomicPolicy.getInitialSatiatedConsumptionLimit());
+        assertEquals(arcToCake(5), mEconomicPolicy.getHardSatiatedConsumptionLimit());
+        assertEquals(arcToCake(13), mEconomicPolicy.getMaxSatiatedBalance());
+        assertEquals(arcToCake(13), mEconomicPolicy.getMinSatiatedBalance(0, pkgExempted));
+        assertEquals(arcToCake(13), mEconomicPolicy.getMinSatiatedBalance(0, "com.any.other.app"));
+    }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/tare/CompleteEconomicPolicyTest.java b/services/tests/mockingservicestests/src/com/android/server/tare/CompleteEconomicPolicyTest.java
new file mode 100644
index 0000000..45c97e4
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/tare/CompleteEconomicPolicyTest.java
@@ -0,0 +1,179 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.tare;
+
+import static android.app.tare.EconomyManager.arcToCake;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.mock;
+
+import android.app.ActivityManager;
+import android.app.IActivityManager;
+import android.app.tare.EconomyManager;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.os.BatteryManager;
+import android.os.Looper;
+import android.os.PowerManager;
+import android.os.RemoteException;
+import android.provider.DeviceConfig;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentMatchers;
+import org.mockito.Mock;
+import org.mockito.MockitoSession;
+import org.mockito.quality.Strictness;
+import org.mockito.stubbing.Answer;
+
+@RunWith(AndroidJUnit4.class)
+public class CompleteEconomicPolicyTest {
+    private CompleteEconomicPolicy mEconomicPolicy;
+    private DeviceConfig.Properties.Builder mDeviceConfigPropertiesBuilder;
+    private final CompleteEconomicPolicy.CompleteInjector mInjector = new InjectorForTest();
+
+    private MockitoSession mMockingSession;
+    @Mock
+    private Context mContext;
+    @Mock
+    private InternalResourceService mIrs;
+
+    private static class InjectorForTest extends CompleteEconomicPolicy.CompleteInjector {
+        public String settingsConstant;
+
+        @Nullable
+        @Override
+        String getSettingsGlobalString(@NonNull ContentResolver resolver, @NonNull String name) {
+            return settingsConstant;
+        }
+
+        @Override
+        boolean isPolicyEnabled(int policy) {
+            // Use a limited set of policies so that the test doesn't need to be updated whenever
+            // a policy is added or removed.
+            return policy == EconomicPolicy.POLICY_AM || policy == EconomicPolicy.POLICY_JS;
+        }
+    }
+
+    @Before
+    public void setUp() {
+        mMockingSession = mockitoSession()
+                .initMocks(this)
+                .strictness(Strictness.LENIENT)
+                .spyStatic(DeviceConfig.class)
+                .startMocking();
+
+        when(mIrs.getContext()).thenReturn(mContext);
+        when(mContext.getMainLooper()).thenReturn(Looper.getMainLooper());
+        when(mContext.getContentResolver()).thenReturn(mock(ContentResolver.class));
+        // Called by Modifiers.
+        when(mContext.getSystemService(BatteryManager.class))
+            .thenReturn(mock(BatteryManager.class));
+        when(mContext.getSystemService(PowerManager.class))
+            .thenReturn(mock(PowerManager.class));
+        IActivityManager activityManager = ActivityManager.getService();
+        spyOn(activityManager);
+        try {
+            doNothing().when(activityManager).registerUidObserver(any(), anyInt(), anyInt(), any());
+        } catch (RemoteException e) {
+            fail("registerUidObserver threw exception: " + e.getMessage());
+        }
+
+        mDeviceConfigPropertiesBuilder =
+                new DeviceConfig.Properties.Builder(DeviceConfig.NAMESPACE_TARE);
+        doAnswer(
+                (Answer<DeviceConfig.Properties>) invocationOnMock
+                        -> mDeviceConfigPropertiesBuilder.build())
+                .when(() -> DeviceConfig.getProperties(
+                        eq(DeviceConfig.NAMESPACE_TARE), ArgumentMatchers.<String>any()));
+
+        // Initialize real objects.
+        // Capture the listeners.
+        mEconomicPolicy = new CompleteEconomicPolicy(mIrs, mInjector);
+    }
+
+    @After
+    public void tearDown() {
+        if (mMockingSession != null) {
+            mMockingSession.finishMocking();
+        }
+    }
+
+    private void setDeviceConfigCakes(String key, long valCakes) {
+        mDeviceConfigPropertiesBuilder.setString(key, valCakes + "c");
+        mEconomicPolicy.setup(mDeviceConfigPropertiesBuilder.build());
+    }
+
+    @Test
+    public void testDefaults() {
+        assertEquals(EconomyManager.DEFAULT_JS_INITIAL_CONSUMPTION_LIMIT_CAKES
+                + EconomyManager.DEFAULT_AM_INITIAL_CONSUMPTION_LIMIT_CAKES,
+                mEconomicPolicy.getInitialSatiatedConsumptionLimit());
+        assertEquals(EconomyManager.DEFAULT_JS_HARD_CONSUMPTION_LIMIT_CAKES
+                + EconomyManager.DEFAULT_AM_HARD_CONSUMPTION_LIMIT_CAKES,
+                mEconomicPolicy.getHardSatiatedConsumptionLimit());
+        assertEquals(EconomyManager.DEFAULT_JS_MAX_SATIATED_BALANCE_CAKES
+                + EconomyManager.DEFAULT_AM_MAX_SATIATED_BALANCE_CAKES,
+                mEconomicPolicy.getMaxSatiatedBalance());
+        final String pkgExempted = "com.pkg.exempted";
+        when(mIrs.isPackageExempted(anyInt(), eq(pkgExempted))).thenReturn(true);
+        assertEquals(EconomyManager.DEFAULT_JS_MIN_SATIATED_BALANCE_EXEMPTED_CAKES
+                + EconomyManager.DEFAULT_AM_MIN_SATIATED_BALANCE_EXEMPTED_CAKES,
+                mEconomicPolicy.getMinSatiatedBalance(0, pkgExempted));
+        assertEquals(EconomyManager.DEFAULT_JS_MIN_SATIATED_BALANCE_OTHER_APP_CAKES
+                + EconomyManager.DEFAULT_AM_MIN_SATIATED_BALANCE_OTHER_APP_CAKES,
+                mEconomicPolicy.getMinSatiatedBalance(0, "com.any.other.app"));
+    }
+
+    @Test
+    public void testConstantsUpdated() {
+        setDeviceConfigCakes(EconomyManager.KEY_JS_INITIAL_CONSUMPTION_LIMIT, arcToCake(4));
+        setDeviceConfigCakes(EconomyManager.KEY_AM_INITIAL_CONSUMPTION_LIMIT, arcToCake(6));
+        setDeviceConfigCakes(EconomyManager.KEY_JS_HARD_CONSUMPTION_LIMIT, arcToCake(24));
+        setDeviceConfigCakes(EconomyManager.KEY_AM_HARD_CONSUMPTION_LIMIT, arcToCake(26));
+        setDeviceConfigCakes(EconomyManager.KEY_JS_MAX_SATIATED_BALANCE, arcToCake(9));
+        setDeviceConfigCakes(EconomyManager.KEY_AM_MAX_SATIATED_BALANCE, arcToCake(11));
+        setDeviceConfigCakes(EconomyManager.KEY_JS_MIN_SATIATED_BALANCE_EXEMPTED, arcToCake(8));
+        setDeviceConfigCakes(EconomyManager.KEY_AM_MIN_SATIATED_BALANCE_EXEMPTED, arcToCake(5));
+        setDeviceConfigCakes(EconomyManager.KEY_JS_MIN_SATIATED_BALANCE_OTHER_APP, arcToCake(3));
+        setDeviceConfigCakes(EconomyManager.KEY_AM_MIN_SATIATED_BALANCE_OTHER_APP, arcToCake(2));
+
+        assertEquals(arcToCake(10), mEconomicPolicy.getInitialSatiatedConsumptionLimit());
+        assertEquals(arcToCake(50), mEconomicPolicy.getHardSatiatedConsumptionLimit());
+        assertEquals(arcToCake(20), mEconomicPolicy.getMaxSatiatedBalance());
+        final String pkgExempted = "com.pkg.exempted";
+        when(mIrs.isPackageExempted(anyInt(), eq(pkgExempted))).thenReturn(true);
+        assertEquals(arcToCake(13), mEconomicPolicy.getMinSatiatedBalance(0, pkgExempted));
+        assertEquals(arcToCake(5), mEconomicPolicy.getMinSatiatedBalance(0, "com.any.other.app"));
+    }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/tare/JobSchedulerEconomicPolicyTest.java b/services/tests/mockingservicestests/src/com/android/server/tare/JobSchedulerEconomicPolicyTest.java
new file mode 100644
index 0000000..03ce91a
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/tare/JobSchedulerEconomicPolicyTest.java
@@ -0,0 +1,194 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.tare;
+
+import static android.app.tare.EconomyManager.arcToCake;
+import static android.provider.Settings.Global.TARE_JOB_SCHEDULER_CONSTANTS;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.mock;
+
+import android.app.ActivityManager;
+import android.app.IActivityManager;
+import android.app.tare.EconomyManager;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.os.BatteryManager;
+import android.os.Looper;
+import android.os.PowerManager;
+import android.os.RemoteException;
+import android.provider.DeviceConfig;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentMatchers;
+import org.mockito.Mock;
+import org.mockito.MockitoSession;
+import org.mockito.quality.Strictness;
+import org.mockito.stubbing.Answer;
+
+@RunWith(AndroidJUnit4.class)
+public class JobSchedulerEconomicPolicyTest {
+    private JobSchedulerEconomicPolicy mEconomicPolicy;
+    private DeviceConfig.Properties.Builder mDeviceConfigPropertiesBuilder;
+    private final EconomicPolicy.Injector mInjector = new InjectorForTest();
+
+    private MockitoSession mMockingSession;
+    @Mock
+    private Context mContext;
+    @Mock
+    private InternalResourceService mIrs;
+
+    private static class InjectorForTest extends EconomicPolicy.Injector {
+        public String settingsConstant;
+
+        @Nullable
+        @Override
+        String getSettingsGlobalString(@NonNull ContentResolver resolver, @NonNull String name) {
+            return TARE_JOB_SCHEDULER_CONSTANTS.equals(name) ? settingsConstant : null;
+        }
+    }
+
+    @Before
+    public void setUp() {
+        mMockingSession = mockitoSession()
+                .initMocks(this)
+                .strictness(Strictness.LENIENT)
+                .spyStatic(DeviceConfig.class)
+                .startMocking();
+
+        when(mIrs.getContext()).thenReturn(mContext);
+        when(mContext.getMainLooper()).thenReturn(Looper.getMainLooper());
+        when(mContext.getContentResolver()).thenReturn(mock(ContentResolver.class));
+        // Called by Modifiers.
+        when(mContext.getSystemService(BatteryManager.class))
+            .thenReturn(mock(BatteryManager.class));
+        when(mContext.getSystemService(PowerManager.class))
+            .thenReturn(mock(PowerManager.class));
+        IActivityManager activityManager = ActivityManager.getService();
+        spyOn(activityManager);
+        try {
+            doNothing().when(activityManager).registerUidObserver(any(), anyInt(), anyInt(), any());
+        } catch (RemoteException e) {
+            fail("registerUidObserver threw exception: " + e.getMessage());
+        }
+
+        mDeviceConfigPropertiesBuilder =
+                new DeviceConfig.Properties.Builder(DeviceConfig.NAMESPACE_TARE);
+        doAnswer(
+                (Answer<DeviceConfig.Properties>) invocationOnMock
+                        -> mDeviceConfigPropertiesBuilder.build())
+                .when(() -> DeviceConfig.getProperties(
+                        eq(DeviceConfig.NAMESPACE_TARE), ArgumentMatchers.<String>any()));
+
+        // Initialize real objects.
+        // Capture the listeners.
+        mEconomicPolicy = new JobSchedulerEconomicPolicy(mIrs, mInjector);
+    }
+
+    @After
+    public void tearDown() {
+        if (mMockingSession != null) {
+            mMockingSession.finishMocking();
+        }
+    }
+
+    private void setDeviceConfigCakes(String key, long valCakes) {
+        mDeviceConfigPropertiesBuilder.setString(key, valCakes + "c");
+        mEconomicPolicy.setup(mDeviceConfigPropertiesBuilder.build());
+    }
+
+    @Test
+    public void testDefaults() {
+        assertEquals(EconomyManager.DEFAULT_JS_INITIAL_CONSUMPTION_LIMIT_CAKES,
+                mEconomicPolicy.getInitialSatiatedConsumptionLimit());
+        assertEquals(EconomyManager.DEFAULT_JS_HARD_CONSUMPTION_LIMIT_CAKES,
+                mEconomicPolicy.getHardSatiatedConsumptionLimit());
+        assertEquals(EconomyManager.DEFAULT_JS_MAX_SATIATED_BALANCE_CAKES,
+                mEconomicPolicy.getMaxSatiatedBalance());
+        final String pkgExempted = "com.pkg.exempted";
+        when(mIrs.isPackageExempted(anyInt(), eq(pkgExempted))).thenReturn(true);
+        assertEquals(EconomyManager.DEFAULT_JS_MIN_SATIATED_BALANCE_EXEMPTED_CAKES,
+                mEconomicPolicy.getMinSatiatedBalance(0, pkgExempted));
+        assertEquals(EconomyManager.DEFAULT_JS_MIN_SATIATED_BALANCE_OTHER_APP_CAKES,
+                mEconomicPolicy.getMinSatiatedBalance(0, "com.any.other.app"));
+    }
+
+    @Test
+    public void testConstantsUpdating_ValidValues() {
+        setDeviceConfigCakes(EconomyManager.KEY_JS_INITIAL_CONSUMPTION_LIMIT, arcToCake(5));
+        setDeviceConfigCakes(EconomyManager.KEY_JS_HARD_CONSUMPTION_LIMIT, arcToCake(25));
+        setDeviceConfigCakes(EconomyManager.KEY_JS_MAX_SATIATED_BALANCE, arcToCake(10));
+        setDeviceConfigCakes(EconomyManager.KEY_JS_MIN_SATIATED_BALANCE_EXEMPTED, arcToCake(9));
+        setDeviceConfigCakes(EconomyManager.KEY_JS_MIN_SATIATED_BALANCE_OTHER_APP, arcToCake(7));
+
+        assertEquals(arcToCake(5), mEconomicPolicy.getInitialSatiatedConsumptionLimit());
+        assertEquals(arcToCake(25), mEconomicPolicy.getHardSatiatedConsumptionLimit());
+        assertEquals(arcToCake(10), mEconomicPolicy.getMaxSatiatedBalance());
+        final String pkgExempted = "com.pkg.exempted";
+        when(mIrs.isPackageExempted(anyInt(), eq(pkgExempted))).thenReturn(true);
+        assertEquals(arcToCake(9), mEconomicPolicy.getMinSatiatedBalance(0, pkgExempted));
+        assertEquals(arcToCake(7), mEconomicPolicy.getMinSatiatedBalance(0, "com.any.other.app"));
+    }
+
+    @Test
+    public void testConstantsUpdating_InvalidValues() {
+        // Test negatives.
+        setDeviceConfigCakes(EconomyManager.KEY_JS_INITIAL_CONSUMPTION_LIMIT, arcToCake(-5));
+        setDeviceConfigCakes(EconomyManager.KEY_JS_HARD_CONSUMPTION_LIMIT, arcToCake(-5));
+        setDeviceConfigCakes(EconomyManager.KEY_JS_MAX_SATIATED_BALANCE, arcToCake(-1));
+        setDeviceConfigCakes(EconomyManager.KEY_JS_MIN_SATIATED_BALANCE_EXEMPTED, arcToCake(-2));
+        setDeviceConfigCakes(EconomyManager.KEY_JS_MIN_SATIATED_BALANCE_OTHER_APP, arcToCake(-3));
+
+        assertEquals(arcToCake(1), mEconomicPolicy.getInitialSatiatedConsumptionLimit());
+        assertEquals(arcToCake(1), mEconomicPolicy.getHardSatiatedConsumptionLimit());
+        assertEquals(arcToCake(1), mEconomicPolicy.getMaxSatiatedBalance());
+        final String pkgExempted = "com.pkg.exempted";
+        when(mIrs.isPackageExempted(anyInt(), eq(pkgExempted))).thenReturn(true);
+        assertEquals(arcToCake(0), mEconomicPolicy.getMinSatiatedBalance(0, pkgExempted));
+        assertEquals(arcToCake(0), mEconomicPolicy.getMinSatiatedBalance(0, "com.any.other.app"));
+
+        // Test min+max reversed.
+        setDeviceConfigCakes(EconomyManager.KEY_JS_INITIAL_CONSUMPTION_LIMIT, arcToCake(5));
+        setDeviceConfigCakes(EconomyManager.KEY_JS_HARD_CONSUMPTION_LIMIT, arcToCake(3));
+        setDeviceConfigCakes(EconomyManager.KEY_JS_MAX_SATIATED_BALANCE, arcToCake(10));
+        setDeviceConfigCakes(EconomyManager.KEY_JS_MIN_SATIATED_BALANCE_EXEMPTED, arcToCake(11));
+        setDeviceConfigCakes(EconomyManager.KEY_JS_MIN_SATIATED_BALANCE_OTHER_APP, arcToCake(13));
+
+        assertEquals(arcToCake(5), mEconomicPolicy.getInitialSatiatedConsumptionLimit());
+        assertEquals(arcToCake(5), mEconomicPolicy.getHardSatiatedConsumptionLimit());
+        assertEquals(arcToCake(13), mEconomicPolicy.getMaxSatiatedBalance());
+        assertEquals(arcToCake(13), mEconomicPolicy.getMinSatiatedBalance(0, pkgExempted));
+        assertEquals(arcToCake(13), mEconomicPolicy.getMinSatiatedBalance(0, "com.any.other.app"));
+    }
+}