Merge "DO NOT MERGE: Merge Oreo MR1 into master"
diff --git a/res/values-bs/strings.xml b/res/values-bs/strings.xml
index c23e9ef..37bc150 100644
--- a/res/values-bs/strings.xml
+++ b/res/values-bs/strings.xml
@@ -28,8 +28,8 @@
     <string name="debug_dump_title" msgid="4916885724165570279">"Kopiraj bazu podataka kontakata"</string>
     <string name="debug_dump_database_message" msgid="406438635002392290">"Upravo ćete 1) napraviti kopiju svoje baze podataka koja sadrži sve informacije o kontaktima i sve popise poziva u unutrašnjoj pohrani i 2) poslati tu kopiju e-poštom. Ne zaboravite izbrisati kopiju čim je uspješno kopirate s uređaja ili čim primite poruku e-pošte."</string>
     <string name="debug_dump_delete_button" msgid="7832879421132026435">"Izbriši sada"</string>
-    <string name="debug_dump_start_button" msgid="2837506913757600001">"Počni"</string>
-    <string name="debug_dump_email_sender_picker" msgid="3534420908672176460">"Izaberite program za slanje fajla"</string>
+    <string name="debug_dump_start_button" msgid="2837506913757600001">"Započni"</string>
+    <string name="debug_dump_email_sender_picker" msgid="3534420908672176460">"Odaberite program za slanje fajla"</string>
     <string name="debug_dump_email_subject" msgid="108188398416385976">"Baza podataka kontakata je u prilogu"</string>
     <string name="debug_dump_email_body" msgid="4577749800871444318">"U prilogu je moja baza podataka kontakata sa svim informacijama o kontaktima. Rukujte oprezno."</string>
 </resources>
diff --git a/res/values-mr/strings.xml b/res/values-mr/strings.xml
index 7e6d605..a4f324e 100644
--- a/res/values-mr/strings.xml
+++ b/res/values-mr/strings.xml
@@ -20,7 +20,7 @@
     <string name="app_label" msgid="3389954322874982620">"संपर्क संचयन"</string>
     <string name="provider_label" msgid="6012150850819899907">"संपर्क"</string>
     <string name="upgrade_out_of_memory_notification_ticker" msgid="7638747231223520477">"संपर्क श्रेणीसुधारित करण्‍यास अधिक मेमरी आवश्‍यक आहे."</string>
-    <string name="upgrade_out_of_memory_notification_title" msgid="8888171924684998531">"संपर्कांसाठी संचयन श्रेणीसुधारित करीत आहे"</string>
+    <string name="upgrade_out_of_memory_notification_title" msgid="8888171924684998531">"संपर्कांसाठी संचयन श्रेणीसुधारित करत आहे"</string>
     <string name="upgrade_out_of_memory_notification_text" msgid="2581831842693151968">"श्रेणीसुधारणा पूर्ण करण्यासाठी टॅप करा."</string>
     <string name="default_directory" msgid="93961630309570294">"संपर्क"</string>
     <string name="local_invisible_directory" msgid="705244318477396120">"इतर"</string>
diff --git a/res/values-sw/strings.xml b/res/values-sw/strings.xml
index 920511a..92bd391 100644
--- a/res/values-sw/strings.xml
+++ b/res/values-sw/strings.xml
@@ -21,7 +21,7 @@
     <string name="provider_label" msgid="6012150850819899907">"Anwani"</string>
     <string name="upgrade_out_of_memory_notification_ticker" msgid="7638747231223520477">"Kupandishwa gredi kwa anwani kunahitaji kumbukumbu zaidi."</string>
     <string name="upgrade_out_of_memory_notification_title" msgid="8888171924684998531">"Inapandisha gredi hifadhi ya anwani"</string>
-    <string name="upgrade_out_of_memory_notification_text" msgid="2581831842693151968">"Gonga ili ukamilishe kusasisha anwani."</string>
+    <string name="upgrade_out_of_memory_notification_text" msgid="2581831842693151968">"Gusa ili ukamilishe kusasisha anwani."</string>
     <string name="default_directory" msgid="93961630309570294">"Anwani"</string>
     <string name="local_invisible_directory" msgid="705244318477396120">"Nyingineyo"</string>
     <string name="voicemail_from_column" msgid="435732568832121444">"Barua ya sauti kutoka "</string>
diff --git a/src/com/android/providers/contacts/CallLogDatabaseHelper.java b/src/com/android/providers/contacts/CallLogDatabaseHelper.java
index d4ed930..736d665 100644
--- a/src/com/android/providers/contacts/CallLogDatabaseHelper.java
+++ b/src/com/android/providers/contacts/CallLogDatabaseHelper.java
@@ -47,6 +47,8 @@
 
     private static final String SHADOW_DATABASE_NAME = "calllog_shadow.db";
 
+    private static final int IDLE_CONNECTION_TIMEOUT_MS = 30000;
+
     private static CallLogDatabaseHelper sInstance;
 
     /** Instance for the "shadow" provider. */
@@ -87,6 +89,8 @@
         public OpenHelper(Context context, String name, SQLiteDatabase.CursorFactory factory,
                 int version) {
             super(context, name, factory, version);
+            // Memory optimization - close idle connections after 30s of inactivity
+            setIdleConnectionTimeout(IDLE_CONNECTION_TIMEOUT_MS);
         }
 
         @Override
diff --git a/src/com/android/providers/contacts/CallLogProvider.java b/src/com/android/providers/contacts/CallLogProvider.java
index 9b53c0e..59e9b14 100644
--- a/src/com/android/providers/contacts/CallLogProvider.java
+++ b/src/com/android/providers/contacts/CallLogProvider.java
@@ -41,6 +41,7 @@
 import android.telecom.PhoneAccountHandle;
 import android.telecom.TelecomManager;
 import android.text.TextUtils;
+import android.util.ArrayMap;
 import android.util.Log;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -50,7 +51,6 @@
 import com.android.providers.contacts.util.UserUtils;
 
 import java.util.Arrays;
-import java.util.HashMap;
 import java.util.List;
 import java.util.concurrent.CountDownLatch;
 
@@ -114,11 +114,11 @@
         sURIMatcher.addURI(CallLog.SHADOW_AUTHORITY, "calls", CALLS);
     }
 
-    private static final HashMap<String, String> sCallsProjectionMap;
+    private static final ArrayMap<String, String> sCallsProjectionMap;
     static {
 
         // Calls projection map
-        sCallsProjectionMap = new HashMap<String, String>();
+        sCallsProjectionMap = new ArrayMap<>();
         sCallsProjectionMap.put(Calls._ID, Calls._ID);
         sCallsProjectionMap.put(Calls.NUMBER, Calls.NUMBER);
         sCallsProjectionMap.put(Calls.POST_DIAL_DIGITS, Calls.POST_DIAL_DIGITS);
diff --git a/src/com/android/providers/contacts/ContactLocaleUtils.java b/src/com/android/providers/contacts/ContactLocaleUtils.java
index 2b7f1ff..a3e9777 100644
--- a/src/com/android/providers/contacts/ContactLocaleUtils.java
+++ b/src/com/android/providers/contacts/ContactLocaleUtils.java
@@ -23,6 +23,7 @@
 import android.provider.ContactsContract.FullNameStyle;
 import android.provider.ContactsContract.PhoneticNameStyle;
 import android.text.TextUtils;
+import android.util.ArraySet;
 import android.util.Log;
 
 import com.android.providers.contacts.HanziToPinyin.Token;
@@ -32,7 +33,6 @@
 import java.lang.Character.UnicodeBlock;
 import java.util.ArrayList;
 import java.util.Collections;
-import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Locale;
@@ -313,7 +313,7 @@
         // blocks are included but Korean Hangul and jamo are not).
         private static final Set<Character.UnicodeBlock> CJ_BLOCKS;
         static {
-            Set<UnicodeBlock> set = new HashSet<UnicodeBlock>();
+            Set<UnicodeBlock> set = new ArraySet<>();
             set.add(UnicodeBlock.HIRAGANA);
             set.add(UnicodeBlock.KATAKANA);
             set.add(UnicodeBlock.KATAKANA_PHONETIC_EXTENSIONS);
@@ -421,7 +421,7 @@
                     TextUtils.equals(name, romajiName)) {
                 return null;
             }
-            final HashSet<String> keys = new HashSet<String>();
+            final ArraySet<String> keys = new ArraySet<>();
             keys.add(romajiName);
             return keys.iterator();
         }
@@ -468,7 +468,7 @@
 
         public static Iterator<String> getPinyinNameLookupKeys(String name) {
             // TODO : Reduce the object allocation.
-            HashSet<String> keys = new HashSet<String>();
+            ArraySet<String> keys = new ArraySet<>();
             ArrayList<Token> tokens = HanziToPinyin.getInstance().getTokens(name);
             final int tokenCount = tokens.size();
             final StringBuilder keyPinyin = new StringBuilder();
diff --git a/src/com/android/providers/contacts/ContactsDatabaseHelper.java b/src/com/android/providers/contacts/ContactsDatabaseHelper.java
index 89cc396..76fe173 100644
--- a/src/com/android/providers/contacts/ContactsDatabaseHelper.java
+++ b/src/com/android/providers/contacts/ContactsDatabaseHelper.java
@@ -21,6 +21,7 @@
 import com.android.providers.contacts.sqlite.SqlChecker.InvalidSqlException;
 import com.android.providers.contacts.util.PropertyUtils;
 
+import android.app.ActivityManager;
 import android.content.ContentResolver;
 import android.content.ContentValues;
 import android.content.Context;
@@ -143,6 +144,8 @@
     @VisibleForTesting
     static final boolean DISALLOW_SUB_QUERIES = false;
 
+    private static final int IDLE_CONNECTION_TIMEOUT_MS = 30000;
+
     public interface Tables {
         public static final String CONTACTS = "contacts";
         public static final String DELETED_CONTACTS = "deleted_contacts";
@@ -1059,7 +1062,12 @@
         super(context, databaseName, null, DATABASE_VERSION, MINIMUM_SUPPORTED_VERSION, null);
         boolean enableWal = android.provider.Settings.Global.getInt(context.getContentResolver(),
                 android.provider.Settings.Global.CONTACTS_DATABASE_WAL_ENABLED, 1) == 1;
+        if (dbForProfile() != 0 || ActivityManager.isLowRamDeviceStatic()) {
+            enableWal = false;
+        }
         setWriteAheadLoggingEnabled(enableWal);
+        // Memory optimization - close idle connections after 30s of inactivity
+        setIdleConnectionTimeout(IDLE_CONNECTION_TIMEOUT_MS);
         mDatabaseOptimizationEnabled = optimizationEnabled;
         mIsTestInstance = isTestInstance;
         Resources resources = context.getResources();
diff --git a/src/com/android/providers/contacts/ContactsProvider2.java b/src/com/android/providers/contacts/ContactsProvider2.java
index 4feb71e..a5af51f 100644
--- a/src/com/android/providers/contacts/ContactsProvider2.java
+++ b/src/com/android/providers/contacts/ContactsProvider2.java
@@ -110,6 +110,7 @@
 import android.telephony.PhoneNumberUtils;
 import android.telephony.TelephonyManager;
 import android.text.TextUtils;
+import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.Log;
 
@@ -198,8 +199,6 @@
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.Date;
-import java.util.HashMap;
-import java.util.HashSet;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
@@ -1459,7 +1458,7 @@
     // Random number generator.
     private final SecureRandom mRandom = new SecureRandom();
 
-    private final HashMap<String, Boolean> mAccountWritability = Maps.newHashMap();
+    private final ArrayMap<String, Boolean> mAccountWritability = new ArrayMap<>();
 
     private PhotoStore mContactsPhotoStore;
     private PhotoStore mProfilePhotoStore;
@@ -1468,13 +1467,13 @@
     private ProfileDatabaseHelper mProfileHelper;
 
     // Separate data row handler instances for contact data and profile data.
-    private HashMap<String, DataRowHandler> mDataRowHandlers;
-    private HashMap<String, DataRowHandler> mProfileDataRowHandlers;
+    private ArrayMap<String, DataRowHandler> mDataRowHandlers;
+    private ArrayMap<String, DataRowHandler> mProfileDataRowHandlers;
 
     /**
      * Cached information about contact directories.
      */
-    private HashMap<String, DirectoryInfo> mDirectoryCache = new HashMap<String, DirectoryInfo>();
+    private ArrayMap<String, DirectoryInfo> mDirectoryCache = new ArrayMap<>();
     private boolean mDirectoryCacheValid = false;
 
     /**
@@ -1484,7 +1483,7 @@
      * be a small number of contact groups. The cache is keyed off source ID.  The value
      * is a list of groups with this group ID.
      */
-    private HashMap<String, ArrayList<GroupIdCacheEntry>> mGroupIdCache = Maps.newHashMap();
+    private ArrayMap<String, ArrayList<GroupIdCacheEntry>> mGroupIdCache = new ArrayMap<>();
 
     /**
      * Sub-provider for handling profile requests against the profile database.
@@ -1684,10 +1683,10 @@
         mProfilePhotoStore =
                 new PhotoStore(new File(getContext().getFilesDir(), "profile"), mProfileHelper);
 
-        mDataRowHandlers = new HashMap<String, DataRowHandler>();
+        mDataRowHandlers = new ArrayMap<>();
         initDataRowHandlers(mDataRowHandlers, mContactsHelper, mContactAggregator,
                 mContactsPhotoStore);
-        mProfileDataRowHandlers = new HashMap<String, DataRowHandler>();
+        mProfileDataRowHandlers = new ArrayMap<>();
         initDataRowHandlers(mProfileDataRowHandlers, mProfileHelper, mProfileAggregator,
                 mProfilePhotoStore);
 
@@ -5050,7 +5049,7 @@
     private Set<Long> queryAggregationRawContactIds(SQLiteDatabase db, long rawContactId) {
         mSelectionArgs2[0] = String.valueOf(rawContactId);
         mSelectionArgs2[1] = String.valueOf(rawContactId);
-        Set<Long> aggregationRawContactIds = new HashSet<>();
+        Set<Long> aggregationRawContactIds = new ArraySet<>();
         final Cursor c = db.query(AggregationExceptionQuery.TABLE,
                 AggregationExceptionQuery.COLUMNS, AggregationExceptionQuery.SELECTION,
                 mSelectionArgs2, null, null, null);
@@ -5122,7 +5121,7 @@
         }
 
         // Update AggregationException table.
-        final Set<Long> aggregationRawContactIdsInServer = new HashSet<>();
+        final Set<Long> aggregationRawContactIdsInServer = new ArraySet<>();
         for (int i = 0; i < metadataEntry.mAggregationDatas.size(); i++) {
             final AggregationData aggregationData = metadataEntry.mAggregationDatas.get(i);
             final int typeInt = getAggregationType(aggregationData.mType, null);
@@ -5385,7 +5384,7 @@
                 // Find all aggregated contacts that used to contain the raw contacts
                 // we have just deleted and see if they are still referencing the deleted
                 // names or photos.  If so, fix up those contacts.
-                HashSet<Long> orphanContactIds = Sets.newHashSet();
+                ArraySet<Long> orphanContactIds = new ArraySet<>();
                 Cursor cursor = db.rawQuery("SELECT " + Contacts._ID +
                         " FROM " + Tables.CONTACTS +
                         " WHERE (" + Contacts.NAME_RAW_CONTACT_ID + " NOT NULL AND " +
@@ -7715,7 +7714,7 @@
             return null;
         }
 
-        HashMap<String, String> projectionMap = Maps.newHashMap();
+        ArrayMap<String, String> projectionMap = new ArrayMap<>();
         projectionMap.put(AddressBookIndexQuery.NAME,
                 sortKey + " AS " + AddressBookIndexQuery.NAME);
         projectionMap.put(AddressBookIndexQuery.BUCKET,
@@ -9866,7 +9865,7 @@
 
         final SQLiteDatabase db = mDbHelper.get().getWritableDatabase();
 
-        final Set<Long> rawContactIds = new HashSet<>();
+        final Set<Long> rawContactIds = new ArraySet<>();
         final Cursor cursor = db.rawQuery(rawContactIdSelect.toString(), null);
         try {
             cursor.moveToPosition(-1);
diff --git a/src/com/android/providers/contacts/DataRowHandlerForGroupMembership.java b/src/com/android/providers/contacts/DataRowHandlerForGroupMembership.java
index 4e9d5d4..b1c4049 100644
--- a/src/com/android/providers/contacts/DataRowHandlerForGroupMembership.java
+++ b/src/com/android/providers/contacts/DataRowHandlerForGroupMembership.java
@@ -23,6 +23,7 @@
 import android.provider.ContactsContract.CommonDataKinds.GroupMembership;
 import android.provider.ContactsContract.Groups;
 import android.provider.ContactsContract.RawContacts;
+
 import com.android.providers.contacts.ContactsDatabaseHelper.Clauses;
 import com.android.providers.contacts.ContactsDatabaseHelper.DataColumns;
 import com.android.providers.contacts.ContactsDatabaseHelper.GroupsColumns;
@@ -33,7 +34,7 @@
 import com.android.providers.contacts.aggregation.AbstractContactAggregator;
 
 import java.util.ArrayList;
-import java.util.HashMap;
+import java.util.Map;
 
 /**
  * Handler for group membership data rows.
@@ -62,11 +63,11 @@
                     + " AND " + Tables.DATA + "." + GroupMembership.RAW_CONTACT_ID + "=?"
                     + " AND " + Groups.FAVORITES + "!=0";
 
-    private final HashMap<String, ArrayList<GroupIdCacheEntry>> mGroupIdCache;
+    private final Map<String, ArrayList<GroupIdCacheEntry>> mGroupIdCache;
 
     public DataRowHandlerForGroupMembership(Context context, ContactsDatabaseHelper dbHelper,
             AbstractContactAggregator aggregator,
-            HashMap<String, ArrayList<GroupIdCacheEntry>> groupIdCache) {
+            Map<String, ArrayList<GroupIdCacheEntry>> groupIdCache) {
         super(context, dbHelper, aggregator, GroupMembership.CONTENT_ITEM_TYPE);
         mGroupIdCache = groupIdCache;
     }
diff --git a/src/com/android/providers/contacts/DbModifierWithNotification.java b/src/com/android/providers/contacts/DbModifierWithNotification.java
index cb5460c..7e7b3e1 100644
--- a/src/com/android/providers/contacts/DbModifierWithNotification.java
+++ b/src/com/android/providers/contacts/DbModifierWithNotification.java
@@ -36,6 +36,7 @@
 import android.provider.VoicemailContract;
 import android.provider.VoicemailContract.Status;
 import android.provider.VoicemailContract.Voicemails;
+import android.util.ArraySet;
 import android.util.Log;
 
 import com.android.common.io.MoreCloseables;
@@ -46,7 +47,6 @@
 import com.google.common.collect.Iterables;
 import java.util.ArrayList;
 import java.util.Collection;
-import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
 
@@ -262,7 +262,7 @@
      * always expected to have the source package field set.
      */
     private Set<String> getModifiedPackages(String whereClause, String[] whereArgs) {
-        Set<String> modifiedPackages = new HashSet<String>();
+        Set<String> modifiedPackages = new ArraySet<>();
         Cursor cursor = mDb.query(mTableName, PROJECTION,
                 DbQueryUtils.concatenateClauses(NON_NULL_SOURCE_PACKAGE_SELECTION, whereClause),
                 whereArgs, null, null, null);
@@ -280,7 +280,7 @@
      * package field set.
      */
     private Set<String> getModifiedPackages(ContentValues values) {
-        Set<String> impactedPackages = new HashSet<String>();
+        Set<String> impactedPackages = new ArraySet<>();
         if(values.containsKey(VoicemailContract.SOURCE_PACKAGE_FIELD)) {
             impactedPackages.add(values.getAsString(VoicemailContract.SOURCE_PACKAGE_FIELD));
         }
diff --git a/src/com/android/providers/contacts/LegacyApiSupport.java b/src/com/android/providers/contacts/LegacyApiSupport.java
index 741639a..c111cf3 100644
--- a/src/com/android/providers/contacts/LegacyApiSupport.java
+++ b/src/com/android/providers/contacts/LegacyApiSupport.java
@@ -50,6 +50,7 @@
 import android.provider.ContactsContract.Settings;
 import android.provider.ContactsContract.StatusUpdates;
 import android.text.TextUtils;
+import android.util.ArrayMap;
 import android.util.Log;
 
 import com.android.providers.contacts.ContactsDatabaseHelper.AccountsColumns;
@@ -65,7 +66,6 @@
 import com.android.providers.contacts.ContactsDatabaseHelper.StatusUpdatesColumns;
 import com.android.providers.contacts.ContactsDatabaseHelper.Tables;
 
-import java.util.HashMap;
 import java.util.Locale;
 
 @SuppressWarnings("deprecation")
@@ -250,14 +250,14 @@
             + " AND " + DataColumns.CONCRETE_ID + " = legacy_photo." + LegacyPhotoData.PHOTO_DATA_ID
             + ")";
 
-    private static final HashMap<String, String> sPeopleProjectionMap;
-    private static final HashMap<String, String> sOrganizationProjectionMap;
-    private static final HashMap<String, String> sContactMethodProjectionMap;
-    private static final HashMap<String, String> sPhoneProjectionMap;
-    private static final HashMap<String, String> sExtensionProjectionMap;
-    private static final HashMap<String, String> sGroupProjectionMap;
-    private static final HashMap<String, String> sGroupMembershipProjectionMap;
-    private static final HashMap<String, String> sPhotoProjectionMap;
+    private static final ArrayMap<String, String> sPeopleProjectionMap;
+    private static final ArrayMap<String, String> sOrganizationProjectionMap;
+    private static final ArrayMap<String, String> sContactMethodProjectionMap;
+    private static final ArrayMap<String, String> sPhoneProjectionMap;
+    private static final ArrayMap<String, String> sExtensionProjectionMap;
+    private static final ArrayMap<String, String> sGroupProjectionMap;
+    private static final ArrayMap<String, String> sGroupMembershipProjectionMap;
+    private static final ArrayMap<String, String> sPhotoProjectionMap;
 
     static {
 
@@ -335,7 +335,7 @@
                 SEARCH_SHORTCUT);
         matcher.addURI(authority, "settings", SETTINGS);
 
-        HashMap<String, String> peopleProjectionMap = new HashMap<String, String>();
+        ArrayMap<String, String> peopleProjectionMap = new ArrayMap<>();
         peopleProjectionMap.put(People.NAME, People.NAME);
         peopleProjectionMap.put(People.DISPLAY_NAME, People.DISPLAY_NAME);
         peopleProjectionMap.put(People.PHONETIC_NAME, People.PHONETIC_NAME);
@@ -349,7 +349,7 @@
         peopleProjectionMap.put(People.PRIMARY_EMAIL_ID, People.PRIMARY_EMAIL_ID);
         peopleProjectionMap.put(People.PRIMARY_PHONE_ID, People.PRIMARY_PHONE_ID);
 
-        sPeopleProjectionMap = new HashMap<String, String>(peopleProjectionMap);
+        sPeopleProjectionMap = new ArrayMap<>(peopleProjectionMap);
         sPeopleProjectionMap.put(People._ID, People._ID);
         sPeopleProjectionMap.put(People.NUMBER, People.NUMBER);
         sPeopleProjectionMap.put(People.TYPE, People.TYPE);
@@ -369,7 +369,7 @@
                 " LIMIT 1" +
                 ") AS " + People.PRESENCE_CUSTOM_STATUS);
 
-        sOrganizationProjectionMap = new HashMap<String, String>();
+        sOrganizationProjectionMap = new ArrayMap<>();
         sOrganizationProjectionMap.put(android.provider.Contacts.Organizations._ID,
                 android.provider.Contacts.Organizations._ID);
         sOrganizationProjectionMap.put(android.provider.Contacts.Organizations.PERSON_ID,
@@ -385,7 +385,7 @@
         sOrganizationProjectionMap.put(android.provider.Contacts.Organizations.TITLE,
                 android.provider.Contacts.Organizations.TITLE);
 
-        sContactMethodProjectionMap = new HashMap<String, String>(peopleProjectionMap);
+        sContactMethodProjectionMap = new ArrayMap<>(peopleProjectionMap);
         sContactMethodProjectionMap.put(ContactMethods._ID, ContactMethods._ID);
         sContactMethodProjectionMap.put(ContactMethods.PERSON_ID, ContactMethods.PERSON_ID);
         sContactMethodProjectionMap.put(ContactMethods.KIND, ContactMethods.KIND);
@@ -395,7 +395,7 @@
         sContactMethodProjectionMap.put(ContactMethods.LABEL, ContactMethods.LABEL);
         sContactMethodProjectionMap.put(ContactMethods.AUX_DATA, ContactMethods.AUX_DATA);
 
-        sPhoneProjectionMap = new HashMap<String, String>(peopleProjectionMap);
+        sPhoneProjectionMap = new ArrayMap<>(peopleProjectionMap);
         sPhoneProjectionMap.put(android.provider.Contacts.Phones._ID,
                 android.provider.Contacts.Phones._ID);
         sPhoneProjectionMap.put(android.provider.Contacts.Phones.PERSON_ID,
@@ -411,7 +411,7 @@
         sPhoneProjectionMap.put(android.provider.Contacts.Phones.NUMBER_KEY,
                 android.provider.Contacts.Phones.NUMBER_KEY);
 
-        sExtensionProjectionMap = new HashMap<String, String>();
+        sExtensionProjectionMap = new ArrayMap<>();
         sExtensionProjectionMap.put(android.provider.Contacts.Extensions._ID,
                 android.provider.Contacts.Extensions._ID);
         sExtensionProjectionMap.put(android.provider.Contacts.Extensions.PERSON_ID,
@@ -421,7 +421,7 @@
         sExtensionProjectionMap.put(android.provider.Contacts.Extensions.VALUE,
                 android.provider.Contacts.Extensions.VALUE);
 
-        sGroupProjectionMap = new HashMap<String, String>();
+        sGroupProjectionMap = new ArrayMap<>();
         sGroupProjectionMap.put(android.provider.Contacts.Groups._ID,
                 android.provider.Contacts.Groups._ID);
         sGroupProjectionMap.put(android.provider.Contacts.Groups.NAME,
@@ -431,7 +431,7 @@
         sGroupProjectionMap.put(android.provider.Contacts.Groups.SYSTEM_ID,
                 android.provider.Contacts.Groups.SYSTEM_ID);
 
-        sGroupMembershipProjectionMap = new HashMap<String, String>(sGroupProjectionMap);
+        sGroupMembershipProjectionMap = new ArrayMap<>(sGroupProjectionMap);
         sGroupMembershipProjectionMap.put(android.provider.Contacts.GroupMembership._ID,
                 android.provider.Contacts.GroupMembership._ID);
         sGroupMembershipProjectionMap.put(android.provider.Contacts.GroupMembership.PERSON_ID,
@@ -448,7 +448,7 @@
                 android.provider.Contacts.GroupMembership.GROUP_SYNC_ACCOUNT_TYPE,
                 android.provider.Contacts.GroupMembership.GROUP_SYNC_ACCOUNT_TYPE);
 
-        sPhotoProjectionMap = new HashMap<String, String>();
+        sPhotoProjectionMap = new ArrayMap<>();
         sPhotoProjectionMap.put(android.provider.Contacts.Photos._ID,
                 android.provider.Contacts.Photos._ID);
         sPhotoProjectionMap.put(android.provider.Contacts.Photos.PERSON_ID,
diff --git a/src/com/android/providers/contacts/NameSplitter.java b/src/com/android/providers/contacts/NameSplitter.java
index ebf6136..50b50fb 100644
--- a/src/com/android/providers/contacts/NameSplitter.java
+++ b/src/com/android/providers/contacts/NameSplitter.java
@@ -20,11 +20,11 @@
 import android.provider.ContactsContract.FullNameStyle;
 import android.provider.ContactsContract.PhoneticNameStyle;
 import android.text.TextUtils;
+import android.util.ArraySet;
 
 import com.android.providers.contacts.util.NeededForTesting;
 
 import java.lang.Character.UnicodeBlock;
-import java.util.HashSet;
 import java.util.Locale;
 import java.util.StringTokenizer;
 
@@ -53,11 +53,11 @@
     // This includes simplified and traditional Chinese
     private static final String CHINESE_LANGUAGE = Locale.CHINESE.getLanguage().toLowerCase();
 
-    private final HashSet<String> mPrefixesSet;
-    private final HashSet<String> mSuffixesSet;
+    private final ArraySet<String> mPrefixesSet;
+    private final ArraySet<String> mSuffixesSet;
     private final int mMaxSuffixLength;
-    private final HashSet<String> mLastNamePrefixesSet;
-    private final HashSet<String> mConjuctions;
+    private final ArraySet<String> mLastNamePrefixesSet;
+    private final ArraySet<String> mConjuctions;
     private final Locale mLocale;
     private final String mLanguage;
 
@@ -304,8 +304,8 @@
      * Converts a comma-separated list of Strings to a set of Strings. Trims strings
      * and converts them to upper case.
      */
-    private static HashSet<String> convertToSet(String strings) {
-        HashSet<String> set = new HashSet<String>();
+    private static ArraySet<String> convertToSet(String strings) {
+        ArraySet<String> set = new ArraySet<>();
         if (strings != null) {
             String[] split = strings.split(",");
             for (int i = 0; i < split.length; i++) {
diff --git a/src/com/android/providers/contacts/PhotoPriorityResolver.java b/src/com/android/providers/contacts/PhotoPriorityResolver.java
index bbf83c5..e9b9464 100644
--- a/src/com/android/providers/contacts/PhotoPriorityResolver.java
+++ b/src/com/android/providers/contacts/PhotoPriorityResolver.java
@@ -21,20 +21,17 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.ResolveInfo;
 import android.content.pm.ServiceInfo;
 import android.content.res.XmlResourceParser;
-import android.util.Log;
+import android.util.ArrayMap;
 
 import com.android.internal.util.XmlUtils;
-import com.google.android.collect.Maps;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
 
 import java.io.IOException;
-import java.util.HashMap;
 import java.util.List;
 
 /**
@@ -76,7 +73,7 @@
     private static final String PRIORITY_ATTR = "priority";
 
     private Context mContext;
-    private HashMap<String, Integer> mPhotoPriorities = Maps.newHashMap();
+    private ArrayMap<String, Integer> mPhotoPriorities = new ArrayMap<>();
 
     public PhotoPriorityResolver(Context context) {
         mContext = context;
diff --git a/src/com/android/providers/contacts/PhotoStore.java b/src/com/android/providers/contacts/PhotoStore.java
index 79042c4..f2b95b6 100644
--- a/src/com/android/providers/contacts/PhotoStore.java
+++ b/src/com/android/providers/contacts/PhotoStore.java
@@ -19,17 +19,18 @@
 import android.database.sqlite.SQLiteDatabase;
 import android.graphics.Bitmap;
 import android.provider.ContactsContract.PhotoFiles;
+import android.util.ArrayMap;
+import android.util.ArraySet;
 import android.util.Log;
 
 import com.android.providers.contacts.ContactsDatabaseHelper.PhotoFilesColumns;
 import com.android.providers.contacts.ContactsDatabaseHelper.Tables;
+
 import com.google.common.annotations.VisibleForTesting;
 
 import java.io.File;
 import java.io.FileOutputStream;
 import java.io.IOException;
-import java.util.HashMap;
-import java.util.HashSet;
 import java.util.Map;
 import java.util.Set;
 
@@ -77,7 +78,7 @@
             }
         }
         mDatabaseHelper = databaseHelper;
-        mEntries = new HashMap<Long, Entry>();
+        mEntries = new ArrayMap<Long, Entry>();
         initialize();
     }
 
@@ -146,7 +147,7 @@
      * @return The set of the keys in use that refer to non-existent entries.
      */
     public Set<Long> cleanup(Set<Long> keysInUse) {
-        Set<Long> keysToRemove = new HashSet<Long>();
+        Set<Long> keysToRemove = new ArraySet<>();
         keysToRemove.addAll(mEntries.keySet());
         keysToRemove.removeAll(keysInUse);
         if (!keysToRemove.isEmpty()) {
@@ -156,7 +157,7 @@
             }
         }
 
-        Set<Long> missingKeys = new HashSet<Long>();
+        Set<Long> missingKeys = new ArraySet<>();
         missingKeys.addAll(keysInUse);
         missingKeys.removeAll(mEntries.keySet());
         return missingKeys;
diff --git a/src/com/android/providers/contacts/SearchIndexManager.java b/src/com/android/providers/contacts/SearchIndexManager.java
index 768fb97..14c78a7 100644
--- a/src/com/android/providers/contacts/SearchIndexManager.java
+++ b/src/com/android/providers/contacts/SearchIndexManager.java
@@ -27,6 +27,7 @@
 import android.provider.ContactsContract.ProviderStatus;
 import android.provider.ContactsContract.RawContacts;
 import android.text.TextUtils;
+import android.util.ArraySet;
 import android.util.Log;
 
 import com.android.providers.contacts.ContactsDatabaseHelper.DataColumns;
@@ -38,7 +39,6 @@
 import com.google.common.annotations.VisibleForTesting;
 
 import java.util.ArrayList;
-import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
 import java.util.regex.Pattern;
@@ -76,7 +76,7 @@
         private StringBuilder mSbName = new StringBuilder();
         private StringBuilder mSbTokens = new StringBuilder();
         private StringBuilder mSbElementContent = new StringBuilder();
-        private HashSet<String> mUniqueElements = new HashSet<String>();
+        private ArraySet<String> mUniqueElements = new ArraySet<>();
         private Cursor mCursor;
 
         void setCursor(Cursor cursor) {
diff --git a/src/com/android/providers/contacts/TransactionContext.java b/src/com/android/providers/contacts/TransactionContext.java
index 5cc9102..dfb6d69 100644
--- a/src/com/android/providers/contacts/TransactionContext.java
+++ b/src/com/android/providers/contacts/TransactionContext.java
@@ -16,11 +16,12 @@
 
 package com.android.providers.contacts;
 
+import android.util.ArrayMap;
+import android.util.ArraySet;
+
 import com.google.android.collect.Maps;
 import com.google.android.collect.Sets;
 
-import java.util.HashMap;
-import java.util.HashSet;
 import java.util.Map.Entry;
 import java.util.Set;
 
@@ -32,19 +33,19 @@
 
     private final boolean mForProfile;
     /** Map from raw contact id to account Id */
-    private HashMap<Long, Long> mInsertedRawContactsAccounts;
-    private HashSet<Long> mUpdatedRawContacts;
-    private HashSet<Long> mMetadataDirtyRawContacts;
-    private HashSet<Long> mBackupIdChangedRawContacts;
-    private HashSet<Long> mDirtyRawContacts;
+    private ArrayMap<Long, Long> mInsertedRawContactsAccounts;
+    private ArraySet<Long> mUpdatedRawContacts;
+    private ArraySet<Long> mMetadataDirtyRawContacts;
+    private ArraySet<Long> mBackupIdChangedRawContacts;
+    private ArraySet<Long> mDirtyRawContacts;
     // Set used to track what has been changed and deleted. This is needed so we can update the
     // contact last touch timestamp.  Dirty set above is only set when sync adapter is false.
     // {@see android.provider.ContactsContract#CALLER_IS_SYNCADAPTER}. While the set below will
     // contain all changed contacts.
-    private HashSet<Long> mChangedRawContacts;
-    private HashSet<Long> mStaleSearchIndexRawContacts;
-    private HashSet<Long> mStaleSearchIndexContacts;
-    private HashMap<Long, Object> mUpdatedSyncStates;
+    private ArraySet<Long> mChangedRawContacts;
+    private ArraySet<Long> mStaleSearchIndexRawContacts;
+    private ArraySet<Long> mStaleSearchIndexContacts;
+    private ArrayMap<Long, Object> mUpdatedSyncStates;
 
     public TransactionContext(boolean forProfile) {
         mForProfile = forProfile;
@@ -55,21 +56,21 @@
     }
 
     public void rawContactInserted(long rawContactId, long accountId) {
-        if (mInsertedRawContactsAccounts == null) mInsertedRawContactsAccounts = Maps.newHashMap();
+        if (mInsertedRawContactsAccounts == null) mInsertedRawContactsAccounts = new ArrayMap<>();
         mInsertedRawContactsAccounts.put(rawContactId, accountId);
 
         markRawContactChangedOrDeletedOrInserted(rawContactId);
     }
 
     public void rawContactUpdated(long rawContactId) {
-        if (mUpdatedRawContacts == null) mUpdatedRawContacts = Sets.newHashSet();
+        if (mUpdatedRawContacts == null) mUpdatedRawContacts = new ArraySet<>();
         mUpdatedRawContacts.add(rawContactId);
     }
 
     public void markRawContactDirtyAndChanged(long rawContactId, boolean isSyncAdapter) {
         if (!isSyncAdapter) {
             if (mDirtyRawContacts == null) {
-                mDirtyRawContacts = Sets.newHashSet();
+                mDirtyRawContacts = new ArraySet<>();
             }
             mDirtyRawContacts.add(rawContactId);
         }
@@ -80,7 +81,7 @@
     public void markRawContactMetadataDirty(long rawContactId, boolean isMetadataSyncAdapter) {
         if (!isMetadataSyncAdapter) {
             if (mMetadataDirtyRawContacts == null) {
-                mMetadataDirtyRawContacts = Sets.newHashSet();
+                mMetadataDirtyRawContacts = new ArraySet<>();
             }
             mMetadataDirtyRawContacts.add(rawContactId);
         }
@@ -88,85 +89,85 @@
 
     public void markBackupIdChangedRawContact(long rawContactId) {
         if (mBackupIdChangedRawContacts == null) {
-            mBackupIdChangedRawContacts = Sets.newHashSet();
+            mBackupIdChangedRawContacts = new ArraySet<>();
         }
         mBackupIdChangedRawContacts.add(rawContactId);
     }
 
     public void markRawContactChangedOrDeletedOrInserted(long rawContactId) {
         if (mChangedRawContacts == null) {
-            mChangedRawContacts = Sets.newHashSet();
+            mChangedRawContacts = new ArraySet<>();
         }
         mChangedRawContacts.add(rawContactId);
     }
 
     public void syncStateUpdated(long rowId, Object data) {
-        if (mUpdatedSyncStates == null) mUpdatedSyncStates = Maps.newHashMap();
+        if (mUpdatedSyncStates == null) mUpdatedSyncStates = new ArrayMap<>();
         mUpdatedSyncStates.put(rowId, data);
     }
 
     public void invalidateSearchIndexForRawContact(long rawContactId) {
-        if (mStaleSearchIndexRawContacts == null) mStaleSearchIndexRawContacts = Sets.newHashSet();
+        if (mStaleSearchIndexRawContacts == null) mStaleSearchIndexRawContacts = new ArraySet<>();
         mStaleSearchIndexRawContacts.add(rawContactId);
     }
 
     public void invalidateSearchIndexForContact(long contactId) {
-        if (mStaleSearchIndexContacts == null) mStaleSearchIndexContacts = Sets.newHashSet();
+        if (mStaleSearchIndexContacts == null) mStaleSearchIndexContacts = new ArraySet<>();
         mStaleSearchIndexContacts.add(contactId);
     }
 
     public Set<Long> getInsertedRawContactIds() {
-        if (mInsertedRawContactsAccounts == null) mInsertedRawContactsAccounts = Maps.newHashMap();
+        if (mInsertedRawContactsAccounts == null) mInsertedRawContactsAccounts = new ArrayMap<>();
         return mInsertedRawContactsAccounts.keySet();
     }
 
     public Set<Long> getUpdatedRawContactIds() {
-        if (mUpdatedRawContacts == null) mUpdatedRawContacts = Sets.newHashSet();
+        if (mUpdatedRawContacts == null) mUpdatedRawContacts = new ArraySet<>();
         return mUpdatedRawContacts;
     }
 
     public Set<Long> getDirtyRawContactIds() {
-        if (mDirtyRawContacts == null) mDirtyRawContacts = Sets.newHashSet();
+        if (mDirtyRawContacts == null) mDirtyRawContacts = new ArraySet<>();
         return mDirtyRawContacts;
     }
 
     public Set<Long> getMetadataDirtyRawContactIds() {
-        if (mMetadataDirtyRawContacts == null) mMetadataDirtyRawContacts = Sets.newHashSet();
+        if (mMetadataDirtyRawContacts == null) mMetadataDirtyRawContacts = new ArraySet<>();
         return mMetadataDirtyRawContacts;
     }
 
     public Set<Long> getBackupIdChangedRawContacts() {
-        if (mBackupIdChangedRawContacts == null) mBackupIdChangedRawContacts = Sets.newHashSet();
+        if (mBackupIdChangedRawContacts == null) mBackupIdChangedRawContacts = new ArraySet<>();
         return mBackupIdChangedRawContacts;
     }
 
     public Set<Long> getChangedRawContactIds() {
-        if (mChangedRawContacts == null) mChangedRawContacts = Sets.newHashSet();
+        if (mChangedRawContacts == null) mChangedRawContacts = new ArraySet<>();
         return mChangedRawContacts;
     }
 
     public Set<Long> getStaleSearchIndexRawContactIds() {
-        if (mStaleSearchIndexRawContacts == null) mStaleSearchIndexRawContacts = Sets.newHashSet();
+        if (mStaleSearchIndexRawContacts == null) mStaleSearchIndexRawContacts = new ArraySet<>();
         return mStaleSearchIndexRawContacts;
     }
 
     public Set<Long> getStaleSearchIndexContactIds() {
-        if (mStaleSearchIndexContacts == null) mStaleSearchIndexContacts = Sets.newHashSet();
+        if (mStaleSearchIndexContacts == null) mStaleSearchIndexContacts = new ArraySet<>();
         return mStaleSearchIndexContacts;
     }
 
     public Set<Entry<Long, Object>> getUpdatedSyncStates() {
-        if (mUpdatedSyncStates == null) mUpdatedSyncStates = Maps.newHashMap();
+        if (mUpdatedSyncStates == null) mUpdatedSyncStates = new ArrayMap<>();
         return mUpdatedSyncStates.entrySet();
     }
 
     public Long getAccountIdOrNullForRawContact(long rawContactId) {
-        if (mInsertedRawContactsAccounts == null) mInsertedRawContactsAccounts = Maps.newHashMap();
+        if (mInsertedRawContactsAccounts == null) mInsertedRawContactsAccounts = new ArrayMap<>();
         return mInsertedRawContactsAccounts.get(rawContactId);
     }
 
     public boolean isNewRawContact(long rawContactId) {
-        if (mInsertedRawContactsAccounts == null) mInsertedRawContactsAccounts = Maps.newHashMap();
+        if (mInsertedRawContactsAccounts == null) mInsertedRawContactsAccounts = new ArrayMap<>();
         return mInsertedRawContactsAccounts.containsKey(rawContactId);
     }
 
diff --git a/src/com/android/providers/contacts/aggregation/AbstractContactAggregator.java b/src/com/android/providers/contacts/aggregation/AbstractContactAggregator.java
index 20e3bbe..965780e 100644
--- a/src/com/android/providers/contacts/aggregation/AbstractContactAggregator.java
+++ b/src/com/android/providers/contacts/aggregation/AbstractContactAggregator.java
@@ -67,14 +67,14 @@
 import android.provider.ContactsContract.RawContacts;
 import android.provider.ContactsContract.StatusUpdates;
 import android.text.TextUtils;
+import android.util.ArrayMap;
+import android.util.ArraySet;
 import android.util.EventLog;
 import android.util.Log;
 import android.util.Slog;
 
 import java.util.ArrayList;
 import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Locale;
@@ -170,7 +170,7 @@
     protected SQLiteStatement mContactInsert;
     protected SQLiteStatement mResetPinnedForRawContact;
 
-    protected HashMap<Long, Integer> mRawContactsMarkedForAggregation = Maps.newHashMap();
+    protected ArrayMap<Long, Integer> mRawContactsMarkedForAggregation = new ArrayMap<>();
 
     protected String[] mSelectionArgs1 = new String[1];
     protected String[] mSelectionArgs2 = new String[2];
@@ -532,7 +532,7 @@
     public final void clearPendingAggregations() {
         // HashMap woulnd't shrink the internal table once expands it, so let's just re-create
         // a new one instead of clear()ing it.
-        mRawContactsMarkedForAggregation = Maps.newHashMap();
+        mRawContactsMarkedForAggregation = new ArrayMap<>();
     }
 
     public final void markNewForAggregation(long rawContactId, int aggregationMode) {
@@ -951,7 +951,7 @@
     }
 
     // A set of raw contact IDs for which there are aggregation exceptions
-    protected final HashSet<Long> mAggregationExceptionIds = new HashSet<Long>();
+    protected final ArraySet<Long> mAggregationExceptionIds = new ArraySet<>();
     protected boolean mAggregationExceptionIdsValid;
 
     public final void invalidateAggregationExceptionCache() {
@@ -1958,7 +1958,7 @@
         try {
             List<MatchScore> bestMatches = findMatchingContacts(db, contactId, parameters);
             List<MatchScore> bestMatchesWithoutDuplicateContactIds = new ArrayList<>();
-            Set<Long> contactIds = new HashSet<>();
+            Set<Long> contactIds = new ArraySet<>();
             for (MatchScore bestMatch : bestMatches) {
                 long cid = bestMatch.getContactId();
                 if (!contactIds.contains(cid) && cid != contactId) {
@@ -2005,7 +2005,7 @@
         }
 
         // Run a query and find ids of best matching contacts satisfying the filter (if any)
-        HashSet<Long> foundIds = new HashSet<Long>();
+        ArraySet<Long> foundIds = new ArraySet<Long>();
         Cursor cursor = db.query(qb.getTables(), ContactIdQuery.COLUMNS, sb.toString(),
                 null, null, null, null);
         try {
diff --git a/src/com/android/providers/contacts/aggregation/ContactAggregator.java b/src/com/android/providers/contacts/aggregation/ContactAggregator.java
index e7fd910..e5bd2ea 100644
--- a/src/com/android/providers/contacts/aggregation/ContactAggregator.java
+++ b/src/com/android/providers/contacts/aggregation/ContactAggregator.java
@@ -25,6 +25,7 @@
 import android.provider.ContactsContract.Data;
 import android.provider.ContactsContract.RawContacts;
 import android.text.TextUtils;
+import android.util.ArraySet;
 import android.util.Log;
 import com.android.providers.contacts.ContactsDatabaseHelper;
 import com.android.providers.contacts.ContactsDatabaseHelper.DataColumns;
@@ -43,7 +44,6 @@
 import com.google.android.collect.Sets;
 
 import java.util.ArrayList;
-import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
 
@@ -95,8 +95,8 @@
         boolean needReaggregate = false;
 
         final ContactMatcher matcher = new ContactMatcher();
-        final Set<Long> rawContactIdsInSameAccount = new HashSet<Long>();
-        final Set<Long> rawContactIdsInOtherAccount = new HashSet<Long>();
+        final Set<Long> rawContactIdsInSameAccount = new ArraySet<>();
+        final Set<Long> rawContactIdsInOtherAccount = new ArraySet<>();
         if (aggregationMode == RawContacts.AGGREGATION_MODE_DEFAULT) {
             candidates.clear();
             matcher.clear();
@@ -202,7 +202,7 @@
             }
         } else if (needReaggregate) {
             // re-aggregate
-            final Set<Long> allRawContactIdSet = new HashSet<Long>();
+            final Set<Long> allRawContactIdSet = new ArraySet<>();
             allRawContactIdSet.addAll(rawContactIdsInSameAccount);
             allRawContactIdSet.addAll(rawContactIdsInOtherAccount);
             // If there is no other raw contacts aggregated with the given raw contact currently,
@@ -345,7 +345,7 @@
         }
 
 
-        final Set<Long> rawContactIdSet = new HashSet<Long>();
+        final Set<Long> rawContactIdSet = new ArraySet<>();
         rawContactIdSet.add(rawContactId);
         if (rawContactIdsInSameAccount.size() > 0 &&
                 isDataMaching(db, rawContactIdSet, rawContactIdsInSameAccount)) {
@@ -419,7 +419,7 @@
         // Find the connected component based on the aggregation exceptions or
         // identity/email/phone matching for all the raw contacts of [contactId] and the give
         // raw contact.
-        final Set<Long> allIds = new HashSet<Long>();
+        final Set<Long> allIds = new ArraySet<>();
         allIds.add(rawContactId);
         allIds.addAll(existingRawContactIds);
         final Set<Set<Long>> connectedRawContactSets = findConnectedRawContacts(db, allIds);
@@ -821,7 +821,7 @@
      */
     private void lookupApproximateNameMatches(SQLiteDatabase db, MatchCandidateList candidates,
             ContactMatcher matcher) {
-        HashSet<String> firstLetters = new HashSet<String>();
+        ArraySet<String> firstLetters = new ArraySet<>();
         for (int i = 0; i < candidates.mCount; i++) {
             final NameMatchCandidate candidate = candidates.mList.get(i);
             if (candidate.mName.length() >= 2) {
diff --git a/src/com/android/providers/contacts/aggregation/ContactAggregator2.java b/src/com/android/providers/contacts/aggregation/ContactAggregator2.java
index 5e1821b..e0bc3bb 100644
--- a/src/com/android/providers/contacts/aggregation/ContactAggregator2.java
+++ b/src/com/android/providers/contacts/aggregation/ContactAggregator2.java
@@ -31,6 +31,7 @@
 import android.provider.ContactsContract.PhotoFiles;
 import android.provider.ContactsContract.RawContacts;
 import android.text.TextUtils;
+import android.util.ArraySet;
 import android.util.Log;
 import com.android.providers.contacts.ContactsDatabaseHelper;
 import com.android.providers.contacts.ContactsDatabaseHelper.DataColumns;
@@ -54,7 +55,6 @@
 import com.google.common.collect.Multimap;
 
 import java.util.ArrayList;
-import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -212,14 +212,14 @@
         updateMatchScores(db, rawContactId, candidates, matcher);
         final RawContactMatchingCandidates matchingCandidates = new RawContactMatchingCandidates(
                 matcher.pickBestMatches());
-        Set<Long> newIds = new HashSet<>();
+        Set<Long> newIds = new ArraySet<>();
         newIds.addAll(matchingCandidates.getRawContactIdSet());
         // Keep doing the following until no new raw contact candidate is found.
         while (!newIds.isEmpty()) {
             if (matchingCandidates.getCount() >= AGGREGATION_CONTACT_SIZE_LIMIT) {
                 return matchingCandidates;
             }
-            final Set<Long> tmpIdSet = new HashSet<>();
+            final Set<Long> tmpIdSet = new ArraySet<>();
             for (long rId : newIds) {
                 final RawContactMatcher rMatcher = new RawContactMatcher();
                 updateMatchScores(db, rId, new MatchCandidateList(),
@@ -315,7 +315,7 @@
         // Find the connected component based on the aggregation exceptions or
         // identity/email/phone matching for all the raw contacts of [contactId] and the give
         // raw contact.
-        final Set<Long> allIds = new HashSet<>();
+        final Set<Long> allIds = new ArraySet<>();
         allIds.add(rawContactId);
         allIds.addAll(matchingCandidates.getRawContactIdSet());
         final Set<Set<Long>> connectedRawContactSets = findConnectedRawContacts(db, allIds);
@@ -331,7 +331,7 @@
         // Update aggregate data for all the contactIds touched by this connected component,
         for (Set<Long> connectedRawContactIds : connectedRawContactSets) {
             Long contactId = null;
-            Set<Long> cidsNeedToBeUpdated = new HashSet<>();
+            Set<Long> cidsNeedToBeUpdated = new ArraySet<>();
             if (connectedRawContactIds.contains(rawContactId)) {
                 // If there is no other raw contacts aggregated with the given raw contact currently
                 // or all the raw contacts in [currentCidForRawContact] are still in the same
@@ -422,7 +422,7 @@
      */
     private void breakComponentsByExceptions(SQLiteDatabase db,
             Set<Set<Long>> connectedRawContacts) {
-        final Set<Set<Long>> tmpSets = new HashSet<>(connectedRawContacts);
+        final Set<Set<Long>> tmpSets = new ArraySet<>(connectedRawContacts);
         for (Set<Long> component : tmpSets) {
             final String rawContacts = TextUtils.join(",", component);
             // If "SEPARATE" exception is found inside an connected component [component],
@@ -691,7 +691,7 @@
      */
     private void lookupApproximateNameMatches(SQLiteDatabase db, MatchCandidateList candidates,
             RawContactMatcher matcher) {
-        HashSet<String> firstLetters = new HashSet<>();
+        ArraySet<String> firstLetters = new ArraySet<>();
         for (int i = 0; i < candidates.mCount; i++) {
             final NameMatchCandidate candidate = candidates.mList.get(i);
             if (candidate.mName.length() >= 2) {
diff --git a/src/com/android/providers/contacts/aggregation/util/CommonNicknameCache.java b/src/com/android/providers/contacts/aggregation/util/CommonNicknameCache.java
index 9643d81..5e8126a 100644
--- a/src/com/android/providers/contacts/aggregation/util/CommonNicknameCache.java
+++ b/src/com/android/providers/contacts/aggregation/util/CommonNicknameCache.java
@@ -16,16 +16,16 @@
 
 package com.android.providers.contacts.aggregation.util;
 
+import android.app.ActivityManager;
 import android.database.Cursor;
 import android.database.sqlite.SQLiteDatabase;
+import android.util.ArrayMap;
 
 import com.android.providers.contacts.ContactsDatabaseHelper.NicknameLookupColumns;
 import com.android.providers.contacts.ContactsDatabaseHelper.Tables;
-import com.google.android.collect.Maps;
 
 import java.lang.ref.SoftReference;
 import java.util.BitSet;
-import java.util.HashMap;
 
 /**
  * Cache for common nicknames.
@@ -36,7 +36,8 @@
     private static final int NICKNAME_BLOOM_FILTER_SIZE = 0x1FFF;   // =long[128]
     private BitSet mNicknameBloomFilter;
 
-    private HashMap<String, SoftReference<String[]>> mNicknameClusterCache = Maps.newHashMap();
+    private final ArrayMap<String, SoftReference<String[]>> mNicknameClusterCache
+            = new ArrayMap<>();
 
     private final SQLiteDatabase mDb;
 
@@ -88,6 +89,9 @@
      * Returns nickname cluster IDs or null. Maintains cache.
      */
     public String[] getCommonNicknameClusters(String normalizedName) {
+        if (ActivityManager.isLowRamDeviceStatic()) {
+            return null; // Do not use common nickname cache on lowram devices.
+        }
         if (mNicknameBloomFilter == null) {
             preloadNicknameBloomFilter();
         }
diff --git a/src/com/android/providers/contacts/aggregation/util/ContactAggregatorHelper.java b/src/com/android/providers/contacts/aggregation/util/ContactAggregatorHelper.java
index 9c19dce..4311fe3 100644
--- a/src/com/android/providers/contacts/aggregation/util/ContactAggregatorHelper.java
+++ b/src/com/android/providers/contacts/aggregation/util/ContactAggregatorHelper.java
@@ -16,12 +16,13 @@
 
 package com.android.providers.contacts.aggregation.util;
 
+import android.util.ArrayMap;
+import android.util.ArraySet;
+
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.collect.Iterables;
 import com.google.common.collect.Multimap;
 
-import java.util.HashMap;
-import java.util.HashSet;
 import java.util.Map;
 import java.util.Set;
 
@@ -40,9 +41,9 @@
     public static void mergeComponentsWithDisjointAccounts(Set<Set<Long>> connectedRawContactSets,
             Map<Long, Long> rawContactsToAccounts) {
         // Index to rawContactIds mapping
-        final Map<Integer, Set<Long>> rawContactIds = new HashMap<>();
+        final Map<Integer, Set<Long>> rawContactIds = new ArrayMap<>();
         // AccountId to indices mapping
-        final Map<Long, Set<Integer>> accounts = new HashMap<>();
+        final Map<Long, Set<Integer>> accounts = new ArrayMap<>();
 
         int index = 0;
         for (Set<Long> rIds : connectedRawContactSets) {
@@ -51,7 +52,7 @@
                 long acctId = rawContactsToAccounts.get(rId);
                 Set<Integer> s = accounts.get(acctId);
                 if (s == null) {
-                    s = new HashSet<Integer>();
+                    s = new ArraySet<Integer>();
                 }
                 s.add(index);
                 accounts.put(acctId, s);
@@ -73,7 +74,7 @@
             }
         }
 
-        final Set<Long> mergedSet = new HashSet<>();
+        final Set<Long> mergedSet = new ArraySet<>();
         for (Long accountId : accounts.keySet()) {
             final Set<Integer> s = accounts.get(accountId);
             if (s.size() == 1) {
@@ -93,11 +94,11 @@
     @VisibleForTesting
     public static Set<Set<Long>> findConnectedComponents(Set<Long> rawContactIdSet, Multimap<Long,
             Long> matchingRawIdPairs) {
-        Set<Set<Long>> connectedRawContactSets = new HashSet<>();
-        Set<Long> visited = new HashSet<>();
+        Set<Set<Long>> connectedRawContactSets = new ArraySet<>();
+        Set<Long> visited = new ArraySet<>();
         for (Long id : rawContactIdSet) {
             if (!visited.contains(id)) {
-                Set<Long> set = new HashSet<>();
+                Set<Long> set = new ArraySet<>();
                 findConnectedComponentForRawContact(matchingRawIdPairs, visited, id, set);
                 connectedRawContactSets.add(set);
             }
diff --git a/src/com/android/providers/contacts/aggregation/util/ContactMatcher.java b/src/com/android/providers/contacts/aggregation/util/ContactMatcher.java
index 9b71651..c5837ed 100644
--- a/src/com/android/providers/contacts/aggregation/util/ContactMatcher.java
+++ b/src/com/android/providers/contacts/aggregation/util/ContactMatcher.java
@@ -18,11 +18,11 @@
 import com.android.providers.contacts.ContactsDatabaseHelper.NameLookupType;
 import com.android.providers.contacts.util.Hex;
 
+import android.util.ArrayMap;
 import android.util.Log;
 
 import java.util.ArrayList;
 import java.util.Collections;
-import java.util.HashMap;
 import java.util.List;
 
 /**
@@ -153,7 +153,7 @@
         return sMaxScore[index];
     }
 
-    private final HashMap<Long, MatchScore> mScores = new HashMap<Long, MatchScore>();
+    private final ArrayMap<Long, MatchScore> mScores = new ArrayMap<>();
     private final ArrayList<MatchScore> mScoreList = new ArrayList<MatchScore>();
     private int mScoreCount = 0;
 
diff --git a/src/com/android/providers/contacts/aggregation/util/RawContactMatcher.java b/src/com/android/providers/contacts/aggregation/util/RawContactMatcher.java
index f39ae96..b483ba8 100644
--- a/src/com/android/providers/contacts/aggregation/util/RawContactMatcher.java
+++ b/src/com/android/providers/contacts/aggregation/util/RawContactMatcher.java
@@ -15,13 +15,13 @@
  */
 package com.android.providers.contacts.aggregation.util;
 
+import android.util.ArrayMap;
 import android.util.Log;
 import com.android.providers.contacts.ContactsDatabaseHelper.NameLookupType;
 import com.android.providers.contacts.util.Hex;
 
 import java.util.ArrayList;
 import java.util.Collections;
-import java.util.HashMap;
 import java.util.List;
 
 /**
@@ -158,7 +158,7 @@
         return sMaxScore[index];
     }
 
-    private final HashMap<Long, MatchScore> mScores = new HashMap<Long, MatchScore>();
+    private final ArrayMap<Long, MatchScore> mScores = new ArrayMap<>();
     private final ArrayList<MatchScore> mScoreList = new ArrayList<MatchScore>();
     private int mScoreCount = 0;
 
diff --git a/src/com/android/providers/contacts/aggregation/util/RawContactMatchingCandidates.java b/src/com/android/providers/contacts/aggregation/util/RawContactMatchingCandidates.java
index 917c810..408e725 100644
--- a/src/com/android/providers/contacts/aggregation/util/RawContactMatchingCandidates.java
+++ b/src/com/android/providers/contacts/aggregation/util/RawContactMatchingCandidates.java
@@ -17,14 +17,15 @@
 package com.android.providers.contacts.aggregation.util;
 
 import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
 import static com.android.internal.util.Preconditions.checkNotNull;
 
+import android.util.ArrayMap;
+import android.util.ArraySet;
+
 /**
  * Matching candidates for a raw contact, used in the contact aggregator.
  */
@@ -89,7 +90,7 @@
     }
 
     private void createRawContactToContactMap() {
-        mRawContactToContact = new HashMap<Long, Long>();
+        mRawContactToContact = new ArrayMap<>();
         for (int i = 0; i < mBestMatches.size(); i++) {
             mRawContactToContact.put(mBestMatches.get(i).getRawContactId(),
                     mBestMatches.get(i).getContactId());
@@ -97,7 +98,7 @@
     }
 
     private void createRawContactToAccountMap() {
-        mRawContactToAccount = new HashMap<Long, Long>();
+        mRawContactToAccount = new ArrayMap<>();
         for (int i = 0; i <  mBestMatches.size(); i++) {
             mRawContactToAccount.put(mBestMatches.get(i).getRawContactId(),
                     mBestMatches.get(i).getAccountId());
@@ -105,7 +106,7 @@
     }
 
     private void createRawContactIdSet() {
-        mRawContactIds = new HashSet<Long>();
+        mRawContactIds = new ArraySet<>();
         for (int i = 0; i < mBestMatches.size(); i++) {
             mRawContactIds.add(mBestMatches.get(i).getRawContactId());
         }
diff --git a/src/com/android/providers/contacts/util/DbQueryUtils.java b/src/com/android/providers/contacts/util/DbQueryUtils.java
index 23c144a..458db61 100644
--- a/src/com/android/providers/contacts/util/DbQueryUtils.java
+++ b/src/com/android/providers/contacts/util/DbQueryUtils.java
@@ -19,7 +19,7 @@
 import android.database.DatabaseUtils;
 import android.text.TextUtils;
 
-import java.util.HashMap;
+import java.util.Map;
 import java.util.Set;
 
 /**
@@ -88,13 +88,13 @@
      * @throws IllegalArgumentException if any value in values is not found in
      * the projection map.
      */
-    public static void checkForSupportedColumns(HashMap<String, String> projectionMap,
+    public static void checkForSupportedColumns(Map<String, String> projectionMap,
             ContentValues values) {
         checkForSupportedColumns(projectionMap.keySet(), values, "Is invalid.");
     }
 
     /**
-     * @see #checkForSupportedColumns(HashMap, ContentValues)
+     * @see #checkForSupportedColumns(Map, ContentValues)
      */
     public static void checkForSupportedColumns(Set<String> allowedColumns, ContentValues values,
             String msgSuffix) {
diff --git a/tests/src/com/android/providers/contacts/aggregation/ContactAggregator2Test.java b/tests/src/com/android/providers/contacts/aggregation/ContactAggregator2Test.java
index 9839f8e..927b215 100644
--- a/tests/src/com/android/providers/contacts/aggregation/ContactAggregator2Test.java
+++ b/tests/src/com/android/providers/contacts/aggregation/ContactAggregator2Test.java
@@ -17,6 +17,7 @@
 package com.android.providers.contacts.aggregation;
 
 import android.accounts.Account;
+import android.app.ActivityManager;
 import android.content.ContentProviderOperation;
 import android.content.ContentProviderResult;
 import android.content.ContentUris;
@@ -412,7 +413,12 @@
         long rawContactId2 = RawContactUtil.createRawContact(mResolver, ACCOUNT_2);
         DataUtil.insertStructuredName(mResolver, rawContactId2, "William", "Gore");
 
-        assertAggregated(rawContactId1, rawContactId2, "William Gore");
+        if (ActivityManager.isLowRamDeviceStatic()) {
+            // No common nickname DB on lowram devices.
+            assertNotAggregated(rawContactId1, rawContactId2);
+        } else {
+            assertAggregated(rawContactId1, rawContactId2, "William Gore");
+        }
     }
 
     public void testAggregationByCommonNicknameOnly() {
@@ -422,7 +428,12 @@
         long rawContactId2 = RawContactUtil.createRawContact(mResolver, ACCOUNT_2);
         DataUtil.insertStructuredName(mResolver, rawContactId2, "Larry", null);
 
-        assertAggregated(rawContactId1, rawContactId2, "Lawrence");
+        if (ActivityManager.isLowRamDeviceStatic()) {
+            // No common nickname DB on lowram devices.
+            assertNotAggregated(rawContactId1, rawContactId2);
+        } else {
+            assertAggregated(rawContactId1, rawContactId2, "Lawrence");
+        }
     }
 
     public void testAggregationByNicknameNoStructuredNameWithinSameAccount() {
diff --git a/tests/src/com/android/providers/contacts/aggregation/ContactAggregatorTest.java b/tests/src/com/android/providers/contacts/aggregation/ContactAggregatorTest.java
index 3b59cdb..fb4f930 100644
--- a/tests/src/com/android/providers/contacts/aggregation/ContactAggregatorTest.java
+++ b/tests/src/com/android/providers/contacts/aggregation/ContactAggregatorTest.java
@@ -17,6 +17,7 @@
 package com.android.providers.contacts.aggregation;
 
 import android.accounts.Account;
+import android.app.ActivityManager;
 import android.content.ContentProviderOperation;
 import android.content.ContentProviderResult;
 import android.content.ContentUris;
@@ -396,7 +397,13 @@
         long rawContactId2 = RawContactUtil.createRawContact(mResolver, ACCOUNT_2);
         DataUtil.insertStructuredName(mResolver, rawContactId2, "William", "Gore");
 
-        assertAggregated(rawContactId1, rawContactId2, "William Gore");
+
+        if (ActivityManager.isLowRamDeviceStatic()) {
+            // No common nickname DB on lowram devices.
+            assertNotAggregated(rawContactId1, rawContactId2);
+        } else {
+            assertAggregated(rawContactId1, rawContactId2, "William Gore");
+        }
     }
 
     public void testAggregationByCommonNicknameOnly() {
@@ -406,7 +413,12 @@
         long rawContactId2 = RawContactUtil.createRawContact(mResolver, ACCOUNT_2);
         DataUtil.insertStructuredName(mResolver, rawContactId2, "Larry", null);
 
-        assertAggregated(rawContactId1, rawContactId2, "Lawrence");
+        if (ActivityManager.isLowRamDeviceStatic()) {
+            // No common nickname DB on lowram devices.
+            assertNotAggregated(rawContactId1, rawContactId2);
+        } else {
+            assertAggregated(rawContactId1, rawContactId2, "Lawrence");
+        }
     }
 
     public void testAggregationByNicknameNoStructuredName() {