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;
2409b45a3ad96379b4181d32f8391f63e9c57dc316Andy Stadlerimport android.os.Trace;
2521bb0deb36af32339521038cdbd827f74468df4aFred Quintana
260c4d04ac2e8aa62560d8d767fa1c87e5361b0b08Fred Quintanaimport java.util.HashMap;
2721bb0deb36af32339521038cdbd827f74468df4aFred Quintanaimport java.util.concurrent.atomic.AtomicInteger;
2821bb0deb36af32339521038cdbd827f74468df4aFred Quintana
2921bb0deb36af32339521038cdbd827f74468df4aFred Quintana/**
3021bb0deb36af32339521038cdbd827f74468df4aFred Quintana * An abstract implementation of a SyncAdapter that spawns a thread to invoke a sync operation.
3121bb0deb36af32339521038cdbd827f74468df4aFred Quintana * If a sync operation is already in progress when a startSync() request is received then an error
3221bb0deb36af32339521038cdbd827f74468df4aFred Quintana * will be returned to the new request and the existing request will be allowed to continue.
3321bb0deb36af32339521038cdbd827f74468df4aFred Quintana * When a startSync() is received and there is no sync operation in progress then a thread
34f038004f4a5e4fab18df9c87573ba1e82790c30fFred Quintana * will be started to run the operation and {@link #onPerformSync} will be invoked on that thread.
3521bb0deb36af32339521038cdbd827f74468df4aFred Quintana * If a cancelSync() is received that matches an existing sync operation then the thread
3621bb0deb36af32339521038cdbd827f74468df4aFred Quintana * that is running that sync operation will be interrupted, which will indicate to the thread
3721bb0deb36af32339521038cdbd827f74468df4aFred Quintana * that the sync has been canceled.
38e6d60ecdf668499f003a81274f18cb57075eb65bFred Quintana * <p>
39e6d60ecdf668499f003a81274f18cb57075eb65bFred Quintana * In order to be a sync adapter one must extend this class, provide implementations for the
40e6d60ecdf668499f003a81274f18cb57075eb65bFred Quintana * abstract methods and write a service that returns the result of {@link #getSyncAdapterBinder()}
41e6d60ecdf668499f003a81274f18cb57075eb65bFred Quintana * in the service's {@link android.app.Service#onBind(android.content.Intent)} when invoked
42e6d60ecdf668499f003a81274f18cb57075eb65bFred Quintana * with an intent with action <code>android.content.SyncAdapter</code>. This service
43e6d60ecdf668499f003a81274f18cb57075eb65bFred Quintana * must specify the following intent filter and metadata tags in its AndroidManifest.xml file
44e6d60ecdf668499f003a81274f18cb57075eb65bFred Quintana * <pre>
45e6d60ecdf668499f003a81274f18cb57075eb65bFred Quintana *   &lt;intent-filter&gt;
46e6d60ecdf668499f003a81274f18cb57075eb65bFred Quintana *     &lt;action android:name="android.content.SyncAdapter" /&gt;
47e6d60ecdf668499f003a81274f18cb57075eb65bFred Quintana *   &lt;/intent-filter&gt;
48e6d60ecdf668499f003a81274f18cb57075eb65bFred Quintana *   &lt;meta-data android:name="android.content.SyncAdapter"
49e6d60ecdf668499f003a81274f18cb57075eb65bFred Quintana *             android:resource="@xml/syncadapter" /&gt;
50e6d60ecdf668499f003a81274f18cb57075eb65bFred Quintana * </pre>
51e6d60ecdf668499f003a81274f18cb57075eb65bFred Quintana * The <code>android:resource</code> attribute must point to a resource that looks like:
52e6d60ecdf668499f003a81274f18cb57075eb65bFred Quintana * <pre>
53e6d60ecdf668499f003a81274f18cb57075eb65bFred Quintana * &lt;sync-adapter xmlns:android="http://schemas.android.com/apk/res/android"
54e6d60ecdf668499f003a81274f18cb57075eb65bFred Quintana *    android:contentAuthority="authority"
55e6d60ecdf668499f003a81274f18cb57075eb65bFred Quintana *    android:accountType="accountType"
56e6d60ecdf668499f003a81274f18cb57075eb65bFred Quintana *    android:userVisible="true|false"
57e6d60ecdf668499f003a81274f18cb57075eb65bFred Quintana *    android:supportsUploading="true|false"
58e6d60ecdf668499f003a81274f18cb57075eb65bFred Quintana *    android:allowParallelSyncs="true|false"
59e6d60ecdf668499f003a81274f18cb57075eb65bFred Quintana *    android:isAlwaysSyncable="true|false"
60e6d60ecdf668499f003a81274f18cb57075eb65bFred Quintana *    android:syncAdapterSettingsAction="ACTION_OF_SETTINGS_ACTIVITY"
61e6d60ecdf668499f003a81274f18cb57075eb65bFred Quintana * /&gt;
62e6d60ecdf668499f003a81274f18cb57075eb65bFred Quintana * </pre>
63e6d60ecdf668499f003a81274f18cb57075eb65bFred Quintana * <ul>
64e6d60ecdf668499f003a81274f18cb57075eb65bFred Quintana * <li>The <code>android:contentAuthority</code> and <code>android:accountType</code> attributes
65e6d60ecdf668499f003a81274f18cb57075eb65bFred Quintana * indicate which content authority and for which account types this sync adapter serves.
66e6d60ecdf668499f003a81274f18cb57075eb65bFred Quintana * <li><code>android:userVisible</code> defaults to true and controls whether or not this sync
67e6d60ecdf668499f003a81274f18cb57075eb65bFred Quintana * adapter shows up in the Sync Settings screen.
68e6d60ecdf668499f003a81274f18cb57075eb65bFred Quintana * <li><code>android:supportsUploading</code> defaults
69e6d60ecdf668499f003a81274f18cb57075eb65bFred Quintana * to true and if true an upload-only sync will be requested for all syncadapters associated
70e6d60ecdf668499f003a81274f18cb57075eb65bFred Quintana * with an authority whenever that authority's content provider does a
71e6d60ecdf668499f003a81274f18cb57075eb65bFred Quintana * {@link ContentResolver#notifyChange(android.net.Uri, android.database.ContentObserver, boolean)}
72e6d60ecdf668499f003a81274f18cb57075eb65bFred Quintana * with syncToNetwork set to true.
73e6d60ecdf668499f003a81274f18cb57075eb65bFred Quintana * <li><code>android:allowParallelSyncs</code> defaults to false and if true indicates that
74e6d60ecdf668499f003a81274f18cb57075eb65bFred Quintana * the sync adapter can handle syncs for multiple accounts at the same time. Otherwise
75e6d60ecdf668499f003a81274f18cb57075eb65bFred Quintana * the SyncManager will wait until the sync adapter is not in use before requesting that
76e6d60ecdf668499f003a81274f18cb57075eb65bFred Quintana * it sync an account's data.
77e6d60ecdf668499f003a81274f18cb57075eb65bFred Quintana * <li><code>android:isAlwaysSyncable</code> defaults to false and if true tells the SyncManager
78e6d60ecdf668499f003a81274f18cb57075eb65bFred Quintana * to intialize the isSyncable state to 1 for that sync adapter for each account that is added.
79e6d60ecdf668499f003a81274f18cb57075eb65bFred Quintana * <li><code>android:syncAdapterSettingsAction</code> defaults to null and if supplied it
80e6d60ecdf668499f003a81274f18cb57075eb65bFred Quintana * specifies an Intent action of an activity that can be used to adjust the sync adapter's
81e6d60ecdf668499f003a81274f18cb57075eb65bFred Quintana * sync settings. The activity must live in the same package as the sync adapter.
82e6d60ecdf668499f003a81274f18cb57075eb65bFred Quintana * </ul>
8321bb0deb36af32339521038cdbd827f74468df4aFred Quintana */
8421bb0deb36af32339521038cdbd827f74468df4aFred Quintanapublic abstract class AbstractThreadedSyncAdapter {
8597ef7637c6799e72956db8e08192539f1b1942f6Fred Quintana    /**
8697ef7637c6799e72956db8e08192539f1b1942f6Fred Quintana     * Kernel event log tag.  Also listed in data/etc/event-log-tags.
87d3ad696b1daaa6c92d8fa268c81ce220ed1d9ffcJoe Onorato     * @deprecated Private constant.  May go away in the next release.
8897ef7637c6799e72956db8e08192539f1b1942f6Fred Quintana     */
89d3ad696b1daaa6c92d8fa268c81ce220ed1d9ffcJoe Onorato    @Deprecated
9097ef7637c6799e72956db8e08192539f1b1942f6Fred Quintana    public static final int LOG_SYNC_DETAILS = 2743;
9197ef7637c6799e72956db8e08192539f1b1942f6Fred Quintana
9221bb0deb36af32339521038cdbd827f74468df4aFred Quintana    private final Context mContext;
9321bb0deb36af32339521038cdbd827f74468df4aFred Quintana    private final AtomicInteger mNumSyncStarts;
9421bb0deb36af32339521038cdbd827f74468df4aFred Quintana    private final ISyncAdapterImpl mISyncAdapterImpl;
9521bb0deb36af32339521038cdbd827f74468df4aFred Quintana
963cff76aaa893049d02467a231d477e86a0f80daaFred Quintana    // all accesses to this member variable must be synchronized on mSyncThreadLock
970c4d04ac2e8aa62560d8d767fa1c87e5361b0b08Fred Quintana    private final HashMap<Account, SyncThread> mSyncThreads = new HashMap<Account, SyncThread>();
983cff76aaa893049d02467a231d477e86a0f80daaFred Quintana    private final Object mSyncThreadLock = new Object();
9921bb0deb36af32339521038cdbd827f74468df4aFred Quintana
1004a6679b97e0285c5b65ec5c0d9080ff90d3e9e81Fred Quintana    private final boolean mAutoInitialize;
1010c4d04ac2e8aa62560d8d767fa1c87e5361b0b08Fred Quintana    private boolean mAllowParallelSyncs;
10221bb0deb36af32339521038cdbd827f74468df4aFred Quintana
10321bb0deb36af32339521038cdbd827f74468df4aFred Quintana    /**
10421bb0deb36af32339521038cdbd827f74468df4aFred Quintana     * Creates an {@link AbstractThreadedSyncAdapter}.
1054a6679b97e0285c5b65ec5c0d9080ff90d3e9e81Fred Quintana     * @param context the {@link android.content.Context} that this is running within.
1064a6679b97e0285c5b65ec5c0d9080ff90d3e9e81Fred Quintana     * @param autoInitialize if true then sync requests that have
1074a6679b97e0285c5b65ec5c0d9080ff90d3e9e81Fred Quintana     * {@link ContentResolver#SYNC_EXTRAS_INITIALIZE} set will be internally handled by
1084a6679b97e0285c5b65ec5c0d9080ff90d3e9e81Fred Quintana     * {@link AbstractThreadedSyncAdapter} by calling
1094a6679b97e0285c5b65ec5c0d9080ff90d3e9e81Fred Quintana     * {@link ContentResolver#setIsSyncable(android.accounts.Account, String, int)} with 1 if it
1104a6679b97e0285c5b65ec5c0d9080ff90d3e9e81Fred Quintana     * is currently set to <0.
11121bb0deb36af32339521038cdbd827f74468df4aFred Quintana     */
1124a6679b97e0285c5b65ec5c0d9080ff90d3e9e81Fred Quintana    public AbstractThreadedSyncAdapter(Context context, boolean autoInitialize) {
1130c4d04ac2e8aa62560d8d767fa1c87e5361b0b08Fred Quintana        this(context, autoInitialize, false /* allowParallelSyncs */);
1140c4d04ac2e8aa62560d8d767fa1c87e5361b0b08Fred Quintana    }
1150c4d04ac2e8aa62560d8d767fa1c87e5361b0b08Fred Quintana
1160c4d04ac2e8aa62560d8d767fa1c87e5361b0b08Fred Quintana    /**
1170c4d04ac2e8aa62560d8d767fa1c87e5361b0b08Fred Quintana     * Creates an {@link AbstractThreadedSyncAdapter}.
1180c4d04ac2e8aa62560d8d767fa1c87e5361b0b08Fred Quintana     * @param context the {@link android.content.Context} that this is running within.
1190c4d04ac2e8aa62560d8d767fa1c87e5361b0b08Fred Quintana     * @param autoInitialize if true then sync requests that have
1200c4d04ac2e8aa62560d8d767fa1c87e5361b0b08Fred Quintana     * {@link ContentResolver#SYNC_EXTRAS_INITIALIZE} set will be internally handled by
1210c4d04ac2e8aa62560d8d767fa1c87e5361b0b08Fred Quintana     * {@link AbstractThreadedSyncAdapter} by calling
1220c4d04ac2e8aa62560d8d767fa1c87e5361b0b08Fred Quintana     * {@link ContentResolver#setIsSyncable(android.accounts.Account, String, int)} with 1 if it
1230c4d04ac2e8aa62560d8d767fa1c87e5361b0b08Fred Quintana     * is currently set to <0.
1240c4d04ac2e8aa62560d8d767fa1c87e5361b0b08Fred Quintana     * @param allowParallelSyncs if true then allow syncs for different accounts to run
1250c4d04ac2e8aa62560d8d767fa1c87e5361b0b08Fred Quintana     * at the same time, each in their own thread. This must be consistent with the setting
1260c4d04ac2e8aa62560d8d767fa1c87e5361b0b08Fred Quintana     * in the SyncAdapter's configuration file.
1270c4d04ac2e8aa62560d8d767fa1c87e5361b0b08Fred Quintana     */
1280c4d04ac2e8aa62560d8d767fa1c87e5361b0b08Fred Quintana    public AbstractThreadedSyncAdapter(Context context,
1290c4d04ac2e8aa62560d8d767fa1c87e5361b0b08Fred Quintana            boolean autoInitialize, boolean allowParallelSyncs) {
13021bb0deb36af32339521038cdbd827f74468df4aFred Quintana        mContext = context;
13121bb0deb36af32339521038cdbd827f74468df4aFred Quintana        mISyncAdapterImpl = new ISyncAdapterImpl();
13221bb0deb36af32339521038cdbd827f74468df4aFred Quintana        mNumSyncStarts = new AtomicInteger(0);
1334a6679b97e0285c5b65ec5c0d9080ff90d3e9e81Fred Quintana        mAutoInitialize = autoInitialize;
1340c4d04ac2e8aa62560d8d767fa1c87e5361b0b08Fred Quintana        mAllowParallelSyncs = allowParallelSyncs;
13521bb0deb36af32339521038cdbd827f74468df4aFred Quintana    }
13621bb0deb36af32339521038cdbd827f74468df4aFred Quintana
137c298a8518a8fd73a303132c7db241f10eb46c5b6Fred Quintana    public Context getContext() {
138c298a8518a8fd73a303132c7db241f10eb46c5b6Fred Quintana        return mContext;
139c298a8518a8fd73a303132c7db241f10eb46c5b6Fred Quintana    }
140c298a8518a8fd73a303132c7db241f10eb46c5b6Fred Quintana
1410c4d04ac2e8aa62560d8d767fa1c87e5361b0b08Fred Quintana    private Account toSyncKey(Account account) {
1420c4d04ac2e8aa62560d8d767fa1c87e5361b0b08Fred Quintana        if (mAllowParallelSyncs) {
1430c4d04ac2e8aa62560d8d767fa1c87e5361b0b08Fred Quintana            return account;
1440c4d04ac2e8aa62560d8d767fa1c87e5361b0b08Fred Quintana        } else {
1450c4d04ac2e8aa62560d8d767fa1c87e5361b0b08Fred Quintana            return null;
1460c4d04ac2e8aa62560d8d767fa1c87e5361b0b08Fred Quintana        }
1470c4d04ac2e8aa62560d8d767fa1c87e5361b0b08Fred Quintana    }
1480c4d04ac2e8aa62560d8d767fa1c87e5361b0b08Fred Quintana
149f038004f4a5e4fab18df9c87573ba1e82790c30fFred Quintana    private class ISyncAdapterImpl extends ISyncAdapter.Stub {
150fa77418134c6f1f80af225a78819f069e9c974fbMatthew Williams        @Override
15121bb0deb36af32339521038cdbd827f74468df4aFred Quintana        public void startSync(ISyncContext syncContext, String authority, Account account,
15221bb0deb36af32339521038cdbd827f74468df4aFred Quintana                Bundle extras) {
15321bb0deb36af32339521038cdbd827f74468df4aFred Quintana            final SyncContext syncContextClient = new SyncContext(syncContext);
15421bb0deb36af32339521038cdbd827f74468df4aFred Quintana
15521bb0deb36af32339521038cdbd827f74468df4aFred Quintana            boolean alreadyInProgress;
1560c4d04ac2e8aa62560d8d767fa1c87e5361b0b08Fred Quintana            // synchronize to make sure that mSyncThreads doesn't change between when we
15721bb0deb36af32339521038cdbd827f74468df4aFred Quintana            // check it and when we use it
1580c4d04ac2e8aa62560d8d767fa1c87e5361b0b08Fred Quintana            final Account threadsKey = toSyncKey(account);
1593cff76aaa893049d02467a231d477e86a0f80daaFred Quintana            synchronized (mSyncThreadLock) {
1600c4d04ac2e8aa62560d8d767fa1c87e5361b0b08Fred Quintana                if (!mSyncThreads.containsKey(threadsKey)) {
1614a6679b97e0285c5b65ec5c0d9080ff90d3e9e81Fred Quintana                    if (mAutoInitialize
1624a6679b97e0285c5b65ec5c0d9080ff90d3e9e81Fred Quintana                            && extras != null
1634a6679b97e0285c5b65ec5c0d9080ff90d3e9e81Fred Quintana                            && extras.getBoolean(ContentResolver.SYNC_EXTRAS_INITIALIZE, false)) {
1648014cbffad1e74814ddd0a98ee5954d8bf3a6c03Daniel Karlberg                        try {
1658014cbffad1e74814ddd0a98ee5954d8bf3a6c03Daniel Karlberg                            if (ContentResolver.getIsSyncable(account, authority) < 0) {
1668014cbffad1e74814ddd0a98ee5954d8bf3a6c03Daniel Karlberg                                ContentResolver.setIsSyncable(account, authority, 1);
1678014cbffad1e74814ddd0a98ee5954d8bf3a6c03Daniel Karlberg                            }
1688014cbffad1e74814ddd0a98ee5954d8bf3a6c03Daniel Karlberg                        } finally {
1698014cbffad1e74814ddd0a98ee5954d8bf3a6c03Daniel Karlberg                            syncContextClient.onFinished(new SyncResult());
1704a6679b97e0285c5b65ec5c0d9080ff90d3e9e81Fred Quintana                        }
1714a6679b97e0285c5b65ec5c0d9080ff90d3e9e81Fred Quintana                        return;
1724a6679b97e0285c5b65ec5c0d9080ff90d3e9e81Fred Quintana                    }
1730c4d04ac2e8aa62560d8d767fa1c87e5361b0b08Fred Quintana                    SyncThread syncThread = new SyncThread(
17421bb0deb36af32339521038cdbd827f74468df4aFred Quintana                            "SyncAdapterThread-" + mNumSyncStarts.incrementAndGet(),
17521bb0deb36af32339521038cdbd827f74468df4aFred Quintana                            syncContextClient, authority, account, extras);
1760c4d04ac2e8aa62560d8d767fa1c87e5361b0b08Fred Quintana                    mSyncThreads.put(threadsKey, syncThread);
1770c4d04ac2e8aa62560d8d767fa1c87e5361b0b08Fred Quintana                    syncThread.start();
17821bb0deb36af32339521038cdbd827f74468df4aFred Quintana                    alreadyInProgress = false;
17921bb0deb36af32339521038cdbd827f74468df4aFred Quintana                } else {
18021bb0deb36af32339521038cdbd827f74468df4aFred Quintana                    alreadyInProgress = true;
18121bb0deb36af32339521038cdbd827f74468df4aFred Quintana                }
18221bb0deb36af32339521038cdbd827f74468df4aFred Quintana            }
18321bb0deb36af32339521038cdbd827f74468df4aFred Quintana
18421bb0deb36af32339521038cdbd827f74468df4aFred Quintana            // do this outside since we don't want to call back into the syncContext while
18521bb0deb36af32339521038cdbd827f74468df4aFred Quintana            // holding the synchronization lock
18621bb0deb36af32339521038cdbd827f74468df4aFred Quintana            if (alreadyInProgress) {
18721bb0deb36af32339521038cdbd827f74468df4aFred Quintana                syncContextClient.onFinished(SyncResult.ALREADY_IN_PROGRESS);
18821bb0deb36af32339521038cdbd827f74468df4aFred Quintana            }
18921bb0deb36af32339521038cdbd827f74468df4aFred Quintana        }
19021bb0deb36af32339521038cdbd827f74468df4aFred Quintana
191fa77418134c6f1f80af225a78819f069e9c974fbMatthew Williams        @Override
19221bb0deb36af32339521038cdbd827f74468df4aFred Quintana        public void cancelSync(ISyncContext syncContext) {
1930c4d04ac2e8aa62560d8d767fa1c87e5361b0b08Fred Quintana            // synchronize to make sure that mSyncThreads doesn't change between when we
19421bb0deb36af32339521038cdbd827f74468df4aFred Quintana            // check it and when we use it
1950c4d04ac2e8aa62560d8d767fa1c87e5361b0b08Fred Quintana            SyncThread info = null;
1963cff76aaa893049d02467a231d477e86a0f80daaFred Quintana            synchronized (mSyncThreadLock) {
1970c4d04ac2e8aa62560d8d767fa1c87e5361b0b08Fred Quintana                for (SyncThread current : mSyncThreads.values()) {
1980c4d04ac2e8aa62560d8d767fa1c87e5361b0b08Fred Quintana                    if (current.mSyncContext.getSyncContextBinder() == syncContext.asBinder()) {
1990c4d04ac2e8aa62560d8d767fa1c87e5361b0b08Fred Quintana                        info = current;
2000c4d04ac2e8aa62560d8d767fa1c87e5361b0b08Fred Quintana                        break;
2010c4d04ac2e8aa62560d8d767fa1c87e5361b0b08Fred Quintana                    }
2020c4d04ac2e8aa62560d8d767fa1c87e5361b0b08Fred Quintana                }
203d5e4fdc8a4743abc0d9fe3cb952a78f9ad078c6bFred Quintana            }
2040c4d04ac2e8aa62560d8d767fa1c87e5361b0b08Fred Quintana            if (info != null) {
2050c4d04ac2e8aa62560d8d767fa1c87e5361b0b08Fred Quintana                if (mAllowParallelSyncs) {
2060c4d04ac2e8aa62560d8d767fa1c87e5361b0b08Fred Quintana                    onSyncCanceled(info);
2070c4d04ac2e8aa62560d8d767fa1c87e5361b0b08Fred Quintana                } else {
2080c4d04ac2e8aa62560d8d767fa1c87e5361b0b08Fred Quintana                    onSyncCanceled();
2090c4d04ac2e8aa62560d8d767fa1c87e5361b0b08Fred Quintana                }
21021bb0deb36af32339521038cdbd827f74468df4aFred Quintana            }
21121bb0deb36af32339521038cdbd827f74468df4aFred Quintana        }
212e7424ffdafb0c18f753f383ebfb121ea5ebf582bFred Quintana
213e7424ffdafb0c18f753f383ebfb121ea5ebf582bFred Quintana        public void initialize(Account account, String authority) throws RemoteException {
214e7424ffdafb0c18f753f383ebfb121ea5ebf582bFred Quintana            Bundle extras = new Bundle();
215e7424ffdafb0c18f753f383ebfb121ea5ebf582bFred Quintana            extras.putBoolean(ContentResolver.SYNC_EXTRAS_INITIALIZE, true);
216e7424ffdafb0c18f753f383ebfb121ea5ebf582bFred Quintana            startSync(null, authority, account, extras);
217e7424ffdafb0c18f753f383ebfb121ea5ebf582bFred Quintana        }
21821bb0deb36af32339521038cdbd827f74468df4aFred Quintana    }
21921bb0deb36af32339521038cdbd827f74468df4aFred Quintana
22021bb0deb36af32339521038cdbd827f74468df4aFred Quintana    /**
221f038004f4a5e4fab18df9c87573ba1e82790c30fFred Quintana     * The thread that invokes {@link AbstractThreadedSyncAdapter#onPerformSync}. It also acquires
222f038004f4a5e4fab18df9c87573ba1e82790c30fFred Quintana     * the provider for this sync before calling onPerformSync and releases it afterwards. Cancel
223f038004f4a5e4fab18df9c87573ba1e82790c30fFred Quintana     * this thread in order to cancel the sync.
22421bb0deb36af32339521038cdbd827f74468df4aFred Quintana     */
22521bb0deb36af32339521038cdbd827f74468df4aFred Quintana    private class SyncThread extends Thread {
22621bb0deb36af32339521038cdbd827f74468df4aFred Quintana        private final SyncContext mSyncContext;
22721bb0deb36af32339521038cdbd827f74468df4aFred Quintana        private final String mAuthority;
22821bb0deb36af32339521038cdbd827f74468df4aFred Quintana        private final Account mAccount;
22921bb0deb36af32339521038cdbd827f74468df4aFred Quintana        private final Bundle mExtras;
2300c4d04ac2e8aa62560d8d767fa1c87e5361b0b08Fred Quintana        private final Account mThreadsKey;
23121bb0deb36af32339521038cdbd827f74468df4aFred Quintana
23221bb0deb36af32339521038cdbd827f74468df4aFred Quintana        private SyncThread(String name, SyncContext syncContext, String authority,
23321bb0deb36af32339521038cdbd827f74468df4aFred Quintana                Account account, Bundle extras) {
23421bb0deb36af32339521038cdbd827f74468df4aFred Quintana            super(name);
23521bb0deb36af32339521038cdbd827f74468df4aFred Quintana            mSyncContext = syncContext;
23621bb0deb36af32339521038cdbd827f74468df4aFred Quintana            mAuthority = authority;
23721bb0deb36af32339521038cdbd827f74468df4aFred Quintana            mAccount = account;
23821bb0deb36af32339521038cdbd827f74468df4aFred Quintana            mExtras = extras;
2390c4d04ac2e8aa62560d8d767fa1c87e5361b0b08Fred Quintana            mThreadsKey = toSyncKey(account);
24021bb0deb36af32339521038cdbd827f74468df4aFred Quintana        }
24121bb0deb36af32339521038cdbd827f74468df4aFred Quintana
24209b45a3ad96379b4181d32f8391f63e9c57dc316Andy Stadler        @Override
24321bb0deb36af32339521038cdbd827f74468df4aFred Quintana        public void run() {
24475d797c2e78d53f49d05b518adb14fd57e0c785cFred Quintana            Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
24575d797c2e78d53f49d05b518adb14fd57e0c785cFred Quintana
24609b45a3ad96379b4181d32f8391f63e9c57dc316Andy Stadler            // Trace this sync instance.  Note, conceptually this should be in
24709b45a3ad96379b4181d32f8391f63e9c57dc316Andy Stadler            // SyncStorageEngine.insertStartSyncEvent(), but the trace functions require unique
24809b45a3ad96379b4181d32f8391f63e9c57dc316Andy Stadler            // threads in order to track overlapping operations, so we'll do it here for now.
24909b45a3ad96379b4181d32f8391f63e9c57dc316Andy Stadler            Trace.traceBegin(Trace.TRACE_TAG_SYNC_MANAGER, mAuthority);
25009b45a3ad96379b4181d32f8391f63e9c57dc316Andy Stadler
25121bb0deb36af32339521038cdbd827f74468df4aFred Quintana            SyncResult syncResult = new SyncResult();
25221bb0deb36af32339521038cdbd827f74468df4aFred Quintana            ContentProviderClient provider = null;
25321bb0deb36af32339521038cdbd827f74468df4aFred Quintana            try {
2549257ec05639ac1a529c81ba94cc631b1fa5f49d9Alon Albert                if (isCanceled()) {
2559257ec05639ac1a529c81ba94cc631b1fa5f49d9Alon Albert                    return;
2569257ec05639ac1a529c81ba94cc631b1fa5f49d9Alon Albert                }
25721bb0deb36af32339521038cdbd827f74468df4aFred Quintana                provider = mContext.getContentResolver().acquireContentProviderClient(mAuthority);
25821bb0deb36af32339521038cdbd827f74468df4aFred Quintana                if (provider != null) {
259f038004f4a5e4fab18df9c87573ba1e82790c30fFred Quintana                    AbstractThreadedSyncAdapter.this.onPerformSync(mAccount, mExtras,
26021bb0deb36af32339521038cdbd827f74468df4aFred Quintana                            mAuthority, provider, syncResult);
26121bb0deb36af32339521038cdbd827f74468df4aFred Quintana                } else {
262f038004f4a5e4fab18df9c87573ba1e82790c30fFred Quintana                    syncResult.databaseError = true;
26321bb0deb36af32339521038cdbd827f74468df4aFred Quintana                }
26421bb0deb36af32339521038cdbd827f74468df4aFred Quintana            } finally {
26509b45a3ad96379b4181d32f8391f63e9c57dc316Andy Stadler                Trace.traceEnd(Trace.TRACE_TAG_SYNC_MANAGER);
26609b45a3ad96379b4181d32f8391f63e9c57dc316Andy Stadler
26721bb0deb36af32339521038cdbd827f74468df4aFred Quintana                if (provider != null) {
26821bb0deb36af32339521038cdbd827f74468df4aFred Quintana                    provider.release();
26921bb0deb36af32339521038cdbd827f74468df4aFred Quintana                }
27021bb0deb36af32339521038cdbd827f74468df4aFred Quintana                if (!isCanceled()) {
27121bb0deb36af32339521038cdbd827f74468df4aFred Quintana                    mSyncContext.onFinished(syncResult);
27221bb0deb36af32339521038cdbd827f74468df4aFred Quintana                }
27321bb0deb36af32339521038cdbd827f74468df4aFred Quintana                // synchronize so that the assignment will be seen by other threads
2740c4d04ac2e8aa62560d8d767fa1c87e5361b0b08Fred Quintana                // that also synchronize accesses to mSyncThreads
2753cff76aaa893049d02467a231d477e86a0f80daaFred Quintana                synchronized (mSyncThreadLock) {
2760c4d04ac2e8aa62560d8d767fa1c87e5361b0b08Fred Quintana                    mSyncThreads.remove(mThreadsKey);
27721bb0deb36af32339521038cdbd827f74468df4aFred Quintana                }
27821bb0deb36af32339521038cdbd827f74468df4aFred Quintana            }
27921bb0deb36af32339521038cdbd827f74468df4aFred Quintana        }
28021bb0deb36af32339521038cdbd827f74468df4aFred Quintana
28121bb0deb36af32339521038cdbd827f74468df4aFred Quintana        private boolean isCanceled() {
28221bb0deb36af32339521038cdbd827f74468df4aFred Quintana            return Thread.currentThread().isInterrupted();
28321bb0deb36af32339521038cdbd827f74468df4aFred Quintana        }
28421bb0deb36af32339521038cdbd827f74468df4aFred Quintana    }
28521bb0deb36af32339521038cdbd827f74468df4aFred Quintana
28621bb0deb36af32339521038cdbd827f74468df4aFred Quintana    /**
287f038004f4a5e4fab18df9c87573ba1e82790c30fFred Quintana     * @return a reference to the IBinder of the SyncAdapter service.
28821bb0deb36af32339521038cdbd827f74468df4aFred Quintana     */
289f038004f4a5e4fab18df9c87573ba1e82790c30fFred Quintana    public final IBinder getSyncAdapterBinder() {
290f038004f4a5e4fab18df9c87573ba1e82790c30fFred Quintana        return mISyncAdapterImpl.asBinder();
29121bb0deb36af32339521038cdbd827f74468df4aFred Quintana    }
29221bb0deb36af32339521038cdbd827f74468df4aFred Quintana
29321bb0deb36af32339521038cdbd827f74468df4aFred Quintana    /**
29421bb0deb36af32339521038cdbd827f74468df4aFred Quintana     * Perform a sync for this account. SyncAdapter-specific parameters may
29521bb0deb36af32339521038cdbd827f74468df4aFred Quintana     * be specified in extras, which is guaranteed to not be null. Invocations
29621bb0deb36af32339521038cdbd827f74468df4aFred Quintana     * of this method are guaranteed to be serialized.
29721bb0deb36af32339521038cdbd827f74468df4aFred Quintana     *
29821bb0deb36af32339521038cdbd827f74468df4aFred Quintana     * @param account the account that should be synced
29921bb0deb36af32339521038cdbd827f74468df4aFred Quintana     * @param extras SyncAdapter-specific parameters
30021bb0deb36af32339521038cdbd827f74468df4aFred Quintana     * @param authority the authority of this sync request
30121bb0deb36af32339521038cdbd827f74468df4aFred Quintana     * @param provider a ContentProviderClient that points to the ContentProvider for this
30221bb0deb36af32339521038cdbd827f74468df4aFred Quintana     *   authority
30321bb0deb36af32339521038cdbd827f74468df4aFred Quintana     * @param syncResult SyncAdapter-specific parameters
30421bb0deb36af32339521038cdbd827f74468df4aFred Quintana     */
305f038004f4a5e4fab18df9c87573ba1e82790c30fFred Quintana    public abstract void onPerformSync(Account account, Bundle extras,
30621bb0deb36af32339521038cdbd827f74468df4aFred Quintana            String authority, ContentProviderClient provider, SyncResult syncResult);
307274dc9d35fdf5d0464f74071a9a8f14e497d4d5fFred Quintana
308274dc9d35fdf5d0464f74071a9a8f14e497d4d5fFred Quintana    /**
309274dc9d35fdf5d0464f74071a9a8f14e497d4d5fFred Quintana     * Indicates that a sync operation has been canceled. This will be invoked on a separate
310274dc9d35fdf5d0464f74071a9a8f14e497d4d5fFred Quintana     * thread than the sync thread and so you must consider the multi-threaded implications
311274dc9d35fdf5d0464f74071a9a8f14e497d4d5fFred Quintana     * of the work that you do in this method.
3120c4d04ac2e8aa62560d8d767fa1c87e5361b0b08Fred Quintana     * <p>
3130c4d04ac2e8aa62560d8d767fa1c87e5361b0b08Fred Quintana     * This will only be invoked when the SyncAdapter indicates that it doesn't support
3140c4d04ac2e8aa62560d8d767fa1c87e5361b0b08Fred Quintana     * parallel syncs.
315274dc9d35fdf5d0464f74071a9a8f14e497d4d5fFred Quintana     */
316d5e4fdc8a4743abc0d9fe3cb952a78f9ad078c6bFred Quintana    public void onSyncCanceled() {
317d5e4fdc8a4743abc0d9fe3cb952a78f9ad078c6bFred Quintana        final SyncThread syncThread;
318d5e4fdc8a4743abc0d9fe3cb952a78f9ad078c6bFred Quintana        synchronized (mSyncThreadLock) {
3190c4d04ac2e8aa62560d8d767fa1c87e5361b0b08Fred Quintana            syncThread = mSyncThreads.get(null);
320d5e4fdc8a4743abc0d9fe3cb952a78f9ad078c6bFred Quintana        }
321d5e4fdc8a4743abc0d9fe3cb952a78f9ad078c6bFred Quintana        if (syncThread != null) {
322d5e4fdc8a4743abc0d9fe3cb952a78f9ad078c6bFred Quintana            syncThread.interrupt();
323d5e4fdc8a4743abc0d9fe3cb952a78f9ad078c6bFred Quintana        }
324274dc9d35fdf5d0464f74071a9a8f14e497d4d5fFred Quintana    }
3250c4d04ac2e8aa62560d8d767fa1c87e5361b0b08Fred Quintana
3260c4d04ac2e8aa62560d8d767fa1c87e5361b0b08Fred Quintana    /**
3270c4d04ac2e8aa62560d8d767fa1c87e5361b0b08Fred Quintana     * Indicates that a sync operation has been canceled. This will be invoked on a separate
3280c4d04ac2e8aa62560d8d767fa1c87e5361b0b08Fred Quintana     * thread than the sync thread and so you must consider the multi-threaded implications
3290c4d04ac2e8aa62560d8d767fa1c87e5361b0b08Fred Quintana     * of the work that you do in this method.
3300c4d04ac2e8aa62560d8d767fa1c87e5361b0b08Fred Quintana     * <p>
3310c4d04ac2e8aa62560d8d767fa1c87e5361b0b08Fred Quintana     * This will only be invoked when the SyncAdapter indicates that it does support
3320c4d04ac2e8aa62560d8d767fa1c87e5361b0b08Fred Quintana     * parallel syncs.
3330c4d04ac2e8aa62560d8d767fa1c87e5361b0b08Fred Quintana     * @param thread the Thread of the sync that is to be canceled.
3340c4d04ac2e8aa62560d8d767fa1c87e5361b0b08Fred Quintana     */
3350c4d04ac2e8aa62560d8d767fa1c87e5361b0b08Fred Quintana    public void onSyncCanceled(Thread thread) {
3360c4d04ac2e8aa62560d8d767fa1c87e5361b0b08Fred Quintana        thread.interrupt();
3370c4d04ac2e8aa62560d8d767fa1c87e5361b0b08Fred Quintana    }
338f038004f4a5e4fab18df9c87573ba1e82790c30fFred Quintana}
339