Loading services/backup/java/com/android/server/backup/BackupManagerService.java +7 −4 Original line number Diff line number Diff line Loading @@ -149,6 +149,9 @@ public class BackupManagerService { if (userBackupManagerService != null) { userBackupManagerService.tearDownService(); KeyValueBackupJob.cancel(userId, mContext); FullBackupJob.cancel(userId, mContext); } } Loading Loading @@ -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); Loading @@ -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(); Loading services/backup/java/com/android/server/backup/FullBackupJob.java +55 −15 Original line number Diff line number Diff line Loading @@ -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; @GuardedBy("mParamsForUser") private final SparseArray<JobParameters> mParamsForUser = new SparseArray<>(); private JobParameters mParams; 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()) Loading @@ -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); } } } Loading @@ -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); } } services/backup/java/com/android/server/backup/JobIdManager.java 0 → 100644 +30 −0 Original line number Diff line number Diff line /* * 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; } } services/backup/java/com/android/server/backup/KeyValueBackupJob.java +54 −22 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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 Loading @@ -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; } Loading @@ -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 Loading @@ -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); } } services/backup/java/com/android/server/backup/Trampoline.java +4 −4 Original line number Diff line number Diff line Loading @@ -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); } } } Loading
services/backup/java/com/android/server/backup/BackupManagerService.java +7 −4 Original line number Diff line number Diff line Loading @@ -149,6 +149,9 @@ public class BackupManagerService { if (userBackupManagerService != null) { userBackupManagerService.tearDownService(); KeyValueBackupJob.cancel(userId, mContext); FullBackupJob.cancel(userId, mContext); } } Loading Loading @@ -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); Loading @@ -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(); Loading
services/backup/java/com/android/server/backup/FullBackupJob.java +55 −15 Original line number Diff line number Diff line Loading @@ -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; @GuardedBy("mParamsForUser") private final SparseArray<JobParameters> mParamsForUser = new SparseArray<>(); private JobParameters mParams; 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()) Loading @@ -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); } } } Loading @@ -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); } }
services/backup/java/com/android/server/backup/JobIdManager.java 0 → 100644 +30 −0 Original line number Diff line number Diff line /* * 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; } }
services/backup/java/com/android/server/backup/KeyValueBackupJob.java +54 −22 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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 Loading @@ -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; } Loading @@ -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 Loading @@ -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); } }
services/backup/java/com/android/server/backup/Trampoline.java +4 −4 Original line number Diff line number Diff line Loading @@ -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); } } }