diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java index 3c66445bb2c18..266f213255762 100644 --- a/services/java/com/android/server/am/ActivityManagerService.java +++ b/services/java/com/android/server/am/ActivityManagerService.java @@ -25,6 +25,8 @@ import com.android.server.SystemServer; import com.android.server.Watchdog; import com.android.server.WindowManagerService; +import dalvik.system.Zygote; + import android.app.Activity; import android.app.ActivityManager; import android.app.ActivityManagerNative; @@ -100,10 +102,6 @@ import android.view.View; import android.view.WindowManager; import android.view.WindowManagerPolicy; -import dalvik.system.Zygote; - -import java.io.ByteArrayInputStream; -import java.io.DataInputStream; import java.io.File; import java.io.FileDescriptor; import java.io.FileInputStream; @@ -254,41 +252,36 @@ public final class ActivityManagerService extends ActivityManagerNative implemen // the first to go! Value set in system/rootdir/init.rc on startup. // This value is initalized in the constructor, careful when refering to // this static variable externally. - static int EMPTY_APP_ADJ; - - // This is a process with a content provider that does not have any clients - // attached to it. If it did have any clients, its adjustment would be the - // one for the highest-priority of those processes. - static int CONTENT_PROVIDER_ADJ; + static final int EMPTY_APP_ADJ; // This is a process only hosting activities that are not visible, // so it can be killed without any disruption. Value set in // system/rootdir/init.rc on startup. - final int HIDDEN_APP_MAX_ADJ; + static final int HIDDEN_APP_MAX_ADJ; static int HIDDEN_APP_MIN_ADJ; // This is a process holding the home application -- we want to try // avoiding killing it, even if it would normally be in the background, // because the user interacts with it so much. - final int HOME_APP_ADJ; + static final int HOME_APP_ADJ; // This is a process currently hosting a backup operation. Killing it // is not entirely fatal but is generally a bad idea. - final int BACKUP_APP_ADJ; + static final int BACKUP_APP_ADJ; // This is a process holding a secondary server -- killing it will not // have much of an impact as far as the user is concerned. Value set in // system/rootdir/init.rc on startup. - final int SECONDARY_SERVER_ADJ; + static final int SECONDARY_SERVER_ADJ; // This is a process only hosting activities that are visible to the // user, so we'd prefer they don't disappear. Value set in // system/rootdir/init.rc on startup. - final int VISIBLE_APP_ADJ; + static final int VISIBLE_APP_ADJ; // This is the process running the current foreground app. We'd really // rather not kill it! Value set in system/rootdir/init.rc on startup. - final int FOREGROUND_APP_ADJ; + static final int FOREGROUND_APP_ADJ; // This is a process running a core server, such as telephony. Definitely // don't want to kill it, but doing so is not completely fatal. @@ -307,13 +300,58 @@ public final class ActivityManagerService extends ActivityManagerNative implemen static final String DEFAULT_ERROR_RECEIVER_PROPERTY = "ro.error.receiver.default"; // Corresponding memory levels for above adjustments. - final int EMPTY_APP_MEM; - final int HIDDEN_APP_MEM; - final int HOME_APP_MEM; - final int BACKUP_APP_MEM; - final int SECONDARY_SERVER_MEM; - final int VISIBLE_APP_MEM; - final int FOREGROUND_APP_MEM; + static final int EMPTY_APP_MEM; + static final int HIDDEN_APP_MEM; + static final int HOME_APP_MEM; + static final int BACKUP_APP_MEM; + static final int SECONDARY_SERVER_MEM; + static final int VISIBLE_APP_MEM; + static final int FOREGROUND_APP_MEM; + + // The minimum number of hidden apps we want to be able to keep around, + // without empty apps being able to push them out of memory. + static final int MIN_HIDDEN_APPS = 2; + + // We put empty content processes after any hidden processes that have + // been idle for less than 30 seconds. + static final long CONTENT_APP_IDLE_OFFSET = 30*1000; + + // We put empty content processes after any hidden processes that have + // been idle for less than 60 seconds. + static final long EMPTY_APP_IDLE_OFFSET = 60*1000; + + static { + // These values are set in system/rootdir/init.rc on startup. + FOREGROUND_APP_ADJ = + Integer.valueOf(SystemProperties.get("ro.FOREGROUND_APP_ADJ")); + VISIBLE_APP_ADJ = + Integer.valueOf(SystemProperties.get("ro.VISIBLE_APP_ADJ")); + SECONDARY_SERVER_ADJ = + Integer.valueOf(SystemProperties.get("ro.SECONDARY_SERVER_ADJ")); + BACKUP_APP_ADJ = + Integer.valueOf(SystemProperties.get("ro.BACKUP_APP_ADJ")); + HOME_APP_ADJ = + Integer.valueOf(SystemProperties.get("ro.HOME_APP_ADJ")); + HIDDEN_APP_MIN_ADJ = + Integer.valueOf(SystemProperties.get("ro.HIDDEN_APP_MIN_ADJ")); + EMPTY_APP_ADJ = + Integer.valueOf(SystemProperties.get("ro.EMPTY_APP_ADJ")); + HIDDEN_APP_MAX_ADJ = EMPTY_APP_ADJ-1; + FOREGROUND_APP_MEM = + Integer.valueOf(SystemProperties.get("ro.FOREGROUND_APP_MEM"))*PAGE_SIZE; + VISIBLE_APP_MEM = + Integer.valueOf(SystemProperties.get("ro.VISIBLE_APP_MEM"))*PAGE_SIZE; + SECONDARY_SERVER_MEM = + Integer.valueOf(SystemProperties.get("ro.SECONDARY_SERVER_MEM"))*PAGE_SIZE; + BACKUP_APP_MEM = + Integer.valueOf(SystemProperties.get("ro.BACKUP_APP_MEM"))*PAGE_SIZE; + HOME_APP_MEM = + Integer.valueOf(SystemProperties.get("ro.HOME_APP_MEM"))*PAGE_SIZE; + HIDDEN_APP_MEM = + Integer.valueOf(SystemProperties.get("ro.HIDDEN_APP_MEM"))*PAGE_SIZE; + EMPTY_APP_MEM = + Integer.valueOf(SystemProperties.get("ro.EMPTY_APP_MEM"))*PAGE_SIZE; + } final int MY_PID; @@ -527,7 +565,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen * It contains ApplicationRecord objects. This list does NOT include * any persistent application records (since we never want to exit them). */ - final ArrayList mLRUProcesses + final ArrayList mLruProcesses = new ArrayList(); /** @@ -1075,8 +1113,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } break; case UPDATE_TIME_ZONE: { synchronized (ActivityManagerService.this) { - for (int i = mLRUProcesses.size() - 1 ; i >= 0 ; i--) { - ProcessRecord r = mLRUProcesses.get(i); + for (int i = mLruProcesses.size() - 1 ; i >= 0 ; i--) { + ProcessRecord r = mLruProcesses.get(i); if (r.thread != null) { try { r.thread.updateTimeZone(); @@ -1192,7 +1230,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen synchronized (mSelf.mPidsSelfLocked) { mSelf.mPidsSelfLocked.put(app.pid, app); } - mSelf.updateLRUListLocked(app, true); + mSelf.updateLruProcessLocked(app, true, true); } } catch (PackageManager.NameNotFoundException e) { throw new RuntimeException( @@ -1349,8 +1387,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } catch (NumberFormatException e) { } - for (int i=0; i=0; i--) { + ProcessRecord proc = service.mLruProcesses.get(i); if (proc.pid == pid) { procs.add(proc); } else if (proc.processName.equals(args[0])) { @@ -1362,7 +1400,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen return; } } else { - procs = service.mLRUProcesses; + procs = service.mLruProcesses; } } dumpApplicationMemoryUsage(fd, pw, procs, " ", args); @@ -1417,39 +1455,6 @@ public final class ActivityManagerService extends ActivityManagerNative implemen // Add ourself to the Watchdog monitors. Watchdog.getInstance().addMonitor(this); - // These values are set in system/rootdir/init.rc on startup. - FOREGROUND_APP_ADJ = - Integer.valueOf(SystemProperties.get("ro.FOREGROUND_APP_ADJ")); - VISIBLE_APP_ADJ = - Integer.valueOf(SystemProperties.get("ro.VISIBLE_APP_ADJ")); - SECONDARY_SERVER_ADJ = - Integer.valueOf(SystemProperties.get("ro.SECONDARY_SERVER_ADJ")); - BACKUP_APP_ADJ = - Integer.valueOf(SystemProperties.get("ro.BACKUP_APP_ADJ")); - HOME_APP_ADJ = - Integer.valueOf(SystemProperties.get("ro.HOME_APP_ADJ")); - HIDDEN_APP_MIN_ADJ = - Integer.valueOf(SystemProperties.get("ro.HIDDEN_APP_MIN_ADJ")); - CONTENT_PROVIDER_ADJ = - Integer.valueOf(SystemProperties.get("ro.CONTENT_PROVIDER_ADJ")); - HIDDEN_APP_MAX_ADJ = CONTENT_PROVIDER_ADJ-1; - EMPTY_APP_ADJ = - Integer.valueOf(SystemProperties.get("ro.EMPTY_APP_ADJ")); - FOREGROUND_APP_MEM = - Integer.valueOf(SystemProperties.get("ro.FOREGROUND_APP_MEM"))*PAGE_SIZE; - VISIBLE_APP_MEM = - Integer.valueOf(SystemProperties.get("ro.VISIBLE_APP_MEM"))*PAGE_SIZE; - SECONDARY_SERVER_MEM = - Integer.valueOf(SystemProperties.get("ro.SECONDARY_SERVER_MEM"))*PAGE_SIZE; - BACKUP_APP_MEM = - Integer.valueOf(SystemProperties.get("ro.BACKUP_APP_MEM"))*PAGE_SIZE; - HOME_APP_MEM = - Integer.valueOf(SystemProperties.get("ro.HOME_APP_MEM"))*PAGE_SIZE; - HIDDEN_APP_MEM = - Integer.valueOf(SystemProperties.get("ro.HIDDEN_APP_MEM"))*PAGE_SIZE; - EMPTY_APP_MEM = - Integer.valueOf(SystemProperties.get("ro.EMPTY_APP_MEM"))*PAGE_SIZE; - mProcessStatsThread = new Thread("ProcessStats") { public void run() { while (true) { @@ -1602,12 +1607,54 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } } - private final void updateLRUListLocked(ProcessRecord app, - boolean oomAdj) { + private final void updateLruProcessLocked(ProcessRecord app, + boolean oomAdj, boolean updateActivityTime) { // put it on the LRU to keep track of when it should be exited. - int lrui = mLRUProcesses.indexOf(app); - if (lrui >= 0) mLRUProcesses.remove(lrui); - mLRUProcesses.add(app); + int lrui = mLruProcesses.indexOf(app); + if (lrui >= 0) mLruProcesses.remove(lrui); + + int i = mLruProcesses.size()-1; + int skipTop = 0; + + // compute the new weight for this process. + if (updateActivityTime) { + app.lastActivityTime = SystemClock.uptimeMillis(); + } + if (app.activities.size() > 0) { + // If this process has activities, we more strongly want to keep + // it around. + app.lruWeight = app.lastActivityTime; + } else if (app.pubProviders.size() > 0) { + // If this process contains content providers, we want to keep + // it a little more strongly. + app.lruWeight = app.lastActivityTime - CONTENT_APP_IDLE_OFFSET; + // Also don't let it kick out the first few "real" hidden processes. + skipTop = MIN_HIDDEN_APPS; + } else { + // If this process doesn't have activities, we less strongly + // want to keep it around, and generally want to avoid getting + // in front of any very recently used activities. + app.lruWeight = app.lastActivityTime - EMPTY_APP_IDLE_OFFSET; + // Also don't let it kick out the first few "real" hidden processes. + skipTop = MIN_HIDDEN_APPS; + } + while (i >= 0) { + ProcessRecord p = mLruProcesses.get(i); + // If this app shouldn't be in front of the first N background + // apps, then skip over that many that are currently hidden. + if (skipTop > 0 && p.setAdj >= HIDDEN_APP_MIN_ADJ) { + skipTop--; + } + if (p.lruWeight <= app.lruWeight){ + mLruProcesses.add(i+1, app); + break; + } + i--; + } + if (i < 0) { + mLruProcesses.add(0, app); + } + //Log.i(TAG, "Putting proc to front: " + app.processName); if (oomAdj) { updateOomAdjLocked(); @@ -1725,7 +1772,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen if (idx < 0) { app.activities.add(r); } - updateLRUListLocked(app, true); + updateLruProcessLocked(app, true, true); try { if (app.thread == null) { @@ -2657,7 +2704,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen next.state = ActivityState.RESUMED; mResumedActivity = next; next.task.touchActiveTime(); - updateLRUListLocked(next.app, true); + updateLruProcessLocked(next.app, true, true); updateLRUListLocked(next); // Have the window manager re-evaluate the orientation of @@ -4305,6 +4352,11 @@ public final class ActivityManagerService extends ActivityManagerNative implemen if (r.persistent) { decPersistentCountLocked(r.app); } + if (r.app.activities.size() == 0) { + // No longer have activities, so update location in + // LRU list. + updateLruProcessLocked(r.app, true, false); + } } boolean skipDestroy = false; @@ -4383,7 +4435,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen boolean restarting) { cleanUpApplicationRecordLocked(app, restarting, -1); if (!restarting) { - mLRUProcesses.remove(app); + mLruProcesses.remove(app); } // Just in case... @@ -4476,10 +4528,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen IBinder threadBinder = thread.asBinder(); // Find the application record. - int count = mLRUProcesses.size(); - int i; - for (i=0; i=0; i--) { + ProcessRecord rec = mLruProcesses.get(i); if (rec.thread != null && rec.thread.asBinder() == threadBinder) { return i; } @@ -4494,7 +4544,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } int appIndex = getLRURecordIndexForAppLocked(thread); - return appIndex >= 0 ? mLRUProcesses.get(appIndex) : null; + return appIndex >= 0 ? mLruProcesses.get(appIndex) : null; } private final void appDiedLocked(ProcessRecord app, int pid, @@ -4517,10 +4567,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen // and the app that died was not running instrumentation, // then tell everyone we are now low on memory. boolean haveBg = false; - int count = mLRUProcesses.size(); - int i; - for (i=0; i=0; i--) { + ProcessRecord rec = mLruProcesses.get(i); if (rec.thread != null && rec.setAdj >= HIDDEN_APP_MIN_ADJ) { haveBg = true; break; @@ -4529,10 +4577,10 @@ public final class ActivityManagerService extends ActivityManagerNative implemen if (!haveBg) { Log.i(TAG, "Low Memory: No more background processes."); - EventLog.writeEvent(EventLogTags.AM_LOW_MEMORY, mLRUProcesses.size()); + EventLog.writeEvent(EventLogTags.AM_LOW_MEMORY, mLruProcesses.size()); long now = SystemClock.uptimeMillis(); - for (i=0; i=0; i--) { + ProcessRecord rec = mLruProcesses.get(i); if (rec != app && rec.thread != null && (rec.lastLowMemory+GC_MIN_INTERVAL) <= now) { // The low memory report is overriding any current @@ -4646,8 +4694,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } } } - for (i = mLRUProcesses.size() - 1 ; i >= 0 ; i--) { - ProcessRecord r = mLRUProcesses.get(i); + for (i = mLruProcesses.size() - 1 ; i >= 0 ; i--) { + ProcessRecord r = mLruProcesses.get(i); boolean done = false; for (int j=0; j=0; i--) { - ProcessRecord proc = mLRUProcesses.get(i); + for (int i=mLruProcesses.size()-1; i>=0; i--) { + ProcessRecord proc = mLruProcesses.get(i); if (!proc.persistent) { mRequestPssList.add(proc); } @@ -8279,8 +8327,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen i++; } - for (i=mLRUProcesses.size()-1; i>=0; i--) { - ProcessRecord proc = mLRUProcesses.get(i); + for (i=mLruProcesses.size()-1; i>=0; i--) { + ProcessRecord proc = mLruProcesses.get(i); if (proc.persistent) { continue; } @@ -8289,15 +8337,14 @@ public final class ActivityManagerService extends ActivityManagerNative implemen stats.mNoPssCount++; continue; } - if (proc.setAdj == EMPTY_APP_ADJ) { - stats.mEmptyPss += proc.lastPss; - stats.mEmptyCount++; - } else if (proc.setAdj == CONTENT_PROVIDER_ADJ) { - stats.mEmptyPss += proc.lastPss; - stats.mEmptyCount++; - } else if (proc.setAdj >= HIDDEN_APP_MIN_ADJ) { - stats.mBackgroundPss += proc.lastPss; - stats.mBackgroundCount++; + if (proc.setAdj >= HIDDEN_APP_MIN_ADJ) { + if (proc.empty) { + stats.mEmptyPss += proc.lastPss; + stats.mEmptyCount++; + } else { + stats.mBackgroundPss += proc.lastPss; + stats.mBackgroundCount++; + } } else if (proc.setAdj >= VISIBLE_APP_ADJ) { stats.mVisiblePss += proc.lastPss; stats.mVisibleCount++; @@ -8922,9 +8969,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen synchronized (this) { // iterate across all processes - final int N = mLRUProcesses.size(); - for (int i = 0; i < N; i++) { - ProcessRecord app = mLRUProcesses.get(i); + for (int i=mLruProcesses.size()-1; i>=0; i--) { + ProcessRecord app = mLruProcesses.get(i); if ((app.thread != null) && (app.crashing || app.notResponding)) { // This one's in trouble, so we'll generate a report for it // crashes are higher priority (in case there's a crash *and* an anr) @@ -8957,9 +9003,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen List runList = null; synchronized (this) { // Iterate across all processes - final int N = mLRUProcesses.size(); - for (int i = 0; i < N; i++) { - ProcessRecord app = mLRUProcesses.get(i); + for (int i=mLruProcesses.size()-1; i>=0; i--) { + ProcessRecord app = mLruProcesses.get(i); if ((app.thread != null) && (!app.crashing && !app.notResponding)) { // Generate process state info for running application ActivityManager.RunningAppProcessInfo currApp = @@ -8967,7 +9012,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen app.pid, app.getPackageList()); currApp.uid = app.info.uid; int adj = app.curAdj; - if (adj >= CONTENT_PROVIDER_ADJ) { + if (adj >= EMPTY_APP_ADJ) { currApp.importance = ActivityManager.RunningAppProcessInfo.IMPORTANCE_EMPTY; } else if (adj >= HIDDEN_APP_MIN_ADJ) { currApp.importance = ActivityManager.RunningAppProcessInfo.IMPORTANCE_BACKGROUND; @@ -9087,11 +9132,11 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } } - if (mLRUProcesses.size() > 0) { + if (mLruProcesses.size() > 0) { if (needSep) pw.println(" "); needSep = true; pw.println(" Running processes (most recent first):"); - dumpProcessList(pw, this, mLRUProcesses, " ", + dumpProcessList(pw, this, mLruProcesses, " ", "App ", "PERS", true); needSep = true; } @@ -9597,35 +9642,23 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } else if (inclOomAdj) { String oomAdj; if (r.setAdj >= EMPTY_APP_ADJ) { - oomAdj = buildOomTag("empty", null, r.setAdj, - EMPTY_APP_ADJ); - } else if (r.setAdj >= CONTENT_PROVIDER_ADJ) { - oomAdj = buildOomTag("cprov", null, r.setAdj, - CONTENT_PROVIDER_ADJ); + oomAdj = buildOomTag("empty", null, r.setAdj, EMPTY_APP_ADJ); } else if (r.setAdj >= HIDDEN_APP_MIN_ADJ) { - oomAdj = buildOomTag("hid", " ", r.setAdj, - HIDDEN_APP_MIN_ADJ); - } else if (r.setAdj >= service.HOME_APP_ADJ) { - oomAdj = buildOomTag("home ", null, r.setAdj, - service.HOME_APP_ADJ); - } else if (r.setAdj >= service.SECONDARY_SERVER_ADJ) { - oomAdj = buildOomTag("svc", " ", r.setAdj, - service.SECONDARY_SERVER_ADJ); - } else if (r.setAdj >= service.BACKUP_APP_ADJ) { - oomAdj = buildOomTag("bckup", null, r.setAdj, - service.BACKUP_APP_ADJ); - } else if (r.setAdj >= service.VISIBLE_APP_ADJ) { - oomAdj = buildOomTag("vis ", null, r.setAdj, - service.VISIBLE_APP_ADJ); - } else if (r.setAdj >= service.FOREGROUND_APP_ADJ) { - oomAdj = buildOomTag("fore ", null, r.setAdj, - service.FOREGROUND_APP_ADJ); + oomAdj = buildOomTag("bak", " ", r.setAdj, HIDDEN_APP_MIN_ADJ); + } else if (r.setAdj >= HOME_APP_ADJ) { + oomAdj = buildOomTag("home ", null, r.setAdj, HOME_APP_ADJ); + } else if (r.setAdj >= SECONDARY_SERVER_ADJ) { + oomAdj = buildOomTag("svc", " ", r.setAdj, SECONDARY_SERVER_ADJ); + } else if (r.setAdj >= BACKUP_APP_ADJ) { + oomAdj = buildOomTag("bckup", null, r.setAdj, BACKUP_APP_ADJ); + } else if (r.setAdj >= VISIBLE_APP_ADJ) { + oomAdj = buildOomTag("vis ", null, r.setAdj, VISIBLE_APP_ADJ); + } else if (r.setAdj >= FOREGROUND_APP_ADJ) { + oomAdj = buildOomTag("fore ", null, r.setAdj, FOREGROUND_APP_ADJ); } else if (r.setAdj >= CORE_SERVER_ADJ) { - oomAdj = buildOomTag("core ", null, r.setAdj, - CORE_SERVER_ADJ); + oomAdj = buildOomTag("core ", null, r.setAdj, CORE_SERVER_ADJ); } else if (r.setAdj >= SYSTEM_ADJ) { - oomAdj = buildOomTag("sys ", null, r.setAdj, - SYSTEM_ADJ); + oomAdj = buildOomTag("sys ", null, r.setAdj, SYSTEM_ADJ); } else { oomAdj = Integer.toString(r.setAdj); } @@ -9643,10 +9676,10 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } pw.println(String.format("%s%s #%2d: adj=%s/%s %s (%s)", prefix, (r.persistent ? persistentLabel : normalLabel), - i, oomAdj, schedGroup, r.toString(), r.adjType)); + i, oomAdj, schedGroup, r.toShortString(), r.adjType)); if (r.adjSource != null || r.adjTarget != null) { pw.println(prefix + " " + r.adjTarget - + " used by " + r.adjSource); + + "<=" + r.adjSource); } } else { pw.println(String.format("%s%s #%2d: %s", @@ -9877,7 +9910,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen private final void cleanUpApplicationRecordLocked(ProcessRecord app, boolean restarting, int index) { if (index >= 0) { - mLRUProcesses.remove(index); + mLruProcesses.remove(index); } mProcessesToGc.remove(app); @@ -10400,7 +10433,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen app.services.add(r); bumpServiceExecutingLocked(r); - updateLRUListLocked(app, true); + updateLruProcessLocked(app, true, true); boolean created = false; try { @@ -11387,7 +11420,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen nextTime = sr.executingStart; } } - if (timeout != null && mLRUProcesses.contains(proc)) { + if (timeout != null && mLruProcesses.contains(proc)) { Log.w(TAG, "Timeout executing service: " + timeout); appNotRespondingLocked(proc, null, null, "Executing service " + timeout.name); } else { @@ -12245,7 +12278,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen r.receiver = app.thread.asBinder(); r.curApp = app; app.curReceiver = r; - updateLRUListLocked(app, true); + updateLruProcessLocked(app, true, true); // Tell the application to launch this receiver. r.intent.setComponent(r.curComponent); @@ -12859,9 +12892,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen msg.obj = new Configuration(mConfiguration); mHandler.sendMessage(msg); - final int N = mLRUProcesses.size(); - for (int i=0; i=0; i--) { + ProcessRecord app = mLruProcesses.get(i); try { if (app.thread != null) { if (DEBUG_CONFIGURATION) Log.v(TAG, "Sending to proc " @@ -13075,10 +13107,16 @@ public final class ActivityManagerService extends ActivityManagerNative implemen // LIFETIME MANAGEMENT // ========================================================= - private final int computeOomAdjLocked( - ProcessRecord app, int hiddenAdj, ProcessRecord TOP_APP) { + private final int computeOomAdjLocked(ProcessRecord app, int hiddenAdj, + ProcessRecord TOP_APP, boolean recursed) { if (mAdjSeq == app.adjSeq) { - // This adjustment has already been computed. + // This adjustment has already been computed. If we are calling + // from the top, we may have already computed our adjustment with + // an earlier hidden adjustment that isn't really for us... if + // so, use the new hidden adjustment. + if (!recursed && app.hidden) { + app.curAdj = hiddenAdj; + } return app.curAdj; } @@ -13101,6 +13139,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen app.adjTypeCode = ActivityManager.RunningAppProcessInfo.REASON_UNKNOWN; app.adjSource = null; app.adjTarget = null; + app.empty = false; + app.hidden = false; // Determine the importance of the process, starting with most // important to least, and assign an appropriate OOM adjustment. @@ -13154,12 +13194,15 @@ public final class ActivityManagerService extends ActivityManagerNative implemen app.adjType = "home"; } else if ((N=app.activities.size()) != 0) { // This app is in the background with paused activities. + app.hidden = true; adj = hiddenAdj; schedGroup = Process.THREAD_GROUP_BG_NONINTERACTIVE; app.adjType = "bg-activities"; + N = app.activities.size(); for (int j=0; j SECONDARY_SERVER_ADJ) { adj = SECONDARY_SERVER_ADJ; app.adjType = "started-services"; + app.hidden = false; } } } @@ -13231,10 +13281,13 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } } int clientAdj = computeOomAdjLocked( - client, myHiddenAdj, TOP_APP); + client, myHiddenAdj, TOP_APP, true); if (adj > clientAdj) { adj = clientAdj > VISIBLE_APP_ADJ ? clientAdj : VISIBLE_APP_ADJ; + if (!client.hidden) { + app.hidden = false; + } app.adjType = "service"; app.adjTypeCode = ActivityManager.RunningAppProcessInfo .REASON_SERVICE_IN_USE; @@ -13256,6 +13309,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen || a.state == ActivityState.PAUSING)) { adj = FOREGROUND_APP_ADJ; schedGroup = Process.THREAD_GROUP_DEFAULT; + app.hidden = false; app.adjType = "service"; app.adjTypeCode = ActivityManager.RunningAppProcessInfo .REASON_SERVICE_IN_USE; @@ -13273,6 +13327,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen // its services we may bump it up from there. if (adj > hiddenAdj) { adj = hiddenAdj; + app.hidden = false; app.adjType = "bg-services"; } } @@ -13300,10 +13355,13 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } } int clientAdj = computeOomAdjLocked( - client, myHiddenAdj, TOP_APP); + client, myHiddenAdj, TOP_APP, true); if (adj > clientAdj) { adj = clientAdj > FOREGROUND_APP_ADJ ? clientAdj : FOREGROUND_APP_ADJ; + if (!client.hidden) { + app.hidden = false; + } app.adjType = "provider"; app.adjTypeCode = ActivityManager.RunningAppProcessInfo .REASON_PROVIDER_IN_USE; @@ -13322,20 +13380,12 @@ public final class ActivityManagerService extends ActivityManagerNative implemen if (adj > FOREGROUND_APP_ADJ) { adj = FOREGROUND_APP_ADJ; schedGroup = Process.THREAD_GROUP_DEFAULT; + app.hidden = false; app.adjType = "provider"; app.adjTarget = cpr.info.name; } } } - - // Finally, if this process has published any content providers, - // then its adjustment makes it at least as important as any of the - // processes using those providers, and no less important than - // CONTENT_PROVIDER_ADJ, which is just shy of EMPTY. - if (adj > CONTENT_PROVIDER_ADJ) { - adj = CONTENT_PROVIDER_ADJ; - app.adjType = "pub-providers"; - } } app.curRawAdj = adj; @@ -13493,7 +13543,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen return true; } - int adj = computeOomAdjLocked(app, hiddenAdj, TOP_APP); + int adj = computeOomAdjLocked(app, hiddenAdj, TOP_APP, false); if ((app.pid != 0 && app.pid != MY_PID) || Process.supportsProcesses()) { if (app.curRawAdj != app.setRawAdj) { @@ -13599,13 +13649,14 @@ public final class ActivityManagerService extends ActivityManagerNative implemen // First try updating the OOM adjustment for each of the // application processes based on their current state. - int i = mLRUProcesses.size(); + int i = mLruProcesses.size(); int curHiddenAdj = HIDDEN_APP_MIN_ADJ; while (i > 0) { i--; - ProcessRecord app = mLRUProcesses.get(i); + ProcessRecord app = mLruProcesses.get(i); + //Log.i(TAG, "OOM " + app + ": cur hidden=" + curHiddenAdj); if (updateOomAdjLocked(app, curHiddenAdj, TOP_APP)) { - if (curHiddenAdj < HIDDEN_APP_MAX_ADJ + if (curHiddenAdj < EMPTY_APP_ADJ && app.curAdj == curHiddenAdj) { curHiddenAdj++; } @@ -13614,8 +13665,9 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } } - // todo: for now pretend like OOM ADJ didn't work, because things - // aren't behaving as expected on Linux -- it's not killing processes. + // If we return false, we will fall back on killing processes to + // have a fixed limit. Do this if a limit has been requested; else + // only return false if one of the adjustments failed. return ENFORCE_PROCESS_LIMIT || mProcessLimit > 0 ? false : didOomAdj; } @@ -13662,8 +13714,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen // Count how many processes are running services. int numServiceProcs = 0; - for (i=mLRUProcesses.size()-1; i>=0; i--) { - final ProcessRecord app = mLRUProcesses.get(i); + for (i=mLruProcesses.size()-1; i>=0; i--) { + final ProcessRecord app = mLruProcesses.get(i); if (app.persistent || app.services.size() != 0 || app.curReceiver != null @@ -13688,10 +13740,10 @@ public final class ActivityManagerService extends ActivityManagerNative implemen // process count. First remove any processes that no longer // have activites running in them. for ( i=0; - i curMaxProcs; + i curMaxProcs; i++) { - final ProcessRecord app = mLRUProcesses.get(i); + final ProcessRecord app = mLruProcesses.get(i); // Quit an application only if it is not currently // running any activities. if (!app.persistent && app.activities.size() == 0 @@ -13722,13 +13774,13 @@ public final class ActivityManagerService extends ActivityManagerNative implemen // If we still have too many processes, now from the least // recently used process we start finishing activities. if (Config.LOGV) Log.v( - TAG, "*** NOW HAVE " + mLRUProcesses.size() + + TAG, "*** NOW HAVE " + mLruProcesses.size() + " of " + curMaxProcs + " processes"); for ( i=0; - i curMaxProcs; + i curMaxProcs; i++) { - final ProcessRecord app = mLRUProcesses.get(i); + final ProcessRecord app = mLruProcesses.get(i); // Quit the application only if we have a state saved for // all of its activities. boolean canQuit = !app.persistent && app.curReceiver == null @@ -13827,8 +13879,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen + android.Manifest.permission.SIGNAL_PERSISTENT_PROCESSES); } - for (int i = mLRUProcesses.size() - 1 ; i >= 0 ; i--) { - ProcessRecord r = mLRUProcesses.get(i); + for (int i = mLruProcesses.size() - 1 ; i >= 0 ; i--) { + ProcessRecord r = mLruProcesses.get(i); if (r.thread != null && r.persistent) { Process.sendSignal(r.pid, sig); } diff --git a/services/java/com/android/server/am/ProcessRecord.java b/services/java/com/android/server/am/ProcessRecord.java index 620225770e5e2..28195ce158e1c 100644 --- a/services/java/com/android/server/am/ProcessRecord.java +++ b/services/java/com/android/server/am/ProcessRecord.java @@ -28,6 +28,7 @@ import android.content.pm.ApplicationInfo; import android.os.Bundle; import android.os.IBinder; import android.os.RemoteException; +import android.os.SystemClock; import android.util.PrintWriterPrinter; import java.io.PrintWriter; @@ -50,6 +51,8 @@ class ProcessRecord implements Watchdog.PssRequestor { // are in the process of launching the app) int pid; // The process of this application; 0 if none boolean starting; // True if the process is being started + long lastActivityTime; // For managing the LRU list + long lruWeight; // Weight for ordering in LRU list int maxAdj; // Maximum OOM adjustment for this process int hiddenAdj; // If hidden, this is the adjustment to use int curRawAdj; // Current OOM unlimited adjustment for this process @@ -73,6 +76,8 @@ class ProcessRecord implements Watchdog.PssRequestor { 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 + boolean empty; // Is this an empty background process? + boolean hidden; // Is this a hidden process? int lastPss; // Last pss size reported by app. String adjType; // Debugging: primary thing impacting oom_adj. int adjTypeCode; // Debugging: adj code to report to app. @@ -108,6 +113,7 @@ class ProcessRecord implements Watchdog.PssRequestor { boolean waitedForDebugger; // has process show wait for debugger dialog? Dialog waitDialog; // current wait for debugger dialog + String shortStringName; // caching of toShortString() result. String stringName; // caching of toString() result. // These reports are generated & stored when an app gets into an error condition. @@ -120,6 +126,7 @@ class ProcessRecord implements Watchdog.PssRequestor { ComponentName errorReportReceiver; void dump(PrintWriter pw, String prefix) { + long now = SystemClock.uptimeMillis(); if (info.className != null) { pw.print(prefix); pw.print("class="); pw.println(info.className); } @@ -149,6 +156,10 @@ class ProcessRecord implements Watchdog.PssRequestor { pw.print(" curReceiver="); pw.println(curReceiver); pw.print(prefix); pw.print("pid="); pw.print(pid); pw.print(" starting="); pw.print(starting); pw.print(" lastPss="); pw.println(lastPss); + pw.print(prefix); pw.print("lastActivityTime="); pw.print(lastActivityTime); + pw.print(" lruWeight="); pw.println(lruWeight); + pw.print(" hidden="); pw.print(hidden); + pw.print(" empty="); pw.println(empty); pw.print(prefix); pw.print("oom: max="); pw.print(maxAdj); pw.print(" hidden="); pw.print(hiddenAdj); pw.print(" curRaw="); pw.print(curRawAdj); @@ -220,6 +231,7 @@ class ProcessRecord implements Watchdog.PssRequestor { public void setPid(int _pid) { pid = _pid; + shortStringName = null; stringName = null; } @@ -256,12 +268,16 @@ class ProcessRecord implements Watchdog.PssRequestor { } } - public String toString() { - if (stringName != null) { - return stringName; + public String toShortString() { + if (shortStringName != null) { + return shortStringName; } StringBuilder sb = new StringBuilder(128); - sb.append("ProcessRecord{"); + toShortString(sb); + return shortStringName = sb.toString(); + } + + void toShortString(StringBuilder sb) { sb.append(Integer.toHexString(System.identityHashCode(this))); sb.append(' '); sb.append(pid); @@ -269,6 +285,15 @@ class ProcessRecord implements Watchdog.PssRequestor { sb.append(processName); sb.append('/'); sb.append(info.uid); + } + + public String toString() { + if (stringName != null) { + return stringName; + } + StringBuilder sb = new StringBuilder(128); + sb.append("ProcessRecord{"); + toShortString(sb); sb.append('}'); return stringName = sb.toString(); }