blob: 3f64515772586f8091e65a328814e512e98b4fad [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
Mathew Inwood5c0d3542018-08-14 13:54:31 +010019import android.annotation.UnsupportedAppUsage;
Dianne Hackborn231cc602009-04-27 17:10:36 -070020import android.os.Parcel;
21import android.os.Parcelable;
22import android.util.Log;
23
Fred Quintanac5d1c6d2010-01-27 12:17:49 -080024import java.util.ArrayList;
Makoto Onuki94986212018-04-11 16:24:46 -070025import java.util.Calendar;
26import java.util.GregorianCalendar;
Fred Quintanac5d1c6d2010-01-27 12:17:49 -080027
Dianne Hackborn231cc602009-04-27 17:10:36 -070028/** @hide */
29public class SyncStatusInfo implements Parcelable {
Makoto Onuki15e7a252017-06-08 17:12:05 -070030 private static final String TAG = "Sync";
31
Makoto Onukif74cf942018-04-16 17:04:58 -070032 static final int VERSION = 6;
Makoto Onuki15e7a252017-06-08 17:12:05 -070033
34 private static final int MAX_EVENT_COUNT = 10;
Fred Quintanac5d1c6d2010-01-27 12:17:49 -080035
Makoto Onukif74cf942018-04-16 17:04:58 -070036 /**
37 * Number of sync sources. KEEP THIS AND SyncStorageEngine.SOURCES IN SYNC.
38 */
39 private static final int SOURCE_COUNT = 6;
40
Mathew Inwood5c0d3542018-08-14 13:54:31 +010041 @UnsupportedAppUsage
Dianne Hackborn231cc602009-04-27 17:10:36 -070042 public final int authorityId;
Makoto Onuki94986212018-04-11 16:24:46 -070043
44 /**
45 * # of syncs for each sync source, etc.
46 */
47 public static class Stats {
48 public long totalElapsedTime;
49 public int numSyncs;
50 public int numSourcePoll;
51 public int numSourceOther;
52 public int numSourceLocal;
53 public int numSourceUser;
54 public int numSourcePeriodic;
55 public int numSourceFeed;
56 public int numFailures;
57 public int numCancels;
58
59 /** Copy all the stats to another instance. */
60 public void copyTo(Stats to) {
61 to.totalElapsedTime = totalElapsedTime;
62 to.numSyncs = numSyncs;
63 to.numSourcePoll = numSourcePoll;
64 to.numSourceOther = numSourceOther;
65 to.numSourceLocal = numSourceLocal;
66 to.numSourceUser = numSourceUser;
67 to.numSourcePeriodic = numSourcePeriodic;
68 to.numSourceFeed = numSourceFeed;
69 to.numFailures = numFailures;
70 to.numCancels = numCancels;
71 }
72
73 /** Clear all the stats. */
74 public void clear() {
75 totalElapsedTime = 0;
76 numSyncs = 0;
77 numSourcePoll = 0;
78 numSourceOther = 0;
79 numSourceLocal = 0;
80 numSourceUser = 0;
81 numSourcePeriodic = 0;
82 numSourceFeed = 0;
83 numFailures = 0;
84 numCancels = 0;
85 }
86
87 /** Write all the stats to a parcel. */
88 public void writeToParcel(Parcel parcel) {
89 parcel.writeLong(totalElapsedTime);
90 parcel.writeInt(numSyncs);
91 parcel.writeInt(numSourcePoll);
92 parcel.writeInt(numSourceOther);
93 parcel.writeInt(numSourceLocal);
94 parcel.writeInt(numSourceUser);
95 parcel.writeInt(numSourcePeriodic);
96 parcel.writeInt(numSourceFeed);
97 parcel.writeInt(numFailures);
98 parcel.writeInt(numCancels);
99 }
100
101 /** Read all the stats from a parcel. */
102 public void readFromParcel(Parcel parcel) {
103 totalElapsedTime = parcel.readLong();
104 numSyncs = parcel.readInt();
105 numSourcePoll = parcel.readInt();
106 numSourceOther = parcel.readInt();
107 numSourceLocal = parcel.readInt();
108 numSourceUser = parcel.readInt();
109 numSourcePeriodic = parcel.readInt();
110 numSourceFeed = parcel.readInt();
111 numFailures = parcel.readInt();
112 numCancels = parcel.readInt();
113 }
114 }
115
116 public long lastTodayResetTime;
117
118 public final Stats totalStats = new Stats();
119 public final Stats todayStats = new Stats();
120 public final Stats yesterdayStats = new Stats();
121
Mathew Inwood5c0d3542018-08-14 13:54:31 +0100122 @UnsupportedAppUsage
Dianne Hackborn231cc602009-04-27 17:10:36 -0700123 public long lastSuccessTime;
Mathew Inwood5c0d3542018-08-14 13:54:31 +0100124 @UnsupportedAppUsage
Dianne Hackborn231cc602009-04-27 17:10:36 -0700125 public int lastSuccessSource;
Mathew Inwood5c0d3542018-08-14 13:54:31 +0100126 @UnsupportedAppUsage
Dianne Hackborn231cc602009-04-27 17:10:36 -0700127 public long lastFailureTime;
Mathew Inwood5c0d3542018-08-14 13:54:31 +0100128 @UnsupportedAppUsage
Dianne Hackborn231cc602009-04-27 17:10:36 -0700129 public int lastFailureSource;
Mathew Inwood5c0d3542018-08-14 13:54:31 +0100130 @UnsupportedAppUsage
Dianne Hackborn231cc602009-04-27 17:10:36 -0700131 public String lastFailureMesg;
Mathew Inwood5c0d3542018-08-14 13:54:31 +0100132 @UnsupportedAppUsage
Dianne Hackborn231cc602009-04-27 17:10:36 -0700133 public long initialFailureTime;
Mathew Inwood5c0d3542018-08-14 13:54:31 +0100134 @UnsupportedAppUsage
Dianne Hackborn231cc602009-04-27 17:10:36 -0700135 public boolean pending;
Mathew Inwood5c0d3542018-08-14 13:54:31 +0100136 @UnsupportedAppUsage
Costin Manolache5ed64cd2009-09-22 14:41:46 -0700137 public boolean initialize;
Makoto Onukif74cf942018-04-16 17:04:58 -0700138
139 public final long[] perSourceLastSuccessTimes = new long[SOURCE_COUNT];
140 public final long[] perSourceLastFailureTimes = new long[SOURCE_COUNT];
141
Georgi Nikolovdbe846b2013-06-25 14:09:56 -0700142 // Warning: It is up to the external caller to ensure there are
143 // no race conditions when accessing this list
Mathew Inwood5c0d3542018-08-14 13:54:31 +0100144 @UnsupportedAppUsage
Georgi Nikolovdbe846b2013-06-25 14:09:56 -0700145 private ArrayList<Long> periodicSyncTimes;
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800146
Makoto Onuki15e7a252017-06-08 17:12:05 -0700147 private final ArrayList<Long> mLastEventTimes = new ArrayList<>();
148 private final ArrayList<String> mLastEvents = new ArrayList<>();
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800149
Mathew Inwood5c0d3542018-08-14 13:54:31 +0100150 @UnsupportedAppUsage
Jeff Sharkey7a96c392012-11-15 14:01:46 -0800151 public SyncStatusInfo(int authorityId) {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700152 this.authorityId = authorityId;
153 }
154
Mathew Inwood5c0d3542018-08-14 13:54:31 +0100155 @UnsupportedAppUsage
Dianne Hackborn231cc602009-04-27 17:10:36 -0700156 public int getLastFailureMesgAsInt(int def) {
Alon Albert5c113fa2013-02-07 08:07:32 -0800157 final int i = ContentResolver.syncErrorStringToInt(lastFailureMesg);
158 if (i > 0) {
159 return i;
160 } else {
161 Log.d(TAG, "Unknown lastFailureMesg:" + lastFailureMesg);
162 return def;
Dianne Hackborn231cc602009-04-27 17:10:36 -0700163 }
Dianne Hackborn231cc602009-04-27 17:10:36 -0700164 }
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800165
Dianne Hackborn231cc602009-04-27 17:10:36 -0700166 public int describeContents() {
167 return 0;
168 }
169
170 public void writeToParcel(Parcel parcel, int flags) {
171 parcel.writeInt(VERSION);
172 parcel.writeInt(authorityId);
Makoto Onuki94986212018-04-11 16:24:46 -0700173
174 // Note we can't use Stats.writeToParcel() here; see the below constructor for the reason.
175 parcel.writeLong(totalStats.totalElapsedTime);
176 parcel.writeInt(totalStats.numSyncs);
177 parcel.writeInt(totalStats.numSourcePoll);
178 parcel.writeInt(totalStats.numSourceOther);
179 parcel.writeInt(totalStats.numSourceLocal);
180 parcel.writeInt(totalStats.numSourceUser);
181
Dianne Hackborn231cc602009-04-27 17:10:36 -0700182 parcel.writeLong(lastSuccessTime);
183 parcel.writeInt(lastSuccessSource);
184 parcel.writeLong(lastFailureTime);
185 parcel.writeInt(lastFailureSource);
186 parcel.writeString(lastFailureMesg);
187 parcel.writeLong(initialFailureTime);
188 parcel.writeInt(pending ? 1 : 0);
Costin Manolache5ed64cd2009-09-22 14:41:46 -0700189 parcel.writeInt(initialize ? 1 : 0);
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800190 if (periodicSyncTimes != null) {
191 parcel.writeInt(periodicSyncTimes.size());
192 for (long periodicSyncTime : periodicSyncTimes) {
193 parcel.writeLong(periodicSyncTime);
194 }
195 } else {
196 parcel.writeInt(-1);
197 }
Makoto Onuki15e7a252017-06-08 17:12:05 -0700198 parcel.writeInt(mLastEventTimes.size());
199 for (int i = 0; i < mLastEventTimes.size(); i++) {
200 parcel.writeLong(mLastEventTimes.get(i));
201 parcel.writeString(mLastEvents.get(i));
202 }
Makoto Onuki94986212018-04-11 16:24:46 -0700203 // Version 4
204 parcel.writeInt(totalStats.numSourcePeriodic);
205
206 // Version 5
207 parcel.writeInt(totalStats.numSourceFeed);
208 parcel.writeInt(totalStats.numFailures);
209 parcel.writeInt(totalStats.numCancels);
210
211 parcel.writeLong(lastTodayResetTime);
212
213 todayStats.writeToParcel(parcel);
214 yesterdayStats.writeToParcel(parcel);
Makoto Onukif74cf942018-04-16 17:04:58 -0700215
216 // Version 6.
217 parcel.writeLongArray(perSourceLastSuccessTimes);
218 parcel.writeLongArray(perSourceLastFailureTimes);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700219 }
220
Mathew Inwood5c0d3542018-08-14 13:54:31 +0100221 @UnsupportedAppUsage
Jeff Sharkey7a96c392012-11-15 14:01:46 -0800222 public SyncStatusInfo(Parcel parcel) {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700223 int version = parcel.readInt();
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800224 if (version != VERSION && version != 1) {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700225 Log.w("SyncStatusInfo", "Unknown version: " + version);
226 }
227 authorityId = parcel.readInt();
Makoto Onuki94986212018-04-11 16:24:46 -0700228
229 // Note we can't use Stats.writeToParcel() here because the data is persisted and we need
230 // to be able to read from the old format too.
231 totalStats.totalElapsedTime = parcel.readLong();
232 totalStats.numSyncs = parcel.readInt();
233 totalStats.numSourcePoll = parcel.readInt();
234 totalStats.numSourceOther = parcel.readInt();
235 totalStats.numSourceLocal = parcel.readInt();
236 totalStats.numSourceUser = parcel.readInt();
Dianne Hackborn231cc602009-04-27 17:10:36 -0700237 lastSuccessTime = parcel.readLong();
238 lastSuccessSource = parcel.readInt();
239 lastFailureTime = parcel.readLong();
240 lastFailureSource = parcel.readInt();
241 lastFailureMesg = parcel.readString();
242 initialFailureTime = parcel.readLong();
243 pending = parcel.readInt() != 0;
Costin Manolache5ed64cd2009-09-22 14:41:46 -0700244 initialize = parcel.readInt() != 0;
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800245 if (version == 1) {
246 periodicSyncTimes = null;
247 } else {
Makoto Onuki15e7a252017-06-08 17:12:05 -0700248 final int count = parcel.readInt();
249 if (count < 0) {
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800250 periodicSyncTimes = null;
251 } else {
252 periodicSyncTimes = new ArrayList<Long>();
Makoto Onuki15e7a252017-06-08 17:12:05 -0700253 for (int i = 0; i < count; i++) {
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800254 periodicSyncTimes.add(parcel.readLong());
255 }
256 }
Makoto Onuki15e7a252017-06-08 17:12:05 -0700257 if (version >= 3) {
258 mLastEventTimes.clear();
259 mLastEvents.clear();
260 final int nEvents = parcel.readInt();
261 for (int i = 0; i < nEvents; i++) {
262 mLastEventTimes.add(parcel.readLong());
263 mLastEvents.add(parcel.readString());
264 }
265 }
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800266 }
Makoto Onukifdc57232017-07-21 15:30:35 -0700267 if (version < 4) {
268 // Before version 4, numSourcePeriodic wasn't persisted.
Makoto Onuki94986212018-04-11 16:24:46 -0700269 totalStats.numSourcePeriodic =
270 totalStats.numSyncs - totalStats.numSourceLocal - totalStats.numSourcePoll
271 - totalStats.numSourceOther
272 - totalStats.numSourceUser;
273 if (totalStats.numSourcePeriodic < 0) { // Sanity check.
274 totalStats.numSourcePeriodic = 0;
Makoto Onukifdc57232017-07-21 15:30:35 -0700275 }
276 } else {
Makoto Onuki94986212018-04-11 16:24:46 -0700277 totalStats.numSourcePeriodic = parcel.readInt();
278 }
279 if (version >= 5) {
280 totalStats.numSourceFeed = parcel.readInt();
281 totalStats.numFailures = parcel.readInt();
282 totalStats.numCancels = parcel.readInt();
283
284 lastTodayResetTime = parcel.readLong();
285
286 todayStats.readFromParcel(parcel);
287 yesterdayStats.readFromParcel(parcel);
Makoto Onukifdc57232017-07-21 15:30:35 -0700288 }
Makoto Onukif74cf942018-04-16 17:04:58 -0700289 if (version >= 6) {
290 parcel.readLongArray(perSourceLastSuccessTimes);
291 parcel.readLongArray(perSourceLastFailureTimes);
292 }
Dianne Hackborn231cc602009-04-27 17:10:36 -0700293 }
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800294
Georgi Nikolovdbe846b2013-06-25 14:09:56 -0700295 public SyncStatusInfo(SyncStatusInfo other) {
296 authorityId = other.authorityId;
Makoto Onuki94986212018-04-11 16:24:46 -0700297
298 other.totalStats.copyTo(totalStats);
299 other.todayStats.copyTo(todayStats);
300 other.yesterdayStats.copyTo(yesterdayStats);
301
302 lastTodayResetTime = other.lastTodayResetTime;
303
Georgi Nikolovdbe846b2013-06-25 14:09:56 -0700304 lastSuccessTime = other.lastSuccessTime;
305 lastSuccessSource = other.lastSuccessSource;
306 lastFailureTime = other.lastFailureTime;
307 lastFailureSource = other.lastFailureSource;
308 lastFailureMesg = other.lastFailureMesg;
309 initialFailureTime = other.initialFailureTime;
310 pending = other.pending;
311 initialize = other.initialize;
312 if (other.periodicSyncTimes != null) {
313 periodicSyncTimes = new ArrayList<Long>(other.periodicSyncTimes);
314 }
Makoto Onuki15e7a252017-06-08 17:12:05 -0700315 mLastEventTimes.addAll(other.mLastEventTimes);
316 mLastEvents.addAll(other.mLastEvents);
Makoto Onukif74cf942018-04-16 17:04:58 -0700317
318 copy(perSourceLastSuccessTimes, other.perSourceLastSuccessTimes);
319 copy(perSourceLastFailureTimes, other.perSourceLastFailureTimes);
320 }
321
322 private static void copy(long[] to, long[] from) {
323 System.arraycopy(from, 0, to, 0, to.length);
Georgi Nikolovdbe846b2013-06-25 14:09:56 -0700324 }
325
Mathew Inwood5c0d3542018-08-14 13:54:31 +0100326 @UnsupportedAppUsage
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800327 public void setPeriodicSyncTime(int index, long when) {
Georgi Nikolovdbe846b2013-06-25 14:09:56 -0700328 // The list is initialized lazily when scheduling occurs so we need to make sure
329 // we initialize elements < index to zero (zero is ignore for scheduling purposes)
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800330 ensurePeriodicSyncTimeSize(index);
331 periodicSyncTimes.set(index, when);
332 }
333
Mathew Inwood5c0d3542018-08-14 13:54:31 +0100334 @UnsupportedAppUsage
Georgi Nikolovdbe846b2013-06-25 14:09:56 -0700335 public long getPeriodicSyncTime(int index) {
336 if (periodicSyncTimes != null && index < periodicSyncTimes.size()) {
337 return periodicSyncTimes.get(index);
338 } else {
339 return 0;
340 }
341 }
342
Mathew Inwood5c0d3542018-08-14 13:54:31 +0100343 @UnsupportedAppUsage
Georgi Nikolovdbe846b2013-06-25 14:09:56 -0700344 public void removePeriodicSyncTime(int index) {
345 if (periodicSyncTimes != null && index < periodicSyncTimes.size()) {
346 periodicSyncTimes.remove(index);
347 }
348 }
349
Makoto Onuki15e7a252017-06-08 17:12:05 -0700350 /** */
351 public void addEvent(String message) {
352 if (mLastEventTimes.size() >= MAX_EVENT_COUNT) {
353 mLastEventTimes.remove(MAX_EVENT_COUNT - 1);
354 mLastEvents.remove(MAX_EVENT_COUNT - 1);
355 }
356 mLastEventTimes.add(0, System.currentTimeMillis());
357 mLastEvents.add(0, message);
358 }
359
360 /** */
361 public int getEventCount() {
362 return mLastEventTimes.size();
363 }
364
365 /** */
366 public long getEventTime(int i) {
367 return mLastEventTimes.get(i);
368 }
369
370 /** */
371 public String getEvent(int i) {
372 return mLastEvents.get(i);
373 }
374
Makoto Onukif74cf942018-04-16 17:04:58 -0700375 /** Call this when a sync has succeeded. */
376 public void setLastSuccess(int source, long lastSyncTime) {
377 lastSuccessTime = lastSyncTime;
378 lastSuccessSource = source;
379 lastFailureTime = 0;
380 lastFailureSource = -1;
381 lastFailureMesg = null;
382 initialFailureTime = 0;
383
384 if (0 <= source && source < perSourceLastSuccessTimes.length) {
385 perSourceLastSuccessTimes[source] = lastSyncTime;
386 }
387 }
388
389 /** Call this when a sync has failed. */
390 public void setLastFailure(int source, long lastSyncTime, String failureMessage) {
391 lastFailureTime = lastSyncTime;
392 lastFailureSource = source;
393 lastFailureMesg = failureMessage;
394 if (initialFailureTime == 0) {
395 initialFailureTime = lastSyncTime;
396 }
397
398 if (0 <= source && source < perSourceLastFailureTimes.length) {
399 perSourceLastFailureTimes[source] = lastSyncTime;
400 }
401 }
402
Mathew Inwood5c0d3542018-08-14 13:54:31 +0100403 @UnsupportedAppUsage
Jeff Sharkey9e8f83d2019-02-28 12:06:45 -0700404 public static final @android.annotation.NonNull Creator<SyncStatusInfo> CREATOR = new Creator<SyncStatusInfo>() {
Georgi Nikolovdbe846b2013-06-25 14:09:56 -0700405 public SyncStatusInfo createFromParcel(Parcel in) {
406 return new SyncStatusInfo(in);
407 }
408
409 public SyncStatusInfo[] newArray(int size) {
410 return new SyncStatusInfo[size];
411 }
412 };
413
Mathew Inwood5c0d3542018-08-14 13:54:31 +0100414 @UnsupportedAppUsage
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800415 private void ensurePeriodicSyncTimeSize(int index) {
416 if (periodicSyncTimes == null) {
417 periodicSyncTimes = new ArrayList<Long>(0);
418 }
419
420 final int requiredSize = index + 1;
421 if (periodicSyncTimes.size() < requiredSize) {
422 for (int i = periodicSyncTimes.size(); i < requiredSize; i++) {
423 periodicSyncTimes.add((long) 0);
424 }
425 }
426 }
Makoto Onuki94986212018-04-11 16:24:46 -0700427
428 /**
Makoto Onukif74cf942018-04-16 17:04:58 -0700429 * 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 -0700430 */
431 public void maybeResetTodayStats(boolean clockValid, boolean force) {
432 final long now = System.currentTimeMillis();
433
434 if (!force) {
435 // Last reset was the same day, nothing to do.
436 if (areSameDates(now, lastTodayResetTime)) {
437 return;
438 }
439
440 // Hack -- on devices with no RTC, until the NTP kicks in, the device won't have the
441 // correct time. So if the time goes back, don't reset, unless we're sure the current
442 // time is correct.
443 if (now < lastTodayResetTime && !clockValid) {
444 return;
445 }
446 }
447
448 lastTodayResetTime = now;
449
450 todayStats.copyTo(yesterdayStats);
451 todayStats.clear();
452 }
453
454 private static boolean areSameDates(long time1, long time2) {
455 final Calendar c1 = new GregorianCalendar();
456 final Calendar c2 = new GregorianCalendar();
457
458 c1.setTimeInMillis(time1);
459 c2.setTimeInMillis(time2);
460
461 return c1.get(Calendar.YEAR) == c2.get(Calendar.YEAR)
462 && c1.get(Calendar.DAY_OF_YEAR) == c2.get(Calendar.DAY_OF_YEAR);
463 }
Makoto Onukif74cf942018-04-16 17:04:58 -0700464}