resolved conflicts for merge of b8fb609b to jb-mr1-dev

Change-Id: I336ee0b3f041f83a4b4e2c9973abb73852c2cc6f
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 6f74368..281f98b 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -14,15 +14,9 @@
     <uses-permission android:name="android.permission.READ_CONTACTS" />
     <uses-permission android:name="android.permission.WRITE_CONTACTS" />
     <uses-permission android:name="android.permission.GET_ACCOUNTS" />
-    <uses-permission android:name="android.permission.READ_SYNC_STATS" />
-    <uses-permission android:name="android.permission.INTERNET" />
-    <uses-permission android:name="android.permission.USE_CREDENTIALS" />
-    <uses-permission android:name="com.google.android.googleapps.permission.GOOGLE_AUTH" />
-    <uses-permission android:name="com.google.android.googleapps.permission.GOOGLE_AUTH.cp" />
-    <uses-permission android:name="android.permission.SUBSCRIBED_FEEDS_READ" />
-    <uses-permission android:name="android.permission.SUBSCRIBED_FEEDS_WRITE" />
     <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
     <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
+    <uses-permission android:name="android.permission.BIND_DIRECTORY_SEARCH" />
 
     <application android:process="android.process.acore"
         android:label="@string/app_label"
@@ -33,6 +27,7 @@
             android:authorities="contacts;com.android.contacts"
             android:label="@string/provider_label"
             android:multiprocess="false"
+            android:exported="true"
             android:readPermission="android.permission.READ_CONTACTS"
             android:writePermission="android.permission.WRITE_CONTACTS">
             <path-permission
@@ -50,6 +45,7 @@
         <provider android:name="CallLogProvider"
             android:authorities="call_log"
             android:syncable="false" android:multiprocess="false"
+            android:exported="true"
             android:readPermission="android.permission.READ_CALL_LOG"
             android:writePermission="android.permission.WRITE_CALL_LOG">
         </provider>
@@ -57,6 +53,7 @@
         <provider android:name="VoicemailContentProvider"
             android:authorities="com.android.voicemail"
             android:syncable="false" android:multiprocess="false"
+            android:exported="true"
             android:permission="com.android.voicemail.permission.ADD_VOICEMAIL">
         </provider>
 
@@ -96,5 +93,21 @@
         </receiver>
 
         <service android:name="VoicemailCleanupService"/>
+
+        <activity android:name=".debug.ContactsDumpActivity"
+                android:label="@string/debug_dump_title"
+                android:theme="@android:style/Theme.Holo.Dialog"
+                >
+            <intent-filter>
+                <action android:name="com.android.providers.contacts.DUMP_DATABASE"/>
+                <category android:name="android.intent.category.DEFAULT"/>
+            </intent-filter>
+        </activity>
+
+        <provider android:name=".debug.DumpFileProvider"
+            android:authorities="com.android.contacts.dumpfile"
+            android:exported="true">
+        </provider>
+
     </application>
 </manifest>
diff --git a/res/layout/contact_dump_activity.xml b/res/layout/contact_dump_activity.xml
new file mode 100644
index 0000000..557b5bd
--- /dev/null
+++ b/res/layout/contact_dump_activity.xml
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2012 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical"
+    android:padding="4dp"
+    android:gravity="center_horizontal">
+
+    <!-- Message to show to use. -->
+    <ScrollView
+        android:layout_width="match_parent"
+        android:layout_height="0dip"
+        android:gravity="center_vertical|left"
+        android:layout_weight="1">
+        <TextView
+            android:id="@+id/text"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:padding="16dp"
+            android:text="@string/debug_dump_database_message"
+            android:textAppearance="?android:attr/textAppearanceMedium" />
+    </ScrollView>
+
+    <!-- Alert dialog style buttons along the bottom. -->
+    <LinearLayout
+        style="?android:attr/buttonBarStyle"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:measureWithLargestChild="true">
+        <Button
+            style="?android:attr/buttonBarButtonStyle"
+            android:id="@+id/confirm"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:onClick="onClick"
+            android:text="@string/debug_dump_start_button" />
+        <Button
+            style="?android:attr/buttonBarButtonStyle"
+            android:id="@+id/delete"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:onClick="onClick"
+            android:text="@string/debug_dump_delete_button"
+            android:enabled="false" />
+        <Button
+            style="?android:attr/buttonBarButtonStyle"
+            android:id="@+id/cancel"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:onClick="onClick"
+            android:text="@android:string/no" />
+    </LinearLayout>
+</LinearLayout>
\ No newline at end of file
diff --git a/res/values-af/strings.xml b/res/values-af/strings.xml
index 2f6562b..d8c1011 100644
--- a/res/values-af/strings.xml
+++ b/res/values-af/strings.xml
@@ -28,4 +28,11 @@
     <string name="read_write_all_voicemail_label" msgid="4557216100818257560">"Kry toegang tot alle stemboodskappe"</string>
     <string name="read_write_all_voicemail_description" msgid="8029809937805761356">"Laat die program toe om alle stemboodskappe te stoor en op te haal wat hierdie toestel kan lees."</string>
     <string name="voicemail_from_column" msgid="435732568832121444">"Stemboodskap van "</string>
+    <string name="debug_dump_title" msgid="4916885724165570279">"Kopieer kontaktedatabasis"</string>
+    <string name="debug_dump_database_message" msgid="406438635002392290">"Jy is op die punt om 1) \'n afskrif van jou databasis te maak wat alle inligting insluit wat verband hou met kontakte en alle oproeploglêers na die interne berging, en 2) dit te e-pos. Onthou om die kopie uit te vee sodra jy dit suksesvol van die toestel af gekopieer het, of sodra die e-pos ontvang is."</string>
+    <string name="debug_dump_delete_button" msgid="7832879421132026435">"Vee nou uit"</string>
+    <string name="debug_dump_start_button" msgid="2837506913757600001">"Begin"</string>
+    <string name="debug_dump_email_sender_picker" msgid="3534420908672176460">"Kies \'n program om jou lêer te stuur"</string>
+    <string name="debug_dump_email_subject" msgid="108188398416385976">"Kontaktedatabasis aangeheg"</string>
+    <string name="debug_dump_email_body" msgid="4577749800871444318">"My kontaktedatabasis met al my kontakinligting is aangeheg. Hanteer versigtig."</string>
 </resources>
diff --git a/res/values-am/strings.xml b/res/values-am/strings.xml
index 6aa55ec..5889bc5 100644
--- a/res/values-am/strings.xml
+++ b/res/values-am/strings.xml
@@ -28,4 +28,11 @@
     <string name="read_write_all_voicemail_label" msgid="4557216100818257560">"ሁሉንም  የድምፅ መልዕክቶች ድረስ"</string>
     <string name="read_write_all_voicemail_description" msgid="8029809937805761356">"ትግበራ ይህ መሣሪያ መድረስ የሚችለውን የድምፅ መልዕክቶች በሙሉ ለማከማቸት እና ሰርስሮ ለማውጣት ይፈቅዳል።"</string>
     <string name="voicemail_from_column" msgid="435732568832121444">"ከ....የድምፅ መልዕክት "</string>
+    <string name="debug_dump_title" msgid="4916885724165570279">"የእውቂያዎች የውሂብ ጎታ ገልብጥ"</string>
+    <string name="debug_dump_database_message" msgid="406438635002392290">"ይህንን ሊያደርጉ ነው፦ 1) ሁሉንም ከእውቂያዎች ጋር የተያያዙ መረጃዎችንና ሁሉንም የጥሪ ምዝግብ ማስታወሻዎችን የያዘው የውሂብ ጎታዎ ቅጂ በውስጣዊ ማከማቻው ላይ ሊያስቀምጡ ነው፤ እና 2) በኢሜይል ሊልኩት። ከመሣሪያው በተገለበጠ ጊዜ ወይም ኢሜይሉ ሲደርስ ወዲያውንኑ ቅጂውን መሰረዝ እንዳለብዎት ያስታውሱ።"</string>
+    <string name="debug_dump_delete_button" msgid="7832879421132026435">"አሁን ሰርዝ"</string>
+    <string name="debug_dump_start_button" msgid="2837506913757600001">"ጀምር"</string>
+    <string name="debug_dump_email_sender_picker" msgid="3534420908672176460">"ፋይልዎትን የሚልኩበት ፕሮግራም ይምረጡ"</string>
+    <string name="debug_dump_email_subject" msgid="108188398416385976">"የእውቂያዎች የውሂብ ጎታ ተያይዟል"</string>
+    <string name="debug_dump_email_body" msgid="4577749800871444318">"የእውቂያዎቼ የውሂብ ጎታ ከሁሉም የእውቂያዎቼ መረጃዎች ጋር አባሪ ተደርጓል። በደንብ ይጠበቅ።"</string>
 </resources>
diff --git a/res/values-ar/strings.xml b/res/values-ar/strings.xml
index 3cd298d..d2fd0a6 100644
--- a/res/values-ar/strings.xml
+++ b/res/values-ar/strings.xml
@@ -28,4 +28,11 @@
     <string name="read_write_all_voicemail_label" msgid="4557216100818257560">"الوصول إلى جميع رسائل البريد الصوتي"</string>
     <string name="read_write_all_voicemail_description" msgid="8029809937805761356">"للسماح للتطبيق بتخزين واسترداد جميع رسائل البريد الصوتي التي يمكن الوصول إليها عبر هذا الجهاز."</string>
     <string name="voicemail_from_column" msgid="435732568832121444">"بريد صوتي من "</string>
+    <string name="debug_dump_title" msgid="4916885724165570279">"نسخ قاعدة بيانات جهات الاتصال"</string>
+    <string name="debug_dump_database_message" msgid="406438635002392290">"أنت على وشك 1) إنشاء نسخة من قاعدة بياناتك التي تتضمن جميع المعلومات المرتبطة بجهات الاتصال وجميع سجلات المكالمات إلى وحدة التخزين الداخلية و2) إرسالها بالبريد الإلكتروني. تذكر حذف النسخة بمجرد إتمام نسخها من الجهاز أو تلقي الرسالة الإلكترونية."</string>
+    <string name="debug_dump_delete_button" msgid="7832879421132026435">"الحذف الآن"</string>
+    <string name="debug_dump_start_button" msgid="2837506913757600001">"البداية"</string>
+    <string name="debug_dump_email_sender_picker" msgid="3534420908672176460">"اختر أحد البرامج لإرسال الملف"</string>
+    <string name="debug_dump_email_subject" msgid="108188398416385976">"قاعدة بيانات جهات الاتصال مرفقة"</string>
+    <string name="debug_dump_email_body" msgid="4577749800871444318">"قاعدة بيانات جهات اتصالي والتي تحتوي على جميع المعلومات ذات الصلة بجهات الاتصال مرفقة. يرجى التعامل معها بحرص."</string>
 </resources>
diff --git a/res/values-be/strings.xml b/res/values-be/strings.xml
index 44357c3..171fbb6 100644
--- a/res/values-be/strings.xml
+++ b/res/values-be/strings.xml
@@ -28,4 +28,11 @@
     <string name="read_write_all_voicemail_label" msgid="4557216100818257560">"Доступ да ўсіх галасавых паведамленняў"</string>
     <string name="read_write_all_voicemail_description" msgid="8029809937805761356">"Дазваляе прыкладанню захоўваць і прайграваць усе даступныя для гэтай прылады паведамленні галасавой пошты."</string>
     <string name="voicemail_from_column" msgid="435732568832121444">"Галасавое паведамленне ад "</string>
+    <string name="debug_dump_title" msgid="4916885724165570279">"Капiраваць базу дадзеных кантактаў"</string>
+    <string name="debug_dump_database_message" msgid="406438635002392290">"Вы збіраецеся 1) зрабіць копію базы дадзеных, якая ўключае ў сябе ўсе звесткi пра кантакты і званкi на ўнутранай памяці, і 2) адправiць яго па электроннай пошце. Не забудзьцеся выдаліць копію, як толькі вы паспяхова скапіруеце іх на прыладу ці атрымаеце па электроннай пошце."</string>
+    <string name="debug_dump_delete_button" msgid="7832879421132026435">"Выдаліць зараз"</string>
+    <string name="debug_dump_start_button" msgid="2837506913757600001">"Пачаць"</string>
+    <string name="debug_dump_email_sender_picker" msgid="3534420908672176460">"Выберыце праграму для адпраўкі файла"</string>
+    <string name="debug_dump_email_subject" msgid="108188398416385976">"Далучаны кантакты Dd"</string>
+    <string name="debug_dump_email_body" msgid="4577749800871444318">"Далучана база дадзеных маiх кантактаў з усёй інфармацыяй. Працуйце з ёй уважліва."</string>
 </resources>
diff --git a/res/values-bg/strings.xml b/res/values-bg/strings.xml
index dee34a7..0690d6c 100644
--- a/res/values-bg/strings.xml
+++ b/res/values-bg/strings.xml
@@ -28,4 +28,11 @@
     <string name="read_write_all_voicemail_label" msgid="4557216100818257560">"Достъп до всички гласови съобщения"</string>
     <string name="read_write_all_voicemail_description" msgid="8029809937805761356">"Разрешава на приложението да съхранява и извлича всички гласови съобщения, до които това устройство има достъп."</string>
     <string name="voicemail_from_column" msgid="435732568832121444">"Гласова поща от "</string>
+    <string name="debug_dump_title" msgid="4916885724165570279">"Копиране на базата от данни на контактите"</string>
+    <string name="debug_dump_database_message" msgid="406438635002392290">"На път сте 1) да направите копие във вътрешното хранилище на базата си от данни, което включва цялата свързана с контактите информация и всички списъци с обаждания, и 2) да го изпратите по имейл. Не забравяйте да го изтриете веднага след като го копирате успешно от устройството или когато имейлът е получен."</string>
+    <string name="debug_dump_delete_button" msgid="7832879421132026435">"Изтриване сега"</string>
+    <string name="debug_dump_start_button" msgid="2837506913757600001">"Начало"</string>
+    <string name="debug_dump_email_sender_picker" msgid="3534420908672176460">"Изберете програма, за да изпратите файла си"</string>
+    <string name="debug_dump_email_subject" msgid="108188398416385976">"Прикачена база от данни на контактите"</string>
+    <string name="debug_dump_email_body" msgid="4577749800871444318">"Прикачена Ви изпращам базата от данни на контактите си заедно с цялата информация за тях. Бъдете внимателни."</string>
 </resources>
diff --git a/res/values-ca/strings.xml b/res/values-ca/strings.xml
index ef073fa..1cd0ea6 100644
--- a/res/values-ca/strings.xml
+++ b/res/values-ca/strings.xml
@@ -28,4 +28,11 @@
     <string name="read_write_all_voicemail_label" msgid="4557216100818257560">"Accés a tots els missatges de veu"</string>
     <string name="read_write_all_voicemail_description" msgid="8029809937805761356">"Permet que l\'aplicació emmagatzemi i recuperi totes les bústies de veu a les quals pot accedir aquest dispositiu."</string>
     <string name="voicemail_from_column" msgid="435732568832121444">"Missatge de veu de "</string>
+    <string name="debug_dump_title" msgid="4916885724165570279">"Copia la base de dades de contactes"</string>
+    <string name="debug_dump_database_message" msgid="406438635002392290">"Estàs a punt de: 1) fer una còpia de la teva base de dades, que inclou tota la informació relacionada amb els contactes i tots els registres de trucades de l\'emmagatzematge intern, i 2) d\'enviar-la per correu electrònic. Recorda suprimir la còpia de seguida que l\'hagis copiat correctament al dispositiu o quan hagis rebut el correu electrònic."</string>
+    <string name="debug_dump_delete_button" msgid="7832879421132026435">"Suprimeix ara"</string>
+    <string name="debug_dump_start_button" msgid="2837506913757600001">"Inicia"</string>
+    <string name="debug_dump_email_sender_picker" msgid="3534420908672176460">"Selecciona un programa per enviar el fitxer"</string>
+    <string name="debug_dump_email_subject" msgid="108188398416385976">"Base de dades de contactes adjunta"</string>
+    <string name="debug_dump_email_body" msgid="4577749800871444318">"Aquí adjunta hi ha la meva base de dades de contactes amb tota la informació dels meus contactes. Tracta-la amb cura."</string>
 </resources>
diff --git a/res/values-cs/strings.xml b/res/values-cs/strings.xml
index 76335a0..561388f 100644
--- a/res/values-cs/strings.xml
+++ b/res/values-cs/strings.xml
@@ -28,4 +28,11 @@
     <string name="read_write_all_voicemail_label" msgid="4557216100818257560">"Přístup ke všem hlasovým zprávám"</string>
     <string name="read_write_all_voicemail_description" msgid="8029809937805761356">"Umožňuje aplikaci ukládat a načítat všechny hlasové zprávy, ke kterým má toto zařízení přístup."</string>
     <string name="voicemail_from_column" msgid="435732568832121444">"Hlasová zpráva od uživatele "</string>
+    <string name="debug_dump_title" msgid="4916885724165570279">"Kopírování databáze kontaktů"</string>
+    <string name="debug_dump_database_message" msgid="406438635002392290">"Chystáte se 1) vytvořit v interním úložišti kopii databáze obsahující všechny informace o kontaktech a veškerou historii hovorů a 2) odeslat ji e-mailem. Po úspěšném zkopírování ze zařízení nebo přijetí e-mailem ji nezapomeňte ihned odstranit."</string>
+    <string name="debug_dump_delete_button" msgid="7832879421132026435">"Smazat nyní"</string>
+    <string name="debug_dump_start_button" msgid="2837506913757600001">"Spustit"</string>
+    <string name="debug_dump_email_sender_picker" msgid="3534420908672176460">"Vyberte program pro odeslání souboru."</string>
+    <string name="debug_dump_email_subject" msgid="108188398416385976">"Databáze kontaktů v příloze"</string>
+    <string name="debug_dump_email_body" msgid="4577749800871444318">"V příloze je databáze s informacemi o všech mých kontaktech. Zacházejte s ní opatrně."</string>
 </resources>
diff --git a/res/values-da/strings.xml b/res/values-da/strings.xml
index 7b708fb..ae4b033 100644
--- a/res/values-da/strings.xml
+++ b/res/values-da/strings.xml
@@ -28,4 +28,11 @@
     <string name="read_write_all_voicemail_label" msgid="4557216100818257560">"Adgang til alle telefonsvarerbeskeder"</string>
     <string name="read_write_all_voicemail_description" msgid="8029809937805761356">"Tillader, at applikationen gemmer og henter alle telefonsvarerbeskeder, som denne enhed kan få adgang til."</string>
     <string name="voicemail_from_column" msgid="435732568832121444">"Telefonsvarerbesked fra "</string>
+    <string name="debug_dump_title" msgid="4916885724165570279">"Kopiér database med kontaktpersoner"</string>
+    <string name="debug_dump_database_message" msgid="406438635002392290">"Du er ved at 1) lave en kopi af din database, som indeholder alle oplysninger om dine kontaktpersoner og alle opkaldslister, til det interne lager, og 2) sende den som e-mail. Husk at slette kopien, så snart du har kopieret den fra enheden, eller e-mailen er modtaget."</string>
+    <string name="debug_dump_delete_button" msgid="7832879421132026435">"Slet nu"</string>
+    <string name="debug_dump_start_button" msgid="2837506913757600001">"Start"</string>
+    <string name="debug_dump_email_sender_picker" msgid="3534420908672176460">"Vælg et program, for at sende din fil"</string>
+    <string name="debug_dump_email_subject" msgid="108188398416385976">"Database med kontaktpersoner er vedhæftet"</string>
+    <string name="debug_dump_email_body" msgid="4577749800871444318">"Min database med kontaktpersoner med alle oplysninger om mine kontaktpersoner er vedhæftet. Brug den med omhu."</string>
 </resources>
diff --git a/res/values-de/strings.xml b/res/values-de/strings.xml
index 1f10b19..3b59396 100644
--- a/res/values-de/strings.xml
+++ b/res/values-de/strings.xml
@@ -28,4 +28,11 @@
     <string name="read_write_all_voicemail_label" msgid="4557216100818257560">"Zugriff auf alle Mailbox-Nachrichten"</string>
     <string name="read_write_all_voicemail_description" msgid="8029809937805761356">"Ermöglicht der App das Speichern und Abrufen aller Mailbox-Nachrichten, auf die dieses Gerät zugreifen kann"</string>
     <string name="voicemail_from_column" msgid="435732568832121444">"Mailbox-Nachricht von "</string>
+    <string name="debug_dump_title" msgid="4916885724165570279">"Kontaktdatenbank kopieren"</string>
+    <string name="debug_dump_database_message" msgid="406438635002392290">"Sie 1) erstellen eine Kopie Ihrer Datenbank, die alle Kontaktinformationen und Anruflisten auf dem internen Speicher enthält, und 2) senden diese Kopie per E-Mail. Denken Sie daran, die Kopie so schnell wie möglich zu löschen, nachdem Sie sie vom Gerät kopiert haben oder die E-Mail empfangen wurde."</string>
+    <string name="debug_dump_delete_button" msgid="7832879421132026435">"Jetzt löschen"</string>
+    <string name="debug_dump_start_button" msgid="2837506913757600001">"Starten"</string>
+    <string name="debug_dump_email_sender_picker" msgid="3534420908672176460">"Programm zum Senden der Datei auswählen"</string>
+    <string name="debug_dump_email_subject" msgid="108188398416385976">"Kontaktdatenbank angehängt"</string>
+    <string name="debug_dump_email_body" msgid="4577749800871444318">"Anbei meine Kontaktdatenbank mit allen Informationen zu meinen Kontakten. Bitte vertraulich behandeln!"</string>
 </resources>
diff --git a/res/values-el/strings.xml b/res/values-el/strings.xml
index 2622515..a85bae3 100644
--- a/res/values-el/strings.xml
+++ b/res/values-el/strings.xml
@@ -28,4 +28,11 @@
     <string name="read_write_all_voicemail_label" msgid="4557216100818257560">"Πρόσβαση σε όλα τα μηνύματα αυτόματου τηλεφωνητή"</string>
     <string name="read_write_all_voicemail_description" msgid="8029809937805761356">"Επιτρέπει στην εφαρμογή την αποθήκευση και ανάκτηση όλων των μηνυμάτων αυτόματου τηλεφωνητή, στα οποία μπορεί να έχει πρόσβαση αυτή η συσκευή."</string>
     <string name="voicemail_from_column" msgid="435732568832121444">"Μήνυμα αυτόματου τηλεφωνητή από "</string>
+    <string name="debug_dump_title" msgid="4916885724165570279">"Αντιγραφή βάσης δεδομένων επαφών"</string>
+    <string name="debug_dump_database_message" msgid="406438635002392290">"Πρόκειται να 1) δημιουργήσετε ένα αντίγραφο της βάσης δεδομένων σας το οποίο περιλαμβάνει όλες τις πληροφορίες που σχετίζονται με τις επαφές και όλα τα αρχεία καταγραφής κλήσεων στον εσωτερικό αποθηκευτικό χώρο και να το 2) αποστείλετε με μήνυμα ηλεκτρονικού ταχυδρομείου. Μην ξεχάσετε να διαγράψετε από τη συσκευή σας το αντίγραφο μόλις το αντιγράψετε επιτυχώς ή μόλις παραδοθεί το μήνυμα ηλεκτρονικού ταχυδρομείου."</string>
+    <string name="debug_dump_delete_button" msgid="7832879421132026435">"Διαγραφή τώρα"</string>
+    <string name="debug_dump_start_button" msgid="2837506913757600001">"Έναρξη"</string>
+    <string name="debug_dump_email_sender_picker" msgid="3534420908672176460">"Επιλέξτε ένα πρόγραμμα για να στείλετε το αρχείο σας"</string>
+    <string name="debug_dump_email_subject" msgid="108188398416385976">"Συνημμένη βάση δεδομένων επαφών"</string>
+    <string name="debug_dump_email_body" msgid="4577749800871444318">"Επισυνάπτεται η βάση δεδομένων των επαφών μου με όλες τις πληροφορίες των επαφών μου. Ο χειρισμός της πρέπει να είναι προσεκτικός."</string>
 </resources>
diff --git a/res/values-en-rGB/strings.xml b/res/values-en-rGB/strings.xml
index 9cbc783..27342c9 100644
--- a/res/values-en-rGB/strings.xml
+++ b/res/values-en-rGB/strings.xml
@@ -28,4 +28,11 @@
     <string name="read_write_all_voicemail_label" msgid="4557216100818257560">"Access all voicemails"</string>
     <string name="read_write_all_voicemail_description" msgid="8029809937805761356">"Allows the app to store and retrieve all voicemails that this device can access."</string>
     <string name="voicemail_from_column" msgid="435732568832121444">"Voicemail from "</string>
+    <string name="debug_dump_title" msgid="4916885724165570279">"Copy contacts database"</string>
+    <string name="debug_dump_database_message" msgid="406438635002392290">"You are about to 1) make a copy of your database which includes all contacts related information and all call log to the internal storage, and 2) email it. Remember to delete the copy as soon as you have successfully copied it off the device or the email is received."</string>
+    <string name="debug_dump_delete_button" msgid="7832879421132026435">"Delete now"</string>
+    <string name="debug_dump_start_button" msgid="2837506913757600001">"Start"</string>
+    <string name="debug_dump_email_sender_picker" msgid="3534420908672176460">"Choose a programme to send your file"</string>
+    <string name="debug_dump_email_subject" msgid="108188398416385976">"Contacts Db attached"</string>
+    <string name="debug_dump_email_body" msgid="4577749800871444318">"Attached is my contacts database with all my contacts information. Handle with care."</string>
 </resources>
diff --git a/res/values-es-rUS/strings.xml b/res/values-es-rUS/strings.xml
index a3cddf2..8a14b6b 100644
--- a/res/values-es-rUS/strings.xml
+++ b/res/values-es-rUS/strings.xml
@@ -28,4 +28,11 @@
     <string name="read_write_all_voicemail_label" msgid="4557216100818257560">"Acceder a todos los mensajes de voz"</string>
     <string name="read_write_all_voicemail_description" msgid="8029809937805761356">"Permite que la aplicación almacene y recupere todos los mensajes de voz a los que este dispositivo puede acceder."</string>
     <string name="voicemail_from_column" msgid="435732568832121444">"Mensaje de voz de "</string>
+    <string name="debug_dump_title" msgid="4916885724165570279">"Copiar base de datos de contactos"</string>
+    <string name="debug_dump_database_message" msgid="406438635002392290">"Estás a punto de 1) copiar tu base datos, que incluye información de todos los contactos y el registro de todas las llamadas, en el almacenamiento interno; y de 2) enviar la copia por correo. Recuerda eliminar la copia inmediatamente después de guardarla fuera del dispositivo o de que se reciba el correo."</string>
+    <string name="debug_dump_delete_button" msgid="7832879421132026435">"Eliminar ahora"</string>
+    <string name="debug_dump_start_button" msgid="2837506913757600001">"Comenzar"</string>
+    <string name="debug_dump_email_sender_picker" msgid="3534420908672176460">"Elige un programa para enviar el archivo."</string>
+    <string name="debug_dump_email_subject" msgid="108188398416385976">"Base de datos de contactos adjunta"</string>
+    <string name="debug_dump_email_body" msgid="4577749800871444318">"Adjunto a este correo la base de datos de mis contactos con toda la información relacionada ellos. Esta información se debe manejar con cuidado."</string>
 </resources>
diff --git a/res/values-es/strings.xml b/res/values-es/strings.xml
index d4f7edf..ae99f67 100644
--- a/res/values-es/strings.xml
+++ b/res/values-es/strings.xml
@@ -28,4 +28,11 @@
     <string name="read_write_all_voicemail_label" msgid="4557216100818257560">"Acceder a todos los mensajes de voz"</string>
     <string name="read_write_all_voicemail_description" msgid="8029809937805761356">"Permite que la aplicación almacene y recupere todos los mensajes de voz a los que puede acceder este dispositivo."</string>
     <string name="voicemail_from_column" msgid="435732568832121444">"Mensaje de voz de "</string>
+    <string name="debug_dump_title" msgid="4916885724165570279">"Copiar base de datos de contactos"</string>
+    <string name="debug_dump_database_message" msgid="406438635002392290">"Vas a 1) hacer una copia de tu base de datos, que incluye la información relacionada con tus contactos y el registro de llamadas, en el almacenamiento interno y a 2) enviarla por correo electrónico. No olvides eliminar la copia en cuanto la hayas copiado en otro dispositivo o hayas recibido el correo electrónico."</string>
+    <string name="debug_dump_delete_button" msgid="7832879421132026435">"Eliminar ahora"</string>
+    <string name="debug_dump_start_button" msgid="2837506913757600001">"Iniciar"</string>
+    <string name="debug_dump_email_sender_picker" msgid="3534420908672176460">"Seleccionar un programa para enviar el archivo"</string>
+    <string name="debug_dump_email_subject" msgid="108188398416385976">"Base de datos de contactos adjunta"</string>
+    <string name="debug_dump_email_body" msgid="4577749800871444318">"Mi base de datos de contactos con toda la información de mis contactos va adjunta a este mensaje. Usa esta información con cuidado."</string>
 </resources>
diff --git a/res/values-et/strings.xml b/res/values-et/strings.xml
index 6fda0d5..e6018f3 100644
--- a/res/values-et/strings.xml
+++ b/res/values-et/strings.xml
@@ -28,4 +28,11 @@
     <string name="read_write_all_voicemail_label" msgid="4557216100818257560">"Juurdepääs kõigile kõnepostisõnumitele"</string>
     <string name="read_write_all_voicemail_description" msgid="8029809937805761356">"Võimaldab rakendusel salvestada ja vastu võtta kõik kõnepostisõnumid, mille juurde seade pääseb."</string>
     <string name="voicemail_from_column" msgid="435732568832121444">"Kõnepost kontaktilt "</string>
+    <string name="debug_dump_title" msgid="4916885724165570279">"Kontaktide andmebaasi kopeerimine"</string>
+    <string name="debug_dump_database_message" msgid="406438635002392290">"Soovite teha 1) sisemisse salvestusruumi koopia andmebaasist, mis sisaldab kogu kontaktidega seotud teavet ja kõikide kõnede logi ning 2) saata koopia meiliga. Kustutage koopia niipea, kui olete selle seadmest kopeerinud või meil on kohale jõudnud."</string>
+    <string name="debug_dump_delete_button" msgid="7832879421132026435">"Kustuta kohe"</string>
+    <string name="debug_dump_start_button" msgid="2837506913757600001">"Alusta"</string>
+    <string name="debug_dump_email_sender_picker" msgid="3534420908672176460">"Valige programm faili saatmiseks"</string>
+    <string name="debug_dump_email_subject" msgid="108188398416385976">"Lisatud kontaktide andmebaas"</string>
+    <string name="debug_dump_email_body" msgid="4577749800871444318">"Lisatud on minu kontaktide andmebaas, mis sisaldab kogu kontaktidega seotud teavet. Käsitsege ettevaatlikult."</string>
 </resources>
diff --git a/res/values-fa/strings.xml b/res/values-fa/strings.xml
index bcdf710..ca544db 100644
--- a/res/values-fa/strings.xml
+++ b/res/values-fa/strings.xml
@@ -28,4 +28,11 @@
     <string name="read_write_all_voicemail_label" msgid="4557216100818257560">"دسترسی به تمام پست‌های صوتی"</string>
     <string name="read_write_all_voicemail_description" msgid="8029809937805761356">"به برنامه اجازه ذخیره و بازیابی تمام پست‌های صوتی قابل دسترس برای این دستگاه را می‌دهد."</string>
     <string name="voicemail_from_column" msgid="435732568832121444">"پست صوتی از "</string>
+    <string name="debug_dump_title" msgid="4916885724165570279">"کپی پایگاه داده مخاطبین"</string>
+    <string name="debug_dump_database_message" msgid="406438635002392290">"شما در شرف ۱) ایجاد یک کپی از پایگاه داده‌ در حافظه داخلی هستید، این کپی حاوی همه اطلاعات مربوط به مخاطبین و همه گزارشات تماس است و همچنین می‌خواهید ۲) آنرا ایمیل کنید. به خاطر داشته باشید که به محض کپی کردن این نسخه در دستگاه یا دریافت ایمیل، آنرا حذف کنید."</string>
+    <string name="debug_dump_delete_button" msgid="7832879421132026435">"اکنون حذف شود"</string>
+    <string name="debug_dump_start_button" msgid="2837506913757600001">"شروع"</string>
+    <string name="debug_dump_email_sender_picker" msgid="3534420908672176460">"یک برنامه را برای ارسال فایل خود انتخاب کنید"</string>
+    <string name="debug_dump_email_subject" msgid="108188398416385976">"پایگاه داده مخاطبین پیوست شد"</string>
+    <string name="debug_dump_email_body" msgid="4577749800871444318">"پایگاه داده مخاطبین من همراه با تمام اطلاعات مخاطبین پیوست شده است. با دقت از آن استفاده شود."</string>
 </resources>
diff --git a/res/values-fi/strings.xml b/res/values-fi/strings.xml
index d9c341f..d4b63f3 100644
--- a/res/values-fi/strings.xml
+++ b/res/values-fi/strings.xml
@@ -28,4 +28,11 @@
     <string name="read_write_all_voicemail_label" msgid="4557216100818257560">"Kaikkien vastaajaviestien käyttäminen"</string>
     <string name="read_write_all_voicemail_description" msgid="8029809937805761356">"Antaa sovelluksen tallentaa ja hakea kaikki vastaajaviestit, jotka ovat laitteen käytettävissä."</string>
     <string name="voicemail_from_column" msgid="435732568832121444">"Vastaajaviesti henkilöltä "</string>
+    <string name="debug_dump_title" msgid="4916885724165570279">"Kopioi kontaktitietokanta"</string>
+    <string name="debug_dump_database_message" msgid="406438635002392290">"Olet aikeissa 1) tehdä sisäiseen tallennustilaan kopion tietokannasta, joka sisältää kaikki yhteystietoihin liittyvät tiedot ja puhelulokit ja 2) lähettää sen. Muista poistaa kopio heti kopioituasi sen laitteelta tai kun sähköposti on vastaanotettu."</string>
+    <string name="debug_dump_delete_button" msgid="7832879421132026435">"Poista nyt"</string>
+    <string name="debug_dump_start_button" msgid="2837506913757600001">"Aloita"</string>
+    <string name="debug_dump_email_sender_picker" msgid="3534420908672176460">"Valitse ohjelma tiedoston lähettämiseen"</string>
+    <string name="debug_dump_email_subject" msgid="108188398416385976">"Kontaktitietokanta liitteenä"</string>
+    <string name="debug_dump_email_body" msgid="4577749800871444318">"Liitteenä on kontaktitietokantani, joka sisältää kaikki kontaktitietoni. Käsiteltävä varoen."</string>
 </resources>
diff --git a/res/values-fr/strings.xml b/res/values-fr/strings.xml
index b25b763..4bc7c61 100644
--- a/res/values-fr/strings.xml
+++ b/res/values-fr/strings.xml
@@ -28,4 +28,11 @@
     <string name="read_write_all_voicemail_label" msgid="4557216100818257560">"Accéder à tous les messages vocaux"</string>
     <string name="read_write_all_voicemail_description" msgid="8029809937805761356">"Permet à l\'application de stocker et de récupérer tous les messages vocaux auxquels cet appareil peut accéder."</string>
     <string name="voicemail_from_column" msgid="435732568832121444">"Message vocal de "</string>
+    <string name="debug_dump_title" msgid="4916885724165570279">"Copier la base de données de contacts"</string>
+    <string name="debug_dump_database_message" msgid="406438635002392290">"Vous êtes sur le point de 1) faire une copie de votre base de données (qui inclut toutes les données relatives aux contacts et l\'intégralité du journal d\'appels) dans la mémoire de stockage interne, puis de 2) l\'envoyer par e-mail. N\'oubliez pas de supprimer la copie une fois qu\'elle a été dupliquée ou dès que l\'e-mail a été reçu."</string>
+    <string name="debug_dump_delete_button" msgid="7832879421132026435">"Supprimer"</string>
+    <string name="debug_dump_start_button" msgid="2837506913757600001">"Commencer"</string>
+    <string name="debug_dump_email_sender_picker" msgid="3534420908672176460">"Sélectionnez un programme pour envoyer votre fichier"</string>
+    <string name="debug_dump_email_subject" msgid="108188398416385976">"Base de données de contacts ci-jointe"</string>
+    <string name="debug_dump_email_body" msgid="4577749800871444318">"Veuillez trouver ci-joint ma base de données de contacts incluant toutes les coordonnées de mes contacts. Merci de la traiter avec précaution."</string>
 </resources>
diff --git a/res/values-hi/strings.xml b/res/values-hi/strings.xml
index f2d706a..cc50237 100644
--- a/res/values-hi/strings.xml
+++ b/res/values-hi/strings.xml
@@ -28,4 +28,11 @@
     <string name="read_write_all_voicemail_label" msgid="4557216100818257560">"सभी ध्‍वनि‍मेल पर पहुंचें"</string>
     <string name="read_write_all_voicemail_description" msgid="8029809937805761356">"एप्‍लि‍केशन को ऐसे सभी ध्‍वनि‍मेल संग्रहीत और पुनर्प्राप्त करने देता है जि‍न पर यह उपकरण पहुंच सकता है."</string>
     <string name="voicemail_from_column" msgid="435732568832121444">"इनका ध्‍वनि‍मेल: "</string>
+    <string name="debug_dump_title" msgid="4916885724165570279">"संपर्क डेटाबेस की प्रतिलिपि बनाएं"</string>
+    <string name="debug_dump_database_message" msgid="406438635002392290">"आप 1) आंतरिक संग्रहण में अपने उस डेटाबेस की प्रतिलिपि बनाने वाले हैं जिसमें सभी संपर्कों संबंधी जानकारी और सभी कॉल लॉग शामिल हैं, और 2) उसे ईमेल करने वाले हैं. जैसे ही आप उपकरण से इसकी प्रतिलिपि सफलतापूर्वक बना लें या ईमेल प्राप्त हो जाए तो प्रतिलिपि को हटाना न भूलें."</string>
+    <string name="debug_dump_delete_button" msgid="7832879421132026435">"अभी हटाएं"</string>
+    <string name="debug_dump_start_button" msgid="2837506913757600001">"प्रारंभ करें"</string>
+    <string name="debug_dump_email_sender_picker" msgid="3534420908672176460">"अपनी फ़ाइल भेजने के लिए कोई प्रोग्राम चुनें"</string>
+    <string name="debug_dump_email_subject" msgid="108188398416385976">"संपर्क Db अनुलग्न है"</string>
+    <string name="debug_dump_email_body" msgid="4577749800871444318">"मेरी समस्त संपर्क जानकारी के साथ मेरा संपर्क डेटाबेस अनुलग्न है. सावधानी से कार्य करें."</string>
 </resources>
diff --git a/res/values-hr/strings.xml b/res/values-hr/strings.xml
index 2b53781..72690b7 100644
--- a/res/values-hr/strings.xml
+++ b/res/values-hr/strings.xml
@@ -28,4 +28,11 @@
     <string name="read_write_all_voicemail_label" msgid="4557216100818257560">"Pristup svoj govornoj pošti"</string>
     <string name="read_write_all_voicemail_description" msgid="8029809937805761356">"Aplikaciji omogućuje pohranjivanje i dohvaćanje svih poruka govorne pošte kojoj ovaj uređaj može pristupati."</string>
     <string name="voicemail_from_column" msgid="435732568832121444">"Govorna pošta od "</string>
+    <string name="debug_dump_title" msgid="4916885724165570279">"Kopiranje podatkovne baze kontakata"</string>
+    <string name="debug_dump_database_message" msgid="406438635002392290">"Upravo ćete 1) napraviti kopiju svoje baze podataka koja uključuje sve podatke koji se odnose na kontakte i sve dnevnike poziva u internoj pohrani i 2) poslat ćete tu kopiju e-poštom. Ne zaboravite izbrisati kopiju čim ju uspješno kopirate s uređaja ili čim primite e-poruku."</string>
+    <string name="debug_dump_delete_button" msgid="7832879421132026435">"Izbriši sada"</string>
+    <string name="debug_dump_start_button" msgid="2837506913757600001">"Kreni"</string>
+    <string name="debug_dump_email_sender_picker" msgid="3534420908672176460">"Odaberite program za slanje datoteke"</string>
+    <string name="debug_dump_email_subject" msgid="108188398416385976">"U prilogu je podatkovna baza"</string>
+    <string name="debug_dump_email_body" msgid="4577749800871444318">"U prilogu je moja podatkovna baza kontakata sa svim mojim kontaktnim informacijama. Oprezno rukujte s njom."</string>
 </resources>
diff --git a/res/values-hu/strings.xml b/res/values-hu/strings.xml
index 7f477e5..c510846 100644
--- a/res/values-hu/strings.xml
+++ b/res/values-hu/strings.xml
@@ -28,4 +28,11 @@
     <string name="read_write_all_voicemail_label" msgid="4557216100818257560">"Hozzáférés az összes hangüzenethez"</string>
     <string name="read_write_all_voicemail_description" msgid="8029809937805761356">"Lehetővé teszi, hogy az alkalmazás tárolja és lekérje azokat a hangüzeneteket, amelyekhez ez a készülék hozzáférhet."</string>
     <string name="voicemail_from_column" msgid="435732568832121444">"Hangüzenet tőle: "</string>
+    <string name="debug_dump_title" msgid="4916885724165570279">"Névjegyadatbázis másolása"</string>
+    <string name="debug_dump_database_message" msgid="406438635002392290">"Ön arra készül, hogy 1) másolatot készítsen a belső tárhelyre az adatbázisról, amely magában foglalja az összes névjegyet és minden kapcsolódó adatot, valamint a hívásnaplót, illetve hogy 2) e-mailben elküldje azt. Ne feledje azonnal törölni a másolatot, amint sikeresen átmásolta a készülékről, vagy amint megkapta az e-mailt."</string>
+    <string name="debug_dump_delete_button" msgid="7832879421132026435">"Törlés most"</string>
+    <string name="debug_dump_start_button" msgid="2837506913757600001">"Indítás"</string>
+    <string name="debug_dump_email_sender_picker" msgid="3534420908672176460">"Válasszon programot a fájl elküldéséhez."</string>
+    <string name="debug_dump_email_subject" msgid="108188398416385976">"Névjegyadatbázis csatolva."</string>
+    <string name="debug_dump_email_body" msgid="4577749800871444318">"Csatolva a névjegyadatbázisom, amely a névjegyeim összes adatát tartalmazza. Óvatosan kezeld."</string>
 </resources>
diff --git a/res/values-in/strings.xml b/res/values-in/strings.xml
index f958b12..1d1f176 100644
--- a/res/values-in/strings.xml
+++ b/res/values-in/strings.xml
@@ -17,15 +17,22 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="sharedUserLabel" msgid="8024311725474286801">"Apl Inti Android"</string>
-    <string name="app_label" msgid="3389954322874982620">"Penyimpanan Kenalan"</string>
-    <string name="provider_label" msgid="6012150850819899907">"Kenalan"</string>
-    <string name="upgrade_msg" msgid="8640807392794309950">"Meningkatkan versi basis data kenalan."</string>
-    <string name="upgrade_out_of_memory_notification_ticker" msgid="7638747231223520477">"Peningkatan versi kenalan memerlukan lebih banyak memori."</string>
-    <string name="upgrade_out_of_memory_notification_title" msgid="8888171924684998531">"Meningkatkan versi penyimpanan untuk kenalan"</string>
+    <string name="app_label" msgid="3389954322874982620">"Penyimpanan Kontak"</string>
+    <string name="provider_label" msgid="6012150850819899907">"Kontak"</string>
+    <string name="upgrade_msg" msgid="8640807392794309950">"Meningkatkan versi basis kontak."</string>
+    <string name="upgrade_out_of_memory_notification_ticker" msgid="7638747231223520477">"Peningkatan versi kontak memerlukan lebih banyak memori."</string>
+    <string name="upgrade_out_of_memory_notification_title" msgid="8888171924684998531">"Meningkatkan versi penyimpanan untuk kontak"</string>
     <string name="upgrade_out_of_memory_notification_text" msgid="8438179450336437626">"Sentuh untuk menyelesaikan peningkatan versi."</string>
-    <string name="default_directory" msgid="93961630309570294">"Kenalan"</string>
+    <string name="default_directory" msgid="93961630309570294">"Kontak"</string>
     <string name="local_invisible_directory" msgid="705244318477396120">"Lainnya"</string>
     <string name="read_write_all_voicemail_label" msgid="4557216100818257560">"Akses semua pesan suara"</string>
     <string name="read_write_all_voicemail_description" msgid="8029809937805761356">"Mengizinkan apl menyimpan dan mengambil semua pesan suara yang dapat diakses oleh perangkat ini."</string>
     <string name="voicemail_from_column" msgid="435732568832121444">"Kotak pesan dari "</string>
+    <string name="debug_dump_title" msgid="4916885724165570279">"Salin basis data kontak"</string>
+    <string name="debug_dump_database_message" msgid="406438635002392290">"Anda akan 1) membuat salinan basis data yang mencakup semua informasi terkait kontak dan semua log panggilan ke penyimpanan internal, dan 2) mengirimkannya sebagai email. Ingat untuk menghapus salinan secepatnya setelah Anda selesai menyalinnya dari perangkat atau saat email telah diterima."</string>
+    <string name="debug_dump_delete_button" msgid="7832879421132026435">"Hapus sekarang"</string>
+    <string name="debug_dump_start_button" msgid="2837506913757600001">"Mulai"</string>
+    <string name="debug_dump_email_sender_picker" msgid="3534420908672176460">"Pilih program untuk mengirim file Anda"</string>
+    <string name="debug_dump_email_subject" msgid="108188398416385976">"Db Kontak terlampir"</string>
+    <string name="debug_dump_email_body" msgid="4577749800871444318">"Terlampir basis data kontak saya dengan semua informasi kontak. Harap tangani dengan benar."</string>
 </resources>
diff --git a/res/values-it/strings.xml b/res/values-it/strings.xml
index dfea4d4..ff80d20 100644
--- a/res/values-it/strings.xml
+++ b/res/values-it/strings.xml
@@ -28,4 +28,11 @@
     <string name="read_write_all_voicemail_label" msgid="4557216100818257560">"Accesso a tutti i messaggi vocali"</string>
     <string name="read_write_all_voicemail_description" msgid="8029809937805761356">"Consente all\'applicazione di archiviare recuperare tutti i messaggi vocali a cui questo dispositivo può accedere."</string>
     <string name="voicemail_from_column" msgid="435732568832121444">"Messaggio vocale da "</string>
+    <string name="debug_dump_title" msgid="4916885724165570279">"Copia database di contatti"</string>
+    <string name="debug_dump_database_message" msgid="406438635002392290">"Stai per 1) creare una copia del tuo database che include tutte le informazioni di contatto e tutti i registri chiamate nella memoria interna e 2) inviarla tramite email. Ricorda di eliminare la copia non appena è stata correttamente copiata dal dispositivo o non appena ricevi l\'email."</string>
+    <string name="debug_dump_delete_button" msgid="7832879421132026435">"Elimina adesso"</string>
+    <string name="debug_dump_start_button" msgid="2837506913757600001">"Inizia"</string>
+    <string name="debug_dump_email_sender_picker" msgid="3534420908672176460">"Scegli un programma per inviare il file"</string>
+    <string name="debug_dump_email_subject" msgid="108188398416385976">"Database di contatti allegato"</string>
+    <string name="debug_dump_email_body" msgid="4577749800871444318">"In allegato è presente il mio database di contatti contenente tutte le informazioni sui miei contatti. Trattare con cura."</string>
 </resources>
diff --git a/res/values-iw/strings.xml b/res/values-iw/strings.xml
index c87a2f9..7a0eb82 100644
--- a/res/values-iw/strings.xml
+++ b/res/values-iw/strings.xml
@@ -28,4 +28,11 @@
     <string name="read_write_all_voicemail_label" msgid="4557216100818257560">"גישה לכל הודעות הדואר הקולי"</string>
     <string name="read_write_all_voicemail_description" msgid="8029809937805761356">"מאפשר ליישום לאחסן ולאחזר את כל הודעות הדואר הקולי שלמכשיר זה יש גישה אליהן."</string>
     <string name="voicemail_from_column" msgid="435732568832121444">"הודעה קולית מאת "</string>
+    <string name="debug_dump_title" msgid="4916885724165570279">"העתקת מסד נתוני אנשי קשר"</string>
+    <string name="debug_dump_database_message" msgid="406438635002392290">"אתה עומד 1) ליצור עותק באחסון הפנימי של מסד הנתונים שכולל את כל המידע הקשור לאנשי הקשר וכל יומני השיחות, 2) לשלוח אותו בדוא\"ל. זכור למחוק את העותק מיד לאחר שתעתיק אותו בהצלחה מהמכשיר או כשהודעת הדוא\"ל מתקבלת."</string>
+    <string name="debug_dump_delete_button" msgid="7832879421132026435">"מחק עכשיו"</string>
+    <string name="debug_dump_start_button" msgid="2837506913757600001">"התחל"</string>
+    <string name="debug_dump_email_sender_picker" msgid="3534420908672176460">"בחר תוכנית לשליחת הקובץ"</string>
+    <string name="debug_dump_email_subject" msgid="108188398416385976">"מסד נתוני אנשי קשר מצורף"</string>
+    <string name="debug_dump_email_body" msgid="4577749800871444318">"מצורף מסד הנתונים של אנשי הקשר שלי עם כל פרטי אנשי הקשר. שמור עליו היטב."</string>
 </resources>
diff --git a/res/values-ja/strings.xml b/res/values-ja/strings.xml
index 74044d8..a48ca04 100644
--- a/res/values-ja/strings.xml
+++ b/res/values-ja/strings.xml
@@ -28,4 +28,11 @@
     <string name="read_write_all_voicemail_label" msgid="4557216100818257560">"すべてのボイスメールにアクセス"</string>
     <string name="read_write_all_voicemail_description" msgid="8029809937805761356">"この端末でアクセス可能なすべてのボイスメールを保存、取得することをアプリに許可します。"</string>
     <string name="voicemail_from_column" msgid="435732568832121444">"受信ボイスメール: "</string>
+    <string name="debug_dump_title" msgid="4916885724165570279">"連絡先データベースをコピー"</string>
+    <string name="debug_dump_database_message" msgid="406438635002392290">"1)すべての連絡先関連情報とすべての通話履歴を格納したデータベースを内部ストレージにコピーし、2)メールで送信しようとしています。端末からのコピーが完了した時点またはメールが受信された時点ですぐにコピーを削除してください。"</string>
+    <string name="debug_dump_delete_button" msgid="7832879421132026435">"今すぐ削除"</string>
+    <string name="debug_dump_start_button" msgid="2837506913757600001">"開始"</string>
+    <string name="debug_dump_email_sender_picker" msgid="3534420908672176460">"ファイルを送信するプログラムを選択"</string>
+    <string name="debug_dump_email_subject" msgid="108188398416385976">"連絡先データベースを添付"</string>
+    <string name="debug_dump_email_body" msgid="4577749800871444318">"添付されているのは、私のすべての連絡先情報を含む連絡先データベースです。取り扱いにご注意ください。"</string>
 </resources>
diff --git a/res/values-ko/strings.xml b/res/values-ko/strings.xml
index 041cc0b..fd4abf8 100644
--- a/res/values-ko/strings.xml
+++ b/res/values-ko/strings.xml
@@ -28,4 +28,11 @@
     <string name="read_write_all_voicemail_label" msgid="4557216100818257560">"모든 음성사서함에 액세스"</string>
     <string name="read_write_all_voicemail_description" msgid="8029809937805761356">"앱이 이 기기에서 액세스할 수 있는 모든 음성사서함을 저장하고 검색하도록 허용합니다."</string>
     <string name="voicemail_from_column" msgid="435732568832121444">"음성사서함 발신자 "</string>
+    <string name="debug_dump_title" msgid="4916885724165570279">"주소록 데이터베이스 복사"</string>
+    <string name="debug_dump_database_message" msgid="406438635002392290">"내부 저장소에 모든 주소록 관련 정보와 통화 기록을 포함하는 데이터베이스의 1) 사본을 만들고 2) 이메일로 보내려고 합니다. 기기 이외의 장소에 사본을 만들거나 사본의 이메일 수신이 완료된 후에는 해당 사본을 즉시 삭제하시기 바랍니다."</string>
+    <string name="debug_dump_delete_button" msgid="7832879421132026435">"지금 삭제"</string>
+    <string name="debug_dump_start_button" msgid="2837506913757600001">"시작"</string>
+    <string name="debug_dump_email_sender_picker" msgid="3534420908672176460">"파일을 전송할 프로그램을 선택하세요."</string>
+    <string name="debug_dump_email_subject" msgid="108188398416385976">"주소록 DB 첨부됨"</string>
+    <string name="debug_dump_email_body" msgid="4577749800871444318">"모든 주소록 정보가 포함된 주소록 데이터베이스가 첨부되어 있습니다. 신중하게 처리해 주세요."</string>
 </resources>
diff --git a/res/values-lt/strings.xml b/res/values-lt/strings.xml
index b873d1f..16d3a0c 100644
--- a/res/values-lt/strings.xml
+++ b/res/values-lt/strings.xml
@@ -28,4 +28,11 @@
     <string name="read_write_all_voicemail_label" msgid="4557216100818257560">"Pasiekti visus balso pašto pranešimus"</string>
     <string name="read_write_all_voicemail_description" msgid="8029809937805761356">"Leidžiama programai saugoti ir nuskaityti visus balso pašto pranešimus, kuriuos gali pasiekti šis įrenginys."</string>
     <string name="voicemail_from_column" msgid="435732568832121444">"Balso pašto pranešimas nuo "</string>
+    <string name="debug_dump_title" msgid="4916885724165570279">"Kopijuoti kontaktų duomenis"</string>
+    <string name="debug_dump_database_message" msgid="406438635002392290">"Ketinate 1. sukurti duomenų, į kuriuos įtraukta visa su kontaktais susijusi informacija ir visi skambučių žurnalai, kopiją vidinėje atmintyje ir 2. išsiųsti ją el. paštu. Nepamirškite ištrinti kopijos, kai ją sėkmingai nukopijuosite iš įrenginio ar gausite el. laišką."</string>
+    <string name="debug_dump_delete_button" msgid="7832879421132026435">"Ištrinti dabar"</string>
+    <string name="debug_dump_start_button" msgid="2837506913757600001">"Pradėti"</string>
+    <string name="debug_dump_email_sender_picker" msgid="3534420908672176460">"Pasirinkite programą, kad išsiųstumėte failą"</string>
+    <string name="debug_dump_email_subject" msgid="108188398416385976">"Pridedami kontaktų duomenys"</string>
+    <string name="debug_dump_email_body" msgid="4577749800871444318">"Pridedami mano kontaktų duomenys su visa kontaktų informacija. Elkitės atsargiai."</string>
 </resources>
diff --git a/res/values-lv/strings.xml b/res/values-lv/strings.xml
index fa1e89e..25af3c9 100644
--- a/res/values-lv/strings.xml
+++ b/res/values-lv/strings.xml
@@ -28,4 +28,11 @@
     <string name="read_write_all_voicemail_label" msgid="4557216100818257560">"Piekļuve visiem balss pasta ziņojumiem"</string>
     <string name="read_write_all_voicemail_description" msgid="8029809937805761356">"Ļauj lietotnei glabāt un izgūt visus balss pasta ziņojumus, kuriem var piekļūt šajā ierīcē."</string>
     <string name="voicemail_from_column" msgid="435732568832121444">"Balss pasta ziņojums no "</string>
+    <string name="debug_dump_title" msgid="4916885724165570279">"Kontaktpersonu datu bāzes kopēšana"</string>
+    <string name="debug_dump_database_message" msgid="406438635002392290">"Jūs gatavojaties 1) iekšējā atmiņā izveidot savas datu bāzes kopiju, ietverot visu kontaktpersonu informāciju un visu zvanu žurnālu, un 2) nosūtīt to pa e-pastu. Dzēsiet kopiju, tiklīdz tā būs veiksmīgi kopēta no ierīces vai tiks saņemts attiecīgais e-pasta ziņojums."</string>
+    <string name="debug_dump_delete_button" msgid="7832879421132026435">"Dzēst tūlīt"</string>
+    <string name="debug_dump_start_button" msgid="2837506913757600001">"Sākt"</string>
+    <string name="debug_dump_email_sender_picker" msgid="3534420908672176460">"Izvēlieties programmu, kuru izmantot faila nosūtīšanai"</string>
+    <string name="debug_dump_email_subject" msgid="108188398416385976">"Pievienota kontaktpersonu datu bāze"</string>
+    <string name="debug_dump_email_body" msgid="4577749800871444318">"Pielikumā ir pievienota mana kontaktpersonu datu bāze ar informāciju par visām manām kontaktpersonām. Rīkojieties uzmanīgi."</string>
 </resources>
diff --git a/res/values-ms/strings.xml b/res/values-ms/strings.xml
index 653a8d9..b4bad94 100644
--- a/res/values-ms/strings.xml
+++ b/res/values-ms/strings.xml
@@ -28,4 +28,11 @@
     <string name="read_write_all_voicemail_label" msgid="4557216100818257560">"Akses semua mel suara"</string>
     <string name="read_write_all_voicemail_description" msgid="8029809937805761356">"Membenarkan aplikasi menyimpan dan mengambil semula semua mel suara yang boleh diakses peranti ini."</string>
     <string name="voicemail_from_column" msgid="435732568832121444">"Mel suara daripada "</string>
+    <string name="debug_dump_title" msgid="4916885724165570279">"Salin pangkalan data kenalan"</string>
+    <string name="debug_dump_database_message" msgid="406438635002392290">"Anda akan 1) membuat salinan pangkalan data anda yang termasuk semua maklumat berkaitan kenalan dan semua log panggilan ke storan dalaman dan 2) hantar melalui e-mel. Jangan lupa untuk memadam salinan ini sebaik sahaja anda telah berjaya menyalin daripada peranti atau apabila e-mel diterima."</string>
+    <string name="debug_dump_delete_button" msgid="7832879421132026435">"Padamkan sekarang"</string>
+    <string name="debug_dump_start_button" msgid="2837506913757600001">"Mula"</string>
+    <string name="debug_dump_email_sender_picker" msgid="3534420908672176460">"Pilih program untuk menghantar fail anda"</string>
+    <string name="debug_dump_email_subject" msgid="108188398416385976">"Pangkalan Data Kenalan dilampirkan"</string>
+    <string name="debug_dump_email_body" msgid="4577749800871444318">"Dilampirkan pangkalan data kenalan saya yang mengandungi semua maklumat kenalan. Sila gunakan dengan cermat."</string>
 </resources>
diff --git a/res/values-nb/strings.xml b/res/values-nb/strings.xml
index 5775854..909e54e 100644
--- a/res/values-nb/strings.xml
+++ b/res/values-nb/strings.xml
@@ -28,4 +28,11 @@
     <string name="read_write_all_voicemail_label" msgid="4557216100818257560">"Tilgang til alle talemeldinger"</string>
     <string name="read_write_all_voicemail_description" msgid="8029809937805761356">"Lar appen lagre og hente alle talepostmeldinger som denne enheten har tilgang til."</string>
     <string name="voicemail_from_column" msgid="435732568832121444">"Talemelding fra "</string>
+    <string name="debug_dump_title" msgid="4916885724165570279">"Kopiér kontaktdatabasen"</string>
+    <string name="debug_dump_database_message" msgid="406438635002392290">"Du er i ferd med å 1) lage en kopi av databasen som omfatter all kontaktrelatert informasjon og alle anropslogger til den interne lagringsplassen, og 2) sende kopien med e-post. Husk å slette kopien så snart du har kopiert den fra enheten eller når e-posten er mottatt."</string>
+    <string name="debug_dump_delete_button" msgid="7832879421132026435">"Slett nå"</string>
+    <string name="debug_dump_start_button" msgid="2837506913757600001">"Start"</string>
+    <string name="debug_dump_email_sender_picker" msgid="3534420908672176460">"Velg et program for å sende filen"</string>
+    <string name="debug_dump_email_subject" msgid="108188398416385976">"Kontaktdatabasen er vedlagt"</string>
+    <string name="debug_dump_email_body" msgid="4577749800871444318">"Vedlagt er kontaktdatabasen min med informasjon om alle kontaktene mine. Håndteres med varsomhet."</string>
 </resources>
diff --git a/res/values-nl/strings.xml b/res/values-nl/strings.xml
index 1a09288..da31aa9 100644
--- a/res/values-nl/strings.xml
+++ b/res/values-nl/strings.xml
@@ -28,4 +28,11 @@
     <string name="read_write_all_voicemail_label" msgid="4557216100818257560">"Toegang tot alle voicemails"</string>
     <string name="read_write_all_voicemail_description" msgid="8029809937805761356">"Toestaan dat de app alle voicemails opslaat en ophaalt waartoe dit apparaat toegang heeft."</string>
     <string name="voicemail_from_column" msgid="435732568832121444">"Voicemail van "</string>
+    <string name="debug_dump_title" msgid="4916885724165570279">"Contactendatabase kopiëren"</string>
+    <string name="debug_dump_database_message" msgid="406438635002392290">"U staat op het punt 1) een kopie van uw database met alle contactgegevens en oproeplogboeken te maken in de interne opslag, en 2) deze te e-mailen. Verwijder de kopie zodra u deze van het apparaat heeft gekopieerd of de e-mail is ontvangen."</string>
+    <string name="debug_dump_delete_button" msgid="7832879421132026435">"Nu verwijderen"</string>
+    <string name="debug_dump_start_button" msgid="2837506913757600001">"Starten"</string>
+    <string name="debug_dump_email_sender_picker" msgid="3534420908672176460">"Selecteer een programma om uw bestand te verzenden"</string>
+    <string name="debug_dump_email_subject" msgid="108188398416385976">"Contactendatabase bijgevoegd"</string>
+    <string name="debug_dump_email_body" msgid="4577749800871444318">"Bijgevoegd is mijn contactendatabase met al mijn contactgegevens. Ga hier zorgvuldig mee om."</string>
 </resources>
diff --git a/res/values-pl/strings.xml b/res/values-pl/strings.xml
index b598221..2ba3f45 100644
--- a/res/values-pl/strings.xml
+++ b/res/values-pl/strings.xml
@@ -28,4 +28,11 @@
     <string name="read_write_all_voicemail_label" msgid="4557216100818257560">"Dostęp do wszystkich wiadomości głosowych"</string>
     <string name="read_write_all_voicemail_description" msgid="8029809937805761356">"Zezwala aplikacji na przechowywanie i pobieranie wszystkich wiadomości głosowych, do których ma dostęp to urządzenie."</string>
     <string name="voicemail_from_column" msgid="435732568832121444">"Poczta głosowa od "</string>
+    <string name="debug_dump_title" msgid="4916885724165570279">"Kopiuj bazę danych kontaktów"</string>
+    <string name="debug_dump_database_message" msgid="406438635002392290">"Zamierzasz: 1) utworzyć w pamięci wewnętrznej kopię bazy danych ze wszystkimi informacjami o kontaktach i dziennikiem połączeń, 2) wysłać ją e-mailem. Pamiętaj, by usunąć kopię zaraz po zapisaniu jej na innym nośniku lub odebraniu e-maila."</string>
+    <string name="debug_dump_delete_button" msgid="7832879421132026435">"Usuń teraz"</string>
+    <string name="debug_dump_start_button" msgid="2837506913757600001">"Rozpocznij"</string>
+    <string name="debug_dump_email_sender_picker" msgid="3534420908672176460">"Wybierz program, którego użyjesz do wysłania pliku"</string>
+    <string name="debug_dump_email_subject" msgid="108188398416385976">"W załączniku baza danych kontaktów"</string>
+    <string name="debug_dump_email_body" msgid="4577749800871444318">"W załączniku przesyłam moją bazę danych kontaktów, która zawiera wszystkie informacje o moich kontaktach. Proszę zachować ostrożność."</string>
 </resources>
diff --git a/res/values-pt-rPT/strings.xml b/res/values-pt-rPT/strings.xml
index acdb0d2..ae32ab7 100644
--- a/res/values-pt-rPT/strings.xml
+++ b/res/values-pt-rPT/strings.xml
@@ -28,4 +28,11 @@
     <string name="read_write_all_voicemail_label" msgid="4557216100818257560">"Acesso a todas as mensagens de correio de voz"</string>
     <string name="read_write_all_voicemail_description" msgid="8029809937805761356">"Permite à aplicação guardar e recuperar todas as mensagens de correio de voz a que este aparelho pode aceder."</string>
     <string name="voicemail_from_column" msgid="435732568832121444">"Correio de voz de "</string>
+    <string name="debug_dump_title" msgid="4916885724165570279">"Copiar base de dados de contactos"</string>
+    <string name="debug_dump_database_message" msgid="406438635002392290">"Está prestes a 1) fazer uma cópia da sua base de dados que inclui todas as informações relativas aos contactos e todo o registo de chamadas para armazenamento interno, e a 2) enviá-los por email. Não se esqueça de eliminar a cópia logo que a tenha copiado com êxito para fora do dispositivo ou que o email tenha sido recebido."</string>
+    <string name="debug_dump_delete_button" msgid="7832879421132026435">"Eliminar agora"</string>
+    <string name="debug_dump_start_button" msgid="2837506913757600001">"Iniciar"</string>
+    <string name="debug_dump_email_sender_picker" msgid="3534420908672176460">"Escolha um programa para enviar o ficheiro"</string>
+    <string name="debug_dump_email_subject" msgid="108188398416385976">"BD de Contactos em anexo"</string>
+    <string name="debug_dump_email_body" msgid="4577749800871444318">"Em anexo segue a minha base de dados de contactos com todas as informações dos meus contactos. Utilize-a com cuidado."</string>
 </resources>
diff --git a/res/values-pt/strings.xml b/res/values-pt/strings.xml
index 5d471a1..31e8909 100644
--- a/res/values-pt/strings.xml
+++ b/res/values-pt/strings.xml
@@ -28,4 +28,11 @@
     <string name="read_write_all_voicemail_label" msgid="4557216100818257560">"Acessar todas as mensagens de voz"</string>
     <string name="read_write_all_voicemail_description" msgid="8029809937805761356">"Permite que o aplicativo armazene e recupere todas as mensagens de voz que esse dispositivo acessa."</string>
     <string name="voicemail_from_column" msgid="435732568832121444">"Correio de voz de "</string>
+    <string name="debug_dump_title" msgid="4916885724165570279">"Copiar banco de dados de contatos"</string>
+    <string name="debug_dump_database_message" msgid="406438635002392290">"Você está prestes a 1) fazer uma cópia de seu banco de dados no armazenamento interno, com todas as informações relacionadas aos contatos e todo o histórico de chamadas e 2) enviar essa cópia por e-mail. Lembre-se de excluir a cópia, logo que você a tiver copiado do dispositivo ou que o e-mail for recebido."</string>
+    <string name="debug_dump_delete_button" msgid="7832879421132026435">"Excluir agora"</string>
+    <string name="debug_dump_start_button" msgid="2837506913757600001">"Iniciar"</string>
+    <string name="debug_dump_email_sender_picker" msgid="3534420908672176460">"Escolha um programa para enviar o arquivo"</string>
+    <string name="debug_dump_email_subject" msgid="108188398416385976">"BD de contatos anexado"</string>
+    <string name="debug_dump_email_body" msgid="4577749800871444318">"Meu banco de dados de contatos está anexado, Lá estão todas as informações de meus contatos. Use-o com cuidado."</string>
 </resources>
diff --git a/res/values-rm/strings.xml b/res/values-rm/strings.xml
index b0e2898..574d09a 100644
--- a/res/values-rm/strings.xml
+++ b/res/values-rm/strings.xml
@@ -36,4 +36,18 @@
     <skip />
     <!-- no translation found for voicemail_from_column (435732568832121444) -->
     <skip />
+    <!-- no translation found for debug_dump_title (4916885724165570279) -->
+    <skip />
+    <!-- no translation found for debug_dump_database_message (406438635002392290) -->
+    <skip />
+    <!-- no translation found for debug_dump_delete_button (7832879421132026435) -->
+    <skip />
+    <!-- no translation found for debug_dump_start_button (2837506913757600001) -->
+    <skip />
+    <!-- no translation found for debug_dump_email_sender_picker (3534420908672176460) -->
+    <skip />
+    <!-- no translation found for debug_dump_email_subject (108188398416385976) -->
+    <skip />
+    <!-- no translation found for debug_dump_email_body (4577749800871444318) -->
+    <skip />
 </resources>
diff --git a/res/values-ro/strings.xml b/res/values-ro/strings.xml
index 110f166..5fafb93 100644
--- a/res/values-ro/strings.xml
+++ b/res/values-ro/strings.xml
@@ -28,4 +28,11 @@
     <string name="read_write_all_voicemail_label" msgid="4557216100818257560">"Accesaţi toate mesajele vocale"</string>
     <string name="read_write_all_voicemail_description" msgid="8029809937805761356">"Permite aplicaţiei să stocheze şi să preia toate mesajele vocale pe care acest dispozitiv le poate accesa."</string>
     <string name="voicemail_from_column" msgid="435732568832121444">"Mesaj vocal de la "</string>
+    <string name="debug_dump_title" msgid="4916885724165570279">"Copiaţi baza de date a agendei"</string>
+    <string name="debug_dump_database_message" msgid="406438635002392290">"Sunteţi pe cale 1) să faceţi o copie, pe stocarea internă, a bazei dvs. de date care include toate informaţiile referitoare la agendă şi întregul jurnal de apeluri şi 2) să trimiteţi această copie prin e-mail. Nu uitaţi să ştergeţi această copie după ce aţi copiat-o de pe dispozitiv sau după ce a fost primit e-mailul."</string>
+    <string name="debug_dump_delete_button" msgid="7832879421132026435">"Ştergeţi acum"</string>
+    <string name="debug_dump_start_button" msgid="2837506913757600001">"Porniţi"</string>
+    <string name="debug_dump_email_sender_picker" msgid="3534420908672176460">"Alegeţi un program pentru a trimite fişierul"</string>
+    <string name="debug_dump_email_subject" msgid="108188398416385976">"Atașată baza de date a agendei"</string>
+    <string name="debug_dump_email_body" msgid="4577749800871444318">"Vă trimit ataşată baza de date cu toate informaţiile din agenda mea. Vă rog să o gestionaţi cu atenţie."</string>
 </resources>
diff --git a/res/values-ru/strings.xml b/res/values-ru/strings.xml
index 45b28a0..87a1798 100644
--- a/res/values-ru/strings.xml
+++ b/res/values-ru/strings.xml
@@ -28,4 +28,11 @@
     <string name="read_write_all_voicemail_label" msgid="4557216100818257560">"Доступ к голосовым сообщениям"</string>
     <string name="read_write_all_voicemail_description" msgid="8029809937805761356">"Приложение сможет сохранять и загружать все голосовые сообщения, к которым есть доступ на этом устройстве."</string>
     <string name="voicemail_from_column" msgid="435732568832121444">"Голосовое сообщение от абонента "</string>
+    <string name="debug_dump_title" msgid="4916885724165570279">"Копирование базы данных контактов"</string>
+    <string name="debug_dump_database_message" msgid="406438635002392290">"Вы собираетесь скопировать базу данных ваших контактов и списка вызовов во внутреннюю память устройства. При этом копия базы будет отправлена по электронной почте. Обязательно удалите эти данные с устройства после того, как они будут скопированы с него либо получены в письме."</string>
+    <string name="debug_dump_delete_button" msgid="7832879421132026435">"Удалить"</string>
+    <string name="debug_dump_start_button" msgid="2837506913757600001">"Копировать"</string>
+    <string name="debug_dump_email_sender_picker" msgid="3534420908672176460">"Выберите приложение для отправки файла"</string>
+    <string name="debug_dump_email_subject" msgid="108188398416385976">"К сообщению добавлена база данных контактов"</string>
+    <string name="debug_dump_email_body" msgid="4577749800871444318">"Будьте внимательны: в приложенном файле находится база данных с моими контактами."</string>
 </resources>
diff --git a/res/values-sk/strings.xml b/res/values-sk/strings.xml
index e8d82c6..2f8ae20 100644
--- a/res/values-sk/strings.xml
+++ b/res/values-sk/strings.xml
@@ -28,4 +28,11 @@
     <string name="read_write_all_voicemail_label" msgid="4557216100818257560">"Prístup ku všetkým hlasovým správam"</string>
     <string name="read_write_all_voicemail_description" msgid="8029809937805761356">"Umožňuje aplikácii uchovávať a načítavať všetky hlasové správy, ku ktorým má zariadenie prístup."</string>
     <string name="voicemail_from_column" msgid="435732568832121444">"Hlasová správa od "</string>
+    <string name="debug_dump_title" msgid="4916885724165570279">"Kopírovanie databázy kontaktov"</string>
+    <string name="debug_dump_database_message" msgid="406438635002392290">"Práve sa chystáte 1) vytvoriť v internom ukladacom priestore kópiu svojej databázy, ktorá obsahuje všetky informácie týkajúce sa kontaktov a všetky hovory, a 2) poslať túto databázu e-mailom. Nezabudnite odstrániť kópiu hneď po úspešnom skopírovaní do zariadenia alebo doručení e-mailu."</string>
+    <string name="debug_dump_delete_button" msgid="7832879421132026435">"Odstrániť"</string>
+    <string name="debug_dump_start_button" msgid="2837506913757600001">"Spustiť"</string>
+    <string name="debug_dump_email_sender_picker" msgid="3534420908672176460">"Vyberte program na odoslanie súboru"</string>
+    <string name="debug_dump_email_subject" msgid="108188398416385976">"Databáza kontaktov v prílohe"</string>
+    <string name="debug_dump_email_body" msgid="4577749800871444318">"V prílohe je databáza s informáciami o všetkých mojich kontaktoch. Zaobchádzajte s ňou opatrne."</string>
 </resources>
diff --git a/res/values-sl/strings.xml b/res/values-sl/strings.xml
index af43531..1753119 100644
--- a/res/values-sl/strings.xml
+++ b/res/values-sl/strings.xml
@@ -28,4 +28,11 @@
     <string name="read_write_all_voicemail_label" msgid="4557216100818257560">"Dostop do glasovne pošte"</string>
     <string name="read_write_all_voicemail_description" msgid="8029809937805761356">"Programu dovoli shranjevanje in prenos sporočil v odzivniku, do katerih lahko dostopa ta naprava."</string>
     <string name="voicemail_from_column" msgid="435732568832121444">"Govorna pošta s številke "</string>
+    <string name="debug_dump_title" msgid="4916885724165570279">"Kopiraj zbirko podatkov o stikih"</string>
+    <string name="debug_dump_database_message" msgid="406438635002392290">"V naslednjem koraku boste 1) naredili kopijo zbirke podatkov, ki vključuje vse informacije o stikih in celoten dnevnik klicev, v notranji pomnilnik ter 2) jo poslali. Ne pozabite izbrisati kopije iz naprave, ko jo boste uspešno kopirali oziroma ko jo boste prejeli po e-pošti."</string>
+    <string name="debug_dump_delete_button" msgid="7832879421132026435">"Izbriši zdaj"</string>
+    <string name="debug_dump_start_button" msgid="2837506913757600001">"Začni"</string>
+    <string name="debug_dump_email_sender_picker" msgid="3534420908672176460">"Izberite program za pošiljanje datoteke"</string>
+    <string name="debug_dump_email_subject" msgid="108188398416385976">"Priložena zbirka podatkov o stikih"</string>
+    <string name="debug_dump_email_body" msgid="4577749800871444318">"Priložena je zbirka podatkov z vsemi informacijami o stikih. Z e-pošto ravnajte previdno."</string>
 </resources>
diff --git a/res/values-sr/strings.xml b/res/values-sr/strings.xml
index c8888db..e7314bc 100644
--- a/res/values-sr/strings.xml
+++ b/res/values-sr/strings.xml
@@ -28,4 +28,11 @@
     <string name="read_write_all_voicemail_label" msgid="4557216100818257560">"Приступ свим порукама говорне поште"</string>
     <string name="read_write_all_voicemail_description" msgid="8029809937805761356">"Дозвољава апликацији да складишти и преузима све поруке говорне поште којима овај уређај може да приступи."</string>
     <string name="voicemail_from_column" msgid="435732568832121444">"Говорна пошта од "</string>
+    <string name="debug_dump_title" msgid="4916885724165570279">"Копирање базе података са контактима"</string>
+    <string name="debug_dump_database_message" msgid="406438635002392290">"Управо ћете 1) направити копију базе података која садржи све информације у вези са контактима и целокупну евиденцију позива у интерној меморији и 2) послати је е-поштом. Не заборавите да избришете копију чим је будете копирали са уређаја или чим будете примили поруку е-поште."</string>
+    <string name="debug_dump_delete_button" msgid="7832879421132026435">"Избриши одмах"</string>
+    <string name="debug_dump_start_button" msgid="2837506913757600001">"Покрени"</string>
+    <string name="debug_dump_email_sender_picker" msgid="3534420908672176460">"Изаберите програм за слање датотеке"</string>
+    <string name="debug_dump_email_subject" msgid="108188398416385976">"База података са контактима је у прилогу"</string>
+    <string name="debug_dump_email_body" msgid="4577749800871444318">"У прилогу је база података са мојим контактима и свим информацијама о њима. Рукујте пажљиво."</string>
 </resources>
diff --git a/res/values-sv/strings.xml b/res/values-sv/strings.xml
index 06ecf9a..4b2d2e4 100644
--- a/res/values-sv/strings.xml
+++ b/res/values-sv/strings.xml
@@ -28,4 +28,11 @@
     <string name="read_write_all_voicemail_label" msgid="4557216100818257560">"Åtkomst till alla röstmeddelanden"</string>
     <string name="read_write_all_voicemail_description" msgid="8029809937805761356">"Tillåter att appen sparar och hämtar alla röstmeddelanden som enheten har åtkomst till."</string>
     <string name="voicemail_from_column" msgid="435732568832121444">"Röstmeddelande från "</string>
+    <string name="debug_dump_title" msgid="4916885724165570279">"Kopiera kontaktdatabas"</string>
+    <string name="debug_dump_database_message" msgid="406438635002392290">"Du kommer att 1) kopiera din databas, inklusive alla kontaktuppgifter och samtalsloggar till det interna lagringsutrymmet, och 2) skicka det via e-post. Kom ihåg att ta bort kopian från enheten när kopieringen har slutförts eller när du har fått e-postmeddelandet."</string>
+    <string name="debug_dump_delete_button" msgid="7832879421132026435">"Ta bort nu"</string>
+    <string name="debug_dump_start_button" msgid="2837506913757600001">"Börja"</string>
+    <string name="debug_dump_email_sender_picker" msgid="3534420908672176460">"Välj program att skicka filen med"</string>
+    <string name="debug_dump_email_subject" msgid="108188398416385976">"Kontaktdatabas bifogad"</string>
+    <string name="debug_dump_email_body" msgid="4577749800871444318">"Jag bifogar min kontaktdatabas med alla mina kontaktuppgifter. Hantera den varsamt."</string>
 </resources>
diff --git a/res/values-sw/strings.xml b/res/values-sw/strings.xml
index b83c636..6682d21 100644
--- a/res/values-sw/strings.xml
+++ b/res/values-sw/strings.xml
@@ -28,4 +28,11 @@
     <string name="read_write_all_voicemail_label" msgid="4557216100818257560">"Fikia barua zote za sauti"</string>
     <string name="read_write_all_voicemail_description" msgid="8029809937805761356">"Inaruhusu programu kuhifadhi na kutoa jumbe zote za sauti ambazo kifaa hiki kinaweza kufikia."</string>
     <string name="voicemail_from_column" msgid="435732568832121444">"Barua ya sauti kutoka "</string>
+    <string name="debug_dump_title" msgid="4916885724165570279">"Nakili hifadhidata ya anwani"</string>
+    <string name="debug_dump_database_message" msgid="406438635002392290">"Unakaribia 1) kuunda nakala ya hifadhidata yako ambayo inajumuisha maelezo yote yanayohusiana na anwani na kumbukumbu zote za simu katika hifadhi ya ndani, na 2) uitume kwa barua pepe. Kumbuka kufuta nakala pindi tu unapoinakili kwa ufanisi kutoka kwenye kifaa au barua pepe imepokewa."</string>
+    <string name="debug_dump_delete_button" msgid="7832879421132026435">"Futa sasa"</string>
+    <string name="debug_dump_start_button" msgid="2837506913757600001">"Anza"</string>
+    <string name="debug_dump_email_sender_picker" msgid="3534420908672176460">"Chagua programu ili kutuma faili yako"</string>
+    <string name="debug_dump_email_subject" msgid="108188398416385976">"Db ya anwani imeambatishwa"</string>
+    <string name="debug_dump_email_body" msgid="4577749800871444318">"Kilichoambatishwa ni hifadhidata ya anwani zangu iliyo na maelezo yote ya anwani zangu. Ishughulikie kwa uangalifu."</string>
 </resources>
diff --git a/res/values-th/strings.xml b/res/values-th/strings.xml
index 0cb6adc..d3c3e82 100644
--- a/res/values-th/strings.xml
+++ b/res/values-th/strings.xml
@@ -28,4 +28,11 @@
     <string name="read_write_all_voicemail_label" msgid="4557216100818257560">"เข้าถึงข้อความเสียงทั้งหมด"</string>
     <string name="read_write_all_voicemail_description" msgid="8029809937805761356">"อนุญาตให้แอปพลิเคชันจัดเก็บและเรียกข้อความเสียงทั้งหมดที่อุปกรณ์นี้สามารถเข้าถึงได้"</string>
     <string name="voicemail_from_column" msgid="435732568832121444">"ข้อความเสียงจาก "</string>
+    <string name="debug_dump_title" msgid="4916885724165570279">"คัดลอกฐานข้อมูลผู้ติดต่อ"</string>
+    <string name="debug_dump_database_message" msgid="406438635002392290">"คุณกำลังจะ 1) ทำสำเนาฐานข้อมูลของคุณซึ่งรวมถึงข้อมูลที่เกี่ยวข้องกับผู้ติดต่อทั้งหมดและบันทึกการโทรทั้งหมดลงในที่จัดเก็บข้อมูลภายใน และ 2) ส่งอีเมล อย่าลืมลบสำเนาออกจากอุปกรณ์ทันทีที่คุณคัดลอกเสร็จเรียบร้อยแล้วหรือเมื่ออีเมลส่งไปถึงผู้รับแล้ว"</string>
+    <string name="debug_dump_delete_button" msgid="7832879421132026435">"ลบเดี๋ยวนี้"</string>
+    <string name="debug_dump_start_button" msgid="2837506913757600001">"เริ่มต้น"</string>
+    <string name="debug_dump_email_sender_picker" msgid="3534420908672176460">"เลือกโปรแกรมเพื่อส่งไฟล์ของคุณ"</string>
+    <string name="debug_dump_email_subject" msgid="108188398416385976">"แนบฐานข้อมูลผู้ติดต่อมาด้วย"</string>
+    <string name="debug_dump_email_body" msgid="4577749800871444318">"ที่แนบอยู่นี้เป็นฐานข้อมูลผู้ติดต่อซึ่งมีข้อมูลผู้ติดต่อของฉันทั้งหมด โปรดจัดการด้วยความระมัดระวัง"</string>
 </resources>
diff --git a/res/values-tl/strings.xml b/res/values-tl/strings.xml
index 44f6b73..8d6855c 100644
--- a/res/values-tl/strings.xml
+++ b/res/values-tl/strings.xml
@@ -28,4 +28,11 @@
     <string name="read_write_all_voicemail_label" msgid="4557216100818257560">"I-access ang lahat ng voicemail"</string>
     <string name="read_write_all_voicemail_description" msgid="8029809937805761356">"Binibigyang-daan ang app upang iimbak at kuning muli ang lahat ng voicemail na naa-access ng device na ito."</string>
     <string name="voicemail_from_column" msgid="435732568832121444">"Voicemail mula sa/kay "</string>
+    <string name="debug_dump_title" msgid="4916885724165570279">"Kopyahin ang database ng mga contact"</string>
+    <string name="debug_dump_database_message" msgid="406438635002392290">"Ikaw ay 1) gagawa na ng kopya ng iyong database na kinapapalooban ng lahat ng impormasyong nauugnay sa mga contact at ng lahat ng log ng tawag sa panloob na storage, at 2) ipapadala mo na ito sa email. Alalahaning tanggalin ang kopya sa sandaling matagumpay mo na itong nakopya mula sa device o sa sandaling natanggap na ang email."</string>
+    <string name="debug_dump_delete_button" msgid="7832879421132026435">"Tanggalin ngayon"</string>
+    <string name="debug_dump_start_button" msgid="2837506913757600001">"Simulan"</string>
+    <string name="debug_dump_email_sender_picker" msgid="3534420908672176460">"Pumili ng program upang ipadala ang iyong file"</string>
+    <string name="debug_dump_email_subject" msgid="108188398416385976">"Naka-attach ang Contacts Db"</string>
+    <string name="debug_dump_email_body" msgid="4577749800871444318">"Naka-attach ang aking database ng mga contact na kasama ang lahat ng impormasyon ng aking mga contact. Pangasiwaan nang may pag-iingat."</string>
 </resources>
diff --git a/res/values-tr/strings.xml b/res/values-tr/strings.xml
index 672288a..9507051 100644
--- a/res/values-tr/strings.xml
+++ b/res/values-tr/strings.xml
@@ -28,4 +28,11 @@
     <string name="read_write_all_voicemail_label" msgid="4557216100818257560">"Tüm sesli mesajlara erişim"</string>
     <string name="read_write_all_voicemail_description" msgid="8029809937805761356">"Uygulamaya, bu cihazın erişebileceği tüm sesli mesajları depolama ve alma izni verir."</string>
     <string name="voicemail_from_column" msgid="435732568832121444">"Sesli mesaj gönderen: "</string>
+    <string name="debug_dump_title" msgid="4916885724165570279">"Kişiler veritabanını kopyala"</string>
+    <string name="debug_dump_database_message" msgid="406438635002392290">"Birazdan 1) veritabanınızın kişilerle ilgili tüm bilgilerini ve çağrı günlüğünün tamamını içeren bir kopyasını dahili depolama birimine kaydetmek ve 2) bunu e-postayla göndermek üzeresiniz. Bu kopyayı cihazın dışına aktardıktan veya e-posta alındıktan sonra hemen silmeyi unutmayın."</string>
+    <string name="debug_dump_delete_button" msgid="7832879421132026435">"Şimdi sil"</string>
+    <string name="debug_dump_start_button" msgid="2837506913757600001">"Başlat"</string>
+    <string name="debug_dump_email_sender_picker" msgid="3534420908672176460">"Dosyanızı göndermek için bir program seçin"</string>
+    <string name="debug_dump_email_subject" msgid="108188398416385976">"Kişiler veritabanı ektedir"</string>
+    <string name="debug_dump_email_body" msgid="4577749800871444318">"Tüm kişi bilgilerimi içeren kişiler veritabanımı ekte bulabilirsiniz. Dikkatli kullanın."</string>
 </resources>
diff --git a/res/values-uk/strings.xml b/res/values-uk/strings.xml
index 1829219..5554902 100644
--- a/res/values-uk/strings.xml
+++ b/res/values-uk/strings.xml
@@ -28,4 +28,11 @@
     <string name="read_write_all_voicemail_label" msgid="4557216100818257560">"Доступ до всієї голосової пошти"</string>
     <string name="read_write_all_voicemail_description" msgid="8029809937805761356">"Дозволяє програмі зберігати й отримувати всю голосову пошту, доступ до якої має цей пристрій."</string>
     <string name="voicemail_from_column" msgid="435732568832121444">"Голосова пошта від "</string>
+    <string name="debug_dump_title" msgid="4916885724165570279">"Копіювати базу даних контактів"</string>
+    <string name="debug_dump_database_message" msgid="406438635002392290">"Ви збираєтеся: 1) скопіювати у внутрішню пам’ять свою базу даних, яка містить усю інформацію про контакти та весь журнал дзвінків; 2) надіслати копію електронною поштою. Не забудьте видалити копію, щойно її буде перенесено з пристрою або отримано електронним листом."</string>
+    <string name="debug_dump_delete_button" msgid="7832879421132026435">"Видалити зараз"</string>
+    <string name="debug_dump_start_button" msgid="2837506913757600001">"Почати"</string>
+    <string name="debug_dump_email_sender_picker" msgid="3534420908672176460">"Виберіть програму для надсилання файлу"</string>
+    <string name="debug_dump_email_subject" msgid="108188398416385976">"У вкладенні – база даних контактів"</string>
+    <string name="debug_dump_email_body" msgid="4577749800871444318">"У вкладенні – база даних моїх контактів з усією інформацією про мої контакти. Працюйте з нею обережно."</string>
 </resources>
diff --git a/res/values-vi/strings.xml b/res/values-vi/strings.xml
index 431a403..0d59f0d 100644
--- a/res/values-vi/strings.xml
+++ b/res/values-vi/strings.xml
@@ -28,4 +28,11 @@
     <string name="read_write_all_voicemail_label" msgid="4557216100818257560">"Truy cập tất cả các thư thoại"</string>
     <string name="read_write_all_voicemail_description" msgid="8029809937805761356">"Cho phép ứng dụng lưu trữ và truy xuất tất cả thư thoại mà thiết bị này có thể truy cập."</string>
     <string name="voicemail_from_column" msgid="435732568832121444">"Thư thoại từ "</string>
+    <string name="debug_dump_title" msgid="4916885724165570279">"Sao chép cơ sở dữ liệu người liên hệ"</string>
+    <string name="debug_dump_database_message" msgid="406438635002392290">"Bạn sắp 1) thực hiện sao chép cơ sở dữ liệu của mình bao gồm tất cả thông tin liên quan đến địa chỉ liên hệ và tất cả nhật ký cuộc gọi sang bộ nhớ trong, và 2) gửi bản sao đó qua email. Hãy nhớ xóa bản sao khỏi thiết bị hoặc email nhận được ngay khi bạn đã sao chép thành công."</string>
+    <string name="debug_dump_delete_button" msgid="7832879421132026435">"Xóa ngay"</string>
+    <string name="debug_dump_start_button" msgid="2837506913757600001">"Bắt đầu"</string>
+    <string name="debug_dump_email_sender_picker" msgid="3534420908672176460">"Chọn chương trình để gửi tệp của bạn"</string>
+    <string name="debug_dump_email_subject" msgid="108188398416385976">"Đã đính kèm cơ sở dữ liệu người liên hệ"</string>
+    <string name="debug_dump_email_body" msgid="4577749800871444318">"Phần đính kèm là cơ sở dữ liệu người liên hệ với tất cả thông tin người liên hệ của tôi. Hãy cẩn trọng khi xử lý."</string>
 </resources>
diff --git a/res/values-zh-rCN/strings.xml b/res/values-zh-rCN/strings.xml
index 402b8c1..2b388c9 100644
--- a/res/values-zh-rCN/strings.xml
+++ b/res/values-zh-rCN/strings.xml
@@ -22,10 +22,17 @@
     <string name="upgrade_msg" msgid="8640807392794309950">"正在升级联系人数据库。"</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_text" msgid="8438179450336437626">"请触摸以完成升级。"</string>
+    <string name="upgrade_out_of_memory_notification_text" msgid="8438179450336437626">"触摸可完成升级。"</string>
     <string name="default_directory" msgid="93961630309570294">"联系人"</string>
     <string name="local_invisible_directory" msgid="705244318477396120">"其他"</string>
     <string name="read_write_all_voicemail_label" msgid="4557216100818257560">"访问所有语音邮件"</string>
     <string name="read_write_all_voicemail_description" msgid="8029809937805761356">"允许应用存储和检索此设备可访问的所有语音邮件。"</string>
     <string name="voicemail_from_column" msgid="435732568832121444">"语音邮件发件人 "</string>
+    <string name="debug_dump_title" msgid="4916885724165570279">"复制通讯录数据库"</string>
+    <string name="debug_dump_database_message" msgid="406438635002392290">"您将要执行以下操作:1) 在内部存储设备中,创建包括通讯录相关信息和所有通话记录的数据库的副本;2) 通过电子邮件发送该副本。从设备中成功复制该副本或在电子邮件送达之后,请务必及时删除该副本。"</string>
+    <string name="debug_dump_delete_button" msgid="7832879421132026435">"立即删除"</string>
+    <string name="debug_dump_start_button" msgid="2837506913757600001">"开始"</string>
+    <string name="debug_dump_email_sender_picker" msgid="3534420908672176460">"选择用于发送文件的程序"</string>
+    <string name="debug_dump_email_subject" msgid="108188398416385976">"随附通讯录数据库"</string>
+    <string name="debug_dump_email_body" msgid="4577749800871444318">"附件是我的通讯录数据库,其中包含我所有的联系人信息,因此请谨慎处理。"</string>
 </resources>
diff --git a/res/values-zh-rTW/strings.xml b/res/values-zh-rTW/strings.xml
index ba929c3..bd580b2 100644
--- a/res/values-zh-rTW/strings.xml
+++ b/res/values-zh-rTW/strings.xml
@@ -28,4 +28,11 @@
     <string name="read_write_all_voicemail_label" msgid="4557216100818257560">"存取所有語音留言"</string>
     <string name="read_write_all_voicemail_description" msgid="8029809937805761356">"允許應用程式儲存及擷取這個裝置可存取的所有語音留言。"</string>
     <string name="voicemail_from_column" msgid="435732568832121444">"語音郵件寄件者: "</string>
+    <string name="debug_dump_title" msgid="4916885724165570279">"複製聯絡人資料庫"</string>
+    <string name="debug_dump_database_message" msgid="406438635002392290">"您即將要 1) 將您的資料庫 (包含所有聯絡人相關資訊及所有通話紀錄) 複製到內部儲存空間,以及 2) 透過電子郵件傳送副本。提醒您,當您順利複製裝置上的資料或收到電子郵件後,請儘快刪除副本。"</string>
+    <string name="debug_dump_delete_button" msgid="7832879421132026435">"立即刪除"</string>
+    <string name="debug_dump_start_button" msgid="2837506913757600001">"開始"</string>
+    <string name="debug_dump_email_sender_picker" msgid="3534420908672176460">"選擇要傳送檔案的程式"</string>
+    <string name="debug_dump_email_subject" msgid="108188398416385976">"內含聯絡人資料庫附件"</string>
+    <string name="debug_dump_email_body" msgid="4577749800871444318">"附件是我的聯絡人資料庫,其中包含我所有的聯絡人資訊,請謹慎處理。"</string>
 </resources>
diff --git a/res/values-zu/strings.xml b/res/values-zu/strings.xml
index 76de636..014a516 100644
--- a/res/values-zu/strings.xml
+++ b/res/values-zu/strings.xml
@@ -28,4 +28,11 @@
     <string name="read_write_all_voicemail_label" msgid="4557216100818257560">"Finyelela kuwo wonke amavoyisimeyili"</string>
     <string name="read_write_all_voicemail_description" msgid="8029809937805761356">"Ivumela uhlelo olusebenzayo lulonde futhi lulande wonke ama-imeyli ezwi ledivayisi engakwazi ukuwafinyelela."</string>
     <string name="voicemail_from_column" msgid="435732568832121444">"Imeyili yezwi kusuka "</string>
+    <string name="debug_dump_title" msgid="4916885724165570279">"Kopisha imininingo egciniwe yoxhumana nabo"</string>
+    <string name="debug_dump_database_message" msgid="406438635002392290">"Useduze nokuthi 1) wenze ikhophi yemininingwane yakho egciniwe ebandakanya yonke imininingwane ehlobene noxhumana nabo kanye nohlu lokushaya ucingo kokokulondoloza kwangaphakathi, kanti futhi 2) uzoyYou are about to 1) make a copy of youithumela nge-imeyili. Khumbula ukuthi ususe ikhophi ngokushesha emumva kokuba uphumelele ukuyikopisha isuka edivayisini noma emumva kokuba kutholakale i-imeyili."</string>
+    <string name="debug_dump_delete_button" msgid="7832879421132026435">"Susa manje"</string>
+    <string name="debug_dump_start_button" msgid="2837506913757600001">"Qala"</string>
+    <string name="debug_dump_email_sender_picker" msgid="3534420908672176460">"Khetha uhlelo ozothumela kulo ifayela lakho"</string>
+    <string name="debug_dump_email_subject" msgid="108188398416385976">"I-Db yoxhumana nabo inamathiselwe"</string>
+    <string name="debug_dump_email_body" msgid="4577749800871444318">"Okunamathiselwe imininingo egciniwe yabaxhumana nami nalo lonke ulwazi labaxhumana nami. Kubambe ngokunakekela."</string>
 </resources>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index aaa7f44..5291017 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -58,4 +58,26 @@
          Note that the trailing space is important, and that to achieve it we have to wrap the
          string in double quotes. -->
     <string name="voicemail_from_column">"Voicemail from "</string>
+
+    <!-- Debug tool - title of the dialog which copies the contact database into the external storage. [CHAR LIMIT=NONE] -->
+    <string name="debug_dump_title">Copy contacts database</string>
+
+    <!-- Debug tool - message shown to the user on the dialog which sends a copy of  the contact database via email or other apps. [CHAR LIMIT=NONE] -->
+    <string name="debug_dump_database_message">You are about to 1) make a copy of your database which includes all contacts related information and all call log to the internal storage, and 2) email it.  Remember to delete the copy as soon as you have successfully copied it off the device or the email is received.</string>
+
+    <!-- Debug tool - dialog button- delete file now [CHAR LIMIT=NONE] -->
+    <string name="debug_dump_delete_button">Delete now</string>
+
+    <!-- Debug tool - dialog button - start copying [CHAR LIMIT=NONE] -->
+    <string name="debug_dump_start_button">Start</string>
+
+    <!-- Debug tool - email subject [CHAR LIMIT=NONE] -->
+    <string name="debug_dump_email_sender_picker">Choose a program to send your file</string>
+
+    <!-- Debug tool - email subject [CHAR LIMIT=NONE] -->
+    <string name="debug_dump_email_subject">Contacts Db attached</string>
+
+    <!-- Debug tool - email body [CHAR LIMIT=NONE] -->
+    <string name="debug_dump_email_body">Attached is my contacts database with all my contacts information.  Handle with care.</string>
+
 </resources>
diff --git a/src/com/android/providers/contacts/AbstractContactsProvider.java b/src/com/android/providers/contacts/AbstractContactsProvider.java
index a33320f..226652e 100644
--- a/src/com/android/providers/contacts/AbstractContactsProvider.java
+++ b/src/com/android/providers/contacts/AbstractContactsProvider.java
@@ -38,9 +38,12 @@
 public abstract class AbstractContactsProvider extends ContentProvider
         implements SQLiteTransactionListener {
 
-    protected static final String TAG = "ContactsProvider";
+    public static final String TAG = "ContactsProvider";
 
-    protected static final boolean VERBOSE_LOGGING = Log.isLoggable(TAG, Log.VERBOSE);
+    public static final boolean VERBOSE_LOGGING = Log.isLoggable(TAG, Log.VERBOSE);
+
+    /** Set true to enable detailed transaction logging. */
+    public static final boolean ENABLE_TRANSACTION_LOG = false; // Don't submit with true.
 
     /**
      * Duration in ms to sleep after successfully yielding the lock during a batch operation.
@@ -72,14 +75,32 @@
      * created by this provider will automatically retrieve a writable database from this helper
      * and initiate a transaction on that database.  This should be used to ensure that operations
      * across multiple databases are all blocked on a single DB lock (to prevent deadlock cases).
+     *
+     * Hint: It's always {@link ContactsDatabaseHelper}.
+     *
+     * TODO Change the structure to make it obvious that it's actually always set, and is the
+     * {@link ContactsDatabaseHelper}.
      */
     private SQLiteOpenHelper mSerializeOnDbHelper;
 
     /**
      * The tag corresponding to the database used for serializing transactions.
+     *
+     * Hint: It's always the contacts db helper tag.
+     *
+     * See also the TODO on {@link #mSerializeOnDbHelper}.
      */
     private String mSerializeDbTag;
 
+    /**
+     * The transaction listener used with {@link #mSerializeOnDbHelper}.
+     *
+     * Hint: It's always {@link ContactsProvider2}.
+     *
+     * See also the TODO on {@link #mSerializeOnDbHelper}.
+     */
+    private SQLiteTransactionListener mSerializedDbTransactionListener;
+
     @Override
     public boolean onCreate() {
         Context context = getContext();
@@ -94,12 +115,14 @@
 
     /**
      * Specifies a database helper (and corresponding tag) to serialize all transactions on.
-     * @param serializeOnDbHelper The database helper to use for serializing transactions.
-     * @param tag The tag for this database.
+     *
+     * See also the TODO on {@link #mSerializeOnDbHelper}.
      */
-    public void setDbHelperToSerializeOn(SQLiteOpenHelper serializeOnDbHelper, String tag) {
+    public void setDbHelperToSerializeOn(SQLiteOpenHelper serializeOnDbHelper, String tag,
+            SQLiteTransactionListener listener) {
         mSerializeOnDbHelper = serializeOnDbHelper;
         mSerializeDbTag = tag;
+        mSerializedDbTransactionListener = listener;
     }
 
     public ContactsTransaction getCurrentTransaction() {
@@ -227,12 +250,16 @@
      * @param callerIsBatch Whether the caller is operating in batch mode.
      */
     private ContactsTransaction startTransaction(boolean callerIsBatch) {
+        if (ENABLE_TRANSACTION_LOG) {
+            Log.i(TAG, "startTransaction " + getClass().getSimpleName() +
+                    "  callerIsBatch=" + callerIsBatch, new RuntimeException("startTransaction"));
+        }
         ContactsTransaction transaction = mTransactionHolder.get();
         if (transaction == null) {
             transaction = new ContactsTransaction(callerIsBatch);
             if (mSerializeOnDbHelper != null) {
                 transaction.startTransactionForDb(mSerializeOnDbHelper.getWritableDatabase(),
-                        mSerializeDbTag, this);
+                        mSerializeDbTag, mSerializedDbTransactionListener);
             }
             mTransactionHolder.set(transaction);
         }
@@ -245,6 +272,10 @@
      * @param callerIsBatch Whether the caller is operating in batch mode.
      */
     private void endTransaction(boolean callerIsBatch) {
+        if (ENABLE_TRANSACTION_LOG) {
+            Log.i(TAG, "endTransaction " + getClass().getSimpleName() +
+                    "  callerIsBatch=" + callerIsBatch, new RuntimeException("endTransaction"));
+        }
         ContactsTransaction transaction = mTransactionHolder.get();
         if (transaction != null && (!transaction.isBatch() || callerIsBatch)) {
             try {
diff --git a/src/com/android/providers/contacts/AccountWithDataSet.java b/src/com/android/providers/contacts/AccountWithDataSet.java
index 3fea8a6..b7484cd 100644
--- a/src/com/android/providers/contacts/AccountWithDataSet.java
+++ b/src/com/android/providers/contacts/AccountWithDataSet.java
@@ -16,11 +16,11 @@
 
 package com.android.providers.contacts;
 
-import com.android.internal.util.Objects;
-
 import android.accounts.Account;
 import android.text.TextUtils;
 
+import com.android.internal.util.Objects;
+
 /**
  * Account information that includes the data set, if any.
  */
diff --git a/src/com/android/providers/contacts/CallLogProvider.java b/src/com/android/providers/contacts/CallLogProvider.java
index 89ae591..3480c79 100644
--- a/src/com/android/providers/contacts/CallLogProvider.java
+++ b/src/com/android/providers/contacts/CallLogProvider.java
@@ -20,10 +20,6 @@
 import static com.android.providers.contacts.util.DbQueryUtils.getEqualityClause;
 import static com.android.providers.contacts.util.DbQueryUtils.getInequalityClause;
 
-import com.android.providers.contacts.ContactsDatabaseHelper.Tables;
-import com.android.providers.contacts.util.SelectionBuilder;
-import com.google.common.annotations.VisibleForTesting;
-
 import android.content.ContentProvider;
 import android.content.ContentUris;
 import android.content.ContentValues;
@@ -38,6 +34,10 @@
 import android.provider.CallLog.Calls;
 import android.util.Log;
 
+import com.android.providers.contacts.ContactsDatabaseHelper.Tables;
+import com.android.providers.contacts.util.SelectionBuilder;
+import com.google.common.annotations.VisibleForTesting;
+
 import java.util.HashMap;
 
 /**
@@ -123,15 +123,15 @@
     @Override
     public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
             String sortOrder) {
-        SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
+        final SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
         qb.setTables(Tables.CALLS);
         qb.setProjectionMap(sCallsProjectionMap);
         qb.setStrict(true);
 
-        SelectionBuilder selectionBuilder = new SelectionBuilder(selection);
+        final SelectionBuilder selectionBuilder = new SelectionBuilder(selection);
         checkVoicemailPermissionAndAddRestriction(uri, selectionBuilder);
 
-        int match = sURIMatcher.match(uri);
+        final int match = sURIMatcher.match(uri);
         switch (match) {
             case CALLS:
                 break;
@@ -154,15 +154,47 @@
                 throw new IllegalArgumentException("Unknown URL " + uri);
         }
 
+        final int limit = getIntParam(uri, Calls.LIMIT_PARAM_KEY, 0);
+        final int offset = getIntParam(uri, Calls.OFFSET_PARAM_KEY, 0);
+        String limitClause = null;
+        if (limit > 0) {
+            limitClause = offset + "," + limit;
+        }
+
         final SQLiteDatabase db = mDbHelper.getReadableDatabase();
-        Cursor c = qb.query(db, projection, selectionBuilder.build(), selectionArgs, null, null,
-                sortOrder, null);
+        final Cursor c = qb.query(db, projection, selectionBuilder.build(), selectionArgs, null,
+                null, sortOrder, limitClause);
         if (c != null) {
             c.setNotificationUri(getContext().getContentResolver(), CallLog.CONTENT_URI);
         }
         return c;
     }
 
+    /**
+     * Gets an integer query parameter from a given uri.
+     *
+     * @param uri The uri to extract the query parameter from.
+     * @param key The query parameter key.
+     * @param defaultValue A default value to return if the query parameter does not exist.
+     * @return The value from the query parameter in the Uri.  Or the default value if the parameter
+     * does not exist in the uri.
+     * @throws IllegalArgumentException when the value in the query parameter is not an integer.
+     */
+    private int getIntParam(Uri uri, String key, int defaultValue) {
+        String valueString = uri.getQueryParameter(key);
+        if (valueString == null) {
+            return defaultValue;
+        }
+
+        try {
+            return Integer.parseInt(valueString);
+        } catch (NumberFormatException e) {
+            String msg = "Integer required for " + key + " parameter but value '" + valueString +
+                    "' was found instead.";
+            throw new IllegalArgumentException(msg, e);
+        }
+    }
+
     @Override
     public String getType(Uri uri) {
         int match = sURIMatcher.match(uri);
diff --git a/src/com/android/providers/contacts/ContactDirectoryManager.java b/src/com/android/providers/contacts/ContactDirectoryManager.java
index 7116ed6..f243e79 100644
--- a/src/com/android/providers/contacts/ContactDirectoryManager.java
+++ b/src/com/android/providers/contacts/ContactDirectoryManager.java
@@ -16,13 +16,6 @@
 
 package com.android.providers.contacts;
 
-import com.android.providers.contacts.ContactsDatabaseHelper.DbProperties;
-import com.android.providers.contacts.ContactsDatabaseHelper.DirectoryColumns;
-import com.android.providers.contacts.ContactsDatabaseHelper.Tables;
-import com.google.android.collect.Lists;
-import com.google.android.collect.Sets;
-import com.google.common.annotations.VisibleForTesting;
-
 import android.content.ContentValues;
 import android.content.Context;
 import android.content.pm.PackageInfo;
@@ -41,6 +34,13 @@
 import android.text.TextUtils;
 import android.util.Log;
 
+import com.android.providers.contacts.ContactsDatabaseHelper.DbProperties;
+import com.android.providers.contacts.ContactsDatabaseHelper.DirectoryColumns;
+import com.android.providers.contacts.ContactsDatabaseHelper.Tables;
+import com.google.android.collect.Lists;
+import com.google.android.collect.Sets;
+import com.google.common.annotations.VisibleForTesting;
+
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Set;
diff --git a/src/com/android/providers/contacts/ContactLocaleUtils.java b/src/com/android/providers/contacts/ContactLocaleUtils.java
index 2eb2ad6..0e7b292 100644
--- a/src/com/android/providers/contacts/ContactLocaleUtils.java
+++ b/src/com/android/providers/contacts/ContactLocaleUtils.java
@@ -16,11 +16,11 @@
 
 package com.android.providers.contacts;
 
-import com.android.providers.contacts.HanziToPinyin.Token;
-
 import android.provider.ContactsContract.FullNameStyle;
 import android.util.SparseArray;
 
+import com.android.providers.contacts.HanziToPinyin.Token;
+
 import java.util.ArrayList;
 import java.util.HashSet;
 import java.util.Iterator;
diff --git a/src/com/android/providers/contacts/ContactsDatabaseHelper.java b/src/com/android/providers/contacts/ContactsDatabaseHelper.java
index 67429ca..5baf9dc 100644
--- a/src/com/android/providers/contacts/ContactsDatabaseHelper.java
+++ b/src/com/android/providers/contacts/ContactsDatabaseHelper.java
@@ -16,11 +16,6 @@
 
 package com.android.providers.contacts;
 
-import com.android.common.content.SyncStateContentProviderHelper;
-import com.android.providers.contacts.aggregation.util.CommonNicknameCache;
-import com.android.providers.contacts.util.NeededForTesting;
-import com.google.android.collect.Sets;
-
 import android.content.ContentResolver;
 import android.content.ContentValues;
 import android.content.Context;
@@ -80,6 +75,11 @@
 import android.text.util.Rfc822Tokenizer;
 import android.util.Log;
 
+import com.android.common.content.SyncStateContentProviderHelper;
+import com.android.providers.contacts.aggregation.util.CommonNicknameCache;
+import com.android.providers.contacts.util.NeededForTesting;
+import com.google.android.collect.Sets;
+
 import java.util.HashMap;
 import java.util.Locale;
 import java.util.Set;
@@ -107,7 +107,7 @@
      *   700-799 Jelly Bean
      * </pre>
      */
-    static final int DATABASE_VERSION = 704;
+    static final int DATABASE_VERSION = 705;
 
     private static final String DATABASE_NAME = "contacts2.db";
     private static final String DATABASE_PRESENCE = "presence_db";
@@ -779,6 +779,10 @@
                         com.android.internal.R.bool.config_use_strict_phone_number_comparation);
     }
 
+    public SQLiteDatabase getDatabase(boolean writable) {
+        return writable ? getWritableDatabase() : getReadableDatabase();
+    }
+
     /**
      * Clear all the cached database information and re-initialize it.
      *
@@ -2371,6 +2375,14 @@
             oldVersion = 704;
         }
 
+        if (oldVersion < 705) {
+            // Before this version, we didn't rebuild the search index on locale changes, so
+            // if the locale has changed after sync, the index contains gets stale.
+            // To correct the issue we have to rebuild the index here.
+            upgradeSearchIndex = true;
+            oldVersion = 705;
+        }
+
         if (upgradeViewsAndTriggers) {
             createContactsViews(db);
             createGroupsView(db);
@@ -2532,8 +2544,6 @@
                 + " ADD " + RawContacts.SORT_KEY_ALTERNATIVE
                 + " TEXT COLLATE " + ContactsProvider2.PHONEBOOK_COLLATOR_NAME + ";");
 
-        final Locale locale = Locale.getDefault();
-
         NameSplitter splitter = createNameSplitter();
 
         SQLiteStatement rawContactUpdate = db.compileStatement(
@@ -4449,20 +4459,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());
     }
 
@@ -4522,28 +4547,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(')');
         }
@@ -5272,6 +5302,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 26aac63..410aaf4 100644
--- a/src/com/android/providers/contacts/ContactsProvider2.java
+++ b/src/com/android/providers/contacts/ContactsProvider2.java
@@ -16,51 +16,6 @@
 
 package com.android.providers.contacts;
 
-import com.android.common.content.ProjectionMap;
-import com.android.common.content.SyncStateContentProviderHelper;
-import com.android.providers.contacts.ContactLookupKey.LookupKeySegment;
-import com.android.providers.contacts.ContactsDatabaseHelper.AccountsColumns;
-import com.android.providers.contacts.ContactsDatabaseHelper.AggregatedPresenceColumns;
-import com.android.providers.contacts.ContactsDatabaseHelper.AggregationExceptionColumns;
-import com.android.providers.contacts.ContactsDatabaseHelper.Clauses;
-import com.android.providers.contacts.ContactsDatabaseHelper.ContactsColumns;
-import com.android.providers.contacts.ContactsDatabaseHelper.ContactsStatusUpdatesColumns;
-import com.android.providers.contacts.ContactsDatabaseHelper.DataColumns;
-import com.android.providers.contacts.ContactsDatabaseHelper.DataUsageStatColumns;
-import com.android.providers.contacts.ContactsDatabaseHelper.DbProperties;
-import com.android.providers.contacts.ContactsDatabaseHelper.GroupsColumns;
-import com.android.providers.contacts.ContactsDatabaseHelper.Joins;
-import com.android.providers.contacts.ContactsDatabaseHelper.NameLookupColumns;
-import com.android.providers.contacts.ContactsDatabaseHelper.NameLookupType;
-import com.android.providers.contacts.ContactsDatabaseHelper.PhoneLookupColumns;
-import com.android.providers.contacts.ContactsDatabaseHelper.PhotoFilesColumns;
-import com.android.providers.contacts.ContactsDatabaseHelper.PresenceColumns;
-import com.android.providers.contacts.ContactsDatabaseHelper.Projections;
-import com.android.providers.contacts.ContactsDatabaseHelper.RawContactsColumns;
-import com.android.providers.contacts.ContactsDatabaseHelper.SearchIndexColumns;
-import com.android.providers.contacts.ContactsDatabaseHelper.SettingsColumns;
-import com.android.providers.contacts.ContactsDatabaseHelper.StatusUpdatesColumns;
-import com.android.providers.contacts.ContactsDatabaseHelper.StreamItemPhotosColumns;
-import com.android.providers.contacts.ContactsDatabaseHelper.StreamItemsColumns;
-import com.android.providers.contacts.ContactsDatabaseHelper.Tables;
-import com.android.providers.contacts.ContactsDatabaseHelper.ViewGroupsColumns;
-import com.android.providers.contacts.ContactsDatabaseHelper.Views;
-import com.android.providers.contacts.SearchIndexManager.FtsQueryBuilder;
-import com.android.providers.contacts.aggregation.ContactAggregator;
-import com.android.providers.contacts.aggregation.ContactAggregator.AggregationSuggestionParameter;
-import com.android.providers.contacts.aggregation.util.CommonNicknameCache;
-import com.android.providers.contacts.aggregation.ProfileAggregator;
-import com.android.providers.contacts.util.Clock;
-import com.android.providers.contacts.util.DbQueryUtils;
-import com.android.providers.contacts.util.NeededForTesting;
-import com.android.vcard.VCardComposer;
-import com.android.vcard.VCardConfig;
-import com.google.android.collect.Lists;
-import com.google.android.collect.Maps;
-import com.google.android.collect.Sets;
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.Preconditions;
-
 import android.accounts.Account;
 import android.accounts.AccountManager;
 import android.accounts.OnAccountsUpdateListener;
@@ -151,6 +106,51 @@
 import android.text.TextUtils;
 import android.util.Log;
 
+import com.android.common.content.ProjectionMap;
+import com.android.common.content.SyncStateContentProviderHelper;
+import com.android.providers.contacts.ContactLookupKey.LookupKeySegment;
+import com.android.providers.contacts.ContactsDatabaseHelper.AccountsColumns;
+import com.android.providers.contacts.ContactsDatabaseHelper.AggregatedPresenceColumns;
+import com.android.providers.contacts.ContactsDatabaseHelper.AggregationExceptionColumns;
+import com.android.providers.contacts.ContactsDatabaseHelper.Clauses;
+import com.android.providers.contacts.ContactsDatabaseHelper.ContactsColumns;
+import com.android.providers.contacts.ContactsDatabaseHelper.ContactsStatusUpdatesColumns;
+import com.android.providers.contacts.ContactsDatabaseHelper.DataColumns;
+import com.android.providers.contacts.ContactsDatabaseHelper.DataUsageStatColumns;
+import com.android.providers.contacts.ContactsDatabaseHelper.DbProperties;
+import com.android.providers.contacts.ContactsDatabaseHelper.GroupsColumns;
+import com.android.providers.contacts.ContactsDatabaseHelper.Joins;
+import com.android.providers.contacts.ContactsDatabaseHelper.NameLookupColumns;
+import com.android.providers.contacts.ContactsDatabaseHelper.NameLookupType;
+import com.android.providers.contacts.ContactsDatabaseHelper.PhoneLookupColumns;
+import com.android.providers.contacts.ContactsDatabaseHelper.PhotoFilesColumns;
+import com.android.providers.contacts.ContactsDatabaseHelper.PresenceColumns;
+import com.android.providers.contacts.ContactsDatabaseHelper.Projections;
+import com.android.providers.contacts.ContactsDatabaseHelper.RawContactsColumns;
+import com.android.providers.contacts.ContactsDatabaseHelper.SearchIndexColumns;
+import com.android.providers.contacts.ContactsDatabaseHelper.SettingsColumns;
+import com.android.providers.contacts.ContactsDatabaseHelper.StatusUpdatesColumns;
+import com.android.providers.contacts.ContactsDatabaseHelper.StreamItemPhotosColumns;
+import com.android.providers.contacts.ContactsDatabaseHelper.StreamItemsColumns;
+import com.android.providers.contacts.ContactsDatabaseHelper.Tables;
+import com.android.providers.contacts.ContactsDatabaseHelper.ViewGroupsColumns;
+import com.android.providers.contacts.ContactsDatabaseHelper.Views;
+import com.android.providers.contacts.SearchIndexManager.FtsQueryBuilder;
+import com.android.providers.contacts.aggregation.ContactAggregator;
+import com.android.providers.contacts.aggregation.ContactAggregator.AggregationSuggestionParameter;
+import com.android.providers.contacts.aggregation.ProfileAggregator;
+import com.android.providers.contacts.aggregation.util.CommonNicknameCache;
+import com.android.providers.contacts.util.Clock;
+import com.android.providers.contacts.util.DbQueryUtils;
+import com.android.providers.contacts.util.NeededForTesting;
+import com.android.vcard.VCardComposer;
+import com.android.vcard.VCardConfig;
+import com.google.android.collect.Lists;
+import com.google.android.collect.Maps;
+import com.google.android.collect.Sets;
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Preconditions;
+
 import java.io.BufferedWriter;
 import java.io.ByteArrayOutputStream;
 import java.io.File;
@@ -1264,13 +1264,6 @@
     /* package */ static final String PROFILE_DB_TAG = "profile";
 
     /**
-     * The active (thread-local) database.  This will be switched between a contacts-specific
-     * database and a profile-specific database, depending on what the current operation is
-     * targeted to.
-     */
-    private final ThreadLocal<SQLiteDatabase> mActiveDb = new ThreadLocal<SQLiteDatabase>();
-
-    /**
      * The thread-local holder of the active transaction.  Shared between this and the profile
      * provider, to keep transactions on both databases synchronized.
      */
@@ -1393,7 +1386,7 @@
         mDbHelper.set(mContactsHelper);
 
         // Set up the DB helper for keeping transactions serialized.
-        setDbHelperToSerializeOn(mContactsHelper, CONTACTS_DB_TAG);
+        setDbHelperToSerializeOn(mContactsHelper, CONTACTS_DB_TAG, this);
 
         mContactDirectoryManager = new ContactDirectoryManager(this);
         mGlobalSearchSupport = new GlobalSearchSupport(this);
@@ -1413,8 +1406,8 @@
         };
 
         // Set up the sub-provider for handling profiles.
-        mProfileProvider = getProfileProvider();
-        mProfileProvider.setDbHelperToSerializeOn(mContactsHelper, CONTACTS_DB_TAG);
+        mProfileProvider = newProfileProvider();
+        mProfileProvider.setDbHelperToSerializeOn(mContactsHelper, CONTACTS_DB_TAG, this);
         ProviderInfo profileInfo = new ProviderInfo();
         profileInfo.readPermission = "android.permission.READ_PROFILE";
         profileInfo.writePermission = "android.permission.WRITE_PROFILE";
@@ -1422,10 +1415,7 @@
         mProfileHelper = mProfileProvider.getDatabaseHelper(getContext());
 
         // Initialize the pre-authorized URI duration.
-        mPreAuthorizedUriDuration = android.provider.Settings.Secure.getLong(
-                getContext().getContentResolver(),
-                android.provider.Settings.Secure.CONTACTS_PREAUTH_URI_EXPIRATION,
-                DEFAULT_PREAUTHORIZED_URI_EXPIRATION);
+        mPreAuthorizedUriDuration = DEFAULT_PREAUTHORIZED_URI_EXPIRATION;
 
         scheduleBackgroundTask(BACKGROUND_TASK_INITIALIZE);
         scheduleBackgroundTask(BACKGROUND_TASK_UPDATE_ACCOUNTS);
@@ -1521,6 +1511,8 @@
     }
 
     protected void performBackgroundTask(int task, Object arg) {
+        // Make sure we operate on the contacts db by default.
+        switchToContactMode();
         switch (task) {
             case BACKGROUND_TASK_INITIALIZE: {
                 initForDefaultLocale();
@@ -1551,6 +1543,8 @@
                 switchToProfileMode();
                 accountsChanged |= updateAccountsInBackground(accounts);
 
+                switchToContactMode();
+
                 updateContactsAccountCount(accounts);
                 updateDirectoriesInBackground(accountsChanged);
                 break;
@@ -1569,6 +1563,7 @@
             case BACKGROUND_TASK_UPGRADE_AGGREGATION_ALGORITHM: {
                 if (isAggregationUpgradeNeeded()) {
                     upgradeAggregationAlgorithmInBackground();
+                    invalidateFastScrollingIndexCache();
                 }
                 break;
             }
@@ -1601,6 +1596,8 @@
                     cleanupPhotoStore();
                     switchToProfileMode();
                     cleanupPhotoStore();
+
+                    switchToContactMode(); // Switch to the default, just in case.
                     break;
                 }
             }
@@ -1641,6 +1638,7 @@
         setProviderStatus(ProviderStatus.STATUS_CHANGING_LOCALE);
         mContactsHelper.setLocale(this, currentLocale);
         mProfileHelper.setLocale(this, currentLocale);
+        mSearchIndexManager.updateIndex(true);
         prefs.edit().putString(PREF_LOCALE, currentLocale.toString()).apply();
         invalidateFastScrollingIndexCache();
         setProviderStatus(providerStatus);
@@ -1671,7 +1669,7 @@
     }
 
     protected void updateSearchIndexInBackground() {
-        mSearchIndexManager.updateIndex();
+        mSearchIndexManager.updateIndex(false);
     }
 
     protected void updateDirectoriesInBackground(boolean rescan) {
@@ -1705,8 +1703,7 @@
 
     @VisibleForTesting
     protected void cleanupPhotoStore() {
-        SQLiteDatabase db = mDbHelper.get().getWritableDatabase();
-        mActiveDb.set(db);
+        final SQLiteDatabase db = mDbHelper.get().getWritableDatabase();
 
         // Assemble the set of photo store file IDs that are in use, and send those to the photo
         // store.  Any photos that aren't in that set will be deleted, and any photos that no
@@ -1759,7 +1756,9 @@
         // using internal APIs or direct DB access to avoid permission errors.
         if (!missingPhotoIds.isEmpty()) {
             try {
-                db.beginTransactionWithListener(this);
+                // Need to set the db listener because we need to run onCommit afterwards.
+                // Make sure to use the proper listener depending on the current mode.
+                db.beginTransactionWithListener(inProfileMode() ? mProfileProvider : this);
                 for (long missingPhotoId : missingPhotoIds) {
                     if (photoFileIdToDataId.containsKey(missingPhotoId)) {
                         long dataId = photoFileIdToDataId.get(missingPhotoId);
@@ -1796,7 +1795,7 @@
         return mTransactionHolder;
     }
 
-    public ProfileProvider getProfileProvider() {
+    public ProfileProvider newProfileProvider() {
         return new ProfileProvider(this);
     }
 
@@ -1835,7 +1834,8 @@
         return Locale.getDefault();
     }
 
-    private boolean inProfileMode() {
+    @VisibleForTesting
+    final boolean inProfileMode() {
         Boolean profileMode = mInProfileMode.get();
         return profileMode != null && profileMode;
     }
@@ -1921,7 +1921,10 @@
      * Switches the provider's thread-local context variables to prepare for performing
      * a profile operation.
      */
-    protected void switchToProfileMode() {
+    private void switchToProfileMode() {
+        if (ENABLE_TRANSACTION_LOG) {
+            Log.i(TAG, "switchToProfileMode", new RuntimeException("switchToProfileMode"));
+        }
         mDbHelper.set(mProfileHelper);
         mTransactionContext.set(mProfileTransactionContext);
         mAggregator.set(mProfileAggregator);
@@ -1933,15 +1936,15 @@
      * Switches the provider's thread-local context variables to prepare for performing
      * a contacts operation.
      */
-    protected void switchToContactMode() {
+    private void switchToContactMode() {
+        if (ENABLE_TRANSACTION_LOG) {
+            Log.i(TAG, "switchToContactMode", new RuntimeException("switchToContactMode"));
+        }
         mDbHelper.set(mContactsHelper);
         mTransactionContext.set(mContactTransactionContext);
         mAggregator.set(mContactAggregator);
         mPhotoStore.set(mContactsPhotoStore);
         mInProfileMode.set(false);
-
-        // Clear out the active database; modification operations will set this to the contacts DB.
-        mActiveDb.set(null);
     }
 
     @Override
@@ -2000,17 +2003,10 @@
         }
     }
 
-    /**
-     * Replaces the current (thread-local) database to use for the operation with the given one.
-     * @param db The database to use.
-     */
-    /* package */ void substituteDb(SQLiteDatabase db) {
-        mActiveDb.set(db);
-    }
-
     @Override
     public Bundle call(String method, String arg, Bundle extras) {
         waitForAccess(mReadAccessLatch);
+        switchToContactMode();
         if (method.equals(Authorization.AUTHORIZATION_METHOD)) {
             Uri uri = (Uri) extras.getParcelable(Authorization.KEY_URI_TO_AUTHORIZE);
 
@@ -2110,13 +2106,20 @@
 
     @Override
     public void onBegin() {
-        if (VERBOSE_LOGGING) {
-            Log.v(TAG, "onBeginTransaction: " + (inProfileMode() ? "profile" : "contacts"));
+        onBeginTransactionInternal(false);
+    }
+
+    protected void onBeginTransactionInternal(boolean forProfile) {
+        if (ENABLE_TRANSACTION_LOG) {
+            Log.i(TAG, "onBeginTransaction: " + (forProfile ? "profile" : "contacts"),
+                    new RuntimeException("onBeginTransactionInternal"));
         }
-        if (inProfileMode()) {
+        if (forProfile) {
+            switchToProfileMode();
             mProfileAggregator.clearPendingAggregations();
             mProfileTransactionContext.clearExceptSearchIndexUpdates();
         } else {
+            switchToContactMode();
             mContactAggregator.clearPendingAggregations();
             mContactTransactionContext.clearExceptSearchIndexUpdates();
         }
@@ -2124,11 +2127,23 @@
 
     @Override
     public void onCommit() {
-        if (VERBOSE_LOGGING) {
-            Log.v(TAG, "beforeTransactionCommit: " + (inProfileMode() ? "profile" : "contacts"));
+        onCommitTransactionInternal(false);
+    }
+
+    protected void onCommitTransactionInternal(boolean forProfile) {
+        if (ENABLE_TRANSACTION_LOG) {
+            Log.i(TAG, "onCommitTransactionInternal: " + (forProfile ? "profile" : "contacts"),
+                    new RuntimeException("onCommitTransactionInternal"));
         }
+        if (forProfile) {
+            switchToProfileMode();
+        } else {
+            switchToContactMode();
+        }
+
         flushTransactionalChanges();
-        mAggregator.get().aggregateInTransaction(mTransactionContext.get(), mActiveDb.get());
+        final SQLiteDatabase db = mDbHelper.get().getWritableDatabase();
+        mAggregator.get().aggregateInTransaction(mTransactionContext.get(), db);
         if (mVisibleTouched) {
             mVisibleTouched = false;
             mDbHelper.get().updateAllVisible();
@@ -2147,13 +2162,21 @@
 
     @Override
     public void onRollback() {
-        if (VERBOSE_LOGGING) {
-            Log.v(TAG, "beforeTransactionRollback: " + (inProfileMode() ? "profile" : "contacts"));
+        onRollbackTransactionInternal(false);
+    }
+
+    protected void onRollbackTransactionInternal(boolean forProfile) {
+        if (ENABLE_TRANSACTION_LOG) {
+            Log.i(TAG, "onRollbackTransactionInternal: " + (forProfile ? "profile" : "contacts"),
+                    new RuntimeException("onRollbackTransactionInternal"));
         }
-        // mDbHelper may not be pointing at the "right" db helper due to a bug,
-        // so we invalidate both for now.
-        mContactsHelper.invalidateAllCache();
-        mProfileHelper.invalidateAllCache();
+        if (forProfile) {
+            switchToProfileMode();
+        } else {
+            switchToContactMode();
+        }
+
+        mDbHelper.get().invalidateAllCache();
     }
 
     private void updateSearchIndexInTransaction() {
@@ -2167,12 +2190,13 @@
 
     private void flushTransactionalChanges() {
         if (VERBOSE_LOGGING) {
-            Log.v(TAG, "flushTransactionChanges");
+            Log.v(TAG, "flushTransactionalChanges: " + (inProfileMode() ? "profile" : "contacts"));
         }
 
+        final SQLiteDatabase db = mDbHelper.get().getWritableDatabase();
         for (long rawContactId : mTransactionContext.get().getInsertedRawContactIds()) {
-            mDbHelper.get().updateRawContactDisplayName(mActiveDb.get(), rawContactId);
-            mAggregator.get().onRawContactInsert(mTransactionContext.get(), mActiveDb.get(),
+            mDbHelper.get().updateRawContactDisplayName(db, rawContactId);
+            mAggregator.get().onRawContactInsert(mTransactionContext.get(), db,
                     rawContactId);
         }
 
@@ -2182,7 +2206,7 @@
             mSb.append(UPDATE_RAW_CONTACT_SET_DIRTY_SQL);
             appendIds(mSb, dirtyRawContacts);
             mSb.append(")");
-            mActiveDb.get().execSQL(mSb.toString());
+            db.execSQL(mSb.toString());
         }
 
         Set<Long> updatedRawContacts = mTransactionContext.get().getUpdatedRawContactIds();
@@ -2191,13 +2215,13 @@
             mSb.append(UPDATE_RAW_CONTACT_SET_VERSION_SQL);
             appendIds(mSb, updatedRawContacts);
             mSb.append(")");
-            mActiveDb.get().execSQL(mSb.toString());
+            db.execSQL(mSb.toString());
         }
 
         // Update sync states.
         for (Map.Entry<Long, Object> entry : mTransactionContext.get().getUpdatedSyncStates()) {
             long id = entry.getKey();
-            if (mDbHelper.get().getSyncState().update(mActiveDb.get(), id, entry.getValue()) <= 0) {
+            if (mDbHelper.get().getSyncState().update(db, id, entry.getValue()) <= 0) {
                 throw new IllegalStateException(
                         "unable to update sync state, does it still exist?");
             }
@@ -2265,10 +2289,7 @@
             Log.v(TAG, "insertInTransaction: uri=" + uri + "  values=[" + values + "]");
         }
 
-        // Default active DB to the contacts DB if none has been set.
-        if (mActiveDb.get() == null) {
-            mActiveDb.set(mContactsHelper.getWritableDatabase());
-        }
+        final SQLiteDatabase db = mDbHelper.get().getWritableDatabase();
 
         final boolean callerIsSyncAdapter =
                 readBooleanQueryParameter(uri, ContactsContract.CALLER_IS_SYNCADAPTER, false);
@@ -2279,7 +2300,7 @@
         switch (match) {
             case SYNCSTATE:
             case PROFILE_SYNCSTATE:
-                id = mDbHelper.get().getSyncState().insert(mActiveDb.get(), values);
+                id = mDbHelper.get().getSyncState().insert(db, values);
                 break;
 
             case CONTACTS: {
@@ -2506,7 +2527,9 @@
             mValues.put(RawContacts.AGGREGATION_MODE, RawContacts.AGGREGATION_MODE_DISABLED);
         }
 
-        long rawContactId = mActiveDb.get().insert(Tables.RAW_CONTACTS,
+        final SQLiteDatabase db = mDbHelper.get().getWritableDatabase();
+
+        long rawContactId = db.insert(Tables.RAW_CONTACTS,
                 RawContacts.CONTACT_ID, mValues);
         int aggregationMode = RawContacts.AGGREGATION_MODE_DEFAULT;
         if (mValues.containsKey(RawContacts.AGGREGATION_MODE)) {
@@ -2538,7 +2561,8 @@
     }
 
     private Long findGroupByRawContactId(String selection, long rawContactId) {
-        Cursor c = mActiveDb.get().query(Tables.GROUPS + "," + Tables.RAW_CONTACTS,
+        final SQLiteDatabase db = mDbHelper.get().getReadableDatabase();
+        Cursor c = db.query(Tables.GROUPS + "," + Tables.RAW_CONTACTS,
                 PROJECTION_GROUP_ID, selection,
                 new String[]{Long.toString(rawContactId)},
                 null /* groupBy */, null /* having */, null /* orderBy */);
@@ -2570,7 +2594,8 @@
         groupMembershipValues.put(GroupMembership.RAW_CONTACT_ID, rawContactId);
         groupMembershipValues.put(DataColumns.MIMETYPE_ID,
                 mDbHelper.get().getMimeTypeId(GroupMembership.CONTENT_ITEM_TYPE));
-        mActiveDb.get().insert(Tables.DATA, null, groupMembershipValues);
+        final SQLiteDatabase db = mDbHelper.get().getWritableDatabase();
+        db.insert(Tables.DATA, null, groupMembershipValues);
     }
 
     private void deleteDataGroupMembership(long rawContactId, long groupId) {
@@ -2578,7 +2603,8 @@
                 Long.toString(mDbHelper.get().getMimeTypeId(GroupMembership.CONTENT_ITEM_TYPE)),
                 Long.toString(groupId),
                 Long.toString(rawContactId)};
-        mActiveDb.get().delete(Tables.DATA, SELECTION_GROUPMEMBERSHIP_DATA, selectionArgs);
+        final SQLiteDatabase db = mDbHelper.get().getWritableDatabase();
+        db.delete(Tables.DATA, SELECTION_GROUPMEMBERSHIP_DATA, selectionArgs);
     }
 
     /**
@@ -2611,7 +2637,8 @@
         mValues.remove(Data.MIMETYPE);
 
         DataRowHandler rowHandler = getDataRowHandler(mimeType);
-        id = rowHandler.insert(mActiveDb.get(), mTransactionContext.get(), rawContactId, mValues);
+        final SQLiteDatabase db = mDbHelper.get().getWritableDatabase();
+        id = rowHandler.insert(db, mTransactionContext.get(), rawContactId, mValues);
         if (!callerIsSyncAdapter) {
             mTransactionContext.get().markRawContactDirty(rawContactId);
         }
@@ -2642,7 +2669,8 @@
         mValues.remove(RawContacts.ACCOUNT_TYPE);
 
         // Insert the new stream item.
-        id = mActiveDb.get().insert(Tables.STREAM_ITEMS, null, mValues);
+        final SQLiteDatabase db = mDbHelper.get().getWritableDatabase();
+        id = db.insert(Tables.STREAM_ITEMS, null, mValues);
         if (id == -1) {
             // Insertion failed.
             return 0;
@@ -2682,7 +2710,8 @@
             // Process the photo and store it.
             if (processStreamItemPhoto(mValues, false)) {
                 // Insert the stream item photo.
-                id = mActiveDb.get().insert(Tables.STREAM_ITEM_PHOTOS, null, mValues);
+                final SQLiteDatabase db = mDbHelper.get().getWritableDatabase();
+                id = db.insert(Tables.STREAM_ITEM_PHOTOS, null, mValues);
             }
         }
         return id;
@@ -2734,7 +2763,8 @@
      */
     private long lookupRawContactIdForStreamId(long streamItemId) {
         long rawContactId = -1;
-        Cursor c = mActiveDb.get().query(Tables.STREAM_ITEMS,
+        final SQLiteDatabase db = mDbHelper.get().getReadableDatabase();
+        Cursor c = db.query(Tables.STREAM_ITEMS,
                 new String[]{StreamItems.RAW_CONTACT_ID},
                 StreamItems._ID + "=?", new String[]{String.valueOf(streamItemId)},
                 null, null, null);
@@ -2786,7 +2816,8 @@
      */
     private long cleanUpOldStreamItems(long rawContactId, long insertedStreamItemId) {
         long postCleanupInsertedStreamId = insertedStreamItemId;
-        Cursor c = mActiveDb.get().query(Tables.STREAM_ITEMS, new String[]{StreamItems._ID},
+        final SQLiteDatabase db = mDbHelper.get().getWritableDatabase();
+        Cursor c = db.query(Tables.STREAM_ITEMS, new String[]{StreamItems._ID},
                 StreamItems.RAW_CONTACT_ID + "=?", new String[]{String.valueOf(rawContactId)},
                 null, null, StreamItems.TIMESTAMP + " DESC, " + StreamItems._ID + " DESC");
         try {
@@ -2802,7 +2833,7 @@
                         // The stream item just inserted is being deleted.
                         postCleanupInsertedStreamId = 0;
                     }
-                    deleteStreamItem(c.getLong(0));
+                    deleteStreamItem(db, c.getLong(0));
                     c.moveToPrevious();
                 }
             }
@@ -2818,6 +2849,8 @@
     private int deleteData(String selection, String[] selectionArgs, boolean callerIsSyncAdapter) {
         int count = 0;
 
+        final SQLiteDatabase db = mDbHelper.get().getWritableDatabase();
+
         // Note that the query will return data according to the access restrictions,
         // so we don't need to worry about deleting data we don't have permission to read.
         Uri dataUri = inProfileMode()
@@ -2830,7 +2863,7 @@
                 long rawContactId = c.getLong(DataRowHandler.DataDeleteQuery.RAW_CONTACT_ID);
                 String mimeType = c.getString(DataRowHandler.DataDeleteQuery.MIMETYPE);
                 DataRowHandler rowHandler = getDataRowHandler(mimeType);
-                count += rowHandler.delete(mActiveDb.get(), mTransactionContext.get(), c);
+                count += rowHandler.delete(db, mTransactionContext.get(), c);
                 if (!callerIsSyncAdapter) {
                     mTransactionContext.get().markRawContactDirty(rawContactId);
                 }
@@ -2847,6 +2880,8 @@
      */
     public int deleteData(long dataId, String[] allowedMimeTypes) {
 
+        final SQLiteDatabase db = mDbHelper.get().getWritableDatabase();
+
         // Note that the query will return data according to the access restrictions,
         // so we don't need to worry about deleting data we don't have permission to read.
         mSelectionArgs1[0] = String.valueOf(dataId);
@@ -2872,7 +2907,7 @@
                         + Lists.newArrayList(allowedMimeTypes));
             }
             DataRowHandler rowHandler = getDataRowHandler(mimeType);
-            return rowHandler.delete(mActiveDb.get(), mTransactionContext.get(), c);
+            return rowHandler.delete(db, mTransactionContext.get(), c);
         } finally {
             c.close();
         }
@@ -2907,12 +2942,14 @@
             mValues.put(Groups.DIRTY, 1);
         }
 
-        long result = mActiveDb.get().insert(Tables.GROUPS, Groups.TITLE, mValues);
+        final SQLiteDatabase db = mDbHelper.get().getWritableDatabase();
+
+        long result = db.insert(Tables.GROUPS, Groups.TITLE, mValues);
 
         if (!callerIsSyncAdapter && isFavoritesGroup) {
             // If the inserted group is a favorite group, add all starred raw contacts to it.
             mSelectionArgs1[0] = Long.toString(accountId);
-            Cursor c = mActiveDb.get().query(Tables.RAW_CONTACTS,
+            Cursor c = db.query(Tables.RAW_CONTACTS,
                     new String[]{RawContacts._ID, RawContacts.STARRED},
                     RawContactsColumns.CONCRETE_ACCOUNT_ID + "=?", mSelectionArgs1,
                     null, null, null);
@@ -2975,8 +3012,10 @@
             c.close();
         }
 
+        final SQLiteDatabase db = mDbHelper.get().getWritableDatabase();
+
         // If we didn't find a duplicate, we're fine to insert.
-        final long id = mActiveDb.get().insert(Tables.SETTINGS, null, values);
+        final long id = db.insert(Tables.SETTINGS, null, values);
 
         if (values.containsKey(Settings.UNGROUPED_VISIBLE)) {
             mVisibleTouched = true;
@@ -2993,6 +3032,8 @@
         final Integer protocol = values.getAsInteger(StatusUpdates.PROTOCOL);
         String customProtocol = null;
 
+        final SQLiteDatabase db = mDbHelper.get().getWritableDatabase();
+
         if (protocol != null && protocol == Im.PROTOCOL_CUSTOM) {
             customProtocol = values.getAsString(StatusUpdates.CUSTOM_PROTOCOL);
             if (TextUtils.isEmpty(customProtocol)) {
@@ -3068,7 +3109,7 @@
 
         Cursor cursor = null;
         try {
-            cursor = mActiveDb.get().query(DataContactsQuery.TABLE, DataContactsQuery.PROJECTION,
+            cursor = db.query(DataContactsQuery.TABLE, DataContactsQuery.PROJECTION,
                     mSb.toString(), mSelectionArgs.toArray(EMPTY_STRING_ARRAY), null, null,
                     Clauses.CONTACT_VISIBLE + " DESC, " + Data.RAW_CONTACT_ID);
             if (cursor.moveToFirst()) {
@@ -3110,7 +3151,7 @@
                     values.getAsString(StatusUpdates.CHAT_CAPABILITY));
 
             // Insert the presence update
-            mActiveDb.get().replace(Tables.PRESENCE, null, mValues);
+            db.replace(Tables.PRESENCE, null, mValues);
         }
 
 
@@ -3237,10 +3278,7 @@
                     "  selection=[" + selection + "]  args=" + Arrays.toString(selectionArgs));
         }
 
-        // Default active DB to the contacts DB if none has been set.
-        if (mActiveDb.get() == null) {
-            mActiveDb.set(mContactsHelper.getWritableDatabase());
-        }
+        final SQLiteDatabase db = mDbHelper.get().getWritableDatabase();
 
         flushTransactionalChanges();
         final boolean callerIsSyncAdapter =
@@ -3249,14 +3287,14 @@
         switch (match) {
             case SYNCSTATE:
             case PROFILE_SYNCSTATE:
-                return mDbHelper.get().getSyncState().delete(mActiveDb.get(), selection,
+                return mDbHelper.get().getSyncState().delete(db, selection,
                         selectionArgs);
 
             case SYNCSTATE_ID: {
                 String selectionWithId =
                         (SyncStateContract.Columns._ID + "=" + ContentUris.parseId(uri) + " ")
                         + (selection == null ? "" : " AND (" + selection + ")");
-                return mDbHelper.get().getSyncState().delete(mActiveDb.get(), selectionWithId,
+                return mDbHelper.get().getSyncState().delete(db, selectionWithId,
                         selectionArgs);
             }
 
@@ -3264,7 +3302,7 @@
                 String selectionWithId =
                         (SyncStateContract.Columns._ID + "=" + ContentUris.parseId(uri) + " ")
                         + (selection == null ? "" : " AND (" + selection + ")");
-                return mProfileHelper.getSyncState().delete(mActiveDb.get(), selectionWithId,
+                return mProfileHelper.getSyncState().delete(db, selectionWithId,
                         selectionArgs);
             }
 
@@ -3289,7 +3327,7 @@
                             "Missing a lookup key", uri));
                 }
                 final String lookupKey = pathSegments.get(2);
-                final long contactId = lookupContactIdByLookupKey(mActiveDb.get(), lookupKey);
+                final long contactId = lookupContactIdByLookupKey(db, lookupKey);
                 return deleteContact(contactId, callerIsSyncAdapter);
             }
 
@@ -3311,7 +3349,7 @@
                 args[0] = String.valueOf(contactId);
                 args[1] = Uri.encode(lookupKey);
                 lookupQb.appendWhere(Contacts._ID + "=? AND " + Contacts.LOOKUP_KEY + "=?");
-                Cursor c = query(mActiveDb.get(), lookupQb, null, selection, args, null, null,
+                Cursor c = query(db, lookupQb, null, selection, args, null, null,
                         null, null, null);
                 try {
                     if (c.getCount() == 1) {
@@ -3335,7 +3373,7 @@
             case PROFILE_RAW_CONTACTS: {
                 invalidateFastScrollingIndexCache();
                 int numDeletes = 0;
-                Cursor c = mActiveDb.get().query(Views.RAW_CONTACTS,
+                Cursor c = db.query(Views.RAW_CONTACTS,
                         new String[]{RawContacts._ID, RawContacts.CONTACT_ID},
                         appendAccountIdToSelection(uri, selection), selectionArgs,
                         null, null, null);
@@ -3388,7 +3426,7 @@
 
             case GROUPS: {
                 int numDeletes = 0;
-                Cursor c = mActiveDb.get().query(Views.GROUPS, Projections.ID,
+                Cursor c = db.query(Views.GROUPS, Projections.ID,
                         appendAccountIdToSelection(uri, selection), selectionArgs,
                         null, null, null);
                 try {
@@ -3464,21 +3502,22 @@
     }
 
     public int deleteGroup(Uri uri, long groupId, boolean callerIsSyncAdapter) {
+        final SQLiteDatabase db = mDbHelper.get().getWritableDatabase();
         mGroupIdCache.clear();
         final long groupMembershipMimetypeId = mDbHelper.get()
                 .getMimeTypeId(GroupMembership.CONTENT_ITEM_TYPE);
-        mActiveDb.get().delete(Tables.DATA, DataColumns.MIMETYPE_ID + "="
+        db.delete(Tables.DATA, DataColumns.MIMETYPE_ID + "="
                 + groupMembershipMimetypeId + " AND " + GroupMembership.GROUP_ROW_ID + "="
                 + groupId, null);
 
         try {
             if (callerIsSyncAdapter) {
-                return mActiveDb.get().delete(Tables.GROUPS, Groups._ID + "=" + groupId, null);
+                return db.delete(Tables.GROUPS, Groups._ID + "=" + groupId, null);
             } else {
                 mValues.clear();
                 mValues.put(Groups.DELETED, 1);
                 mValues.put(Groups.DIRTY, 1);
-                return mActiveDb.get().update(Tables.GROUPS, mValues, Groups._ID + "=" + groupId,
+                return db.update(Tables.GROUPS, mValues, Groups._ID + "=" + groupId,
                         null);
             }
         } finally {
@@ -3487,20 +3526,22 @@
     }
 
     private int deleteSettings(Uri uri, String selection, String[] selectionArgs) {
-        final int count = mActiveDb.get().delete(Tables.SETTINGS, selection, selectionArgs);
+        final SQLiteDatabase db = mDbHelper.get().getWritableDatabase();
+        final int count = db.delete(Tables.SETTINGS, selection, selectionArgs);
         mVisibleTouched = true;
         return count;
     }
 
     private int deleteContact(long contactId, boolean callerIsSyncAdapter) {
+        final SQLiteDatabase db = mDbHelper.get().getWritableDatabase();
         mSelectionArgs1[0] = Long.toString(contactId);
-        Cursor c = mActiveDb.get().query(Tables.RAW_CONTACTS, new String[]{RawContacts._ID},
+        Cursor c = db.query(Tables.RAW_CONTACTS, new String[]{RawContacts._ID},
                 RawContacts.CONTACT_ID + "=?", mSelectionArgs1,
                 null, null, null);
         try {
             while (c.moveToNext()) {
                 long rawContactId = c.getLong(0);
-                markRawContactAsDeleted(rawContactId, callerIsSyncAdapter);
+                markRawContactAsDeleted(db, rawContactId, callerIsSyncAdapter);
             }
         } finally {
             c.close();
@@ -3508,36 +3549,38 @@
 
         mProviderStatusUpdateNeeded = true;
 
-        return mActiveDb.get().delete(Tables.CONTACTS, Contacts._ID + "=" + contactId, null);
+        return db.delete(Tables.CONTACTS, Contacts._ID + "=" + contactId, null);
     }
 
     public int deleteRawContact(long rawContactId, long contactId, boolean callerIsSyncAdapter) {
         mAggregator.get().invalidateAggregationExceptionCache();
         mProviderStatusUpdateNeeded = true;
 
+        final SQLiteDatabase db = mDbHelper.get().getWritableDatabase();
+
         // Find and delete stream items associated with the raw contact.
-        Cursor c = mActiveDb.get().query(Tables.STREAM_ITEMS,
+        Cursor c = db.query(Tables.STREAM_ITEMS,
                 new String[]{StreamItems._ID},
                 StreamItems.RAW_CONTACT_ID + "=?", new String[]{String.valueOf(rawContactId)},
                 null, null, null);
         try {
             while (c.moveToNext()) {
-                deleteStreamItem(c.getLong(0));
+                deleteStreamItem(db, c.getLong(0));
             }
         } finally {
             c.close();
         }
 
         if (callerIsSyncAdapter || rawContactIsLocal(rawContactId)) {
-            mActiveDb.get().delete(Tables.PRESENCE,
+            db.delete(Tables.PRESENCE,
                     PresenceColumns.RAW_CONTACT_ID + "=" + rawContactId, null);
-            int count = mActiveDb.get().delete(Tables.RAW_CONTACTS,
+            int count = db.delete(Tables.RAW_CONTACTS,
                     RawContacts._ID + "=" + rawContactId, null);
             mAggregator.get().updateAggregateData(mTransactionContext.get(), contactId);
             return count;
         } else {
             mDbHelper.get().removeContactIfSingleton(rawContactId);
-            return markRawContactAsDeleted(rawContactId, callerIsSyncAdapter);
+            return markRawContactAsDeleted(db, rawContactId, callerIsSyncAdapter);
         }
     }
 
@@ -3545,7 +3588,8 @@
      * Returns whether the given raw contact ID is local (i.e. has no account associated with it).
      */
     private boolean rawContactIsLocal(long rawContactId) {
-        Cursor c = mActiveDb.get().query(Tables.RAW_CONTACTS, Projections.LITERAL_ONE,
+        final SQLiteDatabase db = mDbHelper.get().getReadableDatabase();
+        Cursor c = db.query(Tables.RAW_CONTACTS, Projections.LITERAL_ONE,
                 RawContactsColumns.CONCRETE_ID + "=? AND " +
                 RawContactsColumns.ACCOUNT_ID + "=" + Clauses.LOCAL_ACCOUNT_ID,
                 new String[] {String.valueOf(rawContactId)}, null, null, null);
@@ -3562,20 +3606,22 @@
       if (VERBOSE_LOGGING) {
           Log.v(TAG, "deleting data from status_updates for " + selection);
       }
-      mActiveDb.get().delete(Tables.STATUS_UPDATES, getWhereClauseForStatusUpdatesTable(selection),
+      final SQLiteDatabase db = mDbHelper.get().getWritableDatabase();
+      db.delete(Tables.STATUS_UPDATES, getWhereClauseForStatusUpdatesTable(selection),
           selectionArgs);
-      return mActiveDb.get().delete(Tables.PRESENCE, selection, selectionArgs);
+      return db.delete(Tables.PRESENCE, selection, selectionArgs);
     }
 
     private int deleteStreamItems(Uri uri, ContentValues values, String selection,
             String[] selectionArgs) {
+        final SQLiteDatabase db = mDbHelper.get().getWritableDatabase();
         int count = 0;
-        final Cursor c = mActiveDb.get().query(Views.STREAM_ITEMS, Projections.ID,
+        final Cursor c = db.query(Views.STREAM_ITEMS, Projections.ID,
                 selection, selectionArgs, null, null, null);
         try {
             c.moveToPosition(-1);
             while (c.moveToNext()) {
-                count += deleteStreamItem(c.getLong(0));
+                count += deleteStreamItem(db, c.getLong(0));
             }
         } finally {
             c.close();
@@ -3583,25 +3629,28 @@
         return count;
     }
 
-    private int deleteStreamItem(long streamItemId) {
+    private int deleteStreamItem(SQLiteDatabase db, long streamItemId) {
         deleteStreamItemPhotos(streamItemId);
-        return mActiveDb.get().delete(Tables.STREAM_ITEMS, StreamItems._ID + "=?",
+        return db.delete(Tables.STREAM_ITEMS, StreamItems._ID + "=?",
                 new String[]{String.valueOf(streamItemId)});
     }
 
     private int deleteStreamItemPhotos(Uri uri, ContentValues values, String selection,
             String[] selectionArgs) {
-        return mActiveDb.get().delete(Tables.STREAM_ITEM_PHOTOS, selection, selectionArgs);
+        final SQLiteDatabase db = mDbHelper.get().getWritableDatabase();
+        return db.delete(Tables.STREAM_ITEM_PHOTOS, selection, selectionArgs);
     }
 
     private int deleteStreamItemPhotos(long streamItemId) {
+        final SQLiteDatabase db = mDbHelper.get().getWritableDatabase();
         // Note that this does not enforce the modifying account.
-        return mActiveDb.get().delete(Tables.STREAM_ITEM_PHOTOS,
+        return db.delete(Tables.STREAM_ITEM_PHOTOS,
                 StreamItemPhotos.STREAM_ITEM_ID + "=?",
                 new String[]{String.valueOf(streamItemId)});
     }
 
-    private int markRawContactAsDeleted(long rawContactId, boolean callerIsSyncAdapter) {
+    private int markRawContactAsDeleted(SQLiteDatabase db, long rawContactId,
+            boolean callerIsSyncAdapter) {
         mSyncToNetwork = true;
 
         mValues.clear();
@@ -3610,11 +3659,11 @@
         mValues.put(RawContactsColumns.AGGREGATION_NEEDED, 1);
         mValues.putNull(RawContacts.CONTACT_ID);
         mValues.put(RawContacts.DIRTY, 1);
-        return updateRawContact(rawContactId, mValues, callerIsSyncAdapter);
+        return updateRawContact(db, rawContactId, mValues, callerIsSyncAdapter);
     }
 
     private int deleteDataUsage() {
-        final SQLiteDatabase db = mActiveDb.get();
+        final SQLiteDatabase db = mDbHelper.get().getWritableDatabase();
         db.execSQL("UPDATE " + Tables.RAW_CONTACTS + " SET " +
                 Contacts.TIMES_CONTACTED + "=0," +
                 Contacts.LAST_TIME_CONTACTED + "=NULL"
@@ -3637,10 +3686,7 @@
                     "  values=[" + values + "]");
         }
 
-        // Default active DB to the contacts DB if none has been set.
-        if (mActiveDb.get() == null) {
-            mActiveDb.set(mContactsHelper.getWritableDatabase());
-        }
+        final SQLiteDatabase db = mDbHelper.get().getWritableDatabase();
 
         int count = 0;
 
@@ -3657,7 +3703,7 @@
         switch(match) {
             case SYNCSTATE:
             case PROFILE_SYNCSTATE:
-                return mDbHelper.get().getSyncState().update(mActiveDb.get(), values,
+                return mDbHelper.get().getSyncState().update(db, values,
                         appendAccountToSelection(uri, selection), selectionArgs);
 
             case SYNCSTATE_ID: {
@@ -3665,7 +3711,7 @@
                 String selectionWithId =
                         (SyncStateContract.Columns._ID + "=" + ContentUris.parseId(uri) + " ")
                         + (selection == null ? "" : " AND (" + selection + ")");
-                return mDbHelper.get().getSyncState().update(mActiveDb.get(), values,
+                return mDbHelper.get().getSyncState().update(db, values,
                         selectionWithId, selectionArgs);
             }
 
@@ -3674,7 +3720,7 @@
                 String selectionWithId =
                         (SyncStateContract.Columns._ID + "=" + ContentUris.parseId(uri) + " ")
                         + (selection == null ? "" : " AND (" + selection + ")");
-                return mProfileHelper.getSyncState().update(mActiveDb.get(), values,
+                return mProfileHelper.getSyncState().update(db, values,
                         selectionWithId, selectionArgs);
             }
 
@@ -3687,7 +3733,8 @@
 
             case CONTACTS_ID: {
                 invalidateFastScrollingIndexCache();
-                count = updateContactOptions(ContentUris.parseId(uri), values, callerIsSyncAdapter);
+                count = updateContactOptions(db, ContentUris.parseId(uri), values,
+                        callerIsSyncAdapter);
                 break;
             }
 
@@ -3701,8 +3748,8 @@
                             "Missing a lookup key", uri));
                 }
                 final String lookupKey = pathSegments.get(2);
-                final long contactId = lookupContactIdByLookupKey(mActiveDb.get(), lookupKey);
-                count = updateContactOptions(contactId, values, callerIsSyncAdapter);
+                final long contactId = lookupContactIdByLookupKey(db, lookupKey);
+                count = updateContactOptions(db, contactId, values, callerIsSyncAdapter);
                 break;
             }
 
@@ -3790,7 +3837,8 @@
             }
 
             case AGGREGATION_EXCEPTIONS: {
-                count = updateAggregationException(mActiveDb.get(), values);
+                count = updateAggregationException(db, values);
+                invalidateFastScrollingIndexCache();
                 break;
             }
 
@@ -3875,12 +3923,13 @@
 
     private int updateStatusUpdate(Uri uri, ContentValues values, String selection,
         String[] selectionArgs) {
+        final SQLiteDatabase db = mDbHelper.get().getWritableDatabase();
         // update status_updates table, if status is provided
         // TODO should account type/name be appended to the where clause?
         int updateCount = 0;
         ContentValues settableValues = getSettableColumnsForStatusUpdatesTable(values);
         if (settableValues.size() > 0) {
-          updateCount = mActiveDb.get().update(Tables.STATUS_UPDATES,
+          updateCount = db.update(Tables.STATUS_UPDATES,
                     settableValues,
                     getWhereClauseForStatusUpdatesTable(selection),
                     selectionArgs);
@@ -3889,7 +3938,7 @@
         // now update the Presence table
         settableValues = getSettableColumnsForPresenceTable(values);
         if (settableValues.size() > 0) {
-          updateCount = mActiveDb.get().update(Tables.PRESENCE, settableValues,
+          updateCount = db.update(Tables.PRESENCE, settableValues,
                     selection, selectionArgs);
         }
         // TODO updateCount is not entirely a valid count of updated rows because 2 tables could
@@ -3906,8 +3955,10 @@
         values.remove(RawContacts.ACCOUNT_NAME);
         values.remove(RawContacts.ACCOUNT_TYPE);
 
+        final SQLiteDatabase db = mDbHelper.get().getWritableDatabase();
+
         // If there's been no exception, the update should be fine.
-        return mActiveDb.get().update(Tables.STREAM_ITEMS, values, selection, selectionArgs);
+        return db.update(Tables.STREAM_ITEMS, values, selection, selectionArgs);
     }
 
     private int updateStreamItemPhotos(Uri uri, ContentValues values, String selection,
@@ -3920,10 +3971,12 @@
         values.remove(RawContacts.ACCOUNT_NAME);
         values.remove(RawContacts.ACCOUNT_TYPE);
 
+        final SQLiteDatabase db = mDbHelper.get().getWritableDatabase();
+
         // Process the photo (since we're updating, it's valid for the photo to not be present).
         if (processStreamItemPhoto(values, true)) {
             // If there's been no exception, the update should be fine.
-            return mActiveDb.get().update(Tables.STREAM_ITEM_PHOTOS, values, selection,
+            return db.update(Tables.STREAM_ITEM_PHOTOS, values, selection,
                     selectionArgs);
         }
         return 0;
@@ -3983,7 +4036,7 @@
             String[] selectionArgs, boolean callerIsSyncAdapter) {
         mGroupIdCache.clear();
 
-        final SQLiteDatabase db = mActiveDb.get();
+        final SQLiteDatabase db = mDbHelper.get().getWritableDatabase();
         final ContactsDatabaseHelper dbHelper = mDbHelper.get();
 
         final ContentValues updatedValues = new ContentValues();
@@ -4068,7 +4121,8 @@
 
     private int updateSettings(Uri uri, ContentValues values, String selection,
             String[] selectionArgs) {
-        final int count = mActiveDb.get().update(Tables.SETTINGS, values, selection, selectionArgs);
+        final SQLiteDatabase db = mDbHelper.get().getWritableDatabase();
+        final int count = db.update(Tables.SETTINGS, values, selection, selectionArgs);
         if (values.containsKey(Settings.UNGROUPED_VISIBLE)) {
             mVisibleTouched = true;
         }
@@ -4088,13 +4142,14 @@
         }
 
         int count = 0;
-        Cursor cursor = mActiveDb.get().query(Views.RAW_CONTACTS,
+        final SQLiteDatabase db = mDbHelper.get().getWritableDatabase();
+        Cursor cursor = db.query(Views.RAW_CONTACTS,
                 Projections.ID, selection,
                 selectionArgs, null, null, null);
         try {
             while (cursor.moveToNext()) {
                 long rawContactId = cursor.getLong(0);
-                updateRawContact(rawContactId, values, callerIsSyncAdapter);
+                updateRawContact(db, rawContactId, values, callerIsSyncAdapter);
                 count++;
             }
         } finally {
@@ -4104,7 +4159,7 @@
         return count;
     }
 
-    private int updateRawContact(long rawContactId, ContentValues values,
+    private int updateRawContact(SQLiteDatabase db, long rawContactId, ContentValues values,
             boolean callerIsSyncAdapter) {
         final String selection = RawContactsColumns.CONCRETE_ID + " = ?";
         mSelectionArgs1[0] = Long.toString(rawContactId);
@@ -4127,7 +4182,7 @@
         String oldDataSet = null;
 
         if (requestUndoDelete || isAccountChanging) {
-            Cursor cursor = mActiveDb.get().query(RawContactsQuery.TABLE, RawContactsQuery.COLUMNS,
+            Cursor cursor = db.query(RawContactsQuery.TABLE, RawContactsQuery.COLUMNS,
                     selection, mSelectionArgs1, null, null, null);
             try {
                 if (cursor.moveToFirst()) {
@@ -4172,7 +4227,7 @@
                     ContactsContract.RawContacts.AGGREGATION_MODE_DEFAULT);
         }
 
-        int count = mActiveDb.get().update(Tables.RAW_CONTACTS, values, selection, mSelectionArgs1);
+        int count = db.update(Tables.RAW_CONTACTS, values, selection, mSelectionArgs1);
         if (count != 0) {
             if (values.containsKey(RawContacts.AGGREGATION_MODE)) {
                 int aggregationMode = values.getAsInteger(RawContacts.AGGREGATION_MODE);
@@ -4195,7 +4250,7 @@
                 // If it is starred, add a group membership, if one doesn't already exist
                 // otherwise delete any matching group memberships.
                 if (!callerIsSyncAdapter && isAccountChanging) {
-                    boolean starred = 0 != DatabaseUtils.longForQuery(mActiveDb.get(),
+                    boolean starred = 0 != DatabaseUtils.longForQuery(db,
                             SELECTION_STARRED_FROM_RAW_CONTACTS,
                             new String[]{Long.toString(rawContactId)});
                     updateFavoritesMembership(rawContactId, starred);
@@ -4209,7 +4264,7 @@
             }
 
             if (values.containsKey(RawContacts.SOURCE_ID)) {
-                mAggregator.get().updateLookupKeyForRawContact(mActiveDb.get(), rawContactId);
+                mAggregator.get().updateLookupKeyForRawContact(db, rawContactId);
             }
             if (values.containsKey(RawContacts.NAME_VERIFIED)) {
 
@@ -4218,7 +4273,7 @@
                 if (values.getAsInteger(RawContacts.NAME_VERIFIED) != 0) {
                     mDbHelper.get().resetNameVerifiedForOtherRawContacts(rawContactId);
                 }
-                mAggregator.get().updateDisplayNameForRawContact(mActiveDb.get(), rawContactId);
+                mAggregator.get().updateDisplayNameForRawContact(db, rawContactId);
             }
             if (requestUndoDelete && previousDeleted == 1) {
                 // Note before the accounts refactoring, we used to use the *old* account here,
@@ -4273,10 +4328,12 @@
             return 0;
         }
 
+        final SQLiteDatabase db = mDbHelper.get().getWritableDatabase();
+
         final String mimeType = c.getString(DataRowHandler.DataUpdateQuery.MIMETYPE);
         DataRowHandler rowHandler = getDataRowHandler(mimeType);
         boolean updated =
-                rowHandler.update(mActiveDb.get(), mTransactionContext.get(), values, c,
+                rowHandler.update(db, mTransactionContext.get(), values, c,
                         callerIsSyncAdapter);
         if (Photo.CONTENT_ITEM_TYPE.equals(mimeType)) {
             scheduleBackgroundTask(BACKGROUND_TASK_CLEANUP_PHOTOS);
@@ -4287,13 +4344,15 @@
     private int updateContactOptions(ContentValues values, String selection,
             String[] selectionArgs, boolean callerIsSyncAdapter) {
         int count = 0;
-        Cursor cursor = mActiveDb.get().query(Views.CONTACTS,
+        final SQLiteDatabase db = mDbHelper.get().getWritableDatabase();
+
+        Cursor cursor = db.query(Views.CONTACTS,
                 new String[] { Contacts._ID }, selection, selectionArgs, null, null, null);
         try {
             while (cursor.moveToNext()) {
                 long contactId = cursor.getLong(0);
 
-                updateContactOptions(contactId, values, callerIsSyncAdapter);
+                updateContactOptions(db, contactId, values, callerIsSyncAdapter);
                 count++;
             }
         } finally {
@@ -4303,7 +4362,7 @@
         return count;
     }
 
-    private int updateContactOptions(long contactId, ContentValues values,
+    private int updateContactOptions(SQLiteDatabase db, long contactId, ContentValues values,
             boolean callerIsSyncAdapter) {
 
         mValues.clear();
@@ -4329,11 +4388,11 @@
         }
 
         mSelectionArgs1[0] = String.valueOf(contactId);
-        mActiveDb.get().update(Tables.RAW_CONTACTS, mValues, RawContacts.CONTACT_ID + "=?"
+        db.update(Tables.RAW_CONTACTS, mValues, RawContacts.CONTACT_ID + "=?"
                 + " AND " + RawContacts.RAW_CONTACT_IS_READ_ONLY + "=0", mSelectionArgs1);
 
         if (mValues.containsKey(RawContacts.STARRED) && !callerIsSyncAdapter) {
-            Cursor cursor = mActiveDb.get().query(Views.RAW_CONTACTS,
+            Cursor cursor = db.query(Views.RAW_CONTACTS,
                     new String[] { RawContacts._ID }, RawContacts.CONTACT_ID + "=?",
                     mSelectionArgs1, null, null, null);
             try {
@@ -4361,13 +4420,13 @@
         ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.STARRED,
                 values, Contacts.STARRED);
 
-        int rslt = mActiveDb.get().update(Tables.CONTACTS, mValues, Contacts._ID + "=?",
+        int rslt = db.update(Tables.CONTACTS, mValues, Contacts._ID + "=?",
                 mSelectionArgs1);
 
         if (values.containsKey(Contacts.LAST_TIME_CONTACTED) &&
                 !values.containsKey(Contacts.TIMES_CONTACTED)) {
-            mActiveDb.get().execSQL(UPDATE_TIMES_CONTACTED_CONTACTS_TABLE, mSelectionArgs1);
-            mActiveDb.get().execSQL(UPDATE_TIMES_CONTACTED_RAWCONTACTS_TABLE, mSelectionArgs1);
+            db.execSQL(UPDATE_TIMES_CONTACTED_CONTACTS_TABLE, mSelectionArgs1);
+            db.execSQL(UPDATE_TIMES_CONTACTED_RAWCONTACTS_TABLE, mSelectionArgs1);
         }
         return rslt;
     }
@@ -4416,6 +4475,7 @@
         return 1;
     }
 
+    @Override
     public void onAccountsUpdated(Account[] accounts) {
         scheduleBackgroundTask(BACKGROUND_TASK_UPDATE_ACCOUNTS);
     }
@@ -4499,7 +4559,6 @@
 
         final ContactsDatabaseHelper dbHelper = mDbHelper.get();
         final SQLiteDatabase db = dbHelper.getWritableDatabase();
-        mActiveDb.set(db);
         db.beginTransaction();
 
         // WARNING: This method can be run in either contacts mode or profile mode.  It is
@@ -4716,7 +4775,6 @@
 
         // Otherwise proceed with a normal query against the contacts DB.
         switchToContactMode();
-        mActiveDb.set(mContactsHelper.getReadableDatabase());
         String directory = getQueryParameter(uri, ContactsContract.DIRECTORY_PARAM_KEY);
         if (directory == null) {
             return addSnippetExtrasToCursor(uri,
@@ -4868,10 +4926,7 @@
             String[] selectionArgs, String sortOrder, final long directoryId,
             final CancellationSignal cancellationSignal) {
 
-        // Default active DB to the contacts DB if none has been set.
-        if (mActiveDb.get() == null) {
-            mActiveDb.set(mContactsHelper.getReadableDatabase());
-        }
+        final SQLiteDatabase db = mDbHelper.get().getReadableDatabase();
 
         SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
         String groupBy = null;
@@ -4886,7 +4941,7 @@
         switch (match) {
             case SYNCSTATE:
             case PROFILE_SYNCSTATE:
-                return mDbHelper.get().getSyncState().query(mActiveDb.get(), projection, selection,
+                return mDbHelper.get().getSyncState().query(db, projection, selection,
                         selectionArgs, sortOrder);
 
             case CONTACTS: {
@@ -4918,7 +4973,7 @@
                     SQLiteQueryBuilder lookupQb = new SQLiteQueryBuilder();
                     setTablesAndProjectionMapForContacts(lookupQb, uri, projection);
 
-                    Cursor c = queryWithContactIdAndLookupKey(lookupQb, mActiveDb.get(), uri,
+                    Cursor c = queryWithContactIdAndLookupKey(lookupQb, db, uri,
                             projection, selection, selectionArgs, sortOrder, groupBy, limit,
                             Contacts._ID, contactId, Contacts.LOOKUP_KEY, lookupKey,
                             cancellationSignal);
@@ -4929,7 +4984,7 @@
 
                 setTablesAndProjectionMapForContacts(qb, uri, projection);
                 selectionArgs = insertSelectionArg(selectionArgs,
-                        String.valueOf(lookupContactIdByLookupKey(mActiveDb.get(), lookupKey)));
+                        String.valueOf(lookupContactIdByLookupKey(db, lookupKey)));
                 qb.appendWhere(Contacts._ID + "=?");
                 break;
             }
@@ -4953,7 +5008,7 @@
                         lookupQb.appendWhere(" AND " + Data._ID + "=" + Contacts.PHOTO_ID);
                     }
                     lookupQb.appendWhere(" AND ");
-                    Cursor c = queryWithContactIdAndLookupKey(lookupQb, mActiveDb.get(), uri,
+                    Cursor c = queryWithContactIdAndLookupKey(lookupQb, db, uri,
                             projection, selection, selectionArgs, sortOrder, groupBy, limit,
                             Data.CONTACT_ID, contactId, Data.LOOKUP_KEY, lookupKey,
                             cancellationSignal);
@@ -4965,7 +5020,7 @@
                 }
 
                 setTablesAndProjectionMapForData(qb, uri, projection, false);
-                long contactId = lookupContactIdByLookupKey(mActiveDb.get(), lookupKey);
+                long contactId = lookupContactIdByLookupKey(db, lookupKey);
                 selectionArgs = insertSelectionArg(selectionArgs,
                         String.valueOf(contactId));
                 if (match == CONTACTS_LOOKUP_PHOTO || match == CONTACTS_LOOKUP_ID_PHOTO) {
@@ -4996,7 +5051,7 @@
                     long contactId = Long.parseLong(pathSegments.get(3));
                     SQLiteQueryBuilder lookupQb = new SQLiteQueryBuilder();
                     setTablesAndProjectionMapForStreamItems(lookupQb);
-                    Cursor c = queryWithContactIdAndLookupKey(lookupQb, mActiveDb.get(), uri,
+                    Cursor c = queryWithContactIdAndLookupKey(lookupQb, db, uri,
                             projection, selection, selectionArgs, sortOrder, groupBy, limit,
                             StreamItems.CONTACT_ID, contactId,
                             StreamItems.CONTACT_LOOKUP_KEY, lookupKey,
@@ -5007,7 +5062,7 @@
                 }
 
                 setTablesAndProjectionMapForStreamItems(qb);
-                long contactId = lookupContactIdByLookupKey(mActiveDb.get(), lookupKey);
+                long contactId = lookupContactIdByLookupKey(db, lookupKey);
                 selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(contactId));
                 qb.appendWhere(RawContacts.CONTACT_ID + "=?");
                 break;
@@ -5015,7 +5070,7 @@
 
             case CONTACTS_AS_VCARD: {
                 final String lookupKey = Uri.encode(uri.getPathSegments().get(2));
-                long contactId = lookupContactIdByLookupKey(mActiveDb.get(), lookupKey);
+                long contactId = lookupContactIdByLookupKey(db, lookupKey);
                 qb.setTables(Views.CONTACTS);
                 qb.setProjectionMap(sContactsVCardProjectionMap);
                 selectionArgs = insertSelectionArg(selectionArgs,
@@ -5027,7 +5082,7 @@
             case CONTACTS_AS_MULTI_VCARD: {
                 SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd_HHmmss");
                 String currentDateString = dateFormat.format(new Date()).toString();
-                return mActiveDb.get().rawQuery(
+                return db.rawQuery(
                     "SELECT" +
                     " 'vcards_' || ? || '.vcf' AS " + OpenableColumns.DISPLAY_NAME + "," +
                     " NULL AS " + OpenableColumns.SIZE,
@@ -5173,7 +5228,7 @@
                     System.arraycopy(selectionArgs, 0, doubledSelectionArgs, length, length);
                 }
 
-                Cursor cursor = mActiveDb.get().rawQuery(unionQuery, doubledSelectionArgs);
+                Cursor cursor = db.rawQuery(unionQuery, doubledSelectionArgs);
                 if (cursor != null) {
                     cursor.setNotificationUri(getContext().getContentResolver(),
                             ContactsContract.AUTHORITY_URI);
@@ -5262,7 +5317,7 @@
                     setTablesAndProjectionMapForEntities(lookupQb, uri, projection);
                     lookupQb.appendWhere(" AND ");
 
-                    Cursor c = queryWithContactIdAndLookupKey(lookupQb, mActiveDb.get(), uri,
+                    Cursor c = queryWithContactIdAndLookupKey(lookupQb, db, uri,
                             projection, selection, selectionArgs, sortOrder, groupBy, limit,
                             Contacts.Entity.CONTACT_ID, contactId,
                             Contacts.Entity.LOOKUP_KEY, lookupKey,
@@ -5274,7 +5329,7 @@
 
                 setTablesAndProjectionMapForEntities(qb, uri, projection);
                 selectionArgs = insertSelectionArg(selectionArgs,
-                        String.valueOf(lookupContactIdByLookupKey(mActiveDb.get(), lookupKey)));
+                        String.valueOf(lookupContactIdByLookupKey(db, lookupKey)));
                 qb.appendWhere(" AND " + Contacts.Entity.CONTACT_ID + "=?");
                 break;
             }
@@ -5713,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
@@ -5731,19 +5789,26 @@
 
                     // 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(mActiveDb.get(), qb, projection, selection, selectionArgs,
+                    Cursor cursor = query(db, qb, projection, selection, selectionArgs,
                             sortOrder, groupBy, null, limit, cancellationSignal);
                     try {
                         if (cursor.getCount() > 0) {
                             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 {
@@ -5867,7 +5932,7 @@
 
             case SEARCH_SUGGESTIONS: {
                 return mGlobalSearchSupport.handleSearchSuggestionsQuery(
-                        mActiveDb.get(), uri, projection, limit);
+                        db, uri, projection, limit);
             }
 
             case SEARCH_SHORTCUT: {
@@ -5875,7 +5940,7 @@
                 String filter = getQueryParameter(
                         uri, SearchManager.SUGGEST_COLUMN_INTENT_EXTRA_DATA);
                 return mGlobalSearchSupport.handleSearchShortcutRefresh(
-                        mActiveDb.get(), projection, lookupKey, filter);
+                        db, projection, lookupKey, filter);
             }
 
             case RAW_CONTACT_ENTITIES:
@@ -5925,11 +5990,11 @@
         qb.setStrict(true);
 
         Cursor cursor =
-                query(mActiveDb.get(), qb, projection, selection, selectionArgs, sortOrder, groupBy,
+                query(db, qb, projection, selection, selectionArgs, sortOrder, groupBy,
                 having, limit, cancellationSignal);
 
         if (readBooleanQueryParameter(uri, ContactCounts.ADDRESS_BOOK_INDEX_EXTRAS, false)) {
-            bundleFastScrollingIndexExtras(cursor, uri, mActiveDb.get(), qb, selection,
+            bundleFastScrollingIndexExtras(cursor, uri, db, qb, selection,
                     selectionArgs, sortOrder, addressBookIndexerCountExpression,
                     cancellationSignal);
         }
@@ -5990,10 +6055,6 @@
     }
 
     private void invalidateFastScrollingIndexCache() {
-        if (VERBOSE_LOGGING) {
-            Log.v(TAG, "invalidatemFastScrollingIndexCache");
-        }
-
         // FastScrollingIndexCache is thread-safe, no need to synchronize here.
         mFastScrollingIndexCache.invalidate();
     }
@@ -7078,20 +7139,15 @@
     public AssetFileDescriptor openAssetFileLocal(Uri uri, String mode)
             throws FileNotFoundException {
 
-        // Default active DB to the contacts DB if none has been set.
-        if (mActiveDb.get() == null) {
-            if (mode.equals("r")) {
-                mActiveDb.set(mContactsHelper.getReadableDatabase());
-            } else {
-                mActiveDb.set(mContactsHelper.getWritableDatabase());
-            }
-        }
+        final boolean writing = mode.contains("w");
+
+        final SQLiteDatabase db = mDbHelper.get().getDatabase(writing);
 
         int match = sUriMatcher.match(uri);
         switch (match) {
             case CONTACTS_ID_PHOTO: {
                 long contactId = Long.parseLong(uri.getPathSegments().get(1));
-                return openPhotoAssetFile(mActiveDb.get(), uri, mode,
+                return openPhotoAssetFile(db, uri, mode,
                         Data._ID + "=" + Contacts.PHOTO_ID + " AND " +
                                 RawContacts.CONTACT_ID + "=?",
                         new String[]{String.valueOf(contactId)});
@@ -7103,7 +7159,7 @@
                             "Display photos retrieved by contact ID can only be read.");
                 }
                 long contactId = Long.parseLong(uri.getPathSegments().get(1));
-                Cursor c = mActiveDb.get().query(Tables.CONTACTS,
+                Cursor c = db.query(Tables.CONTACTS,
                         new String[]{Contacts.PHOTO_FILE_ID},
                         Contacts._ID + "=?", new String[]{String.valueOf(contactId)},
                         null, null, null);
@@ -7125,7 +7181,7 @@
                     throw new IllegalArgumentException(
                             "Display photos retrieved by contact ID can only be read.");
                 }
-                Cursor c = mActiveDb.get().query(Tables.CONTACTS,
+                Cursor c = db.query(Tables.CONTACTS,
                         new String[]{Contacts.PHOTO_FILE_ID}, null, null, null, null, null);
                 try {
                     if (c.moveToFirst()) {
@@ -7163,7 +7219,7 @@
                     long contactId = Long.parseLong(pathSegments.get(3));
                     SQLiteQueryBuilder lookupQb = new SQLiteQueryBuilder();
                     setTablesAndProjectionMapForContacts(lookupQb, uri, projection);
-                    Cursor c = queryWithContactIdAndLookupKey(lookupQb, mActiveDb.get(), uri,
+                    Cursor c = queryWithContactIdAndLookupKey(lookupQb, db, uri,
                             projection, null, null, null, null, null,
                             Contacts._ID, contactId, Contacts.LOOKUP_KEY, lookupKey, null);
                     if (c != null) {
@@ -7175,7 +7231,7 @@
                                 return openDisplayPhotoForRead(photoFileId);
                             } else {
                                 long photoId = c.getLong(c.getColumnIndex(Contacts.PHOTO_ID));
-                                return openPhotoAssetFile(mActiveDb.get(), uri, mode,
+                                return openPhotoAssetFile(db, uri, mode,
                                         Data._ID + "=?", new String[]{String.valueOf(photoId)});
                             }
                         } finally {
@@ -7186,8 +7242,8 @@
 
                 SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
                 setTablesAndProjectionMapForContacts(qb, uri, projection);
-                long contactId = lookupContactIdByLookupKey(mActiveDb.get(), lookupKey);
-                Cursor c = qb.query(mActiveDb.get(), projection, Contacts._ID + "=?",
+                long contactId = lookupContactIdByLookupKey(db, lookupKey);
+                Cursor c = qb.query(db, projection, Contacts._ID + "=?",
                         new String[]{String.valueOf(contactId)}, null, null, null);
                 try {
                     c.moveToFirst();
@@ -7196,7 +7252,7 @@
                         return openDisplayPhotoForRead(photoFileId);
                     } else {
                         long photoId = c.getLong(c.getColumnIndex(Contacts.PHOTO_ID));
-                        return openPhotoAssetFile(mActiveDb.get(), uri, mode,
+                        return openPhotoAssetFile(db, uri, mode,
                                 Data._ID + "=?", new String[]{String.valueOf(photoId)});
                     }
                 } finally {
@@ -7213,7 +7269,7 @@
                 String[] projection = new String[]{Data._ID, Photo.PHOTO_FILE_ID};
                 setTablesAndProjectionMapForData(qb, uri, projection, false);
                 long photoMimetypeId = mDbHelper.get().getMimeTypeId(Photo.CONTENT_ITEM_TYPE);
-                Cursor c = qb.query(mActiveDb.get(), projection,
+                Cursor c = qb.query(db, projection,
                         Data.RAW_CONTACT_ID + "=? AND " + DataColumns.MIMETYPE_ID + "=?",
                         new String[]{String.valueOf(rawContactId), String.valueOf(photoMimetypeId)},
                         null, null, Data.IS_PRIMARY + " DESC");
@@ -7251,7 +7307,7 @@
             case DATA_ID: {
                 long dataId = Long.parseLong(uri.getPathSegments().get(1));
                 long photoMimetypeId = mDbHelper.get().getMimeTypeId(Photo.CONTENT_ITEM_TYPE);
-                return openPhotoAssetFile(mActiveDb.get(), uri, mode,
+                return openPhotoAssetFile(db, uri, mode,
                         Data._ID + "=? AND " + DataColumns.MIMETYPE_ID + "=" + photoMimetypeId,
                         new String[]{String.valueOf(dataId)});
             }
@@ -7290,7 +7346,7 @@
                         inBuilder.append(",");
                     }
                     // TODO: Figure out what to do if the profile contact is in the list.
-                    long contactId = lookupContactIdByLookupKey(mActiveDb.get(), lookupKey);
+                    long contactId = lookupContactIdByLookupKey(db, lookupKey);
                     inBuilder.append(contactId);
                     index++;
                 }
@@ -7959,7 +8015,6 @@
                 // Re-aggregation is only for the contacts DB.
                 switchToContactMode();
                 db = mContactsHelper.getWritableDatabase();
-                mActiveDb.set(db);
 
                 // Start the actual process.
                 db.beginTransaction();
@@ -8064,7 +8119,7 @@
         }
         rawContactIdSelect.append(")");
 
-        final SQLiteDatabase db = mActiveDb.get();
+        final SQLiteDatabase db = mDbHelper.get().getWritableDatabase();
 
         mSelectionArgs1[0] = String.valueOf(currentTimeMillis);
 
@@ -8106,7 +8161,7 @@
     /* package */ int updateDataUsageStat(
             List<Long> dataIds, String type, long currentTimeMillis) {
 
-        final SQLiteDatabase db = mActiveDb.get();
+        final SQLiteDatabase db = mDbHelper.get().getWritableDatabase();
 
         final String typeString = String.valueOf(getDataUsageFeedbackType(type, null));
         final String currentTimeMillisString = String.valueOf(currentTimeMillis);
@@ -8279,4 +8334,15 @@
         }
         throw new IllegalArgumentException("Invalid usage type " + type);
     }
+
+    /** Use only for debug logging */
+    @Override
+    public String toString() {
+        return "ContactsProvider2";
+    }
+
+    @NeededForTesting
+    public void switchToProfileModeForTest() {
+        switchToProfileMode();
+    }
 }
diff --git a/src/com/android/providers/contacts/ContactsTransaction.java b/src/com/android/providers/contacts/ContactsTransaction.java
index 7a92cae..c6c11d9 100644
--- a/src/com/android/providers/contacts/ContactsTransaction.java
+++ b/src/com/android/providers/contacts/ContactsTransaction.java
@@ -16,11 +16,12 @@
 
 package com.android.providers.contacts;
 
-import com.google.android.collect.Lists;
-import com.google.android.collect.Maps;
-
 import android.database.sqlite.SQLiteDatabase;
 import android.database.sqlite.SQLiteTransactionListener;
+import android.util.Log;
+
+import com.google.android.collect.Lists;
+import com.google.android.collect.Maps;
 
 import java.util.List;
 import java.util.Map;
@@ -40,13 +41,16 @@
 
     /**
      * The list of databases that have been enlisted in this transaction.
+     *
+     * Note we insert elements to the head of the list, so that we endTransaction() in the reverse
+     * order.
      */
-    private List<SQLiteDatabase> mDatabasesForTransaction;
+    private final List<SQLiteDatabase> mDatabasesForTransaction;
 
     /**
      * The mapping of tags to databases involved in this transaction.
      */
-    private Map<String, SQLiteDatabase> mDatabaseTagMap;
+    private final Map<String, SQLiteDatabase> mDatabaseTagMap;
 
     /**
      * Whether any actual changes have been made successfully in this transaction.
@@ -97,8 +101,16 @@
      */
     public void startTransactionForDb(SQLiteDatabase db, String tag,
             SQLiteTransactionListener listener) {
+        if (AbstractContactsProvider.ENABLE_TRANSACTION_LOG) {
+            Log.i(AbstractContactsProvider.TAG, "startTransactionForDb: db=" + db.getPath() +
+                    "  tag=" + tag + "  listener=" + listener +
+                    "  startTransaction=" + !hasDbInTransaction(tag),
+                    new RuntimeException("startTransactionForDb"));
+        }
         if (!hasDbInTransaction(tag)) {
-            mDatabasesForTransaction.add(db);
+            // Insert a new db into the head of the list, so that we'll endTransaction() in
+            // the reverse order.
+            mDatabasesForTransaction.add(0, db);
             mDatabaseTagMap.put(tag, db);
             if (listener != null) {
                 db.beginTransactionWithListener(listener);
@@ -154,13 +166,33 @@
     }
 
     /**
+     * @return the tag for a database.  Only intended to be used for logging.
+     */
+    private String getTagForDb(SQLiteDatabase db) {
+        for (String tag : mDatabaseTagMap.keySet()) {
+            if (db == mDatabaseTagMap.get(tag)) {
+                return tag;
+            }
+        }
+        return null;
+    }
+
+    /**
      * Completes the transaction, ending the DB transactions for all associated databases.
      * @param callerIsBatch Whether this is being performed in the context of a batch operation.
      *     If it is not, and the transaction is marked as batch, this call is a no-op.
      */
     public void finish(boolean callerIsBatch) {
+        if (AbstractContactsProvider.ENABLE_TRANSACTION_LOG) {
+            Log.i(AbstractContactsProvider.TAG, "ContactsTransaction.finish  callerIsBatch=" +
+                    callerIsBatch, new RuntimeException("ContactsTransaction.finish"));
+        }
         if (!mBatch || callerIsBatch) {
             for (SQLiteDatabase db : mDatabasesForTransaction) {
+                if (AbstractContactsProvider.ENABLE_TRANSACTION_LOG) {
+                    Log.i(AbstractContactsProvider.TAG, "ContactsTransaction.finish: " +
+                            "endTransaction for " + getTagForDb(db));
+                }
                 // If an exception was thrown while yielding, it's possible that we no longer have
                 // a lock on this database, so we need to check before attempting to end its
                 // transaction.  Otherwise, we should always expect to be in a transaction (and will
diff --git a/src/com/android/providers/contacts/DataRowHandler.java b/src/com/android/providers/contacts/DataRowHandler.java
index b98f6c7..0366532 100644
--- a/src/com/android/providers/contacts/DataRowHandler.java
+++ b/src/com/android/providers/contacts/DataRowHandler.java
@@ -15,12 +15,6 @@
  */
 package com.android.providers.contacts;
 
-import com.android.providers.contacts.ContactsDatabaseHelper.DataColumns;
-import com.android.providers.contacts.ContactsDatabaseHelper.MimetypesColumns;
-import com.android.providers.contacts.ContactsDatabaseHelper.PresenceColumns;
-import com.android.providers.contacts.ContactsDatabaseHelper.Tables;
-import com.android.providers.contacts.aggregation.ContactAggregator;
-
 import android.content.ContentValues;
 import android.content.Context;
 import android.database.Cursor;
@@ -33,6 +27,12 @@
 import android.provider.ContactsContract.Data;
 import android.text.TextUtils;
 
+import com.android.providers.contacts.ContactsDatabaseHelper.DataColumns;
+import com.android.providers.contacts.ContactsDatabaseHelper.MimetypesColumns;
+import com.android.providers.contacts.ContactsDatabaseHelper.PresenceColumns;
+import com.android.providers.contacts.ContactsDatabaseHelper.Tables;
+import com.android.providers.contacts.aggregation.ContactAggregator;
+
 /**
  * Handles inserts and update for a specific Data type.
  */
diff --git a/src/com/android/providers/contacts/DataRowHandlerForCommonDataKind.java b/src/com/android/providers/contacts/DataRowHandlerForCommonDataKind.java
index b717d31..0bb17c2 100644
--- a/src/com/android/providers/contacts/DataRowHandlerForCommonDataKind.java
+++ b/src/com/android/providers/contacts/DataRowHandlerForCommonDataKind.java
@@ -15,8 +15,6 @@
  */
 package com.android.providers.contacts;
 
-import com.android.providers.contacts.aggregation.ContactAggregator;
-
 import android.content.ContentValues;
 import android.content.Context;
 import android.database.Cursor;
@@ -24,6 +22,8 @@
 import android.provider.ContactsContract.CommonDataKinds.BaseTypes;
 import android.text.TextUtils;
 
+import com.android.providers.contacts.aggregation.ContactAggregator;
+
 /**
  * Superclass for data row handlers that deal with types (e.g. Home, Work, Other) and
  * labels, which are custom types.
diff --git a/src/com/android/providers/contacts/DataRowHandlerForCustomMimetype.java b/src/com/android/providers/contacts/DataRowHandlerForCustomMimetype.java
index 0202fd6..502b835 100644
--- a/src/com/android/providers/contacts/DataRowHandlerForCustomMimetype.java
+++ b/src/com/android/providers/contacts/DataRowHandlerForCustomMimetype.java
@@ -15,10 +15,10 @@
  */
 package com.android.providers.contacts;
 
-import com.android.providers.contacts.aggregation.ContactAggregator;
-
 import android.content.Context;
 
+import com.android.providers.contacts.aggregation.ContactAggregator;
+
 public class DataRowHandlerForCustomMimetype extends DataRowHandler {
 
     public DataRowHandlerForCustomMimetype(Context context,
diff --git a/src/com/android/providers/contacts/DataRowHandlerForEmail.java b/src/com/android/providers/contacts/DataRowHandlerForEmail.java
index f1fa941..38cb2e1 100644
--- a/src/com/android/providers/contacts/DataRowHandlerForEmail.java
+++ b/src/com/android/providers/contacts/DataRowHandlerForEmail.java
@@ -15,15 +15,15 @@
  */
 package com.android.providers.contacts;
 
-import com.android.providers.contacts.SearchIndexManager.IndexBuilder;
-import com.android.providers.contacts.aggregation.ContactAggregator;
-
 import android.content.ContentValues;
 import android.content.Context;
 import android.database.Cursor;
 import android.database.sqlite.SQLiteDatabase;
 import android.provider.ContactsContract.CommonDataKinds.Email;
 
+import com.android.providers.contacts.SearchIndexManager.IndexBuilder;
+import com.android.providers.contacts.aggregation.ContactAggregator;
+
 /**
  * Handler for email address data rows.
  */
diff --git a/src/com/android/providers/contacts/DataRowHandlerForGroupMembership.java b/src/com/android/providers/contacts/DataRowHandlerForGroupMembership.java
index 3a4b167..0d2427a 100644
--- a/src/com/android/providers/contacts/DataRowHandlerForGroupMembership.java
+++ b/src/com/android/providers/contacts/DataRowHandlerForGroupMembership.java
@@ -15,15 +15,6 @@
  */
 package com.android.providers.contacts;
 
-import com.android.providers.contacts.ContactsDatabaseHelper.Clauses;
-import com.android.providers.contacts.ContactsDatabaseHelper.DataColumns;
-import com.android.providers.contacts.ContactsDatabaseHelper.GroupsColumns;
-import com.android.providers.contacts.ContactsDatabaseHelper.Projections;
-import com.android.providers.contacts.ContactsDatabaseHelper.RawContactsColumns;
-import com.android.providers.contacts.ContactsDatabaseHelper.Tables;
-import com.android.providers.contacts.ContactsProvider2.GroupIdCacheEntry;
-import com.android.providers.contacts.aggregation.ContactAggregator;
-
 import android.content.ContentValues;
 import android.content.Context;
 import android.database.Cursor;
@@ -33,6 +24,15 @@
 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;
+import com.android.providers.contacts.ContactsDatabaseHelper.Projections;
+import com.android.providers.contacts.ContactsDatabaseHelper.RawContactsColumns;
+import com.android.providers.contacts.ContactsDatabaseHelper.Tables;
+import com.android.providers.contacts.ContactsProvider2.GroupIdCacheEntry;
+import com.android.providers.contacts.aggregation.ContactAggregator;
+
 import java.util.ArrayList;
 import java.util.HashMap;
 
diff --git a/src/com/android/providers/contacts/DataRowHandlerForIdentity.java b/src/com/android/providers/contacts/DataRowHandlerForIdentity.java
index 440e430..48ce5e4 100644
--- a/src/com/android/providers/contacts/DataRowHandlerForIdentity.java
+++ b/src/com/android/providers/contacts/DataRowHandlerForIdentity.java
@@ -15,14 +15,14 @@
  */
 package com.android.providers.contacts;
 
-import com.android.providers.contacts.aggregation.ContactAggregator;
-
 import android.content.ContentValues;
 import android.content.Context;
 import android.database.Cursor;
 import android.database.sqlite.SQLiteDatabase;
 import android.provider.ContactsContract.CommonDataKinds.Identity;
 
+import com.android.providers.contacts.aggregation.ContactAggregator;
+
 /**
  * Handler for Identity data rows.
  */
diff --git a/src/com/android/providers/contacts/DataRowHandlerForIm.java b/src/com/android/providers/contacts/DataRowHandlerForIm.java
index 009bb89..faf10ad 100644
--- a/src/com/android/providers/contacts/DataRowHandlerForIm.java
+++ b/src/com/android/providers/contacts/DataRowHandlerForIm.java
@@ -15,13 +15,13 @@
  */
 package com.android.providers.contacts;
 
-import com.android.providers.contacts.SearchIndexManager.IndexBuilder;
-import com.android.providers.contacts.aggregation.ContactAggregator;
-
 import android.content.ContentValues;
 import android.content.Context;
 import android.provider.ContactsContract.CommonDataKinds.Im;
 
+import com.android.providers.contacts.SearchIndexManager.IndexBuilder;
+import com.android.providers.contacts.aggregation.ContactAggregator;
+
 /**
  * Handler for IM address data rows.
  */
diff --git a/src/com/android/providers/contacts/DataRowHandlerForNickname.java b/src/com/android/providers/contacts/DataRowHandlerForNickname.java
index 0fec6ee..95f24f5 100644
--- a/src/com/android/providers/contacts/DataRowHandlerForNickname.java
+++ b/src/com/android/providers/contacts/DataRowHandlerForNickname.java
@@ -15,9 +15,6 @@
  */
 package com.android.providers.contacts;
 
-import com.android.providers.contacts.SearchIndexManager.IndexBuilder;
-import com.android.providers.contacts.aggregation.ContactAggregator;
-
 import android.content.ContentValues;
 import android.content.Context;
 import android.database.Cursor;
@@ -25,6 +22,9 @@
 import android.provider.ContactsContract.CommonDataKinds.Nickname;
 import android.text.TextUtils;
 
+import com.android.providers.contacts.SearchIndexManager.IndexBuilder;
+import com.android.providers.contacts.aggregation.ContactAggregator;
+
 /**
  * Handler for nickname data rows.
  */
diff --git a/src/com/android/providers/contacts/DataRowHandlerForNote.java b/src/com/android/providers/contacts/DataRowHandlerForNote.java
index 317af1a..ea73637 100644
--- a/src/com/android/providers/contacts/DataRowHandlerForNote.java
+++ b/src/com/android/providers/contacts/DataRowHandlerForNote.java
@@ -15,13 +15,13 @@
  */
 package com.android.providers.contacts;
 
-import com.android.providers.contacts.SearchIndexManager.IndexBuilder;
-import com.android.providers.contacts.aggregation.ContactAggregator;
-
 import android.content.ContentValues;
 import android.content.Context;
 import android.provider.ContactsContract.CommonDataKinds.Note;
 
+import com.android.providers.contacts.SearchIndexManager.IndexBuilder;
+import com.android.providers.contacts.aggregation.ContactAggregator;
+
 /**
  * Handler for note data rows.
  */
diff --git a/src/com/android/providers/contacts/DataRowHandlerForOrganization.java b/src/com/android/providers/contacts/DataRowHandlerForOrganization.java
index 7384ccb..44146e8 100644
--- a/src/com/android/providers/contacts/DataRowHandlerForOrganization.java
+++ b/src/com/android/providers/contacts/DataRowHandlerForOrganization.java
@@ -15,10 +15,6 @@
  */
 package com.android.providers.contacts;
 
-import com.android.providers.contacts.ContactsDatabaseHelper.Tables;
-import com.android.providers.contacts.SearchIndexManager.IndexBuilder;
-import com.android.providers.contacts.aggregation.ContactAggregator;
-
 import android.content.ContentValues;
 import android.content.Context;
 import android.database.Cursor;
@@ -27,6 +23,10 @@
 import android.provider.ContactsContract.CommonDataKinds.Organization;
 import android.provider.ContactsContract.Data;
 
+import com.android.providers.contacts.ContactsDatabaseHelper.Tables;
+import com.android.providers.contacts.SearchIndexManager.IndexBuilder;
+import com.android.providers.contacts.aggregation.ContactAggregator;
+
 /**
  * Handler for organization data rows.
  */
diff --git a/src/com/android/providers/contacts/DataRowHandlerForPhoneNumber.java b/src/com/android/providers/contacts/DataRowHandlerForPhoneNumber.java
index 99313e9..16faf2a 100644
--- a/src/com/android/providers/contacts/DataRowHandlerForPhoneNumber.java
+++ b/src/com/android/providers/contacts/DataRowHandlerForPhoneNumber.java
@@ -15,11 +15,6 @@
  */
 package com.android.providers.contacts;
 
-import com.android.providers.contacts.ContactsDatabaseHelper.PhoneLookupColumns;
-import com.android.providers.contacts.ContactsDatabaseHelper.Tables;
-import com.android.providers.contacts.SearchIndexManager.IndexBuilder;
-import com.android.providers.contacts.aggregation.ContactAggregator;
-
 import android.content.ContentValues;
 import android.content.Context;
 import android.database.Cursor;
@@ -28,6 +23,11 @@
 import android.telephony.PhoneNumberUtils;
 import android.text.TextUtils;
 
+import com.android.providers.contacts.ContactsDatabaseHelper.PhoneLookupColumns;
+import com.android.providers.contacts.ContactsDatabaseHelper.Tables;
+import com.android.providers.contacts.SearchIndexManager.IndexBuilder;
+import com.android.providers.contacts.aggregation.ContactAggregator;
+
 /**
  * Handler for phone number data rows.
  */
@@ -93,9 +93,7 @@
         if (number != null && numberE164 == null) {
             final String newNumberE164 = PhoneNumberUtils.formatNumberToE164(number,
                     mDbHelper.getCurrentCountryIso());
-            if (newNumberE164 != null) {
-                values.put(Phone.NORMALIZED_NUMBER, newNumberE164);
-            }
+            values.put(Phone.NORMALIZED_NUMBER, newNumberE164);
         }
     }
 
diff --git a/src/com/android/providers/contacts/DataRowHandlerForPhoto.java b/src/com/android/providers/contacts/DataRowHandlerForPhoto.java
index 7560ed4..bfaa501 100644
--- a/src/com/android/providers/contacts/DataRowHandlerForPhoto.java
+++ b/src/com/android/providers/contacts/DataRowHandlerForPhoto.java
@@ -15,8 +15,6 @@
  */
 package com.android.providers.contacts;
 
-import com.android.providers.contacts.aggregation.ContactAggregator;
-
 import android.content.ContentValues;
 import android.content.Context;
 import android.database.Cursor;
@@ -24,6 +22,8 @@
 import android.provider.ContactsContract.CommonDataKinds.Photo;
 import android.util.Log;
 
+import com.android.providers.contacts.aggregation.ContactAggregator;
+
 import java.io.IOException;
 
 /**
diff --git a/src/com/android/providers/contacts/DataRowHandlerForStructuredName.java b/src/com/android/providers/contacts/DataRowHandlerForStructuredName.java
index c84d2e8..01ee1ba 100644
--- a/src/com/android/providers/contacts/DataRowHandlerForStructuredName.java
+++ b/src/com/android/providers/contacts/DataRowHandlerForStructuredName.java
@@ -15,9 +15,6 @@
  */
 package com.android.providers.contacts;
 
-import com.android.providers.contacts.SearchIndexManager.IndexBuilder;
-import com.android.providers.contacts.aggregation.ContactAggregator;
-
 import android.content.ContentValues;
 import android.content.Context;
 import android.database.Cursor;
@@ -26,6 +23,9 @@
 import android.provider.ContactsContract.FullNameStyle;
 import android.text.TextUtils;
 
+import com.android.providers.contacts.SearchIndexManager.IndexBuilder;
+import com.android.providers.contacts.aggregation.ContactAggregator;
+
 /**
  * Handler for email address data rows.
  */
diff --git a/src/com/android/providers/contacts/DataRowHandlerForStructuredPostal.java b/src/com/android/providers/contacts/DataRowHandlerForStructuredPostal.java
index 6898a43..26483ed 100644
--- a/src/com/android/providers/contacts/DataRowHandlerForStructuredPostal.java
+++ b/src/com/android/providers/contacts/DataRowHandlerForStructuredPostal.java
@@ -15,9 +15,6 @@
  */
 package com.android.providers.contacts;
 
-import com.android.providers.contacts.SearchIndexManager.IndexBuilder;
-import com.android.providers.contacts.aggregation.ContactAggregator;
-
 import android.content.ContentValues;
 import android.content.Context;
 import android.database.Cursor;
@@ -25,6 +22,9 @@
 import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
 import android.text.TextUtils;
 
+import com.android.providers.contacts.SearchIndexManager.IndexBuilder;
+import com.android.providers.contacts.aggregation.ContactAggregator;
+
 /**
  * Handler for postal address data rows.
  */
diff --git a/src/com/android/providers/contacts/DbModifierWithNotification.java b/src/com/android/providers/contacts/DbModifierWithNotification.java
index c13f4a8..fda8321 100644
--- a/src/com/android/providers/contacts/DbModifierWithNotification.java
+++ b/src/com/android/providers/contacts/DbModifierWithNotification.java
@@ -20,11 +20,6 @@
 import static android.Manifest.permission.ADD_VOICEMAIL;
 import static com.android.providers.contacts.Manifest.permission.READ_WRITE_ALL_VOICEMAIL;
 
-import com.android.common.io.MoreCloseables;
-import com.android.providers.contacts.ContactsDatabaseHelper.Tables;
-import com.android.providers.contacts.util.DbQueryUtils;
-import com.google.android.collect.Lists;
-
 import android.content.ComponentName;
 import android.content.ContentUris;
 import android.content.ContentValues;
@@ -43,6 +38,11 @@
 import android.provider.VoicemailContract.Voicemails;
 import android.util.Log;
 
+import com.android.common.io.MoreCloseables;
+import com.android.providers.contacts.ContactsDatabaseHelper.Tables;
+import com.android.providers.contacts.util.DbQueryUtils;
+import com.google.android.collect.Lists;
+
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.HashSet;
diff --git a/src/com/android/providers/contacts/DefaultCallLogInsertionHelper.java b/src/com/android/providers/contacts/DefaultCallLogInsertionHelper.java
index 6777e43..fcda8f1 100644
--- a/src/com/android/providers/contacts/DefaultCallLogInsertionHelper.java
+++ b/src/com/android/providers/contacts/DefaultCallLogInsertionHelper.java
@@ -16,16 +16,15 @@
 
 package com.android.providers.contacts;
 
+import android.content.ContentValues;
+import android.content.Context;
+import android.provider.CallLog.Calls;
+
 import com.android.i18n.phonenumbers.NumberParseException;
 import com.android.i18n.phonenumbers.PhoneNumberUtil;
 import com.android.i18n.phonenumbers.Phonenumber.PhoneNumber;
 import com.android.i18n.phonenumbers.geocoding.PhoneNumberOfflineGeocoder;
 
-import android.content.ContentValues;
-import android.content.Context;
-import android.provider.CallLog.Calls;
-import android.util.Log;
-
 import java.util.Locale;
 
 /**
diff --git a/src/com/android/providers/contacts/FastScrollingIndexCache.java b/src/com/android/providers/contacts/FastScrollingIndexCache.java
index c1c5602..f07a855 100644
--- a/src/com/android/providers/contacts/FastScrollingIndexCache.java
+++ b/src/com/android/providers/contacts/FastScrollingIndexCache.java
@@ -16,11 +16,9 @@
 
 package com.android.providers.contacts;
 
-import com.google.android.collect.Maps;
-import com.google.common.annotations.VisibleForTesting;
-
 import android.content.Context;
 import android.content.SharedPreferences;
+import android.database.Cursor;
 import android.net.Uri;
 import android.os.Bundle;
 import android.preference.PreferenceManager;
@@ -28,6 +26,9 @@
 import android.text.TextUtils;
 import android.util.Log;
 
+import com.google.android.collect.Maps;
+import com.google.common.annotations.VisibleForTesting;
+
 import java.util.Map;
 import java.util.regex.Pattern;
 
diff --git a/src/com/android/providers/contacts/LegacyApiSupport.java b/src/com/android/providers/contacts/LegacyApiSupport.java
index 78cbc9d..9859d11 100644
--- a/src/com/android/providers/contacts/LegacyApiSupport.java
+++ b/src/com/android/providers/contacts/LegacyApiSupport.java
@@ -15,19 +15,6 @@
  */
 package com.android.providers.contacts;
 
-import com.android.providers.contacts.ContactsDatabaseHelper.AccountsColumns;
-import com.android.providers.contacts.ContactsDatabaseHelper.DataColumns;
-import com.android.providers.contacts.ContactsDatabaseHelper.ExtensionsColumns;
-import com.android.providers.contacts.ContactsDatabaseHelper.GroupsColumns;
-import com.android.providers.contacts.ContactsDatabaseHelper.MimetypesColumns;
-import com.android.providers.contacts.ContactsDatabaseHelper.NameLookupColumns;
-import com.android.providers.contacts.ContactsDatabaseHelper.NameLookupType;
-import com.android.providers.contacts.ContactsDatabaseHelper.PhoneLookupColumns;
-import com.android.providers.contacts.ContactsDatabaseHelper.PresenceColumns;
-import com.android.providers.contacts.ContactsDatabaseHelper.RawContactsColumns;
-import com.android.providers.contacts.ContactsDatabaseHelper.StatusUpdatesColumns;
-import com.android.providers.contacts.ContactsDatabaseHelper.Tables;
-
 import android.accounts.Account;
 import android.app.SearchManager;
 import android.content.ContentUris;
@@ -65,6 +52,19 @@
 import android.text.TextUtils;
 import android.util.Log;
 
+import com.android.providers.contacts.ContactsDatabaseHelper.AccountsColumns;
+import com.android.providers.contacts.ContactsDatabaseHelper.DataColumns;
+import com.android.providers.contacts.ContactsDatabaseHelper.ExtensionsColumns;
+import com.android.providers.contacts.ContactsDatabaseHelper.GroupsColumns;
+import com.android.providers.contacts.ContactsDatabaseHelper.MimetypesColumns;
+import com.android.providers.contacts.ContactsDatabaseHelper.NameLookupColumns;
+import com.android.providers.contacts.ContactsDatabaseHelper.NameLookupType;
+import com.android.providers.contacts.ContactsDatabaseHelper.PhoneLookupColumns;
+import com.android.providers.contacts.ContactsDatabaseHelper.PresenceColumns;
+import com.android.providers.contacts.ContactsDatabaseHelper.RawContactsColumns;
+import com.android.providers.contacts.ContactsDatabaseHelper.StatusUpdatesColumns;
+import com.android.providers.contacts.ContactsDatabaseHelper.Tables;
+
 import java.util.HashMap;
 import java.util.Locale;
 
diff --git a/src/com/android/providers/contacts/NameLookupBuilder.java b/src/com/android/providers/contacts/NameLookupBuilder.java
index 5ebbcd1..8375b88 100644
--- a/src/com/android/providers/contacts/NameLookupBuilder.java
+++ b/src/com/android/providers/contacts/NameLookupBuilder.java
@@ -16,11 +16,11 @@
 
 package com.android.providers.contacts;
 
+import android.provider.ContactsContract.FullNameStyle;
+
 import com.android.providers.contacts.ContactsDatabaseHelper.NameLookupType;
 import com.android.providers.contacts.SearchIndexManager.IndexBuilder;
 
-import android.provider.ContactsContract.FullNameStyle;
-
 import java.util.Arrays;
 import java.util.Comparator;
 import java.util.Iterator;
diff --git a/src/com/android/providers/contacts/NameNormalizer.java b/src/com/android/providers/contacts/NameNormalizer.java
index d91bd7c..e3f98a8 100644
--- a/src/com/android/providers/contacts/NameNormalizer.java
+++ b/src/com/android/providers/contacts/NameNormalizer.java
@@ -16,11 +16,12 @@
 package com.android.providers.contacts;
 
 import com.android.providers.contacts.util.Hex;
+import com.google.common.annotations.VisibleForTesting;
 
-import java.util.Locale;
-import java.text.Collator;
 import java.text.CollationKey;
+import java.text.Collator;
 import java.text.RuleBasedCollator;
+import java.util.Locale;
 
 /**
  * Converts a name to a normalized form by removing all non-letter characters and normalizing
@@ -28,17 +29,45 @@
  */
 public class NameNormalizer {
 
-    private static final RuleBasedCollator sCompressingCollator;
-    static {
-        sCompressingCollator = (RuleBasedCollator)Collator.getInstance(Locale.getDefault());
-        sCompressingCollator.setStrength(Collator.PRIMARY);
-        sCompressingCollator.setDecomposition(Collator.CANONICAL_DECOMPOSITION);
+    private static final Object sCollatorLock = new Object();
+
+    private static Locale sCollatorLocale;
+
+    private static RuleBasedCollator sCachedCompressingCollator;
+    private static RuleBasedCollator sCachedComplexityCollator;
+
+    /**
+     * Ensure that the cached collators are for the current locale.
+     */
+    private static void ensureCollators() {
+        final Locale locale = Locale.getDefault();
+        if (locale.equals(sCollatorLocale)) {
+            return;
+        }
+        sCollatorLocale = locale;
+
+        sCachedCompressingCollator = (RuleBasedCollator) Collator.getInstance(locale);
+        sCachedCompressingCollator.setStrength(Collator.PRIMARY);
+        sCachedCompressingCollator.setDecomposition(Collator.CANONICAL_DECOMPOSITION);
+
+        sCachedComplexityCollator = (RuleBasedCollator) Collator.getInstance(locale);
+        sCachedComplexityCollator.setStrength(Collator.SECONDARY);
     }
 
-    private static final RuleBasedCollator sComplexityCollator;
-    static {
-        sComplexityCollator = (RuleBasedCollator)Collator.getInstance(Locale.getDefault());
-        sComplexityCollator.setStrength(Collator.SECONDARY);
+    @VisibleForTesting
+    static RuleBasedCollator getCompressingCollator() {
+        synchronized (sCollatorLock) {
+            ensureCollators();
+            return sCachedCompressingCollator;
+        }
+    }
+
+    @VisibleForTesting
+    static RuleBasedCollator getComplexityCollator() {
+        synchronized (sCollatorLock) {
+            ensureCollators();
+            return sCachedComplexityCollator;
+        }
     }
 
     /**
@@ -46,7 +75,7 @@
      * of names.  It ignores non-letter, non-digit characters, and removes accents.
      */
     public static String normalize(String name) {
-        CollationKey key = sCompressingCollator.getCollationKey(lettersAndDigitsOnly(name));
+        CollationKey key = getCompressingCollator().getCollationKey(lettersAndDigitsOnly(name));
         return Hex.encodeHex(key.toByteArray(), true);
     }
 
@@ -57,7 +86,7 @@
     public static int compareComplexity(String name1, String name2) {
         String clean1 = lettersAndDigitsOnly(name1);
         String clean2 = lettersAndDigitsOnly(name2);
-        int diff = sComplexityCollator.compare(clean1, clean2);
+        int diff = getComplexityCollator().compare(clean1, clean2);
         if (diff != 0) {
             return diff;
         }
diff --git a/src/com/android/providers/contacts/NameSplitter.java b/src/com/android/providers/contacts/NameSplitter.java
index fd5b096..43743ee 100644
--- a/src/com/android/providers/contacts/NameSplitter.java
+++ b/src/com/android/providers/contacts/NameSplitter.java
@@ -15,14 +15,14 @@
  */
 package com.android.providers.contacts;
 
-import com.android.providers.contacts.util.NeededForTesting;
-
 import android.content.ContentValues;
+import android.provider.ContactsContract.CommonDataKinds.StructuredName;
 import android.provider.ContactsContract.FullNameStyle;
 import android.provider.ContactsContract.PhoneticNameStyle;
-import android.provider.ContactsContract.CommonDataKinds.StructuredName;
 import android.text.TextUtils;
 
+import com.android.providers.contacts.util.NeededForTesting;
+
 import java.lang.Character.UnicodeBlock;
 import java.util.HashSet;
 import java.util.Locale;
diff --git a/src/com/android/providers/contacts/PhotoPriorityResolver.java b/src/com/android/providers/contacts/PhotoPriorityResolver.java
index c0dc4d9..150811c 100644
--- a/src/com/android/providers/contacts/PhotoPriorityResolver.java
+++ b/src/com/android/providers/contacts/PhotoPriorityResolver.java
@@ -16,22 +16,22 @@
 
 package com.android.providers.contacts;
 
-import com.android.internal.util.XmlUtils;
-import com.google.android.collect.Maps;
-
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-
 import android.accounts.AccountManager;
 import android.accounts.AuthenticatorDescription;
 import android.content.Context;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
-import android.content.pm.ServiceInfo;
 import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.ServiceInfo;
 import android.content.res.XmlResourceParser;
 import android.util.Log;
 
+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;
 
diff --git a/src/com/android/providers/contacts/PhotoProcessor.java b/src/com/android/providers/contacts/PhotoProcessor.java
index cf81ff3..1b8fc3f 100644
--- a/src/com/android/providers/contacts/PhotoProcessor.java
+++ b/src/com/android/providers/contacts/PhotoProcessor.java
@@ -15,13 +15,18 @@
  */
 package com.android.providers.contacts;
 
-import com.android.providers.contacts.util.MemoryUtils;
-
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
-import android.graphics.Matrix;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.Rect;
+import android.graphics.RectF;
 import android.os.SystemProperties;
 
+import com.android.providers.contacts.util.MemoryUtils;
+import com.google.common.annotations.VisibleForTesting;
+
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 
@@ -43,6 +48,12 @@
     /** Compression for thumbnails that also have a display photo */
     private static final int COMPRESSION_THUMBNAIL_LOW = 90;
 
+    private static final Paint WHITE_PAINT = new Paint();
+
+    static {
+        WHITE_PAINT.setColor(Color.WHITE);
+    }
+
     private static int sMaxThumbnailDim;
     private static int sMaxDisplayPhotoDim;
 
@@ -169,43 +180,65 @@
         if (mOriginal == null) {
             throw new IOException("Invalid image file");
         }
-        mDisplayPhoto = getScaledBitmap(mMaxDisplayPhotoDim);
-        mThumbnailPhoto = getScaledBitmap(mMaxThumbnailPhotoDim);
+        mDisplayPhoto = getNormalizedBitmap(mOriginal, mMaxDisplayPhotoDim, mForceCropToSquare);
+        mThumbnailPhoto = getNormalizedBitmap(mOriginal,mMaxThumbnailPhotoDim, mForceCropToSquare);
     }
 
     /**
      * Scales down the original bitmap to fit within the given maximum width and height.
      * If the bitmap already fits in those dimensions, the original bitmap will be
      * returned unmodified unless the photo processor is set up to crop it to a square.
+     *
+     * Also, if the image has transparency, conevrt it to white.
+     *
+     * @param original Original bitmap
      * @param maxDim Maximum width and height (in pixels) for the image.
+     * @param forceCropToSquare See {@link #PhotoProcessor(Bitmap, int, int, boolean)}
      * @return A bitmap that fits the maximum dimensions.
      */
     @SuppressWarnings({"SuspiciousNameCombination"})
-    private Bitmap getScaledBitmap(int maxDim) {
-        Bitmap scaledBitmap = mOriginal;
-        int width = mOriginal.getWidth();
-        int height = mOriginal.getHeight();
+    @VisibleForTesting
+    static Bitmap getNormalizedBitmap(Bitmap original, int maxDim, boolean forceCropToSquare) {
+        final boolean originalHasAlpha = original.hasAlpha();
+
+        // All cropXxx's are in the original coordinate.
+        int cropWidth = original.getWidth();
+        int cropHeight = original.getHeight();
         int cropLeft = 0;
         int cropTop = 0;
-        if (mForceCropToSquare && width != height) {
+        if (forceCropToSquare && cropWidth != cropHeight) {
             // Crop the image to the square at its center.
-            if (height > width) {
-                cropTop = (height - width) / 2;
-                height = width;
+            if (cropHeight > cropWidth) {
+                cropTop = (cropHeight - cropWidth) / 2;
+                cropHeight = cropWidth;
             } else {
-                cropLeft = (width - height) / 2;
-                width = height;
+                cropLeft = (cropWidth - cropHeight) / 2;
+                cropWidth = cropHeight;
             }
         }
-        float scaleFactor = ((float) maxDim) / Math.max(width, height);
-        if (scaleFactor < 1.0f || cropLeft != 0 || cropTop != 0) {
-            // Need to scale or crop the photo.
-            Matrix matrix = new Matrix();
-            if (scaleFactor < 1.0f) matrix.setScale(scaleFactor, scaleFactor);
-            scaledBitmap = Bitmap.createBitmap(
-                    mOriginal, cropLeft, cropTop, width, height, matrix, true);
+        // Calculate the scale factor.  We don't want to scale up, so the max scale is 1f.
+        final float scaleFactor = Math.min(1f, ((float) maxDim) / Math.max(cropWidth, cropHeight));
+
+        if (scaleFactor < 1.0f || cropLeft != 0 || cropTop != 0 || originalHasAlpha) {
+            final int newWidth = (int) (cropWidth * scaleFactor);
+            final int newHeight = (int) (cropHeight * scaleFactor);
+            final Bitmap scaledBitmap = Bitmap.createBitmap(newWidth, newHeight,
+                    Bitmap.Config.ARGB_8888);
+            final Canvas c = new Canvas(scaledBitmap);
+
+            if (originalHasAlpha) {
+                c.drawRect(0, 0, scaledBitmap.getWidth(), scaledBitmap.getHeight(), WHITE_PAINT);
+            }
+
+            final Rect src = new Rect(cropLeft, cropTop,
+                    cropLeft + cropWidth, cropTop + cropHeight);
+            final RectF dst = new RectF(0, 0, scaledBitmap.getWidth(), scaledBitmap.getHeight());
+
+            c.drawBitmap(original, src, dst, null);
+            return scaledBitmap;
+        } else {
+            return original;
         }
-        return scaledBitmap;
     }
 
     /**
diff --git a/src/com/android/providers/contacts/PhotoStore.java b/src/com/android/providers/contacts/PhotoStore.java
index e0b5fb4..e7be48c 100644
--- a/src/com/android/providers/contacts/PhotoStore.java
+++ b/src/com/android/providers/contacts/PhotoStore.java
@@ -15,17 +15,16 @@
  */
 package com.android.providers.contacts;
 
-import com.android.providers.contacts.ContactsDatabaseHelper.PhotoFilesColumns;
-import com.android.providers.contacts.ContactsDatabaseHelper.Tables;
-
-import com.google.common.annotations.VisibleForTesting;
-
 import android.content.ContentValues;
 import android.database.sqlite.SQLiteDatabase;
 import android.graphics.Bitmap;
 import android.provider.ContactsContract.PhotoFiles;
 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;
diff --git a/src/com/android/providers/contacts/ProfileAwareUriMatcher.java b/src/com/android/providers/contacts/ProfileAwareUriMatcher.java
index de5cce1..051c60e 100644
--- a/src/com/android/providers/contacts/ProfileAwareUriMatcher.java
+++ b/src/com/android/providers/contacts/ProfileAwareUriMatcher.java
@@ -16,13 +16,13 @@
 
 package com.android.providers.contacts;
 
-import com.google.android.collect.Lists;
-import com.google.android.collect.Maps;
-
 import android.content.UriMatcher;
 import android.net.Uri;
 import android.provider.ContactsContract;
 
+import com.google.android.collect.Lists;
+import com.google.android.collect.Maps;
+
 import java.util.List;
 import java.util.Map;
 import java.util.regex.Pattern;
diff --git a/src/com/android/providers/contacts/ProfileDatabaseHelper.java b/src/com/android/providers/contacts/ProfileDatabaseHelper.java
index 9b707a3..a23e521 100644
--- a/src/com/android/providers/contacts/ProfileDatabaseHelper.java
+++ b/src/com/android/providers/contacts/ProfileDatabaseHelper.java
@@ -16,13 +16,13 @@
 
 package com.android.providers.contacts;
 
-import com.android.providers.contacts.util.NeededForTesting;
-
 import android.content.ContentValues;
 import android.content.Context;
 import android.database.sqlite.SQLiteDatabase;
 import android.provider.ContactsContract.Profile;
 
+import com.android.providers.contacts.util.NeededForTesting;
+
 /**
  * A separate version of the contacts database helper for storing the user's profile data.
  */
diff --git a/src/com/android/providers/contacts/ProfileProvider.java b/src/com/android/providers/contacts/ProfileProvider.java
index d97760d..7fc0916 100644
--- a/src/com/android/providers/contacts/ProfileProvider.java
+++ b/src/com/android/providers/contacts/ProfileProvider.java
@@ -17,11 +17,13 @@
 
 import android.content.ContentValues;
 import android.content.Context;
+import android.content.Intent;
 import android.content.res.AssetFileDescriptor;
 import android.database.Cursor;
 import android.database.sqlite.SQLiteDatabase;
 import android.net.Uri;
 import android.os.CancellationSignal;
+import android.provider.ContactsContract.Intents;
 
 import java.io.FileNotFoundException;
 import java.util.Locale;
@@ -31,7 +33,6 @@
  * database from the rest of contacts.
  */
 public class ProfileProvider extends AbstractContactsProvider {
-
     private static final String READ_PERMISSION = "android.permission.READ_PROFILE";
     private static final String WRITE_PERMISSION = "android.permission.WRITE_PROFILE";
 
@@ -81,7 +82,6 @@
     public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
             String sortOrder, CancellationSignal cancellationSignal) {
         enforceReadPermission(uri);
-        mDelegate.substituteDb(getDatabaseHelper().getReadableDatabase());
         return mDelegate.queryLocal(uri, projection, selection, selectionArgs, sortOrder, -1,
                 cancellationSignal);
     }
@@ -112,10 +112,8 @@
     public AssetFileDescriptor openAssetFile(Uri uri, String mode) throws FileNotFoundException {
         if (mode != null && mode.contains("w")) {
             enforceWritePermission();
-            mDelegate.substituteDb(getDatabaseHelper().getWritableDatabase());
         } else {
             enforceReadPermission(uri);
-            mDelegate.substituteDb(getDatabaseHelper().getReadableDatabase());
         }
         return mDelegate.openAssetFileLocal(uri, mode);
     }
@@ -124,7 +122,6 @@
         ContactsTransaction transaction = getCurrentTransaction();
         SQLiteDatabase db = getDatabaseHelper().getWritableDatabase();
         transaction.startTransactionForDb(db, ContactsProvider2.PROFILE_DB_TAG, this);
-        mDelegate.substituteDb(db);
     }
 
     @Override
@@ -142,20 +139,18 @@
 
     @Override
     public void onBegin() {
-        mDelegate.switchToProfileMode();
-        mDelegate.onBegin();
+        mDelegate.onBeginTransactionInternal(true);
     }
 
     @Override
     public void onCommit() {
-        mDelegate.switchToProfileMode();
-        mDelegate.onCommit();
+        mDelegate.onCommitTransactionInternal(true);
+        sendProfileChangedBroadcast();
     }
 
     @Override
     public void onRollback() {
-        mDelegate.switchToProfileMode();
-        mDelegate.onRollback();
+        mDelegate.onRollbackTransactionInternal(true);
     }
 
     @Override
@@ -167,4 +162,15 @@
     public String getType(Uri uri) {
         return mDelegate.getType(uri);
     }
+
+    /** Use only for debug logging */
+    @Override
+    public String toString() {
+        return "ProfileProvider";
+    }
+
+    private void sendProfileChangedBroadcast() {
+        final Intent intent = new Intent(Intents.ACTION_PROFILE_CHANGED);
+        getContext().sendBroadcast(intent, READ_PERMISSION);
+    }
 }
diff --git a/src/com/android/providers/contacts/SearchIndexManager.java b/src/com/android/providers/contacts/SearchIndexManager.java
index bd4e1cc..20fd16b 100644
--- a/src/com/android/providers/contacts/SearchIndexManager.java
+++ b/src/com/android/providers/contacts/SearchIndexManager.java
@@ -15,14 +15,6 @@
  */
 package com.android.providers.contacts;
 
-import com.android.providers.contacts.ContactsDatabaseHelper.DataColumns;
-import com.android.providers.contacts.ContactsDatabaseHelper.MimetypesColumns;
-import com.android.providers.contacts.ContactsDatabaseHelper.RawContactsColumns;
-import com.android.providers.contacts.ContactsDatabaseHelper.SearchIndexColumns;
-import com.android.providers.contacts.ContactsDatabaseHelper.Tables;
-import com.google.android.collect.Lists;
-import com.google.common.annotations.VisibleForTesting;
-
 import android.content.ContentValues;
 import android.database.Cursor;
 import android.database.sqlite.SQLiteDatabase;
@@ -37,6 +29,14 @@
 import android.text.TextUtils;
 import android.util.Log;
 
+import com.android.providers.contacts.ContactsDatabaseHelper.DataColumns;
+import com.android.providers.contacts.ContactsDatabaseHelper.MimetypesColumns;
+import com.android.providers.contacts.ContactsDatabaseHelper.RawContactsColumns;
+import com.android.providers.contacts.ContactsDatabaseHelper.SearchIndexColumns;
+import com.android.providers.contacts.ContactsDatabaseHelper.Tables;
+import com.google.android.collect.Lists;
+import com.google.common.annotations.VisibleForTesting;
+
 import java.util.ArrayList;
 import java.util.HashSet;
 import java.util.List;
@@ -243,13 +243,19 @@
         mDbHelper = (ContactsDatabaseHelper) mContactsProvider.getDatabaseHelper();
     }
 
-    public void updateIndex() {
-        if (getSearchIndexVersion() == SEARCH_INDEX_VERSION) {
-            return;
+    public void updateIndex(boolean force) {
+        if (force) {
+            setSearchIndexVersion(0);
+        } else {
+            if (getSearchIndexVersion() == SEARCH_INDEX_VERSION) {
+                return;
+            }
         }
         SQLiteDatabase db = mDbHelper.getWritableDatabase();
         db.beginTransaction();
         try {
+            // We do a version check again, because the version might have been modified after
+            // the first check.  We need to do the check again in a transaction to make sure.
             if (getSearchIndexVersion() != SEARCH_INDEX_VERSION) {
                 rebuildIndex(db);
                 setSearchIndexVersion(SEARCH_INDEX_VERSION);
diff --git a/src/com/android/providers/contacts/VoicemailCleanupService.java b/src/com/android/providers/contacts/VoicemailCleanupService.java
index 39f5be5..4ad1406 100644
--- a/src/com/android/providers/contacts/VoicemailCleanupService.java
+++ b/src/com/android/providers/contacts/VoicemailCleanupService.java
@@ -16,8 +16,6 @@
 
 package com.android.providers.contacts;
 
-import com.google.common.annotations.VisibleForTesting;
-
 import android.app.IntentService;
 import android.content.ContentResolver;
 import android.content.Intent;
@@ -25,6 +23,8 @@
 import android.provider.VoicemailContract.Voicemails;
 import android.util.Log;
 
+import com.google.common.annotations.VisibleForTesting;
+
 /**
  * A service that cleans up voicemail related data for packages that are uninstalled.
  */
diff --git a/src/com/android/providers/contacts/VoicemailContentProvider.java b/src/com/android/providers/contacts/VoicemailContentProvider.java
index 79d8f92..b2f6b1e 100644
--- a/src/com/android/providers/contacts/VoicemailContentProvider.java
+++ b/src/com/android/providers/contacts/VoicemailContentProvider.java
@@ -19,11 +19,6 @@
 import static com.android.providers.contacts.util.DbQueryUtils.concatenateClauses;
 import static com.android.providers.contacts.util.DbQueryUtils.getEqualityClause;
 
-import com.android.providers.contacts.ContactsDatabaseHelper.Tables;
-import com.android.providers.contacts.util.SelectionBuilder;
-import com.android.providers.contacts.util.TypedUriMatcherImpl;
-import com.google.common.annotations.VisibleForTesting;
-
 import android.content.ContentProvider;
 import android.content.ContentValues;
 import android.content.Context;
@@ -38,6 +33,11 @@
 import android.provider.VoicemailContract.Voicemails;
 import android.util.Log;
 
+import com.android.providers.contacts.ContactsDatabaseHelper.Tables;
+import com.android.providers.contacts.util.SelectionBuilder;
+import com.android.providers.contacts.util.TypedUriMatcherImpl;
+import com.google.common.annotations.VisibleForTesting;
+
 import java.io.FileNotFoundException;
 import java.util.List;
 
diff --git a/src/com/android/providers/contacts/VoicemailContentTable.java b/src/com/android/providers/contacts/VoicemailContentTable.java
index 5cfbca7..dfa1e76 100644
--- a/src/com/android/providers/contacts/VoicemailContentTable.java
+++ b/src/com/android/providers/contacts/VoicemailContentTable.java
@@ -19,10 +19,6 @@
 import static com.android.providers.contacts.util.DbQueryUtils.concatenateClauses;
 import static com.android.providers.contacts.util.DbQueryUtils.getEqualityClause;
 
-import com.android.common.content.ProjectionMap;
-import com.android.providers.contacts.VoicemailContentProvider.UriData;
-import com.android.providers.contacts.util.CloseUtils;
-
 import android.content.ContentUris;
 import android.content.ContentValues;
 import android.content.Context;
@@ -38,6 +34,9 @@
 import android.provider.VoicemailContract.Voicemails;
 import android.util.Log;
 
+import com.android.common.content.ProjectionMap;
+import com.android.providers.contacts.VoicemailContentProvider.UriData;
+import com.android.providers.contacts.util.CloseUtils;
 import com.google.common.collect.ImmutableSet;
 
 import java.io.File;
diff --git a/src/com/android/providers/contacts/VoicemailStatusTable.java b/src/com/android/providers/contacts/VoicemailStatusTable.java
index a0a61ba..2c1861b 100644
--- a/src/com/android/providers/contacts/VoicemailStatusTable.java
+++ b/src/com/android/providers/contacts/VoicemailStatusTable.java
@@ -18,9 +18,6 @@
 
 import static com.android.providers.contacts.util.DbQueryUtils.concatenateClauses;
 
-import com.android.common.content.ProjectionMap;
-import com.android.providers.contacts.VoicemailContentProvider.UriData;
-
 import android.content.ContentUris;
 import android.content.ContentValues;
 import android.content.Context;
@@ -32,6 +29,9 @@
 import android.os.ParcelFileDescriptor;
 import android.provider.VoicemailContract.Status;
 
+import com.android.common.content.ProjectionMap;
+import com.android.providers.contacts.VoicemailContentProvider.UriData;
+
 /**
  * Implementation of {@link VoicemailTable.Delegate} for the voicemail status table.
  */
diff --git a/src/com/android/providers/contacts/VoicemailTable.java b/src/com/android/providers/contacts/VoicemailTable.java
index db35c98..9e6c431 100644
--- a/src/com/android/providers/contacts/VoicemailTable.java
+++ b/src/com/android/providers/contacts/VoicemailTable.java
@@ -16,13 +16,13 @@
 
 package com.android.providers.contacts;
 
-import com.android.providers.contacts.VoicemailContentProvider.UriData;
-
 import android.content.ContentValues;
 import android.database.Cursor;
 import android.net.Uri;
 import android.os.ParcelFileDescriptor;
 
+import com.android.providers.contacts.VoicemailContentProvider.UriData;
+
 import java.io.FileNotFoundException;
 
 /**
diff --git a/src/com/android/providers/contacts/aggregation/ContactAggregator.java b/src/com/android/providers/contacts/aggregation/ContactAggregator.java
index d3931e6..baae2e5 100644
--- a/src/com/android/providers/contacts/aggregation/ContactAggregator.java
+++ b/src/com/android/providers/contacts/aggregation/ContactAggregator.java
@@ -16,6 +16,29 @@
 
 package com.android.providers.contacts.aggregation;
 
+import android.database.Cursor;
+import android.database.DatabaseUtils;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteQueryBuilder;
+import android.database.sqlite.SQLiteStatement;
+import android.net.Uri;
+import android.provider.ContactsContract.AggregationExceptions;
+import android.provider.ContactsContract.CommonDataKinds.Email;
+import android.provider.ContactsContract.CommonDataKinds.Identity;
+import android.provider.ContactsContract.CommonDataKinds.Phone;
+import android.provider.ContactsContract.CommonDataKinds.Photo;
+import android.provider.ContactsContract.Contacts;
+import android.provider.ContactsContract.Contacts.AggregationSuggestions;
+import android.provider.ContactsContract.Data;
+import android.provider.ContactsContract.DisplayNameSources;
+import android.provider.ContactsContract.FullNameStyle;
+import android.provider.ContactsContract.PhotoFiles;
+import android.provider.ContactsContract.RawContacts;
+import android.provider.ContactsContract.StatusUpdates;
+import android.text.TextUtils;
+import android.util.EventLog;
+import android.util.Log;
+
 import com.android.providers.contacts.ContactLookupKey;
 import com.android.providers.contacts.ContactsDatabaseHelper;
 import com.android.providers.contacts.ContactsDatabaseHelper.AccountsColumns;
@@ -42,29 +65,6 @@
 import com.android.providers.contacts.aggregation.util.ContactMatcher.MatchScore;
 import com.google.android.collect.Maps;
 
-import android.database.Cursor;
-import android.database.DatabaseUtils;
-import android.database.sqlite.SQLiteDatabase;
-import android.database.sqlite.SQLiteQueryBuilder;
-import android.database.sqlite.SQLiteStatement;
-import android.net.Uri;
-import android.provider.ContactsContract.AggregationExceptions;
-import android.provider.ContactsContract.CommonDataKinds.Email;
-import android.provider.ContactsContract.CommonDataKinds.Identity;
-import android.provider.ContactsContract.CommonDataKinds.Phone;
-import android.provider.ContactsContract.CommonDataKinds.Photo;
-import android.provider.ContactsContract.Contacts;
-import android.provider.ContactsContract.Contacts.AggregationSuggestions;
-import android.provider.ContactsContract.Data;
-import android.provider.ContactsContract.DisplayNameSources;
-import android.provider.ContactsContract.FullNameStyle;
-import android.provider.ContactsContract.PhotoFiles;
-import android.provider.ContactsContract.RawContacts;
-import android.provider.ContactsContract.StatusUpdates;
-import android.text.TextUtils;
-import android.util.EventLog;
-import android.util.Log;
-
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashMap;
diff --git a/src/com/android/providers/contacts/aggregation/ProfileAggregator.java b/src/com/android/providers/contacts/aggregation/ProfileAggregator.java
index fedf5fe..276a05f 100644
--- a/src/com/android/providers/contacts/aggregation/ProfileAggregator.java
+++ b/src/com/android/providers/contacts/aggregation/ProfileAggregator.java
@@ -23,11 +23,11 @@
 import com.android.providers.contacts.ContactLookupKey;
 import com.android.providers.contacts.ContactsDatabaseHelper;
 import com.android.providers.contacts.ContactsDatabaseHelper.Tables;
-import com.android.providers.contacts.aggregation.util.CommonNicknameCache;
 import com.android.providers.contacts.ContactsProvider2;
 import com.android.providers.contacts.NameSplitter;
 import com.android.providers.contacts.PhotoPriorityResolver;
 import com.android.providers.contacts.TransactionContext;
+import com.android.providers.contacts.aggregation.util.CommonNicknameCache;
 
 /**
  * A version of the ContactAggregator for use against the profile database.
diff --git a/src/com/android/providers/contacts/aggregation/util/CommonNicknameCache.java b/src/com/android/providers/contacts/aggregation/util/CommonNicknameCache.java
index d6b799f..9643d81 100644
--- a/src/com/android/providers/contacts/aggregation/util/CommonNicknameCache.java
+++ b/src/com/android/providers/contacts/aggregation/util/CommonNicknameCache.java
@@ -16,13 +16,13 @@
 
 package com.android.providers.contacts.aggregation.util;
 
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+
 import com.android.providers.contacts.ContactsDatabaseHelper.NicknameLookupColumns;
 import com.android.providers.contacts.ContactsDatabaseHelper.Tables;
 import com.google.android.collect.Maps;
 
-import android.database.Cursor;
-import android.database.sqlite.SQLiteDatabase;
-
 import java.lang.ref.SoftReference;
 import java.util.BitSet;
 import java.util.HashMap;
diff --git a/src/com/android/providers/contacts/debug/ContactsDumpActivity.java b/src/com/android/providers/contacts/debug/ContactsDumpActivity.java
new file mode 100644
index 0000000..359f3f8
--- /dev/null
+++ b/src/com/android/providers/contacts/debug/ContactsDumpActivity.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.providers.contacts.debug;
+
+import com.android.providers.contacts.R;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.Window;
+import android.widget.Button;
+
+import java.io.IOException;
+
+/**
+ * Activity to export all app data files as a zip file on sdcard, and send it via email.
+ *
+ * Usage:
+ * adb shell am start -a com.android.providers.contacts.DUMP_DATABASE
+ */
+public class ContactsDumpActivity extends Activity implements OnClickListener {
+    private static String TAG = "ContactsDumpActivity";
+    private Button mConfirmButton;
+    private Button mCancelButton;
+    private Button mDeleteButton;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        // Be sure to call the super class.
+        super.onCreate(savedInstanceState);
+
+        requestWindowFeature(Window.FEATURE_LEFT_ICON);
+
+        setContentView(R.layout.contact_dump_activity);
+
+        getWindow().setFeatureDrawableResource(Window.FEATURE_LEFT_ICON,
+                android.R.drawable.ic_dialog_alert);
+
+        mConfirmButton = (Button) findViewById(R.id.confirm);
+        mCancelButton = (Button) findViewById(R.id.cancel);
+        mDeleteButton = (Button) findViewById(R.id.delete);
+        updateDeleteButton();
+    }
+
+    private void updateDeleteButton() {
+        mDeleteButton.setEnabled(DataExporter.dumpFileExists(this));
+    }
+
+    @Override
+    public void onClick(View v) {
+        switch (v.getId()) {
+            case R.id.confirm:
+                mConfirmButton.setEnabled(false);
+                mCancelButton.setEnabled(false);
+                new DumpDbTask().execute();
+                break;
+            case R.id.delete:
+                cleanup();
+                updateDeleteButton();
+                break;
+            case R.id.cancel:
+                finish();
+                break;
+        }
+    }
+
+    private void cleanup() {
+        DataExporter.removeDumpFiles(this);
+    }
+
+    private class DumpDbTask extends AsyncTask<Void, Void, Uri> {
+        /**
+         * Starts spinner while task is running.
+         */
+        @Override
+        protected void onPreExecute() {
+            setProgressBarIndeterminateVisibility(true);
+        }
+
+        @Override
+        protected Uri doInBackground(Void... params) {
+            try {
+                return DataExporter.exportData(getApplicationContext());
+            } catch (IOException e) {
+                Log.e(TAG, "Failed to export", e);
+                return null;
+            }
+        }
+
+        @Override
+        protected void onPostExecute(Uri uri) {
+            if (uri != null) {
+                emailFile(uri);
+            }
+        }
+    }
+
+    private void emailFile(Uri uri) {
+        Log.i(TAG, "Drafting email");
+        Intent intent = new Intent(Intent.ACTION_SEND);
+        intent.putExtra(Intent.EXTRA_SUBJECT, getString(R.string.debug_dump_email_subject));
+        intent.putExtra(Intent.EXTRA_TEXT, getString(R.string.debug_dump_email_body));
+        intent.setType(DataExporter.ZIP_MIME_TYPE);
+        intent.putExtra(Intent.EXTRA_STREAM, uri);
+        startActivityForResult(Intent.createChooser(intent,
+                getString(R.string.debug_dump_email_sender_picker)), 0);
+    }
+
+    @Override
+    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+        updateDeleteButton();
+        mConfirmButton.setEnabled(true);
+        mCancelButton.setEnabled(true);
+    }
+}
diff --git a/src/com/android/providers/contacts/debug/DataExporter.java b/src/com/android/providers/contacts/debug/DataExporter.java
new file mode 100644
index 0000000..84dc072
--- /dev/null
+++ b/src/com/android/providers/contacts/debug/DataExporter.java
@@ -0,0 +1,176 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.providers.contacts.debug;
+
+import com.android.providers.contacts.util.Hex;
+import com.google.common.io.Closeables;
+
+import android.content.Context;
+import android.net.Uri;
+import android.util.Log;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.SecureRandom;
+import java.util.zip.Deflater;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipOutputStream;
+
+/**
+ * Compress all files under the app data dir into a single zip file.
+ *
+ * Make sure not to output dump filenames anywhere, including logcat.
+ */
+public class DataExporter {
+    private static String TAG = "DataExporter";
+
+    public static final String ZIP_MIME_TYPE = "application/zip";
+
+    public static final String DUMP_FILE_DIRECTORY_NAME = "dumpedfiles";
+
+    public static final String OUT_FILE_SUFFIX = "-contacts-db.zip";
+
+    /**
+     * Compress all files under the app data dir into a single zip file, and return the content://
+     * URI to the file, which can be read via {@link DumpFileProvider}.
+     */
+    public static Uri exportData(Context context) throws IOException {
+        final String fileName = generateRandomName() + OUT_FILE_SUFFIX;
+        final File outFile = getOutputFile(context, fileName);
+
+        // Remove all existing ones.
+        removeDumpFiles(context);
+
+        Log.i(TAG, "Dump started...");
+
+        ensureOutputDirectory(context);
+        final ZipOutputStream os = new ZipOutputStream(new FileOutputStream(outFile));
+        os.setLevel(Deflater.BEST_COMPRESSION);
+        try {
+            addDirectory(context, os, context.getFilesDir().getParentFile(), "contacts-files");
+        } finally {
+            Closeables.closeQuietly(os);
+        }
+        Log.i(TAG, "Dump finished.");
+        return DumpFileProvider.AUTHORITY_URI.buildUpon().appendPath(fileName).build();
+    }
+
+    /** @return long random string for a file name */
+    private static String generateRandomName() {
+        final SecureRandom rng = new SecureRandom();
+        final byte[] random = new byte[256 / 8];
+        rng.nextBytes(random);
+
+        return Hex.encodeHex(random, true);
+    }
+
+    private static File getOutputDirectory(Context context) {
+        return new File(context.getCacheDir(), DUMP_FILE_DIRECTORY_NAME);
+    }
+
+    private static void ensureOutputDirectory(Context context) {
+        final File directory = getOutputDirectory(context);
+        if (!directory.exists()) {
+            directory.mkdir();
+        }
+    }
+
+    public static File getOutputFile(Context context, String fileName) {
+        return new File(getOutputDirectory(context), fileName);
+    }
+
+    public static boolean dumpFileExists(Context context) {
+        return getOutputDirectory(context).exists();
+    }
+
+    public static void removeDumpFiles(Context context) {
+        removeFileOrDirectory(getOutputDirectory(context));
+    }
+
+    private static void removeFileOrDirectory(File file) {
+        if (!file.exists()) return;
+
+        if (file.isFile()) {
+            Log.i(TAG, "Removing " + file);
+            file.delete();
+            return;
+        }
+
+        if (file.isDirectory()) {
+            for (File child : file.listFiles()) {
+                removeFileOrDirectory(child);
+            }
+            Log.i(TAG, "Removing " + file);
+            file.delete();
+        }
+    }
+
+    /**
+     * Add all files under {@code current} to {@code os} zip stream
+     */
+    private static void addDirectory(Context context, ZipOutputStream os, File current,
+            String storedPath) throws IOException {
+        for (File child : current.listFiles()) {
+            final String childStoredPath = storedPath + "/" + child.getName();
+
+            if (child.isDirectory()) {
+                // Don't need the cache directory, which also contains the dump files.
+                if (child.equals(context.getCacheDir())) {
+                    continue;
+                }
+                // This check is redundant as the output directory should be in the cache dir,
+                // but just in case...
+                if (child.getName().equals(DUMP_FILE_DIRECTORY_NAME)) {
+                    continue;
+                }
+                addDirectory(context, os, child, childStoredPath);
+            } else if (child.isFile()) {
+                addFile(os, child, childStoredPath);
+            } else {
+                // Shouldn't happen; skip.
+            }
+        }
+    }
+
+    /**
+     * Add a single file {@code current} to {@code os} zip stream using the file name
+     * {@code storedPath}.
+     */
+    private static void addFile(ZipOutputStream os, File current, String storedPath)
+            throws IOException {
+        Log.i(TAG, "Adding " + current.getAbsolutePath() + " ...");
+        final InputStream is = new FileInputStream(current);
+        os.putNextEntry(new ZipEntry(storedPath));
+
+        final byte[] buf = new byte[32 * 1024];
+        int totalLen = 0;
+        while (true) {
+            int len = is.read(buf);
+            if (len <= 0) {
+                break;
+            }
+            os.write(buf, 0, len);
+            totalLen += len;
+        }
+        os.closeEntry();
+        Log.i(TAG, "Added " + current.getAbsolutePath() + " as " + storedPath +
+                " (" + totalLen + " bytes)");
+    }
+}
diff --git a/src/com/android/providers/contacts/debug/DumpFileProvider.java b/src/com/android/providers/contacts/debug/DumpFileProvider.java
new file mode 100644
index 0000000..f349dd2
--- /dev/null
+++ b/src/com/android/providers/contacts/debug/DumpFileProvider.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.providers.contacts.debug;
+
+import android.content.ContentProvider;
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.database.MatrixCursor;
+import android.net.Uri;
+import android.os.ParcelFileDescriptor;
+import android.provider.OpenableColumns;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+
+/**
+ * Provider used to read dump files created by {@link DataExporter}.
+ *
+ * We send content: URI to sender apps (such as gmail).  This provider implement the URI.
+ */
+public class DumpFileProvider extends ContentProvider {
+    public static final String AUTHORITY = "com.android.contacts.dumpfile";
+    public static final Uri AUTHORITY_URI = Uri.parse("content://" + AUTHORITY);
+
+    @Override
+    public boolean onCreate() {
+        return true;
+    }
+
+    @Override
+    public Uri insert(Uri uri, ContentValues values) {
+        // Not needed.
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public int delete(Uri uri, String selection, String[] selectionArgs) {
+        // Not needed.
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
+        // Not needed.
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public String getType(Uri uri) {
+        return DataExporter.ZIP_MIME_TYPE;
+    }
+
+    /** @return the path part of a URI, without the beginning "/". */
+    private static String extractFileName(Uri uri) {
+        final String path = uri.getPath();
+        return path.startsWith("/") ? path.substring(1) : path;
+    }
+
+    /** @return file content */
+    @Override
+    public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {
+        if (!"r".equals(mode)) {
+            throw new UnsupportedOperationException();
+        }
+        final File file = DataExporter.getOutputFile(getContext(), extractFileName(uri));
+        return ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY);
+    }
+
+    /**
+     * Used to provide {@link OpenableColumns#DISPLAY_NAME} and {@link OpenableColumns#SIZE}
+     * for a URI.
+     */
+    @Override
+    public Cursor query(Uri uri, String[] inProjection, String selection, String[] selectionArgs,
+            String sortOrder) {
+        final String[] projection = (inProjection != null) ? inProjection
+                : new String[] {OpenableColumns.DISPLAY_NAME, OpenableColumns.SIZE};
+
+        final MatrixCursor c = new MatrixCursor(projection);
+
+        // Result will always have one row.
+        final MatrixCursor.RowBuilder b = c.newRow();
+
+        for (int i = 0; i < c.getColumnCount(); i++) {
+            final String column = projection[i];
+            if (OpenableColumns.DISPLAY_NAME.equals(column)) {
+                // Just return the requested path as the display name.  We don't care if the file
+                // really exists.
+                b.add(extractFileName(uri));
+            } else if (OpenableColumns.SIZE.equals(column)) {
+                final File file = DataExporter.getOutputFile(getContext(), extractFileName(uri));
+
+                if (file.exists()) {
+                    b.add(file.length());
+                } else {
+                    // File doesn't exist -- return null for "unknown".
+                    b.add(null);
+                }
+            } else {
+                throw new IllegalArgumentException("Unknown column " + column);
+            }
+        }
+
+        return c;
+    }
+}
diff --git a/tests/res/drawable-nodpi/transparent_10x10.png b/tests/res/drawable-nodpi/transparent_10x10.png
new file mode 100644
index 0000000..d11c2cd
--- /dev/null
+++ b/tests/res/drawable-nodpi/transparent_10x10.png
Binary files differ
diff --git a/tests/src/com/android/providers/contacts/BaseContactsProvider2Test.java b/tests/src/com/android/providers/contacts/BaseContactsProvider2Test.java
index 1154186..3759196 100644
--- a/tests/src/com/android/providers/contacts/BaseContactsProvider2Test.java
+++ b/tests/src/com/android/providers/contacts/BaseContactsProvider2Test.java
@@ -18,11 +18,6 @@
 
 import static com.android.providers.contacts.ContactsActor.PACKAGE_GREY;
 
-import com.android.providers.contacts.ContactsDatabaseHelper.Tables;
-import com.android.providers.contacts.util.Hex;
-import com.android.providers.contacts.util.MockClock;
-import com.google.android.collect.Sets;
-
 import android.accounts.Account;
 import android.content.ContentProvider;
 import android.content.ContentResolver;
@@ -62,6 +57,11 @@
 import android.test.mock.MockContentResolver;
 import android.util.Log;
 
+import com.android.providers.contacts.ContactsDatabaseHelper.Tables;
+import com.android.providers.contacts.util.Hex;
+import com.android.providers.contacts.util.MockClock;
+import com.google.android.collect.Sets;
+
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.BitSet;
diff --git a/tests/src/com/android/providers/contacts/CallLogProviderTest.java b/tests/src/com/android/providers/contacts/CallLogProviderTest.java
index 05f8c25..fc33e28 100644
--- a/tests/src/com/android/providers/contacts/CallLogProviderTest.java
+++ b/tests/src/com/android/providers/contacts/CallLogProviderTest.java
@@ -209,6 +209,66 @@
         assertEquals(3, getCount(Calls.CONTENT_URI_WITH_VOICEMAIL, null, null));
     }
 
+    public void testLimitParamReturnsCorrectLimit() {
+        for (int i=0; i<10; i++) {
+            insertCallRecord();
+        }
+        Uri uri = Calls.CONTENT_URI.buildUpon()
+                .appendQueryParameter(Calls.LIMIT_PARAM_KEY, "4")
+                .build();
+        assertEquals(4, getCount(uri, null, null));
+    }
+
+    public void testLimitAndOffsetParamReturnsCorrectEntries() {
+        for (int i=0; i<10; i++) {
+            mResolver.insert(Calls.CONTENT_URI, getDefaultValues(Calls.INCOMING_TYPE));
+        }
+        for (int i=0; i<10; i++) {
+            mResolver.insert(Calls.CONTENT_URI, getDefaultValues(Calls.MISSED_TYPE));
+        }
+        // Limit 4 records.  Discard first 8.
+        Uri uri = Calls.CONTENT_URI.buildUpon()
+                .appendQueryParameter(Calls.LIMIT_PARAM_KEY, "4")
+                .appendQueryParameter(Calls.OFFSET_PARAM_KEY, "8")
+                .build();
+        String[] projection = new String[] {Calls._ID, Calls.TYPE};
+        Cursor c = mResolver.query(uri, projection, null, null, null);
+        try {
+            // First two should be incoming, next two should be missed.
+            for (int i = 0; i < 2; i++) {
+                c.moveToNext();
+                assertEquals(Calls.INCOMING_TYPE, c.getInt(1));
+            }
+            for (int i = 0; i < 2; i++) {
+                c.moveToNext();
+                assertEquals(Calls.MISSED_TYPE, c.getInt(1));
+            }
+        } finally {
+            c.close();
+        }
+    }
+
+    public void testUriWithBadLimitParamThrowsException() {
+        assertParamThrowsIllegalArgumentException(Calls.LIMIT_PARAM_KEY, "notvalid");
+    }
+
+    public void testUriWithBadOffsetParamThrowsException() {
+        assertParamThrowsIllegalArgumentException(Calls.OFFSET_PARAM_KEY, "notvalid");
+    }
+
+    private void assertParamThrowsIllegalArgumentException(String key, String value) {
+        Uri uri = Calls.CONTENT_URI.buildUpon()
+                .appendQueryParameter(key, value)
+                .build();
+        try {
+            mResolver.query(uri, null, null, null, null);
+            fail();
+        } catch (IllegalArgumentException e) {
+            assertTrue("Error does not contain value in question.",
+                    e.toString().contains(value));
+        }
+    }
+
     // Test to check that none of the voicemail provider specific fields are
     // insertable through call_log provider.
     public void testCannotAccessVoicemailSpecificFields_Insert() {
diff --git a/tests/src/com/android/providers/contacts/CallerInfoIntegrationTest.java b/tests/src/com/android/providers/contacts/CallerInfoIntegrationTest.java
index 0ce9bca..4c0d2df 100644
--- a/tests/src/com/android/providers/contacts/CallerInfoIntegrationTest.java
+++ b/tests/src/com/android/providers/contacts/CallerInfoIntegrationTest.java
@@ -16,14 +16,14 @@
 
 package com.android.providers.contacts;
 
-import com.android.internal.telephony.CallerInfo;
-
 import android.content.ContentUris;
 import android.content.ContentValues;
 import android.net.Uri;
 import android.provider.ContactsContract.RawContacts;
 import android.test.suitebuilder.annotation.MediumTest;
 
+import com.android.internal.telephony.CallerInfo;
+
 /**
  * Integration test for {@link CallerInfo} and {@link ContactsProvider2}.
  *
diff --git a/tests/src/com/android/providers/contacts/ContactDirectoryManagerTest.java b/tests/src/com/android/providers/contacts/ContactDirectoryManagerTest.java
index f1ff776..96cbb9b 100644
--- a/tests/src/com/android/providers/contacts/ContactDirectoryManagerTest.java
+++ b/tests/src/com/android/providers/contacts/ContactDirectoryManagerTest.java
@@ -16,9 +16,6 @@
 
 package com.android.providers.contacts;
 
-import com.android.providers.contacts.ContactsDatabaseHelper.AggregationExceptionColumns;
-import com.google.android.collect.Lists;
-
 import android.accounts.Account;
 import android.content.ContentValues;
 import android.content.Context;
@@ -39,6 +36,9 @@
 import android.test.suitebuilder.annotation.MediumTest;
 import android.util.Log;
 
+import com.android.providers.contacts.ContactsDatabaseHelper.AggregationExceptionColumns;
+import com.google.android.collect.Lists;
+
 /**
  * Unit tests for {@link ContactDirectoryManager}. Run the test like this:
  *
diff --git a/tests/src/com/android/providers/contacts/ContactLookupKeyTest.java b/tests/src/com/android/providers/contacts/ContactLookupKeyTest.java
index 739b2cb..08f3a07 100644
--- a/tests/src/com/android/providers/contacts/ContactLookupKeyTest.java
+++ b/tests/src/com/android/providers/contacts/ContactLookupKeyTest.java
@@ -16,8 +16,6 @@
 
 package com.android.providers.contacts;
 
-import com.android.providers.contacts.ContactLookupKey.LookupKeySegment;
-
 import android.content.ContentUris;
 import android.net.Uri;
 import android.provider.ContactsContract.AggregationExceptions;
@@ -25,6 +23,8 @@
 import android.provider.ContactsContract.RawContacts;
 import android.test.suitebuilder.annotation.MediumTest;
 
+import com.android.providers.contacts.ContactLookupKey.LookupKeySegment;
+
 import java.util.ArrayList;
 
 /**
diff --git a/tests/src/com/android/providers/contacts/ContactsActor.java b/tests/src/com/android/providers/contacts/ContactsActor.java
index 038eb97..7a30244 100644
--- a/tests/src/com/android/providers/contacts/ContactsActor.java
+++ b/tests/src/com/android/providers/contacts/ContactsActor.java
@@ -16,9 +16,6 @@
 
 package com.android.providers.contacts;
 
-import com.android.providers.contacts.util.MockSharedPreferences;
-import com.google.android.collect.Sets;
-
 import android.accounts.Account;
 import android.accounts.AccountManager;
 import android.accounts.AccountManagerCallback;
@@ -31,6 +28,7 @@
 import android.content.ContentUris;
 import android.content.ContentValues;
 import android.content.Context;
+import android.content.Intent;
 import android.content.SharedPreferences;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
@@ -59,6 +57,9 @@
 import android.test.mock.MockContentResolver;
 import android.test.mock.MockContext;
 
+import com.android.providers.contacts.util.MockSharedPreferences;
+import com.google.android.collect.Sets;
+
 import java.io.File;
 import java.io.IOException;
 import java.util.Arrays;
@@ -341,6 +342,16 @@
         public void enforceCallingOrSelfPermission(String permission, String message) {
             enforceCallingPermission(permission, message);
         }
+
+        @Override
+        public void sendBroadcast(Intent intent) {
+            mOverallContext.sendBroadcast(intent);
+        }
+
+        @Override
+        public void sendBroadcast(Intent intent, String receiverPermission) {
+            mOverallContext.sendBroadcast(intent, receiverPermission);
+        }
     }
 
     static String sCallingPackage = null;
diff --git a/tests/src/com/android/providers/contacts/ContactsDatabaseHelperTest.java b/tests/src/com/android/providers/contacts/ContactsDatabaseHelperTest.java
index a9d8a36..faddeea 100644
--- a/tests/src/com/android/providers/contacts/ContactsDatabaseHelperTest.java
+++ b/tests/src/com/android/providers/contacts/ContactsDatabaseHelperTest.java
@@ -16,11 +16,11 @@
 
 package com.android.providers.contacts;
 
+import android.test.suitebuilder.annotation.SmallTest;
+
 import com.android.providers.contacts.ContactsDatabaseHelper.Tables;
 import com.google.android.collect.Sets;
 
-import android.test.suitebuilder.annotation.SmallTest;
-
 import java.util.Set;
 
 @SmallTest
diff --git a/tests/src/com/android/providers/contacts/ContactsMockPackageManager.java b/tests/src/com/android/providers/contacts/ContactsMockPackageManager.java
index 69cd9fa..694f0f3 100644
--- a/tests/src/com/android/providers/contacts/ContactsMockPackageManager.java
+++ b/tests/src/com/android/providers/contacts/ContactsMockPackageManager.java
@@ -15,8 +15,6 @@
  */
 package com.android.providers.contacts;
 
-import com.google.android.collect.Lists;
-
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageInfo;
@@ -26,6 +24,8 @@
 import android.os.Binder;
 import android.test.mock.MockPackageManager;
 
+import com.google.android.collect.Lists;
+
 import java.util.HashMap;
 import java.util.List;
 
diff --git a/tests/src/com/android/providers/contacts/ContactsMockResources.java b/tests/src/com/android/providers/contacts/ContactsMockResources.java
index 248d6da..6d98665 100644
--- a/tests/src/com/android/providers/contacts/ContactsMockResources.java
+++ b/tests/src/com/android/providers/contacts/ContactsMockResources.java
@@ -16,10 +16,10 @@
 
 package com.android.providers.contacts;
 
-import com.google.android.collect.Maps;
-
 import android.test.mock.MockResources;
 
+import com.google.android.collect.Maps;
+
 import java.util.Map;
 
 final class ContactsMockResources extends MockResources {
diff --git a/tests/src/com/android/providers/contacts/ContactsProvider2Test.java b/tests/src/com/android/providers/contacts/ContactsProvider2Test.java
index 5706925..77789c3 100644
--- a/tests/src/com/android/providers/contacts/ContactsProvider2Test.java
+++ b/tests/src/com/android/providers/contacts/ContactsProvider2Test.java
@@ -18,16 +18,6 @@
 
 import static com.android.providers.contacts.TestUtils.cv;
 
-import com.android.internal.util.ArrayUtils;
-import com.android.providers.contacts.ContactsDatabaseHelper.AggregationExceptionColumns;
-import com.android.providers.contacts.ContactsDatabaseHelper.DataUsageStatColumns;
-import com.android.providers.contacts.ContactsDatabaseHelper.DbProperties;
-import com.android.providers.contacts.ContactsDatabaseHelper.PresenceColumns;
-import com.android.providers.contacts.ContactsDatabaseHelper.Tables;
-import com.android.providers.contacts.tests.R;
-import com.google.android.collect.Lists;
-import com.google.android.collect.Sets;
-
 import android.accounts.Account;
 import android.content.ContentProviderOperation;
 import android.content.ContentProviderResult;
@@ -76,6 +66,17 @@
 import android.test.suitebuilder.annotation.LargeTest;
 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;
+import com.android.providers.contacts.ContactsDatabaseHelper.PresenceColumns;
+import com.android.providers.contacts.ContactsDatabaseHelper.Tables;
+import com.android.providers.contacts.tests.R;
+import com.google.android.collect.Lists;
+import com.google.android.collect.Sets;
+
 import java.io.FileInputStream;
 import java.io.IOException;
 import java.io.OutputStream;
@@ -1020,12 +1021,17 @@
 
         final Uri dataUri = mResolver.insert(Data.CONTENT_URI, values);
 
-        // Ensure both can be looked up
+        // Check the lookup table.
         assertEquals(1,
                 getCount(Uri.withAppendedPath(Phone.CONTENT_FILTER_URI, "1234"), null, null));
         assertEquals(1,
                 getCount(Uri.withAppendedPath(Phone.CONTENT_FILTER_URI, "5678"), null, null));
 
+        // Check the data table.
+        assertStoredValues(dataUri,
+                cv(Phone.NUMBER, "1234", Phone.NORMALIZED_NUMBER, "5678")
+                );
+
         // Replace both in an UPDATE
         values.clear();
         values.put(Phone.NUMBER, "4321");
@@ -1040,28 +1046,68 @@
         assertEquals(1,
                 getCount(Uri.withAppendedPath(Phone.CONTENT_FILTER_URI, "8765"), null, null));
 
+        assertStoredValues(dataUri,
+                cv(Phone.NUMBER, "4321", Phone.NORMALIZED_NUMBER, "8765")
+                );
+
         // Replace only NUMBER ==> NORMALIZED_NUMBER will be inferred (we test that by making
         // sure the old manual value can not be found anymore)
         values.clear();
-        values.put(Phone.NUMBER, "1-800-466-5432");
+        values.put(Phone.NUMBER, "+1-800-466-5432");
         mResolver.update(dataUri, values, null, null);
         assertEquals(
                 1,
-                getCount(Uri.withAppendedPath(Phone.CONTENT_FILTER_URI, "1-800-466-5432"), null,
+                getCount(Uri.withAppendedPath(Phone.CONTENT_FILTER_URI, "+1-800-466-5432"), null,
                         null));
         assertEquals(0,
                 getCount(Uri.withAppendedPath(Phone.CONTENT_FILTER_URI, "8765"), null, null));
 
+        assertStoredValues(dataUri,
+                cv(Phone.NUMBER, "+1-800-466-5432", Phone.NORMALIZED_NUMBER, "+18004665432")
+                );
+
         // Replace only NORMALIZED_NUMBER ==> call is ignored, things will be unchanged
         values.clear();
         values.put(Phone.NORMALIZED_NUMBER, "8765");
         mResolver.update(dataUri, values, null, null);
         assertEquals(
                 1,
-                getCount(Uri.withAppendedPath(Phone.CONTENT_FILTER_URI, "1-800-466-5432"), null,
+                getCount(Uri.withAppendedPath(Phone.CONTENT_FILTER_URI, "+1-800-466-5432"), null,
                         null));
         assertEquals(0,
                 getCount(Uri.withAppendedPath(Phone.CONTENT_FILTER_URI, "8765"), null, null));
+
+        assertStoredValues(dataUri,
+                cv(Phone.NUMBER, "+1-800-466-5432", Phone.NORMALIZED_NUMBER, "+18004665432")
+                );
+
+        // Replace NUMBER with an "invalid" number which can't be normalized.  It should clear
+        // NORMALIZED_NUMBER.
+
+        // 1. Set 999 to NORMALIZED_NUMBER explicitly.
+        values.clear();
+        values.put(Phone.NUMBER, "888");
+        values.put(Phone.NORMALIZED_NUMBER, "999");
+        mResolver.update(dataUri, values, null, null);
+
+        assertEquals(1,
+                getCount(Uri.withAppendedPath(Phone.CONTENT_FILTER_URI, "999"), null, null));
+
+        assertStoredValues(dataUri,
+                cv(Phone.NUMBER, "888", Phone.NORMALIZED_NUMBER, "999")
+                );
+
+        // 2. Set an invalid number to NUMBER.
+        values.clear();
+        values.put(Phone.NUMBER, "1");
+        mResolver.update(dataUri, values, null, null);
+
+        assertEquals(0,
+                getCount(Uri.withAppendedPath(Phone.CONTENT_FILTER_URI, "999"), null, null));
+
+        assertStoredValues(dataUri,
+                cv(Phone.NUMBER, "1", Phone.NORMALIZED_NUMBER, null)
+                );
     }
 
     public void testPhonesFilterQuery() {
@@ -1237,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() {
@@ -1265,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");
@@ -1307,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();
@@ -1326,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(
@@ -1370,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);
@@ -5756,7 +5875,7 @@
         PhotoStore profilePhotoStore = provider.getProfilePhotoStore();
 
         // Trigger an initial cleanup so another one won't happen while we're running this test.
-        provider.switchToProfileMode();
+        provider.switchToProfileModeForTest();
         provider.cleanupPhotoStore();
 
         // Create the profile contact and add a photo.
@@ -5787,7 +5906,7 @@
         profilePhotoStore.remove(streamItemPhotoFileId);
 
         // Manually trigger another cleanup in the provider.
-        provider.switchToProfileMode();
+        provider.switchToProfileModeForTest();
         provider.cleanupPhotoStore();
 
         // The following things should have happened.
diff --git a/tests/src/com/android/providers/contacts/ContactsProvider2TransactionTest.java b/tests/src/com/android/providers/contacts/ContactsProvider2TransactionTest.java
new file mode 100644
index 0000000..6a82bf9
--- /dev/null
+++ b/tests/src/com/android/providers/contacts/ContactsProvider2TransactionTest.java
@@ -0,0 +1,309 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.providers.contacts;
+
+import static com.android.providers.contacts.TestUtils.cv;
+
+import com.google.android.collect.Lists;
+
+import android.content.ContentProviderOperation;
+import android.content.ContentValues;
+import android.provider.ContactsContract;
+import android.provider.ContactsContract.CommonDataKinds.StructuredName;
+import android.provider.ContactsContract.Contacts;
+import android.provider.ContactsContract.Data;
+import android.provider.ContactsContract.Profile;
+import android.provider.ContactsContract.RawContacts;
+import android.test.suitebuilder.annotation.LargeTest;
+import android.util.Log;
+
+import java.util.ArrayList;
+
+/**
+ * Tests to make sure we're handling DB transactions properly in regard to two databases,
+ * the profile db and the contacts db.
+ */
+@LargeTest
+public class ContactsProvider2TransactionTest extends BaseContactsProvider2Test {
+    private SynchronousContactsProvider2 mProvider;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+
+        mProvider = (SynchronousContactsProvider2) getProvider();
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        super.tearDown();
+
+        mProvider = null;
+    }
+
+    /**
+     * Make sure we start/finish transactions on the right databases for insert.
+     */
+    public void testTransactionCallback_insert() {
+
+        final ContentValues values = cv(RawContacts.LAST_TIME_CONTACTED, 12345);
+
+        // Insert a raw contact.
+        mProvider.resetTrasactionCallbackCalledFlags();
+        mResolver.insert(RawContacts.CONTENT_URI, values);
+
+        // Make sure we only COMMIT on the contacts DB, but there was no transaction on the
+        // profile db.
+        mProvider.assertCommitTransactionCalledForContactMode();
+        mProvider.assertNoTransactionsForProfileMode();
+
+
+        // Insert a profile raw contact.
+        mProvider.resetTrasactionCallbackCalledFlags();
+        mResolver.insert(Profile.CONTENT_RAW_CONTACTS_URI, values);
+
+        // Even though we only touched the profile DB, we also start and finish a transaction
+        // on the contacts db.  AbstractContactsProvider does that to avoid deadlocks.
+        mProvider.assertCommitTransactionCalledForContactMode();
+        mProvider.assertCommitTransactionCalledForProfileMode();
+    }
+
+    /**
+     * Make sure we start/finish transactions on the right databases for update.
+     */
+    public void testTransactionCallback_update() {
+
+        final ContentValues values = cv(RawContacts.LAST_TIME_CONTACTED, 12345);
+
+        // Make sure to create a raw contact and a profile raw contact.
+        mResolver.insert(RawContacts.CONTENT_URI, values);
+        mResolver.insert(Profile.CONTENT_RAW_CONTACTS_URI, values);
+
+        values.clear();
+        values.put(RawContacts.LAST_TIME_CONTACTED, 99999);
+
+        // Update all raw contacts.
+        mProvider.resetTrasactionCallbackCalledFlags();
+        assertTrue(mResolver.update(RawContacts.CONTENT_URI, values, null, null) > 0);
+
+        // Make sure we only COMMIT on the contacts DB, but there was no transaction on the
+        // profile db.
+        mProvider.assertCommitTransactionCalledForContactMode();
+        mProvider.assertNoTransactionsForProfileMode();
+
+
+        // Update all profile raw contacts.
+        mProvider.resetTrasactionCallbackCalledFlags();
+        assertTrue(mResolver.update(Profile.CONTENT_RAW_CONTACTS_URI, values, null, null) > 0);
+
+        // Even though we only touched the profile DB, we also start and finish a transaction
+        // on the contacts db.  AbstractContactsProvider does that to avoid deadlocks.
+        mProvider.assertCommitTransactionCalledForContactMode();
+        mProvider.assertCommitTransactionCalledForProfileMode();
+    }
+
+    /**
+     * Make sure we start/finish transactions on the right databases for delete.
+     */
+    public void testTransactionCallback_delete() {
+
+        final ContentValues values = cv(RawContacts.LAST_TIME_CONTACTED, 12345);
+
+        // Make sure to create a raw contact and a profile raw contact.
+        mResolver.insert(RawContacts.CONTENT_URI, values);
+        mResolver.insert(Profile.CONTENT_RAW_CONTACTS_URI, values);
+
+        // Delete all raw contacts.
+        mProvider.resetTrasactionCallbackCalledFlags();
+        assertTrue(mResolver.delete(RawContacts.CONTENT_URI, null, null) > 0);
+
+        // Make sure we only COMMIT on the contacts DB, but there was no transaction on the
+        // profile db.
+        mProvider.assertCommitTransactionCalledForContactMode();
+        mProvider.assertNoTransactionsForProfileMode();
+
+        // Delete all profile raw contact.
+        mProvider.resetTrasactionCallbackCalledFlags();
+        assertTrue(mResolver.delete(Profile.CONTENT_RAW_CONTACTS_URI, null, null) > 0);
+
+        // Even though we only touched the profile DB, we also start and finish a transaction
+        // on the contacts db.  AbstractContactsProvider does that to avoid deadlocks.
+        mProvider.assertCommitTransactionCalledForContactMode();
+        mProvider.assertCommitTransactionCalledForProfileMode();
+    }
+    /**
+     * Make sure we start/finish transactions on the right databases for bulk insert.
+     */
+    public void testTransactionCallback_bulkInsert() {
+
+        final ContentValues values = cv(RawContacts.LAST_TIME_CONTACTED, 12345);
+
+        // Insert a raw contact.
+        mProvider.resetTrasactionCallbackCalledFlags();
+        mResolver.bulkInsert(RawContacts.CONTENT_URI, new ContentValues[] {values});
+
+        // Make sure we only COMMIT on the contacts DB, but there was no transaction on the
+        // profile db.
+        mProvider.assertCommitTransactionCalledForContactMode();
+        mProvider.assertNoTransactionsForProfileMode();
+
+
+        // Insert a profile raw contact.
+        mProvider.resetTrasactionCallbackCalledFlags();
+        mResolver.bulkInsert(Profile.CONTENT_RAW_CONTACTS_URI, new ContentValues[] {values});
+
+        // Even though we only touched the profile DB, we also start and finish a transaction
+        // on the contacts db.  AbstractContactsProvider does that to avoid deadlocks.
+        mProvider.assertCommitTransactionCalledForContactMode();
+        mProvider.assertCommitTransactionCalledForProfileMode();
+    }
+
+    /**
+     * Add an operation to create a raw contact.
+     */
+    private static void addInsertContactOperations(ArrayList<ContentProviderOperation> ops) {
+        ContentProviderOperation.Builder b;
+        b = ContentProviderOperation.newInsert(RawContacts.CONTENT_URI);
+        b.withValue(RawContacts.STARRED, 1);
+        b.withValue(RawContacts.TIMES_CONTACTED, 200001);
+        ops.add(b.build());
+
+        b = ContentProviderOperation.newInsert(Data.CONTENT_URI);
+        b.withValueBackReference(Data.RAW_CONTACT_ID, ops.size() - 1);
+        b.withValue(StructuredName.DISPLAY_NAME, "Regular Contact");
+        b.withValue(StructuredName.GIVEN_NAME, "Regular");
+        b.withValue(StructuredName.FAMILY_NAME, "Contact");
+        b.withValue(Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE);
+        ops.add(b.build());
+    }
+
+    /**
+     * Check for a contact created that'll be created for {@link #addInsertContactOperations}.
+     */
+    private void checkStoredContact() {
+        assertStoredValues(Contacts.CONTENT_URI, cv(
+                Contacts.DISPLAY_NAME, "Regular Contact",
+                RawContacts.TIMES_CONTACTED, 200001
+                ));
+    }
+
+    /**
+     * Add an operation to create a profile raw contact.
+     */
+    private static void addInsertProfileOperations(ArrayList<ContentProviderOperation> ops) {
+        ContentProviderOperation.Builder b;
+        b = ContentProviderOperation.newInsert(Profile.CONTENT_RAW_CONTACTS_URI);
+        b.withValue(RawContacts.STARRED, 1);
+        b.withValue(RawContacts.TIMES_CONTACTED, 100001);
+        ops.add(b.build());
+
+        b = ContentProviderOperation.newInsert(Data.CONTENT_URI);
+        b.withValueBackReference(Data.RAW_CONTACT_ID, ops.size() - 1);
+        b.withValue(StructuredName.DISPLAY_NAME, "Profile Contact");
+        b.withValue(StructuredName.GIVEN_NAME, "Profile");
+        b.withValue(StructuredName.FAMILY_NAME, "Contact");
+        b.withValue(Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE);
+        ops.add(b.build());
+    }
+
+    /**
+     * Check for a profile contact created that'll be created for
+     * {@link #addInsertProfileOperations}.
+     */
+    private void checkStoredProfile() {
+        assertStoredValues(Profile.CONTENT_URI, cv(
+                Contacts.DISPLAY_NAME, "Profile Contact",
+                RawContacts.TIMES_CONTACTED, 100001
+                ));
+    }
+
+    public void testTransactionCallback_contactBatch() throws Exception {
+        final ArrayList<ContentProviderOperation> ops = Lists.newArrayList();
+
+        addInsertContactOperations(ops);
+
+        mProvider.resetTrasactionCallbackCalledFlags();
+
+        // Execute the operations.
+        mResolver.applyBatch(ContactsContract.AUTHORITY, ops);
+
+        // Check the result
+        mProvider.assertCommitTransactionCalledForContactMode();
+        mProvider.assertNoTransactionsForProfileMode();
+
+        checkStoredContact();
+    }
+
+    public void testTransactionCallback_profileBatch() throws Exception {
+        final ArrayList<ContentProviderOperation> ops = Lists.newArrayList();
+
+        addInsertProfileOperations(ops);
+
+        mProvider.resetTrasactionCallbackCalledFlags();
+
+        // Execute the operations.
+        mResolver.applyBatch(ContactsContract.AUTHORITY, ops);
+
+        // Check the result
+        mProvider.assertCommitTransactionCalledForContactMode();
+        mProvider.assertCommitTransactionCalledForProfileMode();
+
+        checkStoredProfile();
+    }
+
+    public void testTransactionCallback_mixedBatch() throws Exception {
+        final ArrayList<ContentProviderOperation> ops = Lists.newArrayList();
+
+        // Create a raw contact and a profile raw contact in a single batch.
+
+        addInsertContactOperations(ops);
+        addInsertProfileOperations(ops);
+
+        mProvider.resetTrasactionCallbackCalledFlags();
+
+        // Execute the operations.
+        mResolver.applyBatch(ContactsContract.AUTHORITY, ops);
+
+        // Check the result
+        mProvider.assertCommitTransactionCalledForContactMode();
+        mProvider.assertCommitTransactionCalledForProfileMode();
+
+        checkStoredProfile();
+        checkStoredContact();
+    }
+
+    public void testTransactionCallback_mixedBatchReversed() throws Exception {
+        final ArrayList<ContentProviderOperation> ops = Lists.newArrayList();
+
+        // Create a profile raw contact and a raw contact in a single batch.
+
+        addInsertProfileOperations(ops);
+        addInsertContactOperations(ops);
+
+        mProvider.resetTrasactionCallbackCalledFlags();
+
+        // Execute the operations.
+        mResolver.applyBatch(ContactsContract.AUTHORITY, ops);
+
+        // Check the result
+        mProvider.assertCommitTransactionCalledForContactMode();
+        mProvider.assertCommitTransactionCalledForProfileMode();
+
+        checkStoredProfile();
+        checkStoredContact();
+    }
+}
diff --git a/tests/src/com/android/providers/contacts/DirectoryTest.java b/tests/src/com/android/providers/contacts/DirectoryTest.java
index e9592c1..c62824b 100644
--- a/tests/src/com/android/providers/contacts/DirectoryTest.java
+++ b/tests/src/com/android/providers/contacts/DirectoryTest.java
@@ -26,7 +26,7 @@
 import android.provider.ContactsContract.Contacts;
 import android.provider.ContactsContract.Data;
 import android.provider.ContactsContract.Directory;
-import android.test.suitebuilder.annotation.MediumTest;;
+import android.test.suitebuilder.annotation.MediumTest;
 
 
 /**
diff --git a/tests/src/com/android/providers/contacts/EvenMoreAsserts.java b/tests/src/com/android/providers/contacts/EvenMoreAsserts.java
index c73128a..1fc15f2 100644
--- a/tests/src/com/android/providers/contacts/EvenMoreAsserts.java
+++ b/tests/src/com/android/providers/contacts/EvenMoreAsserts.java
@@ -16,19 +16,19 @@
 
 package com.android.providers.contacts;
 
-import com.google.android.collect.Sets;
-
 import android.content.Context;
 import android.graphics.BitmapFactory;
 
+import com.google.android.collect.Sets;
+
+import junit.framework.Assert;
+
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.util.Arrays;
 import java.util.Set;
 
-import junit.framework.Assert;
-
 /**
  * Contains additional assertion methods not found in Junit or MoreAsserts.
  */
diff --git a/tests/src/com/android/providers/contacts/FastScrollingIndexCacheTest.java b/tests/src/com/android/providers/contacts/FastScrollingIndexCacheTest.java
index 7ca2a87..281834f 100644
--- a/tests/src/com/android/providers/contacts/FastScrollingIndexCacheTest.java
+++ b/tests/src/com/android/providers/contacts/FastScrollingIndexCacheTest.java
@@ -16,8 +16,6 @@
 
 package com.android.providers.contacts;
 
-import com.android.providers.contacts.util.MockSharedPreferences;
-
 import android.net.Uri;
 import android.os.Bundle;
 import android.provider.ContactsContract.ContactCounts;
@@ -27,6 +25,8 @@
 import android.test.MoreAsserts;
 import android.test.suitebuilder.annotation.SmallTest;
 
+import com.android.providers.contacts.util.MockSharedPreferences;
+
 @SmallTest
 public class FastScrollingIndexCacheTest extends AndroidTestCase {
     private MockSharedPreferences mPrefs;
diff --git a/tests/src/com/android/providers/contacts/GlobalSearchSupportTest.java b/tests/src/com/android/providers/contacts/GlobalSearchSupportTest.java
index c776109..f63845f 100644
--- a/tests/src/com/android/providers/contacts/GlobalSearchSupportTest.java
+++ b/tests/src/com/android/providers/contacts/GlobalSearchSupportTest.java
@@ -116,20 +116,6 @@
                 "Deer Dough").expectedText2("1-800-4664-411").build().test();
     }
 
-    public void assertCreateContactSuggestion(Cursor c, String number) {
-        ContentValues values = new ContentValues();
-        values.put(SearchManager.SUGGEST_COLUMN_TEXT_1, "Create contact");
-        values.put(SearchManager.SUGGEST_COLUMN_TEXT_2, "using "+ number);
-        values.put(SearchManager.SUGGEST_COLUMN_ICON_1,
-                String.valueOf(com.android.internal.R.drawable.create_contact));
-        values.put(SearchManager.SUGGEST_COLUMN_INTENT_ACTION,
-                Intents.SEARCH_SUGGESTION_CREATE_CONTACT_CLICKED);
-        values.put(SearchManager.SUGGEST_COLUMN_INTENT_DATA, "tel:" + number);
-        values.put(SearchManager.SUGGEST_COLUMN_SHORTCUT_ID,
-                SearchManager.SUGGEST_NEVER_MAKE_SHORTCUT);
-        assertCursorValues(c, values);
-    }
-
     /**
      * Tests that the quick search suggestion returns the expected contact
      * information.
diff --git a/tests/src/com/android/providers/contacts/GroupsTest.java b/tests/src/com/android/providers/contacts/GroupsTest.java
index 3d85064..15cfc71 100644
--- a/tests/src/com/android/providers/contacts/GroupsTest.java
+++ b/tests/src/com/android/providers/contacts/GroupsTest.java
@@ -16,8 +16,6 @@
 
 package com.android.providers.contacts;
 
-import com.google.android.collect.Lists;
-
 import android.accounts.Account;
 import android.content.ContentProviderOperation;
 import android.content.ContentUris;
@@ -32,9 +30,10 @@
 import android.provider.ContactsContract.Contacts;
 import android.provider.ContactsContract.Groups;
 import android.provider.ContactsContract.Settings;
-import android.test.suitebuilder.annotation.LargeTest;
 import android.test.suitebuilder.annotation.MediumTest;
 
+import com.google.android.collect.Lists;
+
 import java.util.ArrayList;
 
 /**
diff --git a/tests/src/com/android/providers/contacts/HanziToPinyinTest.java b/tests/src/com/android/providers/contacts/HanziToPinyinTest.java
index fa3e852..dc01b91 100644
--- a/tests/src/com/android/providers/contacts/HanziToPinyinTest.java
+++ b/tests/src/com/android/providers/contacts/HanziToPinyinTest.java
@@ -16,18 +16,18 @@
 
 package com.android.providers.contacts;
 
-import com.android.providers.contacts.HanziToPinyin.Token;
-
 import android.test.suitebuilder.annotation.SmallTest;
 import android.text.TextUtils;
 
+import com.android.providers.contacts.HanziToPinyin.Token;
+
+import junit.framework.TestCase;
+
 import java.text.Collator;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Locale;
 
-import junit.framework.TestCase;
-
 @SmallTest
 public class HanziToPinyinTest extends TestCase {
     private final static String ONE_HANZI = "\u675C";
diff --git a/tests/src/com/android/providers/contacts/LegacyContactsProviderTest.java b/tests/src/com/android/providers/contacts/LegacyContactsProviderTest.java
index e515af2..c752e4e 100644
--- a/tests/src/com/android/providers/contacts/LegacyContactsProviderTest.java
+++ b/tests/src/com/android/providers/contacts/LegacyContactsProviderTest.java
@@ -16,8 +16,6 @@
 
 package com.android.providers.contacts;
 
-import com.android.providers.contacts.tests.*;
-
 import android.app.SearchManager;
 import android.content.ContentProvider;
 import android.content.ContentUris;
diff --git a/tests/src/com/android/providers/contacts/NameLookupBuilderTest.java b/tests/src/com/android/providers/contacts/NameLookupBuilderTest.java
index a5cff7a..a54193e 100644
--- a/tests/src/com/android/providers/contacts/NameLookupBuilderTest.java
+++ b/tests/src/com/android/providers/contacts/NameLookupBuilderTest.java
@@ -19,12 +19,12 @@
 import android.provider.ContactsContract.FullNameStyle;
 import android.test.suitebuilder.annotation.SmallTest;
 
+import junit.framework.TestCase;
+
 import java.text.Collator;
 import java.util.Arrays;
 import java.util.Locale;
 
-import junit.framework.TestCase;
-
 /**
  * Unit tests for {@link NameLookupBuilder}.
  *
diff --git a/tests/src/com/android/providers/contacts/NameNormalizerTest.java b/tests/src/com/android/providers/contacts/NameNormalizerTest.java
index 9e4aaac..2872962 100644
--- a/tests/src/com/android/providers/contacts/NameNormalizerTest.java
+++ b/tests/src/com/android/providers/contacts/NameNormalizerTest.java
@@ -16,8 +16,12 @@
 
 package com.android.providers.contacts;
 
+import android.test.MoreAsserts;
 import android.test.suitebuilder.annotation.SmallTest;
 
+import java.text.RuleBasedCollator;
+import java.util.Locale;
+
 import junit.framework.TestCase;
 
 /**
@@ -32,6 +36,25 @@
 @SmallTest
 public class NameNormalizerTest extends TestCase {
 
+    private Locale mOriginalLocale;
+
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+
+        mOriginalLocale = Locale.getDefault();
+
+        // Run all test in en_US
+        Locale.setDefault(Locale.US);
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        Locale.setDefault(mOriginalLocale);
+        super.tearDown();
+    }
+
     public void testDifferent() {
         final String name1 = NameNormalizer.normalize("Helene");
         final String name2 = NameNormalizer.normalize("Francesca");
@@ -69,4 +92,33 @@
     public void testComplexityLength() {
         assertTrue(NameNormalizer.compareComplexity("helene2009", "helene") > 0);
     }
+
+    public void testGetCollators() {
+        final RuleBasedCollator compressing1 = NameNormalizer.getCompressingCollator();
+        final RuleBasedCollator complexity1 = NameNormalizer.getComplexityCollator();
+
+        assertNotNull(compressing1);
+        assertNotNull(complexity1);
+        assertNotSame(compressing1, complexity1);
+
+        // Get again.  Should be cached.
+        final RuleBasedCollator compressing2 = NameNormalizer.getCompressingCollator();
+        final RuleBasedCollator complexity2 = NameNormalizer.getComplexityCollator();
+
+        assertSame(compressing1, compressing2);
+        assertSame(complexity1, complexity2);
+
+        // Change locale -- now new collators should be returned.
+        Locale.setDefault(Locale.FRANCE);
+
+        final RuleBasedCollator compressing3 = NameNormalizer.getCompressingCollator();
+        final RuleBasedCollator complexity3 = NameNormalizer.getComplexityCollator();
+
+        assertNotNull(compressing3);
+        assertNotNull(complexity3);
+        assertNotSame(compressing3, complexity3);
+
+        assertNotSame(compressing1, compressing3);
+        assertNotSame(complexity1, complexity3);
+    }
 }
diff --git a/tests/src/com/android/providers/contacts/NameSplitterTest.java b/tests/src/com/android/providers/contacts/NameSplitterTest.java
index 785fb01..d9007fc 100644
--- a/tests/src/com/android/providers/contacts/NameSplitterTest.java
+++ b/tests/src/com/android/providers/contacts/NameSplitterTest.java
@@ -16,16 +16,16 @@
 
 package com.android.providers.contacts;
 
-import com.android.providers.contacts.NameSplitter.Name;
-
 import android.provider.ContactsContract.FullNameStyle;
 import android.provider.ContactsContract.PhoneticNameStyle;
 import android.test.suitebuilder.annotation.SmallTest;
 
-import java.util.Locale;
+import com.android.providers.contacts.NameSplitter.Name;
 
 import junit.framework.TestCase;
 
+import java.util.Locale;
+
 /**
  * Tests for {@link NameSplitter}.
  *
diff --git a/tests/src/com/android/providers/contacts/PhotoLoadingTestCase.java b/tests/src/com/android/providers/contacts/PhotoLoadingTestCase.java
index 20058ad..4b159a8 100644
--- a/tests/src/com/android/providers/contacts/PhotoLoadingTestCase.java
+++ b/tests/src/com/android/providers/contacts/PhotoLoadingTestCase.java
@@ -16,12 +16,12 @@
 
 package com.android.providers.contacts;
 
-import com.google.android.collect.Maps;
-
 import android.content.res.Resources;
 import android.test.AndroidTestCase;
 import android.test.suitebuilder.annotation.SmallTest;
 
+import com.google.android.collect.Maps;
+
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
diff --git a/tests/src/com/android/providers/contacts/PhotoProcessorTest.java b/tests/src/com/android/providers/contacts/PhotoProcessorTest.java
new file mode 100644
index 0000000..b34dd86
--- /dev/null
+++ b/tests/src/com/android/providers/contacts/PhotoProcessorTest.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.providers.contacts;
+
+import android.graphics.Bitmap;
+import android.graphics.Color;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.providers.contacts.tests.R;
+
+
+/**
+ * Tests for {@link PhotoProcessor}.
+ *
+ * Most of tests are covered by {@link PhotoStoreTest}.
+ */
+@SmallTest
+public class PhotoProcessorTest extends AndroidTestCase {
+
+    public void testTransparency() {
+        final Drawable source = getTestContext().getResources().getDrawable(
+                R.drawable.transparent_10x10);
+        final Bitmap sourceBitmap = ((BitmapDrawable) source).getBitmap();
+
+        final Bitmap normalized = PhotoProcessor.getNormalizedBitmap(sourceBitmap, 50, false);
+
+        // Make sure it's not scaled up.
+        assertEquals(10, normalized.getWidth());
+        assertEquals(10, normalized.getHeight());
+
+        // Make sure the transparent pixel is now 100% white.
+        assertEquals(Color.argb(255, 255, 255, 255), normalized.getPixel(0, 0));
+    }
+}
diff --git a/tests/src/com/android/providers/contacts/PhotoStoreTest.java b/tests/src/com/android/providers/contacts/PhotoStoreTest.java
index 1550d0b..4e797f7 100644
--- a/tests/src/com/android/providers/contacts/PhotoStoreTest.java
+++ b/tests/src/com/android/providers/contacts/PhotoStoreTest.java
@@ -16,9 +16,7 @@
 
 package com.android.providers.contacts;
 
-import com.android.providers.contacts.ContactsDatabaseHelper.Tables;
-import com.android.providers.contacts.tests.R;
-import com.android.providers.contacts.util.Hex;
+import static com.android.providers.contacts.ContactsActor.PACKAGE_GREY;
 
 import android.database.Cursor;
 import android.database.sqlite.SQLiteDatabase;
@@ -26,17 +24,17 @@
 import android.provider.ContactsContract.PhotoFiles;
 import android.test.suitebuilder.annotation.MediumTest;
 
+import com.android.providers.contacts.ContactsDatabaseHelper.Tables;
+import com.android.providers.contacts.tests.R;
+
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.IOException;
-import java.util.Arrays;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Map;
 import java.util.Set;
 
-import static com.android.providers.contacts.ContactsActor.PACKAGE_GREY;
-
 /**
  * Tests for {@link PhotoStore}.
  */
@@ -81,7 +79,7 @@
 
     public void testStoreNonSquare300x200Photo() throws IOException {
         // The longer side should be downscaled to the target size
-        runStorageTestForResource(R.drawable.earth_300x200, 256, 171);
+        runStorageTestForResource(R.drawable.earth_300x200, 256, 170);
     }
 
     public void testStoreNonSquare300x200PhotoWithCrop() throws IOException {
diff --git a/tests/src/com/android/providers/contacts/PostalSplitterForJapaneseTest.java b/tests/src/com/android/providers/contacts/PostalSplitterForJapaneseTest.java
index b4be173..eba9d53 100644
--- a/tests/src/com/android/providers/contacts/PostalSplitterForJapaneseTest.java
+++ b/tests/src/com/android/providers/contacts/PostalSplitterForJapaneseTest.java
@@ -16,14 +16,14 @@
 
 package com.android.providers.contacts;
 
-import com.android.providers.contacts.PostalSplitter.Postal;
-
 import android.test.suitebuilder.annotation.SmallTest;
 
-import java.util.Locale;
+import com.android.providers.contacts.PostalSplitter.Postal;
 
 import junit.framework.TestCase;
 
+import java.util.Locale;
+
 /**
  * Tests for {@link PostalSplitter}, especially for ja_JP locale.
  * This class depends on the assumption that all the tests in {@link NameSplitterTest} pass.
diff --git a/tests/src/com/android/providers/contacts/PostalSplitterTest.java b/tests/src/com/android/providers/contacts/PostalSplitterTest.java
index 6778b79..d12b3f3 100644
--- a/tests/src/com/android/providers/contacts/PostalSplitterTest.java
+++ b/tests/src/com/android/providers/contacts/PostalSplitterTest.java
@@ -16,14 +16,14 @@
 
 package com.android.providers.contacts;
 
-import com.android.providers.contacts.PostalSplitter.Postal;
-
 import android.test.suitebuilder.annotation.SmallTest;
 
-import java.util.Locale;
+import com.android.providers.contacts.PostalSplitter.Postal;
 
 import junit.framework.TestCase;
 
+import java.util.Locale;
+
 /**
  * Tests for {@link PostalSplitter}, especially for en_US locale.
  *
diff --git a/tests/src/com/android/providers/contacts/SqlInjectionDetectionTest.java b/tests/src/com/android/providers/contacts/SqlInjectionDetectionTest.java
index f4b8bab..7b3fe95 100644
--- a/tests/src/com/android/providers/contacts/SqlInjectionDetectionTest.java
+++ b/tests/src/com/android/providers/contacts/SqlInjectionDetectionTest.java
@@ -27,8 +27,6 @@
 import android.provider.ContactsContract.Contacts;
 import android.test.suitebuilder.annotation.MediumTest;
 
-import junit.framework.Assert;
-
 /**
  * Unit tests for {@link ContactsProvider2}, to make sure the queries don't allow sql injection.
  *
diff --git a/tests/src/com/android/providers/contacts/SynchronousContactsProvider2.java b/tests/src/com/android/providers/contacts/SynchronousContactsProvider2.java
index 3d28ad1..1d127c7 100644
--- a/tests/src/com/android/providers/contacts/SynchronousContactsProvider2.java
+++ b/tests/src/com/android/providers/contacts/SynchronousContactsProvider2.java
@@ -19,6 +19,9 @@
 import android.accounts.Account;
 import android.content.Context;
 import android.database.sqlite.SQLiteDatabase;
+import android.util.Log;
+
+import junit.framework.Assert;
 
 import java.util.Locale;
 
@@ -46,7 +49,7 @@
     }
 
     @Override
-    public ProfileProvider getProfileProvider() {
+    public ProfileProvider newProfileProvider() {
         return new SynchronousProfileProvider(this);
     }
 
@@ -202,6 +205,7 @@
 
     @Override
     public void wipeData() {
+        Log.i(TAG, "wipeData");
         super.wipeData();
         SQLiteDatabase db = getDatabaseHelper(getContext()).getWritableDatabase();
         db.execSQL("replace into SQLITE_SEQUENCE (name,seq) values('raw_contacts', 42)");
@@ -210,4 +214,84 @@
 
         getContactDirectoryManagerForTest().scanAllPackages();
     }
+
+    // Flags to remember which transaction callback has been called for which mode.
+    private boolean mOnBeginTransactionInternalCalledInProfileMode;
+    private boolean mOnCommitTransactionInternalCalledInProfileMode;
+    private boolean mOnRollbackTransactionInternalCalledInProfileMode;
+
+    private boolean mOnBeginTransactionInternalCalledInContactMode;
+    private boolean mOnCommitTransactionInternalCalledInContactMode;
+    private boolean mOnRollbackTransactionInternalCalledInContactMode;
+
+    public void resetTrasactionCallbackCalledFlags() {
+        mOnBeginTransactionInternalCalledInProfileMode = false;
+        mOnCommitTransactionInternalCalledInProfileMode = false;
+        mOnRollbackTransactionInternalCalledInProfileMode = false;
+
+        mOnBeginTransactionInternalCalledInContactMode = false;
+        mOnCommitTransactionInternalCalledInContactMode = false;
+        mOnRollbackTransactionInternalCalledInContactMode = false;
+    }
+
+    @Override
+    protected void onBeginTransactionInternal(boolean forProfile) {
+        super.onBeginTransactionInternal(forProfile);
+        if (forProfile) {
+            mOnBeginTransactionInternalCalledInProfileMode = true;
+        } else {
+            mOnBeginTransactionInternalCalledInContactMode = true;
+        }
+    }
+
+    @Override
+    protected void onCommitTransactionInternal(boolean forProfile) {
+        super.onCommitTransactionInternal(forProfile);
+        if (forProfile) {
+            mOnCommitTransactionInternalCalledInProfileMode = true;
+        } else {
+            mOnCommitTransactionInternalCalledInContactMode = true;
+        }
+    }
+
+    @Override
+    protected void onRollbackTransactionInternal(boolean forProfile) {
+        super.onRollbackTransactionInternal(forProfile);
+        if (forProfile) {
+            mOnRollbackTransactionInternalCalledInProfileMode = true;
+        } else {
+            mOnRollbackTransactionInternalCalledInContactMode = true;
+        }
+    }
+
+    public void assertCommitTransactionCalledForProfileMode() {
+        Assert.assertTrue("begin", mOnBeginTransactionInternalCalledInProfileMode);
+        Assert.assertTrue("commit", mOnCommitTransactionInternalCalledInProfileMode);
+        Assert.assertFalse("rollback", mOnRollbackTransactionInternalCalledInProfileMode);
+    }
+
+    public void assertRollbackTransactionCalledForProfileMode() {
+        Assert.assertTrue("begin", mOnBeginTransactionInternalCalledInProfileMode);
+        Assert.assertFalse("commit", mOnCommitTransactionInternalCalledInProfileMode);
+        Assert.assertTrue("rollback", mOnRollbackTransactionInternalCalledInProfileMode);
+    }
+
+    public void assertNoTransactionsForProfileMode() {
+        Assert.assertFalse("begin", mOnBeginTransactionInternalCalledInProfileMode);
+        Assert.assertFalse("commit", mOnCommitTransactionInternalCalledInProfileMode);
+        Assert.assertFalse("rollback", mOnRollbackTransactionInternalCalledInProfileMode);
+    }
+
+
+    public void assertCommitTransactionCalledForContactMode() {
+        Assert.assertTrue("begin", mOnBeginTransactionInternalCalledInContactMode);
+        Assert.assertTrue("commit", mOnCommitTransactionInternalCalledInContactMode);
+        Assert.assertFalse("rollback", mOnRollbackTransactionInternalCalledInContactMode);
+    }
+
+    public void assertRollbackTransactionCalledForContactMode() {
+        Assert.assertTrue("begin", mOnBeginTransactionInternalCalledInContactMode);
+        Assert.assertFalse("commit", mOnCommitTransactionInternalCalledInContactMode);
+        Assert.assertTrue("rollback", mOnRollbackTransactionInternalCalledInContactMode);
+    }
 }
diff --git a/tests/src/com/android/providers/contacts/SynchronousProfileProvider.java b/tests/src/com/android/providers/contacts/SynchronousProfileProvider.java
index 93ad70f..308e67a 100644
--- a/tests/src/com/android/providers/contacts/SynchronousProfileProvider.java
+++ b/tests/src/com/android/providers/contacts/SynchronousProfileProvider.java
@@ -16,7 +16,6 @@
 
 package com.android.providers.contacts;
 
-import android.accounts.Account;
 import android.content.Context;
 
 import java.util.Locale;
diff --git a/tests/src/com/android/providers/contacts/TestUtils.java b/tests/src/com/android/providers/contacts/TestUtils.java
index 00789bf..b6d6a27 100644
--- a/tests/src/com/android/providers/contacts/TestUtils.java
+++ b/tests/src/com/android/providers/contacts/TestUtils.java
@@ -21,12 +21,12 @@
 import android.database.Cursor;
 import android.util.Log;
 
+import junit.framework.Assert;
+
 import java.io.File;
 import java.io.FileOutputStream;
 import java.io.IOException;
 
-import junit.framework.Assert;
-
 public class TestUtils {
     private TestUtils() {
     }
diff --git a/tests/src/com/android/providers/contacts/VCardTest.java b/tests/src/com/android/providers/contacts/VCardTest.java
index b022ebd..820c263 100644
--- a/tests/src/com/android/providers/contacts/VCardTest.java
+++ b/tests/src/com/android/providers/contacts/VCardTest.java
@@ -16,12 +16,12 @@
 
 package com.android.providers.contacts;
 
-import com.android.vcard.VCardComposer;
-import com.android.vcard.VCardConfig;
-
 import android.content.ContentResolver;
 import android.test.suitebuilder.annotation.MediumTest;
 
+import com.android.vcard.VCardComposer;
+import com.android.vcard.VCardConfig;
+
 /**
  * Tests (or integration tests) verifying if vCard library works well with {@link ContentResolver}.
  *
diff --git a/tests/src/com/android/providers/contacts/VoicemailProviderTest.java b/tests/src/com/android/providers/contacts/VoicemailProviderTest.java
index 74195b5..8fdbccf 100644
--- a/tests/src/com/android/providers/contacts/VoicemailProviderTest.java
+++ b/tests/src/com/android/providers/contacts/VoicemailProviderTest.java
@@ -16,8 +16,6 @@
 
 package com.android.providers.contacts;
 
-import com.android.common.io.MoreCloseables;
-
 import android.content.ContentUris;
 import android.content.ContentValues;
 import android.database.Cursor;
@@ -30,6 +28,8 @@
 import android.test.MoreAsserts;
 import android.test.suitebuilder.annotation.SmallTest;
 
+import com.android.common.io.MoreCloseables;
+
 import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.io.InputStream;
diff --git a/tests/src/com/android/providers/contacts/aggregation/ContactAggregatorTest.java b/tests/src/com/android/providers/contacts/aggregation/ContactAggregatorTest.java
index 795ea9c..16d06c8 100644
--- a/tests/src/com/android/providers/contacts/aggregation/ContactAggregatorTest.java
+++ b/tests/src/com/android/providers/contacts/aggregation/ContactAggregatorTest.java
@@ -16,25 +16,15 @@
 
 package com.android.providers.contacts.aggregation;
 
-import com.android.providers.contacts.BaseContactsProvider2Test;
-import com.android.providers.contacts.ContactsDatabaseHelper.Tables;
-import com.android.providers.contacts.ContactsProvider2;
-import com.android.providers.contacts.TestUtils;
-import com.android.providers.contacts.tests.R;
-import com.google.android.collect.Lists;
-
 import android.accounts.Account;
 import android.content.ContentProviderOperation;
 import android.content.ContentProviderResult;
 import android.content.ContentUris;
 import android.content.ContentValues;
 import android.database.Cursor;
-import android.database.sqlite.SQLiteDatabase;
 import android.net.Uri;
-import android.provider.BaseColumns;
 import android.provider.ContactsContract;
 import android.provider.ContactsContract.AggregationExceptions;
-import android.provider.ContactsContract.CommonDataKinds.GroupMembership;
 import android.provider.ContactsContract.CommonDataKinds.Organization;
 import android.provider.ContactsContract.CommonDataKinds.StructuredName;
 import android.provider.ContactsContract.Contacts;
@@ -46,6 +36,11 @@
 import android.test.MoreAsserts;
 import android.test.suitebuilder.annotation.MediumTest;
 
+import com.android.providers.contacts.BaseContactsProvider2Test;
+import com.android.providers.contacts.TestUtils;
+import com.android.providers.contacts.tests.R;
+import com.google.android.collect.Lists;
+
 /**
  * Unit tests for {@link ContactAggregator}.
  *
diff --git a/tests/src/com/android/providers/contacts/aggregation/util/ContactMatcherTest.java b/tests/src/com/android/providers/contacts/aggregation/util/ContactMatcherTest.java
index 97faacd..301902a 100644
--- a/tests/src/com/android/providers/contacts/aggregation/util/ContactMatcherTest.java
+++ b/tests/src/com/android/providers/contacts/aggregation/util/ContactMatcherTest.java
@@ -18,7 +18,9 @@
 import com.android.providers.contacts.ContactsDatabaseHelper.NameLookupType;
 
 import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
 
+@SmallTest
 public class ContactMatcherTest extends AndroidTestCase {
 
     public void testMatchName_invalidHexDecimal() {
diff --git a/tests/src/com/android/providers/contacts/aggregation/util/NameDistanceTest.java b/tests/src/com/android/providers/contacts/aggregation/util/NameDistanceTest.java
index 7f9f053..b833220 100644
--- a/tests/src/com/android/providers/contacts/aggregation/util/NameDistanceTest.java
+++ b/tests/src/com/android/providers/contacts/aggregation/util/NameDistanceTest.java
@@ -16,12 +16,11 @@
 
 package com.android.providers.contacts.aggregation.util;
 
-import com.android.providers.contacts.NameNormalizer;
-import com.android.providers.contacts.aggregation.util.NameDistance;
-import com.android.providers.contacts.util.Hex;
-
 import android.test.suitebuilder.annotation.SmallTest;
 
+import com.android.providers.contacts.NameNormalizer;
+import com.android.providers.contacts.util.Hex;
+
 import junit.framework.TestCase;
 
 /**
diff --git a/tests/src/com/android/providers/contacts/util/DBQueryUtilsTest.java b/tests/src/com/android/providers/contacts/util/DBQueryUtilsTest.java
index 43f7c06..7769b49 100644
--- a/tests/src/com/android/providers/contacts/util/DBQueryUtilsTest.java
+++ b/tests/src/com/android/providers/contacts/util/DBQueryUtilsTest.java
@@ -19,13 +19,13 @@
 import static com.android.providers.contacts.util.DbQueryUtils.checkForSupportedColumns;
 import static com.android.providers.contacts.util.DbQueryUtils.concatenateClauses;
 
-import com.android.common.content.ProjectionMap;
-import com.android.providers.contacts.EvenMoreAsserts;
-
 import android.content.ContentValues;
 import android.test.AndroidTestCase;
 import android.test.suitebuilder.annotation.SmallTest;
 
+import com.android.common.content.ProjectionMap;
+import com.android.providers.contacts.EvenMoreAsserts;
+
 /**
  * Unit tests for the {@link DbQueryUtils} class.
  * Run the test like this:
diff --git a/tests/src/com/android/providers/contacts/util/MockSharedPreferences.java b/tests/src/com/android/providers/contacts/util/MockSharedPreferences.java
index d00e711..574ce19 100644
--- a/tests/src/com/android/providers/contacts/util/MockSharedPreferences.java
+++ b/tests/src/com/android/providers/contacts/util/MockSharedPreferences.java
@@ -16,10 +16,10 @@
 
 package com.android.providers.contacts.util;
 
-import com.google.android.collect.Maps;
-
 import android.content.SharedPreferences;
 
+import com.google.android.collect.Maps;
+
 import java.util.HashMap;
 import java.util.Map;
 import java.util.Set;
diff --git a/tests/src/com/android/providers/contacts/util/TypedUriMatcherImplTest.java b/tests/src/com/android/providers/contacts/util/TypedUriMatcherImplTest.java
index 48bd608..329e6e2 100644
--- a/tests/src/com/android/providers/contacts/util/TypedUriMatcherImplTest.java
+++ b/tests/src/com/android/providers/contacts/util/TypedUriMatcherImplTest.java
@@ -20,9 +20,6 @@
 import android.test.AndroidTestCase;
 import android.test.suitebuilder.annotation.SmallTest;
 
-import com.android.providers.contacts.util.TypedUriMatcherImpl;
-import com.android.providers.contacts.util.UriType;
-
 /**
  * Unit tests for {@link TypedUriMatcherImpl}.
  * Run the test like this: