summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDianne Hackborn <hackbod@google.com>2012-03-06 14:57:58 -0800
committerDianne Hackborn <hackbod@google.com>2012-03-06 18:46:32 -0800
commit27ff913d56de8400083a13fc572e2812b32c890c (patch)
treef30ec6efb44658b448c5e049bf0c1c3e40f0f06c
parent4f03d35f9c040222e6a46dde807fe2ff7852beb8 (diff)
downloadframeworks_base-27ff913d56de8400083a13fc572e2812b32c890c.zip
frameworks_base-27ff913d56de8400083a13fc572e2812b32c890c.tar.gz
frameworks_base-27ff913d56de8400083a13fc572e2812b32c890c.tar.bz2
Work on more low memory reporting to apps.
There are now some new trim memory levels that are sent to non-background applications as RAM becomes low. There is a new API for an application to retrieve information about memory trimming and such on demand. Fixed various checks against the memory trim level to be robust (not compare against exact values). Change-Id: Ifd1c6151124350168aef20a94e517166fd2e03eb
-rw-r--r--api/current.txt5
-rw-r--r--core/java/android/app/ActivityManager.java33
-rw-r--r--core/java/android/app/ActivityManagerNative.java25
-rw-r--r--core/java/android/app/IActivityManager.java10
-rw-r--r--core/java/android/content/ComponentCallbacks2.java38
-rw-r--r--core/java/android/view/HardwareRenderer.java13
-rw-r--r--core/java/android/view/WindowManagerImpl.java36
-rw-r--r--services/java/com/android/server/am/ActivityManagerService.java84
8 files changed, 181 insertions, 63 deletions
diff --git a/api/current.txt b/api/current.txt
index c9be4cd..261d9ab 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -2722,6 +2722,7 @@ package android.app {
method public int getLauncherLargeIconSize();
method public int getMemoryClass();
method public void getMemoryInfo(android.app.ActivityManager.MemoryInfo);
+ method public static void getMyMemoryState(android.app.ActivityManager.RunningAppProcessInfo);
method public android.os.Debug.MemoryInfo[] getProcessMemoryInfo(int[]);
method public java.util.List<android.app.ActivityManager.ProcessErrorStateInfo> getProcessesInErrorState();
method public java.util.List<android.app.ActivityManager.RecentTaskInfo> getRecentTasks(int, int) throws java.lang.SecurityException;
@@ -2804,6 +2805,7 @@ package android.app {
field public int importanceReasonCode;
field public android.content.ComponentName importanceReasonComponent;
field public int importanceReasonPid;
+ field public int lastTrimLevel;
field public int lru;
field public int pid;
field public java.lang.String[] pkgList;
@@ -4826,6 +4828,9 @@ package android.content {
field public static final int TRIM_MEMORY_BACKGROUND = 40; // 0x28
field public static final int TRIM_MEMORY_COMPLETE = 80; // 0x50
field public static final int TRIM_MEMORY_MODERATE = 60; // 0x3c
+ field public static final int TRIM_MEMORY_RUNNING_CRITICAL = 15; // 0xf
+ field public static final int TRIM_MEMORY_RUNNING_LOW = 10; // 0xa
+ field public static final int TRIM_MEMORY_RUNNING_MODERATE = 5; // 0x5
field public static final int TRIM_MEMORY_UI_HIDDEN = 20; // 0x14
}
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index d98d87b..59c803e 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -1145,7 +1145,14 @@ public class ActivityManager {
* @hide
*/
public int flags;
-
+
+ /**
+ * Last memory trim level reported to the process: corresponds to
+ * the values supplied to {@link android.content.ComponentCallbacks2#onTrimMemory(int)
+ * ComponentCallbacks2.onTrimMemory(int)}.
+ */
+ public int lastTrimLevel;
+
/**
* Constant for {@link #importance}: this process is running the
* foreground UI.
@@ -1212,7 +1219,7 @@ public class ActivityManager {
* be maintained in the future.
*/
public int lru;
-
+
/**
* Constant for {@link #importanceReasonCode}: nothing special has
* been specified for the reason for this level.
@@ -1282,6 +1289,7 @@ public class ActivityManager {
dest.writeInt(uid);
dest.writeStringArray(pkgList);
dest.writeInt(this.flags);
+ dest.writeInt(lastTrimLevel);
dest.writeInt(importance);
dest.writeInt(lru);
dest.writeInt(importanceReasonCode);
@@ -1296,6 +1304,7 @@ public class ActivityManager {
uid = source.readInt();
pkgList = source.readStringArray();
flags = source.readInt();
+ lastTrimLevel = source.readInt();
importance = source.readInt();
lru = source.readInt();
importanceReasonCode = source.readInt();
@@ -1349,7 +1358,25 @@ public class ActivityManager {
return null;
}
}
-
+
+ /**
+ * Return global memory state information for the calling process. This
+ * does not fill in all fields of the {@link RunningAppProcessInfo}. The
+ * only fields that will be filled in are
+ * {@link RunningAppProcessInfo#pid},
+ * {@link RunningAppProcessInfo#uid},
+ * {@link RunningAppProcessInfo#lastTrimLevel},
+ * {@link RunningAppProcessInfo#importance},
+ * {@link RunningAppProcessInfo#lru}, and
+ * {@link RunningAppProcessInfo#importanceReasonCode}.
+ */
+ static public void getMyMemoryState(RunningAppProcessInfo outState) {
+ try {
+ ActivityManagerNative.getDefault().getMyMemoryState(outState);
+ } catch (RemoteException e) {
+ }
+ }
+
/**
* Return information about the memory usage of one or more processes.
*
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index 24079a5..b952649 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -1126,7 +1126,17 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM
reply.writeNoException();
return true;
}
-
+
+ case GET_MY_MEMORY_STATE_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ ActivityManager.RunningAppProcessInfo info =
+ new ActivityManager.RunningAppProcessInfo();
+ getMyMemoryState(info);
+ reply.writeNoException();
+ info.writeToParcel(reply, 0);
+ return true;
+ }
+
case GET_DEVICE_CONFIGURATION_TRANSACTION: {
data.enforceInterface(IActivityManager.descriptor);
ConfigurationInfo config = getDeviceConfigurationInfo();
@@ -2973,6 +2983,19 @@ class ActivityManagerProxy implements IActivityManager
reply.recycle();
}
+ public void getMyMemoryState(ActivityManager.RunningAppProcessInfo outInfo)
+ throws RemoteException
+ {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ mRemote.transact(GET_MY_MEMORY_STATE_TRANSACTION, data, reply, 0);
+ reply.readException();
+ outInfo.readFromParcel(reply);
+ reply.recycle();
+ data.recycle();
+ }
+
public ConfigurationInfo getDeviceConfigurationInfo() throws RemoteException
{
Parcel data = Parcel.obtain();
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index 53a71db..ea2545f 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -278,13 +278,16 @@ public interface IActivityManager extends IInterface {
* SIGUSR1 is delivered. All others are ignored.
*/
public void signalPersistentProcesses(int signal) throws RemoteException;
- // Retrieve info of applications installed on external media that are currently
- // running.
+ // Retrieve running application processes in the system
public List<ActivityManager.RunningAppProcessInfo> getRunningAppProcesses()
throws RemoteException;
- // Retrieve running application processes in the system
+ // Retrieve info of applications installed on external media that are currently
+ // running.
public List<ApplicationInfo> getRunningExternalApplications()
throws RemoteException;
+ // Get memory information about the calling process.
+ public void getMyMemoryState(ActivityManager.RunningAppProcessInfo outInfo)
+ throws RemoteException;
// Get device configuration
public ConfigurationInfo getDeviceConfigurationInfo() throws RemoteException;
@@ -606,4 +609,5 @@ public interface IActivityManager extends IInterface {
int KILL_ALL_BACKGROUND_PROCESSES_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+139;
int GET_CONTENT_PROVIDER_EXTERNAL_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+140;
int REMOVE_CONTENT_PROVIDER_EXTERNAL_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+141;
+ int GET_MY_MEMORY_STATE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+142;
}
diff --git a/core/java/android/content/ComponentCallbacks2.java b/core/java/android/content/ComponentCallbacks2.java
index 8b9f97c..85294dd 100644
--- a/core/java/android/content/ComponentCallbacks2.java
+++ b/core/java/android/content/ComponentCallbacks2.java
@@ -52,15 +52,49 @@ public interface ComponentCallbacks2 extends ComponentCallbacks {
static final int TRIM_MEMORY_UI_HIDDEN = 20;
/**
+ * Level for {@link #onTrimMemory(int)}: the process is not an expendable
+ * background process, but the device is running extremely low on memory
+ * and is about to not be able to keep any background processes running.
+ * Your running process should free up as many non-critical resources as it
+ * can to allow that memory to be used elsewhere. The next thing that
+ * will happen after this is {@link #onLowMemory()} called to report that
+ * nothing at all can be kept in the background, a situation that can start
+ * to notably impact the user.
+ */
+ static final int TRIM_MEMORY_RUNNING_CRITICAL = 15;
+
+ /**
+ * Level for {@link #onTrimMemory(int)}: the process is not an expendable
+ * background process, but the device is running low on memory.
+ * Your running process should free up unneeded resources to allow that
+ * memory to be used elsewhere.
+ */
+ static final int TRIM_MEMORY_RUNNING_LOW = 10;
+
+
+ /**
+ * Level for {@link #onTrimMemory(int)}: the process is not an expendable
+ * background process, but the device is running moderately low on memory.
+ * Your running process may want to release some unneeded resources for
+ * use elsewhere.
+ */
+ static final int TRIM_MEMORY_RUNNING_MODERATE = 5;
+
+ /**
* Called when the operating system has determined that it is a good
* time for a process to trim unneeded memory from its process. This will
* happen for example when it goes in the background and there is not enough
- * memory to keep as many background processes running as desired.
+ * memory to keep as many background processes running as desired. You
+ * should never compare to exact values of the level, since new intermediate
+ * values may be added -- you will typically want to compare if the value
+ * is greater or equal to a level you are interested in.
*
* @param level The context of the trim, giving a hint of the amount of
* trimming the application may like to perform. May be
* {@link #TRIM_MEMORY_COMPLETE}, {@link #TRIM_MEMORY_MODERATE},
- * {@link #TRIM_MEMORY_BACKGROUND}, or {@link #TRIM_MEMORY_UI_HIDDEN}.
+ * {@link #TRIM_MEMORY_BACKGROUND}, {@link #TRIM_MEMORY_UI_HIDDEN},
+ * {@link #TRIM_MEMORY_RUNNING_CRITICAL}, {@link #TRIM_MEMORY_RUNNING_LOW},
+ * or {@link #TRIM_MEMORY_RUNNING_MODERATE}.
*/
void onTrimMemory(int level);
}
diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java
index ec95863..bf91700 100644
--- a/core/java/android/view/HardwareRenderer.java
+++ b/core/java/android/view/HardwareRenderer.java
@@ -1284,15 +1284,10 @@ public abstract class HardwareRenderer {
usePbufferSurface(managedContext.getContext());
}
- switch (level) {
- case ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN:
- case ComponentCallbacks2.TRIM_MEMORY_BACKGROUND:
- case ComponentCallbacks2.TRIM_MEMORY_MODERATE:
- GLES20Canvas.flushCaches(GLES20Canvas.FLUSH_CACHES_MODERATE);
- break;
- case ComponentCallbacks2.TRIM_MEMORY_COMPLETE:
- GLES20Canvas.flushCaches(GLES20Canvas.FLUSH_CACHES_FULL);
- break;
+ if (level >= ComponentCallbacks2.TRIM_MEMORY_COMPLETE) {
+ GLES20Canvas.flushCaches(GLES20Canvas.FLUSH_CACHES_FULL);
+ } else if (level >= ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) {
+ GLES20Canvas.flushCaches(GLES20Canvas.FLUSH_CACHES_MODERATE);
}
}
diff --git a/core/java/android/view/WindowManagerImpl.java b/core/java/android/view/WindowManagerImpl.java
index d482b35..0e4a30f 100644
--- a/core/java/android/view/WindowManagerImpl.java
+++ b/core/java/android/view/WindowManagerImpl.java
@@ -432,29 +432,25 @@ public class WindowManagerImpl implements WindowManager {
*/
public void trimMemory(int level) {
if (HardwareRenderer.isAvailable()) {
- switch (level) {
- case ComponentCallbacks2.TRIM_MEMORY_COMPLETE:
- case ComponentCallbacks2.TRIM_MEMORY_MODERATE:
- // On low and medium end gfx devices
- if (!ActivityManager.isHighEndGfx(getDefaultDisplay())) {
- // Destroy all hardware surfaces and resources associated to
- // known windows
- synchronized (this) {
- if (mViews == null) return;
- int count = mViews.length;
- for (int i = 0; i < count; i++) {
- mRoots[i].terminateHardwareResources();
- }
+ // On low and medium end gfx devices
+ if (!ActivityManager.isHighEndGfx(getDefaultDisplay())) {
+ if (level >= ComponentCallbacks2.TRIM_MEMORY_MODERATE) {
+ // Destroy all hardware surfaces and resources associated to
+ // known windows
+ synchronized (this) {
+ if (mViews == null) return;
+ int count = mViews.length;
+ for (int i = 0; i < count; i++) {
+ mRoots[i].terminateHardwareResources();
}
- // Force a full memory flush
- HardwareRenderer.trimMemory(ComponentCallbacks2.TRIM_MEMORY_COMPLETE);
- mNeedsEglTerminate = true;
- break;
}
- // high end gfx devices fall through to next case
- default:
- HardwareRenderer.trimMemory(level);
+ // Force a full memory flush
+ HardwareRenderer.trimMemory(ComponentCallbacks2.TRIM_MEMORY_COMPLETE);
+ mNeedsEglTerminate = true;
+ return;
+ }
}
+ HardwareRenderer.trimMemory(level);
}
}
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 3ac446c..4ea2f04 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -7952,6 +7952,22 @@ public final class ActivityManagerService extends ActivityManagerNative
}
}
+ private void fillInProcMemInfo(ProcessRecord app,
+ ActivityManager.RunningAppProcessInfo outInfo) {
+ outInfo.pid = app.pid;
+ outInfo.uid = app.info.uid;
+ if (mHeavyWeightProcess == app) {
+ outInfo.flags |= ActivityManager.RunningAppProcessInfo.FLAG_CANT_SAVE_STATE;
+ }
+ if (app.persistent) {
+ outInfo.flags |= ActivityManager.RunningAppProcessInfo.FLAG_PERSISTENT;
+ }
+ outInfo.lastTrimLevel = app.trimMemoryLevel;
+ int adj = app.curAdj;
+ outInfo.importance = oomAdjToImportance(adj, outInfo);
+ outInfo.importanceReasonCode = app.adjTypeCode;
+ }
+
public List<ActivityManager.RunningAppProcessInfo> getRunningAppProcesses() {
enforceNotIsolatedCaller("getRunningAppProcesses");
// Lazy instantiation of list
@@ -7965,16 +7981,7 @@ public final class ActivityManagerService extends ActivityManagerNative
ActivityManager.RunningAppProcessInfo currApp =
new ActivityManager.RunningAppProcessInfo(app.processName,
app.pid, app.getPackageList());
- currApp.uid = app.info.uid;
- if (mHeavyWeightProcess == app) {
- currApp.flags |= ActivityManager.RunningAppProcessInfo.FLAG_CANT_SAVE_STATE;
- }
- if (app.persistent) {
- currApp.flags |= ActivityManager.RunningAppProcessInfo.FLAG_PERSISTENT;
- }
- int adj = app.curAdj;
- currApp.importance = oomAdjToImportance(adj, currApp);
- currApp.importanceReasonCode = app.adjTypeCode;
+ fillInProcMemInfo(app, currApp);
if (app.adjSource instanceof ProcessRecord) {
currApp.importanceReasonPid = ((ProcessRecord)app.adjSource).pid;
currApp.importanceReasonImportance = oomAdjToImportance(
@@ -8026,6 +8033,18 @@ public final class ActivityManagerService extends ActivityManagerNative
}
@Override
+ public void getMyMemoryState(ActivityManager.RunningAppProcessInfo outInfo) {
+ enforceNotIsolatedCaller("getMyMemoryState");
+ synchronized (this) {
+ ProcessRecord proc;
+ synchronized (mPidsSelfLocked) {
+ proc = mPidsSelfLocked.get(Binder.getCallingPid());
+ }
+ fillInProcMemInfo(proc, outInfo);
+ }
+ }
+
+ @Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (checkCallingPermission(android.Manifest.permission.DUMP)
!= PackageManager.PERMISSION_GRANTED) {
@@ -8479,8 +8498,8 @@ public final class ActivityManagerService extends ActivityManagerNative
pw.print(" Process "); pw.print(pname);
pw.print(" uid "); pw.print(puid);
pw.print(": last crashed ");
- pw.print((now-uids.valueAt(i)));
- pw.println(" ms ago");
+ TimeUtils.formatDuration(now-uids.valueAt(i), pw);
+ pw.println(" ago");
}
}
}
@@ -13982,6 +14001,14 @@ public final class ActivityManagerService extends ActivityManagerNative
if (mPreviousProcess != null) minFactor++;
if (factor < minFactor) factor = minFactor;
step = 0;
+ int fgTrimLevel;
+ if (numHidden <= (ProcessList.MAX_HIDDEN_APPS/5)) {
+ fgTrimLevel = ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL;
+ } else if (numHidden <= (ProcessList.MAX_HIDDEN_APPS/3)) {
+ fgTrimLevel = ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW;
+ } else {
+ fgTrimLevel = ComponentCallbacks2.TRIM_MEMORY_RUNNING_MODERATE;
+ }
int curLevel = ComponentCallbacks2.TRIM_MEMORY_COMPLETE;
for (i=0; i<N; i++) {
ProcessRecord app = mLruProcesses.get(i);
@@ -14008,6 +14035,7 @@ public final class ActivityManagerService extends ActivityManagerNative
app.trimMemoryLevel = curLevel;
step++;
if (step >= factor) {
+ step = 0;
switch (curLevel) {
case ComponentCallbacks2.TRIM_MEMORY_COMPLETE:
curLevel = ComponentCallbacks2.TRIM_MEMORY_MODERATE;
@@ -14027,20 +14055,28 @@ public final class ActivityManagerService extends ActivityManagerNative
}
}
app.trimMemoryLevel = ComponentCallbacks2.TRIM_MEMORY_BACKGROUND;
- } else if ((app.curAdj > ProcessList.VISIBLE_APP_ADJ || app.systemNoUi)
- && app.pendingUiClean) {
- if (app.trimMemoryLevel < ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN
- && app.thread != null) {
+ } else {
+ if ((app.curAdj > ProcessList.VISIBLE_APP_ADJ || 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 {
+ app.thread.scheduleTrimMemory(level);
+ } catch (RemoteException e) {
+ }
+ }
+ app.pendingUiClean = false;
+ }
+ if (app.trimMemoryLevel < fgTrimLevel && app.thread != null) {
try {
- app.thread.scheduleTrimMemory(
- ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN);
+ app.thread.scheduleTrimMemory(fgTrimLevel);
} catch (RemoteException e) {
}
}
- app.trimMemoryLevel = ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN;
- app.pendingUiClean = false;
- } else {
- app.trimMemoryLevel = 0;
+ app.trimMemoryLevel = fgTrimLevel;
}
}
} else {
@@ -14057,11 +14093,9 @@ public final class ActivityManagerService extends ActivityManagerNative
} catch (RemoteException e) {
}
}
- app.trimMemoryLevel = ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN;
app.pendingUiClean = false;
- } else {
- app.trimMemoryLevel = 0;
}
+ app.trimMemoryLevel = 0;
}
}