Merge "Work around a Time design flaw." into jb-mr2-dev
diff --git a/tests/tests/provider/AndroidManifest.xml b/tests/tests/provider/AndroidManifest.xml
index 75a6392..94dc408 100644
--- a/tests/tests/provider/AndroidManifest.xml
+++ b/tests/tests/provider/AndroidManifest.xml
@@ -16,11 +16,28 @@
  -->
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="com.android.cts.provider">
+          package="com.android.cts.provider">
 
-    <uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
+    <uses-permission android:name="android.permission.DISABLE_KEYGUARD"/>
+    <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" />
+
     <application>
-        <uses-library android:name="android.test.runner" />
+        <uses-library android:name="android.test.runner"/>
+
+        <service android:name="android.provider.cts.contacts.account.MockAccountService"
+                 process="com.android.cts.provider"
+                 android:exported="true">
+            <intent-filter>
+                <action android:name="android.accounts.AccountAuthenticator"/>
+            </intent-filter>
+
+            <meta-data android:name="android.accounts.AccountAuthenticator"
+                       android:resource="@xml/contactprovider_authenticator"/>
+        </service>
+
     </application>
 
     <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
diff --git a/tests/tests/provider/res/drawable/ic_cts_minitab_selected.png b/tests/tests/provider/res/drawable/ic_cts_minitab_selected.png
new file mode 100644
index 0000000..c730050
--- /dev/null
+++ b/tests/tests/provider/res/drawable/ic_cts_minitab_selected.png
Binary files differ
diff --git a/tests/tests/provider/res/drawable/ic_cts_selected.png b/tests/tests/provider/res/drawable/ic_cts_selected.png
new file mode 100644
index 0000000..72a065c
--- /dev/null
+++ b/tests/tests/provider/res/drawable/ic_cts_selected.png
Binary files differ
diff --git a/tests/tests/provider/res/values/strings.xml b/tests/tests/provider/res/values/strings.xml
new file mode 100644
index 0000000..b599c31
--- /dev/null
+++ b/tests/tests/provider/res/values/strings.xml
@@ -0,0 +1,20 @@
+<!--
+  ~ Copyright (C) 2013 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">Contacts provider</string>
+</resources>
diff --git a/tests/tests/provider/res/xml/contactprovider_authenticator.xml b/tests/tests/provider/res/xml/contactprovider_authenticator.xml
new file mode 100644
index 0000000..2a53438
--- /dev/null
+++ b/tests/tests/provider/res/xml/contactprovider_authenticator.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+  ~ Copyright (C) 2013 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
+  -->
+
+<account-authenticator xmlns:android="http://schemas.android.com/apk/res/android"
+    android:accountType="com.android.cts.contactsprovider"
+    android:icon="@drawable/ic_cts_selected"
+    android:smallIcon="@drawable/ic_cts_minitab_selected"
+    android:label="@string/label"
+/>
diff --git a/tests/tests/provider/src/android/provider/cts/ContactsContract_ContactsTest.java b/tests/tests/provider/src/android/provider/cts/ContactsContract_ContactsTest.java
index 271588f..006f4df 100644
--- a/tests/tests/provider/src/android/provider/cts/ContactsContract_ContactsTest.java
+++ b/tests/tests/provider/src/android/provider/cts/ContactsContract_ContactsTest.java
@@ -33,6 +33,7 @@
 import android.provider.cts.contacts.ContactUtil;
 import android.provider.cts.contacts.DatabaseAsserts;
 import android.provider.cts.contacts.RawContactUtil;
+import android.provider.cts.contacts.account.StaticAccountAuthenticator;
 import android.test.AndroidTestCase;
 
 import java.util.List;
diff --git a/tests/tests/provider/src/android/provider/cts/ContactsContract_DataTest.java b/tests/tests/provider/src/android/provider/cts/ContactsContract_DataTest.java
index 51d62a3..7cfb183 100644
--- a/tests/tests/provider/src/android/provider/cts/ContactsContract_DataTest.java
+++ b/tests/tests/provider/src/android/provider/cts/ContactsContract_DataTest.java
@@ -215,9 +215,7 @@
 
     public void testDataUpdate_updatesContactLastUpdatedTimestamp() {
         DatabaseAsserts.ContactIdPair ids = DatabaseAsserts.assertAndCreateContact(mResolver);
-
         long dataId = createData(ids.mRawContactId);
-
         long baseTime = ContactUtil.queryContactLastUpdatedTimestamp(mResolver, ids.mContactId);
 
         SystemClock.sleep(1);
@@ -226,7 +224,8 @@
         DataUtil.update(mResolver, dataId, values);
 
         long newTime = ContactUtil.queryContactLastUpdatedTimestamp(mResolver, ids.mContactId);
-        assertTrue(newTime > baseTime);
+        assertTrue("Expected contact " + ids.mContactId + " last updated to be greater than " +
+                baseTime + ". But was " + newTime, newTime > baseTime);
 
         // Clean up
         RawContactUtil.delete(mResolver, ids.mRawContactId, true);
diff --git a/tests/tests/provider/src/android/provider/cts/ContactsContract_DataUsageTest.java b/tests/tests/provider/src/android/provider/cts/ContactsContract_DataUsageTest.java
index a684e41..d585ec2 100644
--- a/tests/tests/provider/src/android/provider/cts/ContactsContract_DataUsageTest.java
+++ b/tests/tests/provider/src/android/provider/cts/ContactsContract_DataUsageTest.java
@@ -16,7 +16,6 @@
 
 package android.provider.cts;
 
-import static android.provider.ContactsContract.CommonDataKinds;
 import static android.provider.ContactsContract.DataUsageFeedback;
 
 import android.content.ContentResolver;
@@ -85,27 +84,10 @@
     private long[] setupRawContactDataItems(long rawContactId) {
         // Create 4 data items.
         long[] dataIds = new long[4];
-
-        ContentValues values = new ContentValues();
-        values.put(ContactsContract.Data.MIMETYPE, CommonDataKinds.Phone.CONTENT_ITEM_TYPE);
-        values.put(CommonDataKinds.Phone.NUMBER, "555-5555");
-        dataIds[0] = DataUtil.insertData(mResolver, rawContactId, values);
-
-        values.clear();
-        values.put(ContactsContract.Data.MIMETYPE, CommonDataKinds.Phone.CONTENT_ITEM_TYPE);
-        values.put(CommonDataKinds.Phone.NUMBER, "555-5554");
-        dataIds[1] = DataUtil.insertData(mResolver, rawContactId, values);
-
-        values.clear();
-        values.put(ContactsContract.Data.MIMETYPE, CommonDataKinds.Email.CONTENT_ITEM_TYPE);
-        values.put(CommonDataKinds.Email.ADDRESS, "test@thisisfake.com");
-        dataIds[2] = DataUtil.insertData(mResolver, rawContactId, values);
-
-        values.clear();
-        values.put(ContactsContract.Data.MIMETYPE, CommonDataKinds.Email.CONTENT_ITEM_TYPE);
-        values.put(CommonDataKinds.Phone.NUMBER, "555-5556");
-        dataIds[3] = DataUtil.insertData(mResolver, rawContactId, values);
-
+        dataIds[0] = DataUtil.insertPhoneNumber(mResolver, rawContactId, "555-5555");
+        dataIds[1] = DataUtil.insertPhoneNumber(mResolver, rawContactId, "555-5554");
+        dataIds[2] = DataUtil.insertEmail(mResolver, rawContactId, "test@thisisfake.com");
+        dataIds[3] = DataUtil.insertPhoneNumber(mResolver, rawContactId, "555-5556");
         return dataIds;
     }
 
diff --git a/tests/tests/provider/src/android/provider/cts/ContactsContract_RawContactsTest.java b/tests/tests/provider/src/android/provider/cts/ContactsContract_RawContactsTest.java
index d9751f4..e1cea4a 100644
--- a/tests/tests/provider/src/android/provider/cts/ContactsContract_RawContactsTest.java
+++ b/tests/tests/provider/src/android/provider/cts/ContactsContract_RawContactsTest.java
@@ -30,6 +30,7 @@
 import android.provider.cts.contacts.ContactUtil;
 import android.provider.cts.contacts.DatabaseAsserts;
 import android.provider.cts.contacts.RawContactUtil;
+import android.provider.cts.contacts.account.StaticAccountAuthenticator;
 import android.test.AndroidTestCase;
 import android.test.MoreAsserts;
 
@@ -91,7 +92,8 @@
     }
 
     public void testRawContactDelete_setsDeleteFlag() {
-        long rawContactid = RawContactUtil.insertRawContact(mResolver);
+        long rawContactid = RawContactUtil.insertRawContact(mResolver,
+                StaticAccountAuthenticator.ACCOUNT_1);
 
         assertTrue(RawContactUtil.rawContactExistsById(mResolver, rawContactid));
 
@@ -111,7 +113,8 @@
     }
 
     public void testRawContactDelete_removesRecord() {
-        long rawContactid = RawContactUtil.insertRawContact(mResolver);
+        long rawContactid = RawContactUtil.insertRawContact(mResolver,
+                StaticAccountAuthenticator.ACCOUNT_1);
         assertTrue(RawContactUtil.rawContactExistsById(mResolver, rawContactid));
 
         RawContactUtil.delete(mResolver, rawContactid, true);
@@ -126,13 +129,14 @@
     public void testRawContactCreate_updatesContactUpdatedTimestamp() {
         long startTime = System.currentTimeMillis();
 
-        long rawContactId = RawContactUtil.createRawContactWithName(mResolver);
-        long lastUpdated = getContactLastUpdatedTimestampByRawContactId(mResolver, rawContactId);
+        DatabaseAsserts.ContactIdPair ids = DatabaseAsserts.assertAndCreateContact(mResolver);
+        long lastUpdated = getContactLastUpdatedTimestampByRawContactId(mResolver,
+                ids.mRawContactId);
 
         assertTrue(lastUpdated > startTime);
 
         // Clean up
-        RawContactUtil.delete(mResolver, rawContactId, true);
+        RawContactUtil.delete(mResolver, ids.mRawContactId, true);
     }
 
     public void testRawContactUpdate_updatesContactUpdatedTimestamp() {
diff --git a/tests/tests/provider/src/android/provider/cts/ContactsProvider2_AccountRemovalTest.java b/tests/tests/provider/src/android/provider/cts/ContactsProvider2_AccountRemovalTest.java
new file mode 100644
index 0000000..f700220
--- /dev/null
+++ b/tests/tests/provider/src/android/provider/cts/ContactsProvider2_AccountRemovalTest.java
@@ -0,0 +1,251 @@
+/*
+ * Copyright (C) 2013 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.provider.cts;
+
+import static android.provider.cts.contacts.DatabaseAsserts.ContactIdPair;
+
+import android.accounts.Account;
+import android.accounts.AccountManager;
+import android.content.ContentResolver;
+import android.os.SystemClock;
+import android.provider.cts.contacts.CommonDatabaseUtils;
+import android.provider.cts.contacts.ContactUtil;
+import android.provider.cts.contacts.DataUtil;
+import android.provider.cts.contacts.DatabaseAsserts;
+import android.provider.cts.contacts.DeletedContactUtil;
+import android.provider.cts.contacts.RawContactUtil;
+import android.provider.cts.contacts.account.StaticAccountAuthenticator;
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.MediumTest;
+
+import com.google.android.collect.Lists;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+
+@MediumTest
+public class ContactsProvider2_AccountRemovalTest extends AndroidTestCase {
+
+    private static long ASYNC_TIMEOUT_LIMIT_MS = 1000 * 60 * 1; // 3 minutes
+    private static long SLEEP_BETWEEN_POLL_MS = 1000 * 10; // 10 seconds
+
+    private static int NOT_MERGED = -1;
+
+    // Not re-using StaticAcountAuthenticator.ACCOUNT_1 because this test may break
+    // other tests running when the account is removed.  No other tests should use the following
+    // accounts.
+    private static final Account ACCT_1 = new Account("cp removal acct 1",
+            StaticAccountAuthenticator.TYPE);
+    private static final Account ACCT_2 = new Account("cp removal acct 2",
+            StaticAccountAuthenticator.TYPE);
+
+    private ContentResolver mResolver;
+    private AccountManager mAccountManager;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mResolver = getContext().getContentResolver();
+        mAccountManager = AccountManager.get(getContext());
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        super.tearDown();
+    }
+
+    public void testAccountRemoval_deletesContacts() {
+        mAccountManager.addAccountExplicitly(ACCT_1, null, null);
+        mAccountManager.addAccountExplicitly(ACCT_2, null, null);
+        ArrayList<ContactIdPair> acc1Ids = createContacts(ACCT_1, 5);
+        ArrayList<ContactIdPair> acc2Ids = createContacts(ACCT_2, 15);
+
+        mAccountManager.removeAccount(ACCT_2, null, null);
+        assertContactsDeletedEventually(System.currentTimeMillis(), acc2Ids);
+
+        mAccountManager.removeAccount(ACCT_1, null, null);
+        assertContactsDeletedEventually(System.currentTimeMillis(), acc1Ids);
+    }
+
+    public void testAccountRemoval_hasDeleteLogsForContacts() {
+        mAccountManager.addAccountExplicitly(ACCT_1, null, null);
+        mAccountManager.addAccountExplicitly(ACCT_2, null, null);
+        ArrayList<ContactIdPair> acc1Ids = createContacts(ACCT_1, 5);
+        ArrayList<ContactIdPair> acc2Ids = createContacts(ACCT_2, 15);
+
+        long start = System.currentTimeMillis();
+        mAccountManager.removeAccount(ACCT_2, null, null);
+        assertContactsInDeleteLogEventually(start, acc2Ids);
+
+        start = System.currentTimeMillis();
+        mAccountManager.removeAccount(ACCT_1, null, null);
+        assertContactsInDeleteLogEventually(start, acc1Ids);
+    }
+
+    /**
+     * Contact has merged raw contacts from a single account.  Contact should be deleted upon
+     * account removal.
+     */
+    public void testAccountRemovalWithMergedContact_deletesContacts() {
+        mAccountManager.addAccountExplicitly(ACCT_1, null, null);
+        ArrayList<ContactIdPair> idList = createAndAssertMergedContact(ACCT_1, ACCT_1);
+        mAccountManager.removeAccount(ACCT_1, null, null);
+        assertContactsDeletedEventually(System.currentTimeMillis(), idList);
+    }
+
+    /**
+     * Contact has merged raw contacts from different accounts. Contact should not be deleted when
+     * one account is removed.  But contact should have last updated timestamp updated.
+     */
+    public void testAccountRemovalWithMergedContact_doesNotDeleteContactAndTimestampUpdated() {
+        mAccountManager.addAccountExplicitly(ACCT_1, null, null);
+        ArrayList<ContactIdPair> idList = createAndAssertMergedContact(ACCT_1, ACCT_2);
+        long contactId = idList.get(0).mContactId;
+
+        long baseTime = ContactUtil.queryContactLastUpdatedTimestamp(mResolver, contactId);
+        long start = System.currentTimeMillis();
+        mAccountManager.removeAccount(ACCT_1, null, null);
+
+        while (ContactUtil.queryContactLastUpdatedTimestamp(mResolver, contactId) == baseTime) {
+            assertWithinTimeoutLimit(start,
+                    "Contact " + contactId + " last updated timestamp has not been updated.");
+            SystemClock.sleep(SLEEP_BETWEEN_POLL_MS);
+        }
+    }
+
+    public void testAccountRemovalWithMergedContact_hasDeleteLogsForContacts() {
+        mAccountManager.addAccountExplicitly(ACCT_1, null, null);
+        ArrayList<ContactIdPair> idList = createAndAssertMergedContact(ACCT_1, ACCT_1);
+        long start = System.currentTimeMillis();
+        mAccountManager.removeAccount(ACCT_1, null, null);
+        assertContactsInDeleteLogEventually(start, idList);
+    }
+
+    private ArrayList<ContactIdPair> createAndAssertMergedContact(Account acct, Account acct2) {
+        ContactIdPair ids1 = DatabaseAsserts.assertAndCreateContactWithName(mResolver, acct,
+                "merge me");
+        DataUtil.insertPhoneNumber(mResolver, ids1.mRawContactId, "555-5555");
+
+        ContactIdPair ids2 = DatabaseAsserts.assertAndCreateContactWithName(mResolver, acct2,
+                "merge me");
+        DataUtil.insertPhoneNumber(mResolver, ids2.mRawContactId, "555-5555");
+
+        // Check merge before continuing. Merge process is async.
+        long mergedContactId = assertMerged(System.currentTimeMillis(), ids1.mRawContactId,
+                ids2.mRawContactId);
+
+        // Update the contact id to the newly merged contact id.
+        ids1.mContactId = mergedContactId;
+        ids2.mContactId = mergedContactId;
+
+        return Lists.newArrayList(ids1, ids2);
+    }
+
+    private long assertMerged(long start, long rawContactId, long rawContactId2) {
+        long contactId = NOT_MERGED;
+        while (contactId == NOT_MERGED) {
+            assertWithinTimeoutLimit(start,
+                    "Raw contact " + rawContactId + " and " + rawContactId2 + " are not merged.");
+
+            SystemClock.sleep(SLEEP_BETWEEN_POLL_MS);
+            contactId = checkMerged(rawContactId, rawContactId2);
+        }
+        return contactId;
+    }
+
+    private long checkMerged(long rawContactId, long rawContactId2) {
+        long contactId = RawContactUtil.queryContactIdByRawContactId(mResolver, rawContactId);
+        long contactId2 = RawContactUtil.queryContactIdByRawContactId(mResolver, rawContactId2);
+        if (contactId == contactId2) {
+            return contactId;
+        }
+        return NOT_MERGED;
+    }
+
+    private void assertContactsInDeleteLogEventually(long start, ArrayList<ContactIdPair> idList) {
+        // Can not use newArrayList() because the version that accepts size is missing.
+        ArrayList<ContactIdPair> remaining = new ArrayList<ContactIdPair>(idList.size());
+        remaining.addAll(idList);
+        while (!remaining.isEmpty()) {
+            // Account cleanup is asynchronous, wait a bit before checking.
+            SystemClock.sleep(SLEEP_BETWEEN_POLL_MS);
+            assertWithinTimeoutLimit(start, "Contacts " + Arrays.toString(remaining.toArray()) +
+                    " are not in delete log after account removal.");
+
+            // Need a second list to remove since we can't remove from the list while iterating.
+            ArrayList<ContactIdPair> toBeRemoved = Lists.newArrayList();
+            for (ContactIdPair ids : remaining) {
+                long deletedTime = DeletedContactUtil.queryDeletedTimestampForContactId(mResolver,
+                        ids.mContactId);
+                if (deletedTime != CommonDatabaseUtils.NOT_FOUND) {
+                    toBeRemoved.add(ids);
+                    assertTrue("Deleted contact was found in delete log but insert time is before"
+                            + " start time", deletedTime > start);
+                }
+            }
+            remaining.removeAll(toBeRemoved);
+        }
+
+        // All contacts in delete log.  Pass.
+    }
+
+    /**
+     * Polls every so often to see if all contacts have been deleted.  If not deleted in the
+     * pre-defined threshold, fails.
+     */
+    private void assertContactsDeletedEventually(long start, ArrayList<ContactIdPair> idList) {
+        // Can not use newArrayList() because the version that accepts size is missing.
+        ArrayList<ContactIdPair> remaining = new ArrayList<ContactIdPair>(idList.size());
+        remaining.addAll(idList);
+        while (!remaining.isEmpty()) {
+            // Account cleanup is asynchronous, wait a bit before checking.
+            SystemClock.sleep(SLEEP_BETWEEN_POLL_MS);
+            assertWithinTimeoutLimit(start, "Contacts have not been deleted after account"
+                    + " removal.");
+
+            ArrayList<ContactIdPair> toBeRemoved = Lists.newArrayList();
+            for (ContactIdPair ids : remaining) {
+                if (!RawContactUtil.rawContactExistsById(mResolver, ids.mRawContactId)) {
+                    toBeRemoved.add(ids);
+                }
+            }
+            remaining.removeAll(toBeRemoved);
+        }
+
+        // All contacts deleted.  Pass.
+    }
+
+    private void assertWithinTimeoutLimit(long start, String message) {
+        long now = System.currentTimeMillis();
+        long elapsed = now - start;
+        if (elapsed > ASYNC_TIMEOUT_LIMIT_MS) {
+            fail(elapsed + "ms has elapsed. The limit is " + ASYNC_TIMEOUT_LIMIT_MS + "ms. " +
+                    message);
+        }
+    }
+
+    /**
+     * Creates a given number of contacts for an account.
+     */
+    private ArrayList<ContactIdPair> createContacts(Account account, int numContacts) {
+        ArrayList<ContactIdPair> accountIds = Lists.newArrayList();
+        for (int i = 0; i < numContacts; i++) {
+            accountIds.add(DatabaseAsserts.assertAndCreateContact(mResolver, account));
+        }
+        return accountIds;
+    }
+}
diff --git a/tests/tests/provider/src/android/provider/cts/contacts/DataUtil.java b/tests/tests/provider/src/android/provider/cts/contacts/DataUtil.java
index ae2812e..1e07450 100644
--- a/tests/tests/provider/src/android/provider/cts/contacts/DataUtil.java
+++ b/tests/tests/provider/src/android/provider/cts/contacts/DataUtil.java
@@ -16,6 +16,8 @@
 
 package android.provider.cts.contacts;
 
+import static android.provider.ContactsContract.CommonDataKinds;
+
 import android.content.ContentResolver;
 import android.content.ContentUris;
 import android.content.ContentValues;
@@ -36,15 +38,33 @@
         return CommonDatabaseUtils.singleRecordToArray(cursor);
     }
 
-    public static void insertName(ContentResolver resolver, long rawContactId) {
+    public static void insertName(ContentResolver resolver, long rawContactId, String name) {
         ContentValues values = new ContentValues();
         values.put(ContactsContract.Data.MIMETYPE,
-                ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE);
-        values.put(ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME,
-                "test raw contact " + rawContactId);
+                CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE);
+        values.put(CommonDataKinds.StructuredName.DISPLAY_NAME, name);
         insertData(resolver, rawContactId, values);
     }
 
+    public static long insertPhoneNumber(ContentResolver resolver, long rawContactId,
+            String number) {
+        ContentValues values = new ContentValues();
+        values.put(ContactsContract.Data.MIMETYPE, CommonDataKinds.Phone.CONTENT_ITEM_TYPE);
+        values.put(CommonDataKinds.Phone.NUMBER, number);
+        return DataUtil.insertData(resolver, rawContactId, values);
+    }
+
+    public static long insertEmail(ContentResolver resolver, long rawContactId, String email) {
+        ContentValues values = new ContentValues();
+        values.put(ContactsContract.Data.MIMETYPE, CommonDataKinds.Email.CONTENT_ITEM_TYPE);
+        values.put(CommonDataKinds.Email.ADDRESS, email);
+        return DataUtil.insertData(resolver, rawContactId, values);
+    }
+
+    public static void insertAutoGeneratedName(ContentResolver resolver, long rawContactId) {
+        insertName(resolver, rawContactId, "test raw contact " + rawContactId);
+    }
+
     public static long insertData(ContentResolver resolver, long rawContactId,
             ContentValues values) {
         // copy
diff --git a/tests/tests/provider/src/android/provider/cts/contacts/DatabaseAsserts.java b/tests/tests/provider/src/android/provider/cts/contacts/DatabaseAsserts.java
index 80a0a65..19b5b13 100644
--- a/tests/tests/provider/src/android/provider/cts/contacts/DatabaseAsserts.java
+++ b/tests/tests/provider/src/android/provider/cts/contacts/DatabaseAsserts.java
@@ -16,10 +16,12 @@
 
 package android.provider.cts.contacts;
 
+import android.accounts.Account;
 import android.content.ContentResolver;
 import android.content.ContentValues;
 import android.database.Cursor;
 import android.net.Uri;
+import android.provider.cts.contacts.account.StaticAccountAuthenticator;
 import android.test.MoreAsserts;
 
 import junit.framework.Assert;
@@ -53,12 +55,36 @@
     }
 
     /**
-     * Create a contact and assert that the record exists.
+     * Create a contact in account 1 and assert that the record exists.
      *
      * @return The created contact id pair.
      */
     public static ContactIdPair assertAndCreateContact(ContentResolver resolver) {
-        long rawContactId = RawContactUtil.createRawContactWithName(resolver);
+        return assertAndCreateContact(resolver, StaticAccountAuthenticator.ACCOUNT_1);
+    }
+
+    /**
+     * Create a contact in a specified account and assert that the record exists.
+     *
+     * @return The created contact id pair.
+     */
+    public static ContactIdPair assertAndCreateContactWithName(ContentResolver resolver,
+            Account account, String name) {
+        long rawContactId = RawContactUtil.createRawContactWithName(resolver, account, name);
+
+        long contactId = RawContactUtil.queryContactIdByRawContactId(resolver, rawContactId);
+        MoreAsserts.assertNotEqual(CommonDatabaseUtils.NOT_FOUND, contactId);
+
+        return new ContactIdPair(contactId, rawContactId);
+    }
+
+    /**
+     * Create a contact in a specified account and assert that the record exists.
+     *
+     * @return The created contact id pair.
+     */
+    public static ContactIdPair assertAndCreateContact(ContentResolver resolver, Account account) {
+        long rawContactId = RawContactUtil.createRawContactWithAutoGeneratedName(resolver, account);
 
         long contactId = RawContactUtil.queryContactIdByRawContactId(resolver, rawContactId);
         MoreAsserts.assertNotEqual(CommonDatabaseUtils.NOT_FOUND, contactId);
@@ -94,6 +120,14 @@
             this.mContactId = contactId;
             this.mRawContactId = rawContactId;
         }
+
+        @Override
+        public String toString() {
+            return "ContactIdPair{" +
+                    "mContactId=" + mContactId +
+                    ", mRawContactId=" + mRawContactId +
+                    '}';
+        }
     }
 
     /**
diff --git a/tests/tests/provider/src/android/provider/cts/contacts/RawContactUtil.java b/tests/tests/provider/src/android/provider/cts/contacts/RawContactUtil.java
index c87dc01..38ff33d 100644
--- a/tests/tests/provider/src/android/provider/cts/contacts/RawContactUtil.java
+++ b/tests/tests/provider/src/android/provider/cts/contacts/RawContactUtil.java
@@ -16,13 +16,13 @@
 
 package android.provider.cts.contacts;
 
+import android.accounts.Account;
 import android.content.ContentResolver;
 import android.content.ContentUris;
 import android.content.ContentValues;
 import android.database.Cursor;
 import android.net.Uri;
 import android.provider.ContactsContract;
-import android.provider.cts.StaticAccountAuthenticator;
 
 import java.util.List;
 
@@ -39,16 +39,24 @@
         resolver.update(uri, values, null, null);
     }
 
-    public static long createRawContactWithName(ContentResolver resolver) {
-        Long rawContactId = insertRawContact(resolver);
-        DataUtil.insertName(resolver, rawContactId);
+    public static long createRawContactWithName(ContentResolver resolver, Account account,
+            String name) {
+        Long rawContactId = insertRawContact(resolver, account);
+        DataUtil.insertName(resolver, rawContactId, name);
         return rawContactId;
     }
 
-    public static long insertRawContact(ContentResolver resolver) {
+    public static long createRawContactWithAutoGeneratedName(ContentResolver resolver,
+            Account account) {
+        Long rawContactId = insertRawContact(resolver, account);
+        DataUtil.insertAutoGeneratedName(resolver, rawContactId);
+        return rawContactId;
+    }
+
+    public static long insertRawContact(ContentResolver resolver, Account account) {
         ContentValues values = new ContentValues();
-        values.put(ContactsContract.RawContacts.ACCOUNT_NAME, StaticAccountAuthenticator.NAME);
-        values.put(ContactsContract.RawContacts.ACCOUNT_TYPE, StaticAccountAuthenticator.TYPE);
+        values.put(ContactsContract.RawContacts.ACCOUNT_NAME, account.name);
+        values.put(ContactsContract.RawContacts.ACCOUNT_TYPE, account.type);
         Uri uri = resolver.insert(URI, values);
         return ContentUris.parseId(uri);
     }
diff --git a/tests/tests/provider/src/android/provider/cts/contacts/account/MockAccountService.java b/tests/tests/provider/src/android/provider/cts/contacts/account/MockAccountService.java
new file mode 100644
index 0000000..33a34b3
--- /dev/null
+++ b/tests/tests/provider/src/android/provider/cts/contacts/account/MockAccountService.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2013 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.provider.cts.contacts.account;
+
+import android.app.Service;
+import android.content.Intent;
+import android.os.IBinder;
+
+/**
+ * Account service for the authenticator
+ */
+public class MockAccountService extends Service {
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        return (new StaticAccountAuthenticator(this)).getIBinder();
+    }
+}
diff --git a/tests/tests/provider/src/android/provider/cts/StaticAccountAuthenticator.java b/tests/tests/provider/src/android/provider/cts/contacts/account/StaticAccountAuthenticator.java
similarity index 91%
rename from tests/tests/provider/src/android/provider/cts/StaticAccountAuthenticator.java
rename to tests/tests/provider/src/android/provider/cts/contacts/account/StaticAccountAuthenticator.java
index d3c9e1d..01db2a1 100644
--- a/tests/tests/provider/src/android/provider/cts/StaticAccountAuthenticator.java
+++ b/tests/tests/provider/src/android/provider/cts/contacts/account/StaticAccountAuthenticator.java
@@ -14,7 +14,7 @@
  * limitations under the License
  */
 
-package android.provider.cts;
+package android.provider.cts.contacts.account;
 
 import android.accounts.AbstractAccountAuthenticator;
 import android.accounts.Account;
@@ -32,7 +32,9 @@
 public class StaticAccountAuthenticator extends AbstractAccountAuthenticator {
 
     public static final String NAME = "test_account_name";
-    public static final String TYPE = "test_account_type";
+    public static final String TYPE = "com.android.cts.contactsprovider";
+    public static final Account ACCOUNT_1 = new Account("cp account 1", TYPE);
+
     private static final String LABEL = "test_auth_token_label";
     private static final String TOKEN = "asdlkjfslkjfdklj";
 
@@ -44,20 +46,12 @@
         sAccountBundle.putString(AccountManager.KEY_AUTHTOKEN, TOKEN);
     }
 
-    private Context mContext;
-
     private static Bundle createResultBundle() {
         return sAccountBundle;
     }
 
-    public void addAccount() {
-        AccountManager.get(mContext).addAccount(TYPE, null, null, null, null, null, null);
-    }
-
     public StaticAccountAuthenticator(Context context) {
         super(context);
-        this.mContext = context;
-        addAccount();
     }
 
     @Override