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.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. */
     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 
    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
    151     public SyncStatusInfo(int authorityId) {
    152         this.authorityId = authorityId;
    153     }
    154 
    155     @UnsupportedAppUsage
    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 
    166     public int describeContents() {
    167         return 0;
    168     }
    169 
    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
    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 
    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 
    322     private static void copy(long[] to, long[] from) {
    323         System.arraycopy(from, 0, to, 0, to.length);
    324     }
    325 
    326     @UnsupportedAppUsage
    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
    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
    344     public void removePeriodicSyncTime(int index) {
    345         if (periodicSyncTimes != null && index < periodicSyncTimes.size()) {
    346             periodicSyncTimes.remove(index);
    347         }
    348     }
    349 
    350     /** */
    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 
    375     /** 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 
    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
    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      */
    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     }
    464 }
    465