Home | History | Annotate | Download | only in content
      1 /*
      2  * Copyright (C) 2013 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.accounts.Account;
     20 import android.os.Bundle;
     21 import android.os.Parcel;
     22 import android.os.Parcelable;
     23 
     24 /**
     25  * Convenience class to construct sync requests. See {@link android.content.SyncRequest.Builder}
     26  * for an explanation of the various functions. The resulting object is passed through to the
     27  * framework via {@link android.content.ContentResolver#requestSync(SyncRequest)}.
     28  */
     29 public class SyncRequest implements Parcelable {
     30     private static final String TAG = "SyncRequest";
     31     /** Account to pass to the sync adapter. Can be null. */
     32     private final Account mAccountToSync;
     33     /** Authority string that corresponds to a ContentProvider. */
     34     private final String mAuthority;
     35     /** Bundle containing user info as well as sync settings. */
     36     private final Bundle mExtras;
     37     /** Don't allow this sync request on metered networks. */
     38     private final boolean mDisallowMetered;
     39     /**
     40      * Amount of time before {@link #mSyncRunTimeSecs} from which the sync may optionally be
     41      * started.
     42      */
     43     private final long mSyncFlexTimeSecs;
     44     /**
     45      * Specifies a point in the future at which the sync must have been scheduled to run.
     46      */
     47     private final long mSyncRunTimeSecs;
     48     /** Periodic versus one-off. */
     49     private final boolean mIsPeriodic;
     50     /** Service versus provider. */
     51     private final boolean mIsAuthority;
     52     /** Sync should be run in lieu of other syncs. */
     53     private final boolean mIsExpedited;
     54 
     55     /**
     56      * {@hide}
     57      * @return whether this sync is periodic or one-time. A Sync Request must be
     58      *         either one of these or an InvalidStateException will be thrown in
     59      *         Builder.build().
     60      */
     61     public boolean isPeriodic() {
     62         return mIsPeriodic;
     63     }
     64 
     65     /**
     66      * {@hide}
     67      * @return whether this sync is expedited.
     68      */
     69     public boolean isExpedited() {
     70         return mIsExpedited;
     71     }
     72 
     73     /**
     74      * {@hide}
     75      *
     76      * @return account object for this sync.
     77      * @throws IllegalArgumentException if this function is called for a request that targets a
     78      * sync service.
     79      */
     80     public Account getAccount() {
     81         return mAccountToSync;
     82     }
     83 
     84     /**
     85      * {@hide}
     86      *
     87      * @return provider for this sync.
     88      * @throws IllegalArgumentException if this function is called for a request that targets a
     89      * sync service.
     90      */
     91     public String getProvider() {
     92         return mAuthority;
     93     }
     94 
     95     /**
     96      * {@hide}
     97      * Retrieve bundle for this SyncRequest. Will not be null.
     98      */
     99     public Bundle getBundle() {
    100         return mExtras;
    101     }
    102 
    103     /**
    104      * {@hide}
    105      * @return the earliest point in time that this sync can be scheduled.
    106      */
    107     public long getSyncFlexTime() {
    108         return mSyncFlexTimeSecs;
    109     }
    110     /**
    111      * {@hide}
    112      * @return the last point in time at which this sync must scheduled.
    113      */
    114     public long getSyncRunTime() {
    115         return mSyncRunTimeSecs;
    116     }
    117 
    118     public static final Creator<SyncRequest> CREATOR = new Creator<SyncRequest>() {
    119 
    120         @Override
    121         public SyncRequest createFromParcel(Parcel in) {
    122             return new SyncRequest(in);
    123         }
    124 
    125         @Override
    126         public SyncRequest[] newArray(int size) {
    127             return new SyncRequest[size];
    128         }
    129     };
    130 
    131     @Override
    132     public int describeContents() {
    133         return 0;
    134     }
    135 
    136     @Override
    137     public void writeToParcel(Parcel parcel, int flags) {
    138         parcel.writeBundle(mExtras);
    139         parcel.writeLong(mSyncFlexTimeSecs);
    140         parcel.writeLong(mSyncRunTimeSecs);
    141         parcel.writeInt((mIsPeriodic ? 1 : 0));
    142         parcel.writeInt((mDisallowMetered ? 1 : 0));
    143         parcel.writeInt((mIsAuthority ? 1 : 0));
    144         parcel.writeInt((mIsExpedited? 1 : 0));
    145         parcel.writeParcelable(mAccountToSync, flags);
    146         parcel.writeString(mAuthority);
    147     }
    148 
    149     private SyncRequest(Parcel in) {
    150         mExtras = Bundle.setDefusable(in.readBundle(), true);
    151         mSyncFlexTimeSecs = in.readLong();
    152         mSyncRunTimeSecs = in.readLong();
    153         mIsPeriodic = (in.readInt() != 0);
    154         mDisallowMetered = (in.readInt() != 0);
    155         mIsAuthority = (in.readInt() != 0);
    156         mIsExpedited = (in.readInt() != 0);
    157         mAccountToSync = in.readParcelable(null);
    158         mAuthority = in.readString();
    159     }
    160 
    161     /** {@hide} Protected ctor to instantiate anonymous SyncRequest. */
    162     protected SyncRequest(SyncRequest.Builder b) {
    163         mSyncFlexTimeSecs = b.mSyncFlexTimeSecs;
    164         mSyncRunTimeSecs = b.mSyncRunTimeSecs;
    165         mAccountToSync = b.mAccount;
    166         mAuthority = b.mAuthority;
    167         mIsPeriodic = (b.mSyncType == Builder.SYNC_TYPE_PERIODIC);
    168         mIsAuthority = (b.mSyncTarget == Builder.SYNC_TARGET_ADAPTER);
    169         mIsExpedited = b.mExpedited;
    170         mExtras = new Bundle(b.mCustomExtras);
    171         // For now we merge the sync config extras & the custom extras into one bundle.
    172         // TODO: pass the configuration extras through separately.
    173         mExtras.putAll(b.mSyncConfigExtras);
    174         mDisallowMetered = b.mDisallowMetered;
    175     }
    176 
    177     /**
    178      * Builder class for a {@link SyncRequest}. As you build your SyncRequest this class will also
    179      * perform validation.
    180      */
    181     public static class Builder {
    182         /** Unknown sync type. */
    183         private static final int SYNC_TYPE_UNKNOWN = 0;
    184         /** Specify that this is a periodic sync. */
    185         private static final int SYNC_TYPE_PERIODIC = 1;
    186         /** Specify that this is a one-time sync. */
    187         private static final int SYNC_TYPE_ONCE = 2;
    188         /** Unknown sync target. */
    189         private static final int SYNC_TARGET_UNKNOWN = 0;
    190         /** Specify that this is a sync with a provider. */
    191         private static final int SYNC_TARGET_ADAPTER = 2;
    192         /**
    193          * Earliest point of displacement into the future at which this sync can
    194          * occur.
    195          */
    196         private long mSyncFlexTimeSecs;
    197         /** Displacement into the future at which this sync must occur. */
    198         private long mSyncRunTimeSecs;
    199         /**
    200          * Sync configuration information - custom user data explicitly provided by the developer.
    201          * This data is handed over to the sync operation.
    202          */
    203         private Bundle mCustomExtras;
    204         /**
    205          * Sync system configuration -  used to store system sync configuration. Corresponds to
    206          * ContentResolver.SYNC_EXTRAS_* flags.
    207          * TODO: Use this instead of dumping into one bundle. Need to decide if these flags should
    208          * discriminate between equivalent syncs.
    209          */
    210         private Bundle mSyncConfigExtras;
    211         /** Whether or not this sync can occur on metered networks. Default false. */
    212         private boolean mDisallowMetered;
    213         /**
    214          * Whether this builder is building a periodic sync, or a one-time sync.
    215          */
    216         private int mSyncType = SYNC_TYPE_UNKNOWN;
    217         /** Whether this will go to a sync adapter. */
    218         private int mSyncTarget = SYNC_TARGET_UNKNOWN;
    219         /** Whether this is a user-activated sync. */
    220         private boolean mIsManual;
    221         /**
    222          * Whether to retry this one-time sync if the sync fails. Not valid for
    223          * periodic syncs. See {@link ContentResolver#SYNC_EXTRAS_DO_NOT_RETRY}.
    224          */
    225         private boolean mNoRetry;
    226         /**
    227          * Whether to respect back-off for this one-time sync. Not valid for
    228          * periodic syncs. See
    229          * {@link ContentResolver#SYNC_EXTRAS_IGNORE_BACKOFF};
    230          */
    231         private boolean mIgnoreBackoff;
    232 
    233         /** Ignore sync system settings and perform sync anyway. */
    234         private boolean mIgnoreSettings;
    235 
    236         /** This sync will run in preference to other non-expedited syncs. */
    237         private boolean mExpedited;
    238 
    239         /**
    240          * The Account object that together with an Authority name define the SyncAdapter (if
    241          * this sync is bound to a provider), otherwise null.
    242          */
    243         private Account mAccount;
    244         /**
    245          * The Authority name that together with an Account define the SyncAdapter (if
    246          * this sync is bound to a provider), otherwise null.
    247          */
    248         private String mAuthority;
    249         /**
    250          * Whether the sync requires the phone to be plugged in.
    251          */
    252         private boolean mRequiresCharging;
    253 
    254         public Builder() {
    255         }
    256 
    257         /**
    258          * Request that a sync occur immediately.
    259          *
    260          * Example
    261          * <pre>
    262          *     SyncRequest.Builder builder = (new SyncRequest.Builder()).syncOnce();
    263          * </pre>
    264          */
    265         public Builder syncOnce() {
    266             if (mSyncType != SYNC_TYPE_UNKNOWN) {
    267                 throw new IllegalArgumentException("Sync type has already been defined.");
    268             }
    269             mSyncType = SYNC_TYPE_ONCE;
    270             setupInterval(0, 0);
    271             return this;
    272         }
    273 
    274         /**
    275          * Build a periodic sync. Either this or syncOnce() <b>must</b> be called for this builder.
    276          * Syncs are identified by target {@link android.provider} and by the
    277          * contents of the extras bundle.
    278          * You cannot reuse the same builder for one-time syncs after having specified a periodic
    279          * sync (by calling this function). If you do, an <code>IllegalArgumentException</code>
    280          * will be thrown.
    281          * <p>The bundle for a periodic sync can be queried by applications with the correct
    282          * permissions using
    283          * {@link ContentResolver#getPeriodicSyncs(Account account, String provider)}, so no
    284          * sensitive data should be transferred here.
    285          *
    286          * Example usage.
    287          *
    288          * <pre>
    289          *     Request a periodic sync every 5 hours with 20 minutes of flex.
    290          *     SyncRequest.Builder builder =
    291          *         (new SyncRequest.Builder()).syncPeriodic(5 * HOUR_IN_SECS, 20 * MIN_IN_SECS);
    292          *
    293          *     Schedule a periodic sync every hour at any point in time during that hour.
    294          *     SyncRequest.Builder builder =
    295          *         (new SyncRequest.Builder()).syncPeriodic(1 * HOUR_IN_SECS, 1 * HOUR_IN_SECS);
    296          * </pre>
    297          *
    298          * N.B.: Periodic syncs are not allowed to have any of
    299          * {@link ContentResolver#SYNC_EXTRAS_DO_NOT_RETRY},
    300          * {@link ContentResolver#SYNC_EXTRAS_IGNORE_BACKOFF},
    301          * {@link ContentResolver#SYNC_EXTRAS_IGNORE_SETTINGS},
    302          * {@link ContentResolver#SYNC_EXTRAS_INITIALIZE},
    303          * {@link ContentResolver#SYNC_EXTRAS_FORCE},
    304          * {@link ContentResolver#SYNC_EXTRAS_EXPEDITED},
    305          * {@link ContentResolver#SYNC_EXTRAS_MANUAL}
    306          * set to true. If any are supplied then an <code>IllegalArgumentException</code> will
    307          * be thrown.
    308          *
    309          * @param pollFrequency the amount of time in seconds that you wish
    310          *            to elapse between periodic syncs. A minimum period of 1 hour is enforced.
    311          * @param beforeSeconds the amount of flex time in seconds before
    312          *            {@code pollFrequency} that you permit for the sync to take
    313          *            place. Must be less than {@code pollFrequency} and greater than
    314          *            MAX(5% of {@code pollFrequency}, 5 minutes)
    315          */
    316         public Builder syncPeriodic(long pollFrequency, long beforeSeconds) {
    317             if (mSyncType != SYNC_TYPE_UNKNOWN) {
    318                 throw new IllegalArgumentException("Sync type has already been defined.");
    319             }
    320             mSyncType = SYNC_TYPE_PERIODIC;
    321             setupInterval(pollFrequency, beforeSeconds);
    322             return this;
    323         }
    324 
    325         private void setupInterval(long at, long before) {
    326             if (before > at) {
    327                 throw new IllegalArgumentException("Specified run time for the sync must be" +
    328                     " after the specified flex time.");
    329             }
    330             mSyncRunTimeSecs = at;
    331             mSyncFlexTimeSecs = before;
    332         }
    333 
    334         /**
    335          * Will throw an <code>IllegalArgumentException</code> if called and
    336          * {@link #setIgnoreSettings(boolean ignoreSettings)} has already been called.
    337          * @param disallow true to allow this transfer on metered networks. Default false.
    338          *
    339          */
    340         public Builder setDisallowMetered(boolean disallow) {
    341             if (mIgnoreSettings && disallow) {
    342                 throw new IllegalArgumentException("setDisallowMetered(true) after having"
    343                         + " specified that settings are ignored.");
    344             }
    345             mDisallowMetered = disallow;
    346             return this;
    347         }
    348 
    349         /**
    350          * Specify whether the sync requires the phone to be plugged in.
    351          * @param requiresCharging true if sync requires the phone to be plugged in. Default false.
    352          */
    353         public Builder setRequiresCharging(boolean requiresCharging) {
    354             mRequiresCharging = requiresCharging;
    355             return this;
    356         }
    357 
    358         /**
    359          * Specify an authority and account for this transfer.
    360          *
    361          * @param authority A String identifying the content provider to be synced.
    362          * @param account Account to sync. Can be null unless this is a periodic
    363          *            sync, for which verification by the ContentResolver will
    364          *            fail. If a sync is performed without an account, the
    365          */
    366         public Builder setSyncAdapter(Account account, String authority) {
    367             if (mSyncTarget != SYNC_TARGET_UNKNOWN) {
    368                 throw new IllegalArgumentException("Sync target has already been defined.");
    369             }
    370             if (authority != null && authority.length() == 0) {
    371                 throw new IllegalArgumentException("Authority must be non-empty");
    372             }
    373             mSyncTarget = SYNC_TARGET_ADAPTER;
    374             mAccount = account;
    375             mAuthority = authority;
    376             return this;
    377         }
    378 
    379         /**
    380          * Developer-provided extras handed back when sync actually occurs. This bundle is copied
    381          * into the SyncRequest returned by {@link #build()}.
    382          *
    383          * Example:
    384          * <pre>
    385          *   String[] syncItems = {"dog", "cat", "frog", "child"};
    386          *   SyncRequest.Builder builder =
    387          *     new SyncRequest.Builder()
    388          *       .setSyncAdapter(dummyAccount, dummyProvider)
    389          *       .syncOnce();
    390          *
    391          *   for (String syncData : syncItems) {
    392          *     Bundle extras = new Bundle();
    393          *     extras.setString("data", syncData);
    394          *     builder.setExtras(extras);
    395          *     ContentResolver.sync(builder.build()); // Each sync() request creates a unique sync.
    396          *   }
    397          * </pre>
    398          * Only values of the following types may be used in the extras bundle:
    399          * <ul>
    400          * <li>Integer</li>
    401          * <li>Long</li>
    402          * <li>Boolean</li>
    403          * <li>Float</li>
    404          * <li>Double</li>
    405          * <li>String</li>
    406          * <li>Account</li>
    407          * <li>null</li>
    408          * </ul>
    409          * If any data is present in the bundle not of this type, build() will
    410          * throw a runtime exception.
    411          *
    412          * @param bundle extras bundle to set.
    413          */
    414         public Builder setExtras(Bundle bundle) {
    415             mCustomExtras = bundle;
    416             return this;
    417         }
    418 
    419         /**
    420          * Convenience function for setting {@link ContentResolver#SYNC_EXTRAS_DO_NOT_RETRY}.
    421          *
    422          * A one-off sync operation that fails will be retried with exponential back-off unless
    423          * this is set to false. Not valid for periodic sync and will throw an
    424          * <code>IllegalArgumentException</code> in build().
    425          *
    426          * @param noRetry true to not retry a failed sync. Default false.
    427          */
    428         public Builder setNoRetry(boolean noRetry) {
    429             mNoRetry = noRetry;
    430             return this;
    431         }
    432 
    433         /**
    434          * Convenience function for setting {@link ContentResolver#SYNC_EXTRAS_IGNORE_SETTINGS}.
    435          *
    436          * Not valid for periodic sync and will throw an <code>IllegalArgumentException</code> in
    437          * {@link #build()}.
    438          * <p>Throws <code>IllegalArgumentException</code> if called and
    439          * {@link #setDisallowMetered(boolean)} has been set.
    440          *
    441          *
    442          * @param ignoreSettings true to ignore the sync automatically settings. Default false.
    443          */
    444         public Builder setIgnoreSettings(boolean ignoreSettings) {
    445             if (mDisallowMetered && ignoreSettings) {
    446                 throw new IllegalArgumentException("setIgnoreSettings(true) after having specified"
    447                         + " sync settings with this builder.");
    448             }
    449             mIgnoreSettings = ignoreSettings;
    450             return this;
    451         }
    452 
    453         /**
    454          * Convenience function for setting {@link ContentResolver#SYNC_EXTRAS_IGNORE_BACKOFF}.
    455          *
    456          * Ignoring back-off will force the sync scheduling process to ignore any back-off that was
    457          * the result of a failed sync, as well as to invalidate any {@link SyncResult#delayUntil}
    458          * value that may have been set by the adapter. Successive failures will not honor this
    459          * flag. Not valid for periodic sync and will throw an <code>IllegalArgumentException</code>
    460          * in {@link #build()}.
    461          *
    462          * @param ignoreBackoff ignore back off settings. Default false.
    463          */
    464         public Builder setIgnoreBackoff(boolean ignoreBackoff) {
    465             mIgnoreBackoff = ignoreBackoff;
    466             return this;
    467         }
    468 
    469         /**
    470          * Convenience function for setting {@link ContentResolver#SYNC_EXTRAS_MANUAL}.
    471          *
    472          * Not valid for periodic sync and will throw an <code>IllegalArgumentException</code> in
    473          * {@link #build()}.
    474          *
    475          * @param isManual User-initiated sync or not. Default false.
    476          */
    477         public Builder setManual(boolean isManual) {
    478             mIsManual = isManual;
    479             return this;
    480         }
    481 
    482         /**
    483          * An expedited sync runs immediately and can preempt other non-expedited running syncs.
    484          *
    485          * Not valid for periodic sync and will throw an <code>IllegalArgumentException</code> in
    486          * {@link #build()}.
    487          *
    488          * @param expedited whether to run expedited. Default false.
    489          */
    490         public Builder setExpedited(boolean expedited) {
    491             mExpedited = expedited;
    492             return this;
    493         }
    494 
    495         /**
    496          * Performs validation over the request and throws the runtime exception
    497          * <code>IllegalArgumentException</code> if this validation fails.
    498          *
    499          * @return a SyncRequest with the information contained within this
    500          *         builder.
    501          */
    502         public SyncRequest build() {
    503             // Validate the extras bundle
    504             ContentResolver.validateSyncExtrasBundle(mCustomExtras);
    505             if (mCustomExtras == null) {
    506                 mCustomExtras = new Bundle();
    507             }
    508             // Combine builder extra flags into the config bundle.
    509             mSyncConfigExtras = new Bundle();
    510             if (mIgnoreBackoff) {
    511                 mSyncConfigExtras.putBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_BACKOFF, true);
    512             }
    513             if (mDisallowMetered) {
    514                 mSyncConfigExtras.putBoolean(ContentResolver.SYNC_EXTRAS_DISALLOW_METERED, true);
    515             }
    516             if (mRequiresCharging) {
    517                 mSyncConfigExtras.putBoolean(ContentResolver.SYNC_EXTRAS_REQUIRE_CHARGING, true);
    518             }
    519             if (mIgnoreSettings) {
    520                 mSyncConfigExtras.putBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_SETTINGS, true);
    521             }
    522             if (mNoRetry) {
    523                 mSyncConfigExtras.putBoolean(ContentResolver.SYNC_EXTRAS_DO_NOT_RETRY, true);
    524             }
    525             if (mExpedited) {
    526                 mSyncConfigExtras.putBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, true);
    527             }
    528             if (mIsManual) {
    529                 mSyncConfigExtras.putBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_BACKOFF, true);
    530                 mSyncConfigExtras.putBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_SETTINGS, true);
    531             }
    532             if (mSyncType == SYNC_TYPE_PERIODIC) {
    533                 // If this is a periodic sync ensure than invalid extras were not set.
    534                 if (ContentResolver.invalidPeriodicExtras(mCustomExtras) ||
    535                         ContentResolver.invalidPeriodicExtras(mSyncConfigExtras)) {
    536                     throw new IllegalArgumentException("Illegal extras were set");
    537                 }
    538             }
    539             // Ensure that a target for the sync has been set.
    540             if (mSyncTarget == SYNC_TARGET_UNKNOWN) {
    541                 throw new IllegalArgumentException("Must specify an adapter with" +
    542                         " setSyncAdapter(Account, String");
    543             }
    544             return new SyncRequest(this);
    545         }
    546     }
    547 }
    548