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

Commit 77ca74d9 authored by Dianne Hackborn's avatar Dianne Hackborn Committed by Android (Google) Code Review
Browse files

Merge "Work on issue #26390161: Throttle syncs/jobs when system is low on RAM" into nyc-dev

parents e05c9230 970510b9
Loading
Loading
Loading
Loading
+20 −0
Original line number Original line Diff line number Diff line
@@ -1540,6 +1540,14 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM
            return true;
            return true;
        }
        }


        case GET_MEMORY_TRIM_LEVEL_TRANSACTION: {
            data.enforceInterface(IActivityManager.descriptor);
            int level = getMemoryTrimLevel();
            reply.writeNoException();
            reply.writeInt(level);
            return true;
        }

        case ENTER_SAFE_MODE_TRANSACTION: {
        case ENTER_SAFE_MODE_TRANSACTION: {
            data.enforceInterface(IActivityManager.descriptor);
            data.enforceInterface(IActivityManager.descriptor);
            enterSafeMode();
            enterSafeMode();
@@ -4874,6 +4882,18 @@ class ActivityManagerProxy implements IActivityManager
        data.recycle();
        data.recycle();
        reply.recycle();
        reply.recycle();
    }
    }
    public int getMemoryTrimLevel() throws RemoteException
    {
        Parcel data = Parcel.obtain();
        Parcel reply = Parcel.obtain();
        data.writeInterfaceToken(IActivityManager.descriptor);
        mRemote.transact(GET_MEMORY_TRIM_LEVEL_TRANSACTION, data, reply, 0);
        reply.readException();
        int level = reply.readInt();
        data.recycle();
        reply.recycle();
        return level;
    }
    public void enterSafeMode() throws RemoteException {
    public void enterSafeMode() throws RemoteException {
        Parcel data = Parcel.obtain();
        Parcel data = Parcel.obtain();
        data.writeInterfaceToken(IActivityManager.descriptor);
        data.writeInterfaceToken(IActivityManager.descriptor);
+2 −0
Original line number Original line Diff line number Diff line
@@ -308,6 +308,7 @@ public interface IActivityManager extends IInterface {
    public void setActivityController(IActivityController watcher)
    public void setActivityController(IActivityController watcher)
        throws RemoteException;
        throws RemoteException;
    public void setLenientBackgroundCheck(boolean enabled) throws RemoteException;
    public void setLenientBackgroundCheck(boolean enabled) throws RemoteException;
    public int getMemoryTrimLevel() throws RemoteException;


    public void enterSafeMode() throws RemoteException;
    public void enterSafeMode() throws RemoteException;


@@ -980,4 +981,5 @@ public interface IActivityManager extends IInterface {
    int NOTIFY_PINNED_STACK_ANIMATION_ENDED_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 366;
    int NOTIFY_PINNED_STACK_ANIMATION_ENDED_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 366;
    int REMOVE_STACK = IBinder.FIRST_CALL_TRANSACTION + 367;
    int REMOVE_STACK = IBinder.FIRST_CALL_TRANSACTION + 367;
    int SET_LENIENT_BACKGROUND_CHECK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+368;
    int SET_LENIENT_BACKGROUND_CHECK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+368;
    int GET_MEMORY_TRIM_LEVEL_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+369;
}
}
+8 −1
Original line number Original line Diff line number Diff line
@@ -102,12 +102,19 @@ public class JobInfo implements Parcelable {
    public static final int PRIORITY_SYNC_INITIALIZATION = 20;
    public static final int PRIORITY_SYNC_INITIALIZATION = 20;


    /**
    /**
     * Value of {@link #getPriority} for the current foreground app (overrides the supplied
     * Value of {@link #getPriority} for a foreground app (overrides the supplied
     * JobInfo priority if it is smaller).
     * JobInfo priority if it is smaller).
     * @hide
     * @hide
     */
     */
    public static final int PRIORITY_FOREGROUND_APP = 30;
    public static final int PRIORITY_FOREGROUND_APP = 30;


    /**
     * Value of {@link #getPriority} for the current top app (overrides the supplied
     * JobInfo priority if it is smaller).
     * @hide
     */
    public static final int PRIORITY_TOP_APP = 40;

    private final int jobId;
    private final int jobId;
    private final PersistableBundle extras;
    private final PersistableBundle extras;
    private final ComponentName service;
    private final ComponentName service;
+8 −0
Original line number Original line Diff line number Diff line
@@ -13394,6 +13394,14 @@ public final class ActivityManagerService extends ActivityManagerNative
        }
        }
    }
    }
    @Override
    public int getMemoryTrimLevel() {
        enforceNotIsolatedCaller("getMyMemoryState");
        synchronized (this) {
            return mLastMemoryLevel;
        }
    }
    @Override
    @Override
    public void onShellCommand(FileDescriptor in, FileDescriptor out,
    public void onShellCommand(FileDescriptor in, FileDescriptor out,
            FileDescriptor err, String[] args, ResultReceiver resultReceiver) {
            FileDescriptor err, String[] args, ResultReceiver resultReceiver) {
+110 −58
Original line number Original line Diff line number Diff line
@@ -53,9 +53,11 @@ import android.os.UserHandle;
import android.util.Slog;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseArray;


import android.util.SparseBooleanArray;
import android.util.SparseIntArray;
import android.util.TimeUtils;
import com.android.internal.app.IBatteryStats;
import com.android.internal.app.IBatteryStats;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.ArrayUtils;
import com.android.internal.app.ProcessStats;
import com.android.server.DeviceIdleController;
import com.android.server.DeviceIdleController;
import com.android.server.LocalServices;
import com.android.server.LocalServices;
import com.android.server.job.JobStore.JobStatusFunctor;
import com.android.server.job.JobStore.JobStatusFunctor;
@@ -87,9 +89,8 @@ public final class JobSchedulerService extends com.android.server.SystemService
    static final String TAG = "JobSchedulerService";
    static final String TAG = "JobSchedulerService";
    public static final boolean DEBUG = false;
    public static final boolean DEBUG = false;


    /** The number of concurrent jobs we run at one time. */
    /** The maximum number of concurrent jobs we run at one time. */
    private static final int MAX_JOB_CONTEXTS_COUNT
    private static final int MAX_JOB_CONTEXTS_COUNT = 8;
            = ActivityManager.isLowRamDeviceStatic() ? 3 : 6;
    /** Enforce a per-app limit on scheduled jobs? */
    /** Enforce a per-app limit on scheduled jobs? */
    private static final boolean ENFORCE_MAX_JOBS = false;
    private static final boolean ENFORCE_MAX_JOBS = false;
    /** The maximum number of jobs that we allow an unprivileged app to schedule */
    /** The maximum number of jobs that we allow an unprivileged app to schedule */
@@ -170,10 +171,34 @@ public final class JobSchedulerService extends com.android.server.SystemService
     */
     */
    boolean mReportedActive;
    boolean mReportedActive;


    /**
     * Current limit on the number of concurrent JobServiceContext entries we want to
     * keep actively running a job.
     */
    int mMaxActiveJobs = MAX_JOB_CONTEXTS_COUNT - 2;

    /**
    /**
     * Which uids are currently in the foreground.
     * Which uids are currently in the foreground.
     */
     */
    final SparseBooleanArray mForegroundUids = new SparseBooleanArray();
    final SparseIntArray mUidPriorityOverride = new SparseIntArray();

    // -- Pre-allocated temporaries only for use in assignJobsToContextsLocked --

    /**
     * This array essentially stores the state of mActiveServices array.
     * The ith index stores the job present on the ith JobServiceContext.
     * We manipulate this array until we arrive at what jobs should be running on
     * what JobServiceContext.
     */
    JobStatus[] mTmpAssignContextIdToJobMap = new JobStatus[MAX_JOB_CONTEXTS_COUNT];
    /**
     * Indicates whether we need to act on this jobContext id
     */
    boolean[] mTmpAssignAct = new boolean[MAX_JOB_CONTEXTS_COUNT];
    /**
     * The uid whose jobs we would like to assign to a context.
     */
    int[] mTmpAssignPreferredUidForContext = new int[MAX_JOB_CONTEXTS_COUNT];


    /**
    /**
     * Cleans up outstanding jobs when a package is removed. Even if it's being replaced later we
     * Cleans up outstanding jobs when a package is removed. Even if it's being replaced later we
@@ -376,19 +401,15 @@ public final class JobSchedulerService extends com.android.server.SystemService


    void updateUidState(int uid, int procState) {
    void updateUidState(int uid, int procState) {
        synchronized (mLock) {
        synchronized (mLock) {
            boolean foreground = procState <= ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE;
            if (procState == ActivityManager.PROCESS_STATE_TOP) {
            boolean changed = false;
                // Only use this if we are exactly the top app.  All others can live
            if (foreground) {
                // with just the foreground priority.  This means that persistent processes
                if (!mForegroundUids.get(uid)) {
                // can never be the top app priority...  that is fine.
                    changed = true;
                mUidPriorityOverride.put(uid, JobInfo.PRIORITY_TOP_APP);
                    mForegroundUids.put(uid, true);
            } else if (procState <= ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE) {
                }
                mUidPriorityOverride.put(uid, JobInfo.PRIORITY_FOREGROUND_APP);
            } else {
            } else {
                int index = mForegroundUids.indexOfKey(uid);
                mUidPriorityOverride.delete(uid);
                if (index >= 0) {
                    mForegroundUids.removeAt(index);
                    changed = true;
                }
            }
            }
        }
        }
    }
    }
@@ -1007,8 +1028,9 @@ public final class JobSchedulerService extends com.android.server.SystemService
        if (priority >= JobInfo.PRIORITY_FOREGROUND_APP) {
        if (priority >= JobInfo.PRIORITY_FOREGROUND_APP) {
            return priority;
            return priority;
        }
        }
        if (mForegroundUids.get(job.getSourceUid())) {
        int override = mUidPriorityOverride.get(job.getSourceUid(), 0);
            return JobInfo.PRIORITY_FOREGROUND_APP;
        if (override != 0) {
            return override;
        }
        }
        return priority;
        return priority;
    }
    }
@@ -1024,24 +1046,44 @@ public final class JobSchedulerService extends com.android.server.SystemService
            Slog.d(TAG, printPendingQueue());
            Slog.d(TAG, printPendingQueue());
        }
        }


        // This array essentially stores the state of mActiveServices array.
        int memLevel;
        // ith index stores the job present on the ith JobServiceContext.
        try {
        // We manipulate this array until we arrive at what jobs should be running on
            memLevel = ActivityManagerNative.getDefault().getMemoryTrimLevel();
        // what JobServiceContext.
        } catch (RemoteException e) {
        JobStatus[] contextIdToJobMap = new JobStatus[MAX_JOB_CONTEXTS_COUNT];
            memLevel = ProcessStats.ADJ_MEM_FACTOR_NORMAL;
        // Indicates whether we need to act on this jobContext id
        }
        boolean[] act = new boolean[MAX_JOB_CONTEXTS_COUNT];
        switch (memLevel) {
        int[] preferredUidForContext = new int[MAX_JOB_CONTEXTS_COUNT];
            case ProcessStats.ADJ_MEM_FACTOR_MODERATE:
        for (int i=0; i<mActiveServices.size(); i++) {
                mMaxActiveJobs = ((MAX_JOB_CONTEXTS_COUNT - 2) * 2) / 3;
            contextIdToJobMap[i] = mActiveServices.get(i).getRunningJob();
                break;
            preferredUidForContext[i] = mActiveServices.get(i).getPreferredUid();
            case ProcessStats.ADJ_MEM_FACTOR_LOW:
                mMaxActiveJobs = (MAX_JOB_CONTEXTS_COUNT - 2) / 3;
                break;
            case ProcessStats.ADJ_MEM_FACTOR_CRITICAL:
                mMaxActiveJobs = 1;
                break;
            default:
                mMaxActiveJobs = MAX_JOB_CONTEXTS_COUNT - 2;
                break;
        }

        JobStatus[] contextIdToJobMap = mTmpAssignContextIdToJobMap;
        boolean[] act = mTmpAssignAct;
        int[] preferredUidForContext = mTmpAssignPreferredUidForContext;
        int numActive = 0;
        for (int i=0; i<MAX_JOB_CONTEXTS_COUNT; i++) {
            final JobServiceContext js = mActiveServices.get(i);
            if ((contextIdToJobMap[i] = js.getRunningJob()) != null) {
                numActive++;
            }
            act[i] = false;
            preferredUidForContext[i] = js.getPreferredUid();
        }
        }
        if (DEBUG) {
        if (DEBUG) {
            Slog.d(TAG, printContextIdToJobMap(contextIdToJobMap, "running jobs initial"));
            Slog.d(TAG, printContextIdToJobMap(contextIdToJobMap, "running jobs initial"));
        }
        }
        Iterator<JobStatus> it = mPendingJobs.iterator();
        for (int i=0; i<mPendingJobs.size(); i++) {
        while (it.hasNext()) {
            JobStatus nextPending = mPendingJobs.get(i);
            JobStatus nextPending = it.next();


            // If job is already running, go to next job.
            // If job is already running, go to next job.
            int jobRunningContext = findJobContextIdFromMap(nextPending, contextIdToJobMap);
            int jobRunningContext = findJobContextIdFromMap(nextPending, contextIdToJobMap);
@@ -1049,24 +1091,30 @@ public final class JobSchedulerService extends com.android.server.SystemService
                continue;
                continue;
            }
            }


            nextPending.lastEvaluatedPriority = evaluateJobPriorityLocked(nextPending);
            final int priority = evaluateJobPriorityLocked(nextPending);
            nextPending.lastEvaluatedPriority = priority;


            // Find a context for nextPending. The context should be available OR
            // Find a context for nextPending. The context should be available OR
            // it should have lowest priority among all running jobs
            // it should have lowest priority among all running jobs
            // (sharing the same Uid as nextPending)
            // (sharing the same Uid as nextPending)
            int minPriority = Integer.MAX_VALUE;
            int minPriority = Integer.MAX_VALUE;
            int minPriorityContextId = -1;
            int minPriorityContextId = -1;
            for (int i=0; i<mActiveServices.size(); i++) {
            for (int j=0; j<MAX_JOB_CONTEXTS_COUNT; j++) {
                JobStatus job = contextIdToJobMap[i];
                JobStatus job = contextIdToJobMap[j];
                int preferredUid = preferredUidForContext[i];
                int preferredUid = preferredUidForContext[j];
                if (job == null && (preferredUid == nextPending.getUid() ||
                if (job == null) {
                    if ((numActive < mMaxActiveJobs || priority >= JobInfo.PRIORITY_TOP_APP) &&
                            (preferredUid == nextPending.getUid() ||
                                    preferredUid == JobServiceContext.NO_PREFERRED_UID)) {
                                    preferredUid == JobServiceContext.NO_PREFERRED_UID)) {
                    minPriorityContextId = i;
                        // This slot is free, and we haven't yet hit the limit on
                        // concurrent jobs...  we can just throw the job in to here.
                        minPriorityContextId = j;
                        numActive++;
                        break;
                        break;
                    }
                    }
                if (job == null) {
                    // No job on this context, but nextPending can't run here because
                    // No job on this context, but nextPending can't run here because
                    // the context has a preferred Uid.
                    // the context has a preferred Uid or we have reached the limit on
                    // concurrent jobs.
                    continue;
                    continue;
                }
                }
                if (job.getUid() != nextPending.getUid()) {
                if (job.getUid() != nextPending.getUid()) {
@@ -1077,7 +1125,7 @@ public final class JobSchedulerService extends com.android.server.SystemService
                }
                }
                if (minPriority > nextPending.lastEvaluatedPriority) {
                if (minPriority > nextPending.lastEvaluatedPriority) {
                    minPriority = nextPending.lastEvaluatedPriority;
                    minPriority = nextPending.lastEvaluatedPriority;
                    minPriorityContextId = i;
                    minPriorityContextId = j;
                }
                }
            }
            }
            if (minPriorityContextId != -1) {
            if (minPriorityContextId != -1) {
@@ -1088,7 +1136,7 @@ public final class JobSchedulerService extends com.android.server.SystemService
        if (DEBUG) {
        if (DEBUG) {
            Slog.d(TAG, printContextIdToJobMap(contextIdToJobMap, "running jobs final"));
            Slog.d(TAG, printContextIdToJobMap(contextIdToJobMap, "running jobs final"));
        }
        }
        for (int i=0; i<mActiveServices.size(); i++) {
        for (int i=0; i<MAX_JOB_CONTEXTS_COUNT; i++) {
            boolean preservePreferredUid = false;
            boolean preservePreferredUid = false;
            if (act[i]) {
            if (act[i]) {
                JobStatus js = mActiveServices.get(i).getRunningJob();
                JobStatus js = mActiveServices.get(i).getRunningJob();
@@ -1329,7 +1377,7 @@ public final class JobSchedulerService extends com.android.server.SystemService
                    public void process(JobStatus job) {
                    public void process(JobStatus job) {
                        pw.print("  Job #"); pw.print(index++); pw.print(": ");
                        pw.print("  Job #"); pw.print(index++); pw.print(": ");
                        pw.println(job.toShortString());
                        pw.println(job.toShortString());
                        job.dump(pw, "    ");
                        job.dump(pw, "    ", true);
                        pw.print("    Ready: ");
                        pw.print("    Ready: ");
                        pw.print(mHandler.isReadyToBeExecutedLocked(job));
                        pw.print(mHandler.isReadyToBeExecutedLocked(job));
                        pw.print(" (job=");
                        pw.print(" (job=");
@@ -1351,9 +1399,10 @@ public final class JobSchedulerService extends com.android.server.SystemService
                mControllers.get(i).dumpControllerStateLocked(pw);
                mControllers.get(i).dumpControllerStateLocked(pw);
            }
            }
            pw.println();
            pw.println();
            pw.println("Foreground uids:");
            pw.println("Uid priority overrides:");
            for (int i=0; i<mForegroundUids.size(); i++) {
            for (int i=0; i< mUidPriorityOverride.size(); i++) {
                pw.print("  "); pw.println(UserHandle.formatUid(mForegroundUids.keyAt(i)));
                pw.print("  "); pw.print(UserHandle.formatUid(mUidPriorityOverride.keyAt(i)));
                pw.print(": "); pw.println(mUidPriorityOverride.valueAt(i));
            }
            }
            pw.println();
            pw.println();
            pw.println("Pending queue:");
            pw.println("Pending queue:");
@@ -1361,6 +1410,7 @@ public final class JobSchedulerService extends com.android.server.SystemService
                JobStatus job = mPendingJobs.get(i);
                JobStatus job = mPendingJobs.get(i);
                pw.print("  Pending #"); pw.print(i); pw.print(": ");
                pw.print("  Pending #"); pw.print(i); pw.print(": ");
                pw.println(job.toShortString());
                pw.println(job.toShortString());
                job.dump(pw, "    ", false);
                int priority = evaluateJobPriorityLocked(job);
                int priority = evaluateJobPriorityLocked(job);
                if (priority != JobInfo.PRIORITY_DEFAULT) {
                if (priority != JobInfo.PRIORITY_DEFAULT) {
                    pw.print("    Evaluated priority: "); pw.println(priority);
                    pw.print("    Evaluated priority: "); pw.println(priority);
@@ -1371,17 +1421,18 @@ public final class JobSchedulerService extends com.android.server.SystemService
            pw.println("Active jobs:");
            pw.println("Active jobs:");
            for (int i=0; i<mActiveServices.size(); i++) {
            for (int i=0; i<mActiveServices.size(); i++) {
                JobServiceContext jsc = mActiveServices.get(i);
                JobServiceContext jsc = mActiveServices.get(i);
                pw.print("  Slot #"); pw.print(i); pw.print(": ");
                if (jsc.getRunningJob() == null) {
                if (jsc.getRunningJob() == null) {
                    pw.println("inactive");
                    continue;
                    continue;
                } else {
                } else {
                    final long timeout = jsc.getTimeoutElapsed();
                    pw.println(jsc.getRunningJob().toShortString());
                    pw.print("    Running for: ");
                    pw.print("    Running for: ");
                    pw.print((now - jsc.getExecutionStartTimeElapsed())/1000);
                    TimeUtils.formatDuration(now - jsc.getExecutionStartTimeElapsed(), pw);
                    pw.print("s timeout=");
                    pw.print(", timeout at: ");
                    pw.print(timeout);
                    TimeUtils.formatDuration(jsc.getTimeoutElapsed() - now, pw);
                    pw.print(" fromnow=");
                    pw.println();
                    pw.println(timeout-now);
                    jsc.getRunningJob().dump(pw, "    ", false);
                    jsc.getRunningJob().dump(pw, "  ");
                    int priority = evaluateJobPriorityLocked(jsc.getRunningJob());
                    int priority = evaluateJobPriorityLocked(jsc.getRunningJob());
                    if (priority != JobInfo.PRIORITY_DEFAULT) {
                    if (priority != JobInfo.PRIORITY_DEFAULT) {
                        pw.print("    Evaluated priority: "); pw.println(priority);
                        pw.print("    Evaluated priority: "); pw.println(priority);
@@ -1392,6 +1443,7 @@ public final class JobSchedulerService extends com.android.server.SystemService
            pw.print("mReadyToRock="); pw.println(mReadyToRock);
            pw.print("mReadyToRock="); pw.println(mReadyToRock);
            pw.print("mDeviceIdleMode="); pw.println(mDeviceIdleMode);
            pw.print("mDeviceIdleMode="); pw.println(mDeviceIdleMode);
            pw.print("mReportedActive="); pw.println(mReportedActive);
            pw.print("mReportedActive="); pw.println(mReportedActive);
            pw.print("mMaxActiveJobs="); pw.println(mMaxActiveJobs);
        }
        }
        pw.println();
        pw.println();
    }
    }
Loading