summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Dianne Hackborn <hackbod@google.com> 2018-08-23 15:20:05 -0700
committer Dianne Hackborn <hackbod@google.com> 2018-08-24 11:27:50 -0700
commit1f3b0302fda5cbbe0d5683cc057c84aaa1b8925c (patch)
tree293706192a11eab79bb17ccab6fd0b86ab9cab87
parent1a4b8154fc7f92aec4f621906c707479feead6e3 (diff)
Throttle requests to ActivityManager.getProcessMemoryInfo().
This is very expensive and needs to run in the system process, we don't want apps abusing it. Also don't allow apps to get information about anything but their own process, unless they have the appropriate privileged permissions. Bug: 112537519 Test: manual Change-Id: I103a11f8d5b49fd4536795ea52c05de297698cb5
-rw-r--r--core/java/android/app/ActivityManager.java9
-rw-r--r--core/java/android/os/Debug.java39
-rw-r--r--core/jni/android_os_Debug.cpp2
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerConstants.java17
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java58
-rw-r--r--services/core/java/com/android/server/am/ProcessRecord.java6
6 files changed, 122 insertions, 9 deletions
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index c12d8e21b5e8..61df7cb8dbc4 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -3266,6 +3266,15 @@ public class ActivityManager {
* <p><b>Note: this method is only intended for debugging or building
* a user-facing process management UI.</b></p>
*
+ * <p>This method is different from
+ * {@link android.os.Debug#getMemoryInfo(int, Debug.MemoryInfo)} in that it will
+ * (a) include additional low-level system allocation (via memtrack), and (b)
+ * be significantly limited in the frequency at which data can be sampled.</p>
+ *
+ * As of {@link android.os.Build.VERSION_CODES#Q Android Q}, for regular apps this method
+ * will only return information about the memory info for the processes running as the
+ * caller's uid; no other process memory info is available and will be zero.
+ *
* @param pids The pids of the processes whose memory usage is to be
* retrieved.
* @return Returns an array of memory information, one for each
diff --git a/core/java/android/os/Debug.java b/core/java/android/os/Debug.java
index 6ddcbe0b3b64..aba81af00282 100644
--- a/core/java/android/os/Debug.java
+++ b/core/java/android/os/Debug.java
@@ -321,6 +321,45 @@ public final class Debug
}
/**
+ * @hide Copy contents from another object.
+ */
+ public void set(MemoryInfo other) {
+ dalvikPss = other.dalvikPss;
+ dalvikSwappablePss = other.dalvikSwappablePss;
+ dalvikRss = other.dalvikRss;
+ dalvikPrivateDirty = other.dalvikPrivateDirty;
+ dalvikSharedDirty = other.dalvikSharedDirty;
+ dalvikPrivateClean = other.dalvikPrivateClean;
+ dalvikSharedClean = other.dalvikSharedClean;
+ dalvikSwappedOut = other.dalvikSwappedOut;
+ dalvikSwappedOutPss = other.dalvikSwappedOutPss;
+
+ nativePss = other.nativePss;
+ nativeSwappablePss = other.nativeSwappablePss;
+ nativeRss = other.nativeRss;
+ nativePrivateDirty = other.nativePrivateDirty;
+ nativeSharedDirty = other.nativeSharedDirty;
+ nativePrivateClean = other.nativePrivateClean;
+ nativeSharedClean = other.nativeSharedClean;
+ nativeSwappedOut = other.nativeSwappedOut;
+ nativeSwappedOutPss = other.nativeSwappedOutPss;
+
+ otherPss = other.otherPss;
+ otherSwappablePss = other.otherSwappablePss;
+ otherRss = other.otherRss;
+ otherPrivateDirty = other.otherPrivateDirty;
+ otherSharedDirty = other.otherSharedDirty;
+ otherPrivateClean = other.otherPrivateClean;
+ otherSharedClean = other.otherSharedClean;
+ otherSwappedOut = other.otherSwappedOut;
+ otherSwappedOutPss = other.otherSwappedOutPss;
+
+ hasSwappedOutPss = other.hasSwappedOutPss;
+
+ System.arraycopy(other.otherStats, 0, otherStats, 0, otherStats.length);
+ }
+
+ /**
* Return total PSS memory usage in kB.
*/
public int getTotalPss() {
diff --git a/core/jni/android_os_Debug.cpp b/core/jni/android_os_Debug.cpp
index 638b3ea71d83..26367c200a94 100644
--- a/core/jni/android_os_Debug.cpp
+++ b/core/jni/android_os_Debug.cpp
@@ -602,7 +602,7 @@ static jlong android_os_Debug_getPssPid(JNIEnv *env, jobject clazz, jint pid,
struct graphics_memory_pss graphics_mem;
if (read_memtrack_memory(pid, &graphics_mem) == 0) {
- pss = uss = memtrack = graphics_mem.graphics + graphics_mem.gl + graphics_mem.other;
+ pss = uss = rss = memtrack = graphics_mem.graphics + graphics_mem.gl + graphics_mem.other;
}
{
diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java
index 9bf72fb86cd6..2c8f2fcd8af1 100644
--- a/services/core/java/com/android/server/am/ActivityManagerConstants.java
+++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java
@@ -67,6 +67,7 @@ final class ActivityManagerConstants extends ContentObserver {
static final String KEY_BOUND_SERVICE_CRASH_RESTART_DURATION = "service_crash_restart_duration";
static final String KEY_BOUND_SERVICE_CRASH_MAX_RETRY = "service_crash_max_retry";
static final String KEY_PROCESS_START_ASYNC = "process_start_async";
+ static final String KEY_MEMORY_INFO_THROTTLE_TIME = "memory_info_throttle_time";
private static final int DEFAULT_MAX_CACHED_PROCESSES = 32;
private static final long DEFAULT_BACKGROUND_SETTLE_TIME = 60*1000;
@@ -95,7 +96,7 @@ final class ActivityManagerConstants extends ContentObserver {
private static final long DEFAULT_BOUND_SERVICE_CRASH_RESTART_DURATION = 30*60_000;
private static final int DEFAULT_BOUND_SERVICE_CRASH_MAX_RETRY = 16;
private static final boolean DEFAULT_PROCESS_START_ASYNC = true;
-
+ private static final long DEFAULT_MEMORY_INFO_THROTTLE_TIME = 5*60*1000;
// Maximum number of cached processes we will allow.
public int MAX_CACHED_PROCESSES = DEFAULT_MAX_CACHED_PROCESSES;
@@ -207,6 +208,10 @@ final class ActivityManagerConstants extends ContentObserver {
// Indicates if the processes need to be started asynchronously.
public boolean FLAG_PROCESS_START_ASYNC = DEFAULT_PROCESS_START_ASYNC;
+ // The minimum time we allow between requests for the MemoryInfo of a process to
+ // throttle requests from apps.
+ public long MEMORY_INFO_THROTTLE_TIME = DEFAULT_MEMORY_INFO_THROTTLE_TIME;
+
// Indicates whether the activity starts logging is enabled.
// Controlled by Settings.Global.ACTIVITY_STARTS_LOGGING_ENABLED
boolean mFlagActivityStartsLoggingEnabled;
@@ -348,6 +353,8 @@ final class ActivityManagerConstants extends ContentObserver {
DEFAULT_BOUND_SERVICE_CRASH_MAX_RETRY);
FLAG_PROCESS_START_ASYNC = mParser.getBoolean(KEY_PROCESS_START_ASYNC,
DEFAULT_PROCESS_START_ASYNC);
+ MEMORY_INFO_THROTTLE_TIME = mParser.getLong(KEY_MEMORY_INFO_THROTTLE_TIME,
+ DEFAULT_MEMORY_INFO_THROTTLE_TIME);
updateMaxCachedProcesses();
}
@@ -423,6 +430,14 @@ final class ActivityManagerConstants extends ContentObserver {
pw.println(MAX_SERVICE_INACTIVITY);
pw.print(" "); pw.print(KEY_BG_START_TIMEOUT); pw.print("=");
pw.println(BG_START_TIMEOUT);
+ pw.print(" "); pw.print(KEY_BOUND_SERVICE_CRASH_RESTART_DURATION); pw.print("=");
+ pw.println(BOUND_SERVICE_CRASH_RESTART_DURATION);
+ pw.print(" "); pw.print(KEY_BOUND_SERVICE_CRASH_MAX_RETRY); pw.print("=");
+ pw.println(BOUND_SERVICE_MAX_CRASH_RETRY);
+ pw.print(" "); pw.print(KEY_PROCESS_START_ASYNC); pw.print("=");
+ pw.println(FLAG_PROCESS_START_ASYNC);
+ pw.print(" "); pw.print(KEY_MEMORY_INFO_THROTTLE_TIME); pw.print("=");
+ pw.println(MEMORY_INFO_THROTTLE_TIME);
pw.println();
if (mOverrideMaxCachedProcesses >= 0) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index a8269824da0e..82805eda1562 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -5196,27 +5196,56 @@ public class ActivityManagerService extends IActivityManager.Stub
@Override
public Debug.MemoryInfo[] getProcessMemoryInfo(int[] pids) {
enforceNotIsolatedCaller("getProcessMemoryInfo");
+
+ final long now = SystemClock.uptimeMillis();
+ final long lastNow = now - mConstants.MEMORY_INFO_THROTTLE_TIME;
+
+ final int callingPid = Binder.getCallingPid();
+ final int callingUid = Binder.getCallingUid();
+ final int userId = UserHandle.getUserId(callingUid);
+ final boolean allUsers = ActivityManager.checkUidPermission(INTERACT_ACROSS_USERS_FULL,
+ callingUid) == PackageManager.PERMISSION_GRANTED;
+ // Check REAL_GET_TASKS to see if they are allowed to access other uids
+ final boolean allUids = mAtmInternal.isGetTasksAllowed(
+ "getProcessMemoryInfo", callingPid, callingUid);
+
Debug.MemoryInfo[] infos = new Debug.MemoryInfo[pids.length];
for (int i=pids.length-1; i>=0; i--) {
- ProcessRecord proc;
- int oomAdj;
+ infos[i] = new Debug.MemoryInfo();
+ final ProcessRecord proc;
+ final int oomAdj;
synchronized (this) {
synchronized (mPidsSelfLocked) {
proc = mPidsSelfLocked.get(pids[i]);
oomAdj = proc != null ? proc.setAdj : 0;
}
}
- infos[i] = new Debug.MemoryInfo();
- long startTime = SystemClock.currentThreadTimeMillis();
- Debug.getMemoryInfo(pids[i], infos[i]);
- long endTime = SystemClock.currentThreadTimeMillis();
+ if (!allUids || (!allUsers && (proc == null
+ || UserHandle.getUserId(proc.uid) != userId))) {
+ // The caller is not allow to get information about this other process...
+ // just leave it empty.
+ continue;
+ }
+ if (proc != null && proc.lastMemInfoTime >= lastNow && proc.lastMemInfo != null) {
+ // It hasn't been long enough that we want to take another sample; return
+ // the last one.
+ infos[i].set(proc.lastMemInfo);
+ continue;
+ }
+ final long startTime = SystemClock.currentThreadTimeMillis();
+ final Debug.MemoryInfo memInfo = new Debug.MemoryInfo();
+ Debug.getMemoryInfo(pids[i], memInfo);
+ final long endTime = SystemClock.currentThreadTimeMillis();
+ infos[i].set(memInfo);
if (proc != null) {
synchronized (this) {
+ proc.lastMemInfo = memInfo;
+ proc.lastMemInfoTime = SystemClock.uptimeMillis();
if (proc.thread != null && proc.setAdj == oomAdj) {
// Record this for posterity if the process has been stable.
proc.baseProcessTracker.addPss(infos[i].getTotalPss(),
infos[i].getTotalUss(), infos[i].getTotalRss(), false,
- ProcessStats.ADD_PSS_EXTERNAL_SLOW, endTime-startTime,
+ ProcessStats.ADD_PSS_EXTERNAL_SLOW, endTime - startTime,
proc.pkgList.mPkgList);
for (int ipkg = proc.pkgList.size() - 1; ipkg >= 0; ipkg--) {
ProcessStats.ProcessStateHolder holder = proc.pkgList.valueAt(ipkg);
@@ -5241,6 +5270,16 @@ public class ActivityManagerService extends IActivityManager.Stub
@Override
public long[] getProcessPss(int[] pids) {
enforceNotIsolatedCaller("getProcessPss");
+
+ final int callingPid = Binder.getCallingPid();
+ final int callingUid = Binder.getCallingUid();
+ final int userId = UserHandle.getUserId(callingUid);
+ final boolean allUsers = ActivityManager.checkUidPermission(INTERACT_ACROSS_USERS_FULL,
+ callingUid) == PackageManager.PERMISSION_GRANTED;
+ // Check REAL_GET_TASKS to see if they are allowed to access other uids
+ final boolean allUids = mAtmInternal.isGetTasksAllowed(
+ "getProcessPss", callingPid, callingUid);
+
long[] pss = new long[pids.length];
for (int i=pids.length-1; i>=0; i--) {
ProcessRecord proc;
@@ -5251,6 +5290,11 @@ public class ActivityManagerService extends IActivityManager.Stub
oomAdj = proc != null ? proc.setAdj : 0;
}
}
+ if (!allUids || (!allUsers && UserHandle.getUserId(proc.uid) != userId)) {
+ // The caller is not allow to get information about this other process...
+ // just leave it empty.
+ continue;
+ }
long[] tmpUss = new long[3];
long startTime = SystemClock.currentThreadTimeMillis();
pss[i] = Debug.getPss(pids[i], tmpUss, null);
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index 8c552b98698a..144f18b219dc 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -20,6 +20,7 @@ import static android.app.ActivityManager.PROCESS_STATE_NONEXISTENT;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import android.os.Debug;
import android.util.ArraySet;
import android.util.DebugUtils;
import android.util.EventLog;
@@ -207,6 +208,11 @@ final class ProcessRecord implements WindowProcessListener {
Object adjTarget; // Debugging: target component impacting oom_adj.
Runnable crashHandler; // Optional local handler to be invoked in the process crash.
+ // Cache of last retrieve memory info and uptime, to throttle how frequently
+ // apps can requyest it.
+ Debug.MemoryInfo lastMemInfo;
+ long lastMemInfoTime;
+
// Controller for driving the process state on the window manager side.
final private WindowProcessController mWindowProcessController;
// all ServiceRecord running in this process