diff --git a/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java b/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java index ed717c4914676..699808156033d 100644 --- a/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java +++ b/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java @@ -195,10 +195,35 @@ public class Bmgr { return; } + if ("scheduling".equals(op)) { + setSchedulingEnabled(userId); + return; + } + System.err.println("Unknown command"); showUsage(); } + private void setSchedulingEnabled(int userId) { + String arg = nextArg(); + if (arg == null) { + showUsage(); + return; + } + + try { + boolean enable = Boolean.parseBoolean(arg); + mBmgr.setFrameworkSchedulingEnabledForUser(userId, enable); + System.out.println( + "Backup scheduling is now " + + (enable ? "enabled" : "disabled") + + " for user " + + userId); + } catch (RemoteException e) { + handleRemoteException(e); + } + } + private void handleRemoteException(RemoteException e) { System.err.println(e.toString()); System.err.println(BMGR_NOT_RUNNING_ERR); @@ -944,6 +969,7 @@ public class Bmgr { System.err.println(" bmgr activate BOOL"); System.err.println(" bmgr activated"); System.err.println(" bmgr autorestore BOOL"); + System.err.println(" bmgr scheduling BOOL"); System.err.println(""); System.err.println("The '--user' option specifies the user on which the operation is run."); System.err.println("It must be the first argument before the operation."); @@ -1021,6 +1047,9 @@ public class Bmgr { System.err.println(""); System.err.println("The 'autorestore' command enables or disables automatic restore when"); System.err.println("a new package is installed."); + System.err.println(""); + System.err.println("The 'scheduling' command enables or disables backup scheduling in the"); + System.err.println("framework."); } private static class BackupMonitor extends IBackupManagerMonitor.Stub { diff --git a/core/java/android/app/backup/BackupManager.java b/core/java/android/app/backup/BackupManager.java index bad282ead3d3a..ddeb2f936c7af 100644 --- a/core/java/android/app/backup/BackupManager.java +++ b/core/java/android/app/backup/BackupManager.java @@ -16,7 +16,6 @@ package android.app.backup; -import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; @@ -38,8 +37,6 @@ import android.os.UserHandle; import android.util.Log; import android.util.Pair; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; import java.util.List; /** @@ -409,6 +406,33 @@ public class BackupManager { } } + /** + * Enable/disable the framework backup scheduling entirely for the current user. When disabled, + * no Key/Value or Full backup jobs will be scheduled by the Android framework. + * + *

Note: This does not disable backups: only their scheduling is affected and backups can + * still be triggered manually. + * + *

Callers must hold the android.permission.BACKUP permission to use this method. + * + * @hide + */ + @RequiresPermission(allOf = {android.Manifest.permission.BACKUP, + android.Manifest.permission.INTERACT_ACROSS_USERS_FULL}, conditional = true) + public void setFrameworkSchedulingEnabled(boolean isEnabled) { + checkServiceBinder(); + if (sService == null) { + Log.e(TAG, "setFrameworkSchedulingEnabled() couldn't connect"); + return; + } + + try { + sService.setFrameworkSchedulingEnabledForUser(mContext.getUserId(), isEnabled); + } catch (RemoteException e) { + Log.e(TAG, "setFrameworkSchedulingEnabled() couldn't connect"); + } + } + /** * Report whether the backup mechanism is currently enabled. * diff --git a/core/java/android/app/backup/IBackupManager.aidl b/core/java/android/app/backup/IBackupManager.aidl index aeb498721fb69..041c2a7c09f42 100644 --- a/core/java/android/app/backup/IBackupManager.aidl +++ b/core/java/android/app/backup/IBackupManager.aidl @@ -155,6 +155,22 @@ interface IBackupManager { */ void setBackupEnabledForUser(int userId, boolean isEnabled); + + /** + * Enable/disable the framework backup scheduling entirely. When disabled, no Key/Value or Full + * backup jobs will be scheduled by the Android framework. + * + *

Note: This does not disable backups: only their scheduling is affected and backups can + * still be triggered manually. + * + *

Callers must hold the android.permission.BACKUP permission to use this method. If + * {@code userId} is different from the calling user id, then the caller must additionally hold + * the android.permission.INTERACT_ACROSS_USERS_FULL permission. + * + * @param userId The user for which backup scheduling should be enabled/disabled. + */ + void setFrameworkSchedulingEnabledForUser(int userId, boolean isEnabled); + /** * {@link android.app.backup.IBackupManager.setBackupEnabledForUser} for the calling user id. */ diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 76ca20cb47187..388670844f490 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -8659,6 +8659,12 @@ public final class Settings { @Readable public static final String BACKUP_AUTO_RESTORE = "backup_auto_restore"; + /** + * Controls whether framework backup scheduling is enabled. + * @hide + */ + public static final String BACKUP_SCHEDULING_ENABLED = "backup_scheduling_enabled"; + /** * Indicates whether settings backup has been fully provisioned. * Type: int ( 0 = unprovisioned, 1 = fully provisioned ) diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java index 4365a9b441b13..5ee36f373783b 100644 --- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java +++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java @@ -695,6 +695,7 @@ public class SettingsBackupTest { Settings.Secure.BACKUP_AUTO_RESTORE, Settings.Secure.BACKUP_ENABLED, Settings.Secure.BACKUP_PROVISIONED, + Settings.Secure.BACKUP_SCHEDULING_ENABLED, Settings.Secure.BACKUP_TRANSPORT, Settings.Secure.CALL_SCREENING_DEFAULT_COMPONENT, Settings.Secure.CAMERA_LIFT_TRIGGER_ENABLED, // Candidate for backup? diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java index 8c09dcde58d0c..dc475f696a96c 100644 --- a/services/backup/java/com/android/server/backup/BackupManagerService.java +++ b/services/backup/java/com/android/server/backup/BackupManagerService.java @@ -25,7 +25,6 @@ import android.annotation.UserIdInt; import android.app.ActivityManager; import android.app.admin.DevicePolicyManager; import android.app.backup.BackupManager; -import android.app.backup.BackupRestoreEventLogger; import android.app.backup.BackupRestoreEventLogger.DataTypeResult; import android.app.backup.IBackupManager; import android.app.backup.IBackupManagerMonitor; @@ -722,6 +721,17 @@ public class BackupManagerService extends IBackupManager.Stub { } } + @Override + public void setFrameworkSchedulingEnabledForUser(int userId, boolean isEnabled) { + UserBackupManagerService userBackupManagerService = + getServiceForUserIfCallerHasPermission(userId, + "setFrameworkSchedulingEnabledForUser()"); + + if (userBackupManagerService != null) { + userBackupManagerService.setFrameworkSchedulingEnabled(isEnabled); + } + } + @Override public void setBackupEnabledForUser(@UserIdInt int userId, boolean isEnabled) throws RemoteException { diff --git a/services/backup/java/com/android/server/backup/FullBackupJob.java b/services/backup/java/com/android/server/backup/FullBackupJob.java index 0bb25e360f152..fe0e1c6b6fcb4 100644 --- a/services/backup/java/com/android/server/backup/FullBackupJob.java +++ b/services/backup/java/com/android/server/backup/FullBackupJob.java @@ -45,9 +45,12 @@ public class FullBackupJob extends JobService { private final SparseArray mParamsForUser = new SparseArray<>(); public static void schedule(int userId, Context ctx, long minDelay, - BackupManagerConstants constants) { + UserBackupManagerService userBackupManagerService) { + if (!userBackupManagerService.isFrameworkSchedulingEnabled()) return; + JobScheduler js = (JobScheduler) ctx.getSystemService(Context.JOB_SCHEDULER_SERVICE); JobInfo.Builder builder = new JobInfo.Builder(getJobIdForUserId(userId), sIdleService); + final BackupManagerConstants constants = userBackupManagerService.getConstants(); synchronized (constants) { builder.setRequiresDeviceIdle(true) .setRequiredNetworkType(constants.getFullBackupRequiredNetworkType()) diff --git a/services/backup/java/com/android/server/backup/KeyValueBackupJob.java b/services/backup/java/com/android/server/backup/KeyValueBackupJob.java index 058dcae3102f8..164bbeaff6bc7 100644 --- a/services/backup/java/com/android/server/backup/KeyValueBackupJob.java +++ b/services/backup/java/com/android/server/backup/KeyValueBackupJob.java @@ -64,14 +64,16 @@ public class KeyValueBackupJob extends JobService { @VisibleForTesting public static final int MAX_JOB_ID = 52418896; - public static void schedule(int userId, Context ctx, BackupManagerConstants constants) { - schedule(userId, ctx, 0, constants); + public static void schedule(int userId, Context ctx, + UserBackupManagerService userBackupManagerService) { + schedule(userId, ctx, 0, userBackupManagerService); } public static void schedule(int userId, Context ctx, long delay, - BackupManagerConstants constants) { + UserBackupManagerService userBackupManagerService) { synchronized (KeyValueBackupJob.class) { - if (sScheduledForUserId.get(userId)) { + if (sScheduledForUserId.get(userId) + || !userBackupManagerService.isFrameworkSchedulingEnabled()) { return; } @@ -80,6 +82,7 @@ public class KeyValueBackupJob extends JobService { final int networkType; final boolean needsCharging; + final BackupManagerConstants constants = userBackupManagerService.getConstants(); synchronized (constants) { interval = constants.getKeyValueBackupIntervalMilliseconds(); fuzz = constants.getKeyValueBackupFuzzMilliseconds(); diff --git a/services/backup/java/com/android/server/backup/UserBackupManagerService.java b/services/backup/java/com/android/server/backup/UserBackupManagerService.java index 6ba01d712e92d..40a4af95413a4 100644 --- a/services/backup/java/com/android/server/backup/UserBackupManagerService.java +++ b/services/backup/java/com/android/server/backup/UserBackupManagerService.java @@ -1958,8 +1958,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(mUserId, mContext, BUSY_BACKOFF_MIN_MILLIS, mConstants); - FullBackupJob.schedule(mUserId, mContext, 2 * BUSY_BACKOFF_MIN_MILLIS, mConstants); + KeyValueBackupJob.schedule(mUserId, mContext, BUSY_BACKOFF_MIN_MILLIS, this); + FullBackupJob.schedule(mUserId, mContext, 2 * BUSY_BACKOFF_MIN_MILLIS, this); } finally { Binder.restoreCallingIdentity(oldToken); } @@ -2088,7 +2088,7 @@ public class UserBackupManagerService { final long interval = mConstants.getFullBackupIntervalMilliseconds(); final long appLatency = (timeSinceLast < interval) ? (interval - timeSinceLast) : 0; final long latency = Math.max(transportMinLatency, appLatency); - FullBackupJob.schedule(mUserId, mContext, latency, mConstants); + FullBackupJob.schedule(mUserId, mContext, latency, this); } else { if (DEBUG_SCHEDULING) { Slog.i( @@ -2226,7 +2226,7 @@ public class UserBackupManagerService { addUserIdToLogMessage( mUserId, "Deferring scheduled full backups in battery saver mode")); } - FullBackupJob.schedule(mUserId, mContext, keyValueBackupInterval, mConstants); + FullBackupJob.schedule(mUserId, mContext, keyValueBackupInterval, this); return false; } @@ -2392,7 +2392,7 @@ public class UserBackupManagerService { + "operation; rescheduling +" + latency)); } final long deferTime = latency; // pin for the closure - FullBackupJob.schedule(mUserId, mContext, deferTime, mConstants); + FullBackupJob.schedule(mUserId, mContext, deferTime, this); return false; } @@ -2495,7 +2495,7 @@ public class UserBackupManagerService { } // ...and schedule a backup pass if necessary - KeyValueBackupJob.schedule(mUserId, mContext, mConstants); + KeyValueBackupJob.schedule(mUserId, mContext, this); } // Note: packageName is currently unused, but may be in the future @@ -2730,7 +2730,7 @@ public class UserBackupManagerService { mUserId, "Not running backup while in battery save mode")); } // Try again in several hours. - KeyValueBackupJob.schedule(mUserId, mContext, mConstants); + KeyValueBackupJob.schedule(mUserId, mContext, this); } else { if (DEBUG) { Slog.v(TAG, addUserIdToLogMessage(mUserId, "Scheduling immediate backup pass")); @@ -3208,12 +3208,49 @@ public class UserBackupManagerService { } } + synchronized void setFrameworkSchedulingEnabled(boolean isEnabled) { + mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, + "setFrameworkSchedulingEnabled"); + + boolean wasEnabled = isFrameworkSchedulingEnabled(); + if (wasEnabled == isEnabled) return; + + Slog.i(TAG, addUserIdToLogMessage(mUserId, + (isEnabled ? "Enabling" : "Disabling") + " backup scheduling")); + + final long oldId = Binder.clearCallingIdentity(); + try { + // TODO(b/264889098): Consider at a later point if we should us a sentinel file as + // setBackupEnabled. + Settings.Secure.putIntForUser(mContext.getContentResolver(), + Settings.Secure.BACKUP_SCHEDULING_ENABLED, isEnabled ? 1 : 0, mUserId); + + if (!isEnabled) { + KeyValueBackupJob.cancel(mUserId, mContext); + FullBackupJob.cancel(mUserId, mContext); + } else { + KeyValueBackupJob.schedule(mUserId, mContext, this); + scheduleNextFullBackupJob(/* transportMinLatency */ 0); + } + } finally { + Binder.restoreCallingIdentity(oldId); + } + } + + synchronized boolean isFrameworkSchedulingEnabled() { + // By default scheduling is enabled + final int defaultSetting = 1; + int isEnabled = Settings.Secure.getIntForUser(mContext.getContentResolver(), + Settings.Secure.BACKUP_SCHEDULING_ENABLED, defaultSetting, mUserId); + return isEnabled == 1; + } + @VisibleForTesting void updateStateOnBackupEnabled(boolean wasEnabled, boolean enable) { synchronized (mQueueLock) { if (enable && !wasEnabled && mSetupComplete) { // if we've just been enabled, start scheduling backup passes - KeyValueBackupJob.schedule(mUserId, mContext, mConstants); + KeyValueBackupJob.schedule(mUserId, mContext, this); scheduleNextFullBackupJob(0); } else if (!enable) { // No longer enabled, so stop running backups @@ -4127,6 +4164,8 @@ public class UserBackupManagerService { pw.println("Auto-restore is " + (mAutoRestore ? "enabled" : "disabled")); if (mBackupRunning) pw.println("Backup currently running"); pw.println(isBackupOperationInProgress() ? "Backup in progress" : "No backups running"); + pw.println("Framework scheduling is " + + (isFrameworkSchedulingEnabled() ? "enabled" : "disabled")); pw.println("Last backup pass started: " + mLastBackupPass + " (now = " + System.currentTimeMillis() + ')'); pw.println(" next scheduled: " + KeyValueBackupJob.nextScheduled(mUserId)); 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 c5e912e6d18b9..f399fe9e7ab9f 100644 --- a/services/backup/java/com/android/server/backup/internal/SetupObserver.java +++ b/services/backup/java/com/android/server/backup/internal/SetupObserver.java @@ -23,7 +23,6 @@ import static com.android.server.backup.UserBackupManagerService.getSetupComplet import android.content.Context; import android.database.ContentObserver; import android.os.Handler; -import android.provider.Settings; import android.util.Slog; import com.android.server.backup.KeyValueBackupJob; @@ -78,7 +77,7 @@ public class SetupObserver extends ContentObserver { Slog.d(TAG, "Setup complete so starting backups"); } KeyValueBackupJob.schedule(mUserBackupManagerService.getUserId(), mContext, - mUserBackupManagerService.getConstants()); + mUserBackupManagerService); 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 ca92b6986731f..41e8092436b54 100644 --- a/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java +++ b/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java @@ -1246,7 +1246,7 @@ public class KeyValueBackupTask implements BackupRestoreTask, Runnable { delay = 0; } KeyValueBackupJob.schedule(mBackupManagerService.getUserId(), - mBackupManagerService.getContext(), delay, mBackupManagerService.getConstants()); + mBackupManagerService.getContext(), delay, mBackupManagerService); for (String packageName : mOriginalQueue) { mBackupManagerService.dataChangedImpl(packageName); diff --git a/services/robotests/backup/src/com/android/server/backup/FullBackupJobTest.java b/services/robotests/backup/src/com/android/server/backup/FullBackupJobTest.java index dbc0da7074777..c8797e25b7407 100644 --- a/services/robotests/backup/src/com/android/server/backup/FullBackupJobTest.java +++ b/services/robotests/backup/src/com/android/server/backup/FullBackupJobTest.java @@ -18,6 +18,8 @@ package com.android.server.backup; import static com.google.common.truth.Truth.assertThat; +import static org.mockito.Mockito.when; + import android.annotation.UserIdInt; import android.app.job.JobScheduler; import android.content.Context; @@ -31,6 +33,8 @@ import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; import org.robolectric.Shadows; @@ -45,14 +49,20 @@ public class FullBackupJobTest { private BackupManagerConstants mConstants; private ShadowJobScheduler mShadowJobScheduler; + @Mock + private UserBackupManagerService mUserBackupManagerService; + @UserIdInt private int mUserOneId; @UserIdInt private int mUserTwoId; @Before public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); mContext = RuntimeEnvironment.application; mConstants = new BackupManagerConstants(Handler.getMain(), mContext.getContentResolver()); mConstants.start(); + when(mUserBackupManagerService.getConstants()).thenReturn(mConstants); + when(mUserBackupManagerService.isFrameworkSchedulingEnabled()).thenReturn(true); mShadowJobScheduler = Shadows.shadowOf(mContext.getSystemService(JobScheduler.class)); @@ -69,8 +79,8 @@ public class FullBackupJobTest { @Test public void testSchedule_afterScheduling_jobExists() { - FullBackupJob.schedule(mUserOneId, mContext, 0, mConstants); - FullBackupJob.schedule(mUserTwoId, mContext, 0, mConstants); + FullBackupJob.schedule(mUserOneId, mContext, 0, mUserBackupManagerService); + FullBackupJob.schedule(mUserTwoId, mContext, 0, mUserBackupManagerService); assertThat(mShadowJobScheduler.getPendingJob(getJobIdForUserId(mUserOneId))).isNotNull(); assertThat(mShadowJobScheduler.getPendingJob(getJobIdForUserId(mUserTwoId))).isNotNull(); @@ -78,18 +88,34 @@ public class FullBackupJobTest { @Test public void testCancel_afterCancelling_jobDoesntExist() { - FullBackupJob.schedule(mUserOneId, mContext, 0, mConstants); - FullBackupJob.schedule(mUserTwoId, mContext, 0, mConstants); + FullBackupJob.schedule(mUserOneId, mContext, 0, mUserBackupManagerService); + FullBackupJob.schedule(mUserTwoId, mContext, 0, mUserBackupManagerService); FullBackupJob.cancel(mUserOneId, mContext); FullBackupJob.cancel(mUserTwoId, mContext); assertThat(mShadowJobScheduler.getPendingJob(getJobIdForUserId(mUserOneId))).isNull(); assertThat(mShadowJobScheduler.getPendingJob(getJobIdForUserId(mUserTwoId))).isNull(); } + + @Test + public void testSchedule_isNoopIfDisabled() { + when(mUserBackupManagerService.isFrameworkSchedulingEnabled()).thenReturn(false); + FullBackupJob.schedule(mUserOneId, mContext, 0, mUserBackupManagerService); + + assertThat(mShadowJobScheduler.getPendingJob(getJobIdForUserId(mUserOneId))).isNull(); + } + + @Test + public void testSchedule_schedulesJobIfEnabled() { + when(mUserBackupManagerService.isFrameworkSchedulingEnabled()).thenReturn(true); + FullBackupJob.schedule(mUserOneId, mContext, 0, mUserBackupManagerService); + + assertThat(mShadowJobScheduler.getPendingJob(getJobIdForUserId(mUserOneId))).isNotNull(); + } // @Test public void testSchedule_onlySchedulesForRequestedUser() { - FullBackupJob.schedule(mUserOneId, mContext, 0, mConstants); + FullBackupJob.schedule(mUserOneId, mContext, 0, mUserBackupManagerService); assertThat(mShadowJobScheduler.getPendingJob(getJobIdForUserId(mUserOneId))).isNotNull(); assertThat(mShadowJobScheduler.getPendingJob(getJobIdForUserId(mUserTwoId))).isNull(); @@ -97,8 +123,8 @@ public class FullBackupJobTest { // @Test public void testCancel_onlyCancelsForRequestedUser() { - FullBackupJob.schedule(mUserOneId, mContext, 0, mConstants); - FullBackupJob.schedule(mUserTwoId, mContext, 0, mConstants); + FullBackupJob.schedule(mUserOneId, mContext, 0, mUserBackupManagerService); + FullBackupJob.schedule(mUserTwoId, mContext, 0, mUserBackupManagerService); FullBackupJob.cancel(mUserOneId, mContext); assertThat(mShadowJobScheduler.getPendingJob(getJobIdForUserId(mUserOneId))).isNull(); 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 1c5fac28de3c5..712ac55d2ff5f 100644 --- a/services/robotests/backup/src/com/android/server/backup/KeyValueBackupJobTest.java +++ b/services/robotests/backup/src/com/android/server/backup/KeyValueBackupJobTest.java @@ -18,6 +18,8 @@ package com.android.server.backup; import static com.google.common.truth.Truth.assertThat; +import static org.mockito.Mockito.when; + import android.annotation.UserIdInt; import android.content.Context; import android.os.Handler; @@ -30,6 +32,8 @@ import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; import org.robolectric.annotation.Config; @@ -41,14 +45,20 @@ public class KeyValueBackupJobTest { private Context mContext; private BackupManagerConstants mConstants; + @Mock + private UserBackupManagerService mUserBackupManagerService; + @UserIdInt private int mUserOneId; @UserIdInt private int mUserTwoId; @Before public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); mContext = RuntimeEnvironment.application; mConstants = new BackupManagerConstants(Handler.getMain(), mContext.getContentResolver()); mConstants.start(); + when(mUserBackupManagerService.getConstants()).thenReturn(mConstants); + when(mUserBackupManagerService.isFrameworkSchedulingEnabled()).thenReturn(true); mUserOneId = UserHandle.USER_SYSTEM; mUserTwoId = mUserOneId + 1; @@ -61,6 +71,22 @@ public class KeyValueBackupJobTest { KeyValueBackupJob.cancel(mUserTwoId, mContext); } + @Test + public void testSchedule_isNoopIfDisabled() { + when(mUserBackupManagerService.isFrameworkSchedulingEnabled()).thenReturn(false); + KeyValueBackupJob.schedule(mUserOneId, mContext, mUserBackupManagerService); + + assertThat(KeyValueBackupJob.isScheduled(mUserOneId)).isFalse(); + } + + @Test + public void testSchedule_schedulesJobIfEnabled() { + when(mUserBackupManagerService.isFrameworkSchedulingEnabled()).thenReturn(true); + KeyValueBackupJob.schedule(mUserOneId, mContext, mUserBackupManagerService); + + assertThat(KeyValueBackupJob.isScheduled(mUserOneId)).isTrue(); + } + @Test public void testIsScheduled_beforeScheduling_returnsFalse() { assertThat(KeyValueBackupJob.isScheduled(mUserOneId)).isFalse(); @@ -69,8 +95,8 @@ public class KeyValueBackupJobTest { @Test public void testIsScheduled_afterScheduling_returnsTrue() { - KeyValueBackupJob.schedule(mUserOneId, mContext, mConstants); - KeyValueBackupJob.schedule(mUserTwoId, mContext, mConstants); + KeyValueBackupJob.schedule(mUserOneId, mContext, mUserBackupManagerService); + KeyValueBackupJob.schedule(mUserTwoId, mContext, mUserBackupManagerService); assertThat(KeyValueBackupJob.isScheduled(mUserOneId)).isTrue(); assertThat(KeyValueBackupJob.isScheduled(mUserTwoId)).isTrue(); @@ -78,8 +104,8 @@ public class KeyValueBackupJobTest { @Test public void testIsScheduled_afterCancelling_returnsFalse() { - KeyValueBackupJob.schedule(mUserOneId, mContext, mConstants); - KeyValueBackupJob.schedule(mUserTwoId, mContext, mConstants); + KeyValueBackupJob.schedule(mUserOneId, mContext, mUserBackupManagerService); + KeyValueBackupJob.schedule(mUserTwoId, mContext, mUserBackupManagerService); KeyValueBackupJob.cancel(mUserOneId, mContext); KeyValueBackupJob.cancel(mUserTwoId, mContext); @@ -89,7 +115,7 @@ public class KeyValueBackupJobTest { @Test public void testIsScheduled_afterScheduling_returnsTrueOnlyForScheduledUser() { - KeyValueBackupJob.schedule(mUserOneId, mContext, mConstants); + KeyValueBackupJob.schedule(mUserOneId, mContext, mUserBackupManagerService); assertThat(KeyValueBackupJob.isScheduled(mUserOneId)).isTrue(); assertThat(KeyValueBackupJob.isScheduled(mUserTwoId)).isFalse(); @@ -97,8 +123,8 @@ public class KeyValueBackupJobTest { @Test public void testIsScheduled_afterCancelling_returnsFalseOnlyForCancelledUser() { - KeyValueBackupJob.schedule(mUserOneId, mContext, mConstants); - KeyValueBackupJob.schedule(mUserTwoId, mContext, mConstants); + KeyValueBackupJob.schedule(mUserOneId, mContext, mUserBackupManagerService); + KeyValueBackupJob.schedule(mUserTwoId, mContext, mUserBackupManagerService); KeyValueBackupJob.cancel(mUserOneId, mContext); assertThat(KeyValueBackupJob.isScheduled(mUserOneId)).isFalse();