From 3f2b6552ec00a69d5a818e3eadc985c45a416c6b Mon Sep 17 00:00:00 2001 From: Michael Wachenschwanz Date: Mon, 15 May 2017 13:53:09 -0700 Subject: [PATCH] Purge Nonexistent User Jobs on Boot In the case that a user has been removed but their jobs still exist on disk, the JobSchedulerService will remove all jobs not associated with current users on boot. Exposed UserManagerService#getUserIds() via UserManagerInternal for quick user id retrieval. Fixes: 38261977 Test: manual Change-Id: Id4b3c0a4142b4818fcd875eef18ea03f3c45ca40 Signed-off-by: Michael Wachenschwanz --- core/java/android/os/UserManagerInternal.java | 8 ++++++++ .../server/job/JobSchedulerService.java | 10 ++++++++++ .../java/com/android/server/job/JobStore.java | 20 +++++++++++++++++++ .../android/server/pm/UserManagerService.java | 5 +++++ 4 files changed, 43 insertions(+) diff --git a/core/java/android/os/UserManagerInternal.java b/core/java/android/os/UserManagerInternal.java index 97da5889a1866..17f00c24988d7 100644 --- a/core/java/android/os/UserManagerInternal.java +++ b/core/java/android/os/UserManagerInternal.java @@ -167,4 +167,12 @@ public abstract class UserManagerInternal { * Remove user's running state */ public abstract void removeUserState(int userId); + + /** + * Returns an array of user ids. This array is cached in UserManagerService and passed as a + * reference, so do not modify the returned array. + * + * @return the array of user ids. + */ + public abstract int[] getUserIds(); } diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java index c08f866086568..c44dfe2bcc388 100644 --- a/services/core/java/com/android/server/job/JobSchedulerService.java +++ b/services/core/java/com/android/server/job/JobSchedulerService.java @@ -63,6 +63,7 @@ import android.os.ServiceManager; import android.os.ShellCallback; import android.os.SystemClock; import android.os.UserHandle; +import android.os.UserManagerInternal; import android.provider.Settings; import android.util.KeyValueListParser; import android.util.Slog; @@ -751,6 +752,13 @@ public final class JobSchedulerService extends com.android.server.SystemService } } + private void cancelJobsForNonExistentUsers() { + UserManagerInternal umi = LocalServices.getService(UserManagerInternal.class); + synchronized (mLock) { + mJobs.removeJobsOfNonUsers(umi.getUserIds()); + } + } + void cancelJobsForPackageAndUid(String pkgName, int uid) { synchronized (mLock) { final List jobsForUid = mJobs.getJobsByUid(uid); @@ -941,6 +949,8 @@ public final class JobSchedulerService extends com.android.server.SystemService } catch (RemoteException e) { // ignored; both services live in system_server } + // Remove any jobs that are not associated with any of the current users. + cancelJobsForNonExistentUsers(); } else if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) { synchronized (mLock) { // Let's go! diff --git a/services/core/java/com/android/server/job/JobStore.java b/services/core/java/com/android/server/job/JobStore.java index 497d36f82539f..22eed3b042504 100644 --- a/services/core/java/com/android/server/job/JobStore.java +++ b/services/core/java/com/android/server/job/JobStore.java @@ -35,6 +35,7 @@ import android.util.SparseArray; import android.util.Xml; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.util.ArrayUtils; import com.android.internal.util.FastXmlSerializer; import com.android.server.IoThread; import com.android.server.job.controllers.JobStatus; @@ -171,6 +172,14 @@ public class JobStore { return removed; } + /** + * Remove the jobs of users not specified in the whitelist. + * @param whitelist Array of User IDs whose jobs are not to be removed. + */ + public void removeJobsOfNonUsers(int[] whitelist) { + mJobSet.removeJobsOfNonUsers(whitelist); + } + @VisibleForTesting public void clear() { mJobSet.clear(); @@ -839,6 +848,17 @@ public class JobStore { return didRemove; } + // Remove the jobs all users not specified by the whitelist of user ids + public void removeJobsOfNonUsers(int[] whitelist) { + for (int jobIndex = mJobs.size() - 1; jobIndex >= 0; jobIndex--) { + int jobUserId = UserHandle.getUserId(mJobs.keyAt(jobIndex)); + // check if job's user id is not in the whitelist + if (!ArrayUtils.contains(whitelist, jobUserId)) { + mJobs.removeAt(jobIndex); + } + } + } + public boolean contains(JobStatus job) { final int uid = job.getUid(); ArraySet jobs = mJobs.get(uid); diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java index 30c40094b067e..b1154227b2040 100644 --- a/services/core/java/com/android/server/pm/UserManagerService.java +++ b/services/core/java/com/android/server/pm/UserManagerService.java @@ -3683,6 +3683,11 @@ public class UserManagerService extends IUserManager.Stub { } } + @Override + public int[] getUserIds() { + return UserManagerService.this.getUserIds(); + } + @Override public boolean isUserUnlockingOrUnlocked(int userId) { synchronized (mUserStates) {