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

Commit 23c83046 authored by Kweku Adams's avatar Kweku Adams Committed by Android (Google) Code Review
Browse files

Merge "Standardizing affordability check."

parents 8c48b0d1 17e33d71
Loading
Loading
Loading
Loading
+105 −80
Original line number Diff line number Diff line
@@ -55,8 +55,6 @@ import com.android.server.pm.UserManagerInternal;
import com.android.server.usage.AppStandbyInternal;
import com.android.server.utils.AlarmQueue;

import libcore.util.EmptyArray;

import java.util.List;
import java.util.Objects;
import java.util.function.Consumer;
@@ -154,6 +152,11 @@ class Agent {
        return balance;
    }

    @GuardedBy("mLock")
    private boolean isAffordableLocked(long balance, long price) {
        return balance >= price;
    }

    @GuardedBy("mLock")
    void noteInstantaneousEventLocked(final int userId, @NonNull final String pkgName,
            final int eventId, @Nullable String tag) {
@@ -257,105 +260,94 @@ class Agent {

    @GuardedBy("mLock")
    void onPricingChangedLocked() {
        onAnythingChangedLocked(true);
    }

    @GuardedBy("mLock")
    void onAppStatesChangedLocked(final int userId, @NonNull ArraySet<String> pkgNames) {
        final long now = getCurrentTimeMillis();
        final long nowElapsed = SystemClock.elapsedRealtime();
        final CompleteEconomicPolicy economicPolicy = mIrs.getCompleteEconomicPolicyLocked();

        mCurrentOngoingEvents.forEach((userId, pkgName, ongoingEvents) -> {
        for (int i = 0; i < pkgNames.size(); ++i) {
            final String pkgName = pkgNames.valueAt(i);
            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];
                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) {
                    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());
                        if (note.isCurrentlyAffordable() != isAffordable) {
                            note.setNewAffordability(isAffordable);
                            mIrs.postAffordabilityChanged(userId, pkgName, note);
                        }
                    }
                }
                scheduleBalanceCheckLocked(userId, pkgName);
        });
            }
        }
    }

    @GuardedBy("mLock")
    void onAppStatesChangedLocked(final int userId, @NonNull ArraySet<String> pkgNames) {
    private void onAnythingChangedLocked(final boolean updateOngoingEvents) {
        final long now = getCurrentTimeMillis();
        final long nowElapsed = SystemClock.elapsedRealtime();
        final CompleteEconomicPolicy economicPolicy = mIrs.getCompleteEconomicPolicyLocked();

        for (int i = 0; i < pkgNames.size(); ++i) {
            final String pkgName = pkgNames.valueAt(i);
        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.get(userId, pkgName);
                        mCurrentOngoingEvents.valueAt(uIdx, pIdx);
                if (ongoingEvents != null) {
                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 n = 0; n < size; ++n) {
                        final ActionAffordabilityNote note = actionAffordabilityNotes.valueAt(n);
                        final long originalBalance =
                                mScribe.getLedgerLocked(userId, pkgName).getCurrentBalance();
                        wasAffordable[n] = originalBalance >= note.getCachedModifiedPrice();
                    if (updateOngoingEvents) {
                        mOngoingEventUpdater.reset(userId, pkgName, now, nowElapsed);
                        ongoingEvents.forEach(mOngoingEventUpdater);
                    }
                } 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);
                });
                    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.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());
                        if (note.isCurrentlyAffordable() != isAffordable) {
                            note.setNewAffordability(isAffordable);
                            mIrs.postAffordabilityChanged(userId, pkgName, note);
                        }
                    }
                }
                scheduleBalanceCheckLocked(userId, pkgName);
            }
        }
    }
@@ -367,9 +359,9 @@ class Agent {
    }

    /**
     * @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")
@@ -486,7 +478,9 @@ class Agent {
                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());
                    if (note.isCurrentlyAffordable() != isAffordable) {
                        note.setNewAffordability(isAffordable);
                        mIrs.postAffordabilityChanged(userId, pkgName, note);
@@ -893,6 +887,36 @@ class Agent {
        }
    }

    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;
@@ -971,9 +995,10 @@ class Agent {
                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()));
            mIrs.postAffordabilityChanged(userId, pkgName, note);
            // Update ongoing alarm
            scheduleBalanceCheckLocked(userId, pkgName);
@@ -1035,7 +1060,7 @@ class Agent {
        }

        @VisibleForTesting
        long recalculateModifiedPrice(@NonNull EconomicPolicy economicPolicy,
        long recalculateCosts(@NonNull EconomicPolicy economicPolicy,
                int userId, @NonNull String pkgName) {
            long modifiedPrice = 0;
            final List<EconomyManagerInternal.AnticipatedAction> anticipatedActions =
@@ -1099,8 +1124,8 @@ class Agent {
                            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());
                                if (note.isCurrentlyAffordable() != isAffordable) {
                                    note.setNewAffordability(isAffordable);
                                    mIrs.postAffordabilityChanged(userId, pkgName, note);
@@ -1155,7 +1180,7 @@ class Agent {
                        pw.print(" runtime=");
                        TimeUtils.formatDuration(nowElapsed - ongoingEvent.startTimeElapsed, pw);
                        pw.print(" delta/sec=");
                        pw.print(ongoingEvent.deltaPerSec);
                        pw.print(narcToString(ongoingEvent.deltaPerSec));
                        pw.print(" refCount=");
                        pw.print(ongoingEvent.refCount);
                        pw.println();
+5 −5
Original line number Diff line number Diff line
@@ -115,7 +115,7 @@ public class AgentTrendCalculatorTest {
                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);
@@ -174,7 +174,7 @@ public class AgentTrendCalculatorTest {
                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.
@@ -222,7 +222,7 @@ public class AgentTrendCalculatorTest {
                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.
@@ -278,7 +278,7 @@ public class AgentTrendCalculatorTest {
                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.
@@ -320,7 +320,7 @@ public class AgentTrendCalculatorTest {
                new AnticipatedAction(AlarmManagerEconomicPolicy.ACTION_ALARM_CLOCK, 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 between both thresholds and events are mixed positive/negative delta.