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

Commit 25020a6f authored by Varad Deshmukh's avatar Varad Deshmukh Committed by Steve Kondik
Browse files

Delay Explicit GC from the SystemServer.

Binder activity as part of the System Server executes an Explicit
GC every 200 Binder references. Such Binder activity is significant
during the application launch of some Google Apps like Music.
The resulting pause due to the GC pauses the main application, affecting
launch time.

This change detects an app launch, and delays the incoming GC by
3 seconds. The API for GC delay can be calllable for different use-cases.
This change deals with only the application launch.

CRs-Fixed: 779174

Change-Id: I0f2dfaf3cb13940c77df8a6fefc2cbc0c76e027c
parent 1511e96f
Loading
Loading
Loading
Loading
+108 −0
Original line number Diff line number Diff line
@@ -23,6 +23,14 @@ import android.util.EventLog;
import java.lang.ref.WeakReference;
import java.util.ArrayList;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.FutureTask;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.lang.Object;
/**
 * Private and debugging Binder APIs.
 * 
@@ -34,6 +42,13 @@ public class BinderInternal {
    static ArrayList<Runnable> sGcWatchers = new ArrayList<>();
    static Runnable[] sTmpWatchers = new Runnable[1];
    static long sLastGcTime;
    /* Maximum duration a GC can be delayed. */
    private static final int GC_DELAY_MAX_DURATION = 3000;
    /**
     * Maximum number of times a GC can be delayed since the
     * original request.
     */
    private static final int POSTPONED_GC_MAX = 5;

    static final class GcWatcher {
        @Override
@@ -99,7 +114,100 @@ public class BinderInternal {
        Runtime.getRuntime().gc();
    }
    
    /**
     * TimerGc Callable : Wait for a certain time, and execute the BinderGc.
     * Set the postponed count to 0.
     */
    public static class TimerGc implements Callable<Void> {
        private long waitTime;
        public TimerGc(long timeInMillis){
            this.waitTime=timeInMillis;
        }
        @Override
        public Void call() throws Exception {
            Thread.sleep(waitTime);
            forceGc("Binder");
            postponedGcCount = 0;
            return null;
        }
    }

    /**
     * lastGcDelayRequestTime records the time-stamp of the last time
     * a GC delay request was made.
     */
    static long lastGcDelayRequestTime = SystemClock.uptimeMillis();
    static TimerGc timerGcInstance = null ;
    static FutureTask<Void> futureTaskInstance = null ;
    static ExecutorService executor = Executors.newFixedThreadPool(1);
    static int postponedGcCount = 0;
    static Object delayGcMonitorObject = new Object();

    /**
     * modifyDelayedGcParams : Call from the framework based on some special Ux event.
     * like appLaunch.
     *
     * 1. If this is the first time for the trigger event, or, if there is no scheduled
     *    task, create a new FutureTaskInstance, and set the lastGcDelayRequestTime.
     *    This will be used by forceBinderGc later.
     *
     * 2. If the postponed iterations hit a maximum limit, do nothing. Let the current
     *    task execute the gc. If not,
     *
     *    a. Set the start time.
     *    b. Increment the postponed count
     *    c. Cancel the current task and start a new one for GC_DELAY_MAX_DURATION.
     */
    public static void modifyDelayedGcParams() {
        long nowTime = SystemClock.uptimeMillis();
        synchronized(delayGcMonitorObject) {
            if ((futureTaskInstance != null) && (postponedGcCount != 0)) {
                if (postponedGcCount <= POSTPONED_GC_MAX) {
                    futureTaskInstance.cancel(true);
                    if (futureTaskInstance.isCancelled()) {
                        lastGcDelayRequestTime = nowTime;
                        postponedGcCount++;
                        timerGcInstance = new TimerGc(GC_DELAY_MAX_DURATION);
                        futureTaskInstance = new FutureTask<Void>(timerGcInstance);
                        executor.execute(futureTaskInstance);
                    }
                }
            } else {
                lastGcDelayRequestTime = nowTime;
                timerGcInstance = new TimerGc(GC_DELAY_MAX_DURATION);
                futureTaskInstance = new FutureTask<Void>(timerGcInstance);
            }
        }
    }

    /**
     * Modified forceBinderGc. The brief algorithm is as follows --
     *
     * 1. If no futureTaskInstance has been initiated, directly force a BinderGc.
     * 2. Check for the duration since the last request, and see if it was within the
     *    last GC_DELAY_MAX_DURATION secs. If yes, we need to delay the GC until
     *    GC_DELAY_MAX_DURATION.
     * 3. If there is a task scheduled (postponedGcCount != 0), we merely prevent this GC,
     *    and let the GC scheduled execute.
     * 4. If no task is scheduled, we schedule one now for (GC_DELAY_MAX_DURATION - touch duration),
     *    and update postponedGcCount.
     */
    static void forceBinderGc() {
        synchronized(delayGcMonitorObject) {
            if (futureTaskInstance != null) {
                long lastGcDelayRequestDuration = (SystemClock.uptimeMillis() - lastGcDelayRequestTime);
                if (lastGcDelayRequestDuration < GC_DELAY_MAX_DURATION) {
                    if (postponedGcCount != 0)
                        return;
                    futureTaskInstance.cancel(true);
                    timerGcInstance = new TimerGc(GC_DELAY_MAX_DURATION - lastGcDelayRequestDuration);
                    futureTaskInstance = new FutureTask<Void>(timerGcInstance);
                    postponedGcCount = 1;
                    executor.execute(futureTaskInstance);
                    return;
                }
            }
        }
        forceGc("Binder");
    }
}
+5 −0
Original line number Diff line number Diff line
@@ -103,6 +103,7 @@ import com.android.internal.widget.LockPatternUtils;
import com.android.server.LocalServices;
import com.android.server.am.ActivityStack.ActivityState;
import com.android.server.wm.WindowManagerService;
import com.android.internal.os.BinderInternal;


import java.io.FileDescriptor;
@@ -2641,6 +2642,10 @@ public final class ActivityStackSupervisor implements DisplayListener {
            }
        }
        mPm.cpuBoost(2000 * 1000);

        /* Delay Binder Explicit GC during application launch */
        BinderInternal.modifyDelayedGcParams();

        if (DEBUG_TASKS) Slog.d(TAG, "No task found");
        return null;
    }