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

Commit 716fa9b9 authored by Lee Shombert's avatar Lee Shombert
Browse files

freezer: add internal per-process callbacks

Add per-process frozen/unfrozen callbacks in ActivityManagerInternal
for use within system_server.  The callbacks are registered on a
per-process object; there is no way to unregister, so the callbacks
exist for the lifetime of the process.

Callbacks are executed when the frozen/unfrozen state changes.  Note
that if the callback is added with a HandlerExecutor, the callback
will be delivered to the end client some time later, possibly in a
different thread.

Test: atest
 * FrameworksServicesTests:com.android.server.am
 * FrameworksMockingServicesTests:com.android.server.am

Bug: 326315985
Flag: EXEMPT refactor
Change-Id: I17e8f26633f70f6d54c742d58ba23f9a20fa3f64
parent a23abe6b
Loading
Loading
Loading
Loading
+23 −0
Original line number Diff line number Diff line
@@ -55,6 +55,7 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Executor;
import java.util.function.BiFunction;

/**
@@ -1267,6 +1268,28 @@ public abstract class ActivityManagerInternal {
     */
    public abstract boolean shouldDelayHomeLaunch(int userId);

    /**
     * Used to track when a process is frozen or unfrozen.
     */
    public interface FrozenProcessListener {
        /**
         * Called when a process is frozen.
         */
        void onProcessFrozen(int pid);

        /**
         * Called when a process is unfrozen.
         */
        void onProcessUnfrozen(int pid);
    }

    /**
     * Register the frozen process event listener callback. The same listener may be reused for
     * multiple pids. Listeners are dropped when the process dies.
     */
    public abstract void addFrozenProcessListener(int pid, @NonNull Executor executor,
            @NonNull FrozenProcessListener listener);

    /**
     * Add a startup timestamp to the most recent start of the specified process.
     *
+17 −0
Original line number Diff line number Diff line
@@ -18443,6 +18443,23 @@ public class ActivityManagerService extends IActivityManager.Stub
    public final class LocalService extends ActivityManagerInternal
            implements ActivityManagerLocal {
        @Override
        public void addFrozenProcessListener(int pid, @NonNull Executor executor,
                @NonNull FrozenProcessListener listener) {
            Objects.requireNonNull(executor);
            Objects.requireNonNull(listener);
            synchronized (mProcLock) {
                final ProcessRecord app;
                synchronized (mPidsSelfLocked) {
                    app = mPidsSelfLocked.get(pid);
                }
                if (app != null) {
                    mOomAdjuster.mCachedAppOptimizer.addFrozenProcessListener(app, executor,
                            listener);
                }
            }
        }
        @Override
        public List<PendingIntentStats> getPendingIntentStats() {
            return mPendingIntentController.dumpPendingIntentStatsForStatsd();
+1 −2
Original line number Diff line number Diff line
@@ -1234,8 +1234,7 @@ final class ActivityManagerShellCommand extends ShellCommand {
                if (freeze) {
                    mInternal.mOomAdjuster.mCachedAppOptimizer.forceFreezeAppAsyncLSP(proc);
                } else {
                    mInternal.mOomAdjuster.mCachedAppOptimizer.unfreezeAppInternalLSP(proc, 0,
                            true);
                    mInternal.mOomAdjuster.mCachedAppOptimizer.unfreezeAppLSP(proc, 0, true);
                }
            }
        }
+40 −9
Original line number Diff line number Diff line
@@ -49,6 +49,7 @@ import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
import android.annotation.IntDef;
import android.annotation.UptimeMillisLong;
import android.app.ActivityManager;
import android.app.ActivityManagerInternal.FrozenProcessListener;
import android.app.ActivityManagerInternal.OomAdjReason;
import android.app.ActivityThread;
import android.app.ApplicationExitInfo;
@@ -99,6 +100,7 @@ import java.util.LinkedList;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import java.util.concurrent.Executor;

public final class CachedAppOptimizer {

@@ -1406,8 +1408,15 @@ public final class CachedAppOptimizer {
        }
    }

    /**
     * Returns true if the app was frozen and became unfrozen, otherwise false.
     *
     * Do not call this directly.  It will unfreeze a process but it will not send out any
     * notifications.  Instead call unfreezeAppLSP().
     */
    @GuardedBy({"mAm", "mProcLock", "mFreezerLock"})
    void unfreezeAppInternalLSP(ProcessRecord app, @UnfreezeReason int reason, boolean force) {
    private boolean unfreezeAppInternalLSP(ProcessRecord app, @UnfreezeReason int reason,
            boolean force) {
        final int pid = app.getPid();
        final ProcessCachedOptimizerRecord opt = app.mOptRecord;
        boolean sticky = opt.isFreezeSticky();
@@ -1418,7 +1427,7 @@ public final class CachedAppOptimizer {
                        "Skip unfreezing because frozen state is sticky pid=" + pid + " "
                                + app.processName);
            }
            return;
            return false;
        }
        boolean processFreezableChangeReported = false;
        if (opt.isPendingFreeze()) {
@@ -1440,7 +1449,7 @@ public final class CachedAppOptimizer {

        opt.setFreezerOverride(false);
        if (pid == 0 || !opt.isFrozen()) {
            return;
            return false;
        }

        // Unfreeze the binder interface first, to avoid transactions triggered by timers fired
@@ -1473,7 +1482,7 @@ public final class CachedAppOptimizer {
        }

        if (processKilled) {
            return;
            return false;
        }
        if (!processFreezableChangeReported) {
            reportProcessFreezableChangedLocked(app);
@@ -1489,7 +1498,7 @@ public final class CachedAppOptimizer {
            app.killLocked("Unable to unfreeze",
                    ApplicationExitInfo.REASON_FREEZER,
                    ApplicationExitInfo.SUBREASON_FREEZER_BINDER_IOCTL, true);
            return;
            return false;
        }

        try {
@@ -1512,16 +1521,27 @@ public final class CachedAppOptimizer {
                        pid,
                        (int) Math.min(opt.getFreezeUnfreezeTime() - freezeTime, Integer.MAX_VALUE),
                        new Pair<ProcessRecord, Integer>(app, reason)));
            return true;
        }
        return false;
    }

    @GuardedBy({"mAm", "mProcLock"})
    void unfreezeAppLSP(ProcessRecord app, @UnfreezeReason int reason) {
    void unfreezeAppLSP(ProcessRecord app, @UnfreezeReason int reason, boolean force) {
        final boolean shouldDispatch;
        synchronized (mFreezerLock) {
            unfreezeAppInternalLSP(app, reason, false);
            shouldDispatch = unfreezeAppInternalLSP(app, reason, force);
        }
        if (shouldDispatch) {
            app.mOptRecord.dispatchUnfrozenEvent();
        }
    }

    @GuardedBy({"mAm", "mProcLock"})
    void unfreezeAppLSP(ProcessRecord app, @UnfreezeReason int reason) {
        unfreezeAppLSP(app, reason, false);
    }

    /**
     * This quick function works around the race condition between WM/ATMS and AMS, allowing
     * the former to directly unfreeze a frozen process before the latter runs updateOomAdj.
@@ -1530,8 +1550,9 @@ public final class CachedAppOptimizer {
     * @param pid pid of the process to be unfrozen
     */
    void unfreezeProcess(int pid, @OomAdjReason int reason) {
        final ProcessRecord app;
        synchronized (mFreezerLock) {
            ProcessRecord app = mFrozenProcesses.get(pid);
            app = mFrozenProcesses.get(pid);
            if (app == null) {
                return;
            }
@@ -1550,6 +1571,7 @@ public final class CachedAppOptimizer {
                Slog.e(TAG_AM, "Unable to quick unfreeze " + pid);
            }
        }
        app.mOptRecord.dispatchUnfrozenEvent();
    }

    /**
@@ -2390,6 +2412,7 @@ public final class CachedAppOptimizer {
                    }
                });
            }
            opt.dispatchFrozenEvent();
        }

        private void reportUnfreeze(ProcessRecord app, int pid, int frozenDuration,
@@ -2549,7 +2572,7 @@ public final class CachedAppOptimizer {
                if (freeze) {
                    forceFreezeAppAsyncLSP(proc);
                } else {
                    unfreezeAppInternalLSP(proc, UNFREEZE_REASON_NONE, true);
                    unfreezeAppLSP(proc, UNFREEZE_REASON_NONE, true);
                }
            }
        }
@@ -2652,4 +2675,12 @@ public final class CachedAppOptimizer {
                exception -> Slog.e(TAG_AM, "Unable to parse binderfs stats"));
        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
    }

    /**
     * Register a callback to notify when a process's frozen state changes.
     */
    public void addFrozenProcessListener(ProcessRecord app, Executor executor,
            FrozenProcessListener listener) {
        app.mOptRecord.addFrozenProcessListener(executor, listener);
    }
}
+30 −0
Original line number Diff line number Diff line
@@ -19,6 +19,8 @@ package com.android.server.am;
import android.annotation.IntDef;
import android.annotation.UptimeMillisLong;
import android.app.ActivityManagerInternal.OomAdjReason;
import android.app.ActivityManagerInternal.FrozenProcessListener;
import android.util.Pair;
import android.util.TimeUtils;

import com.android.internal.annotations.GuardedBy;
@@ -29,6 +31,8 @@ import dalvik.annotation.optimization.NeverCompile;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.Executor;

/**
 * The state info of app when it's cached, used by the optimizer.
@@ -165,6 +169,12 @@ final class ProcessCachedOptimizerRecord {
    @GuardedBy("mProcLock")
    private long mLastUsedTimeout;

    /**
     * The list of callbacks for this process whenever it is frozen or unfrozen.
     */
    final CopyOnWriteArrayList<Pair<Executor, FrozenProcessListener>> mFrozenProcessListeners =
            new CopyOnWriteArrayList<>();

    @GuardedBy("mProcLock")
    long getLastCompactTime() {
        return mLastCompactTime;
@@ -386,6 +396,22 @@ final class ProcessCachedOptimizerRecord {
        mFreezeExempt = exempt;
    }

    void addFrozenProcessListener(Executor executor, FrozenProcessListener listener) {
        mFrozenProcessListeners.add(new Pair<Executor, FrozenProcessListener>(executor, listener));
    }

    void dispatchFrozenEvent() {
        mFrozenProcessListeners.forEach((pair) -> {
            pair.first.execute(() -> pair.second.onProcessFrozen(mApp.mPid));
        });
    }

    void dispatchUnfrozenEvent() {
        mFrozenProcessListeners.forEach((pair) -> {
            pair.first.execute(() -> pair.second.onProcessUnfrozen(mApp.mPid));
        });
    }

    ProcessCachedOptimizerRecord(ProcessRecord app) {
        mApp = app;
        mProcLock = app.mService.mProcLock;
@@ -409,6 +435,10 @@ final class ProcessCachedOptimizerRecord {
        pw.print(" " + IS_FROZEN + "="); pw.println(mFrozen);
        pw.print(prefix); pw.print("earliestFreezableTimeMs=");
        TimeUtils.formatDuration(mEarliestFreezableTimeMillis, nowUptime, pw);
        if (!mFrozenProcessListeners.isEmpty()) {
            pw.print(" mFrozenProcessListeners=");
            mFrozenProcessListeners.forEach((pair) -> pw.print(pair.second + ", "));
        }
        pw.println();
    }
}
Loading