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 static android.app.tare.EconomyManager.DEFAULT_JS_ACTION_JOB_DEFAULT_RUNNING_BASE_PRICE_CAKES;
20 import static android.app.tare.EconomyManager.DEFAULT_JS_ACTION_JOB_DEFAULT_RUNNING_CTP_CAKES;
21 import static android.app.tare.EconomyManager.DEFAULT_JS_ACTION_JOB_DEFAULT_START_BASE_PRICE_CAKES;
22 import static android.app.tare.EconomyManager.DEFAULT_JS_ACTION_JOB_DEFAULT_START_CTP_CAKES;
23 import static android.app.tare.EconomyManager.DEFAULT_JS_ACTION_JOB_HIGH_RUNNING_BASE_PRICE_CAKES;
24 import static android.app.tare.EconomyManager.DEFAULT_JS_ACTION_JOB_HIGH_RUNNING_CTP_CAKES;
25 import static android.app.tare.EconomyManager.DEFAULT_JS_ACTION_JOB_HIGH_START_BASE_PRICE_CAKES;
26 import static android.app.tare.EconomyManager.DEFAULT_JS_ACTION_JOB_HIGH_START_CTP_CAKES;
27 import static android.app.tare.EconomyManager.DEFAULT_JS_ACTION_JOB_LOW_RUNNING_BASE_PRICE_CAKES;
28 import static android.app.tare.EconomyManager.DEFAULT_JS_ACTION_JOB_LOW_RUNNING_CTP_CAKES;
29 import static android.app.tare.EconomyManager.DEFAULT_JS_ACTION_JOB_LOW_START_BASE_PRICE_CAKES;
30 import static android.app.tare.EconomyManager.DEFAULT_JS_ACTION_JOB_LOW_START_CTP_CAKES;
31 import static android.app.tare.EconomyManager.DEFAULT_JS_ACTION_JOB_MAX_RUNNING_BASE_PRICE_CAKES;
32 import static android.app.tare.EconomyManager.DEFAULT_JS_ACTION_JOB_MAX_RUNNING_CTP_CAKES;
33 import static android.app.tare.EconomyManager.DEFAULT_JS_ACTION_JOB_MAX_START_BASE_PRICE_CAKES;
34 import static android.app.tare.EconomyManager.DEFAULT_JS_ACTION_JOB_MAX_START_CTP_CAKES;
35 import static android.app.tare.EconomyManager.DEFAULT_JS_ACTION_JOB_MIN_RUNNING_BASE_PRICE_CAKES;
36 import static android.app.tare.EconomyManager.DEFAULT_JS_ACTION_JOB_MIN_RUNNING_CTP_CAKES;
37 import static android.app.tare.EconomyManager.DEFAULT_JS_ACTION_JOB_MIN_START_BASE_PRICE_CAKES;
38 import static android.app.tare.EconomyManager.DEFAULT_JS_ACTION_JOB_MIN_START_CTP_CAKES;
39 import static android.app.tare.EconomyManager.DEFAULT_JS_ACTION_JOB_TIMEOUT_PENALTY_BASE_PRICE_CAKES;
40 import static android.app.tare.EconomyManager.DEFAULT_JS_ACTION_JOB_TIMEOUT_PENALTY_CTP_CAKES;
41 import static android.app.tare.EconomyManager.DEFAULT_JS_INITIAL_CONSUMPTION_LIMIT_CAKES;
42 import static android.app.tare.EconomyManager.DEFAULT_JS_MAX_CONSUMPTION_LIMIT_CAKES;
43 import static android.app.tare.EconomyManager.DEFAULT_JS_MAX_SATIATED_BALANCE_CAKES;
44 import static android.app.tare.EconomyManager.DEFAULT_JS_MIN_CONSUMPTION_LIMIT_CAKES;
45 import static android.app.tare.EconomyManager.DEFAULT_JS_MIN_SATIATED_BALANCE_EXEMPTED_CAKES;
46 import static android.app.tare.EconomyManager.DEFAULT_JS_MIN_SATIATED_BALANCE_HEADLESS_SYSTEM_APP_CAKES;
47 import static android.app.tare.EconomyManager.DEFAULT_JS_MIN_SATIATED_BALANCE_INCREMENT_APP_UPDATER_CAKES;
48 import static android.app.tare.EconomyManager.DEFAULT_JS_MIN_SATIATED_BALANCE_OTHER_APP_CAKES;
49 import static android.app.tare.EconomyManager.DEFAULT_JS_REWARD_APP_INSTALL_INSTANT_CAKES;
50 import static android.app.tare.EconomyManager.DEFAULT_JS_REWARD_APP_INSTALL_MAX_CAKES;
51 import static android.app.tare.EconomyManager.DEFAULT_JS_REWARD_APP_INSTALL_ONGOING_CAKES;
52 import static android.app.tare.EconomyManager.DEFAULT_JS_REWARD_NOTIFICATION_INTERACTION_INSTANT_CAKES;
53 import static android.app.tare.EconomyManager.DEFAULT_JS_REWARD_NOTIFICATION_INTERACTION_MAX_CAKES;
54 import static android.app.tare.EconomyManager.DEFAULT_JS_REWARD_NOTIFICATION_INTERACTION_ONGOING_CAKES;
55 import static android.app.tare.EconomyManager.DEFAULT_JS_REWARD_NOTIFICATION_SEEN_INSTANT_CAKES;
56 import static android.app.tare.EconomyManager.DEFAULT_JS_REWARD_NOTIFICATION_SEEN_MAX_CAKES;
57 import static android.app.tare.EconomyManager.DEFAULT_JS_REWARD_NOTIFICATION_SEEN_ONGOING_CAKES;
58 import static android.app.tare.EconomyManager.DEFAULT_JS_REWARD_OTHER_USER_INTERACTION_INSTANT_CAKES;
59 import static android.app.tare.EconomyManager.DEFAULT_JS_REWARD_OTHER_USER_INTERACTION_MAX_CAKES;
60 import static android.app.tare.EconomyManager.DEFAULT_JS_REWARD_OTHER_USER_INTERACTION_ONGOING_CAKES;
61 import static android.app.tare.EconomyManager.DEFAULT_JS_REWARD_TOP_ACTIVITY_INSTANT_CAKES;
62 import static android.app.tare.EconomyManager.DEFAULT_JS_REWARD_TOP_ACTIVITY_MAX_CAKES;
63 import static android.app.tare.EconomyManager.DEFAULT_JS_REWARD_TOP_ACTIVITY_ONGOING_CAKES;
64 import static android.app.tare.EconomyManager.DEFAULT_JS_REWARD_WIDGET_INTERACTION_INSTANT_CAKES;
65 import static android.app.tare.EconomyManager.DEFAULT_JS_REWARD_WIDGET_INTERACTION_MAX_CAKES;
66 import static android.app.tare.EconomyManager.DEFAULT_JS_REWARD_WIDGET_INTERACTION_ONGOING_CAKES;
67 import static android.app.tare.EconomyManager.KEY_JS_ACTION_JOB_DEFAULT_RUNNING_BASE_PRICE;
68 import static android.app.tare.EconomyManager.KEY_JS_ACTION_JOB_DEFAULT_RUNNING_CTP;
69 import static android.app.tare.EconomyManager.KEY_JS_ACTION_JOB_DEFAULT_START_BASE_PRICE;
70 import static android.app.tare.EconomyManager.KEY_JS_ACTION_JOB_DEFAULT_START_CTP;
71 import static android.app.tare.EconomyManager.KEY_JS_ACTION_JOB_HIGH_RUNNING_BASE_PRICE;
72 import static android.app.tare.EconomyManager.KEY_JS_ACTION_JOB_HIGH_RUNNING_CTP;
73 import static android.app.tare.EconomyManager.KEY_JS_ACTION_JOB_HIGH_START_BASE_PRICE;
74 import static android.app.tare.EconomyManager.KEY_JS_ACTION_JOB_HIGH_START_CTP;
75 import static android.app.tare.EconomyManager.KEY_JS_ACTION_JOB_LOW_RUNNING_BASE_PRICE;
76 import static android.app.tare.EconomyManager.KEY_JS_ACTION_JOB_LOW_RUNNING_CTP;
77 import static android.app.tare.EconomyManager.KEY_JS_ACTION_JOB_LOW_START_BASE_PRICE;
78 import static android.app.tare.EconomyManager.KEY_JS_ACTION_JOB_LOW_START_CTP;
79 import static android.app.tare.EconomyManager.KEY_JS_ACTION_JOB_MAX_RUNNING_BASE_PRICE;
80 import static android.app.tare.EconomyManager.KEY_JS_ACTION_JOB_MAX_RUNNING_CTP;
81 import static android.app.tare.EconomyManager.KEY_JS_ACTION_JOB_MAX_START_BASE_PRICE;
82 import static android.app.tare.EconomyManager.KEY_JS_ACTION_JOB_MAX_START_CTP;
83 import static android.app.tare.EconomyManager.KEY_JS_ACTION_JOB_MIN_RUNNING_BASE_PRICE;
84 import static android.app.tare.EconomyManager.KEY_JS_ACTION_JOB_MIN_RUNNING_CTP;
85 import static android.app.tare.EconomyManager.KEY_JS_ACTION_JOB_MIN_START_BASE_PRICE;
86 import static android.app.tare.EconomyManager.KEY_JS_ACTION_JOB_MIN_START_CTP;
87 import static android.app.tare.EconomyManager.KEY_JS_ACTION_JOB_TIMEOUT_PENALTY_BASE_PRICE;
88 import static android.app.tare.EconomyManager.KEY_JS_ACTION_JOB_TIMEOUT_PENALTY_CTP;
89 import static android.app.tare.EconomyManager.KEY_JS_INITIAL_CONSUMPTION_LIMIT;
90 import static android.app.tare.EconomyManager.KEY_JS_MAX_CONSUMPTION_LIMIT;
91 import static android.app.tare.EconomyManager.KEY_JS_MAX_SATIATED_BALANCE;
92 import static android.app.tare.EconomyManager.KEY_JS_MIN_CONSUMPTION_LIMIT;
93 import static android.app.tare.EconomyManager.KEY_JS_MIN_SATIATED_BALANCE_EXEMPTED;
94 import static android.app.tare.EconomyManager.KEY_JS_MIN_SATIATED_BALANCE_HEADLESS_SYSTEM_APP;
95 import static android.app.tare.EconomyManager.KEY_JS_MIN_SATIATED_BALANCE_INCREMENT_APP_UPDATER;
96 import static android.app.tare.EconomyManager.KEY_JS_MIN_SATIATED_BALANCE_OTHER_APP;
97 import static android.app.tare.EconomyManager.KEY_JS_REWARD_APP_INSTALL_INSTANT;
98 import static android.app.tare.EconomyManager.KEY_JS_REWARD_APP_INSTALL_MAX;
99 import static android.app.tare.EconomyManager.KEY_JS_REWARD_APP_INSTALL_ONGOING;
100 import static android.app.tare.EconomyManager.KEY_JS_REWARD_NOTIFICATION_INTERACTION_INSTANT;
101 import static android.app.tare.EconomyManager.KEY_JS_REWARD_NOTIFICATION_INTERACTION_MAX;
102 import static android.app.tare.EconomyManager.KEY_JS_REWARD_NOTIFICATION_INTERACTION_ONGOING;
103 import static android.app.tare.EconomyManager.KEY_JS_REWARD_NOTIFICATION_SEEN_INSTANT;
104 import static android.app.tare.EconomyManager.KEY_JS_REWARD_NOTIFICATION_SEEN_MAX;
105 import static android.app.tare.EconomyManager.KEY_JS_REWARD_NOTIFICATION_SEEN_ONGOING;
106 import static android.app.tare.EconomyManager.KEY_JS_REWARD_OTHER_USER_INTERACTION_INSTANT;
107 import static android.app.tare.EconomyManager.KEY_JS_REWARD_OTHER_USER_INTERACTION_MAX;
108 import static android.app.tare.EconomyManager.KEY_JS_REWARD_OTHER_USER_INTERACTION_ONGOING;
109 import static android.app.tare.EconomyManager.KEY_JS_REWARD_TOP_ACTIVITY_INSTANT;
110 import static android.app.tare.EconomyManager.KEY_JS_REWARD_TOP_ACTIVITY_MAX;
111 import static android.app.tare.EconomyManager.KEY_JS_REWARD_TOP_ACTIVITY_ONGOING;
112 import static android.app.tare.EconomyManager.KEY_JS_REWARD_WIDGET_INTERACTION_INSTANT;
113 import static android.app.tare.EconomyManager.KEY_JS_REWARD_WIDGET_INTERACTION_MAX;
114 import static android.app.tare.EconomyManager.KEY_JS_REWARD_WIDGET_INTERACTION_ONGOING;
115 import static android.app.tare.EconomyManager.arcToCake;
116 import static android.provider.Settings.Global.TARE_JOB_SCHEDULER_CONSTANTS;
117 
118 import static com.android.server.tare.Modifier.COST_MODIFIER_CHARGING;
119 import static com.android.server.tare.Modifier.COST_MODIFIER_DEVICE_IDLE;
120 import static com.android.server.tare.Modifier.COST_MODIFIER_POWER_SAVE_MODE;
121 import static com.android.server.tare.Modifier.COST_MODIFIER_PROCESS_STATE;
122 import static com.android.server.tare.TareUtils.appToString;
123 import static com.android.server.tare.TareUtils.cakeToString;
124 
125 import android.annotation.NonNull;
126 import android.annotation.Nullable;
127 import android.content.ContentResolver;
128 import android.provider.DeviceConfig;
129 import android.util.IndentingPrintWriter;
130 import android.util.KeyValueListParser;
131 import android.util.Slog;
132 import android.util.SparseArray;
133 
134 /**
135  * Policy defining pricing information and daily ARC requirements and suggestions for
136  * JobScheduler.
137  */
138 public class JobSchedulerEconomicPolicy extends EconomicPolicy {
139     private static final String TAG = "TARE- " + JobSchedulerEconomicPolicy.class.getSimpleName();
140 
141     public static final int ACTION_JOB_MAX_START = TYPE_ACTION | POLICY_JOB | 0;
142     public static final int ACTION_JOB_MAX_RUNNING = TYPE_ACTION | POLICY_JOB | 1;
143     public static final int ACTION_JOB_HIGH_START = TYPE_ACTION | POLICY_JOB | 2;
144     public static final int ACTION_JOB_HIGH_RUNNING = TYPE_ACTION | POLICY_JOB | 3;
145     public static final int ACTION_JOB_DEFAULT_START = TYPE_ACTION | POLICY_JOB | 4;
146     public static final int ACTION_JOB_DEFAULT_RUNNING = TYPE_ACTION | POLICY_JOB | 5;
147     public static final int ACTION_JOB_LOW_START = TYPE_ACTION | POLICY_JOB | 6;
148     public static final int ACTION_JOB_LOW_RUNNING = TYPE_ACTION | POLICY_JOB | 7;
149     public static final int ACTION_JOB_MIN_START = TYPE_ACTION | POLICY_JOB | 8;
150     public static final int ACTION_JOB_MIN_RUNNING = TYPE_ACTION | POLICY_JOB | 9;
151     public static final int ACTION_JOB_TIMEOUT = TYPE_ACTION | POLICY_JOB | 10;
152 
153     public static final int REWARD_APP_INSTALL = TYPE_REWARD | POLICY_JOB | 0;
154 
155     private static final int[] COST_MODIFIERS = new int[]{
156             COST_MODIFIER_CHARGING,
157             COST_MODIFIER_DEVICE_IDLE,
158             COST_MODIFIER_POWER_SAVE_MODE,
159             COST_MODIFIER_PROCESS_STATE
160     };
161 
162     private long mMinSatiatedBalanceExempted;
163     private long mMinSatiatedBalanceHeadlessSystemApp;
164     private long mMinSatiatedBalanceOther;
165     private long mMinSatiatedBalanceIncrementalAppUpdater;
166     private long mMaxSatiatedBalance;
167     private long mInitialSatiatedConsumptionLimit;
168     private long mMinSatiatedConsumptionLimit;
169     private long mMaxSatiatedConsumptionLimit;
170 
171     private final KeyValueListParser mParser = new KeyValueListParser(',');
172     private final Injector mInjector;
173 
174     private final SparseArray<Action> mActions = new SparseArray<>();
175     private final SparseArray<Reward> mRewards = new SparseArray<>();
176 
JobSchedulerEconomicPolicy(InternalResourceService irs, Injector injector)177     JobSchedulerEconomicPolicy(InternalResourceService irs, Injector injector) {
178         super(irs);
179         mInjector = injector;
180         loadConstants("", null);
181     }
182 
183     @Override
setup(@onNull DeviceConfig.Properties properties)184     void setup(@NonNull DeviceConfig.Properties properties) {
185         super.setup(properties);
186         final ContentResolver resolver = mIrs.getContext().getContentResolver();
187         loadConstants(mInjector.getSettingsGlobalString(resolver, TARE_JOB_SCHEDULER_CONSTANTS),
188                 properties);
189     }
190 
191     @Override
getMinSatiatedBalance(final int userId, @NonNull final String pkgName)192     long getMinSatiatedBalance(final int userId, @NonNull final String pkgName) {
193         if (mIrs.isPackageRestricted(userId, pkgName)) {
194             return 0;
195         }
196 
197         final long baseBalance;
198         if (mIrs.isPackageExempted(userId, pkgName)) {
199             baseBalance = mMinSatiatedBalanceExempted;
200         } else if (mIrs.isHeadlessSystemApp(userId, pkgName)) {
201             baseBalance = mMinSatiatedBalanceHeadlessSystemApp;
202         } else {
203             baseBalance = mMinSatiatedBalanceOther;
204         }
205 
206         long minBalance = baseBalance;
207 
208         final int updateResponsibilityCount = mIrs.getAppUpdateResponsibilityCount(userId, pkgName);
209         minBalance += updateResponsibilityCount * mMinSatiatedBalanceIncrementalAppUpdater;
210 
211         return Math.min(minBalance, mMaxSatiatedBalance);
212     }
213 
214     @Override
getMaxSatiatedBalance(int userId, @NonNull String pkgName)215     long getMaxSatiatedBalance(int userId, @NonNull String pkgName) {
216         if (mIrs.isPackageRestricted(userId, pkgName)) {
217             return 0;
218         }
219         final InstalledPackageInfo ipo = mIrs.getInstalledPackageInfo(userId, pkgName);
220         if (ipo == null) {
221             Slog.wtfStack(TAG,
222                     "Tried to get max balance of invalid app: " + appToString(userId, pkgName));
223         } else {
224             // A system installer's max balance is elevated for some time after first boot so
225             // they can use jobs to download and install apps.
226             if (ipo.isSystemInstaller) {
227                 final long timeSinceFirstSetupMs = mIrs.getRealtimeSinceFirstSetupMs();
228                 final boolean stillExempted = timeSinceFirstSetupMs
229                         < InternalResourceService.INSTALLER_FIRST_SETUP_GRACE_PERIOD_MS;
230                 if (stillExempted) {
231                     return mMaxSatiatedConsumptionLimit;
232                 }
233             }
234         }
235         return mMaxSatiatedBalance;
236     }
237 
238     @Override
239     long getInitialSatiatedConsumptionLimit() {
240         return mInitialSatiatedConsumptionLimit;
241     }
242 
243     @Override
244     long getMinSatiatedConsumptionLimit() {
245         return mMinSatiatedConsumptionLimit;
246     }
247 
248     @Override
249     long getMaxSatiatedConsumptionLimit() {
250         return mMaxSatiatedConsumptionLimit;
251     }
252 
253     @NonNull
254     @Override
255     int[] getCostModifiers() {
256         return COST_MODIFIERS;
257     }
258 
259     @Nullable
260     @Override
261     Action getAction(@AppAction int actionId) {
262         return mActions.get(actionId);
263     }
264 
265     @Nullable
266     @Override
267     Reward getReward(@UtilityReward int rewardId) {
268         return mRewards.get(rewardId);
269     }
270 
271     private void loadConstants(String policyValuesString,
272             @Nullable DeviceConfig.Properties properties) {
273         mActions.clear();
274         mRewards.clear();
275 
276         try {
277             mParser.setString(policyValuesString);
278         } catch (IllegalArgumentException e) {
279             Slog.e(TAG, "Global setting key incorrect: ", e);
280         }
281 
282         mMinSatiatedBalanceOther = getConstantAsCake(mParser, properties,
283             KEY_JS_MIN_SATIATED_BALANCE_OTHER_APP, DEFAULT_JS_MIN_SATIATED_BALANCE_OTHER_APP_CAKES);
284         mMinSatiatedBalanceHeadlessSystemApp = getConstantAsCake(mParser, properties,
285                 KEY_JS_MIN_SATIATED_BALANCE_HEADLESS_SYSTEM_APP,
286                 DEFAULT_JS_MIN_SATIATED_BALANCE_HEADLESS_SYSTEM_APP_CAKES,
287                 mMinSatiatedBalanceOther);
288         mMinSatiatedBalanceExempted = getConstantAsCake(mParser, properties,
289                 KEY_JS_MIN_SATIATED_BALANCE_EXEMPTED,
290                 DEFAULT_JS_MIN_SATIATED_BALANCE_EXEMPTED_CAKES,
291                 mMinSatiatedBalanceHeadlessSystemApp);
292         mMinSatiatedBalanceIncrementalAppUpdater = getConstantAsCake(mParser, properties,
293                 KEY_JS_MIN_SATIATED_BALANCE_INCREMENT_APP_UPDATER,
294                 DEFAULT_JS_MIN_SATIATED_BALANCE_INCREMENT_APP_UPDATER_CAKES);
295         mMaxSatiatedBalance = getConstantAsCake(mParser, properties,
296             KEY_JS_MAX_SATIATED_BALANCE, DEFAULT_JS_MAX_SATIATED_BALANCE_CAKES,
297             Math.max(arcToCake(1), mMinSatiatedBalanceExempted));
298         mMinSatiatedConsumptionLimit = getConstantAsCake(mParser, properties,
299                 KEY_JS_MIN_CONSUMPTION_LIMIT, DEFAULT_JS_MIN_CONSUMPTION_LIMIT_CAKES,
300                 arcToCake(1));
301         mInitialSatiatedConsumptionLimit = getConstantAsCake(mParser, properties,
302                 KEY_JS_INITIAL_CONSUMPTION_LIMIT, DEFAULT_JS_INITIAL_CONSUMPTION_LIMIT_CAKES,
303                 mMinSatiatedConsumptionLimit);
304         mMaxSatiatedConsumptionLimit = getConstantAsCake(mParser, properties,
305                 KEY_JS_MAX_CONSUMPTION_LIMIT, DEFAULT_JS_MAX_CONSUMPTION_LIMIT_CAKES,
306                 mInitialSatiatedConsumptionLimit);
307 
308         mActions.put(ACTION_JOB_MAX_START, new Action(ACTION_JOB_MAX_START,
309                 getConstantAsCake(mParser, properties,
310                         KEY_JS_ACTION_JOB_MAX_START_CTP,
311                         DEFAULT_JS_ACTION_JOB_MAX_START_CTP_CAKES),
312                 getConstantAsCake(mParser, properties,
313                         KEY_JS_ACTION_JOB_MAX_START_BASE_PRICE,
314                         DEFAULT_JS_ACTION_JOB_MAX_START_BASE_PRICE_CAKES)));
315         mActions.put(ACTION_JOB_MAX_RUNNING, new Action(ACTION_JOB_MAX_RUNNING,
316                 getConstantAsCake(mParser, properties,
317                         KEY_JS_ACTION_JOB_MAX_RUNNING_CTP,
318                         DEFAULT_JS_ACTION_JOB_MAX_RUNNING_CTP_CAKES),
319                 getConstantAsCake(mParser, properties,
320                         KEY_JS_ACTION_JOB_MAX_RUNNING_BASE_PRICE,
321                         DEFAULT_JS_ACTION_JOB_MAX_RUNNING_BASE_PRICE_CAKES)));
322         mActions.put(ACTION_JOB_HIGH_START, new Action(ACTION_JOB_HIGH_START,
323                 getConstantAsCake(mParser, properties,
324                         KEY_JS_ACTION_JOB_HIGH_START_CTP,
325                         DEFAULT_JS_ACTION_JOB_HIGH_START_CTP_CAKES),
326                 getConstantAsCake(mParser, properties,
327                         KEY_JS_ACTION_JOB_HIGH_START_BASE_PRICE,
328                         DEFAULT_JS_ACTION_JOB_HIGH_START_BASE_PRICE_CAKES)));
329         mActions.put(ACTION_JOB_HIGH_RUNNING, new Action(ACTION_JOB_HIGH_RUNNING,
330                 getConstantAsCake(mParser, properties,
331                         KEY_JS_ACTION_JOB_HIGH_RUNNING_CTP,
332                         DEFAULT_JS_ACTION_JOB_HIGH_RUNNING_CTP_CAKES),
333                 getConstantAsCake(mParser, properties,
334                         KEY_JS_ACTION_JOB_HIGH_RUNNING_BASE_PRICE,
335                         DEFAULT_JS_ACTION_JOB_HIGH_RUNNING_BASE_PRICE_CAKES)));
336         mActions.put(ACTION_JOB_DEFAULT_START, new Action(ACTION_JOB_DEFAULT_START,
337                 getConstantAsCake(mParser, properties,
338                         KEY_JS_ACTION_JOB_DEFAULT_START_CTP,
339                         DEFAULT_JS_ACTION_JOB_DEFAULT_START_CTP_CAKES),
340                 getConstantAsCake(mParser, properties,
341                         KEY_JS_ACTION_JOB_DEFAULT_START_BASE_PRICE,
342                         DEFAULT_JS_ACTION_JOB_DEFAULT_START_BASE_PRICE_CAKES)));
343         mActions.put(ACTION_JOB_DEFAULT_RUNNING, new Action(ACTION_JOB_DEFAULT_RUNNING,
344                 getConstantAsCake(mParser, properties,
345                         KEY_JS_ACTION_JOB_DEFAULT_RUNNING_CTP,
346                         DEFAULT_JS_ACTION_JOB_DEFAULT_RUNNING_CTP_CAKES),
347                 getConstantAsCake(mParser, properties,
348                         KEY_JS_ACTION_JOB_DEFAULT_RUNNING_BASE_PRICE,
349                         DEFAULT_JS_ACTION_JOB_DEFAULT_RUNNING_BASE_PRICE_CAKES)));
350         mActions.put(ACTION_JOB_LOW_START, new Action(ACTION_JOB_LOW_START,
351                 getConstantAsCake(mParser, properties,
352                         KEY_JS_ACTION_JOB_LOW_START_CTP,
353                         DEFAULT_JS_ACTION_JOB_LOW_START_CTP_CAKES),
354                 getConstantAsCake(mParser, properties,
355                         KEY_JS_ACTION_JOB_LOW_START_BASE_PRICE,
356                         DEFAULT_JS_ACTION_JOB_LOW_START_BASE_PRICE_CAKES)));
357         mActions.put(ACTION_JOB_LOW_RUNNING, new Action(ACTION_JOB_LOW_RUNNING,
358                 getConstantAsCake(mParser, properties,
359                         KEY_JS_ACTION_JOB_LOW_RUNNING_CTP,
360                         DEFAULT_JS_ACTION_JOB_LOW_RUNNING_CTP_CAKES),
361                 getConstantAsCake(mParser, properties,
362                         KEY_JS_ACTION_JOB_LOW_RUNNING_BASE_PRICE,
363                         DEFAULT_JS_ACTION_JOB_LOW_RUNNING_BASE_PRICE_CAKES)));
364         mActions.put(ACTION_JOB_MIN_START, new Action(ACTION_JOB_MIN_START,
365                 getConstantAsCake(mParser, properties,
366                         KEY_JS_ACTION_JOB_MIN_START_CTP,
367                         DEFAULT_JS_ACTION_JOB_MIN_START_CTP_CAKES),
368                 getConstantAsCake(mParser, properties,
369                         KEY_JS_ACTION_JOB_MIN_START_BASE_PRICE,
370                         DEFAULT_JS_ACTION_JOB_MIN_START_BASE_PRICE_CAKES)));
371         mActions.put(ACTION_JOB_MIN_RUNNING, new Action(ACTION_JOB_MIN_RUNNING,
372                 getConstantAsCake(mParser, properties,
373                         KEY_JS_ACTION_JOB_MIN_RUNNING_CTP,
374                         DEFAULT_JS_ACTION_JOB_MIN_RUNNING_CTP_CAKES),
375                 getConstantAsCake(mParser, properties,
376                         KEY_JS_ACTION_JOB_MIN_RUNNING_BASE_PRICE,
377                         DEFAULT_JS_ACTION_JOB_MIN_RUNNING_BASE_PRICE_CAKES)));
378         mActions.put(ACTION_JOB_TIMEOUT, new Action(ACTION_JOB_TIMEOUT,
379                 getConstantAsCake(mParser, properties,
380                         KEY_JS_ACTION_JOB_TIMEOUT_PENALTY_CTP,
381                         DEFAULT_JS_ACTION_JOB_TIMEOUT_PENALTY_CTP_CAKES),
382                 getConstantAsCake(mParser, properties,
383                         KEY_JS_ACTION_JOB_TIMEOUT_PENALTY_BASE_PRICE,
384                         DEFAULT_JS_ACTION_JOB_TIMEOUT_PENALTY_BASE_PRICE_CAKES)));
385 
386         mRewards.put(REWARD_TOP_ACTIVITY, new Reward(REWARD_TOP_ACTIVITY,
387                 getConstantAsCake(mParser, properties,
388                         KEY_JS_REWARD_TOP_ACTIVITY_INSTANT,
389                         DEFAULT_JS_REWARD_TOP_ACTIVITY_INSTANT_CAKES),
390                 getConstantAsCake(mParser, properties,
391                         KEY_JS_REWARD_TOP_ACTIVITY_ONGOING,
392                         DEFAULT_JS_REWARD_TOP_ACTIVITY_ONGOING_CAKES),
393                 getConstantAsCake(mParser, properties,
394                         KEY_JS_REWARD_TOP_ACTIVITY_MAX,
395                         DEFAULT_JS_REWARD_TOP_ACTIVITY_MAX_CAKES)));
396         mRewards.put(REWARD_NOTIFICATION_SEEN, new Reward(REWARD_NOTIFICATION_SEEN,
397                 getConstantAsCake(mParser, properties,
398                         KEY_JS_REWARD_NOTIFICATION_SEEN_INSTANT,
399                         DEFAULT_JS_REWARD_NOTIFICATION_SEEN_INSTANT_CAKES),
400                 getConstantAsCake(mParser, properties,
401                         KEY_JS_REWARD_NOTIFICATION_SEEN_ONGOING,
402                         DEFAULT_JS_REWARD_NOTIFICATION_SEEN_ONGOING_CAKES),
403                 getConstantAsCake(mParser, properties,
404                         KEY_JS_REWARD_NOTIFICATION_SEEN_MAX,
405                         DEFAULT_JS_REWARD_NOTIFICATION_SEEN_MAX_CAKES)));
406         mRewards.put(REWARD_NOTIFICATION_INTERACTION,
407                 new Reward(REWARD_NOTIFICATION_INTERACTION,
408                         getConstantAsCake(mParser, properties,
409                                 KEY_JS_REWARD_NOTIFICATION_INTERACTION_INSTANT,
410                                 DEFAULT_JS_REWARD_NOTIFICATION_INTERACTION_INSTANT_CAKES),
411                         getConstantAsCake(mParser, properties,
412                                 KEY_JS_REWARD_NOTIFICATION_INTERACTION_ONGOING,
413                                 DEFAULT_JS_REWARD_NOTIFICATION_INTERACTION_ONGOING_CAKES),
414                         getConstantAsCake(mParser, properties,
415                                 KEY_JS_REWARD_NOTIFICATION_INTERACTION_MAX,
416                                 DEFAULT_JS_REWARD_NOTIFICATION_INTERACTION_MAX_CAKES)));
417         mRewards.put(REWARD_WIDGET_INTERACTION, new Reward(REWARD_WIDGET_INTERACTION,
418                 getConstantAsCake(mParser, properties,
419                         KEY_JS_REWARD_WIDGET_INTERACTION_INSTANT,
420                         DEFAULT_JS_REWARD_WIDGET_INTERACTION_INSTANT_CAKES),
421                 getConstantAsCake(mParser, properties,
422                         KEY_JS_REWARD_WIDGET_INTERACTION_ONGOING,
423                         DEFAULT_JS_REWARD_WIDGET_INTERACTION_ONGOING_CAKES),
424                 getConstantAsCake(mParser, properties,
425                         KEY_JS_REWARD_WIDGET_INTERACTION_MAX,
426                         DEFAULT_JS_REWARD_WIDGET_INTERACTION_MAX_CAKES)));
427         mRewards.put(REWARD_OTHER_USER_INTERACTION,
428                 new Reward(REWARD_OTHER_USER_INTERACTION,
429                         getConstantAsCake(mParser, properties,
430                                 KEY_JS_REWARD_OTHER_USER_INTERACTION_INSTANT,
431                                 DEFAULT_JS_REWARD_OTHER_USER_INTERACTION_INSTANT_CAKES),
432                         getConstantAsCake(mParser, properties,
433                                 KEY_JS_REWARD_OTHER_USER_INTERACTION_ONGOING,
434                                 DEFAULT_JS_REWARD_OTHER_USER_INTERACTION_ONGOING_CAKES),
435                         getConstantAsCake(mParser, properties,
436                                 KEY_JS_REWARD_OTHER_USER_INTERACTION_MAX,
437                                 DEFAULT_JS_REWARD_OTHER_USER_INTERACTION_MAX_CAKES)));
438         mRewards.put(REWARD_APP_INSTALL,
439                 new Reward(REWARD_APP_INSTALL,
440                         getConstantAsCake(mParser, properties,
441                                 KEY_JS_REWARD_APP_INSTALL_INSTANT,
442                                 DEFAULT_JS_REWARD_APP_INSTALL_INSTANT_CAKES),
443                         getConstantAsCake(mParser, properties,
444                                 KEY_JS_REWARD_APP_INSTALL_ONGOING,
445                                 DEFAULT_JS_REWARD_APP_INSTALL_ONGOING_CAKES),
446                         getConstantAsCake(mParser, properties,
447                                 KEY_JS_REWARD_APP_INSTALL_MAX,
448                                 DEFAULT_JS_REWARD_APP_INSTALL_MAX_CAKES)));
449     }
450 
451     @Override
452     void dump(IndentingPrintWriter pw) {
453         pw.println("Min satiated balance:");
454         pw.increaseIndent();
455         pw.print("Exempted", cakeToString(mMinSatiatedBalanceExempted)).println();
456         pw.print("Other", cakeToString(mMinSatiatedBalanceOther)).println();
457         pw.print("+App Updater", cakeToString(mMinSatiatedBalanceIncrementalAppUpdater)).println();
458         pw.decreaseIndent();
459         pw.print("Max satiated balance", cakeToString(mMaxSatiatedBalance)).println();
460         pw.print("Consumption limits: [");
461         pw.print(cakeToString(mMinSatiatedConsumptionLimit));
462         pw.print(", ");
463         pw.print(cakeToString(mInitialSatiatedConsumptionLimit));
464         pw.print(", ");
465         pw.print(cakeToString(mMaxSatiatedConsumptionLimit));
466         pw.println("]");
467 
468         pw.println();
469         pw.println("Actions:");
470         pw.increaseIndent();
471         for (int i = 0; i < mActions.size(); ++i) {
472             dumpAction(pw, mActions.valueAt(i));
473         }
474         pw.decreaseIndent();
475 
476         pw.println();
477         pw.println("Rewards:");
478         pw.increaseIndent();
479         for (int i = 0; i < mRewards.size(); ++i) {
480             dumpReward(pw, mRewards.valueAt(i));
481         }
482         pw.decreaseIndent();
483     }
484 }
485