diff options
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 |