Merge "Cancel active Futures/Callbacks if transport dies." into tm-dev
diff --git a/apex/jobscheduler/framework/java/android/app/tare/EconomyManager.java b/apex/jobscheduler/framework/java/android/app/tare/EconomyManager.java
index 8c8d2bf..88082f7 100644
--- a/apex/jobscheduler/framework/java/android/app/tare/EconomyManager.java
+++ b/apex/jobscheduler/framework/java/android/app/tare/EconomyManager.java
@@ -39,7 +39,9 @@
/** @hide */
public static final String KEY_AM_MAX_SATIATED_BALANCE = "am_max_satiated_balance";
/** @hide */
- public static final String KEY_AM_MAX_CIRCULATION = "am_max_circulation";
+ public static final String KEY_AM_INITIAL_CONSUMPTION_LIMIT = "am_initial_consumption_limit";
+ /** @hide */
+ public static final String KEY_AM_HARD_CONSUMPTION_LIMIT = "am_hard_consumption_limit";
// TODO: Add AlarmManager modifier keys
/** @hide */
public static final String KEY_AM_REWARD_TOP_ACTIVITY_INSTANT =
@@ -163,7 +165,9 @@
public static final String KEY_JS_MAX_SATIATED_BALANCE =
"js_max_satiated_balance";
/** @hide */
- public static final String KEY_JS_MAX_CIRCULATION = "js_max_circulation";
+ public static final String KEY_JS_INITIAL_CONSUMPTION_LIMIT = "js_initial_consumption_limit";
+ /** @hide */
+ public static final String KEY_JS_HARD_CONSUMPTION_LIMIT = "js_hard_consumption_limit";
// TODO: Add JobScheduler modifier keys
/** @hide */
public static final String KEY_JS_REWARD_TOP_ACTIVITY_INSTANT =
@@ -280,7 +284,9 @@
/** @hide */
public static final int DEFAULT_AM_MAX_SATIATED_BALANCE = 1440;
/** @hide */
- public static final int DEFAULT_AM_MAX_CIRCULATION = 52000;
+ public static final int DEFAULT_AM_INITIAL_CONSUMPTION_LIMIT = 28800;
+ /** @hide */
+ public static final int DEFAULT_AM_HARD_CONSUMPTION_LIMIT = 52000;
// TODO: add AlarmManager modifier default values
/** @hide */
public static final int DEFAULT_AM_REWARD_TOP_ACTIVITY_INSTANT = 0;
@@ -359,7 +365,7 @@
// Default values JobScheduler factors
// TODO: add time_since_usage variable to min satiated balance factors
/** @hide */
- public static final int DEFAULT_JS_MIN_SATIATED_BALANCE_EXEMPTED = 50000;
+ public static final int DEFAULT_JS_MIN_SATIATED_BALANCE_EXEMPTED = 20000;
/** @hide */
public static final int DEFAULT_JS_MIN_SATIATED_BALANCE_HEADLESS_SYSTEM_APP = 10000;
/** @hide */
@@ -367,7 +373,9 @@
/** @hide */
public static final int DEFAULT_JS_MAX_SATIATED_BALANCE = 60000;
/** @hide */
- public static final int DEFAULT_JS_MAX_CIRCULATION = 691200;
+ public static final int DEFAULT_JS_INITIAL_CONSUMPTION_LIMIT = 460_000;
+ /** @hide */
+ public static final int DEFAULT_JS_HARD_CONSUMPTION_LIMIT = 900_000;
// TODO: add JobScheduler modifier default values
/** @hide */
public static final int DEFAULT_JS_REWARD_TOP_ACTIVITY_INSTANT = 0;
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
index 4e73b02..b936278 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -3007,7 +3007,7 @@
}
} else if (BatteryManager.ACTION_DISCHARGING.equals(action)) {
if (DEBUG) {
- Slog.d(TAG, "Disconnected from power @ " + sElapsedRealtimeClock.millis());
+ Slog.d(TAG, "Battery discharging @ " + sElapsedRealtimeClock.millis());
}
if (mCharging) {
mCharging = false;
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 a6a007f..c0a8148 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/Agent.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/Agent.java
@@ -55,9 +55,6 @@
import com.android.server.usage.AppStandbyInternal;
import com.android.server.utils.AlarmQueue;
-import libcore.util.EmptyArray;
-
-import java.util.Comparator;
import java.util.List;
import java.util.Objects;
import java.util.function.Consumer;
@@ -105,54 +102,13 @@
private final BalanceThresholdAlarmQueue mBalanceThresholdAlarmQueue;
/**
- * Comparator to use to sort apps before we distribute ARCs so that we try to give the most
- * important apps ARCs first.
+ * Check the affordability notes of all apps.
*/
- @VisibleForTesting
- final Comparator<PackageInfo> mPackageDistributionComparator =
- new Comparator<PackageInfo>() {
- @Override
- public int compare(PackageInfo pi1, PackageInfo pi2) {
- final ApplicationInfo appInfo1 = pi1.applicationInfo;
- final ApplicationInfo appInfo2 = pi2.applicationInfo;
- // Put any packages that don't declare an application at the end. A missing
- // <application> tag likely means the app won't be doing any work anyway.
- if (appInfo1 == null) {
- if (appInfo2 == null) {
- return 0;
- }
- return 1;
- } else if (appInfo2 == null) {
- return -1;
- }
- // Privileged apps eat first. They're likely required for the device to
- // function properly.
- // TODO: include headless system apps
- if (appInfo1.isPrivilegedApp()) {
- if (!appInfo2.isPrivilegedApp()) {
- return -1;
- }
- } else if (appInfo2.isPrivilegedApp()) {
- return 1;
- }
-
- // Sort by most recently used.
- final long timeSinceLastUsedMs1 =
- mAppStandbyInternal.getTimeSinceLastUsedByUser(
- pi1.packageName, UserHandle.getUserId(pi1.applicationInfo.uid));
- final long timeSinceLastUsedMs2 =
- mAppStandbyInternal.getTimeSinceLastUsedByUser(
- pi2.packageName, UserHandle.getUserId(pi2.applicationInfo.uid));
- if (timeSinceLastUsedMs1 < timeSinceLastUsedMs2) {
- return -1;
- } else if (timeSinceLastUsedMs1 > timeSinceLastUsedMs2) {
- return 1;
- }
- return 0;
- }
- };
-
- private static final int MSG_CHECK_BALANCE = 0;
+ private static final int MSG_CHECK_ALL_AFFORDABILITY = 0;
+ /**
+ * Check the affordability notes of a single app.
+ */
+ private static final int MSG_CHECK_INDIVIDUAL_AFFORDABILITY = 1;
Agent(@NonNull InternalResourceService irs, @NonNull Scribe scribe) {
mLock = irs.getLock();
@@ -179,7 +135,7 @@
@Override
public void accept(OngoingEvent ongoingEvent) {
- mTotal += getActualDeltaLocked(ongoingEvent, mLedger, mNowElapsed, mNow);
+ mTotal += getActualDeltaLocked(ongoingEvent, mLedger, mNowElapsed, mNow).price;
}
}
@@ -204,6 +160,11 @@
}
@GuardedBy("mLock")
+ private boolean isAffordableLocked(long balance, long price, long ctp) {
+ return balance >= price && mScribe.getRemainingConsumableNarcsLocked() >= ctp;
+ }
+
+ @GuardedBy("mLock")
void noteInstantaneousEventLocked(final int userId, @NonNull final String pkgName,
final int eventId, @Nullable String tag) {
if (mIrs.isSystem(userId, pkgName)) {
@@ -218,10 +179,13 @@
final int eventType = getEventType(eventId);
switch (eventType) {
case TYPE_ACTION:
- final long actionCost = economicPolicy.getCostOfAction(eventId, userId, pkgName);
+ final EconomicPolicy.Cost actionCost =
+ economicPolicy.getCostOfAction(eventId, userId, pkgName);
recordTransactionLocked(userId, pkgName, ledger,
- new Ledger.Transaction(now, now, eventId, tag, -actionCost), true);
+ new Ledger.Transaction(now, now, eventId, tag,
+ -actionCost.price, actionCost.costToProduce),
+ true);
break;
case TYPE_REWARD:
@@ -231,7 +195,7 @@
final long rewardVal = Math.max(0,
Math.min(reward.maxDailyReward - rewardSum, reward.instantReward));
recordTransactionLocked(userId, pkgName, ledger,
- new Ledger.Transaction(now, now, eventId, tag, rewardVal), true);
+ new Ledger.Transaction(now, now, eventId, tag, rewardVal, 0), true);
}
break;
@@ -268,11 +232,12 @@
final int eventType = getEventType(eventId);
switch (eventType) {
case TYPE_ACTION:
- final long actionCost = economicPolicy.getCostOfAction(eventId, userId, pkgName);
+ final EconomicPolicy.Cost actionCost =
+ economicPolicy.getCostOfAction(eventId, userId, pkgName);
if (ongoingEvent == null) {
ongoingEvents.add(eventId, tag,
- new OngoingEvent(eventId, tag, null, startElapsed, -actionCost));
+ new OngoingEvent(eventId, tag, startElapsed, actionCost));
} else {
ongoingEvent.refCount++;
}
@@ -283,7 +248,7 @@
if (reward != null) {
if (ongoingEvent == null) {
ongoingEvents.add(eventId, tag, new OngoingEvent(
- eventId, tag, reward, startElapsed, reward.ongoingRewardPerSecond));
+ eventId, tag, startElapsed, reward));
} else {
ongoingEvent.refCount++;
}
@@ -306,52 +271,7 @@
@GuardedBy("mLock")
void onPricingChangedLocked() {
- final long now = getCurrentTimeMillis();
- final long nowElapsed = SystemClock.elapsedRealtime();
- final CompleteEconomicPolicy economicPolicy = mIrs.getCompleteEconomicPolicyLocked();
-
- mCurrentOngoingEvents.forEach((userId, pkgName, ongoingEvents) -> {
- final ArraySet<ActionAffordabilityNote> actionAffordabilityNotes =
- mActionAffordabilityNotes.get(userId, pkgName);
- final boolean[] wasAffordable;
- if (actionAffordabilityNotes != null) {
- final int size = actionAffordabilityNotes.size();
- wasAffordable = new boolean[size];
- for (int i = 0; i < size; ++i) {
- final ActionAffordabilityNote note = actionAffordabilityNotes.valueAt(i);
- final long originalBalance =
- mScribe.getLedgerLocked(userId, pkgName).getCurrentBalance();
- wasAffordable[i] = originalBalance >= note.getCachedModifiedPrice();
- }
- } else {
- wasAffordable = EmptyArray.BOOLEAN;
- }
- ongoingEvents.forEach((ongoingEvent) -> {
- // Disable balance check & affordability notifications here because we're in the
- // middle of updating ongoing action costs/prices and sending out notifications
- // or rescheduling the balance check alarm would be a waste since we'll have to
- // redo them again after all of our internal state is updated.
- stopOngoingActionLocked(userId, pkgName, ongoingEvent.eventId,
- ongoingEvent.tag, nowElapsed, now, false, false);
- noteOngoingEventLocked(userId, pkgName, ongoingEvent.eventId, ongoingEvent.tag,
- nowElapsed, false);
- });
- if (actionAffordabilityNotes != null) {
- final int size = actionAffordabilityNotes.size();
- for (int i = 0; i < size; ++i) {
- final ActionAffordabilityNote note = actionAffordabilityNotes.valueAt(i);
- note.recalculateModifiedPrice(economicPolicy, userId, pkgName);
- final long newBalance =
- mScribe.getLedgerLocked(userId, pkgName).getCurrentBalance();
- final boolean isAffordable = newBalance >= note.getCachedModifiedPrice();
- if (wasAffordable[i] != isAffordable) {
- note.setNewAffordability(isAffordable);
- mIrs.postAffordabilityChanged(userId, pkgName, note);
- }
- }
- }
- scheduleBalanceCheckLocked(userId, pkgName);
- });
+ onAnythingChangedLocked(true);
}
@GuardedBy("mLock")
@@ -365,40 +285,21 @@
SparseArrayMap<String, OngoingEvent> ongoingEvents =
mCurrentOngoingEvents.get(userId, pkgName);
if (ongoingEvents != null) {
+ mOngoingEventUpdater.reset(userId, pkgName, now, nowElapsed);
+ ongoingEvents.forEach(mOngoingEventUpdater);
final ArraySet<ActionAffordabilityNote> actionAffordabilityNotes =
mActionAffordabilityNotes.get(userId, pkgName);
- final boolean[] wasAffordable;
if (actionAffordabilityNotes != null) {
final int size = actionAffordabilityNotes.size();
- wasAffordable = new boolean[size];
+ final long newBalance =
+ mScribe.getLedgerLocked(userId, pkgName).getCurrentBalance();
for (int n = 0; n < size; ++n) {
final ActionAffordabilityNote note = actionAffordabilityNotes.valueAt(n);
- final long originalBalance =
- mScribe.getLedgerLocked(userId, pkgName).getCurrentBalance();
- wasAffordable[n] = originalBalance >= note.getCachedModifiedPrice();
- }
- } else {
- wasAffordable = EmptyArray.BOOLEAN;
- }
- ongoingEvents.forEach((ongoingEvent) -> {
- // Disable balance check & affordability notifications here because we're in the
- // middle of updating ongoing action costs/prices and sending out notifications
- // or rescheduling the balance check alarm would be a waste since we'll have to
- // redo them again after all of our internal state is updated.
- stopOngoingActionLocked(userId, pkgName, ongoingEvent.eventId,
- ongoingEvent.tag, nowElapsed, now, false, false);
- noteOngoingEventLocked(userId, pkgName, ongoingEvent.eventId, ongoingEvent.tag,
- nowElapsed, false);
- });
- if (actionAffordabilityNotes != null) {
- final int size = actionAffordabilityNotes.size();
- for (int n = 0; n < size; ++n) {
- final ActionAffordabilityNote note = actionAffordabilityNotes.valueAt(n);
- note.recalculateModifiedPrice(economicPolicy, userId, pkgName);
- final long newBalance =
- mScribe.getLedgerLocked(userId, pkgName).getCurrentBalance();
- final boolean isAffordable = newBalance >= note.getCachedModifiedPrice();
- if (wasAffordable[n] != isAffordable) {
+ note.recalculateCosts(economicPolicy, userId, pkgName);
+ final boolean isAffordable =
+ isAffordableLocked(newBalance,
+ note.getCachedModifiedPrice(), note.getCtp());
+ if (note.isCurrentlyAffordable() != isAffordable) {
note.setNewAffordability(isAffordable);
mIrs.postAffordabilityChanged(userId, pkgName, note);
}
@@ -410,15 +311,68 @@
}
@GuardedBy("mLock")
+ private void onAnythingChangedLocked(final boolean updateOngoingEvents) {
+ final long now = getCurrentTimeMillis();
+ final long nowElapsed = SystemClock.elapsedRealtime();
+ final CompleteEconomicPolicy economicPolicy = mIrs.getCompleteEconomicPolicyLocked();
+
+ for (int uIdx = mCurrentOngoingEvents.numMaps() - 1; uIdx >= 0; --uIdx) {
+ final int userId = mCurrentOngoingEvents.keyAt(uIdx);
+
+ for (int pIdx = mCurrentOngoingEvents.numElementsForKey(userId) - 1; pIdx >= 0;
+ --pIdx) {
+ final String pkgName = mCurrentOngoingEvents.keyAt(uIdx, pIdx);
+
+ SparseArrayMap<String, OngoingEvent> ongoingEvents =
+ mCurrentOngoingEvents.valueAt(uIdx, pIdx);
+ if (ongoingEvents != null) {
+ if (updateOngoingEvents) {
+ mOngoingEventUpdater.reset(userId, pkgName, now, nowElapsed);
+ ongoingEvents.forEach(mOngoingEventUpdater);
+ }
+ scheduleBalanceCheckLocked(userId, pkgName);
+ }
+ }
+ }
+ for (int uIdx = mActionAffordabilityNotes.numMaps() - 1; uIdx >= 0; --uIdx) {
+ final int userId = mActionAffordabilityNotes.keyAt(uIdx);
+
+ for (int pIdx = mActionAffordabilityNotes.numElementsForKey(userId) - 1; pIdx >= 0;
+ --pIdx) {
+ final String pkgName = mActionAffordabilityNotes.keyAt(uIdx, pIdx);
+
+ final ArraySet<ActionAffordabilityNote> actionAffordabilityNotes =
+ mActionAffordabilityNotes.valueAt(uIdx, pIdx);
+
+ if (actionAffordabilityNotes != null) {
+ final int size = actionAffordabilityNotes.size();
+ final long newBalance = getBalanceLocked(userId, pkgName);
+ for (int n = 0; n < size; ++n) {
+ final ActionAffordabilityNote note = actionAffordabilityNotes.valueAt(n);
+ note.recalculateCosts(economicPolicy, userId, pkgName);
+ final boolean isAffordable =
+ isAffordableLocked(newBalance,
+ note.getCachedModifiedPrice(), note.getCtp());
+ if (note.isCurrentlyAffordable() != isAffordable) {
+ note.setNewAffordability(isAffordable);
+ mIrs.postAffordabilityChanged(userId, pkgName, note);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ @GuardedBy("mLock")
void stopOngoingActionLocked(final int userId, @NonNull final String pkgName, final int eventId,
@Nullable String tag, final long nowElapsed, final long now) {
stopOngoingActionLocked(userId, pkgName, eventId, tag, nowElapsed, now, true, true);
}
/**
- * @param updateBalanceCheck Whether or not to reschedule the affordability/balance
+ * @param updateBalanceCheck Whether to reschedule the affordability/balance
* check alarm.
- * @param notifyOnAffordabilityChange Whether or not to evaluate the app's ability to afford
+ * @param notifyOnAffordabilityChange Whether to evaluate the app's ability to afford
* registered bills and notify listeners about any changes.
*/
@GuardedBy("mLock")
@@ -453,9 +407,11 @@
if (ongoingEvent.refCount <= 0) {
final long startElapsed = ongoingEvent.startTimeElapsed;
final long startTime = now - (nowElapsed - startElapsed);
- final long actualDelta = getActualDeltaLocked(ongoingEvent, ledger, nowElapsed, now);
+ final EconomicPolicy.Cost actualDelta =
+ getActualDeltaLocked(ongoingEvent, ledger, nowElapsed, now);
recordTransactionLocked(userId, pkgName, ledger,
- new Ledger.Transaction(startTime, now, eventId, tag, actualDelta),
+ new Ledger.Transaction(startTime, now, eventId, tag, actualDelta.price,
+ actualDelta.costToProduce),
notifyOnAffordabilityChange);
ongoingEvents.delete(eventId, tag);
@@ -466,17 +422,20 @@
}
@GuardedBy("mLock")
- private long getActualDeltaLocked(@NonNull OngoingEvent ongoingEvent, @NonNull Ledger ledger,
- long nowElapsed, long now) {
+ @NonNull
+ private EconomicPolicy.Cost getActualDeltaLocked(@NonNull OngoingEvent ongoingEvent,
+ @NonNull Ledger ledger, long nowElapsed, long now) {
final long startElapsed = ongoingEvent.startTimeElapsed;
final long durationSecs = (nowElapsed - startElapsed) / 1000;
- final long computedDelta = durationSecs * ongoingEvent.deltaPerSec;
+ final long computedDelta = durationSecs * ongoingEvent.getDeltaPerSec();
if (ongoingEvent.reward == null) {
- return computedDelta;
+ return new EconomicPolicy.Cost(
+ durationSecs * ongoingEvent.getCtpPerSec(), computedDelta);
}
final long rewardSum = ledger.get24HourSum(ongoingEvent.eventId, now);
- return Math.max(0,
- Math.min(ongoingEvent.reward.maxDailyReward - rewardSum, computedDelta));
+ return new EconomicPolicy.Cost(0,
+ Math.max(0,
+ Math.min(ongoingEvent.reward.maxDailyReward - rewardSum, computedDelta)));
}
@VisibleForTesting
@@ -494,22 +453,6 @@
return;
}
final CompleteEconomicPolicy economicPolicy = mIrs.getCompleteEconomicPolicyLocked();
- final long maxCirculationAllowed = mIrs.getMaxCirculationLocked();
- final long curNarcsInCirculation = mScribe.getNarcsInCirculationLocked();
- final long newArcsInCirculation = curNarcsInCirculation + transaction.delta;
- if (transaction.delta > 0 && newArcsInCirculation > maxCirculationAllowed) {
- // Set lower bound at 0 so we don't accidentally take away credits when we were trying
- // to _give_ the app credits.
- final long newDelta = Math.max(0, maxCirculationAllowed - curNarcsInCirculation);
- Slog.i(TAG, "Would result in too many credits in circulation. Decreasing transaction "
- + eventToString(transaction.eventId)
- + (transaction.tag == null ? "" : ":" + transaction.tag)
- + " for " + appToString(userId, pkgName)
- + " by " + narcToString(transaction.delta - newDelta));
- transaction = new Ledger.Transaction(
- transaction.startTimeMs, transaction.endTimeMs,
- transaction.eventId, transaction.tag, newDelta);
- }
final long originalBalance = ledger.getCurrentBalance();
if (transaction.delta > 0
&& originalBalance + transaction.delta > economicPolicy.getMaxSatiatedBalance()) {
@@ -524,10 +467,10 @@
+ " by " + narcToString(transaction.delta - newDelta));
transaction = new Ledger.Transaction(
transaction.startTimeMs, transaction.endTimeMs,
- transaction.eventId, transaction.tag, newDelta);
+ transaction.eventId, transaction.tag, newDelta, transaction.ctp);
}
ledger.recordTransaction(transaction);
- mScribe.adjustNarcsInCirculationLocked(transaction.delta);
+ mScribe.adjustRemainingConsumableNarcsLocked(-transaction.ctp);
if (transaction.delta != 0 && notifyOnAffordabilityChange) {
final ArraySet<ActionAffordabilityNote> actionAffordabilityNotes =
mActionAffordabilityNotes.get(userId, pkgName);
@@ -535,7 +478,9 @@
final long newBalance = ledger.getCurrentBalance();
for (int i = 0; i < actionAffordabilityNotes.size(); ++i) {
final ActionAffordabilityNote note = actionAffordabilityNotes.valueAt(i);
- final boolean isAffordable = newBalance >= note.getCachedModifiedPrice();
+ final boolean isAffordable =
+ isAffordableLocked(newBalance,
+ note.getCachedModifiedPrice(), note.getCtp());
if (note.isCurrentlyAffordable() != isAffordable) {
note.setNewAffordability(isAffordable);
mIrs.postAffordabilityChanged(userId, pkgName, note);
@@ -543,6 +488,10 @@
}
}
}
+ if (transaction.ctp != 0) {
+ mHandler.sendEmptyMessage(MSG_CHECK_ALL_AFFORDABILITY);
+ mIrs.maybePerformQuantitativeEasingLocked();
+ }
}
/**
@@ -599,8 +548,8 @@
}
recordTransactionLocked(userId, pkgName, ledger,
- new Ledger.Transaction(
- now, now, REGULATION_WEALTH_RECLAMATION, null, -toReclaim),
+ new Ledger.Transaction(now, now, REGULATION_WEALTH_RECLAMATION,
+ null, -toReclaim, 0),
true);
}
}
@@ -648,7 +597,7 @@
final long now = getCurrentTimeMillis();
final Ledger ledger = mScribe.getLedgerLocked(userId, pkgName);
recordTransactionLocked(userId, pkgName, ledger,
- new Ledger.Transaction(now, now, REGULATION_DEMOTION, null, -toReclaim),
+ new Ledger.Transaction(now, now, REGULATION_DEMOTION, null, -toReclaim, 0),
true);
}
}
@@ -665,10 +614,13 @@
return !mIrs.isSystem(userId, packageInfo.packageName);
}
+ void onCreditSupplyChanged() {
+ mHandler.sendEmptyMessage(MSG_CHECK_ALL_AFFORDABILITY);
+ }
+
@GuardedBy("mLock")
void distributeBasicIncomeLocked(int batteryLevel) {
List<PackageInfo> pkgs = mIrs.getInstalledPackages();
- pkgs.sort(mPackageDistributionComparator);
final long now = getCurrentTimeMillis();
for (int i = 0; i < pkgs.size(); ++i) {
@@ -686,7 +638,7 @@
if (shortfall > 0) {
recordTransactionLocked(userId, pkgName, ledger,
new Ledger.Transaction(now, now, REGULATION_BASIC_INCOME,
- null, (long) (perc * shortfall)), true);
+ null, (long) (perc * shortfall), 0), true);
}
}
}
@@ -705,12 +657,8 @@
@GuardedBy("mLock")
void grantBirthrightsLocked(final int userId) {
final List<PackageInfo> pkgs = mIrs.getInstalledPackages(userId);
- final long maxBirthright =
- mIrs.getMaxCirculationLocked() / mIrs.getInstalledPackages().size();
final long now = getCurrentTimeMillis();
- pkgs.sort(mPackageDistributionComparator);
-
for (int i = 0; i < pkgs.size(); ++i) {
final PackageInfo packageInfo = pkgs.get(i);
if (!shouldGiveCredits(packageInfo)) {
@@ -726,7 +674,7 @@
recordTransactionLocked(userId, pkgName, ledger,
new Ledger.Transaction(now, now, REGULATION_BIRTHRIGHT, null,
- Math.min(maxBirthright, mIrs.getMinBalanceLocked(userId, pkgName))),
+ mIrs.getMinBalanceLocked(userId, pkgName), 0),
true);
}
}
@@ -740,14 +688,11 @@
return;
}
- List<PackageInfo> pkgs = mIrs.getInstalledPackages();
- final int numPackages = pkgs.size();
- final long maxBirthright = mIrs.getMaxCirculationLocked() / numPackages;
final long now = getCurrentTimeMillis();
recordTransactionLocked(userId, pkgName, ledger,
new Ledger.Transaction(now, now, REGULATION_BIRTHRIGHT, null,
- Math.min(maxBirthright, mIrs.getMinBalanceLocked(userId, pkgName))), true);
+ mIrs.getMinBalanceLocked(userId, pkgName), 0), true);
}
@GuardedBy("mLock")
@@ -762,7 +707,7 @@
final long now = getCurrentTimeMillis();
recordTransactionLocked(userId, pkgName, ledger,
- new Ledger.Transaction(now, now, REGULATION_PROMOTION, null, missing), true);
+ new Ledger.Transaction(now, now, REGULATION_PROMOTION, null, missing, 0), true);
}
@GuardedBy("mLock")
@@ -779,7 +724,7 @@
private void reclaimAssetsLocked(final int userId, @NonNull final String pkgName) {
final Ledger ledger = mScribe.getLedgerLocked(userId, pkgName);
if (ledger.getCurrentBalance() != 0) {
- mScribe.adjustNarcsInCirculationLocked(-ledger.getCurrentBalance());
+ mScribe.adjustRemainingConsumableNarcsLocked(-ledger.getCurrentBalance());
}
mScribe.discardLedgerLocked(userId, pkgName);
mCurrentOngoingEvents.delete(userId, pkgName);
@@ -803,6 +748,7 @@
static final long WILL_NOT_CROSS_THRESHOLD = -1;
private long mCurBalance;
+ private long mRemainingConsumableCredits;
/**
* The maximum change in credits per second towards the upper threshold
* {@link #mUpperThreshold}. A value of 0 means the current ongoing events will never
@@ -815,15 +761,25 @@
* result in the app crossing the lower threshold.
*/
private long mMaxDeltaPerSecToLowerThreshold;
+ /**
+ * The maximum change in credits per second towards the highest CTP threshold below the
+ * remaining consumable credits (cached in {@link #mCtpThreshold}). A value of 0 means
+ * the current ongoing events will never result in the app crossing the lower threshold.
+ */
+ private long mMaxDeltaPerSecToCtpThreshold;
private long mUpperThreshold;
private long mLowerThreshold;
+ private long mCtpThreshold;
- void reset(long curBalance,
+ void reset(long curBalance, long remainingConsumableCredits,
@Nullable ArraySet<ActionAffordabilityNote> actionAffordabilityNotes) {
mCurBalance = curBalance;
+ mRemainingConsumableCredits = remainingConsumableCredits;
mMaxDeltaPerSecToUpperThreshold = mMaxDeltaPerSecToLowerThreshold = 0;
+ mMaxDeltaPerSecToCtpThreshold = 0;
mUpperThreshold = Long.MIN_VALUE;
mLowerThreshold = Long.MAX_VALUE;
+ mCtpThreshold = 0;
if (actionAffordabilityNotes != null) {
for (int i = 0; i < actionAffordabilityNotes.size(); ++i) {
final ActionAffordabilityNote note = actionAffordabilityNotes.valueAt(i);
@@ -835,6 +791,10 @@
mUpperThreshold = (mUpperThreshold == Long.MIN_VALUE)
? price : Math.min(mUpperThreshold, price);
}
+ final long ctp = note.getCtp();
+ if (ctp <= mRemainingConsumableCredits) {
+ mCtpThreshold = Math.max(mCtpThreshold, ctp);
+ }
}
}
}
@@ -847,13 +807,23 @@
* threshold.
*/
long getTimeToCrossLowerThresholdMs() {
- if (mMaxDeltaPerSecToLowerThreshold == 0) {
- // Will never cross upper threshold based on current events.
+ if (mMaxDeltaPerSecToLowerThreshold == 0 && mMaxDeltaPerSecToCtpThreshold == 0) {
+ // Will never cross lower threshold based on current events.
return WILL_NOT_CROSS_THRESHOLD;
}
- // deltaPerSec is a negative value, so do threshold-balance to cancel out the negative.
- final long minSeconds =
- (mLowerThreshold - mCurBalance) / mMaxDeltaPerSecToLowerThreshold;
+ long minSeconds = Long.MAX_VALUE;
+ if (mMaxDeltaPerSecToLowerThreshold != 0) {
+ // deltaPerSec is a negative value, so do threshold-balance to cancel out the
+ // negative.
+ minSeconds = (mLowerThreshold - mCurBalance) / mMaxDeltaPerSecToLowerThreshold;
+ }
+ if (mMaxDeltaPerSecToCtpThreshold != 0) {
+ minSeconds = Math.min(minSeconds,
+ // deltaPerSec is a negative value, so do threshold-balance to cancel
+ // out the negative.
+ (mCtpThreshold - mRemainingConsumableCredits)
+ / mMaxDeltaPerSecToCtpThreshold);
+ }
return minSeconds * 1000;
}
@@ -876,10 +846,15 @@
@Override
public void accept(OngoingEvent ongoingEvent) {
- if (mCurBalance >= mLowerThreshold && ongoingEvent.deltaPerSec < 0) {
- mMaxDeltaPerSecToLowerThreshold += ongoingEvent.deltaPerSec;
- } else if (mCurBalance < mUpperThreshold && ongoingEvent.deltaPerSec > 0) {
- mMaxDeltaPerSecToUpperThreshold += ongoingEvent.deltaPerSec;
+ final long deltaPerSec = ongoingEvent.getDeltaPerSec();
+ if (mCurBalance >= mLowerThreshold && deltaPerSec < 0) {
+ mMaxDeltaPerSecToLowerThreshold += deltaPerSec;
+ } else if (mCurBalance < mUpperThreshold && deltaPerSec > 0) {
+ mMaxDeltaPerSecToUpperThreshold += deltaPerSec;
+ }
+ final long ctpPerSec = ongoingEvent.getCtpPerSec();
+ if (mRemainingConsumableCredits >= mCtpThreshold && deltaPerSec < 0) {
+ mMaxDeltaPerSecToCtpThreshold -= ctpPerSec;
}
}
}
@@ -896,8 +871,9 @@
mBalanceThresholdAlarmQueue.removeAlarmForKey(new Package(userId, pkgName));
return;
}
- mTrendCalculator.reset(
- getBalanceLocked(userId, pkgName), mActionAffordabilityNotes.get(userId, pkgName));
+ mTrendCalculator.reset(getBalanceLocked(userId, pkgName),
+ mScribe.getRemainingConsumableNarcsLocked(),
+ mActionAffordabilityNotes.get(userId, pkgName));
ongoingEvents.forEach(mTrendCalculator);
final long lowerTimeMs = mTrendCalculator.getTimeToCrossLowerThresholdMs();
final long upperTimeMs = mTrendCalculator.getTimeToCrossUpperThresholdMs();
@@ -931,20 +907,79 @@
public final String tag;
@Nullable
public final EconomicPolicy.Reward reward;
- public final long deltaPerSec;
+ @Nullable
+ public final EconomicPolicy.Cost actionCost;
public int refCount;
- OngoingEvent(int eventId, @Nullable String tag,
- @Nullable EconomicPolicy.Reward reward, long startTimeElapsed, long deltaPerSec) {
+ OngoingEvent(int eventId, @Nullable String tag, long startTimeElapsed,
+ @NonNull EconomicPolicy.Reward reward) {
this.startTimeElapsed = startTimeElapsed;
this.eventId = eventId;
this.tag = tag;
this.reward = reward;
- this.deltaPerSec = deltaPerSec;
+ this.actionCost = null;
refCount = 1;
}
+
+ OngoingEvent(int eventId, @Nullable String tag, long startTimeElapsed,
+ @NonNull EconomicPolicy.Cost actionCost) {
+ this.startTimeElapsed = startTimeElapsed;
+ this.eventId = eventId;
+ this.tag = tag;
+ this.reward = null;
+ this.actionCost = actionCost;
+ refCount = 1;
+ }
+
+ long getDeltaPerSec() {
+ if (actionCost != null) {
+ return -actionCost.price;
+ }
+ if (reward != null) {
+ return reward.ongoingRewardPerSecond;
+ }
+ Slog.wtfStack(TAG, "No action or reward in ongoing event?!??!");
+ return 0;
+ }
+
+ long getCtpPerSec() {
+ if (actionCost != null) {
+ return actionCost.costToProduce;
+ }
+ return 0;
+ }
}
+ private class OngoingEventUpdater implements Consumer<OngoingEvent> {
+ private int mUserId;
+ private String mPkgName;
+ private long mNow;
+ private long mNowElapsed;
+
+ private void reset(int userId, String pkgName, long now, long nowElapsed) {
+ mUserId = userId;
+ mPkgName = pkgName;
+ mNow = now;
+ mNowElapsed = nowElapsed;
+ }
+
+ @Override
+ public void accept(OngoingEvent ongoingEvent) {
+ // Disable balance check & affordability notifications here because
+ // we're in the middle of updating ongoing action costs/prices and
+ // sending out notifications or rescheduling the balance check alarm
+ // would be a waste since we'll have to redo them again after all of
+ // our internal state is updated.
+ final boolean updateBalanceCheck = false;
+ stopOngoingActionLocked(mUserId, mPkgName, ongoingEvent.eventId, ongoingEvent.tag,
+ mNowElapsed, mNow, updateBalanceCheck, /* notifyOnAffordabilityChange */ false);
+ noteOngoingEventLocked(mUserId, mPkgName, ongoingEvent.eventId, ongoingEvent.tag,
+ mNowElapsed, updateBalanceCheck);
+ }
+ }
+
+ private final OngoingEventUpdater mOngoingEventUpdater = new OngoingEventUpdater();
+
private static final class Package {
public final String packageName;
public final int userId;
@@ -996,7 +1031,8 @@
protected void processExpiredAlarms(@NonNull ArraySet<Package> expired) {
for (int i = 0; i < expired.size(); ++i) {
Package p = expired.valueAt(i);
- mHandler.obtainMessage(MSG_CHECK_BALANCE, p.userId, 0, p.packageName)
+ mHandler.obtainMessage(
+ MSG_CHECK_INDIVIDUAL_AFFORDABILITY, p.userId, 0, p.packageName)
.sendToTarget();
}
}
@@ -1023,9 +1059,10 @@
note.setNewAffordability(true);
return;
}
- note.recalculateModifiedPrice(economicPolicy, userId, pkgName);
+ note.recalculateCosts(economicPolicy, userId, pkgName);
note.setNewAffordability(
- getBalanceLocked(userId, pkgName) >= note.getCachedModifiedPrice());
+ isAffordableLocked(getBalanceLocked(userId, pkgName),
+ note.getCachedModifiedPrice(), note.getCtp()));
mIrs.postAffordabilityChanged(userId, pkgName, note);
// Update ongoing alarm
scheduleBalanceCheckLocked(userId, pkgName);
@@ -1052,6 +1089,7 @@
static final class ActionAffordabilityNote {
private final EconomyManagerInternal.ActionBill mActionBill;
private final EconomyManagerInternal.AffordabilityChangeListener mListener;
+ private long mCtp;
private long mModifiedPrice;
private boolean mIsAffordable;
@@ -1086,22 +1124,29 @@
return mModifiedPrice;
}
+ private long getCtp() {
+ return mCtp;
+ }
+
@VisibleForTesting
- long recalculateModifiedPrice(@NonNull EconomicPolicy economicPolicy,
+ void recalculateCosts(@NonNull EconomicPolicy economicPolicy,
int userId, @NonNull String pkgName) {
long modifiedPrice = 0;
+ long ctp = 0;
final List<EconomyManagerInternal.AnticipatedAction> anticipatedActions =
mActionBill.getAnticipatedActions();
for (int i = 0; i < anticipatedActions.size(); ++i) {
final EconomyManagerInternal.AnticipatedAction aa = anticipatedActions.get(i);
- final long actionCost =
+ final EconomicPolicy.Cost actionCost =
economicPolicy.getCostOfAction(aa.actionId, userId, pkgName);
- modifiedPrice += actionCost * aa.numInstantaneousCalls
- + actionCost * (aa.ongoingDurationMs / 1000);
+ modifiedPrice += actionCost.price * aa.numInstantaneousCalls
+ + actionCost.price * (aa.ongoingDurationMs / 1000);
+ ctp += actionCost.costToProduce * aa.numInstantaneousCalls
+ + actionCost.costToProduce * (aa.ongoingDurationMs / 1000);
}
mModifiedPrice = modifiedPrice;
- return modifiedPrice;
+ mCtp = ctp;
}
boolean isCurrentlyAffordable() {
@@ -1138,7 +1183,15 @@
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
- case MSG_CHECK_BALANCE: {
+ case MSG_CHECK_ALL_AFFORDABILITY: {
+ synchronized (mLock) {
+ removeMessages(MSG_CHECK_ALL_AFFORDABILITY);
+ onAnythingChangedLocked(false);
+ }
+ }
+ break;
+
+ case MSG_CHECK_INDIVIDUAL_AFFORDABILITY: {
final int userId = msg.arg1;
final String pkgName = (String) msg.obj;
synchronized (mLock) {
@@ -1151,8 +1204,8 @@
for (int i = 0; i < actionAffordabilityNotes.size(); ++i) {
final ActionAffordabilityNote note =
actionAffordabilityNotes.valueAt(i);
- final boolean isAffordable =
- newBalance >= note.getCachedModifiedPrice();
+ final boolean isAffordable = isAffordableLocked(
+ newBalance, note.getCachedModifiedPrice(), note.getCtp());
if (note.isCurrentlyAffordable() != isAffordable) {
note.setNewAffordability(isAffordable);
mIrs.postAffordabilityChanged(userId, pkgName, note);
@@ -1207,7 +1260,12 @@
pw.print(" runtime=");
TimeUtils.formatDuration(nowElapsed - ongoingEvent.startTimeElapsed, pw);
pw.print(" delta/sec=");
- pw.print(ongoingEvent.deltaPerSec);
+ pw.print(narcToString(ongoingEvent.getDeltaPerSec()));
+ final long ctp = ongoingEvent.getCtpPerSec();
+ if (ctp != 0) {
+ pw.print(" ctp/sec=");
+ pw.print(narcToString(ongoingEvent.getCtpPerSec()));
+ }
pw.print(" refCount=");
pw.print(ongoingEvent.refCount);
pw.println();
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/AlarmManagerEconomicPolicy.java b/apex/jobscheduler/service/java/com/android/server/tare/AlarmManagerEconomicPolicy.java
index e1e6e47..71e00cf 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/AlarmManagerEconomicPolicy.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/AlarmManagerEconomicPolicy.java
@@ -33,7 +33,8 @@
import static android.app.tare.EconomyManager.DEFAULT_AM_ACTION_ALARM_INEXACT_NONWAKEUP_CTP;
import static android.app.tare.EconomyManager.DEFAULT_AM_ACTION_ALARM_INEXACT_WAKEUP_BASE_PRICE;
import static android.app.tare.EconomyManager.DEFAULT_AM_ACTION_ALARM_INEXACT_WAKEUP_CTP;
-import static android.app.tare.EconomyManager.DEFAULT_AM_MAX_CIRCULATION;
+import static android.app.tare.EconomyManager.DEFAULT_AM_HARD_CONSUMPTION_LIMIT;
+import static android.app.tare.EconomyManager.DEFAULT_AM_INITIAL_CONSUMPTION_LIMIT;
import static android.app.tare.EconomyManager.DEFAULT_AM_MAX_SATIATED_BALANCE;
import static android.app.tare.EconomyManager.DEFAULT_AM_MIN_SATIATED_BALANCE_EXEMPTED;
import static android.app.tare.EconomyManager.DEFAULT_AM_MIN_SATIATED_BALANCE_OTHER_APP;
@@ -70,7 +71,8 @@
import static android.app.tare.EconomyManager.KEY_AM_ACTION_ALARM_INEXACT_NONWAKEUP_CTP;
import static android.app.tare.EconomyManager.KEY_AM_ACTION_ALARM_INEXACT_WAKEUP_BASE_PRICE;
import static android.app.tare.EconomyManager.KEY_AM_ACTION_ALARM_INEXACT_WAKEUP_CTP;
-import static android.app.tare.EconomyManager.KEY_AM_MAX_CIRCULATION;
+import static android.app.tare.EconomyManager.KEY_AM_HARD_CONSUMPTION_LIMIT;
+import static android.app.tare.EconomyManager.KEY_AM_INITIAL_CONSUMPTION_LIMIT;
import static android.app.tare.EconomyManager.KEY_AM_MAX_SATIATED_BALANCE;
import static android.app.tare.EconomyManager.KEY_AM_MIN_SATIATED_BALANCE_EXEMPTED;
import static android.app.tare.EconomyManager.KEY_AM_MIN_SATIATED_BALANCE_OTHER_APP;
@@ -143,7 +145,8 @@
private long mMinSatiatedBalanceExempted;
private long mMinSatiatedBalanceOther;
private long mMaxSatiatedBalance;
- private long mMaxSatiatedCirculation;
+ private long mInitialSatiatedConsumptionLimit;
+ private long mHardSatiatedConsumptionLimit;
private final KeyValueListParser mParser = new KeyValueListParser(',');
private final InternalResourceService mInternalResourceService;
@@ -179,8 +182,13 @@
}
@Override
- long getMaxSatiatedCirculation() {
- return mMaxSatiatedCirculation;
+ long getInitialSatiatedConsumptionLimit() {
+ return mInitialSatiatedConsumptionLimit;
+ }
+
+ @Override
+ long getHardSatiatedConsumptionLimit() {
+ return mHardSatiatedConsumptionLimit;
}
@NonNull
@@ -217,8 +225,11 @@
DEFAULT_AM_MIN_SATIATED_BALANCE_OTHER_APP));
mMaxSatiatedBalance = arcToNarc(mParser.getInt(KEY_AM_MAX_SATIATED_BALANCE,
DEFAULT_AM_MAX_SATIATED_BALANCE));
- mMaxSatiatedCirculation = arcToNarc(mParser.getInt(KEY_AM_MAX_CIRCULATION,
- DEFAULT_AM_MAX_CIRCULATION));
+ mInitialSatiatedConsumptionLimit = arcToNarc(mParser.getInt(
+ KEY_AM_INITIAL_CONSUMPTION_LIMIT, DEFAULT_AM_INITIAL_CONSUMPTION_LIMIT));
+ mHardSatiatedConsumptionLimit = Math.max(mInitialSatiatedConsumptionLimit,
+ arcToNarc(mParser.getInt(
+ KEY_AM_HARD_CONSUMPTION_LIMIT, DEFAULT_AM_HARD_CONSUMPTION_LIMIT)));
final long exactAllowWhileIdleWakeupBasePrice = arcToNarc(
mParser.getInt(KEY_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_EXACT_WAKEUP_BASE_PRICE,
@@ -357,7 +368,11 @@
pw.print("Other", narcToString(mMinSatiatedBalanceOther)).println();
pw.decreaseIndent();
pw.print("Max satiated balance", narcToString(mMaxSatiatedBalance)).println();
- pw.print("Max satiated circulation", narcToString(mMaxSatiatedCirculation)).println();
+ pw.print("Consumption limits: [");
+ pw.print(narcToString(mInitialSatiatedConsumptionLimit));
+ pw.print(", ");
+ pw.print(narcToString(mHardSatiatedConsumptionLimit));
+ pw.println("]");
pw.println();
pw.println("Actions:");
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/CompleteEconomicPolicy.java b/apex/jobscheduler/service/java/com/android/server/tare/CompleteEconomicPolicy.java
index a4e7b80..2109a85 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/CompleteEconomicPolicy.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/CompleteEconomicPolicy.java
@@ -34,7 +34,7 @@
private final SparseArray<Reward> mRewards = new SparseArray<>();
private final int[] mCostModifiers;
private long mMaxSatiatedBalance;
- private long mMaxSatiatedCirculation;
+ private long mConsumptionLimit;
CompleteEconomicPolicy(@NonNull InternalResourceService irs) {
super(irs);
@@ -74,9 +74,9 @@
max = 0;
for (int i = 0; i < mEnabledEconomicPolicies.size(); ++i) {
- max += mEnabledEconomicPolicies.valueAt(i).getMaxSatiatedCirculation();
+ max += mEnabledEconomicPolicies.valueAt(i).getInitialSatiatedConsumptionLimit();
}
- mMaxSatiatedCirculation = max;
+ mConsumptionLimit = max;
}
@Override
@@ -94,8 +94,13 @@
}
@Override
- long getMaxSatiatedCirculation() {
- return mMaxSatiatedCirculation;
+ long getInitialSatiatedConsumptionLimit() {
+ return mConsumptionLimit;
+ }
+
+ @Override
+ long getHardSatiatedConsumptionLimit() {
+ return mConsumptionLimit;
}
@NonNull
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/EconomicPolicy.java b/apex/jobscheduler/service/java/com/android/server/tare/EconomicPolicy.java
index c1177b2..1e48015 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/EconomicPolicy.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/EconomicPolicy.java
@@ -151,6 +151,16 @@
}
}
+ static class Cost {
+ public final long costToProduce;
+ public final long price;
+
+ Cost(long costToProduce, long price) {
+ this.costToProduce = costToProduce;
+ this.price = price;
+ }
+ }
+
private static final Modifier[] COST_MODIFIER_BY_INDEX = new Modifier[NUM_COST_MODIFIERS];
EconomicPolicy(@NonNull InternalResourceService irs) {
@@ -193,10 +203,18 @@
abstract long getMaxSatiatedBalance();
/**
- * Returns the maximum number of narcs that should be in circulation at once when the device is
- * at 100% battery.
+ * Returns the maximum number of narcs that should be consumed during a full 100% discharge
+ * cycle. This is the initial limit. The system may choose to increase the limit over time,
+ * but the increased limit should never exceed the value returned from
+ * {@link #getHardSatiatedConsumptionLimit()}.
*/
- abstract long getMaxSatiatedCirculation();
+ abstract long getInitialSatiatedConsumptionLimit();
+
+ /**
+ * Returns the maximum number of narcs that should be consumed during a full 100% discharge
+ * cycle. This is the hard limit that should never be exceeded.
+ */
+ abstract long getHardSatiatedConsumptionLimit();
/** Return the set of modifiers that should apply to this policy's costs. */
@NonNull
@@ -211,10 +229,11 @@
void dump(IndentingPrintWriter pw) {
}
- final long getCostOfAction(int actionId, int userId, @NonNull String pkgName) {
+ @NonNull
+ final Cost getCostOfAction(int actionId, int userId, @NonNull String pkgName) {
final Action action = getAction(actionId);
if (action == null) {
- return 0;
+ return new Cost(0, 0);
}
long ctp = action.costToProduce;
long price = action.basePrice;
@@ -235,7 +254,7 @@
(ProcessStateModifier) getModifier(COST_MODIFIER_PROCESS_STATE);
price = processStateModifier.getModifiedPrice(userId, pkgName, ctp, price);
}
- return price;
+ return new Cost(ctp, price);
}
private static void initModifier(@Modifier.CostModifier final int modifierId,
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 36895a5..c934807 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java
@@ -69,6 +69,7 @@
import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.pm.UserManagerInternal;
+import com.android.server.tare.EconomicPolicy.Cost;
import com.android.server.tare.EconomyManagerInternal.TareStateChangeListener;
import java.io.FileDescriptor;
@@ -100,6 +101,11 @@
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 battery level above which we may consider quantitative easing (increasing the consumption
+ * limit).
+ */
+ private static final int QUANTITATIVE_EASING_BATTERY_THRESHOLD = 50;
private static final int PACKAGE_QUERY_FLAGS =
PackageManager.MATCH_DIRECT_BOOT_AWARE | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
| PackageManager.MATCH_APEX;
@@ -122,44 +128,6 @@
@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<>();
@@ -266,8 +234,7 @@
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_FORCE_RECLAIM = 3;
- private static final int MSG_NOTIFY_STATE_CHANGE_LISTENERS = 4;
+ private static final int MSG_NOTIFY_STATE_CHANGE_LISTENERS = 3;
private static final String ALARM_TAG_WEALTH_RECLAMATION = "*tare.reclamation*";
/**
@@ -362,8 +329,8 @@
}
@GuardedBy("mLock")
- long getMaxCirculationLocked() {
- return mCurrentBatteryLevel * mCompleteEconomicPolicy.getMaxSatiatedCirculation() / 100;
+ long getConsumptionLimitLocked() {
+ return mCurrentBatteryLevel * mScribe.getSatiatedConsumptionLimitLocked() / 100;
}
@GuardedBy("mLock")
@@ -372,6 +339,11 @@
/ 100;
}
+ @GuardedBy("mLock")
+ long getInitialSatiatedConsumptionLimitLocked() {
+ return mCompleteEconomicPolicy.getInitialSatiatedConsumptionLimit();
+ }
+
int getUid(final int userId, @NonNull final String pkgName) {
synchronized (mPackageToUidCache) {
Integer uid = mPackageToUidCache.get(userId, pkgName);
@@ -403,12 +375,15 @@
void onBatteryLevelChanged() {
synchronized (mLock) {
final int newBatteryLevel = getCurrentBatteryLevel();
- if (newBatteryLevel > mCurrentBatteryLevel) {
+ final boolean increased = newBatteryLevel > mCurrentBatteryLevel;
+ if (increased) {
mAgent.distributeBasicIncomeLocked(newBatteryLevel);
- } else if (newBatteryLevel < mCurrentBatteryLevel) {
- mHandler.obtainMessage(MSG_MAYBE_FORCE_RECLAIM).sendToTarget();
+ } else if (newBatteryLevel == mCurrentBatteryLevel) {
+ Slog.wtf(TAG, "Battery level stayed the same");
+ return;
}
mCurrentBatteryLevel = newBatteryLevel;
+ adjustCreditSupplyLocked(increased);
}
}
@@ -546,6 +521,31 @@
}
}
+ /**
+ * Try to increase the consumption limit if apps are reaching the current limit too quickly.
+ */
+ @GuardedBy("mLock")
+ void maybePerformQuantitativeEasingLocked() {
+ // We don't need to increase the limit if the device runs out of consumable credits
+ // when the battery is low.
+ final long remainingConsumableNarcs = mScribe.getRemainingConsumableNarcsLocked();
+ if (mCurrentBatteryLevel <= QUANTITATIVE_EASING_BATTERY_THRESHOLD
+ || remainingConsumableNarcs > 0) {
+ return;
+ }
+ final long currentConsumptionLimit = mScribe.getSatiatedConsumptionLimitLocked();
+ final long shortfall = (mCurrentBatteryLevel - QUANTITATIVE_EASING_BATTERY_THRESHOLD)
+ * currentConsumptionLimit / 100;
+ final long newConsumptionLimit = Math.min(currentConsumptionLimit + shortfall,
+ mCompleteEconomicPolicy.getHardSatiatedConsumptionLimit());
+ if (newConsumptionLimit != currentConsumptionLimit) {
+ Slog.i(TAG, "Increasing consumption limit from " + narcToString(currentConsumptionLimit)
+ + " to " + narcToString(newConsumptionLimit));
+ mScribe.setConsumptionLimitLocked(newConsumptionLimit);
+ adjustCreditSupplyLocked(/* allowIncrease */ true);
+ }
+ }
+
void postAffordabilityChanged(final int userId, @NonNull final String pkgName,
@NonNull Agent.ActionAffordabilityNote affordabilityNote) {
if (DEBUG) {
@@ -560,6 +560,23 @@
}
@GuardedBy("mLock")
+ private void adjustCreditSupplyLocked(boolean allowIncrease) {
+ final long newLimit = getConsumptionLimitLocked();
+ final long remainingConsumableNarcs = mScribe.getRemainingConsumableNarcsLocked();
+ if (remainingConsumableNarcs == newLimit) {
+ return;
+ }
+ if (remainingConsumableNarcs > newLimit) {
+ mScribe.adjustRemainingConsumableNarcsLocked(newLimit - remainingConsumableNarcs);
+ } else if (allowIncrease) {
+ final double perc = mCurrentBatteryLevel / 100d;
+ final long shortfall = newLimit - remainingConsumableNarcs;
+ mScribe.adjustRemainingConsumableNarcsLocked((long) (perc * shortfall));
+ }
+ mAgent.onCreditSupplyChanged();
+ }
+
+ @GuardedBy("mLock")
private void processUsageEventLocked(final int userId, @NonNull UsageEvents.Event event) {
if (!mIsEnabled) {
return;
@@ -650,48 +667,6 @@
}
}
- /**
- * 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);
@@ -731,8 +706,18 @@
final boolean isFirstSetup = !mScribe.recordExists();
if (isFirstSetup) {
mAgent.grantBirthrightsLocked();
+ mScribe.setConsumptionLimitLocked(
+ mCompleteEconomicPolicy.getInitialSatiatedConsumptionLimit());
} else {
mScribe.loadFromDiskLocked();
+ if (mScribe.getSatiatedConsumptionLimitLocked()
+ < mCompleteEconomicPolicy.getInitialSatiatedConsumptionLimit()
+ || mScribe.getSatiatedConsumptionLimitLocked()
+ > mCompleteEconomicPolicy.getHardSatiatedConsumptionLimit()) {
+ // Reset the consumption limit since several factors may have changed.
+ mScribe.setConsumptionLimitLocked(
+ mCompleteEconomicPolicy.getInitialSatiatedConsumptionLimit());
+ }
}
scheduleUnusedWealthReclamationLocked();
}
@@ -787,14 +772,6 @@
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
- case MSG_MAYBE_FORCE_RECLAIM: {
- removeMessages(MSG_MAYBE_FORCE_RECLAIM);
- synchronized (mLock) {
- maybeForceReclaimLocked();
- }
- }
- break;
-
case MSG_NOTIFY_AFFORDABILITY_CHANGE_LISTENER: {
final SomeArgs args = (SomeArgs) msg.obj;
final int userId = args.argi1;
@@ -936,12 +913,13 @@
synchronized (mLock) {
for (int i = 0; i < projectedActions.size(); ++i) {
AnticipatedAction action = projectedActions.get(i);
- final long cost = mCompleteEconomicPolicy.getCostOfAction(
+ final Cost cost = mCompleteEconomicPolicy.getCostOfAction(
action.actionId, userId, pkgName);
- requiredBalance += cost * action.numInstantaneousCalls
- + cost * (action.ongoingDurationMs / 1000);
+ requiredBalance += cost.price * action.numInstantaneousCalls
+ + cost.price * (action.ongoingDurationMs / 1000);
}
- return mAgent.getBalanceLocked(userId, pkgName) >= requiredBalance;
+ return mAgent.getBalanceLocked(userId, pkgName) >= requiredBalance
+ && mScribe.getRemainingConsumableNarcsLocked() >= requiredBalance;
}
}
@@ -960,14 +938,17 @@
synchronized (mLock) {
for (int i = 0; i < projectedActions.size(); ++i) {
AnticipatedAction action = projectedActions.get(i);
- final long cost = mCompleteEconomicPolicy.getCostOfAction(
+ final Cost cost = mCompleteEconomicPolicy.getCostOfAction(
action.actionId, userId, pkgName);
- totalCostPerSecond += cost;
+ totalCostPerSecond += cost.price;
}
if (totalCostPerSecond == 0) {
return FOREVER_MS;
}
- return mAgent.getBalanceLocked(userId, pkgName) * 1000 / totalCostPerSecond;
+ final long minBalance = Math.min(
+ mAgent.getBalanceLocked(userId, pkgName),
+ mScribe.getRemainingConsumableNarcsLocked());
+ return minBalance * 1000 / totalCostPerSecond;
}
}
@@ -1085,10 +1066,20 @@
private void updateEconomicPolicy() {
synchronized (mLock) {
+ final long initialLimit =
+ mCompleteEconomicPolicy.getInitialSatiatedConsumptionLimit();
+ final long hardLimit = mCompleteEconomicPolicy.getHardSatiatedConsumptionLimit();
mCompleteEconomicPolicy.tearDown();
mCompleteEconomicPolicy = new CompleteEconomicPolicy(InternalResourceService.this);
if (mIsEnabled && mBootPhase >= PHASE_SYSTEM_SERVICES_READY) {
mCompleteEconomicPolicy.setup();
+ if (initialLimit != mCompleteEconomicPolicy.getInitialSatiatedConsumptionLimit()
+ || hardLimit
+ != mCompleteEconomicPolicy.getHardSatiatedConsumptionLimit()) {
+ // Reset the consumption limit since several factors may have changed.
+ mScribe.setConsumptionLimitLocked(
+ mCompleteEconomicPolicy.getInitialSatiatedConsumptionLimit());
+ }
mAgent.onPricingChangedLocked();
}
}
@@ -1110,18 +1101,23 @@
pw.print("Current battery level: ");
pw.println(mCurrentBatteryLevel);
- final long maxCirculation = getMaxCirculationLocked();
- pw.print("Max circulation (current/satiated): ");
- pw.print(narcToString(maxCirculation));
+ final long consumptionLimit = getConsumptionLimitLocked();
+ pw.print("Consumption limit (current/initial-satiated/current-satiated): ");
+ pw.print(narcToString(consumptionLimit));
pw.print("/");
- pw.println(narcToString(mCompleteEconomicPolicy.getMaxSatiatedCirculation()));
+ pw.print(narcToString(mCompleteEconomicPolicy.getInitialSatiatedConsumptionLimit()));
+ pw.print("/");
+ pw.println(narcToString(mScribe.getSatiatedConsumptionLimitLocked()));
- final long currentCirculation = mScribe.getNarcsInCirculationLocked();
- pw.print("Current GDP: ");
- pw.print(narcToString(currentCirculation));
+ final long remainingConsumable = mScribe.getRemainingConsumableNarcsLocked();
+ pw.print("Goods remaining: ");
+ pw.print(narcToString(remainingConsumable));
pw.print(" (");
- pw.print(String.format("%.2f", 100f * currentCirculation / maxCirculation));
- pw.println("% of current max)");
+ pw.print(String.format("%.2f", 100f * remainingConsumable / consumptionLimit));
+ pw.println("% of current limit)");
+
+ pw.print("Device wealth: ");
+ pw.println(narcToString(mScribe.getNarcsInCirculationForLoggingLocked()));
pw.println();
pw.print("Exempted apps", mExemptedApps);
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 1f8ce26..0eddd22 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/JobSchedulerEconomicPolicy.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/JobSchedulerEconomicPolicy.java
@@ -38,7 +38,8 @@
import static android.app.tare.EconomyManager.DEFAULT_JS_ACTION_JOB_MIN_START_CTP;
import static android.app.tare.EconomyManager.DEFAULT_JS_ACTION_JOB_TIMEOUT_PENALTY_BASE_PRICE;
import static android.app.tare.EconomyManager.DEFAULT_JS_ACTION_JOB_TIMEOUT_PENALTY_CTP;
-import static android.app.tare.EconomyManager.DEFAULT_JS_MAX_CIRCULATION;
+import static android.app.tare.EconomyManager.DEFAULT_JS_HARD_CONSUMPTION_LIMIT;
+import static android.app.tare.EconomyManager.DEFAULT_JS_INITIAL_CONSUMPTION_LIMIT;
import static android.app.tare.EconomyManager.DEFAULT_JS_MAX_SATIATED_BALANCE;
import static android.app.tare.EconomyManager.DEFAULT_JS_MIN_SATIATED_BALANCE_EXEMPTED;
import static android.app.tare.EconomyManager.DEFAULT_JS_MIN_SATIATED_BALANCE_OTHER_APP;
@@ -79,7 +80,8 @@
import static android.app.tare.EconomyManager.KEY_JS_ACTION_JOB_MIN_START_CTP;
import static android.app.tare.EconomyManager.KEY_JS_ACTION_JOB_TIMEOUT_PENALTY_BASE_PRICE;
import static android.app.tare.EconomyManager.KEY_JS_ACTION_JOB_TIMEOUT_PENALTY_CTP;
-import static android.app.tare.EconomyManager.KEY_JS_MAX_CIRCULATION;
+import static android.app.tare.EconomyManager.KEY_JS_HARD_CONSUMPTION_LIMIT;
+import static android.app.tare.EconomyManager.KEY_JS_INITIAL_CONSUMPTION_LIMIT;
import static android.app.tare.EconomyManager.KEY_JS_MAX_SATIATED_BALANCE;
import static android.app.tare.EconomyManager.KEY_JS_MIN_SATIATED_BALANCE_EXEMPTED;
import static android.app.tare.EconomyManager.KEY_JS_MIN_SATIATED_BALANCE_OTHER_APP;
@@ -145,7 +147,8 @@
private long mMinSatiatedBalanceExempted;
private long mMinSatiatedBalanceOther;
private long mMaxSatiatedBalance;
- private long mMaxSatiatedCirculation;
+ private long mInitialSatiatedConsumptionLimit;
+ private long mHardSatiatedConsumptionLimit;
private final KeyValueListParser mParser = new KeyValueListParser(',');
private final InternalResourceService mInternalResourceService;
@@ -181,8 +184,13 @@
}
@Override
- long getMaxSatiatedCirculation() {
- return mMaxSatiatedCirculation;
+ long getInitialSatiatedConsumptionLimit() {
+ return mInitialSatiatedConsumptionLimit;
+ }
+
+ @Override
+ long getHardSatiatedConsumptionLimit() {
+ return mHardSatiatedConsumptionLimit;
}
@NonNull
@@ -221,8 +229,11 @@
DEFAULT_JS_MIN_SATIATED_BALANCE_OTHER_APP));
mMaxSatiatedBalance = arcToNarc(mParser.getInt(KEY_JS_MAX_SATIATED_BALANCE,
DEFAULT_JS_MAX_SATIATED_BALANCE));
- mMaxSatiatedCirculation = arcToNarc(mParser.getInt(KEY_JS_MAX_CIRCULATION,
- DEFAULT_JS_MAX_CIRCULATION));
+ mInitialSatiatedConsumptionLimit = arcToNarc(mParser.getInt(
+ KEY_JS_INITIAL_CONSUMPTION_LIMIT, DEFAULT_JS_INITIAL_CONSUMPTION_LIMIT));
+ mHardSatiatedConsumptionLimit = Math.max(mInitialSatiatedConsumptionLimit,
+ arcToNarc(mParser.getInt(
+ KEY_JS_HARD_CONSUMPTION_LIMIT, DEFAULT_JS_HARD_CONSUMPTION_LIMIT)));
mActions.put(ACTION_JOB_MAX_START, new Action(ACTION_JOB_MAX_START,
arcToNarc(mParser.getInt(KEY_JS_ACTION_JOB_MAX_START_CTP,
@@ -332,7 +343,11 @@
pw.print("Other", narcToString(mMinSatiatedBalanceOther)).println();
pw.decreaseIndent();
pw.print("Max satiated balance", narcToString(mMaxSatiatedBalance)).println();
- pw.print("Max satiated circulation", narcToString(mMaxSatiatedCirculation)).println();
+ pw.print("Consumption limits: [");
+ pw.print(narcToString(mInitialSatiatedConsumptionLimit));
+ pw.print(", ");
+ pw.print(narcToString(mHardSatiatedConsumptionLimit));
+ pw.println("]");
pw.println();
pw.println("Actions:");
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/Ledger.java b/apex/jobscheduler/service/java/com/android/server/tare/Ledger.java
index f4917ad..dfdc20a 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/Ledger.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/Ledger.java
@@ -41,14 +41,16 @@
@Nullable
public final String tag;
public final long delta;
+ public final long ctp;
Transaction(long startTimeMs, long endTimeMs,
- int eventId, @Nullable String tag, long delta) {
+ int eventId, @Nullable String tag, long delta, long ctp) {
this.startTimeMs = startTimeMs;
this.endTimeMs = endTimeMs;
this.eventId = eventId;
this.tag = tag;
this.delta = delta;
+ this.ctp = ctp;
}
}
@@ -144,7 +146,10 @@
pw.print(")");
}
pw.print(" --> ");
- pw.println(narcToString(transaction.delta));
+ pw.print(narcToString(transaction.delta));
+ pw.print(" (ctp=");
+ pw.print(narcToString(transaction.ctp));
+ pw.println(")");
}
}
}
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 86968ef..8662110 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/Scribe.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/Scribe.java
@@ -73,6 +73,7 @@
private static final String XML_TAG_TRANSACTION = "transaction";
private static final String XML_TAG_USER = "user";
+ private static final String XML_ATTR_CTP = "ctp";
private static final String XML_ATTR_DELTA = "delta";
private static final String XML_ATTR_EVENT_ID = "eventId";
private static final String XML_ATTR_TAG = "tag";
@@ -83,6 +84,8 @@
private static final String XML_ATTR_USER_ID = "userId";
private static final String XML_ATTR_VERSION = "version";
private static final String XML_ATTR_LAST_RECLAMATION_TIME = "lastReclamationTime";
+ private static final String XML_ATTR_REMAINING_CONSUMABLE_NARCS = "remainingConsumableNarcs";
+ private static final String XML_ATTR_CONSUMPTION_LIMIT = "consumptionLimit";
/** Version of the file schema. */
private static final int STATE_FILE_VERSION = 0;
@@ -95,7 +98,9 @@
@GuardedBy("mIrs.getLock()")
private long mLastReclamationTime;
@GuardedBy("mIrs.getLock()")
- private long mNarcsInCirculation;
+ private long mSatiatedConsumptionLimit;
+ @GuardedBy("mIrs.getLock()")
+ private long mRemainingConsumableNarcs;
@GuardedBy("mIrs.getLock()")
private final SparseArrayMap<String, Ledger> mLedgers = new SparseArrayMap<>();
@@ -117,10 +122,10 @@
}
@GuardedBy("mIrs.getLock()")
- void adjustNarcsInCirculationLocked(long delta) {
+ void adjustRemainingConsumableNarcsLocked(long delta) {
if (delta != 0) {
// No point doing any work if the change is 0.
- mNarcsInCirculation += delta;
+ mRemainingConsumableNarcs += delta;
postWrite();
}
}
@@ -132,6 +137,11 @@
}
@GuardedBy("mIrs.getLock()")
+ long getSatiatedConsumptionLimitLocked() {
+ return mSatiatedConsumptionLimit;
+ }
+
+ @GuardedBy("mIrs.getLock()")
long getLastReclamationTimeLocked() {
return mLastReclamationTime;
}
@@ -153,19 +163,37 @@
return mLedgers;
}
- /** Returns the total amount of narcs currently allocated to apps. */
+ /**
+ * Returns the sum of credits granted to all apps on the system. This is expensive so don't
+ * call it for normal operation.
+ */
@GuardedBy("mIrs.getLock()")
- long getNarcsInCirculationLocked() {
- return mNarcsInCirculation;
+ long getNarcsInCirculationForLoggingLocked() {
+ long sum = 0;
+ for (int uIdx = mLedgers.numMaps() - 1; uIdx >= 0; --uIdx) {
+ for (int pIdx = mLedgers.numElementsForKeyAt(uIdx) - 1; pIdx >= 0; --pIdx) {
+ sum += mLedgers.valueAt(uIdx, pIdx).getCurrentBalance();
+ }
+ }
+ return sum;
+ }
+
+ /** Returns the total amount of narcs that remain to be consumed. */
+ @GuardedBy("mIrs.getLock()")
+ long getRemainingConsumableNarcsLocked() {
+ return mRemainingConsumableNarcs;
}
@GuardedBy("mIrs.getLock()")
void loadFromDiskLocked() {
mLedgers.clear();
- mNarcsInCirculation = 0;
if (!recordExists()) {
+ mSatiatedConsumptionLimit = mIrs.getInitialSatiatedConsumptionLimitLocked();
+ mRemainingConsumableNarcs = mIrs.getConsumptionLimitLocked();
return;
}
+ mSatiatedConsumptionLimit = 0;
+ mRemainingConsumableNarcs = 0;
final SparseArray<ArraySet<String>> installedPackagesPerUser = new SparseArray<>();
final List<PackageInfo> installedPackages = mIrs.getInstalledPackages();
@@ -222,6 +250,13 @@
case XML_TAG_HIGH_LEVEL_STATE:
mLastReclamationTime =
parser.getAttributeLong(null, XML_ATTR_LAST_RECLAMATION_TIME);
+ mSatiatedConsumptionLimit =
+ parser.getAttributeLong(null, XML_ATTR_CONSUMPTION_LIMIT,
+ mIrs.getInitialSatiatedConsumptionLimitLocked());
+ final long consumptionLimit = mIrs.getConsumptionLimitLocked();
+ mRemainingConsumableNarcs = Math.min(consumptionLimit,
+ parser.getAttributeLong(null, XML_ATTR_REMAINING_CONSUMABLE_NARCS,
+ consumptionLimit));
break;
case XML_TAG_USER:
earliestEndTime = Math.min(earliestEndTime,
@@ -249,6 +284,18 @@
}
@GuardedBy("mIrs.getLock()")
+ void setConsumptionLimitLocked(long limit) {
+ if (mRemainingConsumableNarcs > limit) {
+ mRemainingConsumableNarcs = limit;
+ } else if (limit > mSatiatedConsumptionLimit) {
+ final long diff = mSatiatedConsumptionLimit - mRemainingConsumableNarcs;
+ mRemainingConsumableNarcs = (limit - diff);
+ }
+ mSatiatedConsumptionLimit = limit;
+ postWrite();
+ }
+
+ @GuardedBy("mIrs.getLock()")
void setLastReclamationTimeLocked(long time) {
mLastReclamationTime = time;
postWrite();
@@ -259,7 +306,8 @@
TareHandlerThread.getHandler().removeCallbacks(mCleanRunnable);
TareHandlerThread.getHandler().removeCallbacks(mWriteRunnable);
mLedgers.clear();
- mNarcsInCirculation = 0;
+ mRemainingConsumableNarcs = 0;
+ mSatiatedConsumptionLimit = 0;
mLastReclamationTime = 0;
}
@@ -339,13 +387,14 @@
final long endTime = parser.getAttributeLong(null, XML_ATTR_END_TIME);
final int eventId = parser.getAttributeInt(null, XML_ATTR_EVENT_ID);
final long delta = parser.getAttributeLong(null, XML_ATTR_DELTA);
+ final long ctp = parser.getAttributeLong(null, XML_ATTR_CTP);
if (endTime <= endTimeCutoff) {
if (DEBUG) {
Slog.d(TAG, "Skipping event because it's too old.");
}
continue;
}
- transactions.add(new Ledger.Transaction(startTime, endTime, eventId, tag, delta));
+ transactions.add(new Ledger.Transaction(startTime, endTime, eventId, tag, delta, ctp));
}
if (!isInstalled) {
@@ -395,7 +444,6 @@
final Ledger ledger = ledgerData.second;
if (ledger != null) {
mLedgers.add(curUser, ledgerData.first, ledger);
- mNarcsInCirculation += Math.max(0, ledger.getCurrentBalance());
final Ledger.Transaction transaction = ledger.getEarliestTransaction();
if (transaction != null) {
earliestEndTime = Math.min(earliestEndTime, transaction.endTimeMs);
@@ -442,6 +490,9 @@
out.startTag(null, XML_TAG_HIGH_LEVEL_STATE);
out.attributeLong(null, XML_ATTR_LAST_RECLAMATION_TIME, mLastReclamationTime);
+ out.attributeLong(null, XML_ATTR_CONSUMPTION_LIMIT, mSatiatedConsumptionLimit);
+ out.attributeLong(null, XML_ATTR_REMAINING_CONSUMABLE_NARCS,
+ mRemainingConsumableNarcs);
out.endTag(null, XML_TAG_HIGH_LEVEL_STATE);
for (int uIdx = mLedgers.numMaps() - 1; uIdx >= 0; --uIdx) {
@@ -505,6 +556,7 @@
out.attribute(null, XML_ATTR_TAG, transaction.tag);
}
out.attributeLong(null, XML_ATTR_DELTA, transaction.delta);
+ out.attributeLong(null, XML_ATTR_CTP, transaction.ctp);
out.endTag(null, XML_TAG_TRANSACTION);
}
diff --git a/core/api/current.txt b/core/api/current.txt
index 56415ec..ddfbb44 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -7800,7 +7800,6 @@
}
public final class DevicePolicyResources {
- ctor public DevicePolicyResources();
}
public static final class DevicePolicyResources.Drawables {
@@ -27446,7 +27445,6 @@
field public static final String CATEGORY_PAYMENT = "payment";
field public static final String EXTRA_CATEGORY = "category";
field public static final String EXTRA_SERVICE_COMPONENT = "component";
- field public static final String EXTRA_USERID = "android.nfc.cardemulation.extra.USERID";
field public static final int SELECTION_MODE_ALWAYS_ASK = 1; // 0x1
field public static final int SELECTION_MODE_ASK_IF_CONFLICT = 2; // 0x2
field public static final int SELECTION_MODE_PREFER_DEFAULT = 0; // 0x0
diff --git a/core/java/android/app/admin/DevicePolicyResources.java b/core/java/android/app/admin/DevicePolicyResources.java
index 7f2e5fd..042e407 100644
--- a/core/java/android/app/admin/DevicePolicyResources.java
+++ b/core/java/android/app/admin/DevicePolicyResources.java
@@ -317,6 +317,8 @@
*/
public final class DevicePolicyResources {
+ private DevicePolicyResources() {}
+
/**
* Resource identifiers used to update device management-related system drawable resources.
*
diff --git a/core/java/android/nfc/cardemulation/CardEmulation.java b/core/java/android/nfc/cardemulation/CardEmulation.java
index 0a9fe90..9a780c8 100644
--- a/core/java/android/nfc/cardemulation/CardEmulation.java
+++ b/core/java/android/nfc/cardemulation/CardEmulation.java
@@ -84,13 +84,6 @@
public static final String EXTRA_SERVICE_COMPONENT = "component";
/**
- * The caller userId extra for {@link #ACTION_CHANGE_DEFAULT}.
- *
- * @see #ACTION_CHANGE_DEFAULT
- */
- public static final String EXTRA_USERID = "android.nfc.cardemulation.extra.USERID";
-
- /**
* Category used for NFC payment services.
*/
public static final String CATEGORY_PAYMENT = "payment";
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 1c27046..8ee3e43 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -3222,7 +3222,8 @@
/**
* The window is allowed to extend into the {@link DisplayCutout} area, only if the
- * {@link DisplayCutout} is fully contained within a system bar. Otherwise, the window is
+ * {@link DisplayCutout} is fully contained within a system bar or the {@link DisplayCutout}
+ * is not deeper than 16 dp, but this depends on the OEM choice. Otherwise, the window is
* laid out such that it does not overlap with the {@link DisplayCutout} area.
*
* <p>
@@ -3237,6 +3238,13 @@
* The usual precautions for not overlapping with the status and navigation bar are
* sufficient for ensuring that no important content overlaps with the DisplayCutout.
*
+ * <p>
+ * Note: OEMs can have an option to allow the window to always extend into the
+ * {@link DisplayCutout} area, no matter the cutout flag set, when the {@link DisplayCutout}
+ * is on the different side from system bars, only if the {@link DisplayCutout} overlaps at
+ * most 16dp with the windows.
+ * In such case, OEMs must provide an opt-in/out affordance for users.
+ *
* @see DisplayCutout
* @see WindowInsets
* @see #layoutInDisplayCutoutMode
@@ -3249,8 +3257,16 @@
* The window is always allowed to extend into the {@link DisplayCutout} areas on the short
* edges of the screen.
*
+ * <p>
* The window will never extend into a {@link DisplayCutout} area on the long edges of the
- * screen.
+ * screen, unless the {@link DisplayCutout} is not deeper than 16 dp, but this depends on
+ * the OEM choice.
+ *
+ * <p>
+ * Note: OEMs can have an option to allow the window to extend into the
+ * {@link DisplayCutout} area on the long edge side, only if the cutout overlaps at most
+ * 16dp with the windows. In such case, OEMs must provide an opt-in/out affordance for
+ * users.
*
* <p>
* The window must make sure that no important content overlaps with the
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 7150fca..689d37c 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -7013,30 +7013,42 @@
<!-- Defines the ExtendAnimation used to extend windows during animations -->
<declare-styleable name="ExtendAnimation">
- <!-- Defines the amount a window should be extended outward from the left at
- the start of the animation. -->
- <attr name="fromExtendLeft" format="float|fraction" />
- <!-- Defines the amount a window should be extended outward from the top at
- the start of the animation. -->
- <attr name="fromExtendTop" format="float|fraction" />
- <!-- Defines the amount a window should be extended outward from the right at
- the start of the animation. -->
- <attr name="fromExtendRight" format="float|fraction" />
- <!-- Defines the amount a window should be extended outward from the bottom at
- the start of the animation. -->
- <attr name="fromExtendBottom" format="float|fraction" />
- <!-- Defines the amount a window should be extended outward from the left by
- the end of the animation by transitioning from the fromExtendLeft amount. -->
- <attr name="toExtendLeft" format="float|fraction" />
- <!-- Defines the amount a window should be extended outward from the top by
- the end of the animation by transitioning from the fromExtendTop amount. -->
- <attr name="toExtendTop" format="float|fraction" />
- <!-- Defines the amount a window should be extended outward from the right by
- the end of the animation by transitioning from the fromExtendRight amount. -->
- <attr name="toExtendRight" format="float|fraction" />
- <!-- Defines the amount a window should be extended outward from the bottom by
- the end of the animation by transitioning from the fromExtendBottom amount. -->
- <attr name="toExtendBottom" format="float|fraction" />
+ <!-- Defines the amount a window should be extended outward from the left at the start of
+ the animation in an absolute dimension (interpreted as pixels if no dimension unit is
+ provided) or as a percentage of the animation target's width. -->
+ <attr name="fromExtendLeft" format="float|fraction|dimension" />
+ <!-- Defines the amount a window should be extended outward from the top at the start of
+ the animation in an absolute dimension (interpreted as pixels if no dimension unit is
+ provided) or as a percentage of the animation target's height. -->
+ <attr name="fromExtendTop" format="float|fraction|dimension" />
+ <!-- Defines the amount a window should be extended outward from the right at the start of
+ the animation in an absolute dimension (interpreted as pixels if no dimension unit is
+ provided) or as a percentage of the animation target's width. -->
+ <attr name="fromExtendRight" format="float|fraction|dimension" />
+ <!-- Defines the amount a window should be extended outward from the bottom at the start of
+ the animation in an absolute dimension (interpreted as pixels if no dimension unit is
+ provided) or as a percentage of the animation target's height. -->
+ <attr name="fromExtendBottom" format="float|fraction|dimension" />
+ <!-- Defines the amount a window should be extended outward from the left by the end of the
+ animation by transitioning from the fromExtendLeft amount in an absolute dimension
+ (interpreted as pixels if no dimension unit is provided) or as a percentage of the
+ animation target's width. -->
+ <attr name="toExtendLeft" format="float|fraction|dimension" />
+ <!-- Defines the amount a window should be extended outward from the top by the end of the
+ animation by transitioning from the fromExtendTop amount in an absolute dimension
+ (interpreted as pixels if no dimension unit is provided) or as a percentage of the
+ animation target's height. -->
+ <attr name="toExtendTop" format="float|fraction|dimension" />
+ <!-- Defines the amount a window should be extended outward from the right by the end of
+ the animation by transitioning from the fromExtendRight amount in an absolute
+ dimension (interpreted as pixels if no dimension unit is provided) or as a percentage
+ of the animation target's width. -->
+ <attr name="toExtendRight" format="float|fraction|dimension" />
+ <!-- Defines the amount a window should be extended outward from the bottom by the end of
+ the animation by transitioning from the fromExtendBottom amount in an absolute
+ dimension (interpreted as pixels if no dimension unit is provided) or as a percentage
+ of the animation target's height. -->
+ <attr name="toExtendBottom" format="float|fraction|dimension" />
</declare-styleable>
<declare-styleable name="LayoutAnimation">
diff --git a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/SparseInputStream.java b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/SparseInputStream.java
index 72230b4..4117d0f 100644
--- a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/SparseInputStream.java
+++ b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/SparseInputStream.java
@@ -177,7 +177,7 @@
ret = 0;
break;
case SparseChunk.FILL:
- ret = mCur.fill[(4 - ((int) mLeft & 0x3)) & 0x3];
+ ret = Byte.toUnsignedInt(mCur.fill[(4 - ((int) mLeft & 0x3)) & 0x3]);
break;
default:
throw new IOException("Unsupported Chunk:" + mCur.toString());
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java
index 6d774838..05fba54 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java
@@ -28,6 +28,8 @@
import android.view.MotionEvent;
import android.view.View;
+import androidx.annotation.Nullable;
+
import com.android.internal.annotations.VisibleForTesting;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.assist.AssistManager;
@@ -46,8 +48,11 @@
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
+import com.android.systemui.unfold.FoldAodAnimationController;
+import com.android.systemui.unfold.SysUIUnfoldComponent;
import java.util.ArrayList;
+import java.util.Optional;
import javax.inject.Inject;
@@ -73,6 +78,8 @@
private final WakefulnessLifecycle mWakefulnessLifecycle;
private final SysuiStatusBarStateController mStatusBarStateController;
private final DeviceProvisionedController mDeviceProvisionedController;
+ @Nullable
+ private final FoldAodAnimationController mFoldAodAnimationController;
private final HeadsUpManagerPhone mHeadsUpManagerPhone;
private final BatteryController mBatteryController;
private final ScrimController mScrimController;
@@ -105,6 +112,7 @@
Lazy<AssistManager> assistManagerLazy,
DozeScrimController dozeScrimController, KeyguardUpdateMonitor keyguardUpdateMonitor,
PulseExpansionHandler pulseExpansionHandler,
+ Optional<SysUIUnfoldComponent> sysUIUnfoldComponent,
NotificationShadeWindowController notificationShadeWindowController,
NotificationWakeUpCoordinator notificationWakeUpCoordinator,
AuthController authController,
@@ -128,6 +136,8 @@
mNotificationWakeUpCoordinator = notificationWakeUpCoordinator;
mAuthController = authController;
mNotificationIconAreaController = notificationIconAreaController;
+ mFoldAodAnimationController = sysUIUnfoldComponent
+ .map(SysUIUnfoldComponent::getFoldAodAnimationController).orElse(null);
}
// TODO: we should try to not pass status bar in here if we can avoid it.
@@ -215,6 +225,9 @@
mStatusBarStateController.setIsDozing(dozing);
mNotificationShadeWindowViewController.setDozing(dozing);
+ if (mFoldAodAnimationController != null) {
+ mFoldAodAnimationController.setIsDozing(dozing);
+ }
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/FoldAodAnimationController.kt b/packages/SystemUI/src/com/android/systemui/unfold/FoldAodAnimationController.kt
index 2a9076e..e2374ad 100644
--- a/packages/SystemUI/src/com/android/systemui/unfold/FoldAodAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/unfold/FoldAodAnimationController.kt
@@ -16,9 +16,12 @@
package com.android.systemui.unfold
+import android.content.Context
+import android.hardware.devicestate.DeviceStateManager
import android.os.Handler
import android.os.PowerManager
import android.provider.Settings
+import androidx.core.view.OneShotPreDrawListener
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.keyguard.WakefulnessLifecycle
import com.android.systemui.statusbar.LightRevealScrim
@@ -27,6 +30,8 @@
import com.android.systemui.statusbar.policy.CallbackController
import com.android.systemui.unfold.FoldAodAnimationController.FoldAodAnimationStatus
import com.android.systemui.util.settings.GlobalSettings
+import java.util.concurrent.Executor
+import java.util.function.Consumer
import javax.inject.Inject
/**
@@ -38,13 +43,21 @@
@Inject
constructor(
@Main private val handler: Handler,
+ @Main private val executor: Executor,
+ private val context: Context,
+ private val deviceStateManager: DeviceStateManager,
private val wakefulnessLifecycle: WakefulnessLifecycle,
private val globalSettings: GlobalSettings
) : CallbackController<FoldAodAnimationStatus>, ScreenOffAnimation, WakefulnessLifecycle.Observer {
- private var alwaysOnEnabled: Boolean = false
- private var isScrimOpaque: Boolean = false
private lateinit var statusBar: StatusBar
+
+ private var isFolded = false
+ private var isFoldHandled = true
+
+ private var alwaysOnEnabled: Boolean = false
+ private var isDozing: Boolean = false
+ private var isScrimOpaque: Boolean = false
private var pendingScrimReadyCallback: Runnable? = null
private var shouldPlayAnimation = false
@@ -62,6 +75,7 @@
override fun initialize(statusBar: StatusBar, lightRevealScrim: LightRevealScrim) {
this.statusBar = statusBar
+ deviceStateManager.registerCallback(executor, FoldListener())
wakefulnessLifecycle.addObserver(this)
}
@@ -84,7 +98,7 @@
override fun onStartedWakingUp() {
if (isAnimationPlaying) {
handler.removeCallbacks(startAnimationRunnable)
- statusBar.notificationPanelViewController.cancelFoldToAodAnimation();
+ statusBar.notificationPanelViewController.cancelFoldToAodAnimation()
}
setAnimationState(playing = false)
@@ -105,11 +119,24 @@
*/
fun onScreenTurningOn(onReady: Runnable) {
if (shouldPlayAnimation) {
+ // The device was not dozing and going to sleep after folding, play the animation
+
if (isScrimOpaque) {
onReady.run()
} else {
pendingScrimReadyCallback = onReady
}
+ } else if (isFolded && !isFoldHandled && alwaysOnEnabled && isDozing) {
+ // Screen turning on for the first time after folding and we are already dozing
+ // We should play the folding to AOD animation
+
+ setAnimationState(playing = true)
+ statusBar.notificationPanelViewController.prepareFoldToAodAnimation()
+
+ // We don't need to wait for the scrim as it is already displayed
+ // but we should wait for the initial animation preparations to be drawn
+ // (setting initial alpha/translation)
+ OneShotPreDrawListener.add(statusBar.notificationPanelViewController.view, onReady)
} else {
// No animation, call ready callback immediately
onReady.run()
@@ -136,6 +163,10 @@
}
}
+ fun setIsDozing(dozing: Boolean) {
+ isDozing = dozing
+ }
+
override fun isAnimationPlaying(): Boolean = isAnimationPlaying
override fun isKeyguardHideDelayed(): Boolean = isAnimationPlaying()
@@ -166,4 +197,15 @@
interface FoldAodAnimationStatus {
fun onFoldToAodAnimationChanged()
}
+
+ private inner class FoldListener :
+ DeviceStateManager.FoldStateListener(
+ context,
+ Consumer { isFolded ->
+ if (!isFolded) {
+ // We are unfolded now, reset the fold handle status
+ isFoldHandled = false
+ }
+ this.isFolded = isFolded
+ })
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java
index 38d7ce7..6ce3b4b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java
@@ -59,6 +59,7 @@
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
+import java.util.Optional;
@SmallTest
@RunWith(AndroidTestingRunner.class)
@@ -97,7 +98,7 @@
mStatusBarStateController, mDeviceProvisionedController, mHeadsUpManager,
mBatteryController, mScrimController, () -> mBiometricUnlockController,
mKeyguardViewMediator, () -> mAssistManager, mDozeScrimController,
- mKeyguardUpdateMonitor, mPulseExpansionHandler,
+ mKeyguardUpdateMonitor, mPulseExpansionHandler, Optional.empty(),
mNotificationShadeWindowController, mNotificationWakeUpCoordinator,
mAuthController, mNotificationIconAreaController);
diff --git a/services/core/java/com/android/server/clipboard/ClipboardService.java b/services/core/java/com/android/server/clipboard/ClipboardService.java
index eb2f80b..9f46bd6 100644
--- a/services/core/java/com/android/server/clipboard/ClipboardService.java
+++ b/services/core/java/com/android/server/clipboard/ClipboardService.java
@@ -389,7 +389,7 @@
final long oldIdentity = Binder.clearCallingIdentity();
try {
if (DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_CLIPBOARD,
- PROPERTY_AUTO_CLEAR_ENABLED, false)) {
+ PROPERTY_AUTO_CLEAR_ENABLED, true)) {
mClipboardClearHandler.removeEqualMessages(ClipboardClearHandler.MSG_CLEAR,
userId);
Message clearMessage = Message.obtain(mClipboardClearHandler,
diff --git a/services/core/java/com/android/server/location/LocationManagerService.java b/services/core/java/com/android/server/location/LocationManagerService.java
index 0c3f9f0..45d9822 100644
--- a/services/core/java/com/android/server/location/LocationManagerService.java
+++ b/services/core/java/com/android/server/location/LocationManagerService.java
@@ -1428,6 +1428,7 @@
ipw.println("Location Settings:");
ipw.increaseIndent();
mInjector.getSettingsHelper().dump(fd, ipw, args);
+ mInjector.getLocationSettings().dump(fd, ipw, args);
ipw.decreaseIndent();
synchronized (mLock) {
diff --git a/services/core/java/com/android/server/location/settings/LocationSettings.java b/services/core/java/com/android/server/location/settings/LocationSettings.java
index d521538..be0e7ac 100644
--- a/services/core/java/com/android/server/location/settings/LocationSettings.java
+++ b/services/core/java/com/android/server/location/settings/LocationSettings.java
@@ -18,8 +18,11 @@
import static android.content.pm.PackageManager.FEATURE_AUTOMOTIVE;
+import android.app.ActivityManager;
import android.content.Context;
import android.os.Environment;
+import android.os.RemoteException;
+import android.util.IndentingPrintWriter;
import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy;
@@ -29,6 +32,7 @@
import java.io.DataInput;
import java.io.DataOutput;
import java.io.File;
+import java.io.FileDescriptor;
import java.io.IOException;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.function.Function;
@@ -104,6 +108,33 @@
getUserSettingsStore(userId).update(updater);
}
+ /** Dumps info for debugging. */
+ public final void dump(FileDescriptor fd, IndentingPrintWriter ipw, String[] args) {
+ int[] userIds;
+ try {
+ userIds = ActivityManager.getService().getRunningUserIds();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+
+ if (mContext.getPackageManager().hasSystemFeature(FEATURE_AUTOMOTIVE)) {
+ ipw.print("ADAS Location Setting: ");
+ ipw.increaseIndent();
+ if (userIds.length > 1) {
+ ipw.println();
+ for (int userId : userIds) {
+ ipw.print("[u");
+ ipw.print(userId);
+ ipw.print("] ");
+ ipw.println(getUserSettings(userId).isAdasGnssLocationEnabled());
+ }
+ } else {
+ ipw.println(getUserSettings(userIds[0]).isAdasGnssLocationEnabled());
+ }
+ ipw.decreaseIndent();
+ }
+ }
+
@VisibleForTesting
final void flushFiles() throws InterruptedException {
synchronized (mUserSettings) {
diff --git a/services/core/java/com/android/server/net/LockdownVpnTracker.java b/services/core/java/com/android/server/net/LockdownVpnTracker.java
index 851ea3d..1b7d1ba 100644
--- a/services/core/java/com/android/server/net/LockdownVpnTracker.java
+++ b/services/core/java/com/android/server/net/LockdownVpnTracker.java
@@ -293,7 +293,7 @@
.addAction(R.drawable.ic_menu_refresh, mContext.getString(R.string.reset),
mResetIntent)
.setColor(mContext.getColor(
- com.android.internal.R.color.system_notification_accent_color));
+ android.R.color.system_notification_accent_color));
mNotificationManager.notify(null /* tag */, SystemMessage.NOTE_VPN_STATUS,
builder.build());
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotController.java b/services/core/java/com/android/server/wm/TaskSnapshotController.java
index a9add59..6a23eb5 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotController.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotController.java
@@ -310,7 +310,7 @@
}
final ActivityRecord activity = result.first;
final WindowState mainWindow = result.second;
- final Rect contentInsets = getSystemBarInsets(task.getBounds(),
+ final Rect contentInsets = getSystemBarInsets(mainWindow.getFrame(),
mainWindow.getInsetsStateWithVisibilityOverride());
final Rect letterboxInsets = activity.getLetterboxInsets();
InsetUtils.addInsets(contentInsets, letterboxInsets);
@@ -575,7 +575,7 @@
final LayoutParams attrs = mainWindow.getAttrs();
final Rect taskBounds = task.getBounds();
final InsetsState insetsState = mainWindow.getInsetsStateWithVisibilityOverride();
- final Rect systemBarInsets = getSystemBarInsets(taskBounds, insetsState);
+ final Rect systemBarInsets = getSystemBarInsets(mainWindow.getFrame(), insetsState);
final SystemBarBackgroundPainter decorPainter = new SystemBarBackgroundPainter(attrs.flags,
attrs.privateFlags, attrs.insetsFlags.appearance, task.getTaskDescription(),
mHighResTaskSnapshotScale, insetsState);
diff --git a/services/tests/mockingservicestests/src/com/android/server/tare/AgentTest.java b/services/tests/mockingservicestests/src/com/android/server/tare/AgentTest.java
index 6751b80..41d46f2 100644
--- a/services/tests/mockingservicestests/src/com/android/server/tare/AgentTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/tare/AgentTest.java
@@ -73,6 +73,7 @@
.startMocking();
when(mIrs.getContext()).thenReturn(mContext);
when(mIrs.getCompleteEconomicPolicyLocked()).thenReturn(mEconomicPolicy);
+ when(mIrs.getLock()).thenReturn(mIrs);
when(mContext.getSystemService(Context.ALARM_SERVICE)).thenReturn(mock(AlarmManager.class));
mScribe = new MockScribe(mIrs);
}
@@ -89,75 +90,75 @@
Agent agent = new Agent(mIrs, mScribe);
Ledger ledger = new Ledger();
- doReturn(1_000_000L).when(mIrs).getMaxCirculationLocked();
+ doReturn(1_000_000L).when(mIrs).getConsumptionLimitLocked();
doReturn(1_000_000L).when(mEconomicPolicy).getMaxSatiatedBalance();
- Ledger.Transaction transaction = new Ledger.Transaction(0, 0, 0, null, 5);
+ Ledger.Transaction transaction = new Ledger.Transaction(0, 0, 0, null, 5, 0);
agent.recordTransactionLocked(0, "com.test", ledger, transaction, false);
assertEquals(5, ledger.getCurrentBalance());
- transaction = new Ledger.Transaction(0, 0, 0, null, 995);
+ transaction = new Ledger.Transaction(0, 0, 0, null, 995, 0);
agent.recordTransactionLocked(0, "com.test", ledger, transaction, false);
assertEquals(1000, ledger.getCurrentBalance());
- transaction = new Ledger.Transaction(0, 0, 0, null, -500);
+ transaction = new Ledger.Transaction(0, 0, 0, null, -500, 250);
agent.recordTransactionLocked(0, "com.test", ledger, transaction, false);
assertEquals(500, ledger.getCurrentBalance());
- transaction = new Ledger.Transaction(0, 0, 0, null, 999_500L);
+ transaction = new Ledger.Transaction(0, 0, 0, null, 999_500L, 500);
agent.recordTransactionLocked(0, "com.test", ledger, transaction, false);
assertEquals(1_000_000L, ledger.getCurrentBalance());
- transaction = new Ledger.Transaction(0, 0, 0, null, -1_000_001L);
+ transaction = new Ledger.Transaction(0, 0, 0, null, -1_000_001L, 1000);
agent.recordTransactionLocked(0, "com.test", ledger, transaction, false);
assertEquals(-1, ledger.getCurrentBalance());
}
@Test
- public void testRecordTransaction_MaxCirculation() {
+ public void testRecordTransaction_MaxConsumptionLimit() {
Agent agent = new Agent(mIrs, mScribe);
Ledger ledger = new Ledger();
- doReturn(1000L).when(mIrs).getMaxCirculationLocked();
- doReturn(1000L).when(mEconomicPolicy).getMaxSatiatedBalance();
+ doReturn(1000L).when(mIrs).getConsumptionLimitLocked();
+ doReturn(1_000_000L).when(mEconomicPolicy).getMaxSatiatedBalance();
- Ledger.Transaction transaction = new Ledger.Transaction(0, 0, 0, null, 5);
+ Ledger.Transaction transaction = new Ledger.Transaction(0, 0, 0, null, 5, 0);
agent.recordTransactionLocked(0, "com.test", ledger, transaction, false);
assertEquals(5, ledger.getCurrentBalance());
- transaction = new Ledger.Transaction(0, 0, 0, null, 995);
+ transaction = new Ledger.Transaction(0, 0, 0, null, 995, 0);
agent.recordTransactionLocked(0, "com.test", ledger, transaction, false);
assertEquals(1000, ledger.getCurrentBalance());
- transaction = new Ledger.Transaction(0, 0, 0, null, -500);
+ transaction = new Ledger.Transaction(0, 0, 0, null, -500, 250);
agent.recordTransactionLocked(0, "com.test", ledger, transaction, false);
assertEquals(500, ledger.getCurrentBalance());
- transaction = new Ledger.Transaction(0, 0, 0, null, 2000);
+ transaction = new Ledger.Transaction(0, 0, 0, null, 2000, 0);
agent.recordTransactionLocked(0, "com.test", ledger, transaction, false);
- assertEquals(1000, ledger.getCurrentBalance());
+ assertEquals(2500, ledger.getCurrentBalance());
- // MaxCirculation can change as the battery level changes. Any already allocated ARCSs
- // shouldn't be removed by recordTransaction().
- doReturn(900L).when(mIrs).getMaxCirculationLocked();
+ // ConsumptionLimit can change as the battery level changes. Ledger balances shouldn't be
+ // affected.
+ doReturn(900L).when(mIrs).getConsumptionLimitLocked();
- transaction = new Ledger.Transaction(0, 0, 0, null, 100);
+ transaction = new Ledger.Transaction(0, 0, 0, null, 100, 0);
agent.recordTransactionLocked(0, "com.test", ledger, transaction, false);
- assertEquals(1000, ledger.getCurrentBalance());
+ assertEquals(2600, ledger.getCurrentBalance());
- transaction = new Ledger.Transaction(0, 0, 0, null, -50);
+ transaction = new Ledger.Transaction(0, 0, 0, null, -50, 50);
agent.recordTransactionLocked(0, "com.test", ledger, transaction, false);
- assertEquals(950, ledger.getCurrentBalance());
+ assertEquals(2550, ledger.getCurrentBalance());
- transaction = new Ledger.Transaction(0, 0, 0, null, -200);
+ transaction = new Ledger.Transaction(0, 0, 0, null, -200, 100);
agent.recordTransactionLocked(0, "com.test", ledger, transaction, false);
- assertEquals(750, ledger.getCurrentBalance());
+ assertEquals(2350, ledger.getCurrentBalance());
- doReturn(800L).when(mIrs).getMaxCirculationLocked();
+ doReturn(800L).when(mIrs).getConsumptionLimitLocked();
- transaction = new Ledger.Transaction(0, 0, 0, null, 100);
+ transaction = new Ledger.Transaction(0, 0, 0, null, 100, 0);
agent.recordTransactionLocked(0, "com.test", ledger, transaction, false);
- assertEquals(800, ledger.getCurrentBalance());
+ assertEquals(2450, ledger.getCurrentBalance());
}
@Test
@@ -165,33 +166,33 @@
Agent agent = new Agent(mIrs, mScribe);
Ledger ledger = new Ledger();
- doReturn(1_000_000L).when(mIrs).getMaxCirculationLocked();
+ doReturn(1_000_000L).when(mIrs).getConsumptionLimitLocked();
doReturn(1000L).when(mEconomicPolicy).getMaxSatiatedBalance();
- Ledger.Transaction transaction = new Ledger.Transaction(0, 0, 0, null, 5);
+ Ledger.Transaction transaction = new Ledger.Transaction(0, 0, 0, null, 5, 0);
agent.recordTransactionLocked(0, "com.test", ledger, transaction, false);
assertEquals(5, ledger.getCurrentBalance());
- transaction = new Ledger.Transaction(0, 0, 0, null, 995);
+ transaction = new Ledger.Transaction(0, 0, 0, null, 995, 0);
agent.recordTransactionLocked(0, "com.test", ledger, transaction, false);
assertEquals(1000, ledger.getCurrentBalance());
- transaction = new Ledger.Transaction(0, 0, 0, null, -500);
+ transaction = new Ledger.Transaction(0, 0, 0, null, -500, 250);
agent.recordTransactionLocked(0, "com.test", ledger, transaction, false);
assertEquals(500, ledger.getCurrentBalance());
- transaction = new Ledger.Transaction(0, 0, 0, null, 999_500L);
+ transaction = new Ledger.Transaction(0, 0, 0, null, 999_500L, 1000);
agent.recordTransactionLocked(0, "com.test", ledger, transaction, false);
assertEquals(1_000, ledger.getCurrentBalance());
// Shouldn't change in normal operation, but adding test case in case it does.
doReturn(900L).when(mEconomicPolicy).getMaxSatiatedBalance();
- transaction = new Ledger.Transaction(0, 0, 0, null, 500);
+ transaction = new Ledger.Transaction(0, 0, 0, null, 500, 0);
agent.recordTransactionLocked(0, "com.test", ledger, transaction, false);
assertEquals(1_000, ledger.getCurrentBalance());
- transaction = new Ledger.Transaction(0, 0, 0, null, -1001);
+ transaction = new Ledger.Transaction(0, 0, 0, null, -1001, 500);
agent.recordTransactionLocked(0, "com.test", ledger, transaction, false);
assertEquals(-1, ledger.getCurrentBalance());
}
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 b72fc23..ab29e59 100644
--- a/services/tests/mockingservicestests/src/com/android/server/tare/ScribeTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/tare/ScribeTest.java
@@ -107,20 +107,26 @@
@Test
public void testWriteHighLevelStateToDisk() {
long lastReclamationTime = System.currentTimeMillis();
- long narcsInCirculation = 2000L;
+ long remainingConsumableNarcs = 2000L;
+ long consumptionLimit = 500_000L;
Ledger ledger = mScribeUnderTest.getLedgerLocked(TEST_USER_ID, TEST_PACKAGE);
- ledger.recordTransaction(new Ledger.Transaction(0, 1000L, 1, null, 2000));
+ ledger.recordTransaction(new Ledger.Transaction(0, 1000L, 1, null, 2000, 0));
// Negative ledger balance shouldn't affect the total circulation value.
ledger = mScribeUnderTest.getLedgerLocked(TEST_USER_ID + 1, TEST_PACKAGE);
- ledger.recordTransaction(new Ledger.Transaction(0, 1000L, 1, null, -5000));
+ ledger.recordTransaction(new Ledger.Transaction(0, 1000L, 1, null, -5000, 3000));
mScribeUnderTest.setLastReclamationTimeLocked(lastReclamationTime);
+ mScribeUnderTest.setConsumptionLimitLocked(consumptionLimit);
+ mScribeUnderTest.adjustRemainingConsumableNarcsLocked(
+ remainingConsumableNarcs - consumptionLimit);
mScribeUnderTest.writeImmediatelyForTesting();
mScribeUnderTest.loadFromDiskLocked();
assertEquals(lastReclamationTime, mScribeUnderTest.getLastReclamationTimeLocked());
- assertEquals(narcsInCirculation, mScribeUnderTest.getNarcsInCirculationLocked());
+ assertEquals(remainingConsumableNarcs,
+ mScribeUnderTest.getRemainingConsumableNarcsLocked());
+ assertEquals(consumptionLimit, mScribeUnderTest.getSatiatedConsumptionLimitLocked());
}
@Test
@@ -135,9 +141,9 @@
@Test
public void testWritingPopulatedLedgerToDisk() {
final Ledger ogLedger = mScribeUnderTest.getLedgerLocked(TEST_USER_ID, TEST_PACKAGE);
- ogLedger.recordTransaction(new Ledger.Transaction(0, 1000, 1, null, 51));
- ogLedger.recordTransaction(new Ledger.Transaction(1500, 2000, 2, "green", 52));
- ogLedger.recordTransaction(new Ledger.Transaction(2500, 3000, 3, "blue", 3));
+ ogLedger.recordTransaction(new Ledger.Transaction(0, 1000, 1, null, 51, 0));
+ ogLedger.recordTransaction(new Ledger.Transaction(1500, 2000, 2, "green", 52, -1));
+ ogLedger.recordTransaction(new Ledger.Transaction(2500, 3000, 3, "blue", 3, 12));
mScribeUnderTest.writeImmediatelyForTesting();
mScribeUnderTest.loadFromDiskLocked();
@@ -156,11 +162,11 @@
addInstalledPackage(userId, pkgName);
final Ledger ledger = mScribeUnderTest.getLedgerLocked(userId, pkgName);
ledger.recordTransaction(new Ledger.Transaction(
- 0, 1000L * u + l, 1, null, 51L * u + l));
+ 0, 1000L * u + l, 1, null, -51L * u + l, 50));
ledger.recordTransaction(new Ledger.Transaction(
- 1500L * u + l, 2000L * u + l, 2 * u + l, "green" + u + l, 52L * u + l));
+ 1500L * u + l, 2000L * u + l, 2 * u + l, "green" + u + l, 52L * u + l, 0));
ledger.recordTransaction(new Ledger.Transaction(
- 2500L * u + l, 3000L * u + l, 3 * u + l, "blue" + u + l, 3L * u + l));
+ 2500L * u + l, 3000L * u + l, 3 * u + l, "blue" + u + l, 3L * u + l, 0));
ledgers.add(userId, pkgName, ledger);
}
}
@@ -174,9 +180,9 @@
@Test
public void testDiscardLedgerFromDisk() {
final Ledger ogLedger = mScribeUnderTest.getLedgerLocked(TEST_USER_ID, TEST_PACKAGE);
- ogLedger.recordTransaction(new Ledger.Transaction(0, 1000, 1, null, 51));
- ogLedger.recordTransaction(new Ledger.Transaction(1500, 2000, 2, "green", 52));
- ogLedger.recordTransaction(new Ledger.Transaction(2500, 3000, 3, "blue", 3));
+ ogLedger.recordTransaction(new Ledger.Transaction(0, 1000, 1, null, 51, 1));
+ ogLedger.recordTransaction(new Ledger.Transaction(1500, 2000, 2, "green", 52, 0));
+ ogLedger.recordTransaction(new Ledger.Transaction(2500, 3000, 3, "blue", 3, 1));
mScribeUnderTest.writeImmediatelyForTesting();
mScribeUnderTest.loadFromDiskLocked();
@@ -195,9 +201,9 @@
public void testLoadingMissingPackageFromDisk() {
final String pkgName = TEST_PACKAGE + ".uninstalled";
final Ledger ogLedger = mScribeUnderTest.getLedgerLocked(TEST_USER_ID, pkgName);
- ogLedger.recordTransaction(new Ledger.Transaction(0, 1000, 1, null, 51));
- ogLedger.recordTransaction(new Ledger.Transaction(1500, 2000, 2, "green", 52));
- ogLedger.recordTransaction(new Ledger.Transaction(2500, 3000, 3, "blue", 3));
+ ogLedger.recordTransaction(new Ledger.Transaction(0, 1000, 1, null, 51, 1));
+ ogLedger.recordTransaction(new Ledger.Transaction(1500, 2000, 2, "green", 52, 2));
+ ogLedger.recordTransaction(new Ledger.Transaction(2500, 3000, 3, "blue", 3, 3));
mScribeUnderTest.writeImmediatelyForTesting();
// Package isn't installed, so make sure it's not saved to memory after loading.
@@ -209,9 +215,9 @@
public void testLoadingMissingUserFromDisk() {
final int userId = TEST_USER_ID + 1;
final Ledger ogLedger = mScribeUnderTest.getLedgerLocked(userId, TEST_PACKAGE);
- ogLedger.recordTransaction(new Ledger.Transaction(0, 1000, 1, null, 51));
- ogLedger.recordTransaction(new Ledger.Transaction(1500, 2000, 2, "green", 52));
- ogLedger.recordTransaction(new Ledger.Transaction(2500, 3000, 3, "blue", 3));
+ ogLedger.recordTransaction(new Ledger.Transaction(0, 1000, 1, null, 51, 0));
+ ogLedger.recordTransaction(new Ledger.Transaction(1500, 2000, 2, "green", 52, 1));
+ ogLedger.recordTransaction(new Ledger.Transaction(2500, 3000, 3, "blue", 3, 3));
mScribeUnderTest.writeImmediatelyForTesting();
// User doesn't show up with any packages, so make sure nothing is saved after loading.
@@ -219,6 +225,37 @@
assertLedgersEqual(new Ledger(), mScribeUnderTest.getLedgerLocked(userId, TEST_PACKAGE));
}
+ @Test
+ public void testChangingConsumable() {
+ assertEquals(0, mScribeUnderTest.getSatiatedConsumptionLimitLocked());
+ assertEquals(0, mScribeUnderTest.getRemainingConsumableNarcsLocked());
+
+ // Limit increased, so remaining value should be adjusted as well
+ mScribeUnderTest.setConsumptionLimitLocked(1000);
+ assertEquals(1000, mScribeUnderTest.getSatiatedConsumptionLimitLocked());
+ assertEquals(1000, mScribeUnderTest.getRemainingConsumableNarcsLocked());
+
+ // Limit decreased below remaining, so remaining value should be adjusted as well
+ mScribeUnderTest.setConsumptionLimitLocked(500);
+ assertEquals(500, mScribeUnderTest.getSatiatedConsumptionLimitLocked());
+ assertEquals(500, mScribeUnderTest.getRemainingConsumableNarcsLocked());
+
+ mScribeUnderTest.adjustRemainingConsumableNarcsLocked(-100);
+ assertEquals(500, mScribeUnderTest.getSatiatedConsumptionLimitLocked());
+ assertEquals(400, mScribeUnderTest.getRemainingConsumableNarcsLocked());
+
+ // Limit increased, so remaining value should be adjusted by the difference as well
+ mScribeUnderTest.setConsumptionLimitLocked(1000);
+ assertEquals(1000, mScribeUnderTest.getSatiatedConsumptionLimitLocked());
+ assertEquals(900, mScribeUnderTest.getRemainingConsumableNarcsLocked());
+
+
+ // Limit decreased, but above remaining, so remaining value should left alone
+ mScribeUnderTest.setConsumptionLimitLocked(950);
+ assertEquals(950, mScribeUnderTest.getSatiatedConsumptionLimitLocked());
+ assertEquals(900, mScribeUnderTest.getRemainingConsumableNarcsLocked());
+ }
+
private void assertLedgersEqual(Ledger expected, Ledger actual) {
if (expected == null) {
assertNull(actual);
@@ -245,6 +282,7 @@
assertEquals(expected.eventId, actual.eventId);
assertEquals(expected.tag, actual.tag);
assertEquals(expected.delta, actual.delta);
+ assertEquals(expected.ctp, actual.ctp);
}
private void addInstalledPackage(int userId, String pkgName) {
diff --git a/services/tests/servicestests/src/com/android/server/tare/AgentTrendCalculatorTest.java b/services/tests/servicestests/src/com/android/server/tare/AgentTrendCalculatorTest.java
index b1b53fc..9e986be 100644
--- a/services/tests/servicestests/src/com/android/server/tare/AgentTrendCalculatorTest.java
+++ b/services/tests/servicestests/src/com/android/server/tare/AgentTrendCalculatorTest.java
@@ -65,7 +65,12 @@
}
@Override
- long getMaxSatiatedCirculation() {
+ long getInitialSatiatedConsumptionLimit() {
+ return 0;
+ }
+
+ @Override
+ long getHardSatiatedConsumptionLimit() {
return 0;
}
@@ -102,7 +107,7 @@
TrendCalculator trendCalculator = new TrendCalculator();
mEconomicPolicy.mEventCosts.put(JobSchedulerEconomicPolicy.ACTION_JOB_TIMEOUT, 20);
- trendCalculator.reset(0, null);
+ trendCalculator.reset(0, 0, null);
assertEquals("Expected not to cross lower threshold",
TrendCalculator.WILL_NOT_CROSS_THRESHOLD,
trendCalculator.getTimeToCrossLowerThresholdMs());
@@ -115,10 +120,10 @@
new AnticipatedAction(JobSchedulerEconomicPolicy.ACTION_JOB_TIMEOUT, 1, 0))),
mock(AffordabilityChangeListener.class), mEconomicPolicy));
for (ActionAffordabilityNote note : affordabilityNotes) {
- note.recalculateModifiedPrice(mEconomicPolicy, 0, "com.test.app");
+ note.recalculateCosts(mEconomicPolicy, 0, "com.test.app");
}
- trendCalculator.reset(1234, affordabilityNotes);
+ trendCalculator.reset(1234, 1234, affordabilityNotes);
assertEquals("Expected not to cross lower threshold",
TrendCalculator.WILL_NOT_CROSS_THRESHOLD,
trendCalculator.getTimeToCrossLowerThresholdMs());
@@ -133,32 +138,28 @@
OngoingEvent[] events = new OngoingEvent[]{
new OngoingEvent(JobSchedulerEconomicPolicy.ACTION_JOB_DEFAULT_RUNNING, "1",
- null, 1, 1),
+ 1, new EconomicPolicy.Cost(1, 4)),
new OngoingEvent(JobSchedulerEconomicPolicy.ACTION_JOB_HIGH_RUNNING, "2",
- null, 2, 3),
- new OngoingEvent(EconomicPolicy.REWARD_TOP_ACTIVITY, "3",
- null, 3, -3),
+ 2, new EconomicPolicy.Cost(3, 6)),
+ new OngoingEvent(EconomicPolicy.REWARD_TOP_ACTIVITY, "3", 3,
+ new EconomicPolicy.Reward(EconomicPolicy.REWARD_TOP_ACTIVITY, 0, 3, 3)),
};
- trendCalculator.reset(0, null);
+ trendCalculator.reset(0, 100, null);
for (OngoingEvent event : events) {
trendCalculator.accept(event);
}
- assertEquals("Expected not to cross lower threshold",
- TrendCalculator.WILL_NOT_CROSS_THRESHOLD,
- trendCalculator.getTimeToCrossLowerThresholdMs());
+ assertEquals(25_000, trendCalculator.getTimeToCrossLowerThresholdMs());
assertEquals("Expected not to cross upper threshold",
TrendCalculator.WILL_NOT_CROSS_THRESHOLD,
trendCalculator.getTimeToCrossUpperThresholdMs());
ArraySet<ActionAffordabilityNote> affordabilityNotes = new ArraySet<>();
- trendCalculator.reset(1234, affordabilityNotes);
+ trendCalculator.reset(1234, 1234, affordabilityNotes);
for (OngoingEvent event : events) {
trendCalculator.accept(event);
}
- assertEquals("Expected not to cross lower threshold",
- TrendCalculator.WILL_NOT_CROSS_THRESHOLD,
- trendCalculator.getTimeToCrossLowerThresholdMs());
+ assertEquals(308_000, trendCalculator.getTimeToCrossLowerThresholdMs());
assertEquals("Expected not to cross upper threshold",
TrendCalculator.WILL_NOT_CROSS_THRESHOLD,
trendCalculator.getTimeToCrossUpperThresholdMs());
@@ -174,18 +175,19 @@
new AnticipatedAction(JobSchedulerEconomicPolicy.ACTION_JOB_MAX_RUNNING, 0, 1000))),
mock(AffordabilityChangeListener.class), mEconomicPolicy));
for (ActionAffordabilityNote note : affordabilityNotes) {
- note.recalculateModifiedPrice(mEconomicPolicy, 0, "com.test.app");
+ note.recalculateCosts(mEconomicPolicy, 0, "com.test.app");
}
// Balance is already above threshold and events are all positive delta.
// There should be no time to report.
- trendCalculator.reset(1234, affordabilityNotes);
+ trendCalculator.reset(1234, 1234, affordabilityNotes);
trendCalculator.accept(
- new OngoingEvent(JobSchedulerEconomicPolicy.ACTION_JOB_DEFAULT_RUNNING, "1",
- null, 1, 1));
+ new OngoingEvent(EconomicPolicy.REWARD_TOP_ACTIVITY, "1", 1,
+ new EconomicPolicy.Reward(EconomicPolicy.REWARD_TOP_ACTIVITY, 1, 1, 1)));
trendCalculator.accept(
- new OngoingEvent(JobSchedulerEconomicPolicy.ACTION_JOB_HIGH_RUNNING, "2",
- null, 2, 3));
+ new OngoingEvent(EconomicPolicy.REWARD_OTHER_USER_INTERACTION, "2", 2,
+ new EconomicPolicy.Reward(EconomicPolicy.REWARD_OTHER_USER_INTERACTION,
+ 3, 3, 3)));
assertEquals("Expected not to cross lower threshold",
TrendCalculator.WILL_NOT_CROSS_THRESHOLD,
@@ -196,16 +198,16 @@
// Balance is already below threshold and events are all negative delta.
// There should be no time to report.
- trendCalculator.reset(1, affordabilityNotes);
+ trendCalculator.reset(1, 0, affordabilityNotes);
trendCalculator.accept(
new OngoingEvent(JobSchedulerEconomicPolicy.ACTION_JOB_DEFAULT_RUNNING, "1",
- null, 1, -1));
+ 1, new EconomicPolicy.Cost(1, 1)));
trendCalculator.accept(
new OngoingEvent(JobSchedulerEconomicPolicy.ACTION_JOB_HIGH_RUNNING, "2",
- null, 2, -3));
+ 2, new EconomicPolicy.Cost(3, 3)));
assertEquals("Expected not to cross lower threshold",
- TrendCalculator.WILL_NOT_CROSS_THRESHOLD,
+ 0,
trendCalculator.getTimeToCrossLowerThresholdMs());
assertEquals("Expected not to cross upper threshold",
TrendCalculator.WILL_NOT_CROSS_THRESHOLD,
@@ -213,7 +215,7 @@
}
@Test
- public void testSimpleTrendToThreshold() {
+ public void testSimpleTrendToThreshold_Balance() {
TrendCalculator trendCalculator = new TrendCalculator();
mEconomicPolicy.mEventCosts.put(JobSchedulerEconomicPolicy.ACTION_JOB_MAX_START, 20);
@@ -222,18 +224,19 @@
new AnticipatedAction(JobSchedulerEconomicPolicy.ACTION_JOB_MAX_START, 1, 0))),
mock(AffordabilityChangeListener.class), mEconomicPolicy));
for (ActionAffordabilityNote note : affordabilityNotes) {
- note.recalculateModifiedPrice(mEconomicPolicy, 0, "com.test.app");
+ note.recalculateCosts(mEconomicPolicy, 0, "com.test.app");
}
// Balance is below threshold and events are all positive delta.
// Should report the correct time to the upper threshold.
- trendCalculator.reset(0, affordabilityNotes);
+ trendCalculator.reset(0, 1000, affordabilityNotes);
trendCalculator.accept(
- new OngoingEvent(EconomicPolicy.REWARD_TOP_ACTIVITY, "1",
- null, 1, 1));
+ new OngoingEvent(EconomicPolicy.REWARD_TOP_ACTIVITY, "1", 1,
+ new EconomicPolicy.Reward(EconomicPolicy.REWARD_TOP_ACTIVITY, 1, 1, 1)));
trendCalculator.accept(
- new OngoingEvent(EconomicPolicy.REWARD_OTHER_USER_INTERACTION, "2",
- null, 2, 3));
+ new OngoingEvent(EconomicPolicy.REWARD_OTHER_USER_INTERACTION, "2", 2,
+ new EconomicPolicy.Reward(EconomicPolicy.REWARD_OTHER_USER_INTERACTION,
+ 3, 3, 3)));
assertEquals("Expected not to cross lower threshold",
TrendCalculator.WILL_NOT_CROSS_THRESHOLD,
@@ -242,13 +245,13 @@
// Balance is above the threshold and events are all negative delta.
// Should report the correct time to the lower threshold.
- trendCalculator.reset(40, affordabilityNotes);
+ trendCalculator.reset(40, 100, affordabilityNotes);
trendCalculator.accept(
new OngoingEvent(JobSchedulerEconomicPolicy.ACTION_JOB_DEFAULT_RUNNING, "1",
- null, 1, -1));
+ 1, new EconomicPolicy.Cost(1, 1)));
trendCalculator.accept(
new OngoingEvent(JobSchedulerEconomicPolicy.ACTION_JOB_HIGH_RUNNING, "2",
- null, 2, -3));
+ 2, new EconomicPolicy.Cost(3, 3)));
assertEquals(5_000, trendCalculator.getTimeToCrossLowerThresholdMs());
assertEquals("Expected not to cross upper threshold",
@@ -257,6 +260,123 @@
}
@Test
+ public void testSelectCorrectThreshold_Balance() {
+ TrendCalculator trendCalculator = new TrendCalculator();
+ mEconomicPolicy.mEventCosts.put(JobSchedulerEconomicPolicy.ACTION_JOB_MAX_START, 20);
+ mEconomicPolicy.mEventCosts.put(JobSchedulerEconomicPolicy.ACTION_JOB_HIGH_START, 15);
+ mEconomicPolicy.mEventCosts.put(JobSchedulerEconomicPolicy.ACTION_JOB_LOW_START, 10);
+ mEconomicPolicy.mEventCosts.put(JobSchedulerEconomicPolicy.ACTION_JOB_MIN_START, 5);
+
+ ArraySet<ActionAffordabilityNote> affordabilityNotes = new ArraySet<>();
+ affordabilityNotes.add(new ActionAffordabilityNote(new ActionBill(List.of(
+ new AnticipatedAction(JobSchedulerEconomicPolicy.ACTION_JOB_MAX_START, 1, 0))),
+ mock(AffordabilityChangeListener.class), mEconomicPolicy));
+ affordabilityNotes.add(new ActionAffordabilityNote(new ActionBill(List.of(
+ new AnticipatedAction(JobSchedulerEconomicPolicy.ACTION_JOB_HIGH_START, 1, 0))),
+ mock(AffordabilityChangeListener.class), mEconomicPolicy));
+ affordabilityNotes.add(new ActionAffordabilityNote(new ActionBill(List.of(
+ new AnticipatedAction(JobSchedulerEconomicPolicy.ACTION_JOB_LOW_START, 1, 0))),
+ mock(AffordabilityChangeListener.class), mEconomicPolicy));
+ affordabilityNotes.add(new ActionAffordabilityNote(new ActionBill(List.of(
+ new AnticipatedAction(JobSchedulerEconomicPolicy.ACTION_JOB_MIN_START, 1, 0))),
+ mock(AffordabilityChangeListener.class), mEconomicPolicy));
+ for (ActionAffordabilityNote note : affordabilityNotes) {
+ note.recalculateCosts(mEconomicPolicy, 0, "com.test.app");
+ }
+
+ // Balance is below threshold and events are all positive delta.
+ // Should report the correct time to the correct upper threshold.
+ trendCalculator.reset(0, 10_000, affordabilityNotes);
+ trendCalculator.accept(
+ new OngoingEvent(EconomicPolicy.REWARD_TOP_ACTIVITY, "1", 1,
+ new EconomicPolicy.Reward(EconomicPolicy.REWARD_TOP_ACTIVITY, 1, 1, 1)));
+
+ assertEquals("Expected not to cross lower threshold",
+ TrendCalculator.WILL_NOT_CROSS_THRESHOLD,
+ trendCalculator.getTimeToCrossLowerThresholdMs());
+ assertEquals(5_000, trendCalculator.getTimeToCrossUpperThresholdMs());
+
+ // Balance is above the threshold and events are all negative delta.
+ // Should report the correct time to the correct lower threshold.
+ trendCalculator.reset(30, 500, affordabilityNotes);
+ trendCalculator.accept(
+ new OngoingEvent(JobSchedulerEconomicPolicy.ACTION_JOB_DEFAULT_RUNNING, "1",
+ 1, new EconomicPolicy.Cost(1, 1)));
+
+ assertEquals(10_000, trendCalculator.getTimeToCrossLowerThresholdMs());
+ assertEquals("Expected not to cross upper threshold",
+ TrendCalculator.WILL_NOT_CROSS_THRESHOLD,
+ trendCalculator.getTimeToCrossUpperThresholdMs());
+ }
+
+ @Test
+ public void testTrendsToBothThresholds_Balance() {
+ TrendCalculator trendCalculator = new TrendCalculator();
+ mEconomicPolicy.mEventCosts.put(JobSchedulerEconomicPolicy.ACTION_JOB_MAX_START, 20);
+ mEconomicPolicy.mEventCosts.put(AlarmManagerEconomicPolicy.ACTION_ALARM_CLOCK, 50);
+
+ ArraySet<ActionAffordabilityNote> affordabilityNotes = new ArraySet<>();
+ affordabilityNotes.add(new ActionAffordabilityNote(new ActionBill(List.of(
+ new AnticipatedAction(JobSchedulerEconomicPolicy.ACTION_JOB_MAX_START, 1, 0))),
+ mock(AffordabilityChangeListener.class), mEconomicPolicy));
+ affordabilityNotes.add(new ActionAffordabilityNote(new ActionBill(List.of(
+ new AnticipatedAction(AlarmManagerEconomicPolicy.ACTION_ALARM_CLOCK, 1, 0))),
+ mock(AffordabilityChangeListener.class), mEconomicPolicy));
+ for (ActionAffordabilityNote note : affordabilityNotes) {
+ note.recalculateCosts(mEconomicPolicy, 0, "com.test.app");
+ }
+
+ // Balance is between both thresholds and events are mixed positive/negative delta.
+ // Should report the correct time to each threshold.
+ trendCalculator.reset(35, 10_000, affordabilityNotes);
+ trendCalculator.accept(
+ new OngoingEvent(EconomicPolicy.REWARD_TOP_ACTIVITY, "1", 1,
+ new EconomicPolicy.Reward(EconomicPolicy.REWARD_TOP_ACTIVITY, 3, 3, 3)));
+ trendCalculator.accept(
+ new OngoingEvent(EconomicPolicy.REWARD_OTHER_USER_INTERACTION, "2", 2,
+ new EconomicPolicy.Reward(EconomicPolicy.REWARD_OTHER_USER_INTERACTION, 2,
+ 2, 2)));
+ trendCalculator.accept(
+ new OngoingEvent(JobSchedulerEconomicPolicy.ACTION_JOB_LOW_RUNNING, "3",
+ 3, new EconomicPolicy.Cost(2, 2)));
+ trendCalculator.accept(
+ new OngoingEvent(JobSchedulerEconomicPolicy.ACTION_JOB_DEFAULT_RUNNING, "4",
+ 4, new EconomicPolicy.Cost(3, 3)));
+
+ assertEquals(3_000, trendCalculator.getTimeToCrossLowerThresholdMs());
+ assertEquals(3_000, trendCalculator.getTimeToCrossUpperThresholdMs());
+ }
+
+ @Test
+ public void testSimpleTrendToThreshold_ConsumptionLimit() {
+ TrendCalculator trendCalculator = new TrendCalculator();
+ mEconomicPolicy.mEventCosts.put(JobSchedulerEconomicPolicy.ACTION_JOB_MAX_START, 20);
+
+ ArraySet<ActionAffordabilityNote> affordabilityNotes = new ArraySet<>();
+ affordabilityNotes.add(new ActionAffordabilityNote(new ActionBill(List.of(
+ new AnticipatedAction(JobSchedulerEconomicPolicy.ACTION_JOB_MAX_START, 1, 0))),
+ mock(AffordabilityChangeListener.class), mEconomicPolicy));
+ for (ActionAffordabilityNote note : affordabilityNotes) {
+ note.recalculateCosts(mEconomicPolicy, 0, "com.test.app");
+ }
+
+ // Events are all negative delta. Consumable credits will run out before app's balance.
+ // Should report the correct time to the lower threshold.
+ trendCalculator.reset(10000, 40, affordabilityNotes);
+ trendCalculator.accept(
+ new OngoingEvent(JobSchedulerEconomicPolicy.ACTION_JOB_DEFAULT_RUNNING, "1",
+ 1, new EconomicPolicy.Cost(1, 10)));
+ trendCalculator.accept(
+ new OngoingEvent(JobSchedulerEconomicPolicy.ACTION_JOB_HIGH_RUNNING, "2",
+ 2, new EconomicPolicy.Cost(3, 40)));
+
+ assertEquals(10_000, trendCalculator.getTimeToCrossLowerThresholdMs());
+ assertEquals("Expected not to cross upper threshold",
+ TrendCalculator.WILL_NOT_CROSS_THRESHOLD,
+ trendCalculator.getTimeToCrossUpperThresholdMs());
+ }
+
+ @Test
public void testSelectCorrectThreshold() {
TrendCalculator trendCalculator = new TrendCalculator();
mEconomicPolicy.mEventCosts.put(JobSchedulerEconomicPolicy.ACTION_JOB_MAX_START, 20);
@@ -278,68 +398,45 @@
new AnticipatedAction(JobSchedulerEconomicPolicy.ACTION_JOB_MIN_START, 1, 0))),
mock(AffordabilityChangeListener.class), mEconomicPolicy));
for (ActionAffordabilityNote note : affordabilityNotes) {
- note.recalculateModifiedPrice(mEconomicPolicy, 0, "com.test.app");
+ note.recalculateCosts(mEconomicPolicy, 0, "com.test.app");
}
- // Balance is below threshold and events are all positive delta.
- // Should report the correct time to the correct upper threshold.
- trendCalculator.reset(0, affordabilityNotes);
+ // Balance is above threshold, consumable credits is 0, and events are all positive delta.
+ // There should be no time to the upper threshold since consumable credits is the limiting
+ // factor.
+ trendCalculator.reset(10_000, 0, affordabilityNotes);
trendCalculator.accept(
- new OngoingEvent(EconomicPolicy.REWARD_TOP_ACTIVITY, "1",
- null, 1, 1));
+ new OngoingEvent(EconomicPolicy.REWARD_TOP_ACTIVITY, "1", 1,
+ new EconomicPolicy.Reward(EconomicPolicy.REWARD_TOP_ACTIVITY, 1, 1, 1)));
assertEquals("Expected not to cross lower threshold",
TrendCalculator.WILL_NOT_CROSS_THRESHOLD,
trendCalculator.getTimeToCrossLowerThresholdMs());
- assertEquals(5_000, trendCalculator.getTimeToCrossUpperThresholdMs());
-
- // Balance is above the threshold and events are all negative delta.
- // Should report the correct time to the correct lower threshold.
- trendCalculator.reset(30, affordabilityNotes);
- trendCalculator.accept(
- new OngoingEvent(JobSchedulerEconomicPolicy.ACTION_JOB_DEFAULT_RUNNING, "1",
- null, 1, -1));
-
- assertEquals(10_000, trendCalculator.getTimeToCrossLowerThresholdMs());
assertEquals("Expected not to cross upper threshold",
TrendCalculator.WILL_NOT_CROSS_THRESHOLD,
trendCalculator.getTimeToCrossUpperThresholdMs());
- }
- @Test
- public void testTrendsToBothThresholds() {
- TrendCalculator trendCalculator = new TrendCalculator();
- mEconomicPolicy.mEventCosts.put(JobSchedulerEconomicPolicy.ACTION_JOB_MAX_START, 20);
- mEconomicPolicy.mEventCosts.put(AlarmManagerEconomicPolicy.ACTION_ALARM_CLOCK, 50);
+ // Balance is above threshold, consumable credits is low, and events are all negative delta.
+ trendCalculator.reset(10_000, 4, affordabilityNotes);
+ trendCalculator.accept(
+ new OngoingEvent(JobSchedulerEconomicPolicy.ACTION_JOB_DEFAULT_RUNNING, "1",
+ 1, new EconomicPolicy.Cost(1, 10)));
- ArraySet<ActionAffordabilityNote> affordabilityNotes = new ArraySet<>();
- affordabilityNotes.add(new ActionAffordabilityNote(new ActionBill(List.of(
- new AnticipatedAction(JobSchedulerEconomicPolicy.ACTION_JOB_MAX_START, 1, 0))),
- mock(AffordabilityChangeListener.class), mEconomicPolicy));
- affordabilityNotes.add(new ActionAffordabilityNote(new ActionBill(List.of(
- new AnticipatedAction(AlarmManagerEconomicPolicy.ACTION_ALARM_CLOCK, 1, 0))),
- mock(AffordabilityChangeListener.class), mEconomicPolicy));
- for (ActionAffordabilityNote note : affordabilityNotes) {
- note.recalculateModifiedPrice(mEconomicPolicy, 0, "com.test.app");
- }
+ assertEquals(4000, trendCalculator.getTimeToCrossLowerThresholdMs());
+ assertEquals("Expected not to cross upper threshold",
+ TrendCalculator.WILL_NOT_CROSS_THRESHOLD,
+ trendCalculator.getTimeToCrossUpperThresholdMs());
- // Balance is between both thresholds and events are mixed positive/negative delta.
- // Should report the correct time to each threshold.
- trendCalculator.reset(35, affordabilityNotes);
+ // Balance is above threshold, consumable credits is 0, and events are all negative delta.
+ // Time to the lower threshold should be 0 since consumable credits is already 0.
+ trendCalculator.reset(10_000, 0, affordabilityNotes);
trendCalculator.accept(
- new OngoingEvent(EconomicPolicy.REWARD_TOP_ACTIVITY, "1",
- null, 1, 3));
- trendCalculator.accept(
- new OngoingEvent(EconomicPolicy.REWARD_OTHER_USER_INTERACTION, "2",
- null, 2, 2));
- trendCalculator.accept(
- new OngoingEvent(JobSchedulerEconomicPolicy.ACTION_JOB_LOW_RUNNING, "3",
- null, 3, -2));
- trendCalculator.accept(
- new OngoingEvent(JobSchedulerEconomicPolicy.ACTION_JOB_DEFAULT_RUNNING, "4",
- null, 4, -3));
+ new OngoingEvent(JobSchedulerEconomicPolicy.ACTION_JOB_DEFAULT_RUNNING, "1",
+ 1, new EconomicPolicy.Cost(1, 10)));
- assertEquals(3_000, trendCalculator.getTimeToCrossLowerThresholdMs());
- assertEquals(3_000, trendCalculator.getTimeToCrossUpperThresholdMs());
+ assertEquals(0, trendCalculator.getTimeToCrossLowerThresholdMs());
+ assertEquals("Expected not to cross upper threshold",
+ TrendCalculator.WILL_NOT_CROSS_THRESHOLD,
+ trendCalculator.getTimeToCrossUpperThresholdMs());
}
}
diff --git a/services/tests/servicestests/src/com/android/server/tare/LedgerTest.java b/services/tests/servicestests/src/com/android/server/tare/LedgerTest.java
index 4a25323..22dcf84 100644
--- a/services/tests/servicestests/src/com/android/server/tare/LedgerTest.java
+++ b/services/tests/servicestests/src/com/android/server/tare/LedgerTest.java
@@ -54,13 +54,13 @@
@Test
public void testMultipleTransactions() {
final Ledger ledger = new Ledger();
- ledger.recordTransaction(new Ledger.Transaction(0, 1000, 1, null, 5));
+ ledger.recordTransaction(new Ledger.Transaction(0, 1000, 1, null, 5, 0));
assertEquals(5, ledger.getCurrentBalance());
assertEquals(5, ledger.get24HourSum(1, 60_000));
- ledger.recordTransaction(new Ledger.Transaction(2000, 2000, 1, null, 25));
+ ledger.recordTransaction(new Ledger.Transaction(2000, 2000, 1, null, 25, 0));
assertEquals(30, ledger.getCurrentBalance());
assertEquals(30, ledger.get24HourSum(1, 60_000));
- ledger.recordTransaction(new Ledger.Transaction(5000, 5500, 1, null, -10));
+ ledger.recordTransaction(new Ledger.Transaction(5000, 5500, 1, null, -10, 5));
assertEquals(20, ledger.getCurrentBalance());
assertEquals(20, ledger.get24HourSum(1, 60_000));
}
@@ -68,13 +68,13 @@
@Test
public void test24HourSum() {
final Ledger ledger = new Ledger();
- ledger.recordTransaction(new Ledger.Transaction(0, 1000, 1, null, 500));
+ ledger.recordTransaction(new Ledger.Transaction(0, 1000, 1, null, 500, 0));
assertEquals(500, ledger.get24HourSum(1, 24 * HOUR_IN_MILLIS));
ledger.recordTransaction(
- new Ledger.Transaction(2 * HOUR_IN_MILLIS, 3 * HOUR_IN_MILLIS, 1, null, 2500));
+ new Ledger.Transaction(2 * HOUR_IN_MILLIS, 3 * HOUR_IN_MILLIS, 1, null, 2500, 0));
assertEquals(3000, ledger.get24HourSum(1, 24 * HOUR_IN_MILLIS));
ledger.recordTransaction(
- new Ledger.Transaction(4 * HOUR_IN_MILLIS, 4 * HOUR_IN_MILLIS, 1, null, 1));
+ new Ledger.Transaction(4 * HOUR_IN_MILLIS, 4 * HOUR_IN_MILLIS, 1, null, 1, 0));
assertEquals(3001, ledger.get24HourSum(1, 24 * HOUR_IN_MILLIS));
assertEquals(2501, ledger.get24HourSum(1, 25 * HOUR_IN_MILLIS));
assertEquals(2501, ledger.get24HourSum(1, 26 * HOUR_IN_MILLIS));
@@ -93,17 +93,17 @@
final long now = getCurrentTimeMillis();
Ledger.Transaction transaction1 = new Ledger.Transaction(
- now - 48 * HOUR_IN_MILLIS, now - 40 * HOUR_IN_MILLIS, 1, null, 4800);
+ now - 48 * HOUR_IN_MILLIS, now - 40 * HOUR_IN_MILLIS, 1, null, 4800, 0);
Ledger.Transaction transaction2 = new Ledger.Transaction(
- now - 24 * HOUR_IN_MILLIS, now - 23 * HOUR_IN_MILLIS, 1, null, 600);
+ now - 24 * HOUR_IN_MILLIS, now - 23 * HOUR_IN_MILLIS, 1, null, 600, 0);
Ledger.Transaction transaction3 = new Ledger.Transaction(
- now - 22 * HOUR_IN_MILLIS, now - 21 * HOUR_IN_MILLIS, 1, null, 600);
+ now - 22 * HOUR_IN_MILLIS, now - 21 * HOUR_IN_MILLIS, 1, null, 600, 0);
// Instant event
Ledger.Transaction transaction4 = new Ledger.Transaction(
- now - 20 * HOUR_IN_MILLIS, now - 20 * HOUR_IN_MILLIS, 1, null, 500);
+ now - 20 * HOUR_IN_MILLIS, now - 20 * HOUR_IN_MILLIS, 1, null, 500, 0);
// Recent event
Ledger.Transaction transaction5 = new Ledger.Transaction(
- now - 5 * MINUTE_IN_MILLIS, now - MINUTE_IN_MILLIS, 1, null, 400);
+ now - 5 * MINUTE_IN_MILLIS, now - MINUTE_IN_MILLIS, 1, null, 400, 0);
ledger.recordTransaction(transaction1);
ledger.recordTransaction(transaction2);
ledger.recordTransaction(transaction3);