Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 1f3b0302 authored by Dianne Hackborn's avatar Dianne Hackborn
Browse files

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
parent 1a4b8154
Loading
Loading
Loading
Loading
+9 −0
Original line number Diff line number Diff line
@@ -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
+39 −0
Original line number Diff line number Diff line
@@ -320,6 +320,45 @@ public final class Debug
        public MemoryInfo() {
        }

        /**
         * @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.
         */
+1 −1
Original line number Diff line number Diff line
@@ -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;
    }

    {
+16 −1
Original line number Diff line number Diff line
@@ -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) {
+51 −7
Original line number Diff line number Diff line
@@ -5196,22 +5196,51 @@ 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(),
@@ -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);
Loading