AbstractThreadedSyncAdapter.java revision 4a6679b97e0285c5b65ec5c0d9080ff90d3e9e81
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;
2121bb0deb36af32339521038cdbd827f74468df4aFred Quintanaimport android.os.Process;
2221bb0deb36af32339521038cdbd827f74468df4aFred Quintana
2321bb0deb36af32339521038cdbd827f74468df4aFred Quintanaimport java.util.concurrent.atomic.AtomicInteger;
2421bb0deb36af32339521038cdbd827f74468df4aFred Quintana
2521bb0deb36af32339521038cdbd827f74468df4aFred Quintana/**
2621bb0deb36af32339521038cdbd827f74468df4aFred Quintana * An abstract implementation of a SyncAdapter that spawns a thread to invoke a sync operation.
2721bb0deb36af32339521038cdbd827f74468df4aFred Quintana * If a sync operation is already in progress when a startSync() request is received then an error
2821bb0deb36af32339521038cdbd827f74468df4aFred Quintana * will be returned to the new request and the existing request will be allowed to continue.
2921bb0deb36af32339521038cdbd827f74468df4aFred Quintana * When a startSync() is received and there is no sync operation in progress then a thread
3021bb0deb36af32339521038cdbd827f74468df4aFred Quintana * will be started to run the operation and {@link #performSync} will be invoked on that thread.
3121bb0deb36af32339521038cdbd827f74468df4aFred Quintana * If a cancelSync() is received that matches an existing sync operation then the thread
3221bb0deb36af32339521038cdbd827f74468df4aFred Quintana * that is running that sync operation will be interrupted, which will indicate to the thread
3321bb0deb36af32339521038cdbd827f74468df4aFred Quintana * that the sync has been canceled.
3421bb0deb36af32339521038cdbd827f74468df4aFred Quintana *
3521bb0deb36af32339521038cdbd827f74468df4aFred Quintana * @hide
3621bb0deb36af32339521038cdbd827f74468df4aFred Quintana */
3721bb0deb36af32339521038cdbd827f74468df4aFred Quintanapublic abstract class AbstractThreadedSyncAdapter {
3821bb0deb36af32339521038cdbd827f74468df4aFred Quintana    private final Context mContext;
3921bb0deb36af32339521038cdbd827f74468df4aFred Quintana    private final AtomicInteger mNumSyncStarts;
4021bb0deb36af32339521038cdbd827f74468df4aFred Quintana    private final ISyncAdapterImpl mISyncAdapterImpl;
4121bb0deb36af32339521038cdbd827f74468df4aFred Quintana
4221bb0deb36af32339521038cdbd827f74468df4aFred Quintana    // all accesses to this member variable must be synchronized on "this"
4321bb0deb36af32339521038cdbd827f74468df4aFred Quintana    private SyncThread mSyncThread;
4421bb0deb36af32339521038cdbd827f74468df4aFred Quintana
4521bb0deb36af32339521038cdbd827f74468df4aFred Quintana    /** Kernel event log tag.  Also listed in data/etc/event-log-tags. */
4621bb0deb36af32339521038cdbd827f74468df4aFred Quintana    public static final int LOG_SYNC_DETAILS = 2743;
474a6679b97e0285c5b65ec5c0d9080ff90d3e9e81Fred Quintana    private final boolean mAutoInitialize;
4821bb0deb36af32339521038cdbd827f74468df4aFred Quintana
4921bb0deb36af32339521038cdbd827f74468df4aFred Quintana    /**
5021bb0deb36af32339521038cdbd827f74468df4aFred Quintana     * Creates an {@link AbstractThreadedSyncAdapter}.
514a6679b97e0285c5b65ec5c0d9080ff90d3e9e81Fred Quintana     * @param context the {@link android.content.Context} that this is running within.
524a6679b97e0285c5b65ec5c0d9080ff90d3e9e81Fred Quintana     * @param autoInitialize if true then sync requests that have
534a6679b97e0285c5b65ec5c0d9080ff90d3e9e81Fred Quintana     * {@link ContentResolver#SYNC_EXTRAS_INITIALIZE} set will be internally handled by
544a6679b97e0285c5b65ec5c0d9080ff90d3e9e81Fred Quintana     * {@link AbstractThreadedSyncAdapter} by calling
554a6679b97e0285c5b65ec5c0d9080ff90d3e9e81Fred Quintana     * {@link ContentResolver#setIsSyncable(android.accounts.Account, String, int)} with 1 if it
564a6679b97e0285c5b65ec5c0d9080ff90d3e9e81Fred Quintana     * is currently set to <0.
5721bb0deb36af32339521038cdbd827f74468df4aFred Quintana     */
584a6679b97e0285c5b65ec5c0d9080ff90d3e9e81Fred Quintana    public AbstractThreadedSyncAdapter(Context context, boolean autoInitialize) {
5921bb0deb36af32339521038cdbd827f74468df4aFred Quintana        mContext = context;
6021bb0deb36af32339521038cdbd827f74468df4aFred Quintana        mISyncAdapterImpl = new ISyncAdapterImpl();
6121bb0deb36af32339521038cdbd827f74468df4aFred Quintana        mNumSyncStarts = new AtomicInteger(0);
6221bb0deb36af32339521038cdbd827f74468df4aFred Quintana        mSyncThread = null;
634a6679b97e0285c5b65ec5c0d9080ff90d3e9e81Fred Quintana        mAutoInitialize = autoInitialize;
6421bb0deb36af32339521038cdbd827f74468df4aFred Quintana    }
6521bb0deb36af32339521038cdbd827f74468df4aFred Quintana
6621bb0deb36af32339521038cdbd827f74468df4aFred Quintana    class ISyncAdapterImpl extends ISyncAdapter.Stub {
6721bb0deb36af32339521038cdbd827f74468df4aFred Quintana        public void startSync(ISyncContext syncContext, String authority, Account account,
6821bb0deb36af32339521038cdbd827f74468df4aFred Quintana                Bundle extras) {
6921bb0deb36af32339521038cdbd827f74468df4aFred Quintana            final SyncContext syncContextClient = new SyncContext(syncContext);
7021bb0deb36af32339521038cdbd827f74468df4aFred Quintana
7121bb0deb36af32339521038cdbd827f74468df4aFred Quintana            boolean alreadyInProgress;
7221bb0deb36af32339521038cdbd827f74468df4aFred Quintana            // synchronize to make sure that mSyncThread doesn't change between when we
7321bb0deb36af32339521038cdbd827f74468df4aFred Quintana            // check it and when we use it
7421bb0deb36af32339521038cdbd827f74468df4aFred Quintana            synchronized (this) {
7521bb0deb36af32339521038cdbd827f74468df4aFred Quintana                if (mSyncThread == null) {
764a6679b97e0285c5b65ec5c0d9080ff90d3e9e81Fred Quintana                    if (mAutoInitialize
774a6679b97e0285c5b65ec5c0d9080ff90d3e9e81Fred Quintana                            && extras != null
784a6679b97e0285c5b65ec5c0d9080ff90d3e9e81Fred Quintana                            && extras.getBoolean(ContentResolver.SYNC_EXTRAS_INITIALIZE, false)) {
794a6679b97e0285c5b65ec5c0d9080ff90d3e9e81Fred Quintana                        if (ContentResolver.getIsSyncable(account, authority) < 0) {
804a6679b97e0285c5b65ec5c0d9080ff90d3e9e81Fred Quintana                            ContentResolver.setIsSyncable(account, authority, 1);
814a6679b97e0285c5b65ec5c0d9080ff90d3e9e81Fred Quintana                        }
824a6679b97e0285c5b65ec5c0d9080ff90d3e9e81Fred Quintana                        syncContextClient.onFinished(new SyncResult());
834a6679b97e0285c5b65ec5c0d9080ff90d3e9e81Fred Quintana                        return;
844a6679b97e0285c5b65ec5c0d9080ff90d3e9e81Fred Quintana                    }
8521bb0deb36af32339521038cdbd827f74468df4aFred Quintana                    mSyncThread = new SyncThread(
8621bb0deb36af32339521038cdbd827f74468df4aFred Quintana                            "SyncAdapterThread-" + mNumSyncStarts.incrementAndGet(),
8721bb0deb36af32339521038cdbd827f74468df4aFred Quintana                            syncContextClient, authority, account, extras);
8821bb0deb36af32339521038cdbd827f74468df4aFred Quintana                    Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
8921bb0deb36af32339521038cdbd827f74468df4aFred Quintana                    mSyncThread.start();
9021bb0deb36af32339521038cdbd827f74468df4aFred Quintana                    alreadyInProgress = false;
9121bb0deb36af32339521038cdbd827f74468df4aFred Quintana                } else {
9221bb0deb36af32339521038cdbd827f74468df4aFred Quintana                    alreadyInProgress = true;
9321bb0deb36af32339521038cdbd827f74468df4aFred Quintana                }
9421bb0deb36af32339521038cdbd827f74468df4aFred Quintana            }
9521bb0deb36af32339521038cdbd827f74468df4aFred Quintana
9621bb0deb36af32339521038cdbd827f74468df4aFred Quintana            // do this outside since we don't want to call back into the syncContext while
9721bb0deb36af32339521038cdbd827f74468df4aFred Quintana            // holding the synchronization lock
9821bb0deb36af32339521038cdbd827f74468df4aFred Quintana            if (alreadyInProgress) {
9921bb0deb36af32339521038cdbd827f74468df4aFred Quintana                syncContextClient.onFinished(SyncResult.ALREADY_IN_PROGRESS);
10021bb0deb36af32339521038cdbd827f74468df4aFred Quintana            }
10121bb0deb36af32339521038cdbd827f74468df4aFred Quintana        }
10221bb0deb36af32339521038cdbd827f74468df4aFred Quintana
10321bb0deb36af32339521038cdbd827f74468df4aFred Quintana        public void cancelSync(ISyncContext syncContext) {
10421bb0deb36af32339521038cdbd827f74468df4aFred Quintana            // synchronize to make sure that mSyncThread doesn't change between when we
10521bb0deb36af32339521038cdbd827f74468df4aFred Quintana            // check it and when we use it
10621bb0deb36af32339521038cdbd827f74468df4aFred Quintana            synchronized (this) {
10721bb0deb36af32339521038cdbd827f74468df4aFred Quintana                if (mSyncThread != null
10821bb0deb36af32339521038cdbd827f74468df4aFred Quintana                        && mSyncThread.mSyncContext.getISyncContext() == syncContext) {
10921bb0deb36af32339521038cdbd827f74468df4aFred Quintana                    mSyncThread.interrupt();
11021bb0deb36af32339521038cdbd827f74468df4aFred Quintana                }
11121bb0deb36af32339521038cdbd827f74468df4aFred Quintana            }
11221bb0deb36af32339521038cdbd827f74468df4aFred Quintana        }
11321bb0deb36af32339521038cdbd827f74468df4aFred Quintana    }
11421bb0deb36af32339521038cdbd827f74468df4aFred Quintana
11521bb0deb36af32339521038cdbd827f74468df4aFred Quintana    /**
11621bb0deb36af32339521038cdbd827f74468df4aFred Quintana     * The thread that invokes performSync(). It also acquires the provider for this sync
11721bb0deb36af32339521038cdbd827f74468df4aFred Quintana     * before calling performSync and releases it afterwards. Cancel this thread in order to
11821bb0deb36af32339521038cdbd827f74468df4aFred Quintana     * cancel the sync.
11921bb0deb36af32339521038cdbd827f74468df4aFred Quintana     */
12021bb0deb36af32339521038cdbd827f74468df4aFred Quintana    private class SyncThread extends Thread {
12121bb0deb36af32339521038cdbd827f74468df4aFred Quintana        private final SyncContext mSyncContext;
12221bb0deb36af32339521038cdbd827f74468df4aFred Quintana        private final String mAuthority;
12321bb0deb36af32339521038cdbd827f74468df4aFred Quintana        private final Account mAccount;
12421bb0deb36af32339521038cdbd827f74468df4aFred Quintana        private final Bundle mExtras;
12521bb0deb36af32339521038cdbd827f74468df4aFred Quintana
12621bb0deb36af32339521038cdbd827f74468df4aFred Quintana        private SyncThread(String name, SyncContext syncContext, String authority,
12721bb0deb36af32339521038cdbd827f74468df4aFred Quintana                Account account, Bundle extras) {
12821bb0deb36af32339521038cdbd827f74468df4aFred Quintana            super(name);
12921bb0deb36af32339521038cdbd827f74468df4aFred Quintana            mSyncContext = syncContext;
13021bb0deb36af32339521038cdbd827f74468df4aFred Quintana            mAuthority = authority;
13121bb0deb36af32339521038cdbd827f74468df4aFred Quintana            mAccount = account;
13221bb0deb36af32339521038cdbd827f74468df4aFred Quintana            mExtras = extras;
13321bb0deb36af32339521038cdbd827f74468df4aFred Quintana        }
13421bb0deb36af32339521038cdbd827f74468df4aFred Quintana
13521bb0deb36af32339521038cdbd827f74468df4aFred Quintana        public void run() {
13621bb0deb36af32339521038cdbd827f74468df4aFred Quintana            if (isCanceled()) {
13721bb0deb36af32339521038cdbd827f74468df4aFred Quintana                return;
13821bb0deb36af32339521038cdbd827f74468df4aFred Quintana            }
13921bb0deb36af32339521038cdbd827f74468df4aFred Quintana
14021bb0deb36af32339521038cdbd827f74468df4aFred Quintana            SyncResult syncResult = new SyncResult();
14121bb0deb36af32339521038cdbd827f74468df4aFred Quintana            ContentProviderClient provider = null;
14221bb0deb36af32339521038cdbd827f74468df4aFred Quintana            try {
14321bb0deb36af32339521038cdbd827f74468df4aFred Quintana                provider = mContext.getContentResolver().acquireContentProviderClient(mAuthority);
14421bb0deb36af32339521038cdbd827f74468df4aFred Quintana                if (provider != null) {
14521bb0deb36af32339521038cdbd827f74468df4aFred Quintana                    AbstractThreadedSyncAdapter.this.performSync(mAccount, mExtras,
14621bb0deb36af32339521038cdbd827f74468df4aFred Quintana                            mAuthority, provider, syncResult);
14721bb0deb36af32339521038cdbd827f74468df4aFred Quintana                } else {
14821bb0deb36af32339521038cdbd827f74468df4aFred Quintana                    // TODO(fredq) update the syncResults to indicate that we were unable to
14921bb0deb36af32339521038cdbd827f74468df4aFred Quintana                    // find the provider. maybe with a ProviderError?
15021bb0deb36af32339521038cdbd827f74468df4aFred Quintana                }
15121bb0deb36af32339521038cdbd827f74468df4aFred Quintana            } finally {
15221bb0deb36af32339521038cdbd827f74468df4aFred Quintana                if (provider != null) {
15321bb0deb36af32339521038cdbd827f74468df4aFred Quintana                    provider.release();
15421bb0deb36af32339521038cdbd827f74468df4aFred Quintana                }
15521bb0deb36af32339521038cdbd827f74468df4aFred Quintana                if (!isCanceled()) {
15621bb0deb36af32339521038cdbd827f74468df4aFred Quintana                    mSyncContext.onFinished(syncResult);
15721bb0deb36af32339521038cdbd827f74468df4aFred Quintana                }
15821bb0deb36af32339521038cdbd827f74468df4aFred Quintana                // synchronize so that the assignment will be seen by other threads
15921bb0deb36af32339521038cdbd827f74468df4aFred Quintana                // that also synchronize accesses to mSyncThread
16021bb0deb36af32339521038cdbd827f74468df4aFred Quintana                synchronized (this) {
16121bb0deb36af32339521038cdbd827f74468df4aFred Quintana                    mSyncThread = null;
16221bb0deb36af32339521038cdbd827f74468df4aFred Quintana                }
16321bb0deb36af32339521038cdbd827f74468df4aFred Quintana            }
16421bb0deb36af32339521038cdbd827f74468df4aFred Quintana        }
16521bb0deb36af32339521038cdbd827f74468df4aFred Quintana
16621bb0deb36af32339521038cdbd827f74468df4aFred Quintana        private boolean isCanceled() {
16721bb0deb36af32339521038cdbd827f74468df4aFred Quintana            return Thread.currentThread().isInterrupted();
16821bb0deb36af32339521038cdbd827f74468df4aFred Quintana        }
16921bb0deb36af32339521038cdbd827f74468df4aFred Quintana    }
17021bb0deb36af32339521038cdbd827f74468df4aFred Quintana
17121bb0deb36af32339521038cdbd827f74468df4aFred Quintana    /**
17221bb0deb36af32339521038cdbd827f74468df4aFred Quintana     * @return a reference to the ISyncAdapter interface into this SyncAdapter implementation.
17321bb0deb36af32339521038cdbd827f74468df4aFred Quintana     */
17421bb0deb36af32339521038cdbd827f74468df4aFred Quintana    public final ISyncAdapter getISyncAdapter() {
17521bb0deb36af32339521038cdbd827f74468df4aFred Quintana        return mISyncAdapterImpl;
17621bb0deb36af32339521038cdbd827f74468df4aFred Quintana    }
17721bb0deb36af32339521038cdbd827f74468df4aFred Quintana
17821bb0deb36af32339521038cdbd827f74468df4aFred Quintana    /**
17921bb0deb36af32339521038cdbd827f74468df4aFred Quintana     * Perform a sync for this account. SyncAdapter-specific parameters may
18021bb0deb36af32339521038cdbd827f74468df4aFred Quintana     * be specified in extras, which is guaranteed to not be null. Invocations
18121bb0deb36af32339521038cdbd827f74468df4aFred Quintana     * of this method are guaranteed to be serialized.
18221bb0deb36af32339521038cdbd827f74468df4aFred Quintana     *
18321bb0deb36af32339521038cdbd827f74468df4aFred Quintana     * @param account the account that should be synced
18421bb0deb36af32339521038cdbd827f74468df4aFred Quintana     * @param extras SyncAdapter-specific parameters
18521bb0deb36af32339521038cdbd827f74468df4aFred Quintana     * @param authority the authority of this sync request
18621bb0deb36af32339521038cdbd827f74468df4aFred Quintana     * @param provider a ContentProviderClient that points to the ContentProvider for this
18721bb0deb36af32339521038cdbd827f74468df4aFred Quintana     *   authority
18821bb0deb36af32339521038cdbd827f74468df4aFred Quintana     * @param syncResult SyncAdapter-specific parameters
18921bb0deb36af32339521038cdbd827f74468df4aFred Quintana     */
19021bb0deb36af32339521038cdbd827f74468df4aFred Quintana    public abstract void performSync(Account account, Bundle extras,
19121bb0deb36af32339521038cdbd827f74468df4aFred Quintana            String authority, ContentProviderClient provider, SyncResult syncResult);
19221bb0deb36af32339521038cdbd827f74468df4aFred Quintana}