Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 4bedfc19 authored by Kweku Adams's avatar Kweku Adams
Browse files

Exempt exact alarms from the stock limit.

Allow apps to set and use exact alarms even if the TARE system has run
out of consumable stock. Since apps need to hold the
SCHEDULE_EXACT_ALARM or USE_EXACT_ALARM permissions to use exact alarms,
we can be a little more lenient with the check. Enforcing the stock
limit check could result in some unexpected user journeys. We continue
to apply the app balance check so apps can't runaway with exact alarms.

Bug: 258032540
Test: atest frameworks/base/services/tests/mockingservicestests/src/com/android/server/tare
Test: atest frameworks/base/services/tests/servicestests/src/com/android/server/tare
Change-Id: I307fe35370e0450d2248e4bbdbcf0c00839d3214
parent ce03ab0b
Loading
Loading
Loading
Loading
+25 −16
Original line number Diff line number Diff line
@@ -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<EconomyManagerInternal.AnticipatedAction> 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
                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);
+18 −7
Original line number Diff line number Diff line
@@ -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,
+11 −0
Original line number Diff line number Diff line
@@ -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;
        }
    }