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

Commit 51e01e9f authored by Yasin Kilicdere's avatar Yasin Kilicdere
Browse files

Preserve the LRU positions of its processes when an app is resumed.

When a multiprocess app is sent to background and brought back, the
relative positions of its processes in the process LRU list was being
scrambled due to the connections being stored in an ArraySet, based on
the object's hash value, which is random. With this CL,
updateLruProcessInternalLSP method is split into two stages
(offerLruProcessInternalLSP and completeLruProcessInternalLSP) having
a priority queue in between that stores and sorts the lrui (lru index)
to preserve the positions of the processes in the LRU list relative to
each other. Otherwise, when there is memory pressure, LMKD (Low Memory
Killer Daemon) was killing these processes in an undesired order.

Bug: 359930209
Change-Id: Ia6270dc779dcf959a7c58c836c28a0155dee5215
Test: atest CtsAppTestCases:ServiceTest#testActivityServiceBindingLru
Flag: EXEMPT bugfix
parent 3b0072a7
Loading
Loading
Loading
Loading
+63 −31
Original line number Diff line number Diff line
@@ -3629,42 +3629,68 @@ public final class ProcessList {
    }

    @GuardedBy({"mService", "mProcLock"})
    private int updateLruProcessInternalLSP(ProcessRecord app, long now, int index,
            int lruSeq, String what, Object obj, ProcessRecord srcApp) {
    private int offerLruProcessInternalLSP(ProcessRecord app, long now, String what, Object obj,
            ProcessRecord srcApp) {
        app.setLastActivityTime(now);

        if (app.hasActivitiesOrRecentTasks()) {
            // Don't want to touch dependent processes that are hosting activities.
            return index;
            return -1;
        }

        int lrui = mLruProcesses.lastIndexOf(app);
        final int lrui = mLruProcesses.lastIndexOf(app);
        if (lrui < 0) {
            Slog.wtf(TAG, "Adding dependent process " + app + " not on LRU list: "
                    + what + " " + obj + " from " + srcApp);
            return index;
        }
        return lrui;
    }

    /**
     * This method is called after the indices array is populated by the indices offered by
     * {@link #offerLruProcessInternalLSP} to actually move the processes to the desired locations
     * in the LRU list. Since the indices array is a SparseBooleanArray, the indices are sorted
     * and this allows us to preserve the previous order of the processes relative to each other.
     * Key of the indices array holds the current index of the process in the LRU list and the value
     * is a boolean indicating whether the process is an activity process or not. Activity processes
     * are moved to the nextActivityIndex and non-activity processes are moved to the nextIndex
     * positions, which are provided by the caller.
     *
     * @param indices The indices of the processes to move.
     * @param nextActivityIndex The next index to insert an activity process.
     * @param nextIndex The next index to insert a non-activity process.
     */
    @GuardedBy({"mService", "mProcLock"})
    private void completeLruProcessInternalLSP(SparseBooleanArray indices, int nextActivityIndex,
            int nextIndex) {
        for (int i = indices.size() - 1; i >= 0; i--) {
            final int lrui = indices.keyAt(i);
            if (lrui < 0) {
                // Rest of the indices are invalid, we can return early.
                return;
            }
            final boolean isActivity = indices.valueAt(i);
            int index = isActivity ? nextActivityIndex : nextIndex;

            if (lrui >= index) {
                // Don't want to cause this to move dependent processes *back* in the
                // list as if they were less frequently used.
            return index;
        }

        if (lrui >= mLruProcessActivityStart && index < mLruProcessActivityStart) {
            // Don't want to touch dependent processes that are hosting activities.
            return index;
                continue;
            }

        mLruProcesses.remove(lrui);
        if (index > 0) {
            final ProcessRecord app = mLruProcesses.remove(lrui);
            index--;
        }
            if (DEBUG_LRU) Slog.d(TAG_LRU, "Moving dep from " + lrui + " to " + index
                    + " in LRU list: " + app);
            mLruProcesses.add(index, app);
        app.setLruSeq(lruSeq);
        return index;
            app.setLruSeq(mLruSeq);

            if (isActivity) {
                nextActivityIndex = index;
            } else {
                nextIndex = index;
            }
        }
    }

    /**
@@ -4058,6 +4084,15 @@ public final class ProcessList {

        app.setLruSeq(mLruSeq);

        // Key of the indices array holds the current index of the process in the LRU list and the
        // value is a boolean indicating whether the process is an activity process or not.
        // Activity processes will be moved to the nextActivityIndex and non-activity processes will
        // be moved to the nextIndex positions when completeLruProcessInternalLSP is called.
        // Since SparseBooleanArray's keys are sorted, we'll be able to keep the existing order of
        // the processes relative to each other after the move.
        final SparseBooleanArray indices = new SparseBooleanArray(psr.numberOfConnections()
                + app.mProviders.numberOfProviderConnections());

        // If the app is currently using a content provider or service,
        // bump those processes as well.
        for (int j = psr.numberOfConnections() - 1; j >= 0; j--) {
@@ -4069,16 +4104,12 @@ public final class ProcessList {
                    && !cr.binding.service.app.isPersistent()) {
                if (cr.binding.service.app.mServices.hasClientActivities()) {
                    if (nextActivityIndex >= 0) {
                        nextActivityIndex = updateLruProcessInternalLSP(cr.binding.service.app,
                                now,
                                nextActivityIndex, mLruSeq,
                                "service connection", cr, app);
                        indices.append(offerLruProcessInternalLSP(cr.binding.service.app, now,
                                "service connection", cr, app), true);
                    }
                } else {
                    nextIndex = updateLruProcessInternalLSP(cr.binding.service.app,
                            now,
                            nextIndex, mLruSeq,
                            "service connection", cr, app);
                    indices.append(offerLruProcessInternalLSP(cr.binding.service.app, now,
                            "service connection", cr, app), false);
                }
            }
        }
@@ -4086,10 +4117,11 @@ public final class ProcessList {
        for (int j = ppr.numberOfProviderConnections() - 1; j >= 0; j--) {
            ContentProviderRecord cpr = ppr.getProviderConnectionAt(j).provider;
            if (cpr.proc != null && cpr.proc.getLruSeq() != mLruSeq && !cpr.proc.isPersistent()) {
                nextIndex = updateLruProcessInternalLSP(cpr.proc, now, nextIndex, mLruSeq,
                        "provider reference", cpr, app);
                indices.append(offerLruProcessInternalLSP(cpr.proc, now,
                        "provider reference", cpr, app), false);
            }
        }
        completeLruProcessInternalLSP(indices, nextActivityIndex, nextIndex);
    }

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