From c68c913d357e2955d4bd7ca52829071e531c7825 Mon Sep 17 00:00:00 2001 From: Dianne Hackborn Date: Fri, 29 Jul 2011 01:25:18 -0700 Subject: Various work on out of memory managment. - Improve how we handle processes that have shown UI, to take care of more cases where we want to push them into the background LRU list. - New trim memory level for when an application that has done UI is no longer visible to the user. - Add APIs to get new trim memory callback. - Add a host of new bind flags to tweak how the system will adjust the OOM level of the target process. Change-Id: I23ba354112f411a9f8773a67426b4dff85fa2439 --- .../android/server/am/ActivityManagerService.java | 222 +++++++++++++++------ .../java/com/android/server/am/ActivityStack.java | 1 + .../java/com/android/server/am/AppBindRecord.java | 2 +- .../java/com/android/server/am/ProcessRecord.java | 21 +- 4 files changed, 188 insertions(+), 58 deletions(-) (limited to 'services/java/com') diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java index 66f88fc..14c6306 100644 --- a/services/java/com/android/server/am/ActivityManagerService.java +++ b/services/java/com/android/server/am/ActivityManagerService.java @@ -53,7 +53,7 @@ import android.app.Service; import android.app.backup.IBackupManager; import android.content.ActivityNotFoundException; import android.content.BroadcastReceiver; -import android.content.ComponentCallbacks; +import android.content.ComponentCallbacks2; import android.content.ComponentName; import android.content.ContentResolver; import android.content.Context; @@ -8067,6 +8067,21 @@ public final class ActivityManagerService extends ActivityManagerNative if (needSep) pw.println(" "); needSep = true; + pw.println(" OOM levels:"); + pw.print(" SYSTEM_ADJ: "); pw.println(SYSTEM_ADJ); + pw.print(" CORE_SERVER_ADJ: "); pw.println(CORE_SERVER_ADJ); + pw.print(" FOREGROUND_APP_ADJ: "); pw.println(FOREGROUND_APP_ADJ); + pw.print(" VISIBLE_APP_ADJ: "); pw.println(VISIBLE_APP_ADJ); + pw.print(" PERCEPTIBLE_APP_ADJ: "); pw.println(PERCEPTIBLE_APP_ADJ); + pw.print(" HEAVY_WEIGHT_APP_ADJ: "); pw.println(HEAVY_WEIGHT_APP_ADJ); + pw.print(" BACKUP_APP_ADJ: "); pw.println(BACKUP_APP_ADJ); + pw.print(" SECONDARY_SERVER_ADJ: "); pw.println(SECONDARY_SERVER_ADJ); + pw.print(" HOME_APP_ADJ: "); pw.println(HOME_APP_ADJ); + pw.print(" HIDDEN_APP_MIN_ADJ: "); pw.println(HIDDEN_APP_MIN_ADJ); + pw.print(" EMPTY_APP_ADJ: "); pw.println(EMPTY_APP_ADJ); + + if (needSep) pw.println(" "); + needSep = true; pw.println(" Process OOM control:"); dumpProcessOomList(pw, this, procs, " ", "Proc", "PERS", true); @@ -8814,7 +8829,8 @@ public final class ActivityManagerService extends ActivityManagerNative pw.print(" "); pw.print("keeping="); pw.print(r.keeping); pw.print(" hidden="); pw.print(r.hidden); - pw.print(" empty="); pw.println(r.empty); + pw.print(" empty="); pw.print(r.empty); + pw.print(" hasAboveClient="); pw.println(r.hasAboveClient); if (!r.keeping) { if (r.lastWakeTime != 0) { @@ -9226,6 +9242,7 @@ public final class ActivityManagerService extends ActivityManagerNative app.foregroundServices = false; app.foregroundActivities = false; app.hasShownUi = false; + app.hasAboveClient = false; killServicesLocked(app, allowRestart); @@ -10452,6 +10469,9 @@ public final class ActivityManagerService extends ActivityManagerNative activity.connections.add(c); } b.client.connections.add(c); + if ((c.flags&Context.BIND_ABOVE_CLIENT) != 0) { + b.client.hasAboveClient = true; + } clist = mServiceConnections.get(binder); if (clist == null) { clist = new ArrayList(); @@ -10523,6 +10543,9 @@ public final class ActivityManagerService extends ActivityManagerNative } if (b.client != skipApp) { b.client.connections.remove(c); + if ((c.flags&Context.BIND_ABOVE_CLIENT) != 0) { + b.client.updateHasAboveClientLocked(); + } } clist = mServiceConnections.get(binder); if (clist != null) { @@ -12577,9 +12600,9 @@ public final class ActivityManagerService extends ActivityManagerNative // an earlier hidden adjustment that isn't really for us... if // so, use the new hidden adjustment. if (!recursed && app.hidden) { - app.curAdj = hiddenAdj; + app.curAdj = app.curRawAdj = hiddenAdj; } - return app.curAdj; + return app.curRawAdj; } if (app.thread == null) { @@ -12588,28 +12611,47 @@ public final class ActivityManagerService extends ActivityManagerNative return (app.curAdj=EMPTY_APP_ADJ); } + app.adjTypeCode = ActivityManager.RunningAppProcessInfo.REASON_UNKNOWN; + app.adjSource = null; + app.adjTarget = null; + app.empty = false; + app.hidden = false; + + final int activitiesSize = app.activities.size(); + if (app.maxAdj <= FOREGROUND_APP_ADJ) { // The max adjustment doesn't allow this app to be anything // below foreground, so it is not worth doing work for it. app.adjType = "fixed"; app.adjSeq = mAdjSeq; app.curRawAdj = app.maxAdj; + app.foregroundActivities = false; app.keeping = true; app.curSchedGroup = Process.THREAD_GROUP_DEFAULT; + // System process can do UI, and when they do we want to have + // them trim their memory after the user leaves the UI. To + // facilitate this, here we need to determine whether or not it + // is currently showing UI. + app.systemNoUi = true; + if (app == TOP_APP) { + app.systemNoUi = false; + } else if (activitiesSize > 0) { + for (int j = 0; j < activitiesSize; j++) { + final ActivityRecord r = app.activities.get(j); + if (r.visible) { + app.systemNoUi = false; + break; + } + } + } return (app.curAdj=app.maxAdj); } final boolean hadForegroundActivities = app.foregroundActivities; - app.adjTypeCode = ActivityManager.RunningAppProcessInfo.REASON_UNKNOWN; - app.adjSource = null; - app.adjTarget = null; - app.keeping = false; - app.empty = false; - app.hidden = false; app.foregroundActivities = false; - - final int activitiesSize = app.activities.size(); + app.keeping = false; + app.systemNoUi = false; // Determine the importance of the process, starting with most // important to least, and assign an appropriate OOM adjustment. @@ -12784,7 +12826,7 @@ public final class ActivityManagerService extends ActivityManagerNative // Binding to ourself is not interesting. continue; } - if ((cr.flags&Context.BIND_AUTO_CREATE) != 0) { + if ((cr.flags&Context.BIND_WAIVE_PRIORITY) == 0) { ProcessRecord client = cr.binding.client; int clientAdj = adj; int myHiddenAdj = hiddenAdj; @@ -12825,15 +12867,32 @@ public final class ActivityManagerService extends ActivityManagerNative } } if (adj > clientAdj) { - adj = clientAdj >= VISIBLE_APP_ADJ - ? clientAdj : VISIBLE_APP_ADJ; - if (!client.hidden) { - app.hidden = false; - } - if (client.keeping) { - app.keeping = true; + // 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 && clientAdj > PERCEPTIBLE_APP_ADJ) { + adjType = "bound-bg-ui-services"; + } else { + if ((cr.flags&(Context.BIND_ABOVE_CLIENT + |Context.BIND_IMPORTANT)) != 0) { + adj = clientAdj; + } else if (clientAdj >= VISIBLE_APP_ADJ) { + adj = clientAdj; + } else { + adj = VISIBLE_APP_ADJ; + } + if (!client.hidden) { + app.hidden = false; + } + if (client.keeping) { + app.keeping = true; + } + adjType = "service"; } - adjType = "service"; } if (adjType != null) { app.adjType = adjType; @@ -12848,21 +12907,22 @@ public final class ActivityManagerService extends ActivityManagerNative } } } - ActivityRecord a = cr.activity; - //if (a != null) { - // Slog.i(TAG, "Connection to " + a ": state=" + a.state); - //} - if (a != null && adj > FOREGROUND_APP_ADJ && - (a.state == ActivityState.RESUMED - || a.state == ActivityState.PAUSING)) { - adj = FOREGROUND_APP_ADJ; - schedGroup = Process.THREAD_GROUP_DEFAULT; - app.hidden = false; - app.adjType = "service"; - app.adjTypeCode = ActivityManager.RunningAppProcessInfo - .REASON_SERVICE_IN_USE; - app.adjSource = a; - app.adjTarget = s.name; + if ((cr.flags&Context.BIND_ADJUST_WITH_ACTIVITY) != 0) { + ActivityRecord a = cr.activity; + if (a != null && adj > FOREGROUND_APP_ADJ && + (a.visible || a.state == ActivityState.RESUMED + || a.state == ActivityState.PAUSING)) { + adj = FOREGROUND_APP_ADJ; + if ((cr.flags&Context.BIND_NOT_FOREGROUND) == 0) { + schedGroup = Process.THREAD_GROUP_DEFAULT; + } + app.hidden = false; + app.adjType = "service"; + app.adjTypeCode = ActivityManager.RunningAppProcessInfo + .REASON_SERVICE_IN_USE; + app.adjSource = a; + app.adjTarget = s.name; + } } } } @@ -12906,15 +12966,19 @@ public final class ActivityManagerService extends ActivityManagerNative int clientAdj = computeOomAdjLocked( client, myHiddenAdj, TOP_APP, true); if (adj > clientAdj) { - adj = clientAdj > FOREGROUND_APP_ADJ - ? clientAdj : FOREGROUND_APP_ADJ; + if (app.hasShownUi && clientAdj > PERCEPTIBLE_APP_ADJ) { + app.adjType = "bg-ui-provider"; + } else { + adj = clientAdj > FOREGROUND_APP_ADJ + ? clientAdj : FOREGROUND_APP_ADJ; + app.adjType = "provider"; + } if (!client.hidden) { app.hidden = false; } if (client.keeping) { app.keeping = true; } - app.adjType = "provider"; app.adjTypeCode = ActivityManager.RunningAppProcessInfo .REASON_PROVIDER_IN_USE; app.adjSource = client; @@ -12955,6 +13019,25 @@ 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 < FOREGROUND_APP_ADJ) { + // System process will not get dropped, ever + } else if (adj < VISIBLE_APP_ADJ) { + adj = VISIBLE_APP_ADJ; + } else if (adj < PERCEPTIBLE_APP_ADJ) { + adj = PERCEPTIBLE_APP_ADJ; + } else if (adj < HIDDEN_APP_MIN_ADJ) { + adj = HIDDEN_APP_MIN_ADJ; + } else if (adj < EMPTY_APP_ADJ) { + adj++; + } + } + app.curAdj = adj; app.curSchedGroup = schedGroup; @@ -12963,7 +13046,7 @@ public final class ActivityManagerService extends ActivityManagerNative app.foregroundActivities).sendToTarget(); } - return adj; + return app.curRawAdj; } /** @@ -13204,7 +13287,7 @@ public final class ActivityManagerService extends ActivityManagerNative final boolean wasKeeping = app.keeping; - int adj = computeOomAdjLocked(app, hiddenAdj, TOP_APP, false); + computeOomAdjLocked(app, hiddenAdj, TOP_APP, false); if (app.curRawAdj != app.setRawAdj) { if (app.curRawAdj > FOREGROUND_APP_ADJ @@ -13233,14 +13316,14 @@ public final class ActivityManagerService extends ActivityManagerNative app.setRawAdj = app.curRawAdj; } - if (adj != app.setAdj) { - if (Process.setOomAdj(app.pid, adj)) { + if (app.curAdj != app.setAdj) { + if (Process.setOomAdj(app.pid, app.curAdj)) { if (DEBUG_SWITCH || DEBUG_OOM_ADJ) Slog.v( TAG, "Set app " + app.processName + - " oom adj to " + adj + " because " + app.adjType); - app.setAdj = adj; + " oom adj to " + app.curAdj + " because " + app.adjType); + app.setAdj = app.curAdj; } else { - Slog.w(TAG, "Failed setting oom adj of " + app + " to " + adj); + Slog.w(TAG, "Failed setting oom adj of " + app + " to " + app.curAdj); } } if (app.setSchedGroup != app.curSchedGroup) { @@ -13377,7 +13460,7 @@ public final class ActivityManagerService extends ActivityManagerNative final int N = mLruProcesses.size(); factor = numBg/3; step = 0; - int curLevel = ComponentCallbacks.TRIM_MEMORY_COMPLETE; + int curLevel = ComponentCallbacks2.TRIM_MEMORY_COMPLETE; for (i=0; i= HIDDEN_APP_MIN_ADJ && !app.killedBackground) { @@ -13386,7 +13469,7 @@ public final class ActivityManagerService extends ActivityManagerNative app.thread.scheduleTrimMemory(curLevel); } catch (RemoteException e) { } - if (curLevel >= ComponentCallbacks.TRIM_MEMORY_COMPLETE) { + if (curLevel >= ComponentCallbacks2.TRIM_MEMORY_COMPLETE) { // For these apps we will also finish their activities // to help them free memory. mMainStack.destroyActivitiesLocked(app, false); @@ -13396,23 +13479,36 @@ public final class ActivityManagerService extends ActivityManagerNative step++; if (step >= factor) { switch (curLevel) { - case ComponentCallbacks.TRIM_MEMORY_COMPLETE: - curLevel = ComponentCallbacks.TRIM_MEMORY_MODERATE; + case ComponentCallbacks2.TRIM_MEMORY_COMPLETE: + curLevel = ComponentCallbacks2.TRIM_MEMORY_MODERATE; break; - case ComponentCallbacks.TRIM_MEMORY_MODERATE: - curLevel = ComponentCallbacks.TRIM_MEMORY_BACKGROUND; + case ComponentCallbacks2.TRIM_MEMORY_MODERATE: + curLevel = ComponentCallbacks2.TRIM_MEMORY_BACKGROUND; break; } } } else if (app.curAdj == HEAVY_WEIGHT_APP_ADJ) { - if (app.trimMemoryLevel < ComponentCallbacks.TRIM_MEMORY_BACKGROUND + if (app.trimMemoryLevel < ComponentCallbacks2.TRIM_MEMORY_BACKGROUND && app.thread != null) { try { - app.thread.scheduleTrimMemory(ComponentCallbacks.TRIM_MEMORY_BACKGROUND); + app.thread.scheduleTrimMemory( + ComponentCallbacks2.TRIM_MEMORY_BACKGROUND); } catch (RemoteException e) { } } - app.trimMemoryLevel = ComponentCallbacks.TRIM_MEMORY_BACKGROUND; + app.trimMemoryLevel = ComponentCallbacks2.TRIM_MEMORY_BACKGROUND; + } else if ((app.curAdj > VISIBLE_APP_ADJ || app.systemNoUi) + && app.pendingUiClean) { + if (app.trimMemoryLevel < ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN + && app.thread != null) { + try { + app.thread.scheduleTrimMemory( + ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN); + } catch (RemoteException e) { + } + } + app.trimMemoryLevel = ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN; + app.pendingUiClean = false; } else { app.trimMemoryLevel = 0; } @@ -13421,7 +13517,21 @@ public final class ActivityManagerService extends ActivityManagerNative final int N = mLruProcesses.size(); for (i=0; i VISIBLE_APP_ADJ || app.systemNoUi) + && app.pendingUiClean) { + if (app.trimMemoryLevel < ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN + && app.thread != null) { + try { + app.thread.scheduleTrimMemory( + ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN); + } catch (RemoteException e) { + } + } + app.trimMemoryLevel = ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN; + app.pendingUiClean = false; + } else { + app.trimMemoryLevel = 0; + } } } diff --git a/services/java/com/android/server/am/ActivityStack.java b/services/java/com/android/server/am/ActivityStack.java index cc58eaf..33b21ab 100644 --- a/services/java/com/android/server/am/ActivityStack.java +++ b/services/java/com/android/server/am/ActivityStack.java @@ -560,6 +560,7 @@ final class ActivityStack { showAskCompatModeDialogLocked(r); r.compat = mService.compatibilityInfoForPackageLocked(r.info.applicationInfo); app.hasShownUi = true; + app.pendingUiClean = true; app.thread.scheduleLaunchActivity(new Intent(r.intent), r, System.identityHashCode(r), r.info, r.compat, r.icicle, results, newIntents, !andResume, diff --git a/services/java/com/android/server/am/AppBindRecord.java b/services/java/com/android/server/am/AppBindRecord.java index 9c57360..f1c54fa 100644 --- a/services/java/com/android/server/am/AppBindRecord.java +++ b/services/java/com/android/server/am/AppBindRecord.java @@ -26,7 +26,7 @@ import java.util.Iterator; class AppBindRecord { final ServiceRecord service; // The running service. final IntentBindRecord intent; // The intent we are bound to. - final ProcessRecord client; // Who has started/bound the service. + final ProcessRecord client; // Who has started/bound the service. final HashSet connections = new HashSet(); // All ConnectionRecord for this client. diff --git a/services/java/com/android/server/am/ProcessRecord.java b/services/java/com/android/server/am/ProcessRecord.java index 5b59363..a896ce4 100644 --- a/services/java/com/android/server/am/ProcessRecord.java +++ b/services/java/com/android/server/am/ProcessRecord.java @@ -23,6 +23,7 @@ import android.app.Dialog; import android.app.IApplicationThread; import android.app.IInstrumentationWatcher; import android.content.ComponentName; +import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.res.CompatibilityInfo; import android.os.Bundle; @@ -66,7 +67,10 @@ class ProcessRecord { boolean setIsForeground; // Running foreground UI when last set? boolean foregroundServices; // Running any services that are foreground? boolean foregroundActivities; // Running any activities that are foreground? + boolean systemNoUi; // This is a system process, but not currently showing UI. boolean hasShownUi; // Has UI been shown in this process since it was started? + boolean pendingUiClean; // Want to clean up resources from showing UI? + 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 String waitingToKill; // Process is waiting to be killed when in the bg; reason @@ -185,8 +189,11 @@ class ProcessRecord { pw.print(" set="); pw.println(setAdj); pw.print(prefix); pw.print("curSchedGroup="); pw.print(curSchedGroup); pw.print(" setSchedGroup="); pw.print(setSchedGroup); + pw.print(" systemNoUi="); pw.print(systemNoUi); pw.print(" trimMemoryLevel="); pw.println(trimMemoryLevel); - pw.print(" hasShownUi="); pw.println(hasShownUi); + pw.print(prefix); pw.print("hasShownUi="); pw.print(hasShownUi); + pw.print(" pendingUiClean="); pw.print(pendingUiClean); + pw.print(" hasAboveClient="); pw.println(hasAboveClient); pw.print(prefix); pw.print("setIsForeground="); pw.print(setIsForeground); pw.print(" foregroundServices="); pw.print(foregroundServices); pw.print(" forcingToForeground="); pw.println(forcingToForeground); @@ -307,6 +314,18 @@ class ProcessRecord { deathRecipient = null; } + void updateHasAboveClientLocked() { + hasAboveClient = false; + if (connections.size() > 0) { + for (ConnectionRecord cr : connections) { + if ((cr.flags&Context.BIND_ABOVE_CLIENT) != 0) { + hasAboveClient = true; + break; + } + } + } + } + public String toShortString() { if (shortStringName != null) { return shortStringName; -- cgit v1.1