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