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
This commit is contained in:
Kweku Adams
2023-01-24 21:24:13 +00:00
parent c76e3b50b1
commit 59c3b84f00
2 changed files with 88 additions and 5 deletions

View File

@@ -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);
}
}
}
}
}

View File

@@ -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));
}
}