Merge "Don't unbind from the sync adapter before cancelSync()." into oc-dr1-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
906d8f57bf
@@ -17,11 +17,12 @@
|
||||
package android.content;
|
||||
|
||||
import android.accounts.Account;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.IBinder;
|
||||
import android.os.Process;
|
||||
import android.os.RemoteException;
|
||||
import android.os.Trace;
|
||||
import android.util.Log;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
@@ -95,6 +96,8 @@ import java.util.concurrent.atomic.AtomicInteger;
|
||||
* </ul>
|
||||
*/
|
||||
public abstract class AbstractThreadedSyncAdapter {
|
||||
private static final String TAG = "SyncAdapter";
|
||||
|
||||
/**
|
||||
* Kernel event log tag. Also listed in data/etc/event-log-tags.
|
||||
* @deprecated Private constant. May go away in the next release.
|
||||
@@ -102,6 +105,8 @@ public abstract class AbstractThreadedSyncAdapter {
|
||||
@Deprecated
|
||||
public static final int LOG_SYNC_DETAILS = 2743;
|
||||
|
||||
private static final boolean ENABLE_LOG = Build.IS_DEBUGGABLE && Log.isLoggable(TAG, Log.DEBUG);
|
||||
|
||||
private final Context mContext;
|
||||
private final AtomicInteger mNumSyncStarts;
|
||||
private final ISyncAdapterImpl mISyncAdapterImpl;
|
||||
@@ -163,71 +168,104 @@ public abstract class AbstractThreadedSyncAdapter {
|
||||
@Override
|
||||
public void startSync(ISyncContext syncContext, String authority, Account account,
|
||||
Bundle extras) {
|
||||
final SyncContext syncContextClient = new SyncContext(syncContext);
|
||||
|
||||
boolean alreadyInProgress;
|
||||
// synchronize to make sure that mSyncThreads doesn't change between when we
|
||||
// check it and when we use it
|
||||
final Account threadsKey = toSyncKey(account);
|
||||
synchronized (mSyncThreadLock) {
|
||||
if (!mSyncThreads.containsKey(threadsKey)) {
|
||||
if (mAutoInitialize
|
||||
&& extras != null
|
||||
&& extras.getBoolean(ContentResolver.SYNC_EXTRAS_INITIALIZE, false)) {
|
||||
try {
|
||||
if (ContentResolver.getIsSyncable(account, authority) < 0) {
|
||||
ContentResolver.setIsSyncable(account, authority, 1);
|
||||
}
|
||||
} finally {
|
||||
syncContextClient.onFinished(new SyncResult());
|
||||
}
|
||||
return;
|
||||
}
|
||||
SyncThread syncThread = new SyncThread(
|
||||
"SyncAdapterThread-" + mNumSyncStarts.incrementAndGet(),
|
||||
syncContextClient, authority, account, extras);
|
||||
mSyncThreads.put(threadsKey, syncThread);
|
||||
syncThread.start();
|
||||
alreadyInProgress = false;
|
||||
} else {
|
||||
alreadyInProgress = true;
|
||||
if (ENABLE_LOG) {
|
||||
if (extras != null) {
|
||||
extras.size(); // Unparcel so its toString() will show the contents.
|
||||
}
|
||||
Log.d(TAG, "startSync() start " + authority + " " + account + " " + extras);
|
||||
}
|
||||
try {
|
||||
final SyncContext syncContextClient = new SyncContext(syncContext);
|
||||
|
||||
// do this outside since we don't want to call back into the syncContext while
|
||||
// holding the synchronization lock
|
||||
if (alreadyInProgress) {
|
||||
syncContextClient.onFinished(SyncResult.ALREADY_IN_PROGRESS);
|
||||
boolean alreadyInProgress;
|
||||
// synchronize to make sure that mSyncThreads doesn't change between when we
|
||||
// check it and when we use it
|
||||
final Account threadsKey = toSyncKey(account);
|
||||
synchronized (mSyncThreadLock) {
|
||||
if (!mSyncThreads.containsKey(threadsKey)) {
|
||||
if (mAutoInitialize
|
||||
&& extras != null
|
||||
&& extras.getBoolean(
|
||||
ContentResolver.SYNC_EXTRAS_INITIALIZE, false)) {
|
||||
try {
|
||||
if (ContentResolver.getIsSyncable(account, authority) < 0) {
|
||||
ContentResolver.setIsSyncable(account, authority, 1);
|
||||
}
|
||||
} finally {
|
||||
syncContextClient.onFinished(new SyncResult());
|
||||
}
|
||||
return;
|
||||
}
|
||||
SyncThread syncThread = new SyncThread(
|
||||
"SyncAdapterThread-" + mNumSyncStarts.incrementAndGet(),
|
||||
syncContextClient, authority, account, extras);
|
||||
mSyncThreads.put(threadsKey, syncThread);
|
||||
syncThread.start();
|
||||
alreadyInProgress = false;
|
||||
} else {
|
||||
if (ENABLE_LOG) {
|
||||
Log.d(TAG, " alreadyInProgress");
|
||||
}
|
||||
alreadyInProgress = true;
|
||||
}
|
||||
}
|
||||
|
||||
// do this outside since we don't want to call back into the syncContext while
|
||||
// holding the synchronization lock
|
||||
if (alreadyInProgress) {
|
||||
syncContextClient.onFinished(SyncResult.ALREADY_IN_PROGRESS);
|
||||
}
|
||||
} catch (RuntimeException | Error th) {
|
||||
if (ENABLE_LOG) {
|
||||
Log.d(TAG, "startSync() caught exception", th);
|
||||
}
|
||||
throw th;
|
||||
} finally {
|
||||
if (ENABLE_LOG) {
|
||||
Log.d(TAG, "startSync() finishing");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancelSync(ISyncContext syncContext) {
|
||||
// synchronize to make sure that mSyncThreads doesn't change between when we
|
||||
// check it and when we use it
|
||||
SyncThread info = null;
|
||||
synchronized (mSyncThreadLock) {
|
||||
for (SyncThread current : mSyncThreads.values()) {
|
||||
if (current.mSyncContext.getSyncContextBinder() == syncContext.asBinder()) {
|
||||
info = current;
|
||||
break;
|
||||
try {
|
||||
// synchronize to make sure that mSyncThreads doesn't change between when we
|
||||
// check it and when we use it
|
||||
SyncThread info = null;
|
||||
synchronized (mSyncThreadLock) {
|
||||
for (SyncThread current : mSyncThreads.values()) {
|
||||
if (current.mSyncContext.getSyncContextBinder() == syncContext.asBinder()) {
|
||||
info = current;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (info != null) {
|
||||
if (mAllowParallelSyncs) {
|
||||
onSyncCanceled(info);
|
||||
if (info != null) {
|
||||
if (ENABLE_LOG) {
|
||||
Log.d(TAG, "cancelSync() " + info.mAuthority + " " + info.mAccount);
|
||||
}
|
||||
if (mAllowParallelSyncs) {
|
||||
onSyncCanceled(info);
|
||||
} else {
|
||||
onSyncCanceled();
|
||||
}
|
||||
} else {
|
||||
onSyncCanceled();
|
||||
if (ENABLE_LOG) {
|
||||
Log.w(TAG, "cancelSync() unknown context");
|
||||
}
|
||||
}
|
||||
} catch (RuntimeException | Error th) {
|
||||
if (ENABLE_LOG) {
|
||||
Log.d(TAG, "cancelSync() caught exception", th);
|
||||
}
|
||||
throw th;
|
||||
} finally {
|
||||
if (ENABLE_LOG) {
|
||||
Log.d(TAG, "cancelSync() finishing");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void initialize(Account account, String authority) throws RemoteException {
|
||||
Bundle extras = new Bundle();
|
||||
extras.putBoolean(ContentResolver.SYNC_EXTRAS_INITIALIZE, true);
|
||||
startSync(null, authority, account, extras);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -256,6 +294,10 @@ public abstract class AbstractThreadedSyncAdapter {
|
||||
public void run() {
|
||||
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
|
||||
|
||||
if (ENABLE_LOG) {
|
||||
Log.d(TAG, "Thread started");
|
||||
}
|
||||
|
||||
// Trace this sync instance. Note, conceptually this should be in
|
||||
// SyncStorageEngine.insertStartSyncEvent(), but the trace functions require unique
|
||||
// threads in order to track overlapping operations, so we'll do it here for now.
|
||||
@@ -265,8 +307,15 @@ public abstract class AbstractThreadedSyncAdapter {
|
||||
ContentProviderClient provider = null;
|
||||
try {
|
||||
if (isCanceled()) {
|
||||
if (ENABLE_LOG) {
|
||||
Log.d(TAG, "Already canceled");
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (ENABLE_LOG) {
|
||||
Log.d(TAG, "Calling onPerformSync...");
|
||||
}
|
||||
|
||||
provider = mContext.getContentResolver().acquireContentProviderClient(mAuthority);
|
||||
if (provider != null) {
|
||||
AbstractThreadedSyncAdapter.this.onPerformSync(mAccount, mExtras,
|
||||
@@ -274,10 +323,23 @@ public abstract class AbstractThreadedSyncAdapter {
|
||||
} else {
|
||||
syncResult.databaseError = true;
|
||||
}
|
||||
|
||||
if (ENABLE_LOG) {
|
||||
Log.d(TAG, "onPerformSync done");
|
||||
}
|
||||
|
||||
} catch (SecurityException e) {
|
||||
if (ENABLE_LOG) {
|
||||
Log.d(TAG, "SecurityException", e);
|
||||
}
|
||||
AbstractThreadedSyncAdapter.this.onSecurityException(mAccount, mExtras,
|
||||
mAuthority, syncResult);
|
||||
syncResult.databaseError = true;
|
||||
} catch (RuntimeException | Error th) {
|
||||
if (ENABLE_LOG) {
|
||||
Log.d(TAG, "caught exception", th);
|
||||
}
|
||||
throw th;
|
||||
} finally {
|
||||
Trace.traceEnd(Trace.TRACE_TAG_SYNC_MANAGER);
|
||||
|
||||
@@ -292,6 +354,10 @@ public abstract class AbstractThreadedSyncAdapter {
|
||||
synchronized (mSyncThreadLock) {
|
||||
mSyncThreads.remove(mThreadsKey);
|
||||
}
|
||||
|
||||
if (ENABLE_LOG) {
|
||||
Log.d(TAG, "Thread finished");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -44,12 +44,4 @@ oneway interface ISyncAdapter {
|
||||
* @param syncContext the ISyncContext that was passed to {@link #startSync}
|
||||
*/
|
||||
void cancelSync(ISyncContext syncContext);
|
||||
|
||||
/**
|
||||
* Initialize the SyncAdapter for this account and authority.
|
||||
*
|
||||
* @param account the account that should be synced
|
||||
* @param authority the authority that should be synced
|
||||
*/
|
||||
void initialize(in Account account, String authority);
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ import android.os.Environment;
|
||||
import android.os.FileUtils;
|
||||
import android.os.SystemProperties;
|
||||
import android.text.format.DateUtils;
|
||||
import android.util.Log;
|
||||
import android.util.Slog;
|
||||
|
||||
import com.android.internal.annotations.GuardedBy;
|
||||
@@ -127,6 +128,8 @@ public class SyncLogger {
|
||||
@GuardedBy("mLock")
|
||||
private boolean mErrorShown;
|
||||
|
||||
private static final boolean DO_LOGCAT = Log.isLoggable(TAG, Log.DEBUG);
|
||||
|
||||
RotatingFileLogger() {
|
||||
mLogPath = new File(Environment.getDataSystemDirectory(), "syncmanager-log");
|
||||
}
|
||||
@@ -158,6 +161,8 @@ public class SyncLogger {
|
||||
mStringBuilder.append(android.os.Process.myTid());
|
||||
mStringBuilder.append(' ');
|
||||
|
||||
final int messageStart = mStringBuilder.length();
|
||||
|
||||
for (Object o : message) {
|
||||
mStringBuilder.append(o);
|
||||
}
|
||||
@@ -166,6 +171,11 @@ public class SyncLogger {
|
||||
try {
|
||||
mLogWriter.append(mStringBuilder);
|
||||
mLogWriter.flush();
|
||||
|
||||
// Also write on logcat.
|
||||
if (DO_LOGCAT) {
|
||||
Log.d(TAG, mStringBuilder.substring(messageStart));
|
||||
}
|
||||
} catch (IOException e) {
|
||||
handleException("Failed to write log", e);
|
||||
}
|
||||
|
||||
@@ -1704,6 +1704,8 @@ public class SyncManager {
|
||||
// Include "this" in the message so that the handler can ignore it if this
|
||||
// ActiveSyncContext is no longer the mActiveSyncContext at message handling
|
||||
// time.
|
||||
mLogger.log("onFinished result=", result, " endpoint=",
|
||||
(mSyncOperation == null ? "null" : mSyncOperation.target));
|
||||
sendSyncFinishedOrCanceledMessage(this, result);
|
||||
}
|
||||
|
||||
@@ -1745,6 +1747,7 @@ public class SyncManager {
|
||||
Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND
|
||||
| Context.BIND_ALLOW_OOM_MANAGEMENT,
|
||||
new UserHandle(mSyncOperation.target.userId));
|
||||
mLogger.log("bindService() returned=", mBound, " for ", this);
|
||||
if (!bindResult) {
|
||||
mBound = false;
|
||||
} else {
|
||||
@@ -1767,6 +1770,7 @@ public class SyncManager {
|
||||
}
|
||||
if (mBound) {
|
||||
mBound = false;
|
||||
mLogger.log("unbindService for ", this);
|
||||
mContext.unbindService(this);
|
||||
try {
|
||||
mBatteryStats.noteSyncFinish(mEventName, mSyncAdapterUid);
|
||||
@@ -2729,9 +2733,14 @@ public class SyncManager {
|
||||
// outstanding
|
||||
try {
|
||||
if (currentSyncContext.mSyncAdapter != null) {
|
||||
mLogger.log("Calling cancelSync for SERVICE_DISCONNECTED ",
|
||||
currentSyncContext,
|
||||
" adapter=", currentSyncContext.mSyncAdapter);
|
||||
currentSyncContext.mSyncAdapter.cancelSync(currentSyncContext);
|
||||
mLogger.log("Canceled");
|
||||
}
|
||||
} catch (RemoteException e) {
|
||||
mLogger.log("RemoteException ", Log.getStackTraceString(e));
|
||||
// We don't need to retry this in this case.
|
||||
}
|
||||
|
||||
@@ -3217,14 +3226,15 @@ public class SyncManager {
|
||||
mLogger.log("Sync start: account=" + syncOperation.target.account,
|
||||
" authority=", syncOperation.target.provider,
|
||||
" reason=", SyncOperation.reasonToString(null, syncOperation.reason),
|
||||
" extras=", SyncOperation.extrasToString(syncOperation.extras));
|
||||
" extras=", SyncOperation.extrasToString(syncOperation.extras),
|
||||
" adapter=", activeSyncContext.mSyncAdapter);
|
||||
|
||||
activeSyncContext.mSyncAdapter = ISyncAdapter.Stub.asInterface(syncAdapter);
|
||||
activeSyncContext.mSyncAdapter
|
||||
.startSync(activeSyncContext, syncOperation.target.provider,
|
||||
syncOperation.target.account, syncOperation.extras);
|
||||
|
||||
mLogger.log("Sync finish");
|
||||
mLogger.log("Sync is running now...");
|
||||
} catch (RemoteException remoteExc) {
|
||||
mLogger.log("Sync failed with RemoteException: ", remoteExc.toString());
|
||||
Log.d(TAG, "maybeStartNextSync: caught a RemoteException, rescheduling", remoteExc);
|
||||
@@ -3297,7 +3307,6 @@ public class SyncManager {
|
||||
activeSyncContext.mSyncAdapter.asBinder().unlinkToDeath(activeSyncContext, 0);
|
||||
activeSyncContext.mIsLinkedToDeath = false;
|
||||
}
|
||||
closeActiveSyncContext(activeSyncContext);
|
||||
final long elapsedTime = SystemClock.elapsedRealtime() - activeSyncContext.mStartTime;
|
||||
String historyMessage;
|
||||
int downstreamActivity;
|
||||
@@ -3355,8 +3364,12 @@ public class SyncManager {
|
||||
}
|
||||
if (activeSyncContext.mSyncAdapter != null) {
|
||||
try {
|
||||
mLogger.log("Calling cancelSync for runSyncFinishedOrCanceled ",
|
||||
activeSyncContext, " adapter=", activeSyncContext.mSyncAdapter);
|
||||
activeSyncContext.mSyncAdapter.cancelSync(activeSyncContext);
|
||||
mLogger.log("Canceled");
|
||||
} catch (RemoteException e) {
|
||||
mLogger.log("RemoteException ", Log.getStackTraceString(e));
|
||||
// we don't need to retry this in this case
|
||||
}
|
||||
}
|
||||
@@ -3365,6 +3378,9 @@ public class SyncManager {
|
||||
upstreamActivity = 0;
|
||||
}
|
||||
|
||||
// Close and unbind the service. Don't use activeSyncContext.mSyncAdapter after this.
|
||||
closeActiveSyncContext(activeSyncContext);
|
||||
|
||||
stopSyncEvent(activeSyncContext.mHistoryRowId, syncOperation, historyMessage,
|
||||
upstreamActivity, downstreamActivity, elapsedTime);
|
||||
// Check for full-resync and schedule it after closing off the last sync.
|
||||
|
||||
Reference in New Issue
Block a user