Loading apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java +37 −11 Original line number Original line Diff line number Diff line Loading @@ -882,6 +882,8 @@ public class JobSchedulerService extends com.android.server.SystemService // a user-initiated action, it should be fine to just // a user-initiated action, it should be fine to just // put USER instead of UNINSTALL or DISABLED. // put USER instead of UNINSTALL or DISABLED. cancelJobsForPackageAndUidLocked(pkgName, pkgUid, cancelJobsForPackageAndUidLocked(pkgName, pkgUid, /* includeSchedulingApp */ true, /* includeSourceApp */ true, JobParameters.STOP_REASON_USER, JobParameters.STOP_REASON_USER, JobParameters.INTERNAL_STOP_REASON_UNINSTALL, JobParameters.INTERNAL_STOP_REASON_UNINSTALL, "app disabled"); "app disabled"); Loading Loading @@ -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 // get here, but since this is generally a user-initiated action, it should // be fine to just put USER instead of UNINSTALL or DISABLED. // be fine to just put USER instead of UNINSTALL or DISABLED. cancelJobsForPackageAndUidLocked(pkgName, pkgUid, cancelJobsForPackageAndUidLocked(pkgName, pkgUid, /* includeSchedulingApp */ true, /* includeSourceApp */ true, JobParameters.STOP_REASON_USER, JobParameters.STOP_REASON_USER, JobParameters.INTERNAL_STOP_REASON_UNINSTALL, "app uninstalled"); JobParameters.INTERNAL_STOP_REASON_UNINSTALL, "app uninstalled"); for (int c = 0; c < mControllers.size(); ++c) { for (int c = 0; c < mControllers.size(); ++c) { Loading Loading @@ -986,7 +989,12 @@ public class JobSchedulerService extends com.android.server.SystemService Slog.d(TAG, "Removing jobs for pkg " + pkgName + " at uid " + pkgUid); Slog.d(TAG, "Removing jobs for pkg " + pkgName + " at uid " + pkgUid); } } synchronized (mLock) { 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, cancelJobsForPackageAndUidLocked(pkgName, pkgUid, /* includeSchedulingApp */ true, /* includeSourceApp */ false, JobParameters.STOP_REASON_USER, JobParameters.STOP_REASON_USER, JobParameters.INTERNAL_STOP_REASON_CANCELED, JobParameters.INTERNAL_STOP_REASON_CANCELED, "app force stopped"); "app force stopped"); Loading Loading @@ -1304,16 +1312,18 @@ public class JobSchedulerService extends com.android.server.SystemService } } } } private void cancelJobsForUserLocked(int userHandle) { private final Consumer<JobStatus> mCancelJobDueToUserRemovalConsumer = (toRemove) -> { final List<JobStatus> jobsForUser = mJobs.getJobsByUser(userHandle); // There's no guarantee that the process has been stopped by the time we get for (int i = 0; i < jobsForUser.size(); i++) { // here, but since this is a user-initiated action, it should be fine to just JobStatus toRemove = jobsForUser.get(i); // put USER instead of UNINSTALL or DISABLED. // 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, cancelJobImplLocked(toRemove, null, JobParameters.STOP_REASON_USER, JobParameters.INTERNAL_STOP_REASON_UNINSTALL, "user removed"); JobParameters.INTERNAL_STOP_REASON_UNINSTALL, "user removed"); } }; private void cancelJobsForUserLocked(int userHandle) { mJobs.forEachJob( (job) -> job.getUserId() == userHandle || job.getSourceUserId() == userHandle, mCancelJobDueToUserRemovalConsumer); } } private void cancelJobsForNonExistentUsers() { private void cancelJobsForNonExistentUsers() { Loading @@ -1324,15 +1334,31 @@ public class JobSchedulerService extends com.android.server.SystemService } } private void cancelJobsForPackageAndUidLocked(String pkgName, int uid, private void cancelJobsForPackageAndUidLocked(String pkgName, int uid, boolean includeSchedulingApp, boolean includeSourceApp, @JobParameters.StopReason int reason, int internalReasonCode, String debugReason) { @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)) { if ("android".equals(pkgName)) { Slog.wtfStack(TAG, "Can't cancel all jobs for system package"); Slog.wtfStack(TAG, "Can't cancel all jobs for system package"); return; 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--) { for (int i = jobsForUid.size() - 1; i >= 0; i--) { final JobStatus job = jobsForUid.get(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); cancelJobImplLocked(job, null, reason, internalReasonCode, debugReason); } } } } Loading apex/jobscheduler/service/java/com/android/server/job/JobStore.java +33 −18 Original line number Original line Diff line number Diff line Loading @@ -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.sElapsedRealtimeClock; import static com.android.server.job.JobSchedulerService.sSystemClock; import static com.android.server.job.JobSchedulerService.sSystemClock; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.Nullable; import android.app.job.JobInfo; import android.app.job.JobInfo; import android.content.ComponentName; import android.content.ComponentName; Loading @@ -32,7 +33,6 @@ import android.os.Handler; import android.os.PersistableBundle; import android.os.PersistableBundle; import android.os.Process; import android.os.Process; import android.os.SystemClock; import android.os.SystemClock; import android.os.UserHandle; import android.text.TextUtils; import android.text.TextUtils; import android.text.format.DateUtils; import android.text.format.DateUtils; import android.util.ArraySet; import android.util.ArraySet; Loading Loading @@ -287,26 +287,37 @@ public final class JobStore { } } /** /** * @param userHandle User for whom we are querying the list of jobs. * @param sourceUid Uid of the source app. * @return A list of all the jobs scheduled for the provided user. Never null. * @return A list of all the jobs scheduled for the source app. Never null. */ */ public List<JobStatus> getJobsByUser(int userHandle) { @NonNull return mJobSet.getJobsByUser(userHandle); 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. * @param uid Uid of the requesting app. * @return All JobStatus objects for a given uid from the master list. Never null. * @return All JobStatus objects for a given uid from the master list. Never null. */ */ @NonNull public List<JobStatus> getJobsByUid(int uid) { public List<JobStatus> getJobsByUid(int uid) { return mJobSet.getJobsByUid(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 uid Uid of the requesting app. * @param jobId Job id, specified at schedule-time. * @param jobId Job id, specified at schedule-time. * @return the JobStatus that matches the provided uId and jobId, or null if none found. * @return the JobStatus that matches the provided uId and jobId, or null if none found. */ */ @Nullable public JobStatus getJobByUidAndJobId(int uid, int jobId) { public JobStatus getJobByUidAndJobId(int uid, int jobId) { return mJobSet.get(uid, jobId); return mJobSet.get(uid, jobId); } } Loading Loading @@ -1220,25 +1231,29 @@ public final class JobStore { public List<JobStatus> getJobsByUid(int uid) { public List<JobStatus> getJobsByUid(int uid) { ArrayList<JobStatus> matchingJobs = new ArrayList<JobStatus>(); ArrayList<JobStatus> matchingJobs = new ArrayList<JobStatus>(); getJobsByUid(uid, matchingJobs); return matchingJobs; } public void getJobsByUid(int uid, List<JobStatus> insertInto) { ArraySet<JobStatus> jobs = mJobs.get(uid); ArraySet<JobStatus> jobs = mJobs.get(uid); if (jobs != null) { if (jobs != null) { matchingJobs.addAll(jobs); insertInto.addAll(jobs); } } return matchingJobs; } } // By user, not by uid, so we need to traverse by key and check @NonNull public List<JobStatus> getJobsByUser(int userId) { public List<JobStatus> getJobsBySourceUid(int sourceUid) { final ArrayList<JobStatus> result = new ArrayList<JobStatus>(); final ArrayList<JobStatus> result = new ArrayList<JobStatus>(); for (int i = mJobsPerSourceUid.size() - 1; i >= 0; i--) { getJobsBySourceUid(sourceUid, result); if (UserHandle.getUserId(mJobsPerSourceUid.keyAt(i)) == userId) { return result; final ArraySet<JobStatus> jobs = mJobsPerSourceUid.valueAt(i); if (jobs != null) { result.addAll(jobs); } } } public void getJobsBySourceUid(int sourceUid, List<JobStatus> insertInto) { final ArraySet<JobStatus> jobs = mJobsPerSourceUid.get(sourceUid); if (jobs != null) { insertInto.addAll(jobs); } } return result; } } public boolean add(JobStatus job) { public boolean add(JobStatus job) { Loading Loading @@ -1382,7 +1397,7 @@ public final class JobStore { } } public void forEachJob(@Nullable Predicate<JobStatus> filterPredicate, public void forEachJob(@Nullable Predicate<JobStatus> filterPredicate, Consumer<JobStatus> functor) { @NonNull Consumer<JobStatus> functor) { for (int uidIndex = mJobs.size() - 1; uidIndex >= 0; uidIndex--) { for (int uidIndex = mJobs.size() - 1; uidIndex >= 0; uidIndex--) { ArraySet<JobStatus> jobs = mJobs.valueAt(uidIndex); ArraySet<JobStatus> jobs = mJobs.valueAt(uidIndex); if (jobs != null) { if (jobs != null) { Loading Loading
apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java +37 −11 Original line number Original line Diff line number Diff line Loading @@ -882,6 +882,8 @@ public class JobSchedulerService extends com.android.server.SystemService // a user-initiated action, it should be fine to just // a user-initiated action, it should be fine to just // put USER instead of UNINSTALL or DISABLED. // put USER instead of UNINSTALL or DISABLED. cancelJobsForPackageAndUidLocked(pkgName, pkgUid, cancelJobsForPackageAndUidLocked(pkgName, pkgUid, /* includeSchedulingApp */ true, /* includeSourceApp */ true, JobParameters.STOP_REASON_USER, JobParameters.STOP_REASON_USER, JobParameters.INTERNAL_STOP_REASON_UNINSTALL, JobParameters.INTERNAL_STOP_REASON_UNINSTALL, "app disabled"); "app disabled"); Loading Loading @@ -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 // get here, but since this is generally a user-initiated action, it should // be fine to just put USER instead of UNINSTALL or DISABLED. // be fine to just put USER instead of UNINSTALL or DISABLED. cancelJobsForPackageAndUidLocked(pkgName, pkgUid, cancelJobsForPackageAndUidLocked(pkgName, pkgUid, /* includeSchedulingApp */ true, /* includeSourceApp */ true, JobParameters.STOP_REASON_USER, JobParameters.STOP_REASON_USER, JobParameters.INTERNAL_STOP_REASON_UNINSTALL, "app uninstalled"); JobParameters.INTERNAL_STOP_REASON_UNINSTALL, "app uninstalled"); for (int c = 0; c < mControllers.size(); ++c) { for (int c = 0; c < mControllers.size(); ++c) { Loading Loading @@ -986,7 +989,12 @@ public class JobSchedulerService extends com.android.server.SystemService Slog.d(TAG, "Removing jobs for pkg " + pkgName + " at uid " + pkgUid); Slog.d(TAG, "Removing jobs for pkg " + pkgName + " at uid " + pkgUid); } } synchronized (mLock) { 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, cancelJobsForPackageAndUidLocked(pkgName, pkgUid, /* includeSchedulingApp */ true, /* includeSourceApp */ false, JobParameters.STOP_REASON_USER, JobParameters.STOP_REASON_USER, JobParameters.INTERNAL_STOP_REASON_CANCELED, JobParameters.INTERNAL_STOP_REASON_CANCELED, "app force stopped"); "app force stopped"); Loading Loading @@ -1304,16 +1312,18 @@ public class JobSchedulerService extends com.android.server.SystemService } } } } private void cancelJobsForUserLocked(int userHandle) { private final Consumer<JobStatus> mCancelJobDueToUserRemovalConsumer = (toRemove) -> { final List<JobStatus> jobsForUser = mJobs.getJobsByUser(userHandle); // There's no guarantee that the process has been stopped by the time we get for (int i = 0; i < jobsForUser.size(); i++) { // here, but since this is a user-initiated action, it should be fine to just JobStatus toRemove = jobsForUser.get(i); // put USER instead of UNINSTALL or DISABLED. // 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, cancelJobImplLocked(toRemove, null, JobParameters.STOP_REASON_USER, JobParameters.INTERNAL_STOP_REASON_UNINSTALL, "user removed"); JobParameters.INTERNAL_STOP_REASON_UNINSTALL, "user removed"); } }; private void cancelJobsForUserLocked(int userHandle) { mJobs.forEachJob( (job) -> job.getUserId() == userHandle || job.getSourceUserId() == userHandle, mCancelJobDueToUserRemovalConsumer); } } private void cancelJobsForNonExistentUsers() { private void cancelJobsForNonExistentUsers() { Loading @@ -1324,15 +1334,31 @@ public class JobSchedulerService extends com.android.server.SystemService } } private void cancelJobsForPackageAndUidLocked(String pkgName, int uid, private void cancelJobsForPackageAndUidLocked(String pkgName, int uid, boolean includeSchedulingApp, boolean includeSourceApp, @JobParameters.StopReason int reason, int internalReasonCode, String debugReason) { @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)) { if ("android".equals(pkgName)) { Slog.wtfStack(TAG, "Can't cancel all jobs for system package"); Slog.wtfStack(TAG, "Can't cancel all jobs for system package"); return; 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--) { for (int i = jobsForUid.size() - 1; i >= 0; i--) { final JobStatus job = jobsForUid.get(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); cancelJobImplLocked(job, null, reason, internalReasonCode, debugReason); } } } } Loading
apex/jobscheduler/service/java/com/android/server/job/JobStore.java +33 −18 Original line number Original line Diff line number Diff line Loading @@ -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.sElapsedRealtimeClock; import static com.android.server.job.JobSchedulerService.sSystemClock; import static com.android.server.job.JobSchedulerService.sSystemClock; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.Nullable; import android.app.job.JobInfo; import android.app.job.JobInfo; import android.content.ComponentName; import android.content.ComponentName; Loading @@ -32,7 +33,6 @@ import android.os.Handler; import android.os.PersistableBundle; import android.os.PersistableBundle; import android.os.Process; import android.os.Process; import android.os.SystemClock; import android.os.SystemClock; import android.os.UserHandle; import android.text.TextUtils; import android.text.TextUtils; import android.text.format.DateUtils; import android.text.format.DateUtils; import android.util.ArraySet; import android.util.ArraySet; Loading Loading @@ -287,26 +287,37 @@ public final class JobStore { } } /** /** * @param userHandle User for whom we are querying the list of jobs. * @param sourceUid Uid of the source app. * @return A list of all the jobs scheduled for the provided user. Never null. * @return A list of all the jobs scheduled for the source app. Never null. */ */ public List<JobStatus> getJobsByUser(int userHandle) { @NonNull return mJobSet.getJobsByUser(userHandle); 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. * @param uid Uid of the requesting app. * @return All JobStatus objects for a given uid from the master list. Never null. * @return All JobStatus objects for a given uid from the master list. Never null. */ */ @NonNull public List<JobStatus> getJobsByUid(int uid) { public List<JobStatus> getJobsByUid(int uid) { return mJobSet.getJobsByUid(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 uid Uid of the requesting app. * @param jobId Job id, specified at schedule-time. * @param jobId Job id, specified at schedule-time. * @return the JobStatus that matches the provided uId and jobId, or null if none found. * @return the JobStatus that matches the provided uId and jobId, or null if none found. */ */ @Nullable public JobStatus getJobByUidAndJobId(int uid, int jobId) { public JobStatus getJobByUidAndJobId(int uid, int jobId) { return mJobSet.get(uid, jobId); return mJobSet.get(uid, jobId); } } Loading Loading @@ -1220,25 +1231,29 @@ public final class JobStore { public List<JobStatus> getJobsByUid(int uid) { public List<JobStatus> getJobsByUid(int uid) { ArrayList<JobStatus> matchingJobs = new ArrayList<JobStatus>(); ArrayList<JobStatus> matchingJobs = new ArrayList<JobStatus>(); getJobsByUid(uid, matchingJobs); return matchingJobs; } public void getJobsByUid(int uid, List<JobStatus> insertInto) { ArraySet<JobStatus> jobs = mJobs.get(uid); ArraySet<JobStatus> jobs = mJobs.get(uid); if (jobs != null) { if (jobs != null) { matchingJobs.addAll(jobs); insertInto.addAll(jobs); } } return matchingJobs; } } // By user, not by uid, so we need to traverse by key and check @NonNull public List<JobStatus> getJobsByUser(int userId) { public List<JobStatus> getJobsBySourceUid(int sourceUid) { final ArrayList<JobStatus> result = new ArrayList<JobStatus>(); final ArrayList<JobStatus> result = new ArrayList<JobStatus>(); for (int i = mJobsPerSourceUid.size() - 1; i >= 0; i--) { getJobsBySourceUid(sourceUid, result); if (UserHandle.getUserId(mJobsPerSourceUid.keyAt(i)) == userId) { return result; final ArraySet<JobStatus> jobs = mJobsPerSourceUid.valueAt(i); if (jobs != null) { result.addAll(jobs); } } } public void getJobsBySourceUid(int sourceUid, List<JobStatus> insertInto) { final ArraySet<JobStatus> jobs = mJobsPerSourceUid.get(sourceUid); if (jobs != null) { insertInto.addAll(jobs); } } return result; } } public boolean add(JobStatus job) { public boolean add(JobStatus job) { Loading Loading @@ -1382,7 +1397,7 @@ public final class JobStore { } } public void forEachJob(@Nullable Predicate<JobStatus> filterPredicate, public void forEachJob(@Nullable Predicate<JobStatus> filterPredicate, Consumer<JobStatus> functor) { @NonNull Consumer<JobStatus> functor) { for (int uidIndex = mJobs.size() - 1; uidIndex >= 0; uidIndex--) { for (int uidIndex = mJobs.size() - 1; uidIndex >= 0; uidIndex--) { ArraySet<JobStatus> jobs = mJobs.valueAt(uidIndex); ArraySet<JobStatus> jobs = mJobs.valueAt(uidIndex); if (jobs != null) { if (jobs != null) { Loading