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     /** Internal boolean to avoid reading a bundle everytime we want to compare operations. */
     70     private final boolean expedited;
     71     public SyncStorageEngine.PendingOperation pendingOperation;
     72     /** Elapsed real time in millis at which to run this sync. */
     73     public long latestRunTime;
     74     /** Set by the SyncManager in order to delay retries. */
     75     public Long backoff;
     76     /** Specified by the adapter to delay subsequent sync operations. */
     77     public long delayUntil;
     78     /**
     79      * Elapsed real time in millis when this sync will be run.
     80      * Depends on max(backoff, latestRunTime, and delayUntil).
     81      */
     82     public long effectiveRunTime;
     83     /** Amount of time before {@link #effectiveRunTime} from which this sync can run. */
     84     public long flexTime;
     85 
     86     public SyncOperation(Account account, int userId, int reason, int source, String authority,
     87             Bundle extras, long runTimeFromNow, long flexTime, long backoff,
     88             long delayUntil, boolean allowParallelSyncs) {
     89         this.service = null;
     90         this.account = account;
     91         this.authority = authority;
     92         this.userId = userId;
     93         this.reason = reason;
     94         this.syncSource = source;
     95         this.allowParallelSyncs = allowParallelSyncs;
     96         this.extras = new Bundle(extras);
     97         cleanBundle(this.extras);
     98         this.delayUntil = delayUntil;
     99         this.backoff = backoff;
    100         final long now = SystemClock.elapsedRealtime();
    101         // Checks the extras bundle. Must occur after we set the internal bundle.
    102         if (runTimeFromNow < 0) {
    103             // Sanity check: Will always be true.
    104             if (!this.extras.getBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, false)) {
    105                 this.extras.putBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, true);
    106             }
    107             this.expedited = true;
    108             this.latestRunTime = now;
    109             this.flexTime = 0;
    110         } else {
    111             this.extras.remove(ContentResolver.SYNC_EXTRAS_EXPEDITED);
    112             this.expedited = false;
    113             this.latestRunTime = now + runTimeFromNow;
    114             this.flexTime = flexTime;
    115         }
    116         updateEffectiveRunTime();
    117         this.key = toKey();
    118     }
    119 
    120     /** Only used to immediately reschedule a sync. */
    121     SyncOperation(SyncOperation other) {
    122         this.service = other.service;
    123         this.account = other.account;
    124         this.authority = other.authority;
    125         this.userId = other.userId;
    126         this.reason = other.reason;
    127         this.syncSource = other.syncSource;
    128         this.extras = new Bundle(other.extras);
    129         this.expedited = other.expedited;
    130         this.latestRunTime = SystemClock.elapsedRealtime();
    131         this.flexTime = 0L;
    132         this.backoff = other.backoff;
    133         this.allowParallelSyncs = other.allowParallelSyncs;
    134         this.updateEffectiveRunTime();
    135         this.key = toKey();
    136     }
    137 
    138     /**
    139      * Make sure the bundle attached to this SyncOperation doesn't have unnecessary
    140      * flags set.
    141      * @param bundle to clean.
    142      */
    143     private void cleanBundle(Bundle bundle) {
    144         removeFalseExtra(bundle, ContentResolver.SYNC_EXTRAS_UPLOAD);
    145         removeFalseExtra(bundle, ContentResolver.SYNC_EXTRAS_MANUAL);
    146         removeFalseExtra(bundle, ContentResolver.SYNC_EXTRAS_IGNORE_SETTINGS);
    147         removeFalseExtra(bundle, ContentResolver.SYNC_EXTRAS_IGNORE_BACKOFF);
    148         removeFalseExtra(bundle, ContentResolver.SYNC_EXTRAS_DO_NOT_RETRY);
    149         removeFalseExtra(bundle, ContentResolver.SYNC_EXTRAS_DISCARD_LOCAL_DELETIONS);
    150         removeFalseExtra(bundle, ContentResolver.SYNC_EXTRAS_EXPEDITED);
    151         removeFalseExtra(bundle, ContentResolver.SYNC_EXTRAS_OVERRIDE_TOO_MANY_DELETIONS);
    152         removeFalseExtra(bundle, ContentResolver.SYNC_EXTRAS_DISALLOW_METERED);
    153 
    154         // Remove Config data.
    155         bundle.remove(ContentResolver.SYNC_EXTRAS_EXPECTED_UPLOAD);
    156         bundle.remove(ContentResolver.SYNC_EXTRAS_EXPECTED_DOWNLOAD);
    157     }
    158 
    159     private void removeFalseExtra(Bundle bundle, String extraName) {
    160         if (!bundle.getBoolean(extraName, false)) {
    161             bundle.remove(extraName);
    162         }
    163     }
    164 
    165     @Override
    166     public String toString() {
    167         return dump(null, true);
    168     }
    169 
    170     public String dump(PackageManager pm, boolean useOneLine) {
    171         StringBuilder sb = new StringBuilder()
    172                 .append(account.name)
    173                 .append(" u")
    174                 .append(userId).append(" (")
    175                 .append(account.type)
    176                 .append(")")
    177                 .append(", ")
    178                 .append(authority)
    179                 .append(", ")
    180                 .append(SyncStorageEngine.SOURCES[syncSource])
    181                 .append(", latestRunTime ")
    182                 .append(latestRunTime);
    183         if (expedited) {
    184             sb.append(", EXPEDITED");
    185         }
    186         sb.append(", reason: ");
    187         sb.append(reasonToString(pm, reason));
    188         if (!useOneLine && !extras.keySet().isEmpty()) {
    189             sb.append("\n    ");
    190             extrasToStringBuilder(extras, sb);
    191         }
    192         return sb.toString();
    193     }
    194 
    195     public static String reasonToString(PackageManager pm, int reason) {
    196         if (reason >= 0) {
    197             if (pm != null) {
    198                 final String[] packages = pm.getPackagesForUid(reason);
    199                 if (packages != null && packages.length == 1) {
    200                     return packages[0];
    201                 }
    202                 final String name = pm.getNameForUid(reason);
    203                 if (name != null) {
    204                     return name;
    205                 }
    206                 return String.valueOf(reason);
    207             } else {
    208                 return String.valueOf(reason);
    209             }
    210         } else {
    211             final int index = -reason - 1;
    212             if (index >= REASON_NAMES.length) {
    213                 return String.valueOf(reason);
    214             } else {
    215                 return REASON_NAMES[index];
    216             }
    217         }
    218     }
    219 
    220     public boolean isMeteredDisallowed() {
    221         return extras.getBoolean(ContentResolver.SYNC_EXTRAS_DISALLOW_METERED, false);
    222     }
    223 
    224     public boolean isInitialization() {
    225         return extras.getBoolean(ContentResolver.SYNC_EXTRAS_INITIALIZE, false);
    226     }
    227 
    228     public boolean isExpedited() {
    229         return expedited;
    230     }
    231 
    232     public boolean ignoreBackoff() {
    233         return extras.getBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_BACKOFF, false);
    234     }
    235 
    236     /** Changed in V3. */
    237     private String toKey() {
    238         StringBuilder sb = new StringBuilder();
    239         if (service == null) {
    240             sb.append("authority: ").append(authority);
    241             sb.append(" account {name=" + account.name + ", user=" + userId + ", type=" + account.type
    242                     + "}");
    243         } else {
    244             sb.append("service {package=" )
    245                 .append(service.getPackageName())
    246                 .append(" user=")
    247                 .append(userId)
    248                 .append(", class=")
    249                 .append(service.getClassName())
    250                 .append("}");
    251         }
    252         sb.append(" extras: ");
    253         extrasToStringBuilder(extras, sb);
    254         return sb.toString();
    255     }
    256 
    257     public static void extrasToStringBuilder(Bundle bundle, StringBuilder sb) {
    258         sb.append("[");
    259         for (String key : bundle.keySet()) {
    260             sb.append(key).append("=").append(bundle.get(key)).append(" ");
    261         }
    262         sb.append("]");
    263     }
    264 
    265     /**
    266      * Update the effective run time of this Operation based on latestRunTime (specified at
    267      * creation time of sync), delayUntil (specified by SyncAdapter), or backoff (specified by
    268      * SyncManager on soft failures).
    269      */
    270     public void updateEffectiveRunTime() {
    271         // Regardless of whether we're in backoff or honouring a delayUntil, we still incorporate
    272         // the flex time provided by the developer.
    273         effectiveRunTime = ignoreBackoff() ?
    274                 latestRunTime :
    275                     Math.max(Math.max(latestRunTime, delayUntil), backoff);
    276     }
    277 
    278     /**
    279      * SyncOperations are sorted based on their earliest effective run time.
    280      * This comparator is used to sort the SyncOps at a given time when
    281      * deciding which to run, so earliest run time is the best criteria.
    282      */
    283     @Override
    284     public int compareTo(Object o) {
    285         SyncOperation other = (SyncOperation) o;
    286         if (expedited != other.expedited) {
    287             return expedited ? -1 : 1;
    288         }
    289         long thisIntervalStart = Math.max(effectiveRunTime - flexTime, 0);
    290         long otherIntervalStart = Math.max(
    291             other.effectiveRunTime - other.flexTime, 0);
    292         if (thisIntervalStart < otherIntervalStart) {
    293             return -1;
    294         } else if (otherIntervalStart < thisIntervalStart) {
    295             return 1;
    296         } else {
    297             return 0;
    298         }
    299     }
    300 }
    301