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: I01997d6f888341e8eb2afe6a69545dd5be013744
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index c12d8e2..e00c11c 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -3266,6 +3266,13 @@
      * <p><b>Note: this method is only intended for debugging or building
      * a user-facing process management UI.</b></p>
      *
+     * <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.
+     * Also of {@link android.os.Build.VERSION_CODES#Q Android Q} the sample rate allowed
+     * by this API is significantly limited, if called faster the limit you will receive the
+     * same data as the previous call.</p>
+     *
      * @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 6ddcbe0..aba81af 100644
--- a/core/java/android/os/Debug.java
+++ b/core/java/android/os/Debug.java
@@ -321,6 +321,45 @@
         }
 
         /**
+         * @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 638b3ea..26367c2 100644
--- a/core/jni/android_os_Debug.cpp
+++ b/core/jni/android_os_Debug.cpp
@@ -602,7 +602,7 @@
 
     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 9bf72fb..2c8f2fc 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 @@
     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 @@
     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 @@
     // 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 @@
                 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 @@
         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 a826982..82805ed 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 @@
     @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 @@
     @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 @@
                     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 8c552b9..144f18b 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 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 @@
     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