Add CTS for SyncManager / ContentResolver
- fix imports
- took care of Ken feedbak
	- fixed variables naming
	- fixed comments and copyright year and trailing whitespace
- fixed some other redundant whitespaces
- took care of Fred feedback
	- code formatting
- changed MockSyncAdapter so that it does not use SyncAdapter class anymore
- suppress non needed assert on extra Bundle when init in startSync

Change-Id: I49de8e101d86e5a3ed59eb3b34a5f6aa4bd580b0
diff --git a/tests/tests/content/AndroidManifest.xml b/tests/tests/content/AndroidManifest.xml
index a32bb78..882f080 100644
--- a/tests/tests/content/AndroidManifest.xml
+++ b/tests/tests/content/AndroidManifest.xml
@@ -1,4 +1,3 @@
-<?xml version="1.0" encoding="utf-8"?>
 <!--
  * Copyright (C) 2007 The Android Open Source Project
  *
@@ -16,15 +15,41 @@
  -->
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="com.android.cts.content">
+    package="android.content.cts">
+
+    <uses-permission android:name="android.permission.MANAGE_ACCOUNTS" />
+    <uses-permission android:name="android.permission.AUTHENTICATE_ACCOUNTS" />
+    <uses-permission android:name="android.permission.GET_ACCOUNTS" />
+    <uses-permission android:name="android.permission.USE_CREDENTIALS" />
+    <uses-permission android:name="android.permission.READ_SYNC_SETTINGS" />
+    <uses-permission android:name="android.permission.WRITE_SYNC_SETTINGS" />
+    <uses-permission android:name="android.permission.READ_SYNC_STATS" />
 
     <application>
         <uses-library android:name="android.test.runner" />
+
+        <service android:name="MockSyncAdapterService" android:exported="true">
+            <intent-filter>
+                <action android:name="android.content.SyncAdapter" />
+            </intent-filter>
+
+            <meta-data android:name="android.content.SyncAdapter"
+                       android:resource="@xml/syncadapter" />
+        </service>
+
+        <service android:name="MockAccountService" android:exported="true"
+                 android:process="android.content.cts">
+            <intent-filter>
+                <action android:name="android.accounts.AccountAuthenticator" />
+            </intent-filter>
+
+            <meta-data android:name="android.accounts.AccountAuthenticator"
+                       android:resource="@xml/authenticator" />
+        </service>
     </application>
 
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
-                     android:targetPackage="com.android.cts.stub"
+    <instrumentation android:name="android.test.InstrumentationTestRunner"
+                     android:targetPackage="android.content.cts"
                      android:label="CTS tests of android.content"/>
-
 </manifest>
 
diff --git a/tests/tests/content/res/drawable/ic_cts_minitab_selected.png b/tests/tests/content/res/drawable/ic_cts_minitab_selected.png
new file mode 100644
index 0000000..c730050
--- /dev/null
+++ b/tests/tests/content/res/drawable/ic_cts_minitab_selected.png
Binary files differ
diff --git a/tests/tests/content/res/drawable/ic_cts_selected.png b/tests/tests/content/res/drawable/ic_cts_selected.png
new file mode 100644
index 0000000..72a065c
--- /dev/null
+++ b/tests/tests/content/res/drawable/ic_cts_selected.png
Binary files differ
diff --git a/tests/tests/content/res/values/strings.xml b/tests/tests/content/res/values/strings.xml
new file mode 100644
index 0000000..2369648
--- /dev/null
+++ b/tests/tests/content/res/values/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * Copyright (C) 2010 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.
+ */
+-->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- Label for this package -->
+    <string name="label">Android CTS - Content</string>
+</resources>
\ No newline at end of file
diff --git a/tests/tests/content/res/xml/authenticator.xml b/tests/tests/content/res/xml/authenticator.xml
new file mode 100644
index 0000000..46da929
--- /dev/null
+++ b/tests/tests/content/res/xml/authenticator.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (c) 2009, 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.
+ */
+-->
+
+<!-- The attributes in this XML file provide configuration information -->
+<!-- for the Account Manager. -->
+
+<account-authenticator xmlns:android="http://schemas.android.com/apk/res/android"
+    android:accountType="android.content.cts.account.type"
+    android:icon="@drawable/ic_cts_selected"
+    android:smallIcon="@drawable/ic_cts_minitab_selected"
+    android:label="@string/label"
+/>
\ No newline at end of file
diff --git a/tests/tests/content/res/xml/syncadapter.xml b/tests/tests/content/res/xml/syncadapter.xml
new file mode 100644
index 0000000..1c0bb72
--- /dev/null
+++ b/tests/tests/content/res/xml/syncadapter.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (c) 2009, 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.
+ */
+-->
+
+<!-- The attributes in this XML file provide configuration information -->
+<!-- for the SyncAdapter. -->
+
+<sync-adapter xmlns:android="http://schemas.android.com/apk/res/android"
+    android:contentAuthority="android.content.cts.authority"
+    android:accountType="android.content.cts.account.type"
+/>
diff --git a/tests/tests/content/src/android/content/cts/ContentResolverSyncTestCase.java b/tests/tests/content/src/android/content/cts/ContentResolverSyncTestCase.java
new file mode 100644
index 0000000..df6d5c7
--- /dev/null
+++ b/tests/tests/content/src/android/content/cts/ContentResolverSyncTestCase.java
@@ -0,0 +1,323 @@
+/*
+ * Copyright (C) 2010 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.cts;
+
+import android.accounts.Account;
+import android.accounts.AccountManager;
+import android.accounts.AccountManagerCallback;
+import android.accounts.AccountManagerFuture;
+import android.accounts.AuthenticatorException;
+import android.accounts.OperationCanceledException;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.SyncAdapterType;
+import android.os.Bundle;
+import android.test.AndroidTestCase;
+
+import java.io.IOException;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+public class ContentResolverSyncTestCase extends AndroidTestCase {
+
+    public static final String ACCOUNT_NAME = "android.content.cts.account.name";
+    public static final String ACCOUNT_TYPE = "android.content.cts.account.type";
+    public static final String ACCOUNT_PASSWORD = "android.content.cts.account.password";
+
+    public static final String AUTH_TOKEN = "mockAuthToken";
+    public static final String AUTH_TOKEN_LABEL = "mockAuthTokenLabel";
+
+    private static final String AUTHORITY = "android.content.cts.authority";
+
+    private static final Account ACCOUNT = new Account(ACCOUNT_NAME, ACCOUNT_TYPE);
+
+    private static final int LATCH_TIMEOUT_MS = 5000;
+
+    private static MockSyncAdapter sSyncAdapter;
+    private static AccountManager sAccountManager;
+    private static MockAccountAuthenticator sMockAuthenticator;
+
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        getMockSyncAdapter();
+        sAccountManager = AccountManager.get(getContext());
+    }
+
+    @Override
+    public void tearDown() throws Exception {
+        sSyncAdapter.clearData();
+
+        // Need to clean up created account
+        removeAccount(sAccountManager, ACCOUNT, null /* callback */);
+
+        super.tearDown();
+    }
+
+    public static synchronized MockSyncAdapter getMockSyncAdapter() {
+        if (null == sSyncAdapter) {
+            sSyncAdapter = new MockSyncAdapter();
+        }
+        return sSyncAdapter;
+    }
+
+    public static synchronized MockAccountAuthenticator getMockAuthenticator(Context context) {
+        if (null == sMockAuthenticator) {
+            sMockAuthenticator = new MockAccountAuthenticator(context);
+        }
+        return sMockAuthenticator;
+    }
+
+    private void addAccountExplicitly(Account account, String password, Bundle userdata) {
+        assertTrue(sAccountManager.addAccountExplicitly(account, password, userdata));
+    }
+
+    private boolean removeAccount(AccountManager am, Account account,
+            AccountManagerCallback<Boolean> callback) throws IOException, AuthenticatorException,
+                OperationCanceledException {
+
+        AccountManagerFuture<Boolean> futureBoolean = am.removeAccount(account,
+                callback,
+                null /* handler */);
+        Boolean resultBoolean = futureBoolean.getResult();
+        assertTrue(futureBoolean.isDone());
+
+        return resultBoolean;
+    }
+
+    private CountDownLatch setNewLatch(CountDownLatch latch) {
+        sSyncAdapter.setLatch(latch);
+        sSyncAdapter.clearData();
+        return latch;
+    }
+
+    private void addAccountAndVerifyInitSync(Account account, String password,
+            String authority, int latchTimeoutMs) {
+
+        CountDownLatch latch = setNewLatch(new CountDownLatch(1));
+
+        addAccountExplicitly(account, password, null /* userData */);
+
+        // Wait with timeout for the callback to do its work
+        try {
+            latch.await(latchTimeoutMs, TimeUnit.MILLISECONDS);
+        } catch (InterruptedException e) {
+            fail("should not throw an InterruptedException");
+        }
+
+        assertFalse(sSyncAdapter.isStartSync());
+        assertFalse(sSyncAdapter.isCancelSync());
+        assertTrue(sSyncAdapter.isInitialized());
+        assertEquals(account, sSyncAdapter.getAccount());
+        assertEquals(authority, sSyncAdapter.getAuthority());
+    }
+
+    private void cancelSync(Account account, String authority, int latchTimeoutMillis) {
+        CountDownLatch latch = setNewLatch(new CountDownLatch(1));
+
+        Bundle extras = new Bundle();
+        extras.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true);
+
+        ContentResolver.cancelSync(account, authority);
+
+        // Wait with timeout for the callback to do its work
+        try {
+            latch.await(latchTimeoutMillis, TimeUnit.MILLISECONDS);
+        } catch (InterruptedException e) {
+            fail("should not throw an InterruptedException");
+        }
+    }
+
+    private void requestSync(Account account, String authority, int latchTimeoutMillis) {
+        CountDownLatch latch = setNewLatch(new CountDownLatch(1));
+
+        Bundle extras = new Bundle();
+        extras.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true);
+
+        ContentResolver.requestSync(account, authority, extras);
+
+        // Wait with timeout for the callback to do its work
+        try {
+            latch.await(latchTimeoutMillis, TimeUnit.MILLISECONDS);
+        } catch (InterruptedException e) {
+            fail("should not throw an InterruptedException");
+        }
+    }
+
+    private void setIsSyncable(Account account, String authority, boolean b) {
+        ContentResolver.setIsSyncable(account, authority, (b) ? 1 : 0);
+    }
+
+    /**
+     * Test a sync request
+     */
+    public void testRequestSync() throws IOException, AuthenticatorException,
+            OperationCanceledException {
+
+        // Prevent auto sync
+        ContentResolver.setMasterSyncAutomatically(false);
+        assertEquals(false, ContentResolver.getMasterSyncAutomatically());
+
+        addAccountAndVerifyInitSync(ACCOUNT, ACCOUNT_PASSWORD, AUTHORITY, LATCH_TIMEOUT_MS);
+
+        sSyncAdapter.clearData();
+
+        setIsSyncable(ACCOUNT, AUTHORITY, true);
+        cancelSync(ACCOUNT, AUTHORITY, LATCH_TIMEOUT_MS);
+
+        sSyncAdapter.clearData();
+
+        requestSync(ACCOUNT, AUTHORITY, LATCH_TIMEOUT_MS);
+
+        assertTrue(sSyncAdapter.isStartSync());
+        assertFalse(sSyncAdapter.isCancelSync());
+        assertFalse(sSyncAdapter.isInitialized());
+        assertEquals(ACCOUNT, sSyncAdapter.getAccount());
+        assertEquals(AUTHORITY, sSyncAdapter.getAuthority());
+    }
+
+    /**
+     * Test a sync cancel
+     */
+    public void testCancelSync() throws IOException, AuthenticatorException,
+            OperationCanceledException {
+
+        // Prevent auto sync
+        ContentResolver.setMasterSyncAutomatically(false);
+        assertEquals(false, ContentResolver.getMasterSyncAutomatically());
+
+        addAccountAndVerifyInitSync(ACCOUNT, ACCOUNT_PASSWORD, AUTHORITY, LATCH_TIMEOUT_MS);
+
+        sSyncAdapter.clearData();
+
+        cancelSync(ACCOUNT, AUTHORITY, LATCH_TIMEOUT_MS);
+
+        assertFalse(sSyncAdapter.isStartSync());
+        assertTrue(sSyncAdapter.isCancelSync());
+        assertFalse(sSyncAdapter.isInitialized());
+
+        assertFalse(ContentResolver.isSyncActive(ACCOUNT, AUTHORITY));
+        assertFalse(ContentResolver.isSyncPending(ACCOUNT, AUTHORITY));
+    }
+
+    /**
+     * Test if we can set and get the MasterSyncAutomatically switch
+     */
+    public void testGetAndSetMasterSyncAutomatically() {
+        ContentResolver.setMasterSyncAutomatically(true);
+        assertEquals(true, ContentResolver.getMasterSyncAutomatically());
+
+        ContentResolver.setMasterSyncAutomatically(false);
+        assertEquals(false, ContentResolver.getMasterSyncAutomatically());
+    }
+
+    /**
+     * Test if we can set and get the SyncAutomatically switch for an account
+     */
+    public void testGetAndSetSyncAutomatically() {
+        // Prevent auto sync
+        ContentResolver.setMasterSyncAutomatically(false);
+        assertEquals(false, ContentResolver.getMasterSyncAutomatically());
+
+        ContentResolver.setSyncAutomatically(ACCOUNT, AUTHORITY, false);
+        assertEquals(false, ContentResolver.getSyncAutomatically(ACCOUNT, AUTHORITY));
+
+        ContentResolver.setSyncAutomatically(ACCOUNT, AUTHORITY, true);
+        assertEquals(true, ContentResolver.getSyncAutomatically(ACCOUNT, AUTHORITY));
+    }
+
+    /**
+     * Test if we can set and get the IsSyncable switch for an account
+     */
+    public void testGetAndSetIsSyncable() {
+        // Prevent auto sync
+        ContentResolver.setMasterSyncAutomatically(false);
+        assertEquals(false, ContentResolver.getMasterSyncAutomatically());
+
+        addAccountExplicitly(ACCOUNT, ACCOUNT_PASSWORD, null /* userData */);
+
+        ContentResolver.setIsSyncable(ACCOUNT, AUTHORITY, 2);
+        assertTrue(ContentResolver.getIsSyncable(ACCOUNT, AUTHORITY) > 0);
+
+        ContentResolver.setIsSyncable(ACCOUNT, AUTHORITY, 1);
+        assertTrue(ContentResolver.getIsSyncable(ACCOUNT, AUTHORITY) > 0);
+
+        ContentResolver.setIsSyncable(ACCOUNT, AUTHORITY, 0);
+        assertEquals(0, ContentResolver.getIsSyncable(ACCOUNT, AUTHORITY));
+
+        ContentResolver.setIsSyncable(ACCOUNT, AUTHORITY, -1);
+        assertTrue(ContentResolver.getIsSyncable(ACCOUNT, AUTHORITY) < 0);
+
+        ContentResolver.setIsSyncable(ACCOUNT, AUTHORITY, -2);
+        assertTrue(ContentResolver.getIsSyncable(ACCOUNT, AUTHORITY) < 0);
+    }
+
+    /**
+     * Test if we can get the sync adapter types
+     */
+    public void testGetSyncAdapterTypes() {
+        SyncAdapterType[] types = ContentResolver.getSyncAdapterTypes();
+        assertNotNull(types);
+        int length = types.length;
+        assertTrue(length > 0);
+        boolean found = false;
+        for (int n=0; n < length; n++) {
+            SyncAdapterType type = types[n];
+            if (ACCOUNT_TYPE.equals(type.accountType) && AUTHORITY.equals(type.authority)) {
+                found = true;
+                break;
+            }
+        }
+        assertTrue(found);
+    }
+
+    /**
+     * Test if a badly formed sync request is throwing exceptions
+     */
+    public void testStartSyncFailure() {
+        try {
+            ContentResolver.requestSync(null, null, null);
+            fail("did not throw IllegalArgumentException when extras is null.");
+        } catch (IllegalArgumentException e) {
+            //expected.
+        }
+    }
+
+    /**
+     * Test validate sync extra bundle
+     */
+    public void testValidateSyncExtrasBundle() {
+        Bundle extras = new Bundle();
+        extras.putInt("Integer", 20);
+        extras.putLong("Long", 10l);
+        extras.putBoolean("Boolean", true);
+        extras.putFloat("Float", 5.5f);
+        extras.putDouble("Double", 2.5);
+        extras.putString("String", ACCOUNT_NAME);
+        extras.putCharSequence("CharSequence", null);
+
+        ContentResolver.validateSyncExtrasBundle(extras);
+
+        extras.putChar("Char", 'a'); // type Char is invalid
+        try {
+            ContentResolver.validateSyncExtrasBundle(extras);
+            fail("did not throw IllegalArgumentException when extras is invalide.");
+        } catch (IllegalArgumentException e) {
+            //expected.
+        }
+    }
+}
diff --git a/tests/tests/content/src/android/content/cts/MockAccountAuthenticator.java b/tests/tests/content/src/android/content/cts/MockAccountAuthenticator.java
new file mode 100644
index 0000000..f1c9c38
--- /dev/null
+++ b/tests/tests/content/src/android/content/cts/MockAccountAuthenticator.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2010 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.cts;
+
+import android.accounts.AbstractAccountAuthenticator;
+import android.accounts.Account;
+import android.accounts.AccountAuthenticatorResponse;
+import android.accounts.AccountManager;
+import android.accounts.NetworkErrorException;
+import android.content.Context;
+import android.os.Bundle;
+
+public class MockAccountAuthenticator extends AbstractAccountAuthenticator {
+
+    public MockAccountAuthenticator(Context context) {
+        super(context);
+    }
+
+    private Bundle createResultBundle() {
+        Bundle result = new Bundle();
+        result.putString(AccountManager.KEY_ACCOUNT_NAME, ContentResolverSyncTestCase.ACCOUNT_NAME);
+        result.putString(AccountManager.KEY_ACCOUNT_TYPE, ContentResolverSyncTestCase.ACCOUNT_TYPE);
+        result.putString(AccountManager.KEY_AUTHTOKEN, ContentResolverSyncTestCase.AUTH_TOKEN);
+
+        return result;
+    }
+
+    @Override
+    public Bundle addAccount(AccountAuthenticatorResponse response, String accountType,
+            String authTokenType, String[] requiredFeatures, Bundle options)
+            throws NetworkErrorException {
+        return createResultBundle();
+    }
+
+    @Override
+    public Bundle editProperties(AccountAuthenticatorResponse response, String accountType) {
+        return createResultBundle();
+    }
+
+    @Override
+    public Bundle updateCredentials(AccountAuthenticatorResponse response, Account account,
+            String authTokenType, Bundle options) throws NetworkErrorException {
+        return createResultBundle();
+    }
+
+    @Override
+    public Bundle confirmCredentials(AccountAuthenticatorResponse response, Account account,
+            Bundle options) throws NetworkErrorException {
+
+        Bundle result = new Bundle();
+        result.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, true);
+        return result;
+    }
+
+    @Override
+    public Bundle getAuthToken(AccountAuthenticatorResponse response, Account account,
+            String authTokenType, Bundle options) throws NetworkErrorException {
+        return createResultBundle();
+    }
+
+    @Override
+    public String getAuthTokenLabel(String authTokenType) {
+        return ContentResolverSyncTestCase.AUTH_TOKEN_LABEL;
+    }
+
+    @Override
+    public Bundle hasFeatures(AccountAuthenticatorResponse response, Account account,
+            String[] features) throws NetworkErrorException {
+
+        Bundle result = new Bundle();
+        result.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, true);
+        return result;
+    }
+}
diff --git a/tests/tests/content/src/android/content/cts/MockAccountService.java b/tests/tests/content/src/android/content/cts/MockAccountService.java
new file mode 100644
index 0000000..4e41954
--- /dev/null
+++ b/tests/tests/content/src/android/content/cts/MockAccountService.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2010 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.cts;
+
+import android.app.Service;
+import android.content.Intent;
+import android.os.IBinder;
+
+/**
+ * a basic Mock Service for wrapping the MockAccountAuthenticator
+ */
+public class MockAccountService extends Service {
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        return ContentResolverSyncTestCase.getMockAuthenticator(this).getIBinder();
+    }
+}
diff --git a/tests/tests/content/src/android/content/cts/MockSyncAdapter.java b/tests/tests/content/src/android/content/cts/MockSyncAdapter.java
new file mode 100644
index 0000000..5be9548
--- /dev/null
+++ b/tests/tests/content/src/android/content/cts/MockSyncAdapter.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2010 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.cts;
+
+import android.accounts.Account;
+import android.content.ContentResolver;
+import android.content.ISyncAdapter;
+import android.content.ISyncContext;
+import android.os.Bundle;
+import android.os.RemoteException;
+
+import java.util.concurrent.CountDownLatch;
+
+public class MockSyncAdapter extends ISyncAdapter.Stub {
+
+    private Account mAccount;
+    private String mAuthority;
+    private Bundle mExtras;
+    private boolean mInitialized;
+    private boolean mStartSync;
+    private boolean mCancelSync;
+    private CountDownLatch mLatch;
+
+    public Account getAccount() {
+        return mAccount;
+    }
+
+    public String getAuthority() {
+        return mAuthority;
+    }
+
+    public Bundle getExtras() {
+        return mExtras;
+    }
+
+    public boolean isInitialized() {
+        return mInitialized;
+    }
+
+    public boolean isStartSync() {
+        return mStartSync;
+    }
+
+    public boolean isCancelSync() {
+        return mCancelSync;
+    }
+
+    public void clearData() {
+        mAccount = null;
+        mAuthority = null;
+        mExtras = null;
+        mInitialized = false;
+        mStartSync = false;
+        mCancelSync = false;
+        mLatch = null;
+    }
+
+    public void setLatch(CountDownLatch mLatch) {
+        this.mLatch = mLatch;
+    }
+
+    public void startSync(ISyncContext syncContext, String authority, Account account,
+            Bundle extras) throws RemoteException {
+
+        mAccount = account;
+        mAuthority = authority;
+        mExtras = extras;
+
+        if (null != extras && extras.getBoolean(ContentResolver.SYNC_EXTRAS_INITIALIZE)) {
+            mInitialized = true;
+            mStartSync = false;
+            mCancelSync = false;
+        } else {
+            mInitialized = false;
+            mStartSync = true;
+            mCancelSync = false;
+        }
+
+        if (null != mLatch) {
+            mLatch.countDown();
+        }
+    }
+
+    public void cancelSync(ISyncContext syncContext) throws RemoteException {
+        mAccount = null;
+        mAuthority = null;
+        mExtras = null;
+
+        mInitialized = false;
+        mStartSync = false;
+        mCancelSync = true;
+
+        if (null != mLatch) {
+            mLatch.countDown();
+        }
+    }
+
+    public void initialize(android.accounts.Account account, java.lang.String authority)
+            throws android.os.RemoteException {
+
+        mAccount = account;
+        mAuthority = authority;
+
+        mInitialized = true;
+        mStartSync = false;
+        mCancelSync = false;
+
+        if (null != mLatch) {
+            mLatch.countDown();
+        }
+    }
+}
diff --git a/tests/tests/content/src/android/content/cts/MockSyncAdapterService.java b/tests/tests/content/src/android/content/cts/MockSyncAdapterService.java
new file mode 100644
index 0000000..6fd05af
--- /dev/null
+++ b/tests/tests/content/src/android/content/cts/MockSyncAdapterService.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2010 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.cts;
+
+import android.app.Service;
+import android.content.Intent;
+import android.os.IBinder;
+
+public class MockSyncAdapterService extends Service {
+    @Override
+    public IBinder onBind(Intent intent) {
+        return ContentResolverSyncTestCase.getMockSyncAdapter();
+    }
+}