Fred Quintana | 21bb0de | 2009-06-16 10:24:58 -0700 | [diff] [blame] | 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 | |
| 17 | package android.content; |
| 18 | |
Philip P. Moltmann | 186e3b3 | 2018-03-15 16:32:44 -0700 | [diff] [blame] | 19 | import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage; |
| 20 | |
Fred Quintana | 21bb0de | 2009-06-16 10:24:58 -0700 | [diff] [blame] | 21 | import android.accounts.Account; |
Philip P. Moltmann | 186e3b3 | 2018-03-15 16:32:44 -0700 | [diff] [blame] | 22 | import android.annotation.MainThread; |
| 23 | import android.annotation.NonNull; |
Makoto Onuki | 6a6ae04 | 2017-07-20 13:30:12 -0700 | [diff] [blame] | 24 | import android.os.Build; |
Fred Quintana | 21bb0de | 2009-06-16 10:24:58 -0700 | [diff] [blame] | 25 | import android.os.Bundle; |
Philip P. Moltmann | 186e3b3 | 2018-03-15 16:32:44 -0700 | [diff] [blame] | 26 | import android.os.Handler; |
Fred Quintana | f038004 | 2009-10-06 17:05:58 -0700 | [diff] [blame] | 27 | import android.os.IBinder; |
Ken Shirriff | 1719a39 | 2009-12-07 15:57:35 -0800 | [diff] [blame] | 28 | import android.os.Process; |
Philip P. Moltmann | 486b241 | 2018-01-03 11:29:01 -0800 | [diff] [blame] | 29 | import android.os.RemoteException; |
Andy Stadler | 09b45a3 | 2012-05-03 15:00:49 -0700 | [diff] [blame] | 30 | import android.os.Trace; |
Makoto Onuki | 6a6ae04 | 2017-07-20 13:30:12 -0700 | [diff] [blame] | 31 | import android.util.Log; |
Fred Quintana | 21bb0de | 2009-06-16 10:24:58 -0700 | [diff] [blame] | 32 | |
Fred Quintana | 0c4d04a | 2010-11-03 17:02:55 -0700 | [diff] [blame] | 33 | import java.util.HashMap; |
Fred Quintana | 21bb0de | 2009-06-16 10:24:58 -0700 | [diff] [blame] | 34 | import java.util.concurrent.atomic.AtomicInteger; |
| 35 | |
| 36 | /** |
| 37 | * An abstract implementation of a SyncAdapter that spawns a thread to invoke a sync operation. |
Matthew Williams | 1967c8d | 2015-06-19 19:03:13 -0700 | [diff] [blame] | 38 | * If a sync operation is already in progress when a sync request is received, an error will be |
| 39 | * returned to the new request and the existing request will be allowed to continue. |
| 40 | * However if there is no sync in progress then a thread will be spawned and {@link #onPerformSync} |
| 41 | * will be invoked on that thread. |
| 42 | * <p> |
| 43 | * Syncs can be cancelled at any time by the framework. For example a sync that was not |
| 44 | * user-initiated and lasts longer than 30 minutes will be considered timed-out and cancelled. |
| 45 | * Similarly the framework will attempt to determine whether or not an adapter is making progress |
| 46 | * by monitoring its network activity over the course of a minute. If the network traffic over this |
| 47 | * window is close enough to zero the sync will be cancelled. You can also request the sync be |
| 48 | * cancelled via {@link ContentResolver#cancelSync(Account, String)} or |
| 49 | * {@link ContentResolver#cancelSync(SyncRequest)}. |
| 50 | * <p> |
| 51 | * A sync is cancelled by issuing a {@link Thread#interrupt()} on the syncing thread. <strong>Either |
| 52 | * your code in {@link #onPerformSync(Account, Bundle, String, ContentProviderClient, SyncResult)} |
| 53 | * must check {@link Thread#interrupted()}, or you you must override one of |
| 54 | * {@link #onSyncCanceled(Thread)}/{@link #onSyncCanceled()}</strong> (depending on whether or not |
| 55 | * your adapter supports syncing of multiple accounts in parallel). If your adapter does not |
| 56 | * respect the cancel issued by the framework you run the risk of your app's entire process being |
| 57 | * killed. |
Fred Quintana | e6d60ec | 2011-08-24 11:29:00 -0700 | [diff] [blame] | 58 | * <p> |
| 59 | * In order to be a sync adapter one must extend this class, provide implementations for the |
| 60 | * abstract methods and write a service that returns the result of {@link #getSyncAdapterBinder()} |
| 61 | * in the service's {@link android.app.Service#onBind(android.content.Intent)} when invoked |
| 62 | * with an intent with action <code>android.content.SyncAdapter</code>. This service |
| 63 | * must specify the following intent filter and metadata tags in its AndroidManifest.xml file |
| 64 | * <pre> |
| 65 | * <intent-filter> |
| 66 | * <action android:name="android.content.SyncAdapter" /> |
| 67 | * </intent-filter> |
| 68 | * <meta-data android:name="android.content.SyncAdapter" |
| 69 | * android:resource="@xml/syncadapter" /> |
| 70 | * </pre> |
| 71 | * The <code>android:resource</code> attribute must point to a resource that looks like: |
| 72 | * <pre> |
| 73 | * <sync-adapter xmlns:android="http://schemas.android.com/apk/res/android" |
| 74 | * android:contentAuthority="authority" |
| 75 | * android:accountType="accountType" |
| 76 | * android:userVisible="true|false" |
| 77 | * android:supportsUploading="true|false" |
| 78 | * android:allowParallelSyncs="true|false" |
| 79 | * android:isAlwaysSyncable="true|false" |
| 80 | * android:syncAdapterSettingsAction="ACTION_OF_SETTINGS_ACTIVITY" |
| 81 | * /> |
| 82 | * </pre> |
| 83 | * <ul> |
| 84 | * <li>The <code>android:contentAuthority</code> and <code>android:accountType</code> attributes |
| 85 | * indicate which content authority and for which account types this sync adapter serves. |
| 86 | * <li><code>android:userVisible</code> defaults to true and controls whether or not this sync |
| 87 | * adapter shows up in the Sync Settings screen. |
| 88 | * <li><code>android:supportsUploading</code> defaults |
| 89 | * to true and if true an upload-only sync will be requested for all syncadapters associated |
| 90 | * with an authority whenever that authority's content provider does a |
| 91 | * {@link ContentResolver#notifyChange(android.net.Uri, android.database.ContentObserver, boolean)} |
| 92 | * with syncToNetwork set to true. |
| 93 | * <li><code>android:allowParallelSyncs</code> defaults to false and if true indicates that |
| 94 | * the sync adapter can handle syncs for multiple accounts at the same time. Otherwise |
| 95 | * the SyncManager will wait until the sync adapter is not in use before requesting that |
| 96 | * it sync an account's data. |
| 97 | * <li><code>android:isAlwaysSyncable</code> defaults to false and if true tells the SyncManager |
kopriva | 219f7dc | 2018-10-09 13:42:28 -0700 | [diff] [blame] | 98 | * to initialize the isSyncable state to 1 for that sync adapter for each account that is added. |
Fred Quintana | e6d60ec | 2011-08-24 11:29:00 -0700 | [diff] [blame] | 99 | * <li><code>android:syncAdapterSettingsAction</code> defaults to null and if supplied it |
| 100 | * specifies an Intent action of an activity that can be used to adjust the sync adapter's |
| 101 | * sync settings. The activity must live in the same package as the sync adapter. |
| 102 | * </ul> |
Fred Quintana | 21bb0de | 2009-06-16 10:24:58 -0700 | [diff] [blame] | 103 | */ |
| 104 | public abstract class AbstractThreadedSyncAdapter { |
Makoto Onuki | 6a6ae04 | 2017-07-20 13:30:12 -0700 | [diff] [blame] | 105 | private static final String TAG = "SyncAdapter"; |
| 106 | |
Fred Quintana | 97ef763 | 2009-12-10 10:33:18 -0800 | [diff] [blame] | 107 | /** |
| 108 | * Kernel event log tag. Also listed in data/etc/event-log-tags. |
Joe Onorato | d3ad696 | 2010-09-16 13:38:25 -0400 | [diff] [blame] | 109 | * @deprecated Private constant. May go away in the next release. |
Fred Quintana | 97ef763 | 2009-12-10 10:33:18 -0800 | [diff] [blame] | 110 | */ |
Joe Onorato | d3ad696 | 2010-09-16 13:38:25 -0400 | [diff] [blame] | 111 | @Deprecated |
Fred Quintana | 97ef763 | 2009-12-10 10:33:18 -0800 | [diff] [blame] | 112 | public static final int LOG_SYNC_DETAILS = 2743; |
| 113 | |
Makoto Onuki | 6a6ae04 | 2017-07-20 13:30:12 -0700 | [diff] [blame] | 114 | private static final boolean ENABLE_LOG = Build.IS_DEBUGGABLE && Log.isLoggable(TAG, Log.DEBUG); |
| 115 | |
Fred Quintana | 21bb0de | 2009-06-16 10:24:58 -0700 | [diff] [blame] | 116 | private final Context mContext; |
| 117 | private final AtomicInteger mNumSyncStarts; |
| 118 | private final ISyncAdapterImpl mISyncAdapterImpl; |
| 119 | |
Fred Quintana | 3cff76a | 2009-08-26 18:49:19 -0700 | [diff] [blame] | 120 | // all accesses to this member variable must be synchronized on mSyncThreadLock |
Fred Quintana | 0c4d04a | 2010-11-03 17:02:55 -0700 | [diff] [blame] | 121 | private final HashMap<Account, SyncThread> mSyncThreads = new HashMap<Account, SyncThread>(); |
Fred Quintana | 3cff76a | 2009-08-26 18:49:19 -0700 | [diff] [blame] | 122 | private final Object mSyncThreadLock = new Object(); |
Fred Quintana | 21bb0de | 2009-06-16 10:24:58 -0700 | [diff] [blame] | 123 | |
Fred Quintana | 4a6679b | 2009-08-17 13:05:39 -0700 | [diff] [blame] | 124 | private final boolean mAutoInitialize; |
Fred Quintana | 0c4d04a | 2010-11-03 17:02:55 -0700 | [diff] [blame] | 125 | private boolean mAllowParallelSyncs; |
Fred Quintana | 21bb0de | 2009-06-16 10:24:58 -0700 | [diff] [blame] | 126 | |
| 127 | /** |
| 128 | * Creates an {@link AbstractThreadedSyncAdapter}. |
Fred Quintana | 4a6679b | 2009-08-17 13:05:39 -0700 | [diff] [blame] | 129 | * @param context the {@link android.content.Context} that this is running within. |
| 130 | * @param autoInitialize if true then sync requests that have |
| 131 | * {@link ContentResolver#SYNC_EXTRAS_INITIALIZE} set will be internally handled by |
| 132 | * {@link AbstractThreadedSyncAdapter} by calling |
| 133 | * {@link ContentResolver#setIsSyncable(android.accounts.Account, String, int)} with 1 if it |
| 134 | * is currently set to <0. |
Fred Quintana | 21bb0de | 2009-06-16 10:24:58 -0700 | [diff] [blame] | 135 | */ |
Fred Quintana | 4a6679b | 2009-08-17 13:05:39 -0700 | [diff] [blame] | 136 | public AbstractThreadedSyncAdapter(Context context, boolean autoInitialize) { |
Fred Quintana | 0c4d04a | 2010-11-03 17:02:55 -0700 | [diff] [blame] | 137 | this(context, autoInitialize, false /* allowParallelSyncs */); |
| 138 | } |
| 139 | |
| 140 | /** |
| 141 | * Creates an {@link AbstractThreadedSyncAdapter}. |
| 142 | * @param context the {@link android.content.Context} that this is running within. |
| 143 | * @param autoInitialize if true then sync requests that have |
| 144 | * {@link ContentResolver#SYNC_EXTRAS_INITIALIZE} set will be internally handled by |
| 145 | * {@link AbstractThreadedSyncAdapter} by calling |
| 146 | * {@link ContentResolver#setIsSyncable(android.accounts.Account, String, int)} with 1 if it |
| 147 | * is currently set to <0. |
| 148 | * @param allowParallelSyncs if true then allow syncs for different accounts to run |
| 149 | * at the same time, each in their own thread. This must be consistent with the setting |
| 150 | * in the SyncAdapter's configuration file. |
| 151 | */ |
| 152 | public AbstractThreadedSyncAdapter(Context context, |
| 153 | boolean autoInitialize, boolean allowParallelSyncs) { |
Fred Quintana | 21bb0de | 2009-06-16 10:24:58 -0700 | [diff] [blame] | 154 | mContext = context; |
| 155 | mISyncAdapterImpl = new ISyncAdapterImpl(); |
| 156 | mNumSyncStarts = new AtomicInteger(0); |
Fred Quintana | 4a6679b | 2009-08-17 13:05:39 -0700 | [diff] [blame] | 157 | mAutoInitialize = autoInitialize; |
Fred Quintana | 0c4d04a | 2010-11-03 17:02:55 -0700 | [diff] [blame] | 158 | mAllowParallelSyncs = allowParallelSyncs; |
Fred Quintana | 21bb0de | 2009-06-16 10:24:58 -0700 | [diff] [blame] | 159 | } |
| 160 | |
Fred Quintana | c298a85 | 2009-08-27 18:28:17 -0700 | [diff] [blame] | 161 | public Context getContext() { |
| 162 | return mContext; |
| 163 | } |
| 164 | |
Fred Quintana | 0c4d04a | 2010-11-03 17:02:55 -0700 | [diff] [blame] | 165 | private Account toSyncKey(Account account) { |
| 166 | if (mAllowParallelSyncs) { |
| 167 | return account; |
| 168 | } else { |
| 169 | return null; |
| 170 | } |
| 171 | } |
| 172 | |
Fred Quintana | f038004 | 2009-10-06 17:05:58 -0700 | [diff] [blame] | 173 | private class ISyncAdapterImpl extends ISyncAdapter.Stub { |
Matthew Williams | fa77418 | 2013-06-18 15:44:11 -0700 | [diff] [blame] | 174 | @Override |
Philip P. Moltmann | 186e3b3 | 2018-03-15 16:32:44 -0700 | [diff] [blame] | 175 | public void onUnsyncableAccount(ISyncAdapterUnsyncableAccountCallback cb) { |
| 176 | Handler.getMain().sendMessage(obtainMessage( |
| 177 | AbstractThreadedSyncAdapter::handleOnUnsyncableAccount, |
| 178 | AbstractThreadedSyncAdapter.this, cb)); |
Philip P. Moltmann | 486b241 | 2018-01-03 11:29:01 -0800 | [diff] [blame] | 179 | } |
| 180 | |
| 181 | @Override |
Fred Quintana | 21bb0de | 2009-06-16 10:24:58 -0700 | [diff] [blame] | 182 | public void startSync(ISyncContext syncContext, String authority, Account account, |
| 183 | Bundle extras) { |
Makoto Onuki | 6a6ae04 | 2017-07-20 13:30:12 -0700 | [diff] [blame] | 184 | if (ENABLE_LOG) { |
| 185 | if (extras != null) { |
| 186 | extras.size(); // Unparcel so its toString() will show the contents. |
Fred Quintana | 21bb0de | 2009-06-16 10:24:58 -0700 | [diff] [blame] | 187 | } |
Makoto Onuki | 6a6ae04 | 2017-07-20 13:30:12 -0700 | [diff] [blame] | 188 | Log.d(TAG, "startSync() start " + authority + " " + account + " " + extras); |
Fred Quintana | 21bb0de | 2009-06-16 10:24:58 -0700 | [diff] [blame] | 189 | } |
Makoto Onuki | 6a6ae04 | 2017-07-20 13:30:12 -0700 | [diff] [blame] | 190 | try { |
| 191 | final SyncContext syncContextClient = new SyncContext(syncContext); |
Fred Quintana | 21bb0de | 2009-06-16 10:24:58 -0700 | [diff] [blame] | 192 | |
Makoto Onuki | 6a6ae04 | 2017-07-20 13:30:12 -0700 | [diff] [blame] | 193 | boolean alreadyInProgress; |
| 194 | // synchronize to make sure that mSyncThreads doesn't change between when we |
| 195 | // check it and when we use it |
| 196 | final Account threadsKey = toSyncKey(account); |
| 197 | synchronized (mSyncThreadLock) { |
| 198 | if (!mSyncThreads.containsKey(threadsKey)) { |
| 199 | if (mAutoInitialize |
| 200 | && extras != null |
| 201 | && extras.getBoolean( |
| 202 | ContentResolver.SYNC_EXTRAS_INITIALIZE, false)) { |
| 203 | try { |
| 204 | if (ContentResolver.getIsSyncable(account, authority) < 0) { |
| 205 | ContentResolver.setIsSyncable(account, authority, 1); |
| 206 | } |
| 207 | } finally { |
| 208 | syncContextClient.onFinished(new SyncResult()); |
| 209 | } |
| 210 | return; |
| 211 | } |
| 212 | SyncThread syncThread = new SyncThread( |
| 213 | "SyncAdapterThread-" + mNumSyncStarts.incrementAndGet(), |
| 214 | syncContextClient, authority, account, extras); |
| 215 | mSyncThreads.put(threadsKey, syncThread); |
| 216 | syncThread.start(); |
| 217 | alreadyInProgress = false; |
| 218 | } else { |
| 219 | if (ENABLE_LOG) { |
| 220 | Log.d(TAG, " alreadyInProgress"); |
| 221 | } |
| 222 | alreadyInProgress = true; |
| 223 | } |
| 224 | } |
| 225 | |
| 226 | // do this outside since we don't want to call back into the syncContext while |
| 227 | // holding the synchronization lock |
| 228 | if (alreadyInProgress) { |
| 229 | syncContextClient.onFinished(SyncResult.ALREADY_IN_PROGRESS); |
| 230 | } |
| 231 | } catch (RuntimeException | Error th) { |
| 232 | if (ENABLE_LOG) { |
| 233 | Log.d(TAG, "startSync() caught exception", th); |
| 234 | } |
| 235 | throw th; |
| 236 | } finally { |
| 237 | if (ENABLE_LOG) { |
| 238 | Log.d(TAG, "startSync() finishing"); |
| 239 | } |
Fred Quintana | 21bb0de | 2009-06-16 10:24:58 -0700 | [diff] [blame] | 240 | } |
| 241 | } |
| 242 | |
Matthew Williams | fa77418 | 2013-06-18 15:44:11 -0700 | [diff] [blame] | 243 | @Override |
Fred Quintana | 21bb0de | 2009-06-16 10:24:58 -0700 | [diff] [blame] | 244 | public void cancelSync(ISyncContext syncContext) { |
Makoto Onuki | 6a6ae04 | 2017-07-20 13:30:12 -0700 | [diff] [blame] | 245 | try { |
| 246 | // synchronize to make sure that mSyncThreads doesn't change between when we |
| 247 | // check it and when we use it |
| 248 | SyncThread info = null; |
| 249 | synchronized (mSyncThreadLock) { |
| 250 | for (SyncThread current : mSyncThreads.values()) { |
| 251 | if (current.mSyncContext.getSyncContextBinder() == syncContext.asBinder()) { |
| 252 | info = current; |
| 253 | break; |
| 254 | } |
Fred Quintana | 0c4d04a | 2010-11-03 17:02:55 -0700 | [diff] [blame] | 255 | } |
| 256 | } |
Makoto Onuki | 6a6ae04 | 2017-07-20 13:30:12 -0700 | [diff] [blame] | 257 | if (info != null) { |
| 258 | if (ENABLE_LOG) { |
| 259 | Log.d(TAG, "cancelSync() " + info.mAuthority + " " + info.mAccount); |
| 260 | } |
| 261 | if (mAllowParallelSyncs) { |
| 262 | onSyncCanceled(info); |
| 263 | } else { |
| 264 | onSyncCanceled(); |
| 265 | } |
Fred Quintana | 0c4d04a | 2010-11-03 17:02:55 -0700 | [diff] [blame] | 266 | } else { |
Makoto Onuki | 6a6ae04 | 2017-07-20 13:30:12 -0700 | [diff] [blame] | 267 | if (ENABLE_LOG) { |
| 268 | Log.w(TAG, "cancelSync() unknown context"); |
| 269 | } |
| 270 | } |
| 271 | } catch (RuntimeException | Error th) { |
| 272 | if (ENABLE_LOG) { |
| 273 | Log.d(TAG, "cancelSync() caught exception", th); |
| 274 | } |
| 275 | throw th; |
| 276 | } finally { |
| 277 | if (ENABLE_LOG) { |
| 278 | Log.d(TAG, "cancelSync() finishing"); |
Fred Quintana | 0c4d04a | 2010-11-03 17:02:55 -0700 | [diff] [blame] | 279 | } |
Fred Quintana | 21bb0de | 2009-06-16 10:24:58 -0700 | [diff] [blame] | 280 | } |
| 281 | } |
| 282 | } |
| 283 | |
| 284 | /** |
Fred Quintana | f038004 | 2009-10-06 17:05:58 -0700 | [diff] [blame] | 285 | * The thread that invokes {@link AbstractThreadedSyncAdapter#onPerformSync}. It also acquires |
| 286 | * the provider for this sync before calling onPerformSync and releases it afterwards. Cancel |
| 287 | * this thread in order to cancel the sync. |
Fred Quintana | 21bb0de | 2009-06-16 10:24:58 -0700 | [diff] [blame] | 288 | */ |
| 289 | private class SyncThread extends Thread { |
| 290 | private final SyncContext mSyncContext; |
| 291 | private final String mAuthority; |
| 292 | private final Account mAccount; |
| 293 | private final Bundle mExtras; |
Fred Quintana | 0c4d04a | 2010-11-03 17:02:55 -0700 | [diff] [blame] | 294 | private final Account mThreadsKey; |
Fred Quintana | 21bb0de | 2009-06-16 10:24:58 -0700 | [diff] [blame] | 295 | |
| 296 | private SyncThread(String name, SyncContext syncContext, String authority, |
| 297 | Account account, Bundle extras) { |
| 298 | super(name); |
| 299 | mSyncContext = syncContext; |
| 300 | mAuthority = authority; |
| 301 | mAccount = account; |
| 302 | mExtras = extras; |
Fred Quintana | 0c4d04a | 2010-11-03 17:02:55 -0700 | [diff] [blame] | 303 | mThreadsKey = toSyncKey(account); |
Fred Quintana | 21bb0de | 2009-06-16 10:24:58 -0700 | [diff] [blame] | 304 | } |
| 305 | |
Andy Stadler | 09b45a3 | 2012-05-03 15:00:49 -0700 | [diff] [blame] | 306 | @Override |
Fred Quintana | 21bb0de | 2009-06-16 10:24:58 -0700 | [diff] [blame] | 307 | public void run() { |
Fred Quintana | 75d797c | 2009-08-25 15:05:02 -0700 | [diff] [blame] | 308 | Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); |
| 309 | |
Makoto Onuki | 6a6ae04 | 2017-07-20 13:30:12 -0700 | [diff] [blame] | 310 | if (ENABLE_LOG) { |
| 311 | Log.d(TAG, "Thread started"); |
| 312 | } |
| 313 | |
Andy Stadler | 09b45a3 | 2012-05-03 15:00:49 -0700 | [diff] [blame] | 314 | // Trace this sync instance. Note, conceptually this should be in |
| 315 | // SyncStorageEngine.insertStartSyncEvent(), but the trace functions require unique |
| 316 | // threads in order to track overlapping operations, so we'll do it here for now. |
| 317 | Trace.traceBegin(Trace.TRACE_TAG_SYNC_MANAGER, mAuthority); |
| 318 | |
Fred Quintana | 21bb0de | 2009-06-16 10:24:58 -0700 | [diff] [blame] | 319 | SyncResult syncResult = new SyncResult(); |
| 320 | ContentProviderClient provider = null; |
| 321 | try { |
Alon Albert | 9257ec0 | 2011-02-07 10:53:26 -0800 | [diff] [blame] | 322 | if (isCanceled()) { |
Makoto Onuki | 6a6ae04 | 2017-07-20 13:30:12 -0700 | [diff] [blame] | 323 | if (ENABLE_LOG) { |
| 324 | Log.d(TAG, "Already canceled"); |
| 325 | } |
Alon Albert | 9257ec0 | 2011-02-07 10:53:26 -0800 | [diff] [blame] | 326 | return; |
| 327 | } |
Makoto Onuki | 6a6ae04 | 2017-07-20 13:30:12 -0700 | [diff] [blame] | 328 | if (ENABLE_LOG) { |
| 329 | Log.d(TAG, "Calling onPerformSync..."); |
| 330 | } |
| 331 | |
Fred Quintana | 21bb0de | 2009-06-16 10:24:58 -0700 | [diff] [blame] | 332 | provider = mContext.getContentResolver().acquireContentProviderClient(mAuthority); |
| 333 | if (provider != null) { |
Fred Quintana | f038004 | 2009-10-06 17:05:58 -0700 | [diff] [blame] | 334 | AbstractThreadedSyncAdapter.this.onPerformSync(mAccount, mExtras, |
Fred Quintana | 21bb0de | 2009-06-16 10:24:58 -0700 | [diff] [blame] | 335 | mAuthority, provider, syncResult); |
| 336 | } else { |
Fred Quintana | f038004 | 2009-10-06 17:05:58 -0700 | [diff] [blame] | 337 | syncResult.databaseError = true; |
Fred Quintana | 21bb0de | 2009-06-16 10:24:58 -0700 | [diff] [blame] | 338 | } |
Makoto Onuki | 6a6ae04 | 2017-07-20 13:30:12 -0700 | [diff] [blame] | 339 | |
| 340 | if (ENABLE_LOG) { |
| 341 | Log.d(TAG, "onPerformSync done"); |
| 342 | } |
| 343 | |
Dianne Hackborn | d01ed46 | 2015-06-22 17:41:44 -0700 | [diff] [blame] | 344 | } catch (SecurityException e) { |
Makoto Onuki | 6a6ae04 | 2017-07-20 13:30:12 -0700 | [diff] [blame] | 345 | if (ENABLE_LOG) { |
| 346 | Log.d(TAG, "SecurityException", e); |
| 347 | } |
Dianne Hackborn | d01ed46 | 2015-06-22 17:41:44 -0700 | [diff] [blame] | 348 | AbstractThreadedSyncAdapter.this.onSecurityException(mAccount, mExtras, |
| 349 | mAuthority, syncResult); |
| 350 | syncResult.databaseError = true; |
Makoto Onuki | 6a6ae04 | 2017-07-20 13:30:12 -0700 | [diff] [blame] | 351 | } catch (RuntimeException | Error th) { |
| 352 | if (ENABLE_LOG) { |
| 353 | Log.d(TAG, "caught exception", th); |
| 354 | } |
| 355 | throw th; |
Fred Quintana | 21bb0de | 2009-06-16 10:24:58 -0700 | [diff] [blame] | 356 | } finally { |
Andy Stadler | 09b45a3 | 2012-05-03 15:00:49 -0700 | [diff] [blame] | 357 | Trace.traceEnd(Trace.TRACE_TAG_SYNC_MANAGER); |
| 358 | |
Fred Quintana | 21bb0de | 2009-06-16 10:24:58 -0700 | [diff] [blame] | 359 | if (provider != null) { |
| 360 | provider.release(); |
| 361 | } |
| 362 | if (!isCanceled()) { |
| 363 | mSyncContext.onFinished(syncResult); |
| 364 | } |
| 365 | // synchronize so that the assignment will be seen by other threads |
Fred Quintana | 0c4d04a | 2010-11-03 17:02:55 -0700 | [diff] [blame] | 366 | // that also synchronize accesses to mSyncThreads |
Fred Quintana | 3cff76a | 2009-08-26 18:49:19 -0700 | [diff] [blame] | 367 | synchronized (mSyncThreadLock) { |
Fred Quintana | 0c4d04a | 2010-11-03 17:02:55 -0700 | [diff] [blame] | 368 | mSyncThreads.remove(mThreadsKey); |
Fred Quintana | 21bb0de | 2009-06-16 10:24:58 -0700 | [diff] [blame] | 369 | } |
Makoto Onuki | 6a6ae04 | 2017-07-20 13:30:12 -0700 | [diff] [blame] | 370 | |
| 371 | if (ENABLE_LOG) { |
| 372 | Log.d(TAG, "Thread finished"); |
| 373 | } |
Fred Quintana | 21bb0de | 2009-06-16 10:24:58 -0700 | [diff] [blame] | 374 | } |
| 375 | } |
| 376 | |
| 377 | private boolean isCanceled() { |
| 378 | return Thread.currentThread().isInterrupted(); |
| 379 | } |
| 380 | } |
| 381 | |
| 382 | /** |
Fred Quintana | f038004 | 2009-10-06 17:05:58 -0700 | [diff] [blame] | 383 | * @return a reference to the IBinder of the SyncAdapter service. |
Fred Quintana | 21bb0de | 2009-06-16 10:24:58 -0700 | [diff] [blame] | 384 | */ |
Fred Quintana | f038004 | 2009-10-06 17:05:58 -0700 | [diff] [blame] | 385 | public final IBinder getSyncAdapterBinder() { |
| 386 | return mISyncAdapterImpl.asBinder(); |
Fred Quintana | 21bb0de | 2009-06-16 10:24:58 -0700 | [diff] [blame] | 387 | } |
| 388 | |
| 389 | /** |
Philip P. Moltmann | 186e3b3 | 2018-03-15 16:32:44 -0700 | [diff] [blame] | 390 | * Handle a call of onUnsyncableAccount. |
| 391 | * |
| 392 | * @param cb The callback to report the return value to |
| 393 | */ |
| 394 | private void handleOnUnsyncableAccount(@NonNull ISyncAdapterUnsyncableAccountCallback cb) { |
| 395 | boolean doSync; |
| 396 | try { |
| 397 | doSync = onUnsyncableAccount(); |
| 398 | } catch (RuntimeException e) { |
| 399 | Log.e(TAG, "Exception while calling onUnsyncableAccount, assuming 'true'", e); |
| 400 | doSync = true; |
| 401 | } |
| 402 | |
| 403 | try { |
| 404 | cb.onUnsyncableAccountDone(doSync); |
| 405 | } catch (RemoteException e) { |
| 406 | Log.e(TAG, "Could not report result of onUnsyncableAccount", e); |
| 407 | } |
| 408 | } |
| 409 | |
| 410 | /** |
Philip P. Moltmann | 486b241 | 2018-01-03 11:29:01 -0800 | [diff] [blame] | 411 | * Allows to defer syncing until all accounts are properly set up. |
| 412 | * |
| 413 | * <p>Called when a account / authority pair |
| 414 | * <ul> |
| 415 | * <li>that can be handled by this adapter</li> |
| 416 | * <li>{@link ContentResolver#requestSync(SyncRequest) is synced}</li> |
| 417 | * <li>and the account/provider {@link ContentResolver#getIsSyncable(Account, String) has |
| 418 | * unknown state (<0)}.</li> |
| 419 | * </ul> |
| 420 | * |
| 421 | * <p>This might be called on a different service connection as {@link #onPerformSync}. |
| 422 | * |
Philip P. Moltmann | 186e3b3 | 2018-03-15 16:32:44 -0700 | [diff] [blame] | 423 | * <p>The system expects this method to immediately return. If the call stalls the system |
| 424 | * behaves as if this method returned {@code true}. If it is required to perform a longer task |
| 425 | * (such as interacting with the user), return {@code false} and proceed in a difference |
| 426 | * context, such as an {@link android.app.Activity}, or foreground service. The sync can then be |
| 427 | * rescheduled once the account becomes syncable. |
| 428 | * |
Philip P. Moltmann | 486b241 | 2018-01-03 11:29:01 -0800 | [diff] [blame] | 429 | * @return If {@code false} syncing is deferred. Returns {@code true} by default, i.e. by |
| 430 | * default syncing starts immediately. |
| 431 | */ |
Philip P. Moltmann | 186e3b3 | 2018-03-15 16:32:44 -0700 | [diff] [blame] | 432 | @MainThread |
Philip P. Moltmann | 486b241 | 2018-01-03 11:29:01 -0800 | [diff] [blame] | 433 | public boolean onUnsyncableAccount() { |
| 434 | return true; |
| 435 | } |
| 436 | |
| 437 | /** |
Fred Quintana | 21bb0de | 2009-06-16 10:24:58 -0700 | [diff] [blame] | 438 | * Perform a sync for this account. SyncAdapter-specific parameters may |
| 439 | * be specified in extras, which is guaranteed to not be null. Invocations |
| 440 | * of this method are guaranteed to be serialized. |
| 441 | * |
| 442 | * @param account the account that should be synced |
| 443 | * @param extras SyncAdapter-specific parameters |
| 444 | * @param authority the authority of this sync request |
| 445 | * @param provider a ContentProviderClient that points to the ContentProvider for this |
| 446 | * authority |
| 447 | * @param syncResult SyncAdapter-specific parameters |
| 448 | */ |
Fred Quintana | f038004 | 2009-10-06 17:05:58 -0700 | [diff] [blame] | 449 | public abstract void onPerformSync(Account account, Bundle extras, |
Fred Quintana | 21bb0de | 2009-06-16 10:24:58 -0700 | [diff] [blame] | 450 | String authority, ContentProviderClient provider, SyncResult syncResult); |
Fred Quintana | 274dc9d | 2009-12-11 13:17:08 -0800 | [diff] [blame] | 451 | |
| 452 | /** |
Dianne Hackborn | d01ed46 | 2015-06-22 17:41:44 -0700 | [diff] [blame] | 453 | * Report that there was a security exception when opening the content provider |
| 454 | * prior to calling {@link #onPerformSync}. This will be treated as a sync |
| 455 | * database failure. |
| 456 | * |
| 457 | * @param account the account that attempted to sync |
| 458 | * @param extras SyncAdapter-specific parameters |
| 459 | * @param authority the authority of the failed sync request |
| 460 | * @param syncResult SyncAdapter-specific parameters |
| 461 | */ |
| 462 | public void onSecurityException(Account account, Bundle extras, |
| 463 | String authority, SyncResult syncResult) { |
| 464 | } |
| 465 | |
| 466 | /** |
Fred Quintana | 274dc9d | 2009-12-11 13:17:08 -0800 | [diff] [blame] | 467 | * Indicates that a sync operation has been canceled. This will be invoked on a separate |
| 468 | * thread than the sync thread and so you must consider the multi-threaded implications |
| 469 | * of the work that you do in this method. |
Fred Quintana | 0c4d04a | 2010-11-03 17:02:55 -0700 | [diff] [blame] | 470 | * <p> |
| 471 | * This will only be invoked when the SyncAdapter indicates that it doesn't support |
| 472 | * parallel syncs. |
Fred Quintana | 274dc9d | 2009-12-11 13:17:08 -0800 | [diff] [blame] | 473 | */ |
Fred Quintana | d5e4fdc | 2010-03-30 15:16:21 -0700 | [diff] [blame] | 474 | public void onSyncCanceled() { |
| 475 | final SyncThread syncThread; |
| 476 | synchronized (mSyncThreadLock) { |
Fred Quintana | 0c4d04a | 2010-11-03 17:02:55 -0700 | [diff] [blame] | 477 | syncThread = mSyncThreads.get(null); |
Fred Quintana | d5e4fdc | 2010-03-30 15:16:21 -0700 | [diff] [blame] | 478 | } |
| 479 | if (syncThread != null) { |
| 480 | syncThread.interrupt(); |
| 481 | } |
Fred Quintana | 274dc9d | 2009-12-11 13:17:08 -0800 | [diff] [blame] | 482 | } |
Fred Quintana | 0c4d04a | 2010-11-03 17:02:55 -0700 | [diff] [blame] | 483 | |
| 484 | /** |
| 485 | * Indicates that a sync operation has been canceled. This will be invoked on a separate |
| 486 | * thread than the sync thread and so you must consider the multi-threaded implications |
| 487 | * of the work that you do in this method. |
| 488 | * <p> |
| 489 | * This will only be invoked when the SyncAdapter indicates that it does support |
| 490 | * parallel syncs. |
| 491 | * @param thread the Thread of the sync that is to be canceled. |
| 492 | */ |
| 493 | public void onSyncCanceled(Thread thread) { |
| 494 | thread.interrupt(); |
| 495 | } |
Fred Quintana | f038004 | 2009-10-06 17:05:58 -0700 | [diff] [blame] | 496 | } |