AbstractThreadedSyncAdapter.java revision f038004f4a5e4fab18df9c87573ba1e82790c30f
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;
226fd7385f56b129b49164f606d23771a39507f772Ken Shirriffimport android.os.NetStat;
23f038004f4a5e4fab18df9c87573ba1e82790c30fFred Quintanaimport android.os.IBinder;
246fd7385f56b129b49164f606d23771a39507f772Ken Shirriffimport android.util.EventLog;
2521bb0deb36af32339521038cdbd827f74468df4aFred Quintana
2621bb0deb36af32339521038cdbd827f74468df4aFred Quintanaimport java.util.concurrent.atomic.AtomicInteger;
2721bb0deb36af32339521038cdbd827f74468df4aFred Quintana
2821bb0deb36af32339521038cdbd827f74468df4aFred Quintana/**
2921bb0deb36af32339521038cdbd827f74468df4aFred Quintana * An abstract implementation of a SyncAdapter that spawns a thread to invoke a sync operation.
3021bb0deb36af32339521038cdbd827f74468df4aFred Quintana * If a sync operation is already in progress when a startSync() request is received then an error
3121bb0deb36af32339521038cdbd827f74468df4aFred Quintana * will be returned to the new request and the existing request will be allowed to continue.
3221bb0deb36af32339521038cdbd827f74468df4aFred Quintana * When a startSync() is received and there is no sync operation in progress then a thread
33f038004f4a5e4fab18df9c87573ba1e82790c30fFred Quintana * will be started to run the operation and {@link #onPerformSync} will be invoked on that thread.
3421bb0deb36af32339521038cdbd827f74468df4aFred Quintana * If a cancelSync() is received that matches an existing sync operation then the thread
3521bb0deb36af32339521038cdbd827f74468df4aFred Quintana * that is running that sync operation will be interrupted, which will indicate to the thread
3621bb0deb36af32339521038cdbd827f74468df4aFred Quintana * that the sync has been canceled.
3721bb0deb36af32339521038cdbd827f74468df4aFred Quintana */
3821bb0deb36af32339521038cdbd827f74468df4aFred Quintanapublic abstract class AbstractThreadedSyncAdapter {
3921bb0deb36af32339521038cdbd827f74468df4aFred Quintana    private final Context mContext;
4021bb0deb36af32339521038cdbd827f74468df4aFred Quintana    private final AtomicInteger mNumSyncStarts;
4121bb0deb36af32339521038cdbd827f74468df4aFred Quintana    private final ISyncAdapterImpl mISyncAdapterImpl;
4221bb0deb36af32339521038cdbd827f74468df4aFred Quintana
433cff76aaa893049d02467a231d477e86a0f80daaFred Quintana    // all accesses to this member variable must be synchronized on mSyncThreadLock
4421bb0deb36af32339521038cdbd827f74468df4aFred Quintana    private SyncThread mSyncThread;
453cff76aaa893049d02467a231d477e86a0f80daaFred Quintana    private final Object mSyncThreadLock = new Object();
4621bb0deb36af32339521038cdbd827f74468df4aFred Quintana
4721bb0deb36af32339521038cdbd827f74468df4aFred Quintana    /** Kernel event log tag.  Also listed in data/etc/event-log-tags. */
4821bb0deb36af32339521038cdbd827f74468df4aFred Quintana    public static final int LOG_SYNC_DETAILS = 2743;
496fd7385f56b129b49164f606d23771a39507f772Ken Shirriff    private static final String TAG = "Sync";
504a6679b97e0285c5b65ec5c0d9080ff90d3e9e81Fred Quintana    private final boolean mAutoInitialize;
5121bb0deb36af32339521038cdbd827f74468df4aFred Quintana
5221bb0deb36af32339521038cdbd827f74468df4aFred Quintana    /**
5321bb0deb36af32339521038cdbd827f74468df4aFred Quintana     * Creates an {@link AbstractThreadedSyncAdapter}.
544a6679b97e0285c5b65ec5c0d9080ff90d3e9e81Fred Quintana     * @param context the {@link android.content.Context} that this is running within.
554a6679b97e0285c5b65ec5c0d9080ff90d3e9e81Fred Quintana     * @param autoInitialize if true then sync requests that have
564a6679b97e0285c5b65ec5c0d9080ff90d3e9e81Fred Quintana     * {@link ContentResolver#SYNC_EXTRAS_INITIALIZE} set will be internally handled by
574a6679b97e0285c5b65ec5c0d9080ff90d3e9e81Fred Quintana     * {@link AbstractThreadedSyncAdapter} by calling
584a6679b97e0285c5b65ec5c0d9080ff90d3e9e81Fred Quintana     * {@link ContentResolver#setIsSyncable(android.accounts.Account, String, int)} with 1 if it
594a6679b97e0285c5b65ec5c0d9080ff90d3e9e81Fred Quintana     * is currently set to <0.
6021bb0deb36af32339521038cdbd827f74468df4aFred Quintana     */
614a6679b97e0285c5b65ec5c0d9080ff90d3e9e81Fred Quintana    public AbstractThreadedSyncAdapter(Context context, boolean autoInitialize) {
6221bb0deb36af32339521038cdbd827f74468df4aFred Quintana        mContext = context;
6321bb0deb36af32339521038cdbd827f74468df4aFred Quintana        mISyncAdapterImpl = new ISyncAdapterImpl();
6421bb0deb36af32339521038cdbd827f74468df4aFred Quintana        mNumSyncStarts = new AtomicInteger(0);
6521bb0deb36af32339521038cdbd827f74468df4aFred Quintana        mSyncThread = null;
664a6679b97e0285c5b65ec5c0d9080ff90d3e9e81Fred Quintana        mAutoInitialize = autoInitialize;
6721bb0deb36af32339521038cdbd827f74468df4aFred Quintana    }
6821bb0deb36af32339521038cdbd827f74468df4aFred Quintana
69c298a8518a8fd73a303132c7db241f10eb46c5b6Fred Quintana    public Context getContext() {
70c298a8518a8fd73a303132c7db241f10eb46c5b6Fred Quintana        return mContext;
71c298a8518a8fd73a303132c7db241f10eb46c5b6Fred Quintana    }
72c298a8518a8fd73a303132c7db241f10eb46c5b6Fred Quintana
73f038004f4a5e4fab18df9c87573ba1e82790c30fFred Quintana    private class ISyncAdapterImpl extends ISyncAdapter.Stub {
7421bb0deb36af32339521038cdbd827f74468df4aFred Quintana        public void startSync(ISyncContext syncContext, String authority, Account account,
7521bb0deb36af32339521038cdbd827f74468df4aFred Quintana                Bundle extras) {
7621bb0deb36af32339521038cdbd827f74468df4aFred Quintana            final SyncContext syncContextClient = new SyncContext(syncContext);
7721bb0deb36af32339521038cdbd827f74468df4aFred Quintana
7821bb0deb36af32339521038cdbd827f74468df4aFred Quintana            boolean alreadyInProgress;
7921bb0deb36af32339521038cdbd827f74468df4aFred Quintana            // synchronize to make sure that mSyncThread doesn't change between when we
8021bb0deb36af32339521038cdbd827f74468df4aFred Quintana            // check it and when we use it
813cff76aaa893049d02467a231d477e86a0f80daaFred Quintana            synchronized (mSyncThreadLock) {
8221bb0deb36af32339521038cdbd827f74468df4aFred Quintana                if (mSyncThread == null) {
834a6679b97e0285c5b65ec5c0d9080ff90d3e9e81Fred Quintana                    if (mAutoInitialize
844a6679b97e0285c5b65ec5c0d9080ff90d3e9e81Fred Quintana                            && extras != null
854a6679b97e0285c5b65ec5c0d9080ff90d3e9e81Fred Quintana                            && extras.getBoolean(ContentResolver.SYNC_EXTRAS_INITIALIZE, false)) {
864a6679b97e0285c5b65ec5c0d9080ff90d3e9e81Fred Quintana                        if (ContentResolver.getIsSyncable(account, authority) < 0) {
874a6679b97e0285c5b65ec5c0d9080ff90d3e9e81Fred Quintana                            ContentResolver.setIsSyncable(account, authority, 1);
884a6679b97e0285c5b65ec5c0d9080ff90d3e9e81Fred Quintana                        }
894a6679b97e0285c5b65ec5c0d9080ff90d3e9e81Fred Quintana                        syncContextClient.onFinished(new SyncResult());
904a6679b97e0285c5b65ec5c0d9080ff90d3e9e81Fred Quintana                        return;
914a6679b97e0285c5b65ec5c0d9080ff90d3e9e81Fred Quintana                    }
9221bb0deb36af32339521038cdbd827f74468df4aFred Quintana                    mSyncThread = new SyncThread(
9321bb0deb36af32339521038cdbd827f74468df4aFred Quintana                            "SyncAdapterThread-" + mNumSyncStarts.incrementAndGet(),
9421bb0deb36af32339521038cdbd827f74468df4aFred Quintana                            syncContextClient, authority, account, extras);
9521bb0deb36af32339521038cdbd827f74468df4aFred Quintana                    mSyncThread.start();
9621bb0deb36af32339521038cdbd827f74468df4aFred Quintana                    alreadyInProgress = false;
9721bb0deb36af32339521038cdbd827f74468df4aFred Quintana                } else {
9821bb0deb36af32339521038cdbd827f74468df4aFred Quintana                    alreadyInProgress = true;
9921bb0deb36af32339521038cdbd827f74468df4aFred Quintana                }
10021bb0deb36af32339521038cdbd827f74468df4aFred Quintana            }
10121bb0deb36af32339521038cdbd827f74468df4aFred Quintana
10221bb0deb36af32339521038cdbd827f74468df4aFred Quintana            // do this outside since we don't want to call back into the syncContext while
10321bb0deb36af32339521038cdbd827f74468df4aFred Quintana            // holding the synchronization lock
10421bb0deb36af32339521038cdbd827f74468df4aFred Quintana            if (alreadyInProgress) {
10521bb0deb36af32339521038cdbd827f74468df4aFred Quintana                syncContextClient.onFinished(SyncResult.ALREADY_IN_PROGRESS);
10621bb0deb36af32339521038cdbd827f74468df4aFred Quintana            }
10721bb0deb36af32339521038cdbd827f74468df4aFred Quintana        }
10821bb0deb36af32339521038cdbd827f74468df4aFred Quintana
10921bb0deb36af32339521038cdbd827f74468df4aFred Quintana        public void cancelSync(ISyncContext syncContext) {
11021bb0deb36af32339521038cdbd827f74468df4aFred Quintana            // synchronize to make sure that mSyncThread doesn't change between when we
11121bb0deb36af32339521038cdbd827f74468df4aFred Quintana            // check it and when we use it
1123cff76aaa893049d02467a231d477e86a0f80daaFred Quintana            synchronized (mSyncThreadLock) {
11321bb0deb36af32339521038cdbd827f74468df4aFred Quintana                if (mSyncThread != null
114f038004f4a5e4fab18df9c87573ba1e82790c30fFred Quintana                        && mSyncThread.mSyncContext.getSyncContextBinder()
115b19b4c711f51ef63f16727026b31216f9b4d5818Fred Quintana                        == syncContext.asBinder()) {
116fcb14322f4afaf87b816ee120ad1e2f217b1350eFred Quintana                    mSyncThread.interrupt();
11721bb0deb36af32339521038cdbd827f74468df4aFred Quintana                }
11821bb0deb36af32339521038cdbd827f74468df4aFred Quintana            }
11921bb0deb36af32339521038cdbd827f74468df4aFred Quintana        }
12021bb0deb36af32339521038cdbd827f74468df4aFred Quintana    }
12121bb0deb36af32339521038cdbd827f74468df4aFred Quintana
12221bb0deb36af32339521038cdbd827f74468df4aFred Quintana    /**
123f038004f4a5e4fab18df9c87573ba1e82790c30fFred Quintana     * The thread that invokes {@link AbstractThreadedSyncAdapter#onPerformSync}. It also acquires
124f038004f4a5e4fab18df9c87573ba1e82790c30fFred Quintana     * the provider for this sync before calling onPerformSync and releases it afterwards. Cancel
125f038004f4a5e4fab18df9c87573ba1e82790c30fFred Quintana     * this thread in order to cancel the sync.
12621bb0deb36af32339521038cdbd827f74468df4aFred Quintana     */
12721bb0deb36af32339521038cdbd827f74468df4aFred Quintana    private class SyncThread extends Thread {
12821bb0deb36af32339521038cdbd827f74468df4aFred Quintana        private final SyncContext mSyncContext;
12921bb0deb36af32339521038cdbd827f74468df4aFred Quintana        private final String mAuthority;
13021bb0deb36af32339521038cdbd827f74468df4aFred Quintana        private final Account mAccount;
13121bb0deb36af32339521038cdbd827f74468df4aFred Quintana        private final Bundle mExtras;
1326fd7385f56b129b49164f606d23771a39507f772Ken Shirriff        private long mInitialTxBytes;
1336fd7385f56b129b49164f606d23771a39507f772Ken Shirriff        private long mInitialRxBytes;
13421bb0deb36af32339521038cdbd827f74468df4aFred Quintana
13521bb0deb36af32339521038cdbd827f74468df4aFred Quintana        private SyncThread(String name, SyncContext syncContext, String authority,
13621bb0deb36af32339521038cdbd827f74468df4aFred Quintana                Account account, Bundle extras) {
13721bb0deb36af32339521038cdbd827f74468df4aFred Quintana            super(name);
13821bb0deb36af32339521038cdbd827f74468df4aFred Quintana            mSyncContext = syncContext;
13921bb0deb36af32339521038cdbd827f74468df4aFred Quintana            mAuthority = authority;
14021bb0deb36af32339521038cdbd827f74468df4aFred Quintana            mAccount = account;
14121bb0deb36af32339521038cdbd827f74468df4aFred Quintana            mExtras = extras;
14221bb0deb36af32339521038cdbd827f74468df4aFred Quintana        }
14321bb0deb36af32339521038cdbd827f74468df4aFred Quintana
14421bb0deb36af32339521038cdbd827f74468df4aFred Quintana        public void run() {
14575d797c2e78d53f49d05b518adb14fd57e0c785cFred Quintana            Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
14675d797c2e78d53f49d05b518adb14fd57e0c785cFred Quintana
14721bb0deb36af32339521038cdbd827f74468df4aFred Quintana            if (isCanceled()) {
14821bb0deb36af32339521038cdbd827f74468df4aFred Quintana                return;
14921bb0deb36af32339521038cdbd827f74468df4aFred Quintana            }
15021bb0deb36af32339521038cdbd827f74468df4aFred Quintana
15121bb0deb36af32339521038cdbd827f74468df4aFred Quintana            SyncResult syncResult = new SyncResult();
1526fd7385f56b129b49164f606d23771a39507f772Ken Shirriff            int uid = Process.myUid();
1536fd7385f56b129b49164f606d23771a39507f772Ken Shirriff            mInitialTxBytes = NetStat.getUidTxBytes(uid);
1546fd7385f56b129b49164f606d23771a39507f772Ken Shirriff            mInitialRxBytes = NetStat.getUidRxBytes(uid);
15521bb0deb36af32339521038cdbd827f74468df4aFred Quintana            ContentProviderClient provider = null;
15621bb0deb36af32339521038cdbd827f74468df4aFred Quintana            try {
15721bb0deb36af32339521038cdbd827f74468df4aFred Quintana                provider = mContext.getContentResolver().acquireContentProviderClient(mAuthority);
15821bb0deb36af32339521038cdbd827f74468df4aFred Quintana                if (provider != null) {
159f038004f4a5e4fab18df9c87573ba1e82790c30fFred Quintana                    AbstractThreadedSyncAdapter.this.onPerformSync(mAccount, mExtras,
16021bb0deb36af32339521038cdbd827f74468df4aFred Quintana                            mAuthority, provider, syncResult);
16121bb0deb36af32339521038cdbd827f74468df4aFred Quintana                } else {
162f038004f4a5e4fab18df9c87573ba1e82790c30fFred Quintana                    syncResult.databaseError = true;
16321bb0deb36af32339521038cdbd827f74468df4aFred Quintana                }
16421bb0deb36af32339521038cdbd827f74468df4aFred Quintana            } finally {
16521bb0deb36af32339521038cdbd827f74468df4aFred Quintana                if (provider != null) {
16621bb0deb36af32339521038cdbd827f74468df4aFred Quintana                    provider.release();
16721bb0deb36af32339521038cdbd827f74468df4aFred Quintana                }
16821bb0deb36af32339521038cdbd827f74468df4aFred Quintana                if (!isCanceled()) {
16921bb0deb36af32339521038cdbd827f74468df4aFred Quintana                    mSyncContext.onFinished(syncResult);
17021bb0deb36af32339521038cdbd827f74468df4aFred Quintana                }
171f038004f4a5e4fab18df9c87573ba1e82790c30fFred Quintana                onLogSyncDetails(NetStat.getUidTxBytes(uid) - mInitialTxBytes,
1726fd7385f56b129b49164f606d23771a39507f772Ken Shirriff                        NetStat.getUidRxBytes(uid) - mInitialRxBytes, syncResult);
17321bb0deb36af32339521038cdbd827f74468df4aFred Quintana                // synchronize so that the assignment will be seen by other threads
17421bb0deb36af32339521038cdbd827f74468df4aFred Quintana                // that also synchronize accesses to mSyncThread
1753cff76aaa893049d02467a231d477e86a0f80daaFred Quintana                synchronized (mSyncThreadLock) {
17621bb0deb36af32339521038cdbd827f74468df4aFred Quintana                    mSyncThread = null;
17721bb0deb36af32339521038cdbd827f74468df4aFred Quintana                }
17821bb0deb36af32339521038cdbd827f74468df4aFred Quintana            }
17921bb0deb36af32339521038cdbd827f74468df4aFred Quintana        }
18021bb0deb36af32339521038cdbd827f74468df4aFred Quintana
18121bb0deb36af32339521038cdbd827f74468df4aFred Quintana        private boolean isCanceled() {
18221bb0deb36af32339521038cdbd827f74468df4aFred Quintana            return Thread.currentThread().isInterrupted();
18321bb0deb36af32339521038cdbd827f74468df4aFred Quintana        }
18421bb0deb36af32339521038cdbd827f74468df4aFred Quintana    }
18521bb0deb36af32339521038cdbd827f74468df4aFred Quintana
18621bb0deb36af32339521038cdbd827f74468df4aFred Quintana    /**
187f038004f4a5e4fab18df9c87573ba1e82790c30fFred Quintana     * @return a reference to the IBinder of the SyncAdapter service.
18821bb0deb36af32339521038cdbd827f74468df4aFred Quintana     */
189f038004f4a5e4fab18df9c87573ba1e82790c30fFred Quintana    public final IBinder getSyncAdapterBinder() {
190f038004f4a5e4fab18df9c87573ba1e82790c30fFred Quintana        return mISyncAdapterImpl.asBinder();
19121bb0deb36af32339521038cdbd827f74468df4aFred Quintana    }
19221bb0deb36af32339521038cdbd827f74468df4aFred Quintana
19321bb0deb36af32339521038cdbd827f74468df4aFred Quintana    /**
19421bb0deb36af32339521038cdbd827f74468df4aFred Quintana     * Perform a sync for this account. SyncAdapter-specific parameters may
19521bb0deb36af32339521038cdbd827f74468df4aFred Quintana     * be specified in extras, which is guaranteed to not be null. Invocations
19621bb0deb36af32339521038cdbd827f74468df4aFred Quintana     * of this method are guaranteed to be serialized.
19721bb0deb36af32339521038cdbd827f74468df4aFred Quintana     *
19821bb0deb36af32339521038cdbd827f74468df4aFred Quintana     * @param account the account that should be synced
19921bb0deb36af32339521038cdbd827f74468df4aFred Quintana     * @param extras SyncAdapter-specific parameters
20021bb0deb36af32339521038cdbd827f74468df4aFred Quintana     * @param authority the authority of this sync request
20121bb0deb36af32339521038cdbd827f74468df4aFred Quintana     * @param provider a ContentProviderClient that points to the ContentProvider for this
20221bb0deb36af32339521038cdbd827f74468df4aFred Quintana     *   authority
20321bb0deb36af32339521038cdbd827f74468df4aFred Quintana     * @param syncResult SyncAdapter-specific parameters
20421bb0deb36af32339521038cdbd827f74468df4aFred Quintana     */
205f038004f4a5e4fab18df9c87573ba1e82790c30fFred Quintana    public abstract void onPerformSync(Account account, Bundle extras,
20621bb0deb36af32339521038cdbd827f74468df4aFred Quintana            String authority, ContentProviderClient provider, SyncResult syncResult);
2076fd7385f56b129b49164f606d23771a39507f772Ken Shirriff
2086fd7385f56b129b49164f606d23771a39507f772Ken Shirriff    /**
2096fd7385f56b129b49164f606d23771a39507f772Ken Shirriff     * Logs details on the sync.
2106fd7385f56b129b49164f606d23771a39507f772Ken Shirriff     * Normally this will be overridden by a subclass that will provide
2116fd7385f56b129b49164f606d23771a39507f772Ken Shirriff     * provider-specific details.
2126fd7385f56b129b49164f606d23771a39507f772Ken Shirriff     *
2136fd7385f56b129b49164f606d23771a39507f772Ken Shirriff     * @param bytesSent number of bytes the sync sent over the network
2146fd7385f56b129b49164f606d23771a39507f772Ken Shirriff     * @param bytesReceived number of bytes the sync received over the network
2156fd7385f56b129b49164f606d23771a39507f772Ken Shirriff     * @param result The SyncResult object holding info on the sync
216f038004f4a5e4fab18df9c87573ba1e82790c30fFred Quintana     * @hide
2176fd7385f56b129b49164f606d23771a39507f772Ken Shirriff     */
218f038004f4a5e4fab18df9c87573ba1e82790c30fFred Quintana    protected void onLogSyncDetails(long bytesSent, long bytesReceived, SyncResult result) {
2196fd7385f56b129b49164f606d23771a39507f772Ken Shirriff        EventLog.writeEvent(SyncAdapter.LOG_SYNC_DETAILS, TAG, bytesSent, bytesReceived, "");
2206fd7385f56b129b49164f606d23771a39507f772Ken Shirriff    }
221f038004f4a5e4fab18df9c87573ba1e82790c30fFred Quintana}
222