• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.provider.Settings.Global.TARE_ALARM_MANAGER_CONSTANTS;
20 import static android.provider.Settings.Global.TARE_JOB_SCHEDULER_CONSTANTS;
21 import static android.text.format.DateUtils.DAY_IN_MILLIS;
22 import static android.text.format.DateUtils.HOUR_IN_MILLIS;
23 import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
24 
25 import static com.android.server.tare.TareUtils.appToString;
26 import static com.android.server.tare.TareUtils.cakeToString;
27 import static com.android.server.tare.TareUtils.getCurrentTimeMillis;
28 
29 import android.annotation.NonNull;
30 import android.annotation.Nullable;
31 import android.app.AlarmManager;
32 import android.app.tare.IEconomyManager;
33 import android.app.usage.UsageEvents;
34 import android.app.usage.UsageStatsManagerInternal;
35 import android.content.BroadcastReceiver;
36 import android.content.ContentResolver;
37 import android.content.Context;
38 import android.content.Intent;
39 import android.content.IntentFilter;
40 import android.content.pm.PackageInfo;
41 import android.content.pm.PackageManager;
42 import android.content.pm.PackageManagerInternal;
43 import android.database.ContentObserver;
44 import android.net.Uri;
45 import android.os.BatteryManagerInternal;
46 import android.os.Binder;
47 import android.os.Handler;
48 import android.os.IDeviceIdleController;
49 import android.os.Looper;
50 import android.os.Message;
51 import android.os.PowerManager;
52 import android.os.RemoteException;
53 import android.os.ServiceManager;
54 import android.os.SystemClock;
55 import android.os.UserHandle;
56 import android.provider.DeviceConfig;
57 import android.provider.Settings;
58 import android.util.ArraySet;
59 import android.util.IndentingPrintWriter;
60 import android.util.Log;
61 import android.util.Slog;
62 import android.util.SparseArrayMap;
63 import android.util.SparseSetArray;
64 
65 import com.android.internal.annotations.GuardedBy;
66 import com.android.internal.os.SomeArgs;
67 import com.android.internal.util.ArrayUtils;
68 import com.android.internal.util.DumpUtils;
69 import com.android.server.LocalServices;
70 import com.android.server.SystemService;
71 import com.android.server.pm.UserManagerInternal;
72 import com.android.server.tare.EconomicPolicy.Cost;
73 import com.android.server.tare.EconomyManagerInternal.TareStateChangeListener;
74 
75 import java.io.FileDescriptor;
76 import java.io.PrintWriter;
77 import java.util.ArrayList;
78 import java.util.List;
79 import java.util.concurrent.CopyOnWriteArraySet;
80 
81 /**
82  * Responsible for handling app's ARC count based on events, ensuring ARCs are credited when
83  * appropriate, and reclaiming ARCs at the right times. The IRS deals with the high level details
84  * while the {@link Agent} deals with the nitty-gritty details.
85  *
86  * Note on locking: Any function with the suffix 'Locked' needs to lock on {@link #mLock}.
87  *
88  * @hide
89  */
90 public class InternalResourceService extends SystemService {
91     public static final String TAG = "TARE-IRS";
92     public static final boolean DEBUG = Log.isLoggable("TARE", Log.DEBUG);
93 
94     static final long UNUSED_RECLAMATION_PERIOD_MS = 24 * HOUR_IN_MILLIS;
95     /** How much of an app's unused wealth should be reclaimed periodically. */
96     private static final float DEFAULT_UNUSED_RECLAMATION_PERCENTAGE = .1f;
97     /**
98      * The minimum amount of time an app must not have been used by the user before we start
99      * periodically reclaiming ARCs from it.
100      */
101     private static final long MIN_UNUSED_TIME_MS = 3 * DAY_IN_MILLIS;
102     /** The amount of time to delay reclamation by after boot. */
103     private static final long RECLAMATION_STARTUP_DELAY_MS = 30_000L;
104     /**
105      * The battery level above which we may consider quantitative easing (increasing the consumption
106      * limit).
107      */
108     private static final int QUANTITATIVE_EASING_BATTERY_THRESHOLD = 50;
109     private static final int PACKAGE_QUERY_FLAGS =
110             PackageManager.MATCH_DIRECT_BOOT_AWARE | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
111                     | PackageManager.MATCH_APEX;
112 
113     /** Global lock for all resource economy state. */
114     private final Object mLock = new Object();
115 
116     private final Handler mHandler;
117     private final BatteryManagerInternal mBatteryManagerInternal;
118     private final PackageManager mPackageManager;
119     private final PackageManagerInternal mPackageManagerInternal;
120 
121     private IDeviceIdleController mDeviceIdleController;
122 
123     private final Agent mAgent;
124     private final Analyst mAnalyst;
125     private final ConfigObserver mConfigObserver;
126     private final EconomyManagerStub mEconomyManagerStub;
127     private final Scribe mScribe;
128 
129     @GuardedBy("mLock")
130     private CompleteEconomicPolicy mCompleteEconomicPolicy;
131 
132     @NonNull
133     @GuardedBy("mLock")
134     private final List<PackageInfo> mPkgCache = new ArrayList<>();
135 
136     /** Cached mapping of UIDs (for all users) to a list of packages in the UID. */
137     @GuardedBy("mLock")
138     private final SparseSetArray<String> mUidToPackageCache = new SparseSetArray<>();
139 
140     /** Cached mapping of userId+package to their UIDs (for all users) */
141     @GuardedBy("mPackageToUidCache")
142     private final SparseArrayMap<String, Integer> mPackageToUidCache = new SparseArrayMap<>();
143 
144     private final CopyOnWriteArraySet<TareStateChangeListener> mStateChangeListeners =
145             new CopyOnWriteArraySet<>();
146 
147     /** List of packages that are "exempted" from battery restrictions. */
148     // TODO(144864180): include userID
149     @GuardedBy("mLock")
150     private ArraySet<String> mExemptedApps = new ArraySet<>();
151 
152     private volatile boolean mIsEnabled;
153     private volatile int mBootPhase;
154     private volatile boolean mExemptListLoaded;
155     // In the range [0,100] to represent 0% to 100% battery.
156     @GuardedBy("mLock")
157     private int mCurrentBatteryLevel;
158 
159     private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
160         @Nullable
161         private String getPackageName(Intent intent) {
162             Uri uri = intent.getData();
163             return uri != null ? uri.getSchemeSpecificPart() : null;
164         }
165 
166         @Override
167         public void onReceive(Context context, Intent intent) {
168             switch (intent.getAction()) {
169                 case Intent.ACTION_BATTERY_LEVEL_CHANGED:
170                     onBatteryLevelChanged();
171                     break;
172                 case Intent.ACTION_PACKAGE_FULLY_REMOVED: {
173                     final String pkgName = getPackageName(intent);
174                     final int pkgUid = intent.getIntExtra(Intent.EXTRA_UID, -1);
175                     onPackageRemoved(pkgUid, pkgName);
176                 }
177                 break;
178                 case Intent.ACTION_PACKAGE_ADDED: {
179                     if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
180                         final String pkgName = getPackageName(intent);
181                         final int pkgUid = intent.getIntExtra(Intent.EXTRA_UID, -1);
182                         onPackageAdded(pkgUid, pkgName);
183                     }
184                 }
185                 break;
186                 case Intent.ACTION_PACKAGE_RESTARTED: {
187                     final String pkgName = getPackageName(intent);
188                     final int pkgUid = intent.getIntExtra(Intent.EXTRA_UID, -1);
189                     final int userId = UserHandle.getUserId(pkgUid);
190                     onPackageForceStopped(userId, pkgName);
191                 }
192                 break;
193                 case Intent.ACTION_USER_ADDED: {
194                     final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
195                     onUserAdded(userId);
196                 }
197                 break;
198                 case Intent.ACTION_USER_REMOVED: {
199                     final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
200                     onUserRemoved(userId);
201                 }
202                 break;
203                 case PowerManager.ACTION_POWER_SAVE_WHITELIST_CHANGED:
204                     onExemptionListChanged();
205                     break;
206             }
207         }
208     };
209 
210     private final UsageStatsManagerInternal.UsageEventListener mSurveillanceAgent =
211             new UsageStatsManagerInternal.UsageEventListener() {
212                 /**
213                  * Callback to inform listeners of a new event.
214                  */
215                 @Override
216                 public void onUsageEvent(int userId, @NonNull UsageEvents.Event event) {
217                     mHandler.obtainMessage(MSG_PROCESS_USAGE_EVENT, userId, 0, event)
218                             .sendToTarget();
219                 }
220             };
221 
222     private final AlarmManager.OnAlarmListener mUnusedWealthReclamationListener =
223             new AlarmManager.OnAlarmListener() {
224                 @Override
225                 public void onAlarm() {
226                     synchronized (mLock) {
227                         mAgent.reclaimUnusedAssetsLocked(
228                                 DEFAULT_UNUSED_RECLAMATION_PERCENTAGE, MIN_UNUSED_TIME_MS, false);
229                         mScribe.setLastReclamationTimeLocked(getCurrentTimeMillis());
230                         scheduleUnusedWealthReclamationLocked();
231                     }
232                 }
233             };
234 
235     private static final int MSG_NOTIFY_AFFORDABILITY_CHANGE_LISTENER = 0;
236     private static final int MSG_SCHEDULE_UNUSED_WEALTH_RECLAMATION_EVENT = 1;
237     private static final int MSG_PROCESS_USAGE_EVENT = 2;
238     private static final int MSG_NOTIFY_STATE_CHANGE_LISTENERS = 3;
239     private static final String ALARM_TAG_WEALTH_RECLAMATION = "*tare.reclamation*";
240 
241     /**
242      * Initializes the system service.
243      * <p>
244      * Subclasses must define a single argument constructor that accepts the context
245      * and passes it to super.
246      * </p>
247      *
248      * @param context The system server context.
249      */
InternalResourceService(Context context)250     public InternalResourceService(Context context) {
251         super(context);
252 
253         mHandler = new IrsHandler(TareHandlerThread.get().getLooper());
254         mBatteryManagerInternal = LocalServices.getService(BatteryManagerInternal.class);
255         mPackageManager = context.getPackageManager();
256         mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
257         mEconomyManagerStub = new EconomyManagerStub();
258         mAnalyst = new Analyst();
259         mScribe = new Scribe(this, mAnalyst);
260         mCompleteEconomicPolicy = new CompleteEconomicPolicy(this);
261         mAgent = new Agent(this, mScribe, mAnalyst);
262 
263         mConfigObserver = new ConfigObserver(mHandler, context);
264 
265         publishLocalService(EconomyManagerInternal.class, new LocalService());
266     }
267 
268     @Override
onStart()269     public void onStart() {
270         publishBinderService(Context.RESOURCE_ECONOMY_SERVICE, mEconomyManagerStub);
271     }
272 
273     @Override
onBootPhase(int phase)274     public void onBootPhase(int phase) {
275         mBootPhase = phase;
276 
277         if (PHASE_SYSTEM_SERVICES_READY == phase) {
278             mConfigObserver.start();
279             mDeviceIdleController = IDeviceIdleController.Stub.asInterface(
280                     ServiceManager.getService(Context.DEVICE_IDLE_CONTROLLER));
281             setupEverything();
282         } else if (PHASE_BOOT_COMPLETED == phase) {
283             if (!mExemptListLoaded) {
284                 synchronized (mLock) {
285                     try {
286                         mExemptedApps =
287                                 new ArraySet<>(mDeviceIdleController.getFullPowerWhitelist());
288                     } catch (RemoteException e) {
289                         // Shouldn't happen.
290                         Slog.wtf(TAG, e);
291                     }
292                     mExemptListLoaded = true;
293                 }
294             }
295         }
296     }
297 
298     @NonNull
getLock()299     Object getLock() {
300         return mLock;
301     }
302 
303     /** Returns the installed packages for all users. */
304     @NonNull
305     @GuardedBy("mLock")
getCompleteEconomicPolicyLocked()306     CompleteEconomicPolicy getCompleteEconomicPolicyLocked() {
307         return mCompleteEconomicPolicy;
308     }
309 
310     @NonNull
getInstalledPackages()311     List<PackageInfo> getInstalledPackages() {
312         synchronized (mLock) {
313             return mPkgCache;
314         }
315     }
316 
317     /** Returns the installed packages for the specified user. */
318     @NonNull
getInstalledPackages(final int userId)319     List<PackageInfo> getInstalledPackages(final int userId) {
320         final List<PackageInfo> userPkgs = new ArrayList<>();
321         synchronized (mLock) {
322             for (int i = 0; i < mPkgCache.size(); ++i) {
323                 final PackageInfo packageInfo = mPkgCache.get(i);
324                 if (packageInfo.applicationInfo != null
325                         && UserHandle.getUserId(packageInfo.applicationInfo.uid) == userId) {
326                     userPkgs.add(packageInfo);
327                 }
328             }
329         }
330         return userPkgs;
331     }
332 
333     @GuardedBy("mLock")
getConsumptionLimitLocked()334     long getConsumptionLimitLocked() {
335         return mCurrentBatteryLevel * mScribe.getSatiatedConsumptionLimitLocked() / 100;
336     }
337 
338     @GuardedBy("mLock")
getMinBalanceLocked(final int userId, @NonNull final String pkgName)339     long getMinBalanceLocked(final int userId, @NonNull final String pkgName) {
340         return mCurrentBatteryLevel * mCompleteEconomicPolicy.getMinSatiatedBalance(userId, pkgName)
341                 / 100;
342     }
343 
344     @GuardedBy("mLock")
getInitialSatiatedConsumptionLimitLocked()345     long getInitialSatiatedConsumptionLimitLocked() {
346         return mCompleteEconomicPolicy.getInitialSatiatedConsumptionLimit();
347     }
348 
getUid(final int userId, @NonNull final String pkgName)349     int getUid(final int userId, @NonNull final String pkgName) {
350         synchronized (mPackageToUidCache) {
351             Integer uid = mPackageToUidCache.get(userId, pkgName);
352             if (uid == null) {
353                 uid = mPackageManagerInternal.getPackageUid(pkgName, 0, userId);
354                 mPackageToUidCache.add(userId, pkgName, uid);
355             }
356             return uid;
357         }
358     }
359 
isEnabled()360     boolean isEnabled() {
361         return mIsEnabled;
362     }
363 
isPackageExempted(final int userId, @NonNull String pkgName)364     boolean isPackageExempted(final int userId, @NonNull String pkgName) {
365         synchronized (mLock) {
366             return mExemptedApps.contains(pkgName);
367         }
368     }
369 
isSystem(final int userId, @NonNull String pkgName)370     boolean isSystem(final int userId, @NonNull String pkgName) {
371         if ("android".equals(pkgName)) {
372             return true;
373         }
374         return UserHandle.isCore(getUid(userId, pkgName));
375     }
376 
onBatteryLevelChanged()377     void onBatteryLevelChanged() {
378         synchronized (mLock) {
379             final int newBatteryLevel = getCurrentBatteryLevel();
380             mAnalyst.noteBatteryLevelChange(newBatteryLevel);
381             final boolean increased = newBatteryLevel > mCurrentBatteryLevel;
382             if (increased) {
383                 mAgent.distributeBasicIncomeLocked(newBatteryLevel);
384             } else if (newBatteryLevel == mCurrentBatteryLevel) {
385                 // The broadcast is also sent when the plug type changes...
386                 return;
387             }
388             mCurrentBatteryLevel = newBatteryLevel;
389             adjustCreditSupplyLocked(increased);
390         }
391     }
392 
onDeviceStateChanged()393     void onDeviceStateChanged() {
394         synchronized (mLock) {
395             mAgent.onDeviceStateChangedLocked();
396         }
397     }
398 
onExemptionListChanged()399     void onExemptionListChanged() {
400         final int[] userIds = LocalServices.getService(UserManagerInternal.class).getUserIds();
401         synchronized (mLock) {
402             final ArraySet<String> removed = mExemptedApps;
403             final ArraySet<String> added = new ArraySet<>();
404             try {
405                 mExemptedApps = new ArraySet<>(mDeviceIdleController.getFullPowerWhitelist());
406             } catch (RemoteException e) {
407                 // Shouldn't happen.
408                 Slog.wtf(TAG, e);
409                 return;
410             }
411 
412             for (int i = mExemptedApps.size() - 1; i >= 0; --i) {
413                 final String pkg = mExemptedApps.valueAt(i);
414                 if (!removed.contains(pkg)) {
415                     added.add(pkg);
416                 }
417                 removed.remove(pkg);
418             }
419             for (int a = added.size() - 1; a >= 0; --a) {
420                 final String pkgName = added.valueAt(a);
421                 for (int userId : userIds) {
422                     // Since the exemption list doesn't specify user ID and we track by user ID,
423                     // we need to see if the app exists on the user before talking to the agent.
424                     // Otherwise, we may end up with invalid ledgers.
425                     final boolean appExists = getUid(userId, pkgName) >= 0;
426                     if (appExists) {
427                         mAgent.onAppExemptedLocked(userId, pkgName);
428                     }
429                 }
430             }
431             for (int r = removed.size() - 1; r >= 0; --r) {
432                 final String pkgName = removed.valueAt(r);
433                 for (int userId : userIds) {
434                     // Since the exemption list doesn't specify user ID and we track by user ID,
435                     // we need to see if the app exists on the user before talking to the agent.
436                     // Otherwise, we may end up with invalid ledgers.
437                     final boolean appExists = getUid(userId, pkgName) >= 0;
438                     if (appExists) {
439                         mAgent.onAppUnexemptedLocked(userId, pkgName);
440                     }
441                 }
442             }
443         }
444     }
445 
onPackageAdded(final int uid, @NonNull final String pkgName)446     void onPackageAdded(final int uid, @NonNull final String pkgName) {
447         final int userId = UserHandle.getUserId(uid);
448         final PackageInfo packageInfo;
449         try {
450             packageInfo =
451                     mPackageManager.getPackageInfoAsUser(pkgName, PACKAGE_QUERY_FLAGS, userId);
452         } catch (PackageManager.NameNotFoundException e) {
453             Slog.wtf(TAG, "PM couldn't find newly added package: " + pkgName, e);
454             return;
455         }
456         synchronized (mPackageToUidCache) {
457             mPackageToUidCache.add(userId, pkgName, uid);
458         }
459         synchronized (mLock) {
460             mPkgCache.add(packageInfo);
461             mUidToPackageCache.add(uid, pkgName);
462             // TODO: only do this when the user first launches the app (app leaves stopped state)
463             mAgent.grantBirthrightLocked(userId, pkgName);
464         }
465     }
466 
onPackageForceStopped(final int userId, @NonNull final String pkgName)467     void onPackageForceStopped(final int userId, @NonNull final String pkgName) {
468         synchronized (mLock) {
469             // TODO: reduce ARC count by some amount
470         }
471     }
472 
onPackageRemoved(final int uid, @NonNull final String pkgName)473     void onPackageRemoved(final int uid, @NonNull final String pkgName) {
474         final int userId = UserHandle.getUserId(uid);
475         synchronized (mPackageToUidCache) {
476             mPackageToUidCache.delete(userId, pkgName);
477         }
478         synchronized (mLock) {
479             mUidToPackageCache.remove(uid, pkgName);
480             for (int i = 0; i < mPkgCache.size(); ++i) {
481                 PackageInfo pkgInfo = mPkgCache.get(i);
482                 if (UserHandle.getUserId(pkgInfo.applicationInfo.uid) == userId
483                         && pkgName.equals(pkgInfo.packageName)) {
484                     mPkgCache.remove(i);
485                     break;
486                 }
487             }
488             mAgent.onPackageRemovedLocked(userId, pkgName);
489         }
490     }
491 
onUidStateChanged(final int uid)492     void onUidStateChanged(final int uid) {
493         synchronized (mLock) {
494             final ArraySet<String> pkgNames = getPackagesForUidLocked(uid);
495             if (pkgNames == null) {
496                 Slog.e(TAG, "Don't have packages for uid " + uid);
497             } else {
498                 mAgent.onAppStatesChangedLocked(UserHandle.getUserId(uid), pkgNames);
499             }
500         }
501     }
502 
onUserAdded(final int userId)503     void onUserAdded(final int userId) {
504         synchronized (mLock) {
505             mPkgCache.addAll(
506                     mPackageManager.getInstalledPackagesAsUser(PACKAGE_QUERY_FLAGS, userId));
507             mAgent.grantBirthrightsLocked(userId);
508         }
509     }
510 
onUserRemoved(final int userId)511     void onUserRemoved(final int userId) {
512         synchronized (mLock) {
513             ArrayList<String> removedPkgs = new ArrayList<>();
514             for (int i = mPkgCache.size() - 1; i >= 0; --i) {
515                 PackageInfo pkgInfo = mPkgCache.get(i);
516                 if (UserHandle.getUserId(pkgInfo.applicationInfo.uid) == userId) {
517                     removedPkgs.add(pkgInfo.packageName);
518                     mUidToPackageCache.remove(pkgInfo.applicationInfo.uid);
519                     mPkgCache.remove(i);
520                     break;
521                 }
522             }
523             mAgent.onUserRemovedLocked(userId, removedPkgs);
524         }
525     }
526 
527     /**
528      * Try to increase the consumption limit if apps are reaching the current limit too quickly.
529      */
530     @GuardedBy("mLock")
maybePerformQuantitativeEasingLocked()531     void maybePerformQuantitativeEasingLocked() {
532         // We don't need to increase the limit if the device runs out of consumable credits
533         // when the battery is low.
534         final long remainingConsumableCakes = mScribe.getRemainingConsumableCakesLocked();
535         if (mCurrentBatteryLevel <= QUANTITATIVE_EASING_BATTERY_THRESHOLD
536                 || remainingConsumableCakes > 0) {
537             return;
538         }
539         final long currentConsumptionLimit = mScribe.getSatiatedConsumptionLimitLocked();
540         final long shortfall = (mCurrentBatteryLevel - QUANTITATIVE_EASING_BATTERY_THRESHOLD)
541                 * currentConsumptionLimit / 100;
542         final long newConsumptionLimit = Math.min(currentConsumptionLimit + shortfall,
543                 mCompleteEconomicPolicy.getHardSatiatedConsumptionLimit());
544         if (newConsumptionLimit != currentConsumptionLimit) {
545             Slog.i(TAG, "Increasing consumption limit from " + cakeToString(currentConsumptionLimit)
546                     + " to " + cakeToString(newConsumptionLimit));
547             mScribe.setConsumptionLimitLocked(newConsumptionLimit);
548             adjustCreditSupplyLocked(/* allowIncrease */ true);
549         }
550     }
551 
postAffordabilityChanged(final int userId, @NonNull final String pkgName, @NonNull Agent.ActionAffordabilityNote affordabilityNote)552     void postAffordabilityChanged(final int userId, @NonNull final String pkgName,
553             @NonNull Agent.ActionAffordabilityNote affordabilityNote) {
554         if (DEBUG) {
555             Slog.d(TAG, userId + ":" + pkgName + " affordability changed to "
556                     + affordabilityNote.isCurrentlyAffordable());
557         }
558         final SomeArgs args = SomeArgs.obtain();
559         args.argi1 = userId;
560         args.arg1 = pkgName;
561         args.arg2 = affordabilityNote;
562         mHandler.obtainMessage(MSG_NOTIFY_AFFORDABILITY_CHANGE_LISTENER, args).sendToTarget();
563     }
564 
565     @GuardedBy("mLock")
adjustCreditSupplyLocked(boolean allowIncrease)566     private void adjustCreditSupplyLocked(boolean allowIncrease) {
567         final long newLimit = getConsumptionLimitLocked();
568         final long remainingConsumableCakes = mScribe.getRemainingConsumableCakesLocked();
569         if (remainingConsumableCakes == newLimit) {
570             return;
571         }
572         if (remainingConsumableCakes > newLimit) {
573             mScribe.adjustRemainingConsumableCakesLocked(newLimit - remainingConsumableCakes);
574         } else if (allowIncrease) {
575             final double perc = mCurrentBatteryLevel / 100d;
576             final long shortfall = newLimit - remainingConsumableCakes;
577             mScribe.adjustRemainingConsumableCakesLocked((long) (perc * shortfall));
578         }
579         mAgent.onCreditSupplyChanged();
580     }
581 
582     @GuardedBy("mLock")
processUsageEventLocked(final int userId, @NonNull UsageEvents.Event event)583     private void processUsageEventLocked(final int userId, @NonNull UsageEvents.Event event) {
584         if (!mIsEnabled) {
585             return;
586         }
587         final String pkgName = event.getPackageName();
588         if (DEBUG) {
589             Slog.d(TAG, "Processing event " + event.getEventType()
590                     + " (" + event.mInstanceId + ")"
591                     + " for " + appToString(userId, pkgName));
592         }
593         final long nowElapsed = SystemClock.elapsedRealtime();
594         switch (event.getEventType()) {
595             case UsageEvents.Event.ACTIVITY_RESUMED:
596                 mAgent.noteOngoingEventLocked(userId, pkgName,
597                         EconomicPolicy.REWARD_TOP_ACTIVITY, String.valueOf(event.mInstanceId),
598                         nowElapsed);
599                 break;
600             case UsageEvents.Event.ACTIVITY_PAUSED:
601             case UsageEvents.Event.ACTIVITY_STOPPED:
602             case UsageEvents.Event.ACTIVITY_DESTROYED:
603                 final long now = getCurrentTimeMillis();
604                 mAgent.stopOngoingActionLocked(userId, pkgName,
605                         EconomicPolicy.REWARD_TOP_ACTIVITY, String.valueOf(event.mInstanceId),
606                         nowElapsed, now);
607                 break;
608             case UsageEvents.Event.USER_INTERACTION:
609             case UsageEvents.Event.CHOOSER_ACTION:
610                 mAgent.noteInstantaneousEventLocked(userId, pkgName,
611                         EconomicPolicy.REWARD_OTHER_USER_INTERACTION, null);
612                 break;
613             case UsageEvents.Event.NOTIFICATION_INTERRUPTION:
614             case UsageEvents.Event.NOTIFICATION_SEEN:
615                 mAgent.noteInstantaneousEventLocked(userId, pkgName,
616                         EconomicPolicy.REWARD_NOTIFICATION_SEEN, null);
617                 break;
618         }
619     }
620 
621     @GuardedBy("mLock")
scheduleUnusedWealthReclamationLocked()622     private void scheduleUnusedWealthReclamationLocked() {
623         final long now = getCurrentTimeMillis();
624         final long nextReclamationTime = Math.max(now + RECLAMATION_STARTUP_DELAY_MS,
625                 mScribe.getLastReclamationTimeLocked() + UNUSED_RECLAMATION_PERIOD_MS);
626         mHandler.post(() -> {
627             // Never call out to AlarmManager with the lock held. This sits below AM.
628             AlarmManager alarmManager = getContext().getSystemService(AlarmManager.class);
629             if (alarmManager != null) {
630                 alarmManager.setWindow(AlarmManager.ELAPSED_REALTIME,
631                         SystemClock.elapsedRealtime() + (nextReclamationTime - now),
632                         30 * MINUTE_IN_MILLIS,
633                         ALARM_TAG_WEALTH_RECLAMATION, mUnusedWealthReclamationListener, mHandler);
634             } else {
635                 mHandler.sendEmptyMessageDelayed(
636                         MSG_SCHEDULE_UNUSED_WEALTH_RECLAMATION_EVENT, RECLAMATION_STARTUP_DELAY_MS);
637             }
638         });
639     }
640 
getCurrentBatteryLevel()641     private int getCurrentBatteryLevel() {
642         return mBatteryManagerInternal.getBatteryLevel();
643     }
644 
645     @Nullable
646     @GuardedBy("mLock")
getPackagesForUidLocked(final int uid)647     private ArraySet<String> getPackagesForUidLocked(final int uid) {
648         ArraySet<String> packages = mUidToPackageCache.get(uid);
649         if (packages == null) {
650             final String[] pkgs = mPackageManager.getPackagesForUid(uid);
651             if (pkgs != null) {
652                 for (String pkg : pkgs) {
653                     mUidToPackageCache.add(uid, pkg);
654                 }
655                 packages = mUidToPackageCache.get(uid);
656             }
657         }
658         return packages;
659     }
660 
661     @GuardedBy("mLock")
loadInstalledPackageListLocked()662     private void loadInstalledPackageListLocked() {
663         mPkgCache.clear();
664         final UserManagerInternal userManagerInternal =
665                 LocalServices.getService(UserManagerInternal.class);
666         final int[] userIds = userManagerInternal.getUserIds();
667         for (int userId : userIds) {
668             mPkgCache.addAll(
669                     mPackageManager.getInstalledPackagesAsUser(PACKAGE_QUERY_FLAGS, userId));
670         }
671     }
672 
registerListeners()673     private void registerListeners() {
674         final IntentFilter filter = new IntentFilter();
675         filter.addAction(Intent.ACTION_BATTERY_LEVEL_CHANGED);
676         filter.addAction(PowerManager.ACTION_POWER_SAVE_WHITELIST_CHANGED);
677         getContext().registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, filter, null, null);
678 
679         final IntentFilter pkgFilter = new IntentFilter();
680         pkgFilter.addAction(Intent.ACTION_PACKAGE_FULLY_REMOVED);
681         pkgFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
682         pkgFilter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
683         pkgFilter.addDataScheme("package");
684         getContext()
685                 .registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, pkgFilter, null, null);
686 
687         final IntentFilter userFilter = new IntentFilter(Intent.ACTION_USER_REMOVED);
688         userFilter.addAction(Intent.ACTION_USER_ADDED);
689         getContext()
690                 .registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, userFilter, null, null);
691 
692         UsageStatsManagerInternal usmi = LocalServices.getService(UsageStatsManagerInternal.class);
693         usmi.registerListener(mSurveillanceAgent);
694     }
695 
696     /** Perform long-running and/or heavy setup work. This should be called off the main thread. */
setupHeavyWork()697     private void setupHeavyWork() {
698         synchronized (mLock) {
699             loadInstalledPackageListLocked();
700             if (mBootPhase >= PHASE_BOOT_COMPLETED && !mExemptListLoaded) {
701                 try {
702                     mExemptedApps = new ArraySet<>(mDeviceIdleController.getFullPowerWhitelist());
703                 } catch (RemoteException e) {
704                     // Shouldn't happen.
705                     Slog.wtf(TAG, e);
706                 }
707                 mExemptListLoaded = true;
708             }
709             final boolean isFirstSetup = !mScribe.recordExists();
710             if (isFirstSetup) {
711                 mAgent.grantBirthrightsLocked();
712                 mScribe.setConsumptionLimitLocked(
713                         mCompleteEconomicPolicy.getInitialSatiatedConsumptionLimit());
714             } else {
715                 mScribe.loadFromDiskLocked();
716                 if (mScribe.getSatiatedConsumptionLimitLocked()
717                         < mCompleteEconomicPolicy.getInitialSatiatedConsumptionLimit()
718                         || mScribe.getSatiatedConsumptionLimitLocked()
719                         > mCompleteEconomicPolicy.getHardSatiatedConsumptionLimit()) {
720                     // Reset the consumption limit since several factors may have changed.
721                     mScribe.setConsumptionLimitLocked(
722                             mCompleteEconomicPolicy.getInitialSatiatedConsumptionLimit());
723                 }
724             }
725             scheduleUnusedWealthReclamationLocked();
726         }
727     }
728 
setupEverything()729     private void setupEverything() {
730         if (mBootPhase < PHASE_SYSTEM_SERVICES_READY || !mIsEnabled) {
731             return;
732         }
733         synchronized (mLock) {
734             registerListeners();
735             mCurrentBatteryLevel = getCurrentBatteryLevel();
736             mHandler.post(this::setupHeavyWork);
737             mCompleteEconomicPolicy.setup(mConfigObserver.getAllDeviceConfigProperties());
738         }
739     }
740 
tearDownEverything()741     private void tearDownEverything() {
742         if (mIsEnabled) {
743             return;
744         }
745         synchronized (mLock) {
746             mAgent.tearDownLocked();
747             mAnalyst.tearDown();
748             mCompleteEconomicPolicy.tearDown();
749             mExemptedApps.clear();
750             mExemptListLoaded = false;
751             mHandler.post(() -> {
752                 // Never call out to AlarmManager with the lock held. This sits below AM.
753                 AlarmManager alarmManager = getContext().getSystemService(AlarmManager.class);
754                 if (alarmManager != null) {
755                     alarmManager.cancel(mUnusedWealthReclamationListener);
756                 }
757             });
758             mPkgCache.clear();
759             mScribe.tearDownLocked();
760             mUidToPackageCache.clear();
761             getContext().unregisterReceiver(mBroadcastReceiver);
762             UsageStatsManagerInternal usmi =
763                     LocalServices.getService(UsageStatsManagerInternal.class);
764             usmi.unregisterListener(mSurveillanceAgent);
765         }
766         synchronized (mPackageToUidCache) {
767             mPackageToUidCache.clear();
768         }
769     }
770 
771     private final class IrsHandler extends Handler {
IrsHandler(Looper looper)772         IrsHandler(Looper looper) {
773             super(looper);
774         }
775 
776         @Override
handleMessage(Message msg)777         public void handleMessage(Message msg) {
778             switch (msg.what) {
779                 case MSG_NOTIFY_AFFORDABILITY_CHANGE_LISTENER: {
780                     final SomeArgs args = (SomeArgs) msg.obj;
781                     final int userId = args.argi1;
782                     final String pkgName = (String) args.arg1;
783                     final Agent.ActionAffordabilityNote affordabilityNote =
784                             (Agent.ActionAffordabilityNote) args.arg2;
785 
786                     final EconomyManagerInternal.AffordabilityChangeListener listener =
787                             affordabilityNote.getListener();
788                     listener.onAffordabilityChanged(userId, pkgName,
789                             affordabilityNote.getActionBill(),
790                             affordabilityNote.isCurrentlyAffordable());
791 
792                     args.recycle();
793                 }
794                 break;
795 
796                 case MSG_NOTIFY_STATE_CHANGE_LISTENERS: {
797                     for (TareStateChangeListener listener : mStateChangeListeners) {
798                         listener.onTareEnabledStateChanged(mIsEnabled);
799                     }
800                 }
801                 break;
802 
803                 case MSG_PROCESS_USAGE_EVENT: {
804                     final int userId = msg.arg1;
805                     final UsageEvents.Event event = (UsageEvents.Event) msg.obj;
806                     synchronized (mLock) {
807                         processUsageEventLocked(userId, event);
808                     }
809                 }
810                 break;
811 
812                 case MSG_SCHEDULE_UNUSED_WEALTH_RECLAMATION_EVENT: {
813                     removeMessages(MSG_SCHEDULE_UNUSED_WEALTH_RECLAMATION_EVENT);
814                     synchronized (mLock) {
815                         scheduleUnusedWealthReclamationLocked();
816                     }
817                 }
818                 break;
819             }
820         }
821     }
822 
823     /**
824      * Binder stub trampoline implementation
825      */
826     final class EconomyManagerStub extends IEconomyManager.Stub {
827         /**
828          * "dumpsys" infrastructure
829          */
830         @Override
dump(FileDescriptor fd, PrintWriter pw, String[] args)831         public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
832             if (!DumpUtils.checkDumpAndUsageStatsPermission(getContext(), TAG, pw)) return;
833 
834             boolean dumpAll = true;
835             if (!ArrayUtils.isEmpty(args)) {
836                 String arg = args[0];
837                 if ("-h".equals(arg) || "--help".equals(arg)) {
838                     dumpHelp(pw);
839                     return;
840                 } else if ("-a".equals(arg)) {
841                     // -a is passed when dumping a bug report. Bug reports have a time limit for
842                     // each service dump, so we can't dump everything.
843                     dumpAll = false;
844                 } else if (arg.length() > 0 && arg.charAt(0) == '-') {
845                     pw.println("Unknown option: " + arg);
846                     return;
847                 }
848             }
849 
850             final long identityToken = Binder.clearCallingIdentity();
851             try {
852                 dumpInternal(new IndentingPrintWriter(pw, "  "), dumpAll);
853             } finally {
854                 Binder.restoreCallingIdentity(identityToken);
855             }
856         }
857     }
858 
859     private final class LocalService implements EconomyManagerInternal {
860         /**
861          * Use an extremely large value to indicate that an app can pay for a bill indefinitely.
862          * The value set here should be large/long enough that there's no reasonable expectation
863          * of a device operating uninterrupted (or in the exact same state) for that period of time.
864          * We intentionally don't use Long.MAX_VALUE to avoid potential overflow if a client
865          * doesn't check the value and just immediately adds it to the current time.
866          */
867         private static final long FOREVER_MS = 27 * 365 * 24 * HOUR_IN_MILLIS;
868 
869         @Override
registerAffordabilityChangeListener(int userId, @NonNull String pkgName, @NonNull AffordabilityChangeListener listener, @NonNull ActionBill bill)870         public void registerAffordabilityChangeListener(int userId, @NonNull String pkgName,
871                 @NonNull AffordabilityChangeListener listener, @NonNull ActionBill bill) {
872             if (isSystem(userId, pkgName)) {
873                 // The system's affordability never changes.
874                 return;
875             }
876             synchronized (mLock) {
877                 mAgent.registerAffordabilityChangeListenerLocked(userId, pkgName, listener, bill);
878             }
879         }
880 
881         @Override
unregisterAffordabilityChangeListener(int userId, @NonNull String pkgName, @NonNull AffordabilityChangeListener listener, @NonNull ActionBill bill)882         public void unregisterAffordabilityChangeListener(int userId, @NonNull String pkgName,
883                 @NonNull AffordabilityChangeListener listener, @NonNull ActionBill bill) {
884             if (isSystem(userId, pkgName)) {
885                 // The system's affordability never changes.
886                 return;
887             }
888             synchronized (mLock) {
889                 mAgent.unregisterAffordabilityChangeListenerLocked(userId, pkgName, listener, bill);
890             }
891         }
892 
893         @Override
registerTareStateChangeListener(@onNull TareStateChangeListener listener)894         public void registerTareStateChangeListener(@NonNull TareStateChangeListener listener) {
895             mStateChangeListeners.add(listener);
896         }
897 
898         @Override
unregisterTareStateChangeListener(@onNull TareStateChangeListener listener)899         public void unregisterTareStateChangeListener(@NonNull TareStateChangeListener listener) {
900             mStateChangeListeners.remove(listener);
901         }
902 
903         @Override
canPayFor(int userId, @NonNull String pkgName, @NonNull ActionBill bill)904         public boolean canPayFor(int userId, @NonNull String pkgName, @NonNull ActionBill bill) {
905             if (!mIsEnabled) {
906                 return true;
907             }
908             if (isSystem(userId, pkgName)) {
909                 // The government, I mean the system, can create ARCs as it needs to in order to
910                 // operate.
911                 return true;
912             }
913             // TODO: take temp-allowlist into consideration
914             long requiredBalance = 0;
915             final List<EconomyManagerInternal.AnticipatedAction> projectedActions =
916                     bill.getAnticipatedActions();
917             synchronized (mLock) {
918                 for (int i = 0; i < projectedActions.size(); ++i) {
919                     AnticipatedAction action = projectedActions.get(i);
920                     final Cost cost = mCompleteEconomicPolicy.getCostOfAction(
921                             action.actionId, userId, pkgName);
922                     requiredBalance += cost.price * action.numInstantaneousCalls
923                             + cost.price * (action.ongoingDurationMs / 1000);
924                 }
925                 return mAgent.getBalanceLocked(userId, pkgName) >= requiredBalance
926                         && mScribe.getRemainingConsumableCakesLocked() >= requiredBalance;
927             }
928         }
929 
930         @Override
getMaxDurationMs(int userId, @NonNull String pkgName, @NonNull ActionBill bill)931         public long getMaxDurationMs(int userId, @NonNull String pkgName,
932                 @NonNull ActionBill bill) {
933             if (!mIsEnabled) {
934                 return FOREVER_MS;
935             }
936             if (isSystem(userId, pkgName)) {
937                 return FOREVER_MS;
938             }
939             long totalCostPerSecond = 0;
940             final List<EconomyManagerInternal.AnticipatedAction> projectedActions =
941                     bill.getAnticipatedActions();
942             synchronized (mLock) {
943                 for (int i = 0; i < projectedActions.size(); ++i) {
944                     AnticipatedAction action = projectedActions.get(i);
945                     final Cost cost = mCompleteEconomicPolicy.getCostOfAction(
946                             action.actionId, userId, pkgName);
947                     totalCostPerSecond += cost.price;
948                 }
949                 if (totalCostPerSecond == 0) {
950                     return FOREVER_MS;
951                 }
952                 final long minBalance = Math.min(
953                         mAgent.getBalanceLocked(userId, pkgName),
954                         mScribe.getRemainingConsumableCakesLocked());
955                 return minBalance * 1000 / totalCostPerSecond;
956             }
957         }
958 
959         @Override
isEnabled()960         public boolean isEnabled() {
961             return mIsEnabled;
962         }
963 
964         @Override
noteInstantaneousEvent(int userId, @NonNull String pkgName, int eventId, @Nullable String tag)965         public void noteInstantaneousEvent(int userId, @NonNull String pkgName, int eventId,
966                 @Nullable String tag) {
967             if (!mIsEnabled) {
968                 return;
969             }
970             synchronized (mLock) {
971                 mAgent.noteInstantaneousEventLocked(userId, pkgName, eventId, tag);
972             }
973         }
974 
975         @Override
noteOngoingEventStarted(int userId, @NonNull String pkgName, int eventId, @Nullable String tag)976         public void noteOngoingEventStarted(int userId, @NonNull String pkgName, int eventId,
977                 @Nullable String tag) {
978             if (!mIsEnabled) {
979                 return;
980             }
981             synchronized (mLock) {
982                 final long nowElapsed = SystemClock.elapsedRealtime();
983                 mAgent.noteOngoingEventLocked(userId, pkgName, eventId, tag, nowElapsed);
984             }
985         }
986 
987         @Override
noteOngoingEventStopped(int userId, @NonNull String pkgName, int eventId, @Nullable String tag)988         public void noteOngoingEventStopped(int userId, @NonNull String pkgName, int eventId,
989                 @Nullable String tag) {
990             if (!mIsEnabled) {
991                 return;
992             }
993             final long nowElapsed = SystemClock.elapsedRealtime();
994             final long now = getCurrentTimeMillis();
995             synchronized (mLock) {
996                 mAgent.stopOngoingActionLocked(userId, pkgName, eventId, tag, nowElapsed, now);
997             }
998         }
999     }
1000 
1001     private class ConfigObserver extends ContentObserver
1002             implements DeviceConfig.OnPropertiesChangedListener {
1003         private static final String KEY_DC_ENABLE_TARE = "enable_tare";
1004 
1005         private final ContentResolver mContentResolver;
1006 
ConfigObserver(Handler handler, Context context)1007         ConfigObserver(Handler handler, Context context) {
1008             super(handler);
1009             mContentResolver = context.getContentResolver();
1010         }
1011 
start()1012         public void start() {
1013             DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_TARE,
1014                     TareHandlerThread.getExecutor(), this);
1015             mContentResolver.registerContentObserver(
1016                     Settings.Global.getUriFor(Settings.Global.ENABLE_TARE), false, this);
1017             mContentResolver.registerContentObserver(
1018                     Settings.Global.getUriFor(TARE_ALARM_MANAGER_CONSTANTS), false, this);
1019             mContentResolver.registerContentObserver(
1020                     Settings.Global.getUriFor(TARE_JOB_SCHEDULER_CONSTANTS), false, this);
1021             onPropertiesChanged(getAllDeviceConfigProperties());
1022             updateEnabledStatus();
1023         }
1024 
1025         @NonNull
getAllDeviceConfigProperties()1026         DeviceConfig.Properties getAllDeviceConfigProperties() {
1027             // Don't want to cache the Properties object locally in case it ends up being large,
1028             // especially since it'll only be used once/infrequently (during setup or on a change).
1029             return DeviceConfig.getProperties(DeviceConfig.NAMESPACE_TARE);
1030         }
1031 
1032         @Override
onChange(boolean selfChange, Uri uri)1033         public void onChange(boolean selfChange, Uri uri) {
1034             if (uri.equals(Settings.Global.getUriFor(Settings.Global.ENABLE_TARE))) {
1035                 updateEnabledStatus();
1036             } else if (uri.equals(Settings.Global.getUriFor(TARE_ALARM_MANAGER_CONSTANTS))
1037                     || uri.equals(Settings.Global.getUriFor(TARE_JOB_SCHEDULER_CONSTANTS))) {
1038                 updateEconomicPolicy();
1039             }
1040         }
1041 
1042         @Override
onPropertiesChanged(DeviceConfig.Properties properties)1043         public void onPropertiesChanged(DeviceConfig.Properties properties) {
1044             boolean economicPolicyUpdated = false;
1045             synchronized (mLock) {
1046                 for (String name : properties.getKeyset()) {
1047                     if (name == null) {
1048                         continue;
1049                     }
1050                     switch (name) {
1051                         case KEY_DC_ENABLE_TARE:
1052                             updateEnabledStatus();
1053                             break;
1054                         default:
1055                             if (!economicPolicyUpdated
1056                                     && (name.startsWith("am") || name.startsWith("js"))) {
1057                                 updateEconomicPolicy();
1058                                 economicPolicyUpdated = true;
1059                             }
1060                     }
1061                 }
1062             }
1063         }
1064 
updateEnabledStatus()1065         private void updateEnabledStatus() {
1066             // User setting should override DeviceConfig setting.
1067             // NOTE: There's currently no way for a user to reset the value (via UI), so if a user
1068             // manually toggles TARE via UI, we'll always defer to the user's current setting
1069             // TODO: add a "reset" value if the user toggle is an issue
1070             final boolean isTareEnabledDC = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_TARE,
1071                     KEY_DC_ENABLE_TARE, Settings.Global.DEFAULT_ENABLE_TARE == 1);
1072             final boolean isTareEnabled = Settings.Global.getInt(mContentResolver,
1073                     Settings.Global.ENABLE_TARE, isTareEnabledDC ? 1 : 0) == 1;
1074             if (mIsEnabled != isTareEnabled) {
1075                 mIsEnabled = isTareEnabled;
1076                 if (mIsEnabled) {
1077                     setupEverything();
1078                 } else {
1079                     tearDownEverything();
1080                 }
1081                 mHandler.sendEmptyMessage(MSG_NOTIFY_STATE_CHANGE_LISTENERS);
1082             }
1083         }
1084 
updateEconomicPolicy()1085         private void updateEconomicPolicy() {
1086             synchronized (mLock) {
1087                 final long initialLimit =
1088                         mCompleteEconomicPolicy.getInitialSatiatedConsumptionLimit();
1089                 final long hardLimit = mCompleteEconomicPolicy.getHardSatiatedConsumptionLimit();
1090                 mCompleteEconomicPolicy.tearDown();
1091                 mCompleteEconomicPolicy = new CompleteEconomicPolicy(InternalResourceService.this);
1092                 if (mIsEnabled && mBootPhase >= PHASE_SYSTEM_SERVICES_READY) {
1093                     mCompleteEconomicPolicy.setup(getAllDeviceConfigProperties());
1094                     if (initialLimit != mCompleteEconomicPolicy.getInitialSatiatedConsumptionLimit()
1095                             || hardLimit
1096                             != mCompleteEconomicPolicy.getHardSatiatedConsumptionLimit()) {
1097                         // Reset the consumption limit since several factors may have changed.
1098                         mScribe.setConsumptionLimitLocked(
1099                                 mCompleteEconomicPolicy.getInitialSatiatedConsumptionLimit());
1100                     }
1101                     mAgent.onPricingChangedLocked();
1102                 }
1103             }
1104         }
1105     }
1106 
dumpHelp(PrintWriter pw)1107     private static void dumpHelp(PrintWriter pw) {
1108         pw.println("Resource Economy (economy) dump options:");
1109         pw.println("  [-h|--help] [package] ...");
1110         pw.println("    -h | --help: print this help");
1111         pw.println("  [package] is an optional package name to limit the output to.");
1112     }
1113 
dumpInternal(final IndentingPrintWriter pw, final boolean dumpAll)1114     private void dumpInternal(final IndentingPrintWriter pw, final boolean dumpAll) {
1115         synchronized (mLock) {
1116             pw.print("Is enabled: ");
1117             pw.println(mIsEnabled);
1118 
1119             pw.print("Current battery level: ");
1120             pw.println(mCurrentBatteryLevel);
1121 
1122             final long consumptionLimit = getConsumptionLimitLocked();
1123             pw.print("Consumption limit (current/initial-satiated/current-satiated): ");
1124             pw.print(cakeToString(consumptionLimit));
1125             pw.print("/");
1126             pw.print(cakeToString(mCompleteEconomicPolicy.getInitialSatiatedConsumptionLimit()));
1127             pw.print("/");
1128             pw.println(cakeToString(mScribe.getSatiatedConsumptionLimitLocked()));
1129 
1130             final long remainingConsumable = mScribe.getRemainingConsumableCakesLocked();
1131             pw.print("Goods remaining: ");
1132             pw.print(cakeToString(remainingConsumable));
1133             pw.print(" (");
1134             pw.print(String.format("%.2f", 100f * remainingConsumable / consumptionLimit));
1135             pw.println("% of current limit)");
1136 
1137             pw.print("Device wealth: ");
1138             pw.println(cakeToString(mScribe.getCakesInCirculationForLoggingLocked()));
1139 
1140             pw.println();
1141             pw.print("Exempted apps", mExemptedApps);
1142             pw.println();
1143 
1144             pw.println();
1145             mCompleteEconomicPolicy.dump(pw);
1146 
1147             pw.println();
1148             mScribe.dumpLocked(pw, dumpAll);
1149 
1150             pw.println();
1151             mAgent.dumpLocked(pw);
1152 
1153             pw.println();
1154             mAnalyst.dump(pw);
1155         }
1156     }
1157 }
1158