SyncStatusInfo.java revision 9498621f416d0fac032a34cf5fbd0cfcffbff8e6
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
17package android.content;
18
19import android.os.Parcel;
20import android.os.Parcelable;
21import android.util.Log;
22
23import java.util.ArrayList;
24import java.util.Calendar;
25import java.util.GregorianCalendar;
26
27/** @hide */
28public class SyncStatusInfo implements Parcelable {
29    private static final String TAG = "Sync";
30
31    static final int VERSION = 5;
32
33    private static final int MAX_EVENT_COUNT = 10;
34
35    public final int authorityId;
36
37    /**
38     * # of syncs for each sync source, etc.
39     */
40    public static class Stats {
41        public long totalElapsedTime;
42        public int numSyncs;
43        public int numSourcePoll;
44        public int numSourceOther;
45        public int numSourceLocal;
46        public int numSourceUser;
47        public int numSourcePeriodic;
48        public int numSourceFeed;
49        public int numFailures;
50        public int numCancels;
51
52        /** Copy all the stats to another instance. */
53        public void copyTo(Stats to) {
54            to.totalElapsedTime = totalElapsedTime;
55            to.numSyncs = numSyncs;
56            to.numSourcePoll = numSourcePoll;
57            to.numSourceOther = numSourceOther;
58            to.numSourceLocal = numSourceLocal;
59            to.numSourceUser = numSourceUser;
60            to.numSourcePeriodic = numSourcePeriodic;
61            to.numSourceFeed = numSourceFeed;
62            to.numFailures = numFailures;
63            to.numCancels = numCancels;
64        }
65
66        /** Clear all the stats. */
67        public void clear() {
68            totalElapsedTime = 0;
69            numSyncs = 0;
70            numSourcePoll = 0;
71            numSourceOther = 0;
72            numSourceLocal = 0;
73            numSourceUser = 0;
74            numSourcePeriodic = 0;
75            numSourceFeed = 0;
76            numFailures = 0;
77            numCancels = 0;
78        }
79
80        /** Write all the stats to a parcel. */
81        public void writeToParcel(Parcel parcel) {
82            parcel.writeLong(totalElapsedTime);
83            parcel.writeInt(numSyncs);
84            parcel.writeInt(numSourcePoll);
85            parcel.writeInt(numSourceOther);
86            parcel.writeInt(numSourceLocal);
87            parcel.writeInt(numSourceUser);
88            parcel.writeInt(numSourcePeriodic);
89            parcel.writeInt(numSourceFeed);
90            parcel.writeInt(numFailures);
91            parcel.writeInt(numCancels);
92        }
93
94        /** Read all the stats from a parcel. */
95        public void readFromParcel(Parcel parcel) {
96            totalElapsedTime = parcel.readLong();
97            numSyncs = parcel.readInt();
98            numSourcePoll = parcel.readInt();
99            numSourceOther = parcel.readInt();
100            numSourceLocal = parcel.readInt();
101            numSourceUser = parcel.readInt();
102            numSourcePeriodic = parcel.readInt();
103            numSourceFeed = parcel.readInt();
104            numFailures = parcel.readInt();
105            numCancels = parcel.readInt();
106        }
107    }
108
109    public long lastTodayResetTime;
110
111    public final Stats totalStats = new Stats();
112    public final Stats todayStats = new Stats();
113    public final Stats yesterdayStats = new Stats();
114
115    public long lastSuccessTime;
116    public int lastSuccessSource;
117    public long lastFailureTime;
118    public int lastFailureSource;
119    public String lastFailureMesg;
120    public long initialFailureTime;
121    public boolean pending;
122    public boolean initialize;
123
124  // Warning: It is up to the external caller to ensure there are
125  // no race conditions when accessing this list
126  private ArrayList<Long> periodicSyncTimes;
127
128    private final ArrayList<Long> mLastEventTimes = new ArrayList<>();
129    private final ArrayList<String> mLastEvents = new ArrayList<>();
130
131    public SyncStatusInfo(int authorityId) {
132        this.authorityId = authorityId;
133    }
134
135    public int getLastFailureMesgAsInt(int def) {
136        final int i = ContentResolver.syncErrorStringToInt(lastFailureMesg);
137        if (i > 0) {
138            return i;
139        } else {
140            Log.d(TAG, "Unknown lastFailureMesg:" + lastFailureMesg);
141            return def;
142        }
143    }
144
145    public int describeContents() {
146        return 0;
147    }
148
149    public void writeToParcel(Parcel parcel, int flags) {
150        parcel.writeInt(VERSION);
151        parcel.writeInt(authorityId);
152
153        // Note we can't use Stats.writeToParcel() here; see the below constructor for the reason.
154        parcel.writeLong(totalStats.totalElapsedTime);
155        parcel.writeInt(totalStats.numSyncs);
156        parcel.writeInt(totalStats.numSourcePoll);
157        parcel.writeInt(totalStats.numSourceOther);
158        parcel.writeInt(totalStats.numSourceLocal);
159        parcel.writeInt(totalStats.numSourceUser);
160
161        parcel.writeLong(lastSuccessTime);
162        parcel.writeInt(lastSuccessSource);
163        parcel.writeLong(lastFailureTime);
164        parcel.writeInt(lastFailureSource);
165        parcel.writeString(lastFailureMesg);
166        parcel.writeLong(initialFailureTime);
167        parcel.writeInt(pending ? 1 : 0);
168        parcel.writeInt(initialize ? 1 : 0);
169        if (periodicSyncTimes != null) {
170            parcel.writeInt(periodicSyncTimes.size());
171            for (long periodicSyncTime : periodicSyncTimes) {
172                parcel.writeLong(periodicSyncTime);
173            }
174        } else {
175            parcel.writeInt(-1);
176        }
177        parcel.writeInt(mLastEventTimes.size());
178        for (int i = 0; i < mLastEventTimes.size(); i++) {
179            parcel.writeLong(mLastEventTimes.get(i));
180            parcel.writeString(mLastEvents.get(i));
181        }
182        // Version 4
183        parcel.writeInt(totalStats.numSourcePeriodic);
184
185        // Version 5
186        parcel.writeInt(totalStats.numSourceFeed);
187        parcel.writeInt(totalStats.numFailures);
188        parcel.writeInt(totalStats.numCancels);
189
190        parcel.writeLong(lastTodayResetTime);
191
192        todayStats.writeToParcel(parcel);
193        yesterdayStats.writeToParcel(parcel);
194    }
195
196    public SyncStatusInfo(Parcel parcel) {
197        int version = parcel.readInt();
198        if (version != VERSION && version != 1) {
199            Log.w("SyncStatusInfo", "Unknown version: " + version);
200        }
201        authorityId = parcel.readInt();
202
203        // Note we can't use Stats.writeToParcel() here because the data is persisted and we need
204        // to be able to read from the old format too.
205        totalStats.totalElapsedTime = parcel.readLong();
206        totalStats.numSyncs = parcel.readInt();
207        totalStats.numSourcePoll = parcel.readInt();
208        totalStats.numSourceOther = parcel.readInt();
209        totalStats.numSourceLocal = parcel.readInt();
210        totalStats.numSourceUser = parcel.readInt();
211        lastSuccessTime = parcel.readLong();
212        lastSuccessSource = parcel.readInt();
213        lastFailureTime = parcel.readLong();
214        lastFailureSource = parcel.readInt();
215        lastFailureMesg = parcel.readString();
216        initialFailureTime = parcel.readLong();
217        pending = parcel.readInt() != 0;
218        initialize = parcel.readInt() != 0;
219        if (version == 1) {
220            periodicSyncTimes = null;
221        } else {
222            final int count = parcel.readInt();
223            if (count < 0) {
224                periodicSyncTimes = null;
225            } else {
226                periodicSyncTimes = new ArrayList<Long>();
227                for (int i = 0; i < count; i++) {
228                    periodicSyncTimes.add(parcel.readLong());
229                }
230            }
231            if (version >= 3) {
232                mLastEventTimes.clear();
233                mLastEvents.clear();
234                final int nEvents = parcel.readInt();
235                for (int i = 0; i < nEvents; i++) {
236                    mLastEventTimes.add(parcel.readLong());
237                    mLastEvents.add(parcel.readString());
238                }
239            }
240        }
241        if (version < 4) {
242            // Before version 4, numSourcePeriodic wasn't persisted.
243            totalStats.numSourcePeriodic =
244                    totalStats.numSyncs - totalStats.numSourceLocal - totalStats.numSourcePoll
245                            - totalStats.numSourceOther
246                            - totalStats.numSourceUser;
247            if (totalStats.numSourcePeriodic < 0) { // Sanity check.
248                totalStats.numSourcePeriodic = 0;
249            }
250        } else {
251            totalStats.numSourcePeriodic = parcel.readInt();
252        }
253        if (version >= 5) {
254            totalStats.numSourceFeed = parcel.readInt();
255            totalStats.numFailures = parcel.readInt();
256            totalStats.numCancels = parcel.readInt();
257
258            lastTodayResetTime = parcel.readLong();
259
260            todayStats.readFromParcel(parcel);
261            yesterdayStats.readFromParcel(parcel);
262        }
263    }
264
265    public SyncStatusInfo(SyncStatusInfo other) {
266        authorityId = other.authorityId;
267
268        other.totalStats.copyTo(totalStats);
269        other.todayStats.copyTo(todayStats);
270        other.yesterdayStats.copyTo(yesterdayStats);
271
272        lastTodayResetTime = other.lastTodayResetTime;
273
274        lastSuccessTime = other.lastSuccessTime;
275        lastSuccessSource = other.lastSuccessSource;
276        lastFailureTime = other.lastFailureTime;
277        lastFailureSource = other.lastFailureSource;
278        lastFailureMesg = other.lastFailureMesg;
279        initialFailureTime = other.initialFailureTime;
280        pending = other.pending;
281        initialize = other.initialize;
282        if (other.periodicSyncTimes != null) {
283            periodicSyncTimes = new ArrayList<Long>(other.periodicSyncTimes);
284        }
285        mLastEventTimes.addAll(other.mLastEventTimes);
286        mLastEvents.addAll(other.mLastEvents);
287    }
288
289    public void setPeriodicSyncTime(int index, long when) {
290        // The list is initialized lazily when scheduling occurs so we need to make sure
291        // we initialize elements < index to zero (zero is ignore for scheduling purposes)
292        ensurePeriodicSyncTimeSize(index);
293        periodicSyncTimes.set(index, when);
294    }
295
296    public long getPeriodicSyncTime(int index) {
297        if (periodicSyncTimes != null && index < periodicSyncTimes.size()) {
298            return periodicSyncTimes.get(index);
299        } else {
300            return 0;
301        }
302    }
303
304    public void removePeriodicSyncTime(int index) {
305        if (periodicSyncTimes != null && index < periodicSyncTimes.size()) {
306            periodicSyncTimes.remove(index);
307        }
308    }
309
310    /** */
311    public void addEvent(String message) {
312        if (mLastEventTimes.size() >= MAX_EVENT_COUNT) {
313            mLastEventTimes.remove(MAX_EVENT_COUNT - 1);
314            mLastEvents.remove(MAX_EVENT_COUNT - 1);
315        }
316        mLastEventTimes.add(0, System.currentTimeMillis());
317        mLastEvents.add(0, message);
318    }
319
320    /** */
321    public int getEventCount() {
322        return mLastEventTimes.size();
323    }
324
325    /** */
326    public long getEventTime(int i) {
327        return mLastEventTimes.get(i);
328    }
329
330    /** */
331    public String getEvent(int i) {
332        return mLastEvents.get(i);
333    }
334
335    public static final Creator<SyncStatusInfo> CREATOR = new Creator<SyncStatusInfo>() {
336        public SyncStatusInfo createFromParcel(Parcel in) {
337            return new SyncStatusInfo(in);
338        }
339
340        public SyncStatusInfo[] newArray(int size) {
341            return new SyncStatusInfo[size];
342        }
343    };
344
345    private void ensurePeriodicSyncTimeSize(int index) {
346        if (periodicSyncTimes == null) {
347            periodicSyncTimes = new ArrayList<Long>(0);
348        }
349
350        final int requiredSize = index + 1;
351        if (periodicSyncTimes.size() < requiredSize) {
352            for (int i = periodicSyncTimes.size(); i < requiredSize; i++) {
353                periodicSyncTimes.add((long) 0);
354            }
355        }
356    }
357
358    /**
359     * If the last reset was not not today, move today's stats to yesterday's and clear today's.
360     */
361    public void maybeResetTodayStats(boolean clockValid, boolean force) {
362        final long now = System.currentTimeMillis();
363
364        if (!force) {
365            // Last reset was the same day, nothing to do.
366            if (areSameDates(now, lastTodayResetTime)) {
367                return;
368            }
369
370            // Hack -- on devices with no RTC, until the NTP kicks in, the device won't have the
371            // correct time. So if the time goes back, don't reset, unless we're sure the current
372            // time is correct.
373            if (now < lastTodayResetTime && !clockValid) {
374                return;
375            }
376        }
377
378        lastTodayResetTime = now;
379
380        todayStats.copyTo(yesterdayStats);
381        todayStats.clear();
382    }
383
384    private static boolean areSameDates(long time1, long time2) {
385        final Calendar c1 = new GregorianCalendar();
386        final Calendar c2 = new GregorianCalendar();
387
388        c1.setTimeInMillis(time1);
389        c2.setTimeInMillis(time2);
390
391        return c1.get(Calendar.YEAR) == c2.get(Calendar.YEAR)
392                && c1.get(Calendar.DAY_OF_YEAR) == c2.get(Calendar.DAY_OF_YEAR);
393    }
394}