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

Commit 301b97ac authored by Android (Google) Code Review's avatar Android (Google) Code Review
Browse files

Merge change 22883 into donut

* changes:
  Various tweaks to try to improve low memory behavior.
parents 7566c1de fd12af4e
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
@@ -1277,6 +1277,11 @@ public final class ViewRoot extends Handler implements ViewParent,
            // TODO: we should ask the window manager to do something!
            // for now we just do nothing
            return;
        } catch (IllegalArgumentException e) {
            Log.e("ViewRoot", "IllegalArgumentException locking surface", e);
            // TODO: we should ask the window manager to do something!
            // for now we just do nothing
            return;
        }

        try {
+0 −5
Original line number Diff line number Diff line
@@ -19,7 +19,6 @@ package com.android.server;
import com.android.server.am.ActivityManagerService;
import com.android.server.status.StatusBarService;

import dalvik.system.PathClassLoader;
import dalvik.system.VMRuntime;

import android.app.ActivityManagerNative;
@@ -32,7 +31,6 @@ import android.content.pm.IPackageManager;
import android.database.ContentObserver;
import android.database.Cursor;
import android.media.AudioService;
import android.os.IBinder;
import android.os.Looper;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -46,9 +44,6 @@ import android.server.search.SearchManagerService;
import android.util.EventLog;
import android.util.Log;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

class ServerThread extends Thread {
    private static final String TAG = "SystemServer";
    private final static boolean INCLUDE_DEMO = false;
+145 −32
Original line number Diff line number Diff line
@@ -237,6 +237,9 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
    // How long to wait after going idle before forcing apps to GC.
    static final int GC_TIMEOUT = 5*1000;
    // The minimum amount of time between successive GC requests for a process.
    static final int GC_MIN_INTERVAL = 60*1000;
    // How long we wait until giving up on an activity telling us it has
    // finished destroying itself.
    static final int DESTROY_TIMEOUT = 10*1000;
@@ -251,10 +254,23 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
    // is no longer considered to be a relaunch of the service.
    static final int SERVICE_RESTART_DURATION = 5*1000;
    // How long a service needs to be running until it will start back at
    // SERVICE_RESTART_DURATION after being killed.
    static final int SERVICE_RESET_RUN_DURATION = 60*1000;
    // Multiplying factor to increase restart duration time by, for each time
    // a service is killed before it has run for SERVICE_RESET_RUN_DURATION.
    static final int SERVICE_RESTART_DURATION_FACTOR = 4;
    
    // The minimum amount of time between restarting services that we allow.
    // That is, when multiple services are restarting, we won't allow each
    // to restart less than this amount of time from the last one.
    static final int SERVICE_MIN_RESTART_TIME_BETWEEN = 10*1000;
    // Maximum amount of time for there to be no activity on a service before
    // we consider it non-essential and allow its process to go on the
    // LRU background list.
    static final int MAX_SERVICE_INACTIVITY = 10*60*1000;
    static final int MAX_SERVICE_INACTIVITY = 30*60*1000;
    
    // How long we wait until we timeout on key dispatching.
    static final int KEY_DISPATCHING_TIMEOUT = 5*1000;
@@ -1090,8 +1106,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
                        }
                    }
                }
                break;
            }
            } break;
            case SHOW_UID_ERROR_MSG: {
                // XXX This is a temporary dialog, no need to localize.
                AlertDialog d = new BaseErrorDialog(mContext);
@@ -1135,7 +1150,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
                synchronized (ActivityManagerService.this) {
                    resumeTopActivityLocked(null);
                }
            }
            } break;
            case PROC_START_TIMEOUT_MSG: {
                if (mDidDexOpt) {
                    mDidDexOpt = false;
@@ -1148,12 +1163,12 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
                synchronized (ActivityManagerService.this) {
                    processStartTimedOutLocked(app);
                }
            }
            } break;
            case DO_PENDING_ACTIVITY_LAUNCHES_MSG: {
                synchronized (ActivityManagerService.this) {
                    doPendingActivityLaunchesLocked(true);
                }
            }
            } break;
            case KILL_APPLICATION_MSG: {
                synchronized (ActivityManagerService.this) {
                    int uid = msg.arg1;
@@ -4426,17 +4441,26 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
                if (!haveBg) {
                    Log.i(TAG, "Low Memory: No more background processes.");
                    EventLog.writeEvent(LOG_AM_LOW_MEMORY, mLRUProcesses.size());
                    long now = SystemClock.uptimeMillis();
                    for (i=0; i<count; i++) {
                        ProcessRecord rec = mLRUProcesses.get(i);
                        if (rec.thread != null) {
                            rec.lastRequestedGc = SystemClock.uptimeMillis();
                            try {
                                rec.thread.scheduleLowMemory();
                            } catch (RemoteException e) {
                                // Don't care if the process is gone.
                        if (rec.thread != null &&
                                (rec.lastLowMemory+GC_MIN_INTERVAL) <= now) {
                            // The low memory report is overriding any current
                            // state for a GC request.  Make sure to do
                            // visible/foreground processes first.
                            if (rec.setAdj <= VISIBLE_APP_ADJ) {
                                rec.lastRequestedGc = 0;
                            } else {
                                rec.lastRequestedGc = rec.lastLowMemory;
                            }
                            rec.reportLowMemory = true;
                            rec.lastLowMemory = now;
                            mProcessesToGc.remove(rec);
                            addProcessToGcListLocked(rec);
                        }
                    }
                    scheduleAppGcsLocked();
                }
            }
        } else if (Config.LOGD) {
@@ -5112,7 +5136,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
                    app.instrumentationArguments, app.instrumentationWatcher, testMode, 
                    isRestrictedBackupMode, mConfiguration, getCommonServicesLocked());
            updateLRUListLocked(app, false);
            app.lastRequestedGc = SystemClock.uptimeMillis();
            app.lastRequestedGc = app.lastLowMemory = SystemClock.uptimeMillis();
        } catch (Exception e) {
            // todo: Yikes!  What should we do?  For now we will try to
            // start another process, but that could easily get us in
@@ -8795,6 +8819,24 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
                        "OnHold Norm", "OnHold PERS", false);
            }
            if (mProcessesToGc.size() > 0) {
                if (needSep) pw.println(" ");
                needSep = true;
                pw.println("  Processes that are waiting to GC:");
                long now = SystemClock.uptimeMillis();
                for (int i=0; i<mProcessesToGc.size(); i++) {
                    ProcessRecord proc = mProcessesToGc.get(i);
                    pw.print("    Process "); pw.println(proc);
                    pw.print("      lowMem="); pw.print(proc.reportLowMemory);
                            pw.print(", last gced=");
                            pw.print(now-proc.lastRequestedGc);
                            pw.print(" ms ago, last lowMwm=");
                            pw.print(now-proc.lastLowMemory);
                            pw.println(" ms ago");
                    
                }
            }
            
            if (mProcessCrashTimes.getMap().size() > 0) {
                if (needSep) pw.println(" ");
                needSep = true;
@@ -9907,6 +9949,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
    }
    private final void scheduleServiceRestartLocked(ServiceRecord r) {
        final long now = SystemClock.uptimeMillis();
        
        r.totalRestartCount++;
        if (r.restartDelay == 0) {
            r.restartCount++;
@@ -9917,19 +9961,41 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
            // the beginning, so we don't infinitely increase the duration
            // on a service that just occasionally gets killed (which is
            // a normal case, due to process being killed to reclaim memory).
            long now = SystemClock.uptimeMillis();
            if (now > (r.restartTime+(SERVICE_RESTART_DURATION*2*2*2))) {
            if (now > (r.restartTime+SERVICE_RESET_RUN_DURATION)) {
                r.restartCount = 1;
                r.restartDelay = SERVICE_RESTART_DURATION;
            } else {
                r.restartDelay *= 2;
                r.restartDelay *= SERVICE_RESTART_DURATION_FACTOR;
            }
        }
        
        r.nextRestartTime = now + r.restartDelay;
        
        // Make sure that we don't end up restarting a bunch of services
        // all at the same time.
        boolean repeat;
        do {
            repeat = false;
            for (int i=mRestartingServices.size()-1; i>=0; i--) {
                ServiceRecord r2 = mRestartingServices.get(i);
                if (r2 != r && r.nextRestartTime
                        >= (r2.nextRestartTime-SERVICE_MIN_RESTART_TIME_BETWEEN)
                        && r.nextRestartTime
                        < (r2.nextRestartTime+SERVICE_MIN_RESTART_TIME_BETWEEN)) {
                    r.nextRestartTime = r2.nextRestartTime + SERVICE_MIN_RESTART_TIME_BETWEEN;
                    r.restartDelay = r.nextRestartTime - now;
                    repeat = true;
                    break;
                }
            }
        } while (repeat);
        
        if (!mRestartingServices.contains(r)) {
            mRestartingServices.add(r);
        }
        
        mHandler.removeCallbacks(r.restarter);
        mHandler.postDelayed(r.restarter, r.restartDelay);
        mHandler.postAtTime(r.restarter, r.nextRestartTime);
        r.nextRestartTime = SystemClock.uptimeMillis() + r.restartDelay;
        Log.w(TAG, "Scheduling restart of crashed service "
                + r.shortName + " in " + r.restartDelay + "ms");
@@ -12304,15 +12370,15 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
        if (app == TOP_APP) {
            // The last app on the list is the foreground app.
            adj = FOREGROUND_APP_ADJ;
            app.adjType = "top";
            app.adjType = "top-activity";
        } else if (app.instrumentationClass != null) {
            // Don't want to kill running instrumentation.
            adj = FOREGROUND_APP_ADJ;
            app.adjType = "instr";
            app.adjType = "instrumentation";
        } else if (app.persistentActivities > 0) {
            // Special persistent activities...  shouldn't be used these days.
            adj = FOREGROUND_APP_ADJ;
            app.adjType = "pers";
            app.adjType = "persistent";
        } else if (app.curReceiver != null ||
                (mPendingBroadcast != null && mPendingBroadcast.curApp == app)) {
            // An app that is currently receiving a broadcast also
@@ -12341,6 +12407,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
        } else if ((N=app.activities.size()) != 0) {
            // This app is in the background with paused activities.
            adj = hiddenAdj;
            app.adjType = "bg-activities";
            for (int j=0; j<N; j++) {
                if (((HistoryRecord)app.activities.get(j)).visible) {
                    // This app has a visible activity!
@@ -12380,7 +12447,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
            // its services we may bump it up from there.
            if (adj > hiddenAdj) {
                adj = hiddenAdj;
                app.adjType = "services";
                app.adjType = "bg-services";
            }
            final long now = SystemClock.uptimeMillis();
            // This process is more important if the top activity is
@@ -12522,8 +12589,13 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
        try {
            app.lastRequestedGc = SystemClock.uptimeMillis();
            if (app.thread != null) {
                if (app.reportLowMemory) {
                    app.reportLowMemory = false;
                    app.thread.scheduleLowMemory();
                } else {
                    app.thread.processInBackground();
                }
            }
        } catch (Exception e) {
            // whatever.
        }
@@ -12551,15 +12623,25 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
        if (canGcNow()) {
            while (mProcessesToGc.size() > 0) {
                ProcessRecord proc = mProcessesToGc.remove(0);
                if (proc.curRawAdj > VISIBLE_APP_ADJ) {
                if (proc.curRawAdj > VISIBLE_APP_ADJ || proc.reportLowMemory) {
                    if ((proc.lastRequestedGc+GC_MIN_INTERVAL)
                            <= SystemClock.uptimeMillis()) {
                        // To avoid spamming the system, we will GC processes one
                        // at a time, waiting a few seconds between each.
                        performAppGcLocked(proc);
                        scheduleAppGcsLocked();
                        return;
                    } else {
                        // It hasn't been long enough since we last GCed this
                        // process...  put it in the list to wait for its time.
                        addProcessToGcListLocked(proc);
                        break;
                    }
                }
            }
            
            scheduleAppGcsLocked();
        }
    }
    
    /**
@@ -12579,8 +12661,39 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
     */
    final void scheduleAppGcsLocked() {
        mHandler.removeMessages(GC_BACKGROUND_PROCESSES_MSG);
        
        if (mProcessesToGc.size() > 0) {
            // Schedule a GC for the time to the next process.
            ProcessRecord proc = mProcessesToGc.get(0);
            Message msg = mHandler.obtainMessage(GC_BACKGROUND_PROCESSES_MSG);
        mHandler.sendMessageDelayed(msg, GC_TIMEOUT);
            
            long when = mProcessesToGc.get(0).lastRequestedGc + GC_MIN_INTERVAL;
            long now = SystemClock.uptimeMillis();
            if (when < (now+GC_TIMEOUT)) {
                when = now + GC_TIMEOUT;
            }
            mHandler.sendMessageAtTime(msg, when);
        }
    }
    
    /**
     * Add a process to the array of processes waiting to be GCed.  Keeps the
     * list in sorted order by the last GC time.  The process can't already be
     * on the list.
     */
    final void addProcessToGcListLocked(ProcessRecord proc) {
        boolean added = false;
        for (int i=mProcessesToGc.size()-1; i>=0; i--) {
            if (mProcessesToGc.get(i).lastRequestedGc <
                    proc.lastRequestedGc) {
                added = true;
                mProcessesToGc.add(i+1, proc);
                break;
            }
        }
        if (!added) {
            mProcessesToGc.add(0, proc);
        }
    }
    
    /**
@@ -12590,11 +12703,11 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
     */
    final void scheduleAppGcLocked(ProcessRecord app) {
        long now = SystemClock.uptimeMillis();
        if ((app.lastRequestedGc+5000) > now) {
        if ((app.lastRequestedGc+GC_MIN_INTERVAL) > now) {
            return;
        }
        if (!mProcessesToGc.contains(app)) {
            mProcessesToGc.add(app);
            addProcessToGcListLocked(app);
            scheduleAppGcsLocked();
        }
    }
+2 −0
Original line number Diff line number Diff line
@@ -71,6 +71,8 @@ class ProcessRecord implements Watchdog.PssRequestor {
    ComponentName instrumentationResultClass;// copy of instrumentationClass
    BroadcastRecord curReceiver;// receiver currently running in the app
    long lastRequestedGc;       // When we last asked the app to do a gc
    long lastLowMemory;         // When we last told the app that memory is low
    boolean reportLowMemory;    // Set to true when waiting to report low mem
    int lastPss;                // Last pss size reported by app.
    String adjType;             // Debugging: primary thing impacting oom_adj.
    Object adjSource;           // Debugging: option dependent object.
+5 −4
Original line number Diff line number Diff line
@@ -88,22 +88,23 @@ class ServiceRecord extends Binder {
        if (permission != null) {
            pw.print(prefix); pw.print("permission="); pw.println(permission);
        }
        long now = SystemClock.uptimeMillis();
        pw.print(prefix); pw.print("baseDir="); pw.print(baseDir);
                if (!resDir.equals(baseDir)) pw.print(" resDir="); pw.print(resDir);
                pw.print(" dataDir="); pw.println(dataDir);
        pw.print(prefix); pw.print("app="); pw.println(app);
        pw.print(prefix); pw.print("isForeground="); pw.print(isForeground);
                pw.print(" lastActivity="); pw.println(lastActivity);
                pw.print(" lastActivity="); pw.println(lastActivity-now);
        pw.print(prefix); pw.print("startRequested="); pw.print(startRequested);
                pw.print(" startId="); pw.print(lastStartId);
                pw.print(" executeNesting="); pw.print(executeNesting);
                pw.print(" executingStart="); pw.print(executingStart);
                pw.print(" executingStart="); pw.print(executingStart-now);
                pw.print(" crashCount="); pw.println(crashCount);
        pw.print(prefix); pw.print("totalRestartCount="); pw.print(totalRestartCount);
                pw.print(" restartCount="); pw.print(restartCount);
                pw.print(" restartDelay="); pw.print(restartDelay);
                pw.print(" restartTime="); pw.print(restartTime);
                pw.print(" nextRestartTime="); pw.println(nextRestartTime);
                pw.print(" restartTime="); pw.print(restartTime-now);
                pw.print(" nextRestartTime="); pw.println(nextRestartTime-now);
        if (bindings.size() > 0) {
            Iterator<IntentBindRecord> it = bindings.values().iterator();
            while (it.hasNext()) {