• Home
  • 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.compat.annotation.UnsupportedAppUsage;
20 import android.os.Parcel;
21 import android.os.Parcelable;
22 import android.util.Log;
23 import android.util.Pair;
24 
25 import com.android.internal.util.ArrayUtils;
26 
27 import java.util.ArrayList;
28 import java.util.Calendar;
29 import java.util.GregorianCalendar;
30 
31 /** @hide */
32 public class SyncStatusInfo implements Parcelable {
33     private static final String TAG = "Sync";
34 
35     static final int VERSION = 6;
36 
37     private static final int MAX_EVENT_COUNT = 10;
38 
39     /**
40      * Number of sync sources. KEEP THIS AND SyncStorageEngine.SOURCES IN SYNC.
41      */
42     private static final int SOURCE_COUNT = 6;
43 
44     @UnsupportedAppUsage
45     public final int authorityId;
46 
47     /**
48      * # of syncs for each sync source, etc.
49      */
50     public static class Stats {
51         public long totalElapsedTime;
52         public int numSyncs;
53         public int numSourcePoll;
54         public int numSourceOther;
55         public int numSourceLocal;
56         public int numSourceUser;
57         public int numSourcePeriodic;
58         public int numSourceFeed;
59         public int numFailures;
60         public int numCancels;
61 
62         /** Copy all the stats to another instance. */
copyTo(Stats to)63         public void copyTo(Stats to) {
64             to.totalElapsedTime = totalElapsedTime;
65             to.numSyncs = numSyncs;
66             to.numSourcePoll = numSourcePoll;
67             to.numSourceOther = numSourceOther;
68             to.numSourceLocal = numSourceLocal;
69             to.numSourceUser = numSourceUser;
70             to.numSourcePeriodic = numSourcePeriodic;
71             to.numSourceFeed = numSourceFeed;
72             to.numFailures = numFailures;
73             to.numCancels = numCancels;
74         }
75 
76         /** Clear all the stats. */
clear()77         public void clear() {
78             totalElapsedTime = 0;
79             numSyncs = 0;
80             numSourcePoll = 0;
81             numSourceOther = 0;
82             numSourceLocal = 0;
83             numSourceUser = 0;
84             numSourcePeriodic = 0;
85             numSourceFeed = 0;
86             numFailures = 0;
87             numCancels = 0;
88         }
89 
90         /** Write all the stats to a parcel. */
writeToParcel(Parcel parcel)91         public void writeToParcel(Parcel parcel) {
92             parcel.writeLong(totalElapsedTime);
93             parcel.writeInt(numSyncs);
94             parcel.writeInt(numSourcePoll);
95             parcel.writeInt(numSourceOther);
96             parcel.writeInt(numSourceLocal);
97             parcel.writeInt(numSourceUser);
98             parcel.writeInt(numSourcePeriodic);
99             parcel.writeInt(numSourceFeed);
100             parcel.writeInt(numFailures);
101             parcel.writeInt(numCancels);
102         }
103 
104         /** Read all the stats from a parcel. */
readFromParcel(Parcel parcel)105         public void readFromParcel(Parcel parcel) {
106             totalElapsedTime = parcel.readLong();
107             numSyncs = parcel.readInt();
108             numSourcePoll = parcel.readInt();
109             numSourceOther = parcel.readInt();
110             numSourceLocal = parcel.readInt();
111             numSourceUser = parcel.readInt();
112             numSourcePeriodic = parcel.readInt();
113             numSourceFeed = parcel.readInt();
114             numFailures = parcel.readInt();
115             numCancels = parcel.readInt();
116         }
117     }
118 
119     public long lastTodayResetTime;
120 
121     public final Stats totalStats = new Stats();
122     public final Stats todayStats = new Stats();
123     public final Stats yesterdayStats = new Stats();
124 
125     @UnsupportedAppUsage
126     public long lastSuccessTime;
127     @UnsupportedAppUsage
128     public int lastSuccessSource;
129     @UnsupportedAppUsage
130     public long lastFailureTime;
131     @UnsupportedAppUsage
132     public int lastFailureSource;
133     @UnsupportedAppUsage
134     public String lastFailureMesg;
135     @UnsupportedAppUsage
136     public long initialFailureTime;
137     @UnsupportedAppUsage
138     public boolean pending;
139     @UnsupportedAppUsage
140     public boolean initialize;
141 
142     public final long[] perSourceLastSuccessTimes = new long[SOURCE_COUNT];
143     public final long[] perSourceLastFailureTimes = new long[SOURCE_COUNT];
144 
145     // Warning: It is up to the external caller to ensure there are
146     // no race conditions when accessing this list
147     @UnsupportedAppUsage
148     private ArrayList<Long> periodicSyncTimes;
149 
150     private final ArrayList<Long> mLastEventTimes = new ArrayList<>();
151     private final ArrayList<String> mLastEvents = new ArrayList<>();
152 
153     @UnsupportedAppUsage
SyncStatusInfo(int authorityId)154     public SyncStatusInfo(int authorityId) {
155         this.authorityId = authorityId;
156     }
157 
158     @UnsupportedAppUsage
getLastFailureMesgAsInt(int def)159     public int getLastFailureMesgAsInt(int def) {
160         final int i = ContentResolver.syncErrorStringToInt(lastFailureMesg);
161         if (i > 0) {
162             return i;
163         } else {
164             Log.d(TAG, "Unknown lastFailureMesg:" + lastFailureMesg);
165             return def;
166         }
167     }
168 
describeContents()169     public int describeContents() {
170         return 0;
171     }
172 
writeToParcel(Parcel parcel, int flags)173     public void writeToParcel(Parcel parcel, int flags) {
174         parcel.writeInt(VERSION);
175         parcel.writeInt(authorityId);
176 
177         // Note we can't use Stats.writeToParcel() here; see the below constructor for the reason.
178         parcel.writeLong(totalStats.totalElapsedTime);
179         parcel.writeInt(totalStats.numSyncs);
180         parcel.writeInt(totalStats.numSourcePoll);
181         parcel.writeInt(totalStats.numSourceOther);
182         parcel.writeInt(totalStats.numSourceLocal);
183         parcel.writeInt(totalStats.numSourceUser);
184 
185         parcel.writeLong(lastSuccessTime);
186         parcel.writeInt(lastSuccessSource);
187         parcel.writeLong(lastFailureTime);
188         parcel.writeInt(lastFailureSource);
189         parcel.writeString(lastFailureMesg);
190         parcel.writeLong(initialFailureTime);
191         parcel.writeInt(pending ? 1 : 0);
192         parcel.writeInt(initialize ? 1 : 0);
193         if (periodicSyncTimes != null) {
194             parcel.writeInt(periodicSyncTimes.size());
195             for (long periodicSyncTime : periodicSyncTimes) {
196                 parcel.writeLong(periodicSyncTime);
197             }
198         } else {
199             parcel.writeInt(-1);
200         }
201         parcel.writeInt(mLastEventTimes.size());
202         for (int i = 0; i < mLastEventTimes.size(); i++) {
203             parcel.writeLong(mLastEventTimes.get(i));
204             parcel.writeString(mLastEvents.get(i));
205         }
206         // Version 4
207         parcel.writeInt(totalStats.numSourcePeriodic);
208 
209         // Version 5
210         parcel.writeInt(totalStats.numSourceFeed);
211         parcel.writeInt(totalStats.numFailures);
212         parcel.writeInt(totalStats.numCancels);
213 
214         parcel.writeLong(lastTodayResetTime);
215 
216         todayStats.writeToParcel(parcel);
217         yesterdayStats.writeToParcel(parcel);
218 
219         // Version 6.
220         parcel.writeLongArray(perSourceLastSuccessTimes);
221         parcel.writeLongArray(perSourceLastFailureTimes);
222     }
223 
224     @UnsupportedAppUsage
SyncStatusInfo(Parcel parcel)225     public SyncStatusInfo(Parcel parcel) {
226         int version = parcel.readInt();
227         if (version != VERSION && version != 1) {
228             Log.w("SyncStatusInfo", "Unknown version: " + version);
229         }
230         authorityId = parcel.readInt();
231 
232         // Note we can't use Stats.writeToParcel() here because the data is persisted and we need
233         // to be able to read from the old format too.
234         totalStats.totalElapsedTime = parcel.readLong();
235         totalStats.numSyncs = parcel.readInt();
236         totalStats.numSourcePoll = parcel.readInt();
237         totalStats.numSourceOther = parcel.readInt();
238         totalStats.numSourceLocal = parcel.readInt();
239         totalStats.numSourceUser = parcel.readInt();
240         lastSuccessTime = parcel.readLong();
241         lastSuccessSource = parcel.readInt();
242         lastFailureTime = parcel.readLong();
243         lastFailureSource = parcel.readInt();
244         lastFailureMesg = parcel.readString();
245         initialFailureTime = parcel.readLong();
246         pending = parcel.readInt() != 0;
247         initialize = parcel.readInt() != 0;
248         if (version == 1) {
249             periodicSyncTimes = null;
250         } else {
251             final int count = parcel.readInt();
252             if (count < 0) {
253                 periodicSyncTimes = null;
254             } else {
255                 periodicSyncTimes = new ArrayList<Long>();
256                 for (int i = 0; i < count; i++) {
257                     periodicSyncTimes.add(parcel.readLong());
258                 }
259             }
260             if (version >= 3) {
261                 mLastEventTimes.clear();
262                 mLastEvents.clear();
263                 final int nEvents = parcel.readInt();
264                 for (int i = 0; i < nEvents; i++) {
265                     mLastEventTimes.add(parcel.readLong());
266                     mLastEvents.add(parcel.readString());
267                 }
268             }
269         }
270         if (version < 4) {
271             // Before version 4, numSourcePeriodic wasn't persisted.
272             totalStats.numSourcePeriodic =
273                     totalStats.numSyncs - totalStats.numSourceLocal - totalStats.numSourcePoll
274                             - totalStats.numSourceOther
275                             - totalStats.numSourceUser;
276             if (totalStats.numSourcePeriodic < 0) { // Consistency check.
277                 totalStats.numSourcePeriodic = 0;
278             }
279         } else {
280             totalStats.numSourcePeriodic = parcel.readInt();
281         }
282         if (version >= 5) {
283             totalStats.numSourceFeed = parcel.readInt();
284             totalStats.numFailures = parcel.readInt();
285             totalStats.numCancels = parcel.readInt();
286 
287             lastTodayResetTime = parcel.readLong();
288 
289             todayStats.readFromParcel(parcel);
290             yesterdayStats.readFromParcel(parcel);
291         }
292         if (version >= 6) {
293             parcel.readLongArray(perSourceLastSuccessTimes);
294             parcel.readLongArray(perSourceLastFailureTimes);
295         }
296     }
297 
298     /**
299      * Copies all data from the given SyncStatusInfo object.
300      *
301      * @param other the SyncStatusInfo object to copy data from
302      */
SyncStatusInfo(SyncStatusInfo other)303     public SyncStatusInfo(SyncStatusInfo other) {
304         authorityId = other.authorityId;
305         copyFrom(other);
306     }
307 
308     /**
309      * Copies all data from the given SyncStatusInfo object except for its authority id.
310      *
311      * @param authorityId the new authority id
312      * @param other the SyncStatusInfo object to copy data from
313      */
SyncStatusInfo(int authorityId, SyncStatusInfo other)314     public SyncStatusInfo(int authorityId, SyncStatusInfo other) {
315         this.authorityId = authorityId;
316         copyFrom(other);
317     }
318 
copyFrom(SyncStatusInfo other)319     private void copyFrom(SyncStatusInfo other) {
320         other.totalStats.copyTo(totalStats);
321         other.todayStats.copyTo(todayStats);
322         other.yesterdayStats.copyTo(yesterdayStats);
323 
324         lastTodayResetTime = other.lastTodayResetTime;
325 
326         lastSuccessTime = other.lastSuccessTime;
327         lastSuccessSource = other.lastSuccessSource;
328         lastFailureTime = other.lastFailureTime;
329         lastFailureSource = other.lastFailureSource;
330         lastFailureMesg = other.lastFailureMesg;
331         initialFailureTime = other.initialFailureTime;
332         pending = other.pending;
333         initialize = other.initialize;
334         if (other.periodicSyncTimes != null) {
335             periodicSyncTimes = new ArrayList<Long>(other.periodicSyncTimes);
336         }
337         mLastEventTimes.addAll(other.mLastEventTimes);
338         mLastEvents.addAll(other.mLastEvents);
339 
340         copy(perSourceLastSuccessTimes, other.perSourceLastSuccessTimes);
341         copy(perSourceLastFailureTimes, other.perSourceLastFailureTimes);
342     }
343 
copy(long[] to, long[] from)344     private static void copy(long[] to, long[] from) {
345         System.arraycopy(from, 0, to, 0, to.length);
346     }
347 
getPeriodicSyncTimesSize()348     public int getPeriodicSyncTimesSize() {
349         return periodicSyncTimes == null ? 0 : periodicSyncTimes.size();
350     }
351 
addPeriodicSyncTime(long time)352     public void addPeriodicSyncTime(long time) {
353         periodicSyncTimes = ArrayUtils.add(periodicSyncTimes, time);
354     }
355 
356     @UnsupportedAppUsage
setPeriodicSyncTime(int index, long when)357     public void setPeriodicSyncTime(int index, long when) {
358         // The list is initialized lazily when scheduling occurs so we need to make sure
359         // we initialize elements < index to zero (zero is ignore for scheduling purposes)
360         ensurePeriodicSyncTimeSize(index);
361         periodicSyncTimes.set(index, when);
362     }
363 
364     @UnsupportedAppUsage
getPeriodicSyncTime(int index)365     public long getPeriodicSyncTime(int index) {
366         if (periodicSyncTimes != null && index < periodicSyncTimes.size()) {
367             return periodicSyncTimes.get(index);
368         } else {
369             return 0;
370         }
371     }
372 
373     @UnsupportedAppUsage
removePeriodicSyncTime(int index)374     public void removePeriodicSyncTime(int index) {
375         if (periodicSyncTimes != null && index < periodicSyncTimes.size()) {
376             periodicSyncTimes.remove(index);
377         }
378     }
379 
380     /**
381      * Populates {@code mLastEventTimes} and {@code mLastEvents} with the given list. <br>
382      * <i>Note: This method is mainly used to repopulate the event info from disk and it will clear
383      * both {@code mLastEventTimes} and {@code mLastEvents} before populating.</i>
384      *
385      * @param lastEventInformation the list to populate with
386      */
populateLastEventsInformation(ArrayList<Pair<Long, String>> lastEventInformation)387     public void populateLastEventsInformation(ArrayList<Pair<Long, String>> lastEventInformation) {
388         mLastEventTimes.clear();
389         mLastEvents.clear();
390         final int size = lastEventInformation.size();
391         for (int i = 0; i < size; i++) {
392             final Pair<Long, String> lastEventInfo = lastEventInformation.get(i);
393             mLastEventTimes.add(lastEventInfo.first);
394             mLastEvents.add(lastEventInfo.second);
395         }
396     }
397 
398     /** */
addEvent(String message)399     public void addEvent(String message) {
400         if (mLastEventTimes.size() >= MAX_EVENT_COUNT) {
401             mLastEventTimes.remove(MAX_EVENT_COUNT - 1);
402             mLastEvents.remove(MAX_EVENT_COUNT - 1);
403         }
404         mLastEventTimes.add(0, System.currentTimeMillis());
405         mLastEvents.add(0, message);
406     }
407 
408     /** */
getEventCount()409     public int getEventCount() {
410         return mLastEventTimes.size();
411     }
412 
413     /** */
getEventTime(int i)414     public long getEventTime(int i) {
415         return mLastEventTimes.get(i);
416     }
417 
418     /** */
getEvent(int i)419     public String getEvent(int i) {
420         return mLastEvents.get(i);
421     }
422 
423     /** Call this when a sync has succeeded. */
setLastSuccess(int source, long lastSyncTime)424     public void setLastSuccess(int source, long lastSyncTime) {
425         lastSuccessTime = lastSyncTime;
426         lastSuccessSource = source;
427         lastFailureTime = 0;
428         lastFailureSource = -1;
429         lastFailureMesg = null;
430         initialFailureTime = 0;
431 
432         if (0 <= source && source < perSourceLastSuccessTimes.length) {
433             perSourceLastSuccessTimes[source] = lastSyncTime;
434         }
435     }
436 
437     /** Call this when a sync has failed. */
setLastFailure(int source, long lastSyncTime, String failureMessage)438     public void setLastFailure(int source, long lastSyncTime, String failureMessage) {
439         lastFailureTime = lastSyncTime;
440         lastFailureSource = source;
441         lastFailureMesg = failureMessage;
442         if (initialFailureTime == 0) {
443             initialFailureTime = lastSyncTime;
444         }
445 
446         if (0 <= source && source < perSourceLastFailureTimes.length) {
447             perSourceLastFailureTimes[source] = lastSyncTime;
448         }
449     }
450 
451     @UnsupportedAppUsage
452     public static final @android.annotation.NonNull Creator<SyncStatusInfo> CREATOR = new Creator<SyncStatusInfo>() {
453         public SyncStatusInfo createFromParcel(Parcel in) {
454             return new SyncStatusInfo(in);
455         }
456 
457         public SyncStatusInfo[] newArray(int size) {
458             return new SyncStatusInfo[size];
459         }
460     };
461 
462     @UnsupportedAppUsage
ensurePeriodicSyncTimeSize(int index)463     private void ensurePeriodicSyncTimeSize(int index) {
464         if (periodicSyncTimes == null) {
465             periodicSyncTimes = new ArrayList<Long>(0);
466         }
467 
468         final int requiredSize = index + 1;
469         if (periodicSyncTimes.size() < requiredSize) {
470             for (int i = periodicSyncTimes.size(); i < requiredSize; i++) {
471                 periodicSyncTimes.add((long) 0);
472             }
473         }
474     }
475 
476     /**
477      * If the last reset was not today, move today's stats to yesterday's and clear today's.
478      */
maybeResetTodayStats(boolean clockValid, boolean force)479     public void maybeResetTodayStats(boolean clockValid, boolean force) {
480         final long now = System.currentTimeMillis();
481 
482         if (!force) {
483             // Last reset was the same day, nothing to do.
484             if (areSameDates(now, lastTodayResetTime)) {
485                 return;
486             }
487 
488             // Hack -- on devices with no RTC, until the NTP kicks in, the device won't have the
489             // correct time. So if the time goes back, don't reset, unless we're sure the current
490             // time is correct.
491             if (now < lastTodayResetTime && !clockValid) {
492                 return;
493             }
494         }
495 
496         lastTodayResetTime = now;
497 
498         todayStats.copyTo(yesterdayStats);
499         todayStats.clear();
500     }
501 
areSameDates(long time1, long time2)502     private static boolean areSameDates(long time1, long time2) {
503         final Calendar c1 = new GregorianCalendar();
504         final Calendar c2 = new GregorianCalendar();
505 
506         c1.setTimeInMillis(time1);
507         c2.setTimeInMillis(time2);
508 
509         return c1.get(Calendar.YEAR) == c2.get(Calendar.YEAR)
510                 && c1.get(Calendar.DAY_OF_YEAR) == c2.get(Calendar.DAY_OF_YEAR);
511     }
512 }
513