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 = in.readBundle(); 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 public Builder() { 251 } 252 253 /** 254 * Request that a sync occur immediately. 255 * 256 * Example 257 * <pre> 258 * SyncRequest.Builder builder = (new SyncRequest.Builder()).syncOnce(); 259 * </pre> 260 */ 261 public Builder syncOnce() { 262 if (mSyncType != SYNC_TYPE_UNKNOWN) { 263 throw new IllegalArgumentException("Sync type has already been defined."); 264 } 265 mSyncType = SYNC_TYPE_ONCE; 266 setupInterval(0, 0); 267 return this; 268 } 269 270 /** 271 * Build a periodic sync. Either this or syncOnce() <b>must</b> be called for this builder. 272 * Syncs are identified by target {@link android.provider} and by the 273 * contents of the extras bundle. 274 * You cannot reuse the same builder for one-time syncs after having specified a periodic 275 * sync (by calling this function). If you do, an <code>IllegalArgumentException</code> 276 * will be thrown. 277 * <p>The bundle for a periodic sync can be queried by applications with the correct 278 * permissions using 279 * {@link ContentResolver#getPeriodicSyncs(Account account, String provider)}, so no 280 * sensitive data should be transferred here. 281 * 282 * Example usage. 283 * 284 * <pre> 285 * Request a periodic sync every 5 hours with 20 minutes of flex. 286 * SyncRequest.Builder builder = 287 * (new SyncRequest.Builder()).syncPeriodic(5 * HOUR_IN_SECS, 20 * MIN_IN_SECS); 288 * 289 * Schedule a periodic sync every hour at any point in time during that hour. 290 * SyncRequest.Builder builder = 291 * (new SyncRequest.Builder()).syncPeriodic(1 * HOUR_IN_SECS, 1 * HOUR_IN_SECS); 292 * </pre> 293 * 294 * N.B.: Periodic syncs are not allowed to have any of 295 * {@link ContentResolver#SYNC_EXTRAS_DO_NOT_RETRY}, 296 * {@link ContentResolver#SYNC_EXTRAS_IGNORE_BACKOFF}, 297 * {@link ContentResolver#SYNC_EXTRAS_IGNORE_SETTINGS}, 298 * {@link ContentResolver#SYNC_EXTRAS_INITIALIZE}, 299 * {@link ContentResolver#SYNC_EXTRAS_FORCE}, 300 * {@link ContentResolver#SYNC_EXTRAS_EXPEDITED}, 301 * {@link ContentResolver#SYNC_EXTRAS_MANUAL} 302 * set to true. If any are supplied then an <code>IllegalArgumentException</code> will 303 * be thrown. 304 * 305 * @param pollFrequency the amount of time in seconds that you wish 306 * to elapse between periodic syncs. 307 * @param beforeSeconds the amount of flex time in seconds before 308 * {@code pollFrequency} that you permit for the sync to take 309 * place. Must be less than {@code pollFrequency}. 310 */ 311 public Builder syncPeriodic(long pollFrequency, long beforeSeconds) { 312 if (mSyncType != SYNC_TYPE_UNKNOWN) { 313 throw new IllegalArgumentException("Sync type has already been defined."); 314 } 315 mSyncType = SYNC_TYPE_PERIODIC; 316 setupInterval(pollFrequency, beforeSeconds); 317 return this; 318 } 319 320 private void setupInterval(long at, long before) { 321 if (before > at) { 322 throw new IllegalArgumentException("Specified run time for the sync must be" + 323 " after the specified flex time."); 324 } 325 mSyncRunTimeSecs = at; 326 mSyncFlexTimeSecs = before; 327 } 328 329 /** 330 * Will throw an <code>IllegalArgumentException</code> if called and 331 * {@link #setIgnoreSettings(boolean ignoreSettings)} has already been called. 332 * @param disallow true to allow this transfer on metered networks. Default false. 333 * 334 */ 335 public Builder setDisallowMetered(boolean disallow) { 336 if (mIgnoreSettings && disallow) { 337 throw new IllegalArgumentException("setDisallowMetered(true) after having" 338 + "specified that settings are ignored."); 339 } 340 mDisallowMetered = disallow; 341 return this; 342 } 343 344 /** 345 * Specify an authority and account for this transfer. 346 * 347 * @param authority A String identifying the content provider to be synced. 348 * @param account Account to sync. Can be null unless this is a periodic 349 * sync, for which verification by the ContentResolver will 350 * fail. If a sync is performed without an account, the 351 */ 352 public Builder setSyncAdapter(Account account, String authority) { 353 if (mSyncTarget != SYNC_TARGET_UNKNOWN) { 354 throw new IllegalArgumentException("Sync target has already been defined."); 355 } 356 if (authority != null && authority.length() == 0) { 357 throw new IllegalArgumentException("Authority must be non-empty"); 358 } 359 mSyncTarget = SYNC_TARGET_ADAPTER; 360 mAccount = account; 361 mAuthority = authority; 362 return this; 363 } 364 365 /** 366 * Developer-provided extras handed back when sync actually occurs. This bundle is copied 367 * into the SyncRequest returned by {@link #build()}. 368 * 369 * Example: 370 * <pre> 371 * String[] syncItems = {"dog", "cat", "frog", "child"}; 372 * SyncRequest.Builder builder = 373 * new SyncRequest.Builder() 374 * .setSyncAdapter(dummyAccount, dummyProvider) 375 * .syncOnce(); 376 * 377 * for (String syncData : syncItems) { 378 * Bundle extras = new Bundle(); 379 * extras.setString("data", syncData); 380 * builder.setExtras(extras); 381 * ContentResolver.sync(builder.build()); // Each sync() request creates a unique sync. 382 * } 383 * </pre> 384 * Only values of the following types may be used in the extras bundle: 385 * <ul> 386 * <li>Integer</li> 387 * <li>Long</li> 388 * <li>Boolean</li> 389 * <li>Float</li> 390 * <li>Double</li> 391 * <li>String</li> 392 * <li>Account</li> 393 * <li>null</li> 394 * </ul> 395 * If any data is present in the bundle not of this type, build() will 396 * throw a runtime exception. 397 * 398 * @param bundle extras bundle to set. 399 */ 400 public Builder setExtras(Bundle bundle) { 401 mCustomExtras = bundle; 402 return this; 403 } 404 405 /** 406 * Convenience function for setting {@link ContentResolver#SYNC_EXTRAS_DO_NOT_RETRY}. 407 * 408 * A one-off sync operation that fails will be retried with exponential back-off unless 409 * this is set to false. Not valid for periodic sync and will throw an 410 * <code>IllegalArgumentException</code> in build(). 411 * 412 * @param noRetry true to not retry a failed sync. Default false. 413 */ 414 public Builder setNoRetry(boolean noRetry) { 415 mNoRetry = noRetry; 416 return this; 417 } 418 419 /** 420 * Convenience function for setting {@link ContentResolver#SYNC_EXTRAS_IGNORE_SETTINGS}. 421 * 422 * Not valid for periodic sync and will throw an <code>IllegalArgumentException</code> in 423 * {@link #build()}. 424 * <p>Throws <code>IllegalArgumentException</code> if called and 425 * {@link #setDisallowMetered(boolean)} has been set. 426 * 427 * 428 * @param ignoreSettings true to ignore the sync automatically settings. Default false. 429 */ 430 public Builder setIgnoreSettings(boolean ignoreSettings) { 431 if (mDisallowMetered && ignoreSettings) { 432 throw new IllegalArgumentException("setIgnoreSettings(true) after having specified" 433 + " sync settings with this builder."); 434 } 435 mIgnoreSettings = ignoreSettings; 436 return this; 437 } 438 439 /** 440 * Convenience function for setting {@link ContentResolver#SYNC_EXTRAS_IGNORE_BACKOFF}. 441 * 442 * Ignoring back-off will force the sync scheduling process to ignore any back-off that was 443 * the result of a failed sync, as well as to invalidate any {@link SyncResult#delayUntil} 444 * value that may have been set by the adapter. Successive failures will not honor this 445 * flag. Not valid for periodic sync and will throw an <code>IllegalArgumentException</code> 446 * in {@link #build()}. 447 * 448 * @param ignoreBackoff ignore back off settings. Default false. 449 */ 450 public Builder setIgnoreBackoff(boolean ignoreBackoff) { 451 mIgnoreBackoff = ignoreBackoff; 452 return this; 453 } 454 455 /** 456 * Convenience function for setting {@link ContentResolver#SYNC_EXTRAS_MANUAL}. 457 * 458 * Not valid for periodic sync and will throw an <code>IllegalArgumentException</code> in 459 * {@link #build()}. 460 * 461 * @param isManual User-initiated sync or not. Default false. 462 */ 463 public Builder setManual(boolean isManual) { 464 mIsManual = isManual; 465 return this; 466 } 467 468 /** 469 * An expedited sync runs immediately and can preempt other non-expedited running syncs. 470 * 471 * Not valid for periodic sync and will throw an <code>IllegalArgumentException</code> in 472 * {@link #build()}. 473 * 474 * @param expedited whether to run expedited. Default false. 475 */ 476 public Builder setExpedited(boolean expedited) { 477 mExpedited = expedited; 478 return this; 479 } 480 481 /** 482 * Performs validation over the request and throws the runtime exception 483 * <code>IllegalArgumentException</code> if this validation fails. 484 * 485 * @return a SyncRequest with the information contained within this 486 * builder. 487 */ 488 public SyncRequest build() { 489 // Validate the extras bundle 490 ContentResolver.validateSyncExtrasBundle(mCustomExtras); 491 if (mCustomExtras == null) { 492 mCustomExtras = new Bundle(); 493 } 494 // Combine builder extra flags into the config bundle. 495 mSyncConfigExtras = new Bundle(); 496 if (mIgnoreBackoff) { 497 mSyncConfigExtras.putBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_BACKOFF, true); 498 } 499 if (mDisallowMetered) { 500 mSyncConfigExtras.putBoolean(ContentResolver.SYNC_EXTRAS_DISALLOW_METERED, true); 501 } 502 if (mIgnoreSettings) { 503 mSyncConfigExtras.putBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_SETTINGS, true); 504 } 505 if (mNoRetry) { 506 mSyncConfigExtras.putBoolean(ContentResolver.SYNC_EXTRAS_DO_NOT_RETRY, true); 507 } 508 if (mExpedited) { 509 mSyncConfigExtras.putBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, true); 510 } 511 if (mIsManual) { 512 mSyncConfigExtras.putBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_BACKOFF, true); 513 mSyncConfigExtras.putBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_SETTINGS, true); 514 } 515 if (mSyncType == SYNC_TYPE_PERIODIC) { 516 // If this is a periodic sync ensure than invalid extras were not set. 517 if (ContentResolver.invalidPeriodicExtras(mCustomExtras) || 518 ContentResolver.invalidPeriodicExtras(mSyncConfigExtras)) { 519 throw new IllegalArgumentException("Illegal extras were set"); 520 } 521 } 522 // Ensure that a target for the sync has been set. 523 if (mSyncTarget == SYNC_TARGET_UNKNOWN) { 524 throw new IllegalArgumentException("Must specify an adapter with" + 525 " setSyncAdapter(Account, String"); 526 } 527 return new SyncRequest(this); 528 } 529 } 530 } 531