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

Commit e1a2f7b4 authored by Yasin Kilicdere's avatar Yasin Kilicdere
Browse files

Fix: Improve vis oom scores in split screen and desktop windowing.

Previously, visible applications in desktop windowing mode received a
`vis` oom score, with their Z-index (`vis+X`) added to prioritize
foreground apps. This was effective for single-window, single-process
applications, making those above others less likely to be targeted by
the Low Memory Killer Daemon (lmkd).

However, this approach fell short for multi-window and multi-process
applications (e.g., browsers). Without a direct mapping between an app's
activities and its various processes, assigning a correct `vis+X` oom
score to unfocused windows of an app with a focused window was
impossible. This led to unexpected and suboptimal killing behavior.

This change addresses the issue by laddering the oom scores of processes
within the `vis` range based on their relative positions in the process
LRU (Least Recently Used) list. The most recently used process now
receives a `vis+0` score, and subsequent processes receive `vis+X`
(where X is their position in the LRU). This leverages the existing
mechanism where processes are moved to the top of the LRU list when
focused, and allows applications to modify binding flags based on
`onWindowFocusChanged` events, ensuring consistent and correct oom score
assignments across all processes of a multi-window app.

Bug: 421872956
Test: atest MockingOomAdjusterTests
Flag: com.android.server.am.oomadjuster_vis_laddering
Flag: com.android.server.am.remove_lru_spam_prevention
Change-Id: Ie5436154bdd7423501ceb8f7d2118e6fdcfb0a61
parent f613adca
Loading
Loading
Loading
Loading
+24 −6
Original line number Diff line number Diff line
@@ -104,6 +104,7 @@ import static com.android.server.am.ProcessList.SERVICE_ADJ;
import static com.android.server.am.ProcessList.TAG_PROCESS_OBSERVERS;
import static com.android.server.am.ProcessList.UNKNOWN_ADJ;
import static com.android.server.am.ProcessList.VISIBLE_APP_ADJ;
import static com.android.server.am.ProcessList.VISIBLE_APP_MAX_ADJ;
import static com.android.server.am.psc.PlatformCompatCache.CACHED_COMPAT_CHANGE_USE_SHORT_FGS_USAGE_INTERACTION_TIME;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_SWITCH;
import static com.android.server.wm.WindowProcessController.ACTIVITY_STATE_FLAG_IS_PAUSING_OR_PAUSED;
@@ -950,6 +951,7 @@ public abstract class OomAdjuster {
    @GuardedBy({"mService", "mProcLock"})
    protected void applyLruAdjust(ArrayList<ProcessRecord> lruList) {
        final int numLru = lruList.size();
        int nextVisibleAppAdj = VISIBLE_APP_ADJ;
        int nextPreviousAppAdj = PREVIOUS_APP_ADJ;
        if (mConstants.USE_TIERED_CACHED_ADJ) {
            final long now = mInjector.getUptimeMillis();
@@ -961,7 +963,10 @@ public abstract class OomAdjuster {
                final ProcessStateRecord state = app.mState;
                final ProcessCachedOptimizerRecord opt = app.mOptRecord;
                final int curAdj = state.getCurAdj();
                if (PREVIOUS_APP_ADJ <= curAdj && curAdj <= PREVIOUS_APP_MAX_ADJ) {
                if (VISIBLE_APP_ADJ <= curAdj && curAdj <= VISIBLE_APP_MAX_ADJ) {
                    state.setCurAdj(nextVisibleAppAdj);
                    nextVisibleAppAdj = Math.min(nextVisibleAppAdj + 1, VISIBLE_APP_MAX_ADJ);
                } else if (PREVIOUS_APP_ADJ <= curAdj && curAdj <= PREVIOUS_APP_MAX_ADJ) {
                    state.setCurAdj(nextPreviousAppAdj);
                    nextPreviousAppAdj = Math.min(nextPreviousAppAdj + 1, PREVIOUS_APP_MAX_ADJ);
                } else if (!app.isKilledByAm() && app.getThread() != null && (curAdj >= UNKNOWN_ADJ
@@ -1033,7 +1038,10 @@ public abstract class OomAdjuster {
                ProcessRecord app = lruList.get(i);
                final ProcessStateRecord state = app.mState;
                final int curAdj = state.getCurAdj();
                if (PREVIOUS_APP_ADJ <= curAdj && curAdj <= PREVIOUS_APP_MAX_ADJ) {
                if (VISIBLE_APP_ADJ <= curAdj && curAdj <= VISIBLE_APP_MAX_ADJ) {
                    state.setCurAdj(nextVisibleAppAdj);
                    nextVisibleAppAdj = Math.min(nextVisibleAppAdj + 1, VISIBLE_APP_MAX_ADJ);
                } else if (PREVIOUS_APP_ADJ <= curAdj && curAdj <= PREVIOUS_APP_MAX_ADJ) {
                    state.setCurAdj(nextPreviousAppAdj);
                    nextPreviousAppAdj = Math.min(nextPreviousAppAdj + 1, PREVIOUS_APP_MAX_ADJ);
                } else if (!app.isKilledByAm() && app.getThread() != null
@@ -1568,11 +1576,21 @@ public abstract class OomAdjuster {
                onOtherActivity(ts);
            }

            if (Flags.oomadjusterVisLaddering() && Flags.removeLruSpamPrevention()) {
                // Do nothing.
                // When these flags are enabled, processes within the vis oom score range will
                // have vis+X oom scores according their position in the LRU list with respect
                // to the other vis processes, rather than their activity's taskLayer, which is
                // not handling all the cases for apps with multi-activities and multi-processes,
                // because there is no direct connection between the activities and bindings
                // (processes) of an app.
            } else {
                if (mAdj == ProcessList.VISIBLE_APP_ADJ) {
                    final int taskLayer = flags & ACTIVITY_STATE_FLAG_MASK_MIN_TASK_LAYER;
                    final int minLayer = Math.min(ProcessList.VISIBLE_APP_LAYER_MAX, taskLayer);
                    mAdj += minLayer;
                }
            }

            mState.setCachedAdj(mAdj);
            mState.setCachedForegroundActivities(mForegroundActivities);
+7 −2
Original line number Diff line number Diff line
@@ -84,6 +84,7 @@ import static com.android.server.am.ProcessList.SERVICE_B_ADJ;
import static com.android.server.am.ProcessList.SYSTEM_ADJ;
import static com.android.server.am.ProcessList.UNKNOWN_ADJ;
import static com.android.server.am.ProcessList.VISIBLE_APP_ADJ;
import static com.android.server.am.ProcessList.VISIBLE_APP_MAX_ADJ;
import static com.android.server.am.psc.PlatformCompatCache.CACHED_COMPAT_CHANGE_CAMERA_MICROPHONE_CAPABILITY;
import static com.android.server.am.psc.PlatformCompatCache.CACHED_COMPAT_CHANGE_PROCESS_CAPABILITY;

@@ -864,8 +865,12 @@ public class OomAdjusterImpl extends OomAdjuster {
            final int curAdj = state.getCurAdj();
            // Processes assigned the PREV oomscore will have a laddered oomscore with respect to
            // their positions in the LRU list. i.e. prev+0, prev+1, prev+2, etc.
            final boolean isPrevApp = PREVIOUS_APP_ADJ <= curAdj && curAdj <= PREVIOUS_APP_MAX_ADJ;
            if (curAdj >= UNKNOWN_ADJ || (Flags.oomadjusterPrevLaddering() && isPrevApp)) {
            final boolean isPrevApp = Flags.oomadjusterPrevLaddering()
                    && PREVIOUS_APP_ADJ <= curAdj && curAdj <= PREVIOUS_APP_MAX_ADJ;
            final boolean isVisApp = Flags.oomadjusterVisLaddering()
                    && Flags.removeLruSpamPrevention()
                    && VISIBLE_APP_ADJ <= curAdj && curAdj <= VISIBLE_APP_MAX_ADJ;
            if (curAdj >= UNKNOWN_ADJ || isPrevApp || isVisApp) {
                needLruAdjust = true;
            }
        }
+3 −0
Original line number Diff line number Diff line
@@ -266,6 +266,9 @@ public final class ProcessList {
    // This is a process only hosting activities that are visible to the
    // user, so we'd prefer they don't disappear.
    public static final int VISIBLE_APP_ADJ = 100;
    public static final int VISIBLE_APP_MAX_ADJ = Flags.oomadjusterVisLaddering()
            && Flags.removeLruSpamPrevention() ? 199 : 100;

    static final int VISIBLE_APP_LAYER_MAX = PERCEPTIBLE_APP_ADJ - VISIBLE_APP_ADJ - 1;

    // This is a process that was recently TOP and moved to FGS. Continue to treat it almost
+8 −0
Original line number Diff line number Diff line
@@ -99,6 +99,14 @@ flag {
    bug: "359912586"
}

flag {
    name: "oomadjuster_vis_laddering"
    namespace: "backstage_power"
    is_fixed_read_only: true
    description: "Add +X to the vis scores according to their positions in the process LRU list"
    bug: "421872956"
}

flag {
    name: "fix_apply_oomadj_order"
    namespace: "backstage_power"
+57 −3
Original line number Diff line number Diff line
@@ -75,6 +75,7 @@ import static com.android.server.am.ProcessList.SERVICE_B_ADJ;
import static com.android.server.am.ProcessList.SYSTEM_ADJ;
import static com.android.server.am.ProcessList.UNKNOWN_ADJ;
import static com.android.server.am.ProcessList.VISIBLE_APP_ADJ;
import static com.android.server.am.ProcessList.VISIBLE_APP_MAX_ADJ;
import static com.android.server.wm.WindowProcessController.ACTIVITY_STATE_FLAG_IS_PAUSING_OR_PAUSED;
import static com.android.server.wm.WindowProcessController.ACTIVITY_STATE_FLAG_IS_STOPPING;
import static com.android.server.wm.WindowProcessController.ACTIVITY_STATE_FLAG_IS_STOPPING_FINISHING;
@@ -1160,7 +1161,7 @@ public class MockingOomAdjusterTests {

        // Bind to many services from that previous activity and populated an LRU list.
        for (int i = 0; i < numberOfApps - 1; i++) {
            apps[i] = makeDefaultProcessRecord(MOCKAPP_PID + i, MOCKAPP_UID + i,
            apps[i] = makeDefaultProcessRecord(MOCKAPP_PID + i + 1, MOCKAPP_UID + i + 1,
                    MOCKAPP_PROCESSNAME + i, MOCKAPP_PACKAGENAME + i, false);
            bindService(apps[i], previous,
                    null, null, Context.BIND_IMPORTANT, mock(IBinder.class));
@@ -1206,6 +1207,58 @@ public class MockingOomAdjusterTests {
        }
    }

    @SuppressWarnings("GuardedBy")
    @Test
    @EnableFlags({Flags.FLAG_REMOVE_LRU_SPAM_PREVENTION, Flags.FLAG_OOMADJUSTER_VIS_LADDERING})
    public void testUpdateOomAdj_DoPending_VisibleApp() {
        testUpdateOomAdj_VisibleApp(apps -> {
            for (ProcessRecord app : apps) {
                mProcessStateController.enqueueUpdateTarget(app);
            }
            mProcessStateController.runPendingUpdate(OOM_ADJ_REASON_NONE);
        });
    }

    @SuppressWarnings("GuardedBy")
    @Test
    @EnableFlags({Flags.FLAG_REMOVE_LRU_SPAM_PREVENTION, Flags.FLAG_OOMADJUSTER_VIS_LADDERING})
    public void testUpdateOomAdj_DoAll_VisibleApp() {
        testUpdateOomAdj_VisibleApp(apps -> {
            mProcessStateController.runFullUpdate(OOM_ADJ_REASON_NONE);
        });
    }

    private void testUpdateOomAdj_VisibleApp(Consumer<ProcessRecord[]> updater) {
        final int numberOfApps = 105;
        final ProcessRecord[] apps = new ProcessRecord[numberOfApps];
        // Create an activity that has recently been backgrounded.
        final ProcessRecord visible = makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
                MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true);
        setTopProcess(visible);
        setHasActivity(visible.getWindowProcessController(), true);

        // Bind to many services from that previous activity and populated an LRU list.
        for (int i = 0; i < numberOfApps - 1; i++) {
            apps[i] = makeDefaultProcessRecord(MOCKAPP_PID + i + 1, MOCKAPP_UID + i + 1,
                    MOCKAPP_PROCESSNAME + i, MOCKAPP_PACKAGENAME + i, false);
            bindService(apps[i], visible,
                    null, null, Context.BIND_AUTO_CREATE, mock(IBinder.class));
        }
        // Set the most recently used spot as the activity.
        apps[numberOfApps - 1] = visible;
        setWakefulness(PowerManagerInternal.WAKEFULNESS_AWAKE);
        setProcessesToLru(apps);
        updater.accept(apps);
        assertProcStates(visible, PROCESS_STATE_TOP, FOREGROUND_APP_ADJ,
                SCHED_GROUP_TOP_APP, "top-activity");
        for (int i = 0; i < numberOfApps - 1; i++) {
            final int mruIndex = numberOfApps - i - 2;
            final int expectedAdj = Math.min(VISIBLE_APP_ADJ + mruIndex, VISIBLE_APP_MAX_ADJ);
            assertProcStates(apps[i], PROCESS_STATE_BOUND_TOP, expectedAdj,
                    SCHED_GROUP_DEFAULT, "service");
        }
    }

    @SuppressWarnings("GuardedBy")
    @Test
    public void testUpdateOomAdj_DoOne_Backup() {
@@ -2462,8 +2515,9 @@ public class MockingOomAdjusterTests {
        bindService(app1, client2, null, null, 0, mock(IBinder.class));
        mProcessStateController.setHasForegroundServices(client2.mServices, false, 0, false);
        updateOomAdj(app1, client1, client2);
        assertProcStates(app1, PROCESS_STATE_IMPORTANT_FOREGROUND, VISIBLE_APP_ADJ,
                SCHED_GROUP_TOP_APP);
        assertProcStates(app1, PROCESS_STATE_IMPORTANT_FOREGROUND, VISIBLE_APP_ADJ
                        + (Flags.oomadjusterVisLaddering() && Flags.removeLruSpamPrevention()
                        ? 1 : 0), SCHED_GROUP_TOP_APP);
    }

    @SuppressWarnings("GuardedBy")