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