Merge "Start reclaiming assets when circulation is too high."
This commit is contained in:
@@ -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;
|
||||
@@ -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;
|
||||
@@ -550,41 +542,62 @@ 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);
|
||||
// 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) {
|
||||
// Use a constant floor instead of the scaled floor from the IRS.
|
||||
final long minBalance = economicPolicy.getMinSatiatedBalance(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();
|
||||
long toReclaim = (long) (curBalance * percentage);
|
||||
if (curBalance - toReclaim < minBalance) {
|
||||
toReclaim = curBalance - minBalance;
|
||||
if (curBalance <= 0) {
|
||||
continue;
|
||||
}
|
||||
if (toReclaim > 0) {
|
||||
Slog.i(TAG, "Reclaiming unused wealth! Taking " + toReclaim
|
||||
+ " from " + appToString(userId, pkgName));
|
||||
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 >= minUnusedTimeMs) {
|
||||
final long minBalance;
|
||||
if (!scaleMinBalance) {
|
||||
// Use a constant floor instead of the scaled floor from the IRS.
|
||||
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(
|
||||
now, now, REGULATION_WEALTH_RECLAMATION, null, -toReclaim),
|
||||
true);
|
||||
recordTransactionLocked(userId, pkgName, ledger,
|
||||
new Ledger.Transaction(
|
||||
now, now, REGULATION_WEALTH_RECLAMATION, null, -toReclaim),
|
||||
true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 =
|
||||
@@ -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<>();
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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";
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
@@ -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;
|
||||
|
||||
@@ -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() {
|
||||
|
||||
Reference in New Issue
Block a user