Loading apex/jobscheduler/service/java/com/android/server/tare/Agent.java +48 −35 Original line number Diff line number Diff line Loading @@ -16,8 +16,6 @@ package com.android.server.tare; import static android.text.format.DateUtils.HOUR_IN_MILLIS; import static com.android.server.tare.EconomicPolicy.REGULATION_BASIC_INCOME; import static com.android.server.tare.EconomicPolicy.REGULATION_BIRTHRIGHT; import static com.android.server.tare.EconomicPolicy.REGULATION_WEALTH_RECLAMATION; Loading Loading @@ -69,12 +67,6 @@ class Agent { private static final boolean DEBUG = InternalResourceService.DEBUG || Log.isLoggable(TAG, Log.DEBUG); /** * The minimum amount of time an app must not have been used by the user before we start * regularly reclaiming ARCs from it. */ private static final long MIN_UNUSED_TIME_MS = 3 * 24 * HOUR_IN_MILLIS; private static final String ALARM_TAG_AFFORDABILITY_CHECK = "*tare.affordability_check*"; private final Object mLock; Loading Loading @@ -550,36 +542,56 @@ class Agent { /** * Reclaim a percentage of unused ARCs from every app that hasn't been used recently. The * reclamation will not reduce an app's balance below its minimum balance as dictated by the * EconomicPolicy. * reclamation will not reduce an app's balance below its minimum balance as dictated by * {@code scaleMinBalance}. * * @param percentage A value between 0 and 1 to indicate how much of the unused balance should * be reclaimed. * @param percentage A value between 0 and 1 to indicate how much of the unused balance * should be reclaimed. * @param minUnusedTimeMs The minimum amount of time (in milliseconds) that must have * transpired since the last user usage event before we will consider * reclaiming ARCs from the app. * @param scaleMinBalance Whether or not to used the scaled minimum app balance. If false, * this will use the constant min balance floor given by * {@link EconomicPolicy#getMinSatiatedBalance(int, String)}. If true, * this will use the scaled balance given by * {@link InternalResourceService#getMinBalanceLocked(int, String)}. */ @GuardedBy("mLock") void reclaimUnusedAssetsLocked(double percentage) { void reclaimUnusedAssetsLocked(double percentage, long minUnusedTimeMs, boolean scaleMinBalance) { final CompleteEconomicPolicy economicPolicy = mIrs.getCompleteEconomicPolicyLocked(); final List<PackageInfo> pkgs = mIrs.getInstalledPackages(); final SparseArrayMap<String, Ledger> ledgers = mScribe.getLedgersLocked(); final long now = getCurrentTimeMillis(); for (int i = 0; i < pkgs.size(); ++i) { final int userId = UserHandle.getUserId(pkgs.get(i).applicationInfo.uid); final String pkgName = pkgs.get(i).packageName; final Ledger ledger = mScribe.getLedgerLocked(userId, pkgName); for (int u = 0; u < ledgers.numMaps(); ++u) { final int userId = ledgers.keyAt(u); for (int p = 0; p < ledgers.numElementsForKey(userId); ++p) { final Ledger ledger = ledgers.valueAt(u, p); final long curBalance = ledger.getCurrentBalance(); if (curBalance <= 0) { continue; } final String pkgName = ledgers.keyAt(u, p); // AppStandby only counts elapsed time for things like this // TODO: should we use clock time instead? final long timeSinceLastUsedMs = mAppStandbyInternal.getTimeSinceLastUsedByUser(pkgName, userId); if (timeSinceLastUsedMs >= MIN_UNUSED_TIME_MS) { if (timeSinceLastUsedMs >= minUnusedTimeMs) { final long minBalance; if (!scaleMinBalance) { // Use a constant floor instead of the scaled floor from the IRS. final long minBalance = economicPolicy.getMinSatiatedBalance(userId, pkgName); final long curBalance = ledger.getCurrentBalance(); minBalance = economicPolicy.getMinSatiatedBalance(userId, pkgName); } else { minBalance = mIrs.getMinBalanceLocked(userId, pkgName); } long toReclaim = (long) (curBalance * percentage); if (curBalance - toReclaim < minBalance) { toReclaim = curBalance - minBalance; } if (toReclaim > 0) { if (DEBUG) { Slog.i(TAG, "Reclaiming unused wealth! Taking " + toReclaim + " from " + appToString(userId, pkgName)); } recordTransactionLocked(userId, pkgName, ledger, new Ledger.Transaction( Loading @@ -589,6 +601,7 @@ class Agent { } } } } /** Returns true if an app should be given credits in the general distributions. */ private boolean shouldGiveCredits(@NonNull PackageInfo packageInfo) { Loading apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java +99 −1 Original line number Diff line number Diff line Loading @@ -18,6 +18,7 @@ package com.android.server.tare; import static android.provider.Settings.Global.TARE_ALARM_MANAGER_CONSTANTS; import static android.provider.Settings.Global.TARE_JOB_SCHEDULER_CONSTANTS; import static android.text.format.DateUtils.DAY_IN_MILLIS; import static android.text.format.DateUtils.HOUR_IN_MILLIS; import static android.text.format.DateUtils.MINUTE_IN_MILLIS; Loading Loading @@ -85,6 +86,11 @@ public class InternalResourceService extends SystemService { static final long UNUSED_RECLAMATION_PERIOD_MS = 24 * HOUR_IN_MILLIS; /** How much of an app's unused wealth should be reclaimed periodically. */ private static final float DEFAULT_UNUSED_RECLAMATION_PERCENTAGE = .1f; /** * The minimum amount of time an app must not have been used by the user before we start * periodically reclaiming ARCs from it. */ private static final long MIN_UNUSED_TIME_MS = 3 * DAY_IN_MILLIS; /** The amount of time to delay reclamation by after boot. */ private static final long RECLAMATION_STARTUP_DELAY_MS = 30_000L; private static final int PACKAGE_QUERY_FLAGS = Loading @@ -107,6 +113,44 @@ public class InternalResourceService extends SystemService { @GuardedBy("mLock") private CompleteEconomicPolicy mCompleteEconomicPolicy; private static final class ReclamationConfig { /** * ARC circulation threshold (% circulating vs scaled maximum) above which this config * should come into play. */ public final double circulationPercentageThreshold; /** @see Agent#reclaimUnusedAssetsLocked(double, long, boolean) */ public final double reclamationPercentage; /** @see Agent#reclaimUnusedAssetsLocked(double, long, boolean) */ public final long minUsedTimeMs; /** @see Agent#reclaimUnusedAssetsLocked(double, long, boolean) */ public final boolean scaleMinBalance; ReclamationConfig(double circulationPercentageThreshold, double reclamationPercentage, long minUsedTimeMs, boolean scaleMinBalance) { this.circulationPercentageThreshold = circulationPercentageThreshold; this.reclamationPercentage = reclamationPercentage; this.minUsedTimeMs = minUsedTimeMs; this.scaleMinBalance = scaleMinBalance; } } /** * Sorted list of reclamation configs used to determine how many credits to force reclaim when * the circulation percentage is too high. The list should *always* be sorted in descending * order of {@link ReclamationConfig#circulationPercentageThreshold}. */ @GuardedBy("mLock") private final List<ReclamationConfig> mReclamationConfigs = List.of( new ReclamationConfig(2, .75, 12 * HOUR_IN_MILLIS, true), new ReclamationConfig(1.6, .5, DAY_IN_MILLIS, true), new ReclamationConfig(1.4, .25, DAY_IN_MILLIS, true), new ReclamationConfig(1.2, .25, 2 * DAY_IN_MILLIS, true), new ReclamationConfig(1, .25, MIN_UNUSED_TIME_MS, false), new ReclamationConfig( .9, DEFAULT_UNUSED_RECLAMATION_PERCENTAGE, MIN_UNUSED_TIME_MS, false) ); @NonNull @GuardedBy("mLock") private final List<PackageInfo> mPkgCache = new ArrayList<>(); Loading Loading @@ -190,7 +234,8 @@ public class InternalResourceService extends SystemService { @Override public void onAlarm() { synchronized (mLock) { mAgent.reclaimUnusedAssetsLocked(DEFAULT_UNUSED_RECLAMATION_PERCENTAGE); mAgent.reclaimUnusedAssetsLocked( DEFAULT_UNUSED_RECLAMATION_PERCENTAGE, MIN_UNUSED_TIME_MS, false); mScribe.setLastReclamationTimeLocked(getCurrentTimeMillis()); scheduleUnusedWealthReclamationLocked(); } Loading @@ -200,6 +245,7 @@ public class InternalResourceService extends SystemService { private static final int MSG_NOTIFY_AFFORDABILITY_CHANGE_LISTENER = 0; private static final int MSG_SCHEDULE_UNUSED_WEALTH_RECLAMATION_EVENT = 1; private static final int MSG_PROCESS_USAGE_EVENT = 2; private static final int MSG_MAYBE_FOCE_RECLAIM = 3; private static final String ALARM_TAG_WEALTH_RECLAMATION = "*tare.reclamation*"; private static final String KEY_PKG = "pkg"; Loading Loading @@ -317,6 +363,8 @@ public class InternalResourceService extends SystemService { final int newBatteryLevel = getCurrentBatteryLevel(); if (newBatteryLevel > mCurrentBatteryLevel) { mAgent.distributeBasicIncomeLocked(newBatteryLevel); } else if (newBatteryLevel < mCurrentBatteryLevel) { mHandler.obtainMessage(MSG_MAYBE_FOCE_RECLAIM).sendToTarget(); } mCurrentBatteryLevel = newBatteryLevel; } Loading Loading @@ -511,6 +559,48 @@ public class InternalResourceService extends SystemService { } } /** * Reclaim unused ARCs above apps' minimum balances if there are too many credits currently * in circulation. */ @GuardedBy("mLock") private void maybeForceReclaimLocked() { final long maxCirculation = getMaxCirculationLocked(); if (maxCirculation == 0) { Slog.wtf(TAG, "Max scaled circulation is 0..."); mAgent.reclaimUnusedAssetsLocked(1, HOUR_IN_MILLIS, true); mScribe.setLastReclamationTimeLocked(getCurrentTimeMillis()); scheduleUnusedWealthReclamationLocked(); return; } final long curCirculation = mScribe.getNarcsInCirculationLocked(); final double circulationPerc = 1.0 * curCirculation / maxCirculation; if (DEBUG) { Slog.d(TAG, "Circulation %: " + circulationPerc); } final int numConfigs = mReclamationConfigs.size(); if (numConfigs == 0) { return; } // The configs are sorted in descending order of circulationPercentageThreshold, so we can // short-circuit if the current circulation is lower than the lowest threshold. if (circulationPerc < mReclamationConfigs.get(numConfigs - 1).circulationPercentageThreshold) { return; } // TODO: maybe exclude apps we think will be launched in the next few hours for (int i = 0; i < numConfigs; ++i) { final ReclamationConfig config = mReclamationConfigs.get(i); if (circulationPerc >= config.circulationPercentageThreshold) { mAgent.reclaimUnusedAssetsLocked( config.reclamationPercentage, config.minUsedTimeMs, config.scaleMinBalance); mScribe.setLastReclamationTimeLocked(getCurrentTimeMillis()); scheduleUnusedWealthReclamationLocked(); break; } } } private void registerListeners() { final IntentFilter filter = new IntentFilter(); filter.addAction(Intent.ACTION_BATTERY_LEVEL_CHANGED); Loading Loading @@ -594,6 +684,14 @@ public class InternalResourceService extends SystemService { @Override public void handleMessage(Message msg) { switch (msg.what) { case MSG_MAYBE_FOCE_RECLAIM: { removeMessages(MSG_MAYBE_FOCE_RECLAIM); synchronized (mLock) { maybeForceReclaimLocked(); } } break; case MSG_NOTIFY_AFFORDABILITY_CHANGE_LISTENER: { Bundle data = msg.getData(); final int userId = msg.arg1; Loading apex/jobscheduler/service/java/com/android/server/tare/Scribe.java +6 −0 Original line number Diff line number Diff line Loading @@ -147,6 +147,12 @@ public class Scribe { return ledger; } @GuardedBy("mIrs.getLock()") @NonNull SparseArrayMap<String, Ledger> getLedgersLocked() { return mLedgers; } /** Returns the total amount of narcs currently allocated to apps. */ @GuardedBy("mIrs.getLock()") long getNarcsInCirculationLocked() { Loading Loading
apex/jobscheduler/service/java/com/android/server/tare/Agent.java +48 −35 Original line number Diff line number Diff line Loading @@ -16,8 +16,6 @@ package com.android.server.tare; import static android.text.format.DateUtils.HOUR_IN_MILLIS; import static com.android.server.tare.EconomicPolicy.REGULATION_BASIC_INCOME; import static com.android.server.tare.EconomicPolicy.REGULATION_BIRTHRIGHT; import static com.android.server.tare.EconomicPolicy.REGULATION_WEALTH_RECLAMATION; Loading Loading @@ -69,12 +67,6 @@ class Agent { private static final boolean DEBUG = InternalResourceService.DEBUG || Log.isLoggable(TAG, Log.DEBUG); /** * The minimum amount of time an app must not have been used by the user before we start * regularly reclaiming ARCs from it. */ private static final long MIN_UNUSED_TIME_MS = 3 * 24 * HOUR_IN_MILLIS; private static final String ALARM_TAG_AFFORDABILITY_CHECK = "*tare.affordability_check*"; private final Object mLock; Loading Loading @@ -550,36 +542,56 @@ class Agent { /** * Reclaim a percentage of unused ARCs from every app that hasn't been used recently. The * reclamation will not reduce an app's balance below its minimum balance as dictated by the * EconomicPolicy. * reclamation will not reduce an app's balance below its minimum balance as dictated by * {@code scaleMinBalance}. * * @param percentage A value between 0 and 1 to indicate how much of the unused balance should * be reclaimed. * @param percentage A value between 0 and 1 to indicate how much of the unused balance * should be reclaimed. * @param minUnusedTimeMs The minimum amount of time (in milliseconds) that must have * transpired since the last user usage event before we will consider * reclaiming ARCs from the app. * @param scaleMinBalance Whether or not to used the scaled minimum app balance. If false, * this will use the constant min balance floor given by * {@link EconomicPolicy#getMinSatiatedBalance(int, String)}. If true, * this will use the scaled balance given by * {@link InternalResourceService#getMinBalanceLocked(int, String)}. */ @GuardedBy("mLock") void reclaimUnusedAssetsLocked(double percentage) { void reclaimUnusedAssetsLocked(double percentage, long minUnusedTimeMs, boolean scaleMinBalance) { final CompleteEconomicPolicy economicPolicy = mIrs.getCompleteEconomicPolicyLocked(); final List<PackageInfo> pkgs = mIrs.getInstalledPackages(); final SparseArrayMap<String, Ledger> ledgers = mScribe.getLedgersLocked(); final long now = getCurrentTimeMillis(); for (int i = 0; i < pkgs.size(); ++i) { final int userId = UserHandle.getUserId(pkgs.get(i).applicationInfo.uid); final String pkgName = pkgs.get(i).packageName; final Ledger ledger = mScribe.getLedgerLocked(userId, pkgName); for (int u = 0; u < ledgers.numMaps(); ++u) { final int userId = ledgers.keyAt(u); for (int p = 0; p < ledgers.numElementsForKey(userId); ++p) { final Ledger ledger = ledgers.valueAt(u, p); final long curBalance = ledger.getCurrentBalance(); if (curBalance <= 0) { continue; } final String pkgName = ledgers.keyAt(u, p); // AppStandby only counts elapsed time for things like this // TODO: should we use clock time instead? final long timeSinceLastUsedMs = mAppStandbyInternal.getTimeSinceLastUsedByUser(pkgName, userId); if (timeSinceLastUsedMs >= MIN_UNUSED_TIME_MS) { if (timeSinceLastUsedMs >= minUnusedTimeMs) { final long minBalance; if (!scaleMinBalance) { // Use a constant floor instead of the scaled floor from the IRS. final long minBalance = economicPolicy.getMinSatiatedBalance(userId, pkgName); final long curBalance = ledger.getCurrentBalance(); minBalance = economicPolicy.getMinSatiatedBalance(userId, pkgName); } else { minBalance = mIrs.getMinBalanceLocked(userId, pkgName); } long toReclaim = (long) (curBalance * percentage); if (curBalance - toReclaim < minBalance) { toReclaim = curBalance - minBalance; } if (toReclaim > 0) { if (DEBUG) { Slog.i(TAG, "Reclaiming unused wealth! Taking " + toReclaim + " from " + appToString(userId, pkgName)); } recordTransactionLocked(userId, pkgName, ledger, new Ledger.Transaction( Loading @@ -589,6 +601,7 @@ class Agent { } } } } /** Returns true if an app should be given credits in the general distributions. */ private boolean shouldGiveCredits(@NonNull PackageInfo packageInfo) { Loading
apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java +99 −1 Original line number Diff line number Diff line Loading @@ -18,6 +18,7 @@ package com.android.server.tare; import static android.provider.Settings.Global.TARE_ALARM_MANAGER_CONSTANTS; import static android.provider.Settings.Global.TARE_JOB_SCHEDULER_CONSTANTS; import static android.text.format.DateUtils.DAY_IN_MILLIS; import static android.text.format.DateUtils.HOUR_IN_MILLIS; import static android.text.format.DateUtils.MINUTE_IN_MILLIS; Loading Loading @@ -85,6 +86,11 @@ public class InternalResourceService extends SystemService { static final long UNUSED_RECLAMATION_PERIOD_MS = 24 * HOUR_IN_MILLIS; /** How much of an app's unused wealth should be reclaimed periodically. */ private static final float DEFAULT_UNUSED_RECLAMATION_PERCENTAGE = .1f; /** * The minimum amount of time an app must not have been used by the user before we start * periodically reclaiming ARCs from it. */ private static final long MIN_UNUSED_TIME_MS = 3 * DAY_IN_MILLIS; /** The amount of time to delay reclamation by after boot. */ private static final long RECLAMATION_STARTUP_DELAY_MS = 30_000L; private static final int PACKAGE_QUERY_FLAGS = Loading @@ -107,6 +113,44 @@ public class InternalResourceService extends SystemService { @GuardedBy("mLock") private CompleteEconomicPolicy mCompleteEconomicPolicy; private static final class ReclamationConfig { /** * ARC circulation threshold (% circulating vs scaled maximum) above which this config * should come into play. */ public final double circulationPercentageThreshold; /** @see Agent#reclaimUnusedAssetsLocked(double, long, boolean) */ public final double reclamationPercentage; /** @see Agent#reclaimUnusedAssetsLocked(double, long, boolean) */ public final long minUsedTimeMs; /** @see Agent#reclaimUnusedAssetsLocked(double, long, boolean) */ public final boolean scaleMinBalance; ReclamationConfig(double circulationPercentageThreshold, double reclamationPercentage, long minUsedTimeMs, boolean scaleMinBalance) { this.circulationPercentageThreshold = circulationPercentageThreshold; this.reclamationPercentage = reclamationPercentage; this.minUsedTimeMs = minUsedTimeMs; this.scaleMinBalance = scaleMinBalance; } } /** * Sorted list of reclamation configs used to determine how many credits to force reclaim when * the circulation percentage is too high. The list should *always* be sorted in descending * order of {@link ReclamationConfig#circulationPercentageThreshold}. */ @GuardedBy("mLock") private final List<ReclamationConfig> mReclamationConfigs = List.of( new ReclamationConfig(2, .75, 12 * HOUR_IN_MILLIS, true), new ReclamationConfig(1.6, .5, DAY_IN_MILLIS, true), new ReclamationConfig(1.4, .25, DAY_IN_MILLIS, true), new ReclamationConfig(1.2, .25, 2 * DAY_IN_MILLIS, true), new ReclamationConfig(1, .25, MIN_UNUSED_TIME_MS, false), new ReclamationConfig( .9, DEFAULT_UNUSED_RECLAMATION_PERCENTAGE, MIN_UNUSED_TIME_MS, false) ); @NonNull @GuardedBy("mLock") private final List<PackageInfo> mPkgCache = new ArrayList<>(); Loading Loading @@ -190,7 +234,8 @@ public class InternalResourceService extends SystemService { @Override public void onAlarm() { synchronized (mLock) { mAgent.reclaimUnusedAssetsLocked(DEFAULT_UNUSED_RECLAMATION_PERCENTAGE); mAgent.reclaimUnusedAssetsLocked( DEFAULT_UNUSED_RECLAMATION_PERCENTAGE, MIN_UNUSED_TIME_MS, false); mScribe.setLastReclamationTimeLocked(getCurrentTimeMillis()); scheduleUnusedWealthReclamationLocked(); } Loading @@ -200,6 +245,7 @@ public class InternalResourceService extends SystemService { private static final int MSG_NOTIFY_AFFORDABILITY_CHANGE_LISTENER = 0; private static final int MSG_SCHEDULE_UNUSED_WEALTH_RECLAMATION_EVENT = 1; private static final int MSG_PROCESS_USAGE_EVENT = 2; private static final int MSG_MAYBE_FOCE_RECLAIM = 3; private static final String ALARM_TAG_WEALTH_RECLAMATION = "*tare.reclamation*"; private static final String KEY_PKG = "pkg"; Loading Loading @@ -317,6 +363,8 @@ public class InternalResourceService extends SystemService { final int newBatteryLevel = getCurrentBatteryLevel(); if (newBatteryLevel > mCurrentBatteryLevel) { mAgent.distributeBasicIncomeLocked(newBatteryLevel); } else if (newBatteryLevel < mCurrentBatteryLevel) { mHandler.obtainMessage(MSG_MAYBE_FOCE_RECLAIM).sendToTarget(); } mCurrentBatteryLevel = newBatteryLevel; } Loading Loading @@ -511,6 +559,48 @@ public class InternalResourceService extends SystemService { } } /** * Reclaim unused ARCs above apps' minimum balances if there are too many credits currently * in circulation. */ @GuardedBy("mLock") private void maybeForceReclaimLocked() { final long maxCirculation = getMaxCirculationLocked(); if (maxCirculation == 0) { Slog.wtf(TAG, "Max scaled circulation is 0..."); mAgent.reclaimUnusedAssetsLocked(1, HOUR_IN_MILLIS, true); mScribe.setLastReclamationTimeLocked(getCurrentTimeMillis()); scheduleUnusedWealthReclamationLocked(); return; } final long curCirculation = mScribe.getNarcsInCirculationLocked(); final double circulationPerc = 1.0 * curCirculation / maxCirculation; if (DEBUG) { Slog.d(TAG, "Circulation %: " + circulationPerc); } final int numConfigs = mReclamationConfigs.size(); if (numConfigs == 0) { return; } // The configs are sorted in descending order of circulationPercentageThreshold, so we can // short-circuit if the current circulation is lower than the lowest threshold. if (circulationPerc < mReclamationConfigs.get(numConfigs - 1).circulationPercentageThreshold) { return; } // TODO: maybe exclude apps we think will be launched in the next few hours for (int i = 0; i < numConfigs; ++i) { final ReclamationConfig config = mReclamationConfigs.get(i); if (circulationPerc >= config.circulationPercentageThreshold) { mAgent.reclaimUnusedAssetsLocked( config.reclamationPercentage, config.minUsedTimeMs, config.scaleMinBalance); mScribe.setLastReclamationTimeLocked(getCurrentTimeMillis()); scheduleUnusedWealthReclamationLocked(); break; } } } private void registerListeners() { final IntentFilter filter = new IntentFilter(); filter.addAction(Intent.ACTION_BATTERY_LEVEL_CHANGED); Loading Loading @@ -594,6 +684,14 @@ public class InternalResourceService extends SystemService { @Override public void handleMessage(Message msg) { switch (msg.what) { case MSG_MAYBE_FOCE_RECLAIM: { removeMessages(MSG_MAYBE_FOCE_RECLAIM); synchronized (mLock) { maybeForceReclaimLocked(); } } break; case MSG_NOTIFY_AFFORDABILITY_CHANGE_LISTENER: { Bundle data = msg.getData(); final int userId = msg.arg1; Loading
apex/jobscheduler/service/java/com/android/server/tare/Scribe.java +6 −0 Original line number Diff line number Diff line Loading @@ -147,6 +147,12 @@ public class Scribe { return ledger; } @GuardedBy("mIrs.getLock()") @NonNull SparseArrayMap<String, Ledger> getLedgersLocked() { return mLedgers; } /** Returns the total amount of narcs currently allocated to apps. */ @GuardedBy("mIrs.getLock()") long getNarcsInCirculationLocked() { Loading