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

Commit 86b0c6ad authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Update EconomicPolicy loading."

parents 972afec9 e56b3907
Loading
Loading
Loading
Loading
+16 −14
Original line number Diff line number Diff line
@@ -91,6 +91,7 @@ import static android.app.tare.EconomyManager.KEY_AM_REWARD_TOP_ACTIVITY_ONGOING
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.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 android.util.Slog;
@@ -150,13 +150,15 @@ public class AlarmManagerEconomicPolicy extends EconomicPolicy {

    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 @@ public class AlarmManagerEconomicPolicy extends EconomicPolicy {
    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 @@ public class AlarmManagerEconomicPolicy extends EconomicPolicy {
            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,
+40 −18
Original line number Diff line number Diff line
@@ -23,11 +23,13 @@ import android.util.ArraySet;
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 @@ public class CompleteEconomicPolicy extends EconomicPolicy {
    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 @@ public class CompleteEconomicPolicy extends EconomicPolicy {
            mCostModifiers[i] = costModifiers.valueAt(i);
        }

        updateMaxBalances();
        updateLimits();
    }

    @Override
@@ -63,21 +76,22 @@ public class CompleteEconomicPolicy extends EconomicPolicy {
        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();
        }
        mMaxSatiatedBalance = max;

        max = 0;
        for (int i = 0; i < mEnabledEconomicPolicies.size(); ++i) {
            max += mEnabledEconomicPolicies.valueAt(i).getInitialSatiatedConsumptionLimit();
            final EconomicPolicy economicPolicy = mEnabledEconomicPolicies.valueAt(i);
            maxSatiatedBalance += economicPolicy.getMaxSatiatedBalance();
            initialConsumptionLimit += economicPolicy.getInitialSatiatedConsumptionLimit();
            hardConsumptionLimit += economicPolicy.getHardSatiatedConsumptionLimit();
        }
        mConsumptionLimit = max;
        mMaxSatiatedBalance = maxSatiatedBalance;
        mInitialConsumptionLimit = initialConsumptionLimit;
        mHardConsumptionLimit = hardConsumptionLimit;
    }

    @Override
@@ -96,12 +110,12 @@ public class CompleteEconomicPolicy extends EconomicPolicy {

    @Override
    long getInitialSatiatedConsumptionLimit() {
        return mConsumptionLimit;
        return mInitialConsumptionLimit;
    }

    @Override
    long getHardSatiatedConsumptionLimit() {
        return mConsumptionLimit;
        return mHardConsumptionLimit;
    }

    @NonNull
@@ -156,6 +170,14 @@ public class CompleteEconomicPolicy extends EconomicPolicy {
        return reward;
    }

    @VisibleForTesting
    static class CompleteInjector extends Injector {

        boolean isPolicyEnabled(int policy) {
            return true;
        }
    }

    @Override
    void dump(IndentingPrintWriter pw) {
        dumpActiveModifiers(pw);
+23 −3
Original line number Diff line number Diff line
@@ -29,10 +29,14 @@ import android.annotation.CallSuper;
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 @@ public abstract class EconomicPolicy {

    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 Math.max(minValCake, defaultValCake);
    }

    @VisibleForTesting
    static class Injector {
        @Nullable
        String getSettingsGlobalString(@NonNull ContentResolver resolver, @NonNull String name) {
            return Settings.Global.getString(resolver, name);
        }
        return defaultValCake;
    }

    protected static void dumpActiveModifiers(IndentingPrintWriter pw) {
+16 −16
Original line number Diff line number Diff line
@@ -100,6 +100,7 @@ import static android.app.tare.EconomyManager.KEY_JS_REWARD_TOP_ACTIVITY_ONGOING
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.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 android.util.Slog;
@@ -152,13 +152,15 @@ public class JobSchedulerEconomicPolicy extends EconomicPolicy {

    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 @@ public class JobSchedulerEconomicPolicy extends EconomicPolicy {
    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 @@ public class JobSchedulerEconomicPolicy extends EconomicPolicy {
            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,
+194 −0
Original line number Diff line number Diff line
/*
 * 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"));
    }
}
Loading