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

Commit 6a27b13f authored by Kweku Adams's avatar Kweku Adams Committed by Android (Google) Code Review
Browse files

Merge "Avoid recalculating the same information."

parents ce8d11f3 b68852f8
Loading
Loading
Loading
Loading
+37 −23
Original line number Diff line number Diff line
@@ -327,6 +327,7 @@ class JobConcurrencyManager {
    private final ArraySet<ContextAssignment> mRecycledIdle = new ArraySet<>();
    private final ArrayList<ContextAssignment> mRecycledPreferredUidOnly = new ArrayList<>();
    private final ArrayList<ContextAssignment> mRecycledStoppable = new ArrayList<>();
    private final AssignmentInfo mRecycledAssignmentInfo = new AssignmentInfo();

    private final Pools.Pool<ContextAssignment> mContextAssignmentPool =
            new Pools.SimplePool<>(MAX_RETAINED_OBJECTS);
@@ -414,7 +415,7 @@ class JobConcurrencyManager {
    @VisibleForTesting
    JobConcurrencyManager(JobSchedulerService service, Injector injector) {
        mService = service;
        mLock = mService.mLock;
        mLock = mService.getLock();
        mContext = service.getTestableContext();
        mInjector = injector;

@@ -693,8 +694,9 @@ class JobConcurrencyManager {
            return;
        }

        final long minPreferredUidOnlyWaitingTimeMs = prepareForAssignmentDeterminationLocked(
                mRecycledIdle, mRecycledPreferredUidOnly, mRecycledStoppable);
        prepareForAssignmentDeterminationLocked(
                mRecycledIdle, mRecycledPreferredUidOnly, mRecycledStoppable,
                mRecycledAssignmentInfo);

        if (DEBUG) {
            Slog.d(TAG, printAssignments("running jobs initial",
@@ -703,7 +705,7 @@ class JobConcurrencyManager {

        determineAssignmentsLocked(
                mRecycledChanged, mRecycledIdle, mRecycledPreferredUidOnly, mRecycledStoppable,
                minPreferredUidOnlyWaitingTimeMs);
                mRecycledAssignmentInfo);

        if (DEBUG) {
            Slog.d(TAG, printAssignments("running jobs final",
@@ -715,17 +717,18 @@ class JobConcurrencyManager {
        carryOutAssignmentChangesLocked(mRecycledChanged);

        cleanUpAfterAssignmentChangesLocked(
                mRecycledChanged, mRecycledIdle, mRecycledPreferredUidOnly, mRecycledStoppable);
                mRecycledChanged, mRecycledIdle, mRecycledPreferredUidOnly, mRecycledStoppable,
                mRecycledAssignmentInfo);

        noteConcurrency();
    }

    /** @return the minimum remaining execution time for preferred UID only JobServiceContexts. */
    @VisibleForTesting
    @GuardedBy("mLock")
    long prepareForAssignmentDeterminationLocked(final ArraySet<ContextAssignment> idle,
    void prepareForAssignmentDeterminationLocked(final ArraySet<ContextAssignment> idle,
            final List<ContextAssignment> preferredUidOnly,
            final List<ContextAssignment> stoppable) {
            final List<ContextAssignment> stoppable,
            final AssignmentInfo info) {
        final PendingJobQueue pendingJobQueue = mService.getPendingJobQueue();
        final List<JobServiceContext> activeServices = mActiveServices;

@@ -755,6 +758,9 @@ class JobConcurrencyManager {
            if (js != null) {
                mWorkCountTracker.incrementRunningJobCount(jsc.getRunningJobWorkType());
                assignment.workType = jsc.getRunningJobWorkType();
                if (js.startedAsExpeditedJob && js.lastEvaluatedBias == JobInfo.BIAS_TOP_APP) {
                    info.numRunningTopEj++;
                }
            }

            assignment.preferredUid = jsc.getPreferredUid();
@@ -789,9 +795,10 @@ class JobConcurrencyManager {
        }

        mWorkCountTracker.onCountDone();
        // Return 0 if there were no preferred UID only contexts to indicate no waiting time due
        // Set 0 if there were no preferred UID only contexts to indicate no waiting time due
        // to such jobs.
        return minPreferredUidOnlyWaitingTimeMs == Long.MAX_VALUE
        info.minPreferredUidOnlyWaitingTimeMs =
                minPreferredUidOnlyWaitingTimeMs == Long.MAX_VALUE
                        ? 0 : minPreferredUidOnlyWaitingTimeMs;
    }

@@ -801,7 +808,7 @@ class JobConcurrencyManager {
            final ArraySet<ContextAssignment> idle,
            final List<ContextAssignment> preferredUidOnly,
            final List<ContextAssignment> stoppable,
            long minPreferredUidOnlyWaitingTimeMs) {
            @NonNull AssignmentInfo info) {
        final PendingJobQueue pendingJobQueue = mService.getPendingJobQueue();
        final List<JobServiceContext> activeServices = mActiveServices;
        pendingJobQueue.resetIterator();
@@ -832,7 +839,7 @@ class JobConcurrencyManager {
            // pending jobs that could be designated as waiting too long, and those other jobs
            // would only have to wait for the new slots to become available.
            final long minWaitingTimeMs =
                    Math.min(minPreferredUidOnlyWaitingTimeMs, minChangedWaitingTimeMs);
                    Math.min(info.minPreferredUidOnlyWaitingTimeMs, minChangedWaitingTimeMs);

            // Find an available slot for nextPending. The context should be one of the following:
            // 1. Unused
@@ -861,13 +868,6 @@ class JobConcurrencyManager {
                }
            }
            if (selectedContext == null && stoppable.size() > 0) {
                int topEjCount = 0;
                for (int r = mRunningJobs.size() - 1; r >= 0; --r) {
                    JobStatus js = mRunningJobs.valueAt(r);
                    if (js.startedAsExpeditedJob && js.lastEvaluatedBias == JobInfo.BIAS_TOP_APP) {
                        topEjCount++;
                    }
                }
                for (int s = stoppable.size() - 1; s >= 0; --s) {
                    final ContextAssignment assignment = stoppable.get(s);
                    final JobStatus runningJob = assignment.context.getRunningJobLocked();
@@ -888,7 +888,8 @@ class JobConcurrencyManager {
                        final int currentJobBias = mService.evaluateJobBiasLocked(runningJob);
                        canReplace = runningJob.lastEvaluatedBias < JobInfo.BIAS_TOP_APP // Case 2
                                || currentJobBias < JobInfo.BIAS_TOP_APP // Case 3
                                || topEjCount > .5 * mWorkTypeConfig.getMaxTotal(); // Case 4
                                // Case 4
                                || info.numRunningTopEj > .5 * mWorkTypeConfig.getMaxTotal();
                    }
                    if (!canReplace && mMaxWaitTimeBypassEnabled) { // Case 5
                        if (nextPending.shouldTreatAsExpeditedJob()) {
@@ -955,7 +956,7 @@ class JobConcurrencyManager {
                if (selectedContext != null) {
                    selectedContext.newJob = nextPending;
                    preferredUidOnly.remove(selectedContext);
                    minPreferredUidOnlyWaitingTimeMs = newMinPreferredUidOnlyWaitingTimeMs;
                    info.minPreferredUidOnlyWaitingTimeMs = newMinPreferredUidOnlyWaitingTimeMs;
                }
            }
            // Make sure to run EJs for the TOP app immediately.
@@ -1072,7 +1073,8 @@ class JobConcurrencyManager {
    private void cleanUpAfterAssignmentChangesLocked(final ArraySet<ContextAssignment> changed,
            final ArraySet<ContextAssignment> idle,
            final List<ContextAssignment> preferredUidOnly,
            final List<ContextAssignment> stoppable) {
            final List<ContextAssignment> stoppable,
            final AssignmentInfo assignmentInfo) {
        for (int s = stoppable.size() - 1; s >= 0; --s) {
            final ContextAssignment assignment = stoppable.get(s);
            assignment.clear();
@@ -1093,6 +1095,7 @@ class JobConcurrencyManager {
        idle.clear();
        stoppable.clear();
        preferredUidOnly.clear();
        assignmentInfo.clear();
        mWorkCountTracker.resetStagingCount();
        mActivePkgStats.forEach(mPackageStatsStagingCountClearer);
    }
@@ -2540,6 +2543,17 @@ class JobConcurrencyManager {
        }
    }

    @VisibleForTesting
    static final class AssignmentInfo {
        public long minPreferredUidOnlyWaitingTimeMs;
        public int numRunningTopEj;

        void clear() {
            minPreferredUidOnlyWaitingTimeMs = 0;
            numRunningTopEj = 0;
        }
    }

    // TESTING HELPERS

    @VisibleForTesting
+89 −24
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
package com.android.server.job;

import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
@@ -34,16 +35,20 @@ import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;

import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.anyLong;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
import android.app.AppGlobals;
import android.app.IActivityManager;
import android.app.job.JobInfo;
import android.content.ComponentName;
import android.content.Context;
@@ -51,6 +56,8 @@ import android.content.pm.IPackageManager;
import android.content.pm.UserInfo;
import android.content.res.Resources;
import android.os.Looper;
import android.os.PowerManager;
import android.os.RemoteException;
import android.os.UserHandle;
import android.provider.DeviceConfig;
import android.util.ArrayMap;
@@ -149,12 +156,14 @@ public final class JobConcurrencyManagerTest {
                R.bool.config_jobSchedulerRestrictBackgroundUser);
        when(mContext.getResources()).thenReturn(mResources);
        doReturn(mContext).when(jobSchedulerService).getTestableContext();
        doReturn(jobSchedulerService).when(jobSchedulerService).getLock();
        mConfigBuilder = new DeviceConfig.Properties.Builder(DeviceConfig.NAMESPACE_JOB_SCHEDULER);
        doAnswer((Answer<DeviceConfig.Properties>) invocationOnMock -> mConfigBuilder.build())
                .when(() -> DeviceConfig.getProperties(eq(DeviceConfig.NAMESPACE_JOB_SCHEDULER)));
        mPendingJobQueue = new PendingJobQueue();
        doReturn(mPendingJobQueue).when(jobSchedulerService).getPendingJobQueue();
        doReturn(mIPackageManager).when(AppGlobals::getPackageManager);
        doReturn(mock(PowerManager.class)).when(mContext).getSystemService(PowerManager.class);
        mInjector = new InjectorForTest();
        doAnswer((Answer<Long>) invocationOnMock -> {
            Object[] args = invocationOnMock.getArguments();
@@ -171,6 +180,16 @@ public final class JobConcurrencyManagerTest {
        createCurrentUser(true);
        mNextUserId = 10;
        mJobConcurrencyManager.mGracePeriodObserver = mGracePeriodObserver;

        IActivityManager activityManager = ActivityManager.getService();
        spyOn(activityManager);
        try {
            doNothing().when(activityManager).registerUserSwitchObserver(any(), anyString());
        } catch (RemoteException e) {
            fail("registerUserSwitchObserver threw exception: " + e.getMessage());
        }

        mJobConcurrencyManager.onSystemReady();
    }

    @After
@@ -188,13 +207,16 @@ public final class JobConcurrencyManagerTest {
        final ArraySet<JobConcurrencyManager.ContextAssignment> idle = new ArraySet<>();
        final List<JobConcurrencyManager.ContextAssignment> preferredUidOnly = new ArrayList<>();
        final List<JobConcurrencyManager.ContextAssignment> stoppable = new ArrayList<>();
        final long minPreferredUidOnlyWaitingTimeMs = mJobConcurrencyManager
                .prepareForAssignmentDeterminationLocked(idle, preferredUidOnly, stoppable);
        final JobConcurrencyManager.AssignmentInfo assignmentInfo =
                new JobConcurrencyManager.AssignmentInfo();
        mJobConcurrencyManager.prepareForAssignmentDeterminationLocked(
                idle, preferredUidOnly, stoppable, assignmentInfo);

        assertEquals(JobConcurrencyManager.STANDARD_CONCURRENCY_LIMIT, idle.size());
        assertEquals(0, preferredUidOnly.size());
        assertEquals(0, stoppable.size());
        assertEquals(0, minPreferredUidOnlyWaitingTimeMs);
        assertEquals(0, assignmentInfo.minPreferredUidOnlyWaitingTimeMs);
        assertEquals(0, assignmentInfo.numRunningTopEj);
    }

    @Test
@@ -207,13 +229,16 @@ public final class JobConcurrencyManagerTest {
        final ArraySet<JobConcurrencyManager.ContextAssignment> idle = new ArraySet<>();
        final List<JobConcurrencyManager.ContextAssignment> preferredUidOnly = new ArrayList<>();
        final List<JobConcurrencyManager.ContextAssignment> stoppable = new ArrayList<>();
        final long minPreferredUidOnlyWaitingTimeMs = mJobConcurrencyManager
                .prepareForAssignmentDeterminationLocked(idle, preferredUidOnly, stoppable);
        final JobConcurrencyManager.AssignmentInfo assignmentInfo =
                new JobConcurrencyManager.AssignmentInfo();
        mJobConcurrencyManager.prepareForAssignmentDeterminationLocked(
                idle, preferredUidOnly, stoppable, assignmentInfo);

        assertEquals(JobConcurrencyManager.STANDARD_CONCURRENCY_LIMIT, idle.size());
        assertEquals(0, preferredUidOnly.size());
        assertEquals(0, stoppable.size());
        assertEquals(0, minPreferredUidOnlyWaitingTimeMs);
        assertEquals(0, assignmentInfo.minPreferredUidOnlyWaitingTimeMs);
        assertEquals(0, assignmentInfo.numRunningTopEj);
    }

    @Test
@@ -230,13 +255,45 @@ public final class JobConcurrencyManagerTest {
        final ArraySet<JobConcurrencyManager.ContextAssignment> idle = new ArraySet<>();
        final List<JobConcurrencyManager.ContextAssignment> preferredUidOnly = new ArrayList<>();
        final List<JobConcurrencyManager.ContextAssignment> stoppable = new ArrayList<>();
        final long minPreferredUidOnlyWaitingTimeMs = mJobConcurrencyManager
                .prepareForAssignmentDeterminationLocked(idle, preferredUidOnly, stoppable);
        final JobConcurrencyManager.AssignmentInfo assignmentInfo =
                new JobConcurrencyManager.AssignmentInfo();
        mJobConcurrencyManager.prepareForAssignmentDeterminationLocked(
                idle, preferredUidOnly, stoppable, assignmentInfo);

        assertEquals(0, idle.size());
        assertEquals(JobConcurrencyManager.STANDARD_CONCURRENCY_LIMIT, preferredUidOnly.size());
        assertEquals(0, stoppable.size());
        assertEquals(0, minPreferredUidOnlyWaitingTimeMs);
        assertEquals(0, assignmentInfo.minPreferredUidOnlyWaitingTimeMs);
        assertEquals(0, assignmentInfo.numRunningTopEj);
    }

    @Test
    public void testPrepareForAssignmentDetermination_onlyRunningTopEjs() {
        for (int i = 0; i < JobConcurrencyManager.STANDARD_CONCURRENCY_LIMIT; ++i) {
            JobStatus job = createJob(mDefaultUserId * UserHandle.PER_USER_RANGE + i);
            job.startedAsExpeditedJob = true;
            job.lastEvaluatedBias = JobInfo.BIAS_TOP_APP;
            mJobConcurrencyManager.addRunningJobForTesting(job);
        }

        for (int i = 0; i < mInjector.contexts.size(); ++i) {
            doReturn(i % 2 == 0).when(mInjector.contexts.keyAt(i)).isWithinExecutionGuaranteeTime();
        }

        final ArraySet<JobConcurrencyManager.ContextAssignment> idle = new ArraySet<>();
        final List<JobConcurrencyManager.ContextAssignment> preferredUidOnly = new ArrayList<>();
        final List<JobConcurrencyManager.ContextAssignment> stoppable = new ArrayList<>();
        final JobConcurrencyManager.AssignmentInfo assignmentInfo =
                new JobConcurrencyManager.AssignmentInfo();
        mJobConcurrencyManager.prepareForAssignmentDeterminationLocked(
                idle, preferredUidOnly, stoppable, assignmentInfo);

        assertEquals(0, idle.size());
        assertEquals(JobConcurrencyManager.STANDARD_CONCURRENCY_LIMIT / 2, preferredUidOnly.size());
        assertEquals(JobConcurrencyManager.STANDARD_CONCURRENCY_LIMIT / 2, stoppable.size());
        assertEquals(0, assignmentInfo.minPreferredUidOnlyWaitingTimeMs);
        assertEquals(JobConcurrencyManager.STANDARD_CONCURRENCY_LIMIT,
                assignmentInfo.numRunningTopEj);
    }

    @Test
@@ -257,11 +314,13 @@ public final class JobConcurrencyManagerTest {
        final ArraySet<JobConcurrencyManager.ContextAssignment> idle = new ArraySet<>();
        final List<JobConcurrencyManager.ContextAssignment> preferredUidOnly = new ArrayList<>();
        final List<JobConcurrencyManager.ContextAssignment> stoppable = new ArrayList<>();
        mJobConcurrencyManager
                .prepareForAssignmentDeterminationLocked(idle, preferredUidOnly, stoppable);
        final JobConcurrencyManager.AssignmentInfo assignmentInfo =
                new JobConcurrencyManager.AssignmentInfo();
        mJobConcurrencyManager.prepareForAssignmentDeterminationLocked(
                idle, preferredUidOnly, stoppable, assignmentInfo);
        mJobConcurrencyManager
                .determineAssignmentsLocked(changed, idle, preferredUidOnly, stoppable,
                        Long.MAX_VALUE);
                        assignmentInfo);

        assertEquals(JobConcurrencyManager.STANDARD_CONCURRENCY_LIMIT, changed.size());
        for (int i = changed.size() - 1; i >= 0; --i) {
@@ -301,15 +360,17 @@ public final class JobConcurrencyManagerTest {
        final ArraySet<JobConcurrencyManager.ContextAssignment> idle = new ArraySet<>();
        final List<JobConcurrencyManager.ContextAssignment> preferredUidOnly = new ArrayList<>();
        final List<JobConcurrencyManager.ContextAssignment> stoppable = new ArrayList<>();
        final JobConcurrencyManager.AssignmentInfo assignmentInfo =
                new JobConcurrencyManager.AssignmentInfo();

        long minPreferredUidOnlyWaitingTimeMs = mJobConcurrencyManager
                .prepareForAssignmentDeterminationLocked(idle, preferredUidOnly, stoppable);
        assertEquals(remainingTimeMs, minPreferredUidOnlyWaitingTimeMs);
        mJobConcurrencyManager.prepareForAssignmentDeterminationLocked(
                idle, preferredUidOnly, stoppable, assignmentInfo);
        assertEquals(remainingTimeMs, assignmentInfo.minPreferredUidOnlyWaitingTimeMs);
        assertEquals(JobConcurrencyManager.STANDARD_CONCURRENCY_LIMIT, preferredUidOnly.size());

        mJobConcurrencyManager
                .determineAssignmentsLocked(changed, idle, preferredUidOnly, stoppable,
                        minPreferredUidOnlyWaitingTimeMs);
                        assignmentInfo);

        assertEquals(JobConcurrencyManager.STANDARD_CONCURRENCY_LIMIT, preferredUidOnly.size());
        assertEquals(0, changed.size());
@@ -350,15 +411,17 @@ public final class JobConcurrencyManagerTest {
        final ArraySet<JobConcurrencyManager.ContextAssignment> idle = new ArraySet<>();
        final List<JobConcurrencyManager.ContextAssignment> preferredUidOnly = new ArrayList<>();
        final List<JobConcurrencyManager.ContextAssignment> stoppable = new ArrayList<>();
        final JobConcurrencyManager.AssignmentInfo assignmentInfo =
                new JobConcurrencyManager.AssignmentInfo();

        long minPreferredUidOnlyWaitingTimeMs = mJobConcurrencyManager
                .prepareForAssignmentDeterminationLocked(idle, preferredUidOnly, stoppable);
        assertEquals(remainingTimeMs, minPreferredUidOnlyWaitingTimeMs);
        mJobConcurrencyManager.prepareForAssignmentDeterminationLocked(
                idle, preferredUidOnly, stoppable, assignmentInfo);
        assertEquals(remainingTimeMs, assignmentInfo.minPreferredUidOnlyWaitingTimeMs);
        assertEquals(JobConcurrencyManager.STANDARD_CONCURRENCY_LIMIT, preferredUidOnly.size());

        mJobConcurrencyManager
                .determineAssignmentsLocked(changed, idle, preferredUidOnly, stoppable,
                        minPreferredUidOnlyWaitingTimeMs);
                        assignmentInfo);

        assertEquals(JobConcurrencyManager.STANDARD_CONCURRENCY_LIMIT, preferredUidOnly.size());
        for (int i = changed.size() - 1; i >= 0; --i) {
@@ -404,15 +467,17 @@ public final class JobConcurrencyManagerTest {
        final ArraySet<JobConcurrencyManager.ContextAssignment> idle = new ArraySet<>();
        final List<JobConcurrencyManager.ContextAssignment> preferredUidOnly = new ArrayList<>();
        final List<JobConcurrencyManager.ContextAssignment> stoppable = new ArrayList<>();
        final JobConcurrencyManager.AssignmentInfo assignmentInfo =
                new JobConcurrencyManager.AssignmentInfo();

        long minPreferredUidOnlyWaitingTimeMs = mJobConcurrencyManager
                .prepareForAssignmentDeterminationLocked(idle, preferredUidOnly, stoppable);
        assertEquals(remainingTimeMs, minPreferredUidOnlyWaitingTimeMs);
        mJobConcurrencyManager.prepareForAssignmentDeterminationLocked(
                idle, preferredUidOnly, stoppable, assignmentInfo);
        assertEquals(remainingTimeMs, assignmentInfo.minPreferredUidOnlyWaitingTimeMs);
        assertEquals(JobConcurrencyManager.STANDARD_CONCURRENCY_LIMIT, preferredUidOnly.size());

        mJobConcurrencyManager
                .determineAssignmentsLocked(changed, idle, preferredUidOnly, stoppable,
                        minPreferredUidOnlyWaitingTimeMs);
                        assignmentInfo);

        assertEquals(JobConcurrencyManager.STANDARD_CONCURRENCY_LIMIT, preferredUidOnly.size());
        // Depending on iteration order, we may create 1 or 2 contexts.