Home | History | Annotate | Download | only in content
      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.os.Parcel;
     20 import android.os.Parcelable;
     21 import android.util.Log;
     22 
     23 import java.util.ArrayList;
     24 import java.util.Calendar;
     25 import java.util.GregorianCalendar;
     26 
     27 /** @hide */
     28 public class SyncStatusInfo implements Parcelable {
     29     private static final String TAG = "Sync";
     30 
     31     static final int VERSION = 6;
     32 
     33     private static final int MAX_EVENT_COUNT = 10;
     34 
     35     /**
     36      * Number of sync sources. KEEP THIS AND SyncStorageEngine.SOURCES IN SYNC.
     37      */
     38     private static final int SOURCE_COUNT = 6;
     39 
     40     public final int authorityId;
     41 
     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 
    120     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;
    127     public boolean initialize;
    128 
    129     public final long[] perSourceLastSuccessTimes = new long[SOURCE_COUNT];
    130     public final long[] perSourceLastFailureTimes = new long[SOURCE_COUNT];
    131 
    132   // 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;
    135 
    136     private final ArrayList<Long> mLastEventTimes = new ArrayList<>();
    137     private final ArrayList<String> mLastEvents = new ArrayList<>();
    138 
    139     public SyncStatusInfo(int authorityId) {
    140         this.authorityId = authorityId;
    141     }
    142 
    143     public int getLastFailureMesgAsInt(int def) {
    144         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;
    150         }
    151     }
    152 
    153     public int describeContents() {
    154         return 0;
    155     }
    156 
    157     public void writeToParcel(Parcel parcel, int flags) {
    158         parcel.writeInt(VERSION);
    159         parcel.writeInt(authorityId);
    160 
    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 
    169         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);
    176         parcel.writeInt(initialize ? 1 : 0);
    177         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         }
    185         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         }
    190         // 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);
    202 
    203         // Version 6.
    204         parcel.writeLongArray(perSourceLastSuccessTimes);
    205         parcel.writeLongArray(perSourceLastFailureTimes);
    206     }
    207 
    208     public SyncStatusInfo(Parcel parcel) {
    209         int version = parcel.readInt();
    210         if (version != VERSION && version != 1) {
    211             Log.w("SyncStatusInfo", "Unknown version: " + version);
    212         }
    213         authorityId = parcel.readInt();
    214 
    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();
    223         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;
    230         initialize = parcel.readInt() != 0;
    231         if (version == 1) {
    232             periodicSyncTimes = null;
    233         } else {
    234             final int count = parcel.readInt();
    235             if (count < 0) {
    236                 periodicSyncTimes = null;
    237             } else {
    238                 periodicSyncTimes = new ArrayList<Long>();
    239                 for (int i = 0; i < count; i++) {
    240                     periodicSyncTimes.add(parcel.readLong());
    241                 }
    242             }
    243             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             }
    252         }
    253         if (version < 4) {
    254             // Before version 4, numSourcePeriodic wasn't persisted.
    255             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;
    261             }
    262         } else {
    263             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);
    274         }
    275         if (version >= 6) {
    276             parcel.readLongArray(perSourceLastSuccessTimes);
    277             parcel.readLongArray(perSourceLastFailureTimes);
    278         }
    279     }
    280 
    281     public SyncStatusInfo(SyncStatusInfo other) {
    282         authorityId = other.authorityId;
    283 
    284         other.totalStats.copyTo(totalStats);
    285         other.todayStats.copyTo(todayStats);
    286         other.yesterdayStats.copyTo(yesterdayStats);
    287 
    288         lastTodayResetTime = other.lastTodayResetTime;
    289 
    290         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         }
    301         mLastEventTimes.addAll(other.mLastEventTimes);
    302         mLastEvents.addAll(other.mLastEvents);
    303 
    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);
    310     }
    311 
    312     public void setPeriodicSyncTime(int index, long when) {
    313         // 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)
    315         ensurePeriodicSyncTimeSize(index);
    316         periodicSyncTimes.set(index, when);
    317     }
    318 
    319     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 
    333     /** */
    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 
    358     /** 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 
    386     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 
    396     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     }
    408 
    409     /**
    410      * If the last reset was not today, move today's stats to yesterday's and clear today's.
    411      */
    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     }
    445 }
    446