blob: c0a268f65dde1bdb3e92b78a858a1ce98feffe15 [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
19import android.accounts.Account;
20import android.os.Bundle;
Fred Quintanaf0380042009-10-06 17:05:58 -070021import android.os.IBinder;
Ken Shirriff1719a392009-12-07 15:57:35 -080022import android.os.Process;
Fred Quintanae7424ff2009-10-14 15:59:21 -070023import android.os.RemoteException;
Fred Quintana21bb0de2009-06-16 10:24:58 -070024
25import java.util.concurrent.atomic.AtomicInteger;
26
27/**
28 * An abstract implementation of a SyncAdapter that spawns a thread to invoke a sync operation.
29 * If a sync operation is already in progress when a startSync() request is received then an error
30 * will be returned to the new request and the existing request will be allowed to continue.
31 * When a startSync() is received and there is no sync operation in progress then a thread
Fred Quintanaf0380042009-10-06 17:05:58 -070032 * will be started to run the operation and {@link #onPerformSync} will be invoked on that thread.
Fred Quintana21bb0de2009-06-16 10:24:58 -070033 * If a cancelSync() is received that matches an existing sync operation then the thread
34 * that is running that sync operation will be interrupted, which will indicate to the thread
35 * that the sync has been canceled.
Fred Quintana21bb0de2009-06-16 10:24:58 -070036 */
37public abstract class AbstractThreadedSyncAdapter {
Fred Quintana97ef7632009-12-10 10:33:18 -080038 /**
39 * Kernel event log tag. Also listed in data/etc/event-log-tags.
Joe Onoratod3ad6962010-09-16 13:38:25 -040040 * @deprecated Private constant. May go away in the next release.
Fred Quintana97ef7632009-12-10 10:33:18 -080041 */
Joe Onoratod3ad6962010-09-16 13:38:25 -040042 @Deprecated
Fred Quintana97ef7632009-12-10 10:33:18 -080043 public static final int LOG_SYNC_DETAILS = 2743;
44
Fred Quintana21bb0de2009-06-16 10:24:58 -070045 private final Context mContext;
46 private final AtomicInteger mNumSyncStarts;
47 private final ISyncAdapterImpl mISyncAdapterImpl;
48
Fred Quintana3cff76a2009-08-26 18:49:19 -070049 // all accesses to this member variable must be synchronized on mSyncThreadLock
Fred Quintana21bb0de2009-06-16 10:24:58 -070050 private SyncThread mSyncThread;
Fred Quintana3cff76a2009-08-26 18:49:19 -070051 private final Object mSyncThreadLock = new Object();
Fred Quintana21bb0de2009-06-16 10:24:58 -070052
Fred Quintana4a6679b2009-08-17 13:05:39 -070053 private final boolean mAutoInitialize;
Fred Quintana21bb0de2009-06-16 10:24:58 -070054
55 /**
56 * Creates an {@link AbstractThreadedSyncAdapter}.
Fred Quintana4a6679b2009-08-17 13:05:39 -070057 * @param context the {@link android.content.Context} that this is running within.
58 * @param autoInitialize if true then sync requests that have
59 * {@link ContentResolver#SYNC_EXTRAS_INITIALIZE} set will be internally handled by
60 * {@link AbstractThreadedSyncAdapter} by calling
61 * {@link ContentResolver#setIsSyncable(android.accounts.Account, String, int)} with 1 if it
62 * is currently set to <0.
Fred Quintana21bb0de2009-06-16 10:24:58 -070063 */
Fred Quintana4a6679b2009-08-17 13:05:39 -070064 public AbstractThreadedSyncAdapter(Context context, boolean autoInitialize) {
Fred Quintana21bb0de2009-06-16 10:24:58 -070065 mContext = context;
66 mISyncAdapterImpl = new ISyncAdapterImpl();
67 mNumSyncStarts = new AtomicInteger(0);
68 mSyncThread = null;
Fred Quintana4a6679b2009-08-17 13:05:39 -070069 mAutoInitialize = autoInitialize;
Fred Quintana21bb0de2009-06-16 10:24:58 -070070 }
71
Fred Quintanac298a852009-08-27 18:28:17 -070072 public Context getContext() {
73 return mContext;
74 }
75
Fred Quintanaf0380042009-10-06 17:05:58 -070076 private class ISyncAdapterImpl extends ISyncAdapter.Stub {
Fred Quintana21bb0de2009-06-16 10:24:58 -070077 public void startSync(ISyncContext syncContext, String authority, Account account,
78 Bundle extras) {
79 final SyncContext syncContextClient = new SyncContext(syncContext);
80
81 boolean alreadyInProgress;
82 // synchronize to make sure that mSyncThread doesn't change between when we
83 // check it and when we use it
Fred Quintana3cff76a2009-08-26 18:49:19 -070084 synchronized (mSyncThreadLock) {
Fred Quintana21bb0de2009-06-16 10:24:58 -070085 if (mSyncThread == null) {
Fred Quintana4a6679b2009-08-17 13:05:39 -070086 if (mAutoInitialize
87 && extras != null
88 && extras.getBoolean(ContentResolver.SYNC_EXTRAS_INITIALIZE, false)) {
89 if (ContentResolver.getIsSyncable(account, authority) < 0) {
90 ContentResolver.setIsSyncable(account, authority, 1);
91 }
92 syncContextClient.onFinished(new SyncResult());
93 return;
94 }
Fred Quintana21bb0de2009-06-16 10:24:58 -070095 mSyncThread = new SyncThread(
96 "SyncAdapterThread-" + mNumSyncStarts.incrementAndGet(),
97 syncContextClient, authority, account, extras);
Fred Quintana21bb0de2009-06-16 10:24:58 -070098 mSyncThread.start();
99 alreadyInProgress = false;
100 } else {
101 alreadyInProgress = true;
102 }
103 }
104
105 // do this outside since we don't want to call back into the syncContext while
106 // holding the synchronization lock
107 if (alreadyInProgress) {
108 syncContextClient.onFinished(SyncResult.ALREADY_IN_PROGRESS);
109 }
110 }
111
112 public void cancelSync(ISyncContext syncContext) {
113 // synchronize to make sure that mSyncThread doesn't change between when we
114 // check it and when we use it
Fred Quintanad5e4fdc2010-03-30 15:16:21 -0700115 final SyncThread syncThread;
Fred Quintana3cff76a2009-08-26 18:49:19 -0700116 synchronized (mSyncThreadLock) {
Fred Quintanad5e4fdc2010-03-30 15:16:21 -0700117 syncThread = mSyncThread;
118 }
119 if (syncThread != null
120 && syncThread.mSyncContext.getSyncContextBinder() == syncContext.asBinder()) {
121 onSyncCanceled();
Fred Quintana21bb0de2009-06-16 10:24:58 -0700122 }
123 }
Fred Quintanae7424ff2009-10-14 15:59:21 -0700124
125 public void initialize(Account account, String authority) throws RemoteException {
126 Bundle extras = new Bundle();
127 extras.putBoolean(ContentResolver.SYNC_EXTRAS_INITIALIZE, true);
128 startSync(null, authority, account, extras);
129 }
Fred Quintana21bb0de2009-06-16 10:24:58 -0700130 }
131
132 /**
Fred Quintanaf0380042009-10-06 17:05:58 -0700133 * The thread that invokes {@link AbstractThreadedSyncAdapter#onPerformSync}. It also acquires
134 * the provider for this sync before calling onPerformSync and releases it afterwards. Cancel
135 * this thread in order to cancel the sync.
Fred Quintana21bb0de2009-06-16 10:24:58 -0700136 */
137 private class SyncThread extends Thread {
138 private final SyncContext mSyncContext;
139 private final String mAuthority;
140 private final Account mAccount;
141 private final Bundle mExtras;
142
143 private SyncThread(String name, SyncContext syncContext, String authority,
144 Account account, Bundle extras) {
145 super(name);
146 mSyncContext = syncContext;
147 mAuthority = authority;
148 mAccount = account;
149 mExtras = extras;
150 }
151
152 public void run() {
Fred Quintana75d797c2009-08-25 15:05:02 -0700153 Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
154
Fred Quintana21bb0de2009-06-16 10:24:58 -0700155 if (isCanceled()) {
156 return;
157 }
158
159 SyncResult syncResult = new SyncResult();
160 ContentProviderClient provider = null;
161 try {
162 provider = mContext.getContentResolver().acquireContentProviderClient(mAuthority);
163 if (provider != null) {
Fred Quintanaf0380042009-10-06 17:05:58 -0700164 AbstractThreadedSyncAdapter.this.onPerformSync(mAccount, mExtras,
Fred Quintana21bb0de2009-06-16 10:24:58 -0700165 mAuthority, provider, syncResult);
166 } else {
Fred Quintanaf0380042009-10-06 17:05:58 -0700167 syncResult.databaseError = true;
Fred Quintana21bb0de2009-06-16 10:24:58 -0700168 }
169 } finally {
170 if (provider != null) {
171 provider.release();
172 }
173 if (!isCanceled()) {
174 mSyncContext.onFinished(syncResult);
175 }
176 // synchronize so that the assignment will be seen by other threads
177 // that also synchronize accesses to mSyncThread
Fred Quintana3cff76a2009-08-26 18:49:19 -0700178 synchronized (mSyncThreadLock) {
Fred Quintana21bb0de2009-06-16 10:24:58 -0700179 mSyncThread = null;
180 }
181 }
182 }
183
184 private boolean isCanceled() {
185 return Thread.currentThread().isInterrupted();
186 }
187 }
188
189 /**
Fred Quintanaf0380042009-10-06 17:05:58 -0700190 * @return a reference to the IBinder of the SyncAdapter service.
Fred Quintana21bb0de2009-06-16 10:24:58 -0700191 */
Fred Quintanaf0380042009-10-06 17:05:58 -0700192 public final IBinder getSyncAdapterBinder() {
193 return mISyncAdapterImpl.asBinder();
Fred Quintana21bb0de2009-06-16 10:24:58 -0700194 }
195
196 /**
197 * Perform a sync for this account. SyncAdapter-specific parameters may
198 * be specified in extras, which is guaranteed to not be null. Invocations
199 * of this method are guaranteed to be serialized.
200 *
201 * @param account the account that should be synced
202 * @param extras SyncAdapter-specific parameters
203 * @param authority the authority of this sync request
204 * @param provider a ContentProviderClient that points to the ContentProvider for this
205 * authority
206 * @param syncResult SyncAdapter-specific parameters
207 */
Fred Quintanaf0380042009-10-06 17:05:58 -0700208 public abstract void onPerformSync(Account account, Bundle extras,
Fred Quintana21bb0de2009-06-16 10:24:58 -0700209 String authority, ContentProviderClient provider, SyncResult syncResult);
Fred Quintana274dc9d2009-12-11 13:17:08 -0800210
211 /**
212 * Indicates that a sync operation has been canceled. This will be invoked on a separate
213 * thread than the sync thread and so you must consider the multi-threaded implications
214 * of the work that you do in this method.
215 *
Fred Quintana274dc9d2009-12-11 13:17:08 -0800216 */
Fred Quintanad5e4fdc2010-03-30 15:16:21 -0700217 public void onSyncCanceled() {
218 final SyncThread syncThread;
219 synchronized (mSyncThreadLock) {
220 syncThread = mSyncThread;
221 }
222 if (syncThread != null) {
223 syncThread.interrupt();
224 }
Fred Quintana274dc9d2009-12-11 13:17:08 -0800225 }
Fred Quintanaf0380042009-10-06 17:05:58 -0700226}