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

Commit 59c3b84f authored by Kweku Adams's avatar Kweku Adams
Browse files

Remove jobs from the pending queue on user stop.

Remove jobs from the pending queue when the user stops the app via Task
Manager so we don't immediately restart the process.

Bug: 261999509
Test: atest FrameworksMockingServicesTests:JobSchedulerServiceTest
Change-Id: I76f9bb9778ca9b2fd3cfaef99430334547206ad4
parent c76e3b50
Loading
Loading
Loading
Loading
+23 −1
Original line number Diff line number Diff line
@@ -1584,6 +1584,12 @@ public class JobSchedulerService extends com.android.server.SystemService
        return reason;
    }

    @VisibleForTesting
    @JobScheduler.PendingJobReason
    int getPendingJobReason(JobStatus job) {
        return getPendingJobReason(job.getUid(), job.getNamespace(), job.getJobId());
    }

    @JobScheduler.PendingJobReason
    @GuardedBy("mLock")
    private int getPendingJobReasonLocked(int uid, String namespace, int jobId) {
@@ -1694,7 +1700,8 @@ public class JobSchedulerService extends com.android.server.SystemService
        }
    }

    private void stopUserVisibleJobsInternal(@NonNull String packageName, int userId) {
    @VisibleForTesting
    void stopUserVisibleJobsInternal(@NonNull String packageName, int userId) {
        final int packageUid = mLocalPM.getPackageUid(packageName, 0, userId);
        if (packageUid < 0) {
            Slog.wtf(TAG, "Asked to stop jobs of an unknown package");
@@ -1716,6 +1723,21 @@ public class JobSchedulerService extends com.android.server.SystemService
                // to stop only that work, B's jobs would be demoted as well.
                // TODO(255768978): make it possible to demote only the relevant subset of jobs
                job.addInternalFlags(JobStatus.INTERNAL_FLAG_DEMOTED_BY_USER);

                // The app process will be killed soon. There's no point keeping its jobs in
                // the pending queue to try and start them.
                if (mPendingJobQueue.remove(job)) {
                    synchronized (mPendingJobReasonCache) {
                        SparseIntArray jobIdToReason = mPendingJobReasonCache.get(
                                job.getUid(), job.getNamespace());
                        if (jobIdToReason == null) {
                            jobIdToReason = new SparseIntArray();
                            mPendingJobReasonCache.add(job.getUid(), job.getNamespace(),
                                    jobIdToReason);
                        }
                        jobIdToReason.put(job.getJobId(), JobScheduler.PENDING_JOB_REASON_USER);
                    }
                }
            }
        }
    }
+65 −4
Original line number Diff line number Diff line
@@ -30,7 +30,9 @@ import static com.android.server.job.JobSchedulerService.RARE_INDEX;
import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
@@ -95,6 +97,8 @@ public class JobSchedulerServiceTest {
    private ActivityManagerInternal mActivityMangerInternal;
    @Mock
    private Context mContext;
    @Mock
    private PackageManagerInternal mPackageManagerInternal;

    private class TestJobSchedulerService extends JobSchedulerService {
        TestJobSchedulerService(Context context) {
@@ -121,6 +125,8 @@ public class JobSchedulerServiceTest {
                .when(() -> LocalServices.getService(AppStandbyInternal.class));
        doReturn(mock(BatteryManagerInternal.class))
                .when(() -> LocalServices.getService(BatteryManagerInternal.class));
        doReturn(mPackageManagerInternal)
                .when(() -> LocalServices.getService(PackageManagerInternal.class));
        doReturn(mock(UsageStatsManagerInternal.class))
                .when(() -> LocalServices.getService(UsageStatsManagerInternal.class));
        when(mContext.getString(anyInt())).thenReturn("some_test_string");
@@ -138,9 +144,6 @@ public class JobSchedulerServiceTest {
        // Used in JobConcurrencyManager.
        doReturn(mock(UserManagerInternal.class))
                .when(() -> LocalServices.getService(UserManagerInternal.class));
        // Used in JobStatus.
        doReturn(mock(PackageManagerInternal.class))
                .when(() -> LocalServices.getService(PackageManagerInternal.class));
        // Called via IdleController constructor.
        when(mContext.getPackageManager()).thenReturn(mock(PackageManager.class));
        when(mContext.getResources()).thenReturn(mock(Resources.class));
@@ -197,8 +200,13 @@ public class JobSchedulerServiceTest {

    private JobStatus createJobStatus(String testTag, JobInfo.Builder jobInfoBuilder,
            int callingUid) {
        return createJobStatus(testTag, jobInfoBuilder, callingUid, "com.android.test");
    }

    private JobStatus createJobStatus(String testTag, JobInfo.Builder jobInfoBuilder,
            int callingUid, String sourcePkg) {
        return JobStatus.createFromJobInfo(
                jobInfoBuilder.build(), callingUid, "com.android.test", 0, "JSSTest", testTag);
                jobInfoBuilder.build(), callingUid, sourcePkg, 0, "JSSTest", testTag);
    }

    private void grantRunLongJobsPermission(boolean grant) {
@@ -1238,4 +1246,57 @@ public class JobSchedulerServiceTest {
                            0, "JSSTest", ""));
        }
    }

    /** Tests that jobs are removed from the pending list if the user stops the app. */
    @Test
    public void testUserStopRemovesPending() {
        spyOn(mService);

        JobStatus job1a = createJobStatus("testUserStopRemovesPending",
                createJobInfo(1), 1, "pkg1");
        JobStatus job1b = createJobStatus("testUserStopRemovesPending",
                createJobInfo(2), 1, "pkg1");
        JobStatus job2a = createJobStatus("testUserStopRemovesPending",
                createJobInfo(1), 2, "pkg2");
        JobStatus job2b = createJobStatus("testUserStopRemovesPending",
                createJobInfo(2), 2, "pkg2");
        doReturn(1).when(mPackageManagerInternal).getPackageUid("pkg1", 0, 0);
        doReturn(11).when(mPackageManagerInternal).getPackageUid("pkg1", 0, 1);
        doReturn(2).when(mPackageManagerInternal).getPackageUid("pkg2", 0, 0);

        mService.getPendingJobQueue().clear();
        mService.getPendingJobQueue().add(job1a);
        mService.getPendingJobQueue().add(job1b);
        mService.getPendingJobQueue().add(job2a);
        mService.getPendingJobQueue().add(job2b);
        mService.getJobStore().add(job1a);
        mService.getJobStore().add(job1b);
        mService.getJobStore().add(job2a);
        mService.getJobStore().add(job2b);

        mService.stopUserVisibleJobsInternal("pkg1", 1);
        assertEquals(4, mService.getPendingJobQueue().size());
        assertTrue(mService.getPendingJobQueue().contains(job1a));
        assertTrue(mService.getPendingJobQueue().contains(job1b));
        assertTrue(mService.getPendingJobQueue().contains(job2a));
        assertTrue(mService.getPendingJobQueue().contains(job2b));

        mService.stopUserVisibleJobsInternal("pkg1", 0);
        assertEquals(2, mService.getPendingJobQueue().size());
        assertFalse(mService.getPendingJobQueue().contains(job1a));
        assertEquals(JobScheduler.PENDING_JOB_REASON_USER, mService.getPendingJobReason(job1a));
        assertFalse(mService.getPendingJobQueue().contains(job1b));
        assertEquals(JobScheduler.PENDING_JOB_REASON_USER, mService.getPendingJobReason(job1b));
        assertTrue(mService.getPendingJobQueue().contains(job2a));
        assertTrue(mService.getPendingJobQueue().contains(job2b));

        mService.stopUserVisibleJobsInternal("pkg2", 0);
        assertEquals(0, mService.getPendingJobQueue().size());
        assertFalse(mService.getPendingJobQueue().contains(job1a));
        assertFalse(mService.getPendingJobQueue().contains(job1b));
        assertFalse(mService.getPendingJobQueue().contains(job2a));
        assertEquals(JobScheduler.PENDING_JOB_REASON_USER, mService.getPendingJobReason(job2a));
        assertFalse(mService.getPendingJobQueue().contains(job2b));
        assertEquals(JobScheduler.PENDING_JOB_REASON_USER, mService.getPendingJobReason(job2b));
    }
}