Merge "Snap for 6877830 from 21415e1ec20f386e419cbf9f4aad987af049811a to sdk-release" into sdk-release
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index cd7de03..0748e0f 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -11,6 +11,7 @@
     <uses-permission android:name="android.permission.PROCESS_PHONE_ACCOUNT_REGISTRATION" />
     <uses-permission android:name="android.permission.READ_CONTACTS" />
     <uses-permission android:name="android.permission.READ_PHONE_STATE" />
+    <uses-permission android:name="android.permission.READ_PRIVILEGED_PHONE_STATE" />
     <uses-permission android:name="android.permission.READ_SYNC_SETTINGS" />
     <uses-permission android:name="android.permission.SEND_CALL_LOG_CHANGE" />
     <uses-permission android:name="android.permission.UPDATE_APP_OPS_STATS" />
@@ -25,8 +26,8 @@
 
     <application android:process="android.process.acore"
         android:label="@string/app_label"
-        android:icon="@drawable/app_icon"
         android:allowBackup="false"
+        android:forceQueryable="true"
         android:usesCleartextTraffic="false">
 
         <provider android:name="ContactsProvider2"
diff --git a/TEST_MAPPING b/TEST_MAPPING
new file mode 100644
index 0000000..c66ff24
--- /dev/null
+++ b/TEST_MAPPING
@@ -0,0 +1,15 @@
+{
+    "presubmit": [
+        {
+            "name": "ContactsProviderTests"
+        },
+        {
+            "name": "CtsProviderTestCases",
+            "options": [
+                {
+                    "include-filter": "android.provider.cts.contacts."
+                }
+            ]
+        }
+    ]
+}
diff --git a/res/drawable-hdpi/app_icon.png b/res/drawable-hdpi/app_icon.png
deleted file mode 100644
index 64eff00..0000000
--- a/res/drawable-hdpi/app_icon.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/app_icon.png b/res/drawable-mdpi/app_icon.png
deleted file mode 100644
index b4ee821..0000000
--- a/res/drawable-mdpi/app_icon.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/app_icon.png b/res/drawable-xhdpi/app_icon.png
deleted file mode 100644
index 6feeadf..0000000
--- a/res/drawable-xhdpi/app_icon.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/app_icon.png b/res/drawable-xxhdpi/app_icon.png
deleted file mode 100644
index 01a3fde..0000000
--- a/res/drawable-xxhdpi/app_icon.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxxhdpi/app_icon.png b/res/drawable-xxxhdpi/app_icon.png
deleted file mode 100644
index 328e067..0000000
--- a/res/drawable-xxxhdpi/app_icon.png
+++ /dev/null
Binary files differ
diff --git a/res/values-de/strings.xml b/res/values-de/strings.xml
index a725953..eb68085 100644
--- a/res/values-de/strings.xml
+++ b/res/values-de/strings.xml
@@ -26,7 +26,7 @@
     <string name="local_invisible_directory" msgid="705244318477396120">"Sonstige"</string>
     <string name="voicemail_from_column" msgid="435732568832121444">"Mailboxnachricht von "</string>
     <string name="debug_dump_title" msgid="4916885724165570279">"Kontaktdatenbank kopieren"</string>
-    <string name="debug_dump_database_message" msgid="406438635002392290">"Du 1) erstellst eine Kopie deiner Datenbank, die alle Kontaktinformationen und Anruflisten auf dem internen Speicher enthält, und 2) sendest diese Kopie per E-Mail. Denke daran, die Kopie so schnell wie möglich zu löschen, nachdem du sie vom Gerät kopiert hast oder die E-Mail empfangen wurde."</string>
+    <string name="debug_dump_database_message" msgid="406438635002392290">"Du 1) erstellst eine Kopie deiner Datenbank, die alle Kontaktinformationen und Anruflisteneinträge auf dem internen Speicher enthält, und 2) sendest diese Kopie per E-Mail. Denke daran, die Kopie so schnell wie möglich zu löschen, nachdem du sie vom Gerät kopiert hast 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>
diff --git a/res/values-eu/strings.xml b/res/values-eu/strings.xml
index e9f97d2..b1c7431 100644
--- a/res/values-eu/strings.xml
+++ b/res/values-eu/strings.xml
@@ -24,7 +24,7 @@
     <string name="upgrade_out_of_memory_notification_text" msgid="2581831842693151968">"Sakatu bertsio-berritzea osatzeko."</string>
     <string name="default_directory" msgid="93961630309570294">"Kontaktuak"</string>
     <string name="local_invisible_directory" msgid="705244318477396120">"Beste bat"</string>
-    <string name="voicemail_from_column" msgid="435732568832121444">"Honen ahots-mezua: "</string>
+    <string name="voicemail_from_column" msgid="435732568832121444">"Mezu bat utzi du erantzungailuan honek: "</string>
     <string name="debug_dump_title" msgid="4916885724165570279">"Kopiatu kontaktuen datu-basea"</string>
     <string name="debug_dump_database_message" msgid="406438635002392290">"Bi gauza egitera zoaz: 1) kontaktuekin erlazionatutako informazio guztia eta deien erregistro osoa jasotzen dituen datu-basearen kopia bat egingo duzu eta barneko memorian gordeko duzu eta 2) posta elektronikoz bidaliko duzu. Gogoratu kopia ezabatu behar duzula gailutik kopiatu edo mezu elektronikoa jaso bezain laster."</string>
     <string name="debug_dump_delete_button" msgid="7832879421132026435">"Ezabatu"</string>
diff --git a/res/values-fa/strings.xml b/res/values-fa/strings.xml
index 4af29ec..9fa5c9b 100644
--- a/res/values-fa/strings.xml
+++ b/res/values-fa/strings.xml
@@ -17,7 +17,7 @@
 <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">"Android Core Apps"</string>
-    <string name="app_label" msgid="3389954322874982620">"حافظه مخاطبین"</string>
+    <string name="app_label" msgid="3389954322874982620">"فضای ذخیره‌سازی مخاطبین"</string>
     <string name="provider_label" msgid="6012150850819899907">"مخاطبین"</string>
     <string name="upgrade_out_of_memory_notification_ticker" msgid="7638747231223520477">"برای ارتقای مخاطبین به حافظه بیشتری نیاز است."</string>
     <string name="upgrade_out_of_memory_notification_title" msgid="8888171924684998531">"ارتقا حافظه برای مخاطبین"</string>
diff --git a/res/values-hi/strings.xml b/res/values-hi/strings.xml
index a9b2025..478e5ce 100644
--- a/res/values-hi/strings.xml
+++ b/res/values-hi/strings.xml
@@ -19,17 +19,17 @@
     <string name="sharedUserLabel" msgid="8024311725474286801">"Android Core Apps"</string>
     <string name="app_label" msgid="3389954322874982620">"संपर्क मेमोरी"</string>
     <string name="provider_label" msgid="6012150850819899907">"संपर्क"</string>
-    <string name="upgrade_out_of_memory_notification_ticker" msgid="7638747231223520477">"संपर्क अपग्रेड के लिए अधिक मेमोरी की आवश्यकता होती है."</string>
+    <string name="upgrade_out_of_memory_notification_ticker" msgid="7638747231223520477">"संपर्क अपग्रेड के लिए ज़्यादा मेमोरी की आवश्यकता होती है."</string>
     <string name="upgrade_out_of_memory_notification_title" msgid="8888171924684998531">"संपर्कों के लिए मेमोरी अपग्रेड करना"</string>
-    <string name="upgrade_out_of_memory_notification_text" msgid="2581831842693151968">"अपग्रेड पूर्ण करने के लिए टैप करें."</string>
+    <string name="upgrade_out_of_memory_notification_text" msgid="2581831842693151968">"अपग्रेड पूरा करने के लिए टैप करें."</string>
     <string name="default_directory" msgid="93961630309570294">"संपर्क"</string>
     <string name="local_invisible_directory" msgid="705244318477396120">"अन्य"</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_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>
+    <string name="debug_dump_email_body" msgid="4577749800871444318">"मेरी समस्त संपर्क जानकारी के साथ मेरा संपर्क डेटाबेस अनुलग्न है. सावधानी से काम करें."</string>
 </resources>
diff --git a/res/values-in/strings.xml b/res/values-in/strings.xml
index 9a8a599..76ca82c 100644
--- a/res/values-in/strings.xml
+++ b/res/values-in/strings.xml
@@ -19,7 +19,7 @@
     <string name="sharedUserLabel" msgid="8024311725474286801">"Aplikasi Inti Android"</string>
     <string name="app_label" msgid="3389954322874982620">"Penyimpanan Kontak"</string>
     <string name="provider_label" msgid="6012150850819899907">"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_ticker" msgid="7638747231223520477">"Upgrade 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="2581831842693151968">"Ketuk untuk menyelesaikan peningkatan versi."</string>
     <string name="default_directory" msgid="93961630309570294">"Kontak"</string>
diff --git a/res/values-ja/strings.xml b/res/values-ja/strings.xml
index 1756ab8..4e7a44c 100644
--- a/res/values-ja/strings.xml
+++ b/res/values-ja/strings.xml
@@ -26,7 +26,7 @@
     <string name="local_invisible_directory" msgid="705244318477396120">"その他"</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_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>
diff --git a/res/values-ky/strings.xml b/res/values-ky/strings.xml
index d8251c5..4798476 100644
--- a/res/values-ky/strings.xml
+++ b/res/values-ky/strings.xml
@@ -26,8 +26,8 @@
     <string name="local_invisible_directory" msgid="705244318477396120">"Башка"</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_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>
diff --git a/res/values-lo/strings.xml b/res/values-lo/strings.xml
index b75c816..1efef33 100644
--- a/res/values-lo/strings.xml
+++ b/res/values-lo/strings.xml
@@ -18,7 +18,7 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="sharedUserLabel" msgid="8024311725474286801">"ແອັບພລິເຄຊັນຫຼັກຂອງ Android"</string>
     <string name="app_label" msgid="3389954322874982620">"ບ່ອນຈັດເກັບຂໍ້ມູນລາຍຊື່ຜູ່ຕິດຕໍ່"</string>
-    <string name="provider_label" msgid="6012150850819899907">"ລາຍຊື່ຜູ່ຕິດຕໍ່"</string>
+    <string name="provider_label" msgid="6012150850819899907">"ລາຍຊື່ຜູ້ຕິດຕໍ່"</string>
     <string name="upgrade_out_of_memory_notification_ticker" msgid="7638747231223520477">"ການອັບເກຣດລາຍຊື່ຜູ່ຕິດຕໍ່ ຈະຕ້ອງໃຊ້ໜ່ວຍຄວາມຈຳເພີ່ມຕື່ມ."</string>
     <string name="upgrade_out_of_memory_notification_title" msgid="8888171924684998531">"ອັບເກຣດບ່ອນຈັດເກັບຂໍ້ມູນລາຍຊື່ຜູ່ຕິດຕໍ່"</string>
     <string name="upgrade_out_of_memory_notification_text" msgid="2581831842693151968">"ແຕະເພື່ອສຳເລັດການອັບເກຣດ."</string>
diff --git a/res/values-mk/strings.xml b/res/values-mk/strings.xml
index dc1a07a..84ff871 100644
--- a/res/values-mk/strings.xml
+++ b/res/values-mk/strings.xml
@@ -17,7 +17,7 @@
 <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">"Основни апликации на Android"</string>
-    <string name="app_label" msgid="3389954322874982620">"Меморирање контакти"</string>
+    <string name="app_label" msgid="3389954322874982620">"Склад за контакти"</string>
     <string name="provider_label" msgid="6012150850819899907">"Контакти"</string>
     <string name="upgrade_out_of_memory_notification_ticker" msgid="7638747231223520477">"За надградбата на контакти е потребно повеќе меморија."</string>
     <string name="upgrade_out_of_memory_notification_title" msgid="8888171924684998531">"Надградување меморија за контакти"</string>
diff --git a/res/values-ml/strings.xml b/res/values-ml/strings.xml
index dc2abdf..1ed08d2 100644
--- a/res/values-ml/strings.xml
+++ b/res/values-ml/strings.xml
@@ -18,11 +18,11 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="sharedUserLabel" msgid="8024311725474286801">"Android കോർ അപ്ലിക്കേഷനുകൾ"</string>
     <string name="app_label" msgid="3389954322874982620">"കോൺടാക്റ്റുകളുടെ സ്റ്റോറേജ്"</string>
-    <string name="provider_label" msgid="6012150850819899907">"വിലാസങ്ങൾ"</string>
+    <string name="provider_label" msgid="6012150850819899907">"കോണ്‍ടാക്റ്റുകള്‍"</string>
     <string name="upgrade_out_of_memory_notification_ticker" msgid="7638747231223520477">"കോൺ‌ടാക്റ്റുകൾ അപ്‌ഗ്രേഡുചെയ്യാൻ കൂടുതൽ മെമ്മറി ആവശ്യമാണ്."</string>
     <string name="upgrade_out_of_memory_notification_title" msgid="8888171924684998531">"കോൺ‌ടാക്റ്റുകൾക്കായുള്ള സ്റ്റോറേജ്  അപ്‌ഗ്രേഡുചെയ്യുന്നു"</string>
     <string name="upgrade_out_of_memory_notification_text" msgid="2581831842693151968">"അപ്‌ഗ്രേഡ് പൂർത്തിയാക്കാൻ ടാപ്പുചെയ്യുക."</string>
-    <string name="default_directory" msgid="93961630309570294">"വിലാസങ്ങൾ"</string>
+    <string name="default_directory" msgid="93961630309570294">"കോണ്‍‌ടാക്റ്റുകള്‍"</string>
     <string name="local_invisible_directory" msgid="705244318477396120">"മറ്റുള്ളവ"</string>
     <string name="voicemail_from_column" msgid="435732568832121444">"ഈ നമ്പറിൽ നിന്നുള്ള വോയ്‌സ്‌മെയിൽ "</string>
     <string name="debug_dump_title" msgid="4916885724165570279">"കോൺടാക്റ്റുകളുടെ ഡാറ്റാബേസ് പകർത്തുക"</string>
diff --git a/res/values-ne/strings.xml b/res/values-ne/strings.xml
index e813957..fd716da 100644
--- a/res/values-ne/strings.xml
+++ b/res/values-ne/strings.xml
@@ -16,7 +16,7 @@
 
 <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">"एन्ड्रोइड कोर अनुप्रयोगहरू"</string>
+    <string name="sharedUserLabel" msgid="8024311725474286801">"एन्ड्रोइड कोर एपहरू"</string>
     <string name="app_label" msgid="3389954322874982620">"सम्पर्कहरू भण्डारण"</string>
     <string name="provider_label" msgid="6012150850819899907">"सम्पर्कहरू"</string>
     <string name="upgrade_out_of_memory_notification_ticker" msgid="7638747231223520477">"सम्पर्क अद्यावधिकका लागि अझै धेरै मेमोरी चाहिन्छ।"</string>
diff --git a/res/values-pt-rPT/strings.xml b/res/values-pt-rPT/strings.xml
index beaa2c7..8d94fbb 100644
--- a/res/values-pt-rPT/strings.xml
+++ b/res/values-pt-rPT/strings.xml
@@ -16,7 +16,7 @@
 
 <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">"Aplicações Principais do Android"</string>
+    <string name="sharedUserLabel" msgid="8024311725474286801">"Apps Principais do Android"</string>
     <string name="app_label" msgid="3389954322874982620">"Armazenamento de contactos"</string>
     <string name="provider_label" msgid="6012150850819899907">"Contactos"</string>
     <string name="upgrade_out_of_memory_notification_ticker" msgid="7638747231223520477">"A atualização de contactos necessita de mais memória."</string>
diff --git a/res/values-ta/strings.xml b/res/values-ta/strings.xml
index 1116f19..c6e2539 100644
--- a/res/values-ta/strings.xml
+++ b/res/values-ta/strings.xml
@@ -16,7 +16,7 @@
 
 <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">"Android முக்கிய பயன்பாடுகள்"</string>
+    <string name="sharedUserLabel" msgid="8024311725474286801">"Android முக்கிய ஆப்ஸ்"</string>
     <string name="app_label" msgid="3389954322874982620">"தொடர்புகள் சேமிப்பிடம்"</string>
     <string name="provider_label" msgid="6012150850819899907">"தொடர்புகள்"</string>
     <string name="upgrade_out_of_memory_notification_ticker" msgid="7638747231223520477">"தொடர்புகளின் மேம்படுத்தலுக்கு கூடுதல் நினைவகம் தேவை."</string>
diff --git a/src/com/android/providers/contacts/CallLogProvider.java b/src/com/android/providers/contacts/CallLogProvider.java
index 3999520..ebe111f 100644
--- a/src/com/android/providers/contacts/CallLogProvider.java
+++ b/src/com/android/providers/contacts/CallLogProvider.java
@@ -138,6 +138,7 @@
         sCallsProjectionMap.put(Calls.FEATURES, Calls.FEATURES);
         sCallsProjectionMap.put(Calls.PHONE_ACCOUNT_COMPONENT_NAME, Calls.PHONE_ACCOUNT_COMPONENT_NAME);
         sCallsProjectionMap.put(Calls.PHONE_ACCOUNT_ID, Calls.PHONE_ACCOUNT_ID);
+        sCallsProjectionMap.put(Calls.PHONE_ACCOUNT_HIDDEN, Calls.PHONE_ACCOUNT_HIDDEN);
         sCallsProjectionMap.put(Calls.PHONE_ACCOUNT_ADDRESS, Calls.PHONE_ACCOUNT_ADDRESS);
         sCallsProjectionMap.put(Calls.NEW, Calls.NEW);
         sCallsProjectionMap.put(Calls.VOICEMAIL_URI, Calls.VOICEMAIL_URI);
@@ -313,6 +314,7 @@
             Log.v(TAG, "query: uri=" + uri + "  projection=" + Arrays.toString(projection) +
                     "  selection=[" + selection + "]  args=" + Arrays.toString(selectionArgs) +
                     "  order=[" + sortOrder + "] CPID=" + Binder.getCallingPid() +
+                    " CUID=" + Binder.getCallingUid() +
                     " User=" + UserUtils.getCurrentUserHandle(getContext()));
         }
 
@@ -323,6 +325,13 @@
         qb.setTables(Tables.CALLS);
         qb.setProjectionMap(sCallsProjectionMap);
         qb.setStrict(true);
+        // If the caller doesn't have READ_VOICEMAIL, make sure they can't
+        // do any SQL shenanigans to get access to the voicemails. If the caller does have the
+        // READ_VOICEMAIL permission, then they have sufficient permissions to access any data in
+        // the database, so the strict check is unnecessary.
+        if (!mVoicemailPermissions.callerHasReadAccess(getCallingPackage())) {
+            qb.setStrictGrammar(true);
+        }
 
         final SelectionBuilder selectionBuilder = new SelectionBuilder(selection);
         checkVoicemailPermissionAndAddRestriction(uri, selectionBuilder, true /*isQuery*/);
@@ -480,7 +489,8 @@
     private Uri insertInternal(Uri uri, ContentValues values) {
         if (VERBOSE_LOGGING) {
             Log.v(TAG, "insert: uri=" + uri + "  values=[" + values + "]" +
-                    " CPID=" + Binder.getCallingPid());
+                    " CPID=" + Binder.getCallingPid() +
+                    " CUID=" + Binder.getCallingUid());
         }
         waitForAccess(mReadAccessLatch);
         checkForSupportedColumns(sCallsProjectionMap, values);
@@ -513,6 +523,7 @@
             Log.v(TAG, "update: uri=" + uri +
                     "  selection=[" + selection + "]  args=" + Arrays.toString(selectionArgs) +
                     "  values=[" + values + "] CPID=" + Binder.getCallingPid() +
+                    " CUID=" + Binder.getCallingUid() +
                     " User=" + UserUtils.getCurrentUserHandle(getContext()));
         }
         waitForAccess(mReadAccessLatch);
@@ -526,6 +537,18 @@
         SelectionBuilder selectionBuilder = new SelectionBuilder(selection);
         checkVoicemailPermissionAndAddRestriction(uri, selectionBuilder, false /*isQuery*/);
 
+        final SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
+        qb.setTables(Tables.CALLS);
+        qb.setProjectionMap(sCallsProjectionMap);
+        qb.setStrict(true);
+        // If the caller doesn't have READ_VOICEMAIL, make sure they can't
+        // do any SQL shenanigans to get access to the voicemails. If the caller does have the
+        // READ_VOICEMAIL permission, then they have sufficient permissions to access any data in
+        // the database, so the strict check is unnecessary.
+        if (!mVoicemailPermissions.callerHasReadAccess(getCallingPackage())) {
+            qb.setStrictGrammar(true);
+        }
+
         final SQLiteDatabase db = mDbHelper.getWritableDatabase();
         final int matchedUriId = sURIMatcher.match(uri);
         switch (matchedUriId) {
@@ -540,8 +563,7 @@
                 throw new UnsupportedOperationException("Cannot update URL: " + uri);
         }
 
-        return createDatabaseModifier(db).update(uri, Tables.CALLS, values, selectionBuilder.build(),
-                selectionArgs);
+        return qb.update(db, values, selectionBuilder.build(), selectionArgs);
     }
 
     private int deleteInternal(Uri uri, String selection, String[] selectionArgs) {
@@ -549,20 +571,32 @@
             Log.v(TAG, "delete: uri=" + uri +
                     "  selection=[" + selection + "]  args=" + Arrays.toString(selectionArgs) +
                     " CPID=" + Binder.getCallingPid() +
+                    " CUID=" + Binder.getCallingUid() +
                     " User=" + UserUtils.getCurrentUserHandle(getContext()));
         }
         waitForAccess(mReadAccessLatch);
         SelectionBuilder selectionBuilder = new SelectionBuilder(selection);
         checkVoicemailPermissionAndAddRestriction(uri, selectionBuilder, false /*isQuery*/);
 
+        final SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
+        qb.setTables(Tables.CALLS);
+        qb.setProjectionMap(sCallsProjectionMap);
+        qb.setStrict(true);
+        // If the caller doesn't have READ_VOICEMAIL, make sure they can't
+        // do any SQL shenanigans to get access to the voicemails. If the caller does have the
+        // READ_VOICEMAIL permission, then they have sufficient permissions to access any data in
+        // the database, so the strict check is unnecessary.
+        if (!mVoicemailPermissions.callerHasReadAccess(getCallingPackage())) {
+            qb.setStrictGrammar(true);
+        }
+
         final SQLiteDatabase db = mDbHelper.getWritableDatabase();
         final int matchedUriId = sURIMatcher.match(uri);
         switch (matchedUriId) {
             case CALLS:
                 // TODO: Special case - We may want to forward the delete request on user 0 to the
                 // shadow provider too.
-                return createDatabaseModifier(db).delete(Tables.CALLS,
-                        selectionBuilder.build(), selectionArgs);
+                return qb.delete(db, selectionBuilder.build(), selectionArgs);
             default:
                 throw new UnsupportedOperationException("Cannot delete that URL: " + uri);
         }
diff --git a/src/com/android/providers/contacts/ContactsProvider2.java b/src/com/android/providers/contacts/ContactsProvider2.java
index b92a917..4fc9bfc 100644
--- a/src/com/android/providers/contacts/ContactsProvider2.java
+++ b/src/com/android/providers/contacts/ContactsProvider2.java
@@ -16,6 +16,10 @@
 
 package com.android.providers.contacts;
 
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.Manifest.permission.INTERACT_ACROSS_USERS;
+import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
+
 import android.accounts.Account;
 import android.accounts.AccountManager;
 import android.accounts.OnAccountsUpdateListener;
@@ -214,7 +218,6 @@
 
     private static final String READ_PERMISSION = "android.permission.READ_CONTACTS";
     private static final String WRITE_PERMISSION = "android.permission.WRITE_CONTACTS";
-    private static final String INTERACT_ACROSS_USERS = "android.permission.INTERACT_ACROSS_USERS";
 
 
     /* package */ static final String PHONEBOOK_COLLATOR_NAME = "PHONEBOOK";
@@ -532,23 +535,6 @@
             " SET " + RawContacts.DIRTY + "=1" +
             " WHERE " + RawContacts._ID + " IN (";
 
-    /** Sql for updating METADATA_DIRTY flag on multiple raw contacts */
-    private static final String UPDATE_RAW_CONTACT_SET_METADATA_DIRTY_SQL =
-            "UPDATE " + Tables.RAW_CONTACTS +
-                    " SET " + RawContacts.METADATA_DIRTY + "=1" +
-                    " WHERE " + RawContacts._ID + " IN (";
-
-    // Sql for updating MetadataSync.DELETED flag on multiple raw contacts.
-    // When using this sql, add comma separated raw contacts ids and "))".
-    private static final String UPDATE_METADATASYNC_SET_DELETED_SQL =
-            "UPDATE " + Tables.METADATA_SYNC
-                    + " SET " + MetadataSync.DELETED + "=1"
-                    + " WHERE " + MetadataSync._ID + " IN "
-                            + "(SELECT " + MetadataSyncColumns.CONCRETE_ID
-                            + " FROM " + Tables.RAW_CONTACTS_JOIN_METADATA_SYNC
-                            + " WHERE " + RawContactsColumns.CONCRETE_DELETED + "=1 AND "
-                            + RawContactsColumns.CONCRETE_ID + " IN (";
-
     /** Sql for updating VERSION on multiple raw contacts */
     private static final String UPDATE_RAW_CONTACT_SET_VERSION_SQL =
             "UPDATE " + Tables.RAW_CONTACTS +
@@ -2433,14 +2419,6 @@
         for (long rawContactId : mTransactionContext.get().getInsertedRawContactIds()) {
             mDbHelper.get().updateRawContactDisplayName(db, rawContactId);
             mAggregator.get().onRawContactInsert(mTransactionContext.get(), db, rawContactId);
-            if (mMetadataSyncEnabled) {
-                updateMetadataOnRawContactInsert(db, rawContactId);
-            }
-        }
-        if (mMetadataSyncEnabled) {
-            for (long rawContactId : mTransactionContext.get().getBackupIdChangedRawContacts()) {
-                updateMetadataOnRawContactInsert(db, rawContactId);
-            }
         }
 
         final Set<Long> dirtyRawContacts = mTransactionContext.get().getDirtyRawContactIds();
@@ -2461,29 +2439,8 @@
             db.execSQL(mSb.toString());
         }
 
-        final Set<Long> metadataDirtyRawContacts =
-                mTransactionContext.get().getMetadataDirtyRawContactIds();
-        if (!metadataDirtyRawContacts.isEmpty() && mMetadataSyncEnabled) {
-            mSb.setLength(0);
-            mSb.append(UPDATE_RAW_CONTACT_SET_METADATA_DIRTY_SQL);
-            appendIds(mSb, metadataDirtyRawContacts);
-            mSb.append(")");
-            db.execSQL(mSb.toString());
-            mSyncToMetadataNetWork = true;
-        }
-
         final Set<Long> changedRawContacts = mTransactionContext.get().getChangedRawContactIds();
         ContactsTableUtil.updateContactLastUpdateByRawContactId(db, changedRawContacts);
-        if (!changedRawContacts.isEmpty() && mMetadataSyncEnabled) {
-            // For the deleted raw contact, set related metadata as deleted
-            // if metadata flag is enabled.
-            mSb.setLength(0);
-            mSb.append(UPDATE_METADATASYNC_SET_DELETED_SQL);
-            appendIds(mSb, changedRawContacts);
-            mSb.append("))");
-            db.execSQL(mSb.toString());
-            mSyncToMetadataNetWork = true;
-        }
 
         // Update sync states.
         for (Map.Entry<Long, Object> entry : mTransactionContext.get().getUpdatedSyncStates()) {
@@ -2502,49 +2459,6 @@
         mMetadataSyncEnabled = enabled;
     }
 
-    interface MetadataSyncQuery {
-        String TABLE = Tables.RAW_CONTACTS_JOIN_METADATA_SYNC;
-        String[] COLUMNS = new String[] {
-                MetadataSyncColumns.CONCRETE_ID,
-                MetadataSync.DATA
-        };
-        int METADATA_SYNC_ID = 0;
-        int METADATA_SYNC_DATA = 1;
-        String SELECTION = MetadataSyncColumns.CONCRETE_DELETED + "=0 AND " +
-                RawContactsColumns.CONCRETE_ID + "=?";
-    }
-
-    /**
-     * Fetch the related metadataSync data column for the raw contact id.
-     * Returns null if there's no metadata for the raw contact.
-     */
-    private String queryMetadataSyncData(SQLiteDatabase db, long rawContactId) {
-        String metadataSyncData = null;
-        mSelectionArgs1[0] = String.valueOf(rawContactId);
-        final Cursor cursor = db.query(MetadataSyncQuery.TABLE,
-                MetadataSyncQuery.COLUMNS, MetadataSyncQuery.SELECTION,
-                mSelectionArgs1, null, null, null);
-        try {
-            if (cursor.moveToFirst()) {
-                metadataSyncData = cursor.getString(MetadataSyncQuery.METADATA_SYNC_DATA);
-            }
-        } finally {
-            cursor.close();
-        }
-        return metadataSyncData;
-    }
-
-    private void updateMetadataOnRawContactInsert(SQLiteDatabase db, long rawContactId) {
-        // Read metadata from MetadataSync table for the raw contact, and update.
-        final String metadataSyncData = queryMetadataSyncData(db, rawContactId);
-        if (TextUtils.isEmpty(metadataSyncData)) {
-            return;
-        }
-        final MetadataEntry metadataEntry = MetadataEntryParser.parseDataToMetaDataEntry(
-                metadataSyncData);
-        updateFromMetaDataEntry(db, metadataEntry);
-    }
-
     /**
      * Appends comma separated IDs.
      * @param ids Should not be empty
@@ -2566,10 +2480,7 @@
 
     protected void notifyChange(boolean syncToNetwork, boolean syncToMetadataNetwork) {
         getContext().getContentResolver().notifyChange(ContactsContract.AUTHORITY_URI, null,
-                syncToNetwork || syncToMetadataNetwork);
-
-        getContext().getContentResolver().notifyChange(MetadataSync.METADATA_AUTHORITY_URI,
-                null, syncToMetadataNetwork);
+                syncToNetwork);
     }
 
     protected void setProviderStatus(int status) {
@@ -2606,7 +2517,8 @@
     protected Uri insertInTransaction(Uri uri, ContentValues values) {
         if (VERBOSE_LOGGING) {
             Log.v(TAG, "insertInTransaction: uri=" + uri + "  values=[" + values + "]" +
-                    " CPID=" + Binder.getCallingPid());
+                    " CPID=" + Binder.getCallingPid() +
+                    " CUID=" + Binder.getCallingUid());
         }
 
         final SQLiteDatabase db = mDbHelper.get().getWritableDatabase();
@@ -2836,7 +2748,6 @@
             values.put(RawContacts.AGGREGATION_MODE, RawContacts.AGGREGATION_MODE_DISABLED);
         }
 
-        final boolean needToUpdateMetadata = shouldMarkMetadataDirtyForRawContact(values);
         // Databases that were created prior to the 906 upgrade have a default of Int.MAX_VALUE
         // for RawContacts.PINNED. Manually set the value to the correct default (0) if it is not
         // set.
@@ -2848,14 +2759,6 @@
         final SQLiteDatabase db = mDbHelper.get().getWritableDatabase();
         final long rawContactId = db.insert(Tables.RAW_CONTACTS, RawContacts.CONTACT_ID, values);
 
-        if (needToUpdateMetadata) {
-            mTransactionContext.get().markRawContactMetadataDirty(rawContactId,
-                    /* isMetadataSyncAdapter =*/false);
-        }
-        // If the new raw contact is inserted by a sync adapter, mark mSyncToMetadataNetWork as true
-        // so that it can trigger the metadata syncing from the server.
-        mSyncToMetadataNetWork |= callerIsSyncAdapter;
-
         final int aggregationMode = getIntValue(values, RawContacts.AGGREGATION_MODE,
                 RawContacts.AGGREGATION_MODE_DEFAULT);
         mAggregator.get().markNewForAggregation(rawContactId, aggregationMode);
@@ -3550,6 +3453,7 @@
             Log.v(TAG, "deleteInTransaction: uri=" + uri +
                     "  selection=[" + selection + "]  args=" + Arrays.toString(selectionArgs) +
                     " CPID=" + Binder.getCallingPid() +
+                    " CUID=" + Binder.getCallingUid() +
                     " User=" + UserUtils.getCurrentUserHandle(getContext()));
         }
 
@@ -3802,15 +3706,25 @@
     }
 
     private int deleteContact(long contactId, boolean callerIsSyncAdapter) {
+        ArrayList<Long> localRawContactIds = new ArrayList();
+
         final SQLiteDatabase db = mDbHelper.get().getWritableDatabase();
         mSelectionArgs1[0] = Long.toString(contactId);
         Cursor c = db.query(Tables.RAW_CONTACTS, new String[] {RawContacts._ID},
                 RawContacts.CONTACT_ID + "=?", mSelectionArgs1,
                 null, null, null);
+
+        // Raw contacts need to be deleted after the contact so just loop through and mark
+        // non-local raw contacts as deleted and collect the local raw contacts that will be
+        // deleted after the contact is deleted.
         try {
             while (c.moveToNext()) {
                 long rawContactId = c.getLong(0);
-                markRawContactAsDeleted(db, rawContactId, callerIsSyncAdapter);
+                if (rawContactIsLocal(rawContactId)) {
+                    localRawContactIds.add(rawContactId);
+                } else {
+                    markRawContactAsDeleted(db, rawContactId, callerIsSyncAdapter);
+                }
             }
         } finally {
             c.close();
@@ -3819,6 +3733,10 @@
         mProviderStatusUpdateNeeded = true;
 
         int result = ContactsTableUtil.deleteContact(db, contactId);
+
+        // Now purge the local raw contacts
+        deleteRawContactsImmediately(db, localRawContactIds);
+
         scheduleBackgroundTask(BACKGROUND_TASK_CLEAN_DELETE_LOG);
         return result;
     }
@@ -3842,19 +3760,19 @@
             c.close();
         }
 
+        // When a raw contact is deleted, a sqlite trigger deletes the parent contact.
+        // TODO: all contact deletes was consolidated into ContactTableUtil but this one can't
+        // because it's in a trigger.  Consider removing trigger and replacing with java code.
+        // This has to happen before the raw contact is deleted since it relies on the number
+        // of raw contacts.
         final boolean contactIsSingleton =
                 ContactsTableUtil.deleteContactIfSingleton(db, rawContactId) == 1;
         final int count;
 
         if (callerIsSyncAdapter || rawContactIsLocal(rawContactId)) {
-            // When a raw contact is deleted, a SQLite trigger deletes the parent contact.
-            // TODO: all contact deletes was consolidated into ContactTableUtil but this one can't
-            // because it's in a trigger.  Consider removing trigger and replacing with java code.
-            // This has to happen before the raw contact is deleted since it relies on the number
-            // of raw contacts.
-            db.delete(Tables.PRESENCE, PresenceColumns.RAW_CONTACT_ID + "=" + rawContactId, null);
-            count = db.delete(Tables.RAW_CONTACTS, RawContacts._ID + "=" + rawContactId, null);
-            mTransactionContext.get().markRawContactChangedOrDeletedOrInserted(rawContactId);
+            ArrayList<Long> rawContactsIds = new ArrayList<>();
+            rawContactsIds.add(rawContactId);
+            count = deleteRawContactsImmediately(db, rawContactsIds);
         } else {
             count = markRawContactAsDeleted(db, rawContactId, callerIsSyncAdapter);
         }
@@ -3865,6 +3783,43 @@
     }
 
     /**
+     * Returns the number of raw contacts that were deleted immediately -- we don't merely set
+     * the DELETED column to 1, the entire raw contact row is deleted straightaway.
+     */
+    private int deleteRawContactsImmediately(SQLiteDatabase db, List<Long> rawContactIds) {
+        if (rawContactIds == null || rawContactIds.isEmpty()) {
+            return 0;
+        }
+
+        // Build the where clause for the raw contacts to be deleted
+        ArrayList<String> whereArgs = new ArrayList<>();
+        StringBuilder whereClause = new StringBuilder(rawContactIds.size() * 2 - 1);
+        whereClause.append(" IN (?");
+        whereArgs.add(String.valueOf(rawContactIds.get(0)));
+        for (int i = 1; i < rawContactIds.size(); i++) {
+            whereClause.append(",?");
+            whereArgs.add(String.valueOf(rawContactIds.get(i)));
+        }
+        whereClause.append(")");
+
+        // Remove presence rows
+        db.delete(Tables.PRESENCE, PresenceColumns.RAW_CONTACT_ID + whereClause.toString(),
+                whereArgs.toArray(new String[0]));
+
+        // Remove raw contact rows
+        int result = db.delete(Tables.RAW_CONTACTS, RawContacts._ID + whereClause.toString(),
+                whereArgs.toArray(new String[0]));
+
+        if (result > 0) {
+            for (Long rawContactId : rawContactIds) {
+                mTransactionContext.get().markRawContactChangedOrDeletedOrInserted(rawContactId);
+            }
+        }
+
+        return result;
+    }
+
+    /**
      * Returns whether the given raw contact ID is local (i.e. has no account associated with it).
      */
     private boolean rawContactIsLocal(long rawContactId) {
@@ -3964,6 +3919,7 @@
             Log.v(TAG, "updateInTransaction: uri=" + uri +
                     "  selection=[" + selection + "]  args=" + Arrays.toString(selectionArgs) +
                     "  values=[" + values + "] CPID=" + Binder.getCallingPid() +
+                    " CUID=" + Binder.getCallingUid() +
                     " User=" + UserUtils.getCurrentUserHandle(getContext()));
         }
 
@@ -4498,7 +4454,6 @@
         final boolean isDataSetChanging = values.containsKey(RawContacts.DATA_SET);
         final boolean isAccountChanging =
                 isAccountNameChanging || isAccountTypeChanging || isDataSetChanging;
-        final boolean isBackupIdChanging = values.containsKey(RawContacts.BACKUP_ID);
 
         int previousDeleted = 0;
         long accountId = 0;
@@ -4561,32 +4516,6 @@
             if (aggregationMode != RawContacts.AGGREGATION_MODE_DEFAULT) {
                 aggregator.markForAggregation(rawContactId, aggregationMode, false);
             }
-            if (shouldMarkMetadataDirtyForRawContact(values)) {
-                mTransactionContext.get().markRawContactMetadataDirty(
-                        rawContactId, callerIsMetadataSyncAdapter);
-            }
-            if (isBackupIdChanging) {
-                Cursor cursor = db.query(Tables.RAW_CONTACTS,
-                        new String[] {RawContactsColumns.CONCRETE_METADATA_DIRTY},
-                        selection, mSelectionArgs1, null, null, null);
-                int metadataDirty = 0;
-                try {
-                    if (cursor.moveToFirst()) {
-                        metadataDirty = cursor.getInt(0);
-                    }
-                } finally {
-                    cursor.close();
-                }
-
-                if (metadataDirty == 1) {
-                    // Re-notify metadata network if backup_id is updated and metadata is dirty.
-                    mTransactionContext.get().markRawContactMetadataDirty(
-                            rawContactId, callerIsMetadataSyncAdapter);
-                } else {
-                    // Merge from metadata sync table if backup_id is updated and no dirty change.
-                    mTransactionContext.get().markBackupIdChangedRawContact(rawContactId);
-                }
-            }
             if (flagExists(values, RawContacts.STARRED)) {
                 if (!callerIsSyncAdapter) {
                     updateFavoritesMembership(rawContactId, flagIsSet(values, RawContacts.STARRED));
@@ -4760,10 +4689,6 @@
             // Mark dirty when changing starred to trigger sync.
             values.put(RawContacts.DIRTY, 1);
         }
-        if (mMetadataSyncEnabled && (hasStarredValue || hasPinnedValue || hasVoiceMailValue)) {
-            // Mark dirty to trigger metadata syncing.
-            values.put(RawContacts.METADATA_DIRTY, 1);
-        }
 
         mSelectionArgs1[0] = String.valueOf(contactId);
         db.update(Tables.RAW_CONTACTS, values, RawContacts.CONTACT_ID + "=?"
@@ -4781,11 +4706,6 @@
                                 flagIsSet(values, RawContacts.STARRED));
                         mSyncToNetwork |= !callerIsSyncAdapter;
                     }
-
-                    if (hasStarredValue || hasPinnedValue || hasVoiceMailValue) {
-                        mTransactionContext.get().markRawContactMetadataDirty(rawContactId,
-                                false /*callerIsMetadataSyncAdapter*/);
-                    }
                 }
             } finally {
                 cursor.close();
@@ -4863,21 +4783,12 @@
 
         aggregator.aggregateContact(mTransactionContext.get(), db, rawContactId1);
         aggregator.aggregateContact(mTransactionContext.get(), db, rawContactId2);
-        mTransactionContext.get().markRawContactMetadataDirty(rawContactId1,
-                callerIsMetadataSyncAdapter);
-        mTransactionContext.get().markRawContactMetadataDirty(rawContactId2,
-                callerIsMetadataSyncAdapter);
 
         // The return value is fake - we just confirm that we made a change, not count actual
         // rows changed.
         return 1;
     }
 
-    private boolean shouldMarkMetadataDirtyForRawContact(ContentValues values) {
-        return (flagExists(values, RawContacts.STARRED) || flagExists(values, RawContacts.PINNED)
-                || flagExists(values, RawContacts.SEND_TO_VOICEMAIL));
-    }
-
     @Override
     public void onAccountsUpdated(Account[] accounts) {
         scheduleBackgroundTask(BACKGROUND_TASK_UPDATE_ACCOUNTS);
@@ -5441,6 +5352,7 @@
             Log.v(TAG, "query: uri=" + uri + "  projection=" + Arrays.toString(projection) +
                     "  selection=[" + selection + "]  args=" + Arrays.toString(selectionArgs) +
                     "  order=[" + sortOrder + "] CPID=" + Binder.getCallingPid() +
+                    " CUID=" + Binder.getCallingUid() +
                     " User=" + UserUtils.getCurrentUserHandle(getContext()));
         }
 
@@ -5454,10 +5366,12 @@
             return null;
         }
 
-        // Check enterprise policy if caller does not come from same profile
-        if (!(isCallerFromSameUser() || mEnterprisePolicyGuard.isCrossProfileAllowed(uri))) {
-            return createEmptyCursor(uri, projection);
+        // If caller does not come from same profile, Check if it's privileged or allowed by
+        // enterprise policy
+        if (!queryAllowedByEnterprisePolicy(uri)) {
+            return null;
         }
+
         // Query the profile DB if appropriate.
         if (mapsToProfileDb(uri)) {
             switchToProfileMode();
@@ -5477,9 +5391,46 @@
         }
     }
 
+    private boolean queryAllowedByEnterprisePolicy(Uri uri) {
+        if (isCallerFromSameUser()) {
+            // Caller is on the same user; query allowed.
+            return true;
+        }
+        if (!doesCallerHoldInteractAcrossUserPermission()) {
+            // Cross-user and the caller has no INTERACT_ACROSS_USERS; don't allow query.
+            // Technically, in a cross-profile sharing case, this would be a valid query.
+            // But for now we don't allow it. (We never allowe it and no one complained about it.)
+            return false;
+        }
+        if (isCallerAnotherSelf()) {
+            // The caller is the other CP2 (which has INTERACT_ACROSS_USERS), meaning the reuest
+            // is on behalf of a "real" client app.
+            // Consult the enterprise policy.
+            return mEnterprisePolicyGuard.isCrossProfileAllowed(uri);
+        }
+        return true;
+    }
+
     private boolean isCallerFromSameUser() {
-        return Binder.getCallingUserHandle().getIdentifier() == UserUtils
-                .getCurrentUserHandle(getContext());
+        return UserHandle.getUserId(Binder.getCallingUid()) == UserHandle.myUserId();
+    }
+
+    /**
+     * Returns true if called by a different user's CP2.
+     */
+    private boolean isCallerAnotherSelf() {
+        // Note normally myUid is always different from the callerUid in the code path where
+        // this method is used, except during unit tests, where the caller is always the same
+        // process.
+        final int myUid = android.os.Process.myUid();
+        final int callingUid = Binder.getCallingUid();
+        return (myUid != callingUid) && UserHandle.isSameApp(myUid, callingUid);
+    }
+
+    private boolean doesCallerHoldInteractAcrossUserPermission() {
+        final Context context = getContext();
+        return context.checkCallingPermission(INTERACT_ACROSS_USERS_FULL) == PERMISSION_GRANTED
+                || context.checkCallingPermission(INTERACT_ACROSS_USERS) == PERMISSION_GRANTED;
     }
 
     private Cursor queryDirectoryIfNecessary(Uri uri, String[] projection, String selection,
@@ -5526,7 +5477,7 @@
 
     private String getRealCallerPackageName(Uri queryUri) {
         // If called by another CP2, then the URI should contain the original package name.
-        if (calledByAnotherSelf()) {
+        if (isCallerAnotherSelf()) {
             final String passedPackage = queryUri.getQueryParameter(
                     Directory.CALLER_PACKAGE_PARAM_KEY);
             if (TextUtils.isEmpty(passedPackage)) {
@@ -5541,18 +5492,6 @@
         }
     }
 
-    /**
-     * Returns true if called by a different user's CP2.
-     */
-    private boolean calledByAnotherSelf() {
-        // Note normally myUid is always different from the callerUid in the code path where
-        // this method is used, except during unit tests, where the caller is always the same
-        // process.
-        final int myUid = android.os.Process.myUid();
-        final int callerUid = Binder.getCallingUid();
-        return (myUid != callerUid) && UserHandle.isSameApp(myUid, callerUid);
-    }
-
     private Cursor queryDirectoryAuthority(Uri uri, String[] projection, String selection,
             String[] selectionArgs, String sortOrder, String directory,
             final CancellationSignal cancellationSignal) {
@@ -5609,6 +5548,17 @@
             return null;
         }
 
+        if (cursor.getCount() > 0) {
+            final int callingUid = Binder.getCallingUid();
+            final String directoryAuthority = directoryInfo.authority;
+            if (VERBOSE_LOGGING) {
+                Log.v(TAG, "Making authority " + directoryAuthority
+                        + " visible to UID " + callingUid);
+            }
+            getContext().getPackageManager().grantImplicitAccess(
+                    callingUid, directoryAuthority);
+        }
+
         // Load the cursor contents into a memory cursor (backed by a cursor window) and close the
         // underlying cursor.
         try {
@@ -8525,9 +8475,7 @@
             if (!isDirectoryParamValid(uri)){
                 return null;
             }
-            if (!isCallerFromSameUser() /* From differnt user */
-                    && !mEnterprisePolicyGuard.isCrossProfileAllowed(uri)
-                    /* Policy not allowed */){
+            if (!queryAllowedByEnterprisePolicy(uri)) {
                 return null;
             }
             waitForAccess(mode.equals("r") ? mReadAccessLatch : mWriteAccessLatch);
@@ -8545,6 +8493,7 @@
             if (VERBOSE_LOGGING) {
                 Log.v(TAG, "openAssetFile uri=" + uri + " mode=" + mode + " success=" + success +
                         " CPID=" + Binder.getCallingPid() +
+                        " CUID=" + Binder.getCallingUid() +
                         " User=" + UserUtils.getCurrentUserHandle(getContext()));
             }
         }
diff --git a/src/com/android/providers/contacts/MetadataEntryParser.java b/src/com/android/providers/contacts/MetadataEntryParser.java
index 1a630a3..2fe423a 100644
--- a/src/com/android/providers/contacts/MetadataEntryParser.java
+++ b/src/com/android/providers/contacts/MetadataEntryParser.java
@@ -60,8 +60,11 @@
 
     @NeededForTesting
     public static class UsageStats {
+        @NeededForTesting
         final String mUsageType;
+        @NeededForTesting
         final long mLastTimeUsed;
+        @NeededForTesting
         final int mTimesUsed;
 
         @NeededForTesting
@@ -74,9 +77,13 @@
 
     @NeededForTesting
     public static class FieldData {
+        @NeededForTesting
         final String mDataHashId;
+        @NeededForTesting
         final boolean mIsPrimary;
+        @NeededForTesting
         final boolean mIsSuperPrimary;
+        @NeededForTesting
         final ArrayList<UsageStats> mUsageStatsList;
 
         @NeededForTesting
@@ -91,9 +98,13 @@
 
     @NeededForTesting
     public static class RawContactInfo {
+        @NeededForTesting
         final String mBackupId;
+        @NeededForTesting
         final String mAccountType;
+        @NeededForTesting
         final String mAccountName;
+        @NeededForTesting
         final String mDataSet;
 
         @NeededForTesting
@@ -108,8 +119,11 @@
 
     @NeededForTesting
     public static class AggregationData {
+        @NeededForTesting
         final RawContactInfo mRawContactInfo1;
+        @NeededForTesting
         final RawContactInfo mRawContactInfo2;
+        @NeededForTesting
         final String mType;
 
         @NeededForTesting
@@ -123,11 +137,17 @@
 
     @NeededForTesting
     public static class MetadataEntry {
+        @NeededForTesting
         final RawContactInfo mRawContactInfo;
+        @NeededForTesting
         final int mSendToVoicemail;
+        @NeededForTesting
         final int mStarred;
+        @NeededForTesting
         final int mPinned;
+        @NeededForTesting
         final ArrayList<FieldData> mFieldDatas;
+        @NeededForTesting
         final ArrayList<AggregationData> mAggregationDatas;
 
         @NeededForTesting
diff --git a/src/com/android/providers/contacts/VoicemailContentProvider.java b/src/com/android/providers/contacts/VoicemailContentProvider.java
index 1ced1be..daecd97 100644
--- a/src/com/android/providers/contacts/VoicemailContentProvider.java
+++ b/src/com/android/providers/contacts/VoicemailContentProvider.java
@@ -22,6 +22,7 @@
 import static com.android.providers.contacts.util.DbQueryUtils.getEqualityClause;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.app.AppOpsManager;
 import android.content.ContentProvider;
 import android.content.ContentValues;
@@ -118,24 +119,24 @@
     }
 
     @Override
-    protected int enforceReadPermissionInner(Uri uri, String callingPkg, IBinder callerToken)
-            throws SecurityException {
+    protected int enforceReadPermissionInner(Uri uri, String callingPkg,
+            @Nullable String featureId, IBinder callerToken) throws SecurityException {
         // Permit carrier-privileged apps regardless of ADD_VOICEMAIL permission state.
         if (mVoicemailPermissions.callerHasCarrierPrivileges()) {
             return MODE_ALLOWED;
         }
-        return super.enforceReadPermissionInner(uri, callingPkg, callerToken);
+        return super.enforceReadPermissionInner(uri, callingPkg, featureId, callerToken);
     }
 
 
     @Override
-    protected int enforceWritePermissionInner(Uri uri, String callingPkg, IBinder callerToken)
-            throws SecurityException {
+    protected int enforceWritePermissionInner(Uri uri, String callingPkg,
+            @Nullable String featureId, IBinder callerToken) throws SecurityException {
         // Permit carrier-privileged apps regardless of ADD_VOICEMAIL permission state.
         if (mVoicemailPermissions.callerHasCarrierPrivileges()) {
             return MODE_ALLOWED;
         }
-        return super.enforceWritePermissionInner(uri, callingPkg, callerToken);
+        return super.enforceWritePermissionInner(uri, callingPkg, featureId, callerToken);
     }
 
     @VisibleForTesting
@@ -179,7 +180,8 @@
     public Uri insert(Uri uri, ContentValues values) {
         if (VERBOSE_LOGGING) {
             Log.v(TAG, "insert: uri=" + uri + "  values=[" + values + "]" +
-                    " CPID=" + Binder.getCallingPid());
+                    " CPID=" + Binder.getCallingPid() +
+                    " CUID=" + Binder.getCallingUid());
         }
         UriData uriData = checkPermissionsAndCreateUriDataForWrite(uri, values);
         return getTableDelegate(uriData).insert(uriData, values);
@@ -198,6 +200,7 @@
             Log.v(TAG, "query: uri=" + uri + "  projection=" + Arrays.toString(projection) +
                     "  selection=[" + selection + "]  args=" + Arrays.toString(selectionArgs) +
                     "  order=[" + sortOrder + "] CPID=" + Binder.getCallingPid() +
+                    " CUID=" + Binder.getCallingUid() +
                     " User=" + UserUtils.getCurrentUserHandle(getContext()));
         }
         UriData uriData = checkPermissionsAndCreateUriDataForRead(uri);
@@ -213,6 +216,7 @@
             Log.v(TAG, "update: uri=" + uri +
                     "  selection=[" + selection + "]  args=" + Arrays.toString(selectionArgs) +
                     "  values=[" + values + "] CPID=" + Binder.getCallingPid() +
+                    " CUID=" + Binder.getCallingUid() +
                     " User=" + UserUtils.getCurrentUserHandle(getContext()));
         }
         UriData uriData = checkPermissionsAndCreateUriDataForWrite(uri, values);
@@ -228,6 +232,7 @@
             Log.v(TAG, "delete: uri=" + uri +
                     "  selection=[" + selection + "]  args=" + Arrays.toString(selectionArgs) +
                     " CPID=" + Binder.getCallingPid() +
+                    " CUID=" + Binder.getCallingUid() +
                     " User=" + UserUtils.getCurrentUserHandle(getContext()));
         }
         UriData uriData = checkPermissionsAndCreateUriDataForWrite(uri);
@@ -254,6 +259,7 @@
             if (VERBOSE_LOGGING) {
                 Log.v(TAG, "openFile uri=" + uri + " mode=" + mode + " success=" + success +
                         " CPID=" + Binder.getCallingPid() +
+                        " CUID=" + Binder.getCallingUid() +
                         " User=" + UserUtils.getCurrentUserHandle(getContext()));
             }
         }
diff --git a/test_common/src/com/android/providers/contacts/testutil/DatabaseAsserts.java b/test_common/src/com/android/providers/contacts/testutil/DatabaseAsserts.java
index 2af9829..e0a7836 100644
--- a/test_common/src/com/android/providers/contacts/testutil/DatabaseAsserts.java
+++ b/test_common/src/com/android/providers/contacts/testutil/DatabaseAsserts.java
@@ -16,6 +16,7 @@
 
 package com.android.providers.contacts.testutil;
 
+import android.accounts.Account;
 import android.content.ContentResolver;
 import android.content.ContentValues;
 import android.net.Uri;
@@ -50,12 +51,21 @@
     }
 
     /**
-     * Create a contact and assert that the record exists.
+     * Create a contact in the local account and assert that the record exists.
      *
      * @return The created contact id pair.
      */
     public static ContactIdPair assertAndCreateContact(ContentResolver resolver) {
-        long rawContactId = RawContactUtil.createRawContactWithName(resolver);
+        return assertAndCreateContact(resolver, null);
+    }
+
+    /**
+     * Create a contact in the given account and assert that the record exists.
+     *
+     * @return The created contact id pair.
+     */
+    public static ContactIdPair assertAndCreateContact(ContentResolver resolver, Account account) {
+        long rawContactId = RawContactUtil.createRawContactWithName(resolver, account);
 
         long contactId = RawContactUtil.queryContactIdByRawContactId(resolver, rawContactId);
         MoreAsserts.assertNotEqual(CommonDatabaseUtils.NOT_FOUND, contactId);
diff --git a/tests/src/com/android/providers/contacts/CallLogProviderTest.java b/tests/src/com/android/providers/contacts/CallLogProviderTest.java
index 9efdfaa..9baf1e4 100644
--- a/tests/src/com/android/providers/contacts/CallLogProviderTest.java
+++ b/tests/src/com/android/providers/contacts/CallLogProviderTest.java
@@ -18,6 +18,7 @@
 
 import android.telecom.CallerInfo;
 import com.android.providers.contacts.testutil.CommonDatabaseUtils;
+import com.android.providers.contacts.util.ContactsPermissions;
 
 import android.content.ComponentName;
 import android.content.ContentProvider;
@@ -59,7 +60,7 @@
             Voicemails.DIRTY,
             Voicemails.DELETED};
     /** Total number of columns exposed by call_log provider. */
-    private static final int NUM_CALLLOG_FIELDS = 34;
+    private static final int NUM_CALLLOG_FIELDS = 35;
 
     private static final int MIN_MATCH = 7;
 
@@ -194,9 +195,12 @@
         PhoneAccountHandle subscription = new PhoneAccountHandle(
                 sComponentName, "sub0");
 
+        // Allow self-calls in order to add the call
+        ContactsPermissions.ALLOW_SELF_CALL = true;
         Uri uri = Calls.addCall(ci, getMockContext(), "1-800-263-7643",
                 Calls.PRESENTATION_ALLOWED, Calls.OUTGOING_TYPE, 0, subscription, 2000,
                 40, null);
+        ContactsPermissions.ALLOW_SELF_CALL = false;
         assertNotNull(uri);
         assertEquals("0@" + CallLog.AUTHORITY, uri.getAuthority());
 
diff --git a/tests/src/com/android/providers/contacts/ContactsActor.java b/tests/src/com/android/providers/contacts/ContactsActor.java
index 89f296b..804e79a 100644
--- a/tests/src/com/android/providers/contacts/ContactsActor.java
+++ b/tests/src/com/android/providers/contacts/ContactsActor.java
@@ -96,6 +96,8 @@
     public static final String PACKAGE_GREEN = "com.example.green";
     public static final String PACKAGE_BLUE = "org.example.blue";
 
+    private static final int DEFAULT_USER_ID = 0;
+
     public Context context;
     public String packageName;
     public MockContentResolver resolver;
@@ -158,11 +160,8 @@
 
     public static class MockUserManager extends UserManager {
         public static UserInfo createUserInfo(String name, int id, int groupId, int flags) {
-            final UserInfo ui = new UserInfo();
-            ui.name = name;
-            ui.id = id;
+            final UserInfo ui = new UserInfo(id, name, flags | UserInfo.FLAG_INITIALIZED);
             ui.profileGroupId = groupId;
-            ui.flags = flags | UserInfo.FLAG_INITIALIZED;
             return ui;
         }
 
@@ -173,7 +172,7 @@
         public static final UserInfo SECONDARY_USER = createUserInfo("2nd", 11, 11, 0);
 
         /** "My" user.  Set it to change the current user. */
-        public int myUser = 0;
+        public int myUser = DEFAULT_USER_ID;
 
         private ArrayList<UserInfo> mUsers = new ArrayList<>();
 
@@ -399,7 +398,11 @@
 
             @Override
             public int getUserId() {
-                return mockUserManager.getUserHandle();
+                if (mockUserManager != null) {
+                    return mockUserManager.getUserHandle();
+                } else {
+                    return DEFAULT_USER_ID;
+                }
             }
 
             @Override
diff --git a/tests/src/com/android/providers/contacts/ContactsProvider2Test.java b/tests/src/com/android/providers/contacts/ContactsProvider2Test.java
index 6421c8f..d5643d2 100644
--- a/tests/src/com/android/providers/contacts/ContactsProvider2Test.java
+++ b/tests/src/com/android/providers/contacts/ContactsProvider2Test.java
@@ -3061,16 +3061,16 @@
         long rawContactId = RawContactUtil.createRawContactWithBackupId(mResolver, backupId,
                 account);
         Uri rawContactUri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId);
-        // Check if the raw contact is updated.
+        // Check if the raw contact is not updated since Lychee is removed.
         assertStoredValue(rawContactUri, RawContacts._ID, rawContactId);
         assertStoredValue(rawContactUri, RawContacts.ACCOUNT_TYPE, accountType);
         assertStoredValue(rawContactUri, RawContacts.ACCOUNT_NAME, accountName);
         assertStoredValue(rawContactUri, RawContacts.BACKUP_ID, backupId);
-        assertStoredValue(rawContactUri, RawContacts.SEND_TO_VOICEMAIL, "1");
-        assertStoredValue(rawContactUri, RawContacts.STARRED, "1");
-        assertStoredValue(rawContactUri, RawContacts.PINNED, "1");
-        // Notify metadata network on raw contact insertion
-        assertMetadataNetworkNotified(true);
+        assertStoredValue(rawContactUri, RawContacts.SEND_TO_VOICEMAIL, "0");
+        assertStoredValue(rawContactUri, RawContacts.STARRED, "0");
+        assertStoredValue(rawContactUri, RawContacts.PINNED, "0");
+        // No metadata network notify.
+        assertMetadataNetworkNotified(false);
     }
 
     public void testUpdateMetadataOnRawContactBackupIdChange() throws Exception {
@@ -3128,15 +3128,15 @@
         ContentValues updatedValues = new ContentValues();
         updatedValues.put(RawContacts.BACKUP_ID, backupId);
         mResolver.update(RawContacts.CONTENT_URI, updatedValues, null, null);
-        // Check if the raw contact is updated because of backup_id change.
+        // Check if the raw contact is still not updated.
         assertStoredValue(rawContactUri, RawContacts._ID, rawContactId);
         assertStoredValue(rawContactUri, RawContacts.ACCOUNT_TYPE, accountType);
         assertStoredValue(rawContactUri, RawContacts.ACCOUNT_NAME, accountName);
-        assertStoredValue(rawContactUri, RawContacts.SEND_TO_VOICEMAIL, "1");
-        assertStoredValue(rawContactUri, RawContacts.STARRED, "1");
-        assertStoredValue(rawContactUri, RawContacts.PINNED, "1");
-        // Notify metadata network because of the changed raw contact.
-        assertMetadataNetworkNotified(true);
+        assertStoredValue(rawContactUri, RawContacts.SEND_TO_VOICEMAIL, "0");
+        assertStoredValue(rawContactUri, RawContacts.STARRED, "0");
+        assertStoredValue(rawContactUri, RawContacts.PINNED, "0");
+        // No metadata network notify.
+        assertMetadataNetworkNotified(false);
     }
 
     public void testDeleteMetadataOnRawContactDelete() throws Exception {
@@ -3186,12 +3186,12 @@
 
         // Delete raw contact.
         mResolver.delete(rawContactUri, null, null);
-        // Check if the metadata is deleted.
-        assertStoredValue(metadataUri, MetadataSync.DELETED, "1");
+        // Check if the metadata is not deleted.
+        assertStoredValue(metadataUri, MetadataSync.DELETED, "0");
         // check raw contact metadata_dirty column is not changed on raw contact deletion
         assertMetadataDirty(rawContactUri, false);
-        // Notify metadata network on raw contact deletion
-        assertMetadataNetworkNotified(true);
+        // Lychee removed. Will not notify it.
+        assertMetadataNetworkNotified(false);
 
         // Add another rawcontact and metadata, and don't delete them.
         // Insert a raw contact.
@@ -3215,10 +3215,10 @@
 
         // Check if the metadata is not marked as deleted.
         assertStoredValue(metadataUri2, MetadataSync.DELETED, "0");
-        // check raw contact metadata_dirty column is changed on raw contact update
-        assertMetadataDirty(rawContactUri2, true);
-        // Notify metadata network on raw contact update
-        assertMetadataNetworkNotified(true);
+        // Will not set metadata_dirty since Lychee is removed.
+        assertMetadataDirty(rawContactUri2, false);
+        // Will not notify Lychee since it's removed.
+        assertMetadataNetworkNotified(false);
     }
 
     public void testPostalsQuery() {
@@ -5210,16 +5210,16 @@
         updateSendToVoicemailAndRingtone(contactId, true, "foo");
         assertSendToVoicemailAndRingtone(contactId, true, "foo");
         assertNetworkNotified(false);
-        assertMetadataNetworkNotified(true);
+        assertMetadataNetworkNotified(false);
         assertDirty(rawContactUri, false);
-        assertMetadataDirty(rawContactUri, true);
+        assertMetadataDirty(rawContactUri, false);
 
         updateSendToVoicemailAndRingtoneWithSelection(contactId, false, "bar");
         assertSendToVoicemailAndRingtone(contactId, false, "bar");
         assertNetworkNotified(false);
-        assertMetadataNetworkNotified(true);
+        assertMetadataNetworkNotified(false);
         assertDirty(rawContactUri, false);
-        assertMetadataDirty(rawContactUri, true);
+        assertMetadataDirty(rawContactUri, false);
     }
 
     public void testSendToVoicemailAndRingtoneAfterAggregation() {
@@ -5297,10 +5297,10 @@
 
         assertDirty(rawContactUri1, false);
         assertDirty(rawContactUri2, false);
-        assertMetadataDirty(rawContactUri1, true);
-        assertMetadataDirty(rawContactUri2, true);
+        assertMetadataDirty(rawContactUri1, false);
+        assertMetadataDirty(rawContactUri2, false);
         assertNetworkNotified(false);
-        assertMetadataNetworkNotified(true);
+        assertMetadataNetworkNotified(false);
     }
 
     public void testStatusUpdateInsert() {
@@ -6901,7 +6901,7 @@
         assertDirty(rawContactUri, false);
         assertMetadataDirty(rawContactUri, false);
         assertNetworkNotified(true);
-        assertMetadataNetworkNotified(true);
+        assertMetadataNetworkNotified(false);
 
         // When inserting a rawcontact with metadata.
         ContentValues values = new ContentValues();
@@ -6910,9 +6910,9 @@
         values.put(ContactsContract.RawContacts.ACCOUNT_TYPE, mAccount.type);
         Uri rawContactId2Uri = mResolver.insert(RawContacts.CONTENT_URI, values);
         assertDirty(rawContactId2Uri, false);
-        assertMetadataDirty(rawContactId2Uri, true);
+        assertMetadataDirty(rawContactId2Uri, false);
         assertNetworkNotified(true);
-        assertMetadataNetworkNotified(true);
+        assertMetadataNetworkNotified(false);
     }
 
     public void testRawContactDirtyAndVersion() {
@@ -6934,8 +6934,8 @@
 
         assertDirty(uri, false);
         assertNetworkNotified(false);
-        assertMetadataDirty(uri, true);
-        assertMetadataNetworkNotified(true);
+        assertMetadataDirty(uri, false);
+        assertMetadataNetworkNotified(false);
 
         Uri emailUri = insertEmail(rawContactId, "goo@woo.com");
         assertDirty(uri, true);
@@ -7005,9 +7005,8 @@
         long rawContactId = ContentUris.parseId(mResolver.insert(uri, new ContentValues()));
         Uri rawContactUri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId);
         assertMetadataDirty(rawContactUri, false);
-        // If the raw contact is inserted by sync adapter, it will notify metadata change no matter
-        // if there is any metadata change.
-        assertMetadataNetworkNotified(true);
+        // Will not notify Lychee since it's removed.
+        assertMetadataNetworkNotified(false);
     }
 
     public void testMarkAsMetadataDirtyForRawContactMetadataChange() {
@@ -7025,8 +7024,8 @@
         assertStoredValue(contactUri, Contacts.STARRED, 1);
 
         Uri rawContactUri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId);
-        assertMetadataDirty(rawContactUri, true);
-        assertMetadataNetworkNotified(true);
+        assertMetadataDirty(rawContactUri, false);
+        assertMetadataNetworkNotified(false);
 
         clearMetadataDirty(rawContactUri);
         values = new ContentValues();
@@ -7034,8 +7033,8 @@
         mResolver.update(contactUri, values, null, null);
         assertStoredValue(contactUri, Contacts.PINNED, 1);
 
-        assertMetadataDirty(rawContactUri, true);
-        assertMetadataNetworkNotified(true);
+        assertMetadataDirty(rawContactUri, false);
+        assertMetadataNetworkNotified(false);
 
         clearMetadataDirty(rawContactUri);
         values = new ContentValues();
@@ -7043,8 +7042,8 @@
         mResolver.update(contactUri, values, null, null);
         assertStoredValue(contactUri, Contacts.SEND_TO_VOICEMAIL, 1);
 
-        assertMetadataDirty(rawContactUri, true);
-        assertMetadataNetworkNotified(true);
+        assertMetadataDirty(rawContactUri, false);
+        assertMetadataNetworkNotified(false);
     }
 
     public void testMarkAsMetadataDirtyForRawContactBackupIdChange() {
@@ -7059,15 +7058,15 @@
         ContentValues values = new ContentValues();
         values.put(RawContacts.SEND_TO_VOICEMAIL, "1");
         mResolver.update(rawContactUri, values, null, null);
-        assertMetadataDirty(rawContactUri, true);
+        assertMetadataDirty(rawContactUri, false);
 
         // Update the backup_id and check metadata network should be notified.
         values = new ContentValues();
         values.put(RawContacts.BACKUP_ID, "newBackupId");
         mResolver.update(rawContactUri, values, null, null);
         assertStoredValue(rawContactUri, RawContacts.BACKUP_ID, "newBackupId");
-        assertMetadataDirty(rawContactUri, true);
-        assertMetadataNetworkNotified(true);
+        assertMetadataDirty(rawContactUri, false);
+        assertMetadataNetworkNotified(false);
     }
 
     public void testMarkAsMetadataDirtyForAggregationExceptionChange() {
@@ -7082,10 +7081,10 @@
                 rawContactId1, rawContactId2);
 
         assertMetadataDirty(ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId1),
-                true);
+                false);
         assertMetadataDirty(ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId2),
-                true);
-        assertMetadataNetworkNotified(true);
+                false);
+        assertMetadataNetworkNotified(false);
     }
 
     public void testMarkAsMetadataNotDirtyForUsageStatsChange() {
@@ -7113,8 +7112,8 @@
         assertStoredValue(mailUri11, Data.IS_PRIMARY, 1);
         assertStoredValue(mailUri11, Data.IS_SUPER_PRIMARY, 1);
         assertMetadataDirty(ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId1),
-                true);
-        assertMetadataNetworkNotified(true);
+                false);
+        assertMetadataNetworkNotified(false);
     }
 
     public void testMarkAsMetadataDirtyForDataPrimarySettingUpdate() {
@@ -7133,8 +7132,8 @@
         mResolver.update(mailUri1, values, null, null);
 
         assertMetadataDirty(ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId),
-                true);
-        assertMetadataNetworkNotified(true);
+                false);
+        assertMetadataNetworkNotified(false);
     }
 
     public void testMarkAsMetadataDirtyForDataDelete() {
@@ -7148,8 +7147,8 @@
         mResolver.delete(mailUri1, null, null);
 
         assertMetadataDirty(ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId),
-                true);
-        assertMetadataNetworkNotified(true);
+                false);
+        assertMetadataNetworkNotified(false);
     }
 
     public void testDeleteContactWithoutName() {
@@ -7190,6 +7189,98 @@
         assertEquals(1, mResolver.delete(lookupUri, null, null));
     }
 
+    public void testDeleteContactComposedOfSingleLocalRawContact() {
+        // Create a raw contact in the local (null) account
+        long rawContactId = RawContactUtil.createRawContact(mResolver, null);
+        DataUtil.insertStructuredName(mResolver, rawContactId, "John", "Smith");
+
+        // Delete the contact
+        long contactId = queryContactId(rawContactId);
+        Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
+        assertEquals(1, mResolver.delete(contactUri, null, null));
+
+        // Assert that the raw contact was removed
+        Cursor c1 = queryRawContact(rawContactId);
+        assertEquals(0, c1.getCount());
+        c1.close();
+
+        // Assert that the contact was removed
+        Cursor c2 = mResolver.query(contactUri, null, null, null, "");
+        assertEquals(0, c2.getCount());
+        c2.close();
+    }
+
+    public void testDeleteContactComposedOfTwoLocalRawContacts() {
+        // Create a raw contact in the local (null) account
+        long rawContactId1 = RawContactUtil.createRawContact(mResolver, null);
+        DataUtil.insertStructuredName(mResolver, rawContactId1, "John", "Smith");
+
+        // Create another local raw contact with the same name
+        long rawContactId2 = RawContactUtil.createRawContact(mResolver, null);
+        DataUtil.insertStructuredName(mResolver, rawContactId2, "John", "Smith");
+
+        // Join the two raw contacts explicitly
+        setAggregationException(AggregationExceptions.TYPE_KEEP_TOGETHER,
+                rawContactId1, rawContactId2);
+
+        // Check that the two raw contacts are aggregated together
+        assertAggregated(rawContactId1, rawContactId2, "John Smith");
+
+        // Delete the aggregate contact
+        long contactId = queryContactId(rawContactId1);
+        Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
+        assertEquals(1, mResolver.delete(contactUri, null, null));
+
+        // Assert that both of the local raw contacts were removed completely
+        Cursor c1 = queryRawContact(rawContactId1);
+        assertEquals(0, c1.getCount());
+        c1.close();
+
+        Cursor c2 = queryRawContact(rawContactId2);
+        assertEquals(0, c2.getCount());
+        c2.close();
+
+        // Assert that the contact was removed
+        Cursor c3 = queryContact(contactId);
+        assertEquals(0, c3.getCount());
+        c3.close();
+    }
+
+    public void testDeleteContactComposedOfSomeLocalRawContacts() {
+        // Create a raw contact in the local (null) account
+        long rawContactId1 = RawContactUtil.createRawContact(mResolver, null);
+        DataUtil.insertStructuredName(mResolver, rawContactId1, "John", "Smith");
+
+        // Create another one in a non-local account with the same name
+        long rawContactId2 = RawContactUtil.createRawContact(mResolver, mAccount);
+        DataUtil.insertStructuredName(mResolver, rawContactId2, "John", "Smith");
+
+        // Check that the two new raw contacts are aggregated together
+        assertAggregated(rawContactId1, rawContactId2, "John Smith");
+
+        // Delete the aggregate contact
+        long contactId = queryContactId(rawContactId1);
+        Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
+        assertEquals(1, mResolver.delete(contactUri, null, null));
+
+        // Assert that the local raw contact was removed completely
+        Cursor c1 = queryRawContact(rawContactId1);
+        assertEquals(0, c1.getCount());
+        c1.close();
+
+        // Assert that the non-local raw contact is still present just marked as deleted
+        Cursor c2 = queryRawContact(rawContactId2);
+        assertEquals(1, c2.getCount());
+        assertTrue(c2.moveToFirst());
+        assertEquals(1, c2.getInt(c2.getColumnIndex(RawContacts.DELETED)));
+        c2.close();
+
+        // Assert that the contact was removed
+        Cursor c3 = queryContact(contactId);
+        assertEquals(0, c3.getCount());
+        c3.close();
+    }
+
     public void testQueryContactWithEscapedUri() {
         ContentValues values = new ContentValues();
         values.put(RawContacts.SOURCE_ID, "!@#$%^&*()_+=-/.,<>?;'\":[]}{\\|`~");
@@ -8889,7 +8980,7 @@
     }
 
     public void testContactDelete_marksRawContactsForDeletion() {
-        DatabaseAsserts.ContactIdPair ids = assertContactCreateDelete();
+        DatabaseAsserts.ContactIdPair ids = assertContactCreateDelete(mAccount);
 
         String[] projection = new String[]{ContactsContract.RawContacts.DIRTY,
                 ContactsContract.RawContacts.DELETED};
@@ -8903,7 +8994,7 @@
     }
 
     public void testContactDelete_checkRawContactContactId() {
-        DatabaseAsserts.ContactIdPair ids = assertContactCreateDelete();
+        DatabaseAsserts.ContactIdPair ids = assertContactCreateDelete(mAccount);
 
         String[] projection = new String[]{ContactsContract.RawContacts.CONTACT_ID};
         String[] record = RawContactUtil.queryByRawContactId(mResolver, ids.mRawContactId,
@@ -8929,9 +9020,9 @@
 
         ContactUtil.update(mResolver, ids.mContactId, values);
         assertDirty(rawContactUri, false);
-        assertMetadataDirty(rawContactUri, true);
+        assertMetadataDirty(rawContactUri, false);
         assertNetworkNotified(false);
-        assertMetadataNetworkNotified(true);
+        assertMetadataNetworkNotified(false);
     }
 
     public void testContactUpdate_updatesContactUpdatedTimestamp() {
@@ -9144,13 +9235,23 @@
     }
 
     /**
-     * Create a contact. Assert it's not present in the delete log. Delete it.
-     * And assert that the contact record is no longer present.
+     * Creates a contact in the local account. Assert it's not present in the delete log.
+     * Delete it. And assert that the contact record is no longer present.
      *
      * @return The contact id and raw contact id that was created.
      */
     private DatabaseAsserts.ContactIdPair assertContactCreateDelete() {
-        DatabaseAsserts.ContactIdPair ids = DatabaseAsserts.assertAndCreateContact(mResolver);
+        return assertContactCreateDelete(null);
+    }
+
+    /**
+     * Creates a contact in the given account. Assert it's not present in the delete log.
+     * Delete it. And assert that the contact record is no longer present.
+     * @return The contact id and raw contact id that was created.
+     */
+    private DatabaseAsserts.ContactIdPair assertContactCreateDelete(Account account) {
+        DatabaseAsserts.ContactIdPair ids = DatabaseAsserts.assertAndCreateContact(mResolver,
+                account);
 
         assertEquals(CommonDatabaseUtils.NOT_FOUND,
                 DeletedContactUtil.queryDeletedTimestampForContactId(mResolver, ids.mContactId));