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 <mwachens@google.com>
diff --git a/core/java/android/os/UserManagerInternal.java b/core/java/android/os/UserManagerInternal.java
index 97da588..17f00c2 100644
--- a/core/java/android/os/UserManagerInternal.java
+++ b/core/java/android/os/UserManagerInternal.java
@@ -167,4 +167,12 @@
* 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 c08f866..c44dfe2 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.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 @@
}
}
+ 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<JobStatus> jobsForUid = mJobs.getJobsByUid(uid);
@@ -941,6 +949,8 @@
} 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 497d36f..22eed3b 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.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 @@
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 @@
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<JobStatus> 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 30c4009..b115422 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -3684,6 +3684,11 @@
}
@Override
+ public int[] getUserIds() {
+ return UserManagerService.this.getUserIds();
+ }
+
+ @Override
public boolean isUserUnlockingOrUnlocked(int userId) {
synchronized (mUserStates) {
int state = mUserStates.get(userId, -1);