blob: 2d521e9e4e6060df09771083c48d7498e4bcdb88 [file] [log] [blame]
Dianne Hackborn231cc602009-04-27 17:10:36 -07001/*
2 * Copyright (C) 2009 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
17package android.content;
18
19import android.os.Parcel;
20import android.os.Parcelable;
21import android.util.Log;
22
Fred Quintanac5d1c6d2010-01-27 12:17:49 -080023import java.util.ArrayList;
Makoto Onuki94986212018-04-11 16:24:46 -070024import java.util.Calendar;
25import java.util.GregorianCalendar;
Fred Quintanac5d1c6d2010-01-27 12:17:49 -080026
Dianne Hackborn231cc602009-04-27 17:10:36 -070027/** @hide */
28public class SyncStatusInfo implements Parcelable {
Makoto Onuki15e7a252017-06-08 17:12:05 -070029 private static final String TAG = "Sync";
30
Makoto Onukif74cf942018-04-16 17:04:58 -070031 static final int VERSION = 6;
Makoto Onuki15e7a252017-06-08 17:12:05 -070032
33 private static final int MAX_EVENT_COUNT = 10;
Fred Quintanac5d1c6d2010-01-27 12:17:49 -080034
Makoto Onukif74cf942018-04-16 17:04:58 -070035 /**
36 * Number of sync sources. KEEP THIS AND SyncStorageEngine.SOURCES IN SYNC.
37 */
38 private static final int SOURCE_COUNT = 6;
39
Dianne Hackborn231cc602009-04-27 17:10:36 -070040 public final int authorityId;
Makoto Onuki94986212018-04-11 16:24:46 -070041
42 /**
43 * # of syncs for each sync source, etc.
44 */
45 public static class Stats {
46 public long totalElapsedTime;
47 public int numSyncs;
48 public int numSourcePoll;
49 public int numSourceOther;
50 public int numSourceLocal;
51 public int numSourceUser;
52 public int numSourcePeriodic;
53 public int numSourceFeed;
54 public int numFailures;
55 public int numCancels;
56
57 /** Copy all the stats to another instance. */
58 public void copyTo(Stats to) {
59 to.totalElapsedTime = totalElapsedTime;
60 to.numSyncs = numSyncs;
61 to.numSourcePoll = numSourcePoll;
62 to.numSourceOther = numSourceOther;
63 to.numSourceLocal = numSourceLocal;
64 to.numSourceUser = numSourceUser;
65 to.numSourcePeriodic = numSourcePeriodic;
66 to.numSourceFeed = numSourceFeed;
67 to.numFailures = numFailures;
68 to.numCancels = numCancels;
69 }
70
71 /** Clear all the stats. */
72 public void clear() {
73 totalElapsedTime = 0;
74 numSyncs = 0;
75 numSourcePoll = 0;
76 numSourceOther = 0;
77 numSourceLocal = 0;
78 numSourceUser = 0;
79 numSourcePeriodic = 0;
80 numSourceFeed = 0;
81 numFailures = 0;
82 numCancels = 0;
83 }
84
85 /** Write all the stats to a parcel. */
86 public void writeToParcel(Parcel parcel) {
87 parcel.writeLong(totalElapsedTime);
88 parcel.writeInt(numSyncs);
89 parcel.writeInt(numSourcePoll);
90 parcel.writeInt(numSourceOther);
91 parcel.writeInt(numSourceLocal);
92 parcel.writeInt(numSourceUser);
93 parcel.writeInt(numSourcePeriodic);
94 parcel.writeInt(numSourceFeed);
95 parcel.writeInt(numFailures);
96 parcel.writeInt(numCancels);
97 }
98
99 /** Read all the stats from a parcel. */
100 public void readFromParcel(Parcel parcel) {
101 totalElapsedTime = parcel.readLong();
102 numSyncs = parcel.readInt();
103 numSourcePoll = parcel.readInt();
104 numSourceOther = parcel.readInt();
105 numSourceLocal = parcel.readInt();
106 numSourceUser = parcel.readInt();
107 numSourcePeriodic = parcel.readInt();
108 numSourceFeed = parcel.readInt();
109 numFailures = parcel.readInt();
110 numCancels = parcel.readInt();
111 }
112 }
113
114 public long lastTodayResetTime;
115
116 public final Stats totalStats = new Stats();
117 public final Stats todayStats = new Stats();
118 public final Stats yesterdayStats = new Stats();
119
Dianne Hackborn231cc602009-04-27 17:10:36 -0700120 public long lastSuccessTime;
121 public int lastSuccessSource;
122 public long lastFailureTime;
123 public int lastFailureSource;
124 public String lastFailureMesg;
125 public long initialFailureTime;
126 public boolean pending;
Costin Manolache5ed64cd2009-09-22 14:41:46 -0700127 public boolean initialize;
Makoto Onukif74cf942018-04-16 17:04:58 -0700128
129 public final long[] perSourceLastSuccessTimes = new long[SOURCE_COUNT];
130 public final long[] perSourceLastFailureTimes = new long[SOURCE_COUNT];
131
Georgi Nikolovdbe846b2013-06-25 14:09:56 -0700132 // Warning: It is up to the external caller to ensure there are
133 // no race conditions when accessing this list
134 private ArrayList<Long> periodicSyncTimes;
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800135
Makoto Onuki15e7a252017-06-08 17:12:05 -0700136 private final ArrayList<Long> mLastEventTimes = new ArrayList<>();
137 private final ArrayList<String> mLastEvents = new ArrayList<>();
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800138
Jeff Sharkey7a96c392012-11-15 14:01:46 -0800139 public SyncStatusInfo(int authorityId) {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700140 this.authorityId = authorityId;
141 }
142
143 public int getLastFailureMesgAsInt(int def) {
Alon Albert5c113fa2013-02-07 08:07:32 -0800144 final int i = ContentResolver.syncErrorStringToInt(lastFailureMesg);
145 if (i > 0) {
146 return i;
147 } else {
148 Log.d(TAG, "Unknown lastFailureMesg:" + lastFailureMesg);
149 return def;
Dianne Hackborn231cc602009-04-27 17:10:36 -0700150 }
Dianne Hackborn231cc602009-04-27 17:10:36 -0700151 }
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800152
Dianne Hackborn231cc602009-04-27 17:10:36 -0700153 public int describeContents() {
154 return 0;
155 }
156
157 public void writeToParcel(Parcel parcel, int flags) {
158 parcel.writeInt(VERSION);
159 parcel.writeInt(authorityId);
Makoto Onuki94986212018-04-11 16:24:46 -0700160
161 // Note we can't use Stats.writeToParcel() here; see the below constructor for the reason.
162 parcel.writeLong(totalStats.totalElapsedTime);
163 parcel.writeInt(totalStats.numSyncs);
164 parcel.writeInt(totalStats.numSourcePoll);
165 parcel.writeInt(totalStats.numSourceOther);
166 parcel.writeInt(totalStats.numSourceLocal);
167 parcel.writeInt(totalStats.numSourceUser);
168
Dianne Hackborn231cc602009-04-27 17:10:36 -0700169 parcel.writeLong(lastSuccessTime);
170 parcel.writeInt(lastSuccessSource);
171 parcel.writeLong(lastFailureTime);
172 parcel.writeInt(lastFailureSource);
173 parcel.writeString(lastFailureMesg);
174 parcel.writeLong(initialFailureTime);
175 parcel.writeInt(pending ? 1 : 0);
Costin Manolache5ed64cd2009-09-22 14:41:46 -0700176 parcel.writeInt(initialize ? 1 : 0);
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800177 if (periodicSyncTimes != null) {
178 parcel.writeInt(periodicSyncTimes.size());
179 for (long periodicSyncTime : periodicSyncTimes) {
180 parcel.writeLong(periodicSyncTime);
181 }
182 } else {
183 parcel.writeInt(-1);
184 }
Makoto Onuki15e7a252017-06-08 17:12:05 -0700185 parcel.writeInt(mLastEventTimes.size());
186 for (int i = 0; i < mLastEventTimes.size(); i++) {
187 parcel.writeLong(mLastEventTimes.get(i));
188 parcel.writeString(mLastEvents.get(i));
189 }
Makoto Onuki94986212018-04-11 16:24:46 -0700190 // Version 4
191 parcel.writeInt(totalStats.numSourcePeriodic);
192
193 // Version 5
194 parcel.writeInt(totalStats.numSourceFeed);
195 parcel.writeInt(totalStats.numFailures);
196 parcel.writeInt(totalStats.numCancels);
197
198 parcel.writeLong(lastTodayResetTime);
199
200 todayStats.writeToParcel(parcel);
201 yesterdayStats.writeToParcel(parcel);
Makoto Onukif74cf942018-04-16 17:04:58 -0700202
203 // Version 6.
204 parcel.writeLongArray(perSourceLastSuccessTimes);
205 parcel.writeLongArray(perSourceLastFailureTimes);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700206 }
207
Jeff Sharkey7a96c392012-11-15 14:01:46 -0800208 public SyncStatusInfo(Parcel parcel) {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700209 int version = parcel.readInt();
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800210 if (version != VERSION && version != 1) {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700211 Log.w("SyncStatusInfo", "Unknown version: " + version);
212 }
213 authorityId = parcel.readInt();
Makoto Onuki94986212018-04-11 16:24:46 -0700214
215 // Note we can't use Stats.writeToParcel() here because the data is persisted and we need
216 // to be able to read from the old format too.
217 totalStats.totalElapsedTime = parcel.readLong();
218 totalStats.numSyncs = parcel.readInt();
219 totalStats.numSourcePoll = parcel.readInt();
220 totalStats.numSourceOther = parcel.readInt();
221 totalStats.numSourceLocal = parcel.readInt();
222 totalStats.numSourceUser = parcel.readInt();
Dianne Hackborn231cc602009-04-27 17:10:36 -0700223 lastSuccessTime = parcel.readLong();
224 lastSuccessSource = parcel.readInt();
225 lastFailureTime = parcel.readLong();
226 lastFailureSource = parcel.readInt();
227 lastFailureMesg = parcel.readString();
228 initialFailureTime = parcel.readLong();
229 pending = parcel.readInt() != 0;
Costin Manolache5ed64cd2009-09-22 14:41:46 -0700230 initialize = parcel.readInt() != 0;
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800231 if (version == 1) {
232 periodicSyncTimes = null;
233 } else {
Makoto Onuki15e7a252017-06-08 17:12:05 -0700234 final int count = parcel.readInt();
235 if (count < 0) {
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800236 periodicSyncTimes = null;
237 } else {
238 periodicSyncTimes = new ArrayList<Long>();
Makoto Onuki15e7a252017-06-08 17:12:05 -0700239 for (int i = 0; i < count; i++) {
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800240 periodicSyncTimes.add(parcel.readLong());
241 }
242 }
Makoto Onuki15e7a252017-06-08 17:12:05 -0700243 if (version >= 3) {
244 mLastEventTimes.clear();
245 mLastEvents.clear();
246 final int nEvents = parcel.readInt();
247 for (int i = 0; i < nEvents; i++) {
248 mLastEventTimes.add(parcel.readLong());
249 mLastEvents.add(parcel.readString());
250 }
251 }
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800252 }
Makoto Onukifdc57232017-07-21 15:30:35 -0700253 if (version < 4) {
254 // Before version 4, numSourcePeriodic wasn't persisted.
Makoto Onuki94986212018-04-11 16:24:46 -0700255 totalStats.numSourcePeriodic =
256 totalStats.numSyncs - totalStats.numSourceLocal - totalStats.numSourcePoll
257 - totalStats.numSourceOther
258 - totalStats.numSourceUser;
259 if (totalStats.numSourcePeriodic < 0) { // Sanity check.
260 totalStats.numSourcePeriodic = 0;
Makoto Onukifdc57232017-07-21 15:30:35 -0700261 }
262 } else {
Makoto Onuki94986212018-04-11 16:24:46 -0700263 totalStats.numSourcePeriodic = parcel.readInt();
264 }
265 if (version >= 5) {
266 totalStats.numSourceFeed = parcel.readInt();
267 totalStats.numFailures = parcel.readInt();
268 totalStats.numCancels = parcel.readInt();
269
270 lastTodayResetTime = parcel.readLong();
271
272 todayStats.readFromParcel(parcel);
273 yesterdayStats.readFromParcel(parcel);
Makoto Onukifdc57232017-07-21 15:30:35 -0700274 }
Makoto Onukif74cf942018-04-16 17:04:58 -0700275 if (version >= 6) {
276 parcel.readLongArray(perSourceLastSuccessTimes);
277 parcel.readLongArray(perSourceLastFailureTimes);
278 }
Dianne Hackborn231cc602009-04-27 17:10:36 -0700279 }
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800280
Georgi Nikolovdbe846b2013-06-25 14:09:56 -0700281 public SyncStatusInfo(SyncStatusInfo other) {
282 authorityId = other.authorityId;
Makoto Onuki94986212018-04-11 16:24:46 -0700283
284 other.totalStats.copyTo(totalStats);
285 other.todayStats.copyTo(todayStats);
286 other.yesterdayStats.copyTo(yesterdayStats);
287
288 lastTodayResetTime = other.lastTodayResetTime;
289
Georgi Nikolovdbe846b2013-06-25 14:09:56 -0700290 lastSuccessTime = other.lastSuccessTime;
291 lastSuccessSource = other.lastSuccessSource;
292 lastFailureTime = other.lastFailureTime;
293 lastFailureSource = other.lastFailureSource;
294 lastFailureMesg = other.lastFailureMesg;
295 initialFailureTime = other.initialFailureTime;
296 pending = other.pending;
297 initialize = other.initialize;
298 if (other.periodicSyncTimes != null) {
299 periodicSyncTimes = new ArrayList<Long>(other.periodicSyncTimes);
300 }
Makoto Onuki15e7a252017-06-08 17:12:05 -0700301 mLastEventTimes.addAll(other.mLastEventTimes);
302 mLastEvents.addAll(other.mLastEvents);
Makoto Onukif74cf942018-04-16 17:04:58 -0700303
304 copy(perSourceLastSuccessTimes, other.perSourceLastSuccessTimes);
305 copy(perSourceLastFailureTimes, other.perSourceLastFailureTimes);
306 }
307
308 private static void copy(long[] to, long[] from) {
309 System.arraycopy(from, 0, to, 0, to.length);
Georgi Nikolovdbe846b2013-06-25 14:09:56 -0700310 }
311
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800312 public void setPeriodicSyncTime(int index, long when) {
Georgi Nikolovdbe846b2013-06-25 14:09:56 -0700313 // The list is initialized lazily when scheduling occurs so we need to make sure
314 // we initialize elements < index to zero (zero is ignore for scheduling purposes)
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800315 ensurePeriodicSyncTimeSize(index);
316 periodicSyncTimes.set(index, when);
317 }
318
Georgi Nikolovdbe846b2013-06-25 14:09:56 -0700319 public long getPeriodicSyncTime(int index) {
320 if (periodicSyncTimes != null && index < periodicSyncTimes.size()) {
321 return periodicSyncTimes.get(index);
322 } else {
323 return 0;
324 }
325 }
326
327 public void removePeriodicSyncTime(int index) {
328 if (periodicSyncTimes != null && index < periodicSyncTimes.size()) {
329 periodicSyncTimes.remove(index);
330 }
331 }
332
Makoto Onuki15e7a252017-06-08 17:12:05 -0700333 /** */
334 public void addEvent(String message) {
335 if (mLastEventTimes.size() >= MAX_EVENT_COUNT) {
336 mLastEventTimes.remove(MAX_EVENT_COUNT - 1);
337 mLastEvents.remove(MAX_EVENT_COUNT - 1);
338 }
339 mLastEventTimes.add(0, System.currentTimeMillis());
340 mLastEvents.add(0, message);
341 }
342
343 /** */
344 public int getEventCount() {
345 return mLastEventTimes.size();
346 }
347
348 /** */
349 public long getEventTime(int i) {
350 return mLastEventTimes.get(i);
351 }
352
353 /** */
354 public String getEvent(int i) {
355 return mLastEvents.get(i);
356 }
357
Makoto Onukif74cf942018-04-16 17:04:58 -0700358 /** Call this when a sync has succeeded. */
359 public void setLastSuccess(int source, long lastSyncTime) {
360 lastSuccessTime = lastSyncTime;
361 lastSuccessSource = source;
362 lastFailureTime = 0;
363 lastFailureSource = -1;
364 lastFailureMesg = null;
365 initialFailureTime = 0;
366
367 if (0 <= source && source < perSourceLastSuccessTimes.length) {
368 perSourceLastSuccessTimes[source] = lastSyncTime;
369 }
370 }
371
372 /** Call this when a sync has failed. */
373 public void setLastFailure(int source, long lastSyncTime, String failureMessage) {
374 lastFailureTime = lastSyncTime;
375 lastFailureSource = source;
376 lastFailureMesg = failureMessage;
377 if (initialFailureTime == 0) {
378 initialFailureTime = lastSyncTime;
379 }
380
381 if (0 <= source && source < perSourceLastFailureTimes.length) {
382 perSourceLastFailureTimes[source] = lastSyncTime;
383 }
384 }
385
Georgi Nikolovdbe846b2013-06-25 14:09:56 -0700386 public static final Creator<SyncStatusInfo> CREATOR = new Creator<SyncStatusInfo>() {
387 public SyncStatusInfo createFromParcel(Parcel in) {
388 return new SyncStatusInfo(in);
389 }
390
391 public SyncStatusInfo[] newArray(int size) {
392 return new SyncStatusInfo[size];
393 }
394 };
395
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800396 private void ensurePeriodicSyncTimeSize(int index) {
397 if (periodicSyncTimes == null) {
398 periodicSyncTimes = new ArrayList<Long>(0);
399 }
400
401 final int requiredSize = index + 1;
402 if (periodicSyncTimes.size() < requiredSize) {
403 for (int i = periodicSyncTimes.size(); i < requiredSize; i++) {
404 periodicSyncTimes.add((long) 0);
405 }
406 }
407 }
Makoto Onuki94986212018-04-11 16:24:46 -0700408
409 /**
Makoto Onukif74cf942018-04-16 17:04:58 -0700410 * If the last reset was not today, move today's stats to yesterday's and clear today's.
Makoto Onuki94986212018-04-11 16:24:46 -0700411 */
412 public void maybeResetTodayStats(boolean clockValid, boolean force) {
413 final long now = System.currentTimeMillis();
414
415 if (!force) {
416 // Last reset was the same day, nothing to do.
417 if (areSameDates(now, lastTodayResetTime)) {
418 return;
419 }
420
421 // Hack -- on devices with no RTC, until the NTP kicks in, the device won't have the
422 // correct time. So if the time goes back, don't reset, unless we're sure the current
423 // time is correct.
424 if (now < lastTodayResetTime && !clockValid) {
425 return;
426 }
427 }
428
429 lastTodayResetTime = now;
430
431 todayStats.copyTo(yesterdayStats);
432 todayStats.clear();
433 }
434
435 private static boolean areSameDates(long time1, long time2) {
436 final Calendar c1 = new GregorianCalendar();
437 final Calendar c2 = new GregorianCalendar();
438
439 c1.setTimeInMillis(time1);
440 c2.setTimeInMillis(time2);
441
442 return c1.get(Calendar.YEAR) == c2.get(Calendar.YEAR)
443 && c1.get(Calendar.DAY_OF_YEAR) == c2.get(Calendar.DAY_OF_YEAR);
444 }
Makoto Onukif74cf942018-04-16 17:04:58 -0700445}