Home | History | Annotate | Download | only in content
      1 /*
      2  * Copyright (C) 2010 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 com.android.server.content;
     18 
     19 import android.accounts.Account;
     20 import android.content.pm.PackageManager;
     21 import android.content.ComponentName;
     22 import android.content.ContentResolver;
     23 import android.content.SyncRequest;
     24 import android.os.Bundle;
     25 import android.os.SystemClock;
     26 import android.util.Pair;
     27 
     28 /**
     29  * Value type that represents a sync operation.
     30  * TODO: This is the class to flesh out with all the scheduling data - metered/unmetered,
     31  * transfer-size, etc.
     32  * {@hide}
     33  */
     34 public class SyncOperation implements Comparable {
     35     public static final int REASON_BACKGROUND_DATA_SETTINGS_CHANGED = -1;
     36     public static final int REASON_ACCOUNTS_UPDATED = -2;
     37     public static final int REASON_SERVICE_CHANGED = -3;
     38     public static final int REASON_PERIODIC = -4;
     39     public static final int REASON_IS_SYNCABLE = -5;
     40     /** Sync started because it has just been set to sync automatically. */
     41     public static final int REASON_SYNC_AUTO = -6;
     42     /** Sync started because master sync automatically has been set to true. */
     43     public static final int REASON_MASTER_SYNC_AUTO = -7;
     44     public static final int REASON_USER_START = -8;
     45 
     46     private static String[] REASON_NAMES = new String[] {
     47             "DataSettingsChanged",
     48             "AccountsUpdated",
     49             "ServiceChanged",
     50             "Periodic",
     51             "IsSyncable",
     52             "AutoSync",
     53             "MasterSyncAuto",
     54             "UserStart",
     55     };
     56 
     57     /** Account info to identify a SyncAdapter registered with the system. */
     58     public final Account account;
     59     /** Authority info to identify a SyncAdapter registered with the system. */
     60     public final String authority;
     61     /** Service to which this operation will bind to perform the sync. */
     62     public final ComponentName service;
     63     public final int userId;
     64     public final int reason;
     65     public int syncSource;
     66     public final boolean allowParallelSyncs;
     67     public Bundle extras;
     68     public final String key;
     69     public boolean expedited;
     70     public SyncStorageEngine.PendingOperation pendingOperation;
     71     /** Elapsed real time in millis at which to run this sync. */
     72     public long latestRunTime;
     73     /** Set by the SyncManager in order to delay retries. */
     74     public Long backoff;
     75     /** Specified by the adapter to delay subsequent sync operations. */
     76     public long delayUntil;
     77     /**
     78      * Elapsed real time in millis when this sync will be run.
     79      * Depends on max(backoff, latestRunTime, and delayUntil).
     80      */
     81     public long effectiveRunTime;
     82     /** Amount of time before {@link effectiveRunTime} from which this sync can run. */
     83     public long flexTime;
     84 
     85     public SyncOperation(Account account, int userId, int reason, int source, String authority,
     86             Bundle extras, long runTimeFromNow, long flexTime, long backoff,
     87             long delayUntil, boolean allowParallelSyncs) {
     88         this.service = null;
     89         this.account = account;
     90         this.authority = authority;
     91         this.userId = userId;
     92         this.reason = reason;
     93         this.syncSource = source;
     94         this.allowParallelSyncs = allowParallelSyncs;
     95         this.extras = new Bundle(extras);
     96         cleanBundle(this.extras);
     97         this.delayUntil = delayUntil;
     98         this.backoff = backoff;
     99         final long now = SystemClock.elapsedRealtime();
    100         // Checks the extras bundle. Must occur after we set the internal bundle.
    101         if (runTimeFromNow < 0 || isExpedited()) {
    102             this.expedited = true;
    103             this.latestRunTime = now;
    104             this.flexTime = 0;
    105         } else {
    106             this.expedited = false;
    107             this.latestRunTime = now + runTimeFromNow;
    108             this.flexTime = flexTime;
    109         }
    110         updateEffectiveRunTime();
    111         this.key = toKey();
    112     }
    113 
    114     /**
    115      * Make sure the bundle attached to this SyncOperation doesn't have unnecessary
    116      * flags set.
    117      * @param bundle to clean.
    118      */
    119     private void cleanBundle(Bundle bundle) {
    120         removeFalseExtra(bundle, ContentResolver.SYNC_EXTRAS_UPLOAD);
    121         removeFalseExtra(bundle, ContentResolver.SYNC_EXTRAS_MANUAL);
    122         removeFalseExtra(bundle, ContentResolver.SYNC_EXTRAS_IGNORE_SETTINGS);
    123         removeFalseExtra(bundle, ContentResolver.SYNC_EXTRAS_IGNORE_BACKOFF);
    124         removeFalseExtra(bundle, ContentResolver.SYNC_EXTRAS_DO_NOT_RETRY);
    125         removeFalseExtra(bundle, ContentResolver.SYNC_EXTRAS_DISCARD_LOCAL_DELETIONS);
    126         removeFalseExtra(bundle, ContentResolver.SYNC_EXTRAS_EXPEDITED);
    127         removeFalseExtra(bundle, ContentResolver.SYNC_EXTRAS_OVERRIDE_TOO_MANY_DELETIONS);
    128         removeFalseExtra(bundle, ContentResolver.SYNC_EXTRAS_DISALLOW_METERED);
    129 
    130         // Remove Config data.
    131         bundle.remove(ContentResolver.SYNC_EXTRAS_EXPECTED_UPLOAD);
    132         bundle.remove(ContentResolver.SYNC_EXTRAS_EXPECTED_DOWNLOAD);
    133     }
    134 
    135     private void removeFalseExtra(Bundle bundle, String extraName) {
    136         if (!bundle.getBoolean(extraName, false)) {
    137             bundle.remove(extraName);
    138         }
    139     }
    140 
    141     /** Only used to immediately reschedule a sync. */
    142     SyncOperation(SyncOperation other) {
    143         this.service = other.service;
    144         this.account = other.account;
    145         this.authority = other.authority;
    146         this.userId = other.userId;
    147         this.reason = other.reason;
    148         this.syncSource = other.syncSource;
    149         this.extras = new Bundle(other.extras);
    150         this.expedited = other.expedited;
    151         this.latestRunTime = SystemClock.elapsedRealtime();
    152         this.flexTime = 0L;
    153         this.backoff = other.backoff;
    154         this.allowParallelSyncs = other.allowParallelSyncs;
    155         this.updateEffectiveRunTime();
    156         this.key = toKey();
    157     }
    158 
    159     @Override
    160     public String toString() {
    161         return dump(null, true);
    162     }
    163 
    164     public String dump(PackageManager pm, boolean useOneLine) {
    165         StringBuilder sb = new StringBuilder()
    166                 .append(account.name)
    167                 .append(" u")
    168                 .append(userId).append(" (")
    169                 .append(account.type)
    170                 .append(")")
    171                 .append(", ")
    172                 .append(authority)
    173                 .append(", ")
    174                 .append(SyncStorageEngine.SOURCES[syncSource])
    175                 .append(", latestRunTime ")
    176                 .append(latestRunTime);
    177         if (expedited) {
    178             sb.append(", EXPEDITED");
    179         }
    180         sb.append(", reason: ");
    181         sb.append(reasonToString(pm, reason));
    182         if (!useOneLine && !extras.keySet().isEmpty()) {
    183             sb.append("\n    ");
    184             extrasToStringBuilder(extras, sb);
    185         }
    186         return sb.toString();
    187     }
    188 
    189     public static String reasonToString(PackageManager pm, int reason) {
    190         if (reason >= 0) {
    191             if (pm != null) {
    192                 final String[] packages = pm.getPackagesForUid(reason);
    193                 if (packages != null && packages.length == 1) {
    194                     return packages[0];
    195                 }
    196                 final String name = pm.getNameForUid(reason);
    197                 if (name != null) {
    198                     return name;
    199                 }
    200                 return String.valueOf(reason);
    201             } else {
    202                 return String.valueOf(reason);
    203             }
    204         } else {
    205             final int index = -reason - 1;
    206             if (index >= REASON_NAMES.length) {
    207                 return String.valueOf(reason);
    208             } else {
    209                 return REASON_NAMES[index];
    210             }
    211         }
    212     }
    213 
    214     public boolean isMeteredDisallowed() {
    215         return extras.getBoolean(ContentResolver.SYNC_EXTRAS_DISALLOW_METERED, false);
    216     }
    217 
    218     public boolean isInitialization() {
    219         return extras.getBoolean(ContentResolver.SYNC_EXTRAS_INITIALIZE, false);
    220     }
    221 
    222     public boolean isExpedited() {
    223         return extras.getBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, false) || expedited;
    224     }
    225 
    226     public boolean ignoreBackoff() {
    227         return extras.getBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_BACKOFF, false);
    228     }
    229 
    230     /** Changed in V3. */
    231     private String toKey() {
    232         StringBuilder sb = new StringBuilder();
    233         if (service == null) {
    234             sb.append("authority: ").append(authority);
    235             sb.append(" account {name=" + account.name + ", user=" + userId + ", type=" + account.type
    236                     + "}");
    237         } else {
    238             sb.append("service {package=" )
    239                 .append(service.getPackageName())
    240                 .append(" user=")
    241                 .append(userId)
    242                 .append(", class=")
    243                 .append(service.getClassName())
    244                 .append("}");
    245         }
    246         sb.append(" extras: ");
    247         extrasToStringBuilder(extras, sb);
    248         return sb.toString();
    249     }
    250 
    251     public static void extrasToStringBuilder(Bundle bundle, StringBuilder sb) {
    252         sb.append("[");
    253         for (String key : bundle.keySet()) {
    254             sb.append(key).append("=").append(bundle.get(key)).append(" ");
    255         }
    256         sb.append("]");
    257     }
    258 
    259     /**
    260      * Update the effective run time of this Operation based on latestRunTime (specified at
    261      * creation time of sync), delayUntil (specified by SyncAdapter), or backoff (specified by
    262      * SyncManager on soft failures).
    263      */
    264     public void updateEffectiveRunTime() {
    265         // Regardless of whether we're in backoff or honouring a delayUntil, we still incorporate
    266         // the flex time provided by the developer.
    267         effectiveRunTime = ignoreBackoff() ?
    268                 latestRunTime :
    269                     Math.max(Math.max(latestRunTime, delayUntil), backoff);
    270     }
    271 
    272     /**
    273      * SyncOperations are sorted based on their earliest effective run time.
    274      * This comparator is used to sort the SyncOps at a given time when
    275      * deciding which to run, so earliest run time is the best criteria.
    276      */
    277     @Override
    278     public int compareTo(Object o) {
    279         SyncOperation other = (SyncOperation) o;
    280         if (expedited != other.expedited) {
    281             return expedited ? -1 : 1;
    282         }
    283         long thisIntervalStart = Math.max(effectiveRunTime - flexTime, 0);
    284         long otherIntervalStart = Math.max(
    285             other.effectiveRunTime - other.flexTime, 0);
    286         if (thisIntervalStart < otherIntervalStart) {
    287             return -1;
    288         } else if (otherIntervalStart < thisIntervalStart) {
    289             return 1;
    290         } else {
    291             return 0;
    292         }
    293     }
    294 }
    295