AbstractThreadedSyncAdapter.java revision 21bb0deb36af32339521038cdbd827f74468df4a
1/* 2 * Copyright (C) 2009 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package android.content; 18 19import android.accounts.Account; 20import android.os.Bundle; 21import android.os.Process; 22 23import java.util.concurrent.atomic.AtomicInteger; 24 25/** 26 * An abstract implementation of a SyncAdapter that spawns a thread to invoke a sync operation. 27 * If a sync operation is already in progress when a startSync() request is received then an error 28 * will be returned to the new request and the existing request will be allowed to continue. 29 * When a startSync() is received and there is no sync operation in progress then a thread 30 * will be started to run the operation and {@link #performSync} will be invoked on that thread. 31 * If a cancelSync() is received that matches an existing sync operation then the thread 32 * that is running that sync operation will be interrupted, which will indicate to the thread 33 * that the sync has been canceled. 34 * 35 * @hide 36 */ 37public abstract class AbstractThreadedSyncAdapter { 38 private final Context mContext; 39 private final AtomicInteger mNumSyncStarts; 40 private final ISyncAdapterImpl mISyncAdapterImpl; 41 42 // all accesses to this member variable must be synchronized on "this" 43 private SyncThread mSyncThread; 44 45 /** Kernel event log tag. Also listed in data/etc/event-log-tags. */ 46 public static final int LOG_SYNC_DETAILS = 2743; 47 48 /** 49 * Creates an {@link AbstractThreadedSyncAdapter}. 50 * @param context the {@link Context} that this is running within. 51 */ 52 public AbstractThreadedSyncAdapter(Context context) { 53 mContext = context; 54 mISyncAdapterImpl = new ISyncAdapterImpl(); 55 mNumSyncStarts = new AtomicInteger(0); 56 mSyncThread = null; 57 } 58 59 class ISyncAdapterImpl extends ISyncAdapter.Stub { 60 public void startSync(ISyncContext syncContext, String authority, Account account, 61 Bundle extras) { 62 final SyncContext syncContextClient = new SyncContext(syncContext); 63 64 boolean alreadyInProgress; 65 // synchronize to make sure that mSyncThread doesn't change between when we 66 // check it and when we use it 67 synchronized (this) { 68 if (mSyncThread == null) { 69 mSyncThread = new SyncThread( 70 "SyncAdapterThread-" + mNumSyncStarts.incrementAndGet(), 71 syncContextClient, authority, account, extras); 72 Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); 73 mSyncThread.start(); 74 alreadyInProgress = false; 75 } else { 76 alreadyInProgress = true; 77 } 78 } 79 80 // do this outside since we don't want to call back into the syncContext while 81 // holding the synchronization lock 82 if (alreadyInProgress) { 83 syncContextClient.onFinished(SyncResult.ALREADY_IN_PROGRESS); 84 } 85 } 86 87 public void cancelSync(ISyncContext syncContext) { 88 // synchronize to make sure that mSyncThread doesn't change between when we 89 // check it and when we use it 90 synchronized (this) { 91 if (mSyncThread != null 92 && mSyncThread.mSyncContext.getISyncContext() == syncContext) { 93 mSyncThread.interrupt(); 94 } 95 } 96 } 97 } 98 99 /** 100 * The thread that invokes performSync(). It also acquires the provider for this sync 101 * before calling performSync and releases it afterwards. Cancel this thread in order to 102 * cancel the sync. 103 */ 104 private class SyncThread extends Thread { 105 private final SyncContext mSyncContext; 106 private final String mAuthority; 107 private final Account mAccount; 108 private final Bundle mExtras; 109 110 private SyncThread(String name, SyncContext syncContext, String authority, 111 Account account, Bundle extras) { 112 super(name); 113 mSyncContext = syncContext; 114 mAuthority = authority; 115 mAccount = account; 116 mExtras = extras; 117 } 118 119 public void run() { 120 if (isCanceled()) { 121 return; 122 } 123 124 SyncResult syncResult = new SyncResult(); 125 ContentProviderClient provider = null; 126 try { 127 provider = mContext.getContentResolver().acquireContentProviderClient(mAuthority); 128 if (provider != null) { 129 AbstractThreadedSyncAdapter.this.performSync(mAccount, mExtras, 130 mAuthority, provider, syncResult); 131 } else { 132 // TODO(fredq) update the syncResults to indicate that we were unable to 133 // find the provider. maybe with a ProviderError? 134 } 135 } finally { 136 if (provider != null) { 137 provider.release(); 138 } 139 if (!isCanceled()) { 140 mSyncContext.onFinished(syncResult); 141 } 142 // synchronize so that the assignment will be seen by other threads 143 // that also synchronize accesses to mSyncThread 144 synchronized (this) { 145 mSyncThread = null; 146 } 147 } 148 } 149 150 private boolean isCanceled() { 151 return Thread.currentThread().isInterrupted(); 152 } 153 } 154 155 /** 156 * @return a reference to the ISyncAdapter interface into this SyncAdapter implementation. 157 */ 158 public final ISyncAdapter getISyncAdapter() { 159 return mISyncAdapterImpl; 160 } 161 162 /** 163 * Perform a sync for this account. SyncAdapter-specific parameters may 164 * be specified in extras, which is guaranteed to not be null. Invocations 165 * of this method are guaranteed to be serialized. 166 * 167 * @param account the account that should be synced 168 * @param extras SyncAdapter-specific parameters 169 * @param authority the authority of this sync request 170 * @param provider a ContentProviderClient that points to the ContentProvider for this 171 * authority 172 * @param syncResult SyncAdapter-specific parameters 173 */ 174 public abstract void performSync(Account account, Bundle extras, 175 String authority, ContentProviderClient provider, SyncResult syncResult); 176}