Merge "Cancel all jobs associated with an uninstalled app."

This commit is contained in:
TreeHugger Robot
2022-09-28 19:52:46 +00:00
committed by Android (Google) Code Review
2 changed files with 72 additions and 31 deletions

View File

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

View File

@@ -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);
}
@@ -1220,27 +1231,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();
@@ -1382,7 +1397,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) {