1 /*
2  * Copyright (C) 2021 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.server.tare;
18 
19 import android.annotation.NonNull;
20 import android.annotation.Nullable;
21 import android.app.tare.EconomyManager;
22 import android.provider.DeviceConfig;
23 import android.util.ArraySet;
24 import android.util.IndentingPrintWriter;
25 import android.util.Slog;
26 import android.util.SparseArray;
27 
28 import com.android.internal.annotations.VisibleForTesting;
29 import com.android.internal.util.ArrayUtils;
30 
31 import libcore.util.EmptyArray;
32 
33 /** Combines all enabled policies into one. */
34 public class CompleteEconomicPolicy extends EconomicPolicy {
35     private static final String TAG = "TARE-" + CompleteEconomicPolicy.class.getSimpleName();
36 
37     private final CompleteInjector mInjector;
38     private final ArraySet<EconomicPolicy> mEnabledEconomicPolicies = new ArraySet<>();
39     /** Lazily populated set of actions covered by this policy. */
40     private final SparseArray<Action> mActions = new SparseArray<>();
41     /** Lazily populated set of rewards covered by this policy. */
42     private final SparseArray<Reward> mRewards = new SparseArray<>();
43     private int mEnabledEconomicPolicyIds = 0;
44     private int[] mCostModifiers = EmptyArray.INT;
45     private long mInitialConsumptionLimit;
46     private long mMinConsumptionLimit;
47     private long mMaxConsumptionLimit;
48 
CompleteEconomicPolicy(@onNull InternalResourceService irs)49     CompleteEconomicPolicy(@NonNull InternalResourceService irs) {
50         this(irs, new CompleteInjector());
51     }
52 
53     @VisibleForTesting
CompleteEconomicPolicy(@onNull InternalResourceService irs, @NonNull CompleteInjector injector)54     CompleteEconomicPolicy(@NonNull InternalResourceService irs,
55             @NonNull CompleteInjector injector) {
56         super(irs);
57         mInjector = injector;
58 
59         if (mInjector.isPolicyEnabled(POLICY_ALARM, null)) {
60             mEnabledEconomicPolicyIds |= POLICY_ALARM;
61             mEnabledEconomicPolicies.add(new AlarmManagerEconomicPolicy(mIrs, mInjector));
62         }
63         if (mInjector.isPolicyEnabled(POLICY_JOB, null)) {
64             mEnabledEconomicPolicyIds |= POLICY_JOB;
65             mEnabledEconomicPolicies.add(new JobSchedulerEconomicPolicy(mIrs, mInjector));
66         }
67     }
68 
69     @Override
setup(@onNull DeviceConfig.Properties properties)70     void setup(@NonNull DeviceConfig.Properties properties) {
71         super.setup(properties);
72 
73         mActions.clear();
74         mRewards.clear();
75 
76         mEnabledEconomicPolicies.clear();
77         mEnabledEconomicPolicyIds = 0;
78         if (mInjector.isPolicyEnabled(POLICY_ALARM, properties)) {
79             mEnabledEconomicPolicyIds |= POLICY_ALARM;
80             mEnabledEconomicPolicies.add(new AlarmManagerEconomicPolicy(mIrs, mInjector));
81         }
82         if (mInjector.isPolicyEnabled(POLICY_JOB, properties)) {
83             mEnabledEconomicPolicyIds |= POLICY_JOB;
84             mEnabledEconomicPolicies.add(new JobSchedulerEconomicPolicy(mIrs, mInjector));
85         }
86 
87         ArraySet<Integer> costModifiers = new ArraySet<>();
88         for (int i = 0; i < mEnabledEconomicPolicies.size(); ++i) {
89             final int[] sm = mEnabledEconomicPolicies.valueAt(i).getCostModifiers();
90             for (int s : sm) {
91                 costModifiers.add(s);
92             }
93         }
94         mCostModifiers = ArrayUtils.convertToIntArray(costModifiers);
95 
96         for (int i = 0; i < mEnabledEconomicPolicies.size(); ++i) {
97             mEnabledEconomicPolicies.valueAt(i).setup(properties);
98         }
99         updateLimits();
100     }
101 
updateLimits()102     private void updateLimits() {
103         long initialConsumptionLimit = 0;
104         long minConsumptionLimit = 0;
105         long maxConsumptionLimit = 0;
106         for (int i = 0; i < mEnabledEconomicPolicies.size(); ++i) {
107             final EconomicPolicy economicPolicy = mEnabledEconomicPolicies.valueAt(i);
108             initialConsumptionLimit += economicPolicy.getInitialSatiatedConsumptionLimit();
109             minConsumptionLimit += economicPolicy.getMinSatiatedConsumptionLimit();
110             maxConsumptionLimit += economicPolicy.getMaxSatiatedConsumptionLimit();
111         }
112         mInitialConsumptionLimit = initialConsumptionLimit;
113         mMinConsumptionLimit = minConsumptionLimit;
114         mMaxConsumptionLimit = maxConsumptionLimit;
115     }
116 
117     @Override
getMinSatiatedBalance(final int userId, @NonNull final String pkgName)118     long getMinSatiatedBalance(final int userId, @NonNull final String pkgName) {
119         long min = 0;
120         for (int i = 0; i < mEnabledEconomicPolicies.size(); ++i) {
121             min += mEnabledEconomicPolicies.valueAt(i).getMinSatiatedBalance(userId, pkgName);
122         }
123         return min;
124     }
125 
126     @Override
getMaxSatiatedBalance(int userId, @NonNull String pkgName)127     long getMaxSatiatedBalance(int userId, @NonNull String pkgName) {
128         long max = 0;
129         for (int i = 0; i < mEnabledEconomicPolicies.size(); ++i) {
130             max += mEnabledEconomicPolicies.valueAt(i).getMaxSatiatedBalance(userId, pkgName);
131         }
132         return max;
133     }
134 
135     @Override
getInitialSatiatedConsumptionLimit()136     long getInitialSatiatedConsumptionLimit() {
137         return mInitialConsumptionLimit;
138     }
139 
140     @Override
getMinSatiatedConsumptionLimit()141     long getMinSatiatedConsumptionLimit() {
142         return mMinConsumptionLimit;
143     }
144 
145     @Override
getMaxSatiatedConsumptionLimit()146     long getMaxSatiatedConsumptionLimit() {
147         return mMaxConsumptionLimit;
148     }
149 
150     @NonNull
151     @Override
getCostModifiers()152     int[] getCostModifiers() {
153         return mCostModifiers == null ? EmptyArray.INT : mCostModifiers;
154     }
155 
156     @Nullable
157     @Override
getAction(@ppAction int actionId)158     Action getAction(@AppAction int actionId) {
159         if (mActions.contains(actionId)) {
160             return mActions.get(actionId);
161         }
162 
163         long ctp = 0, price = 0;
164         boolean exists = false;
165         for (int i = 0; i < mEnabledEconomicPolicies.size(); ++i) {
166             Action a = mEnabledEconomicPolicies.valueAt(i).getAction(actionId);
167             if (a != null) {
168                 exists = true;
169                 ctp += a.costToProduce;
170                 price += a.basePrice;
171             }
172         }
173         final Action action = exists ? new Action(actionId, ctp, price) : null;
174         mActions.put(actionId, action);
175         return action;
176     }
177 
178     @Nullable
179     @Override
getReward(@tilityReward int rewardId)180     Reward getReward(@UtilityReward int rewardId) {
181         if (mRewards.contains(rewardId)) {
182             return mRewards.get(rewardId);
183         }
184 
185         long instantReward = 0, ongoingReward = 0, maxReward = 0;
186         boolean exists = false;
187         for (int i = 0; i < mEnabledEconomicPolicies.size(); ++i) {
188             Reward r = mEnabledEconomicPolicies.valueAt(i).getReward(rewardId);
189             if (r != null) {
190                 exists = true;
191                 instantReward += r.instantReward;
192                 ongoingReward += r.ongoingRewardPerSecond;
193                 maxReward += r.maxDailyReward;
194             }
195         }
196         final Reward reward = exists
197                 ? new Reward(rewardId, instantReward, ongoingReward, maxReward) : null;
198         mRewards.put(rewardId, reward);
199         return reward;
200     }
201 
isPolicyEnabled(@olicy int policyId)202     boolean isPolicyEnabled(@Policy int policyId) {
203         return (mEnabledEconomicPolicyIds & policyId) == policyId;
204     }
205 
getEnabledPolicyIds()206     int getEnabledPolicyIds() {
207         return mEnabledEconomicPolicyIds;
208     }
209 
210     @VisibleForTesting
211     static class CompleteInjector extends Injector {
212 
isPolicyEnabled(int policy, @Nullable DeviceConfig.Properties properties)213         boolean isPolicyEnabled(int policy, @Nullable DeviceConfig.Properties properties) {
214             final String key;
215             final boolean defaultEnable;
216             switch (policy) {
217                 case POLICY_ALARM:
218                     key = EconomyManager.KEY_ENABLE_POLICY_ALARM;
219                     defaultEnable = EconomyManager.DEFAULT_ENABLE_POLICY_ALARM;
220                     break;
221                 case POLICY_JOB:
222                     key = EconomyManager.KEY_ENABLE_POLICY_JOB_SCHEDULER;
223                     defaultEnable = EconomyManager.DEFAULT_ENABLE_POLICY_JOB_SCHEDULER;
224                     break;
225                 default:
226                     Slog.wtf(TAG, "Unknown policy: " + policy);
227                     return false;
228             }
229             if (properties == null) {
230                 return defaultEnable;
231             }
232             return properties.getBoolean(key, defaultEnable);
233         }
234     }
235 
236     @Override
dump(IndentingPrintWriter pw)237     void dump(IndentingPrintWriter pw) {
238         dumpActiveModifiers(pw);
239 
240         pw.println();
241         pw.println(getClass().getSimpleName() + ":");
242         pw.increaseIndent();
243 
244         pw.println("Cached actions:");
245         pw.increaseIndent();
246         for (int i = 0; i < mActions.size(); ++i) {
247             final Action action = mActions.valueAt(i);
248             if (action != null) {
249                 dumpAction(pw, action);
250             }
251         }
252         pw.decreaseIndent();
253 
254         pw.println();
255         pw.println("Cached rewards:");
256         pw.increaseIndent();
257         for (int i = 0; i < mRewards.size(); ++i) {
258             final Reward reward = mRewards.valueAt(i);
259             if (reward != null) {
260                 dumpReward(pw, reward);
261             }
262         }
263         pw.decreaseIndent();
264 
265         for (int i = 0; i < mEnabledEconomicPolicies.size(); i++) {
266             final EconomicPolicy economicPolicy = mEnabledEconomicPolicies.valueAt(i);
267             pw.println();
268             pw.print("(Includes) ");
269             pw.println(economicPolicy.getClass().getSimpleName() + ":");
270             pw.increaseIndent();
271             economicPolicy.dump(pw);
272             pw.decreaseIndent();
273         }
274         pw.decreaseIndent();
275     }
276 }
277