121bb0deb36af32339521038cdbd827f74468df4aFred Quintana/* 221bb0deb36af32339521038cdbd827f74468df4aFred Quintana * Copyright (C) 2009 The Android Open Source Project 321bb0deb36af32339521038cdbd827f74468df4aFred Quintana * 421bb0deb36af32339521038cdbd827f74468df4aFred Quintana * Licensed under the Apache License, Version 2.0 (the "License"); 521bb0deb36af32339521038cdbd827f74468df4aFred Quintana * you may not use this file except in compliance with the License. 621bb0deb36af32339521038cdbd827f74468df4aFred Quintana * You may obtain a copy of the License at 721bb0deb36af32339521038cdbd827f74468df4aFred Quintana * 821bb0deb36af32339521038cdbd827f74468df4aFred Quintana * http://www.apache.org/licenses/LICENSE-2.0 921bb0deb36af32339521038cdbd827f74468df4aFred Quintana * 1021bb0deb36af32339521038cdbd827f74468df4aFred Quintana * Unless required by applicable law or agreed to in writing, software 1121bb0deb36af32339521038cdbd827f74468df4aFred Quintana * distributed under the License is distributed on an "AS IS" BASIS, 1221bb0deb36af32339521038cdbd827f74468df4aFred Quintana * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1321bb0deb36af32339521038cdbd827f74468df4aFred Quintana * See the License for the specific language governing permissions and 1421bb0deb36af32339521038cdbd827f74468df4aFred Quintana * limitations under the License. 1521bb0deb36af32339521038cdbd827f74468df4aFred Quintana */ 1621bb0deb36af32339521038cdbd827f74468df4aFred Quintana 1721bb0deb36af32339521038cdbd827f74468df4aFred Quintanapackage android.content; 1821bb0deb36af32339521038cdbd827f74468df4aFred Quintana 1921bb0deb36af32339521038cdbd827f74468df4aFred Quintanaimport android.accounts.Account; 2021bb0deb36af32339521038cdbd827f74468df4aFred Quintanaimport android.os.Bundle; 21f038004f4a5e4fab18df9c87573ba1e82790c30fFred Quintanaimport android.os.IBinder; 221719a39a4c0ff3afbf9c9e5f03f20ba50f490902Ken Shirriffimport android.os.Process; 23e7424ffdafb0c18f753f383ebfb121ea5ebf582bFred Quintanaimport android.os.RemoteException; 2421bb0deb36af32339521038cdbd827f74468df4aFred Quintana 2521bb0deb36af32339521038cdbd827f74468df4aFred Quintanaimport java.util.concurrent.atomic.AtomicInteger; 2621bb0deb36af32339521038cdbd827f74468df4aFred Quintana 2721bb0deb36af32339521038cdbd827f74468df4aFred Quintana/** 2821bb0deb36af32339521038cdbd827f74468df4aFred Quintana * An abstract implementation of a SyncAdapter that spawns a thread to invoke a sync operation. 2921bb0deb36af32339521038cdbd827f74468df4aFred Quintana * If a sync operation is already in progress when a startSync() request is received then an error 3021bb0deb36af32339521038cdbd827f74468df4aFred Quintana * will be returned to the new request and the existing request will be allowed to continue. 3121bb0deb36af32339521038cdbd827f74468df4aFred Quintana * When a startSync() is received and there is no sync operation in progress then a thread 32f038004f4a5e4fab18df9c87573ba1e82790c30fFred Quintana * will be started to run the operation and {@link #onPerformSync} will be invoked on that thread. 3321bb0deb36af32339521038cdbd827f74468df4aFred Quintana * If a cancelSync() is received that matches an existing sync operation then the thread 3421bb0deb36af32339521038cdbd827f74468df4aFred Quintana * that is running that sync operation will be interrupted, which will indicate to the thread 3521bb0deb36af32339521038cdbd827f74468df4aFred Quintana * that the sync has been canceled. 3621bb0deb36af32339521038cdbd827f74468df4aFred Quintana */ 3721bb0deb36af32339521038cdbd827f74468df4aFred Quintanapublic abstract class AbstractThreadedSyncAdapter { 3897ef7637c6799e72956db8e08192539f1b1942f6Fred Quintana /** 3997ef7637c6799e72956db8e08192539f1b1942f6Fred Quintana * Kernel event log tag. Also listed in data/etc/event-log-tags. 4097ef7637c6799e72956db8e08192539f1b1942f6Fred Quintana * @Deprecated 4197ef7637c6799e72956db8e08192539f1b1942f6Fred Quintana */ 4297ef7637c6799e72956db8e08192539f1b1942f6Fred Quintana public static final int LOG_SYNC_DETAILS = 2743; 4397ef7637c6799e72956db8e08192539f1b1942f6Fred Quintana 4421bb0deb36af32339521038cdbd827f74468df4aFred Quintana private final Context mContext; 4521bb0deb36af32339521038cdbd827f74468df4aFred Quintana private final AtomicInteger mNumSyncStarts; 4621bb0deb36af32339521038cdbd827f74468df4aFred Quintana private final ISyncAdapterImpl mISyncAdapterImpl; 4721bb0deb36af32339521038cdbd827f74468df4aFred Quintana 483cff76aaa893049d02467a231d477e86a0f80daaFred Quintana // all accesses to this member variable must be synchronized on mSyncThreadLock 4921bb0deb36af32339521038cdbd827f74468df4aFred Quintana private SyncThread mSyncThread; 503cff76aaa893049d02467a231d477e86a0f80daaFred Quintana private final Object mSyncThreadLock = new Object(); 5121bb0deb36af32339521038cdbd827f74468df4aFred Quintana 524a6679b97e0285c5b65ec5c0d9080ff90d3e9e81Fred Quintana private final boolean mAutoInitialize; 5321bb0deb36af32339521038cdbd827f74468df4aFred Quintana 5421bb0deb36af32339521038cdbd827f74468df4aFred Quintana /** 5521bb0deb36af32339521038cdbd827f74468df4aFred Quintana * Creates an {@link AbstractThreadedSyncAdapter}. 564a6679b97e0285c5b65ec5c0d9080ff90d3e9e81Fred Quintana * @param context the {@link android.content.Context} that this is running within. 574a6679b97e0285c5b65ec5c0d9080ff90d3e9e81Fred Quintana * @param autoInitialize if true then sync requests that have 584a6679b97e0285c5b65ec5c0d9080ff90d3e9e81Fred Quintana * {@link ContentResolver#SYNC_EXTRAS_INITIALIZE} set will be internally handled by 594a6679b97e0285c5b65ec5c0d9080ff90d3e9e81Fred Quintana * {@link AbstractThreadedSyncAdapter} by calling 604a6679b97e0285c5b65ec5c0d9080ff90d3e9e81Fred Quintana * {@link ContentResolver#setIsSyncable(android.accounts.Account, String, int)} with 1 if it 614a6679b97e0285c5b65ec5c0d9080ff90d3e9e81Fred Quintana * is currently set to <0. 6221bb0deb36af32339521038cdbd827f74468df4aFred Quintana */ 634a6679b97e0285c5b65ec5c0d9080ff90d3e9e81Fred Quintana public AbstractThreadedSyncAdapter(Context context, boolean autoInitialize) { 6421bb0deb36af32339521038cdbd827f74468df4aFred Quintana mContext = context; 6521bb0deb36af32339521038cdbd827f74468df4aFred Quintana mISyncAdapterImpl = new ISyncAdapterImpl(); 6621bb0deb36af32339521038cdbd827f74468df4aFred Quintana mNumSyncStarts = new AtomicInteger(0); 6721bb0deb36af32339521038cdbd827f74468df4aFred Quintana mSyncThread = null; 684a6679b97e0285c5b65ec5c0d9080ff90d3e9e81Fred Quintana mAutoInitialize = autoInitialize; 6921bb0deb36af32339521038cdbd827f74468df4aFred Quintana } 7021bb0deb36af32339521038cdbd827f74468df4aFred Quintana 71c298a8518a8fd73a303132c7db241f10eb46c5b6Fred Quintana public Context getContext() { 72c298a8518a8fd73a303132c7db241f10eb46c5b6Fred Quintana return mContext; 73c298a8518a8fd73a303132c7db241f10eb46c5b6Fred Quintana } 74c298a8518a8fd73a303132c7db241f10eb46c5b6Fred Quintana 75f038004f4a5e4fab18df9c87573ba1e82790c30fFred Quintana private class ISyncAdapterImpl extends ISyncAdapter.Stub { 7621bb0deb36af32339521038cdbd827f74468df4aFred Quintana public void startSync(ISyncContext syncContext, String authority, Account account, 7721bb0deb36af32339521038cdbd827f74468df4aFred Quintana Bundle extras) { 7821bb0deb36af32339521038cdbd827f74468df4aFred Quintana final SyncContext syncContextClient = new SyncContext(syncContext); 7921bb0deb36af32339521038cdbd827f74468df4aFred Quintana 8021bb0deb36af32339521038cdbd827f74468df4aFred Quintana boolean alreadyInProgress; 8121bb0deb36af32339521038cdbd827f74468df4aFred Quintana // synchronize to make sure that mSyncThread doesn't change between when we 8221bb0deb36af32339521038cdbd827f74468df4aFred Quintana // check it and when we use it 833cff76aaa893049d02467a231d477e86a0f80daaFred Quintana synchronized (mSyncThreadLock) { 8421bb0deb36af32339521038cdbd827f74468df4aFred Quintana if (mSyncThread == null) { 854a6679b97e0285c5b65ec5c0d9080ff90d3e9e81Fred Quintana if (mAutoInitialize 864a6679b97e0285c5b65ec5c0d9080ff90d3e9e81Fred Quintana && extras != null 874a6679b97e0285c5b65ec5c0d9080ff90d3e9e81Fred Quintana && extras.getBoolean(ContentResolver.SYNC_EXTRAS_INITIALIZE, false)) { 884a6679b97e0285c5b65ec5c0d9080ff90d3e9e81Fred Quintana if (ContentResolver.getIsSyncable(account, authority) < 0) { 894a6679b97e0285c5b65ec5c0d9080ff90d3e9e81Fred Quintana ContentResolver.setIsSyncable(account, authority, 1); 904a6679b97e0285c5b65ec5c0d9080ff90d3e9e81Fred Quintana } 914a6679b97e0285c5b65ec5c0d9080ff90d3e9e81Fred Quintana syncContextClient.onFinished(new SyncResult()); 924a6679b97e0285c5b65ec5c0d9080ff90d3e9e81Fred Quintana return; 934a6679b97e0285c5b65ec5c0d9080ff90d3e9e81Fred Quintana } 9421bb0deb36af32339521038cdbd827f74468df4aFred Quintana mSyncThread = new SyncThread( 9521bb0deb36af32339521038cdbd827f74468df4aFred Quintana "SyncAdapterThread-" + mNumSyncStarts.incrementAndGet(), 9621bb0deb36af32339521038cdbd827f74468df4aFred Quintana syncContextClient, authority, account, extras); 9721bb0deb36af32339521038cdbd827f74468df4aFred Quintana mSyncThread.start(); 9821bb0deb36af32339521038cdbd827f74468df4aFred Quintana alreadyInProgress = false; 9921bb0deb36af32339521038cdbd827f74468df4aFred Quintana } else { 10021bb0deb36af32339521038cdbd827f74468df4aFred Quintana alreadyInProgress = true; 10121bb0deb36af32339521038cdbd827f74468df4aFred Quintana } 10221bb0deb36af32339521038cdbd827f74468df4aFred Quintana } 10321bb0deb36af32339521038cdbd827f74468df4aFred Quintana 10421bb0deb36af32339521038cdbd827f74468df4aFred Quintana // do this outside since we don't want to call back into the syncContext while 10521bb0deb36af32339521038cdbd827f74468df4aFred Quintana // holding the synchronization lock 10621bb0deb36af32339521038cdbd827f74468df4aFred Quintana if (alreadyInProgress) { 10721bb0deb36af32339521038cdbd827f74468df4aFred Quintana syncContextClient.onFinished(SyncResult.ALREADY_IN_PROGRESS); 10821bb0deb36af32339521038cdbd827f74468df4aFred Quintana } 10921bb0deb36af32339521038cdbd827f74468df4aFred Quintana } 11021bb0deb36af32339521038cdbd827f74468df4aFred Quintana 11121bb0deb36af32339521038cdbd827f74468df4aFred Quintana public void cancelSync(ISyncContext syncContext) { 11221bb0deb36af32339521038cdbd827f74468df4aFred Quintana // synchronize to make sure that mSyncThread doesn't change between when we 11321bb0deb36af32339521038cdbd827f74468df4aFred Quintana // check it and when we use it 114d5e4fdc8a4743abc0d9fe3cb952a78f9ad078c6bFred Quintana final SyncThread syncThread; 1153cff76aaa893049d02467a231d477e86a0f80daaFred Quintana synchronized (mSyncThreadLock) { 116d5e4fdc8a4743abc0d9fe3cb952a78f9ad078c6bFred Quintana syncThread = mSyncThread; 117d5e4fdc8a4743abc0d9fe3cb952a78f9ad078c6bFred Quintana } 118d5e4fdc8a4743abc0d9fe3cb952a78f9ad078c6bFred Quintana if (syncThread != null 119d5e4fdc8a4743abc0d9fe3cb952a78f9ad078c6bFred Quintana && syncThread.mSyncContext.getSyncContextBinder() == syncContext.asBinder()) { 120d5e4fdc8a4743abc0d9fe3cb952a78f9ad078c6bFred Quintana onSyncCanceled(); 12121bb0deb36af32339521038cdbd827f74468df4aFred Quintana } 12221bb0deb36af32339521038cdbd827f74468df4aFred Quintana } 123e7424ffdafb0c18f753f383ebfb121ea5ebf582bFred Quintana 124e7424ffdafb0c18f753f383ebfb121ea5ebf582bFred Quintana public void initialize(Account account, String authority) throws RemoteException { 125e7424ffdafb0c18f753f383ebfb121ea5ebf582bFred Quintana Bundle extras = new Bundle(); 126e7424ffdafb0c18f753f383ebfb121ea5ebf582bFred Quintana extras.putBoolean(ContentResolver.SYNC_EXTRAS_INITIALIZE, true); 127e7424ffdafb0c18f753f383ebfb121ea5ebf582bFred Quintana startSync(null, authority, account, extras); 128e7424ffdafb0c18f753f383ebfb121ea5ebf582bFred Quintana } 12921bb0deb36af32339521038cdbd827f74468df4aFred Quintana } 13021bb0deb36af32339521038cdbd827f74468df4aFred Quintana 13121bb0deb36af32339521038cdbd827f74468df4aFred Quintana /** 132f038004f4a5e4fab18df9c87573ba1e82790c30fFred Quintana * The thread that invokes {@link AbstractThreadedSyncAdapter#onPerformSync}. It also acquires 133f038004f4a5e4fab18df9c87573ba1e82790c30fFred Quintana * the provider for this sync before calling onPerformSync and releases it afterwards. Cancel 134f038004f4a5e4fab18df9c87573ba1e82790c30fFred Quintana * this thread in order to cancel the sync. 13521bb0deb36af32339521038cdbd827f74468df4aFred Quintana */ 13621bb0deb36af32339521038cdbd827f74468df4aFred Quintana private class SyncThread extends Thread { 13721bb0deb36af32339521038cdbd827f74468df4aFred Quintana private final SyncContext mSyncContext; 13821bb0deb36af32339521038cdbd827f74468df4aFred Quintana private final String mAuthority; 13921bb0deb36af32339521038cdbd827f74468df4aFred Quintana private final Account mAccount; 14021bb0deb36af32339521038cdbd827f74468df4aFred Quintana private final Bundle mExtras; 14121bb0deb36af32339521038cdbd827f74468df4aFred Quintana 14221bb0deb36af32339521038cdbd827f74468df4aFred Quintana private SyncThread(String name, SyncContext syncContext, String authority, 14321bb0deb36af32339521038cdbd827f74468df4aFred Quintana Account account, Bundle extras) { 14421bb0deb36af32339521038cdbd827f74468df4aFred Quintana super(name); 14521bb0deb36af32339521038cdbd827f74468df4aFred Quintana mSyncContext = syncContext; 14621bb0deb36af32339521038cdbd827f74468df4aFred Quintana mAuthority = authority; 14721bb0deb36af32339521038cdbd827f74468df4aFred Quintana mAccount = account; 14821bb0deb36af32339521038cdbd827f74468df4aFred Quintana mExtras = extras; 14921bb0deb36af32339521038cdbd827f74468df4aFred Quintana } 15021bb0deb36af32339521038cdbd827f74468df4aFred Quintana 15121bb0deb36af32339521038cdbd827f74468df4aFred Quintana public void run() { 15275d797c2e78d53f49d05b518adb14fd57e0c785cFred Quintana Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); 15375d797c2e78d53f49d05b518adb14fd57e0c785cFred Quintana 15421bb0deb36af32339521038cdbd827f74468df4aFred Quintana if (isCanceled()) { 15521bb0deb36af32339521038cdbd827f74468df4aFred Quintana return; 15621bb0deb36af32339521038cdbd827f74468df4aFred Quintana } 15721bb0deb36af32339521038cdbd827f74468df4aFred Quintana 15821bb0deb36af32339521038cdbd827f74468df4aFred Quintana SyncResult syncResult = new SyncResult(); 15921bb0deb36af32339521038cdbd827f74468df4aFred Quintana ContentProviderClient provider = null; 16021bb0deb36af32339521038cdbd827f74468df4aFred Quintana try { 16121bb0deb36af32339521038cdbd827f74468df4aFred Quintana provider = mContext.getContentResolver().acquireContentProviderClient(mAuthority); 16221bb0deb36af32339521038cdbd827f74468df4aFred Quintana if (provider != null) { 163f038004f4a5e4fab18df9c87573ba1e82790c30fFred Quintana AbstractThreadedSyncAdapter.this.onPerformSync(mAccount, mExtras, 16421bb0deb36af32339521038cdbd827f74468df4aFred Quintana mAuthority, provider, syncResult); 16521bb0deb36af32339521038cdbd827f74468df4aFred Quintana } else { 166f038004f4a5e4fab18df9c87573ba1e82790c30fFred Quintana syncResult.databaseError = true; 16721bb0deb36af32339521038cdbd827f74468df4aFred Quintana } 16821bb0deb36af32339521038cdbd827f74468df4aFred Quintana } finally { 16921bb0deb36af32339521038cdbd827f74468df4aFred Quintana if (provider != null) { 17021bb0deb36af32339521038cdbd827f74468df4aFred Quintana provider.release(); 17121bb0deb36af32339521038cdbd827f74468df4aFred Quintana } 17221bb0deb36af32339521038cdbd827f74468df4aFred Quintana if (!isCanceled()) { 17321bb0deb36af32339521038cdbd827f74468df4aFred Quintana mSyncContext.onFinished(syncResult); 17421bb0deb36af32339521038cdbd827f74468df4aFred Quintana } 17521bb0deb36af32339521038cdbd827f74468df4aFred Quintana // synchronize so that the assignment will be seen by other threads 17621bb0deb36af32339521038cdbd827f74468df4aFred Quintana // that also synchronize accesses to mSyncThread 1773cff76aaa893049d02467a231d477e86a0f80daaFred Quintana synchronized (mSyncThreadLock) { 17821bb0deb36af32339521038cdbd827f74468df4aFred Quintana mSyncThread = null; 17921bb0deb36af32339521038cdbd827f74468df4aFred Quintana } 18021bb0deb36af32339521038cdbd827f74468df4aFred Quintana } 18121bb0deb36af32339521038cdbd827f74468df4aFred Quintana } 18221bb0deb36af32339521038cdbd827f74468df4aFred Quintana 18321bb0deb36af32339521038cdbd827f74468df4aFred Quintana private boolean isCanceled() { 18421bb0deb36af32339521038cdbd827f74468df4aFred Quintana return Thread.currentThread().isInterrupted(); 18521bb0deb36af32339521038cdbd827f74468df4aFred Quintana } 18621bb0deb36af32339521038cdbd827f74468df4aFred Quintana } 18721bb0deb36af32339521038cdbd827f74468df4aFred Quintana 18821bb0deb36af32339521038cdbd827f74468df4aFred Quintana /** 189f038004f4a5e4fab18df9c87573ba1e82790c30fFred Quintana * @return a reference to the IBinder of the SyncAdapter service. 19021bb0deb36af32339521038cdbd827f74468df4aFred Quintana */ 191f038004f4a5e4fab18df9c87573ba1e82790c30fFred Quintana public final IBinder getSyncAdapterBinder() { 192f038004f4a5e4fab18df9c87573ba1e82790c30fFred Quintana return mISyncAdapterImpl.asBinder(); 19321bb0deb36af32339521038cdbd827f74468df4aFred Quintana } 19421bb0deb36af32339521038cdbd827f74468df4aFred Quintana 19521bb0deb36af32339521038cdbd827f74468df4aFred Quintana /** 19621bb0deb36af32339521038cdbd827f74468df4aFred Quintana * Perform a sync for this account. SyncAdapter-specific parameters may 19721bb0deb36af32339521038cdbd827f74468df4aFred Quintana * be specified in extras, which is guaranteed to not be null. Invocations 19821bb0deb36af32339521038cdbd827f74468df4aFred Quintana * of this method are guaranteed to be serialized. 19921bb0deb36af32339521038cdbd827f74468df4aFred Quintana * 20021bb0deb36af32339521038cdbd827f74468df4aFred Quintana * @param account the account that should be synced 20121bb0deb36af32339521038cdbd827f74468df4aFred Quintana * @param extras SyncAdapter-specific parameters 20221bb0deb36af32339521038cdbd827f74468df4aFred Quintana * @param authority the authority of this sync request 20321bb0deb36af32339521038cdbd827f74468df4aFred Quintana * @param provider a ContentProviderClient that points to the ContentProvider for this 20421bb0deb36af32339521038cdbd827f74468df4aFred Quintana * authority 20521bb0deb36af32339521038cdbd827f74468df4aFred Quintana * @param syncResult SyncAdapter-specific parameters 20621bb0deb36af32339521038cdbd827f74468df4aFred Quintana */ 207f038004f4a5e4fab18df9c87573ba1e82790c30fFred Quintana public abstract void onPerformSync(Account account, Bundle extras, 20821bb0deb36af32339521038cdbd827f74468df4aFred Quintana String authority, ContentProviderClient provider, SyncResult syncResult); 209274dc9d35fdf5d0464f74071a9a8f14e497d4d5fFred Quintana 210274dc9d35fdf5d0464f74071a9a8f14e497d4d5fFred Quintana /** 211274dc9d35fdf5d0464f74071a9a8f14e497d4d5fFred Quintana * Indicates that a sync operation has been canceled. This will be invoked on a separate 212274dc9d35fdf5d0464f74071a9a8f14e497d4d5fFred Quintana * thread than the sync thread and so you must consider the multi-threaded implications 213274dc9d35fdf5d0464f74071a9a8f14e497d4d5fFred Quintana * of the work that you do in this method. 214274dc9d35fdf5d0464f74071a9a8f14e497d4d5fFred Quintana * 215274dc9d35fdf5d0464f74071a9a8f14e497d4d5fFred Quintana */ 216d5e4fdc8a4743abc0d9fe3cb952a78f9ad078c6bFred Quintana public void onSyncCanceled() { 217d5e4fdc8a4743abc0d9fe3cb952a78f9ad078c6bFred Quintana final SyncThread syncThread; 218d5e4fdc8a4743abc0d9fe3cb952a78f9ad078c6bFred Quintana synchronized (mSyncThreadLock) { 219d5e4fdc8a4743abc0d9fe3cb952a78f9ad078c6bFred Quintana syncThread = mSyncThread; 220d5e4fdc8a4743abc0d9fe3cb952a78f9ad078c6bFred Quintana } 221d5e4fdc8a4743abc0d9fe3cb952a78f9ad078c6bFred Quintana if (syncThread != null) { 222d5e4fdc8a4743abc0d9fe3cb952a78f9ad078c6bFred Quintana syncThread.interrupt(); 223d5e4fdc8a4743abc0d9fe3cb952a78f9ad078c6bFred Quintana } 224274dc9d35fdf5d0464f74071a9a8f14e497d4d5fFred Quintana } 225f038004f4a5e4fab18df9c87573ba1e82790c30fFred Quintana} 226