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

Commit 509d4ce7 authored by Varun Shah's avatar Varun Shah
Browse files

Rate limit calls to AM.getMyMemoryState().

Apps do not need to call getMyMemoryState any more than 200 times per
second. Rate limit this API to 2 calls every 10ms which will allow
apps to keep getting updated info at a reasonable interval.

Flag: android.app.rate_limit_get_my_memory_state
Bug: 365182205
Test: atest ActivityManagerTest
Test: manually check logs for cache hits/misses
Change-Id: I0ab53c8f477dc5a82c812d1fe541491e04367cc9
parent d65f8d06
Loading
Loading
Loading
Loading
+40 −1
Original line number Diff line number Diff line
@@ -247,6 +247,14 @@ public class ActivityManager {
    @GuardedBy("mMemoryInfoCache")
    private static final MemoryInfo mRateLimitedMemInfo = new MemoryInfo();

    /** Rate-Limiting cache that allows no more than 200 calls to the service per second. */
    @GuardedBy("mMyMemoryStateCache")
    private static final RateLimitingCache<RunningAppProcessInfo> mMyMemoryStateCache =
            new RateLimitingCache<>(10, 2);
    /** Used to store cached results for rate-limited calls to getMyMemoryState(). */
    @GuardedBy("mMyMemoryStateCache")
    private static final RunningAppProcessInfo mRateLimitedMemState = new RunningAppProcessInfo();

    /**
     * Query handler for mGetCurrentUserIdCache - returns a cached value of the current foreground
     * user id if the backstage_power/android.app.cache_get_current_user_id flag is enabled.
@@ -4223,6 +4231,23 @@ public class ActivityManager {
            lastActivityTime = source.readLong();
        }

        /**
         * Note: only fields that are updated in ProcessList.fillInProcMemInfoLOSP() are copied.
         * @hide
         */
        public void copyTo(RunningAppProcessInfo other) {
            other.pid = pid;
            other.uid = uid;
            other.flags = flags;
            other.lastTrimLevel = lastTrimLevel;
            other.importance = importance;
            other.lru = lru;
            other.importanceReasonCode = importanceReasonCode;
            other.processState = processState;
            other.isFocused = isFocused;
            other.lastActivityTime = lastActivityTime;
        }

        public static final @android.annotation.NonNull Creator<RunningAppProcessInfo> CREATOR =
            new Creator<RunningAppProcessInfo>() {
            public RunningAppProcessInfo createFromParcel(Parcel source) {
@@ -4854,7 +4879,21 @@ public class ActivityManager {
     * {@link RunningAppProcessInfo#lru}, and
     * {@link RunningAppProcessInfo#importanceReasonCode}.
     */
    static public void getMyMemoryState(RunningAppProcessInfo outState) {
    public static void getMyMemoryState(RunningAppProcessInfo outState) {
        if (Flags.rateLimitGetMyMemoryState()) {
            synchronized (mMyMemoryStateCache) {
                mMyMemoryStateCache.get(() -> {
                    getMyMemoryStateInternal(mRateLimitedMemState);
                    return mRateLimitedMemState;
                });
                mRateLimitedMemState.copyTo(outState);
            }
        } else {
            getMyMemoryStateInternal(outState);
        }
    }

    private static void getMyMemoryStateInternal(RunningAppProcessInfo outState) {
        try {
            getService().getMyMemoryState(outState);
        } catch (RemoteException e) {
+11 −0
Original line number Diff line number Diff line
@@ -136,3 +136,14 @@ flag {
         purpose: PURPOSE_BUGFIX
     }
}

flag {
     namespace: "backstage_power"
     name: "rate_limit_get_my_memory_state"
     description: "Rate limit calls to getMyMemoryState using a cache"
     is_fixed_read_only: true
     bug: "365182205"
     metadata {
         purpose: PURPOSE_BUGFIX
     }
}
+10 −17
Original line number Diff line number Diff line
@@ -4121,19 +4121,6 @@ public final class ProcessList {
        return false;
    }

    private static int procStateToImportance(int procState, int memAdj,
            ActivityManager.RunningAppProcessInfo currApp,
            int clientTargetSdk) {
        int imp = ActivityManager.RunningAppProcessInfo.procStateToImportanceForTargetSdk(
                procState, clientTargetSdk);
        if (imp == ActivityManager.RunningAppProcessInfo.IMPORTANCE_BACKGROUND) {
            currApp.lru = memAdj;
        } else {
            currApp.lru = 0;
        }
        return imp;
    }

    @GuardedBy(anyOf = {"mService", "mProcLock"})
    void fillInProcMemInfoLOSP(ProcessRecord app,
            ActivityManager.RunningAppProcessInfo outInfo,
@@ -4151,14 +4138,20 @@ public final class ProcessList {
        }
        outInfo.lastTrimLevel = app.mProfile.getTrimMemoryLevel();
        final ProcessStateRecord state = app.mState;
        int adj = state.getCurAdj();
        int procState = state.getCurProcState();
        outInfo.importance = procStateToImportance(procState, adj, outInfo,
                clientTargetSdk);
        final int procState = state.getCurProcState();
        outInfo.importance = ActivityManager.RunningAppProcessInfo
                                .procStateToImportanceForTargetSdk(procState, clientTargetSdk);
        if (outInfo.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_BACKGROUND) {
            outInfo.lru = state.getCurAdj();
        } else {
            outInfo.lru = 0;
        }
        outInfo.importanceReasonCode = state.getAdjTypeCode();
        outInfo.processState = procState;
        outInfo.isFocused = (app == mService.getTopApp());
        outInfo.lastActivityTime = app.getLastActivityTime();
        // Note: ActivityManager$RunningAppProcessInfo.copyTo() must be updated if what gets
        // "filled into" outInfo in this method changes.
    }

    @GuardedBy(anyOf = {"mService", "mProcLock"})