/* * 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 mEnabledEconomicPolicies = new ArraySet<>(); /** Lazily populated set of actions covered by this policy. */ private final SparseArray mActions = new SparseArray<>(); /** Lazily populated set of rewards covered by this policy. */ private final SparseArray 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 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(); } }