From c8230519728b14065effd3b7d4eca273ff86160c Mon Sep 17 00:00:00 2001 From: Dianne Hackborn Date: Sat, 13 Jul 2013 21:32:12 -0700 Subject: Switch proc stats to use new process state constants. These new constants are a better mapping to the kind of information that procstats is wanting to collect about processes. In doing this, the process states are tweaked to have a bit more information that we care about for procstats. This changes the format of the data printed by procstats, so the checkin version is bumped to 2. The structure is the same, however the codes for process states have all changed. The new codes are, in order of precedence: p -- persistent system process. t -- top activity; actually any visible activity. f -- important foreground process (ime, wallpaper, etc). b -- important background process u -- performing backup operation. w -- heavy-weight process (currently not used). s -- background process running a service. r -- process running a receiver. h -- process hosting home/launcher app when not on top. l -- process hosting the last app the user was in. a -- cached process hosting a previous activity. c -- cached process hosting a client activity. e -- cached process that is empty. In addition, we are now collecting uss along with pss data for each process, so the pss checkin entries now have three new values at the end of the min/avg/max uss values of that process. With this switch to using process state constants more fundamentally, I realized that they could actually be used by the core oom adj code to make it a lot cleaner. So that change has been made, that code has changed quite radically, and lost a lot of its secondary states and flags that it used to use in its computation, now relying on primarily the oom_adj and proc state values for the process. This also cleaned up a few problems -- for example for purposes of determing the memory level of the device, if a long-running service dropped into the cached oom_adj level, it would start being counted as a cached process and thus make us think that the memory state is better than it is. Now we do this based on the proc state, which always stays as a service regardless of what is happening like this, giving as a more consistent view of the memory state of the device. Making proc state a more fundamentally part of the oom adj computation means that the values can also be more carefully tuned in semantic meaning so the value assigned to a process doesn't tend to change unless the semantics of the process has really significantly changed. For example, a process will be assigned the service state regardless of whether that services is executing operations in the foreground, running normally, or has been dropped to the lru list for pruning. The top state is used for everything related to activities visible to the user: when actually on top, visible but not on top, currently pausing, etc. There is a new Context.BIND_SHOWING_UI added for when system services bind to apps, to explicitly indicate that the app is showing UI for the system. This gives us a better metric to determine when it is showing UI, and thus when it needs to do a memory trim when it is no longer in that state. Without this, services could get in bad states of continually trimming. Finally, more HashSet containers have been changed to ArraySet, reducing the temporary iterators created for iterating over them. Change-Id: I1724113f42abe7862e8aecb6faae5a7620245e89 --- core/java/android/app/ActivityManager.java | 39 +- core/java/android/content/Context.java | 10 +- core/java/android/os/Debug.java | 13 +- core/jni/android_os_Debug.cpp | 36 +- .../android/server/InputMethodManagerService.java | 2 +- .../android/server/WallpaperManagerService.java | 3 +- .../java/com/android/server/am/ActiveServices.java | 126 ++- .../android/server/am/ActivityManagerService.java | 971 ++++++++++----------- .../com/android/server/am/IntentBindRecord.java | 31 +- .../java/com/android/server/am/ProcessList.java | 43 +- .../java/com/android/server/am/ProcessRecord.java | 86 +- .../java/com/android/server/am/ProcessTracker.java | 227 ++++- 12 files changed, 869 insertions(+), 718 deletions(-) diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index c79768ef1d7d..4e6c3dcb0702 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -234,32 +234,49 @@ public class ActivityManager { /** @hide Process is a persistent system process and is doing UI. */ public static final int PROCESS_STATE_PERSISTENT_UI = 1; - /** @hide Process is hosting the current top activity. */ + /** @hide Process is hosting the current top activities. Note that this covers + * all activities that are visible to the user. */ public static final int PROCESS_STATE_TOP = 2; /** @hide Process is important to the user, and something they are aware of. */ - public static final int PROCESS_STATE_IMPORTANT_PERCEPTIBLE = 3; + public static final int PROCESS_STATE_IMPORTANT_FOREGROUND = 3; /** @hide Process is important to the user, but not something they are aware of. */ public static final int PROCESS_STATE_IMPORTANT_BACKGROUND = 4; - /** @hide Process is in the background running a receiver. */ - public static final int PROCESS_STATE_RECEIVER = 5; - /** @hide Process is in the background running a backup/restore operation. */ - public static final int PROCESS_STATE_BACKUP = 6; + public static final int PROCESS_STATE_BACKUP = 5; + + /** @hide Process is in the background, but it can't restore its state so we want + * to try to avoid killing it. */ + public static final int PROCESS_STATE_HEAVY_WEIGHT = 6; - /** @hide Process is in the background running a service. */ + /** @hide Process is in the background running a service. Unlike oom_adj, this level + * is used for both the normal running in background state and the executing + * operations state. */ public static final int PROCESS_STATE_SERVICE = 7; + /** @hide Process is in the background running a receiver. Note that from the + * perspective of oom_adj receivers run at a higher foreground level, but for our + * prioritization here that is not necessary and putting them below services means + * many fewer changes in some process states as they receive broadcasts. */ + public static final int PROCESS_STATE_RECEIVER = 8; + /** @hide Process is in the background but hosts the home activity. */ - public static final int PROCESS_STATE_HOME = 8; + public static final int PROCESS_STATE_HOME = 9; /** @hide Process is in the background but hosts the last shown activity. */ - public static final int PROCESS_STATE_LAST_ACTIVITY = 9; + public static final int PROCESS_STATE_LAST_ACTIVITY = 10; + + /** @hide Process is being cached for later use and contains activities. */ + public static final int PROCESS_STATE_CACHED_ACTIVITY = 11; + + /** @hide Process is being cached for later use and is a client of another cached + * process that contains activities. */ + public static final int PROCESS_STATE_CACHED_ACTIVITY_CLIENT = 12; - /** @hide Process is being cached for later use. */ - public static final int PROCESS_STATE_CACHED = 10; + /** @hide Process is being cached for later use and is empty. */ + public static final int PROCESS_STATE_CACHED_EMPTY = 13; /*package*/ ActivityManager(Context context, Handler handler) { mContext = context; diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index c3d1971828e4..aa326adb498f 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -230,7 +230,15 @@ public abstract class Context { * tries to balance such requests from one app vs. the importantance of * keeping other apps around. */ - public static final int BIND_VISIBLE = 0x0100; + public static final int BIND_VISIBLE = 0x10000000; + + /** + * @hide + * Flag for {@link #bindService}: Consider this binding to be causing the target + * process to be showing UI, so it will be do a UI_HIDDEN memory trim when it goes + * away. + */ + public static final int BIND_SHOWING_UI = 0x20000000; /** * Flag for {@link #bindService}: Don't consider the bound service to be diff --git a/core/java/android/os/Debug.java b/core/java/android/os/Debug.java index c5f473ea1946..0a6db25443a4 100644 --- a/core/java/android/os/Debug.java +++ b/core/java/android/os/Debug.java @@ -192,6 +192,14 @@ public final class Debug return dalvikPss + nativePss + otherPss; } + /** + * @hide Return total PSS memory usage in kB. + */ + public int getTotalUss() { + return dalvikPrivateClean + dalvikPrivateDirty + + nativePrivateClean + nativePrivateDirty + + otherPrivateClean + otherPrivateDirty; + } /** * Return total PSS memory usage in kB. @@ -1001,9 +1009,10 @@ href="{@docRoot}guide/developing/tools/traceview.html">Traceview: A Graphical Lo /** * Retrieves the PSS memory used by the process as given by the - * smaps. @hide + * smaps. Optionally supply a long array of 1 entry to also + * receive the uss of the process. @hide */ - public static native long getPss(int pid); + public static native long getPss(int pid, long[] outUss); /** * Establish an object allocation limit in the current thread. diff --git a/core/jni/android_os_Debug.cpp b/core/jni/android_os_Debug.cpp index 8b7f3dd5fd3c..61ace4a19bc5 100644 --- a/core/jni/android_os_Debug.cpp +++ b/core/jni/android_os_Debug.cpp @@ -380,10 +380,11 @@ static void android_os_Debug_getDirtyPages(JNIEnv *env, jobject clazz, jobject o android_os_Debug_getDirtyPagesPid(env, clazz, getpid(), object); } -static jlong android_os_Debug_getPssPid(JNIEnv *env, jobject clazz, jint pid) +static jlong android_os_Debug_getPssPid(JNIEnv *env, jobject clazz, jint pid, jlongArray outUss) { char line[1024]; jlong pss = 0; + jlong uss = 0; unsigned temp; char tmp[128]; @@ -398,23 +399,42 @@ static jlong android_os_Debug_getPssPid(JNIEnv *env, jobject clazz, jint pid) break; } - if (strncmp(line, "Pss: ", 5) == 0) { - char* c = line + 5; - while (*c != 0 && (*c < '0' || *c > '9')) { - c++; + if (line[0] == 'P') { + if (strncmp(line, "Pss:", 4) == 0) { + char* c = line + 4; + while (*c != 0 && (*c < '0' || *c > '9')) { + c++; + } + pss += atoi(c); + } else if (strncmp(line, "Private_Clean:", 14) + || strncmp(line, "Private_Dirty:", 14)) { + char* c = line + 14; + while (*c != 0 && (*c < '0' || *c > '9')) { + c++; + } + uss += atoi(c); } - pss += atoi(c); } } fclose(fp); + if (outUss != NULL) { + if (env->GetArrayLength(outUss) >= 1) { + jlong* outUssArray = env->GetLongArrayElements(outUss, 0); + if (outUssArray != NULL) { + outUssArray[0] = uss; + } + env->ReleaseLongArrayElements(outUss, outUssArray, 0); + } + } + return pss; } static jlong android_os_Debug_getPss(JNIEnv *env, jobject clazz) { - return android_os_Debug_getPssPid(env, clazz, getpid()); + return android_os_Debug_getPssPid(env, clazz, getpid(), NULL); } static jint read_binder_stat(const char* stat) @@ -689,7 +709,7 @@ static JNINativeMethod gMethods[] = { (void*) android_os_Debug_getDirtyPagesPid }, { "getPss", "()J", (void*) android_os_Debug_getPss }, - { "getPss", "(I)J", + { "getPss", "(I[J)J", (void*) android_os_Debug_getPssPid }, { "dumpNativeHeap", "(Ljava/io/FileDescriptor;)V", (void*) android_os_Debug_dumpNativeHeap }, diff --git a/services/java/com/android/server/InputMethodManagerService.java b/services/java/com/android/server/InputMethodManagerService.java index 1c1b0020b462..c0a521480528 100644 --- a/services/java/com/android/server/InputMethodManagerService.java +++ b/services/java/com/android/server/InputMethodManagerService.java @@ -1210,7 +1210,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub mCurIntent.putExtra(Intent.EXTRA_CLIENT_INTENT, PendingIntent.getActivity( mContext, 0, new Intent(Settings.ACTION_INPUT_METHOD_SETTINGS), 0)); if (bindCurrentInputMethodService(mCurIntent, this, Context.BIND_AUTO_CREATE - | Context.BIND_NOT_VISIBLE)) { + | Context.BIND_NOT_VISIBLE | Context.BIND_SHOWING_UI)) { mLastBindTime = SystemClock.uptimeMillis(); mHaveConnection = true; mCurId = info.getId(); diff --git a/services/java/com/android/server/WallpaperManagerService.java b/services/java/com/android/server/WallpaperManagerService.java index 6823f1363fc8..9a7390980405 100644 --- a/services/java/com/android/server/WallpaperManagerService.java +++ b/services/java/com/android/server/WallpaperManagerService.java @@ -885,7 +885,8 @@ class WallpaperManagerService extends IWallpaperManager.Stub { Intent.createChooser(new Intent(Intent.ACTION_SET_WALLPAPER), mContext.getText(com.android.internal.R.string.chooser_wallpaper)), 0, null, new UserHandle(serviceUserId))); - if (!mContext.bindServiceAsUser(intent, newConn, Context.BIND_AUTO_CREATE, + if (!mContext.bindServiceAsUser(intent, newConn, + Context.BIND_AUTO_CREATE | Context.BIND_SHOWING_UI, new UserHandle(serviceUserId))) { String msg = "Unable to bind service: " + componentName; diff --git a/services/java/com/android/server/am/ActiveServices.java b/services/java/com/android/server/am/ActiveServices.java index 5d721022990c..795e1424844c 100644 --- a/services/java/com/android/server/am/ActiveServices.java +++ b/services/java/com/android/server/am/ActiveServices.java @@ -421,7 +421,8 @@ public final class ActiveServices { private void updateServiceForegroundLocked(ProcessRecord proc, boolean oomAdj) { boolean anyForeground = false; - for (ServiceRecord sr : proc.services) { + for (int i=proc.services.size()-1; i>=0; i--) { + ServiceRecord sr = proc.services.valueAt(i); if (sr.isForeground) { anyForeground = true; break; @@ -1670,78 +1671,72 @@ public final class ActiveServices { } // Clean up any connections this application has to other services. - if (app.connections.size() > 0) { - Iterator it = app.connections.iterator(); - while (it.hasNext()) { - ConnectionRecord r = it.next(); - removeConnectionLocked(r, app, null); - } + for (int i=app.connections.size()-1; i>=0; i--) { + ConnectionRecord r = app.connections.valueAt(i); + removeConnectionLocked(r, app, null); } app.connections.clear(); - if (app.services.size() != 0) { + for (int i=app.services.size()-1; i>=0; i--) { // Any services running in the application need to be placed // back in the pending list. - Iterator it = app.services.iterator(); - while (it.hasNext()) { - ServiceRecord sr = it.next(); - synchronized (sr.stats.getBatteryStats()) { - sr.stats.stopLaunchedLocked(); - } - sr.app = null; - sr.isolatedProc = null; - sr.executeNesting = 0; - if (sr.tracker != null) { - sr.tracker.setExecuting(false, mAm.mProcessTracker.getMemFactorLocked(), - SystemClock.uptimeMillis()); - } - if (mStoppingServices.remove(sr)) { - if (DEBUG_SERVICE) Slog.v(TAG, "killServices remove stopping " + sr); - } - - final int numClients = sr.bindings.size(); - for (int bindingi=numClients-1; bindingi>=0; bindingi--) { - IntentBindRecord b = sr.bindings.valueAt(bindingi); - if (DEBUG_SERVICE) Slog.v(TAG, "Killing binding " + b - + ": shouldUnbind=" + b.hasBound); - b.binder = null; - b.requested = b.received = b.hasBound = false; - } - - if (sr.crashCount >= 2 && (sr.serviceInfo.applicationInfo.flags - &ApplicationInfo.FLAG_PERSISTENT) == 0) { - Slog.w(TAG, "Service crashed " + sr.crashCount - + " times, stopping: " + sr); - EventLog.writeEvent(EventLogTags.AM_SERVICE_CRASHED_TOO_MUCH, - sr.userId, sr.crashCount, sr.shortName, app.pid); - bringDownServiceLocked(sr); - } else if (!allowRestart) { - bringDownServiceLocked(sr); - } else { - boolean canceled = scheduleServiceRestartLocked(sr, true); - - // Should the service remain running? Note that in the - // extreme case of so many attempts to deliver a command - // that it failed we also will stop it here. - if (sr.startRequested && (sr.stopIfKilled || canceled)) { - if (sr.pendingStarts.size() == 0) { - sr.startRequested = false; - if (sr.tracker != null) { - sr.tracker.setStarted(false, mAm.mProcessTracker.getMemFactorLocked(), - SystemClock.uptimeMillis()); - } - if (!sr.hasAutoCreateConnections()) { - // Whoops, no reason to restart! - bringDownServiceLocked(sr); - } + ServiceRecord sr = app.services.valueAt(i); + synchronized (sr.stats.getBatteryStats()) { + sr.stats.stopLaunchedLocked(); + } + sr.app = null; + sr.isolatedProc = null; + sr.executeNesting = 0; + if (sr.tracker != null) { + sr.tracker.setExecuting(false, mAm.mProcessTracker.getMemFactorLocked(), + SystemClock.uptimeMillis()); + } + if (mStoppingServices.remove(sr)) { + if (DEBUG_SERVICE) Slog.v(TAG, "killServices remove stopping " + sr); + } + + final int numClients = sr.bindings.size(); + for (int bindingi=numClients-1; bindingi>=0; bindingi--) { + IntentBindRecord b = sr.bindings.valueAt(bindingi); + if (DEBUG_SERVICE) Slog.v(TAG, "Killing binding " + b + + ": shouldUnbind=" + b.hasBound); + b.binder = null; + b.requested = b.received = b.hasBound = false; + } + + if (sr.crashCount >= 2 && (sr.serviceInfo.applicationInfo.flags + &ApplicationInfo.FLAG_PERSISTENT) == 0) { + Slog.w(TAG, "Service crashed " + sr.crashCount + + " times, stopping: " + sr); + EventLog.writeEvent(EventLogTags.AM_SERVICE_CRASHED_TOO_MUCH, + sr.userId, sr.crashCount, sr.shortName, app.pid); + bringDownServiceLocked(sr); + } else if (!allowRestart) { + bringDownServiceLocked(sr); + } else { + boolean canceled = scheduleServiceRestartLocked(sr, true); + + // Should the service remain running? Note that in the + // extreme case of so many attempts to deliver a command + // that it failed we also will stop it here. + if (sr.startRequested && (sr.stopIfKilled || canceled)) { + if (sr.pendingStarts.size() == 0) { + sr.startRequested = false; + if (sr.tracker != null) { + sr.tracker.setStarted(false, mAm.mProcessTracker.getMemFactorLocked(), + SystemClock.uptimeMillis()); + } + if (!sr.hasAutoCreateConnections()) { + // Whoops, no reason to restart! + bringDownServiceLocked(sr); } } } } + } - if (!allowRestart) { - app.services.clear(); - } + if (!allowRestart) { + app.services.clear(); } // Make sure we have no more records on the stopping list. @@ -1880,11 +1875,10 @@ public final class ActiveServices { return; } long maxTime = SystemClock.uptimeMillis() - SERVICE_TIMEOUT; - Iterator it = proc.executingServices.iterator(); ServiceRecord timeout = null; long nextTime = 0; - while (it.hasNext()) { - ServiceRecord sr = it.next(); + for (int i=proc.executingServices.size()-1; i>=0; i--) { + ServiceRecord sr = proc.executingServices.valueAt(i); if (sr.executingStart < maxTime) { timeout = sr; break; diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java index c41c23ee73ab..591b3917fba3 100644 --- a/services/java/com/android/server/am/ActivityManagerService.java +++ b/services/java/com/android/server/am/ActivityManagerService.java @@ -1505,6 +1505,7 @@ public final class ActivityManagerService extends ActivityManagerNative case COLLECT_PSS_BG_MSG: { int i=0; long start = SystemClock.uptimeMillis(); + long[] tmp = new long[1]; do { ProcessRecord proc; int oomAdj; @@ -1528,10 +1529,10 @@ public final class ActivityManagerService extends ActivityManagerNative i++; } if (proc != null) { - long pss = Debug.getPss(pid); + long pss = Debug.getPss(pid, tmp); synchronized (ActivityManagerService.this) { if (proc.thread != null && proc.setAdj == oomAdj && proc.pid == pid) { - proc.baseProcessTracker.addPss(pss, true); + proc.baseProcessTracker.addPss(pss, tmp[0], true); } } } @@ -2157,13 +2158,12 @@ public final class ActivityManagerService extends ActivityManagerNative // If the app is currently using a content provider or service, // bump those processes as well. - if (app.connections.size() > 0) { - for (ConnectionRecord cr : app.connections) { - if (cr.binding != null && cr.binding.service != null - && cr.binding.service.app != null - && cr.binding.service.app.lruSeq != mLruSeq) { - updateLruProcessInternalLocked(cr.binding.service.app, i+1); - } + for (int j=app.connections.size()-1; j>=0; j--) { + ConnectionRecord cr = app.connections.valueAt(j); + if (cr.binding != null && cr.binding.service != null + && cr.binding.service.app != null + && cr.binding.service.app.lruSeq != mLruSeq) { + updateLruProcessInternalLocked(cr.binding.service.app, i+1); } } for (int j=app.conProviders.size()-1; j>=0; j--) { @@ -4010,7 +4010,8 @@ public final class ActivityManagerService extends ActivityManagerNative synchronized (this) { if (proc.thread != null && proc.setAdj == oomAdj) { // Record this for posterity if the process has been stable. - proc.baseProcessTracker.addPss(infos[i].getTotalPss(), false); + proc.baseProcessTracker.addPss(infos[i].getTotalPss(), + infos[i].getTotalUss(), false); } } } @@ -4031,12 +4032,13 @@ public final class ActivityManagerService extends ActivityManagerNative oomAdj = proc != null ? proc.setAdj : 0; } } - pss[i] = Debug.getPss(pids[i]); + long[] tmpUss = new long[1]; + pss[i] = Debug.getPss(pids[i], tmpUss); if (proc != null) { synchronized (this) { if (proc.thread != null && proc.setAdj == oomAdj) { // Record this for posterity if the process has been stable. - proc.baseProcessTracker.addPss(pss[i], false); + proc.baseProcessTracker.addPss(pss[i], tmpUss[0], false); } } } @@ -8802,14 +8804,11 @@ public final class ActivityManagerService extends ActivityManagerNative } // Bump up the crash count of any services currently running in the proc. - if (app.services.size() != 0) { + for (int i=app.services.size()-1; i>=0; i--) { // Any services running in the application need to be placed // back in the pending list. - Iterator it = app.services.iterator(); - while (it.hasNext()) { - ServiceRecord sr = it.next(); - sr.crashCount++; - } + ServiceRecord sr = app.services.valueAt(i); + sr.crashCount++; } // If the crashing process is what we consider to be the "home process" and it has been @@ -10269,8 +10268,8 @@ public final class ActivityManagerService extends ActivityManagerNative pw.print(" FOREGROUND_APP_ADJ: "); pw.println(ProcessList.FOREGROUND_APP_ADJ); pw.print(" VISIBLE_APP_ADJ: "); pw.println(ProcessList.VISIBLE_APP_ADJ); pw.print(" PERCEPTIBLE_APP_ADJ: "); pw.println(ProcessList.PERCEPTIBLE_APP_ADJ); - pw.print(" HEAVY_WEIGHT_APP_ADJ: "); pw.println(ProcessList.HEAVY_WEIGHT_APP_ADJ); pw.print(" BACKUP_APP_ADJ: "); pw.println(ProcessList.BACKUP_APP_ADJ); + pw.print(" HEAVY_WEIGHT_APP_ADJ: "); pw.println(ProcessList.HEAVY_WEIGHT_APP_ADJ); pw.print(" SERVICE_ADJ: "); pw.println(ProcessList.SERVICE_ADJ); pw.print(" HOME_APP_ADJ: "); pw.println(ProcessList.HOME_APP_ADJ); pw.print(" PREVIOUS_APP_ADJ: "); pw.println(ProcessList.PREVIOUS_APP_ADJ); @@ -10772,10 +10771,10 @@ public final class ActivityManagerService extends ActivityManagerNative oomAdj = buildOomTag("home ", null, r.setAdj, ProcessList.HOME_APP_ADJ); } else if (r.setAdj >= ProcessList.SERVICE_ADJ) { oomAdj = buildOomTag("svc ", null, r.setAdj, ProcessList.SERVICE_ADJ); - } else if (r.setAdj >= ProcessList.BACKUP_APP_ADJ) { - oomAdj = buildOomTag("bkup ", null, r.setAdj, ProcessList.BACKUP_APP_ADJ); } else if (r.setAdj >= ProcessList.HEAVY_WEIGHT_APP_ADJ) { oomAdj = buildOomTag("hvy ", null, r.setAdj, ProcessList.HEAVY_WEIGHT_APP_ADJ); + } else if (r.setAdj >= ProcessList.BACKUP_APP_ADJ) { + oomAdj = buildOomTag("bkup ", null, r.setAdj, ProcessList.BACKUP_APP_ADJ); } else if (r.setAdj >= ProcessList.PERCEPTIBLE_APP_ADJ) { oomAdj = buildOomTag("prcp ", null, r.setAdj, ProcessList.PERCEPTIBLE_APP_ADJ); } else if (r.setAdj >= ProcessList.VISIBLE_APP_ADJ) { @@ -10820,29 +10819,38 @@ public final class ActivityManagerService extends ActivityManagerNative case ActivityManager.PROCESS_STATE_TOP: procState = "T "; break; - case ActivityManager.PROCESS_STATE_IMPORTANT_PERCEPTIBLE: - procState = "IP"; + case ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND: + procState = "IF"; break; case ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND: procState = "IB"; break; - case ActivityManager.PROCESS_STATE_RECEIVER: - procState = "R "; - break; case ActivityManager.PROCESS_STATE_BACKUP: procState = "BU"; break; + case ActivityManager.PROCESS_STATE_HEAVY_WEIGHT: + procState = "HW"; + break; case ActivityManager.PROCESS_STATE_SERVICE: procState = "S "; break; + case ActivityManager.PROCESS_STATE_RECEIVER: + procState = "R "; + break; case ActivityManager.PROCESS_STATE_HOME: - procState = "H "; + procState = "HO"; break; case ActivityManager.PROCESS_STATE_LAST_ACTIVITY: procState = "LA"; break; - case ActivityManager.PROCESS_STATE_CACHED: - procState = "C "; + case ActivityManager.PROCESS_STATE_CACHED_ACTIVITY: + procState = "CA"; + break; + case ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT: + procState = "Ca"; + break; + case ActivityManager.PROCESS_STATE_CACHED_EMPTY: + procState = "CE"; break; default: procState = "??"; @@ -10895,9 +10903,6 @@ public final class ActivityManagerService extends ActivityManagerNative pw.print(prefix); pw.print(" "); pw.print("oom: max="); pw.print(r.maxAdj); - pw.print(" cached="); pw.print(r.cachedAdj); - pw.print(" client="); pw.print(r.clientCachedAdj); - pw.print(" empty="); pw.print(r.emptyAdj); pw.print(" curRaw="); pw.print(r.curRawAdj); pw.print(" setRaw="); pw.print(r.setRawAdj); pw.print(" cur="); pw.print(r.curAdj); @@ -11139,21 +11144,24 @@ public final class ActivityManagerService extends ActivityManagerNative static final int[] DUMP_MEM_OOM_ADJ = new int[] { ProcessList.SYSTEM_ADJ, ProcessList.PERSISTENT_PROC_ADJ, ProcessList.FOREGROUND_APP_ADJ, - ProcessList.VISIBLE_APP_ADJ, ProcessList.PERCEPTIBLE_APP_ADJ, ProcessList.HEAVY_WEIGHT_APP_ADJ, - ProcessList.BACKUP_APP_ADJ, ProcessList.SERVICE_ADJ, ProcessList.HOME_APP_ADJ, + ProcessList.VISIBLE_APP_ADJ, ProcessList.PERCEPTIBLE_APP_ADJ, + ProcessList.BACKUP_APP_ADJ, ProcessList.HEAVY_WEIGHT_APP_ADJ, + ProcessList.SERVICE_ADJ, ProcessList.HOME_APP_ADJ, ProcessList.PREVIOUS_APP_ADJ, ProcessList.SERVICE_B_ADJ, ProcessList.CACHED_APP_MAX_ADJ }; static final String[] DUMP_MEM_OOM_LABEL = new String[] { "System", "Persistent", "Foreground", - "Visible", "Perceptible", "Heavy Weight", - "Backup", "A Services", "Home", "Previous", - "B Services", "Cached" + "Visible", "Perceptible", + "Heavy Weight", "Backup", + "A Services", "Home", + "Previous", "B Services", "Cached" }; static final String[] DUMP_MEM_OOM_COMPACT_LABEL = new String[] { "sys", "pers", "fore", - "vis", "percept", "heavy", - "backup", "servicea", "home", "prev", - "serviceb", "cached" + "vis", "percept", + "heavy", "backup", + "servicea", "home", + "prev", "serviceb", "cached" }; final void dumpApplicationMemoryUsage(FileDescriptor fd, @@ -11225,6 +11233,7 @@ public final class ActivityManagerService extends ActivityManagerNative long oomPss[] = new long[DUMP_MEM_OOM_LABEL.length]; ArrayList[] oomProcs = (ArrayList[]) new ArrayList[DUMP_MEM_OOM_LABEL.length]; + final long[] tmpLong = new long[1]; long totalPss = 0; long cachedPss = 0; @@ -11264,16 +11273,18 @@ public final class ActivityManagerService extends ActivityManagerNative if (!brief && !oomOnly) { Debug.getMemoryInfo(pid, mi); } else { - mi.dalvikPss = (int)Debug.getPss(pid); + mi.dalvikPss = (int)Debug.getPss(pid, tmpLong); + mi.dalvikPrivateDirty = (int)tmpLong[0]; } } final long myTotalPss = mi.getTotalPss(); + final long myTotalUss = mi.getTotalUss(); synchronized (this) { if (r.thread != null && oomAdj == r.getSetAdjWithServices()) { // Record this for posterity if the process has been stable. - r.baseProcessTracker.addPss(myTotalPss, true); + r.baseProcessTracker.addPss(myTotalPss, myTotalUss, true); } } @@ -11642,14 +11653,11 @@ public final class ActivityManagerService extends ActivityManagerNative skipCurrentReceiverLocked(app); // Unregister any receivers. - if (app.receivers.size() > 0) { - Iterator it = app.receivers.iterator(); - while (it.hasNext()) { - removeReceiverLocked(it.next()); - } - app.receivers.clear(); + for (int i=app.receivers.size()-1; i>=0; i--) { + removeReceiverLocked(app.receivers.valueAt(i)); } - + app.receivers.clear(); + // If the app is undergoing backup, tell the backup manager about it if (mBackupTarget != null && app.pid == mBackupTarget.app.pid) { if (DEBUG_BACKUP || DEBUG_CLEANUP) Slog.d(TAG, "App " @@ -13409,29 +13417,17 @@ public final class ActivityManagerService extends ActivityManagerNative return null; } - private final int computeOomAdjLocked(ProcessRecord app, int cachedAdj, int clientCachedAdj, - int emptyAdj, ProcessRecord TOP_APP, boolean recursed, boolean doingAll) { + private final int computeOomAdjLocked(ProcessRecord app, int cachedAdj, ProcessRecord TOP_APP, + boolean doingAll, long now) { if (mAdjSeq == app.adjSeq) { - // This adjustment has already been computed. If we are calling - // from the top, we may have already computed our adjustment with - // an earlier cached adjustment that isn't really for us... if - // so, use the new cached adjustment. - if (!recursed && app.cached) { - if (app.hasActivities) { - app.curAdj = app.curRawAdj = app.nonStoppingAdj = cachedAdj; - } else if (app.hasClientActivities) { - app.curAdj = app.curRawAdj = app.nonStoppingAdj = clientCachedAdj; - } else { - app.curAdj = app.curRawAdj = app.nonStoppingAdj = emptyAdj; - } - } + // This adjustment has already been computed. return app.curRawAdj; } if (app.thread == null) { app.adjSeq = mAdjSeq; app.curSchedGroup = Process.THREAD_GROUP_BG_NONINTERACTIVE; - app.curProcState = ActivityManager.PROCESS_STATE_CACHED; + app.curProcState = ActivityManager.PROCESS_STATE_CACHED_EMPTY; return (app.curAdj=app.curRawAdj=ProcessList.CACHED_APP_MAX_ADJ); } @@ -13449,7 +13445,7 @@ public final class ActivityManagerService extends ActivityManagerNative // below foreground, so it is not worth doing work for it. app.adjType = "fixed"; app.adjSeq = mAdjSeq; - app.curRawAdj = app.nonStoppingAdj = app.maxAdj; + app.curRawAdj = app.maxAdj; app.hasActivities = false; app.foregroundActivities = false; app.keeping = true; @@ -13507,7 +13503,7 @@ public final class ActivityManagerService extends ActivityManagerNative schedGroup = Process.THREAD_GROUP_DEFAULT; app.adjType = "instrumentation"; interesting = true; - procState = ActivityManager.PROCESS_STATE_IMPORTANT_PERCEPTIBLE; + procState = ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND; } else if ((queue = isReceivingBroadcast(app)) != null) { // An app that is currently receiving a broadcast also // counts as being in the foreground for OOM killer purposes. @@ -13526,33 +13522,39 @@ public final class ActivityManagerService extends ActivityManagerNative app.adjType = "exec-service"; procState = ActivityManager.PROCESS_STATE_SERVICE; } else { - // Assume process is cached (has activities); we will correct - // later if this is not the case. - adj = cachedAdj; + // As far as we know the process is empty. We may change our mind later. schedGroup = Process.THREAD_GROUP_BG_NONINTERACTIVE; + // At this point we don't actually know the adjustment. Use the cached adj + // value that the caller wants us to. + adj = cachedAdj; + procState = ActivityManager.PROCESS_STATE_CACHED_EMPTY; app.cached = true; - app.adjType = "cch-act"; - procState = ActivityManager.PROCESS_STATE_CACHED; + app.empty = true; + app.adjType = "cch-empty"; } - boolean hasStoppingActivities = false; - // Examine all activities if not already foreground. if (!foregroundActivities && activitiesSize > 0) { for (int j = 0; j < activitiesSize; j++) { final ActivityRecord r = app.activities.get(j); + if (r.app != app) { + Slog.w(TAG, "Wtf, activity " + r + " in proc activity list not using proc " + + app + "?!?"); + continue; + } + app.hasActivities = true; if (r.visible) { // App has a visible activity; only upgrade adjustment. if (adj > ProcessList.VISIBLE_APP_ADJ) { adj = ProcessList.VISIBLE_APP_ADJ; app.adjType = "visible"; } - if (procState > ActivityManager.PROCESS_STATE_IMPORTANT_PERCEPTIBLE) { - procState = ActivityManager.PROCESS_STATE_IMPORTANT_PERCEPTIBLE; + if (procState > ActivityManager.PROCESS_STATE_TOP) { + procState = ActivityManager.PROCESS_STATE_TOP; } schedGroup = Process.THREAD_GROUP_DEFAULT; app.cached = false; - app.hasActivities = true; + app.empty = false; foregroundActivities = true; break; } else if (r.state == ActivityState.PAUSING || r.state == ActivityState.PAUSED) { @@ -13560,53 +13562,54 @@ public final class ActivityManagerService extends ActivityManagerNative adj = ProcessList.PERCEPTIBLE_APP_ADJ; app.adjType = "pausing"; } - if (procState > ActivityManager.PROCESS_STATE_IMPORTANT_PERCEPTIBLE) { - procState = ActivityManager.PROCESS_STATE_IMPORTANT_PERCEPTIBLE; + if (procState > ActivityManager.PROCESS_STATE_TOP) { + procState = ActivityManager.PROCESS_STATE_TOP; } + schedGroup = Process.THREAD_GROUP_DEFAULT; app.cached = false; + app.empty = false; foregroundActivities = true; } else if (r.state == ActivityState.STOPPING) { - // We will apply the actual adjustment later, because - // we want to allow this process to immediately go through - // any memory trimming that is in effect. - if (procState > ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND) { - procState = ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND; + if (adj > ProcessList.PERCEPTIBLE_APP_ADJ) { + adj = ProcessList.PERCEPTIBLE_APP_ADJ; + app.adjType = "stopping"; + } + // For the process state, we will at this point consider the + // process to be cached. It will be cached either as an activity + // or empty depending on whether the activity is finishing. We do + // this so that we can treat the process as cached for purposes of + // memory trimming (determing current memory level, trim command to + // send to process) since there can be an arbitrary number of stopping + // processes and they should soon all go into the cached state. + if (!r.finishing) { + if (procState > ActivityManager.PROCESS_STATE_CACHED_ACTIVITY) { + procState = ActivityManager.PROCESS_STATE_CACHED_ACTIVITY; + } } app.cached = false; + app.empty = false; foregroundActivities = true; - hasStoppingActivities = true; - } - if (r.app == app) { - app.hasActivities = true; + } else { + if (procState > ActivityManager.PROCESS_STATE_CACHED_ACTIVITY) { + procState = ActivityManager.PROCESS_STATE_CACHED_ACTIVITY; + app.adjType = "cch-act"; + } } } } - if (adj == cachedAdj && !app.hasActivities) { - if (app.hasClientActivities) { - adj = clientCachedAdj; - app.adjType = "cch-client-act"; - } else { - // Whoops, this process is completely empty as far as we know - // at this point. - adj = emptyAdj; - app.empty = true; - app.adjType = "cch-empty"; - } - } - if (adj > ProcessList.PERCEPTIBLE_APP_ADJ) { if (app.foregroundServices) { // The user is aware of this app, so make it visible. adj = ProcessList.PERCEPTIBLE_APP_ADJ; - procState = ActivityManager.PROCESS_STATE_IMPORTANT_PERCEPTIBLE; + procState = ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND; app.cached = false; app.adjType = "fg-service"; schedGroup = Process.THREAD_GROUP_DEFAULT; } else if (app.forcingToForeground != null) { // The user is aware of this app, so make it visible. adj = ProcessList.PERCEPTIBLE_APP_ADJ; - procState = ActivityManager.PROCESS_STATE_IMPORTANT_PERCEPTIBLE; + procState = ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND; app.cached = false; app.adjType = "force-fg"; app.adjSource = app.forcingToForeground; @@ -13618,12 +13621,17 @@ public final class ActivityManagerService extends ActivityManagerNative interesting = true; } - if (adj > ProcessList.HEAVY_WEIGHT_APP_ADJ && app == mHeavyWeightProcess) { - // We don't want to kill the current heavy-weight process. - adj = ProcessList.HEAVY_WEIGHT_APP_ADJ; - schedGroup = Process.THREAD_GROUP_BG_NONINTERACTIVE; - app.cached = false; - app.adjType = "heavy"; + if (app == mHeavyWeightProcess) { + if (adj > ProcessList.HEAVY_WEIGHT_APP_ADJ) { + // We don't want to kill the current heavy-weight process. + adj = ProcessList.HEAVY_WEIGHT_APP_ADJ; + schedGroup = Process.THREAD_GROUP_BG_NONINTERACTIVE; + app.cached = false; + app.adjType = "heavy"; + } + if (procState > ActivityManager.PROCESS_STATE_HEAVY_WEIGHT) { + procState = ActivityManager.PROCESS_STATE_HEAVY_WEIGHT; + } } if (app == mHomeProcess) { @@ -13663,7 +13671,7 @@ public final class ActivityManagerService extends ActivityManagerNative // this gives us a baseline and makes sure we don't get into an // infinite recursion. app.adjSeq = mAdjSeq; - app.curRawAdj = app.nonStoppingAdj = adj; + app.curRawAdj = adj; app.hasStartedServices = false; if (mBackupTarget != null && app == mBackupTarget.app) { @@ -13682,243 +13690,220 @@ public final class ActivityManagerService extends ActivityManagerNative } } - if (app.services.size() != 0 && (adj > ProcessList.FOREGROUND_APP_ADJ - || schedGroup == Process.THREAD_GROUP_BG_NONINTERACTIVE)) { - final long now = SystemClock.uptimeMillis(); - // This process is more important if the top activity is - // bound to the service. - Iterator jt = app.services.iterator(); - while (jt.hasNext() && adj > ProcessList.FOREGROUND_APP_ADJ) { - ServiceRecord s = jt.next(); - if (s.startRequested) { - app.hasStartedServices = true; - if (procState > ActivityManager.PROCESS_STATE_SERVICE) { - procState = ActivityManager.PROCESS_STATE_SERVICE; - } - if (app.hasShownUi && app != mHomeProcess) { - // If this process has shown some UI, let it immediately - // go to the LRU list because it may be pretty heavy with - // UI stuff. We'll tag it with a label just to help - // debug and understand what is going on. - if (adj > ProcessList.SERVICE_ADJ) { - app.adjType = "cch-started-ui-services"; - } - } else { - if (now < (s.lastActivity + ActiveServices.MAX_SERVICE_INACTIVITY)) { - // This service has seen some activity within - // recent memory, so we will keep its process ahead - // of the background processes. - if (adj > ProcessList.SERVICE_ADJ) { - adj = ProcessList.SERVICE_ADJ; - app.adjType = "started-services"; - app.cached = false; - } - } - // If we have let the service slide into the background - // state, still have some text describing what it is doing - // even though the service no longer has an impact. + for (int is = app.services.size()-1; + is >= 0 && (adj > ProcessList.FOREGROUND_APP_ADJ + || schedGroup == Process.THREAD_GROUP_BG_NONINTERACTIVE + || procState > ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND); + is--) { + ServiceRecord s = app.services.valueAt(is); + if (s.startRequested) { + app.hasStartedServices = true; + if (procState > ActivityManager.PROCESS_STATE_SERVICE) { + procState = ActivityManager.PROCESS_STATE_SERVICE; + } + if (app.hasShownUi && app != mHomeProcess) { + // If this process has shown some UI, let it immediately + // go to the LRU list because it may be pretty heavy with + // UI stuff. We'll tag it with a label just to help + // debug and understand what is going on. + if (adj > ProcessList.SERVICE_ADJ) { + app.adjType = "cch-started-ui-services"; + } + } else { + if (now < (s.lastActivity + ActiveServices.MAX_SERVICE_INACTIVITY)) { + // This service has seen some activity within + // recent memory, so we will keep its process ahead + // of the background processes. if (adj > ProcessList.SERVICE_ADJ) { - app.adjType = "cch-started-services"; + adj = ProcessList.SERVICE_ADJ; + app.adjType = "started-services"; + app.cached = false; } } - // Don't kill this process because it is doing work; it - // has said it is doing work. - app.keeping = true; - } - for (int conni = s.connections.size()-1; - conni >= 0 && (adj > ProcessList.FOREGROUND_APP_ADJ - || schedGroup == Process.THREAD_GROUP_BG_NONINTERACTIVE); - conni--) { - ArrayList clist = s.connections.valueAt(conni); - for (int i = 0; - i < clist.size() && (adj > ProcessList.FOREGROUND_APP_ADJ - || schedGroup == Process.THREAD_GROUP_BG_NONINTERACTIVE); - i++) { - // XXX should compute this based on the max of - // all connected clients. - ConnectionRecord cr = clist.get(i); - if (cr.binding.client == app) { - // Binding to ourself is not interesting. - continue; - } - if ((cr.flags&Context.BIND_WAIVE_PRIORITY) == 0) { - ProcessRecord client = cr.binding.client; - int myCachedAdj = cachedAdj; - if (myCachedAdj > client.cachedAdj) { - if (client.cachedAdj >= ProcessList.VISIBLE_APP_ADJ) { - myCachedAdj = client.cachedAdj; - } else { - myCachedAdj = ProcessList.VISIBLE_APP_ADJ; - } - } - int myClientCachedAdj = clientCachedAdj; - if (myClientCachedAdj > client.clientCachedAdj) { - if (client.clientCachedAdj >= ProcessList.VISIBLE_APP_ADJ) { - myClientCachedAdj = client.clientCachedAdj; - } else { - myClientCachedAdj = ProcessList.VISIBLE_APP_ADJ; - } - } - int myEmptyAdj = emptyAdj; - if (myEmptyAdj > client.emptyAdj) { - if (client.emptyAdj >= ProcessList.VISIBLE_APP_ADJ) { - myEmptyAdj = client.emptyAdj; - } else { - myEmptyAdj = ProcessList.VISIBLE_APP_ADJ; + // If we have let the service slide into the background + // state, still have some text describing what it is doing + // even though the service no longer has an impact. + if (adj > ProcessList.SERVICE_ADJ) { + app.adjType = "cch-started-services"; + } + } + // Don't kill this process because it is doing work; it + // has said it is doing work. + app.keeping = true; + } + for (int conni = s.connections.size()-1; + conni >= 0 && (adj > ProcessList.FOREGROUND_APP_ADJ + || schedGroup == Process.THREAD_GROUP_BG_NONINTERACTIVE + || procState > ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND); + conni--) { + ArrayList clist = s.connections.valueAt(conni); + for (int i = 0; + i < clist.size() && (adj > ProcessList.FOREGROUND_APP_ADJ + || schedGroup == Process.THREAD_GROUP_BG_NONINTERACTIVE + || procState > ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND); + i++) { + // XXX should compute this based on the max of + // all connected clients. + ConnectionRecord cr = clist.get(i); + if (cr.binding.client == app) { + // Binding to ourself is not interesting. + continue; + } + if ((cr.flags&Context.BIND_WAIVE_PRIORITY) == 0) { + ProcessRecord client = cr.binding.client; + int clientAdj = computeOomAdjLocked(client, cachedAdj, + TOP_APP, doingAll, now); + int clientProcState = client.curProcState; + String adjType = null; + if ((cr.flags&Context.BIND_ALLOW_OOM_MANAGEMENT) != 0) { + // Not doing bind OOM management, so treat + // this guy more like a started service. + if (app.hasShownUi && app != mHomeProcess) { + // If this process has shown some UI, let it immediately + // go to the LRU list because it may be pretty heavy with + // UI stuff. We'll tag it with a label just to help + // debug and understand what is going on. + if (adj > clientAdj) { + adjType = "cch-bound-ui-services"; } - } - int clientAdj = computeOomAdjLocked(client, myCachedAdj, - myClientCachedAdj, myEmptyAdj, TOP_APP, true, doingAll); - int clientProcState = client.curProcState; - String adjType = null; - if ((cr.flags&Context.BIND_ALLOW_OOM_MANAGEMENT) != 0) { - // Not doing bind OOM management, so treat - // this guy more like a started service. - if (app.hasShownUi && app != mHomeProcess) { - // If this process has shown some UI, let it immediately - // go to the LRU list because it may be pretty heavy with - // UI stuff. We'll tag it with a label just to help - // debug and understand what is going on. + app.cached = false; + clientAdj = adj; + clientProcState = procState; + } else { + if (now >= (s.lastActivity + + ActiveServices.MAX_SERVICE_INACTIVITY)) { + // This service has not seen activity within + // recent memory, so allow it to drop to the + // LRU list if there is no other reason to keep + // it around. We'll also tag it with a label just + // to help debug and undertand what is going on. if (adj > clientAdj) { - adjType = "cch-bound-ui-services"; + adjType = "cch-bound-services"; } - app.cached = false; clientAdj = adj; - clientProcState = procState; - } else { - if (now >= (s.lastActivity - + ActiveServices.MAX_SERVICE_INACTIVITY)) { - // This service has not seen activity within - // recent memory, so allow it to drop to the - // LRU list if there is no other reason to keep - // it around. We'll also tag it with a label just - // to help debug and undertand what is going on. - if (adj > clientAdj) { - adjType = "cch-bound-services"; - } - clientAdj = adj; - } - } - } else if ((cr.flags&Context.BIND_AUTO_CREATE) != 0) { - if ((cr.flags&Context.BIND_NOT_VISIBLE) == 0) { - // If this connection is keeping the service - // created, then we want to try to better follow - // its memory management semantics for activities. - // That is, if it is sitting in the background - // LRU list as a cached process (with activities), - // we don't want the service it is connected to - // to go into the empty LRU and quickly get killed, - // because all we'll do is just end up restarting - // the service. - app.hasClientActivities |= client.hasActivities; } } - if (adj > clientAdj) { - // If this process has recently shown UI, and - // the process that is binding to it is less - // important than being visible, then we don't - // care about the binding as much as we care - // about letting this process get into the LRU - // list to be killed and restarted if needed for - // memory. - if (app.hasShownUi && app != mHomeProcess - && clientAdj > ProcessList.PERCEPTIBLE_APP_ADJ) { - adjType = "cch-bound-ui-services"; - } else { - if ((cr.flags&(Context.BIND_ABOVE_CLIENT - |Context.BIND_IMPORTANT)) != 0) { - adj = clientAdj; - } else if ((cr.flags&Context.BIND_NOT_VISIBLE) != 0 - && clientAdj < ProcessList.PERCEPTIBLE_APP_ADJ - && adj > ProcessList.PERCEPTIBLE_APP_ADJ) { - adj = ProcessList.PERCEPTIBLE_APP_ADJ; - } else if (clientAdj > ProcessList.VISIBLE_APP_ADJ) { - adj = clientAdj; - } else { - app.pendingUiClean = true; - if (adj > ProcessList.VISIBLE_APP_ADJ) { - adj = ProcessList.VISIBLE_APP_ADJ; - } - } - if (!client.cached) { - app.cached = false; - } - if (client.keeping) { - app.keeping = true; + } else if ((cr.flags&Context.BIND_AUTO_CREATE) != 0) { + if ((cr.flags&Context.BIND_NOT_VISIBLE) == 0) { + // If this connection is keeping the service + // created, then we want to try to better follow + // its memory management semantics for activities. + // That is, if it is sitting in the background + // LRU list as a cached process (with activities), + // we don't want the service it is connected to + // to go into the empty LRU and quickly get killed, + // because all we'll do is just end up restarting + // the service. + if (client.hasActivities) { + if (procState > + ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT) { + procState = + ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT; + app.adjType = "cch-client-act"; } - adjType = "service"; } + app.hasClientActivities |= client.hasActivities; } - if ((cr.flags&Context.BIND_NOT_FOREGROUND) == 0) { - if (client.curSchedGroup == Process.THREAD_GROUP_DEFAULT) { - schedGroup = Process.THREAD_GROUP_DEFAULT; + } + if (adj > clientAdj) { + // If this process has recently shown UI, and + // the process that is binding to it is less + // important than being visible, then we don't + // care about the binding as much as we care + // about letting this process get into the LRU + // list to be killed and restarted if needed for + // memory. + if (app.hasShownUi && app != mHomeProcess + && clientAdj > ProcessList.PERCEPTIBLE_APP_ADJ) { + adjType = "cch-bound-ui-services"; + } else { + if ((cr.flags&(Context.BIND_ABOVE_CLIENT + |Context.BIND_IMPORTANT)) != 0) { + adj = clientAdj; + } else if ((cr.flags&Context.BIND_NOT_VISIBLE) != 0 + && clientAdj < ProcessList.PERCEPTIBLE_APP_ADJ + && adj > ProcessList.PERCEPTIBLE_APP_ADJ) { + adj = ProcessList.PERCEPTIBLE_APP_ADJ; + } else if (clientAdj > ProcessList.VISIBLE_APP_ADJ) { + adj = clientAdj; + } else { + if (adj > ProcessList.VISIBLE_APP_ADJ) { + adj = ProcessList.VISIBLE_APP_ADJ; + } } - if (clientProcState < - ActivityManager.PROCESS_STATE_IMPORTANT_PERCEPTIBLE) { - clientProcState = - ActivityManager.PROCESS_STATE_IMPORTANT_PERCEPTIBLE; + if (!client.cached) { + app.cached = false; } - } else { - if (clientProcState < - ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND) { - clientProcState = - ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND; + if (client.keeping) { + app.keeping = true; } + adjType = "service"; + } + } + if ((cr.flags&Context.BIND_NOT_FOREGROUND) == 0) { + if (client.curSchedGroup == Process.THREAD_GROUP_DEFAULT) { + schedGroup = Process.THREAD_GROUP_DEFAULT; } - if (procState > clientProcState) { - procState = clientProcState; + if (clientProcState < + ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND) { + clientProcState = + ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND; } - if (adjType != null) { - app.adjType = adjType; - app.adjTypeCode = ActivityManager.RunningAppProcessInfo - .REASON_SERVICE_IN_USE; - app.adjSource = cr.binding.client; - app.adjSourceOom = clientAdj; - app.adjTarget = s.name; + } else { + if (clientProcState < + ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND) { + clientProcState = + ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND; } } - final ActivityRecord a = cr.activity; - if ((cr.flags&Context.BIND_ADJUST_WITH_ACTIVITY) != 0) { - if (a != null && adj > ProcessList.FOREGROUND_APP_ADJ && - (a.visible || a.state == ActivityState.RESUMED - || a.state == ActivityState.PAUSING)) { - adj = ProcessList.FOREGROUND_APP_ADJ; - if ((cr.flags&Context.BIND_NOT_FOREGROUND) == 0) { - schedGroup = Process.THREAD_GROUP_DEFAULT; - } - app.cached = false; - app.adjType = "service"; - app.adjTypeCode = ActivityManager.RunningAppProcessInfo - .REASON_SERVICE_IN_USE; - app.adjSource = a; - app.adjSourceOom = adj; - app.adjTarget = s.name; + if (procState > clientProcState) { + procState = clientProcState; + } + if (procState < ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND + && (cr.flags&Context.BIND_SHOWING_UI) != 0) { + app.pendingUiClean = true; + } + if (adjType != null) { + app.adjType = adjType; + app.adjTypeCode = ActivityManager.RunningAppProcessInfo + .REASON_SERVICE_IN_USE; + app.adjSource = cr.binding.client; + app.adjSourceOom = clientAdj; + app.adjTarget = s.name; + } + } + final ActivityRecord a = cr.activity; + if ((cr.flags&Context.BIND_ADJUST_WITH_ACTIVITY) != 0) { + if (a != null && adj > ProcessList.FOREGROUND_APP_ADJ && + (a.visible || a.state == ActivityState.RESUMED + || a.state == ActivityState.PAUSING)) { + adj = ProcessList.FOREGROUND_APP_ADJ; + if ((cr.flags&Context.BIND_NOT_FOREGROUND) == 0) { + schedGroup = Process.THREAD_GROUP_DEFAULT; } + app.cached = false; + app.adjType = "service"; + app.adjTypeCode = ActivityManager.RunningAppProcessInfo + .REASON_SERVICE_IN_USE; + app.adjSource = a; + app.adjSourceOom = adj; + app.adjTarget = s.name; } } } } - - // Finally, if this process has active services running in it, we - // would like to avoid killing it unless it would prevent the current - // application from running. By default we put the process in - // with the rest of the background processes; as we scan through - // its services we may bump it up from there. - if (adj > cachedAdj) { - adj = cachedAdj; - app.cached = false; - app.adjType = "cch-services"; - } } for (int provi = app.pubProviders.size()-1; provi >= 0 && (adj > ProcessList.FOREGROUND_APP_ADJ - || schedGroup == Process.THREAD_GROUP_BG_NONINTERACTIVE); + || schedGroup == Process.THREAD_GROUP_BG_NONINTERACTIVE + || procState > ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND); provi--) { ContentProviderRecord cpr = app.pubProviders.valueAt(provi); for (int i = cpr.connections.size()-1; i >= 0 && (adj > ProcessList.FOREGROUND_APP_ADJ - || schedGroup == Process.THREAD_GROUP_BG_NONINTERACTIVE); + || schedGroup == Process.THREAD_GROUP_BG_NONINTERACTIVE + || procState > ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND); i--) { ContentProviderConnection conn = cpr.connections.get(i); ProcessRecord client = conn.client; @@ -13926,32 +13911,7 @@ public final class ActivityManagerService extends ActivityManagerNative // Being our own client is not interesting. continue; } - int myCachedAdj = cachedAdj; - if (myCachedAdj > client.cachedAdj) { - if (client.cachedAdj > ProcessList.FOREGROUND_APP_ADJ) { - myCachedAdj = client.cachedAdj; - } else { - myCachedAdj = ProcessList.FOREGROUND_APP_ADJ; - } - } - int myClientCachedAdj = clientCachedAdj; - if (myClientCachedAdj > client.clientCachedAdj) { - if (client.clientCachedAdj >= ProcessList.FOREGROUND_APP_ADJ) { - myClientCachedAdj = client.clientCachedAdj; - } else { - myClientCachedAdj = ProcessList.FOREGROUND_APP_ADJ; - } - } - int myEmptyAdj = emptyAdj; - if (myEmptyAdj > client.emptyAdj) { - if (client.emptyAdj > ProcessList.FOREGROUND_APP_ADJ) { - myEmptyAdj = client.emptyAdj; - } else { - myEmptyAdj = ProcessList.FOREGROUND_APP_ADJ; - } - } - int clientAdj = computeOomAdjLocked(client, myCachedAdj, - myClientCachedAdj, myEmptyAdj, TOP_APP, true, doingAll); + int clientAdj = computeOomAdjLocked(client, cachedAdj, TOP_APP, doingAll, now); if (adj > clientAdj) { if (app.hasShownUi && app != mHomeProcess && clientAdj > ProcessList.PERCEPTIBLE_APP_ADJ) { @@ -13961,12 +13921,8 @@ public final class ActivityManagerService extends ActivityManagerNative ? clientAdj : ProcessList.FOREGROUND_APP_ADJ; app.adjType = "provider"; } - if (!client.cached) { - app.cached = false; - } - if (client.keeping) { - app.keeping = true; - } + app.cached &= client.cached; + app.keeping |= client.keeping; app.adjTypeCode = ActivityManager.RunningAppProcessInfo .REASON_PROVIDER_IN_USE; app.adjSource = client; @@ -13974,7 +13930,10 @@ public final class ActivityManagerService extends ActivityManagerNative app.adjTarget = cpr.name; } if (procState > client.curProcState) { - procState = client.curProcState; + procState = client.curProcState > + ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND + ? client.curProcState + : ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND; } if (client.curSchedGroup == Process.THREAD_GROUP_DEFAULT) { schedGroup = Process.THREAD_GROUP_DEFAULT; @@ -13992,8 +13951,8 @@ public final class ActivityManagerService extends ActivityManagerNative app.adjType = "provider"; app.adjTarget = cpr.name; } - if (procState > ActivityManager.PROCESS_STATE_IMPORTANT_PERCEPTIBLE) { - procState = ActivityManager.PROCESS_STATE_IMPORTANT_PERCEPTIBLE; + if (procState > ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND) { + procState = ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND; } } } @@ -14010,16 +13969,6 @@ public final class ActivityManagerService extends ActivityManagerNative app.serviceb = false; } - app.nonStoppingAdj = adj; - - if (hasStoppingActivities) { - // Only upgrade adjustment. - if (adj > ProcessList.PERCEPTIBLE_APP_ADJ) { - adj = ProcessList.PERCEPTIBLE_APP_ADJ; - app.adjType = "stopping"; - } - } - app.curRawAdj = adj; //Slog.i(TAG, "OOM ADJ " + app + ": pid=" + app.pid + @@ -14034,24 +13983,12 @@ public final class ActivityManagerService extends ActivityManagerNative app.keeping = true; } - if (app.hasAboveClient) { - // If this process has bound to any services with BIND_ABOVE_CLIENT, - // then we need to drop its adjustment to be lower than the service's - // in order to honor the request. We want to drop it by one adjustment - // level... but there is special meaning applied to various levels so - // we will skip some of them. - if (adj < ProcessList.FOREGROUND_APP_ADJ) { - // System process will not get dropped, ever - } else if (adj < ProcessList.VISIBLE_APP_ADJ) { - adj = ProcessList.VISIBLE_APP_ADJ; - } else if (adj < ProcessList.PERCEPTIBLE_APP_ADJ) { - adj = ProcessList.PERCEPTIBLE_APP_ADJ; - } else if (adj < ProcessList.CACHED_APP_MIN_ADJ) { - adj = ProcessList.CACHED_APP_MIN_ADJ; - } else if (adj < ProcessList.CACHED_APP_MAX_ADJ) { - adj++; - } - } + // Do final modification to adj. Everything we do between here and applying + // the final setAdj must be done in this function, because we will also use + // it when computing the final cached adj later. Note that we don't need to + // worry about this for max adj above, since max adj will always be used to + // keep it out of the cached vaues. + adj = app.modifyRawOomAdj(adj); app.curProcState = procState; @@ -14407,23 +14344,10 @@ public final class ActivityManagerService extends ActivityManagerNative } } - private final boolean updateOomAdjLocked(ProcessRecord app, int cachedAdj, - int clientCachedAdj, int emptyAdj, ProcessRecord TOP_APP, boolean doingAll, - boolean doingProcessState, long now) { - app.cachedAdj = cachedAdj; - app.clientCachedAdj = clientCachedAdj; - app.emptyAdj = emptyAdj; - - if (app.thread == null) { - return false; - } - - final boolean wasKeeping = app.keeping; - + private final boolean applyOomAdjLocked(ProcessRecord app, boolean wasKeeping, + ProcessRecord TOP_APP, boolean doingAll, boolean reportingProcessState, long now) { boolean success = true; - computeOomAdjLocked(app, cachedAdj, clientCachedAdj, emptyAdj, TOP_APP, false, doingAll); - if (app.curRawAdj != app.setRawAdj) { if (wasKeeping && !app.keeping) { // This app is no longer something we want to keep. Note @@ -14467,11 +14391,6 @@ public final class ActivityManagerService extends ActivityManagerNative requestPssLocked(app, now, true); } app.setAdj = app.curAdj; - app.setAdjChanged = true; - if (!doingAll) { - app.setProcessTrackerState(TOP_APP, mProcessTracker.getMemFactorLocked(), - now, mProcessList); - } } else { success = false; Slog.w(TAG, "Failed setting oom adj of " + app + " to " + app.curAdj); @@ -14514,7 +14433,7 @@ public final class ActivityManagerService extends ActivityManagerNative } if (app.repProcState != app.curProcState) { app.repProcState = app.curProcState; - if (!doingProcessState && app.thread != null) { + if (!reportingProcessState && app.thread != null) { try { if (false) { //RuntimeException h = new RuntimeException("here"); @@ -14526,9 +14445,33 @@ public final class ActivityManagerService extends ActivityManagerNative } } } + if (app.setProcState != app.curProcState) { + if (DEBUG_SWITCH || DEBUG_OOM_ADJ) Slog.v(TAG, + "Proc state change of " + app.processName + + " to " + app.curProcState); + app.setProcState = app.curProcState; + app.procStateChanged = true; + if (!doingAll) { + app.setProcessTrackerState(mProcessTracker.getMemFactorLocked(), now); + } + } return success; } + private final boolean updateOomAdjLocked(ProcessRecord app, int cachedAdj, + ProcessRecord TOP_APP, boolean doingAll, boolean reportingProcessState, long now) { + if (app.thread == null) { + return false; + } + + final boolean wasKeeping = app.keeping; + + computeOomAdjLocked(app, cachedAdj, TOP_APP, doingAll, now); + + return applyOomAdjLocked(app, wasKeeping, TOP_APP, doingAll, + reportingProcessState, now); + } + private final ActivityRecord resumedAppLocked() { return mStackSupervisor.resumedAppLocked(); } @@ -14540,17 +14483,19 @@ public final class ActivityManagerService extends ActivityManagerNative final boolean updateOomAdjLocked(ProcessRecord app, boolean doingProcessState) { final ActivityRecord TOP_ACT = resumedAppLocked(); final ProcessRecord TOP_APP = TOP_ACT != null ? TOP_ACT.app : null; - int curAdj = app.curAdj; - final boolean wasCached = curAdj >= ProcessList.CACHED_APP_MIN_ADJ - && curAdj <= ProcessList.CACHED_APP_MAX_ADJ; + final boolean wasCached = app.cached; mAdjSeq++; - boolean success = updateOomAdjLocked(app, app.cachedAdj, app.clientCachedAdj, - app.emptyAdj, TOP_APP, false, doingProcessState, SystemClock.uptimeMillis()); - final boolean nowCached = app.curAdj >= ProcessList.CACHED_APP_MIN_ADJ - && app.curAdj <= ProcessList.CACHED_APP_MAX_ADJ; - if (nowCached != wasCached) { + // This is the desired cached adjusment we want to tell it to use. + // If our app is currently cached, we know it, and that is it. Otherwise, + // we don't know it yet, and it needs to now be cached we will then + // need to do a complete oom adj. + final int cachedAdj = app.curRawAdj >= ProcessList.CACHED_APP_MIN_ADJ + ? app.curRawAdj : ProcessList.UNKNOWN_ADJ; + boolean success = updateOomAdjLocked(app, cachedAdj, TOP_APP, false, doingProcessState, + SystemClock.uptimeMillis()); + if (wasCached != app.cached || app.curRawAdj == ProcessList.UNKNOWN_ADJ) { // Changed to/from cached state, so apps after it in the LRU // list may also be changed. updateOomAdjLocked(); @@ -14563,6 +14508,7 @@ public final class ActivityManagerService extends ActivityManagerNative final ProcessRecord TOP_APP = TOP_ACT != null ? TOP_ACT.app : null; final long now = SystemClock.uptimeMillis(); final long oldTime = now - ProcessList.MAX_EMPTY_TIME; + final int N = mLruProcesses.size(); if (false) { RuntimeException e = new RuntimeException(); @@ -14591,7 +14537,7 @@ public final class ActivityManagerService extends ActivityManagerNative // them. int numSlots = (ProcessList.CACHED_APP_MAX_ADJ - ProcessList.CACHED_APP_MIN_ADJ + 1) / 2; - int numEmptyProcs = mLruProcesses.size()- mNumNonCachedProcs - mNumCachedHiddenProcs; + int numEmptyProcs = N - mNumNonCachedProcs - mNumCachedHiddenProcs; if (numEmptyProcs > cachedProcessLimit) { // If there are more empty processes than our limit on cached // processes, then use the cached process limit for the factor. @@ -14616,80 +14562,97 @@ public final class ActivityManagerService extends ActivityManagerNative // First update the OOM adjustment for each of the // application processes based on their current state. - int i = mLruProcesses.size(); int curCachedAdj = ProcessList.CACHED_APP_MIN_ADJ; int nextCachedAdj = curCachedAdj+1; int curEmptyAdj = ProcessList.CACHED_APP_MIN_ADJ; int nextEmptyAdj = curEmptyAdj+2; int curClientCachedAdj = curEmptyAdj; - boolean changed = false; - while (i > 0) { - i--; + for (int i=0; i ProcessList.CACHED_APP_MAX_ADJ) { - curClientCachedAdj = ProcessList.CACHED_APP_MAX_ADJ; - } - } else { - if (app.curRawAdj == curEmptyAdj || app.curRawAdj == curCachedAdj) { - // This process was assigned as an empty process... step the - // empty level. - if (curEmptyAdj != nextEmptyAdj) { - stepEmpty++; - if (stepEmpty >= emptyFactor) { - stepEmpty = 0; - curEmptyAdj = nextEmptyAdj; - nextEmptyAdj += 2; - if (nextEmptyAdj > ProcessList.CACHED_APP_MAX_ADJ) { - nextEmptyAdj = ProcessList.CACHED_APP_MAX_ADJ; + break; + case ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT: + // Special case for cached client processes... just step + // down from after regular cached processes. + app.curRawAdj = curClientCachedAdj; + app.curAdj = app.modifyRawOomAdj(curClientCachedAdj); + curClientCachedAdj++; + if (curClientCachedAdj > ProcessList.CACHED_APP_MAX_ADJ) { + curClientCachedAdj = ProcessList.CACHED_APP_MAX_ADJ; + } + break; + default: + // For everything else, assign next empty cached process + // level and bump that up. Note that this means that + // long-running services that have dropped down to the + // cached level will be treated as empty (since their process + // state is still as a service), which is what we want. + app.curRawAdj = curEmptyAdj; + app.curAdj = app.modifyRawOomAdj(curEmptyAdj); + if (curEmptyAdj != nextEmptyAdj) { + stepEmpty++; + if (stepEmpty >= emptyFactor) { + stepEmpty = 0; + curEmptyAdj = nextEmptyAdj; + nextEmptyAdj += 2; + if (nextEmptyAdj > ProcessList.CACHED_APP_MAX_ADJ) { + nextEmptyAdj = ProcessList.CACHED_APP_MAX_ADJ; + } } } - } - } else if (app.curRawAdj < ProcessList.CACHED_APP_MIN_ADJ) { - mNumNonCachedProcs++; + break; } - if (app.curAdj >= ProcessList.CACHED_APP_MIN_ADJ - && !app.hasClientActivities) { + } + + applyOomAdjLocked(app, wasKeeping, TOP_APP, true, false, now); + + // Count the number of process types. + switch (app.curProcState) { + case ActivityManager.PROCESS_STATE_CACHED_ACTIVITY: + case ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT: + mNumCachedHiddenProcs++; + numCached++; + if (numCached > cachedProcessLimit) { + Slog.i(TAG, "No longer want " + app.processName + + " (pid " + app.pid + "): cached #" + numCached); + EventLog.writeEvent(EventLogTags.AM_KILL, app.userId, app.pid, + app.processName, app.setAdj, "too many background"); + app.killedBackground = true; + Process.killProcessQuiet(app.pid); + } + break; + case ActivityManager.PROCESS_STATE_CACHED_EMPTY: if (numEmpty > ProcessList.TRIM_EMPTY_APPS && app.lastActivityTime < oldTime) { Slog.i(TAG, "No longer want " + app.processName @@ -14711,8 +14674,12 @@ public final class ActivityManagerService extends ActivityManagerNative Process.killProcessQuiet(app.pid); } } - } + break; + default: + mNumNonCachedProcs++; + break; } + if (app.isolated && app.services.size() <= 0) { // If this is an isolated process, and there are no // services running in it, then the process is no longer @@ -14727,8 +14694,8 @@ public final class ActivityManagerService extends ActivityManagerNative app.killedBackground = true; Process.killProcessQuiet(app.pid); } - if (app.nonStoppingAdj >= ProcessList.HOME_APP_ADJ - && app.nonStoppingAdj != ProcessList.SERVICE_B_ADJ + + if (app.curProcState >= ActivityManager.PROCESS_STATE_HOME && !app.killedBackground) { numTrimming++; } @@ -14743,11 +14710,10 @@ public final class ActivityManagerService extends ActivityManagerNative // are managing to keep around is less than half the maximum we desire; // if we are keeping a good number around, we'll let them use whatever // memory they want. - int memFactor = ProcessTracker.ADJ_MEM_FACTOR_NORMAL; + boolean allChanged; if (numCached <= ProcessList.TRIM_CACHED_APPS && numEmpty <= ProcessList.TRIM_EMPTY_APPS) { final int numCachedAndEmpty = numCached + numEmpty; - final int N = mLruProcesses.size(); int factor = numTrimming/3; int minFactor = 2; if (mHomeProcess != null) minFactor++; @@ -14755,6 +14721,7 @@ public final class ActivityManagerService extends ActivityManagerNative if (factor < minFactor) factor = minFactor; int step = 0; int fgTrimLevel; + int memFactor; if (numCachedAndEmpty <= ProcessList.TRIM_CRITICAL_THRESHOLD) { fgTrimLevel = ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL; memFactor = ProcessTracker.ADJ_MEM_FACTOR_CRITICAL; @@ -14766,13 +14733,19 @@ public final class ActivityManagerService extends ActivityManagerNative memFactor = ProcessTracker.ADJ_MEM_FACTOR_MODERATE; } int curLevel = ComponentCallbacks2.TRIM_MEMORY_COMPLETE; - for (i=0; i=0; i--) { ProcessRecord app = mLruProcesses.get(i); - if (app.nonStoppingAdj >= ProcessList.HOME_APP_ADJ - && app.nonStoppingAdj != ProcessList.SERVICE_B_ADJ + if (allChanged || app.procStateChanged) { + app.setProcessTrackerState(memFactor, now); + } + if (app.curProcState >= ActivityManager.PROCESS_STATE_HOME && !app.killedBackground) { if (app.trimMemoryLevel < curLevel && app.thread != null) { try { + if (DEBUG_SWITCH || DEBUG_OOM_ADJ) Slog.v(TAG, + "Trimming memory of " + app.processName + + " to " + curLevel); app.thread.scheduleTrimMemory(curLevel); } catch (RemoteException e) { } @@ -14803,10 +14776,13 @@ public final class ActivityManagerService extends ActivityManagerNative break; } } - } else if (app.nonStoppingAdj == ProcessList.HEAVY_WEIGHT_APP_ADJ) { + } else if (app.curProcState == ActivityManager.PROCESS_STATE_HEAVY_WEIGHT) { if (app.trimMemoryLevel < ComponentCallbacks2.TRIM_MEMORY_BACKGROUND && app.thread != null) { try { + if (DEBUG_SWITCH || DEBUG_OOM_ADJ) Slog.v(TAG, + "Trimming memory of heavy-weight " + app.processName + + " to " + ComponentCallbacks2.TRIM_MEMORY_BACKGROUND); app.thread.scheduleTrimMemory( ComponentCallbacks2.TRIM_MEMORY_BACKGROUND); } catch (RemoteException e) { @@ -14814,14 +14790,17 @@ public final class ActivityManagerService extends ActivityManagerNative } app.trimMemoryLevel = ComponentCallbacks2.TRIM_MEMORY_BACKGROUND; } else { - if ((app.nonStoppingAdj > ProcessList.VISIBLE_APP_ADJ || app.systemNoUi) - && app.pendingUiClean) { + if ((app.curProcState >= ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND + || app.systemNoUi) && app.pendingUiClean) { // If this application is now in the background and it // had done UI, then give it the special trim level to // have it free UI resources. final int level = ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN; if (app.trimMemoryLevel < level && app.thread != null) { try { + if (DEBUG_SWITCH || DEBUG_OOM_ADJ) Slog.v(TAG, + "Trimming memory of bg-ui " + app.processName + + " to " + level); app.thread.scheduleTrimMemory(level); } catch (RemoteException e) { } @@ -14830,6 +14809,9 @@ public final class ActivityManagerService extends ActivityManagerNative } if (app.trimMemoryLevel < fgTrimLevel && app.thread != null) { try { + if (DEBUG_SWITCH || DEBUG_OOM_ADJ) Slog.v(TAG, + "Trimming memory of fg " + app.processName + + " to " + fgTrimLevel); app.thread.scheduleTrimMemory(fgTrimLevel); } catch (RemoteException e) { } @@ -14838,14 +14820,21 @@ public final class ActivityManagerService extends ActivityManagerNative } } } else { - final int N = mLruProcesses.size(); - for (i=0; i=0; i--) { ProcessRecord app = mLruProcesses.get(i); - if ((app.nonStoppingAdj > ProcessList.VISIBLE_APP_ADJ || app.systemNoUi) - && app.pendingUiClean) { + if (allChanged || app.procStateChanged) { + app.setProcessTrackerState(ProcessTracker.ADJ_MEM_FACTOR_NORMAL, now); + } + if ((app.curProcState >= ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND + || app.systemNoUi) && app.pendingUiClean) { if (app.trimMemoryLevel < ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN && app.thread != null) { try { + if (DEBUG_SWITCH || DEBUG_OOM_ADJ) Slog.v(TAG, + "Trimming memory of ui hidden " + app.processName + + " to " + ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN); app.thread.scheduleTrimMemory( ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN); } catch (RemoteException e) { @@ -14863,16 +14852,6 @@ public final class ActivityManagerService extends ActivityManagerNative mStackSupervisor.scheduleDestroyAllActivities(null, "always-finish"); } - boolean allChanged = mProcessTracker.setMemFactorLocked(memFactor, !mSleeping, now); - if (changed || allChanged) { - memFactor = mProcessTracker.getMemFactorLocked(); - for (i=mLruProcesses.size()-1; i>=0; i--) { - ProcessRecord app = mLruProcesses.get(i); - if (allChanged || app.setAdjChanged) { - app.setProcessTrackerState(TOP_APP, memFactor, now, mProcessList); - } - } - } if (allChanged) { requestPssAllProcsLocked(now, false); } diff --git a/services/java/com/android/server/am/IntentBindRecord.java b/services/java/com/android/server/am/IntentBindRecord.java index 6c84b9fb1ab6..21cf266c62b1 100644 --- a/services/java/com/android/server/am/IntentBindRecord.java +++ b/services/java/com/android/server/am/IntentBindRecord.java @@ -19,6 +19,7 @@ package com.android.server.am; import android.content.Context; import android.content.Intent; import android.os.IBinder; +import android.util.ArrayMap; import java.io.PrintWriter; import java.util.HashMap; @@ -33,8 +34,8 @@ final class IntentBindRecord { /** The intent that is bound.*/ final Intent.FilterComparison intent; // /** All apps that have bound to this Intent. */ - final HashMap apps - = new HashMap(); + final ArrayMap apps + = new ArrayMap(); /** Binder published from service. */ IBinder binder; /** Set when we have initiated a request for this binder. */ @@ -62,15 +63,12 @@ final class IntentBindRecord { pw.print(" received="); pw.print(received); pw.print(" hasBound="); pw.print(hasBound); pw.print(" doRebind="); pw.println(doRebind); - if (apps.size() > 0) { - Iterator it = apps.values().iterator(); - while (it.hasNext()) { - AppBindRecord a = it.next(); - pw.print(prefix); pw.print("* Client AppBindRecord{"); - pw.print(Integer.toHexString(System.identityHashCode(a))); - pw.print(' '); pw.print(a.client); pw.println('}'); - a.dumpInIntentBind(pw, prefix + " "); - } + for (int i=0; i 0) { - for (AppBindRecord app : apps.values()) { - if (app.connections.size() > 0) { - for (ConnectionRecord conn : app.connections) { - flags |= conn.flags; - } + for (int i=apps.size()-1; i>=0; i--) { + AppBindRecord app = apps.valueAt(i); + if (app.connections.size() > 0) { + for (ConnectionRecord conn : app.connections) { + flags |= conn.flags; } } } diff --git a/services/java/com/android/server/am/ProcessList.java b/services/java/com/android/server/am/ProcessList.java index 6a72e44f71f4..8b0466f94797 100644 --- a/services/java/com/android/server/am/ProcessList.java +++ b/services/java/com/android/server/am/ProcessList.java @@ -36,6 +36,11 @@ final class ProcessList { // OOM adjustments for processes in various states: + // Adjustment used in certain places where we don't know it yet. + // (Generally this is something that is going to be cached, but we + // don't know the exact value in the cached range to assign yet.) + static final int UNKNOWN_ADJ = 16; + // This is a process only hosting activities that are not visible, // so it can be killed without any disruption. static final int CACHED_APP_MAX_ADJ = 15; @@ -62,14 +67,14 @@ final class ProcessList { // have much of an impact as far as the user is concerned. static final int SERVICE_ADJ = 5; - // This is a process currently hosting a backup operation. Killing it - // is not entirely fatal but is generally a bad idea. - static final int BACKUP_APP_ADJ = 4; - // This is a process with a heavy-weight application. It is in the // background, but we want to try to avoid killing it. Value set in // system/rootdir/init.rc on startup. - static final int HEAVY_WEIGHT_APP_ADJ = 3; + static final int HEAVY_WEIGHT_APP_ADJ = 4; + + // This is a process currently hosting a backup operation. Killing it + // is not entirely fatal but is generally a bad idea. + static final int BACKUP_APP_ADJ = 3; // This is a process only hosting components that are perceptible to the // user, and we really want to avoid killing them, but they are not @@ -163,34 +168,11 @@ final class ProcessList { private boolean mHaveDisplaySize; - private final int[] mAdjToTrackedState = new int[CACHED_APP_MAX_ADJ+1]; - ProcessList() { MemInfoReader minfo = new MemInfoReader(); minfo.readMemInfo(); mTotalMemMb = minfo.getTotalSize()/(1024*1024); updateOomLevels(0, 0, false); - for (int i=0; i<=CACHED_APP_MAX_ADJ; i++) { - if (i <= FOREGROUND_APP_ADJ) { - mAdjToTrackedState[i] = ProcessTracker.STATE_FOREGROUND; - } else if (i <= VISIBLE_APP_ADJ) { - mAdjToTrackedState[i] = ProcessTracker.STATE_VISIBLE; - } else if (i <= PERCEPTIBLE_APP_ADJ) { - mAdjToTrackedState[i] = ProcessTracker.STATE_PERCEPTIBLE; - } else if (i <= BACKUP_APP_ADJ) { - mAdjToTrackedState[i] = ProcessTracker.STATE_BACKUP; - } else if (i <= SERVICE_ADJ) { - mAdjToTrackedState[i] = ProcessTracker.STATE_SERVICE; - } else if (i <= HOME_APP_ADJ) { - mAdjToTrackedState[i] = ProcessTracker.STATE_HOME; - } else if (i <= PREVIOUS_APP_ADJ) { - mAdjToTrackedState[i] = ProcessTracker.STATE_PREVIOUS; - } else if (i <= SERVICE_B_ADJ) { - mAdjToTrackedState[i] = ProcessTracker.STATE_SERVICE; - } else { - mAdjToTrackedState[i] = ProcessTracker.STATE_CACHED; - } - } } void applyDisplaySize(WindowManagerService wm) { @@ -256,11 +238,6 @@ final class ProcessList { return mOomMinFree[mOomAdj.length-1] * 1024; } - int adjToTrackedState(int adj) { - return adj >= FOREGROUND_APP_ADJ - ? mAdjToTrackedState[adj] : ProcessTracker.STATE_PERSISTENT; - } - private void writeFile(String path, String data) { FileOutputStream fos = null; try { diff --git a/services/java/com/android/server/am/ProcessRecord.java b/services/java/com/android/server/am/ProcessRecord.java index cb9a76dcfa3e..5c1b7f2405cf 100644 --- a/services/java/com/android/server/am/ProcessRecord.java +++ b/services/java/com/android/server/am/ProcessRecord.java @@ -16,6 +16,7 @@ package com.android.server.am; +import android.util.ArraySet; import com.android.internal.os.BatteryStatsImpl; import android.app.ActivityManager; @@ -64,12 +65,8 @@ final class ProcessRecord { long lruWeight; // Weight for ordering in LRU list long lastPssTime; // Last time we requested PSS data int maxAdj; // Maximum OOM adjustment for this process - int cachedAdj; // If cached, this is the adjustment to use - int clientCachedAdj; // If empty but cached client, this is the adjustment to use - int emptyAdj; // If empty, this is the adjustment to use int curRawAdj; // Current OOM unlimited adjustment for this process int setRawAdj; // Last set OOM unlimited adjustment for this process - int nonStoppingAdj; // Adjustment not counting any stopping activities int curAdj; // Current OOM adjustment for this process int setAdj; // Last set OOM adjustment for this process int curSchedGroup; // Currently desired scheduling class @@ -78,6 +75,7 @@ final class ProcessRecord { int memImportance; // Importance constant computed from curAdj int curProcState = -1; // Currently computed process state: ActivityManager.PROCESS_STATE_* int repProcState = -1; // Last reported process state + int setProcState = -1; // Last set process state in process tracker boolean serviceb; // Process currently is on the service B list boolean keeping; // Actively running code so don't kill due to that? boolean setIsForeground; // Running foreground UI when last set? @@ -92,7 +90,7 @@ final class ProcessRecord { boolean hasAboveClient; // Bound using BIND_ABOVE_CLIENT, so want to be lower boolean bad; // True if disabled in the bad process list boolean killedBackground; // True when proc has been killed due to too many bg - boolean setAdjChanged; // Keep track of whether we changed 'setAdj'. + boolean procStateChanged; // Keep track of whether we changed 'setAdj'. String waitingToKill; // Process is waiting to be killed when in the bg; reason IBinder forcingToForeground;// Token that is forcing this process to be foreground int adjSeq; // Sequence id for identifying oom_adj assignment cycles @@ -125,15 +123,15 @@ final class ProcessRecord { // contains HistoryRecord objects final ArrayList activities = new ArrayList(); // all ServiceRecord running in this process - final HashSet services = new HashSet(); + final ArraySet services = new ArraySet(); // services that are currently executing code (need to remain foreground). - final HashSet executingServices - = new HashSet(); + final ArraySet executingServices + = new ArraySet(); // All ConnectionRecord this process holds - final HashSet connections - = new HashSet(); + final ArraySet connections + = new ArraySet(); // all IIntentReceivers that are registered from this process. - final HashSet receivers = new HashSet(); + final ArraySet receivers = new ArraySet(); // class (String) -> ContentProviderRecord final ArrayMap pubProviders = new ArrayMap(); @@ -215,12 +213,8 @@ final class ProcessRecord { pw.print(" cached="); pw.print(cached); pw.print(" empty="); pw.println(empty); pw.print(prefix); pw.print("oom: max="); pw.print(maxAdj); - pw.print(" cached="); pw.print(cachedAdj); - pw.print(" client="); pw.print(clientCachedAdj); - pw.print(" empty="); pw.print(emptyAdj); pw.print(" curRaw="); pw.print(curRawAdj); pw.print(" setRaw="); pw.print(setRawAdj); - pw.print(" nonStopping="); pw.print(nonStoppingAdj); pw.print(" cur="); pw.print(curAdj); pw.print(" set="); pw.println(setAdj); pw.print(prefix); pw.print("curSchedGroup="); pw.print(curSchedGroup); @@ -228,7 +222,8 @@ final class ProcessRecord { pw.print(" systemNoUi="); pw.print(systemNoUi); pw.print(" trimMemoryLevel="); pw.println(trimMemoryLevel); pw.print(prefix); pw.print("curProcState="); pw.print(curProcState); - pw.print(" repProcState="); pw.println(repProcState); + pw.print(" repProcState="); pw.print(repProcState); + pw.print(" setProcState="); pw.println(setProcState); pw.print(prefix); pw.print("adjSeq="); pw.print(adjSeq); pw.print(" lruSeq="); pw.print(lruSeq); pw.print(" lastPssTime="); pw.println(lastPssTime); @@ -301,20 +296,20 @@ final class ProcessRecord { } if (services.size() > 0) { pw.print(prefix); pw.println("Services:"); - for (ServiceRecord sr : services) { - pw.print(prefix); pw.print(" - "); pw.println(sr); + for (int i=0; i 0) { pw.print(prefix); pw.println("Executing Services:"); - for (ServiceRecord sr : executingServices) { - pw.print(prefix); pw.print(" - "); pw.println(sr); + for (int i=0; i 0) { pw.print(prefix); pw.println("Connections:"); - for (ConnectionRecord cr : connections) { - pw.print(prefix); pw.print(" - "); pw.println(cr); + for (int i=0; i 0) { @@ -335,8 +330,8 @@ final class ProcessRecord { } if (receivers.size() > 0) { pw.print(prefix); pw.println("Receivers:"); - for (ReceiverList rl : receivers) { - pw.print(prefix); pw.print(" - "); pw.println(rl); + for (int i=0; i 0) { - for (ConnectionRecord cr : connections) { - if ((cr.flags&Context.BIND_ABOVE_CLIENT) != 0) { - hasAboveClient = true; - break; - } + for (int i=connections.size()-1; i>=0; i--) { + ConnectionRecord cr = connections.valueAt(i); + if ((cr.flags&Context.BIND_ABOVE_CLIENT) != 0) { + hasAboveClient = true; + break; + } + } + } + + int modifyRawOomAdj(int adj) { + if (hasAboveClient) { + // If this process has bound to any services with BIND_ABOVE_CLIENT, + // then we need to drop its adjustment to be lower than the service's + // in order to honor the request. We want to drop it by one adjustment + // level... but there is special meaning applied to various levels so + // we will skip some of them. + if (adj < ProcessList.FOREGROUND_APP_ADJ) { + // System process will not get dropped, ever + } else if (adj < ProcessList.VISIBLE_APP_ADJ) { + adj = ProcessList.VISIBLE_APP_ADJ; + } else if (adj < ProcessList.PERCEPTIBLE_APP_ADJ) { + adj = ProcessList.PERCEPTIBLE_APP_ADJ; + } else if (adj < ProcessList.CACHED_APP_MIN_ADJ) { + adj = ProcessList.CACHED_APP_MIN_ADJ; + } else if (adj < ProcessList.CACHED_APP_MAX_ADJ) { + adj++; } } + return adj; } public String toShortString() { @@ -483,11 +498,8 @@ final class ProcessRecord { } } - public void setProcessTrackerState(ProcessRecord TOP_APP, int memFactor, long now, - ProcessList plist) { - int state = this == TOP_APP ? ProcessTracker.STATE_TOP - : plist.adjToTrackedState(getSetAdjWithServices()); - baseProcessTracker.setState(state, memFactor, now, pkgList); + public void setProcessTrackerState(int memFactor, long now) { + baseProcessTracker.setState(repProcState, memFactor, now, pkgList); } /* diff --git a/services/java/com/android/server/am/ProcessTracker.java b/services/java/com/android/server/am/ProcessTracker.java index 0ea93e828a92..ef032ba51330 100644 --- a/services/java/com/android/server/am/ProcessTracker.java +++ b/services/java/com/android/server/am/ProcessTracker.java @@ -52,26 +52,34 @@ public final class ProcessTracker { public static final int STATE_NOTHING = -1; public static final int STATE_PERSISTENT = 0; public static final int STATE_TOP = 1; - public static final int STATE_FOREGROUND = 2; - public static final int STATE_VISIBLE = 3; - public static final int STATE_PERCEPTIBLE = 4; - public static final int STATE_BACKUP = 5; + public static final int STATE_IMPORTANT_FOREGROUND = 2; + public static final int STATE_IMPORTANT_BACKGROUND = 3; + public static final int STATE_BACKUP = 4; + public static final int STATE_HEAVY_WEIGHT = 5; public static final int STATE_SERVICE = 6; - public static final int STATE_HOME = 7; - public static final int STATE_PREVIOUS = 8; - public static final int STATE_CACHED = 9; - public static final int STATE_COUNT = STATE_CACHED+1; - - static final int[] ALL_PROC_STATES = new int[] { STATE_PERSISTENT, STATE_TOP, - STATE_FOREGROUND, STATE_VISIBLE, STATE_PERCEPTIBLE, STATE_BACKUP, - STATE_SERVICE, STATE_HOME, STATE_PREVIOUS, STATE_CACHED + public static final int STATE_RECEIVER = 7; + public static final int STATE_HOME = 8; + public static final int STATE_LAST_ACTIVITY = 9; + public static final int STATE_CACHED_ACTIVITY = 10; + public static final int STATE_CACHED_ACTIVITY_CLIENT = 11; + public static final int STATE_CACHED_EMPTY = 12; + public static final int STATE_COUNT = STATE_CACHED_EMPTY+1; + + static final int[] ALL_PROC_STATES = new int[] { STATE_PERSISTENT, + STATE_TOP, STATE_IMPORTANT_FOREGROUND, STATE_IMPORTANT_BACKGROUND, + STATE_BACKUP, STATE_HEAVY_WEIGHT, STATE_SERVICE, STATE_RECEIVER, + STATE_HOME, STATE_LAST_ACTIVITY, STATE_CACHED_ACTIVITY, + STATE_CACHED_ACTIVITY_CLIENT, STATE_CACHED_EMPTY }; public static final int PSS_SAMPLE_COUNT = 0; public static final int PSS_MINIMUM = 1; public static final int PSS_AVERAGE = 2; public static final int PSS_MAXIMUM = 3; - public static final int PSS_COUNT = PSS_MAXIMUM+1; + public static final int PSS_USS_MINIMUM = 4; + public static final int PSS_USS_AVERAGE = 5; + public static final int PSS_USS_MAXIMUM = 6; + public static final int PSS_COUNT = PSS_USS_MAXIMUM+1; public static final int ADJ_NOTHING = -1; public static final int ADJ_MEM_FACTOR_NORMAL = 0; @@ -106,8 +114,9 @@ public final class ProcessTracker { static int OFFSET_INDEX_MASK = 0xffff; static final String[] STATE_NAMES = new String[] { - "Persistent ", "Top ", "Foreground ", "Visible ", "Perceptible", - "Backup ", "Service ", "Home ", "Previous ", "Cached " + "Persistent", "Top ", "Imp Fg ", "Imp Bg ", + "Backup ", "Heavy Wght", "Service ", "Receiver ", "Home ", + "Last Act ", "Cch Actvty", "Cch Client", "Cch Empty " }; static final String[] ADJ_SCREEN_NAMES_CSV = new String[] { @@ -119,8 +128,9 @@ public final class ProcessTracker { }; static final String[] STATE_NAMES_CSV = new String[] { - "pers", "top", "fore", "vis", "percept", - "backup", "service", "home", "prev", "cached" + "pers", "top", "impfg", "impbg", "backup", "heavy", + "service", "receiver", "home", "lastact", + "cch-activity", "cch-aclient", "cch-empty" }; static final String[] ADJ_SCREEN_TAGS = new String[] { @@ -132,8 +142,26 @@ public final class ProcessTracker { }; static final String[] STATE_TAGS = new String[] { - "y", "t", "f", "v", "r", - "b", "s", "h", "p", "c" + "p", "t", "f", "b", "u", "w", + "s", "r", "h", "l", "a", "c", "e" + }; + + // Map from process states to the states we track. + static final int[] PROCESS_STATE_TO_STATE = new int[] { + STATE_PERSISTENT, // ActivityManager.PROCESS_STATE_PERSISTENT + STATE_PERSISTENT, // ActivityManager.PROCESS_STATE_PERSISTENT_UI + STATE_TOP, // ActivityManager.PROCESS_STATE_TOP + STATE_IMPORTANT_FOREGROUND, // ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND + STATE_IMPORTANT_BACKGROUND, // ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND + STATE_BACKUP, // ActivityManager.PROCESS_STATE_BACKUP + STATE_HEAVY_WEIGHT, // ActivityManager.PROCESS_STATE_HEAVY_WEIGHT + STATE_SERVICE, // ActivityManager.PROCESS_STATE_SERVICE + STATE_RECEIVER, // ActivityManager.PROCESS_STATE_RECEIVER + STATE_HOME, // ActivityManager.PROCESS_STATE_HOME + STATE_LAST_ACTIVITY, // ActivityManager.PROCESS_STATE_LAST_ACTIVITY + STATE_CACHED_ACTIVITY, // ActivityManager.PROCESS_STATE_CACHED_ACTIVITY + STATE_CACHED_ACTIVITY_CLIENT, // ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT + STATE_CACHED_EMPTY, // ActivityManager.PROCESS_STATE_CACHED_EMPTY }; static final String CSV_SEP = "\t"; @@ -322,10 +350,20 @@ public final class ProcessTracker { return true; } + /** + * Update the current state of the given list of processes. + * + * @param state Current ActivityManager.PROCESS_STATE_* + * @param memFactor Current mem factor constant. + * @param now Current time. + * @param pkgList Processes to update. + */ public void setState(int state, int memFactor, long now, ArrayMap pkgList) { - if (state != STATE_NOTHING) { - state += memFactor*STATE_COUNT; + if (state < 0) { + state = STATE_NOTHING; + } else { + state = PROCESS_STATE_TO_STATE[state] + (memFactor*STATE_COUNT); } // First update the common process. @@ -370,7 +408,7 @@ public final class ProcessTracker { mStartTime = now; } - public void addPss(long pss, boolean always) { + public void addPss(long pss, long uss, boolean always) { if (!always) { if (mLastPssState == mCurState && SystemClock.uptimeMillis() < (mLastPssTime+(30*1000))) { @@ -399,16 +437,27 @@ public final class ProcessTracker { longs[idx+PSS_MINIMUM] = pss; longs[idx+PSS_AVERAGE] = pss; longs[idx+PSS_MAXIMUM] = pss; + longs[idx+PSS_USS_MINIMUM] = uss; + longs[idx+PSS_USS_AVERAGE] = uss; + longs[idx+PSS_USS_MAXIMUM] = uss; } else { longs[idx+PSS_SAMPLE_COUNT] = count+1; if (longs[idx+PSS_MINIMUM] > pss) { longs[idx+PSS_MINIMUM] = pss; } - longs[idx+PSS_AVERAGE] = (long)( ((longs[idx+PSS_AVERAGE]*(double)count)+pss) - / (count+1) ); + longs[idx+PSS_AVERAGE] = (long)( + ((longs[idx+PSS_AVERAGE]*(double)count)+pss) / (count+1) ); if (longs[idx+PSS_MAXIMUM] < pss) { longs[idx+PSS_MAXIMUM] = pss; } + if (longs[idx+PSS_USS_MINIMUM] > uss) { + longs[idx+PSS_USS_MINIMUM] = uss; + } + longs[idx+PSS_USS_AVERAGE] = (long)( + ((longs[idx+PSS_USS_AVERAGE]*(double)count)+uss) / (count+1) ); + if (longs[idx+PSS_USS_MAXIMUM] < uss) { + longs[idx+PSS_USS_MAXIMUM] = uss; + } } } } @@ -480,6 +529,21 @@ public final class ProcessTracker { int idx = State.binarySearch(mPssTable, mPssTableSize, state); return idx >= 0 ? mState.getLong(mPssTable[idx], PSS_MAXIMUM) : 0; } + + long getPssUssMinimum(int state) { + int idx = State.binarySearch(mPssTable, mPssTableSize, state); + return idx >= 0 ? mState.getLong(mPssTable[idx], PSS_USS_MINIMUM) : 0; + } + + long getPssUssAverage(int state) { + int idx = State.binarySearch(mPssTable, mPssTableSize, state); + return idx >= 0 ? mState.getLong(mPssTable[idx], PSS_USS_AVERAGE) : 0; + } + + long getPssUssMaximum(int state) { + int idx = State.binarySearch(mPssTable, mPssTableSize, state); + return idx >= 0 ? mState.getLong(mPssTable[idx], PSS_USS_MAXIMUM) : 0; + } } public static final class ServiceState { @@ -589,10 +653,13 @@ public final class ProcessTracker { static final class State { // Current version of the parcel format. - private static final int PARCEL_VERSION = 3; + private static final int PARCEL_VERSION = 6; // In-memory Parcel magic number, used to detect attempts to unmarshall bad data private static final int MAGIC = 0x50535453; + static final int FLAG_COMPLETE = 1<<0; + static final int FLAG_SHUTDOWN = 1<<1; + final File mBaseDir; final ProcessTracker mProcessTracker; AtomicFile mFile; @@ -603,6 +670,7 @@ public final class ProcessTracker { long mTimePeriodStartRealtime; long mTimePeriodEndRealtime; boolean mRunning; + int mFlags; final ProcessMap mPackages = new ProcessMap(); final ProcessMap mProcesses = new ProcessMap(); @@ -690,6 +758,7 @@ public final class ProcessTracker { Arrays.fill(mMemFactorDurations, 0); mMemFactor = STATE_NOTHING; mStartTime = 0; + mReadError = null; } private void buildTimePeriodStartClockStr() { @@ -724,7 +793,7 @@ public final class ProcessTracker { } } - void readLocked() { + boolean readLocked() { try { FileInputStream stream = mFile.openRead(); @@ -770,11 +839,14 @@ public final class ProcessTracker { } } } + return false; } } catch (Throwable e) { - mReadError = "error reading: " + e; + mReadError = "caught exception: " + e; Slog.e(TAG, "Error reading process statistics", e); + return false; } + return true; } private void writeStateLocked(boolean sync, final boolean commit) { @@ -848,6 +920,7 @@ public final class ProcessTracker { out.writeLong(mTimePeriodStartClock); out.writeLong(mTimePeriodStartRealtime); out.writeLong(mTimePeriodEndRealtime); + out.writeInt(mFlags); out.writeInt(mLongs.size()); out.writeInt(mNextLong); @@ -920,7 +993,7 @@ public final class ProcessTracker { private boolean readCheckedInt(Parcel in, int val, String what) { int got; if ((got=in.readInt()) != val) { - mReadError = "bad " + ": " + got; + mReadError = "bad " + what + ": " + got; return false; } return true; @@ -956,6 +1029,7 @@ public final class ProcessTracker { buildTimePeriodStartClockStr(); mTimePeriodStartRealtime = in.readLong(); mTimePeriodEndRealtime = in.readLong(); + mFlags = in.readInt(); final int NLONGS = in.readInt(); final int NEXTLONG = in.readInt(); @@ -1365,8 +1439,9 @@ public final class ProcessTracker { void dumpSummaryLocked(PrintWriter pw, String reqPackage, long now) { dumpFilteredSummaryLocked(pw, null, " ", ALL_SCREEN_ADJ, ALL_MEM_ADJ, - new int[] { STATE_PERSISTENT, STATE_TOP, STATE_FOREGROUND, STATE_VISIBLE, - STATE_PERCEPTIBLE, STATE_BACKUP, STATE_SERVICE, STATE_HOME, STATE_PREVIOUS }, + new int[] { STATE_PERSISTENT, STATE_TOP, STATE_IMPORTANT_FOREGROUND, + STATE_IMPORTANT_BACKGROUND, STATE_BACKUP, STATE_HEAVY_WEIGHT, + STATE_SERVICE, STATE_RECEIVER, STATE_HOME, STATE_LAST_ACTIVITY }, now, reqPackage); pw.println(); pw.println("Run time Stats:"); @@ -1379,6 +1454,9 @@ public final class ProcessTracker { TimeUtils.formatDuration( (mRunning ? SystemClock.elapsedRealtime() : mTimePeriodEndRealtime) - mTimePeriodStartRealtime, pw); + if ((mFlags&FLAG_COMPLETE) != 0) pw.print(" (complete)"); + else if ((mFlags&FLAG_SHUTDOWN) != 0) pw.print(" (shutdown)"); + else pw.print(" (partial)"); pw.println(); } @@ -1452,10 +1530,13 @@ public final class ProcessTracker { void dumpCheckinLocked(PrintWriter pw, String reqPackage) { final long now = SystemClock.uptimeMillis(); ArrayMap> pkgMap = mPackages.getMap(); - pw.println("vers,1"); + pw.println("vers,2"); pw.print("period,"); pw.print(mTimePeriodStartClockStr); pw.print(","); pw.print(mTimePeriodStartRealtime); pw.print(","); pw.print(mRunning ? SystemClock.elapsedRealtime() : mTimePeriodEndRealtime); + if ((mFlags&FLAG_COMPLETE) != 0) pw.print(",complete"); + else if ((mFlags&FLAG_SHUTDOWN) != 0) pw.print(",shutdown"); + else pw.print(",partial"); pw.println(); for (int ip=0; ip (mState.mLastWriteTime+WRITE_PERIOD)) { if (SystemClock.elapsedRealtime() > (mState.mTimePeriodStartRealtime+COMMIT_PERIOD)) { mCommitPending = true; + mState.mFlags |= State.FLAG_COMPLETE; } return true; } @@ -1637,6 +1719,7 @@ public final class ProcessTracker { public void shutdownLocked() { Slog.w(TAG, "Writing process stats before shutdown..."); + mState.mFlags |= State.FLAG_SHUTDOWN; writeStateSyncLocked(); mShuttingDown = true; } @@ -1841,6 +1924,9 @@ public final class ProcessTracker { long minPss; long avgPss; long maxPss; + long minUss; + long avgUss; + long maxUss; ProcessDataCollection(int[] _screenStates, int[] _memStates, int[] _procStates) { screenStates = _screenStates; @@ -1857,6 +1943,12 @@ public final class ProcessTracker { printSizeValue(pw, avgPss * 1024); pw.print("-"); printSizeValue(pw, maxPss * 1024); + pw.print("/"); + printSizeValue(pw, minUss * 1024); + pw.print("-"); + printSizeValue(pw, avgUss * 1024); + pw.print("-"); + printSizeValue(pw, maxUss * 1024); if (full) { pw.print(" over "); pw.print(numPss); @@ -1868,7 +1960,8 @@ public final class ProcessTracker { static void computeProcessData(ProcessState proc, ProcessDataCollection data, long now) { data.totalTime = 0; - data.numPss = data.minPss = data.avgPss = data.maxPss = 0; + data.numPss = data.minPss = data.avgPss = data.maxPss = + data.minUss = data.avgUss = data.maxUss = 0; for (int is=0; is data.maxPss) { data.maxPss = maxPss; } + if (minUss < data.minUss) { + data.minUss = minUss; + } + data.avgUss = (long)( ((data.avgUss*(double)data.numPss) + + (avgUss*(double)samples)) / (data.numPss+samples) ); + if (maxUss > data.maxUss) { + data.maxUss = maxUss; + } } data.numPss += samples; } @@ -1989,7 +2096,7 @@ public final class ProcessTracker { if (count > 0) { if (!printedHeader) { pw.print(prefix); - pw.print("PSS ("); + pw.print("PSS/USS ("); pw.print(proc.mPssTableSize); pw.println(" entries):"); printedHeader = true; @@ -2013,6 +2120,12 @@ public final class ProcessTracker { printSizeValue(pw, proc.getPssAverage(bucket) * 1024); pw.print(" "); printSizeValue(pw, proc.getPssMaximum(bucket) * 1024); + pw.print(" / "); + printSizeValue(pw, proc.getPssUssMinimum(bucket) * 1024); + pw.print(" "); + printSizeValue(pw, proc.getPssUssAverage(bucket) * 1024); + pw.print(" "); + printSizeValue(pw, proc.getPssUssMaximum(bucket) * 1024); pw.println(); } } @@ -2148,21 +2261,28 @@ public final class ProcessTracker { dumpProcessSummaryDetails(pw, proc, prefix, " TOTAL: ", screenStates, memStates, procStates, now, true); dumpProcessSummaryDetails(pw, proc, prefix, " Persistent: ", screenStates, memStates, - new int[] {STATE_PERSISTENT}, now, true); + new int[] { STATE_PERSISTENT }, now, true); dumpProcessSummaryDetails(pw, proc, prefix, " Top: ", screenStates, memStates, new int[] {STATE_TOP}, now, true); - dumpProcessSummaryDetails(pw, proc, prefix, " Foreground: ", screenStates, memStates, - new int[] {STATE_FOREGROUND, STATE_VISIBLE, STATE_PERCEPTIBLE}, now, true); + dumpProcessSummaryDetails(pw, proc, prefix, " Imp Fg: ", screenStates, memStates, + new int[] { STATE_IMPORTANT_FOREGROUND }, now, true); + dumpProcessSummaryDetails(pw, proc, prefix, " Imp Bg: ", screenStates, memStates, + new int[] {STATE_IMPORTANT_BACKGROUND}, now, true); dumpProcessSummaryDetails(pw, proc, prefix, " Backup: ", screenStates, memStates, new int[] {STATE_BACKUP}, now, true); + dumpProcessSummaryDetails(pw, proc, prefix, " Heavy Wgt: ", screenStates, memStates, + new int[] {STATE_HEAVY_WEIGHT}, now, true); dumpProcessSummaryDetails(pw, proc, prefix, " Service: ", screenStates, memStates, new int[] {STATE_SERVICE}, now, true); + dumpProcessSummaryDetails(pw, proc, prefix, " Receiver: ", screenStates, memStates, + new int[] {STATE_RECEIVER}, now, true); dumpProcessSummaryDetails(pw, proc, prefix, " Home: ", screenStates, memStates, new int[] {STATE_HOME}, now, true); - dumpProcessSummaryDetails(pw, proc, prefix, " Previous: ", screenStates, memStates, - new int[] {STATE_PREVIOUS}, now, true); + dumpProcessSummaryDetails(pw, proc, prefix, " Last Act: ", screenStates, memStates, + new int[] {STATE_LAST_ACTIVITY}, now, true); dumpProcessSummaryDetails(pw, proc, prefix, " (Cached): ", screenStates, memStates, - new int[] {STATE_CACHED}, now, true); + new int[] {STATE_CACHED_ACTIVITY_CLIENT, STATE_CACHED_ACTIVITY_CLIENT, + STATE_CACHED_EMPTY}, now, true); } } @@ -2193,9 +2313,9 @@ public final class ProcessTracker { if (result < 1) { value = String.format("%.2f", result); } else if (result < 10) { - value = String.format("%.2f", result); + value = String.format("%.1f", result); } else if (result < 100) { - value = String.format("%.2f", result); + value = String.format("%.0f", result); } else { value = String.format("%.0f", result); } @@ -2300,6 +2420,9 @@ public final class ProcessTracker { long min = proc.mState.getLong(off, PSS_MINIMUM); long avg = proc.mState.getLong(off, PSS_AVERAGE); long max = proc.mState.getLong(off, PSS_MAXIMUM); + long umin = proc.mState.getLong(off, PSS_USS_MINIMUM); + long uavg = proc.mState.getLong(off, PSS_USS_AVERAGE); + long umax = proc.mState.getLong(off, PSS_USS_MAXIMUM); pw.print(','); printProcStateTag(pw, type); pw.print(':'); @@ -2310,6 +2433,12 @@ public final class ProcessTracker { pw.print(avg); pw.print(':'); pw.print(max); + pw.print(':'); + pw.print(umin); + pw.print(':'); + pw.print(uavg); + pw.print(':'); + pw.print(umax); } } @@ -2391,9 +2520,10 @@ public final class ProcessTracker { int[] csvMemStats = new int[] {ADJ_MEM_FACTOR_CRITICAL}; boolean csvSepProcStats = true; int[] csvProcStats = new int[] { - STATE_PERSISTENT, STATE_TOP, STATE_FOREGROUND, STATE_VISIBLE, - STATE_PERCEPTIBLE, STATE_BACKUP, STATE_SERVICE, STATE_HOME, - STATE_PREVIOUS, STATE_CACHED }; + STATE_PERSISTENT, STATE_TOP, STATE_IMPORTANT_FOREGROUND, + STATE_IMPORTANT_BACKGROUND, STATE_BACKUP, STATE_HEAVY_WEIGHT, STATE_SERVICE, + STATE_RECEIVER, STATE_HOME, STATE_LAST_ACTIVITY, + STATE_CACHED_ACTIVITY, STATE_CACHED_ACTIVITY_CLIENT, STATE_CACHED_EMPTY }; if (args != null) { for (int i=0; i