Initial cut of legacy public API unit test.
diff --git a/tests/Android.mk b/tests/Android.mk
index 96ae80f..ec48f5a 100644
--- a/tests/Android.mk
+++ b/tests/Android.mk
@@ -7,7 +7,7 @@
 # Only compile source java files in this apk.
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
-LOCAL_PACKAGE_NAME := ContactsProvider2Tests
+LOCAL_PACKAGE_NAME := ContactsProviderTests
 
 LOCAL_JAVA_LIBRARIES := android.test.runner
 
diff --git a/tests/AndroidManifest.xml b/tests/AndroidManifest.xml
index d662b53..5063a7d 100644
--- a/tests/AndroidManifest.xml
+++ b/tests/AndroidManifest.xml
@@ -34,7 +34,7 @@
     -->
     <instrumentation android:name="android.test.InstrumentationTestRunner"
         android:targetPackage="com.android.providers.contacts"
-        android:label="Contacts2 provider tests">
+        android:label="Contacts Provider Tests">
     </instrumentation>
 
 </manifest>
diff --git a/tests/src/com/android/providers/contacts/BaseContactsProvider2Test.java b/tests/src/com/android/providers/contacts/BaseContactsProvider2Test.java
index aa2b2ef..3188a6e 100644
--- a/tests/src/com/android/providers/contacts/BaseContactsProvider2Test.java
+++ b/tests/src/com/android/providers/contacts/BaseContactsProvider2Test.java
@@ -19,10 +19,12 @@
 
 import com.android.providers.contacts.ContactsActor;
 
+import android.content.ContentProvider;
 import android.content.ContentUris;
 import android.content.ContentValues;
 import android.database.Cursor;
 import android.net.Uri;
+import android.provider.ContactsContract;
 import android.provider.ContactsContract.Aggregates;
 import android.provider.ContactsContract.AggregationExceptions;
 import android.provider.ContactsContract.Contacts;
@@ -36,6 +38,9 @@
 import android.test.AndroidTestCase;
 import android.test.mock.MockContentResolver;
 import android.test.suitebuilder.annotation.LargeTest;
+import android.util.Log;
+
+import java.util.Locale;
 
 /**
  * A common superclass for {@link ContactsProvider2}-related tests.
@@ -45,14 +50,22 @@
 
     protected static final String PACKAGE = "ContactsProvider2Test";
 
-    private ContactsActor mActor;
+    protected ContactsActor mActor;
     protected MockContentResolver mResolver;
 
+    protected Class<? extends ContentProvider> getProviderClass() {
+        return SynchronousContactsProvider2.class;
+    }
+
+    protected String getAuthority() {
+        return ContactsContract.AUTHORITY;
+    }
+
     @Override
     protected void setUp() throws Exception {
         super.setUp();
 
-        mActor = new ContactsActor(getContext(), PACKAGE_GREY);
+        mActor = new ContactsActor(getContext(), PACKAGE_GREY, getProviderClass(), getAuthority());
         mResolver = mActor.resolver;
     }
 
diff --git a/tests/src/com/android/providers/contacts/ContactsActor.java b/tests/src/com/android/providers/contacts/ContactsActor.java
index 5936f8f..951c80f 100644
--- a/tests/src/com/android/providers/contacts/ContactsActor.java
+++ b/tests/src/com/android/providers/contacts/ContactsActor.java
@@ -16,6 +16,7 @@
 
 package com.android.providers.contacts;
 
+import android.content.ContentProvider;
 import android.content.ContentUris;
 import android.content.ContentValues;
 import android.content.Context;
@@ -59,7 +60,7 @@
     public Context context;
     public String packageName;
     public MockContentResolver resolver;
-    public SynchronousContactsProvider2 provider;
+    public ContentProvider provider;
 
     /**
      * Create an "actor" using the given parent {@link Context} and the specific
@@ -67,18 +68,19 @@
      * a new instance of {@link RestrictionMockContext}, which stubs out the
      * security infrastructure.
      */
-    public ContactsActor(Context overallContext, String packageName) {
+    public ContactsActor(Context overallContext, String packageName,
+            Class<? extends ContentProvider> providerClass, String authority) throws Exception {
         context = new RestrictionMockContext(overallContext, packageName);
         this.packageName = packageName;
         resolver = new MockContentResolver();
 
-        RenamingDelegatingContext targetContextWrapper = new RenamingDelegatingContext(
-                context, overallContext, FILENAME_PREFIX);
+        RenamingDelegatingContext targetContextWrapper = new RenamingDelegatingContext(context,
+                overallContext, FILENAME_PREFIX);
         Context providerContext = new IsolatedContext(resolver, targetContextWrapper);
 
-        provider = new SynchronousContactsProvider2();
+        provider = providerClass.newInstance();
         provider.attachInfo(providerContext, null);
-        resolver.addProvider(ContactsContract.AUTHORITY, provider);
+        resolver.addProvider(authority, provider);
     }
 
     /**
diff --git a/tests/src/com/android/providers/contacts/GroupsTest.java b/tests/src/com/android/providers/contacts/GroupsTest.java
index a9428ae..88203e6 100644
--- a/tests/src/com/android/providers/contacts/GroupsTest.java
+++ b/tests/src/com/android/providers/contacts/GroupsTest.java
@@ -35,21 +35,7 @@
  * </code>
  */
 @LargeTest
-public class GroupsTest extends AndroidTestCase {
-
-    private ContactsActor mActor;
-    private MockContentResolver mResolver;
-
-    public GroupsTest() {
-    }
-
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-
-        mActor = new ContactsActor(getContext(), PACKAGE_GREY);
-        mResolver = mActor.resolver;
-    }
+public class GroupsTest extends BaseContactsProvider2Test {
 
     private static final String GROUP_GREY = "Grey";
     private static final String GROUP_RED = "Red";
@@ -70,7 +56,8 @@
     public void testGroupSummary() {
 
         // Clear any existing data before starting
-        mActor.provider.wipeData();
+        // TODO make the provider wipe data automatically
+        ((SynchronousContactsProvider2)mActor.provider).wipeData();
 
         // Create a handful of groups
         long groupGrey = mActor.createGroup(GROUP_GREY);
diff --git a/tests/src/com/android/providers/contacts/LegacyContactsProviderTest.java b/tests/src/com/android/providers/contacts/LegacyContactsProviderTest.java
new file mode 100644
index 0000000..7275d18
--- /dev/null
+++ b/tests/src/com/android/providers/contacts/LegacyContactsProviderTest.java
@@ -0,0 +1,638 @@
+/*
+ * 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.
+ */
+
+package com.android.providers.contacts;
+
+import android.content.ContentProvider;
+import android.content.ContentUris;
+import android.content.ContentValues;
+import android.content.res.Resources;
+import android.database.Cursor;
+import android.net.Uri;
+import android.provider.Contacts;
+import android.provider.Contacts.ContactMethods;
+import android.provider.Contacts.Extensions;
+import android.provider.Contacts.GroupMembership;
+import android.provider.Contacts.Groups;
+import android.provider.Contacts.Organizations;
+import android.provider.Contacts.People;
+import android.provider.Contacts.Phones;
+import android.provider.Contacts.Photos;
+import android.provider.Contacts.Presence;
+import android.test.suitebuilder.annotation.LargeTest;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Tests for legacy contacts APIs.
+ */
+@LargeTest
+public class LegacyContactsProviderTest extends BaseContactsProvider2Test {
+
+    @Override
+    protected Class<? extends ContentProvider> getProviderClass() {
+        return ContactsProvider.class;
+    }
+
+    @Override
+    protected String getAuthority() {
+        return Contacts.AUTHORITY;
+    }
+
+    public void testPeopleInsert() {
+        ContentValues values = new ContentValues();
+        putContactValues(values);
+
+        Uri uri = mResolver.insert(People.CONTENT_URI, values);
+        assertStoredValues(uri, values);
+    }
+
+    public void testPeopleDelete() {
+        ContentValues values = new ContentValues();
+        values.put(People.NAME, "John Doe");
+        Uri personId = mResolver.insert(People.CONTENT_URI, values);
+        mResolver.delete(personId, null, null);
+
+        Cursor c = mResolver.query(personId, null, People.NAME + "='John Doe'" , null, null);
+        assertEquals("Record count after deletion", 0, c.getCount());
+        c.close();
+
+        try {
+            mResolver.query(People.DELETED_CONTENT_URI, null, null, null, null);
+        } catch (UnsupportedOperationException e) {
+            // Expected exception
+        }
+    }
+
+    public void testPeopleFilter() {
+        ContentValues values = new ContentValues();
+        values.put(People.NAME, "Deer Doe");
+        mResolver.insert(People.CONTENT_URI, values);
+
+        values.clear();
+        values.put(People.NAME, "Dear Dough");
+        mResolver.insert(People.CONTENT_URI, values);
+
+        values.clear();
+        values.put(People.NAME, "D.R. Dauwe");
+        mResolver.insert(People.CONTENT_URI, values);
+
+        assertFilteredContacts("d", "Deer Doe", "Dear Dough", "D.R. Dauwe");
+        assertFilteredContacts("de", "Deer Doe", "Dear Dough");
+        assertFilteredContacts("dee", "Deer Doe");
+        assertFilteredContacts("der");
+    }
+
+    public void testDefaultDisplayName() {
+        ContentValues values = new ContentValues();
+        values.put(People.NAME, "John Doe");
+        Uri personUri = mResolver.insert(People.CONTENT_URI, values);
+        assertStoredValues(personUri, People.DISPLAY_NAME, "John Doe");
+    }
+
+    public void testPrimaryOrganization() {
+        ContentValues values = new ContentValues();
+        final Uri personUri = mResolver.insert(People.CONTENT_URI, values);
+        long personId = ContentUris.parseId(personUri);
+
+        // Primary
+        values.clear();
+        values.put(Organizations.ISPRIMARY, 1);
+        values.put(Organizations.COMPANY, "Google");
+        values.put(Organizations.TYPE, Organizations.TYPE_WORK);
+        values.put(Organizations.PERSON_ID, personId);
+        Uri orgUri1 = mResolver.insert(Organizations.CONTENT_URI, values);
+
+        // Non-primary
+        values.clear();
+        values.put(Organizations.COMPANY, "Acme");
+        values.put(Organizations.TYPE, Organizations.TYPE_WORK);
+        values.put(Organizations.PERSON_ID, personId);
+        Uri orgUri2 = mResolver.insert(Organizations.CONTENT_URI, values);
+
+        values.clear();
+        values.put(People.PRIMARY_ORGANIZATION_ID, ContentUris.parseId(orgUri1));
+        values.put(People.DISPLAY_NAME, "Google");
+        assertStoredValues(personUri, values);
+
+        // Remove the original primary organization
+        mResolver.delete(orgUri1, null, null);
+
+        values.clear();
+        values.put(People.PRIMARY_ORGANIZATION_ID, ContentUris.parseId(orgUri2));
+        values.put(People.DISPLAY_NAME, "Acme");
+        assertStoredValues(personUri, values);
+
+        // Remove the remaining organization
+        mResolver.delete(orgUri2, null, null);
+
+        values.clear();
+        values.putNull(People.PRIMARY_ORGANIZATION_ID);
+        values.putNull(People.DISPLAY_NAME);
+        assertStoredValues(personUri, values);
+    }
+
+    public void testPrimaryPhone() {
+        ContentValues values = new ContentValues();
+        Uri personUri = mResolver.insert(People.CONTENT_URI, values);
+        long personId = ContentUris.parseId(personUri);
+
+        // Primary
+        values.clear();
+        values.put(Phones.ISPRIMARY, 1);
+        values.put(Phones.TYPE, Phones.TYPE_WORK);
+        values.put(Phones.PERSON_ID, personId);
+        values.put(Phones.NUMBER, "12345");
+        Uri phoneUri1 = mResolver.insert(Phones.CONTENT_URI, values);
+
+        // Non-primary
+        values.clear();
+        values.put(Phones.TYPE, Phones.TYPE_WORK);
+        values.put(Phones.PERSON_ID, personId);
+        values.put(Phones.NUMBER, "67890");
+        Uri phoneUri2 = mResolver.insert(Phones.CONTENT_URI, values);
+
+        values.clear();
+        values.put(People.PRIMARY_PHONE_ID, ContentUris.parseId(phoneUri1));
+        values.put(People.DISPLAY_NAME, "12345");
+        assertStoredValues(personUri, values);
+
+        // Remove the primary phone number
+        mResolver.delete(phoneUri1, null, null);
+
+        values.clear();
+        values.put(People.PRIMARY_PHONE_ID, ContentUris.parseId(phoneUri2));
+        values.put(People.DISPLAY_NAME, "67890");
+        assertStoredValues(personUri, values);
+
+        // Remove the remaining phone number
+        mResolver.delete(phoneUri2, null, null);
+
+        values.clear();
+        values.putNull(People.PRIMARY_PHONE_ID);
+        values.putNull(People.DISPLAY_NAME);
+        assertStoredValues(personUri, values);
+    }
+
+    public void testPrimaryEmail() {
+        ContentValues values = new ContentValues();
+        Uri personUri = mResolver.insert(People.CONTENT_URI, values);
+        long personId = ContentUris.parseId(personUri);
+
+        // Primary
+        values.clear();
+        values.put(ContactMethods.PERSON_ID, personId);
+        values.put(ContactMethods.KIND, Contacts.KIND_EMAIL);
+        values.put(ContactMethods.TYPE, ContactMethods.TYPE_HOME);
+        values.put(ContactMethods.DATA, "foo@acme.com");
+        values.put(ContactMethods.ISPRIMARY, 1);
+        Uri emailUri1 = mResolver.insert(ContactMethods.CONTENT_URI, values);
+
+        // Non-primary
+        values.clear();
+        values.put(ContactMethods.PERSON_ID, personId);
+        values.put(ContactMethods.KIND, Contacts.KIND_EMAIL);
+        values.put(ContactMethods.TYPE, ContactMethods.TYPE_WORK);
+        values.put(ContactMethods.DATA, "bar@acme.com");
+        Uri emailUri2 = mResolver.insert(ContactMethods.CONTENT_URI, values);
+
+        values.clear();
+        values.put(People.PRIMARY_EMAIL_ID, ContentUris.parseId(emailUri1));
+        values.put(People.DISPLAY_NAME, "foo@acme.com");
+        assertStoredValues(personUri, values);
+
+        // Remove the primary email
+        mResolver.delete(emailUri1, null, null);
+
+        values.clear();
+        values.put(People.PRIMARY_EMAIL_ID, ContentUris.parseId(emailUri2));
+        values.put(People.DISPLAY_NAME, "bar@acme.com");
+        assertStoredValues(personUri, values);
+
+        // Remove the remaining email
+        mResolver.delete(emailUri2, null, null);
+
+        values.clear();
+        values.putNull(People.PRIMARY_EMAIL_ID);
+        values.putNull(People.DISPLAY_NAME);
+        assertStoredValues(personUri, values);
+    }
+
+    public void testMarkAsContacted() {
+        ContentValues values = new ContentValues();
+        Uri personUri = mResolver.insert(People.CONTENT_URI, values);
+        long personId = ContentUris.parseId(personUri);
+
+        int timesContactedBefore =
+                Integer.parseInt(getStoredValue(personUri, People.TIMES_CONTACTED));
+        long timeBefore = System.currentTimeMillis();
+        People.markAsContacted(mResolver, personId);
+        long timeAfter = System.currentTimeMillis();
+
+        long lastContacted = Long.parseLong(getStoredValue(personUri, People.LAST_TIME_CONTACTED));
+        int timesContactedAfter =
+            Integer.parseInt(getStoredValue(personUri, People.TIMES_CONTACTED));
+
+        assertTrue(lastContacted >= timeBefore);
+        assertTrue(lastContacted <= timeAfter);
+        assertEquals(timesContactedAfter, timesContactedBefore + 1);
+    }
+
+    public void testOrganizationsInsert() {
+        ContentValues values = new ContentValues();
+        Uri personUri = mResolver.insert(People.CONTENT_URI, values);
+        long personId = ContentUris.parseId(personUri);
+
+        values.clear();
+        values.put(Organizations.COMPANY, "Sierra");
+        values.put(Organizations.PERSON_ID, personId);
+        values.put(Organizations.TYPE, Organizations.TYPE_CUSTOM);
+        values.put(Organizations.LABEL, "Club");
+        values.put(Organizations.TITLE, "Member");
+        values.put(Organizations.ISPRIMARY, 1);
+
+        Uri uri = mResolver.insert(Organizations.CONTENT_URI, values);
+        assertStoredValues(uri, values);
+
+        assertPersonIdConstraint(Organizations.CONTENT_URI, Organizations.TYPE,
+                Organizations.TYPE_WORK);
+
+        assertTypeAndLabelConstraints(Organizations.CONTENT_URI, Organizations.PERSON_ID, personId,
+                Organizations.TYPE, Organizations.TYPE_CUSTOM, Organizations.TYPE_OTHER,
+                Organizations.LABEL);
+    }
+
+    public void testPhonesInsert() {
+        ContentValues values = new ContentValues();
+        putContactValues(values);
+        Uri personUri = mResolver.insert(People.CONTENT_URI, values);
+        long personId = ContentUris.parseId(personUri);
+
+        values.clear();
+        values.put(Phones.PERSON_ID, personId);
+        values.put(Phones.TYPE, Phones.TYPE_CUSTOM);
+        values.put(Phones.LABEL, "Directory");
+        values.put(Phones.NUMBER, "1-800-4664-411");
+        values.put(Phones.ISPRIMARY, 1);
+
+        Uri uri = mResolver.insert(Phones.CONTENT_URI, values);
+
+        // Adding another value to assert
+        values.put(Phones.NUMBER_KEY, "11446640081");
+
+        // The result is joined with People
+        putContactValues(values);
+        assertStoredValues(uri, values);
+
+        // Access the phone through People
+        Uri twigUri = Uri.withAppendedPath(personUri, People.Phones.CONTENT_DIRECTORY);
+        assertStoredValues(twigUri, values);
+
+        // Now the person should be joined with Phone
+        values.clear();
+        putContactValues(values);
+        values.put(People.TYPE, Phones.TYPE_CUSTOM);
+        values.put(People.LABEL, "Directory");
+        values.put(People.NUMBER, "1-800-4664-411");
+        assertStoredValues(personUri, values);
+
+        assertPersonIdConstraint(Phones.CONTENT_URI, Phones.TYPE, Phones.TYPE_WORK);
+
+        assertTypeAndLabelConstraints(Phones.CONTENT_URI, Phones.PERSON_ID, personId, Phones.TYPE,
+                Phones.TYPE_CUSTOM, Phones.TYPE_OTHER, Phones.LABEL);
+    }
+
+    public void testContactMethodsInsert() {
+        ContentValues values = new ContentValues();
+        putContactValues(values);
+        final Uri personUri = mResolver.insert(People.CONTENT_URI, values);
+        long personId = ContentUris.parseId(personUri);
+
+        values.clear();
+        values.put(ContactMethods.PERSON_ID, personId);
+        values.put(ContactMethods.KIND, Contacts.KIND_POSTAL);
+        values.put(ContactMethods.TYPE, ContactMethods.TYPE_CUSTOM);
+        values.put(ContactMethods.LABEL, "Some other way");
+        values.put(ContactMethods.DATA, "Foo");
+        values.put(ContactMethods.AUX_DATA, "Bar");
+        values.put(ContactMethods.ISPRIMARY, 1);
+
+        Uri uri = mResolver.insert(ContactMethods.CONTENT_URI, values);
+
+        // The result is joined with People
+        putContactValues(values);
+        assertStoredValues(uri, values);
+
+        // Access the email through People
+        Uri twigUri = Uri.withAppendedPath(personUri, People.ContactMethods.CONTENT_DIRECTORY);
+        assertStoredValues(twigUri, values);
+
+        assertPersonIdConstraint(ContactMethods.CONTENT_URI, ContactMethods.TYPE,
+                ContactMethods.TYPE_WORK);
+
+        assertTypeAndLabelConstraints(ContactMethods.CONTENT_URI, ContactMethods.PERSON_ID,
+                personId, ContactMethods.TYPE, ContactMethods.TYPE_CUSTOM,
+                ContactMethods.TYPE_OTHER, ContactMethods.LABEL);
+    }
+
+    public void testExtensionsInsert() {
+        ContentValues values = new ContentValues();
+        final Uri personUri = mResolver.insert(People.CONTENT_URI, values);
+        long personId = ContentUris.parseId(personUri);
+
+        values.clear();
+        values.put(Extensions.PERSON_ID, personId);
+        values.put(Extensions.NAME, "Foo");
+        values.put(Extensions.VALUE, "Bar");
+
+        Uri uri = mResolver.insert(Extensions.CONTENT_URI, values);
+        assertStoredValues(uri, values);
+
+        // Access the extensions through People
+        Uri twigUri = Uri.withAppendedPath(personUri, People.Extensions.CONTENT_DIRECTORY);
+        assertStoredValues(twigUri, values);
+    }
+
+    public void testGroupsInsert() {
+        ContentValues values = new ContentValues();
+        values.put(Groups.NAME, "Galois");
+        values.put(Groups.NOTES, "Abel");
+
+        Uri groupUri = mResolver.insert(Groups.CONTENT_URI, values);
+        assertStoredValues(groupUri, values);
+    }
+
+    public void testGroupMembershipsInsert() {
+        ContentValues values = new ContentValues();
+        values.put(Groups.NAME, "Galois");
+        values.put(Groups.NOTES, "Abel");
+        Uri groupUri = mResolver.insert(Groups.CONTENT_URI, values);
+
+        values.clear();
+        values.put(People.NAME, "Klein");
+        Uri personUri = mResolver.insert(People.CONTENT_URI, values);
+
+        long groupId = ContentUris.parseId(groupUri);
+        long personId = ContentUris.parseId(personUri);
+
+        ContentValues values1 = new ContentValues();
+        values1.put(GroupMembership.GROUP_ID, groupId);
+        values1.put(GroupMembership.PERSON_ID, personId);
+        Uri membershipUri = mResolver.insert(GroupMembership.CONTENT_URI, values1);
+        assertStoredValues(membershipUri, values1);
+
+        Uri personsGroupsUri = Uri.withAppendedPath(personUri, GroupMembership.CONTENT_DIRECTORY);
+        assertStoredValues(personsGroupsUri, values1);
+    }
+
+    public void testAddToGroup() {
+        ContentValues values = new ContentValues();
+        Uri personUri = mResolver.insert(People.CONTENT_URI, values);
+        long personId = ContentUris.parseId(personUri);
+
+        values.clear();
+        values.put(Groups.NAME, "Galois");
+        Uri groupUri = mResolver.insert(Groups.CONTENT_URI, values);
+
+        People.addToGroup(mResolver, personId, "Galois");
+
+        values.clear();
+        values.put(GroupMembership.GROUP_ID, ContentUris.parseId(groupUri));
+        values.put(GroupMembership.PERSON_ID, personId);
+
+        Uri personsGroupsUri = Uri.withAppendedPath(personUri, GroupMembership.CONTENT_DIRECTORY);
+        assertStoredValues(personsGroupsUri, values);
+    }
+
+    public void testPresenceInsertMatchOnHandle() {
+        ContentValues values = new ContentValues();
+        putContactValues(values);
+        Uri personUri = mResolver.insert(People.CONTENT_URI, values);
+        long personId = ContentUris.parseId(personUri);
+
+        String encodedProtocol =
+                ContactMethods.encodePredefinedImProtocol(ContactMethods.PROTOCOL_GOOGLE_TALK);
+
+        values.clear();
+        values.put(ContactMethods.PERSON_ID, personId);
+        values.put(ContactMethods.KIND, Contacts.KIND_IM);
+        values.put(ContactMethods.TYPE, ContactMethods.TYPE_HOME);
+        values.put(ContactMethods.DATA, "Android");
+        values.put(ContactMethods.AUX_DATA, encodedProtocol);
+        mResolver.insert(ContactMethods.CONTENT_URI, values);
+
+        values.clear();
+        values.put(Presence.IM_PROTOCOL, encodedProtocol);
+        values.put(Presence.IM_HANDLE, "Android");
+        values.put(Presence.IM_ACCOUNT, "foo");
+
+        Uri presenceUri = mResolver.insert(Presence.CONTENT_URI, values);
+
+        values.put(Presence.PERSON_ID, personId);
+
+        // Presence is joined with People
+        putContactValues(values);
+        assertStoredValues(presenceUri, values);
+    }
+
+    public void testPresenceInsertMatchOnEmail() {
+        ContentValues values = new ContentValues();
+        putContactValues(values);
+        Uri personUri = mResolver.insert(People.CONTENT_URI, values);
+        long personId = ContentUris.parseId(personUri);
+
+        String protocol =
+            ContactMethods.encodePredefinedImProtocol(ContactMethods.PROTOCOL_GOOGLE_TALK);
+
+        values.clear();
+        values.put(ContactMethods.PERSON_ID, personId);
+        values.put(ContactMethods.KIND, Contacts.KIND_EMAIL);
+        values.put(ContactMethods.TYPE, ContactMethods.TYPE_HOME);
+        values.put(ContactMethods.DATA, "Android@android.com");
+        mResolver.insert(ContactMethods.CONTENT_URI, values);
+
+        values.clear();
+        values.put(Presence.IM_PROTOCOL, protocol);
+        values.put(Presence.IM_HANDLE, "Android@android.com");
+        values.put(Presence.IM_ACCOUNT, "foo");
+        values.put(Presence.PRESENCE_STATUS, Presence.OFFLINE);
+        values.put(Presence.PRESENCE_CUSTOM_STATUS, "Coding for Android");
+
+        Uri presenceUri = mResolver.insert(Presence.CONTENT_URI, values);
+
+        values.put(Presence.PERSON_ID, personId);
+        assertStoredValues(presenceUri, values);
+
+        // Now the person should be joined with Presence
+        values.clear();
+        putContactValues(values);
+        values.put(People.IM_PROTOCOL, protocol);
+        values.put(People.IM_HANDLE, "Android@android.com");
+        values.put(People.IM_ACCOUNT, "foo");
+        values.put(People.PRESENCE_STATUS, Presence.OFFLINE);
+        values.put(People.PRESENCE_CUSTOM_STATUS, "Coding for Android");
+        assertStoredValues(personUri, values);
+    }
+
+    public void testPhotoUpdate() throws Exception {
+        byte[] photo = loadTestPhoto();
+
+        ContentValues values = new ContentValues();
+        Uri personUri = mResolver.insert(People.CONTENT_URI, values);
+
+        values.clear();
+        values.put(Photos.DATA, photo);
+
+        Uri photoUri = Uri.withAppendedPath(personUri, Photos.CONTENT_DIRECTORY);
+        mResolver.update(photoUri, values, null, null);
+        assertStoredValues(photoUri, values);
+    }
+
+    private void putContactValues(ContentValues values) {
+        // Populating only unhidden columns
+        values.put(People.NAME, "Deer Dough");
+        values.put(People.PHONETIC_NAME, "Dear Doe");
+        values.put(People.NOTES, "Cash Cow");
+        values.put(People.TIMES_CONTACTED, 3);
+        values.put(People.LAST_TIME_CONTACTED, 10);
+        values.put(People.CUSTOM_RINGTONE, "ringtone");
+        values.put(People.SEND_TO_VOICEMAIL, 1);
+        values.put(People.STARRED, 1);
+    }
+
+    private byte[] loadTestPhoto() throws IOException {
+        final Resources resources = getContext().getResources();
+        InputStream is =
+                resources.openRawResource(com.android.internal.R.drawable.ic_contact_picture);
+        ByteArrayOutputStream os = new ByteArrayOutputStream();
+        byte[] buffer = new byte[1000];
+        int count;
+        while((count = is.read(buffer)) != -1) {
+            os.write(buffer, 0, count);
+        }
+        return os.toByteArray();
+    }
+
+    private void assertFilteredContacts(String filter, String... expectedNames) {
+        Uri filterUri = Uri.withAppendedPath(People.CONTENT_FILTER_URI, filter);
+        Cursor c = mResolver.query(filterUri, null, null, null, null);
+        try {
+            assertEquals("Record count", expectedNames.length, c.getCount());
+            int column = c.getColumnIndex(People.NAME);
+            for (int i = 0; i < expectedNames.length; i++) {
+                c.moveToNext();
+                assertEquals(expectedNames[i], c.getString(column));
+            }
+        } finally {
+            c.close();
+        }
+    }
+
+    private void assertPersonIdConstraint(Uri uri, String typeColumn, int defaultType) {
+        ContentValues values = new ContentValues();
+        values.put(typeColumn, defaultType);
+        try {
+            mResolver.insert(uri, values);
+            fail("Inserted row without person ID");
+        } catch (Exception e) {
+            // Exception expected
+        }
+    }
+
+    private void assertTypeAndLabelConstraints(Uri uri, String personIdColumn, long personId,
+            String typeColumn, int defaultType, int otherType, String labelColumn) {
+        ContentValues values = new ContentValues();
+        values.put(personIdColumn, personId);
+        values.put(typeColumn, defaultType);
+        try {
+            mResolver.insert(uri, values);
+            fail("Inserted row with custom type but without label");
+        } catch (Exception e) {
+            // Exception expected
+        }
+
+        values.clear();
+        values.put(personIdColumn, personId);
+        try {
+            mResolver.insert(uri, values);
+            fail("Inserted row without either type or label");
+        } catch (Exception e) {
+            // Exception expected
+        }
+
+        values.clear();
+        values.put(personIdColumn, personId);
+        values.put(typeColumn, otherType);
+        values.put(labelColumn, "Foo");
+        try {
+            mResolver.insert(uri, values);
+            fail("Inserted row with both type and lable");
+        } catch (Exception e) {
+            // Exception expected
+        }
+    }
+
+    private void assertStoredValues(Uri rowUri, String column, String expectedValue) {
+        String value = getStoredValue(rowUri, column);
+        assertEquals("Column value " + column, expectedValue, value);
+    }
+
+    private String getStoredValue(Uri rowUri, String column) {
+        String value;
+        Cursor c = mResolver.query(rowUri, new String[] { column }, null, null, null);
+        try {
+            c.moveToFirst();
+            value = c.getString(c.getColumnIndex(column));
+        } finally {
+            c.close();
+        }
+        return value;
+    }
+
+    private void assertStoredValues(Uri rowUri, ContentValues expectedValues) {
+        Cursor c = mResolver.query(rowUri, null, null, null, null);
+        try {
+            assertEquals("Record count", 1, c.getCount());
+            c.moveToFirst();
+            assertCursorValues(c, expectedValues);
+        } finally {
+            c.close();
+        }
+    }
+
+    private void assertCursorValues(Cursor cursor, ContentValues expectedValues) {
+        Set<Map.Entry<String, Object>> entries = expectedValues.valueSet();
+        for (Map.Entry<String, Object> entry : entries) {
+            String column = entry.getKey();
+            int index = cursor.getColumnIndex(column);
+            assertTrue("No such column: " + column, index != -1);
+            Object expectedValue = expectedValues.get(column);
+            String value;
+            if (expectedValue instanceof byte[]) {
+                expectedValue = Hex.encodeHex((byte[])expectedValue, false);
+                value = Hex.encodeHex(cursor.getBlob(index), false);
+            } else {
+                expectedValue = expectedValues.getAsString(column);
+                value = cursor.getString(index);
+            }
+            assertEquals("Column value " + column, expectedValue, value);
+        }
+    }
+}
diff --git a/tests/src/com/android/providers/contacts/RestrictionExceptionsTest.java b/tests/src/com/android/providers/contacts/RestrictionExceptionsTest.java
index aad267f..068df23 100644
--- a/tests/src/com/android/providers/contacts/RestrictionExceptionsTest.java
+++ b/tests/src/com/android/providers/contacts/RestrictionExceptionsTest.java
@@ -26,6 +26,7 @@
 import android.database.Cursor;
 import android.net.Uri;
 import android.provider.BaseColumns;
+import android.provider.ContactsContract;
 import android.provider.ContactsContract.Aggregates;
 import android.provider.ContactsContract.Contacts;
 import android.provider.ContactsContract.Data;
@@ -68,10 +69,17 @@
         final Context overallContext = this.getContext();
 
         // Build each of our specific actors in their own Contexts
-        mGrey = new ContactsActor(overallContext, PACKAGE_GREY);
-        mRed = new ContactsActor(overallContext, PACKAGE_RED);
-        mGreen = new ContactsActor(overallContext, PACKAGE_GREEN);
-        mBlue = new ContactsActor(overallContext, PACKAGE_BLUE);
+        mGrey = new ContactsActor(overallContext, PACKAGE_GREY,
+                SynchronousContactsProvider2.class, ContactsContract.AUTHORITY);
+        mRed = new ContactsActor(overallContext, PACKAGE_RED,
+                SynchronousContactsProvider2.class, ContactsContract.AUTHORITY);
+        mGreen = new ContactsActor(overallContext, PACKAGE_GREEN,
+                SynchronousContactsProvider2.class, ContactsContract.AUTHORITY);
+        mBlue = new ContactsActor(overallContext, PACKAGE_BLUE,
+                SynchronousContactsProvider2.class, ContactsContract.AUTHORITY);
+
+        // TODO make the provider wipe data automatically
+        ((SynchronousContactsProvider2)mGrey.provider).wipeData();
     }
 
     /**
@@ -81,9 +89,6 @@
      */
     public void testDataRestriction() {
 
-        // Clear all previous data before starting this test
-        mGrey.provider.wipeData();
-
         // Grey creates an unprotected contact
         long greyContact = mGrey.createContact(false);
         long greyData = mGrey.createPhone(greyContact, PHONE_GREY);
@@ -151,9 +156,6 @@
      */
     public void testAggregateSummary() {
 
-        // Clear all previous data before starting this test
-        mGrey.provider.wipeData();
-
         // Red grants exceptions to itself and Grey
         mRed.updateException(PACKAGE_RED, PACKAGE_RED, true);
         mRed.updateException(PACKAGE_RED, PACKAGE_GREY, true);
@@ -237,9 +239,6 @@
     public void testRestrictionSilence() {
         Cursor cursor;
 
-        // Clear all previous data before starting this test
-        mGrey.provider.wipeData();
-
         // Green grants exception to itself
         mGreen.updateException(PACKAGE_GREEN, PACKAGE_GREEN, true);