Introducing AGGREGATED_PRESENCE - thanks to JSharkey for the idea.

This new DB table is maintained in memory and summarizes the presence
status and custom status for each (aggregate) contact.  With the help
of this table, we avoid having to do GROUP BY and MAX(status) when
running contacts summary queries. An added benefit is that
we can now get the latest custom status update as part of the
summary query.
diff --git a/src/com/android/providers/contacts/ContactAggregator.java b/src/com/android/providers/contacts/ContactAggregator.java
index ce4f90d..c4b4ad0 100644
--- a/src/com/android/providers/contacts/ContactAggregator.java
+++ b/src/com/android/providers/contacts/ContactAggregator.java
@@ -17,6 +17,7 @@
 package com.android.providers.contacts;
 
 import com.android.providers.contacts.ContactMatcher.MatchScore;
+import com.android.providers.contacts.OpenHelper.AggregatedPresenceColumns;
 import com.android.providers.contacts.OpenHelper.AggregationExceptionColumns;
 import com.android.providers.contacts.OpenHelper.ContactsColumns;
 import com.android.providers.contacts.OpenHelper.DataColumns;
@@ -32,10 +33,12 @@
 import android.database.DatabaseUtils;
 import android.database.sqlite.SQLiteDatabase;
 import android.database.sqlite.SQLiteQueryBuilder;
+import android.database.sqlite.SQLiteStatement;
 import android.provider.ContactsContract.AggregationExceptions;
 import android.provider.ContactsContract.CommonDataKinds;
 import android.provider.ContactsContract.Contacts;
 import android.provider.ContactsContract.Data;
+import android.provider.ContactsContract.Presence;
 import android.provider.ContactsContract.RawContacts;
 import android.provider.ContactsContract.CommonDataKinds.Email;
 import android.provider.ContactsContract.CommonDataKinds.Nickname;
@@ -136,6 +139,8 @@
     // Set if the current aggregation pass should be interrupted
     private volatile boolean mCancel;
 
+    /** Precompiled sql statement for setting an aggregated presence */
+    private SQLiteStatement mAggregatedPresenceReplace;
 
     /**
      * Captures a potential match for a given name. The matching algorithm
@@ -187,6 +192,24 @@
     public ContactAggregator(Context context, OpenHelper openHelper,
             ContactAggregationScheduler scheduler) {
         mOpenHelper = openHelper;
+
+        SQLiteDatabase db = mOpenHelper.getReadableDatabase();
+
+        // Since we have no way of determining which custom status was set last,
+        // we'll just pick one randomly.  We are using MAX as an approximation of randomness
+        mAggregatedPresenceReplace = db.compileStatement(
+                "INSERT OR REPLACE INTO " + Tables.AGGREGATED_PRESENCE + "("
+                        + AggregatedPresenceColumns.CONTACT_ID + ", "
+                        + Presence.PRESENCE_STATUS + ", "
+                        + Presence.PRESENCE_CUSTOM_STATUS
+                + ") SELECT ?, "
+                            + "MAX(" + Presence.PRESENCE_STATUS + "), "
+                            + "MAX(" + Presence.PRESENCE_CUSTOM_STATUS + ")"
+                        + " FROM " + Tables.PRESENCE + "," + Tables.RAW_CONTACTS
+                        + " WHERE " + Presence.RAW_CONTACT_ID + "="
+                                + RawContactsColumns.CONCRETE_ID
+                        + "   AND " + RawContacts.CONTACT_ID + "=?");
+
         mScheduler = scheduler;
         mScheduler.setAggregator(this);
         mScheduler.start();
@@ -344,6 +367,9 @@
         computeAggregateData(db, RawContacts.CONTACT_ID + "=" + contactId, values);
         db.update(Tables.CONTACTS, values, Contacts._ID + "=" + contactId, null);
 
+        mAggregatedPresenceReplace.bindLong(1, contactId);
+        mAggregatedPresenceReplace.bindLong(2, contactId);
+        mAggregatedPresenceReplace.execute();
     }
 
     /**
diff --git a/src/com/android/providers/contacts/ContactsProvider2.java b/src/com/android/providers/contacts/ContactsProvider2.java
index a631577..5600916 100644
--- a/src/com/android/providers/contacts/ContactsProvider2.java
+++ b/src/com/android/providers/contacts/ContactsProvider2.java
@@ -17,6 +17,7 @@
 package com.android.providers.contacts;
 
 import com.android.internal.content.SyncStateContentProviderHelper;
+import com.android.providers.contacts.OpenHelper.AggregatedPresenceColumns;
 import com.android.providers.contacts.OpenHelper.AggregationExceptionColumns;
 import com.android.providers.contacts.OpenHelper.Clauses;
 import com.android.providers.contacts.OpenHelper.ContactsColumns;
@@ -49,7 +50,6 @@
 import android.database.DatabaseUtils;
 import android.database.sqlite.SQLiteCursor;
 import android.database.sqlite.SQLiteDatabase;
-import android.database.sqlite.SQLiteException;
 import android.database.sqlite.SQLiteQueryBuilder;
 import android.database.sqlite.SQLiteStatement;
 import android.net.Uri;
@@ -57,7 +57,6 @@
 import android.preference.PreferenceManager;
 import android.provider.BaseColumns;
 import android.provider.ContactsContract;
-import android.provider.Contacts.People;
 import android.provider.ContactsContract.AggregationExceptions;
 import android.provider.ContactsContract.CommonDataKinds;
 import android.provider.ContactsContract.Contacts;
@@ -333,6 +332,10 @@
     private SQLiteStatement mContactDisplayNameUpdate;
     /** Precompiled sql statement for marking a raw contact as dirty */
     private SQLiteStatement mRawContactDirtyUpdate;
+    /** Precompiled sql statement for setting an aggregated presence */
+    private SQLiteStatement mAggregatedPresenceReplace;
+    /** Precompiled sql statement for updating an aggregated presence status */
+    private SQLiteStatement mAggregatedPresenceStatusUpdate;
 
     static {
         // Contacts URI matching table
@@ -395,7 +398,7 @@
     static {
         sCountProjectionMap = new HashMap<String, String>();
         sCountProjectionMap.put(BaseColumns._COUNT, "COUNT(*)");
-        
+
         sContactsProjectionMap = new HashMap<String, String>();
         sContactsProjectionMap.put(Contacts._ID, Contacts._ID);
         sContactsProjectionMap.put(Contacts.DISPLAY_NAME, Contacts.DISPLAY_NAME);
@@ -411,7 +414,11 @@
         sContactsSummaryProjectionMap = new HashMap<String, String>();
         sContactsSummaryProjectionMap.putAll(sContactsProjectionMap);
         sContactsSummaryProjectionMap.put(Contacts.PRESENCE_STATUS,
-                "MAX(" + Presence.PRESENCE_STATUS + ") AS " + Contacts.PRESENCE_STATUS);
+                Presence.PRESENCE_STATUS + " AS " + Contacts.PRESENCE_STATUS);
+
+        // TODO change this from Presence.PRESENCE_CUSTOM_STATUS to Contacts.PRESENCE_CUSTOM_STATUS
+        sContactsSummaryProjectionMap.put(Presence.PRESENCE_CUSTOM_STATUS,
+                Presence.PRESENCE_CUSTOM_STATUS + " AS " + Presence.PRESENCE_CUSTOM_STATUS);
 
         sRawContactsProjectionMap = new HashMap<String, String>();
         sRawContactsProjectionMap.put(RawContacts._ID, RawContacts._ID);
@@ -581,11 +588,9 @@
         sDataWithPresenceProjectionMap = new HashMap<String, String>();
         sDataWithPresenceProjectionMap.putAll(sDataProjectionMap);
         sDataWithPresenceProjectionMap.put(Presence.PRESENCE_STATUS,
-                "MAX(" + Presence.PRESENCE_STATUS + ") AS " + Presence.PRESENCE_STATUS);
-
-        // TODO implement support for latest custom status
+                Presence.PRESENCE_STATUS);
         sDataWithPresenceProjectionMap.put(Presence.PRESENCE_CUSTOM_STATUS,
-                "NULL AS " + Presence.PRESENCE_CUSTOM_STATUS);
+                Presence.PRESENCE_CUSTOM_STATUS);
 
         sNestedRawContactIdSelect = "SELECT " + Data.RAW_CONTACT_ID + " FROM " + Tables.DATA + " WHERE "
                 + Data._ID + "=?";
@@ -1001,6 +1006,21 @@
         mRawContactDirtyUpdate = db.compileStatement("UPDATE " + Tables.RAW_CONTACTS + " SET "
                 + RawContacts.DIRTY + "=1 WHERE " + RawContacts._ID + "=?");
 
+        mAggregatedPresenceReplace = db.compileStatement(
+                "INSERT OR REPLACE INTO " + Tables.AGGREGATED_PRESENCE + "("
+                        + AggregatedPresenceColumns.CONTACT_ID + ", "
+                        + Presence.PRESENCE_STATUS
+                + ") VALUES (?, (SELECT MAX(" + Presence.PRESENCE_STATUS + ")"
+                        + " FROM " + Tables.PRESENCE + "," + Tables.RAW_CONTACTS
+                        + " WHERE " + Presence.RAW_CONTACT_ID + "="
+                                + RawContactsColumns.CONCRETE_ID
+                        + "   AND " + RawContacts.CONTACT_ID + "=?))");
+
+        mAggregatedPresenceStatusUpdate = db.compileStatement(
+                "UPDATE " + Tables.AGGREGATED_PRESENCE
+                + " SET " + Presence.PRESENCE_CUSTOM_STATUS + "=? "
+                + " WHERE " + AggregatedPresenceColumns.CONTACT_ID + "=?");
+
         mNameSplitter = new NameSplitter(
                 context.getString(com.android.internal.R.string.common_name_prefixes),
                 context.getString(com.android.internal.R.string.common_last_name_prefixes),
@@ -1537,6 +1557,7 @@
                     .append(values.getAsLong(Presence.DATA_ID));
         }
 
+        // TODO remove this capability
         if (values.containsKey(Presence.RAW_CONTACT_ID)) {
             selection.append(" AND " + DataColumns.CONCRETE_RAW_CONTACT_ID + "=")
                     .append(values.getAsLong(Presence.RAW_CONTACT_ID));
@@ -1546,6 +1567,7 @@
 
         long dataId = -1;
         long rawContactId = -1;
+        long contactId = -1;
 
         Cursor cursor = null;
         try {
@@ -1554,6 +1576,7 @@
             if (cursor.moveToFirst()) {
                 dataId = cursor.getLong(DataContactsQuery.DATA_ID);
                 rawContactId = cursor.getLong(DataContactsQuery.RAW_CONTACT_ID);
+                contactId = cursor.getLong(DataContactsQuery.CONTACT_ID);
             } else {
                 // No contact found, return a null URI
                 return -1;
@@ -1569,6 +1592,20 @@
 
         // Insert the presence update
         long presenceId = mDb.replace(Tables.PRESENCE, null, values);
+
+        if (contactId != -1) {
+            if (values.containsKey(Presence.PRESENCE_STATUS)) {
+                mAggregatedPresenceReplace.bindLong(1, contactId);
+                mAggregatedPresenceReplace.bindLong(2, contactId);
+                mAggregatedPresenceReplace.execute();
+            }
+            String status = values.getAsString(Presence.PRESENCE_CUSTOM_STATUS);
+            if (status != null) {
+                mAggregatedPresenceStatusUpdate.bindString(1, status);
+                mAggregatedPresenceStatusUpdate.bindLong(2, contactId);
+                mAggregatedPresenceStatusUpdate.execute();
+            }
+        }
         return presenceId;
     }
 
@@ -2104,7 +2141,6 @@
                 // TODO: join into social status tables
                 qb.setTables(mOpenHelper.getContactSummaryView());
                 qb.setProjectionMap(sContactsSummaryProjectionMap);
-                groupBy = Contacts._ID;
                 break;
             }
 
@@ -2113,7 +2149,6 @@
                 long contactId = ContentUris.parseId(uri);
                 qb.setTables(mOpenHelper.getContactSummaryView());
                 qb.setProjectionMap(sContactsSummaryProjectionMap);
-                groupBy = Contacts._ID;
                 qb.appendWhere(Contacts._ID + "=" + contactId);
                 break;
             }
@@ -2121,13 +2156,12 @@
             case CONTACTS_SUMMARY_FILTER: {
                 qb.setTables(mOpenHelper.getContactSummaryView());
                 qb.setProjectionMap(sContactsSummaryProjectionMap);
-                groupBy = Contacts._ID;
 
                 if (uri.getPathSegments().size() > 2) {
                     String filterParam = uri.getLastPathSegment();
                     StringBuilder sb = new StringBuilder();
-                    sb.append("raw_contact_id IN ");
-                    appendRawContactsByFilterAsNestedQuery(sb, filterParam, null);
+                    sb.append(Contacts._ID + " IN ");
+                    appendContactByFilterAsNestedQuery(sb, filterParam);
                     qb.appendWhere(sb.toString());
                 }
                 break;
@@ -2140,8 +2174,8 @@
                         && uri.getPathSegments().size() > 3) {
                     String filterParam = uri.getLastPathSegment();
                     StringBuilder sb = new StringBuilder();
-                    sb.append("raw_contact_id IN ");
-                    appendRawContactsByFilterAsNestedQuery(sb, filterParam, null);
+                    sb.append(Contacts._ID + " IN ");
+                    appendContactByFilterAsNestedQuery(sb, filterParam);
                     filterSql = sb.toString();
                 }
 
@@ -2184,7 +2218,6 @@
                     qb.appendWhere(sContactsInGroupSelect);
                     selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
                 }
-                groupBy = Contacts._ID;
                 break;
             }
 
@@ -2282,11 +2315,10 @@
 
             case DATA_WITH_PRESENCE: {
                 qb.setTables(mOpenHelper.getDataView() + " data"
-                        + " LEFT OUTER JOIN " + Tables.PRESENCE
-                        + " ON (" + Presence.RAW_CONTACT_ID + "="
-                                + DataColumns.CONCRETE_RAW_CONTACT_ID + ")");
+                        + " LEFT OUTER JOIN " + Tables.AGGREGATED_PRESENCE
+                        + " ON (" + AggregatedPresenceColumns.CONTACT_ID + "="
+                                + RawContacts.CONTACT_ID + ")");
                 qb.setProjectionMap(sDataWithPresenceProjectionMap);
-                groupBy = DataColumns.CONCRETE_ID;
                 break;
             }
 
@@ -2749,7 +2781,7 @@
             }
             mEntityCursor.moveToFirst();
         }
-        
+
         public Entity next() throws RemoteException {
             if (mIsClosed) {
                 throw new IllegalStateException("calling next() when the iterator is closed");
@@ -2914,6 +2946,21 @@
         mSetSuperPrimaryStatement.execute();
     }
 
+    private String getContactByFilterAsNestedQuery(String filterParam) {
+        StringBuilder sb = new StringBuilder();
+        sb.append(Contacts._ID + " IN ");
+        appendContactByFilterAsNestedQuery(sb, filterParam);
+        return sb.toString();
+    }
+
+    private void appendContactByFilterAsNestedQuery(StringBuilder sb, String filterParam) {
+        sb.append("(SELECT DISTINCT " + RawContacts.CONTACT_ID + " FROM " + Tables.RAW_CONTACTS
+                + " JOIN name_lookup ON(" + RawContactsColumns.CONCRETE_ID + "=raw_contact_id)"
+                + " WHERE normalized_name GLOB '");
+        sb.append(NameNormalizer.normalize(filterParam));
+        sb.append("*')");
+    }
+
     public String getRawContactsByFilterAsNestedQuery(String filterParam) {
         StringBuilder sb = new StringBuilder();
         appendRawContactsByFilterAsNestedQuery(sb, filterParam, null);
diff --git a/src/com/android/providers/contacts/OpenHelper.java b/src/com/android/providers/contacts/OpenHelper.java
index 45a157f..e2ff79b 100644
--- a/src/com/android/providers/contacts/OpenHelper.java
+++ b/src/com/android/providers/contacts/OpenHelper.java
@@ -60,7 +60,7 @@
 /* package */ class OpenHelper extends SQLiteOpenHelper {
     private static final String TAG = "OpenHelper";
 
-    private static final int DATABASE_VERSION = 71;
+    private static final int DATABASE_VERSION = 72;
     private static final String DATABASE_NAME = "contacts2.db";
     private static final String DATABASE_PRESENCE = "presence_db";
 
@@ -80,15 +80,11 @@
         public static final String DATA = "data";
         public static final String GROUPS = "groups";
         public static final String PRESENCE = "presence";
+        public static final String AGGREGATED_PRESENCE = "agg_presence";
         public static final String NICKNAME_LOOKUP = "nickname_lookup";
         public static final String CALLS = "calls";
         public static final String CONTACT_ENTITIES = "contact_entities_view";
 
-        public static final String CONTACTS_JOIN_PRESENCE_PRIMARY_PHONE = "contacts "
-                + "LEFT OUTER JOIN raw_contacts ON (contacts._id = raw_contacts.contact_id) "
-                + "LEFT OUTER JOIN presence ON (raw_contacts._id = presence_raw_contact_id) "
-                + "LEFT OUTER JOIN data ON (primary_phone_id = data._id)";
-
         public static final String DATA_JOIN_MIMETYPES = "data "
                 + "LEFT OUTER JOIN mimetypes ON (data.mimetype_id = mimetypes._id)";
 
@@ -386,6 +382,10 @@
         public static final String CLUSTER = "cluster";
     }
 
+    public interface AggregatedPresenceColumns {
+        String CONTACT_ID = "presence_contact_id";
+    }
+
     private static final String[] NICKNAME_LOOKUP_COLUMNS = new String[] {
         NicknameLookupColumns.CLUSTER
     };
@@ -486,12 +486,8 @@
         mVisibleSpecificUpdate = db.compileStatement(visibleUpdate + " WHERE "
                 + ContactsColumns.CONCRETE_ID + "=?");
 
-        // Make sure we have an in-memory presence table
-        final String tableName = DATABASE_PRESENCE + "." + Tables.PRESENCE;
-        final String indexName = DATABASE_PRESENCE + ".presenceIndex";
-
         db.execSQL("ATTACH DATABASE ':memory:' AS " + DATABASE_PRESENCE + ";");
-        db.execSQL("CREATE TABLE IF NOT EXISTS " + tableName + " ("+
+        db.execSQL("CREATE TABLE IF NOT EXISTS " + DATABASE_PRESENCE + "." + Tables.PRESENCE + " ("+
                 Presence._ID + " INTEGER PRIMARY KEY," +
                 Presence.RAW_CONTACT_ID + " INTEGER REFERENCES raw_contacts(_id)," +
                 Presence.DATA_ID + " INTEGER REFERENCES data(_id)," +
@@ -504,8 +500,16 @@
                         + Presence.IM_ACCOUNT + ")" +
         ");");
 
-        db.execSQL("CREATE INDEX IF NOT EXISTS " + indexName + " ON " + Tables.PRESENCE + " ("
-                + Presence.RAW_CONTACT_ID + ");");
+        db.execSQL("CREATE INDEX IF NOT EXISTS " + DATABASE_PRESENCE + ".presenceIndex" + " ON "
+                + Tables.PRESENCE + " (" + Presence.RAW_CONTACT_ID + ");");
+
+        db.execSQL("CREATE TABLE IF NOT EXISTS "
+                        + DATABASE_PRESENCE + "." + Tables.AGGREGATED_PRESENCE + " ("+
+                AggregatedPresenceColumns.CONTACT_ID
+                        + " INTEGER PRIMARY KEY REFERENCES contacts(_id)," +
+                Presence.PRESENCE_STATUS + " INTEGER," +
+                Presence.PRESENCE_CUSTOM_STATUS + " TEXT" +
+        ");");
     }
 
     @Override
@@ -992,19 +996,13 @@
 
         String contactSummarySelect = "SELECT "
                 + ContactsColumns.CONCRETE_ID + " AS " + Contacts._ID + ","
-                + RawContactsColumns.CONCRETE_ID + " AS raw_contact_id,"
                 + contactsColumns
-                + " FROM " + Tables.CONTACTS
-                + " LEFT OUTER JOIN " + Tables.RAW_CONTACTS + " ON ("
-                        + ContactsColumns.CONCRETE_ID + "=" + RawContacts.CONTACT_ID + ")";
+                + " FROM " + Tables.CONTACTS;
 
         String restrictedContactSummarySelect = "SELECT "
                 + ContactsColumns.CONCRETE_ID + " AS " + Contacts._ID + ","
-                + RawContactsColumns.CONCRETE_ID + " AS raw_contact_id,"
                 + contactsColumns
                 + " FROM " + Tables.CONTACTS
-                + " LEFT OUTER JOIN " + Tables.RAW_CONTACTS + " ON ("
-                        + ContactsColumns.CONCRETE_ID + "=" + RawContacts.CONTACT_ID + ")"
                 + " WHERE " + ContactsColumns.SINGLE_IS_RESTRICTED + "=0";
 
         db.execSQL("CREATE VIEW " + Views.CONTACT_SUMMARY_ALL
@@ -1484,7 +1482,7 @@
     public String getContactSummaryView() {
         String summaryView = hasRestrictedAccess() ? Views.CONTACT_SUMMARY_ALL
                 : Views.CONTACT_SUMMARY_RESTRICTED;
-        return summaryView + " LEFT OUTER JOIN " + Tables.PRESENCE + " ON (raw_contact_id = "
-                + Presence.RAW_CONTACT_ID + ") ";
+        return summaryView + " LEFT OUTER JOIN " + Tables.AGGREGATED_PRESENCE + " ON ("
+                + Contacts._ID + " = " + AggregatedPresenceColumns.CONTACT_ID + ") ";
     }
 }
diff --git a/tests/src/com/android/providers/contacts/BaseContactsProvider2Test.java b/tests/src/com/android/providers/contacts/BaseContactsProvider2Test.java
index 1be7c5a..55e288c 100644
--- a/tests/src/com/android/providers/contacts/BaseContactsProvider2Test.java
+++ b/tests/src/com/android/providers/contacts/BaseContactsProvider2Test.java
@@ -249,11 +249,12 @@
         return mResolver.insert(Data.CONTENT_URI, values);
     }
 
-    protected Uri insertPresence(int protocol, String handle, int presence) {
+    protected Uri insertPresence(int protocol, String handle, int presence, String status) {
         ContentValues values = new ContentValues();
         values.put(Presence.IM_PROTOCOL, protocol);
         values.put(Presence.IM_HANDLE, handle);
         values.put(Presence.PRESENCE_STATUS, presence);
+        values.put(Presence.PRESENCE_CUSTOM_STATUS, status);
 
         Uri resultUri = mResolver.insert(Presence.CONTENT_URI, values);
         return resultUri;
diff --git a/tests/src/com/android/providers/contacts/ContactsProvider2Test.java b/tests/src/com/android/providers/contacts/ContactsProvider2Test.java
index 46b7a2f..f882339 100644
--- a/tests/src/com/android/providers/contacts/ContactsProvider2Test.java
+++ b/tests/src/com/android/providers/contacts/ContactsProvider2Test.java
@@ -361,8 +361,8 @@
         insertEmail(rawContactId, "goog411@acme.com");
         insertEmail(rawContactId, "goog412@acme.com");
 
-        insertPresence(Im.PROTOCOL_GOOGLE_TALK, "goog411@acme.com", Presence.INVISIBLE);
-        insertPresence(Im.PROTOCOL_GOOGLE_TALK, "goog412@acme.com", Presence.AVAILABLE);
+        insertPresence(Im.PROTOCOL_GOOGLE_TALK, "goog411@acme.com", Presence.INVISIBLE, "Bad");
+        insertPresence(Im.PROTOCOL_GOOGLE_TALK, "goog412@acme.com", Presence.AVAILABLE, "Good");
         long contactId = queryContactId(rawContactId);
 
         Uri uri = Uri.withAppendedPath(ContactsContract.AUTHORITY_URI, "data_with_presence");
@@ -375,6 +375,7 @@
 
         values.clear();
         values.put(Presence.PRESENCE_STATUS, Presence.AVAILABLE);
+        values.put(Presence.PRESENCE_CUSTOM_STATUS, "Good");
         values.put(Contacts.DISPLAY_NAME, "John Doe");
         values.put(Phone.NUMBER, "18004664411");
         values.putNull(Phone.LABEL);
@@ -385,6 +386,7 @@
 
         values.clear();
         values.put(Presence.PRESENCE_STATUS, Presence.AVAILABLE);
+        values.put(Presence.PRESENCE_CUSTOM_STATUS, "Good");
         values.put(Contacts.DISPLAY_NAME, "John Doe");
         values.put(Phone.NUMBER, "18004664412");
         values.putNull(Phone.LABEL);
@@ -515,16 +517,17 @@
         long rawContactId1 = createRawContact();
         insertImHandle(rawContactId1, protocol1, handle1);
 
-        insertPresence(protocol1, handle1, Presence.AVAILABLE);
-        insertPresence(protocol1, handle1, Presence.AWAY);
-        insertPresence(protocol1, handle1, Presence.INVISIBLE);
+        insertPresence(protocol1, handle1, Presence.AVAILABLE, "Green");
+        insertPresence(protocol1, handle1, Presence.AWAY, "Yellow");
+        insertPresence(protocol1, handle1, Presence.INVISIBLE, "Red");
 
         Cursor c = queryContactSummary(queryContactId(rawContactId1),
-                new String[] {Presence.PRESENCE_STATUS});
+                new String[] {Presence.PRESENCE_STATUS, Presence.PRESENCE_CUSTOM_STATUS});
         assertEquals(1, c.getCount());
 
         c.moveToFirst();
         assertEquals(Presence.AVAILABLE, c.getInt(0));
+        assertEquals("Red", c.getString(1));    // Last inserted
 
     }
 
@@ -744,7 +747,7 @@
         Uri uri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId);
 
         insertImHandle(rawContactId, Im.PROTOCOL_GOOGLE_TALK, "deleteme@android.com");
-        insertPresence(Im.PROTOCOL_GOOGLE_TALK, "deleteme@android.com", Presence.AVAILABLE);
+        insertPresence(Im.PROTOCOL_GOOGLE_TALK, "deleteme@android.com", Presence.AVAILABLE, null);
         assertEquals(1, getCount(Uri.withAppendedPath(uri, RawContacts.Data.CONTENT_DIRECTORY),
                 null, null));
         assertEquals(1, getCount(Presence.CONTENT_URI, Presence.RAW_CONTACT_ID + "=" + rawContactId,
@@ -876,7 +879,7 @@
         insertPhoneNumber(rawContactId, phoneNumber);
         insertEmail(rawContactId, email);
 
-        insertPresence(Im.PROTOCOL_GOOGLE_TALK, email, presenceStatus);
+        insertPresence(Im.PROTOCOL_GOOGLE_TALK, email, presenceStatus, "hacking");
 
         if (groupId != 0) {
             insertGroupMembership(rawContactId, groupId);