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 b84c8a41af1cc..2b59209127183 100644 --- a/apex/jobscheduler/service/java/com/android/server/tare/Agent.java +++ b/apex/jobscheduler/service/java/com/android/server/tare/Agent.java @@ -164,8 +164,9 @@ class Agent { } @GuardedBy("mLock") - private boolean isAffordableLocked(long balance, long price, long ctp) { - return balance >= price && mScribe.getRemainingConsumableCakesLocked() >= ctp; + private boolean isAffordableLocked(long balance, long price, long stockLimitHonoringCtp) { + return balance >= price + && mScribe.getRemainingConsumableCakesLocked() >= stockLimitHonoringCtp; } @GuardedBy("mLock") @@ -303,7 +304,8 @@ class Agent { note.recalculateCosts(economicPolicy, userId, pkgName); final boolean isAffordable = isVip || isAffordableLocked(newBalance, - note.getCachedModifiedPrice(), note.getCtp()); + note.getCachedModifiedPrice(), + note.getStockLimitHonoringCtp()); if (note.isCurrentlyAffordable() != isAffordable) { note.setNewAffordability(isAffordable); mIrs.postAffordabilityChanged(userId, pkgName, note); @@ -339,7 +341,7 @@ class Agent { note.recalculateCosts(economicPolicy, userId, pkgName); final boolean isAffordable = isVip || isAffordableLocked(newBalance, - note.getCachedModifiedPrice(), note.getCtp()); + note.getCachedModifiedPrice(), note.getStockLimitHonoringCtp()); if (note.isCurrentlyAffordable() != isAffordable) { note.setNewAffordability(isAffordable); mIrs.postAffordabilityChanged(userId, pkgName, note); @@ -403,7 +405,8 @@ class Agent { note.recalculateCosts(economicPolicy, userId, pkgName); final boolean isAffordable = isVip || isAffordableLocked(newBalance, - note.getCachedModifiedPrice(), note.getCtp()); + note.getCachedModifiedPrice(), + note.getStockLimitHonoringCtp()); if (note.isCurrentlyAffordable() != isAffordable) { note.setNewAffordability(isAffordable); mIrs.postAffordabilityChanged(userId, pkgName, note); @@ -541,7 +544,7 @@ class Agent { final ActionAffordabilityNote note = actionAffordabilityNotes.valueAt(i); final boolean isAffordable = isVip || isAffordableLocked(newBalance, - note.getCachedModifiedPrice(), note.getCtp()); + note.getCachedModifiedPrice(), note.getStockLimitHonoringCtp()); if (note.isCurrentlyAffordable() != isAffordable) { note.setNewAffordability(isAffordable); mIrs.postAffordabilityChanged(userId, pkgName, note); @@ -882,7 +885,7 @@ class Agent { mUpperThreshold = (mUpperThreshold == Long.MIN_VALUE) ? price : Math.min(mUpperThreshold, price); } - final long ctp = note.getCtp(); + final long ctp = note.getStockLimitHonoringCtp(); if (ctp <= mRemainingConsumableCredits) { mCtpThreshold = Math.max(mCtpThreshold, ctp); } @@ -1119,7 +1122,7 @@ class Agent { note.recalculateCosts(economicPolicy, userId, pkgName); note.setNewAffordability(isVip || isAffordableLocked(getBalanceLocked(userId, pkgName), - note.getCachedModifiedPrice(), note.getCtp())); + note.getCachedModifiedPrice(), note.getStockLimitHonoringCtp())); mIrs.postAffordabilityChanged(userId, pkgName, note); // Update ongoing alarm scheduleBalanceCheckLocked(userId, pkgName); @@ -1146,7 +1149,7 @@ class Agent { static final class ActionAffordabilityNote { private final EconomyManagerInternal.ActionBill mActionBill; private final EconomyManagerInternal.AffordabilityChangeListener mListener; - private long mCtp; + private long mStockLimitHonoringCtp; private long mModifiedPrice; private boolean mIsAffordable; @@ -1185,29 +1188,34 @@ class Agent { return mModifiedPrice; } - private long getCtp() { - return mCtp; + /** Returns the cumulative CTP of actions in this note that respect the stock limit. */ + private long getStockLimitHonoringCtp() { + return mStockLimitHonoringCtp; } @VisibleForTesting void recalculateCosts(@NonNull EconomicPolicy economicPolicy, int userId, @NonNull String pkgName) { long modifiedPrice = 0; - long ctp = 0; + long stockLimitHonoringCtp = 0; final List anticipatedActions = mActionBill.getAnticipatedActions(); for (int i = 0; i < anticipatedActions.size(); ++i) { final EconomyManagerInternal.AnticipatedAction aa = anticipatedActions.get(i); + final EconomicPolicy.Action action = economicPolicy.getAction(aa.actionId); final EconomicPolicy.Cost actionCost = economicPolicy.getCostOfAction(aa.actionId, userId, pkgName); modifiedPrice += actionCost.price * aa.numInstantaneousCalls + actionCost.price * (aa.ongoingDurationMs / 1000); - ctp += actionCost.costToProduce * aa.numInstantaneousCalls - + actionCost.costToProduce * (aa.ongoingDurationMs / 1000); + if (action.respectsStockLimit) { + stockLimitHonoringCtp += + actionCost.costToProduce * aa.numInstantaneousCalls + + actionCost.costToProduce * (aa.ongoingDurationMs / 1000); + } } mModifiedPrice = modifiedPrice; - mCtp = ctp; + mStockLimitHonoringCtp = stockLimitHonoringCtp; } boolean isCurrentlyAffordable() { @@ -1267,7 +1275,8 @@ class Agent { final ActionAffordabilityNote note = actionAffordabilityNotes.valueAt(i); final boolean isAffordable = isVip || isAffordableLocked( - newBalance, note.getCachedModifiedPrice(), note.getCtp()); + newBalance, note.getCachedModifiedPrice(), + note.getStockLimitHonoringCtp()); if (note.isCurrentlyAffordable() != isAffordable) { note.setNewAffordability(isAffordable); mIrs.postAffordabilityChanged(userId, pkgName, note); 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 46338fa69eb0d..a6d064c760445 100644 --- a/apex/jobscheduler/service/java/com/android/server/tare/AlarmManagerEconomicPolicy.java +++ b/apex/jobscheduler/service/java/com/android/server/tare/AlarmManagerEconomicPolicy.java @@ -262,12 +262,17 @@ public class AlarmManagerEconomicPolicy extends EconomicPolicy { KEY_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_EXACT_WAKEUP_BASE_PRICE, DEFAULT_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_EXACT_WAKEUP_BASE_PRICE_CAKES); + // Apps must hold the SCHEDULE_EXACT_ALARM or USE_EXACT_ALARMS permission in order to use + // exact alarms. Since the user has the option of granting/revoking the permission, we can + // be a little lenient on the action cost checks and only stop the action if the app has + // run out of credits, and not when the system has run out of stock. mActions.put(ACTION_ALARM_WAKEUP_EXACT_ALLOW_WHILE_IDLE, new Action(ACTION_ALARM_WAKEUP_EXACT_ALLOW_WHILE_IDLE, getConstantAsCake(mParser, properties, KEY_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_EXACT_WAKEUP_CTP, DEFAULT_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_EXACT_WAKEUP_CTP_CAKES), - exactAllowWhileIdleWakeupBasePrice)); + exactAllowWhileIdleWakeupBasePrice, + /* respectsStockLimit */ false)); mActions.put(ACTION_ALARM_WAKEUP_EXACT, new Action(ACTION_ALARM_WAKEUP_EXACT, getConstantAsCake(mParser, properties, @@ -275,7 +280,8 @@ public class AlarmManagerEconomicPolicy extends EconomicPolicy { DEFAULT_AM_ACTION_ALARM_EXACT_WAKEUP_CTP_CAKES), getConstantAsCake(mParser, properties, KEY_AM_ACTION_ALARM_EXACT_WAKEUP_BASE_PRICE, - DEFAULT_AM_ACTION_ALARM_EXACT_WAKEUP_BASE_PRICE_CAKES))); + DEFAULT_AM_ACTION_ALARM_EXACT_WAKEUP_BASE_PRICE_CAKES), + /* respectsStockLimit */ false)); final long inexactAllowWhileIdleWakeupBasePrice = getConstantAsCake(mParser, properties, @@ -287,7 +293,8 @@ public class AlarmManagerEconomicPolicy extends EconomicPolicy { getConstantAsCake(mParser, properties, KEY_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_INEXACT_WAKEUP_CTP, DEFAULT_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_INEXACT_WAKEUP_CTP_CAKES), - inexactAllowWhileIdleWakeupBasePrice)); + inexactAllowWhileIdleWakeupBasePrice, + /* respectsStockLimit */ false)); mActions.put(ACTION_ALARM_WAKEUP_INEXACT, new Action(ACTION_ALARM_WAKEUP_INEXACT, getConstantAsCake(mParser, properties, @@ -295,7 +302,8 @@ public class AlarmManagerEconomicPolicy extends EconomicPolicy { DEFAULT_AM_ACTION_ALARM_INEXACT_WAKEUP_CTP_CAKES), getConstantAsCake(mParser, properties, KEY_AM_ACTION_ALARM_INEXACT_WAKEUP_BASE_PRICE, - DEFAULT_AM_ACTION_ALARM_INEXACT_WAKEUP_BASE_PRICE_CAKES))); + DEFAULT_AM_ACTION_ALARM_INEXACT_WAKEUP_BASE_PRICE_CAKES), + /* respectsStockLimit */ false)); final long exactAllowWhileIdleNonWakeupBasePrice = getConstantAsCake(mParser, properties, KEY_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_EXACT_NONWAKEUP_BASE_PRICE, @@ -305,7 +313,8 @@ public class AlarmManagerEconomicPolicy extends EconomicPolicy { getConstantAsCake(mParser, properties, KEY_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_EXACT_NONWAKEUP_CTP, DEFAULT_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_EXACT_NONWAKEUP_CTP_CAKES), - exactAllowWhileIdleNonWakeupBasePrice)); + exactAllowWhileIdleNonWakeupBasePrice, + /* respectsStockLimit */ false)); mActions.put(ACTION_ALARM_NONWAKEUP_EXACT, new Action(ACTION_ALARM_NONWAKEUP_EXACT, @@ -314,7 +323,8 @@ public class AlarmManagerEconomicPolicy extends EconomicPolicy { DEFAULT_AM_ACTION_ALARM_EXACT_NONWAKEUP_CTP_CAKES), getConstantAsCake(mParser, properties, KEY_AM_ACTION_ALARM_EXACT_NONWAKEUP_BASE_PRICE, - DEFAULT_AM_ACTION_ALARM_EXACT_NONWAKEUP_BASE_PRICE_CAKES))); + DEFAULT_AM_ACTION_ALARM_EXACT_NONWAKEUP_BASE_PRICE_CAKES), + /* respectsStockLimit */ false)); final long inexactAllowWhileIdleNonWakeupBasePrice = getConstantAsCake(mParser, properties, KEY_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_INEXACT_NONWAKEUP_BASE_PRICE, @@ -342,7 +352,8 @@ public class AlarmManagerEconomicPolicy extends EconomicPolicy { DEFAULT_AM_ACTION_ALARM_ALARMCLOCK_CTP_CAKES), getConstantAsCake(mParser, properties, KEY_AM_ACTION_ALARM_ALARMCLOCK_BASE_PRICE, - DEFAULT_AM_ACTION_ALARM_ALARMCLOCK_BASE_PRICE_CAKES))); + DEFAULT_AM_ACTION_ALARM_ALARMCLOCK_BASE_PRICE_CAKES), + /* respectsStockLimit */ false)); mRewards.put(REWARD_TOP_ACTIVITY, new Reward(REWARD_TOP_ACTIVITY, getConstantAsCake(mParser, properties, 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 b52f6f11b3bfc..a4043dd8ba789 100644 --- a/apex/jobscheduler/service/java/com/android/server/tare/EconomicPolicy.java +++ b/apex/jobscheduler/service/java/com/android/server/tare/EconomicPolicy.java @@ -149,11 +149,22 @@ public abstract class EconomicPolicy { * the action unless a modifier lowers the cost to produce. */ public final long basePrice; + /** + * Whether the remaining stock limit affects an app's ability to perform this action. + * If {@code false}, then the action can be performed, even if the cost is higher + * than the remaining stock. This does not affect checking against an app's balance. + */ + public final boolean respectsStockLimit; Action(int id, long costToProduce, long basePrice) { + this(id, costToProduce, basePrice, true); + } + + Action(int id, long costToProduce, long basePrice, boolean respectsStockLimit) { this.id = id; this.costToProduce = costToProduce; this.basePrice = basePrice; + this.respectsStockLimit = respectsStockLimit; } }