Merge "Send a broadcast when profile changes" into jb-mr1-dev
diff --git a/src/com/android/providers/contacts/ContactsDatabaseHelper.java b/src/com/android/providers/contacts/ContactsDatabaseHelper.java
index 62f49ae..95008d7 100644
--- a/src/com/android/providers/contacts/ContactsDatabaseHelper.java
+++ b/src/com/android/providers/contacts/ContactsDatabaseHelper.java
@@ -4453,20 +4453,35 @@
     }
 
     /**
-     * As opposed to {@link #buildPhoneLookupAndContactQuery}, this phone lookup will only do
-     * a comparison based on the last seven digits of the given phone number.  This is only intended
-     * to be used as a fallback, in case the regular lookup does not return any results.
+     * Phone lookup method that uses the custom SQLite function phone_number_compare_loose
+     * that serves as a fallback in case the regular lookup does not return any results.
      * @param qb The query builder.
      * @param number The phone number to search for.
      */
-    public void buildMinimalPhoneLookupAndContactQuery(SQLiteQueryBuilder qb, String number) {
-        String minMatch = PhoneNumberUtils.toCallerIDMinMatch(number);
-        StringBuilder sb = new StringBuilder();
-        appendPhoneLookupTables(sb, minMatch, true);
+    public void buildFallbackPhoneLookupAndContactQuery(SQLiteQueryBuilder qb, String number) {
+        final String minMatch = PhoneNumberUtils.toCallerIDMinMatch(number);
+        final StringBuilder sb = new StringBuilder();
+        //append lookup tables
+        sb.append(Tables.RAW_CONTACTS);
+        sb.append(" JOIN " + Views.CONTACTS + " as contacts_view"
+                + " ON (contacts_view._id = " + Tables.RAW_CONTACTS
+                + "." + RawContacts.CONTACT_ID + ")" +
+                " JOIN (SELECT " + PhoneLookupColumns.DATA_ID + "," +
+                PhoneLookupColumns.NORMALIZED_NUMBER + " FROM "+ Tables.PHONE_LOOKUP + " "
+                + "WHERE (" + Tables.PHONE_LOOKUP + "." + PhoneLookupColumns.MIN_MATCH + " = '");
+        sb.append(minMatch);
+        sb.append("')) AS lookup " +
+                "ON lookup." + PhoneLookupColumns.DATA_ID + "=" + Tables.DATA + "." + Data._ID
+                + " JOIN " + Tables.DATA + " "
+                + "ON " + Tables.DATA + "." + Data.RAW_CONTACT_ID + "=" + Tables.RAW_CONTACTS + "."
+                + RawContacts._ID);
+
         qb.setTables(sb.toString());
 
-        sb = new StringBuilder();
-        appendPhoneLookupSelection(sb, null, null);
+        sb.setLength(0);
+        sb.append("PHONE_NUMBERS_EQUAL(" + Tables.DATA + "." + Phone.NUMBER + ", ");
+        DatabaseUtils.appendEscapedSQLString(sb, number);
+        sb.append(mUseStrictPhoneNumberComparison ? ", 1)" : ", 0)");
         qb.appendWhere(sb.toString());
     }
 
@@ -4526,28 +4541,33 @@
                 sb.append(" OR ");
             }
             if (hasNumber) {
-                int numberLen = number.length();
-                sb.append(" lookup.len <= ");
-                sb.append(numberLen);
-                sb.append(" AND substr(");
-                DatabaseUtils.appendEscapedSQLString(sb, number);
-                sb.append(',');
-                sb.append(numberLen);
-                sb.append(" - lookup.len + 1) = lookup.normalized_number");
+                // skip the suffix match entirely if we are using strict number comparison
+                if (!mUseStrictPhoneNumberComparison) {
+                    int numberLen = number.length();
+                    sb.append(" lookup.len <= ");
+                    sb.append(numberLen);
+                    sb.append(" AND substr(");
+                    DatabaseUtils.appendEscapedSQLString(sb, number);
+                    sb.append(',');
+                    sb.append(numberLen);
+                    sb.append(" - lookup.len + 1) = lookup.normalized_number");
 
-                // Some countries (e.g. Brazil) can have incoming calls which contain only the local
-                // number (no country calling code and no area code). This case is handled below.
-                // Details see b/5197612.
-                // This also handles a Gingerbread -> ICS upgrade issue; see b/5638376.
-                sb.append(" OR (");
-                sb.append(" lookup.len > ");
-                sb.append(numberLen);
-                sb.append(" AND substr(lookup.normalized_number,");
-                sb.append("lookup.len + 1 - ");
-                sb.append(numberLen);
-                sb.append(") = ");
-                DatabaseUtils.appendEscapedSQLString(sb, number);
-                sb.append(")");
+                    // Some countries (e.g. Brazil) can have incoming calls which contain only the local
+                    // number (no country calling code and no area code). This case is handled below.
+                    // Details see b/5197612.
+                    // This also handles a Gingerbread -> ICS upgrade issue; see b/5638376.
+                    sb.append(" OR (");
+                    sb.append(" lookup.len > ");
+                    sb.append(numberLen);
+                    sb.append(" AND substr(lookup.normalized_number,");
+                    sb.append("lookup.len + 1 - ");
+                    sb.append(numberLen);
+                    sb.append(") = ");
+                    DatabaseUtils.appendEscapedSQLString(sb, number);
+                    sb.append(")");
+                } else {
+                    sb.append("0");
+                }
             }
             sb.append(')');
         }
@@ -5276,6 +5296,16 @@
     }
 
     @NeededForTesting
+    /* package */ void setUseStrictPhoneNumberComparisonForTest(boolean useStrict) {
+        mUseStrictPhoneNumberComparison = useStrict;
+    }
+
+    @NeededForTesting
+    /* package */ boolean getUseStrictPhoneNumberComparisonForTest() {
+        return mUseStrictPhoneNumberComparison;
+    }
+
+    @NeededForTesting
     /* package */ String querySearchIndexContentForTest(long contactId) {
         return DatabaseUtils.stringForQuery(getReadableDatabase(),
                 "SELECT " + SearchIndexColumns.CONTENT +
diff --git a/src/com/android/providers/contacts/ContactsProvider2.java b/src/com/android/providers/contacts/ContactsProvider2.java
index ef2ea9a..d236de3 100644
--- a/src/com/android/providers/contacts/ContactsProvider2.java
+++ b/src/com/android/providers/contacts/ContactsProvider2.java
@@ -5768,10 +5768,13 @@
                     selectionArgs = mDbHelper.get().buildSipContactQuery(sb, sipAddress);
                     selection = sb.toString();
                 } else {
+                    // Use this flag to track whether sortOrder was originally empty
+                    boolean sortOrderIsEmpty = false;
                     if (TextUtils.isEmpty(sortOrder)) {
                         // Default the sort order to something reasonable so we get consistent
                         // results when callers don't request an ordering
                         sortOrder = " length(lookup.normalized_number) DESC";
+                        sortOrderIsEmpty = true;
                     }
 
                     String number = uri.getPathSegments().size() > 1
@@ -5786,7 +5789,8 @@
 
                     // Peek at the results of the first query (which attempts to use fully
                     // normalized and internationalized numbers for comparison).  If no results
-                    // were returned, fall back to doing a match of the trailing 7 digits.
+                    // were returned, fall back to using the SQLite function
+                    // phone_number_compare_loose.
                     qb.setStrict(true);
                     boolean foundResult = false;
                     Cursor cursor = query(db, qb, projection, selection, selectionArgs,
@@ -5796,9 +5800,15 @@
                             foundResult = true;
                             return cursor;
                         } else {
+                            // Use fallback lookup method
+
                             qb = new SQLiteQueryBuilder();
-                            mDbHelper.get().buildMinimalPhoneLookupAndContactQuery(
-                                    qb, normalizedNumber);
+
+                            // use the raw number instead of the normalized number because
+                            // phone_number_compare_loose in SQLite works only with non-normalized
+                            // numbers
+                            mDbHelper.get().buildFallbackPhoneLookupAndContactQuery(qb, number);
+
                             qb.setProjectionMap(sPhoneLookupProjectionMap);
                         }
                     } finally {
diff --git a/tests/src/com/android/providers/contacts/ContactsProvider2Test.java b/tests/src/com/android/providers/contacts/ContactsProvider2Test.java
index 003012e..77789c3 100644
--- a/tests/src/com/android/providers/contacts/ContactsProvider2Test.java
+++ b/tests/src/com/android/providers/contacts/ContactsProvider2Test.java
@@ -67,6 +67,7 @@
 import android.text.TextUtils;
 
 import com.android.internal.util.ArrayUtils;
+import com.android.providers.contacts.ContactsDatabaseHelper;
 import com.android.providers.contacts.ContactsDatabaseHelper.AggregationExceptionColumns;
 import com.android.providers.contacts.ContactsDatabaseHelper.DataUsageStatColumns;
 import com.android.providers.contacts.ContactsDatabaseHelper.DbProperties;
@@ -1282,6 +1283,10 @@
         // call id should  match to both "8004664411" and "+18004664411".
         Uri lookupUri2 = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, "4664411");
         assertEquals(2, getCount(lookupUri2, null, null));
+
+        // A wrong area code 799 vs 800 should not be matched
+        lookupUri2 = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, "7994664411");
+        assertEquals(0, getCount(lookupUri2, null, null));
     }
 
     public void testPhoneLookupUseCases() {
@@ -1310,6 +1315,18 @@
         lookupUri2 = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, "650 861 0000");
         assertEquals(1, getCount(lookupUri2, null, null));
 
+        // does not match with wrong area code
+        lookupUri2 = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, "649 861 0000");
+        assertEquals(0, getCount(lookupUri2, null, null));
+
+        // does not match with missing digits in mistyped area code
+        lookupUri2 = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, "5 861 0000");
+        assertEquals(0, getCount(lookupUri2, null, null));
+
+        // does not match with missing digit in mistyped area code
+        lookupUri2 = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, "65 861 0000");
+        assertEquals(0, getCount(lookupUri2, null, null));
+
         // National format in contacts
         values.clear();
         values.put(RawContacts.CUSTOM_RINGTONE, "d");
@@ -1352,8 +1369,8 @@
     }
 
     public void testIntlPhoneLookupUseCases() {
-        // Checks the logic that relies on using the trailing 7-digits as a fallback for phone
-        // number lookups.
+        // Checks the logic that relies on phone_number_compare_loose(Gingerbread) as a fallback
+        //for phone number lookups.
         String fullNumber = "01197297427289";
 
         ContentValues values = new ContentValues();
@@ -1371,9 +1388,9 @@
         assertEquals(2, getCount(Uri.withAppendedPath(
                 PhoneLookup.CONTENT_FILTER_URI, "097427289"), null, null));
 
-        // Shorter (local) number with +0 prefix should also match.
-        assertEquals(2, getCount(Uri.withAppendedPath(
-                PhoneLookup.CONTENT_FILTER_URI, "+097427289"), null, null));
+        // Number with international (+972) prefix should also match.
+        assertEquals(1, getCount(Uri.withAppendedPath(
+                PhoneLookup.CONTENT_FILTER_URI, "+97297427289"), null, null));
 
         // Same shorter number with dashes should match.
         assertEquals(2, getCount(Uri.withAppendedPath(
@@ -1415,6 +1432,63 @@
                 PhoneLookup.CONTENT_FILTER_URI, "4 879 601 0101"), null, null));
     }
 
+    public void testPhoneLookupUseStrictPhoneNumberCompare() {
+        // Test lookup cases when mUseStrictPhoneNumberComparison is true
+        final ContactsProvider2 cp = (ContactsProvider2) getProvider();
+        final ContactsDatabaseHelper dbHelper = cp.getThreadActiveDatabaseHelperForTest();
+        // Get and save the original value of mUseStrictPhoneNumberComparison so that we
+        // can restore it when we are done with the test
+        final boolean oldUseStrict = dbHelper.getUseStrictPhoneNumberComparisonForTest();
+        dbHelper.setUseStrictPhoneNumberComparisonForTest(true);
+
+
+        try {
+            String fullNumber = "01197297427289";
+            ContentValues values = new ContentValues();
+            values.put(RawContacts.CUSTOM_RINGTONE, "d");
+            values.put(RawContacts.SEND_TO_VOICEMAIL, 1);
+            long rawContactId = ContentUris.parseId(
+                    mResolver.insert(RawContacts.CONTENT_URI, values));
+            insertStructuredName(rawContactId, "Senor", "Chang");
+            insertPhoneNumber(rawContactId, fullNumber);
+            insertPhoneNumber(rawContactId, "5103337596");
+            insertPhoneNumber(rawContactId, "+19012345678");
+            // One match for full number
+            assertEquals(1, getCount(Uri.withAppendedPath(
+                    PhoneLookup.CONTENT_FILTER_URI, fullNumber), null, null));
+
+            // No matches for extra digit at the front
+            assertEquals(0, getCount(Uri.withAppendedPath(
+                    PhoneLookup.CONTENT_FILTER_URI, "55103337596"), null, null));
+            // No matches for mispelled area code
+            assertEquals(0, getCount(Uri.withAppendedPath(
+                    PhoneLookup.CONTENT_FILTER_URI, "5123337596"), null, null));
+
+            // One match for matching number with dashes
+            assertEquals(1, getCount(Uri.withAppendedPath(
+                    PhoneLookup.CONTENT_FILTER_URI, "510-333-7596"), null, null));
+
+            // One match for matching number with international code
+            assertEquals(1, getCount(Uri.withAppendedPath(
+                    PhoneLookup.CONTENT_FILTER_URI, "+1-510-333-7596"), null, null));
+            values.clear();
+
+            // No matches for extra 0 in front
+            assertEquals(0, getCount(Uri.withAppendedPath(
+                    PhoneLookup.CONTENT_FILTER_URI, "0-510-333-7596"), null, null));
+            values.clear();
+
+            // No matches for different country code
+            assertEquals(0, getCount(Uri.withAppendedPath(
+                    PhoneLookup.CONTENT_FILTER_URI, "+819012345678"), null, null));
+            values.clear();
+        } finally {
+            // restore the original value of mUseStrictPhoneNumberComparison
+            // upon test completion or failure
+            dbHelper.setUseStrictPhoneNumberComparisonForTest(oldUseStrict);
+        }
+    }
+
     public void testPhoneUpdate() {
         ContentValues values = new ContentValues();
         Uri rawContactUri = mResolver.insert(RawContacts.CONTENT_URI, values);