summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Kweku Adams <kwekua@google.com> 2022-08-25 22:53:28 +0000
committer Kweku Adams <kwekua@google.com> 2022-11-29 19:45:01 +0000
commit88844c437844e12a11170b0b77f2517ff6198e7c (patch)
treec281afb1827f6b43ca75674da828de705dccf934
parent813816a4cc475d8894a74ed2741a901507bdabec (diff)
Give installers exemptions after first setup.
Temporarily grant system installers VIP status for some time after TARE's first setup so they can install and update all the apps they need to. The temporary grant will be reapplied after reboots until the allotted time has transpired. The VIP status means they won't use any of their credits and will be able to do work even if the stock/consumption limit is depleted. The installer exemption is set to expire after the device has been on for 7 days, at which point, the installer's maximum balance will be lowered back to normal levels and the balance will eventually stabilize to normal levels. We will also give installers on a newly added user the temporary exemptions. Bug: 243588926 Test: reset device and check TARE dump Test: atest frameworks/base/services/tests/mockingservicestests/src/com/android/server/tare Test: atest frameworks/base/services/tests/servicestests/src/com/android/server/tare Change-Id: Ic468f652e30cc59a41e8034304ea90c54d60781e
-rw-r--r--apex/jobscheduler/service/java/com/android/server/tare/Agent.java11
-rw-r--r--apex/jobscheduler/service/java/com/android/server/tare/InstalledPackageInfo.java16
-rw-r--r--apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java164
-rw-r--r--apex/jobscheduler/service/java/com/android/server/tare/JobSchedulerEconomicPolicy.java17
-rw-r--r--apex/jobscheduler/service/java/com/android/server/tare/Scribe.java58
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/tare/AgentTrendCalculatorTest.java3
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/tare/ScribeTest.java2
7 files changed, 256 insertions, 15 deletions
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/Agent.java b/apex/jobscheduler/service/java/com/android/server/tare/Agent.java
index abc196f36d7c..b84c8a41af1c 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/Agent.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/Agent.java
@@ -286,7 +286,7 @@ class Agent {
for (int i = 0; i < pkgNames.size(); ++i) {
final String pkgName = pkgNames.valueAt(i);
- final boolean isVip = mIrs.isVip(userId, pkgName);
+ final boolean isVip = mIrs.isVip(userId, pkgName, nowElapsed);
SparseArrayMap<String, OngoingEvent> ongoingEvents =
mCurrentOngoingEvents.get(userId, pkgName);
if (ongoingEvents != null) {
@@ -321,7 +321,7 @@ class Agent {
final long nowElapsed = SystemClock.elapsedRealtime();
final CompleteEconomicPolicy economicPolicy = mIrs.getCompleteEconomicPolicyLocked();
- final boolean isVip = mIrs.isVip(userId, pkgName);
+ final boolean isVip = mIrs.isVip(userId, pkgName, nowElapsed);
SparseArrayMap<String, OngoingEvent> ongoingEvents =
mCurrentOngoingEvents.get(userId, pkgName);
if (ongoingEvents != null) {
@@ -397,7 +397,7 @@ class Agent {
if (actionAffordabilityNotes != null) {
final int size = actionAffordabilityNotes.size();
final long newBalance = getBalanceLocked(userId, pkgName);
- final boolean isVip = mIrs.isVip(userId, pkgName);
+ final boolean isVip = mIrs.isVip(userId, pkgName, nowElapsed);
for (int n = 0; n < size; ++n) {
final ActionAffordabilityNote note = actionAffordabilityNotes.valueAt(n);
note.recalculateCosts(economicPolicy, userId, pkgName);
@@ -503,7 +503,8 @@ class Agent {
"Tried to adjust system balance for " + appToString(userId, pkgName));
return;
}
- if (mIrs.isVip(userId, pkgName)) {
+ final boolean isVip = mIrs.isVip(userId, pkgName);
+ if (isVip) {
// This could happen if the app was made a VIP after it started performing actions.
// Continue recording the transaction for debugging purposes, but don't let it change
// any numbers.
@@ -536,7 +537,6 @@ class Agent {
mActionAffordabilityNotes.get(userId, pkgName);
if (actionAffordabilityNotes != null) {
final long newBalance = ledger.getCurrentBalance();
- final boolean isVip = mIrs.isVip(userId, pkgName);
for (int i = 0; i < actionAffordabilityNotes.size(); ++i) {
final ActionAffordabilityNote note = actionAffordabilityNotes.valueAt(i);
final boolean isAffordable = isVip
@@ -830,7 +830,6 @@ class Agent {
@GuardedBy("mLock")
void onUserRemovedLocked(final int userId) {
- mScribe.discardLedgersLocked(userId);
mCurrentOngoingEvents.delete(userId);
mBalanceThresholdAlarmQueue.removeAlarmsForUserId(userId);
}
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/InstalledPackageInfo.java b/apex/jobscheduler/service/java/com/android/server/tare/InstalledPackageInfo.java
index fcb3e6759412..1ff389deedd2 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/InstalledPackageInfo.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/InstalledPackageInfo.java
@@ -16,14 +16,20 @@
package com.android.server.tare;
+import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.AppGlobals;
+import android.content.Context;
+import android.content.PermissionChecker;
import android.content.pm.ApplicationInfo;
import android.content.pm.InstallSourceInfo;
import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
import android.os.RemoteException;
+import com.android.internal.util.ArrayUtils;
+
/** POJO to cache only the information about installed packages that TARE cares about. */
class InstalledPackageInfo {
static final int NO_UID = -1;
@@ -31,14 +37,22 @@ class InstalledPackageInfo {
public final int uid;
public final String packageName;
public final boolean hasCode;
+ public final boolean isSystemInstaller;
@Nullable
public final String installerPackageName;
- InstalledPackageInfo(@NonNull PackageInfo packageInfo) {
+ InstalledPackageInfo(@NonNull Context context, @NonNull PackageInfo packageInfo) {
final ApplicationInfo applicationInfo = packageInfo.applicationInfo;
uid = applicationInfo == null ? NO_UID : applicationInfo.uid;
packageName = packageInfo.packageName;
hasCode = applicationInfo != null && applicationInfo.hasCode();
+ isSystemInstaller = applicationInfo != null
+ && ArrayUtils.indexOf(
+ packageInfo.requestedPermissions, Manifest.permission.INSTALL_PACKAGES) >= 0
+ && PackageManager.PERMISSION_GRANTED
+ == PermissionChecker.checkPermissionForPreflight(context,
+ Manifest.permission.INSTALL_PACKAGES, PermissionChecker.PID_UNKNOWN,
+ applicationInfo.uid, packageName);
InstallSourceInfo installSourceInfo = null;
try {
installSourceInfo = AppGlobals.getPackageManager().getInstallSourceInfo(packageName);
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java b/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java
index 17b87469f7be..4001d9b06ee0 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java
@@ -64,6 +64,7 @@ import android.util.IndentingPrintWriter;
import android.util.Log;
import android.util.Slog;
import android.util.SparseArrayMap;
+import android.util.SparseLongArray;
import android.util.SparseSetArray;
import com.android.internal.annotations.GuardedBy;
@@ -108,6 +109,16 @@ public class InternalResourceService extends SystemService {
/** The amount of time to delay reclamation by after boot. */
private static final long RECLAMATION_STARTUP_DELAY_MS = 30_000L;
/**
+ * The amount of time after TARE has first been set up that a system installer will be allowed
+ * expanded credit privileges.
+ */
+ static final long INSTALLER_FIRST_SETUP_GRACE_PERIOD_MS = 7 * DAY_IN_MILLIS;
+ /**
+ * The amount of time to wait after TARE has first been set up before considering adjusting the
+ * stock/consumption limit.
+ */
+ private static final long STOCK_ADJUSTMENT_FIRST_SETUP_GRACE_PERIOD_MS = 5 * DAY_IN_MILLIS;
+ /**
* The battery level above which we may consider quantitative easing (increasing the consumption
* limit).
*/
@@ -127,7 +138,7 @@ public class InternalResourceService extends SystemService {
private static final long STOCK_RECALCULATION_MIN_DATA_DURATION_MS = 8 * HOUR_IN_MILLIS;
private static final int PACKAGE_QUERY_FLAGS =
PackageManager.MATCH_DIRECT_BOOT_AWARE | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
- | PackageManager.MATCH_APEX;
+ | PackageManager.MATCH_APEX | PackageManager.GET_PERMISSIONS;
/** Global lock for all resource economy state. */
private final Object mLock = new Object();
@@ -179,6 +190,13 @@ public class InternalResourceService extends SystemService {
@GuardedBy("mLock")
private final SparseArrayMap<String, Boolean> mVipOverrides = new SparseArrayMap<>();
+ /**
+ * Set of temporary Very Important Packages and when their VIP status ends, in the elapsed
+ * realtime ({@link android.annotation.ElapsedRealtimeLong}) timebase.
+ */
+ @GuardedBy("mLock")
+ private final SparseArrayMap<String, Long> mTemporaryVips = new SparseArrayMap<>();
+
/** Set of apps each installer is responsible for installing. */
@GuardedBy("mLock")
private final SparseArrayMap<String, ArraySet<String>> mInstallers = new SparseArrayMap<>();
@@ -308,6 +326,7 @@ public class InternalResourceService extends SystemService {
private static final int MSG_PROCESS_USAGE_EVENT = 2;
private static final int MSG_NOTIFY_STATE_CHANGE_LISTENERS = 3;
private static final int MSG_NOTIFY_STATE_CHANGE_LISTENER = 4;
+ private static final int MSG_CLEAN_UP_TEMP_VIP_LIST = 5;
private static final String ALARM_TAG_WEALTH_RECLAMATION = "*tare.reclamation*";
/**
@@ -413,6 +432,13 @@ public class InternalResourceService extends SystemService {
return userPkgs;
}
+ @Nullable
+ InstalledPackageInfo getInstalledPackageInfo(final int userId, @NonNull final String pkgName) {
+ synchronized (mLock) {
+ return mPkgCache.get(userId, pkgName);
+ }
+ }
+
@GuardedBy("mLock")
long getConsumptionLimitLocked() {
return mCurrentBatteryLevel * mScribe.getSatiatedConsumptionLimitLocked() / 100;
@@ -429,6 +455,11 @@ public class InternalResourceService extends SystemService {
return mCompleteEconomicPolicy.getInitialSatiatedConsumptionLimit();
}
+
+ long getRealtimeSinceFirstSetupMs() {
+ return mScribe.getRealtimeSinceFirstSetupMs(SystemClock.elapsedRealtime());
+ }
+
int getUid(final int userId, @NonNull final String pkgName) {
synchronized (mPackageToUidCache) {
Integer uid = mPackageToUidCache.get(userId, pkgName);
@@ -470,6 +501,10 @@ public class InternalResourceService extends SystemService {
}
boolean isVip(final int userId, @NonNull String pkgName) {
+ return isVip(userId, pkgName, SystemClock.elapsedRealtime());
+ }
+
+ boolean isVip(final int userId, @NonNull String pkgName, final long nowElapsed) {
synchronized (mLock) {
final Boolean override = mVipOverrides.get(userId, pkgName);
if (override != null) {
@@ -481,6 +516,12 @@ public class InternalResourceService extends SystemService {
// operate.
return true;
}
+ synchronized (mLock) {
+ final Long expirationTimeElapsed = mTemporaryVips.get(userId, pkgName);
+ if (expirationTimeElapsed != null) {
+ return nowElapsed <= expirationTimeElapsed;
+ }
+ }
return false;
}
@@ -569,7 +610,7 @@ public class InternalResourceService extends SystemService {
mPackageToUidCache.add(userId, pkgName, uid);
}
synchronized (mLock) {
- final InstalledPackageInfo ipo = new InstalledPackageInfo(packageInfo);
+ final InstalledPackageInfo ipo = new InstalledPackageInfo(getContext(), packageInfo);
final InstalledPackageInfo oldIpo = mPkgCache.add(userId, pkgName, ipo);
maybeUpdateInstallerStatusLocked(oldIpo, ipo);
mUidToPackageCache.add(uid, pkgName);
@@ -626,11 +667,16 @@ public class InternalResourceService extends SystemService {
final List<PackageInfo> pkgs =
mPackageManager.getInstalledPackagesAsUser(PACKAGE_QUERY_FLAGS, userId);
for (int i = pkgs.size() - 1; i >= 0; --i) {
- final InstalledPackageInfo ipo = new InstalledPackageInfo(pkgs.get(i));
+ final InstalledPackageInfo ipo =
+ new InstalledPackageInfo(getContext(), pkgs.get(i));
final InstalledPackageInfo oldIpo = mPkgCache.add(userId, ipo.packageName, ipo);
maybeUpdateInstallerStatusLocked(oldIpo, ipo);
}
mAgent.grantBirthrightsLocked(userId);
+ final long nowElapsed = SystemClock.elapsedRealtime();
+ mScribe.setUserAddedTimeLocked(userId, nowElapsed);
+ grantInstallersTemporaryVipStatusLocked(userId,
+ nowElapsed, INSTALLER_FIRST_SETUP_GRACE_PERIOD_MS);
}
}
@@ -647,6 +693,7 @@ public class InternalResourceService extends SystemService {
mInstallers.delete(userId);
mPkgCache.delete(userId);
mAgent.onUserRemovedLocked(userId);
+ mScribe.onUserRemovedLocked(userId);
}
}
@@ -659,6 +706,10 @@ public class InternalResourceService extends SystemService {
maybeAdjustDesiredStockLevelLocked();
return;
}
+ if (getRealtimeSinceFirstSetupMs() < STOCK_ADJUSTMENT_FIRST_SETUP_GRACE_PERIOD_MS) {
+ // Things can be very tumultuous soon after first setup.
+ return;
+ }
// We don't need to increase the limit if the device runs out of consumable credits
// when the battery is low.
final long remainingConsumableCakes = mScribe.getRemainingConsumableCakesLocked();
@@ -687,6 +738,10 @@ public class InternalResourceService extends SystemService {
if (!mConfigObserver.ENABLE_TIP3) {
return;
}
+ if (getRealtimeSinceFirstSetupMs() < STOCK_ADJUSTMENT_FIRST_SETUP_GRACE_PERIOD_MS) {
+ // Things can be very tumultuous soon after first setup.
+ return;
+ }
// Don't adjust the limit too often or while the battery is low.
final long now = getCurrentTimeMillis();
if ((now - mScribe.getLastStockRecalculationTimeLocked()) < STOCK_RECALCULATION_DELAY_MS
@@ -776,6 +831,28 @@ public class InternalResourceService extends SystemService {
}
@GuardedBy("mLock")
+ private void grantInstallersTemporaryVipStatusLocked(int userId, long nowElapsed,
+ long grantDurationMs) {
+ final long grantEndTimeElapsed = nowElapsed + grantDurationMs;
+ final int uIdx = mPkgCache.indexOfKey(userId);
+ if (uIdx < 0) {
+ return;
+ }
+ for (int pIdx = mPkgCache.numElementsForKey(uIdx) - 1; pIdx >= 0; --pIdx) {
+ final InstalledPackageInfo ipo = mPkgCache.valueAt(uIdx, pIdx);
+
+ if (ipo.isSystemInstaller) {
+ final Long currentGrantEndTimeElapsed = mTemporaryVips.get(userId, ipo.packageName);
+ if (currentGrantEndTimeElapsed == null
+ || currentGrantEndTimeElapsed < grantEndTimeElapsed) {
+ mTemporaryVips.add(userId, ipo.packageName, grantEndTimeElapsed);
+ }
+ }
+ }
+ mHandler.sendEmptyMessageDelayed(MSG_CLEAN_UP_TEMP_VIP_LIST, grantDurationMs);
+ }
+
+ @GuardedBy("mLock")
private void processUsageEventLocked(final int userId, @NonNull UsageEvents.Event event) {
if (!mIsEnabled) {
return;
@@ -870,7 +947,8 @@ public class InternalResourceService extends SystemService {
final List<PackageInfo> pkgs =
mPackageManager.getInstalledPackagesAsUser(PACKAGE_QUERY_FLAGS, userId);
for (int i = pkgs.size() - 1; i >= 0; --i) {
- final InstalledPackageInfo ipo = new InstalledPackageInfo(pkgs.get(i));
+ final InstalledPackageInfo ipo =
+ new InstalledPackageInfo(getContext(), pkgs.get(i));
final InstalledPackageInfo oldIpo = mPkgCache.add(userId, ipo.packageName, ipo);
maybeUpdateInstallerStatusLocked(oldIpo, ipo);
}
@@ -953,11 +1031,17 @@ public class InternalResourceService extends SystemService {
synchronized (mLock) {
mCompleteEconomicPolicy.setup(mConfigObserver.getAllDeviceConfigProperties());
loadInstalledPackageListLocked();
+ final SparseLongArray timeSinceUsersAdded;
final boolean isFirstSetup = !mScribe.recordExists();
+ final long nowElapsed = SystemClock.elapsedRealtime();
if (isFirstSetup) {
mAgent.grantBirthrightsLocked();
mScribe.setConsumptionLimitLocked(
mCompleteEconomicPolicy.getInitialSatiatedConsumptionLimit());
+ // Set the last reclamation time to now so we don't start reclaiming assets
+ // too early.
+ mScribe.setLastReclamationTimeLocked(getCurrentTimeMillis());
+ timeSinceUsersAdded = new SparseLongArray();
} else {
mScribe.loadFromDiskLocked();
if (mScribe.getSatiatedConsumptionLimitLocked()
@@ -971,6 +1055,21 @@ public class InternalResourceService extends SystemService {
// Adjust the supply in case battery level changed while the device was off.
adjustCreditSupplyLocked(true);
}
+ timeSinceUsersAdded = mScribe.getRealtimeSinceUsersAddedLocked(nowElapsed);
+ }
+
+ final int[] userIds = LocalServices.getService(UserManagerInternal.class).getUserIds();
+ for (int userId : userIds) {
+ final long timeSinceUserAddedMs = timeSinceUsersAdded.get(userId, 0);
+ // Temporarily mark installers as VIPs so they aren't subject to credit
+ // limits and policies on first boot.
+ if (timeSinceUserAddedMs < INSTALLER_FIRST_SETUP_GRACE_PERIOD_MS) {
+ final long remainingGraceDurationMs =
+ INSTALLER_FIRST_SETUP_GRACE_PERIOD_MS - timeSinceUserAddedMs;
+
+ grantInstallersTemporaryVipStatusLocked(userId, nowElapsed,
+ remainingGraceDurationMs);
+ }
}
scheduleUnusedWealthReclamationLocked();
}
@@ -1079,6 +1178,36 @@ public class InternalResourceService extends SystemService {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
+ case MSG_CLEAN_UP_TEMP_VIP_LIST: {
+ removeMessages(MSG_CLEAN_UP_TEMP_VIP_LIST);
+
+ synchronized (mLock) {
+ final long nowElapsed = SystemClock.elapsedRealtime();
+
+ long earliestExpiration = Long.MAX_VALUE;
+ for (int u = 0; u < mTemporaryVips.numMaps(); ++u) {
+ final int userId = mTemporaryVips.keyAt(u);
+
+ for (int p = mTemporaryVips.numElementsForKeyAt(u) - 1; p >= 0; --p) {
+ final String pkgName = mTemporaryVips.keyAt(u, p);
+ final Long expiration = mTemporaryVips.valueAt(u, p);
+
+ if (expiration == null || expiration < nowElapsed) {
+ mTemporaryVips.delete(userId, pkgName);
+ } else {
+ earliestExpiration = Math.min(earliestExpiration, expiration);
+ }
+ }
+ }
+
+ if (earliestExpiration < Long.MAX_VALUE) {
+ sendEmptyMessageDelayed(MSG_CLEAN_UP_TEMP_VIP_LIST,
+ earliestExpiration - nowElapsed);
+ }
+ }
+ }
+ break;
+
case MSG_NOTIFY_AFFORDABILITY_CHANGE_LISTENER: {
final SomeArgs args = (SomeArgs) msg.obj;
final int userId = args.argi1;
@@ -1558,6 +1687,7 @@ public class InternalResourceService extends SystemService {
boolean printedVips = false;
pw.println();
pw.print("VIPs:");
+ pw.increaseIndent();
for (int u = 0; u < mVipOverrides.numMaps(); ++u) {
final int userId = mVipOverrides.keyAt(u);
@@ -1576,6 +1706,32 @@ public class InternalResourceService extends SystemService {
} else {
pw.print(" None");
}
+ pw.decreaseIndent();
+ pw.println();
+
+ boolean printedTempVips = false;
+ pw.println();
+ pw.print("Temp VIPs:");
+ pw.increaseIndent();
+ for (int u = 0; u < mTemporaryVips.numMaps(); ++u) {
+ final int userId = mTemporaryVips.keyAt(u);
+
+ for (int p = 0; p < mTemporaryVips.numElementsForKeyAt(u); ++p) {
+ final String pkgName = mTemporaryVips.keyAt(u, p);
+
+ printedTempVips = true;
+ pw.println();
+ pw.print(appToString(userId, pkgName));
+ pw.print("=");
+ pw.print(mTemporaryVips.valueAt(u, p));
+ }
+ }
+ if (printedTempVips) {
+ pw.println();
+ } else {
+ pw.print(" None");
+ }
+ pw.decreaseIndent();
pw.println();
pw.println();
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/JobSchedulerEconomicPolicy.java b/apex/jobscheduler/service/java/com/android/server/tare/JobSchedulerEconomicPolicy.java
index 7cf459c2e494..c2a6e43ba930 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/JobSchedulerEconomicPolicy.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/JobSchedulerEconomicPolicy.java
@@ -117,6 +117,7 @@ import static com.android.server.tare.Modifier.COST_MODIFIER_CHARGING;
import static com.android.server.tare.Modifier.COST_MODIFIER_DEVICE_IDLE;
import static com.android.server.tare.Modifier.COST_MODIFIER_POWER_SAVE_MODE;
import static com.android.server.tare.Modifier.COST_MODIFIER_PROCESS_STATE;
+import static com.android.server.tare.TareUtils.appToString;
import static com.android.server.tare.TareUtils.cakeToString;
import android.annotation.NonNull;
@@ -210,6 +211,22 @@ public class JobSchedulerEconomicPolicy extends EconomicPolicy {
if (mIrs.isPackageRestricted(userId, pkgName)) {
return 0;
}
+ final InstalledPackageInfo ipo = mIrs.getInstalledPackageInfo(userId, pkgName);
+ if (ipo == null) {
+ Slog.wtfStack(TAG,
+ "Tried to get max balance of invalid app: " + appToString(userId, pkgName));
+ } else {
+ // A system installer's max balance is elevated for some time after first boot so
+ // they can use jobs to download and install apps.
+ if (ipo.isSystemInstaller) {
+ final long timeSinceFirstSetupMs = mIrs.getRealtimeSinceFirstSetupMs();
+ final boolean stillExempted = timeSinceFirstSetupMs
+ < InternalResourceService.INSTALLER_FIRST_SETUP_GRACE_PERIOD_MS;
+ if (stillExempted) {
+ return mMaxSatiatedConsumptionLimit;
+ }
+ }
+ }
return mMaxSatiatedBalance;
}
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/Scribe.java b/apex/jobscheduler/service/java/com/android/server/tare/Scribe.java
index ee448b5a6add..b41c0d1371c0 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/Scribe.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/Scribe.java
@@ -24,6 +24,7 @@ import static com.android.server.tare.TareUtils.cakeToString;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.Environment;
+import android.os.SystemClock;
import android.os.UserHandle;
import android.util.ArraySet;
import android.util.AtomicFile;
@@ -33,6 +34,7 @@ import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseArrayMap;
+import android.util.SparseLongArray;
import android.util.Xml;
import com.android.internal.annotations.GuardedBy;
@@ -88,6 +90,7 @@ public class Scribe {
"lastStockRecalculationTime";
private static final String XML_ATTR_REMAINING_CONSUMABLE_CAKES = "remainingConsumableCakes";
private static final String XML_ATTR_CONSUMPTION_LIMIT = "consumptionLimit";
+ private static final String XML_ATTR_TIME_SINCE_FIRST_SETUP_MS = "timeSinceFirstSetup";
private static final String XML_ATTR_PR_DISCHARGE = "discharge";
private static final String XML_ATTR_PR_BATTERY_LEVEL = "batteryLevel";
private static final String XML_ATTR_PR_PROFIT = "profit";
@@ -112,6 +115,11 @@ public class Scribe {
private final InternalResourceService mIrs;
private final Analyst mAnalyst;
+ /**
+ * The value of elapsed realtime since TARE was first setup that was read from disk.
+ * This will only be changed when the persisted file is read.
+ */
+ private long mLoadedTimeSinceFirstSetup;
@GuardedBy("mIrs.getLock()")
private long mLastReclamationTime;
@GuardedBy("mIrs.getLock()")
@@ -122,6 +130,9 @@ public class Scribe {
private long mRemainingConsumableCakes;
@GuardedBy("mIrs.getLock()")
private final SparseArrayMap<String, Ledger> mLedgers = new SparseArrayMap<>();
+ /** Offsets used to calculate the total realtime since each user was added. */
+ @GuardedBy("mIrs.getLock()")
+ private final SparseLongArray mRealtimeSinceUsersAddedOffsets = new SparseLongArray();
private final Runnable mCleanRunnable = this::cleanupLedgers;
private final Runnable mWriteRunnable = this::writeState;
@@ -163,8 +174,9 @@ public class Scribe {
}
@GuardedBy("mIrs.getLock()")
- void discardLedgersLocked(final int userId) {
+ void onUserRemovedLocked(final int userId) {
mLedgers.delete(userId);
+ mRealtimeSinceUsersAddedOffsets.delete(userId);
postWrite();
}
@@ -215,6 +227,11 @@ public class Scribe {
return sum;
}
+ /** Returns the cumulative elapsed realtime since TARE was first setup. */
+ long getRealtimeSinceFirstSetupMs(long nowElapsed) {
+ return mLoadedTimeSinceFirstSetup + nowElapsed;
+ }
+
/** Returns the total amount of cakes that remain to be consumed. */
@GuardedBy("mIrs.getLock()")
long getRemainingConsumableCakesLocked() {
@@ -222,6 +239,16 @@ public class Scribe {
}
@GuardedBy("mIrs.getLock()")
+ SparseLongArray getRealtimeSinceUsersAddedLocked(long nowElapsed) {
+ final SparseLongArray realtimes = new SparseLongArray();
+ for (int i = mRealtimeSinceUsersAddedOffsets.size() - 1; i >= 0; --i) {
+ realtimes.put(mRealtimeSinceUsersAddedOffsets.keyAt(i),
+ mRealtimeSinceUsersAddedOffsets.valueAt(i) + nowElapsed);
+ }
+ return realtimes;
+ }
+
+ @GuardedBy("mIrs.getLock()")
void loadFromDiskLocked() {
mLedgers.clear();
if (!recordExists()) {
@@ -276,7 +303,8 @@ public class Scribe {
}
}
- final long endTimeCutoff = System.currentTimeMillis() - MAX_TRANSACTION_AGE_MS;
+ final long now = System.currentTimeMillis();
+ final long endTimeCutoff = now - MAX_TRANSACTION_AGE_MS;
long earliestEndTime = Long.MAX_VALUE;
for (eventType = parser.next(); eventType != XmlPullParser.END_DOCUMENT;
eventType = parser.next()) {
@@ -294,6 +322,12 @@ public class Scribe {
parser.getAttributeLong(null, XML_ATTR_LAST_RECLAMATION_TIME);
mLastStockRecalculationTime = parser.getAttributeLong(null,
XML_ATTR_LAST_STOCK_RECALCULATION_TIME, 0);
+ mLoadedTimeSinceFirstSetup =
+ parser.getAttributeLong(null, XML_ATTR_TIME_SINCE_FIRST_SETUP_MS,
+ // If there's no recorded time since first setup, then
+ // offset the current elapsed time so it doesn't shift the
+ // timing too much.
+ -SystemClock.elapsedRealtime());
mSatiatedConsumptionLimit =
parser.getAttributeLong(null, XML_ATTR_CONSUMPTION_LIMIT,
mIrs.getInitialSatiatedConsumptionLimitLocked());
@@ -356,6 +390,13 @@ public class Scribe {
}
@GuardedBy("mIrs.getLock()")
+ void setUserAddedTimeLocked(int userId, long timeElapsed) {
+ // Use the current time as an offset so that when we persist the time, it correctly persists
+ // as "time since now".
+ mRealtimeSinceUsersAddedOffsets.put(userId, -timeElapsed);
+ }
+
+ @GuardedBy("mIrs.getLock()")
void tearDownLocked() {
TareHandlerThread.getHandler().removeCallbacks(mCleanRunnable);
TareHandlerThread.getHandler().removeCallbacks(mWriteRunnable);
@@ -486,6 +527,14 @@ public class Scribe {
// Don't return early since we need to go through all the ledger tags and get to the end
// of the user tag.
}
+ if (curUser != UserHandle.USER_NULL) {
+ mRealtimeSinceUsersAddedOffsets.put(curUser,
+ parser.getAttributeLong(null, XML_ATTR_TIME_SINCE_FIRST_SETUP_MS,
+ // If there's no recorded time since first setup, then
+ // offset the current elapsed time so it doesn't shift the
+ // timing too much.
+ -SystemClock.elapsedRealtime()));
+ }
long earliestEndTime = Long.MAX_VALUE;
for (int eventType = parser.next(); eventType != XmlPullParser.END_DOCUMENT;
@@ -630,6 +679,8 @@ public class Scribe {
out.attributeLong(null, XML_ATTR_LAST_RECLAMATION_TIME, mLastReclamationTime);
out.attributeLong(null,
XML_ATTR_LAST_STOCK_RECALCULATION_TIME, mLastStockRecalculationTime);
+ out.attributeLong(null, XML_ATTR_TIME_SINCE_FIRST_SETUP_MS,
+ mLoadedTimeSinceFirstSetup + SystemClock.elapsedRealtime());
out.attributeLong(null, XML_ATTR_CONSUMPTION_LIMIT, mSatiatedConsumptionLimit);
out.attributeLong(null, XML_ATTR_REMAINING_CONSUMABLE_CAKES,
mRemainingConsumableCakes);
@@ -665,6 +716,9 @@ public class Scribe {
out.startTag(null, XML_TAG_USER);
out.attributeInt(null, XML_ATTR_USER_ID, userId);
+ out.attributeLong(null, XML_ATTR_TIME_SINCE_FIRST_SETUP_MS,
+ mRealtimeSinceUsersAddedOffsets.get(userId,
+ mLoadedTimeSinceFirstSetup + SystemClock.elapsedRealtime()));
for (int pIdx = mLedgers.numElementsForKey(userId) - 1; pIdx >= 0; --pIdx) {
final String pkgName = mLedgers.keyAt(uIdx, pIdx);
final Ledger ledger = mLedgers.get(userId, pkgName);
diff --git a/services/tests/mockingservicestests/src/com/android/server/tare/AgentTrendCalculatorTest.java b/services/tests/mockingservicestests/src/com/android/server/tare/AgentTrendCalculatorTest.java
index d477cb6f356c..799a7fe3a3db 100644
--- a/services/tests/mockingservicestests/src/com/android/server/tare/AgentTrendCalculatorTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/tare/AgentTrendCalculatorTest.java
@@ -18,6 +18,7 @@ package com.android.server.tare;
import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@@ -108,7 +109,7 @@ public class AgentTrendCalculatorTest {
@Before
public void setUp() {
final InternalResourceService irs = mock(InternalResourceService.class);
- when(irs.isVip(anyInt(), anyString())).thenReturn(false);
+ when(irs.isVip(anyInt(), anyString(), anyLong())).thenReturn(false);
mEconomicPolicy = new MockEconomicPolicy(irs);
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/tare/ScribeTest.java b/services/tests/mockingservicestests/src/com/android/server/tare/ScribeTest.java
index ddfa05cf5a2e..c46ebf20c3ee 100644
--- a/services/tests/mockingservicestests/src/com/android/server/tare/ScribeTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/tare/ScribeTest.java
@@ -402,6 +402,6 @@ public class ScribeTest {
ApplicationInfo applicationInfo = new ApplicationInfo();
applicationInfo.uid = UserHandle.getUid(userId, Math.abs(pkgName.hashCode()));
pkgInfo.applicationInfo = applicationInfo;
- mInstalledPackages.add(userId, pkgName, new InstalledPackageInfo(pkgInfo));
+ mInstalledPackages.add(userId, pkgName, new InstalledPackageInfo(getContext(), pkgInfo));
}
}