Merge "Provider side changes for exposing data usage stats" into jb-mr2-dev
diff --git a/src/com/android/providers/contacts/ContactLocaleUtils.java b/src/com/android/providers/contacts/ContactLocaleUtils.java
index b53dab4..c63f7fc 100644
--- a/src/com/android/providers/contacts/ContactLocaleUtils.java
+++ b/src/com/android/providers/contacts/ContactLocaleUtils.java
@@ -17,6 +17,8 @@
 package com.android.providers.contacts;
 
 import android.provider.ContactsContract.FullNameStyle;
+import android.provider.ContactsContract.PhoneticNameStyle;
+import android.text.TextUtils;
 import android.util.Log;
 
 import com.android.providers.contacts.HanziToPinyin.Token;
@@ -35,6 +37,7 @@
 
 import libcore.icu.AlphabeticIndex;
 import libcore.icu.AlphabeticIndex.ImmutableIndex;
+import libcore.icu.Transliterator;
 
 /**
  * This utility class provides specialized handling for locale specific
@@ -43,6 +46,13 @@
 public class ContactLocaleUtils {
     public static final String TAG = "ContactLocale";
 
+    public static final Locale LOCALE_ARABIC = new Locale("ar");
+    public static final Locale LOCALE_GREEK = new Locale("el");
+    public static final Locale LOCALE_HEBREW = new Locale("he");
+    // Ukrainian labels are superset of Russian
+    public static final Locale LOCALE_UKRAINIAN = new Locale("uk");
+    public static final Locale LOCALE_THAI = new Locale("th");
+
     /**
      * This class is the default implementation and should be the base class
      * for other locales.
@@ -61,8 +71,28 @@
         private final int mNumberBucketIndex;
 
         public ContactLocaleUtilsBase(Locale locale) {
+            // AlphabeticIndex.getBucketLabel() uses a binary search across
+            // the entire label set so care should be taken about growing this
+            // set too large. The following set determines for which locales
+            // we will show labels other than your primary locale. General rules
+            // of thumb for adding a locale: should be a supported locale; and
+            // should not be included if from a name it is not deterministic
+            // which way to label it (so eg Chinese cannot be added because
+            // the labeling of a Chinese character varies between Simplified,
+            // Traditional, and Japanese locales). Use English only for all
+            // Latin based alphabets. Ukrainian is chosen for Cyrillic because
+            // its alphabet is a superset of Russian.
             mAlphabeticIndex = new AlphabeticIndex(locale)
-                .addLabels(Locale.US).getImmutableIndex();
+                .setMaxLabelCount(300)
+                .addLabels(Locale.ENGLISH)
+                .addLabels(Locale.JAPANESE)
+                .addLabels(Locale.KOREAN)
+                .addLabels(LOCALE_THAI)
+                .addLabels(LOCALE_ARABIC)
+                .addLabels(LOCALE_HEBREW)
+                .addLabels(LOCALE_GREEK)
+                .addLabels(LOCALE_UKRAINIAN)
+                .getImmutableIndex();
             mAlphabeticIndexBucketCount = mAlphabeticIndex.getBucketCount();
             mNumberBucketIndex = mAlphabeticIndexBucketCount - 1;
         }
@@ -138,7 +168,7 @@
         }
 
         @SuppressWarnings("unused")
-        public Iterator<String> getNameLookupKeys(String name) {
+        public Iterator<String> getNameLookupKeys(String name, int nameStyle) {
             return null;
         }
 
@@ -248,11 +278,56 @@
             }
             return super.getBucketLabel(bucketIndex);
         }
+
+        @Override
+        public Iterator<String> getNameLookupKeys(String name, int nameStyle) {
+            // Hiragana and Katakana will be positively identified as Japanese.
+            if (nameStyle == PhoneticNameStyle.JAPANESE) {
+                return getRomajiNameLookupKeys(name);
+            }
+            return null;
+        }
+
+        private static boolean mInitializedTransliterator;
+        private static Transliterator mJapaneseTransliterator;
+
+        private static Transliterator getJapaneseTransliterator() {
+            synchronized(JapaneseContactUtils.class) {
+                if (!mInitializedTransliterator) {
+                    mInitializedTransliterator = true;
+                    Transliterator t = null;
+                    try {
+                        t = new Transliterator("Hiragana-Latin; Katakana-Latin;"
+                                + " Latin-Ascii");
+                    } catch (RuntimeException e) {
+                        Log.w(TAG, "Hiragana/Katakana-Latin transliterator data"
+                                + " is missing");
+                    }
+                    mJapaneseTransliterator = t;
+                }
+                return mJapaneseTransliterator;
+            }
+        }
+
+        public static Iterator<String> getRomajiNameLookupKeys(String name) {
+            final Transliterator t = getJapaneseTransliterator();
+            if (t == null) {
+                return null;
+            }
+            final String romajiName = t.transliterate(name);
+            if (TextUtils.isEmpty(romajiName) ||
+                    TextUtils.equals(name, romajiName)) {
+                return null;
+            }
+            final HashSet<String> keys = new HashSet<String>();
+            keys.add(romajiName);
+            return keys.iterator();
+        }
     }
 
     /**
-     * Chinese specific locale overrides. Uses ICU Transliterator for
-     * generating pinyin transliteration.
+     * Simplified Chinese specific locale overrides. Uses ICU Transliterator
+     * for generating pinyin transliteration.
      *
      * sortKey: unchanged (same as name)
      * nameLookupKeys: adds additional name lookup keys
@@ -260,17 +335,20 @@
      *     - Latin word and initial character.
      * labels: unchanged
      *     Simplified Chinese labels are the same as English: [A-Z], #, " "
-     *     Traditional Chinese labels are stroke count, then English labels:
-     *         [1-33, 35, 36, 48]劃, [A-Z], #, " "
      */
-    private static class ChineseContactUtils extends ContactLocaleUtilsBase {
-        public ChineseContactUtils(Locale locale) {
+    private static class SimplifiedChineseContactUtils
+        extends ContactLocaleUtilsBase {
+        public SimplifiedChineseContactUtils(Locale locale) {
             super(locale);
         }
 
         @Override
-        public Iterator<String> getNameLookupKeys(String name) {
-            return getPinyinNameLookupKeys(name);
+        public Iterator<String> getNameLookupKeys(String name, int nameStyle) {
+            if (nameStyle != FullNameStyle.JAPANESE &&
+                    nameStyle != FullNameStyle.KOREAN) {
+                return getPinyinNameLookupKeys(name);
+            }
+            return null;
         }
 
         public static Iterator<String> getPinyinNameLookupKeys(String name) {
@@ -286,6 +364,9 @@
             final StringBuilder keyOriginal = new StringBuilder();
             for (int i = tokenCount - 1; i >= 0; i--) {
                 final Token token = tokens.get(i);
+                if (Token.UNKNOWN == token.type) {
+                    continue;
+                }
                 if (Token.PINYIN == token.type) {
                     keyPinyin.insert(0, token.target);
                     keyInitial.insert(0, token.target.charAt(0));
@@ -328,8 +409,8 @@
         mLanguage = mLocale.getLanguage().toLowerCase();
         if (mLanguage.equals(JAPANESE_LANGUAGE)) {
             mUtils = new JapaneseContactUtils(mLocale);
-        } else if (mLanguage.equals(CHINESE_LANGUAGE)) {
-            mUtils = new ChineseContactUtils(mLocale);
+        } else if (mLocale.equals(Locale.CHINA)) {
+            mUtils = new SimplifiedChineseContactUtils(mLocale);
         } else {
             mUtils = new ContactLocaleUtilsBase(mLocale);
         }
@@ -382,22 +463,21 @@
      *  Determine which utility should be used for generating NameLookupKey.
      *  (ie, whether we generate Pinyin lookup keys or not)
      *
-     *  a. For unclassified CJK name, if current locale language is neither
-     *     Japanese nor Korean, use ChineseContactUtils.
-     *  b. If we're sure this is a Chinese name, always use ChineseContactUtils.
-     *  c. Otherwise, use whichever ContactUtils are appropriate for the locale
-     *     (so, Western names in Chinese locale will use ChineseContactUtils)
+     *  Hiragana and Katakana are tagged as JAPANESE; Kanji is unclassified
+     *  and tagged as CJK. For Hiragana/Katakana names, generate Romaji
+     *  lookup keys when not in a Chinese or Korean locale.
+     *
+     *  Otherwise, use the default behavior of that locale:
+     *  a. For Japan, generate Romaji lookup keys for Hiragana/Katakana.
+     *  b. For Simplified Chinese locale, generate Pinyin lookup keys.
      */
     public Iterator<String> getNameLookupKeys(String name, int nameStyle) {
-        if (nameStyle == FullNameStyle.CJK &&
-            !JAPANESE_LANGUAGE.equals(mLanguage) &&
-            !KOREAN_LANGUAGE.equals(mLanguage)) {
-            return ChineseContactUtils.getPinyinNameLookupKeys(name);
+        if (nameStyle == FullNameStyle.JAPANESE &&
+                !CHINESE_LANGUAGE.equals(mLanguage) &&
+                !KOREAN_LANGUAGE.equals(mLanguage)) {
+            return JapaneseContactUtils.getRomajiNameLookupKeys(name);
         }
-        if (nameStyle == FullNameStyle.CHINESE) {
-            return ChineseContactUtils.getPinyinNameLookupKeys(name);
-        }
-        return mUtils.getNameLookupKeys(name);
+        return mUtils.getNameLookupKeys(name, nameStyle);
     }
 
 }
diff --git a/src/com/android/providers/contacts/ContactsDatabaseHelper.java b/src/com/android/providers/contacts/ContactsDatabaseHelper.java
index 260d05f..25b8a00 100644
--- a/src/com/android/providers/contacts/ContactsDatabaseHelper.java
+++ b/src/com/android/providers/contacts/ContactsDatabaseHelper.java
@@ -109,7 +109,7 @@
      *   700-799 Jelly Bean
      * </pre>
      */
-    static final int DATABASE_VERSION = 708;
+    static final int DATABASE_VERSION = 709;
 
     private static final String DATABASE_NAME = "contacts2.db";
     private static final String DATABASE_PRESENCE = "presence_db";
@@ -996,6 +996,9 @@
                 RawContacts.DISPLAY_NAME_SOURCE + " INTEGER NOT NULL DEFAULT " +
                         DisplayNameSources.UNDEFINED + "," +
                 RawContacts.PHONETIC_NAME + " TEXT," +
+                // TODO: PHONETIC_NAME_STYLE should be INTEGER. There is a
+                // mismatch between how the column is created here (TEXT) and
+                // how it is created in upgradeToVersion205 (INTEGER).
                 RawContacts.PHONETIC_NAME_STYLE + " TEXT," +
                 RawContacts.SORT_KEY_PRIMARY + " TEXT COLLATE " +
                         ContactsProvider2.PHONEBOOK_COLLATOR_NAME + "," +
@@ -2453,6 +2456,12 @@
             upgradeLocaleSpecificData = true;
             oldVersion = 708;
         }
+        if (oldVersion < 709) {
+            // Added secondary locale phonebook labels; changed Japanese
+            // and Chinese sort keys.
+            upgradeLocaleSpecificData = true;
+            oldVersion = 709;
+        }
 
         if (upgradeViewsAndTriggers) {
             createContactsViews(db);
@@ -5271,6 +5280,7 @@
                 bestPhoneticNameStyle = mNameSplitter.guessPhoneticNameStyle(bestPhoneticName);
             }
         } else {
+            bestPhoneticNameStyle = PhoneticNameStyle.UNDEFINED;
             if (displayNameStyle == FullNameStyle.UNDEFINED) {
                 displayNameStyle = mNameSplitter.guessFullNameStyle(bestDisplayName);
                 if (displayNameStyle == FullNameStyle.UNDEFINED
diff --git a/src/com/android/providers/contacts/DataRowHandlerForStructuredName.java b/src/com/android/providers/contacts/DataRowHandlerForStructuredName.java
index 01ee1ba..8781d2f 100644
--- a/src/com/android/providers/contacts/DataRowHandlerForStructuredName.java
+++ b/src/com/android/providers/contacts/DataRowHandlerForStructuredName.java
@@ -21,6 +21,7 @@
 import android.database.sqlite.SQLiteDatabase;
 import android.provider.ContactsContract.CommonDataKinds.StructuredName;
 import android.provider.ContactsContract.FullNameStyle;
+import android.provider.ContactsContract.PhoneticNameStyle;
 import android.text.TextUtils;
 
 import com.android.providers.contacts.SearchIndexManager.IndexBuilder;
@@ -141,6 +142,7 @@
             name.fromValues(augmented);
             // As the name could be changed, let's guess the name style again.
             name.fullNameStyle = FullNameStyle.UNDEFINED;
+            name.phoneticNameStyle = PhoneticNameStyle.UNDEFINED;
             mSplitter.guessNameStyle(name);
             int unadjustedFullNameStyle = name.fullNameStyle;
             name.fullNameStyle = mSplitter.getAdjustedFullNameStyle(name.fullNameStyle);
@@ -155,8 +157,11 @@
                         mSplitter.guessFullNameStyle(unstruct));
             }
             if (!update.containsKey(StructuredName.PHONETIC_NAME_STYLE)) {
-                update.put(StructuredName.PHONETIC_NAME_STYLE,
-                        mSplitter.guessPhoneticNameStyle(unstruct));
+                NameSplitter.Name name = new NameSplitter.Name();
+                name.fromValues(update);
+                name.phoneticNameStyle = PhoneticNameStyle.UNDEFINED;
+                mSplitter.guessNameStyle(name);
+                update.put(StructuredName.PHONETIC_NAME_STYLE, name.phoneticNameStyle);
             }
         }
     }
@@ -219,7 +224,14 @@
                 builder.appendName(phoneticGiven);
                 mSb.append(phoneticGiven);
             }
-            builder.appendName(mSb.toString().trim());
+            final String phoneticName = mSb.toString().trim();
+            int phoneticNameStyle = builder.getInt(StructuredName.PHONETIC_NAME_STYLE);
+            if (phoneticNameStyle == PhoneticNameStyle.UNDEFINED) {
+                phoneticNameStyle = mSplitter.guessPhoneticNameStyle(phoneticName);
+            }
+            builder.appendName(phoneticName);
+            mNameLookupBuilder.appendNameShorthandLookup(builder, phoneticName,
+                    phoneticNameStyle);
         }
     }
 }
diff --git a/src/com/android/providers/contacts/HanziToPinyin.java b/src/com/android/providers/contacts/HanziToPinyin.java
index 161bb58..0c35e21 100644
--- a/src/com/android/providers/contacts/HanziToPinyin.java
+++ b/src/com/android/providers/contacts/HanziToPinyin.java
@@ -34,7 +34,8 @@
     private static final String TAG = "HanziToPinyin";
 
     private static HanziToPinyin sInstance;
-    private final Transliterator mPinyinTransliterator;
+    private Transliterator mPinyinTransliterator;
+    private Transliterator mAsciiTransliterator;
 
     public static class Token {
         /**
@@ -71,14 +72,13 @@
     }
 
     private HanziToPinyin() {
-        Transliterator t = null;
         try {
-            t = new Transliterator("Han-Latin/Names; Latin-Ascii; Any-Upper");
+            mPinyinTransliterator = new Transliterator("Han-Latin/Names; Latin-Ascii; Any-Upper");
+            mAsciiTransliterator = new Transliterator("Latin-Ascii");
         } catch (RuntimeException e) {
             Log.w(TAG, "Han-Latin/Names transliterator data is missing,"
                   + " HanziToPinyin is disabled");
         }
-        mPinyinTransliterator = t;
     }
 
     public boolean hasChineseTransliterator() {
@@ -94,14 +94,22 @@
         }
     }
 
-    private Token getToken(char character) {
-        Token token = new Token();
+    private void tokenize(char character, Token token) {
         token.source = Character.toString(character);
 
-        if (character < 256) {
+        // ASCII
+        if (character < 128) {
             token.type = Token.LATIN;
             token.target = token.source;
-            return token;
+            return;
+        }
+
+        // Extended Latin. Transcode these to ASCII equivalents
+        if (character < 0x250 || (0x1e00 <= character && character < 0x1eff)) {
+            token.type = Token.LATIN;
+            token.target = mAsciiTransliterator == null ? token.source :
+                mAsciiTransliterator.transliterate(token.source);
+            return;
         }
 
         token.type = Token.PINYIN;
@@ -111,7 +119,6 @@
             token.type = Token.UNKNOWN;
             token.target = token.source;
         }
-        return token;
     }
 
     /**
@@ -125,40 +132,37 @@
             // return empty tokens.
             return tokens;
         }
+
         final int inputLength = input.length();
         final StringBuilder sb = new StringBuilder();
         int tokenType = Token.LATIN;
+        Token token = new Token();
+
         // Go through the input, create a new token when
         // a. Token type changed
         // b. Get the Pinyin of current charater.
         // c. current character is space.
         for (int i = 0; i < inputLength; i++) {
             final char character = input.charAt(i);
-            if (character == ' ') {
+            if (Character.isSpaceChar(character)) {
                 if (sb.length() > 0) {
                     addToken(sb, tokens, tokenType);
                 }
-            } else if (character < 256) {
-                if (tokenType != Token.LATIN && sb.length() > 0) {
-                    addToken(sb, tokens, tokenType);
-                }
-                tokenType = Token.LATIN;
-                sb.append(character);
             } else {
-                Token t = getToken(character);
-                if (t.type == Token.PINYIN) {
+                tokenize(character, token);
+                if (token.type == Token.PINYIN) {
                     if (sb.length() > 0) {
                         addToken(sb, tokens, tokenType);
                     }
-                    tokens.add(t);
-                    tokenType = Token.PINYIN;
+                    tokens.add(token);
+                    token = new Token();
                 } else {
-                    if (tokenType != t.type && sb.length() > 0) {
+                    if (tokenType != token.type && sb.length() > 0) {
                         addToken(sb, tokens, tokenType);
                     }
-                    tokenType = t.type;
-                    sb.append(character);
+                    sb.append(token.target);
                 }
+                tokenType = token.type;
             }
         }
         if (sb.length() > 0) {
diff --git a/src/com/android/providers/contacts/NameLookupBuilder.java b/src/com/android/providers/contacts/NameLookupBuilder.java
index 67dae46..fb266da 100644
--- a/src/com/android/providers/contacts/NameLookupBuilder.java
+++ b/src/com/android/providers/contacts/NameLookupBuilder.java
@@ -319,7 +319,12 @@
         }
     }
 
-    private void appendNameShorthandLookup(IndexBuilder builder, String name, int fullNameStyle) {
+    /**
+     * Insert more name indexes according to locale specifies for those locales
+     * for which we have alternative shorthand name methods (eg, Pinyin for
+     * Chinese, Romaji for Japanese).
+     */
+    public void appendNameShorthandLookup(IndexBuilder builder, String name, int fullNameStyle) {
         Iterator<String> it =
                 ContactLocaleUtils.getInstance().getNameLookupKeys(name, fullNameStyle);
         if (it != null) {
diff --git a/tests/src/com/android/providers/contacts/ContactLocaleUtilsTest.java b/tests/src/com/android/providers/contacts/ContactLocaleUtilsTest.java
index 637c4e3..ea4ef0f 100644
--- a/tests/src/com/android/providers/contacts/ContactLocaleUtilsTest.java
+++ b/tests/src/com/android/providers/contacts/ContactLocaleUtilsTest.java
@@ -25,6 +25,7 @@
 import java.util.Arrays;
 import java.util.HashSet;
 import java.util.Iterator;
+import java.util.List;
 import java.util.Locale;
 
 @SmallTest
@@ -160,9 +161,7 @@
         assertEquals("D", getLabel(CHINESE_LATIN_MIX_NAME_1));
 
         assertNull(getNameLookupKeys(CHINESE_NAME, FullNameStyle.CJK));
-        Iterator<String> keys = getNameLookupKeys(CHINESE_NAME,
-                FullNameStyle.CHINESE);
-        verifyKeys(keys, CHINESE_NAME_KEY);
+        assertNull(getNameLookupKeys(CHINESE_NAME, FullNameStyle.CHINESE));
 
         // Following two tests are broken with ICU 50
         verifyLabels(getLabels(), LABELS_JA_JP);
@@ -213,14 +212,12 @@
         }
 
         ContactLocaleUtils.setLocale(Locale.ENGLISH);
-        Iterator<String> keys = getNameLookupKeys(CHINESE_NAME,
-                FullNameStyle.CHINESE);
-        verifyKeys(keys, CHINESE_NAME_KEY);
-        keys = getNameLookupKeys(CHINESE_NAME, FullNameStyle.CJK);
-        verifyKeys(keys, CHINESE_NAME_KEY);
+        assertNull(getNameLookupKeys(CHINESE_NAME, FullNameStyle.CHINESE));
+        assertNull(getNameLookupKeys(CHINESE_NAME, FullNameStyle.CJK));
 
-        ContactLocaleUtils.setLocale(Locale.CHINESE);
-        keys = getNameLookupKeys(CHINESE_NAME, FullNameStyle.CJK);
+        ContactLocaleUtils.setLocale(Locale.CHINA);
+        Iterator<String> keys = getNameLookupKeys(CHINESE_NAME,
+                FullNameStyle.CJK);
         verifyKeys(keys, CHINESE_NAME_KEY);
         keys = getNameLookupKeys(LATIN_NAME, FullNameStyle.WESTERN);
         verifyKeys(keys, LATIN_NAME_KEY);
@@ -228,8 +225,7 @@
         verifyKeys(keys, LATIN_NAME_KEY_2);
 
         ContactLocaleUtils.setLocale(Locale.TRADITIONAL_CHINESE);
-        keys = getNameLookupKeys(CHINESE_NAME, FullNameStyle.CJK);
-        verifyKeys(keys, CHINESE_NAME_KEY);
+        assertNull(getNameLookupKeys(CHINESE_NAME, FullNameStyle.CJK));
     }
 
     public void testKoreanContactLocaleUtils() throws Exception {
@@ -278,10 +274,14 @@
         assertEquals(new HashSet<String>(Arrays.asList(expectedKeys)), allKeys);
     }
 
+    // Verify that the initial set of resultLabels matches the expectedLabels.
+    // Ignore the (large) number of secondary locale labels that make up the
+    // tail labels in the result set right before the final "#" and "" buckets.
     private void verifyLabels(final ArrayList<String> resultLabels,
-                              final String[] expectedLabels)
-            throws Exception {
-        assertEquals(new ArrayList<String>(Arrays.asList(expectedLabels)),
-                     resultLabels);
+            final String[] expectedLabels) throws Exception {
+        final List<String> expectedLabelList = Arrays.asList(expectedLabels);
+        final int numLabels = expectedLabelList.size() - 2;
+        assertEquals(expectedLabelList.subList(0, numLabels),
+                resultLabels.subList(0, numLabels));
     }
 }
diff --git a/tests/src/com/android/providers/contacts/ContactsProvider2Test.java b/tests/src/com/android/providers/contacts/ContactsProvider2Test.java
index df8e6d6..3f8b001 100644
--- a/tests/src/com/android/providers/contacts/ContactsProvider2Test.java
+++ b/tests/src/com/android/providers/contacts/ContactsProvider2Test.java
@@ -3383,6 +3383,27 @@
         assertStoredValues(dataUri, values);
     }
 
+    public void testJapaneseNameContactInEnglishLocale() {
+        // Need Japanese locale data for transliteration
+        if (!hasJapaneseCollator()) {
+            return;
+        }
+        ContactLocaleUtils.setLocale(Locale.US);
+        long rawContactId = createRawContact(null);
+
+        ContentValues values = new ContentValues();
+        values.put(StructuredName.GIVEN_NAME, "\u7A7A\u6D77");
+        values.put(StructuredName.PHONETIC_GIVEN_NAME, "\u304B\u3044\u304F\u3046");
+        insertStructuredName(rawContactId, values);
+
+        long contactId = queryContactId(rawContactId);
+        // en_US should behave same as ja_JP (match on Hiragana and Romaji
+        // but not Pinyin)
+        assertContactFilter(contactId, "\u304B\u3044\u304F\u3046");
+        assertContactFilter(contactId, "kaiku");
+        assertContactFilterNoResult("kong");
+    }
+
     public void testContactWithJapaneseName() {
         if (!hasJapaneseCollator()) {
             return;
@@ -3426,6 +3447,12 @@
 
         // The same values should be available through a join with Data
         assertStoredValues(dataUri, values);
+
+        long contactId = queryContactId(rawContactId);
+        // ja_JP should match on Hiragana and Romaji but not Pinyin
+        assertContactFilter(contactId, "\u304B\u3044\u304F\u3046");
+        assertContactFilter(contactId, "kaiku");
+        assertContactFilterNoResult("kong");
     }
 
     public void testDisplayNameUpdate() {
@@ -3616,8 +3643,8 @@
     }
 
     private void assertContactFilterNoResult(String filter) {
-        Uri filterUri4 = Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI, filter);
-        assertEquals(0, getCount(filterUri4, null, null));
+        Uri filterUri = Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI, Uri.encode(filter));
+        assertEquals(0, getCount(filterUri, null, null));
     }
 
     public void testSearchSnippetOrganization() throws Exception {
diff --git a/tests/src/com/android/providers/contacts/SearchIndexManagerTest.java b/tests/src/com/android/providers/contacts/SearchIndexManagerTest.java
index 3abdc3f..ac6e5e3 100644
--- a/tests/src/com/android/providers/contacts/SearchIndexManagerTest.java
+++ b/tests/src/com/android/providers/contacts/SearchIndexManagerTest.java
@@ -87,6 +87,7 @@
         if (!Arrays.asList(Collator.getAvailableLocales()).contains(Locale.CHINA)) {
             return;
         }
+        ContactLocaleUtils.setLocale(Locale.SIMPLIFIED_CHINESE);
 
         long rawContactId = createRawContact();
         ContentValues values = new ContentValues();