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 abc196f36d7c8..b84c8a41af1cc 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 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 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 fcb3e6759412f..1ff389deedd22 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 17b87469f7beb..4001d9b06ee00 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; @@ -107,6 +108,16 @@ public class InternalResourceService extends SystemService { 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; + /** + * 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 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 mTemporaryVips = new SparseArrayMap<>(); + /** Set of apps each installer is responsible for installing. */ @GuardedBy("mLock") private final SparseArrayMap> 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 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 @@ -775,6 +830,28 @@ public class InternalResourceService extends SystemService { mAgent.onCreditSupplyChanged(); } + @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) { @@ -870,7 +947,8 @@ public class InternalResourceService extends SystemService { final List 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 7cf459c2e494f..c2a6e43ba9306 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 ee448b5a6add9..b41c0d1371c04 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 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,12 +227,27 @@ 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() { return mRemainingConsumableCakes; } + @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(); @@ -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()); @@ -355,6 +389,13 @@ public class Scribe { postWrite(); } + @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); @@ -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 d477cb6f356c4..799a7fe3a3db7 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 ddfa05cf5a2e0..c46ebf20c3ee0 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)); } }