• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.content;
18 
19 import android.annotation.Nullable;
20 import android.app.job.JobParameters;
21 import android.app.job.JobService;
22 import android.os.Message;
23 import android.os.SystemClock;
24 import android.util.Log;
25 import android.util.Slog;
26 import android.util.SparseArray;
27 import android.util.SparseBooleanArray;
28 import android.util.SparseLongArray;
29 
30 import com.android.internal.annotations.GuardedBy;
31 
32 public class SyncJobService extends JobService {
33     private static final String TAG = "SyncManager";
34 
35     private static final Object sLock = new Object();
36 
37     @GuardedBy("sLock")
38     private static SyncJobService sInstance;
39 
40     @GuardedBy("sLock")
41     private static final SparseArray<JobParameters> sJobParamsMap = new SparseArray<>();
42 
43     @GuardedBy("sLock")
44     private static final SparseBooleanArray sStartedSyncs = new SparseBooleanArray();
45 
46     @GuardedBy("sLock")
47     private static final SparseLongArray sJobStartUptimes = new SparseLongArray();
48 
49     private static final SyncLogger sLogger = SyncLogger.getInstance();
50 
updateInstance()51     private void updateInstance() {
52         synchronized (SyncJobService.class) {
53             sInstance = this;
54         }
55     }
56 
57     @Nullable
getInstance()58     private static SyncJobService getInstance() {
59         synchronized (sLock) {
60             if (sInstance == null) {
61                 Slog.wtf(TAG, "sInstance == null");
62             }
63             return sInstance;
64         }
65     }
66 
isReady()67     public static boolean isReady() {
68         synchronized (sLock) {
69             return sInstance != null;
70         }
71     }
72 
73     @Override
onStartJob(JobParameters params)74     public boolean onStartJob(JobParameters params) {
75         updateInstance();
76 
77         sLogger.purgeOldLogs();
78 
79         SyncOperation op = SyncOperation.maybeCreateFromJobExtras(params.getExtras());
80 
81         if (op == null) {
82             Slog.wtf(TAG, "Got invalid job " + params.getJobId());
83             return false;
84         }
85 
86         final boolean readyToSync = SyncManager.readyToSync(op.target.userId);
87 
88         sLogger.log("onStartJob() jobid=", params.getJobId(), " op=", op,
89                 " readyToSync", readyToSync);
90 
91         if (!readyToSync) {
92             // If the user isn't unlocked or the device has been provisioned yet, just stop the job
93             // at this point. If it's a non-periodic sync, ask the job scheduler to reschedule it.
94             // If it's a periodic sync, then just wait until the next cycle.
95             final boolean wantsReschedule = !op.isPeriodic;
96             jobFinished(params, wantsReschedule);
97             return true;
98         }
99 
100         boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE);
101         synchronized (sLock) {
102             final int jobId = params.getJobId();
103             sJobParamsMap.put(jobId, params);
104 
105             sStartedSyncs.delete(jobId);
106             sJobStartUptimes.put(jobId, SystemClock.uptimeMillis());
107         }
108         Message m = Message.obtain();
109         m.what = SyncManager.SyncHandler.MESSAGE_START_SYNC;
110         if (isLoggable) {
111             Slog.v(TAG, "Got start job message " + op.target);
112         }
113         m.obj = op;
114         SyncManager.sendMessage(m);
115         return true;
116     }
117 
118     @Override
onStopJob(JobParameters params)119     public boolean onStopJob(JobParameters params) {
120         if (Log.isLoggable(TAG, Log.VERBOSE)) {
121             Slog.v(TAG, "onStopJob called " + params.getJobId() + ", reason: "
122                     + params.getInternalStopReasonCode());
123         }
124         final SyncOperation op = SyncOperation.maybeCreateFromJobExtras(params.getExtras());
125         if (op == null) {
126             Slog.wtf(TAG, "Got invalid job " + params.getJobId());
127             return false;
128         }
129 
130         final boolean readyToSync = SyncManager.readyToSync(op.target.userId);
131 
132         sLogger.log("onStopJob() ", sLogger.jobParametersToString(params),
133                 " readyToSync=", readyToSync);
134 
135         synchronized (sLock) {
136             final int jobId = params.getJobId();
137             sJobParamsMap.remove(jobId);
138 
139             final long startUptime = sJobStartUptimes.get(jobId);
140             final long nowUptime = SystemClock.uptimeMillis();
141             final long runtime = nowUptime - startUptime;
142 
143 
144             if (runtime > 60 * 1000) {
145                 // WTF if startSyncH() hasn't happened, *unless* onStopJob() was called too soon.
146                 // (1 minute threshold.)
147                 // Also don't wtf when it's not ready to sync.
148                 if (readyToSync && !sStartedSyncs.get(jobId)) {
149                     wtf("Job " + jobId + " didn't start: "
150                             + " startUptime=" + startUptime
151                             + " nowUptime=" + nowUptime
152                             + " params=" + jobParametersToString(params));
153                 }
154             }
155 
156             sStartedSyncs.delete(jobId);
157             sJobStartUptimes.delete(jobId);
158         }
159         Message m = Message.obtain();
160         m.what = SyncManager.SyncHandler.MESSAGE_STOP_SYNC;
161         m.obj = op;
162 
163         // Reschedule if this job was NOT explicitly canceled.
164         m.arg1 = params.getInternalStopReasonCode() != JobParameters.INTERNAL_STOP_REASON_CANCELED
165                 ? 1 : 0;
166         // Apply backoff only if stop is called due to timeout.
167         m.arg2 = params.getInternalStopReasonCode() == JobParameters.INTERNAL_STOP_REASON_TIMEOUT
168                 ? 1 : 0;
169 
170         SyncManager.sendMessage(m);
171         return false;
172     }
173 
callJobFinished(int jobId, boolean needsReschedule, String why)174     public static void callJobFinished(int jobId, boolean needsReschedule, String why) {
175         final SyncJobService instance = getInstance();
176         if (instance != null) {
177             instance.callJobFinishedInner(jobId, needsReschedule, why);
178         }
179     }
180 
callJobFinishedInner(int jobId, boolean needsReschedule, String why)181     public void callJobFinishedInner(int jobId, boolean needsReschedule, String why) {
182         synchronized (sLock) {
183             JobParameters params = sJobParamsMap.get(jobId);
184             sLogger.log("callJobFinished()",
185                     " jobid=", jobId,
186                     " needsReschedule=", needsReschedule,
187                     " ", sLogger.jobParametersToString(params),
188                     " why=", why);
189             if (params != null) {
190                 jobFinished(params, needsReschedule);
191                 sJobParamsMap.remove(jobId);
192             } else {
193                 Slog.e(TAG, "Job params not found for " + String.valueOf(jobId));
194             }
195         }
196     }
197 
markSyncStarted(int jobId)198     public static void markSyncStarted(int jobId) {
199         synchronized (sLock) {
200             sStartedSyncs.put(jobId, true);
201         }
202     }
203 
jobParametersToString(JobParameters params)204     public static String jobParametersToString(JobParameters params) {
205         if (params == null) {
206             return "job:null";
207         } else {
208             return "job:#" + params.getJobId() + ":"
209                     + "sr=[" + params.getInternalStopReasonCode()
210                     + "/" + params.getDebugStopReason() + "]:"
211                     + SyncOperation.maybeCreateFromJobExtras(params.getExtras());
212         }
213     }
214 
wtf(String message)215     private static void wtf(String message) {
216         sLogger.log(message);
217         Slog.wtf(TAG, message);
218     }
219 }
220