summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Ruslan Tkhakokhov <rthakohov@google.com> 2019-01-14 20:07:14 +0000
committer Android (Google) Code Review <android-gerrit@google.com> 2019-01-14 20:07:14 +0000
commit1cf011cd7658f7f10ae5c802799a79ef91e3ec2b (patch)
tree9d94bce56c09b154fafabd878b017a4dfd47d83f
parent9e60699ea0323b8a12095e4a032dccde1de5942a (diff)
parent851bdd45711400f83e8c1d8be421735f183d7c1d (diff)
Merge "[Multi-User] Make JobScheduler logic multi-user aware"
-rw-r--r--services/backup/java/com/android/server/backup/BackupManagerService.java11
-rw-r--r--services/backup/java/com/android/server/backup/FullBackupJob.java70
-rw-r--r--services/backup/java/com/android/server/backup/JobIdManager.java30
-rw-r--r--services/backup/java/com/android/server/backup/KeyValueBackupJob.java76
-rw-r--r--services/backup/java/com/android/server/backup/Trampoline.java8
-rw-r--r--services/backup/java/com/android/server/backup/UserBackupManagerService.java23
-rw-r--r--services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java2
-rw-r--r--services/backup/java/com/android/server/backup/internal/SetupObserver.java3
-rw-r--r--services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java2
-rw-r--r--services/robotests/backup/src/com/android/server/backup/BackupManagerServiceTest.java8
-rw-r--r--services/robotests/backup/src/com/android/server/backup/FullBackupJobTest.java110
-rw-r--r--services/robotests/backup/src/com/android/server/backup/JobIdManagerTest.java69
-rw-r--r--services/robotests/backup/src/com/android/server/backup/KeyValueBackupJobTest.java51
-rw-r--r--services/robotests/backup/src/com/android/server/backup/UserBackupManagerServiceTest.java5
-rw-r--r--services/robotests/backup/src/com/android/server/backup/internal/SetupObserverTest.java2
-rw-r--r--services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java2
-rw-r--r--services/robotests/src/com/android/server/testing/shadows/ShadowKeyValueBackupJob.java3
-rw-r--r--services/tests/servicestests/src/com/android/server/backup/TrampolineTest.java14
18 files changed, 407 insertions, 82 deletions
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index 12db4f37da58..ff378b373775 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -149,6 +149,9 @@ public class BackupManagerService {
if (userBackupManagerService != null) {
userBackupManagerService.tearDownService();
+
+ KeyValueBackupJob.cancel(userId, mContext);
+ FullBackupJob.cancel(userId, mContext);
}
}
@@ -577,9 +580,9 @@ public class BackupManagerService {
* @return Whether ongoing work will continue. The return value here will be passed along as the
* return value to the callback {@link JobService#onStartJob(JobParameters)}.
*/
- public boolean beginFullBackup(FullBackupJob scheduledJob) {
+ public boolean beginFullBackup(@UserIdInt int userId, FullBackupJob scheduledJob) {
UserBackupManagerService userBackupManagerService =
- getServiceForUserIfCallerHasPermission(UserHandle.USER_SYSTEM, "beginFullBackup()");
+ getServiceForUserIfCallerHasPermission(userId, "beginFullBackup()");
return userBackupManagerService != null
&& userBackupManagerService.beginFullBackup(scheduledJob);
@@ -589,9 +592,9 @@ public class BackupManagerService {
* Used by the {@link JobScheduler} to end the current full backup task when conditions are no
* longer met for running the full backup job.
*/
- public void endFullBackup() {
+ public void endFullBackup(@UserIdInt int userId) {
UserBackupManagerService userBackupManagerService =
- getServiceForUserIfCallerHasPermission(UserHandle.USER_SYSTEM, "endFullBackup()");
+ getServiceForUserIfCallerHasPermission(userId, "endFullBackup()");
if (userBackupManagerService != null) {
userBackupManagerService.endFullBackup();
diff --git a/services/backup/java/com/android/server/backup/FullBackupJob.java b/services/backup/java/com/android/server/backup/FullBackupJob.java
index 5708b1c36822..33d21cb0bf91 100644
--- a/services/backup/java/com/android/server/backup/FullBackupJob.java
+++ b/services/backup/java/com/android/server/backup/FullBackupJob.java
@@ -22,18 +22,30 @@ import android.app.job.JobScheduler;
import android.app.job.JobService;
import android.content.ComponentName;
import android.content.Context;
+import android.os.Bundle;
+import android.util.SparseArray;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
public class FullBackupJob extends JobService {
+ private static final String USER_ID_EXTRA_KEY = "userId";
+
+ @VisibleForTesting
+ static final int MIN_JOB_ID = 52418896;
+ @VisibleForTesting
+ static final int MAX_JOB_ID = 52419896;
+
private static ComponentName sIdleService =
new ComponentName("android", FullBackupJob.class.getName());
- private static final int JOB_ID = 0x5038;
-
- private JobParameters mParams;
+ @GuardedBy("mParamsForUser")
+ private final SparseArray<JobParameters> mParamsForUser = new SparseArray<>();
- public static void schedule(Context ctx, long minDelay, BackupManagerConstants constants) {
+ public static void schedule(int userId, Context ctx, long minDelay,
+ BackupManagerConstants constants) {
JobScheduler js = (JobScheduler) ctx.getSystemService(Context.JOB_SCHEDULER_SERVICE);
- JobInfo.Builder builder = new JobInfo.Builder(JOB_ID, sIdleService);
+ JobInfo.Builder builder = new JobInfo.Builder(getJobIdForUserId(userId), sIdleService);
synchronized (constants) {
builder.setRequiresDeviceIdle(true)
.setRequiredNetworkType(constants.getFullBackupRequiredNetworkType())
@@ -42,14 +54,28 @@ public class FullBackupJob extends JobService {
if (minDelay > 0) {
builder.setMinimumLatency(minDelay);
}
+
+ Bundle extraInfo = new Bundle();
+ extraInfo.putInt(USER_ID_EXTRA_KEY, userId);
+ builder.setTransientExtras(extraInfo);
+
js.schedule(builder.build());
}
+ public static void cancel(int userId, Context ctx) {
+ JobScheduler js = (JobScheduler) ctx.getSystemService(
+ Context.JOB_SCHEDULER_SERVICE);
+ js.cancel(getJobIdForUserId(userId));
+ }
+
// callback from the Backup Manager Service: it's finished its work for this pass
- public void finishBackupPass() {
- if (mParams != null) {
- jobFinished(mParams, false);
- mParams = null;
+ public void finishBackupPass(int userId) {
+ synchronized (mParamsForUser) {
+ JobParameters jobParameters = mParamsForUser.get(userId);
+ if (jobParameters != null) {
+ jobFinished(jobParameters, false);
+ mParamsForUser.remove(userId);
+ }
}
}
@@ -57,19 +83,33 @@ public class FullBackupJob extends JobService {
@Override
public boolean onStartJob(JobParameters params) {
- mParams = params;
+ int userId = params.getTransientExtras().getInt(USER_ID_EXTRA_KEY);
+
+ synchronized (mParamsForUser) {
+ mParamsForUser.put(userId, params);
+ }
+
Trampoline service = BackupManagerService.getInstance();
- return service.beginFullBackup(this);
+ return service.beginFullBackup(userId, this);
}
@Override
public boolean onStopJob(JobParameters params) {
- if (mParams != null) {
- mParams = null;
- Trampoline service = BackupManagerService.getInstance();
- service.endFullBackup();
+ int userId = params.getTransientExtras().getInt(USER_ID_EXTRA_KEY);
+
+ synchronized (mParamsForUser) {
+ if (mParamsForUser.removeReturnOld(userId) == null) {
+ return false;
+ }
}
+
+ Trampoline service = BackupManagerService.getInstance();
+ service.endFullBackup(userId);
+
return false;
}
+ private static int getJobIdForUserId(int userId) {
+ return JobIdManager.getJobIdForUserId(MIN_JOB_ID, MAX_JOB_ID, userId);
+ }
}
diff --git a/services/backup/java/com/android/server/backup/JobIdManager.java b/services/backup/java/com/android/server/backup/JobIdManager.java
new file mode 100644
index 000000000000..2e834dbfe245
--- /dev/null
+++ b/services/backup/java/com/android/server/backup/JobIdManager.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup;
+
+/**
+ * Allocates job IDs for {@link FullBackupJob} and {@link KeyValueBackupJob}
+ */
+public class JobIdManager {
+ public static int getJobIdForUserId(int minJobId, int maxJobId, int userId) {
+ if (minJobId + userId > maxJobId) {
+ throw new RuntimeException("No job IDs available in the given range");
+ }
+
+ return minJobId + userId;
+ }
+}
diff --git a/services/backup/java/com/android/server/backup/KeyValueBackupJob.java b/services/backup/java/com/android/server/backup/KeyValueBackupJob.java
index f2e74352b004..d43859ebeeda 100644
--- a/services/backup/java/com/android/server/backup/KeyValueBackupJob.java
+++ b/services/backup/java/com/android/server/backup/KeyValueBackupJob.java
@@ -25,8 +25,14 @@ import android.app.job.JobScheduler;
import android.app.job.JobService;
import android.content.ComponentName;
import android.content.Context;
+import android.os.Bundle;
import android.os.RemoteException;
import android.util.Slog;
+import android.util.SparseBooleanArray;
+import android.util.SparseLongArray;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
import java.util.Random;
@@ -38,7 +44,8 @@ public class KeyValueBackupJob extends JobService {
private static final String TAG = "KeyValueBackupJob";
private static ComponentName sKeyValueJobService =
new ComponentName("android", KeyValueBackupJob.class.getName());
- private static final int JOB_ID = 0x5039;
+
+ private static final String USER_ID_EXTRA_KEY = "userId";
// Once someone asks for a backup, this is how long we hold off until we find
// an on-charging opportunity. If we hit this max latency we will run the operation
@@ -46,16 +53,22 @@ public class KeyValueBackupJob extends JobService {
// BackupManager.backupNow().
private static final long MAX_DEFERRAL = AlarmManager.INTERVAL_DAY;
- private static boolean sScheduled = false;
- private static long sNextScheduled = 0;
+ @GuardedBy("KeyValueBackupJob.class")
+ private static final SparseBooleanArray sScheduledForUserId = new SparseBooleanArray();
+ @GuardedBy("KeyValueBackupJob.class")
+ private static final SparseLongArray sNextScheduledForUserId = new SparseLongArray();
+
+ private static final int MIN_JOB_ID = 52417896;
+ private static final int MAX_JOB_ID = 52418896;
- public static void schedule(Context ctx, BackupManagerConstants constants) {
- schedule(ctx, 0, constants);
+ public static void schedule(int userId, Context ctx, BackupManagerConstants constants) {
+ schedule(userId, ctx, 0, constants);
}
- public static void schedule(Context ctx, long delay, BackupManagerConstants constants) {
+ public static void schedule(int userId, Context ctx, long delay,
+ BackupManagerConstants constants) {
synchronized (KeyValueBackupJob.class) {
- if (sScheduled) {
+ if (sScheduledForUserId.get(userId)) {
return;
}
@@ -76,51 +89,61 @@ public class KeyValueBackupJob extends JobService {
if (DEBUG_SCHEDULING) {
Slog.v(TAG, "Scheduling k/v pass in " + (delay / 1000 / 60) + " minutes");
}
- JobInfo.Builder builder = new JobInfo.Builder(JOB_ID, sKeyValueJobService)
+
+ JobInfo.Builder builder = new JobInfo.Builder(getJobIdForUserId(userId),
+ sKeyValueJobService)
.setMinimumLatency(delay)
.setRequiredNetworkType(networkType)
.setRequiresCharging(needsCharging)
.setOverrideDeadline(MAX_DEFERRAL);
+
+ Bundle extraInfo = new Bundle();
+ extraInfo.putInt(USER_ID_EXTRA_KEY, userId);
+ builder.setTransientExtras(extraInfo);
+
JobScheduler js = (JobScheduler) ctx.getSystemService(Context.JOB_SCHEDULER_SERVICE);
js.schedule(builder.build());
- sNextScheduled = System.currentTimeMillis() + delay;
- sScheduled = true;
+ sScheduledForUserId.put(userId, true);
+ sNextScheduledForUserId.put(userId, System.currentTimeMillis() + delay);
}
}
- public static void cancel(Context ctx) {
+ public static void cancel(int userId, Context ctx) {
synchronized (KeyValueBackupJob.class) {
- JobScheduler js = (JobScheduler) ctx.getSystemService(Context.JOB_SCHEDULER_SERVICE);
- js.cancel(JOB_ID);
- sNextScheduled = 0;
- sScheduled = false;
+ JobScheduler js = (JobScheduler) ctx.getSystemService(
+ Context.JOB_SCHEDULER_SERVICE);
+ js.cancel(getJobIdForUserId(userId));
+
+ clearScheduledForUserId(userId);
}
}
- public static long nextScheduled() {
+ public static long nextScheduled(int userId) {
synchronized (KeyValueBackupJob.class) {
- return sNextScheduled;
+ return sNextScheduledForUserId.get(userId);
}
}
- public static boolean isScheduled() {
+ @VisibleForTesting
+ public static boolean isScheduled(int userId) {
synchronized (KeyValueBackupJob.class) {
- return sScheduled;
+ return sScheduledForUserId.get(userId);
}
}
@Override
public boolean onStartJob(JobParameters params) {
+ int userId = params.getTransientExtras().getInt(USER_ID_EXTRA_KEY);
+
synchronized (KeyValueBackupJob.class) {
- sNextScheduled = 0;
- sScheduled = false;
+ clearScheduledForUserId(userId);
}
// Time to run a key/value backup!
Trampoline service = BackupManagerService.getInstance();
try {
- service.backupNow();
+ service.backupNowForUser(userId);
} catch (RemoteException e) {}
// This was just a trigger; ongoing wakelock management is done by the
@@ -134,4 +157,13 @@ public class KeyValueBackupJob extends JobService {
return false;
}
+ @GuardedBy("KeyValueBackupJob.class")
+ private static void clearScheduledForUserId(int userId) {
+ sScheduledForUserId.delete(userId);
+ sNextScheduledForUserId.delete(userId);
+ }
+
+ private static int getJobIdForUserId(int userId) {
+ return JobIdManager.getJobIdForUserId(MIN_JOB_ID, MAX_JOB_ID, userId);
+ }
}
diff --git a/services/backup/java/com/android/server/backup/Trampoline.java b/services/backup/java/com/android/server/backup/Trampoline.java
index 79d4a2cb33de..4ca2545802c7 100644
--- a/services/backup/java/com/android/server/backup/Trampoline.java
+++ b/services/backup/java/com/android/server/backup/Trampoline.java
@@ -698,15 +698,15 @@ public class Trampoline extends IBackupManager.Stub {
// Full backup/restore entry points - non-Binder; called directly
// by the full-backup scheduled job
- /* package */ boolean beginFullBackup(FullBackupJob scheduledJob) {
+ /* package */ boolean beginFullBackup(@UserIdInt int userId, FullBackupJob scheduledJob) {
BackupManagerService svc = mService;
- return (svc != null) ? svc.beginFullBackup(scheduledJob) : false;
+ return (svc != null) ? svc.beginFullBackup(userId, scheduledJob) : false;
}
- /* package */ void endFullBackup() {
+ /* package */ void endFullBackup(@UserIdInt int userId) {
BackupManagerService svc = mService;
if (svc != null) {
- svc.endFullBackup();
+ svc.endFullBackup(userId);
}
}
}
diff --git a/services/backup/java/com/android/server/backup/UserBackupManagerService.java b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
index e07578570b79..ed6a46cc58aa 100644
--- a/services/backup/java/com/android/server/backup/UserBackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
@@ -1674,8 +1674,8 @@ public class UserBackupManagerService {
}
// We don't want the backup jobs to kick in any time soon.
// Reschedules them to run in the distant future.
- KeyValueBackupJob.schedule(mContext, BUSY_BACKOFF_MIN_MILLIS, mConstants);
- FullBackupJob.schedule(mContext, 2 * BUSY_BACKOFF_MIN_MILLIS, mConstants);
+ KeyValueBackupJob.schedule(mUserId, mContext, BUSY_BACKOFF_MIN_MILLIS, mConstants);
+ FullBackupJob.schedule(mUserId, mContext, 2 * BUSY_BACKOFF_MIN_MILLIS, mConstants);
} finally {
Binder.restoreCallingIdentity(oldToken);
}
@@ -1910,7 +1910,7 @@ public class UserBackupManagerService {
Runnable r = new Runnable() {
@Override
public void run() {
- FullBackupJob.schedule(mContext, latency, mConstants);
+ FullBackupJob.schedule(mUserId, mContext, latency, mConstants);
}
};
mBackupHandler.postDelayed(r, 2500);
@@ -2033,7 +2033,7 @@ public class UserBackupManagerService {
mPowerManager.getPowerSaveState(ServiceType.FULL_BACKUP);
if (result.batterySaverEnabled) {
if (DEBUG) Slog.i(TAG, "Deferring scheduled full backups in battery saver mode");
- FullBackupJob.schedule(mContext, keyValueBackupInterval, mConstants);
+ FullBackupJob.schedule(mUserId, mContext, keyValueBackupInterval, mConstants);
return false;
}
@@ -2147,7 +2147,7 @@ public class UserBackupManagerService {
mBackupHandler.post(new Runnable() {
@Override
public void run() {
- FullBackupJob.schedule(mContext, deferTime, mConstants);
+ FullBackupJob.schedule(mUserId, mContext, deferTime, mConstants);
}
});
return false;
@@ -2251,7 +2251,7 @@ public class UserBackupManagerService {
}
// ...and schedule a backup pass if necessary
- KeyValueBackupJob.schedule(mContext, mConstants);
+ KeyValueBackupJob.schedule(mUserId, mContext, mConstants);
}
// Note: packageName is currently unused, but may be in the future
@@ -2401,7 +2401,8 @@ public class UserBackupManagerService {
mPowerManager.getPowerSaveState(ServiceType.KEYVALUE_BACKUP);
if (result.batterySaverEnabled) {
if (DEBUG) Slog.v(TAG, "Not running backup while in battery save mode");
- KeyValueBackupJob.schedule(mContext, mConstants); // try again in several hours
+ // Try again in several hours.
+ KeyValueBackupJob.schedule(mUserId, mContext, mConstants);
} else {
if (DEBUG) Slog.v(TAG, "Scheduling immediate backup pass");
synchronized (mQueueLock) {
@@ -2414,7 +2415,7 @@ public class UserBackupManagerService {
}
// ...and cancel any pending scheduled job, because we've just superseded it
- KeyValueBackupJob.cancel(mContext);
+ KeyValueBackupJob.cancel(mUserId, mContext);
}
}
} finally {
@@ -2737,13 +2738,13 @@ public class UserBackupManagerService {
synchronized (mQueueLock) {
if (enable && !wasEnabled && mSetupComplete) {
// if we've just been enabled, start scheduling backup passes
- KeyValueBackupJob.schedule(mContext, mConstants);
+ KeyValueBackupJob.schedule(mUserId, mContext, mConstants);
scheduleNextFullBackupJob(0);
} else if (!enable) {
// No longer enabled, so stop running backups
if (MORE_DEBUG) Slog.i(TAG, "Opting out of backup");
- KeyValueBackupJob.cancel(mContext);
+ KeyValueBackupJob.cancel(mUserId, mContext);
// This also constitutes an opt-out, so we wipe any data for
// this device from the backend. We start that process with
@@ -3451,7 +3452,7 @@ public class UserBackupManagerService {
pw.println(isBackupOperationInProgress() ? "Backup in progress" : "No backups running");
pw.println("Last backup pass started: " + mLastBackupPass
+ " (now = " + System.currentTimeMillis() + ')');
- pw.println(" next scheduled: " + KeyValueBackupJob.nextScheduled());
+ pw.println(" next scheduled: " + KeyValueBackupJob.nextScheduled(mUserId));
pw.println("Transport whitelist:");
for (ComponentName transport : mTransportManager.getTransportWhitelist()) {
diff --git a/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java b/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java
index 2ee96d190f90..0fb4f93e542b 100644
--- a/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java
+++ b/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java
@@ -633,7 +633,7 @@ public class PerformFullTransportBackupTask extends FullBackupTask implements Ba
unregisterTask();
if (mJob != null) {
- mJob.finishBackupPass();
+ mJob.finishBackupPass(backupManagerService.getUserId());
}
synchronized (backupManagerService.getQueueLock()) {
diff --git a/services/backup/java/com/android/server/backup/internal/SetupObserver.java b/services/backup/java/com/android/server/backup/internal/SetupObserver.java
index 62ae3eb8e192..c5e912e6d18b 100644
--- a/services/backup/java/com/android/server/backup/internal/SetupObserver.java
+++ b/services/backup/java/com/android/server/backup/internal/SetupObserver.java
@@ -77,7 +77,8 @@ public class SetupObserver extends ContentObserver {
if (MORE_DEBUG) {
Slog.d(TAG, "Setup complete so starting backups");
}
- KeyValueBackupJob.schedule(mContext, mUserBackupManagerService.getConstants());
+ KeyValueBackupJob.schedule(mUserBackupManagerService.getUserId(), mContext,
+ mUserBackupManagerService.getConstants());
mUserBackupManagerService.scheduleNextFullBackupJob(0);
}
}
diff --git a/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java b/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java
index f39d795735f3..ef7ff9270a66 100644
--- a/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java
+++ b/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java
@@ -1003,7 +1003,7 @@ public class KeyValueBackupTask implements BackupRestoreTask, Runnable {
// Use the scheduler's default.
delay = 0;
}
- KeyValueBackupJob.schedule(
+ KeyValueBackupJob.schedule(mBackupManagerService.getUserId(),
mBackupManagerService.getContext(), delay, mBackupManagerService.getConstants());
for (String packageName : mOriginalQueue) {
diff --git a/services/robotests/backup/src/com/android/server/backup/BackupManagerServiceTest.java b/services/robotests/backup/src/com/android/server/backup/BackupManagerServiceTest.java
index 769a9d4a2bd4..0d3b6b2afd2c 100644
--- a/services/robotests/backup/src/com/android/server/backup/BackupManagerServiceTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/BackupManagerServiceTest.java
@@ -1075,7 +1075,7 @@ public class BackupManagerServiceTest {
createServiceAndRegisterUser(UserHandle.USER_SYSTEM, mUserOneService);
FullBackupJob job = new FullBackupJob();
- backupManagerService.beginFullBackup(job);
+ backupManagerService.beginFullBackup(UserHandle.USER_SYSTEM, job);
verify(mUserOneService).beginFullBackup(job);
}
@@ -1086,7 +1086,7 @@ public class BackupManagerServiceTest {
BackupManagerService backupManagerService = createService();
FullBackupJob job = new FullBackupJob();
- backupManagerService.beginFullBackup(job);
+ backupManagerService.beginFullBackup(UserHandle.USER_SYSTEM, job);
verify(mUserOneService, never()).beginFullBackup(job);
}
@@ -1097,7 +1097,7 @@ public class BackupManagerServiceTest {
BackupManagerService backupManagerService =
createServiceAndRegisterUser(UserHandle.USER_SYSTEM, mUserOneService);
- backupManagerService.endFullBackup();
+ backupManagerService.endFullBackup(UserHandle.USER_SYSTEM);
verify(mUserOneService).endFullBackup();
}
@@ -1107,7 +1107,7 @@ public class BackupManagerServiceTest {
public void testEndFullBackup_onUnknownUser_doesNotPropagateCall() throws Exception {
BackupManagerService backupManagerService = createService();
- backupManagerService.endFullBackup();
+ backupManagerService.endFullBackup(UserHandle.USER_SYSTEM);
verify(mUserOneService, never()).endFullBackup();
}
diff --git a/services/robotests/backup/src/com/android/server/backup/FullBackupJobTest.java b/services/robotests/backup/src/com/android/server/backup/FullBackupJobTest.java
new file mode 100644
index 000000000000..9a78d0b3f456
--- /dev/null
+++ b/services/robotests/backup/src/com/android/server/backup/FullBackupJobTest.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.backup;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.annotation.UserIdInt;
+import android.app.job.JobScheduler;
+import android.content.Context;
+import android.os.Handler;
+import android.os.UserHandle;
+import android.platform.test.annotations.Presubmit;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.Shadows;
+import org.robolectric.annotation.Config;
+import org.robolectric.shadows.ShadowJobScheduler;
+
+@RunWith(RobolectricTestRunner.class)
+@Config(shadows = {ShadowJobScheduler.class})
+@Presubmit
+public class FullBackupJobTest {
+ private Context mContext;
+ private BackupManagerConstants mConstants;
+ private ShadowJobScheduler mShadowJobScheduler;
+
+ @UserIdInt private int mUserOneId;
+ @UserIdInt private int mUserTwoId;
+
+ @Before
+ public void setUp() throws Exception {
+ mContext = RuntimeEnvironment.application;
+ mConstants = new BackupManagerConstants(Handler.getMain(), mContext.getContentResolver());
+ mConstants.start();
+
+ mShadowJobScheduler = Shadows.shadowOf(mContext.getSystemService(JobScheduler.class));
+
+ mUserOneId = UserHandle.USER_SYSTEM;
+ mUserTwoId = mUserOneId + 1;
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ mConstants.stop();
+ FullBackupJob.cancel(mUserOneId, mContext);
+ FullBackupJob.cancel(mUserTwoId, mContext);
+ }
+
+ @Test
+ public void testSchedule_afterScheduling_jobExists() {
+ FullBackupJob.schedule(mUserOneId, mContext, 0, mConstants);
+ FullBackupJob.schedule(mUserTwoId, mContext, 0, mConstants);
+
+ assertThat(mShadowJobScheduler.getPendingJob(getJobIdForUserId(mUserOneId))).isNotNull();
+ assertThat(mShadowJobScheduler.getPendingJob(getJobIdForUserId(mUserTwoId))).isNotNull();
+ }
+
+ @Test
+ public void testCancel_afterCancelling_jobDoesntExist() {
+ FullBackupJob.schedule(mUserOneId, mContext, 0, mConstants);
+ FullBackupJob.schedule(mUserTwoId, mContext, 0, mConstants);
+ FullBackupJob.cancel(mUserOneId, mContext);
+ FullBackupJob.cancel(mUserTwoId, mContext);
+
+ assertThat(mShadowJobScheduler.getPendingJob(getJobIdForUserId(mUserOneId))).isNull();
+ assertThat(mShadowJobScheduler.getPendingJob(getJobIdForUserId(mUserTwoId))).isNull();
+ }
+//
+ @Test
+ public void testSchedule_onlySchedulesForRequestedUser() {
+ FullBackupJob.schedule(mUserOneId, mContext, 0, mConstants);
+
+ assertThat(mShadowJobScheduler.getPendingJob(getJobIdForUserId(mUserOneId))).isNotNull();
+ assertThat(mShadowJobScheduler.getPendingJob(getJobIdForUserId(mUserTwoId))).isNull();
+ }
+//
+ @Test
+ public void testCancel_onlyCancelsForRequestedUser() {
+ FullBackupJob.schedule(mUserOneId, mContext, 0, mConstants);
+ FullBackupJob.schedule(mUserTwoId, mContext, 0, mConstants);
+ FullBackupJob.cancel(mUserOneId, mContext);
+
+ assertThat(mShadowJobScheduler.getPendingJob(getJobIdForUserId(mUserOneId))).isNull();
+ assertThat(mShadowJobScheduler.getPendingJob(getJobIdForUserId(mUserTwoId))).isNotNull();
+ }
+
+ private static int getJobIdForUserId(int userId) {
+ return JobIdManager.getJobIdForUserId(FullBackupJob.MIN_JOB_ID, FullBackupJob.MAX_JOB_ID,
+ userId);
+ }
+}
diff --git a/services/robotests/backup/src/com/android/server/backup/JobIdManagerTest.java b/services/robotests/backup/src/com/android/server/backup/JobIdManagerTest.java
new file mode 100644
index 000000000000..f8bb1ee60b80
--- /dev/null
+++ b/services/robotests/backup/src/com/android/server/backup/JobIdManagerTest.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.backup;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.annotation.UserIdInt;
+import android.os.UserHandle;
+import android.platform.test.annotations.Presubmit;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import static org.testng.Assert.expectThrows;
+
+@RunWith(RobolectricTestRunner.class)
+@Presubmit
+public class JobIdManagerTest {
+ private static final int MIN_JOB_ID = 10;
+ private static final int MAX_JOB_ID = 20;
+
+ @UserIdInt private int mUserOneId;
+ @UserIdInt private int mUserTwoId;
+
+ @Before
+ public void setUp() {
+ mUserOneId = UserHandle.USER_SYSTEM;
+ mUserTwoId = mUserOneId + 1;
+ }
+
+ @Test
+ public void testGetJobIdForUserId_returnsDifferentJobIdsForDifferentUsers() {
+ int jobIdOne = JobIdManager.getJobIdForUserId(MIN_JOB_ID, MAX_JOB_ID, mUserOneId);
+ int jobIdTwo = JobIdManager.getJobIdForUserId(MIN_JOB_ID, MAX_JOB_ID, mUserTwoId);
+
+ assertThat(jobIdOne).isNotEqualTo(jobIdTwo);
+ }
+
+ @Test
+ public void testGetJobIdForUserId_returnsSameJobIdForSameUser() {
+ int jobIdOne = JobIdManager.getJobIdForUserId(MIN_JOB_ID, MAX_JOB_ID, mUserOneId);
+ int jobIdTwo = JobIdManager.getJobIdForUserId(MIN_JOB_ID, MAX_JOB_ID, mUserOneId);
+
+ assertThat(jobIdOne).isEqualTo(jobIdTwo);
+ }
+
+ @Test
+ public void testGetJobIdForUserId_throwsExceptionIfRangeIsExceeded() {
+ expectThrows(
+ RuntimeException.class,
+ () -> JobIdManager.getJobIdForUserId(MIN_JOB_ID, MAX_JOB_ID,
+ MAX_JOB_ID + 1));
+ }
+}
diff --git a/services/robotests/backup/src/com/android/server/backup/KeyValueBackupJobTest.java b/services/robotests/backup/src/com/android/server/backup/KeyValueBackupJobTest.java
index 8e17209e1f50..8d9e44fbf1ad 100644
--- a/services/robotests/backup/src/com/android/server/backup/KeyValueBackupJobTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/KeyValueBackupJobTest.java
@@ -18,8 +18,10 @@ package com.android.server.backup;
import static com.google.common.truth.Truth.assertThat;
+import android.annotation.UserIdInt;
import android.content.Context;
import android.os.Handler;
+import android.os.UserHandle;
import android.platform.test.annotations.Presubmit;
import org.junit.After;
@@ -35,32 +37,67 @@ public class KeyValueBackupJobTest {
private Context mContext;
private BackupManagerConstants mConstants;
+ @UserIdInt private int mUserOneId;
+ @UserIdInt private int mUserTwoId;
+
@Before
public void setUp() throws Exception {
mContext = RuntimeEnvironment.application;
mConstants = new BackupManagerConstants(Handler.getMain(), mContext.getContentResolver());
mConstants.start();
+
+ mUserOneId = UserHandle.USER_SYSTEM;
+ mUserTwoId = mUserOneId + 1;
}
@After
public void tearDown() throws Exception {
mConstants.stop();
- KeyValueBackupJob.cancel(mContext);
+ KeyValueBackupJob.cancel(mUserOneId, mContext);
+ KeyValueBackupJob.cancel(mUserTwoId, mContext);
}
@Test
public void testIsScheduled_beforeScheduling_returnsFalse() {
- boolean isScheduled = KeyValueBackupJob.isScheduled();
-
- assertThat(isScheduled).isFalse();
+ assertThat(KeyValueBackupJob.isScheduled(mUserOneId)).isFalse();
+ assertThat(KeyValueBackupJob.isScheduled(mUserTwoId)).isFalse();
}
@Test
public void testIsScheduled_afterScheduling_returnsTrue() {
- KeyValueBackupJob.schedule(mContext, mConstants);
+ KeyValueBackupJob.schedule(mUserOneId, mContext, mConstants);
+ KeyValueBackupJob.schedule(mUserTwoId, mContext, mConstants);
+
+ assertThat(KeyValueBackupJob.isScheduled(mUserOneId)).isTrue();
+ assertThat(KeyValueBackupJob.isScheduled(mUserTwoId)).isTrue();
+ }
+
+ @Test
+ public void testIsScheduled_afterCancelling_returnsFalse() {
+ KeyValueBackupJob.schedule(mUserOneId, mContext, mConstants);
+ KeyValueBackupJob.schedule(mUserTwoId, mContext, mConstants);
+ KeyValueBackupJob.cancel(mUserOneId, mContext);
+ KeyValueBackupJob.cancel(mUserTwoId, mContext);
- boolean isScheduled = KeyValueBackupJob.isScheduled();
+ assertThat(KeyValueBackupJob.isScheduled(mUserOneId)).isFalse();
+ assertThat(KeyValueBackupJob.isScheduled(mUserTwoId)).isFalse();
+ }
+
+ @Test
+ public void testIsScheduled_afterScheduling_returnsTrueOnlyForScheduledUser() {
+ KeyValueBackupJob.schedule(mUserOneId, mContext, mConstants);
+
+ assertThat(KeyValueBackupJob.isScheduled(mUserOneId)).isTrue();
+ assertThat(KeyValueBackupJob.isScheduled(mUserTwoId)).isFalse();
+ }
+
+ @Test
+ public void testIsScheduled_afterCancelling_returnsFalseOnlyForCancelledUser() {
+ KeyValueBackupJob.schedule(mUserOneId, mContext, mConstants);
+ KeyValueBackupJob.schedule(mUserTwoId, mContext, mConstants);
+ KeyValueBackupJob.cancel(mUserOneId, mContext);
- assertThat(isScheduled).isTrue();
+ assertThat(KeyValueBackupJob.isScheduled(mUserOneId)).isFalse();
+ assertThat(KeyValueBackupJob.isScheduled(mUserTwoId)).isTrue();
}
}
diff --git a/services/robotests/backup/src/com/android/server/backup/UserBackupManagerServiceTest.java b/services/robotests/backup/src/com/android/server/backup/UserBackupManagerServiceTest.java
index 8d5c301d93e7..496817613216 100644
--- a/services/robotests/backup/src/com/android/server/backup/UserBackupManagerServiceTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/UserBackupManagerServiceTest.java
@@ -1137,8 +1137,9 @@ public class UserBackupManagerServiceTest {
* Implementation of {@link ShadowKeyValueBackupJob#schedule(Context, long,
* BackupManagerConstants)} that throws an {@link IllegalArgumentException}.
*/
- public static void schedule(Context ctx, long delay, BackupManagerConstants constants) {
- ShadowKeyValueBackupJob.schedule(ctx, delay, constants);
+ public static void schedule(int userId, Context ctx, long delay,
+ BackupManagerConstants constants) {
+ ShadowKeyValueBackupJob.schedule(userId, ctx, delay, constants);
throw new IllegalArgumentException();
}
}
diff --git a/services/robotests/backup/src/com/android/server/backup/internal/SetupObserverTest.java b/services/robotests/backup/src/com/android/server/backup/internal/SetupObserverTest.java
index b754356ca0cb..b08ae6d5756e 100644
--- a/services/robotests/backup/src/com/android/server/backup/internal/SetupObserverTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/internal/SetupObserverTest.java
@@ -125,7 +125,7 @@ public class SetupObserverTest {
setupObserver.onChange(true);
- assertThat(KeyValueBackupJob.isScheduled()).isTrue();
+ assertThat(KeyValueBackupJob.isScheduled(mUserBackupManagerService.getUserId())).isTrue();
// Verifies that the full backup job is scheduled. The job is scheduled via a posted message
// on the backup handler so we verify that a message exists.
assertThat(mUserBackupManagerService.getBackupHandler().hasMessagesOrCallbacks()).isTrue();
diff --git a/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java b/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java
index 7dac79599ac6..b923bb09a423 100644
--- a/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java
@@ -2727,7 +2727,7 @@ public class KeyValueBackupTaskTest {
throws RemoteException, IOException {
verify(transportMock.transport).requestBackupTime();
assertBackupPendingFor(packages);
- assertThat(KeyValueBackupJob.isScheduled()).isTrue();
+ assertThat(KeyValueBackupJob.isScheduled(mBackupManagerService.getUserId())).isTrue();
}
private void assertBackupPendingFor(PackageData... packages) throws IOException {
diff --git a/services/robotests/src/com/android/server/testing/shadows/ShadowKeyValueBackupJob.java b/services/robotests/src/com/android/server/testing/shadows/ShadowKeyValueBackupJob.java
index 23c44b0e8c83..f90ea6aff41b 100644
--- a/services/robotests/src/com/android/server/testing/shadows/ShadowKeyValueBackupJob.java
+++ b/services/robotests/src/com/android/server/testing/shadows/ShadowKeyValueBackupJob.java
@@ -34,7 +34,8 @@ public class ShadowKeyValueBackupJob {
}
@Implementation
- protected static void schedule(Context ctx, long delay, BackupManagerConstants constants) {
+ protected static void schedule(int userId, Context ctx, long delay,
+ BackupManagerConstants constants) {
callingUid = Binder.getCallingUid();
}
}
diff --git a/services/tests/servicestests/src/com/android/server/backup/TrampolineTest.java b/services/tests/servicestests/src/com/android/server/backup/TrampolineTest.java
index db83505900c5..6a9a12133e70 100644
--- a/services/tests/servicestests/src/com/android/server/backup/TrampolineTest.java
+++ b/services/tests/servicestests/src/com/android/server/backup/TrampolineTest.java
@@ -1149,31 +1149,31 @@ public class TrampolineTest {
@Test
public void beginFullBackup_calledBeforeInitialize_ignored() throws RemoteException {
- mTrampoline.beginFullBackup(new FullBackupJob());
+ mTrampoline.beginFullBackup(mUserId, new FullBackupJob());
verifyNoMoreInteractions(mBackupManagerServiceMock);
}
@Test
public void beginFullBackup_forwarded() throws RemoteException {
FullBackupJob fullBackupJob = new FullBackupJob();
- when(mBackupManagerServiceMock.beginFullBackup(fullBackupJob)).thenReturn(true);
+ when(mBackupManagerServiceMock.beginFullBackup(mUserId, fullBackupJob)).thenReturn(true);
mTrampoline.initializeService();
- assertTrue(mTrampoline.beginFullBackup(fullBackupJob));
- verify(mBackupManagerServiceMock).beginFullBackup(fullBackupJob);
+ assertTrue(mTrampoline.beginFullBackup(mUserId, fullBackupJob));
+ verify(mBackupManagerServiceMock).beginFullBackup(mUserId, fullBackupJob);
}
@Test
public void endFullBackup_calledBeforeInitialize_ignored() throws RemoteException {
- mTrampoline.endFullBackup();
+ mTrampoline.endFullBackup(mUserId);
verifyNoMoreInteractions(mBackupManagerServiceMock);
}
@Test
public void endFullBackup_forwarded() throws RemoteException {
mTrampoline.initializeService();
- mTrampoline.endFullBackup();
- verify(mBackupManagerServiceMock).endFullBackup();
+ mTrampoline.endFullBackup(mUserId);
+ verify(mBackupManagerServiceMock).endFullBackup(mUserId);
}
@Test