• Home
  • History
  • Annotate
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1  /*
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  
17  package android.content;
18  
19  import android.annotation.UnsupportedAppUsage;
20  import android.os.Parcel;
21  import android.os.Parcelable;
22  import android.util.Log;
23  
24  import java.util.ArrayList;
25  import java.util.Calendar;
26  import java.util.GregorianCalendar;
27  
28  /** @hide */
29  public class SyncStatusInfo implements Parcelable {
30      private static final String TAG = "Sync";
31  
32      static final int VERSION = 6;
33  
34      private static final int MAX_EVENT_COUNT = 10;
35  
36      /**
37       * Number of sync sources. KEEP THIS AND SyncStorageEngine.SOURCES IN SYNC.
38       */
39      private static final int SOURCE_COUNT = 6;
40  
41      @UnsupportedAppUsage
42      public final int authorityId;
43  
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. */
copyTo(Stats to)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. */
clear()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. */
writeToParcel(Parcel 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. */
readFromParcel(Parcel 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  
122      @UnsupportedAppUsage
123      public long lastSuccessTime;
124      @UnsupportedAppUsage
125      public int lastSuccessSource;
126      @UnsupportedAppUsage
127      public long lastFailureTime;
128      @UnsupportedAppUsage
129      public int lastFailureSource;
130      @UnsupportedAppUsage
131      public String lastFailureMesg;
132      @UnsupportedAppUsage
133      public long initialFailureTime;
134      @UnsupportedAppUsage
135      public boolean pending;
136      @UnsupportedAppUsage
137      public boolean initialize;
138  
139      public final long[] perSourceLastSuccessTimes = new long[SOURCE_COUNT];
140      public final long[] perSourceLastFailureTimes = new long[SOURCE_COUNT];
141  
142    // Warning: It is up to the external caller to ensure there are
143    // no race conditions when accessing this list
144    @UnsupportedAppUsage
145    private ArrayList<Long> periodicSyncTimes;
146  
147      private final ArrayList<Long> mLastEventTimes = new ArrayList<>();
148      private final ArrayList<String> mLastEvents = new ArrayList<>();
149  
150      @UnsupportedAppUsage
SyncStatusInfo(int authorityId)151      public SyncStatusInfo(int authorityId) {
152          this.authorityId = authorityId;
153      }
154  
155      @UnsupportedAppUsage
getLastFailureMesgAsInt(int def)156      public int getLastFailureMesgAsInt(int def) {
157          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;
163          }
164      }
165  
describeContents()166      public int describeContents() {
167          return 0;
168      }
169  
writeToParcel(Parcel parcel, int flags)170      public void writeToParcel(Parcel parcel, int flags) {
171          parcel.writeInt(VERSION);
172          parcel.writeInt(authorityId);
173  
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  
182          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);
189          parcel.writeInt(initialize ? 1 : 0);
190          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          }
198          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          }
203          // 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);
215  
216          // Version 6.
217          parcel.writeLongArray(perSourceLastSuccessTimes);
218          parcel.writeLongArray(perSourceLastFailureTimes);
219      }
220  
221      @UnsupportedAppUsage
SyncStatusInfo(Parcel parcel)222      public SyncStatusInfo(Parcel parcel) {
223          int version = parcel.readInt();
224          if (version != VERSION && version != 1) {
225              Log.w("SyncStatusInfo", "Unknown version: " + version);
226          }
227          authorityId = parcel.readInt();
228  
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();
237          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;
244          initialize = parcel.readInt() != 0;
245          if (version == 1) {
246              periodicSyncTimes = null;
247          } else {
248              final int count = parcel.readInt();
249              if (count < 0) {
250                  periodicSyncTimes = null;
251              } else {
252                  periodicSyncTimes = new ArrayList<Long>();
253                  for (int i = 0; i < count; i++) {
254                      periodicSyncTimes.add(parcel.readLong());
255                  }
256              }
257              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              }
266          }
267          if (version < 4) {
268              // Before version 4, numSourcePeriodic wasn't persisted.
269              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;
275              }
276          } else {
277              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);
288          }
289          if (version >= 6) {
290              parcel.readLongArray(perSourceLastSuccessTimes);
291              parcel.readLongArray(perSourceLastFailureTimes);
292          }
293      }
294  
SyncStatusInfo(SyncStatusInfo other)295      public SyncStatusInfo(SyncStatusInfo other) {
296          authorityId = other.authorityId;
297  
298          other.totalStats.copyTo(totalStats);
299          other.todayStats.copyTo(todayStats);
300          other.yesterdayStats.copyTo(yesterdayStats);
301  
302          lastTodayResetTime = other.lastTodayResetTime;
303  
304          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          }
315          mLastEventTimes.addAll(other.mLastEventTimes);
316          mLastEvents.addAll(other.mLastEvents);
317  
318          copy(perSourceLastSuccessTimes, other.perSourceLastSuccessTimes);
319          copy(perSourceLastFailureTimes, other.perSourceLastFailureTimes);
320      }
321  
copy(long[] to, long[] from)322      private static void copy(long[] to, long[] from) {
323          System.arraycopy(from, 0, to, 0, to.length);
324      }
325  
326      @UnsupportedAppUsage
setPeriodicSyncTime(int index, long when)327      public void setPeriodicSyncTime(int index, long when) {
328          // 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)
330          ensurePeriodicSyncTimeSize(index);
331          periodicSyncTimes.set(index, when);
332      }
333  
334      @UnsupportedAppUsage
getPeriodicSyncTime(int index)335      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  
343      @UnsupportedAppUsage
removePeriodicSyncTime(int index)344      public void removePeriodicSyncTime(int index) {
345          if (periodicSyncTimes != null && index < periodicSyncTimes.size()) {
346              periodicSyncTimes.remove(index);
347          }
348      }
349  
350      /** */
addEvent(String message)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      /** */
getEventCount()361      public int getEventCount() {
362          return mLastEventTimes.size();
363      }
364  
365      /** */
getEventTime(int i)366      public long getEventTime(int i) {
367          return mLastEventTimes.get(i);
368      }
369  
370      /** */
getEvent(int i)371      public String getEvent(int i) {
372          return mLastEvents.get(i);
373      }
374  
375      /** Call this when a sync has succeeded. */
setLastSuccess(int source, long lastSyncTime)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. */
setLastFailure(int source, long lastSyncTime, String failureMessage)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  
403      @UnsupportedAppUsage
404      public static final @android.annotation.NonNull Creator<SyncStatusInfo> CREATOR = new Creator<SyncStatusInfo>() {
405          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  
414      @UnsupportedAppUsage
ensurePeriodicSyncTimeSize(int index)415      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      }
427  
428      /**
429       * If the last reset was not today, move today's stats to yesterday's and clear today's.
430       */
maybeResetTodayStats(boolean clockValid, boolean force)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  
areSameDates(long time1, long time2)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      }
464  }
465