Loading apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java +23 −1 Original line number Diff line number Diff line Loading @@ -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) { Loading Loading @@ -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"); Loading @@ -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); } } } } } Loading services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java +65 −4 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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) { Loading @@ -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"); Loading @@ -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)); Loading Loading @@ -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) { Loading Loading @@ -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)); } } Loading
apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java +23 −1 Original line number Diff line number Diff line Loading @@ -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) { Loading Loading @@ -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"); Loading @@ -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); } } } } } Loading
services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java +65 −4 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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) { Loading @@ -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"); Loading @@ -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)); Loading Loading @@ -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) { Loading Loading @@ -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)); } }