blob: 9dd7b9f095632dd7244978a0fea2d09e424e38f2 [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.
40 * @Deprecated
41 */
42 public static final int LOG_SYNC_DETAILS = 2743;
43
Fred Quintana21bb0de2009-06-16 10:24:58 -070044 private final Context mContext;
45 private final AtomicInteger mNumSyncStarts;
46 private final ISyncAdapterImpl mISyncAdapterImpl;
47
Fred Quintana3cff76a2009-08-26 18:49:19 -070048 // all accesses to this member variable must be synchronized on mSyncThreadLock
Fred Quintana21bb0de2009-06-16 10:24:58 -070049 private SyncThread mSyncThread;
Fred Quintana3cff76a2009-08-26 18:49:19 -070050 private final Object mSyncThreadLock = new Object();
Fred Quintana21bb0de2009-06-16 10:24:58 -070051
Fred Quintana4a6679b2009-08-17 13:05:39 -070052 private final boolean mAutoInitialize;
Fred Quintana21bb0de2009-06-16 10:24:58 -070053
54 /**
55 * Creates an {@link AbstractThreadedSyncAdapter}.
Fred Quintana4a6679b2009-08-17 13:05:39 -070056 * @param context the {@link android.content.Context} that this is running within.
57 * @param autoInitialize if true then sync requests that have
58 * {@link ContentResolver#SYNC_EXTRAS_INITIALIZE} set will be internally handled by
59 * {@link AbstractThreadedSyncAdapter} by calling
60 * {@link ContentResolver#setIsSyncable(android.accounts.Account, String, int)} with 1 if it
61 * is currently set to <0.
Fred Quintana21bb0de2009-06-16 10:24:58 -070062 */
Fred Quintana4a6679b2009-08-17 13:05:39 -070063 public AbstractThreadedSyncAdapter(Context context, boolean autoInitialize) {
Fred Quintana21bb0de2009-06-16 10:24:58 -070064 mContext = context;
65 mISyncAdapterImpl = new ISyncAdapterImpl();
66 mNumSyncStarts = new AtomicInteger(0);
67 mSyncThread = null;
Fred Quintana4a6679b2009-08-17 13:05:39 -070068 mAutoInitialize = autoInitialize;
Fred Quintana21bb0de2009-06-16 10:24:58 -070069 }
70
Fred Quintanac298a852009-08-27 18:28:17 -070071 public Context getContext() {
72 return mContext;
73 }
74
Fred Quintanaf0380042009-10-06 17:05:58 -070075 private class ISyncAdapterImpl extends ISyncAdapter.Stub {
Fred Quintana21bb0de2009-06-16 10:24:58 -070076 public void startSync(ISyncContext syncContext, String authority, Account account,
77 Bundle extras) {
78 final SyncContext syncContextClient = new SyncContext(syncContext);
79
80 boolean alreadyInProgress;
81 // synchronize to make sure that mSyncThread doesn't change between when we
82 // check it and when we use it
Fred Quintana3cff76a2009-08-26 18:49:19 -070083 synchronized (mSyncThreadLock) {
Fred Quintana21bb0de2009-06-16 10:24:58 -070084 if (mSyncThread == null) {
Fred Quintana4a6679b2009-08-17 13:05:39 -070085 if (mAutoInitialize
86 && extras != null
87 && extras.getBoolean(ContentResolver.SYNC_EXTRAS_INITIALIZE, false)) {
88 if (ContentResolver.getIsSyncable(account, authority) < 0) {
89 ContentResolver.setIsSyncable(account, authority, 1);
90 }
91 syncContextClient.onFinished(new SyncResult());
92 return;
93 }
Fred Quintana21bb0de2009-06-16 10:24:58 -070094 mSyncThread = new SyncThread(
95 "SyncAdapterThread-" + mNumSyncStarts.incrementAndGet(),
96 syncContextClient, authority, account, extras);
Fred Quintana21bb0de2009-06-16 10:24:58 -070097 mSyncThread.start();
98 alreadyInProgress = false;
99 } else {
100 alreadyInProgress = true;
101 }
102 }
103
104 // do this outside since we don't want to call back into the syncContext while
105 // holding the synchronization lock
106 if (alreadyInProgress) {
107 syncContextClient.onFinished(SyncResult.ALREADY_IN_PROGRESS);
108 }
109 }
110
111 public void cancelSync(ISyncContext syncContext) {
112 // synchronize to make sure that mSyncThread doesn't change between when we
113 // check it and when we use it
Fred Quintanad5e4fdc2010-03-30 15:16:21 -0700114 final SyncThread syncThread;
Fred Quintana3cff76a2009-08-26 18:49:19 -0700115 synchronized (mSyncThreadLock) {
Fred Quintanad5e4fdc2010-03-30 15:16:21 -0700116 syncThread = mSyncThread;
117 }
118 if (syncThread != null
119 && syncThread.mSyncContext.getSyncContextBinder() == syncContext.asBinder()) {
120 onSyncCanceled();
Fred Quintana21bb0de2009-06-16 10:24:58 -0700121 }
122 }
Fred Quintanae7424ff2009-10-14 15:59:21 -0700123
124 public void initialize(Account account, String authority) throws RemoteException {
125 Bundle extras = new Bundle();
126 extras.putBoolean(ContentResolver.SYNC_EXTRAS_INITIALIZE, true);
127 startSync(null, authority, account, extras);
128 }
Fred Quintana21bb0de2009-06-16 10:24:58 -0700129 }
130
131 /**
Fred Quintanaf0380042009-10-06 17:05:58 -0700132 * The thread that invokes {@link AbstractThreadedSyncAdapter#onPerformSync}. It also acquires
133 * the provider for this sync before calling onPerformSync and releases it afterwards. Cancel
134 * this thread in order to cancel the sync.
Fred Quintana21bb0de2009-06-16 10:24:58 -0700135 */
136 private class SyncThread extends Thread {
137 private final SyncContext mSyncContext;
138 private final String mAuthority;
139 private final Account mAccount;
140 private final Bundle mExtras;
141
142 private SyncThread(String name, SyncContext syncContext, String authority,
143 Account account, Bundle extras) {
144 super(name);
145 mSyncContext = syncContext;
146 mAuthority = authority;
147 mAccount = account;
148 mExtras = extras;
149 }
150
151 public void run() {
Fred Quintana75d797c2009-08-25 15:05:02 -0700152 Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
153
Fred Quintana21bb0de2009-06-16 10:24:58 -0700154 if (isCanceled()) {
155 return;
156 }
157
158 SyncResult syncResult = new SyncResult();
159 ContentProviderClient provider = null;
160 try {
161 provider = mContext.getContentResolver().acquireContentProviderClient(mAuthority);
162 if (provider != null) {
Fred Quintanaf0380042009-10-06 17:05:58 -0700163 AbstractThreadedSyncAdapter.this.onPerformSync(mAccount, mExtras,
Fred Quintana21bb0de2009-06-16 10:24:58 -0700164 mAuthority, provider, syncResult);
165 } else {
Fred Quintanaf0380042009-10-06 17:05:58 -0700166 syncResult.databaseError = true;
Fred Quintana21bb0de2009-06-16 10:24:58 -0700167 }
168 } finally {
169 if (provider != null) {
170 provider.release();
171 }
172 if (!isCanceled()) {
173 mSyncContext.onFinished(syncResult);
174 }
175 // synchronize so that the assignment will be seen by other threads
176 // that also synchronize accesses to mSyncThread
Fred Quintana3cff76a2009-08-26 18:49:19 -0700177 synchronized (mSyncThreadLock) {
Fred Quintana21bb0de2009-06-16 10:24:58 -0700178 mSyncThread = null;
179 }
180 }
181 }
182
183 private boolean isCanceled() {
184 return Thread.currentThread().isInterrupted();
185 }
186 }
187
188 /**
Fred Quintanaf0380042009-10-06 17:05:58 -0700189 * @return a reference to the IBinder of the SyncAdapter service.
Fred Quintana21bb0de2009-06-16 10:24:58 -0700190 */
Fred Quintanaf0380042009-10-06 17:05:58 -0700191 public final IBinder getSyncAdapterBinder() {
192 return mISyncAdapterImpl.asBinder();
Fred Quintana21bb0de2009-06-16 10:24:58 -0700193 }
194
195 /**
196 * Perform a sync for this account. SyncAdapter-specific parameters may
197 * be specified in extras, which is guaranteed to not be null. Invocations
198 * of this method are guaranteed to be serialized.
199 *
200 * @param account the account that should be synced
201 * @param extras SyncAdapter-specific parameters
202 * @param authority the authority of this sync request
203 * @param provider a ContentProviderClient that points to the ContentProvider for this
204 * authority
205 * @param syncResult SyncAdapter-specific parameters
206 */
Fred Quintanaf0380042009-10-06 17:05:58 -0700207 public abstract void onPerformSync(Account account, Bundle extras,
Fred Quintana21bb0de2009-06-16 10:24:58 -0700208 String authority, ContentProviderClient provider, SyncResult syncResult);
Fred Quintana274dc9d2009-12-11 13:17:08 -0800209
210 /**
211 * Indicates that a sync operation has been canceled. This will be invoked on a separate
212 * thread than the sync thread and so you must consider the multi-threaded implications
213 * of the work that you do in this method.
214 *
Fred Quintana274dc9d2009-12-11 13:17:08 -0800215 */
Fred Quintanad5e4fdc2010-03-30 15:16:21 -0700216 public void onSyncCanceled() {
217 final SyncThread syncThread;
218 synchronized (mSyncThreadLock) {
219 syncThread = mSyncThread;
220 }
221 if (syncThread != null) {
222 syncThread.interrupt();
223 }
Fred Quintana274dc9d2009-12-11 13:17:08 -0800224 }
Fred Quintanaf0380042009-10-06 17:05:58 -0700225}