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"));
+ }
+}