diff options
| author | 2013-07-16 18:15:08 +0000 | |
|---|---|---|
| committer | 2013-07-16 18:15:09 +0000 | |
| commit | cdae0f3c608ed5aee5ddbdf9c54ac86a5619e64b (patch) | |
| tree | b8e54420bcf9b85f3ef4d9e8bff926c739f7f4eb | |
| parent | 78b6a8615cf6c2c062187c23ced0c6fe6046498e (diff) | |
| parent | c8230519728b14065effd3b7d4eca273ff86160c (diff) | |
Merge "Switch proc stats to use new process state constants."
| -rw-r--r-- | core/java/android/app/ActivityManager.java | 39 | ||||
| -rw-r--r-- | core/java/android/content/Context.java | 10 | ||||
| -rw-r--r-- | core/java/android/os/Debug.java | 13 | ||||
| -rw-r--r-- | core/jni/android_os_Debug.cpp | 36 | ||||
| -rw-r--r-- | services/java/com/android/server/InputMethodManagerService.java | 2 | ||||
| -rw-r--r-- | services/java/com/android/server/WallpaperManagerService.java | 3 | ||||
| -rw-r--r-- | services/java/com/android/server/am/ActiveServices.java | 126 | ||||
| -rw-r--r-- | services/java/com/android/server/am/ActivityManagerService.java | 971 | ||||
| -rw-r--r-- | services/java/com/android/server/am/IntentBindRecord.java | 31 | ||||
| -rw-r--r-- | services/java/com/android/server/am/ProcessList.java | 43 | ||||
| -rw-r--r-- | services/java/com/android/server/am/ProcessRecord.java | 86 | ||||
| -rw-r--r-- | services/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<ConnectionRecord> 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<ServiceRecord> 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<ServiceRecord> 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 90fb0037ffc1..b51415f5c655 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<ServiceRecord> 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<MemItem>[] oomProcs = (ArrayList<MemItem>[]) 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<ReceiverList> 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<ServiceRecord> 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<ConnectionRecord> 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<ConnectionRecord> 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); @@ -14516,7 +14435,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"); @@ -14528,9 +14447,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(); } @@ -14542,17 +14485,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(); @@ -14565,6 +14510,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(); @@ -14593,7 +14539,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. @@ -14618,80 +14564,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<N; i++) { ProcessRecord app = mLruProcesses.get(i); - //Slog.i(TAG, "OOM " + app + ": cur cached=" + curCachedAdj); - app.setAdjChanged = false; - updateOomAdjLocked(app, curCachedAdj, curClientCachedAdj, curEmptyAdj, TOP_APP, - true, false, now); - changed |= app.setAdjChanged; - if (!app.killedBackground) { - if (app.curRawAdj == curCachedAdj && app.hasActivities) { - // This process was assigned as a cached process... step the - // cached level. - mNumCachedHiddenProcs++; - if (curCachedAdj != nextCachedAdj) { - stepCached++; - if (stepCached >= cachedFactor) { - stepCached = 0; - curCachedAdj = nextCachedAdj; - nextCachedAdj += 2; - if (nextCachedAdj > ProcessList.CACHED_APP_MAX_ADJ) { - nextCachedAdj = ProcessList.CACHED_APP_MAX_ADJ; - } - if (curClientCachedAdj <= curCachedAdj) { - curClientCachedAdj = curCachedAdj + 1; - if (curClientCachedAdj > ProcessList.CACHED_APP_MAX_ADJ) { - curClientCachedAdj = ProcessList.CACHED_APP_MAX_ADJ; + if (!app.killedBackground && app.thread != null) { + app.procStateChanged = false; + final boolean wasKeeping = app.keeping; + computeOomAdjLocked(app, ProcessList.UNKNOWN_ADJ, TOP_APP, true, now); + + // If we haven't yet assigned the final cached adj + // to the process, do that now. + if (app.curAdj >= ProcessList.UNKNOWN_ADJ) { + switch (app.curProcState) { + case ActivityManager.PROCESS_STATE_CACHED_ACTIVITY: + // This process is a cached process holding activities... + // assign it the next cached value for that type, and then + // step that cached level. + app.curRawAdj = curCachedAdj; + app.curAdj = app.modifyRawOomAdj(curCachedAdj); + if (curCachedAdj != nextCachedAdj) { + stepCached++; + if (stepCached >= cachedFactor) { + stepCached = 0; + curCachedAdj = nextCachedAdj; + nextCachedAdj += 2; + if (nextCachedAdj > ProcessList.CACHED_APP_MAX_ADJ) { + nextCachedAdj = ProcessList.CACHED_APP_MAX_ADJ; + } + if (curClientCachedAdj <= curCachedAdj) { + curClientCachedAdj = curCachedAdj + 1; + if (curClientCachedAdj > ProcessList.CACHED_APP_MAX_ADJ) { + curClientCachedAdj = ProcessList.CACHED_APP_MAX_ADJ; + } + } } } - } - } - 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); - } - } else if (app.curRawAdj == curCachedAdj && app.hasClientActivities) { - // This process has a client that has activities. We will have - // given it the current cached adj; here we will just leave it - // without stepping the cached adj. - curClientCachedAdj++; - if (curClientCachedAdj > 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 @@ -14713,8 +14676,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 @@ -14729,8 +14696,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++; } @@ -14745,11 +14712,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++; @@ -14757,6 +14723,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; @@ -14768,13 +14735,19 @@ public final class ActivityManagerService extends ActivityManagerNative memFactor = ProcessTracker.ADJ_MEM_FACTOR_MODERATE; } int curLevel = ComponentCallbacks2.TRIM_MEMORY_COMPLETE; - for (i=0; i<N; i++) { + allChanged = mProcessTracker.setMemFactorLocked(memFactor, !mSleeping, now); + for (int i=N-1; 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) { } @@ -14805,10 +14778,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) { @@ -14816,14 +14792,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) { } @@ -14832,6 +14811,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) { } @@ -14840,14 +14822,21 @@ public final class ActivityManagerService extends ActivityManagerNative } } } else { - final int N = mLruProcesses.size(); - for (i=0; i<N; i++) { + allChanged = mProcessTracker.setMemFactorLocked( + ProcessTracker.ADJ_MEM_FACTOR_NORMAL, !mSleeping, now); + for (int i=N-1; 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) { @@ -14865,16 +14854,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<ProcessRecord, AppBindRecord> apps - = new HashMap<ProcessRecord, AppBindRecord>(); + final ArrayMap<ProcessRecord, AppBindRecord> apps + = new ArrayMap<ProcessRecord, AppBindRecord>(); /** 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<AppBindRecord> 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<apps.size(); i++) { + AppBindRecord a = apps.valueAt(i); + 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 + " "); } } @@ -81,12 +79,11 @@ final class IntentBindRecord { int collectFlags() { int flags = 0; - if (apps.size() > 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<ActivityRecord> activities = new ArrayList<ActivityRecord>(); // all ServiceRecord running in this process - final HashSet<ServiceRecord> services = new HashSet<ServiceRecord>(); + final ArraySet<ServiceRecord> services = new ArraySet<ServiceRecord>(); // services that are currently executing code (need to remain foreground). - final HashSet<ServiceRecord> executingServices - = new HashSet<ServiceRecord>(); + final ArraySet<ServiceRecord> executingServices + = new ArraySet<ServiceRecord>(); // All ConnectionRecord this process holds - final HashSet<ConnectionRecord> connections - = new HashSet<ConnectionRecord>(); + final ArraySet<ConnectionRecord> connections + = new ArraySet<ConnectionRecord>(); // all IIntentReceivers that are registered from this process. - final HashSet<ReceiverList> receivers = new HashSet<ReceiverList>(); + final ArraySet<ReceiverList> receivers = new ArraySet<ReceiverList>(); // class (String) -> ContentProviderRecord final ArrayMap<String, ContentProviderRecord> pubProviders = new ArrayMap<String, ContentProviderRecord>(); @@ -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<services.size(); i++) { + pw.print(prefix); pw.print(" - "); pw.println(services.valueAt(i)); } } if (executingServices.size() > 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<executingServices.size(); i++) { + pw.print(prefix); pw.print(" - "); pw.println(executingServices.valueAt(i)); } } if (connections.size() > 0) { pw.print(prefix); pw.println("Connections:"); - for (ConnectionRecord cr : connections) { - pw.print(prefix); pw.print(" - "); pw.println(cr); + for (int i=0; i<connections.size(); i++) { + pw.print(prefix); pw.print(" - "); pw.println(connections.valueAt(i)); } } if (pubProviders.size() > 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<receivers.size(); i++) { + pw.print(prefix); pw.print(" - "); pw.println(receivers.valueAt(i)); } } } @@ -353,8 +348,7 @@ final class ProcessRecord { baseProcessTracker = tracker; pkgList.put(_info.packageName, tracker); thread = _thread; - maxAdj = ProcessList.CACHED_APP_MAX_ADJ; - cachedAdj = clientCachedAdj = emptyAdj = ProcessList.CACHED_APP_MIN_ADJ; + maxAdj = ProcessList.UNKNOWN_ADJ; curRawAdj = setRawAdj = -100; curAdj = setAdj = -100; persistent = false; @@ -400,14 +394,35 @@ final class ProcessRecord { void updateHasAboveClientLocked() { hasAboveClient = false; - if (connections.size() > 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<String, ProcessTracker.ProcessState> 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<PackageState> mPackages = new ProcessMap<PackageState>(); final ProcessMap<ProcessState> mProcesses = new ProcessMap<ProcessState>(); @@ -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<String, SparseArray<PackageState>> 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<pkgMap.size(); ip++) { String pkgName = pkgMap.keyAt(ip); @@ -1629,6 +1710,7 @@ public final class ProcessTracker { if (now > (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.screenStates.length; is++) { for (int im=0; im<data.memStates.length; im++) { for (int ip=0; ip<data.procStates.length; ip++) { @@ -1880,10 +1973,16 @@ public final class ProcessTracker { long minPss = proc.getPssMinimum(bucket); long avgPss = proc.getPssAverage(bucket); long maxPss = proc.getPssMaximum(bucket); + long minUss = proc.getPssUssMinimum(bucket); + long avgUss = proc.getPssUssAverage(bucket); + long maxUss = proc.getPssUssMaximum(bucket); if (data.numPss == 0) { data.minPss = minPss; data.avgPss = avgPss; data.maxPss = maxPss; + data.minUss = minUss; + data.avgUss = avgUss; + data.maxUss = maxUss; } else { if (minPss < data.minPss) { data.minPss = minPss; @@ -1893,6 +1992,14 @@ public final class ProcessTracker { if (maxPss > 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<args.length; i++) { String arg = args[i]; @@ -2457,6 +2587,7 @@ public final class ProcessTracker { } else if ("--current".equals(arg)) { currentOnly = true; } else if ("--commit".equals(arg)) { + mState.mFlags |= State.FLAG_COMPLETE; mState.writeStateLocked(true, true); pw.println("Process stats committed."); return; @@ -2557,6 +2688,13 @@ public final class ProcessTracker { if (DEBUG) Slog.d(TAG, "Retrieving state: " + files.get(i)); try { State state = new State(files.get(i)); + if (state.mReadError != null) { + pw.print("Failure reading "); pw.print(files.get(i)); + pw.print("; "); pw.println(state.mReadError); + if (DEBUG) Slog.d(TAG, "Deleting state: " + files.get(i)); + (new File(files.get(i))).delete(); + continue; + } String fileStr = state.mFile.getBaseFile().getPath(); boolean checkedIn = fileStr.endsWith(STATE_FILE_CHECKIN_SUFFIX); if (isCheckin || isCompact) { @@ -2588,7 +2726,6 @@ public final class ProcessTracker { pw.print("**** FAILURE DUMPING STATE: "); pw.println(files.get(i)); e.printStackTrace(pw); } - if (DEBUG) Slog.d(TAG, "Deleting state: " + files.get(i)); } } } finally { |