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 */ isPeriodic()61 public boolean isPeriodic() { 62 return mIsPeriodic; 63 } 64 65 /** 66 * {@hide} 67 * @return whether this sync is expedited. 68 */ isExpedited()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 */ getAccount()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 */ getProvider()91 public String getProvider() { 92 return mAuthority; 93 } 94 95 /** 96 * {@hide} 97 * Retrieve bundle for this SyncRequest. Will not be null. 98 */ getBundle()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 */ getSyncFlexTime()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 */ getSyncRunTime()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 describeContents()132 public int describeContents() { 133 return 0; 134 } 135 136 @Override writeToParcel(Parcel parcel, int flags)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 SyncRequest(Parcel in)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. */ SyncRequest(SyncRequest.Builder b)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 Builder()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 */ syncOnce()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 */ syncPeriodic(long pollFrequency, long beforeSeconds)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 setupInterval(long at, long before)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 */ setDisallowMetered(boolean disallow)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 */ setRequiresCharging(boolean requiresCharging)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 */ setSyncAdapter(Account account, String authority)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 */ setExtras(Bundle bundle)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 */ setNoRetry(boolean noRetry)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 */ setIgnoreSettings(boolean ignoreSettings)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 */ setIgnoreBackoff(boolean ignoreBackoff)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 */ setManual(boolean isManual)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 */ setExpedited(boolean expedited)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 */ build()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