SyncRequest.java revision fa77418134c6f1f80af225a78819f069e9c974fb
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 17package android.content; 18 19import android.accounts.Account; 20import android.os.Bundle; 21import android.os.Parcel; 22import android.os.Parcelable; 23import android.util.Pair; 24 25public class SyncRequest implements Parcelable { 26 private static final String TAG = "SyncRequest"; 27 /** Account to pass to the sync adapter. Can be null. */ 28 private final Account mAccountToSync; 29 /** Authority string that corresponds to a ContentProvider. */ 30 private final String mAuthority; 31 /** {@link SyncService} identifier. */ 32 private final ComponentName mComponentInfo; 33 /** Bundle containing user info as well as sync settings. */ 34 private final Bundle mExtras; 35 /** Allow this sync request on metered networks. */ 36 private final boolean mAllowMetered; 37 /** 38 * Anticipated upload size in bytes. 39 * TODO: Not yet used - we put this information into the bundle for simplicity. 40 */ 41 private final long mTxBytes; 42 /** 43 * Anticipated download size in bytes. 44 * TODO: Not yet used - we put this information into the bundle. 45 */ 46 private final long mRxBytes; 47 /** 48 * Amount of time before {@link mSyncRunTimeSecs} from which the sync may optionally be 49 * started. 50 */ 51 private final long mSyncFlexTimeSecs; 52 /** 53 * Specifies a point in the future at which the sync must have been scheduled to run. 54 */ 55 private final long mSyncRunTimeSecs; 56 /** Periodic versus one-off. */ 57 private final boolean mIsPeriodic; 58 /** Service versus provider. */ 59 private final boolean mIsAuthority; 60 /** Sync should be run in lieu of other syncs. */ 61 private final boolean mIsExpedited; 62 63 /** 64 * {@hide} 65 * @return whether this sync is periodic or one-time. A Sync Request must be 66 * either one of these or an InvalidStateException will be thrown in 67 * Builder.build(). 68 */ 69 public boolean isPeriodic() { 70 return mIsPeriodic; 71 } 72 73 public boolean isExpedited() { 74 return mIsExpedited; 75 } 76 77 /** 78 * {@hide} 79 * @return true if this sync uses an account/authority pair, or false if 80 * this is an anonymous sync bound to an @link AnonymousSyncService. 81 */ 82 public boolean hasAuthority() { 83 return mIsAuthority; 84 } 85 86 /** 87 * {@hide} 88 * Throws a runtime IllegalArgumentException if this function is called for an 89 * anonymous sync. 90 * 91 * @return (Account, Provider) for this SyncRequest. 92 */ 93 public Pair<Account, String> getProviderInfo() { 94 if (!hasAuthority()) { 95 throw new IllegalArgumentException("Cannot getProviderInfo() for an anonymous sync."); 96 } 97 return Pair.create(mAccountToSync, mAuthority); 98 } 99 100 /** 101 * {@hide} 102 * Throws a runtime IllegalArgumentException if this function is called for a 103 * SyncRequest that is bound to an account/provider. 104 * 105 * @return ComponentName for the service that this sync will bind to. 106 */ 107 public ComponentName getService() { 108 if (hasAuthority()) { 109 throw new IllegalArgumentException( 110 "Cannot getAnonymousService() for a sync that has specified a provider."); 111 } 112 return mComponentInfo; 113 } 114 115 /** 116 * {@hide} 117 * Retrieve bundle for this SyncRequest. Will not be null. 118 */ 119 public Bundle getBundle() { 120 return mExtras; 121 } 122 123 /** 124 * {@hide} 125 * @return the earliest point in time that this sync can be scheduled. 126 */ 127 public long getSyncFlexTime() { 128 return mSyncFlexTimeSecs; 129 } 130 /** 131 * {@hide} 132 * @return the last point in time at which this sync must scheduled. 133 */ 134 public long getSyncRunTime() { 135 return mSyncRunTimeSecs; 136 } 137 138 public static final Creator<SyncRequest> CREATOR = new Creator<SyncRequest>() { 139 140 @Override 141 public SyncRequest createFromParcel(Parcel in) { 142 return new SyncRequest(in); 143 } 144 145 @Override 146 public SyncRequest[] newArray(int size) { 147 return new SyncRequest[size]; 148 } 149 }; 150 151 @Override 152 public int describeContents() { 153 return 0; 154 } 155 156 @Override 157 public void writeToParcel(Parcel parcel, int flags) { 158 parcel.writeBundle(mExtras); 159 parcel.writeLong(mSyncFlexTimeSecs); 160 parcel.writeLong(mSyncRunTimeSecs); 161 parcel.writeInt((mIsPeriodic ? 1 : 0)); 162 parcel.writeInt((mAllowMetered ? 1 : 0)); 163 parcel.writeLong(mTxBytes); 164 parcel.writeLong(mRxBytes); 165 parcel.writeInt((mIsAuthority ? 1 : 0)); 166 parcel.writeInt((mIsExpedited? 1 : 0)); 167 if (mIsAuthority) { 168 parcel.writeParcelable(mAccountToSync, flags); 169 parcel.writeString(mAuthority); 170 } else { 171 parcel.writeParcelable(mComponentInfo, flags); 172 } 173 } 174 175 private SyncRequest(Parcel in) { 176 mExtras = in.readBundle(); 177 mSyncFlexTimeSecs = in.readLong(); 178 mSyncRunTimeSecs = in.readLong(); 179 mIsPeriodic = (in.readInt() != 0); 180 mAllowMetered = (in.readInt() != 0); 181 mTxBytes = in.readLong(); 182 mRxBytes = in.readLong(); 183 mIsAuthority = (in.readInt() != 0); 184 mIsExpedited = (in.readInt() != 0); 185 if (mIsAuthority) { 186 mComponentInfo = null; 187 mAccountToSync = in.readParcelable(null); 188 mAuthority = in.readString(); 189 } else { 190 mComponentInfo = in.readParcelable(null); 191 mAccountToSync = null; 192 mAuthority = null; 193 } 194 } 195 196 /** {@hide} Protected ctor to instantiate anonymous SyncRequest. */ 197 protected SyncRequest(SyncRequest.Builder b) { 198 mSyncFlexTimeSecs = b.mSyncFlexTimeSecs; 199 mSyncRunTimeSecs = b.mSyncRunTimeSecs; 200 mAccountToSync = b.mAccount; 201 mAuthority = b.mAuthority; 202 mComponentInfo = b.mComponentName; 203 mIsPeriodic = (b.mSyncType == Builder.SYNC_TYPE_PERIODIC); 204 mIsAuthority = (b.mSyncTarget == Builder.SYNC_TARGET_ADAPTER); 205 mIsExpedited = b.mExpedited; 206 mExtras = new Bundle(b.mCustomExtras); 207 mAllowMetered = b.mAllowMetered; 208 mTxBytes = b.mTxBytes; 209 mRxBytes = b.mRxBytes; 210 } 211 212 /** 213 * Builder class for a @link SyncRequest. As you build your SyncRequest this class will also 214 * perform validation. 215 */ 216 public static class Builder { 217 /** Unknown sync type. */ 218 private static final int SYNC_TYPE_UNKNOWN = 0; 219 /** Specify that this is a periodic sync. */ 220 private static final int SYNC_TYPE_PERIODIC = 1; 221 /** Specify that this is a one-time sync. */ 222 private static final int SYNC_TYPE_ONCE = 2; 223 /** Unknown sync target. */ 224 private static final int SYNC_TARGET_UNKNOWN = 0; 225 /** Specify that this is an anonymous sync. */ 226 private static final int SYNC_TARGET_SERVICE = 1; 227 /** Specify that this is a sync with a provider. */ 228 private static final int SYNC_TARGET_ADAPTER = 2; 229 /** 230 * Earliest point of displacement into the future at which this sync can 231 * occur. 232 */ 233 private long mSyncFlexTimeSecs; 234 /** Displacement into the future at which this sync must occur. */ 235 private long mSyncRunTimeSecs; 236 /** 237 * Sync configuration information - custom user data explicitly provided by the developer. 238 * This data is handed over to the sync operation. 239 */ 240 private Bundle mCustomExtras; 241 /** 242 * Sync system configuration - used to store system sync configuration. Corresponds to 243 * ContentResolver.SYNC_EXTRAS_* flags. 244 * TODO: Use this instead of dumping into one bundle. Need to decide if these flags should 245 * discriminate between equivalent syncs. 246 */ 247 private Bundle mSyncConfigExtras; 248 /** Expected upload transfer in bytes. */ 249 private long mTxBytes = -1L; 250 /** Expected download transfer in bytes. */ 251 private long mRxBytes = -1L; 252 /** Whether or not this sync can occur on metered networks. Default false. */ 253 private boolean mAllowMetered; 254 /** Priority of this sync relative to others from calling app [-2, 2]. Default 0. */ 255 private int mPriority = 0; 256 /** 257 * Whether this builder is building a periodic sync, or a one-time sync. 258 */ 259 private int mSyncType = SYNC_TYPE_UNKNOWN; 260 /** Whether this will go to a sync adapter or to a sync service. */ 261 private int mSyncTarget = SYNC_TARGET_UNKNOWN; 262 /** Whether this is a user-activated sync. */ 263 private boolean mIsManual; 264 /** 265 * Whether to retry this one-time sync if the sync fails. Not valid for 266 * periodic syncs. See {@link ContentResolver.SYNC_EXTRAS_DO_NOT_RETRY}. 267 */ 268 private boolean mNoRetry; 269 /** 270 * Whether to respect back-off for this one-time sync. Not valid for 271 * periodic syncs. See 272 * {@link android.content.ContentResolver.SYNC_EXTRAS_IGNORE_BACKOFF}; 273 */ 274 private boolean mIgnoreBackoff; 275 276 /** Ignore sync system settings and perform sync anyway. */ 277 private boolean mIgnoreSettings; 278 279 /** This sync will run in preference to other non-expedited syncs. */ 280 private boolean mExpedited; 281 282 /** 283 * The @link android.content.AnonymousSyncService component that 284 * contains the sync logic if this is a provider-less sync, otherwise 285 * null. 286 */ 287 private ComponentName mComponentName; 288 /** 289 * The Account object that together with an Authority name define the SyncAdapter (if 290 * this sync is bound to a provider), otherwise null. This gets resolved 291 * against a {@link com.android.server.content.SyncStorageEngine}. 292 */ 293 private Account mAccount; 294 /** 295 * The Authority name that together with an Account define the SyncAdapter (if 296 * this sync is bound to a provider), otherwise null. This gets resolved 297 * against a {@link com.android.server.content.SyncStorageEngine}. 298 */ 299 private String mAuthority; 300 301 public Builder() { 302 } 303 304 /** 305 * Developer can define timing constraints for this one-shot request. 306 * These values are elapsed real-time. 307 * 308 * @param whenSeconds The time in seconds at which you want this 309 * sync to occur. 310 * @param beforeSeconds The amount of time in advance of whenSeconds that this 311 * sync may be permitted to occur. This is rounded up to a minimum of 5 312 * seconds, for any sync for which whenSeconds > 5. 313 * 314 * Example 315 * <pre> 316 * Perform an immediate sync. 317 * SyncRequest.Builder builder = (new SyncRequest.Builder()).syncOnce(0, 0); 318 * That is, a sync 0 seconds from now with 0 seconds of flex. 319 * 320 * Perform a sync in exactly 5 minutes. 321 * SyncRequest.Builder builder = 322 * new SyncRequest.Builder().syncOnce(5 * MIN_IN_SECS, 0); 323 * 324 * Perform a sync in 5 minutes, with one minute of leeway (between 4 and 5 minutes from 325 * now). 326 * SyncRequest.Builder builder = 327 * new SyncRequest.Builder().syncOnce(5 * MIN_IN_SECS, 1 * MIN_IN_SECS); 328 * </pre> 329 */ 330 public Builder syncOnce(long whenSeconds, long beforeSeconds) { 331 if (mSyncType != SYNC_TYPE_UNKNOWN) { 332 throw new IllegalArgumentException("Sync type has already been defined."); 333 } 334 mSyncType = SYNC_TYPE_ONCE; 335 setupInterval(whenSeconds, beforeSeconds); 336 return this; 337 } 338 339 /** 340 * Build a periodic sync. Either this or syncOnce() <b>must</b> be called for this builder. 341 * Syncs are identified by target {@link SyncService}/{@link android.provider} and by the 342 * contents of the extras bundle. 343 * You cannot reuse the same builder for one-time syncs after having specified a periodic 344 * sync (by calling this function). If you do, an {@link IllegalArgumentException} will be 345 * thrown. 346 * 347 * Example usage. 348 * 349 * <pre> 350 * Request a periodic sync every 5 hours with 20 minutes of flex. 351 * SyncRequest.Builder builder = 352 * (new SyncRequest.Builder()).syncPeriodic(5 * HOUR_IN_SECS, 20 * MIN_IN_SECS); 353 * 354 * Schedule a periodic sync every hour at any point in time during that hour. 355 * SyncRequest.Builder builder = 356 * (new SyncRequest.Builder()).syncPeriodic(1 * HOUR_IN_SECS, 1 * HOUR_IN_SECS); 357 * </pre> 358 * 359 * N.B.: Periodic syncs are not allowed to have any of 360 * {@link #SYNC_EXTRAS_DO_NOT_RETRY}, 361 * {@link #SYNC_EXTRAS_IGNORE_BACKOFF}, 362 * {@link #SYNC_EXTRAS_IGNORE_SETTINGS}, 363 * {@link #SYNC_EXTRAS_INITIALIZE}, 364 * {@link #SYNC_EXTRAS_FORCE}, 365 * {@link #SYNC_EXTRAS_EXPEDITED}, 366 * {@link #SYNC_EXTRAS_MANUAL} 367 * set to true. If any are supplied then an {@link IllegalArgumentException} will 368 * be thrown. 369 * 370 * @param pollFrequency the amount of time in seconds that you wish 371 * to elapse between periodic syncs. 372 * @param beforeSeconds the amount of flex time in seconds before 373 * {@code pollFrequency} that you permit for the sync to take 374 * place. Must be less than {@code pollFrequency}. 375 */ 376 public Builder syncPeriodic(long pollFrequency, long beforeSeconds) { 377 if (mSyncType != SYNC_TYPE_UNKNOWN) { 378 throw new IllegalArgumentException("Sync type has already been defined."); 379 } 380 mSyncType = SYNC_TYPE_PERIODIC; 381 setupInterval(pollFrequency, beforeSeconds); 382 return this; 383 } 384 385 /** {@hide} */ 386 private void setupInterval(long at, long before) { 387 if (before > at) { 388 throw new IllegalArgumentException("Specified run time for the sync must be" + 389 " after the specified flex time."); 390 } 391 mSyncRunTimeSecs = at; 392 mSyncFlexTimeSecs = before; 393 } 394 395 /** 396 * Developer can provide insight into their payload size; optional. -1 specifies 397 * unknown, so that you are not restricted to defining both fields. 398 * 399 * @param rxBytes Bytes expected to be downloaded. 400 * @param txBytes Bytes expected to be uploaded. 401 */ 402 public Builder setTransferSize(long rxBytes, long txBytes) { 403 mRxBytes = rxBytes; 404 mTxBytes = txBytes; 405 return this; 406 } 407 408 /** 409 * @param allow false to allow this transfer on metered networks. 410 * Default true. 411 */ 412 public Builder setAllowMetered(boolean allow) { 413 mAllowMetered = true; 414 return this; 415 } 416 417 /** 418 * Give ourselves a concrete way of binding. Use an explicit 419 * authority+account SyncAdapter for this transfer, otherwise we bind 420 * anonymously to given componentname. 421 * 422 * @param authority 423 * @param account Account to sync. Can be null unless this is a periodic 424 * sync, for which verification by the ContentResolver will 425 * fail. If a sync is performed without an account, the 426 */ 427 public Builder setSyncAdapter(Account account, String authority) { 428 if (mSyncTarget != SYNC_TARGET_UNKNOWN) { 429 throw new IllegalArgumentException("Sync target has already been defined."); 430 } 431 mSyncTarget = SYNC_TARGET_ADAPTER; 432 mAccount = account; 433 mAuthority = authority; 434 mComponentName = null; 435 return this; 436 } 437 438 /** 439 * Set Service component name for anonymous sync. This is not validated 440 * until sync time so providing an incorrect component name here will 441 * not fail. 442 * 443 * @param cname ComponentName to identify your Anonymous service 444 */ 445 public Builder setSyncAdapter(ComponentName cname) { 446 if (mSyncTarget != SYNC_TARGET_UNKNOWN) { 447 throw new IllegalArgumentException("Sync target has already been defined."); 448 } 449 mSyncTarget = SYNC_TARGET_SERVICE; 450 mComponentName = cname; 451 mAccount = null; 452 mAuthority = null; 453 return this; 454 } 455 456 /** 457 * Developer-provided extras handed back when sync actually occurs. This bundle is copied 458 * into the SyncRequest returned by build(). 459 * 460 * Example: 461 * <pre> 462 * String[] syncItems = {"dog", "cat", "frog", "child"}; 463 * SyncRequest.Builder builder = 464 * new SyncRequest.Builder() 465 * .setSyncAdapter(dummyAccount, dummyProvider) 466 * .syncOnce(5 * MINUTES_IN_SECS); 467 * 468 * for (String syncData : syncItems) { 469 * Bundle extras = new Bundle(); 470 * extras.setString("data", syncData); 471 * builder.setExtras(extras); 472 * ContentResolver.sync(builder.build()); // Each sync() request creates a unique sync. 473 * } 474 * </pre> 475 * 476 * Only values of the following types may be used in the extras bundle: 477 * <ul> 478 * <li>Integer</li> 479 * <li>Long</li> 480 * <li>Boolean</li> 481 * <li>Float</li> 482 * <li>Double</li> 483 * <li>String</li> 484 * <li>Account</li> 485 * <li>null</li> 486 * </ul> 487 * If any data is present in the bundle not of this type, build() will 488 * throw a runtime exception. 489 * 490 * @param bundle 491 */ 492 public Builder setExtras(Bundle bundle) { 493 mCustomExtras = bundle; 494 return this; 495 } 496 497 /** 498 * Convenience function for setting {@link ContentResolver.SYNC_EXTRAS_DO_NOT_RETRY}. A 499 * one-off sync operation that fails will be retried at a later date unless this is 500 * set to false. Default is true. Not valid for periodic sync and will throw an 501 * IllegalArgumentException in Builder.build(). 502 * 503 * @param retry false to not retry a failed sync. Default true. 504 */ 505 public Builder setNoRetry(boolean retry) { 506 mNoRetry = retry; 507 return this; 508 } 509 510 /** 511 * {@link ContentResolver.SYNC_EXTRAS_IGNORE_SETTINGS}. Not valid for 512 * periodic sync and will throw an IllegalArgumentException in 513 * Builder.build(). Default false. 514 */ 515 public Builder setIgnoreSettings(boolean ignoreSettings) { 516 mIgnoreSettings = ignoreSettings; 517 return this; 518 } 519 520 /** 521 * Convenience function for setting {@link ContentResolver.SYNC_EXTRAS_IGNORE_BACKOFF}. 522 * 523 * @param ignoreBackoff 524 */ 525 public Builder setIgnoreBackoff(boolean ignoreBackoff) { 526 mIgnoreBackoff = ignoreBackoff; 527 return this; 528 } 529 530 /** 531 * {@link ContentResolver.SYNC_EXTRAS_MANUAL}. Default false. 532 */ 533 public Builder setManual(boolean isManual) { 534 mIsManual = isManual; 535 return this; 536 } 537 538 /** 539 * {@link ContentResolver.SYNC_EXTRAS_} Default false. 540 */ 541 public Builder setExpedited(boolean expedited) { 542 mExpedited = expedited; 543 return this; 544 } 545 546 /** 547 * Priority of this request among all requests from the calling app. 548 * Range of [-2,2] similar to {@link android.app.Notification.priority}. 549 */ 550 public Builder setPriority(int priority) { 551 if (priority < -2 || priority > 2) { 552 throw new IllegalArgumentException("Priority must be within range [-2, 2]"); 553 } 554 mPriority = priority; 555 return this; 556 } 557 558 /** 559 * Performs validation over the request and throws the runtime exception 560 * IllegalArgumentException if this validation fails. TODO: Add 561 * validation of SyncRequest here. 1) Cannot specify both periodic & 562 * one-off (fails above). 2) Cannot specify both service and 563 * account/provider (fails above). 564 * 565 * @return a SyncRequest with the information contained within this 566 * builder. 567 */ 568 public SyncRequest build() { 569 // Validate the extras bundle 570 try { 571 ContentResolver.validateSyncExtrasBundle(mCustomExtras); 572 } catch (IllegalArgumentException e) { 573 throw new IllegalArgumentException(e.getMessage()); 574 } 575 if (mCustomExtras == null) { 576 mCustomExtras = new Bundle(); 577 } 578 // Combine the builder extra flags into the copy of the bundle. 579 if (mIgnoreBackoff) { 580 mCustomExtras.putBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_BACKOFF, true); 581 } 582 if (mAllowMetered) { 583 mCustomExtras.putBoolean(ContentResolver.SYNC_EXTRAS_ALLOW_METERED, true); 584 } 585 if (mIgnoreSettings) { 586 mCustomExtras.putBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_SETTINGS, true); 587 } 588 if (mNoRetry) { 589 mCustomExtras.putBoolean(ContentResolver.SYNC_EXTRAS_DO_NOT_RETRY, true); 590 } 591 if (mExpedited) { 592 mCustomExtras.putBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, true); 593 } 594 if (mIsManual) { 595 mCustomExtras.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true); 596 } 597 // Upload/download expectations. 598 mCustomExtras.putLong(ContentResolver.SYNC_EXTRAS_EXPECTED_UPLOAD, mTxBytes); 599 mCustomExtras.putLong(ContentResolver.SYNC_EXTRAS_EXPECTED_DOWNLOAD, mRxBytes); 600 // Priority. 601 mCustomExtras.putInt(ContentResolver.SYNC_EXTRAS_PRIORITY, mPriority); 602 if (mSyncType == SYNC_TYPE_PERIODIC) { 603 // If this is a periodic sync ensure than invalid extras were 604 // not set. 605 if (mCustomExtras.getBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, false) 606 || mCustomExtras.getBoolean(ContentResolver.SYNC_EXTRAS_DO_NOT_RETRY, false) 607 || mCustomExtras.getBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_BACKOFF, false) 608 || mCustomExtras.getBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_SETTINGS, false) 609 || mCustomExtras.getBoolean(ContentResolver.SYNC_EXTRAS_INITIALIZE, false) 610 || mCustomExtras.getBoolean(ContentResolver.SYNC_EXTRAS_FORCE, false) 611 || mCustomExtras.getBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, false)) { 612 throw new IllegalArgumentException("Illegal extras were set"); 613 } 614 } else if (mSyncType == SYNC_TYPE_UNKNOWN) { 615 throw new IllegalArgumentException("Must call either syncOnce() or syncPeriodic()"); 616 } 617 // Ensure that a target for the sync has been set. 618 if (mSyncTarget == SYNC_TARGET_UNKNOWN) { 619 throw new IllegalArgumentException("Must specify an adapter with one of" 620 + "setSyncAdapter(ComponentName) or setSyncAdapter(Account, String"); 621 } 622 return new SyncRequest(this); 623 } 624 } 625} 626