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