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

Commit ce92cdfa authored by Shashi Shekar Shankar's avatar Shashi Shekar Shankar Committed by Bruno Martins
Browse files

Performance: Memory Optimizations.

The following optimizations are squashed in this change.

1. Enable Aggressive trim settings.

This change will enable aggressive trim settings for targets
up to 1GB. The change can be turned on/off from system properties.
By default, the properties are set for targets up to 1GB

Changes spread in to : ActivityManagerConstants.java

2. Propagate B-services to higher adj.

Depending on the inactivity of a service, move the B-services to
highest adj 906. Under memory pressure, these services will
be killed first ahead of cached apps which results in better
concurrency numbers with bg apps. Inactivity time and minumum
no of services to be maintained are configurable as system properties.
Improves concurrency.

Changes spread in to : ActivityManagerService.java

3. Postpone service restart during app launch.

In the android framework, when the service gets killed,
it will be rescheduled. Postpone the service restart if
the app is in process of startup. By Default this feature
is disabled. It can be enabled from build.prop of target.

Changes spread in to : ActiveServices.java

4. Performance: Make Cached apps limit configurable.

Set it via a target specific property
file. Allows flexibility to set the limit
based on device config.

Normally, setting a smaller value for
low-end device helps with performance.

Changes spread in to : ActivityManagerConstants.java

5. am: Add new flag to detect activity "launching" state

Use this flag to determine service re-schdelue delay and
exclude persistent services from delay.

CRs-Fixed: 783020 792122 879756 911145 2111356

Squash of following change-IDs:
    Change-Id: I233dddbff07e7ec1fe2ee96402fe1d411903beb5
    Change-Id: Ied6cfcc3d11951f32f18de680b0e3483db8e163e
    Change-Id: Ia86edf027e20df2c7aa6f34e3aa293f7a8e6f1fa
    Change-Id: I90a733d7134d86efffadf6815ce3d2944dd09419
    Change-Id: Id38d536ff375b57d69f5ca73265f62e85740fc04

Change-Id: I3b2110743a6e0dd1379aa3b7703b3a897db3f6e6
parent 5ef7a9bb
Loading
Loading
Loading
Loading
+72 −1
Original line number Diff line number Diff line
@@ -127,6 +127,10 @@ public final class ActiveServices {
    // at the same time.
    final int mMaxStartingBackground;

    // Flag to reschedule the services during app launch. Disable by default.
    private static final boolean SERVICE_RESCHEDULE
            = SystemProperties.getBoolean("ro.vendor.qti.am.reschedule_service", false);

    final SparseArray<ServiceMap> mServiceMap = new SparseArray<>();

    /**
@@ -2143,6 +2147,13 @@ public final class ActiveServices {
                        r.pendingStarts.add(0, si);
                        long dur = SystemClock.uptimeMillis() - si.deliveredTime;
                        dur *= 2;
                        if (SERVICE_RESCHEDULE && DEBUG_DELAYED_SERVICE) {
                            Slog.w(TAG, "Can add more delay !!!"
                                    + " si.deliveredTime " + si.deliveredTime + " dur " + dur
                                    + " si.deliveryCount " + si.deliveryCount
                                    + " si.doneExecutingCount " + si.doneExecutingCount
                                    + " allowCancel " + allowCancel);
                        }
                        if (minDuration < dur) minDuration = dur;
                        if (resetTime < dur) resetTime = dur;
                    } else {
@@ -2155,6 +2166,13 @@ public final class ActiveServices {
            }

            r.totalRestartCount++;
            if (SERVICE_RESCHEDULE && DEBUG_DELAYED_SERVICE) {
                Slog.w(TAG, "r.name " + r.name + " N " + N + " minDuration " + minDuration
                        + " resetTime " + resetTime + " now " + now
                        + " r.restartDelay " + r.restartDelay
                        + " r.restartTime+resetTime " + (r.restartTime + resetTime)
                        + " allowCancel " + allowCancel);
            }
            if (r.restartDelay == 0) {
                r.restartCount++;
                r.restartDelay = minDuration;
@@ -2179,6 +2197,14 @@ public final class ActiveServices {
            }

            r.nextRestartTime = now + r.restartDelay;
            if (SERVICE_RESCHEDULE && DEBUG_DELAYED_SERVICE) {
                Slog.w(TAG, "r.name " + r.name + " N " + N + " minDuration " + minDuration
                        + " resetTime " + resetTime + " now " + now
                        + " r.restartDelay " + r.restartDelay
                        + " r.restartTime+resetTime " + (r.restartTime + resetTime)
                        + " r.nextRestartTime " + r.nextRestartTime
                        + " allowCancel " + allowCancel);
            }

            // Make sure that we don't end up restarting a bunch of services
            // all at the same time.
@@ -2220,6 +2246,15 @@ public final class ActiveServices {
        r.nextRestartTime = SystemClock.uptimeMillis() + r.restartDelay;
        Slog.w(TAG, "Scheduling restart of crashed service "
                + r.shortName + " in " + r.restartDelay + "ms");

        if (SERVICE_RESCHEDULE && DEBUG_DELAYED_SERVICE) {
            for (int i = mRestartingServices.size() - 1; i >= 0; i--) {
                ServiceRecord r2 = mRestartingServices.get(i);
                Slog.w(TAG, "Restarting list - i " + i
                        + " r2.nextRestartTime " + r2.nextRestartTime + " r2.name " + r2.name);
            }
        }

        EventLog.writeEvent(EventLogTags.AM_SCHEDULE_SERVICE_RESTART,
                r.userId, r.shortName, r.restartDelay);

@@ -2240,7 +2275,38 @@ public final class ActiveServices {
            return;
        }
        try {
            bringUpServiceLocked(r, r.intent.getIntent().getFlags(), r.createdFromFg, true, false);
            if (SERVICE_RESCHEDULE) {
                boolean shouldDelay = false;
                ActivityRecord top_rc = null;
                ActivityStack stack = mAm.getFocusedStack();

                if (stack != null) {
                    top_rc = stack.topRunningActivityLocked();
                }

                final boolean isPersistent = (r.serviceInfo.applicationInfo.flags &
                        ApplicationInfo.FLAG_PERSISTENT) != 0;
                if (top_rc != null) {
                    if (top_rc.launching && !r.shortName.contains(top_rc.packageName)
                            && !isPersistent) {
                        shouldDelay = true;
                    }
                }
                if (!shouldDelay) {
                    bringUpServiceLocked(r, r.intent.getIntent().getFlags(), r.createdFromFg,
                            true, false);
                } else {
                    if (DEBUG_DELAYED_SERVICE) {
                        Slog.v(TAG, "Reschedule service restart due to app launch"
                                + " r.shortName " + r.shortName + " r.app = " + r.app);
                    }
                    r.resetRestartCounter();
                    scheduleServiceRestartLocked(r, true);
                }
            } else {
                bringUpServiceLocked(r, r.intent.getIntent().getFlags(), r.createdFromFg,
                        true, false);
            }
        } catch (TransactionTooLargeException e) {
            // Ignore, it's been logged and nothing upstack cares.
        }
@@ -2481,6 +2547,11 @@ public final class ActiveServices {
                if (newService) {
                    app.services.remove(r);
                    r.app = null;
                    if (SERVICE_RESCHEDULE && DEBUG_DELAYED_SERVICE) {
                        Slog.w(TAG, "Failed to create Service !!!"
                                + " This will introduce huge delay... "
                                + r.shortName + " in " + r.restartDelay + "ms");
                    }
                }

                // Retry.
+43 −4
Original line number Diff line number Diff line
@@ -20,6 +20,8 @@ import android.content.ContentResolver;
import android.database.ContentObserver;
import android.net.Uri;
import android.os.Handler;
import android.os.Process;
import android.os.SystemProperties;
import android.provider.Settings;
import android.util.KeyValueListParser;
import android.util.Slog;
@@ -69,7 +71,8 @@ final class ActivityManagerConstants extends ContentObserver {
    static final String KEY_PROCESS_START_ASYNC = "process_start_async";
    static final String KEY_TOP_TO_FGS_GRACE_DURATION = "top_to_fgs_grace_duration";

    private static final int DEFAULT_MAX_CACHED_PROCESSES = 32;
    private static final int DEFAULT_MAX_CACHED_PROCESSES =
            SystemProperties.getInt("ro.vendor.qti.sys.fw.bg_apps_limit", 32);
    private static final long DEFAULT_BACKGROUND_SETTLE_TIME = 60*1000;
    private static final long DEFAULT_FGSERVICE_MIN_SHOWN_TIME = 2*1000;
    private static final long DEFAULT_FGSERVICE_MIN_REPORT_TIME = 3*1000;
@@ -233,6 +236,17 @@ final class ActivityManagerConstants extends ContentObserver {
    // process limit.
    public int CUR_MAX_CACHED_PROCESSES;

    static final boolean USE_TRIM_SETTINGS =
            SystemProperties.getBoolean("ro.vendor.qti.sys.fw.use_trim_settings", true);
    static final int EMPTY_APP_PERCENT =
            SystemProperties.getInt("ro.vendor.qti.sys.fw.empty_app_percent", 50);
    static final int TRIM_EMPTY_PERCENT =
            SystemProperties.getInt("ro.vendor.qti.sys.fw.trim_empty_percent", 100);
    static final int TRIM_CACHE_PERCENT =
            SystemProperties.getInt("ro.vendor.qti.sys.fw.trim_cache_percent", 100);
    static final long TRIM_ENABLE_MEMORY =
            SystemProperties.getLong("ro.vendor.qti.sys.fw.trim_enable_memory",1073741824);

    // The maximum number of empty app processes we will let sit around.
    public int CUR_MAX_EMPTY_PROCESSES;

@@ -274,8 +288,12 @@ final class ActivityManagerConstants extends ContentObserver {
    }

    public static int computeEmptyProcessLimit(int totalProcessLimit) {
        if (USE_TRIM_SETTINGS && allowTrim()) {
            return totalProcessLimit * EMPTY_APP_PERCENT / 100;
        } else {
            return totalProcessLimit / 2;
        }
    }

    @Override
    public void onChange(boolean selfChange, Uri uri) {
@@ -374,8 +392,9 @@ final class ActivityManagerConstants extends ContentObserver {
        // to consider the same level the point where we do trimming regardless of any
        // additional enforced limit.
        final int rawMaxEmptyProcesses = computeEmptyProcessLimit(MAX_CACHED_PROCESSES);
        CUR_TRIM_EMPTY_PROCESSES = rawMaxEmptyProcesses/2;
        CUR_TRIM_CACHED_PROCESSES = (MAX_CACHED_PROCESSES-rawMaxEmptyProcesses)/3;
        CUR_TRIM_EMPTY_PROCESSES = computeTrimEmptyApps(rawMaxEmptyProcesses);
        CUR_TRIM_CACHED_PROCESSES = computeTrimCachedApps(
                rawMaxEmptyProcesses, MAX_CACHED_PROCESSES);
    }

    void dump(PrintWriter pw) {
@@ -442,4 +461,24 @@ final class ActivityManagerConstants extends ContentObserver {
        pw.print("  CUR_TRIM_EMPTY_PROCESSES="); pw.println(CUR_TRIM_EMPTY_PROCESSES);
        pw.print("  CUR_TRIM_CACHED_PROCESSES="); pw.println(CUR_TRIM_CACHED_PROCESSES);
    }

    private static boolean allowTrim() {
        return Process.getTotalMemory() < TRIM_ENABLE_MEMORY;
    }

    private static int computeTrimEmptyApps(int rawMaxEmptyProcesses) {
        if (USE_TRIM_SETTINGS && allowTrim()) {
            return rawMaxEmptyProcesses * TRIM_EMPTY_PERCENT / 100;
        } else {
            return rawMaxEmptyProcesses / 2;
        }
    }

    private static int computeTrimCachedApps(int rawMaxEmptyProcesses, int totalProcessLimit) {
        if (USE_TRIM_SETTINGS && allowTrim()) {
            return totalProcessLimit * TRIM_CACHE_PERCENT / 100;
        } else {
            return (totalProcessLimit - rawMaxEmptyProcesses) / 3;
        }
    }
}
+49 −0
Original line number Diff line number Diff line
@@ -1951,6 +1951,16 @@ public class ActivityManagerService extends IActivityManager.Stub
    CompatModeDialog mCompatModeDialog;
    long mLastMemUsageReportTime = 0;
    // Min aging threshold in milliseconds to consider a B-service
    int mMinBServiceAgingTime =
            SystemProperties.getInt("ro.vendor.qti.sys.fw.bservice_age", 5000);
    // Threshold for B-services when in memory pressure
    int mBServiceAppThreshold =
            SystemProperties.getInt("ro.vendor.qti.sys.fw.bservice_limit", 5);
    // Enable B-service aging propagation on memory pressure.
    boolean mEnableBServicePropagation =
            SystemProperties.getBoolean("ro.vendor.qti.sys.fw.bservice_enable", false);
    /**
     * Flag whether the current user is a "monkey", i.e. whether
     * the UI is driven by a UI automation tool.
@@ -25064,6 +25074,9 @@ public class ActivityManagerService extends IActivityManager.Stub
        int nextCachedAdj = curCachedAdj+1;
        int curEmptyAdj = ProcessList.CACHED_APP_MIN_ADJ;
        int nextEmptyAdj = curEmptyAdj+2;
        ProcessRecord selectedAppRecord = null;
        long serviceLastActivity = 0;
        int numBServices = 0;
        boolean retryCycles = false;
@@ -25074,6 +25087,34 @@ public class ActivityManagerService extends IActivityManager.Stub
        }
        for (int i=N-1; i>=0; i--) {
            ProcessRecord app = mLruProcesses.get(i);
            if (mEnableBServicePropagation && app.serviceb
                    && (app.curAdj == ProcessList.SERVICE_B_ADJ)) {
                numBServices++;
                for (int s = app.services.size() - 1; s >= 0; s--) {
                    ServiceRecord sr = app.services.valueAt(s);
                    if (DEBUG_OOM_ADJ) Slog.d(TAG,"app.processName = " + app.processName
                            + " serviceb = " + app.serviceb + " s = " + s + " sr.lastActivity = "
                            + sr.lastActivity + " packageName = " + sr.packageName
                            + " processName = " + sr.processName);
                    if (SystemClock.uptimeMillis() - sr.lastActivity
                            < mMinBServiceAgingTime) {
                        if (DEBUG_OOM_ADJ) {
                            Slog.d(TAG,"Not aged enough!!!");
                        }
                        continue;
                    }
                    if (serviceLastActivity == 0) {
                        serviceLastActivity = sr.lastActivity;
                        selectedAppRecord = app;
                    } else if (sr.lastActivity < serviceLastActivity) {
                        serviceLastActivity = sr.lastActivity;
                        selectedAppRecord = app;
                    }
                }
            }
            if (DEBUG_OOM_ADJ && selectedAppRecord != null) Slog.d(TAG,
                    "Identified app.processName = " + selectedAppRecord.processName
                    + " app.pid = " + selectedAppRecord.pid);
            if (!app.killedByAm && app.thread != null) {
                app.procStateChanged = false;
                computeOomAdjLocked(app, ProcessList.UNKNOWN_ADJ, TOP_APP, true, now);
@@ -25228,6 +25269,14 @@ public class ActivityManagerService extends IActivityManager.Stub
                }
            }
        }
        if (numBServices > mBServiceAppThreshold && true == mAllowLowerMemLevel
                && selectedAppRecord != null) {
            ProcessList.setOomAdj(selectedAppRecord.pid, selectedAppRecord.info.uid,
                    ProcessList.CACHED_APP_MAX_ADJ);
            selectedAppRecord.setAdj = selectedAppRecord.curAdj;
            if (DEBUG_OOM_ADJ) Slog.d(TAG,"app.processName = " + selectedAppRecord.processName
                        + " app.pid = " + selectedAppRecord.pid + " is moved to higher adj");
        }
        incrementProcStateSeqAndNotifyAppsLocked();
+3 −0
Original line number Diff line number Diff line
@@ -312,6 +312,7 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo
    private boolean mDeferHidingClient; // If true we told WM to defer reporting to the client
                                        // process that it is hidden.
    boolean sleeping;       // have we told the activity to sleep?
    boolean launching;      // is activity launch in progress?
    boolean nowVisible;     // is this activity's window visible?
    boolean mClientVisibilityDeferred;// was the visibility change message to client deferred?
    boolean idle;           // has the activity gone idle?
@@ -2100,6 +2101,7 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo
            if (DEBUG_SWITCH) Log.v(TAG_SWITCH, "windowsVisibleLocked(): " + this);
            if (!nowVisible) {
                nowVisible = true;
                launching = false;
                lastVisibleTime = SystemClock.uptimeMillis();
                if (idle || mStackSupervisor.isStoppingNoHistoryActivity()) {
                    // If this activity was already idle or there is an activity that must be
@@ -2132,6 +2134,7 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo
        synchronized (service) {
            if (DEBUG_SWITCH) Log.v(TAG_SWITCH, "windowsGone(): " + this);
            nowVisible = false;
            launching = false;
        }
    }

+2 −0
Original line number Diff line number Diff line
@@ -2411,6 +2411,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
        mStackSupervisor.mGoingToSleepActivities.remove(next);
        next.sleeping = false;
        mStackSupervisor.mActivitiesWaitingForVisibleActivity.remove(next);
        next.launching = true;

        if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "Resuming " + next);

@@ -3455,6 +3456,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai

    final void stopActivityLocked(ActivityRecord r) {
        if (DEBUG_SWITCH) Slog.d(TAG_SWITCH, "Stopping: " + r);
        r.launching = false;
        if ((r.intent.getFlags()&Intent.FLAG_ACTIVITY_NO_HISTORY) != 0
                || (r.info.flags&ActivityInfo.FLAG_NO_HISTORY) != 0) {
            if (!r.finishing) {