moved the hidden sync helpers out of the framework
diff --git a/api/current.xml b/api/current.xml
index 1ab1f5e..c2e84be 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -28137,6 +28137,19 @@
 <parameter name="syncResult" type="android.content.SyncResult">
 </parameter>
 </method>
+<method name="onSyncCanceled"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="thread" type="java.lang.Thread">
+</parameter>
+</method>
 <field name="LOG_SYNC_DETAILS"
  type="int"
  transient="false"
diff --git a/core/java/android/content/AbstractSyncableContentProvider.java b/core/java/android/content/AbstractSyncableContentProvider.java
deleted file mode 100644
index 5903c83..0000000
--- a/core/java/android/content/AbstractSyncableContentProvider.java
+++ /dev/null
@@ -1,758 +0,0 @@
-package android.content;
-
-import android.database.sqlite.SQLiteOpenHelper;
-import android.database.sqlite.SQLiteDatabase;
-import android.database.Cursor;
-import android.net.Uri;
-import android.accounts.OnAccountsUpdateListener;
-import android.accounts.Account;
-import android.accounts.AccountManager;
-import android.provider.SyncConstValue;
-import android.util.Config;
-import android.util.Log;
-import android.os.Bundle;
-import android.text.TextUtils;
-
-import java.util.Collections;
-import java.util.Map;
-import java.util.Vector;
-import java.util.ArrayList;
-import java.util.Set;
-import java.util.HashSet;
-
-import com.google.android.collect.Maps;
-
-/**
- * A specialization of the ContentProvider that centralizes functionality
- * used by ContentProviders that are syncable. It also wraps calls to the ContentProvider
- * inside of database transactions.
- *
- * @hide
- */
-public abstract class AbstractSyncableContentProvider extends SyncableContentProvider {
-    private static final String TAG = "SyncableContentProvider";
-    protected SQLiteOpenHelper mOpenHelper;
-    protected SQLiteDatabase mDb;
-    private final String mDatabaseName;
-    private final int mDatabaseVersion;
-    private final Uri mContentUri;
-
-    /** the account set in the last call to onSyncStart() */
-    private Account mSyncingAccount;
-
-    private SyncStateContentProviderHelper mSyncState = null;
-
-    private static final String[] sAccountProjection =
-            new String[] {SyncConstValue._SYNC_ACCOUNT, SyncConstValue._SYNC_ACCOUNT_TYPE};
-
-    private boolean mIsTemporary;
-
-    private AbstractTableMerger mCurrentMerger = null;
-    private boolean mIsMergeCancelled = false;
-
-    private static final String SYNC_ACCOUNT_WHERE_CLAUSE =
-            SyncConstValue._SYNC_ACCOUNT + "=? AND " + SyncConstValue._SYNC_ACCOUNT_TYPE + "=?";
-
-    protected boolean isTemporary() {
-        return mIsTemporary;
-    }
-
-    private final ThreadLocal<Boolean> mApplyingBatch = new ThreadLocal<Boolean>();
-    private final ThreadLocal<Set<Uri>> mPendingBatchNotifications = new ThreadLocal<Set<Uri>>();
-
-    /**
-     * Indicates whether or not this ContentProvider contains a full
-     * set of data or just diffs. This knowledge comes in handy when
-     * determining how to incorporate the contents of a temporary
-     * provider into a real provider.
-     */
-    private boolean mContainsDiffs;
-
-    /**
-     * Initializes the AbstractSyncableContentProvider
-     * @param dbName the filename of the database
-     * @param dbVersion the current version of the database schema
-     * @param contentUri The base Uri of the syncable content in this provider
-     */
-    public AbstractSyncableContentProvider(String dbName, int dbVersion, Uri contentUri) {
-        super();
-
-        mDatabaseName = dbName;
-        mDatabaseVersion = dbVersion;
-        mContentUri = contentUri;
-        mIsTemporary = false;
-        setContainsDiffs(false);
-        if (Config.LOGV) {
-            Log.v(TAG, "created SyncableContentProvider " + this);
-        }
-    }
-
-    /**
-     * Close resources that must be closed. You must call this to properly release
-     * the resources used by the AbstractSyncableContentProvider.
-     */
-    public void close() {
-        if (mOpenHelper != null) {
-            mOpenHelper.close();  // OK to call .close() repeatedly.
-        }
-    }
-
-    /**
-     * Override to create your schema and do anything else you need to do with a new database.
-     * This is run inside a transaction (so you don't need to use one).
-     * This method may not use getDatabase(), or call content provider methods, it must only
-     * use the database handle passed to it.
-     */
-    protected void bootstrapDatabase(SQLiteDatabase db) {}
-
-    /**
-     * Override to upgrade your database from an old version to the version you specified.
-     * Don't set the DB version; this will automatically be done after the method returns.
-     * This method may not use getDatabase(), or call content provider methods, it must only
-     * use the database handle passed to it.
-     *
-     * @param oldVersion version of the existing database
-     * @param newVersion current version to upgrade to
-     * @return true if the upgrade was lossless, false if it was lossy
-     */
-    protected abstract boolean upgradeDatabase(SQLiteDatabase db, int oldVersion, int newVersion);
-
-    /**
-     * Override to do anything (like cleanups or checks) you need to do after opening a database.
-     * Does nothing by default.  This is run inside a transaction (so you don't need to use one).
-     * This method may not use getDatabase(), or call content provider methods, it must only
-     * use the database handle passed to it.
-     */
-    protected void onDatabaseOpened(SQLiteDatabase db) {}
-
-    private class DatabaseHelper extends SQLiteOpenHelper {
-        DatabaseHelper(Context context, String name) {
-            // Note: context and name may be null for temp providers
-            super(context, name, null, mDatabaseVersion);
-        }
-
-        @Override
-        public void onCreate(SQLiteDatabase db) {
-            bootstrapDatabase(db);
-            mSyncState.createDatabase(db);
-            if (!isTemporary()) {
-		ContentResolver.requestSync(null /* all accounts */,
-                    mContentUri.getAuthority(), new Bundle());
-	    }
-        }
-
-        @Override
-        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
-            if (!upgradeDatabase(db, oldVersion, newVersion)) {
-                mSyncState.discardSyncData(db, null /* all accounts */);
-                ContentResolver.requestSync(null /* all accounts */,
-                        mContentUri.getAuthority(), new Bundle());
-            }
-        }
-
-        @Override
-        public void onOpen(SQLiteDatabase db) {
-            onDatabaseOpened(db);
-            mSyncState.onDatabaseOpened(db);
-        }
-    }
-
-    @Override
-    public boolean onCreate() {
-        if (isTemporary()) throw new IllegalStateException("onCreate() called for temp provider");
-        mOpenHelper = new AbstractSyncableContentProvider.DatabaseHelper(getContext(),
-                mDatabaseName);
-        mSyncState = new SyncStateContentProviderHelper(mOpenHelper);
-        AccountManager.get(getContext()).addOnAccountsUpdatedListener(
-                new OnAccountsUpdateListener() {
-                    public void onAccountsUpdated(Account[] accounts) {
-                        // Some providers override onAccountsChanged(); give them a database to
-                        // work with.
-                        mDb = mOpenHelper.getWritableDatabase();
-                        // Only call onAccountsChanged on GAIA accounts; otherwise, the contacts and
-                        // calendar providers will choke as they try to sync unknown accounts with
-                        // AbstractGDataSyncAdapter, which will put acore into a crash loop
-                        ArrayList<Account> gaiaAccounts = new ArrayList<Account>();
-                        for (Account acct: accounts) {
-                            if (acct.type.equals("com.google")) {
-                                gaiaAccounts.add(acct);
-                            }
-                        }
-                        accounts = new Account[gaiaAccounts.size()];
-                        int i = 0;
-                        for (Account acct: gaiaAccounts) {
-                            accounts[i++] = acct;
-                        }
-                        onAccountsChanged(accounts);
-                        TempProviderSyncAdapter syncAdapter = getTempProviderSyncAdapter();
-                        if (syncAdapter != null) {
-                            syncAdapter.onAccountsChanged(accounts);
-                        }
-                    }
-                }, null /* handler */, true /* updateImmediately */);
-
-        return true;
-    }
-    /**
-     * Get a non-persistent instance of this content provider.
-     * You must call {@link #close} on the returned
-     * SyncableContentProvider when you are done with it.
-     *
-     * @return a non-persistent content provider with the same layout as this
-     * provider.
-     */
-    public AbstractSyncableContentProvider getTemporaryInstance() {
-        AbstractSyncableContentProvider temp;
-        try {
-            temp = getClass().newInstance();
-        } catch (InstantiationException e) {
-            throw new RuntimeException("unable to instantiate class, "
-                    + "this should never happen", e);
-        } catch (IllegalAccessException e) {
-            throw new RuntimeException(
-                    "IllegalAccess while instantiating class, "
-                            + "this should never happen", e);
-        }
-
-        // Note: onCreate() isn't run for the temp provider, and it has no Context.
-        temp.mIsTemporary = true;
-        temp.setContainsDiffs(true);
-        temp.mOpenHelper = temp.new DatabaseHelper(null, null);
-        temp.mSyncState = new SyncStateContentProviderHelper(temp.mOpenHelper);
-        if (!isTemporary()) {
-            mSyncState.copySyncState(
-                    mOpenHelper.getReadableDatabase(),
-                    temp.mOpenHelper.getWritableDatabase(),
-                    getSyncingAccount());
-        }
-        return temp;
-    }
-
-    public SQLiteDatabase getDatabase() {
-       if (mDb == null) mDb = mOpenHelper.getWritableDatabase();
-       return mDb;
-    }
-
-    public boolean getContainsDiffs() {
-        return mContainsDiffs;
-    }
-
-    public void setContainsDiffs(boolean containsDiffs) {
-        if (containsDiffs && !isTemporary()) {
-            throw new IllegalStateException(
-                    "only a temporary provider can contain diffs");
-        }
-        mContainsDiffs = containsDiffs;
-    }
-
-    /**
-     * Each subclass of this class should define a subclass of {@link
-     * android.content.AbstractTableMerger} for each table they wish to merge.  It
-     * should then override this method and return one instance of
-     * each merger, in sequence.  Their {@link
-     * android.content.AbstractTableMerger#merge merge} methods will be called, one at a
-     * time, in the order supplied.
-     *
-     * <p>The default implementation returns an empty list, so that no
-     * merging will occur.
-     * @return A sequence of subclasses of {@link
-     * android.content.AbstractTableMerger}, one for each table that should be merged.
-     */
-    protected Iterable<? extends AbstractTableMerger> getMergers() {
-        return Collections.emptyList();
-    }
-
-    @Override
-    public final int update(final Uri url, final ContentValues values,
-            final String selection, final String[] selectionArgs) {
-        mDb = mOpenHelper.getWritableDatabase();
-        final boolean notApplyingBatch = !applyingBatch();
-        if (notApplyingBatch) {
-            mDb.beginTransaction();
-        }
-        try {
-            if (isTemporary() && mSyncState.matches(url)) {
-                int numRows = mSyncState.asContentProvider().update(
-                        url, values, selection, selectionArgs);
-                if (notApplyingBatch) {
-                    mDb.setTransactionSuccessful();
-                }
-                return numRows;
-            }
-
-            int result = updateInternal(url, values, selection, selectionArgs);
-            if (notApplyingBatch) {
-                mDb.setTransactionSuccessful();
-            }
-            if (!isTemporary() && result > 0) {
-                if (notApplyingBatch) {
-                    getContext().getContentResolver().notifyChange(url, null /* observer */,
-                            changeRequiresLocalSync(url));
-                } else {
-                    mPendingBatchNotifications.get().add(url);
-                }
-            }
-            return result;
-        } finally {
-            if (notApplyingBatch) {
-                mDb.endTransaction();
-            }
-        }
-    }
-
-    @Override
-    public final int delete(final Uri url, final String selection,
-            final String[] selectionArgs) {
-        mDb = mOpenHelper.getWritableDatabase();
-        final boolean notApplyingBatch = !applyingBatch();
-        if (notApplyingBatch) {
-            mDb.beginTransaction();
-        }
-        try {
-            if (isTemporary() && mSyncState.matches(url)) {
-                int numRows = mSyncState.asContentProvider().delete(url, selection, selectionArgs);
-                if (notApplyingBatch) {
-                    mDb.setTransactionSuccessful();
-                }
-                return numRows;
-            }
-            int result = deleteInternal(url, selection, selectionArgs);
-            if (notApplyingBatch) {
-                mDb.setTransactionSuccessful();
-            }
-            if (!isTemporary() && result > 0) {
-                if (notApplyingBatch) {
-                    getContext().getContentResolver().notifyChange(url, null /* observer */,
-                            changeRequiresLocalSync(url));
-                } else {
-                    mPendingBatchNotifications.get().add(url);
-                }
-            }
-            return result;
-        } finally {
-            if (notApplyingBatch) {
-                mDb.endTransaction();
-            }
-        }
-    }
-
-    private boolean applyingBatch() {
-        return mApplyingBatch.get() != null && mApplyingBatch.get();
-    }
-
-    @Override
-    public final Uri insert(final Uri url, final ContentValues values) {
-        mDb = mOpenHelper.getWritableDatabase();
-        final boolean notApplyingBatch = !applyingBatch();
-        if (notApplyingBatch) {
-            mDb.beginTransaction();
-        }
-        try {
-            if (isTemporary() && mSyncState.matches(url)) {
-                Uri result = mSyncState.asContentProvider().insert(url, values);
-                if (notApplyingBatch) {
-                    mDb.setTransactionSuccessful();
-                }
-                return result;
-            }
-            Uri result = insertInternal(url, values);
-            if (notApplyingBatch) {
-                mDb.setTransactionSuccessful();
-            }
-            if (!isTemporary() && result != null) {
-                if (notApplyingBatch) {
-                    getContext().getContentResolver().notifyChange(url, null /* observer */,
-                            changeRequiresLocalSync(url));
-                } else {
-                    mPendingBatchNotifications.get().add(url);
-                }
-            }
-            return result;
-        } finally {
-            if (notApplyingBatch) {
-                mDb.endTransaction();
-            }
-        }
-    }
-
-    @Override
-    public final int bulkInsert(final Uri uri, final ContentValues[] values) {
-        int size = values.length;
-        int completed = 0;
-        final boolean isSyncStateUri = mSyncState.matches(uri);
-        mDb = mOpenHelper.getWritableDatabase();
-        mDb.beginTransaction();
-        try {
-            for (int i = 0; i < size; i++) {
-                Uri result;
-                if (isTemporary() && isSyncStateUri) {
-                    result = mSyncState.asContentProvider().insert(uri, values[i]);
-                } else {
-                    result = insertInternal(uri, values[i]);
-                    mDb.yieldIfContended();
-                }
-                if (result != null) {
-                    completed++;
-                }
-            }
-            mDb.setTransactionSuccessful();
-        } finally {
-            mDb.endTransaction();
-        }
-        if (!isTemporary() && completed == size) {
-            getContext().getContentResolver().notifyChange(uri, null /* observer */,
-                    changeRequiresLocalSync(uri));
-        }
-        return completed;
-    }
-
-    /**
-     * <p>
-     * Start batch transaction. {@link #endTransaction} MUST be called after 
-     * calling this method. Those methods should be used like this:
-     * </p>
-     *
-     * <pre class="prettyprint">
-     * boolean successful = false;
-     * beginBatch()
-     * try {
-     *     // Do something related to mDb
-     *     successful = true;
-     *     return ret;
-     * } finally {
-     *     endBatch(successful);
-     * }
-     * </pre>
-     *
-     * @hide This method should be used only when {@link ContentProvider#applyBatch} is not enough and must be
-     * used with {@link #endBatch}.
-     * e.g. If returned value has to be used during one transaction, this method might be useful.
-     */
-    public final void beginBatch() {
-        // initialize if this is the first time this thread has applied a batch
-        if (mApplyingBatch.get() == null) {
-            mApplyingBatch.set(false);
-            mPendingBatchNotifications.set(new HashSet<Uri>());
-        }
-
-        if (applyingBatch()) {
-            throw new IllegalStateException(
-                    "applyBatch is not reentrant but mApplyingBatch is already set");
-        }
-        SQLiteDatabase db = getDatabase();
-        db.beginTransaction();
-        boolean successful = false;
-        try {
-            mApplyingBatch.set(true);
-            successful = true;
-        } finally {
-            if (!successful) {
-                // Something unexpected happened. We must call endTransaction() at least.
-                db.endTransaction();
-            }
-        }
-    }
-
-    /**
-     * <p>
-     * Finish batch transaction. If "successful" is true, try to call
-     * mDb.setTransactionSuccessful() before calling mDb.endTransaction().
-     * This method MUST be used with {@link #beginBatch()}.
-     * </p>
-     *
-     * @hide This method must be used with {@link #beginTransaction}
-     */
-    public final void endBatch(boolean successful) {
-        try {
-            if (successful) {
-                // setTransactionSuccessful() must be called just once during opening the
-                // transaction.
-                mDb.setTransactionSuccessful();
-            }
-        } finally {
-            mApplyingBatch.set(false);
-            getDatabase().endTransaction();
-            for (Uri url : mPendingBatchNotifications.get()) {
-                getContext().getContentResolver().notifyChange(url, null /* observer */,
-                        changeRequiresLocalSync(url));
-            }
-        }
-    }
-
-    public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations)
-            throws OperationApplicationException {
-        boolean successful = false;
-        beginBatch();
-        try {
-            ContentProviderResult[] results = super.applyBatch(operations);
-            successful = true;
-            return results;
-        } finally {
-            endBatch(successful);
-        }
-    }
-
-    /**
-     * Check if changes to this URI can be syncable changes.
-     * @param uri the URI of the resource that was changed
-     * @return true if changes to this URI can be syncable changes, false otherwise
-     */
-    public boolean changeRequiresLocalSync(Uri uri) {
-        return true;
-    }
-
-    @Override
-    public final Cursor query(final Uri url, final String[] projection,
-            final String selection, final String[] selectionArgs,
-            final String sortOrder) {
-        mDb = mOpenHelper.getReadableDatabase();
-        if (isTemporary() && mSyncState.matches(url)) {
-            return mSyncState.asContentProvider().query(
-                    url, projection, selection,  selectionArgs, sortOrder);
-        }
-        return queryInternal(url, projection, selection, selectionArgs, sortOrder);
-    }
-
-    /**
-     * Called right before a sync is started.
-     *
-     * @param context the sync context for the operation
-     * @param account
-     */
-    public void onSyncStart(SyncContext context, Account account) {
-        if (account == null) {
-            throw new IllegalArgumentException("you passed in an empty account");
-        }
-        mSyncingAccount = account;
-    }
-
-    /**
-     * Called right after a sync is completed
-     *
-     * @param context the sync context for the operation
-     * @param success true if the sync succeeded, false if an error occurred
-     */
-    public void onSyncStop(SyncContext context, boolean success) {
-    }
-
-    /**
-     * The account of the most recent call to onSyncStart()
-     * @return the account
-     */
-    public Account getSyncingAccount() {
-        return mSyncingAccount;
-    }
-
-    /**
-     * Merge diffs from a sync source with this content provider.
-     *
-     * @param context the SyncContext within which this merge is taking place
-     * @param diffs A temporary content provider containing diffs from a sync
-     *   source.
-     * @param result a MergeResult that contains information about the merge, including
-     *   a temporary content provider with the same layout as this provider containing
-     * @param syncResult
-     */
-    public void merge(SyncContext context, SyncableContentProvider diffs,
-            TempProviderSyncResult result, SyncResult syncResult) {
-        SQLiteDatabase db = mOpenHelper.getWritableDatabase();
-        db.beginTransaction();
-        try {
-            synchronized(this) {
-                mIsMergeCancelled = false;
-            }
-            Iterable<? extends AbstractTableMerger> mergers = getMergers();
-            try {
-                for (AbstractTableMerger merger : mergers) {
-                    synchronized(this) {
-                        if (mIsMergeCancelled) break;
-                        mCurrentMerger = merger;
-                    }
-                    merger.merge(context, getSyncingAccount(), diffs, result, syncResult, this);
-                }
-                if (mIsMergeCancelled) return;
-                if (diffs != null) {
-                    mSyncState.copySyncState(
-                        ((AbstractSyncableContentProvider)diffs).mOpenHelper.getReadableDatabase(),
-                        mOpenHelper.getWritableDatabase(),
-                        getSyncingAccount());
-                }
-            } finally {
-                synchronized (this) {
-                    mCurrentMerger = null;
-                }
-            }
-            db.setTransactionSuccessful();
-        } finally {
-            db.endTransaction();
-        }
-    }
-
-
-    /**
-     * Invoked when the active sync has been canceled. Sets the sync state of this provider and
-     * its merger to canceled.
-     */
-    public void onSyncCanceled() {
-        synchronized (this) {
-            mIsMergeCancelled = true;
-            if (mCurrentMerger != null) {
-                mCurrentMerger.onMergeCancelled();
-            }
-        }
-    }
-
-
-    public boolean isMergeCancelled() {
-        return mIsMergeCancelled;
-    }
-
-    /**
-     * Subclasses should override this instead of update(). See update()
-     * for details.
-     *
-     * <p> This method is called within a acquireDbLock()/releaseDbLock() block,
-     * which means a database transaction will be active during the call;
-     */
-    protected abstract int updateInternal(Uri url, ContentValues values,
-            String selection, String[] selectionArgs);
-
-    /**
-     * Subclasses should override this instead of delete(). See delete()
-     * for details.
-     *
-     * <p> This method is called within a acquireDbLock()/releaseDbLock() block,
-     * which means a database transaction will be active during the call;
-     */
-    protected abstract int deleteInternal(Uri url, String selection, String[] selectionArgs);
-
-    /**
-     * Subclasses should override this instead of insert(). See insert()
-     * for details.
-     *
-     * <p> This method is called within a acquireDbLock()/releaseDbLock() block,
-     * which means a database transaction will be active during the call;
-     */
-    protected abstract Uri insertInternal(Uri url, ContentValues values);
-
-    /**
-     * Subclasses should override this instead of query(). See query()
-     * for details.
-     *
-     * <p> This method is *not* called within a acquireDbLock()/releaseDbLock()
-     * block for performance reasons. If an implementation needs atomic access
-     * to the database the lock can be acquired then.
-     */
-    protected abstract Cursor queryInternal(Uri url, String[] projection,
-            String selection, String[] selectionArgs, String sortOrder);
-
-    /**
-     * Make sure that there are no entries for accounts that no longer exist
-     * @param accountsArray the array of currently-existing accounts
-     */
-    protected void onAccountsChanged(Account[] accountsArray) {
-        Map<Account, Boolean> accounts = Maps.newHashMap();
-        for (Account account : accountsArray) {
-            accounts.put(account, false);
-        }
-
-        SQLiteDatabase db = mOpenHelper.getWritableDatabase();
-        Map<String, String> tableMap = db.getSyncedTables();
-        Vector<String> tables = new Vector<String>();
-        tables.addAll(tableMap.keySet());
-        tables.addAll(tableMap.values());
-
-        db.beginTransaction();
-        try {
-            mSyncState.onAccountsChanged(accountsArray);
-            for (String table : tables) {
-                deleteRowsForRemovedAccounts(accounts, table);
-            }
-            db.setTransactionSuccessful();
-        } finally {
-            db.endTransaction();
-        }
-    }
-
-    /**
-     * A helper method to delete all rows whose account is not in the accounts
-     * map. The accountColumnName is the name of the column that is expected
-     * to hold the account. If a row has an empty account it is never deleted.
-     *
-     * @param accounts a map of existing accounts
-     * @param table the table to delete from
-     */
-    protected void deleteRowsForRemovedAccounts(Map<Account, Boolean> accounts, String table) {
-        SQLiteDatabase db = mOpenHelper.getWritableDatabase();
-        Cursor c = db.query(table, sAccountProjection, null, null,
-                "_sync_account, _sync_account_type", null, null);
-        try {
-            while (c.moveToNext()) {
-                String accountName = c.getString(0);
-                String accountType = c.getString(1);
-                if (TextUtils.isEmpty(accountName)) {
-                    continue;
-                }
-                Account account = new Account(accountName, accountType);
-                if (!accounts.containsKey(account)) {
-                    int numDeleted;
-                    numDeleted = db.delete(table, "_sync_account=? AND _sync_account_type=?",
-                            new String[]{account.name, account.type});
-                    if (Config.LOGV) {
-                        Log.v(TAG, "deleted " + numDeleted
-                                + " records from table " + table
-                                + " for account " + account);
-                    }
-                }
-            }
-        } finally {
-            c.close();
-        }
-    }
-
-    /**
-     * Called when the sync system determines that this provider should no longer
-     * contain records for the specified account.
-     */
-    public void wipeAccount(Account account) {
-        SQLiteDatabase db = mOpenHelper.getWritableDatabase();
-        Map<String, String> tableMap = db.getSyncedTables();
-        ArrayList<String> tables = new ArrayList<String>();
-        tables.addAll(tableMap.keySet());
-        tables.addAll(tableMap.values());
-
-        db.beginTransaction();
-
-        try {
-            // remove the SyncState data
-            mSyncState.discardSyncData(db, account);
-
-            // remove the data in the synced tables
-            for (String table : tables) {
-                db.delete(table, SYNC_ACCOUNT_WHERE_CLAUSE,
-                        new String[]{account.name, account.type});
-            }
-            db.setTransactionSuccessful();
-        } finally {
-            db.endTransaction();
-        }
-    }
-
-    /**
-     * Retrieves the SyncData bytes for the given account. The byte array returned may be null.
-     */
-    public byte[] readSyncDataBytes(Account account) {
-        return mSyncState.readSyncDataBytes(mOpenHelper.getReadableDatabase(), account);
-    }
-
-    /**
-     * Sets the SyncData bytes for the given account. The byte array may be null.
-     */
-    public void writeSyncDataBytes(Account account, byte[] data) {
-        mSyncState.writeSyncDataBytes(mOpenHelper.getWritableDatabase(), account, data);
-    }
-
-    protected ContentProvider getSyncStateProvider() {
-        return mSyncState.asContentProvider();
-    }
-}
diff --git a/core/java/android/content/AbstractTableMerger.java b/core/java/android/content/AbstractTableMerger.java
deleted file mode 100644
index 9545fd7f..0000000
--- a/core/java/android/content/AbstractTableMerger.java
+++ /dev/null
@@ -1,599 +0,0 @@
-/*
- * Copyright (C) 2006 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.content;
-
-import android.database.Cursor;
-import android.database.DatabaseUtils;
-import android.database.sqlite.SQLiteDatabase;
-import android.net.Uri;
-import android.os.Debug;
-import android.provider.BaseColumns;
-import static android.provider.SyncConstValue.*;
-import android.text.TextUtils;
-import android.util.Log;
-import android.accounts.Account;
-
-/**
- * @hide
- */
-public abstract class AbstractTableMerger
-{
-    private ContentValues mValues;
-
-    protected SQLiteDatabase mDb;
-    protected String mTable;
-    protected Uri mTableURL;
-    protected String mDeletedTable;
-    protected Uri mDeletedTableURL;
-    static protected ContentValues mSyncMarkValues;
-    static private boolean TRACE;
-
-    static {
-        mSyncMarkValues = new ContentValues();
-        mSyncMarkValues.put(_SYNC_MARK, 1);
-        TRACE = false;
-    }
-
-    private static final String TAG = "AbstractTableMerger";
-    private static final String[] syncDirtyProjection =
-            new String[] {_SYNC_DIRTY, BaseColumns._ID, _SYNC_ID, _SYNC_VERSION};
-    private static final String[] syncIdAndVersionProjection =
-            new String[] {_SYNC_ID, _SYNC_VERSION};
-
-    private volatile boolean mIsMergeCancelled;
-
-    private static final String SELECT_MARKED = _SYNC_MARK + "> 0 and "
-            + _SYNC_ACCOUNT + "=? and " + _SYNC_ACCOUNT_TYPE + "=?";
-
-    private static final String SELECT_BY_SYNC_ID_AND_ACCOUNT =
-            _SYNC_ID +"=? and " + _SYNC_ACCOUNT + "=? and " + _SYNC_ACCOUNT_TYPE + "=?";
-    private static final String SELECT_BY_ID = BaseColumns._ID +"=?";
-
-    private static final String SELECT_UNSYNCED =
-            "(" + _SYNC_ACCOUNT + " IS NULL OR ("
-                + _SYNC_ACCOUNT + "=? and " + _SYNC_ACCOUNT_TYPE + "=?)) and "
-            + "(" + _SYNC_ID + " IS NULL OR (" + _SYNC_DIRTY + " > 0 and "
-                                              + _SYNC_VERSION + " IS NOT NULL))";
-
-    public AbstractTableMerger(SQLiteDatabase database,
-            String table, Uri tableURL, String deletedTable,
-            Uri deletedTableURL)
-    {
-        mDb = database;
-        mTable = table;
-        mTableURL = tableURL;
-        mDeletedTable = deletedTable;
-        mDeletedTableURL = deletedTableURL;
-        mValues = new ContentValues();
-    }
-
-    public abstract void insertRow(ContentProvider diffs,
-            Cursor diffsCursor);
-    public abstract void updateRow(long localPersonID,
-            ContentProvider diffs, Cursor diffsCursor);
-    public abstract void resolveRow(long localPersonID,
-            String syncID, ContentProvider diffs, Cursor diffsCursor);
-
-    /**
-     * This is called when it is determined that a row should be deleted from the
-     * ContentProvider. The localCursor is on a table from the local ContentProvider
-     * and its current position is of the row that should be deleted. The localCursor
-     * is only guaranteed to contain the BaseColumns.ID column so the implementation
-     * of deleteRow() must query the database directly if other columns are needed.
-     * <p>
-     * It is the responsibility of the implementation of this method to ensure that the cursor
-     * points to the next row when this method returns, either by calling Cursor.deleteRow() or
-     * Cursor.next().
-     *
-     * @param localCursor The Cursor into the local table, which points to the row that
-     *   is to be deleted.
-     */
-    public void deleteRow(Cursor localCursor) {
-        localCursor.deleteRow();
-    }
-
-    /**
-     * After {@link #merge} has completed, this method is called to send
-     * notifications to {@link android.database.ContentObserver}s of changes
-     * to the containing {@link ContentProvider}.  These notifications likely
-     * do not want to request a sync back to the network.
-     */
-    protected abstract void notifyChanges();
-
-    private static boolean findInCursor(Cursor cursor, int column, String id) {
-        while (!cursor.isAfterLast() && !cursor.isNull(column)) {
-            int comp = id.compareTo(cursor.getString(column));
-            if (comp > 0) {
-                cursor.moveToNext();
-                continue;
-            }
-            return comp == 0;
-        }
-        return false;
-    }
-
-    public void onMergeCancelled() {
-        mIsMergeCancelled = true;
-    }
-
-    /**
-     * Carry out a merge of the given diffs, and add the results to
-     * the given MergeResult.  If we are the first merge to find
-     * client-side diffs, we'll use the given ContentProvider to
-     * construct a temporary instance to hold them.
-     */
-    public void merge(final SyncContext context,
-            final Account account,
-            final SyncableContentProvider serverDiffs,
-            TempProviderSyncResult result,
-            SyncResult syncResult, SyncableContentProvider temporaryInstanceFactory) {
-        mIsMergeCancelled = false;
-        if (serverDiffs != null) {
-            if (!mDb.isDbLockedByCurrentThread()) {
-                throw new IllegalStateException("this must be called from within a DB transaction");
-            }
-            mergeServerDiffs(context, account, serverDiffs, syncResult);
-            notifyChanges();
-        }
-
-        if (result != null) {
-            findLocalChanges(result, temporaryInstanceFactory, account, syncResult);
-        }
-        if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "merge complete");
-    }
-
-    /**
-     * @hide this is public for testing purposes only
-     */
-    public void mergeServerDiffs(SyncContext context,
-            Account account, SyncableContentProvider serverDiffs, SyncResult syncResult) {
-        boolean diffsArePartial = serverDiffs.getContainsDiffs();
-        // mark the current rows so that we can distinguish these from new
-        // inserts that occur during the merge
-        mDb.update(mTable, mSyncMarkValues, null, null);
-        if (mDeletedTable != null) {
-            mDb.update(mDeletedTable, mSyncMarkValues, null, null);
-        }
-
-        Cursor localCursor = null;
-        Cursor deletedCursor = null;
-        Cursor diffsCursor = null;
-        try {
-            // load the local database entries, so we can merge them with the server
-            final String[] accountSelectionArgs = new String[]{account.name, account.type};
-            localCursor = mDb.query(mTable, syncDirtyProjection,
-                    SELECT_MARKED, accountSelectionArgs, null, null,
-                    mTable + "." + _SYNC_ID);
-            if (mDeletedTable != null) {
-                deletedCursor = mDb.query(mDeletedTable, syncIdAndVersionProjection,
-                        SELECT_MARKED, accountSelectionArgs, null, null,
-                        mDeletedTable + "." + _SYNC_ID);
-            } else {
-                deletedCursor =
-                        mDb.rawQuery("select 'a' as _sync_id, 'b' as _sync_version limit 0", null);
-            }
-
-            // Apply updates and insertions from the server
-            diffsCursor = serverDiffs.query(mTableURL,
-                    null, null, null, mTable + "." + _SYNC_ID);
-            int deletedSyncIDColumn = deletedCursor.getColumnIndexOrThrow(_SYNC_ID);
-            int deletedSyncVersionColumn = deletedCursor.getColumnIndexOrThrow(_SYNC_VERSION);
-            int serverSyncIDColumn = diffsCursor.getColumnIndexOrThrow(_SYNC_ID);
-            int serverSyncVersionColumn = diffsCursor.getColumnIndexOrThrow(_SYNC_VERSION);
-            int serverSyncLocalIdColumn = diffsCursor.getColumnIndexOrThrow(_SYNC_LOCAL_ID);
-
-            String lastSyncId = null;
-            int diffsCount = 0;
-            int localCount = 0;
-            localCursor.moveToFirst();
-            deletedCursor.moveToFirst();
-            while (diffsCursor.moveToNext()) {
-                if (mIsMergeCancelled) {
-                    return;
-                }
-                mDb.yieldIfContended();
-                String serverSyncId = diffsCursor.getString(serverSyncIDColumn);
-                String serverSyncVersion = diffsCursor.getString(serverSyncVersionColumn);
-                long localRowId = 0;
-                String localSyncVersion = null;
-
-                diffsCount++;
-                context.setStatusText("Processing " + diffsCount + "/"
-                        + diffsCursor.getCount());
-                if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "processing server entry " +
-                        diffsCount + ", " + serverSyncId);
-
-                if (TRACE) {
-                    if (diffsCount == 10) {
-                        Debug.startMethodTracing("atmtrace");
-                    }
-                    if (diffsCount == 20) {
-                        Debug.stopMethodTracing();
-                    }
-                }
-
-                boolean conflict = false;
-                boolean update = false;
-                boolean insert = false;
-
-                if (Log.isLoggable(TAG, Log.VERBOSE)) {
-                    Log.v(TAG, "found event with serverSyncID " + serverSyncId);
-                }
-                if (TextUtils.isEmpty(serverSyncId)) {
-                    if (Log.isLoggable(TAG, Log.VERBOSE)) {
-                        Log.e(TAG, "server entry doesn't have a serverSyncID");
-                    }
-                    continue;
-                }
-
-                // It is possible that the sync adapter wrote the same record multiple times,
-                // e.g. if the same record came via multiple feeds. If this happens just ignore
-                // the duplicate records.
-                if (serverSyncId.equals(lastSyncId)) {
-                    if (Log.isLoggable(TAG, Log.VERBOSE)) {
-                        Log.v(TAG, "skipping record with duplicate remote server id " + lastSyncId);
-                    }
-                    continue;
-                }
-                lastSyncId = serverSyncId;
-
-                String localSyncID = null;
-                boolean localSyncDirty = false;
-
-                while (!localCursor.isAfterLast()) {
-                    if (mIsMergeCancelled) {
-                        return;
-                    }
-                    localCount++;
-                    localSyncID = localCursor.getString(2);
-
-                    // If the local record doesn't have a _sync_id then
-                    // it is new. Ignore it for now, we will send an insert
-                    // the the server later.
-                    if (TextUtils.isEmpty(localSyncID)) {
-                        if (Log.isLoggable(TAG, Log.VERBOSE)) {
-                            Log.v(TAG, "local record " +
-                                    localCursor.getLong(1) +
-                                    " has no _sync_id, ignoring");
-                        }
-                        localCursor.moveToNext();
-                        localSyncID = null;
-                        continue;
-                    }
-
-                    int comp = serverSyncId.compareTo(localSyncID);
-
-                    // the local DB has a record that the server doesn't have
-                    if (comp > 0) {
-                        if (Log.isLoggable(TAG, Log.VERBOSE)) {
-                            Log.v(TAG, "local record " +
-                                    localCursor.getLong(1) +
-                                    " has _sync_id " + localSyncID +
-                                    " that is < server _sync_id " + serverSyncId);
-                        }
-                        if (diffsArePartial) {
-                            localCursor.moveToNext();
-                        } else {
-                            deleteRow(localCursor);
-                            if (mDeletedTable != null) {
-                                mDb.delete(mDeletedTable, _SYNC_ID +"=?", new String[] {localSyncID});
-                            }
-                            syncResult.stats.numDeletes++;
-                            mDb.yieldIfContended();
-                        }
-                        localSyncID = null;
-                        continue;
-                    }
-
-                    // the server has a record that the local DB doesn't have
-                    if (comp < 0) {
-                        if (Log.isLoggable(TAG, Log.VERBOSE)) {
-                            Log.v(TAG, "local record " +
-                                    localCursor.getLong(1) +
-                                    " has _sync_id " + localSyncID +
-                                    " that is > server _sync_id " + serverSyncId);
-                        }
-                        localSyncID = null;
-                    }
-
-                    // the server and the local DB both have this record
-                    if (comp == 0) {
-                        if (Log.isLoggable(TAG, Log.VERBOSE)) {
-                            Log.v(TAG, "local record " +
-                                    localCursor.getLong(1) +
-                                    " has _sync_id " + localSyncID +
-                                    " that matches the server _sync_id");
-                        }
-                        localSyncDirty = localCursor.getInt(0) != 0;
-                        localRowId = localCursor.getLong(1);
-                        localSyncVersion = localCursor.getString(3);
-                        localCursor.moveToNext();
-                    }
-
-                    break;
-                }
-
-                // If this record is in the deleted table then update the server version
-                // in the deleted table, if necessary, and then ignore it here.
-                // We will send a deletion indication to the server down a
-                // little further.
-                if (findInCursor(deletedCursor, deletedSyncIDColumn, serverSyncId)) {
-                    if (Log.isLoggable(TAG, Log.VERBOSE)) {
-                        Log.v(TAG, "remote record " + serverSyncId + " is in the deleted table");
-                    }
-                    final String deletedSyncVersion = deletedCursor.getString(deletedSyncVersionColumn);
-                    if (!TextUtils.equals(deletedSyncVersion, serverSyncVersion)) {
-                        if (Log.isLoggable(TAG, Log.VERBOSE)) {
-                            Log.v(TAG, "setting version of deleted record " + serverSyncId + " to "
-                                    + serverSyncVersion);
-                        }
-                        ContentValues values = new ContentValues();
-                        values.put(_SYNC_VERSION, serverSyncVersion);
-                        mDb.update(mDeletedTable, values, "_sync_id=?", new String[]{serverSyncId});
-                    }
-                    continue;
-                }
-
-                // If the _sync_local_id is present in the diffsCursor
-                // then this record corresponds to a local record that was just
-                // inserted into the server and the _sync_local_id is the row id
-                // of the local record. Set these fields so that the next check
-                // treats this record as an update, which will allow the
-                // merger to update the record with the server's sync id
-                if (!diffsCursor.isNull(serverSyncLocalIdColumn)) {
-                    localRowId = diffsCursor.getLong(serverSyncLocalIdColumn);
-                    if (Log.isLoggable(TAG, Log.VERBOSE)) {
-                        Log.v(TAG, "the remote record with sync id " + serverSyncId
-                                + " has a local sync id, " + localRowId);
-                    }
-                    localSyncID = serverSyncId;
-                    localSyncDirty = false;
-                    localSyncVersion = null;
-                }
-
-                if (!TextUtils.isEmpty(localSyncID)) {
-                    // An existing server item has changed
-                    // If serverSyncVersion is null, there is no edit URL;
-                    // server won't let this change be written.
-                    boolean recordChanged = (localSyncVersion == null) ||
-                            (serverSyncVersion == null) ||
-                            !serverSyncVersion.equals(localSyncVersion);
-                    if (recordChanged) {
-                        if (localSyncDirty) {
-                            if (Log.isLoggable(TAG, Log.VERBOSE)) {
-                                Log.v(TAG, "remote record " + serverSyncId
-                                        + " conflicts with local _sync_id " + localSyncID
-                                        + ", local _id " + localRowId);
-                            }
-                            conflict = true;
-                        } else {
-                            if (Log.isLoggable(TAG, Log.VERBOSE)) {
-                                Log.v(TAG,
-                                        "remote record " +
-                                                serverSyncId +
-                                                " updates local _sync_id " +
-                                                localSyncID + ", local _id " +
-                                                localRowId);
-                            }
-                            update = true;
-                        }
-                    } else {
-                        if (Log.isLoggable(TAG, Log.VERBOSE)) {
-                            Log.v(TAG,
-                                    "Skipping update: localSyncVersion: " + localSyncVersion +
-                                    ", serverSyncVersion: " + serverSyncVersion);
-                        }
-                    }
-                } else {
-                    // the local db doesn't know about this record so add it
-                    if (Log.isLoggable(TAG, Log.VERBOSE)) {
-                        Log.v(TAG, "remote record " + serverSyncId + " is new, inserting");
-                    }
-                    insert = true;
-                }
-
-                if (update) {
-                    updateRow(localRowId, serverDiffs, diffsCursor);
-                    syncResult.stats.numUpdates++;
-                } else if (conflict) {
-                    resolveRow(localRowId, serverSyncId, serverDiffs, diffsCursor);
-                    syncResult.stats.numUpdates++;
-                } else if (insert) {
-                    insertRow(serverDiffs, diffsCursor);
-                    syncResult.stats.numInserts++;
-                }
-            }
-
-            if (Log.isLoggable(TAG, Log.VERBOSE)) {
-                Log.v(TAG, "processed " + diffsCount + " server entries");
-            }
-
-            // If tombstones aren't in use delete any remaining local rows that
-            // don't have corresponding server rows. Keep the rows that don't
-            // have a sync id since those were created locally and haven't been
-            // synced to the server yet.
-            if (!diffsArePartial) {
-                while (!localCursor.isAfterLast() && !TextUtils.isEmpty(localCursor.getString(2))) {
-                    if (mIsMergeCancelled) {
-                        return;
-                    }
-                    localCount++;
-                    final String localSyncId = localCursor.getString(2);
-                    if (Log.isLoggable(TAG, Log.VERBOSE)) {
-                        Log.v(TAG,
-                                "deleting local record " +
-                                        localCursor.getLong(1) +
-                                        " _sync_id " + localSyncId);
-                    }
-                    deleteRow(localCursor);
-                    if (mDeletedTable != null) {
-                        mDb.delete(mDeletedTable, _SYNC_ID + "=?", new String[] {localSyncId});
-                    }
-                    syncResult.stats.numDeletes++;
-                    mDb.yieldIfContended();
-                }
-            }
-            if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "checked " + localCount +
-                    " local entries");
-        } finally {
-            if (diffsCursor != null) diffsCursor.close();
-            if (localCursor != null) localCursor.close();
-            if (deletedCursor != null) deletedCursor.close();
-        }
-
-
-        if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "applying deletions from the server");
-
-        // Apply deletions from the server
-        if (mDeletedTableURL != null) {
-            diffsCursor = serverDiffs.query(mDeletedTableURL, null, null, null, null);
-            try {
-                while (diffsCursor.moveToNext()) {
-                    if (mIsMergeCancelled) {
-                        return;
-                    }
-                    // delete all rows that match each element in the diffsCursor
-                    fullyDeleteMatchingRows(diffsCursor, account, syncResult);
-                    mDb.yieldIfContended();
-                }
-            } finally {
-                diffsCursor.close();
-            }
-        }
-    }
-
-    private void fullyDeleteMatchingRows(Cursor diffsCursor, Account account,
-            SyncResult syncResult) {
-        int serverSyncIdColumn = diffsCursor.getColumnIndexOrThrow(_SYNC_ID);
-        final boolean deleteBySyncId = !diffsCursor.isNull(serverSyncIdColumn);
-
-        // delete the rows explicitly so that the delete operation can be overridden
-        final String[] selectionArgs;
-        Cursor c = null;
-        try {
-            if (deleteBySyncId) {
-                selectionArgs = new String[]{diffsCursor.getString(serverSyncIdColumn),
-                        account.name, account.type};
-                c = mDb.query(mTable, new String[]{BaseColumns._ID}, SELECT_BY_SYNC_ID_AND_ACCOUNT,
-                        selectionArgs, null, null, null);
-            } else {
-                int serverSyncLocalIdColumn = diffsCursor.getColumnIndexOrThrow(_SYNC_LOCAL_ID);
-                selectionArgs = new String[]{diffsCursor.getString(serverSyncLocalIdColumn)};
-                c = mDb.query(mTable, new String[]{BaseColumns._ID}, SELECT_BY_ID, selectionArgs,
-                        null, null, null);
-            }
-            c.moveToFirst();
-            while (!c.isAfterLast()) {
-                deleteRow(c); // advances the cursor
-                syncResult.stats.numDeletes++;
-            }
-        } finally {
-          if (c != null) c.close();
-        }
-        if (deleteBySyncId && mDeletedTable != null) {
-            mDb.delete(mDeletedTable, SELECT_BY_SYNC_ID_AND_ACCOUNT, selectionArgs);
-        }
-    }
-
-    /**
-     * Converts cursor into a Map, using the correct types for the values.
-     */
-    protected void cursorRowToContentValues(Cursor cursor, ContentValues map) {
-        DatabaseUtils.cursorRowToContentValues(cursor, map);
-    }
-
-    /**
-     * Finds local changes, placing the results in the given result object.
-     * @param temporaryInstanceFactory As an optimization for the case
-     * where there are no client-side diffs, mergeResult may initially
-     * have no {@link TempProviderSyncResult#tempContentProvider}.  If this is
-     * the first in the sequence of AbstractTableMergers to find
-     * client-side diffs, it will use the given ContentProvider to
-     * create a temporary instance and store its {@link
-     * android.content.ContentProvider} in the mergeResult.
-     * @param account
-     * @param syncResult
-     */
-    private void findLocalChanges(TempProviderSyncResult mergeResult,
-            SyncableContentProvider temporaryInstanceFactory, Account account,
-            SyncResult syncResult) {
-        SyncableContentProvider clientDiffs = mergeResult.tempContentProvider;
-        if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "generating client updates");
-
-        final String[] accountSelectionArgs = new String[]{account.name, account.type};
-
-        // Generate the client updates and insertions
-        // Create a cursor for dirty records
-        long numInsertsOrUpdates = 0;
-        Cursor localChangesCursor = mDb.query(mTable, null, SELECT_UNSYNCED, accountSelectionArgs,
-                null, null, null);
-        try {
-            numInsertsOrUpdates = localChangesCursor.getCount();
-            while (localChangesCursor.moveToNext()) {
-                if (mIsMergeCancelled) {
-                    return;
-                }
-                if (clientDiffs == null) {
-                    clientDiffs = temporaryInstanceFactory.getTemporaryInstance();
-                }
-                mValues.clear();
-                cursorRowToContentValues(localChangesCursor, mValues);
-                mValues.remove("_id");
-                DatabaseUtils.cursorLongToContentValues(localChangesCursor, "_id", mValues,
-                        _SYNC_LOCAL_ID);
-                clientDiffs.insert(mTableURL, mValues);
-            }
-        } finally {
-          localChangesCursor.close();
-        }
-
-        // Generate the client deletions
-        if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "generating client deletions");
-        long numEntries = DatabaseUtils.queryNumEntries(mDb, mTable);
-        long numDeletedEntries = 0;
-        if (mDeletedTable != null) {
-            Cursor deletedCursor = mDb.query(mDeletedTable,
-                    syncIdAndVersionProjection,
-                    _SYNC_ACCOUNT + "=? AND " + _SYNC_ACCOUNT_TYPE + "=? AND "
-                            + _SYNC_ID + " IS NOT NULL", accountSelectionArgs,
-                    null, null, mDeletedTable + "." + _SYNC_ID);
-            try {
-                numDeletedEntries = deletedCursor.getCount();
-                while (deletedCursor.moveToNext()) {
-                    if (mIsMergeCancelled) {
-                        return;
-                    }
-                    if (clientDiffs == null) {
-                        clientDiffs = temporaryInstanceFactory.getTemporaryInstance();
-                    }
-                    mValues.clear();
-                    DatabaseUtils.cursorRowToContentValues(deletedCursor, mValues);
-                    clientDiffs.insert(mDeletedTableURL, mValues);
-                }
-            } finally {
-                deletedCursor.close();
-            }
-        }
-
-        if (clientDiffs != null) {
-            mergeResult.tempContentProvider = clientDiffs;
-        }
-        syncResult.stats.numDeletes += numDeletedEntries;
-        syncResult.stats.numUpdates += numInsertsOrUpdates;
-        syncResult.stats.numEntries += numEntries;
-    }
-}
diff --git a/core/java/android/content/AbstractThreadedSyncAdapter.java b/core/java/android/content/AbstractThreadedSyncAdapter.java
index 154c4a6..14bc5dd 100644
--- a/core/java/android/content/AbstractThreadedSyncAdapter.java
+++ b/core/java/android/content/AbstractThreadedSyncAdapter.java
@@ -117,7 +117,7 @@
                 if (mSyncThread != null
                         && mSyncThread.mSyncContext.getSyncContextBinder()
                         == syncContext.asBinder()) {
-                    mSyncThread.interrupt();
+                    onSyncCanceled(mSyncThread);
                 }
             }
         }
@@ -207,4 +207,15 @@
      */
     public abstract void onPerformSync(Account account, Bundle extras,
             String authority, ContentProviderClient provider, SyncResult syncResult);
+
+    /**
+     * Indicates that a sync operation has been canceled. This will be invoked on a separate
+     * thread than the sync thread and so you must consider the multi-threaded implications
+     * of the work that you do in this method.
+     *
+     * @param thread the thread that is running the sync operation to cancel
+     */
+    public void onSyncCanceled(Thread thread) {
+        thread.interrupt();
+    }
 }
diff --git a/core/java/android/content/SyncAdapter.java b/core/java/android/content/SyncAdapter.java
deleted file mode 100644
index a5afe87..0000000
--- a/core/java/android/content/SyncAdapter.java
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * Copyright (C) 2006 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.content;
-
-import android.os.Bundle;
-import android.os.RemoteException;
-import android.accounts.Account;
-
-/**
- * @hide
- */
-public abstract class SyncAdapter {
-    private static final String TAG = "SyncAdapter";
-
-    /** Kernel event log tag. */
-    public static final int LOG_SYNC_DETAILS = EventLogTags.SYNC_DETAILS;
-
-    class Transport extends ISyncAdapter.Stub {
-        public void startSync(ISyncContext syncContext, String authority, Account account,
-                Bundle extras) throws RemoteException {
-            SyncAdapter.this.startSync(new SyncContext(syncContext), account, authority, extras);
-        }
-
-        public void cancelSync(ISyncContext syncContext) throws RemoteException {
-            SyncAdapter.this.cancelSync();
-        }
-
-        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);
-        }
-    }
-
-    Transport mTransport = new Transport();
-
-    /**
-     * Get the Transport object.
-     */
-    public final ISyncAdapter getISyncAdapter()
-    {
-        return mTransport;
-    }
-
-    /**
-     * Initiate a sync for this account. SyncAdapter-specific parameters may
-     * be specified in extras, which is guaranteed to not be null. IPC invocations
-     * of this method and cancelSync() are guaranteed to be serialized.
-     *
-     * @param syncContext the ISyncContext used to indicate the progress of the sync. When
-     *   the sync is finished (successfully or not) ISyncContext.onFinished() must be called.
-     * @param account the account that should be synced
-     * @param authority the authority if the sync request
-     * @param extras SyncAdapter-specific parameters
-     */
-    public abstract void startSync(SyncContext syncContext, Account account, String authority, 
-            Bundle extras);
-
-    /**
-     * Cancel the most recently initiated sync. Due to race conditions, this may arrive
-     * after the ISyncContext.onFinished() for that sync was called. IPC invocations
-     * of this method and startSync() are guaranteed to be serialized.
-     */
-    public abstract void cancelSync();
-}
diff --git a/core/java/android/content/SyncStateContentProviderHelper.java b/core/java/android/content/SyncStateContentProviderHelper.java
deleted file mode 100644
index 64bbe25..0000000
--- a/core/java/android/content/SyncStateContentProviderHelper.java
+++ /dev/null
@@ -1,243 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.content;
-
-import com.android.internal.util.ArrayUtils;
-
-import android.database.Cursor;
-import android.database.DatabaseUtils;
-import android.database.sqlite.SQLiteDatabase;
-import android.database.sqlite.SQLiteOpenHelper;
-import android.net.Uri;
-import android.accounts.Account;
-
-/**
- * Extends the schema of a ContentProvider to include the _sync_state table
- * and implements query/insert/update/delete to access that table using the
- * authority "syncstate". This can be used to store the sync state for a
- * set of accounts.
- * 
- * @hide
- */
-public class SyncStateContentProviderHelper {
-    final SQLiteOpenHelper mOpenHelper;
-
-    private static final String SYNC_STATE_AUTHORITY = "syncstate";
-    private static final UriMatcher sURIMatcher = new UriMatcher(UriMatcher.NO_MATCH);
-
-    private static final int STATE = 0;
-
-    private static final Uri CONTENT_URI =
-            Uri.parse("content://" + SYNC_STATE_AUTHORITY + "/state");
-
-    private static final String ACCOUNT_WHERE = "_sync_account = ? AND _sync_account_type = ?";
-
-    private final Provider mInternalProviderInterface;
-
-    private static final String SYNC_STATE_TABLE = "_sync_state";
-    private static long DB_VERSION = 3;
-
-    private static final String[] ACCOUNT_PROJECTION =
-            new String[]{"_sync_account", "_sync_account_type"};
-
-    static {
-        sURIMatcher.addURI(SYNC_STATE_AUTHORITY, "state", STATE);
-    }
-
-    public SyncStateContentProviderHelper(SQLiteOpenHelper openHelper) {
-        mOpenHelper = openHelper;
-        mInternalProviderInterface = new Provider();
-    }
-
-    public ContentProvider asContentProvider() {
-        return mInternalProviderInterface;
-    }
-
-    public void createDatabase(SQLiteDatabase db) {
-        db.execSQL("DROP TABLE IF EXISTS _sync_state");
-        db.execSQL("CREATE TABLE _sync_state (" +
-                   "_id INTEGER PRIMARY KEY," +
-                   "_sync_account TEXT," +
-                   "_sync_account_type TEXT," +
-                   "data TEXT," +
-                   "UNIQUE(_sync_account, _sync_account_type)" +
-                   ");");
-
-        db.execSQL("DROP TABLE IF EXISTS _sync_state_metadata");
-        db.execSQL("CREATE TABLE _sync_state_metadata (" +
-                    "version INTEGER" +
-                    ");");
-        ContentValues values = new ContentValues();
-        values.put("version", DB_VERSION);
-        db.insert("_sync_state_metadata", "version", values);
-    }
-
-    protected void onDatabaseOpened(SQLiteDatabase db) {
-        long version = DatabaseUtils.longForQuery(db,
-                "select version from _sync_state_metadata", null);
-        if (version != DB_VERSION) {
-            createDatabase(db);
-        }
-    }
-
-    class Provider extends ContentProvider {
-        public boolean onCreate() {
-            throw new UnsupportedOperationException("not implemented");
-        }
-
-        public Cursor query(Uri url, String[] projection, String selection, String[] selectionArgs,
-                String sortOrder) {
-            SQLiteDatabase db = mOpenHelper.getReadableDatabase();
-            int match = sURIMatcher.match(url);
-            switch (match) {
-                case STATE:
-                    return db.query(SYNC_STATE_TABLE, projection, selection, selectionArgs,
-                            null, null, sortOrder);
-                default:
-                    throw new UnsupportedOperationException("Cannot query URL: " + url);
-            }
-        }
-
-        public String getType(Uri uri) {
-            throw new UnsupportedOperationException("not implemented");
-        }
-
-        public Uri insert(Uri url, ContentValues values) {
-            SQLiteDatabase db = mOpenHelper.getWritableDatabase();
-            int match = sURIMatcher.match(url);
-            switch (match) {
-                case STATE: {
-                    long id = db.insert(SYNC_STATE_TABLE, "feed", values);
-                    return CONTENT_URI.buildUpon().appendPath(String.valueOf(id)).build();
-                }
-                default:
-                    throw new UnsupportedOperationException("Cannot insert into URL: " + url);
-            }
-        }
-
-        public int delete(Uri url, String userWhere, String[] whereArgs) {
-            SQLiteDatabase db = mOpenHelper.getWritableDatabase();
-            switch (sURIMatcher.match(url)) {
-                case STATE:
-                    return db.delete(SYNC_STATE_TABLE, userWhere, whereArgs);
-                default:
-                    throw new IllegalArgumentException("Unknown URL " + url);
-            }
-
-        }
-
-        public int update(Uri url, ContentValues values, String selection, String[] selectionArgs) {
-            SQLiteDatabase db = mOpenHelper.getWritableDatabase();
-            switch (sURIMatcher.match(url)) {
-                case STATE:
-                    return db.update(SYNC_STATE_TABLE, values, selection, selectionArgs);
-                default:
-                    throw new UnsupportedOperationException("Cannot update URL: " + url);
-            }
-
-        }
-    }
-
-    /**
-     * Check if the url matches content that this ContentProvider manages.
-     * @param url the Uri to check
-     * @return true if this ContentProvider can handle that Uri.
-     */
-    public boolean matches(Uri url) {
-        return (SYNC_STATE_AUTHORITY.equals(url.getAuthority()));
-    }
-
-    /**
-     * Replaces the contents of the _sync_state table in the destination ContentProvider
-     * with the row that matches account, if any, in the source ContentProvider.
-     * <p>
-     * The ContentProviders must expose the _sync_state table as URI content://syncstate/state.
-     * @param dbSrc the database to read from
-     * @param dbDest the database to write to
-     * @param account the account of the row that should be copied over.
-     */
-    public void copySyncState(SQLiteDatabase dbSrc, SQLiteDatabase dbDest,
-            Account account) {
-        final String[] whereArgs = new String[]{account.name, account.type};
-        Cursor c = dbSrc.query(SYNC_STATE_TABLE,
-                new String[]{"_sync_account", "_sync_account_type", "data"},
-                ACCOUNT_WHERE, whereArgs, null, null, null);
-        try {
-            if (c.moveToNext()) {
-                ContentValues values = new ContentValues();
-                values.put("_sync_account", c.getString(0));
-                values.put("_sync_account_type", c.getString(1));
-                values.put("data", c.getBlob(2));
-                dbDest.replace(SYNC_STATE_TABLE, "_sync_account", values);
-            }
-        } finally {
-            c.close();
-        }
-    }
-
-    public void onAccountsChanged(Account[] accounts) {
-        SQLiteDatabase db = mOpenHelper.getWritableDatabase();
-        Cursor c = db.query(SYNC_STATE_TABLE, ACCOUNT_PROJECTION, null, null, null, null, null);
-        try {
-            while (c.moveToNext()) {
-                final String accountName = c.getString(0);
-                final String accountType = c.getString(1);
-                Account account = new Account(accountName, accountType);
-                if (!ArrayUtils.contains(accounts, account)) {
-                    db.delete(SYNC_STATE_TABLE, ACCOUNT_WHERE,
-                            new String[]{accountName, accountType});
-                }
-            }
-        } finally {
-            c.close();
-        }
-    }
-
-    public void discardSyncData(SQLiteDatabase db, Account account) {
-        if (account != null) {
-            db.delete(SYNC_STATE_TABLE, ACCOUNT_WHERE, new String[]{account.name, account.type});
-        } else {
-            db.delete(SYNC_STATE_TABLE, null, null);
-        }
-    }
-
-    /**
-     * Retrieves the SyncData bytes for the given account. The byte array returned may be null.
-     */
-    public byte[] readSyncDataBytes(SQLiteDatabase db, Account account) {
-        Cursor c = db.query(SYNC_STATE_TABLE, null, ACCOUNT_WHERE,
-                new String[]{account.name, account.type}, null, null, null);
-        try {
-            if (c.moveToFirst()) {
-                return c.getBlob(c.getColumnIndexOrThrow("data"));
-            }
-        } finally {
-            c.close();
-        }
-        return null;
-    }
-
-    /**
-     * Sets the SyncData bytes for the given account. The bytes array may be null.
-     */
-    public void writeSyncDataBytes(SQLiteDatabase db, Account account, byte[] data) {
-        ContentValues values = new ContentValues();
-        values.put("data", data);
-        db.update(SYNC_STATE_TABLE, values, ACCOUNT_WHERE,
-                new String[]{account.name, account.type});
-    }
-}
diff --git a/core/java/android/content/SyncableContentProvider.java b/core/java/android/content/SyncableContentProvider.java
deleted file mode 100644
index ab4e91c..0000000
--- a/core/java/android/content/SyncableContentProvider.java
+++ /dev/null
@@ -1,237 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.content;
-
-import android.database.Cursor;
-import android.database.sqlite.SQLiteDatabase;
-import android.net.Uri;
-import android.accounts.Account;
-
-import java.util.Map;
-
-/**
- * A specialization of the ContentProvider that centralizes functionality
- * used by ContentProviders that are syncable. It also wraps calls to the ContentProvider
- * inside of database transactions.
- *
- * @hide
- */
-public abstract class SyncableContentProvider extends ContentProvider {
-    protected abstract boolean isTemporary();
-
-    private volatile TempProviderSyncAdapter mTempProviderSyncAdapter;
-
-    public void setTempProviderSyncAdapter(TempProviderSyncAdapter syncAdapter) {
-        mTempProviderSyncAdapter = syncAdapter;
-    }
-
-    public TempProviderSyncAdapter getTempProviderSyncAdapter() {
-        return mTempProviderSyncAdapter;
-    }
-
-    /**
-     * Close resources that must be closed. You must call this to properly release
-     * the resources used by the SyncableContentProvider.
-     */
-    public abstract void close();
-
-    /**
-     * Override to create your schema and do anything else you need to do with a new database.
-     * This is run inside a transaction (so you don't need to use one).
-     * This method may not use getDatabase(), or call content provider methods, it must only
-     * use the database handle passed to it.
-     */
-    protected abstract void bootstrapDatabase(SQLiteDatabase db);
-
-    /**
-     * Override to upgrade your database from an old version to the version you specified.
-     * Don't set the DB version, this will automatically be done after the method returns.
-     * This method may not use getDatabase(), or call content provider methods, it must only
-     * use the database handle passed to it.
-     *
-     * @param oldVersion version of the existing database
-     * @param newVersion current version to upgrade to
-     * @return true if the upgrade was lossless, false if it was lossy
-     */
-    protected abstract boolean upgradeDatabase(SQLiteDatabase db, int oldVersion, int newVersion);
-
-    /**
-     * Override to do anything (like cleanups or checks) you need to do after opening a database.
-     * Does nothing by default.  This is run inside a transaction (so you don't need to use one).
-     * This method may not use getDatabase(), or call content provider methods, it must only
-     * use the database handle passed to it.
-     */
-    protected abstract void onDatabaseOpened(SQLiteDatabase db);
-
-    /**
-     * Get a non-persistent instance of this content provider.
-     * You must call {@link #close} on the returned
-     * SyncableContentProvider when you are done with it.
-     *
-     * @return a non-persistent content provider with the same layout as this
-     * provider.
-     */
-    public abstract SyncableContentProvider getTemporaryInstance();
-
-    public abstract SQLiteDatabase getDatabase();
-
-    public abstract boolean getContainsDiffs();
-
-    public abstract void setContainsDiffs(boolean containsDiffs);
-
-    /**
-     * Each subclass of this class should define a subclass of {@link
-     * AbstractTableMerger} for each table they wish to merge.  It
-     * should then override this method and return one instance of
-     * each merger, in sequence.  Their {@link
-     * AbstractTableMerger#merge merge} methods will be called, one at a
-     * time, in the order supplied.
-     *
-     * <p>The default implementation returns an empty list, so that no
-     * merging will occur.
-     * @return A sequence of subclasses of {@link
-     * AbstractTableMerger}, one for each table that should be merged.
-     */
-    protected abstract Iterable<? extends AbstractTableMerger> getMergers();
-
-    /**
-     * Check if changes to this URI can be syncable changes.
-     * @param uri the URI of the resource that was changed
-     * @return true if changes to this URI can be syncable changes, false otherwise
-     */
-    public abstract boolean changeRequiresLocalSync(Uri uri);
-
-    /**
-     * Called right before a sync is started.
-     *
-     * @param context the sync context for the operation
-     * @param account
-     */
-    public abstract void onSyncStart(SyncContext context, Account account);
-
-    /**
-     * Called right after a sync is completed
-     *
-     * @param context the sync context for the operation
-     * @param success true if the sync succeeded, false if an error occurred
-     */
-    public abstract void onSyncStop(SyncContext context, boolean success);
-
-    /**
-     * The account of the most recent call to onSyncStart()
-     * @return the account
-     */
-    public abstract Account getSyncingAccount();
-
-    /**
-     * Merge diffs from a sync source with this content provider.
-     *
-     * @param context the SyncContext within which this merge is taking place
-     * @param diffs A temporary content provider containing diffs from a sync
-     *   source.
-     * @param result a MergeResult that contains information about the merge, including
-     *   a temporary content provider with the same layout as this provider containing
-     * @param syncResult
-     */
-    public abstract void merge(SyncContext context, SyncableContentProvider diffs,
-            TempProviderSyncResult result, SyncResult syncResult);
-
-
-    /**
-     * Invoked when the active sync has been canceled. The default
-     * implementation doesn't do anything (except ensure that this
-     * provider is syncable). Subclasses of ContentProvider
-     * that support canceling of sync should override this.
-     */
-    public abstract void onSyncCanceled();
-
-
-    public abstract boolean isMergeCancelled();
-
-    /**
-     * Subclasses should override this instead of update(). See update()
-     * for details.
-     *
-     * <p> This method is called within a acquireDbLock()/releaseDbLock() block,
-     * which means a database transaction will be active during the call;
-     */
-    protected abstract int updateInternal(Uri url, ContentValues values,
-            String selection, String[] selectionArgs);
-
-    /**
-     * Subclasses should override this instead of delete(). See delete()
-     * for details.
-     *
-     * <p> This method is called within a acquireDbLock()/releaseDbLock() block,
-     * which means a database transaction will be active during the call;
-     */
-    protected abstract int deleteInternal(Uri url, String selection, String[] selectionArgs);
-
-    /**
-     * Subclasses should override this instead of insert(). See insert()
-     * for details.
-     *
-     * <p> This method is called within a acquireDbLock()/releaseDbLock() block,
-     * which means a database transaction will be active during the call;
-     */
-    protected abstract Uri insertInternal(Uri url, ContentValues values);
-
-    /**
-     * Subclasses should override this instead of query(). See query()
-     * for details.
-     *
-     * <p> This method is *not* called within a acquireDbLock()/releaseDbLock()
-     * block for performance reasons. If an implementation needs atomic access
-     * to the database the lock can be acquired then.
-     */
-    protected abstract Cursor queryInternal(Uri url, String[] projection,
-            String selection, String[] selectionArgs, String sortOrder);
-
-    /**
-     * Make sure that there are no entries for accounts that no longer exist
-     * @param accountsArray the array of currently-existing accounts
-     */
-    protected abstract void onAccountsChanged(Account[] accountsArray);
-
-    /**
-     * A helper method to delete all rows whose account is not in the accounts
-     * map. The accountColumnName is the name of the column that is expected
-     * to hold the account. If a row has an empty account it is never deleted.
-     *
-     * @param accounts a map of existing accounts
-     * @param table the table to delete from
-     */
-    protected abstract void deleteRowsForRemovedAccounts(Map<Account, Boolean> accounts,
-            String table);
-
-    /**
-     * Called when the sync system determines that this provider should no longer
-     * contain records for the specified account.
-     */
-    public abstract void wipeAccount(Account account);
-
-    /**
-     * Retrieves the SyncData bytes for the given account. The byte array returned may be null.
-     */
-    public abstract byte[] readSyncDataBytes(Account account);
-
-    /**
-     * Sets the SyncData bytes for the given account. The bytes array may be null.
-     */
-    public abstract void writeSyncDataBytes(Account account, byte[] data);
-}
-
diff --git a/core/java/android/content/TempProviderSyncAdapter.java b/core/java/android/content/TempProviderSyncAdapter.java
deleted file mode 100644
index 5ccaa26..0000000
--- a/core/java/android/content/TempProviderSyncAdapter.java
+++ /dev/null
@@ -1,585 +0,0 @@
-package android.content;
-
-import android.accounts.Account;
-import android.accounts.AuthenticatorException;
-import android.accounts.OperationCanceledException;
-import android.database.SQLException;
-import android.net.TrafficStats;
-import android.os.Bundle;
-import android.os.Debug;
-import android.os.Parcelable;
-import android.os.Process;
-import android.os.SystemProperties;
-import android.text.TextUtils;
-import android.util.Config;
-import android.util.EventLog;
-import android.util.Log;
-import android.util.TimingLogger;
-
-import java.io.IOException;
-
-/**
- * @hide
- */
-public abstract class TempProviderSyncAdapter extends SyncAdapter {
-    private static final String TAG = "Sync";
-
-    private static final int MAX_GET_SERVER_DIFFS_LOOP_COUNT = 20;
-    private static final int MAX_UPLOAD_CHANGES_LOOP_COUNT = 10;
-    private static final int NUM_ALLOWED_SIMULTANEOUS_DELETIONS = 5;
-    private static final long PERCENT_ALLOWED_SIMULTANEOUS_DELETIONS = 20;
-
-    private volatile SyncableContentProvider mProvider;
-    private volatile SyncThread mSyncThread = null;
-    private volatile boolean mProviderSyncStarted;
-    private volatile boolean mAdapterSyncStarted;
-    
-    public TempProviderSyncAdapter(SyncableContentProvider provider) {
-        super();
-        mProvider = provider;
-    }
-
-    /**
-     * Used by getServerDiffs() to track the sync progress for a given
-     * sync adapter. Implementations of SyncAdapter generally specialize
-     * this class in order to track specific data about that SyncAdapter's
-     * sync. If an implementation of SyncAdapter doesn't need to store
-     * any data for a sync it may use TrivialSyncData.
-     */
-    public static abstract class SyncData implements Parcelable {
-
-    }
-
-    public final void setContext(Context context) {
-        mContext = context;
-    }
-
-    /**
-     * Retrieve the Context this adapter is running in.  Only available
-     * once onSyncStarting() is called (not available from constructor).
-     */
-    final public Context getContext() {
-        return mContext;
-    }
-
-    /**
-     * Called right before a sync is started.
-     *
-     * @param context allows you to publish status and interact with the
-     * @param account the account to sync
-     * @param manualSync true if this sync was requested manually by the user
-     * @param result information to track what happened during this sync attempt
-     */
-    public abstract void onSyncStarting(SyncContext context, Account account, boolean manualSync,
-            SyncResult result);
-
-    /**
-     * Called right after a sync is completed
-     *
-     * @param context allows you to publish status and interact with the
-     *                user during interactive syncs.
-     * @param success true if the sync suceeded, false if an error occured
-     */
-    public abstract void onSyncEnding(SyncContext context, boolean success);
-
-    /**
-     * Implement this to return true if the data in your content provider
-     * is read only.
-     */
-    public abstract boolean isReadOnly();
-
-    public abstract boolean getIsSyncable(Account account)
-            throws IOException, AuthenticatorException, OperationCanceledException;
-
-    /**
-     * Get diffs from the server since the last completed sync and put them
-     * into a temporary provider.
-     *
-     * @param context allows you to publish status and interact with the
-     *                user during interactive syncs.
-     * @param syncData used to track the progress this client has made in syncing data
-     *   from the server
-     * @param tempProvider this is where the diffs should be stored
-     * @param extras any extra data describing the sync that is desired
-     * @param syncInfo sync adapter-specific data that is used during a single sync operation
-     * @param syncResult information to track what happened during this sync attempt
-     */
-    public abstract void getServerDiffs(SyncContext context,
-            SyncData syncData, SyncableContentProvider tempProvider,
-            Bundle extras, Object syncInfo, SyncResult syncResult);
-
-    /**
-     * Send client diffs to the server, optionally receiving more diffs from the server
-     *
-     * @param context allows you to publish status and interact with the
-     *                user during interactive syncs.
-     * @param clientDiffs the diffs from the client
-     * @param serverDiffs the SyncableContentProvider that should be populated with
-*   the entries that were returned in response to an insert/update/delete request
-*   to the server
-     * @param syncResult information to track what happened during this sync attempt
-     * @param dontActuallySendDeletes
-     */
-    public abstract void sendClientDiffs(SyncContext context,
-            SyncableContentProvider clientDiffs,
-            SyncableContentProvider serverDiffs, SyncResult syncResult,
-            boolean dontActuallySendDeletes);
-
-    /**
-     * Reads the sync data from the ContentProvider
-     * @param contentProvider the ContentProvider to read from
-     * @return the SyncData for the provider. This may be null.
-     */
-    public SyncData readSyncData(SyncableContentProvider contentProvider) {
-        return null;
-    }
-
-    /**
-     * Create and return a new, empty SyncData object
-     */
-    public SyncData newSyncData() {
-        return null;
-    }
-
-    /**
-     * Stores the sync data in the Sync Stats database, keying it by
-     * the account that was set in the last call to onSyncStarting()
-     */
-    public void writeSyncData(SyncData syncData, SyncableContentProvider contentProvider) {}
-
-    /**
-     * Indicate to the SyncAdapter that the last sync that was started has
-     * been cancelled.
-     */
-    public abstract void onSyncCanceled();
-
-    /**
-     * Initializes the temporary content providers used during
-     * {@link TempProviderSyncAdapter#sendClientDiffs}.
-     * May copy relevant data from the underlying db into this provider so
-     * joins, etc., can work.
-     *
-     * @param cp The ContentProvider to initialize.
-     */
-    protected void initTempProvider(SyncableContentProvider cp) {}
-
-    protected Object createSyncInfo() {
-        return null;
-    }
-
-    /**
-     * Called when the accounts list possibly changed, to give the
-     * SyncAdapter a chance to do any necessary bookkeeping, e.g.
-     * to make sure that any required SubscribedFeeds subscriptions
-     * exist.
-     * @param accounts the list of accounts
-     */
-    public abstract void onAccountsChanged(Account[] accounts);
-
-    private Context mContext;
-
-    private class SyncThread extends Thread {
-        private final Account mAccount;
-        private final String mAuthority;
-        private final Bundle mExtras;
-        private final SyncContext mSyncContext;
-        private volatile boolean mIsCanceled = false;
-        private long mInitialTxBytes;
-        private long mInitialRxBytes;
-        private final SyncResult mResult;
-
-        SyncThread(SyncContext syncContext, Account account, String authority, Bundle extras) {
-            super("SyncThread");
-            mAccount = account;
-            mAuthority = authority;
-            mExtras = extras;
-            mSyncContext = syncContext;
-            mResult = new SyncResult();
-        }
-
-        void cancelSync() {
-            mIsCanceled = true;
-            if (mAdapterSyncStarted) onSyncCanceled();
-            if (mProviderSyncStarted) mProvider.onSyncCanceled();
-            // We may lose the last few sync events when canceling.  Oh well.
-            int uid = Process.myUid();
-            logSyncDetails(TrafficStats.getUidTxBytes(uid) - mInitialTxBytes,
-                    TrafficStats.getUidRxBytes(uid) - mInitialRxBytes, mResult);
-        }
-        
-        @Override
-        public void run() {
-            Process.setThreadPriority(Process.myTid(),
-                    Process.THREAD_PRIORITY_BACKGROUND);
-            int uid = Process.myUid();
-            mInitialTxBytes = TrafficStats.getUidTxBytes(uid);
-            mInitialRxBytes = TrafficStats.getUidRxBytes(uid);
-            try {
-                sync(mSyncContext, mAccount, mAuthority, mExtras);
-            } catch (SQLException e) {
-                Log.e(TAG, "Sync failed", e);
-                mResult.databaseError = true;
-            } finally {
-                mSyncThread = null;
-                if (!mIsCanceled) {
-                    logSyncDetails(TrafficStats.getUidTxBytes(uid) - mInitialTxBytes,
-                    TrafficStats.getUidRxBytes(uid) - mInitialRxBytes, mResult);
-                    mSyncContext.onFinished(mResult);
-                }
-            }
-        }
-
-        private void sync(SyncContext syncContext, Account account, String authority,
-                Bundle extras) {
-            mIsCanceled = false;
-
-            mProviderSyncStarted = false;
-            mAdapterSyncStarted = false;
-            String message = null;
-
-            // always attempt to initialize if the isSyncable state isn't set yet
-            int isSyncable = ContentResolver.getIsSyncable(account, authority);
-            if (isSyncable < 0) {
-                try {
-                    isSyncable = (getIsSyncable(account)) ? 1 : 0;
-                    ContentResolver.setIsSyncable(account, authority, isSyncable);
-                } catch (IOException e) {
-                    ++mResult.stats.numIoExceptions;
-                } catch (AuthenticatorException e) {
-                    ++mResult.stats.numParseExceptions;
-                } catch (OperationCanceledException e) {
-                    // do nothing
-                }
-            }
-
-            // if this is an initialization request then our work is done here
-            if (extras.getBoolean(ContentResolver.SYNC_EXTRAS_INITIALIZE, false)) {
-                return;
-            }
-
-            // if we aren't syncable then get out
-            if (isSyncable <= 0) {
-                return;
-            }
-
-            boolean manualSync = extras.getBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, false);
-
-            try {
-                mProvider.onSyncStart(syncContext, account);
-                mProviderSyncStarted = true;
-                onSyncStarting(syncContext, account, manualSync, mResult);
-                if (mResult.hasError()) {
-                    message = "SyncAdapter failed while trying to start sync";
-                    return;
-                }
-                mAdapterSyncStarted = true;
-                if (mIsCanceled) {
-                    return;
-                }
-                final String syncTracingEnabledValue = SystemProperties.get(TAG + "Tracing");
-                final boolean syncTracingEnabled = !TextUtils.isEmpty(syncTracingEnabledValue);
-                try {
-                    if (syncTracingEnabled) {
-                        System.gc();
-                        System.gc();
-                        Debug.startMethodTracing("synctrace." + System.currentTimeMillis());
-                    }
-                    runSyncLoop(syncContext, account, extras);
-                } finally {
-                    if (syncTracingEnabled) Debug.stopMethodTracing();
-                }
-                onSyncEnding(syncContext, !mResult.hasError());
-                mAdapterSyncStarted = false;
-                mProvider.onSyncStop(syncContext, true);
-                mProviderSyncStarted = false;
-            } finally {
-                if (mAdapterSyncStarted) {
-                    mAdapterSyncStarted = false;
-                    onSyncEnding(syncContext, false);
-                }
-                if (mProviderSyncStarted) {
-                    mProviderSyncStarted = false;
-                    mProvider.onSyncStop(syncContext, false);
-                }
-                if (!mIsCanceled) {
-                    if (message != null) syncContext.setStatusText(message);
-                }
-            }
-        }
-
-        private void runSyncLoop(SyncContext syncContext, Account account, Bundle extras) {
-            TimingLogger syncTimer = new TimingLogger(TAG + "Profiling", "sync");
-            syncTimer.addSplit("start");
-            int loopCount = 0;
-            boolean tooManyGetServerDiffsAttempts = false;
-
-            final boolean overrideTooManyDeletions =
-                    extras.getBoolean(ContentResolver.SYNC_EXTRAS_OVERRIDE_TOO_MANY_DELETIONS,
-                            false);
-            final boolean discardLocalDeletions =
-                    extras.getBoolean(ContentResolver.SYNC_EXTRAS_DISCARD_LOCAL_DELETIONS, false);
-            boolean uploadOnly = extras.getBoolean(ContentResolver.SYNC_EXTRAS_UPLOAD,
-                    false /* default this flag to false */);
-            SyncableContentProvider serverDiffs = null;
-            TempProviderSyncResult result = new TempProviderSyncResult();
-            try {
-                if (!uploadOnly) {
-                    /**
-                     * This loop repeatedly calls SyncAdapter.getServerDiffs()
-                     * (to get changes from the feed) followed by
-                     * ContentProvider.merge() (to incorporate these changes
-                     * into the provider), stopping when the SyncData returned
-                     * from getServerDiffs() indicates that all the data was
-                     * fetched.
-                     */
-                    while (!mIsCanceled) {
-                        // Don't let a bad sync go forever
-                        if (loopCount++ == MAX_GET_SERVER_DIFFS_LOOP_COUNT) {
-                            Log.e(TAG, "runSyncLoop: Hit max loop count while getting server diffs "
-                                    + getClass().getName());
-                            // TODO: change the structure here to schedule a new sync
-                            // with a backoff time, keeping track to be sure
-                            // we don't keep doing this forever (due to some bug or
-                            // mismatch between the client and the server)
-                            tooManyGetServerDiffsAttempts = true;
-                            break;
-                        }
-
-                        // Get an empty content provider to put the diffs into
-                        if (serverDiffs != null) serverDiffs.close();
-                        serverDiffs = mProvider.getTemporaryInstance();
-
-                        // Get records from the server which will be put into the serverDiffs
-                        initTempProvider(serverDiffs);
-                        Object syncInfo = createSyncInfo();
-                        SyncData syncData = readSyncData(serverDiffs);
-                        // syncData will only be null if there was a demarshalling error
-                        // while reading the sync data.
-                        if (syncData == null) {
-                            mProvider.wipeAccount(account);
-                            syncData = newSyncData();
-                        }
-                        mResult.clear();
-                        if (Log.isLoggable(TAG, Log.VERBOSE)) {
-                            Log.v(TAG, "runSyncLoop: running getServerDiffs using syncData "
-                                    + syncData.toString());
-                        }
-                        getServerDiffs(syncContext, syncData, serverDiffs, extras, syncInfo,
-                                mResult);
-
-                        if (mIsCanceled) return;
-                        if (Log.isLoggable(TAG, Log.VERBOSE)) {
-                            Log.v(TAG, "runSyncLoop: result: " + mResult);
-                        }
-                        if (mResult.hasError()) return;
-                        if (mResult.partialSyncUnavailable) {
-                            if (Config.LOGD) {
-                                Log.d(TAG, "partialSyncUnavailable is set, setting "
-                                        + "ignoreSyncData and retrying");
-                            }
-                            mProvider.wipeAccount(account);
-                            continue;
-                        }
-
-                        // write the updated syncData back into the temp provider
-                        writeSyncData(syncData, serverDiffs);
-
-                        // apply the downloaded changes to the provider
-                        if (Log.isLoggable(TAG, Log.VERBOSE)) {
-                            Log.v(TAG, "runSyncLoop: running merge");
-                        }
-                        mProvider.merge(syncContext, serverDiffs,
-                                null /* don't return client diffs */, mResult);
-                        if (mIsCanceled) return;
-                        if (Log.isLoggable(TAG, Log.VERBOSE)) {
-                            Log.v(TAG, "runSyncLoop: result: " + mResult);
-                        }
-
-                        // if the server has no more changes then break out of the loop
-                        if (!mResult.moreRecordsToGet) {
-                            if (Log.isLoggable(TAG, Log.VERBOSE)) {
-                                Log.v(TAG, "runSyncLoop: fetched all data, moving on");
-                            }
-                            break;
-                        }
-                        if (Log.isLoggable(TAG, Log.VERBOSE)) {
-                            Log.v(TAG, "runSyncLoop: more data to fetch, looping");
-                        }
-                    }
-                }
-
-                /**
-                 * This loop repeatedly calls ContentProvider.merge() followed
-                 * by SyncAdapter.merge() until either indicate that there is
-                 * no more work to do by returning null.
-                 * <p>
-                 * The initial ContentProvider.merge() returns a temporary
-                 * ContentProvider that contains any local changes that need
-                 * to be committed to the server.
-                 * <p>
-                 * The SyncAdapter.merge() calls upload the changes to the server
-                 * and populates temporary provider (the serverDiffs) with the
-                 * result.
-                 * <p>
-                 * Subsequent calls to ContentProvider.merge() incoporate the
-                 * result of previous SyncAdapter.merge() calls into the
-                 * real ContentProvider and again return a temporary
-                 * ContentProvider that contains any local changes that need
-                 * to be committed to the server.
-                 */
-                loopCount = 0;
-                boolean readOnly = isReadOnly();
-                long previousNumModifications = 0;
-                if (serverDiffs != null) {
-                    serverDiffs.close();
-                    serverDiffs = null;
-                }
-
-                // If we are discarding local deletions then we need to redownload all the items
-                // again (since some of them might have been deleted). We do this by deleting the
-                // sync data for the current account by writing in a null one.
-                if (discardLocalDeletions) {
-                    serverDiffs = mProvider.getTemporaryInstance();
-                    initTempProvider(serverDiffs);
-                    writeSyncData(null, serverDiffs);
-                }
-
-                while (!mIsCanceled) {
-                    if (Config.LOGV) {
-                        Log.v(TAG, "runSyncLoop: Merging diffs from server to client");
-                    }
-                    if (result.tempContentProvider != null) {
-                        result.tempContentProvider.close();
-                        result.tempContentProvider = null;
-                    }
-                    mResult.clear();
-                    mProvider.merge(syncContext, serverDiffs, readOnly ? null : result,
-                            mResult);
-                    if (mIsCanceled) return;
-                    if (Log.isLoggable(TAG, Log.VERBOSE)) {
-                        Log.v(TAG, "runSyncLoop: result: " + mResult);
-                    }
-
-                    SyncableContentProvider clientDiffs =
-                            readOnly ? null : result.tempContentProvider;
-                    if (clientDiffs == null) {
-                        // Nothing to commit back to the server
-                        if (Config.LOGV) Log.v(TAG, "runSyncLoop: No client diffs");
-                        break;
-                    }
-
-                    long numModifications = mResult.stats.numUpdates
-                            + mResult.stats.numDeletes
-                            + mResult.stats.numInserts;
-
-                    // as long as we are making progress keep resetting the loop count
-                    if (numModifications < previousNumModifications) {
-                        loopCount = 0;
-                    }
-                    previousNumModifications = numModifications;
-
-                    // Don't let a bad sync go forever
-                    if (loopCount++ >= MAX_UPLOAD_CHANGES_LOOP_COUNT) {
-                        Log.e(TAG, "runSyncLoop: Hit max loop count while syncing "
-                                + getClass().getName());
-                        mResult.tooManyRetries = true;
-                        break;
-                    }
-
-                    if (!overrideTooManyDeletions && !discardLocalDeletions
-                            && hasTooManyDeletions(mResult.stats)) {
-                        if (Config.LOGD) {
-                            Log.d(TAG, "runSyncLoop: Too many deletions were found in provider "
-                                    + getClass().getName() + ", not doing any more updates");
-                        }
-                        long numDeletes = mResult.stats.numDeletes;
-                        mResult.stats.clear();
-                        mResult.tooManyDeletions = true;
-                        mResult.stats.numDeletes = numDeletes;
-                        break;
-                    }
-
-                    if (Config.LOGV) Log.v(TAG, "runSyncLoop: Merging diffs from client to server");
-                    if (serverDiffs != null) serverDiffs.close();
-                    serverDiffs = clientDiffs.getTemporaryInstance();
-                    initTempProvider(serverDiffs);
-                    mResult.clear();
-                    sendClientDiffs(syncContext, clientDiffs, serverDiffs, mResult,
-                            discardLocalDeletions);
-                    if (Log.isLoggable(TAG, Log.VERBOSE)) {
-                        Log.v(TAG, "runSyncLoop: result: " + mResult);
-                    }
-
-                    if (!mResult.madeSomeProgress()) {
-                        if (Log.isLoggable(TAG, Log.VERBOSE)) {
-                            Log.v(TAG, "runSyncLoop: No data from client diffs merge");
-                        }
-                        break;
-                    }
-                    if (Log.isLoggable(TAG, Log.VERBOSE)) {
-                        Log.v(TAG, "runSyncLoop: made some progress, looping");
-                    }
-                }
-
-                // add in any status codes that we saved from earlier
-                mResult.tooManyRetries |= tooManyGetServerDiffsAttempts;
-                if (Log.isLoggable(TAG, Log.VERBOSE)) {
-                    Log.v(TAG, "runSyncLoop: final result: " + mResult);
-                }
-            } finally {
-                // do this in the finally block to guarantee that is is set and not overwritten
-                if (discardLocalDeletions) {
-                    mResult.fullSyncRequested = true;
-                }
-                if (serverDiffs != null) serverDiffs.close();
-                if (result.tempContentProvider != null) result.tempContentProvider.close();
-                syncTimer.addSplit("stop");
-                syncTimer.dumpToLog();
-            }
-        }
-    }
-
-    /**
-     * Logs details on the sync.
-     * Normally this will be overridden by a subclass that will provide
-     * provider-specific details.
-     * 
-     * @param bytesSent number of bytes the sync sent over the network
-     * @param bytesReceived number of bytes the sync received over the network
-     * @param result The SyncResult object holding info on the sync
-     */
-    protected void logSyncDetails(long bytesSent, long bytesReceived, SyncResult result) {
-        EventLog.writeEvent(SyncAdapter.LOG_SYNC_DETAILS, TAG, bytesSent, bytesReceived, "");
-    }
-
-    public void startSync(SyncContext syncContext, Account account, String authority,
-            Bundle extras) {
-        if (mSyncThread != null) {
-            syncContext.onFinished(SyncResult.ALREADY_IN_PROGRESS);
-            return;
-        }
-
-        mSyncThread = new SyncThread(syncContext, account, authority, extras);
-        mSyncThread.start();
-    }
-
-    public void cancelSync() {
-        if (mSyncThread != null) {
-            mSyncThread.cancelSync();
-        }
-    }
-
-    protected boolean hasTooManyDeletions(SyncStats stats) {
-        long numEntries = stats.numEntries;
-        long numDeletedEntries = stats.numDeletes;
-
-        long percentDeleted = (numDeletedEntries == 0)
-                ? 0
-                : (100 * numDeletedEntries /
-                        (numEntries + numDeletedEntries));
-        boolean tooManyDeletions =
-                (numDeletedEntries > NUM_ALLOWED_SIMULTANEOUS_DELETIONS)
-                && (percentDeleted > PERCENT_ALLOWED_SIMULTANEOUS_DELETIONS);
-        return tooManyDeletions;
-    }
-}
diff --git a/core/java/android/content/TempProviderSyncResult.java b/core/java/android/content/TempProviderSyncResult.java
deleted file mode 100644
index 81f6f79..0000000
--- a/core/java/android/content/TempProviderSyncResult.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.content;
-
-/**
- * Used to hold data returned from a given phase of a TempProviderSync.
- * @hide
- */
-public class TempProviderSyncResult {
-    /**
-     * An interface to a temporary content provider that contains
-     * the result of updates that were sent to the server. This
-     * provider must be merged into the permanent content provider.
-     * This may be null, which indicates that there is nothing to
-     * merge back into the content provider.
-     */
-    public SyncableContentProvider tempContentProvider;
-
-    public TempProviderSyncResult() {
-        tempContentProvider = null;
-    }
-}
diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java
index 117de15..b5c9900 100644
--- a/core/java/android/provider/ContactsContract.java
+++ b/core/java/android/provider/ContactsContract.java
@@ -30,6 +30,7 @@
 import android.content.res.Resources;
 import android.database.Cursor;
 import android.database.DatabaseUtils;
+import android.database.sqlite.SQLiteException;
 import android.graphics.Rect;
 import android.net.Uri;
 import android.os.RemoteException;
@@ -1447,7 +1448,11 @@
                         if (cursor.isNull(columnIndex)) {
                             // don't put anything
                         } else {
-                            cv.put(key, cursor.getString(columnIndex));
+                            try {
+                                cv.put(key, cursor.getString(columnIndex));
+                            } catch (SQLiteException e) {
+                                cv.put(key, cursor.getBlob(columnIndex));
+                            }
                         }
                         // TODO: go back to this version of the code when bug
                         // http://b/issue?id=2306370 is fixed.
diff --git a/preloaded-classes b/preloaded-classes
index a50ccc8..762bac6 100644
--- a/preloaded-classes
+++ b/preloaded-classes
@@ -126,9 +126,6 @@
 android.bluetooth.IBluetoothHeadset$Stub$Proxy
 android.bluetooth.IBluetoothPbap$Stub
 android.bluetooth.ScoSocket
-android.content.AbstractSyncableContentProvider
-android.content.AbstractSyncableContentProvider$DatabaseHelper
-android.content.AbstractTableMerger
 android.content.AsyncQueryHandler
 android.content.BroadcastReceiver
 android.content.ComponentName
@@ -161,15 +158,9 @@
 android.content.IntentSender$1
 android.content.SearchRecentSuggestionsProvider
 android.content.SearchRecentSuggestionsProvider$DatabaseHelper
-android.content.SyncAdapter
-android.content.SyncAdapter$Transport
 android.content.SyncAdapterType
 android.content.SyncResult
-android.content.SyncStateContentProviderHelper
 android.content.SyncStats
-android.content.SyncableContentProvider
-android.content.TempProviderSyncAdapter
-android.content.TempProviderSyncAdapter$SyncThread
 android.content.UriMatcher
 android.content.pm.ActivityInfo
 android.content.pm.ApplicationInfo
diff --git a/tests/FrameworkTest/tests/src/android/content/AbstractTableMergerTest.java b/tests/FrameworkTest/tests/src/android/content/AbstractTableMergerTest.java
deleted file mode 100644
index a8af7f8..0000000
--- a/tests/FrameworkTest/tests/src/android/content/AbstractTableMergerTest.java
+++ /dev/null
@@ -1,587 +0,0 @@
-package android.content;
-
-import com.google.android.collect.Lists;
-import com.google.android.collect.Sets;
-
-import android.database.Cursor;
-import android.database.sqlite.SQLiteDatabase;
-import android.net.Uri;
-import android.test.AndroidTestCase;
-import android.text.TextUtils;
-import android.accounts.Account;
-
-import java.util.ArrayList;
-import java.util.Map;
-import java.util.SortedSet;
-
-/** Unit test for {@link android.content.AbstractTableMerger}. */
-public class AbstractTableMergerTest extends AndroidTestCase {
-    MockSyncableContentProvider mRealProvider;
-    MockSyncableContentProvider mTempProvider;
-    MockTableMerger mMerger;
-    MockSyncContext mSyncContext;
-
-    static final String TABLE_NAME = "items";
-    static final String DELETED_TABLE_NAME = "deleted_items";
-    static final Uri CONTENT_URI = Uri.parse("content://testdata");
-    static final Uri TABLE_URI = Uri.withAppendedPath(CONTENT_URI, TABLE_NAME);
-    static final Uri DELETED_TABLE_URI = Uri.withAppendedPath(CONTENT_URI, DELETED_TABLE_NAME);
-
-    private final Account ACCOUNT = new Account("account@goo.com", "example.type");
-
-    private final ArrayList<Expectation> mExpectations = Lists.newArrayList();
-
-    static class Expectation {
-        enum Type {
-            UPDATE,
-            INSERT,
-            DELETE,
-            RESOLVE
-        }
-
-        Type mType;
-        ContentValues mValues;
-        Long mLocalRowId;
-
-        Expectation(Type type, Long localRowId, ContentValues values) {
-            mType = type;
-            mValues = values;
-            mLocalRowId = localRowId;
-            if (type == Type.DELETE) {
-                assertNull(values);
-            } else {
-                assertFalse(values.containsKey("_id"));
-            }
-        }
-    }
-
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-        mSyncContext = new MockSyncContext();
-        mRealProvider = new MockSyncableContentProvider();
-        mTempProvider = mRealProvider.getTemporaryInstance();
-        mMerger = new MockTableMerger(mRealProvider.getDatabase(),
-                TABLE_NAME, TABLE_URI, DELETED_TABLE_NAME, DELETED_TABLE_URI);
-        mExpectations.clear();
-    }
-
-    ContentValues newValues(String data, String syncId, Account syncAccount,
-            String syncTime, String syncVersion, Long syncLocalId) {
-        ContentValues values = new ContentValues();
-        if (data != null) values.put("data", data);
-        if (syncTime != null) values.put("_sync_time", syncTime);
-        if (syncVersion != null) values.put("_sync_version", syncVersion);
-        if (syncId != null) values.put("_sync_id", syncId);
-        if (syncAccount != null) {
-            values.put("_sync_account", syncAccount.name);
-            values.put("_sync_account_type", syncAccount.type);
-        }
-        values.put("_sync_local_id", syncLocalId);
-        values.put("_sync_dirty", 0);
-        return values;
-    }
-
-    ContentValues newDeletedValues(String syncId, Account syncAccount, String syncVersion,
-            Long syncLocalId) {
-        ContentValues values = new ContentValues();
-        if (syncVersion != null) values.put("_sync_version", syncVersion);
-        if (syncId != null) values.put("_sync_id", syncId);
-        if (syncAccount != null) {
-            values.put("_sync_account", syncAccount.name);
-            values.put("_sync_account_type", syncAccount.type);
-        }
-        if (syncLocalId != null) values.put("_sync_local_id", syncLocalId);
-        return values;
-    }
-
-    ContentValues newModifyData(String data) {
-        ContentValues values = new ContentValues();
-        values.put("data", data);
-        values.put("_sync_dirty", 1);
-        return values;
-    }
-
-    // Want to test adding, changing, deleting entries to a provider that has extra entries
-    // before and after the entries being changed.
-    public void testInsert() {
-        // add rows to the real provider
-        // add new row to the temp provider
-        final ContentValues row1 = newValues("d1", "si1", ACCOUNT, "st1", "sv1", null);
-        mTempProvider.insert(TABLE_URI, row1);
-
-        // add expected callbacks to merger
-        mExpectations.add(new Expectation(Expectation.Type.INSERT, null /* syncLocalId */, row1));
-
-        // run merger
-        SyncResult syncResult = new SyncResult();
-        mMerger.mergeServerDiffs(mSyncContext, ACCOUNT, mTempProvider, syncResult);
-
-        // check that all expectations were met
-        assertEquals("not all expectations were met", 0, mExpectations.size());
-    }
-
-    public void testUpdateWithLocalId() {
-        // add rows to the real provider
-        // add new row to the temp provider that matches an unsynced row in the real provider
-        final ContentValues row1 = newValues("d1", "si1", ACCOUNT, "st1", "sv1", 11L);
-        mTempProvider.insert(TABLE_URI, row1);
-
-        // add expected callbacks to merger
-        mExpectations.add(new Expectation(Expectation.Type.UPDATE, 11L, row1));
-
-        // run merger
-        SyncResult syncResult = new SyncResult();
-        mMerger.mergeServerDiffs(mSyncContext, ACCOUNT, mTempProvider, syncResult);
-
-        // check that all expectations were met
-        assertEquals("not all expectations were met", 0, mExpectations.size());
-    }
-
-    public void testUpdateWithoutLocalId() {
-        // add rows to the real provider
-        Uri i1 = mRealProvider.insert(TABLE_URI,
-                newValues("d1", "si1", ACCOUNT, "st1", "sv1", null));
-
-        // add new row to the temp provider that matches an unsynced row in the real provider
-        final ContentValues row1 = newValues("d2", "si1", ACCOUNT, "st2", "sv2", null);
-        mTempProvider.insert(TABLE_URI, row1);
-
-        // add expected callbacks to merger
-        mExpectations.add(new Expectation(Expectation.Type.UPDATE, ContentUris.parseId(i1), row1));
-
-        // run merger
-        SyncResult syncResult = new SyncResult();
-        mMerger.mergeServerDiffs(mSyncContext, ACCOUNT, mTempProvider, syncResult);
-
-        // check that all expectations were met
-        assertEquals("not all expectations were met", 0, mExpectations.size());
-    }
-
-    public void testResolve() {
-        // add rows to the real provider
-        Uri i1 = mRealProvider.insert(TABLE_URI,
-                newValues("d1", "si1", ACCOUNT, "st1", "sv1", null));
-        mRealProvider.update(TABLE_URI, newModifyData("d2"), null, null);
-
-        // add row to the temp provider that matches a dirty, synced row in the real provider
-        final ContentValues row1 = newValues("d3", "si1", ACCOUNT, "st2", "sv2", null);
-        mTempProvider.insert(TABLE_URI, row1);
-
-        // add expected callbacks to merger
-        mExpectations.add(new Expectation(Expectation.Type.RESOLVE, ContentUris.parseId(i1), row1));
-
-        // run merger
-        SyncResult syncResult = new SyncResult();
-        mMerger.mergeServerDiffs(mSyncContext, ACCOUNT, mTempProvider, syncResult);
-
-        // check that all expectations were met
-        assertEquals("not all expectations were met", 0, mExpectations.size());
-    }
-
-    public void testResolveWithLocalId() {
-        // add rows to the real provider
-        Uri i1 = mRealProvider.insert(TABLE_URI,
-                newValues("d1", "si1", ACCOUNT, "st1", "sv1", null));
-        mRealProvider.update(TABLE_URI, newModifyData("d2"), null, null);
-
-        // add row to the temp provider that matches a dirty, synced row in the real provider
-        ContentValues row1 = newValues("d2", "si1", ACCOUNT, "st2", "sv2", ContentUris.parseId(i1));
-        mTempProvider.insert(TABLE_URI, row1);
-
-        // add expected callbacks to merger
-        mExpectations.add(new Expectation(Expectation.Type.UPDATE, ContentUris.parseId(i1), row1));
-
-        // run merger
-        SyncResult syncResult = new SyncResult();
-        mMerger.mergeServerDiffs(mSyncContext, ACCOUNT, mTempProvider, syncResult);
-
-        // check that all expectations were met
-        assertEquals("not all expectations were met", 0, mExpectations.size());
-    }
-
-    public void testDeleteRowAfterDelete() {
-        // add rows to the real provider
-        Uri i1 = mRealProvider.insert(TABLE_URI,
-                newValues("d1", "si1", ACCOUNT, "st1", "sv1", null));
-
-        // add a deleted record to the temp provider
-        ContentValues row1 = newDeletedValues(null, null, null, ContentUris.parseId(i1));
-        mTempProvider.insert(DELETED_TABLE_URI, row1);
-
-        // add expected callbacks to merger
-        mExpectations.add(new Expectation(Expectation.Type.DELETE, ContentUris.parseId(i1), null));
-
-        // run merger
-        SyncResult syncResult = new SyncResult();
-        mMerger.mergeServerDiffs(mSyncContext, ACCOUNT, mTempProvider, syncResult);
-
-        // check that all expectations were met
-        assertEquals("not all expectations were met", 0, mExpectations.size());
-    }
-
-    public void testDeleteRowAfterInsert() {
-        // add rows to the real provider
-        Uri i1 = mRealProvider.insert(TABLE_URI, newModifyData("d1"));
-
-        // add a deleted record to the temp provider
-        ContentValues row1 = newDeletedValues(null, null, null, ContentUris.parseId(i1));
-        mTempProvider.insert(DELETED_TABLE_URI, row1);
-
-        // add expected callbacks to merger
-        mExpectations.add(new Expectation(Expectation.Type.DELETE, ContentUris.parseId(i1), null));
-
-        // run merger
-        SyncResult syncResult = new SyncResult();
-        mMerger.mergeServerDiffs(mSyncContext, ACCOUNT, mTempProvider, syncResult);
-
-        // check that all expectations were met
-        assertEquals("not all expectations were met", 0, mExpectations.size());
-    }
-
-    public void testDeleteRowAfterUpdate() {
-        // add rows to the real provider
-        Uri i1 = mRealProvider.insert(TABLE_URI,
-                newValues("d1", "si1", ACCOUNT, "st1", "sv1", null));
-
-        // add a deleted record to the temp provider
-        ContentValues row1 = newDeletedValues("si1", ACCOUNT, "sv1", ContentUris.parseId(i1));
-        mTempProvider.insert(DELETED_TABLE_URI, row1);
-
-        // add expected callbacks to merger
-        mExpectations.add(new Expectation(Expectation.Type.DELETE, ContentUris.parseId(i1), null));
-
-        // run merger
-        SyncResult syncResult = new SyncResult();
-        mMerger.mergeServerDiffs(mSyncContext, ACCOUNT, mTempProvider, syncResult);
-
-        // check that all expectations were met
-        assertEquals("not all expectations were met", 0, mExpectations.size());
-    }
-
-    public void testDeleteRowFromServer() {
-        // add rows to the real provider
-        Uri i1 = mRealProvider.insert(TABLE_URI,
-                newValues("d1", "si1", ACCOUNT, "st1", "sv1", null));
-
-        // add a deleted record to the temp provider
-        ContentValues row1 = newDeletedValues("si1", ACCOUNT, "sv1", null);
-        mTempProvider.insert(DELETED_TABLE_URI, row1);
-
-        // add expected callbacks to merger
-        mExpectations.add(new Expectation(Expectation.Type.DELETE, ContentUris.parseId(i1), null));
-
-        // run merger
-        SyncResult syncResult = new SyncResult();
-        mMerger.mergeServerDiffs(mSyncContext, ACCOUNT, mTempProvider, syncResult);
-
-        // check that all expectations were met
-        assertEquals("not all expectations were met", 0, mExpectations.size());
-    }
-
-    class MockTableMerger extends AbstractTableMerger {
-        public MockTableMerger(SQLiteDatabase database, String table, Uri tableURL,
-                String deletedTable, Uri deletedTableURL) {
-            super(database, table, tableURL, deletedTable, deletedTableURL);
-        }
-
-        public void insertRow(ContentProvider diffs, Cursor diffsCursor) {
-            Expectation expectation = mExpectations.remove(0);
-            checkExpectation(expectation,
-                    Expectation.Type.INSERT, null /* syncLocalId */, diffsCursor);
-        }
-
-        public void updateRow(long localPersonID, ContentProvider diffs, Cursor diffsCursor) {
-            Expectation expectation = mExpectations.remove(0);
-            checkExpectation(expectation, Expectation.Type.UPDATE, localPersonID, diffsCursor);
-        }
-
-        public void resolveRow(long localPersonID, String syncID, ContentProvider diffs,
-                Cursor diffsCursor) {
-            Expectation expectation = mExpectations.remove(0);
-            checkExpectation(expectation, Expectation.Type.RESOLVE, localPersonID, diffsCursor);
-        }
-
-        @Override
-        public void deleteRow(Cursor cursor) {
-            Expectation expectation = mExpectations.remove(0);
-            assertEquals(expectation.mType, Expectation.Type.DELETE);
-            assertNotNull(expectation.mLocalRowId);
-            final long localRowId = cursor.getLong(cursor.getColumnIndexOrThrow("_id"));
-            assertEquals((long)expectation.mLocalRowId, localRowId);
-            cursor.moveToNext();
-            mDb.delete(TABLE_NAME, "_id=" + localRowId, null);
-        }
-
-        protected void notifyChanges() {
-            throw new UnsupportedOperationException();
-        }
-
-        void checkExpectation(Expectation expectation,
-                Expectation.Type actualType, Long localRowId,
-                Cursor cursor) {
-            assertEquals(expectation.mType, actualType);
-            assertEquals(expectation.mLocalRowId, localRowId);
-
-            final SortedSet<String> actualKeys = Sets.newSortedSet(cursor.getColumnNames());
-            final SortedSet<String> expectedKeys = Sets.newSortedSet();
-            for (Map.Entry<String, Object> entry : expectation.mValues.valueSet()) {
-                expectedKeys.add(entry.getKey());
-            }
-            actualKeys.remove("_id");
-            actualKeys.remove("_sync_mark");
-            actualKeys.remove("_sync_local_id");
-            expectedKeys.remove("_sync_local_id");
-            expectedKeys.remove("_id");
-            assertEquals("column mismatch",
-                    TextUtils.join(",", expectedKeys), TextUtils.join(",", actualKeys));
-
-//            if (localRowId != null) {
-//                assertEquals((long) localRowId,
-//                        cursor.getLong(cursor.getColumnIndexOrThrow("_sync_local_id")));
-//            } else {
-//                assertTrue("unexpected _sync_local_id, "
-//                        + cursor.getLong(cursor.getColumnIndexOrThrow("_sync_local_id")),
-//                        cursor.isNull(cursor.getColumnIndexOrThrow("_sync_local_id")));
-//            }
-
-            for (String name : cursor.getColumnNames()) {
-                if ("_id".equals(name)) {
-                    continue;
-                }
-                if (cursor.isNull(cursor.getColumnIndexOrThrow(name))) {
-                    assertNull(expectation.mValues.getAsString(name));
-                } else {
-                    String actualValue =
-                            cursor.getString(cursor.getColumnIndexOrThrow(name));
-                    assertEquals("mismatch on column " + name,
-                            expectation.mValues.getAsString(name), actualValue);
-                }
-            }
-        }
-    }
-
-    class MockSyncableContentProvider extends SyncableContentProvider {
-        SQLiteDatabase mDb;
-        boolean mIsTemporary;
-        boolean mContainsDiffs;
-
-        private final UriMatcher sURIMatcher = new UriMatcher(UriMatcher.NO_MATCH);
-
-        private static final int MATCHER_ITEMS = 0;
-        private static final int MATCHER_DELETED_ITEMS = 1;
-
-        public MockSyncableContentProvider() {
-            mIsTemporary = false;
-            setContainsDiffs(false);
-            sURIMatcher.addURI(CONTENT_URI.getAuthority(), "items", MATCHER_ITEMS);
-            sURIMatcher.addURI(CONTENT_URI.getAuthority(), "deleted_items", MATCHER_DELETED_ITEMS);
-
-            mDb = SQLiteDatabase.create(null);
-            mDb.execSQL("CREATE TABLE items ("
-                    + "_id INTEGER PRIMARY KEY AUTOINCREMENT, "
-                    + "data TEXT, "
-                    + "_sync_time TEXT, "
-                    + "_sync_version TEXT, "
-                    + "_sync_id TEXT, "
-                    + "_sync_local_id INTEGER, "
-                    + "_sync_dirty INTEGER NOT NULL DEFAULT 0, "
-                    + "_sync_account TEXT, "
-                    + "_sync_account_type TEXT, "
-                    + "_sync_mark INTEGER)");
-
-            mDb.execSQL("CREATE TABLE deleted_items ("
-                    + "_id INTEGER PRIMARY KEY AUTOINCREMENT, "
-                    + "_sync_version TEXT, "
-                    + "_sync_id TEXT, "
-                    + "_sync_local_id INTEGER, "
-                    + "_sync_account TEXT, "
-                    + "_sync_account_type TEXT, "
-                    + "_sync_mark INTEGER)");
-        }
-
-        public boolean onCreate() {
-            throw new UnsupportedOperationException();
-        }
-
-        public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
-                String sortOrder) {
-            int match = sURIMatcher.match(uri);
-            switch (match) {
-                case MATCHER_ITEMS:
-                    return mDb.query(TABLE_NAME, projection, selection, selectionArgs,
-                            null, null, sortOrder);
-                case MATCHER_DELETED_ITEMS:
-                    return mDb.query(DELETED_TABLE_NAME, projection, selection, selectionArgs,
-                            null, null, sortOrder);
-                default:
-                    throw new UnsupportedOperationException("Cannot query URL: " + uri);
-            }
-        }
-
-        public String getType(Uri uri) {
-            throw new UnsupportedOperationException();
-        }
-
-        public Uri insert(Uri uri, ContentValues values) {
-            int match = sURIMatcher.match(uri);
-            switch (match) {
-                case MATCHER_ITEMS: {
-                    long id = mDb.insert(TABLE_NAME, "_id", values);
-                    return CONTENT_URI.buildUpon().appendPath(String.valueOf(id)).build();
-                }
-                case MATCHER_DELETED_ITEMS: {
-                    long id = mDb.insert(DELETED_TABLE_NAME, "_id", values);
-                    return CONTENT_URI.buildUpon().appendPath(String.valueOf(id)).build();
-                }
-                default:
-                    throw new UnsupportedOperationException("Cannot query URL: " + uri);
-            }
-        }
-
-        public int delete(Uri uri, String selection, String[] selectionArgs) {
-            int match = sURIMatcher.match(uri);
-            switch (match) {
-                case MATCHER_ITEMS:
-                    return mDb.delete(TABLE_NAME, selection, selectionArgs);
-                case MATCHER_DELETED_ITEMS:
-                    return mDb.delete(DELETED_TABLE_NAME, selection, selectionArgs);
-                default:
-                    throw new UnsupportedOperationException("Cannot query URL: " + uri);
-            }
-        }
-
-        public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
-            int match = sURIMatcher.match(uri);
-            switch (match) {
-                case MATCHER_ITEMS:
-                    return mDb.update(TABLE_NAME, values, selection, selectionArgs);
-                case MATCHER_DELETED_ITEMS:
-                    return mDb.update(DELETED_TABLE_NAME, values, selection, selectionArgs);
-                default:
-                    throw new UnsupportedOperationException("Cannot query URL: " + uri);
-            }
-        }
-
-        protected boolean isTemporary() {
-            return mIsTemporary;
-        }
-
-        public void close() {
-            throw new UnsupportedOperationException();
-        }
-
-        protected void bootstrapDatabase(SQLiteDatabase db) {
-            throw new UnsupportedOperationException();
-        }
-
-        protected boolean upgradeDatabase(SQLiteDatabase db, int oldVersion, int newVersion) {
-            throw new UnsupportedOperationException();
-        }
-
-        protected void onDatabaseOpened(SQLiteDatabase db) {
-            throw new UnsupportedOperationException();
-        }
-
-        public MockSyncableContentProvider getTemporaryInstance() {
-            MockSyncableContentProvider temp = new MockSyncableContentProvider();
-            temp.mIsTemporary = true;
-            temp.setContainsDiffs(true);
-            return temp;
-        }
-
-        public SQLiteDatabase getDatabase() {
-            return mDb;
-        }
-
-        public boolean getContainsDiffs() {
-            return mContainsDiffs;
-        }
-
-        public void setContainsDiffs(boolean containsDiffs) {
-            mContainsDiffs = containsDiffs;
-        }
-
-        protected Iterable<? extends AbstractTableMerger> getMergers() {
-            throw new UnsupportedOperationException();
-        }
-
-        public boolean changeRequiresLocalSync(Uri uri) {
-            throw new UnsupportedOperationException();
-        }
-
-        public void onSyncStart(SyncContext context, Account account) {
-            throw new UnsupportedOperationException();
-        }
-
-        public void onSyncStop(SyncContext context, boolean success) {
-            throw new UnsupportedOperationException();
-        }
-
-        public Account getSyncingAccount() {
-            throw new UnsupportedOperationException();
-        }
-
-        public void merge(SyncContext context, SyncableContentProvider diffs,
-                TempProviderSyncResult result, SyncResult syncResult) {
-            throw new UnsupportedOperationException();
-        }
-
-        public void onSyncCanceled() {
-            throw new UnsupportedOperationException();
-        }
-
-        public boolean isMergeCancelled() {
-            return false;
-        }
-
-        protected int updateInternal(Uri url, ContentValues values, String selection,
-                String[] selectionArgs) {
-            throw new UnsupportedOperationException();
-        }
-
-        protected int deleteInternal(Uri url, String selection, String[] selectionArgs) {
-            throw new UnsupportedOperationException();
-        }
-
-        protected Uri insertInternal(Uri url, ContentValues values) {
-            throw new UnsupportedOperationException();
-        }
-
-        protected Cursor queryInternal(Uri url, String[] projection, String selection,
-                String[] selectionArgs, String sortOrder) {
-            throw new UnsupportedOperationException();
-        }
-
-        protected void onAccountsChanged(Account[] accountsArray) {
-            throw new UnsupportedOperationException();
-        }
-
-        protected void deleteRowsForRemovedAccounts(Map<Account, Boolean> accounts, String table
-        ) {
-            throw new UnsupportedOperationException();
-        }
-
-        public void wipeAccount(Account account) {
-            throw new UnsupportedOperationException();
-        }
-
-        public byte[] readSyncDataBytes(Account account) {
-            throw new UnsupportedOperationException();
-        }
-
-        public void writeSyncDataBytes(Account account, byte[] data) {
-            throw new UnsupportedOperationException();
-        }
-    }
-
-    class MockSyncContext extends SyncContext {
-        public MockSyncContext() {
-            super(null);
-        }
-
-        @Override
-        public void setStatusText(String message) {
-        }
-    }
-}