1 /* 2 * Copyright (C) 2015 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.backup; 18 19 import android.app.AlarmManager; 20 import android.app.job.JobInfo; 21 import android.app.job.JobParameters; 22 import android.app.job.JobScheduler; 23 import android.app.job.JobService; 24 import android.content.ComponentName; 25 import android.content.Context; 26 import android.os.RemoteException; 27 import android.util.Slog; 28 29 import java.util.Random; 30 31 /** 32 * Job for scheduling key/value backup work. This module encapsulates all 33 * of the policy around when those backup passes are executed. 34 */ 35 public class KeyValueBackupJob extends JobService { 36 private static final String TAG = "KeyValueBackupJob"; 37 private static ComponentName sKeyValueJobService = 38 new ComponentName("android", KeyValueBackupJob.class.getName()); 39 private static final int JOB_ID = 0x5039; 40 41 // Once someone asks for a backup, this is how long we hold off until we find 42 // an on-charging opportunity. If we hit this max latency we will run the operation 43 // regardless. Privileged callers can always trigger an immediate pass via 44 // BackupManager.backupNow(). 45 private static final long MAX_DEFERRAL = AlarmManager.INTERVAL_DAY; 46 47 private static boolean sScheduled = false; 48 private static long sNextScheduled = 0; 49 schedule(Context ctx, BackupManagerConstants constants)50 public static void schedule(Context ctx, BackupManagerConstants constants) { 51 schedule(ctx, 0, constants); 52 } 53 schedule(Context ctx, long delay, BackupManagerConstants constants)54 public static void schedule(Context ctx, long delay, BackupManagerConstants constants) { 55 synchronized (KeyValueBackupJob.class) { 56 if (sScheduled) { 57 return; 58 } 59 60 final long interval; 61 final long fuzz; 62 final int networkType; 63 final boolean needsCharging; 64 65 synchronized (constants) { 66 interval = constants.getKeyValueBackupIntervalMilliseconds(); 67 fuzz = constants.getKeyValueBackupFuzzMilliseconds(); 68 networkType = constants.getKeyValueBackupRequiredNetworkType(); 69 needsCharging = constants.getKeyValueBackupRequireCharging(); 70 } 71 if (delay <= 0) { 72 delay = interval + new Random().nextInt((int) fuzz); 73 } 74 if (BackupManagerService.DEBUG_SCHEDULING) { 75 Slog.v(TAG, "Scheduling k/v pass in " + (delay / 1000 / 60) + " minutes"); 76 } 77 JobInfo.Builder builder = new JobInfo.Builder(JOB_ID, sKeyValueJobService) 78 .setMinimumLatency(delay) 79 .setRequiredNetworkType(networkType) 80 .setRequiresCharging(needsCharging) 81 .setOverrideDeadline(MAX_DEFERRAL); 82 JobScheduler js = (JobScheduler) ctx.getSystemService(Context.JOB_SCHEDULER_SERVICE); 83 js.schedule(builder.build()); 84 85 sNextScheduled = System.currentTimeMillis() + delay; 86 sScheduled = true; 87 } 88 } 89 cancel(Context ctx)90 public static void cancel(Context ctx) { 91 synchronized (KeyValueBackupJob.class) { 92 JobScheduler js = (JobScheduler) ctx.getSystemService(Context.JOB_SCHEDULER_SERVICE); 93 js.cancel(JOB_ID); 94 sNextScheduled = 0; 95 sScheduled = false; 96 } 97 } 98 nextScheduled()99 public static long nextScheduled() { 100 synchronized (KeyValueBackupJob.class) { 101 return sNextScheduled; 102 } 103 } 104 105 @Override onStartJob(JobParameters params)106 public boolean onStartJob(JobParameters params) { 107 synchronized (KeyValueBackupJob.class) { 108 sNextScheduled = 0; 109 sScheduled = false; 110 } 111 112 // Time to run a key/value backup! 113 Trampoline service = BackupManagerService.getInstance(); 114 try { 115 service.backupNow(); 116 } catch (RemoteException e) {} 117 118 // This was just a trigger; ongoing wakelock management is done by the 119 // rest of the backup system. 120 return false; 121 } 122 123 @Override onStopJob(JobParameters params)124 public boolean onStopJob(JobParameters params) { 125 // Intentionally empty; the job starting was just a trigger 126 return false; 127 } 128 129 } 130