Merge change 21858 into eclair

* changes:
  Upgrading CallerInfo to the new Contacts API
diff --git a/src/com/android/providers/contacts/ContactAggregator.java b/src/com/android/providers/contacts/ContactAggregator.java
index a5a2aa9..ce4f90d 100644
--- a/src/com/android/providers/contacts/ContactAggregator.java
+++ b/src/com/android/providers/contacts/ContactAggregator.java
@@ -719,9 +719,9 @@
 
     private void lookupPhoneMatches(SQLiteDatabase db, String phoneNumber, ContactMatcher matcher) {
         SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
-        OpenHelper.buildPhoneLookupQuery(qb, phoneNumber, false /* join mimetypes */);
-        Cursor c = qb.query(db, CONTACT_ID_COLUMNS,
-                RawContacts.CONTACT_ID + " NOT NULL", null, null, null, null);
+        mOpenHelper.buildPhoneLookupAndRawContactQuery(qb, phoneNumber);
+        Cursor c = qb.query(db, CONTACT_ID_COLUMNS, RawContacts.CONTACT_ID + " NOT NULL", null,
+                null, null, null);
         try {
             while (c.moveToNext()) {
                 long contactId = c.getLong(COL_CONTACT_ID);
diff --git a/src/com/android/providers/contacts/ContactsProvider2.java b/src/com/android/providers/contacts/ContactsProvider2.java
index 3827a05..0424cf5 100644
--- a/src/com/android/providers/contacts/ContactsProvider2.java
+++ b/src/com/android/providers/contacts/ContactsProvider2.java
@@ -49,6 +49,7 @@
 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;
@@ -62,8 +63,10 @@
 import android.provider.ContactsContract.Contacts;
 import android.provider.ContactsContract.Data;
 import android.provider.ContactsContract.Groups;
+import android.provider.ContactsContract.PhoneLookup;
 import android.provider.ContactsContract.Presence;
 import android.provider.ContactsContract.RawContacts;
+import android.provider.ContactsContract.Settings;
 import android.provider.ContactsContract.CommonDataKinds.BaseTypes;
 import android.provider.ContactsContract.CommonDataKinds.Email;
 import android.provider.ContactsContract.CommonDataKinds.GroupMembership;
@@ -145,6 +148,8 @@
 
     private static final int AGGREGATION_SUGGESTIONS = 8000;
 
+    private static final int SETTINGS = 9000;
+
     private static final int GROUPS = 10000;
     private static final int GROUPS_ID = 10001;
     private static final int GROUPS_SUMMARY = 10003;
@@ -274,48 +279,24 @@
     public static final String DEFAULT_ACCOUNT_TYPE = "com.google.GAIA";
     public static final String FEATURE_LEGACY_HOSTED_OR_GOOGLE = "legacy_hosted_or_google";
 
-    /** Contains just the contacts columns
-     * @deprecated*/
-    @Deprecated
-    private static final HashMap<String, String> sDeprecatedContactsProjectionMap;
-
+    /** Contains just the contacts columns */
     private static final HashMap<String, String> sContactsProjectionMap;
-
-
     /** Contains the contact columns along with primary phone */
     private static final HashMap<String, String> sContactsSummaryProjectionMap;
     /** Contains just the contacts columns */
     private static final HashMap<String, String> sRawContactsProjectionMap;
-
-    /**
-     * Contains just the contacts columns
-     *
-     * @deprecated
-     */
-    @Deprecated
-    private static final HashMap<String, String> sDeprecatedRawContactsProjectionMap;
-
-    /**
-     * Contains just the data columns
-     *
-     * @deprecated
-     */
-    @Deprecated
-    private static final HashMap<String, String> sDeprecatedDataGroupsProjectionMap;
-
     /** Contains columns from the data view */
     private static final HashMap<String, String> sDataProjectionMap;
-
     /** Contains the data and contacts columns, for joined tables */
-    private static final HashMap<String, String> sDataRawContactsGroupsProjectionMap;
-    /** Contains the data and contacts columns, for joined tables */
-    private static final HashMap<String, String> sDataRawContactsProjectionMap;
+    private static final HashMap<String, String> sPhoneLookupProjectionMap;
     /** Contains the just the {@link Groups} columns */
     private static final HashMap<String, String> sGroupsProjectionMap;
     /** Contains {@link Groups} columns along with summary details */
     private static final HashMap<String, String> sGroupsSummaryProjectionMap;
     /** Contains the agg_exceptions columns */
     private static final HashMap<String, String> sAggregationExceptionsProjectionMap;
+    /** Contains the agg_exceptions columns */
+    private static final HashMap<String, String> sSettingsProjectionMap;
     /** Contains Presence columns */
     private static final HashMap<String, String> sPresenceProjectionMap;
 
@@ -388,6 +369,8 @@
         matcher.addURI(ContactsContract.AUTHORITY, "aggregation_exceptions/*",
                 AGGREGATION_EXCEPTION_ID);
 
+        matcher.addURI(ContactsContract.AUTHORITY, "settings", SETTINGS);
+
         matcher.addURI(ContactsContract.AUTHORITY, "presence", PRESENCE);
         matcher.addURI(ContactsContract.AUTHORITY, "presence/#", PRESENCE_ID);
 
@@ -415,87 +398,6 @@
         sContactsSummaryProjectionMap.put(Contacts.PRESENCE_STATUS,
                 "MAX(" + Presence.PRESENCE_STATUS + ") AS " + Contacts.PRESENCE_STATUS);
 
-        HashMap<String, String> columns;
-
-        // Contacts projection map
-        columns = new HashMap<String, String>();
-        columns.put(Contacts._ID, "contacts._id AS _id");
-        columns.put(Contacts.DISPLAY_NAME, ContactsColumns.CONCRETE_DISPLAY_NAME + " AS "
-                + Contacts.DISPLAY_NAME);
-        columns.put(Contacts.LAST_TIME_CONTACTED, ContactsColumns.CONCRETE_LAST_TIME_CONTACTED
-                + " AS " + Contacts.LAST_TIME_CONTACTED);
-        columns.put(Contacts.TIMES_CONTACTED, ContactsColumns.CONCRETE_TIMES_CONTACTED + " AS "
-                + Contacts.TIMES_CONTACTED);
-        columns.put(Contacts.STARRED, ContactsColumns.CONCRETE_STARRED + " AS "
-                + Contacts.STARRED);
-        columns.put(Contacts.IN_VISIBLE_GROUP, Contacts.IN_VISIBLE_GROUP);
-        columns.put(Contacts.PHOTO_ID, Contacts.PHOTO_ID);
-        columns.put(Contacts.CUSTOM_RINGTONE, ContactsColumns.CONCRETE_CUSTOM_RINGTONE + " AS "
-                + Contacts.CUSTOM_RINGTONE);
-        columns.put(Contacts.SEND_TO_VOICEMAIL, ContactsColumns.CONCRETE_SEND_TO_VOICEMAIL
-                + " AS " + Contacts.SEND_TO_VOICEMAIL);
-        sDeprecatedContactsProjectionMap = columns;
-
-        columns = new HashMap<String, String>();
-        columns.putAll(sDeprecatedContactsProjectionMap);
-
-//        // Contacts primaries projection map. The overall presence status is
-//        // the most-present value, as indicated by the largest value.
-//        columns.put(Contacts.PRESENCE_STATUS, "MAX(" + Presence.PRESENCE_STATUS + ") AS "
-//                + Contacts.PRESENCE_STATUS);
-//        columns.put(Contacts.PRIMARY_PHONE_TYPE, CommonDataKinds.Phone.TYPE);
-//        columns.put(Contacts.PRIMARY_PHONE_LABEL, CommonDataKinds.Phone.LABEL);
-//        columns.put(Contacts.PRIMARY_PHONE_NUMBER, CommonDataKinds.Phone.NUMBER);
-//        sContactsSummaryProjectionMap = columns;
-
-        // RawContacts projection map
-        columns = new HashMap<String, String>();
-        columns.put(RawContacts._ID, Tables.RAW_CONTACTS + "." + RawContacts._ID + " AS _id");
-        columns.put(RawContacts.CONTACT_ID, RawContacts.CONTACT_ID);
-        columns.put(RawContacts.ACCOUNT_NAME,
-                OpenHelper.RawContactsColumns.CONCRETE_ACCOUNT_NAME
-                        + " AS " + RawContacts.ACCOUNT_NAME);
-        columns.put(RawContacts.ACCOUNT_TYPE,
-                OpenHelper.RawContactsColumns.CONCRETE_ACCOUNT_TYPE
-                        + " AS " + RawContacts.ACCOUNT_TYPE);
-        columns.put(RawContacts.SOURCE_ID,
-                OpenHelper.RawContactsColumns.CONCRETE_SOURCE_ID
-                        + " AS " + RawContacts.SOURCE_ID);
-        columns.put(RawContacts.VERSION,
-                OpenHelper.RawContactsColumns.CONCRETE_VERSION
-                        + " AS " + RawContacts.VERSION);
-        columns.put(RawContacts.DIRTY,
-                OpenHelper.RawContactsColumns.CONCRETE_DIRTY
-                        + " AS " + RawContacts.DIRTY);
-        columns.put(RawContacts.DELETED,
-                OpenHelper.RawContactsColumns.CONCRETE_DELETED
-                        + " AS " + RawContacts.DELETED);
-        columns.put(RawContacts.TIMES_CONTACTED,
-                Tables.RAW_CONTACTS + "." + RawContacts.TIMES_CONTACTED
-                        + " AS " + People.TIMES_CONTACTED);
-        columns.put(RawContacts.LAST_TIME_CONTACTED,
-                Tables.RAW_CONTACTS + "." + RawContacts.LAST_TIME_CONTACTED
-                        + " AS " + People.LAST_TIME_CONTACTED);
-        columns.put(RawContacts.CUSTOM_RINGTONE,
-                Tables.RAW_CONTACTS + "." + RawContacts.CUSTOM_RINGTONE
-                        + " AS " + People.CUSTOM_RINGTONE);
-        columns.put(RawContacts.SEND_TO_VOICEMAIL,
-                Tables.RAW_CONTACTS + "." + RawContacts.SEND_TO_VOICEMAIL
-                        + " AS " + People.SEND_TO_VOICEMAIL);
-        columns.put(RawContacts.STARRED,
-                Tables.RAW_CONTACTS + "." + RawContacts.STARRED
-                        + " AS " + People.STARRED);
-        columns.put(RawContacts.AGGREGATION_MODE, RawContacts.AGGREGATION_MODE);
-        columns.put(RawContacts.SYNC1,
-                Tables.RAW_CONTACTS + "." + RawContacts.SYNC1 + " AS " + RawContacts.SYNC1);
-        columns.put(RawContacts.SYNC2,
-                Tables.RAW_CONTACTS + "." + RawContacts.SYNC2 + " AS " + RawContacts.SYNC2);
-        columns.put(RawContacts.SYNC3,
-                Tables.RAW_CONTACTS + "." + RawContacts.SYNC3 + " AS " + RawContacts.SYNC3);
-        columns.put(RawContacts.SYNC4,
-                Tables.RAW_CONTACTS + "." + RawContacts.SYNC4 + " AS " + RawContacts.SYNC4);
-        sDeprecatedRawContactsProjectionMap = columns;
-
         sRawContactsProjectionMap = new HashMap<String, String>();
         sRawContactsProjectionMap.put(RawContacts._ID, RawContacts._ID);
         sRawContactsProjectionMap.put(RawContacts.CONTACT_ID, RawContacts.CONTACT_ID);
@@ -517,42 +419,6 @@
         sRawContactsProjectionMap.put(RawContacts.SYNC3, RawContacts.SYNC3);
         sRawContactsProjectionMap.put(RawContacts.SYNC4, RawContacts.SYNC4);
 
-        // Data projection map
-        columns = new HashMap<String, String>();
-        columns.put(Data._ID, Tables.DATA + "." + Data._ID + " AS _id");
-        columns.put(Data.RAW_CONTACT_ID, Data.RAW_CONTACT_ID);
-        columns.put(Data.RES_PACKAGE, PackagesColumns.PACKAGE + " AS " + Data.RES_PACKAGE);
-        columns.put(Data.MIMETYPE, Data.MIMETYPE);
-        columns.put(Data.IS_PRIMARY, Data.IS_PRIMARY);
-        columns.put(Data.IS_SUPER_PRIMARY, Data.IS_SUPER_PRIMARY);
-        columns.put(Data.DATA_VERSION, Data.DATA_VERSION);
-        columns.put(Data.DATA1, "data.data1 as data1");
-        columns.put(Data.DATA2, "data.data2 as data2");
-        columns.put(Data.DATA3, "data.data3 as data3");
-        columns.put(Data.DATA4, "data.data4 as data4");
-        columns.put(Data.DATA5, "data.data5 as data5");
-        columns.put(Data.DATA6, "data.data6 as data6");
-        columns.put(Data.DATA7, "data.data7 as data7");
-        columns.put(Data.DATA8, "data.data8 as data8");
-        columns.put(Data.DATA9, "data.data9 as data9");
-        columns.put(Data.DATA10, "data.data10 as data10");
-        columns.put(Data.DATA11, "data.data11 as data11");
-        columns.put(Data.DATA12, "data.data12 as data12");
-        columns.put(Data.DATA13, "data.data13 as data13");
-        columns.put(Data.DATA14, "data.data14 as data14");
-        columns.put(Data.DATA15, "data.data15 as data15");
-        columns.put(Data.SYNC1, Tables.DATA + "." + Data.SYNC1 + " AS " + Data.SYNC1);
-        columns.put(Data.SYNC2, Tables.DATA + "." + Data.SYNC2 + " AS " + Data.SYNC2);
-        columns.put(Data.SYNC3, Tables.DATA + "." + Data.SYNC3 + " AS " + Data.SYNC3);
-        columns.put(Data.SYNC4, Tables.DATA + "." + Data.SYNC4 + " AS " + Data.SYNC4);
-        columns.put(GroupMembership.GROUP_SOURCE_ID, GroupsColumns.CONCRETE_SOURCE_ID + " AS "
-                + GroupMembership.GROUP_SOURCE_ID);
-
-        // TODO: remove this projection
-        // Mappings used for backwards compatibility.
-        columns.put("number", Phone.NUMBER);
-        sDeprecatedDataGroupsProjectionMap = columns;
-
         sDataProjectionMap = new HashMap<String, String>();
         sDataProjectionMap.put(Data._ID, Data._ID);
         sDataProjectionMap.put(Data.RAW_CONTACT_ID, Data.RAW_CONTACT_ID);
@@ -595,18 +461,37 @@
         sDataProjectionMap.put(Contacts.PHOTO_ID, Contacts.PHOTO_ID);
         sDataProjectionMap.put(GroupMembership.GROUP_SOURCE_ID, GroupMembership.GROUP_SOURCE_ID);
 
-        // Data, groups and contacts projection map for joins. _id comes from the data table
-        columns = new HashMap<String, String>();
-        columns.putAll(sDeprecatedRawContactsProjectionMap);
-        columns.putAll(sDeprecatedDataGroupsProjectionMap);
-        columns.put(Data.RAW_CONTACT_ID, DataColumns.CONCRETE_RAW_CONTACT_ID);
-        sDataRawContactsGroupsProjectionMap = columns;
+        sPhoneLookupProjectionMap = new HashMap<String, String>();
+        sPhoneLookupProjectionMap.put(PhoneLookup._ID,
+                ContactsColumns.CONCRETE_ID + " AS " + PhoneLookup._ID);
+        sPhoneLookupProjectionMap.put(PhoneLookup.DISPLAY_NAME,
+                ContactsColumns.CONCRETE_DISPLAY_NAME + " AS " + PhoneLookup.DISPLAY_NAME);
+        sPhoneLookupProjectionMap.put(PhoneLookup.LAST_TIME_CONTACTED,
+                ContactsColumns.CONCRETE_LAST_TIME_CONTACTED
+                        + " AS " + PhoneLookup.LAST_TIME_CONTACTED);
+        sPhoneLookupProjectionMap.put(PhoneLookup.TIMES_CONTACTED,
+                ContactsColumns.CONCRETE_TIMES_CONTACTED + " AS " + PhoneLookup.TIMES_CONTACTED);
+        sPhoneLookupProjectionMap.put(PhoneLookup.STARRED,
+                ContactsColumns.CONCRETE_STARRED + " AS " + PhoneLookup.STARRED);
+        sPhoneLookupProjectionMap.put(PhoneLookup.IN_VISIBLE_GROUP,
+                Contacts.IN_VISIBLE_GROUP + " AS " + PhoneLookup.IN_VISIBLE_GROUP);
+        sPhoneLookupProjectionMap.put(PhoneLookup.PHOTO_ID,
+                Contacts.PHOTO_ID + " AS " + PhoneLookup.PHOTO_ID);
+        sPhoneLookupProjectionMap.put(PhoneLookup.CUSTOM_RINGTONE,
+                ContactsColumns.CONCRETE_CUSTOM_RINGTONE + " AS " + PhoneLookup.CUSTOM_RINGTONE);
+        sPhoneLookupProjectionMap.put(PhoneLookup.HAS_PHONE_NUMBER,
+                Contacts.HAS_PHONE_NUMBER + " AS " + PhoneLookup.HAS_PHONE_NUMBER);
+        sPhoneLookupProjectionMap.put(PhoneLookup.SEND_TO_VOICEMAIL,
+                ContactsColumns.CONCRETE_SEND_TO_VOICEMAIL
+                        + " AS " + PhoneLookup.SEND_TO_VOICEMAIL);
+        sPhoneLookupProjectionMap.put(PhoneLookup.NUMBER,
+                Phone.NUMBER + " AS " + PhoneLookup.NUMBER);
+        sPhoneLookupProjectionMap.put(PhoneLookup.TYPE,
+                Phone.TYPE + " AS " + PhoneLookup.TYPE);
+        sPhoneLookupProjectionMap.put(PhoneLookup.LABEL,
+                Phone.LABEL + " AS " + PhoneLookup.LABEL);
 
-        // Data and contacts projection map for joins. _id comes from the data table
-        columns = new HashMap<String, String>();
-        columns.putAll(sDataRawContactsGroupsProjectionMap);
-        columns.remove(GroupMembership.GROUP_SOURCE_ID);
-        sDataRawContactsProjectionMap = columns;
+        HashMap<String, String> columns;
 
         // Groups projection map
         columns = new HashMap<String, String>();
@@ -657,6 +542,16 @@
         columns.put(AggregationExceptions.RAW_CONTACT_ID, AggregationExceptionColumns.RAW_CONTACT_ID2);
         sAggregationExceptionsProjectionMap = columns;
 
+        // Settings projection map
+        columns = new HashMap<String, String>();
+        columns.put(Settings._ID, Settings._ID);
+        columns.put(Settings.ACCOUNT_NAME, Settings.ACCOUNT_NAME);
+        columns.put(Settings.ACCOUNT_TYPE, Settings.ACCOUNT_TYPE);
+        columns.put(Settings.UNGROUPED_VISIBLE, Settings.UNGROUPED_VISIBLE);
+        columns.put(Settings.SHOULD_SYNC_MODE, Settings.SHOULD_SYNC_MODE);
+        columns.put(Settings.SHOULD_SYNC, Settings.SHOULD_SYNC);
+        sSettingsProjectionMap = columns;
+
 
         columns = new HashMap<String, String>();
         columns.put(Presence._ID, Presence._ID);
@@ -1307,6 +1202,11 @@
                 break;
             }
 
+            case SETTINGS: {
+                id = mDb.insert(Tables.SETTINGS, null, values);
+                break;
+            }
+
             case PRESENCE: {
                 id = insertPresence(values);
                 break;
@@ -1668,8 +1568,28 @@
                 return mDb.delete(Tables.CONTACTS, BaseColumns._ID + "=" + contactId, null);
             }
 
+            case RAW_CONTACTS: {
+                final boolean permanently =
+                        readBooleanQueryParameter(uri, RawContacts.DELETE_PERMANENTLY, false);
+                int numDeletes = 0;
+                Cursor c = mDb.query(Tables.RAW_CONTACTS, new String[]{RawContacts._ID},
+                        selection, selectionArgs, null, null, null);
+                try {
+                    while (c.moveToNext()) {
+                        final long rawContactId = c.getLong(0);
+                        numDeletes += deleteRawContact(rawContactId, permanently);
+                    }
+                } finally {
+                    c.close();
+                }
+                return numDeletes;
+            }
+
             case RAW_CONTACTS_ID: {
-                return deleteRawContact(uri);
+                final boolean permanently =
+                        readBooleanQueryParameter(uri, RawContacts.DELETE_PERMANENTLY, false);
+                final long rawContactId = ContentUris.parseId(uri);
+                return deleteRawContact(rawContactId, permanently);
             }
 
             case DATA: {
@@ -1682,11 +1602,35 @@
             }
 
             case GROUPS_ID: {
-                return deleteGroup(uri);
+                boolean markAsDirty = shouldMarkGroupAsDirty(uri);
+                final boolean deletePermanently =
+                        readBooleanQueryParameter(uri, Groups.DELETE_PERMANENTLY, false);
+                return deleteGroup(ContentUris.parseId(uri), markAsDirty, deletePermanently);
+            }
+
+            case GROUPS: {
+                boolean markAsDirty = shouldMarkGroupAsDirty(uri);
+                final boolean permanently = 
+                        readBooleanQueryParameter(uri, RawContacts.DELETE_PERMANENTLY, false);
+                int numDeletes = 0;
+                Cursor c = mDb.query(Tables.GROUPS, new String[]{Groups._ID},
+                        selection, selectionArgs, null, null, null);
+                try {
+                    while (c.moveToNext()) {
+                        numDeletes += deleteGroup(c.getLong(0), markAsDirty, permanently);
+                    }
+                } finally {
+                    c.close();
+                }
+                return numDeletes;
+            }
+
+            case SETTINGS: {
+                return mDb.delete(Tables.SETTINGS, selection, selectionArgs);
             }
 
             case PRESENCE: {
-                return mDb.delete(Tables.PRESENCE, null, null);
+                return mDb.delete(Tables.PRESENCE, selection, selectionArgs);
             }
 
             default:
@@ -1694,11 +1638,11 @@
         }
     }
 
-    private int deleteGroup(Uri uri) {
-        final String flag = uri.getQueryParameter(Groups.DELETE_PERMANENTLY);
-        final boolean permanently = flag != null && "true".equals(flag.toLowerCase());
-        boolean markAsDirty = shouldMarkGroupAsDirty(uri);
-        return deleteGroup(ContentUris.parseId(uri), markAsDirty, permanently);
+    private boolean readBooleanQueryParameter(Uri uri, String name, boolean defaultValue) {
+        final String flag = uri.getQueryParameter(name);
+        return flag == null
+                ? defaultValue
+                : (!"false".equals(flag.toLowerCase()) && !"0".equals(flag.toLowerCase()));
     }
 
     private int deleteGroup(long groupId, boolean markAsDirty, boolean permanently) {
@@ -1724,13 +1668,6 @@
         }
     }
 
-    private int deleteRawContact(Uri uri) {
-        final String flag = uri.getQueryParameter(RawContacts.DELETE_PERMANENTLY);
-        final boolean permanently = flag != null && "true".equals(flag.toLowerCase());
-        final long rawContactId = ContentUris.parseId(uri);
-        return deleteRawContact(rawContactId, permanently);
-    }
-
     public int deleteRawContact(long rawContactId, boolean permanently) {
         // TODO delete aggregation exceptions
         mOpenHelper.removeContactIfSingleton(rawContactId);
@@ -1827,6 +1764,11 @@
                 break;
             }
 
+            case SETTINGS: {
+                count = mDb.update(Tables.SETTINGS, values, selection, selectionArgs);
+                break;
+            }
+
             default:
                 return mLegacyApiSupport.update(uri, values, selection, selectionArgs);
         }
@@ -2317,17 +2259,19 @@
 
             case PHONE_LOOKUP: {
 
-                // TODO: optimize this query returning fewer fields and using an int mimetype
-                // TODO: filter query based on callingUid
                 if (TextUtils.isEmpty(sortOrder)) {
                     // Default the sort order to something reasonable so we get consistent
                     // results when callers don't request an ordering
-                    sortOrder = Data.RAW_CONTACT_ID;
+                    sortOrder = RawContactsColumns.CONCRETE_ID;
                 }
 
-                final String number = uri.getLastPathSegment();
-                OpenHelper.buildPhoneLookupQuery(qb, number, true /* join mimetype */);
-                qb.setProjectionMap(sDataRawContactsProjectionMap);
+                String number = uri.getPathSegments().size() > 1 ? uri.getLastPathSegment() : "";
+                mOpenHelper.buildPhoneLookupAndContactQuery(qb, number);
+                qb.setProjectionMap(sPhoneLookupProjectionMap);
+
+                // Phone lookup cannot be combined with a selection
+                selection = null;
+                selectionArgs = null;
                 break;
             }
 
@@ -2371,6 +2315,12 @@
                         sContactsProjectionMap, maxSuggestions);
             }
 
+            case SETTINGS: {
+                qb.setTables(Tables.SETTINGS);
+                qb.setProjectionMap(sSettingsProjectionMap);
+                break;
+            }
+
             case PRESENCE: {
                 qb.setTables(Tables.PRESENCE);
                 qb.setProjectionMap(sPresenceProjectionMap);
@@ -2832,6 +2782,7 @@
                 return mOpenHelper.getDataMimeType(dataId);
             case AGGREGATION_EXCEPTIONS: return AggregationExceptions.CONTENT_TYPE;
             case AGGREGATION_EXCEPTION_ID: return AggregationExceptions.CONTENT_ITEM_TYPE;
+            case SETTINGS: return Settings.CONTENT_TYPE;
             case AGGREGATION_SUGGESTIONS: return Contacts.CONTENT_TYPE;
             case SEARCH_SUGGESTIONS:
                 return SearchManager.SUGGEST_MIME_TYPE;
@@ -2883,8 +2834,7 @@
             return false;
         }
 
-        String param = uri.getQueryParameter(Groups.MARK_AS_DIRTY);
-        return param == null || (!param.equalsIgnoreCase("false") && !param.equals("0"));
+        return readBooleanQueryParameter(uri, Groups.MARK_AS_DIRTY, true);
     }
 
     /*
diff --git a/src/com/android/providers/contacts/LegacyApiSupport.java b/src/com/android/providers/contacts/LegacyApiSupport.java
index 6c7e53b..9be5760 100644
--- a/src/com/android/providers/contacts/LegacyApiSupport.java
+++ b/src/com/android/providers/contacts/LegacyApiSupport.java
@@ -1248,10 +1248,8 @@
                 applyRawContactsAccount(qb, uri);
                 if (uri.getPathSegments().size() > 2) {
                     String filterParam = uri.getLastPathSegment();
-                    StringBuilder sb = new StringBuilder();
-                    sb.append(" AND person =");
-                    mOpenHelper.appendRawContactsByPhoneNumberAsNestedQuery(sb, filterParam);
-                    qb.appendWhere(sb.toString());
+                    qb.appendWhere(" AND person =");
+                    qb.appendWhere(mOpenHelper.buildPhoneLookupAsNestedQuery(filterParam));
                 }
                 break;
 
diff --git a/src/com/android/providers/contacts/OpenHelper.java b/src/com/android/providers/contacts/OpenHelper.java
index 73b51ca..45a157f 100644
--- a/src/com/android/providers/contacts/OpenHelper.java
+++ b/src/com/android/providers/contacts/OpenHelper.java
@@ -40,6 +40,7 @@
 import android.provider.ContactsContract.Groups;
 import android.provider.ContactsContract.Presence;
 import android.provider.ContactsContract.RawContacts;
+import android.provider.ContactsContract.Settings;
 import android.provider.ContactsContract.CommonDataKinds.Email;
 import android.provider.ContactsContract.CommonDataKinds.GroupMembership;
 import android.provider.ContactsContract.CommonDataKinds.Im;
@@ -59,7 +60,7 @@
 /* package */ class OpenHelper extends SQLiteOpenHelper {
     private static final String TAG = "OpenHelper";
 
-    private static final int DATABASE_VERSION = 68;
+    private static final int DATABASE_VERSION = 71;
     private static final String DATABASE_NAME = "contacts2.db";
     private static final String DATABASE_PRESENCE = "presence_db";
 
@@ -75,6 +76,7 @@
         public static final String PHONE_LOOKUP = "phone_lookup";
         public static final String NAME_LOOKUP = "name_lookup";
         public static final String AGGREGATION_EXCEPTIONS = "agg_exceptions";
+        public static final String SETTINGS = "settings";
         public static final String DATA = "data";
         public static final String GROUPS = "groups";
         public static final String PRESENCE = "presence";
@@ -731,7 +733,7 @@
                 Groups.NOTES + " TEXT," +
                 Groups.SYSTEM_ID + " TEXT," +
                 Groups.DELETED + " INTEGER NOT NULL DEFAULT 0," +
-                Groups.GROUP_VISIBLE + " INTEGER," +
+                Groups.GROUP_VISIBLE + " INTEGER NOT NULL DEFAULT 0," +
                 Groups.SHOULD_SYNC + " INTEGER NOT NULL DEFAULT 1," +
                 Groups.SYNC1 + " TEXT, " +
                 Groups.SYNC2 + " TEXT, " +
@@ -769,6 +771,17 @@
                 AggregationExceptionColumns.RAW_CONTACT_ID1 +
         ");");
 
+        // Settings uses SYNC_MODE_UNSUPPORTED as default unless specified.
+        db.execSQL("CREATE TABLE IF NOT EXISTS " + Tables.SETTINGS + " (" +
+                Settings.ACCOUNT_NAME + " STRING NOT NULL," +
+                Settings.ACCOUNT_TYPE + " STRING NOT NULL," +
+                Settings.UNGROUPED_VISIBLE + " INTEGER NOT NULL DEFAULT 0," +
+                Settings.SHOULD_SYNC_MODE + " INTEGER NOT NULL DEFAULT 0, " +
+                Settings.SHOULD_SYNC + " INTEGER NOT NULL DEFAULT 1, " +
+                "PRIMARY KEY (" + Settings.ACCOUNT_NAME + ", " +
+                Settings.ACCOUNT_TYPE + ") ON CONFLICT REPLACE" +
+        ");");
+
         // The table for recent calls is here so we can do table joins
         // on people, phones, and calls all in one place.
         db.execSQL("CREATE TABLE " + Tables.CALLS + " (" +
@@ -1022,6 +1035,7 @@
         db.execSQL("DROP TABLE IF EXISTS " + Tables.GROUPS + ";");
         db.execSQL("DROP TABLE IF EXISTS " + Tables.ACTIVITIES + ";");
         db.execSQL("DROP TABLE IF EXISTS " + Tables.CALLS + ";");
+        db.execSQL("DROP TABLE IF EXISTS " + Tables.SETTINGS + ";");
 
         db.execSQL("DROP VIEW IF EXISTS " + Tables.CONTACT_ENTITIES + ";");
         db.execSQL("DROP VIEW IF EXISTS " + Views.CONTACTS_ALL + ";");
@@ -1061,6 +1075,7 @@
         db.execSQL("DELETE FROM " + Tables.NAME_LOOKUP + ";");
         db.execSQL("DELETE FROM " + Tables.GROUPS + ";");
         db.execSQL("DELETE FROM " + Tables.AGGREGATION_EXCEPTIONS + ";");
+        db.execSQL("DELETE FROM " + Tables.SETTINGS + ";");
         db.execSQL("DELETE FROM " + Tables.ACTIVITIES + ";");
         db.execSQL("DELETE FROM " + Tables.CALLS + ";");
 
@@ -1242,40 +1257,57 @@
         mNameLookupInsert.executeInsert();
     }
 
-    @Deprecated
-    public static void buildPhoneLookupQuery(SQLiteQueryBuilder qb, String number,
-            boolean joinWithMimetypes) {
-        final String normalizedNumber = PhoneNumberUtils.toCallerIDMinMatch(number);
-        final StringBuilder tables = new StringBuilder();
-        tables.append(Tables.RAW_CONTACTS + ", (SELECT data_id FROM phone_lookup "
-                + "WHERE (phone_lookup.normalized_number GLOB '");
-        tables.append(normalizedNumber);
-        tables.append("*')) AS lookup, ");
-        if (joinWithMimetypes) {
-            tables.append(Tables.DATA_JOIN_MIMETYPES);
-        } else {
-            tables.append(Tables.DATA);
-        }
-        qb.setTables(tables.toString());
-        qb.appendWhere("lookup.data_id=data._id AND data.raw_contact_id=raw_contacts._id AND ");
-        qb.appendWhere("PHONE_NUMBERS_EQUAL(data." + Phone.NUMBER + ", ");
-        qb.appendWhereEscapeString(number);
-        qb.appendWhere(")");
+    public void buildPhoneLookupAndRawContactQuery(SQLiteQueryBuilder qb, String number) {
+        String normalizedNumber = PhoneNumberUtils.toCallerIDMinMatch(number);
+        StringBuilder sb = new StringBuilder();
+        appendPhoneLookupTables(sb, normalizedNumber, false);
+        qb.setTables(sb.toString());
+
+        sb = new StringBuilder();
+        appendPhoneLookupSelection(sb, number);
+        qb.appendWhere(sb.toString());
     }
 
-    public void appendRawContactsByPhoneNumberAsNestedQuery(StringBuilder sb, String number) {
+    public void buildPhoneLookupAndContactQuery(SQLiteQueryBuilder qb, String number) {
+        String normalizedNumber = PhoneNumberUtils.toCallerIDMinMatch(number);
+        StringBuilder sb = new StringBuilder();
+        appendPhoneLookupTables(sb, normalizedNumber, true);
+        qb.setTables(sb.toString());
+
+        sb = new StringBuilder();
+        appendPhoneLookupSelection(sb, number);
+        qb.appendWhere(sb.toString());
+    }
+
+    public String buildPhoneLookupAsNestedQuery(String number) {
+        StringBuilder sb = new StringBuilder();
         final String normalizedNumber = PhoneNumberUtils.toCallerIDMinMatch(number);
-        sb.append("(SELECT DISTINCT raw_contact_id"
-                + " FROM " + Tables.RAW_CONTACTS
-                + ", (SELECT data_id FROM phone_lookup "
+        sb.append("(SELECT DISTINCT raw_contact_id" + " FROM ");
+        appendPhoneLookupTables(sb, normalizedNumber, false);
+        sb.append(" WHERE ");
+        appendPhoneLookupSelection(sb, number);
+        sb.append(")");
+        return sb.toString();
+    }
+
+    private void appendPhoneLookupTables(StringBuilder sb, final String normalizedNumber,
+            boolean joinContacts) {
+        sb.append(Tables.RAW_CONTACTS);
+        if (joinContacts) {
+            sb.append(" JOIN " + getContactView() + " contacts"
+                    + " ON (contacts._id = raw_contacts.contact_id)");
+        }
+        sb.append(", (SELECT data_id FROM phone_lookup "
                 + "WHERE (phone_lookup.normalized_number GLOB '");
         sb.append(normalizedNumber);
-        sb.append("*')) AS lookup, " + Tables.DATA
-                + " WHERE lookup.data_id=data._id"
-                + " AND data.raw_contact_id=raw_contacts._id"
+        sb.append("*')) AS lookup, " + Tables.DATA);
+    }
+
+    private void appendPhoneLookupSelection(StringBuilder sb, String number) {
+        sb.append("lookup.data_id=data._id AND data.raw_contact_id=raw_contacts._id"
                 + " AND PHONE_NUMBERS_EQUAL(data." + Phone.NUMBER + ", ");
         DatabaseUtils.appendEscapedSQLString(sb, number);
-        sb.append("))");
+        sb.append(")");
     }
 
     /**
@@ -1413,7 +1445,7 @@
      */
     static final String[] sAllowedPackages = new String[] {
         "com.android.contacts",
-        "com.facebook",
+        "com.facebook.katana",
     };
 
     /**
diff --git a/tests/src/com/android/providers/contacts/ContactsProvider2Test.java b/tests/src/com/android/providers/contacts/ContactsProvider2Test.java
index 1ca3853..4714bbc 100644
--- a/tests/src/com/android/providers/contacts/ContactsProvider2Test.java
+++ b/tests/src/com/android/providers/contacts/ContactsProvider2Test.java
@@ -156,21 +156,30 @@
         assertEquals(0, getCount(filterUri2, null, null));
     }
 
-    // TODO fix and reenable the test
-    public void _testPhoneLookup() {
-        long rawContactId = createRawContactWithName("Hot", "Tamale");
+    public void testPhoneLookup() {
+        ContentValues values = new ContentValues();
+        values.put(RawContacts.CUSTOM_RINGTONE, "d");
+        values.put(RawContacts.SEND_TO_VOICEMAIL, 1);
+
+        Uri rawContactUri = mResolver.insert(RawContacts.CONTENT_URI, values);
+        long rawContactId = ContentUris.parseId(rawContactUri);
+
+        insertStructuredName(rawContactId, "Hot", "Tamale");
         insertPhoneNumber(rawContactId, "18004664411");
 
         Uri lookupUri1 = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, "8004664411");
-        ContentValues values = new ContentValues();
-        values.put(Contacts.DISPLAY_NAME, "Hot Tamale");
-        values.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE);
-        values.put(Phone.NUMBER, "18004664411");
-        values.put(Phone.TYPE, Phone.TYPE_HOME);
-        values.putNull(Phone.LABEL);
+
+        values.clear();
+        values.put(PhoneLookup._ID, queryContactId(rawContactId));
+        values.put(PhoneLookup.DISPLAY_NAME, "Hot Tamale");
+        values.put(PhoneLookup.NUMBER, "18004664411");
+        values.put(PhoneLookup.TYPE, Phone.TYPE_HOME);
+        values.putNull(PhoneLookup.LABEL);
+        values.put(PhoneLookup.CUSTOM_RINGTONE, "d");
+        values.put(PhoneLookup.SEND_TO_VOICEMAIL, 1);
         assertStoredValues(lookupUri1, values);
 
-        Uri lookupUri2 = Uri.withAppendedPath(Phone.CONTENT_FILTER_URI, "4664411");
+        Uri lookupUri2 = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, "4664411");
         assertEquals(0, getCount(lookupUri2, null, null));
     }