blob: 7a9607657972103d8a3a2db15913e66f14483be6 [file] [log] [blame]
/*
* Copyright (C) 2021 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 android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.tare.EconomyManager;
import android.provider.DeviceConfig;
import android.util.ArraySet;
import android.util.IndentingPrintWriter;
import android.util.Slog;
import android.util.SparseArray;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ArrayUtils;
import libcore.util.EmptyArray;
/** Combines all enabled policies into one. */
public class CompleteEconomicPolicy extends EconomicPolicy {
private static final String TAG = "TARE-" + CompleteEconomicPolicy.class.getSimpleName();
private final CompleteInjector mInjector;
private final ArraySet<EconomicPolicy> mEnabledEconomicPolicies = new ArraySet<>();
/** Lazily populated set of actions covered by this policy. */
private final SparseArray<Action> mActions = new SparseArray<>();
/** Lazily populated set of rewards covered by this policy. */
private final SparseArray<Reward> mRewards = new SparseArray<>();
private int mEnabledEconomicPolicyIds = 0;
private int[] mCostModifiers = EmptyArray.INT;
private long mInitialConsumptionLimit;
private long mMinConsumptionLimit;
private long mMaxConsumptionLimit;
CompleteEconomicPolicy(@NonNull InternalResourceService irs) {
this(irs, new CompleteInjector());
}
@VisibleForTesting
CompleteEconomicPolicy(@NonNull InternalResourceService irs,
@NonNull CompleteInjector injector) {
super(irs);
mInjector = injector;
if (mInjector.isPolicyEnabled(POLICY_ALARM, null)) {
mEnabledEconomicPolicyIds |= POLICY_ALARM;
mEnabledEconomicPolicies.add(new AlarmManagerEconomicPolicy(mIrs, mInjector));
}
if (mInjector.isPolicyEnabled(POLICY_JOB, null)) {
mEnabledEconomicPolicyIds |= POLICY_JOB;
mEnabledEconomicPolicies.add(new JobSchedulerEconomicPolicy(mIrs, mInjector));
}
}
@Override
void setup(@NonNull DeviceConfig.Properties properties) {
super.setup(properties);
mActions.clear();
mRewards.clear();
mEnabledEconomicPolicies.clear();
mEnabledEconomicPolicyIds = 0;
if (mInjector.isPolicyEnabled(POLICY_ALARM, properties)) {
mEnabledEconomicPolicyIds |= POLICY_ALARM;
mEnabledEconomicPolicies.add(new AlarmManagerEconomicPolicy(mIrs, mInjector));
}
if (mInjector.isPolicyEnabled(POLICY_JOB, properties)) {
mEnabledEconomicPolicyIds |= POLICY_JOB;
mEnabledEconomicPolicies.add(new JobSchedulerEconomicPolicy(mIrs, mInjector));
}
ArraySet<Integer> costModifiers = new ArraySet<>();
for (int i = 0; i < mEnabledEconomicPolicies.size(); ++i) {
final int[] sm = mEnabledEconomicPolicies.valueAt(i).getCostModifiers();
for (int s : sm) {
costModifiers.add(s);
}
}
mCostModifiers = ArrayUtils.convertToIntArray(costModifiers);
for (int i = 0; i < mEnabledEconomicPolicies.size(); ++i) {
mEnabledEconomicPolicies.valueAt(i).setup(properties);
}
updateLimits();
}
private void updateLimits() {
long initialConsumptionLimit = 0;
long minConsumptionLimit = 0;
long maxConsumptionLimit = 0;
for (int i = 0; i < mEnabledEconomicPolicies.size(); ++i) {
final EconomicPolicy economicPolicy = mEnabledEconomicPolicies.valueAt(i);
initialConsumptionLimit += economicPolicy.getInitialSatiatedConsumptionLimit();
minConsumptionLimit += economicPolicy.getMinSatiatedConsumptionLimit();
maxConsumptionLimit += economicPolicy.getMaxSatiatedConsumptionLimit();
}
mInitialConsumptionLimit = initialConsumptionLimit;
mMinConsumptionLimit = minConsumptionLimit;
mMaxConsumptionLimit = maxConsumptionLimit;
}
@Override
long getMinSatiatedBalance(final int userId, @NonNull final String pkgName) {
long min = 0;
for (int i = 0; i < mEnabledEconomicPolicies.size(); ++i) {
min += mEnabledEconomicPolicies.valueAt(i).getMinSatiatedBalance(userId, pkgName);
}
return min;
}
@Override
long getMaxSatiatedBalance(int userId, @NonNull String pkgName) {
long max = 0;
for (int i = 0; i < mEnabledEconomicPolicies.size(); ++i) {
max += mEnabledEconomicPolicies.valueAt(i).getMaxSatiatedBalance(userId, pkgName);
}
return max;
}
@Override
long getInitialSatiatedConsumptionLimit() {
return mInitialConsumptionLimit;
}
@Override
long getMinSatiatedConsumptionLimit() {
return mMinConsumptionLimit;
}
@Override
long getMaxSatiatedConsumptionLimit() {
return mMaxConsumptionLimit;
}
@NonNull
@Override
int[] getCostModifiers() {
return mCostModifiers == null ? EmptyArray.INT : mCostModifiers;
}
@Nullable
@Override
Action getAction(@AppAction int actionId) {
if (mActions.contains(actionId)) {
return mActions.get(actionId);
}
long ctp = 0, price = 0;
boolean exists = false;
for (int i = 0; i < mEnabledEconomicPolicies.size(); ++i) {
Action a = mEnabledEconomicPolicies.valueAt(i).getAction(actionId);
if (a != null) {
exists = true;
ctp += a.costToProduce;
price += a.basePrice;
}
}
final Action action = exists ? new Action(actionId, ctp, price) : null;
mActions.put(actionId, action);
return action;
}
@Nullable
@Override
Reward getReward(@UtilityReward int rewardId) {
if (mRewards.contains(rewardId)) {
return mRewards.get(rewardId);
}
long instantReward = 0, ongoingReward = 0, maxReward = 0;
boolean exists = false;
for (int i = 0; i < mEnabledEconomicPolicies.size(); ++i) {
Reward r = mEnabledEconomicPolicies.valueAt(i).getReward(rewardId);
if (r != null) {
exists = true;
instantReward += r.instantReward;
ongoingReward += r.ongoingRewardPerSecond;
maxReward += r.maxDailyReward;
}
}
final Reward reward = exists
? new Reward(rewardId, instantReward, ongoingReward, maxReward) : null;
mRewards.put(rewardId, reward);
return reward;
}
boolean isPolicyEnabled(@Policy int policyId) {
return (mEnabledEconomicPolicyIds & policyId) == policyId;
}
int getEnabledPolicyIds() {
return mEnabledEconomicPolicyIds;
}
@VisibleForTesting
static class CompleteInjector extends Injector {
boolean isPolicyEnabled(int policy, @Nullable DeviceConfig.Properties properties) {
final String key;
final boolean defaultEnable;
switch (policy) {
case POLICY_ALARM:
key = EconomyManager.KEY_ENABLE_POLICY_ALARM;
defaultEnable = EconomyManager.DEFAULT_ENABLE_POLICY_ALARM;
break;
case POLICY_JOB:
key = EconomyManager.KEY_ENABLE_POLICY_JOB_SCHEDULER;
defaultEnable = EconomyManager.DEFAULT_ENABLE_POLICY_JOB_SCHEDULER;
break;
default:
Slog.wtf(TAG, "Unknown policy: " + policy);
return false;
}
if (properties == null) {
return defaultEnable;
}
return properties.getBoolean(key, defaultEnable);
}
}
@Override
void dump(IndentingPrintWriter pw) {
dumpActiveModifiers(pw);
pw.println();
pw.println(getClass().getSimpleName() + ":");
pw.increaseIndent();
pw.println("Cached actions:");
pw.increaseIndent();
for (int i = 0; i < mActions.size(); ++i) {
final Action action = mActions.valueAt(i);
if (action != null) {
dumpAction(pw, action);
}
}
pw.decreaseIndent();
pw.println();
pw.println("Cached rewards:");
pw.increaseIndent();
for (int i = 0; i < mRewards.size(); ++i) {
final Reward reward = mRewards.valueAt(i);
if (reward != null) {
dumpReward(pw, reward);
}
}
pw.decreaseIndent();
for (int i = 0; i < mEnabledEconomicPolicies.size(); i++) {
final EconomicPolicy economicPolicy = mEnabledEconomicPolicies.valueAt(i);
pw.println();
pw.print("(Includes) ");
pw.println(economicPolicy.getClass().getSimpleName() + ":");
pw.increaseIndent();
economicPolicy.dump(pw);
pw.decreaseIndent();
}
pw.decreaseIndent();
}
}