Cancel all jobs associated with an uninstalled app.
Make sure we don't leave around any jobs scheduled on behalf of the uninstalled app when it is uninstalled. Bug: 247808104 Test: atest CtsJobSchedulerTestCases Change-Id: I9114b7670bc20bffe9666c48bbf244d87f097d3b
This commit is contained in:
@@ -882,6 +882,8 @@ public class JobSchedulerService extends com.android.server.SystemService
|
||||
// a user-initiated action, it should be fine to just
|
||||
// put USER instead of UNINSTALL or DISABLED.
|
||||
cancelJobsForPackageAndUidLocked(pkgName, pkgUid,
|
||||
/* includeSchedulingApp */ true,
|
||||
/* includeSourceApp */ true,
|
||||
JobParameters.STOP_REASON_USER,
|
||||
JobParameters.INTERNAL_STOP_REASON_UNINSTALL,
|
||||
"app disabled");
|
||||
@@ -932,6 +934,7 @@ public class JobSchedulerService extends com.android.server.SystemService
|
||||
// get here, but since this is generally a user-initiated action, it should
|
||||
// be fine to just put USER instead of UNINSTALL or DISABLED.
|
||||
cancelJobsForPackageAndUidLocked(pkgName, pkgUid,
|
||||
/* includeSchedulingApp */ true, /* includeSourceApp */ true,
|
||||
JobParameters.STOP_REASON_USER,
|
||||
JobParameters.INTERNAL_STOP_REASON_UNINSTALL, "app uninstalled");
|
||||
for (int c = 0; c < mControllers.size(); ++c) {
|
||||
@@ -986,7 +989,12 @@ public class JobSchedulerService extends com.android.server.SystemService
|
||||
Slog.d(TAG, "Removing jobs for pkg " + pkgName + " at uid " + pkgUid);
|
||||
}
|
||||
synchronized (mLock) {
|
||||
// Exclude jobs scheduled on behalf of this app for now because SyncManager
|
||||
// and other job proxy agents may not know to reschedule the job properly
|
||||
// after force stop.
|
||||
// TODO(209852664): determine how to best handle syncs & other proxied jobs
|
||||
cancelJobsForPackageAndUidLocked(pkgName, pkgUid,
|
||||
/* includeSchedulingApp */ true, /* includeSourceApp */ false,
|
||||
JobParameters.STOP_REASON_USER,
|
||||
JobParameters.INTERNAL_STOP_REASON_CANCELED,
|
||||
"app force stopped");
|
||||
@@ -1304,16 +1312,18 @@ public class JobSchedulerService extends com.android.server.SystemService
|
||||
}
|
||||
}
|
||||
|
||||
private final Consumer<JobStatus> mCancelJobDueToUserRemovalConsumer = (toRemove) -> {
|
||||
// There's no guarantee that the process has been stopped by the time we get
|
||||
// here, but since this is a user-initiated action, it should be fine to just
|
||||
// put USER instead of UNINSTALL or DISABLED.
|
||||
cancelJobImplLocked(toRemove, null, JobParameters.STOP_REASON_USER,
|
||||
JobParameters.INTERNAL_STOP_REASON_UNINSTALL, "user removed");
|
||||
};
|
||||
|
||||
private void cancelJobsForUserLocked(int userHandle) {
|
||||
final List<JobStatus> jobsForUser = mJobs.getJobsByUser(userHandle);
|
||||
for (int i = 0; i < jobsForUser.size(); i++) {
|
||||
JobStatus toRemove = jobsForUser.get(i);
|
||||
// There's no guarantee that the process has been stopped by the time we get here,
|
||||
// but since this is a user-initiated action, it should be fine to just put USER
|
||||
// instead of UNINSTALL or DISABLED.
|
||||
cancelJobImplLocked(toRemove, null, JobParameters.STOP_REASON_USER,
|
||||
JobParameters.INTERNAL_STOP_REASON_UNINSTALL, "user removed");
|
||||
}
|
||||
mJobs.forEachJob(
|
||||
(job) -> job.getUserId() == userHandle || job.getSourceUserId() == userHandle,
|
||||
mCancelJobDueToUserRemovalConsumer);
|
||||
}
|
||||
|
||||
private void cancelJobsForNonExistentUsers() {
|
||||
@@ -1324,15 +1334,31 @@ public class JobSchedulerService extends com.android.server.SystemService
|
||||
}
|
||||
|
||||
private void cancelJobsForPackageAndUidLocked(String pkgName, int uid,
|
||||
boolean includeSchedulingApp, boolean includeSourceApp,
|
||||
@JobParameters.StopReason int reason, int internalReasonCode, String debugReason) {
|
||||
if (!includeSchedulingApp && !includeSourceApp) {
|
||||
Slog.wtfStack(TAG,
|
||||
"Didn't indicate whether to cancel jobs for scheduling and/or source app");
|
||||
includeSourceApp = true;
|
||||
}
|
||||
if ("android".equals(pkgName)) {
|
||||
Slog.wtfStack(TAG, "Can't cancel all jobs for system package");
|
||||
return;
|
||||
}
|
||||
final List<JobStatus> jobsForUid = mJobs.getJobsByUid(uid);
|
||||
final List<JobStatus> jobsForUid = new ArrayList<>();
|
||||
if (includeSchedulingApp) {
|
||||
mJobs.getJobsByUid(uid, jobsForUid);
|
||||
}
|
||||
if (includeSourceApp) {
|
||||
mJobs.getJobsBySourceUid(uid, jobsForUid);
|
||||
}
|
||||
for (int i = jobsForUid.size() - 1; i >= 0; i--) {
|
||||
final JobStatus job = jobsForUid.get(i);
|
||||
if (job.getSourcePackageName().equals(pkgName)) {
|
||||
final boolean shouldCancel =
|
||||
(includeSchedulingApp
|
||||
&& job.getServiceComponent().getPackageName().equals(pkgName))
|
||||
|| (includeSourceApp && job.getSourcePackageName().equals(pkgName));
|
||||
if (shouldCancel) {
|
||||
cancelJobImplLocked(job, null, reason, internalReasonCode, debugReason);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ import static android.net.NetworkCapabilities.TRANSPORT_TEST;
|
||||
import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock;
|
||||
import static com.android.server.job.JobSchedulerService.sSystemClock;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.app.job.JobInfo;
|
||||
import android.content.ComponentName;
|
||||
@@ -32,7 +33,6 @@ import android.os.Handler;
|
||||
import android.os.PersistableBundle;
|
||||
import android.os.Process;
|
||||
import android.os.SystemClock;
|
||||
import android.os.UserHandle;
|
||||
import android.text.TextUtils;
|
||||
import android.text.format.DateUtils;
|
||||
import android.util.ArraySet;
|
||||
@@ -287,26 +287,37 @@ public final class JobStore {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param userHandle User for whom we are querying the list of jobs.
|
||||
* @return A list of all the jobs scheduled for the provided user. Never null.
|
||||
* @param sourceUid Uid of the source app.
|
||||
* @return A list of all the jobs scheduled for the source app. Never null.
|
||||
*/
|
||||
public List<JobStatus> getJobsByUser(int userHandle) {
|
||||
return mJobSet.getJobsByUser(userHandle);
|
||||
@NonNull
|
||||
public List<JobStatus> getJobsBySourceUid(int sourceUid) {
|
||||
return mJobSet.getJobsBySourceUid(sourceUid);
|
||||
}
|
||||
|
||||
public void getJobsBySourceUid(int sourceUid, @NonNull List<JobStatus> insertInto) {
|
||||
mJobSet.getJobsBySourceUid(sourceUid, insertInto);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param uid Uid of the requesting app.
|
||||
* @return All JobStatus objects for a given uid from the master list. Never null.
|
||||
*/
|
||||
@NonNull
|
||||
public List<JobStatus> getJobsByUid(int uid) {
|
||||
return mJobSet.getJobsByUid(uid);
|
||||
}
|
||||
|
||||
public void getJobsByUid(int uid, @NonNull List<JobStatus> insertInto) {
|
||||
mJobSet.getJobsByUid(uid, insertInto);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param uid Uid of the requesting app.
|
||||
* @param jobId Job id, specified at schedule-time.
|
||||
* @return the JobStatus that matches the provided uId and jobId, or null if none found.
|
||||
*/
|
||||
@Nullable
|
||||
public JobStatus getJobByUidAndJobId(int uid, int jobId) {
|
||||
return mJobSet.get(uid, jobId);
|
||||
}
|
||||
@@ -1207,27 +1218,31 @@ public final class JobStore {
|
||||
|
||||
public List<JobStatus> getJobsByUid(int uid) {
|
||||
ArrayList<JobStatus> matchingJobs = new ArrayList<JobStatus>();
|
||||
ArraySet<JobStatus> jobs = mJobs.get(uid);
|
||||
if (jobs != null) {
|
||||
matchingJobs.addAll(jobs);
|
||||
}
|
||||
getJobsByUid(uid, matchingJobs);
|
||||
return matchingJobs;
|
||||
}
|
||||
|
||||
// By user, not by uid, so we need to traverse by key and check
|
||||
public List<JobStatus> getJobsByUser(int userId) {
|
||||
final ArrayList<JobStatus> result = new ArrayList<JobStatus>();
|
||||
for (int i = mJobsPerSourceUid.size() - 1; i >= 0; i--) {
|
||||
if (UserHandle.getUserId(mJobsPerSourceUid.keyAt(i)) == userId) {
|
||||
final ArraySet<JobStatus> jobs = mJobsPerSourceUid.valueAt(i);
|
||||
if (jobs != null) {
|
||||
result.addAll(jobs);
|
||||
}
|
||||
}
|
||||
public void getJobsByUid(int uid, List<JobStatus> insertInto) {
|
||||
ArraySet<JobStatus> jobs = mJobs.get(uid);
|
||||
if (jobs != null) {
|
||||
insertInto.addAll(jobs);
|
||||
}
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public List<JobStatus> getJobsBySourceUid(int sourceUid) {
|
||||
final ArrayList<JobStatus> result = new ArrayList<JobStatus>();
|
||||
getJobsBySourceUid(sourceUid, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
public void getJobsBySourceUid(int sourceUid, List<JobStatus> insertInto) {
|
||||
final ArraySet<JobStatus> jobs = mJobsPerSourceUid.get(sourceUid);
|
||||
if (jobs != null) {
|
||||
insertInto.addAll(jobs);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean add(JobStatus job) {
|
||||
final int uid = job.getUid();
|
||||
final int sourceUid = job.getSourceUid();
|
||||
@@ -1369,7 +1384,7 @@ public final class JobStore {
|
||||
}
|
||||
|
||||
public void forEachJob(@Nullable Predicate<JobStatus> filterPredicate,
|
||||
Consumer<JobStatus> functor) {
|
||||
@NonNull Consumer<JobStatus> functor) {
|
||||
for (int uidIndex = mJobs.size() - 1; uidIndex >= 0; uidIndex--) {
|
||||
ArraySet<JobStatus> jobs = mJobs.valueAt(uidIndex);
|
||||
if (jobs != null) {
|
||||
|
||||
Reference in New Issue
Block a user