blob: b528e397906f5252d314801d84780247eaf16023 [file] [log] [blame]
Fred Quintana21bb0de2009-06-16 10:24:58 -07001/*
2 * Copyright (C) 2009 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
Philip P. Moltmann186e3b32018-03-15 16:32:44 -070019import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
20
Fred Quintana21bb0de2009-06-16 10:24:58 -070021import android.accounts.Account;
Philip P. Moltmann186e3b32018-03-15 16:32:44 -070022import android.annotation.MainThread;
23import android.annotation.NonNull;
Makoto Onuki6a6ae042017-07-20 13:30:12 -070024import android.os.Build;
Fred Quintana21bb0de2009-06-16 10:24:58 -070025import android.os.Bundle;
Philip P. Moltmann186e3b32018-03-15 16:32:44 -070026import android.os.Handler;
Fred Quintanaf0380042009-10-06 17:05:58 -070027import android.os.IBinder;
Ken Shirriff1719a392009-12-07 15:57:35 -080028import android.os.Process;
Philip P. Moltmann486b2412018-01-03 11:29:01 -080029import android.os.RemoteException;
Andy Stadler09b45a32012-05-03 15:00:49 -070030import android.os.Trace;
Makoto Onuki6a6ae042017-07-20 13:30:12 -070031import android.util.Log;
Fred Quintana21bb0de2009-06-16 10:24:58 -070032
Fred Quintana0c4d04a2010-11-03 17:02:55 -070033import java.util.HashMap;
Fred Quintana21bb0de2009-06-16 10:24:58 -070034import java.util.concurrent.atomic.AtomicInteger;
35
36/**
37 * An abstract implementation of a SyncAdapter that spawns a thread to invoke a sync operation.
Matthew Williams1967c8d2015-06-19 19:03:13 -070038 * If a sync operation is already in progress when a sync request is received, an error will be
39 * returned to the new request and the existing request will be allowed to continue.
40 * However if there is no sync in progress then a thread will be spawned and {@link #onPerformSync}
41 * will be invoked on that thread.
42 * <p>
43 * Syncs can be cancelled at any time by the framework. For example a sync that was not
44 * user-initiated and lasts longer than 30 minutes will be considered timed-out and cancelled.
45 * Similarly the framework will attempt to determine whether or not an adapter is making progress
46 * by monitoring its network activity over the course of a minute. If the network traffic over this
47 * window is close enough to zero the sync will be cancelled. You can also request the sync be
48 * cancelled via {@link ContentResolver#cancelSync(Account, String)} or
49 * {@link ContentResolver#cancelSync(SyncRequest)}.
50 * <p>
51 * A sync is cancelled by issuing a {@link Thread#interrupt()} on the syncing thread. <strong>Either
52 * your code in {@link #onPerformSync(Account, Bundle, String, ContentProviderClient, SyncResult)}
53 * must check {@link Thread#interrupted()}, or you you must override one of
54 * {@link #onSyncCanceled(Thread)}/{@link #onSyncCanceled()}</strong> (depending on whether or not
55 * your adapter supports syncing of multiple accounts in parallel). If your adapter does not
56 * respect the cancel issued by the framework you run the risk of your app's entire process being
57 * killed.
Fred Quintanae6d60ec2011-08-24 11:29:00 -070058 * <p>
59 * In order to be a sync adapter one must extend this class, provide implementations for the
60 * abstract methods and write a service that returns the result of {@link #getSyncAdapterBinder()}
61 * in the service's {@link android.app.Service#onBind(android.content.Intent)} when invoked
62 * with an intent with action <code>android.content.SyncAdapter</code>. This service
63 * must specify the following intent filter and metadata tags in its AndroidManifest.xml file
64 * <pre>
65 * &lt;intent-filter&gt;
66 * &lt;action android:name="android.content.SyncAdapter" /&gt;
67 * &lt;/intent-filter&gt;
68 * &lt;meta-data android:name="android.content.SyncAdapter"
69 * android:resource="@xml/syncadapter" /&gt;
70 * </pre>
71 * The <code>android:resource</code> attribute must point to a resource that looks like:
72 * <pre>
73 * &lt;sync-adapter xmlns:android="http://schemas.android.com/apk/res/android"
74 * android:contentAuthority="authority"
75 * android:accountType="accountType"
76 * android:userVisible="true|false"
77 * android:supportsUploading="true|false"
78 * android:allowParallelSyncs="true|false"
79 * android:isAlwaysSyncable="true|false"
80 * android:syncAdapterSettingsAction="ACTION_OF_SETTINGS_ACTIVITY"
81 * /&gt;
82 * </pre>
83 * <ul>
84 * <li>The <code>android:contentAuthority</code> and <code>android:accountType</code> attributes
85 * indicate which content authority and for which account types this sync adapter serves.
86 * <li><code>android:userVisible</code> defaults to true and controls whether or not this sync
87 * adapter shows up in the Sync Settings screen.
88 * <li><code>android:supportsUploading</code> defaults
89 * to true and if true an upload-only sync will be requested for all syncadapters associated
90 * with an authority whenever that authority's content provider does a
91 * {@link ContentResolver#notifyChange(android.net.Uri, android.database.ContentObserver, boolean)}
92 * with syncToNetwork set to true.
93 * <li><code>android:allowParallelSyncs</code> defaults to false and if true indicates that
94 * the sync adapter can handle syncs for multiple accounts at the same time. Otherwise
95 * the SyncManager will wait until the sync adapter is not in use before requesting that
96 * it sync an account's data.
97 * <li><code>android:isAlwaysSyncable</code> defaults to false and if true tells the SyncManager
98 * to intialize the isSyncable state to 1 for that sync adapter for each account that is added.
99 * <li><code>android:syncAdapterSettingsAction</code> defaults to null and if supplied it
100 * specifies an Intent action of an activity that can be used to adjust the sync adapter's
101 * sync settings. The activity must live in the same package as the sync adapter.
102 * </ul>
Fred Quintana21bb0de2009-06-16 10:24:58 -0700103 */
104public abstract class AbstractThreadedSyncAdapter {
Makoto Onuki6a6ae042017-07-20 13:30:12 -0700105 private static final String TAG = "SyncAdapter";
106
Fred Quintana97ef7632009-12-10 10:33:18 -0800107 /**
108 * Kernel event log tag. Also listed in data/etc/event-log-tags.
Joe Onoratod3ad6962010-09-16 13:38:25 -0400109 * @deprecated Private constant. May go away in the next release.
Fred Quintana97ef7632009-12-10 10:33:18 -0800110 */
Joe Onoratod3ad6962010-09-16 13:38:25 -0400111 @Deprecated
Fred Quintana97ef7632009-12-10 10:33:18 -0800112 public static final int LOG_SYNC_DETAILS = 2743;
113
Makoto Onuki6a6ae042017-07-20 13:30:12 -0700114 private static final boolean ENABLE_LOG = Build.IS_DEBUGGABLE && Log.isLoggable(TAG, Log.DEBUG);
115
Fred Quintana21bb0de2009-06-16 10:24:58 -0700116 private final Context mContext;
117 private final AtomicInteger mNumSyncStarts;
118 private final ISyncAdapterImpl mISyncAdapterImpl;
119
Fred Quintana3cff76a2009-08-26 18:49:19 -0700120 // all accesses to this member variable must be synchronized on mSyncThreadLock
Fred Quintana0c4d04a2010-11-03 17:02:55 -0700121 private final HashMap<Account, SyncThread> mSyncThreads = new HashMap<Account, SyncThread>();
Fred Quintana3cff76a2009-08-26 18:49:19 -0700122 private final Object mSyncThreadLock = new Object();
Fred Quintana21bb0de2009-06-16 10:24:58 -0700123
Fred Quintana4a6679b2009-08-17 13:05:39 -0700124 private final boolean mAutoInitialize;
Fred Quintana0c4d04a2010-11-03 17:02:55 -0700125 private boolean mAllowParallelSyncs;
Fred Quintana21bb0de2009-06-16 10:24:58 -0700126
127 /**
128 * Creates an {@link AbstractThreadedSyncAdapter}.
Fred Quintana4a6679b2009-08-17 13:05:39 -0700129 * @param context the {@link android.content.Context} that this is running within.
130 * @param autoInitialize if true then sync requests that have
131 * {@link ContentResolver#SYNC_EXTRAS_INITIALIZE} set will be internally handled by
132 * {@link AbstractThreadedSyncAdapter} by calling
133 * {@link ContentResolver#setIsSyncable(android.accounts.Account, String, int)} with 1 if it
134 * is currently set to <0.
Fred Quintana21bb0de2009-06-16 10:24:58 -0700135 */
Fred Quintana4a6679b2009-08-17 13:05:39 -0700136 public AbstractThreadedSyncAdapter(Context context, boolean autoInitialize) {
Fred Quintana0c4d04a2010-11-03 17:02:55 -0700137 this(context, autoInitialize, false /* allowParallelSyncs */);
138 }
139
140 /**
141 * Creates an {@link AbstractThreadedSyncAdapter}.
142 * @param context the {@link android.content.Context} that this is running within.
143 * @param autoInitialize if true then sync requests that have
144 * {@link ContentResolver#SYNC_EXTRAS_INITIALIZE} set will be internally handled by
145 * {@link AbstractThreadedSyncAdapter} by calling
146 * {@link ContentResolver#setIsSyncable(android.accounts.Account, String, int)} with 1 if it
147 * is currently set to <0.
148 * @param allowParallelSyncs if true then allow syncs for different accounts to run
149 * at the same time, each in their own thread. This must be consistent with the setting
150 * in the SyncAdapter's configuration file.
151 */
152 public AbstractThreadedSyncAdapter(Context context,
153 boolean autoInitialize, boolean allowParallelSyncs) {
Fred Quintana21bb0de2009-06-16 10:24:58 -0700154 mContext = context;
155 mISyncAdapterImpl = new ISyncAdapterImpl();
156 mNumSyncStarts = new AtomicInteger(0);
Fred Quintana4a6679b2009-08-17 13:05:39 -0700157 mAutoInitialize = autoInitialize;
Fred Quintana0c4d04a2010-11-03 17:02:55 -0700158 mAllowParallelSyncs = allowParallelSyncs;
Fred Quintana21bb0de2009-06-16 10:24:58 -0700159 }
160
Fred Quintanac298a852009-08-27 18:28:17 -0700161 public Context getContext() {
162 return mContext;
163 }
164
Fred Quintana0c4d04a2010-11-03 17:02:55 -0700165 private Account toSyncKey(Account account) {
166 if (mAllowParallelSyncs) {
167 return account;
168 } else {
169 return null;
170 }
171 }
172
Fred Quintanaf0380042009-10-06 17:05:58 -0700173 private class ISyncAdapterImpl extends ISyncAdapter.Stub {
Matthew Williamsfa774182013-06-18 15:44:11 -0700174 @Override
Philip P. Moltmann186e3b32018-03-15 16:32:44 -0700175 public void onUnsyncableAccount(ISyncAdapterUnsyncableAccountCallback cb) {
176 Handler.getMain().sendMessage(obtainMessage(
177 AbstractThreadedSyncAdapter::handleOnUnsyncableAccount,
178 AbstractThreadedSyncAdapter.this, cb));
Philip P. Moltmann486b2412018-01-03 11:29:01 -0800179 }
180
181 @Override
Fred Quintana21bb0de2009-06-16 10:24:58 -0700182 public void startSync(ISyncContext syncContext, String authority, Account account,
183 Bundle extras) {
Makoto Onuki6a6ae042017-07-20 13:30:12 -0700184 if (ENABLE_LOG) {
185 if (extras != null) {
186 extras.size(); // Unparcel so its toString() will show the contents.
Fred Quintana21bb0de2009-06-16 10:24:58 -0700187 }
Makoto Onuki6a6ae042017-07-20 13:30:12 -0700188 Log.d(TAG, "startSync() start " + authority + " " + account + " " + extras);
Fred Quintana21bb0de2009-06-16 10:24:58 -0700189 }
Makoto Onuki6a6ae042017-07-20 13:30:12 -0700190 try {
191 final SyncContext syncContextClient = new SyncContext(syncContext);
Fred Quintana21bb0de2009-06-16 10:24:58 -0700192
Makoto Onuki6a6ae042017-07-20 13:30:12 -0700193 boolean alreadyInProgress;
194 // synchronize to make sure that mSyncThreads doesn't change between when we
195 // check it and when we use it
196 final Account threadsKey = toSyncKey(account);
197 synchronized (mSyncThreadLock) {
198 if (!mSyncThreads.containsKey(threadsKey)) {
199 if (mAutoInitialize
200 && extras != null
201 && extras.getBoolean(
202 ContentResolver.SYNC_EXTRAS_INITIALIZE, false)) {
203 try {
204 if (ContentResolver.getIsSyncable(account, authority) < 0) {
205 ContentResolver.setIsSyncable(account, authority, 1);
206 }
207 } finally {
208 syncContextClient.onFinished(new SyncResult());
209 }
210 return;
211 }
212 SyncThread syncThread = new SyncThread(
213 "SyncAdapterThread-" + mNumSyncStarts.incrementAndGet(),
214 syncContextClient, authority, account, extras);
215 mSyncThreads.put(threadsKey, syncThread);
216 syncThread.start();
217 alreadyInProgress = false;
218 } else {
219 if (ENABLE_LOG) {
220 Log.d(TAG, " alreadyInProgress");
221 }
222 alreadyInProgress = true;
223 }
224 }
225
226 // do this outside since we don't want to call back into the syncContext while
227 // holding the synchronization lock
228 if (alreadyInProgress) {
229 syncContextClient.onFinished(SyncResult.ALREADY_IN_PROGRESS);
230 }
231 } catch (RuntimeException | Error th) {
232 if (ENABLE_LOG) {
233 Log.d(TAG, "startSync() caught exception", th);
234 }
235 throw th;
236 } finally {
237 if (ENABLE_LOG) {
238 Log.d(TAG, "startSync() finishing");
239 }
Fred Quintana21bb0de2009-06-16 10:24:58 -0700240 }
241 }
242
Matthew Williamsfa774182013-06-18 15:44:11 -0700243 @Override
Fred Quintana21bb0de2009-06-16 10:24:58 -0700244 public void cancelSync(ISyncContext syncContext) {
Makoto Onuki6a6ae042017-07-20 13:30:12 -0700245 try {
246 // synchronize to make sure that mSyncThreads doesn't change between when we
247 // check it and when we use it
248 SyncThread info = null;
249 synchronized (mSyncThreadLock) {
250 for (SyncThread current : mSyncThreads.values()) {
251 if (current.mSyncContext.getSyncContextBinder() == syncContext.asBinder()) {
252 info = current;
253 break;
254 }
Fred Quintana0c4d04a2010-11-03 17:02:55 -0700255 }
256 }
Makoto Onuki6a6ae042017-07-20 13:30:12 -0700257 if (info != null) {
258 if (ENABLE_LOG) {
259 Log.d(TAG, "cancelSync() " + info.mAuthority + " " + info.mAccount);
260 }
261 if (mAllowParallelSyncs) {
262 onSyncCanceled(info);
263 } else {
264 onSyncCanceled();
265 }
Fred Quintana0c4d04a2010-11-03 17:02:55 -0700266 } else {
Makoto Onuki6a6ae042017-07-20 13:30:12 -0700267 if (ENABLE_LOG) {
268 Log.w(TAG, "cancelSync() unknown context");
269 }
270 }
271 } catch (RuntimeException | Error th) {
272 if (ENABLE_LOG) {
273 Log.d(TAG, "cancelSync() caught exception", th);
274 }
275 throw th;
276 } finally {
277 if (ENABLE_LOG) {
278 Log.d(TAG, "cancelSync() finishing");
Fred Quintana0c4d04a2010-11-03 17:02:55 -0700279 }
Fred Quintana21bb0de2009-06-16 10:24:58 -0700280 }
281 }
282 }
283
284 /**
Fred Quintanaf0380042009-10-06 17:05:58 -0700285 * The thread that invokes {@link AbstractThreadedSyncAdapter#onPerformSync}. It also acquires
286 * the provider for this sync before calling onPerformSync and releases it afterwards. Cancel
287 * this thread in order to cancel the sync.
Fred Quintana21bb0de2009-06-16 10:24:58 -0700288 */
289 private class SyncThread extends Thread {
290 private final SyncContext mSyncContext;
291 private final String mAuthority;
292 private final Account mAccount;
293 private final Bundle mExtras;
Fred Quintana0c4d04a2010-11-03 17:02:55 -0700294 private final Account mThreadsKey;
Fred Quintana21bb0de2009-06-16 10:24:58 -0700295
296 private SyncThread(String name, SyncContext syncContext, String authority,
297 Account account, Bundle extras) {
298 super(name);
299 mSyncContext = syncContext;
300 mAuthority = authority;
301 mAccount = account;
302 mExtras = extras;
Fred Quintana0c4d04a2010-11-03 17:02:55 -0700303 mThreadsKey = toSyncKey(account);
Fred Quintana21bb0de2009-06-16 10:24:58 -0700304 }
305
Andy Stadler09b45a32012-05-03 15:00:49 -0700306 @Override
Fred Quintana21bb0de2009-06-16 10:24:58 -0700307 public void run() {
Fred Quintana75d797c2009-08-25 15:05:02 -0700308 Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
309
Makoto Onuki6a6ae042017-07-20 13:30:12 -0700310 if (ENABLE_LOG) {
311 Log.d(TAG, "Thread started");
312 }
313
Andy Stadler09b45a32012-05-03 15:00:49 -0700314 // Trace this sync instance. Note, conceptually this should be in
315 // SyncStorageEngine.insertStartSyncEvent(), but the trace functions require unique
316 // threads in order to track overlapping operations, so we'll do it here for now.
317 Trace.traceBegin(Trace.TRACE_TAG_SYNC_MANAGER, mAuthority);
318
Fred Quintana21bb0de2009-06-16 10:24:58 -0700319 SyncResult syncResult = new SyncResult();
320 ContentProviderClient provider = null;
321 try {
Alon Albert9257ec02011-02-07 10:53:26 -0800322 if (isCanceled()) {
Makoto Onuki6a6ae042017-07-20 13:30:12 -0700323 if (ENABLE_LOG) {
324 Log.d(TAG, "Already canceled");
325 }
Alon Albert9257ec02011-02-07 10:53:26 -0800326 return;
327 }
Makoto Onuki6a6ae042017-07-20 13:30:12 -0700328 if (ENABLE_LOG) {
329 Log.d(TAG, "Calling onPerformSync...");
330 }
331
Fred Quintana21bb0de2009-06-16 10:24:58 -0700332 provider = mContext.getContentResolver().acquireContentProviderClient(mAuthority);
333 if (provider != null) {
Fred Quintanaf0380042009-10-06 17:05:58 -0700334 AbstractThreadedSyncAdapter.this.onPerformSync(mAccount, mExtras,
Fred Quintana21bb0de2009-06-16 10:24:58 -0700335 mAuthority, provider, syncResult);
336 } else {
Fred Quintanaf0380042009-10-06 17:05:58 -0700337 syncResult.databaseError = true;
Fred Quintana21bb0de2009-06-16 10:24:58 -0700338 }
Makoto Onuki6a6ae042017-07-20 13:30:12 -0700339
340 if (ENABLE_LOG) {
341 Log.d(TAG, "onPerformSync done");
342 }
343
Dianne Hackbornd01ed462015-06-22 17:41:44 -0700344 } catch (SecurityException e) {
Makoto Onuki6a6ae042017-07-20 13:30:12 -0700345 if (ENABLE_LOG) {
346 Log.d(TAG, "SecurityException", e);
347 }
Dianne Hackbornd01ed462015-06-22 17:41:44 -0700348 AbstractThreadedSyncAdapter.this.onSecurityException(mAccount, mExtras,
349 mAuthority, syncResult);
350 syncResult.databaseError = true;
Makoto Onuki6a6ae042017-07-20 13:30:12 -0700351 } catch (RuntimeException | Error th) {
352 if (ENABLE_LOG) {
353 Log.d(TAG, "caught exception", th);
354 }
355 throw th;
Fred Quintana21bb0de2009-06-16 10:24:58 -0700356 } finally {
Andy Stadler09b45a32012-05-03 15:00:49 -0700357 Trace.traceEnd(Trace.TRACE_TAG_SYNC_MANAGER);
358
Fred Quintana21bb0de2009-06-16 10:24:58 -0700359 if (provider != null) {
360 provider.release();
361 }
362 if (!isCanceled()) {
363 mSyncContext.onFinished(syncResult);
364 }
365 // synchronize so that the assignment will be seen by other threads
Fred Quintana0c4d04a2010-11-03 17:02:55 -0700366 // that also synchronize accesses to mSyncThreads
Fred Quintana3cff76a2009-08-26 18:49:19 -0700367 synchronized (mSyncThreadLock) {
Fred Quintana0c4d04a2010-11-03 17:02:55 -0700368 mSyncThreads.remove(mThreadsKey);
Fred Quintana21bb0de2009-06-16 10:24:58 -0700369 }
Makoto Onuki6a6ae042017-07-20 13:30:12 -0700370
371 if (ENABLE_LOG) {
372 Log.d(TAG, "Thread finished");
373 }
Fred Quintana21bb0de2009-06-16 10:24:58 -0700374 }
375 }
376
377 private boolean isCanceled() {
378 return Thread.currentThread().isInterrupted();
379 }
380 }
381
382 /**
Fred Quintanaf0380042009-10-06 17:05:58 -0700383 * @return a reference to the IBinder of the SyncAdapter service.
Fred Quintana21bb0de2009-06-16 10:24:58 -0700384 */
Fred Quintanaf0380042009-10-06 17:05:58 -0700385 public final IBinder getSyncAdapterBinder() {
386 return mISyncAdapterImpl.asBinder();
Fred Quintana21bb0de2009-06-16 10:24:58 -0700387 }
388
389 /**
Philip P. Moltmann186e3b32018-03-15 16:32:44 -0700390 * Handle a call of onUnsyncableAccount.
391 *
392 * @param cb The callback to report the return value to
393 */
394 private void handleOnUnsyncableAccount(@NonNull ISyncAdapterUnsyncableAccountCallback cb) {
395 boolean doSync;
396 try {
397 doSync = onUnsyncableAccount();
398 } catch (RuntimeException e) {
399 Log.e(TAG, "Exception while calling onUnsyncableAccount, assuming 'true'", e);
400 doSync = true;
401 }
402
403 try {
404 cb.onUnsyncableAccountDone(doSync);
405 } catch (RemoteException e) {
406 Log.e(TAG, "Could not report result of onUnsyncableAccount", e);
407 }
408 }
409
410 /**
Philip P. Moltmann486b2412018-01-03 11:29:01 -0800411 * Allows to defer syncing until all accounts are properly set up.
412 *
413 * <p>Called when a account / authority pair
414 * <ul>
415 * <li>that can be handled by this adapter</li>
416 * <li>{@link ContentResolver#requestSync(SyncRequest) is synced}</li>
417 * <li>and the account/provider {@link ContentResolver#getIsSyncable(Account, String) has
418 * unknown state (<0)}.</li>
419 * </ul>
420 *
421 * <p>This might be called on a different service connection as {@link #onPerformSync}.
422 *
Philip P. Moltmann186e3b32018-03-15 16:32:44 -0700423 * <p>The system expects this method to immediately return. If the call stalls the system
424 * behaves as if this method returned {@code true}. If it is required to perform a longer task
425 * (such as interacting with the user), return {@code false} and proceed in a difference
426 * context, such as an {@link android.app.Activity}, or foreground service. The sync can then be
427 * rescheduled once the account becomes syncable.
428 *
Philip P. Moltmann486b2412018-01-03 11:29:01 -0800429 * @return If {@code false} syncing is deferred. Returns {@code true} by default, i.e. by
430 * default syncing starts immediately.
431 */
Philip P. Moltmann186e3b32018-03-15 16:32:44 -0700432 @MainThread
Philip P. Moltmann486b2412018-01-03 11:29:01 -0800433 public boolean onUnsyncableAccount() {
434 return true;
435 }
436
437 /**
Fred Quintana21bb0de2009-06-16 10:24:58 -0700438 * Perform a sync for this account. SyncAdapter-specific parameters may
439 * be specified in extras, which is guaranteed to not be null. Invocations
440 * of this method are guaranteed to be serialized.
441 *
442 * @param account the account that should be synced
443 * @param extras SyncAdapter-specific parameters
444 * @param authority the authority of this sync request
445 * @param provider a ContentProviderClient that points to the ContentProvider for this
446 * authority
447 * @param syncResult SyncAdapter-specific parameters
448 */
Fred Quintanaf0380042009-10-06 17:05:58 -0700449 public abstract void onPerformSync(Account account, Bundle extras,
Fred Quintana21bb0de2009-06-16 10:24:58 -0700450 String authority, ContentProviderClient provider, SyncResult syncResult);
Fred Quintana274dc9d2009-12-11 13:17:08 -0800451
452 /**
Dianne Hackbornd01ed462015-06-22 17:41:44 -0700453 * Report that there was a security exception when opening the content provider
454 * prior to calling {@link #onPerformSync}. This will be treated as a sync
455 * database failure.
456 *
457 * @param account the account that attempted to sync
458 * @param extras SyncAdapter-specific parameters
459 * @param authority the authority of the failed sync request
460 * @param syncResult SyncAdapter-specific parameters
461 */
462 public void onSecurityException(Account account, Bundle extras,
463 String authority, SyncResult syncResult) {
464 }
465
466 /**
Fred Quintana274dc9d2009-12-11 13:17:08 -0800467 * Indicates that a sync operation has been canceled. This will be invoked on a separate
468 * thread than the sync thread and so you must consider the multi-threaded implications
469 * of the work that you do in this method.
Fred Quintana0c4d04a2010-11-03 17:02:55 -0700470 * <p>
471 * This will only be invoked when the SyncAdapter indicates that it doesn't support
472 * parallel syncs.
Fred Quintana274dc9d2009-12-11 13:17:08 -0800473 */
Fred Quintanad5e4fdc2010-03-30 15:16:21 -0700474 public void onSyncCanceled() {
475 final SyncThread syncThread;
476 synchronized (mSyncThreadLock) {
Fred Quintana0c4d04a2010-11-03 17:02:55 -0700477 syncThread = mSyncThreads.get(null);
Fred Quintanad5e4fdc2010-03-30 15:16:21 -0700478 }
479 if (syncThread != null) {
480 syncThread.interrupt();
481 }
Fred Quintana274dc9d2009-12-11 13:17:08 -0800482 }
Fred Quintana0c4d04a2010-11-03 17:02:55 -0700483
484 /**
485 * Indicates that a sync operation has been canceled. This will be invoked on a separate
486 * thread than the sync thread and so you must consider the multi-threaded implications
487 * of the work that you do in this method.
488 * <p>
489 * This will only be invoked when the SyncAdapter indicates that it does support
490 * parallel syncs.
491 * @param thread the Thread of the sync that is to be canceled.
492 */
493 public void onSyncCanceled(Thread thread) {
494 thread.interrupt();
495 }
Fred Quintanaf0380042009-10-06 17:05:58 -0700496}