From 0d0e3ce02f3ad22bd80a47a45f166fd9a3e3b853 Mon Sep 17 00:00:00 2001 From: Bill Yi Date: Tue, 12 Apr 2022 21:09:40 +0000 Subject: Import translations. DO NOT MERGE ANYWHERE Auto-generated-cl: translation import Change-Id: I73184d4ea26d9597e6cb6dc1949a705a866a0cb4 --- res/values-as/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/res/values-as/strings.xml b/res/values-as/strings.xml index 9a6ddf15..c1f5b360 100644 --- a/res/values-as/strings.xml +++ b/res/values-as/strings.xml @@ -26,7 +26,7 @@ "অন্যান্য" "ইয়াৰ পৰা অহা ভইচমেল " "সম্পৰ্কসূচীৰ ডেটাবেছ প্ৰতিলিপি কৰক" - "আপুনি এই কাৰ্যবোৰ কৰিবলৈ লৈছে ১) আটাইবোৰ সম্পৰ্ক সম্বন্ধীয় তথ্য় আৰু কল লগ সন্নিবিষ্ট থকা আপোনাৰ ডেটাবেছক আভ্য়ন্তৰীণ ষ্ট’ৰেজলৈ প্ৰতিলিপি কৰা আৰু ২) ইয়াক ইমেইল কৰা। ডিভাইচৰ পৰা সফলতাৰে প্ৰতিলিপি কৰাৰ বা ইমেইল পোৱাৰ পিছত উক্ত প্ৰতিলিপি মচিবলৈ নাপাহৰিব।" + "আপুনি এই কাৰ্যবোৰ কৰিবলৈ লৈছে ১) আটাইবোৰ সম্পৰ্ক সম্বন্ধীয় তথ্য আৰু কল লগ সন্নিবিষ্ট থকা আপোনাৰ ডেটাবেছক আভ্য়ন্তৰীণ ষ্ট’ৰেজলৈ প্ৰতিলিপি কৰা আৰু ২) ইয়াক ইমেইল কৰা। ডিভাইচৰ পৰা সফলতাৰে প্ৰতিলিপি কৰাৰ বা ইমেইল পোৱাৰ পিছত উক্ত প্ৰতিলিপি মচিবলৈ নাপাহৰিব।" "এতিয়াই মচক" "আৰম্ভ কৰক" "আপোনাৰ ফাইল পঠাবলৈ কোনো প্ৰ\'গ্ৰাম বাছনি কৰক" -- cgit v1.2.3 From 990d75e3ee2aaf9ae36f77eb6a1db6807f34c357 Mon Sep 17 00:00:00 2001 From: Bill Yi Date: Tue, 12 Apr 2022 21:10:02 +0000 Subject: Import translations. DO NOT MERGE ANYWHERE Auto-generated-cl: translation import Change-Id: Ie97005809b6c031555bc536c6294eead7f6d6b54 --- res/values-as/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/res/values-as/strings.xml b/res/values-as/strings.xml index 9a6ddf15..c1f5b360 100644 --- a/res/values-as/strings.xml +++ b/res/values-as/strings.xml @@ -26,7 +26,7 @@ "অন্যান্য" "ইয়াৰ পৰা অহা ভইচমেল " "সম্পৰ্কসূচীৰ ডেটাবেছ প্ৰতিলিপি কৰক" - "আপুনি এই কাৰ্যবোৰ কৰিবলৈ লৈছে ১) আটাইবোৰ সম্পৰ্ক সম্বন্ধীয় তথ্য় আৰু কল লগ সন্নিবিষ্ট থকা আপোনাৰ ডেটাবেছক আভ্য়ন্তৰীণ ষ্ট’ৰেজলৈ প্ৰতিলিপি কৰা আৰু ২) ইয়াক ইমেইল কৰা। ডিভাইচৰ পৰা সফলতাৰে প্ৰতিলিপি কৰাৰ বা ইমেইল পোৱাৰ পিছত উক্ত প্ৰতিলিপি মচিবলৈ নাপাহৰিব।" + "আপুনি এই কাৰ্যবোৰ কৰিবলৈ লৈছে ১) আটাইবোৰ সম্পৰ্ক সম্বন্ধীয় তথ্য আৰু কল লগ সন্নিবিষ্ট থকা আপোনাৰ ডেটাবেছক আভ্য়ন্তৰীণ ষ্ট’ৰেজলৈ প্ৰতিলিপি কৰা আৰু ২) ইয়াক ইমেইল কৰা। ডিভাইচৰ পৰা সফলতাৰে প্ৰতিলিপি কৰাৰ বা ইমেইল পোৱাৰ পিছত উক্ত প্ৰতিলিপি মচিবলৈ নাপাহৰিব।" "এতিয়াই মচক" "আৰম্ভ কৰক" "আপোনাৰ ফাইল পঠাবলৈ কোনো প্ৰ\'গ্ৰাম বাছনি কৰক" -- cgit v1.2.3 From 4214594346608061f4f5cb02ed2752b90a78d330 Mon Sep 17 00:00:00 2001 From: Varun Shah Date: Mon, 18 Apr 2022 16:15:12 +0000 Subject: Fix NumberFormatException in CallLogProvider. Change-Id: Iaa1febf36297d8ab6f72c6a80b5811d0d7b35044 Fixes: 229124902 Test: atest CallLogProviderTest --- src/com/android/providers/contacts/CallLogProvider.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/com/android/providers/contacts/CallLogProvider.java b/src/com/android/providers/contacts/CallLogProvider.java index 948adaa4..d7f8dd5c 100644 --- a/src/com/android/providers/contacts/CallLogProvider.java +++ b/src/com/android/providers/contacts/CallLogProvider.java @@ -1282,8 +1282,13 @@ public class CallLogProvider extends ContentProvider { adjustForNewPhoneAccountInternal((PhoneAccountHandle) arg); } else if (task == BACKGROUND_TASK_MIGRATE_PHONE_ACCOUNT_HANDLES) { PhoneAccountHandle phoneAccountHandle = (PhoneAccountHandle) arg; - String iccId = mSubscriptionManager.getActiveSubscriptionInfo( + String iccId = null; + try { + iccId = mSubscriptionManager.getActiveSubscriptionInfo( Integer.parseInt(phoneAccountHandle.getId())).getIccId(); + } catch (NumberFormatException nfe) { + // Ignore the exception, iccId will remain null and be handled below. + } if (iccId == null) { Log.i(TAG, "ACTION_PHONE_ACCOUNT_REGISTERED received null IccId."); } else { -- cgit v1.2.3 From 732508b8ac09b1aab82a7b24a0e2ecef308b72ea Mon Sep 17 00:00:00 2001 From: Dongzhuo Zhang Date: Fri, 20 May 2022 23:14:04 +0000 Subject: Change the CP2 logging to be correct order. The order of the fields must match the one in atoms.proto Bug: b/233238488 Change-Id: Ia6ab165453298f4a45ad4cad001a93a546afafc1 --- src/com/android/providers/contacts/util/LogUtils.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/com/android/providers/contacts/util/LogUtils.java b/src/com/android/providers/contacts/util/LogUtils.java index 368411c7..a2301006 100644 --- a/src/com/android/providers/contacts/util/LogUtils.java +++ b/src/com/android/providers/contacts/util/LogUtils.java @@ -54,6 +54,8 @@ public class LogUtils { private static final int STATSD_LOG_ATOM_ID = 301; + // The write methods must be called in the same order as the order of fields in the + // atom (frameworks/proto_logging/stats/atoms.proto) definition. public static void log(LogFields logFields) { StatsLog.write(StatsEvent.newBuilder() .setAtomId(STATSD_LOG_ATOM_ID) @@ -61,9 +63,9 @@ public class LogUtils { .writeInt(logFields.getUriType()) .writeInt(getCallerType(logFields.isCallerIsSyncAdapter())) .writeInt(getResultType(logFields.getException())) - .writeInt(logFields.getTaskType()) .writeInt(logFields.getResultCount()) .writeLong(getLatencyMicros(logFields.getStartNanos())) + .writeInt(logFields.getTaskType()) .usePooledBuffer() .build()); } @@ -89,5 +91,3 @@ public class LogUtils { return (SystemClock.elapsedRealtimeNanos() - startNanos) / 1000; } } - - -- cgit v1.2.3 From fbbc6578553ded2648070ca801a418f110fc8b4e Mon Sep 17 00:00:00 2001 From: Bill Yi Date: Thu, 26 May 2022 22:06:33 -0700 Subject: Import translations. DO NOT MERGE ANYWHERE Auto-generated-cl: translation import Change-Id: Ic0d0d6368257d3d19b7888226191ec9f5fb2e196 --- res/values-pt-rBR/strings.xml | 2 +- res/values-pt/strings.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/res/values-pt-rBR/strings.xml b/res/values-pt-rBR/strings.xml index 3ffd077f..5cee4f36 100644 --- a/res/values-pt-rBR/strings.xml +++ b/res/values-pt-rBR/strings.xml @@ -26,7 +26,7 @@ "Outros" "Correio de voz de " "Copiar banco de dados de contatos" - "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." + "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 ligações 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." "Excluir agora" "Iniciar" "Escolha um programa para enviar o arquivo" diff --git a/res/values-pt/strings.xml b/res/values-pt/strings.xml index 3ffd077f..5cee4f36 100644 --- a/res/values-pt/strings.xml +++ b/res/values-pt/strings.xml @@ -26,7 +26,7 @@ "Outros" "Correio de voz de " "Copiar banco de dados de contatos" - "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." + "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 ligações 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." "Excluir agora" "Iniciar" "Escolha um programa para enviar o arquivo" -- cgit v1.2.3 From 5c515c907ba0dbc435e625be5836eff01292f1c1 Mon Sep 17 00:00:00 2001 From: Bill Yi Date: Tue, 31 May 2022 08:19:09 -0700 Subject: Import translations. DO NOT MERGE ANYWHERE Auto-generated-cl: translation import Change-Id: I6762ebd97a5532f0c784b25a5b0466d2d78030f9 --- res/values-pt-rBR/strings.xml | 2 +- res/values-pt/strings.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/res/values-pt-rBR/strings.xml b/res/values-pt-rBR/strings.xml index 3ffd077f..5cee4f36 100644 --- a/res/values-pt-rBR/strings.xml +++ b/res/values-pt-rBR/strings.xml @@ -26,7 +26,7 @@ "Outros" "Correio de voz de " "Copiar banco de dados de contatos" - "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." + "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 ligações 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." "Excluir agora" "Iniciar" "Escolha um programa para enviar o arquivo" diff --git a/res/values-pt/strings.xml b/res/values-pt/strings.xml index 3ffd077f..5cee4f36 100644 --- a/res/values-pt/strings.xml +++ b/res/values-pt/strings.xml @@ -26,7 +26,7 @@ "Outros" "Correio de voz de " "Copiar banco de dados de contatos" - "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." + "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 ligações 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." "Excluir agora" "Iniciar" "Escolha um programa para enviar o arquivo" -- cgit v1.2.3 From 7db0751c663ab74b2b704c72bedde4a8ed421440 Mon Sep 17 00:00:00 2001 From: Varun Shah Date: Fri, 10 Jun 2022 15:52:25 -0700 Subject: Fix and Update ContactDirectionManagerTest. Fixes testGetDirectoryProviderPackages() inconsistencies. Bug: 204577394 Test: ContactDirectoryManagerTest#testGetDirectoryProviderPackages Change-Id: I52995672314d2216fc56c29c4621c372f5c84aab --- .../contacts/ContactDirectoryManagerTest.java | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/tests/src/com/android/providers/contacts/ContactDirectoryManagerTest.java b/tests/src/com/android/providers/contacts/ContactDirectoryManagerTest.java index fca00afc..a4165ce5 100644 --- a/tests/src/com/android/providers/contacts/ContactDirectoryManagerTest.java +++ b/tests/src/com/android/providers/contacts/ContactDirectoryManagerTest.java @@ -16,8 +16,6 @@ package com.android.providers.contacts; -import static com.android.providers.contacts.ContactsActor.PACKAGE_GREY; - import android.accounts.Account; import android.content.ContentValues; import android.content.Context; @@ -38,10 +36,15 @@ import android.test.mock.MockContentProvider; import android.test.suitebuilder.annotation.MediumTest; import android.util.Log; +import androidx.test.platform.app.InstrumentationRegistry; + import com.android.providers.contacts.ContactsDatabaseHelper.AggregationExceptionColumns; import com.google.android.collect.Lists; +import java.util.Arrays; +import java.util.Set; + /** * Unit tests for {@link ContactDirectoryManager}. Run the test like this: * @@ -634,8 +637,17 @@ public class ContactDirectoryManagerTest extends BaseContactsProvider2Test { return; } + try { + InstrumentationRegistry.getInstrumentation().getUiAutomation() + .executeShellCommand("am wait-for-broadcast-idle"); + Thread.sleep(1000); // wait for the system + } catch (Exception ignored) { } + // If installed, getDirectoryProviderPackages() should return it. - assertTrue(ContactDirectoryManager.getDirectoryProviderPackages(pm).contains(googleSync)); + Set dirProviderPackages = ContactDirectoryManager.getDirectoryProviderPackages(pm); + assertTrue(googleSync + " package not found in the list of directory provider packages: " + + Arrays.toString(dirProviderPackages.toArray()), + dirProviderPackages.contains(googleSync)); } protected PackageInfo createProviderPackage(String packageName, String authority) { -- cgit v1.2.3 From f26a8c9f944c096dbcdbf10e89857fca228b80ad Mon Sep 17 00:00:00 2001 From: Grace Jia Date: Fri, 8 Jul 2022 10:42:59 -0700 Subject: Catch SQLiteCantOpenDatabaseException to avoid system app crash. Bug: 237994662 Test: atest CallLogProviderTest Change-Id: Ieb003618a86cd7fb1beff47061e5be3c860d9bfb --- src/com/android/providers/contacts/CallLogDatabaseHelper.java | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/com/android/providers/contacts/CallLogDatabaseHelper.java b/src/com/android/providers/contacts/CallLogDatabaseHelper.java index c5052d70..0b29d991 100644 --- a/src/com/android/providers/contacts/CallLogDatabaseHelper.java +++ b/src/com/android/providers/contacts/CallLogDatabaseHelper.java @@ -21,6 +21,7 @@ import android.content.Context; import android.content.SharedPreferences; import android.database.Cursor; import android.database.DatabaseUtils; +import android.database.sqlite.SQLiteCantOpenDatabaseException; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; import android.preference.PreferenceManager; @@ -621,9 +622,15 @@ public class CallLogDatabaseHelper { } @VisibleForTesting - @Nullable // We return null during tests when migration is not needed. + @Nullable // We return null during tests when migration is not needed or database + // is unavailable. SQLiteDatabase getContactsWritableDatabaseForMigration() { - return ContactsDatabaseHelper.getInstance(mContext).getWritableDatabase(); + try { + return ContactsDatabaseHelper.getInstance(mContext).getWritableDatabase(); + } catch (SQLiteCantOpenDatabaseException e) { + Log.i(TAG, "Exception caught during opening database for migration: " + e); + return null; + } } public PhoneAccountHandleMigrationUtils getPhoneAccountHandleMigrationUtils() { -- cgit v1.2.3 From ae50af44179249505db40b85d4c4ada327ca5bcc Mon Sep 17 00:00:00 2001 From: Bill Yi Date: Thu, 14 Jul 2022 16:16:15 -0700 Subject: Import translations. DO NOT MERGE ANYWHERE Auto-generated-cl: translation import Change-Id: I906bf40d0fe2aa6d028b8edf551b1f4f8edddc39 --- res/values-my/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/res/values-my/strings.xml b/res/values-my/strings.xml index 02cb4d54..1efa6e93 100644 --- a/res/values-my/strings.xml +++ b/res/values-my/strings.xml @@ -26,7 +26,7 @@ "တစ်ခြား" "မှ အသံစာ " "လိပ်စာဒေတာဘေ့စ်ကို ကူးရန်" - "သင့်အနေဖြင့် ၁) လိပ်စာအားလုံးနဲ့ဆိုင်သော အချက်အလက်များ၊ ဖုန်းခေါ်မှု မှတ်တမ်းများ ပါဝင်သော ဒေတာဘေ့စ်ကို စက်အတွင်းသိမ်းဆည်းသော ကော်ပီတစ်ခု ပြုလုပ်ပြီး ၂) အီးမေးလ်ပို့ခြင်း လုပ်တော့မည် ဖြစ်ပါသည်။ ဒီကော်ပီကို စက်ထဲမှ ထုတ်ပြီးပြီးချင်း သို့မဟုတ် အီးမေးလ်ရောက်ရှိပြီးချင်းချင်း ဖျက်ပစ်ဖို့ မမေ့ပါနှင့်။" + "သင်သည် ၁) အဆက်အသွယ်အားလုံးနှင့်ဆိုင်သော အချက်အလက်များ၊ ဖုန်းခေါ်မှု မှတ်တမ်းများ ပါဝင်သည့် ဒေတာဘေ့စ်ကို စက်တွင်းသိုလှောင်ခန်းသို့ မိတ္တူကူးပြီး ၂) အီးမေးလ်ပို့ပါတော့မည်။ စက်တွင်းမှ မိတ္တူကူးယူပြီးသည်နှင့်၊ သို့မဟုတ် အီးမေးလ်ရောက်ရှိသည်နှင့် ဤမိတ္တူကို ဖျက်ရန် မမေ့ပါနှင့်။" "အခုဖျက်ပါ" "စတင်ရန်" "သင့်ဖိုင်အား ပို့ရန် ပရိုဂရမ် ရွေးပါ" -- cgit v1.2.3 From cc38473324abc06e3f300698fb68c9ba55d9e54b Mon Sep 17 00:00:00 2001 From: Bill Yi Date: Thu, 14 Jul 2022 16:16:46 -0700 Subject: Import translations. DO NOT MERGE ANYWHERE Auto-generated-cl: translation import Change-Id: I644cb5df466dd6a56e9d38f3630082c3c7889c9c --- res/values-my/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/res/values-my/strings.xml b/res/values-my/strings.xml index 02cb4d54..1efa6e93 100644 --- a/res/values-my/strings.xml +++ b/res/values-my/strings.xml @@ -26,7 +26,7 @@ "တစ်ခြား" "မှ အသံစာ " "လိပ်စာဒေတာဘေ့စ်ကို ကူးရန်" - "သင့်အနေဖြင့် ၁) လိပ်စာအားလုံးနဲ့ဆိုင်သော အချက်အလက်များ၊ ဖုန်းခေါ်မှု မှတ်တမ်းများ ပါဝင်သော ဒေတာဘေ့စ်ကို စက်အတွင်းသိမ်းဆည်းသော ကော်ပီတစ်ခု ပြုလုပ်ပြီး ၂) အီးမေးလ်ပို့ခြင်း လုပ်တော့မည် ဖြစ်ပါသည်။ ဒီကော်ပီကို စက်ထဲမှ ထုတ်ပြီးပြီးချင်း သို့မဟုတ် အီးမေးလ်ရောက်ရှိပြီးချင်းချင်း ဖျက်ပစ်ဖို့ မမေ့ပါနှင့်။" + "သင်သည် ၁) အဆက်အသွယ်အားလုံးနှင့်ဆိုင်သော အချက်အလက်များ၊ ဖုန်းခေါ်မှု မှတ်တမ်းများ ပါဝင်သည့် ဒေတာဘေ့စ်ကို စက်တွင်းသိုလှောင်ခန်းသို့ မိတ္တူကူးပြီး ၂) အီးမေးလ်ပို့ပါတော့မည်။ စက်တွင်းမှ မိတ္တူကူးယူပြီးသည်နှင့်၊ သို့မဟုတ် အီးမေးလ်ရောက်ရှိသည်နှင့် ဤမိတ္တူကို ဖျက်ရန် မမေ့ပါနှင့်။" "အခုဖျက်ပါ" "စတင်ရန်" "သင့်ဖိုင်အား ပို့ရန် ပရိုဂရမ် ရွေးပါ" -- cgit v1.2.3 From 297c0ce0db249a795cbab840564960b3d3fc334f Mon Sep 17 00:00:00 2001 From: Bill Yi Date: Sat, 15 Oct 2022 19:19:45 -0700 Subject: Import translations. DO NOT MERGE ANYWHERE Auto-generated-cl: translation import Change-Id: I5bf7c79f123ef617e2d248ef6528c064c4dc3b34 --- res/values-as/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/res/values-as/strings.xml b/res/values-as/strings.xml index c1f5b360..dff8c453 100644 --- a/res/values-as/strings.xml +++ b/res/values-as/strings.xml @@ -26,7 +26,7 @@ "অন্যান্য" "ইয়াৰ পৰা অহা ভইচমেল " "সম্পৰ্কসূচীৰ ডেটাবেছ প্ৰতিলিপি কৰক" - "আপুনি এই কাৰ্যবোৰ কৰিবলৈ লৈছে ১) আটাইবোৰ সম্পৰ্ক সম্বন্ধীয় তথ্য আৰু কল লগ সন্নিবিষ্ট থকা আপোনাৰ ডেটাবেছক আভ্য়ন্তৰীণ ষ্ট’ৰেজলৈ প্ৰতিলিপি কৰা আৰু ২) ইয়াক ইমেইল কৰা। ডিভাইচৰ পৰা সফলতাৰে প্ৰতিলিপি কৰাৰ বা ইমেইল পোৱাৰ পিছত উক্ত প্ৰতিলিপি মচিবলৈ নাপাহৰিব।" + "আপুনি এই কাৰ্যবোৰ কৰিবলৈ লৈছে ১) আটাইবোৰ সম্পৰ্ক সম্বন্ধীয় তথ্য আৰু কল লগ সন্নিবিষ্ট থকা আপোনাৰ ডেটাবেছক আভ্য়ন্তৰীণ ষ্ট’ৰেজলৈ প্ৰতিলিপি কৰা আৰু ২) ইয়াক ইমেইল কৰা। ডিভাইচৰ পৰা সফলতাৰে প্ৰতিলিপি কৰাৰ বা ইমেইল পোৱাৰ পাছত উক্ত প্ৰতিলিপি মচিবলৈ নাপাহৰিব।" "এতিয়াই মচক" "আৰম্ভ কৰক" "আপোনাৰ ফাইল পঠাবলৈ কোনো প্ৰ\'গ্ৰাম বাছনি কৰক" -- cgit v1.2.3 From 275669a290d2d5c29a2bd18aeb476d1983a95622 Mon Sep 17 00:00:00 2001 From: Bill Yi Date: Sat, 15 Oct 2022 19:20:04 -0700 Subject: Import translations. DO NOT MERGE ANYWHERE Auto-generated-cl: translation import Change-Id: I33bf62f6e96643843afc094ae70788015ac1a0a1 --- res/values-as/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/res/values-as/strings.xml b/res/values-as/strings.xml index c1f5b360..dff8c453 100644 --- a/res/values-as/strings.xml +++ b/res/values-as/strings.xml @@ -26,7 +26,7 @@ "অন্যান্য" "ইয়াৰ পৰা অহা ভইচমেল " "সম্পৰ্কসূচীৰ ডেটাবেছ প্ৰতিলিপি কৰক" - "আপুনি এই কাৰ্যবোৰ কৰিবলৈ লৈছে ১) আটাইবোৰ সম্পৰ্ক সম্বন্ধীয় তথ্য আৰু কল লগ সন্নিবিষ্ট থকা আপোনাৰ ডেটাবেছক আভ্য়ন্তৰীণ ষ্ট’ৰেজলৈ প্ৰতিলিপি কৰা আৰু ২) ইয়াক ইমেইল কৰা। ডিভাইচৰ পৰা সফলতাৰে প্ৰতিলিপি কৰাৰ বা ইমেইল পোৱাৰ পিছত উক্ত প্ৰতিলিপি মচিবলৈ নাপাহৰিব।" + "আপুনি এই কাৰ্যবোৰ কৰিবলৈ লৈছে ১) আটাইবোৰ সম্পৰ্ক সম্বন্ধীয় তথ্য আৰু কল লগ সন্নিবিষ্ট থকা আপোনাৰ ডেটাবেছক আভ্য়ন্তৰীণ ষ্ট’ৰেজলৈ প্ৰতিলিপি কৰা আৰু ২) ইয়াক ইমেইল কৰা। ডিভাইচৰ পৰা সফলতাৰে প্ৰতিলিপি কৰাৰ বা ইমেইল পোৱাৰ পাছত উক্ত প্ৰতিলিপি মচিবলৈ নাপাহৰিব।" "এতিয়াই মচক" "আৰম্ভ কৰক" "আপোনাৰ ফাইল পঠাবলৈ কোনো প্ৰ\'গ্ৰাম বাছনি কৰক" -- cgit v1.2.3 From 99175a2d960ad49d16d396da9f6d8c720ff6d178 Mon Sep 17 00:00:00 2001 From: Kunal Malhotra Date: Mon, 17 Oct 2022 21:24:45 +0000 Subject: Adding in permission check for directory queries done in CP2 to prevent directories without permissions from accessing GAL data Test: manual testing done on phone by creating a work profile and revoking permissions and via CTS atest packages/providers/ContactsProvider/ atest CtsContactsProviderTestCases atest CtsDevicePolicyManagerTestCases Bug: b/161166785 Change-Id: I569adc492386932f7b30eb817a0f3cb69b3d1206 --- .../providers/contacts/ContactsProvider2.java | 28 ++++++++++++++++++---- .../contacts/ContactDirectoryManagerTest.java | 18 ++++++++++++++ .../contacts/ContactsMockPackageManager.java | 5 ++++ 3 files changed, 47 insertions(+), 4 deletions(-) diff --git a/src/com/android/providers/contacts/ContactsProvider2.java b/src/com/android/providers/contacts/ContactsProvider2.java index 3f61a15a..9fbed3c0 100644 --- a/src/com/android/providers/contacts/ContactsProvider2.java +++ b/src/com/android/providers/contacts/ContactsProvider2.java @@ -18,11 +18,12 @@ package com.android.providers.contacts; import static android.Manifest.permission.INTERACT_ACROSS_USERS; import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL; +import static android.Manifest.permission.READ_CALL_LOG; +import static android.Manifest.permission.WRITE_CONTACTS; import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static com.android.providers.contacts.util.PhoneAccountHandleMigrationUtils.TELEPHONY_COMPONENT_NAME; -import android.os.Looper; import android.accounts.Account; import android.accounts.AccountManager; import android.accounts.OnAccountsUpdateListener; @@ -69,6 +70,7 @@ import android.os.Build; import android.os.Bundle; import android.os.CancellationSignal; import android.os.Handler; +import android.os.Looper; import android.os.ParcelFileDescriptor; import android.os.ParcelFileDescriptor.AutoCloseInputStream; import android.os.RemoteException; @@ -179,7 +181,6 @@ import com.android.providers.contacts.util.DbQueryUtils; import com.android.providers.contacts.util.LogFields; import com.android.providers.contacts.util.LogUtils; import com.android.providers.contacts.util.NeededForTesting; -import com.android.providers.contacts.util.PhoneAccountHandleMigrationUtils; import com.android.providers.contacts.util.UserUtils; import com.android.vcard.VCardComposer; import com.android.vcard.VCardConfig; @@ -1355,6 +1356,7 @@ public class ContactsProvider2 extends AbstractContactsProvider String authority; String accountName; String accountType; + String packageName; } /** @@ -4212,7 +4214,6 @@ public class ContactsProvider2 extends AbstractContactsProvider @Override protected int updateInTransaction( Uri uri, ContentValues values, String selection, String[] selectionArgs) { - if (VERBOSE_LOGGING) { Log.v(TAG, "updateInTransaction: uri=" + uri + " selection=[" + selection + "] args=" + Arrays.toString(selectionArgs) + @@ -5760,6 +5761,22 @@ public class ContactsProvider2 extends AbstractContactsProvider " Caller=" + getCallingPackage() + " User=" + UserUtils.getCurrentUserHandle(getContext())); } + final String packageName = directoryInfo.packageName; + // enforce permissions + final int queryType = sUriMatcher.match(uri); + final PackageManager pm = getContext().getPackageManager(); + if (queryType == PHONE_LOOKUP || queryType == PHONES_FILTER) { + if (pm.checkPermission(READ_CALL_LOG, packageName) != PERMISSION_GRANTED) { + Log.w(TAG, "Package " + packageName + + " does not have permission for phone lookup queries."); + return null; + } + } + if (pm.checkPermission(WRITE_CONTACTS, packageName) != PERMISSION_GRANTED) { + Log.w(TAG, "Package " + packageName + + " does not have permission for contact lookup queries."); + return null; + } cursor = getContext().getContentResolver().query( directoryUri, projection, selection, selectionArgs, sortOrder); if (cursor == null) { @@ -5862,7 +5879,8 @@ public class ContactsProvider2 extends AbstractContactsProvider Directory._ID, Directory.DIRECTORY_AUTHORITY, Directory.ACCOUNT_NAME, - Directory.ACCOUNT_TYPE + Directory.ACCOUNT_TYPE, + Directory.PACKAGE_NAME, }; public static final int DIRECTORY_ID = 0; @@ -5888,6 +5906,8 @@ public class ContactsProvider2 extends AbstractContactsProvider info.authority = cursor.getString(DirectoryQuery.AUTHORITY); info.accountName = cursor.getString(DirectoryQuery.ACCOUNT_NAME); info.accountType = cursor.getString(DirectoryQuery.ACCOUNT_TYPE); + info.packageName = + cursor.getString(cursor.getColumnIndex(Directory.PACKAGE_NAME)); mDirectoryCache.put(id, info); } } finally { diff --git a/tests/src/com/android/providers/contacts/ContactDirectoryManagerTest.java b/tests/src/com/android/providers/contacts/ContactDirectoryManagerTest.java index a4165ce5..7e8a7ad5 100644 --- a/tests/src/com/android/providers/contacts/ContactDirectoryManagerTest.java +++ b/tests/src/com/android/providers/contacts/ContactDirectoryManagerTest.java @@ -27,6 +27,7 @@ import android.database.Cursor; import android.database.MatrixCursor; import android.net.Uri; import android.os.Bundle; +import android.os.Process; import android.provider.ContactsContract; import android.provider.ContactsContract.AggregationExceptions; import android.provider.ContactsContract.Contacts; @@ -551,6 +552,11 @@ public class ContactDirectoryManagerTest extends BaseContactsProvider2Test { mPackageManager.setInstalledPackages( Lists.newArrayList(createProviderPackage("test.package1", "authority1"), createPackage("test.packageX", "authorityX", false))); +// mPackageManager.grantRuntimePermission("test.package1", "android.permission.WRITE_CONTACTS", +// Process.myUserHandle()); +// mPackageManager.grantRuntimePermission("test.package1", "android.permission.READ_CALL_LOG", +// Process.myUserHandle()); + MockContactDirectoryProvider provider1 = (MockContactDirectoryProvider) addProvider( MockContactDirectoryProvider.class, "authority1"); @@ -585,12 +591,20 @@ public class ContactDirectoryManagerTest extends BaseContactsProvider2Test { assertEquals("account-name1", cursor.getString(cursor.getColumnIndex("accountName"))); assertEquals("account-type1", cursor.getString(cursor.getColumnIndex("accountType"))); cursor.close(); +// mPackageManager.revokeRuntimePermission("test.package1", "android.permission.WRITE_CONTACTS", +// Process.myUserHandle()); +// mPackageManager.revokeRuntimePermission("test.package1", "android.permission.READ_CALL_LOG", +// Process.myUserHandle()); } public void testProjectionPopulated() throws Exception { mPackageManager.setInstalledPackages( Lists.newArrayList(createProviderPackage("test.package1", "authority1"), createPackage("test.packageX", "authorityX", false))); +// mPackageManager.grantRuntimePermission("test.package1", "android.permission.WRITE_CONTACTS", +// Process.myUserHandle()); +// mPackageManager.grantRuntimePermission("test.package1", "android.permission.READ_CALL_LOG", +// Process.myUserHandle()); MockContactDirectoryProvider provider1 = (MockContactDirectoryProvider) addProvider( MockContactDirectoryProvider.class, "authority1"); @@ -619,6 +633,10 @@ public class ContactDirectoryManagerTest extends BaseContactsProvider2Test { AggregationExceptions.RAW_CONTACT_ID1, AggregationExceptions.RAW_CONTACT_ID2, }); +// mPackageManager.revokeRuntimePermission("test.package1", "android.permission.WRITE_CONTACTS", +// Process.myUserHandle()); +// mPackageManager.revokeRuntimePermission("test.package1", "android.permission.READ_CALL_LOG", +// Process.myUserHandle()); } /** diff --git a/tests/src/com/android/providers/contacts/ContactsMockPackageManager.java b/tests/src/com/android/providers/contacts/ContactsMockPackageManager.java index a9420dda..fea63b23 100644 --- a/tests/src/com/android/providers/contacts/ContactsMockPackageManager.java +++ b/tests/src/com/android/providers/contacts/ContactsMockPackageManager.java @@ -149,4 +149,9 @@ public class ContactsMockPackageManager extends MockPackageManager { } return ret; } + + @Override + public int checkPermission(String permission, String packageName) { + return PERMISSION_GRANTED; + } } -- cgit v1.2.3 From 1f31ba7df111d695a82a1d66aa46975c9cae8c96 Mon Sep 17 00:00:00 2001 From: Jigar Thakkar Date: Tue, 25 Oct 2022 17:43:01 +0000 Subject: Disable writes for clone CP2 The clone CP2 should not store any contacts of its own but redirect all calls to the parent profile CP2. This CL disables writes for the clone CP2. All the write queries to the clone CP2 would return empty results - insert would return a fake uri while all other queries would return "0" to indicate that no changes have been made as the result of the calls. Bug: b/253449539 Test: atest ContactsProviderTests Change-Id: I13baabf8f4791dc909d56b9797d017f5eb0ba4f8 --- .../providers/contacts/ContactsProvider2.java | 46 +++++++++++++++++++++- .../android/providers/contacts/util/UserUtils.java | 9 +++++ .../android/providers/contacts/ContactsActor.java | 7 ++++ 3 files changed, 61 insertions(+), 1 deletion(-) diff --git a/src/com/android/providers/contacts/ContactsProvider2.java b/src/com/android/providers/contacts/ContactsProvider2.java index 3f61a15a..fb5b30fd 100644 --- a/src/com/android/providers/contacts/ContactsProvider2.java +++ b/src/com/android/providers/contacts/ContactsProvider2.java @@ -179,7 +179,6 @@ import com.android.providers.contacts.util.DbQueryUtils; import com.android.providers.contacts.util.LogFields; import com.android.providers.contacts.util.LogUtils; import com.android.providers.contacts.util.NeededForTesting; -import com.android.providers.contacts.util.PhoneAccountHandleMigrationUtils; import com.android.providers.contacts.util.UserUtils; import com.android.vcard.VCardComposer; import com.android.vcard.VCardConfig; @@ -2305,6 +2304,12 @@ public class ContactsProvider2 extends AbstractContactsProvider mContactsHelper.validateContentValues(getCallingPackage(), values); + if (!areContactWritesEnabled()) { + // Returning fake uri since the insert was rejected + Log.w(TAG, "Blocked insert with uri [" + uri + "]. Contact writes not enabled " + + "for the user"); + return rejectInsert(uri, values); + } if (mapsToProfileDbWithInsertedValues(uri, values)) { switchToProfileMode(); resultUri = mProfileProvider.insert(uri, values); @@ -2339,6 +2344,12 @@ public class ContactsProvider2 extends AbstractContactsProvider mContactsHelper.validateContentValues(getCallingPackage(), values); mContactsHelper.validateSql(getCallingPackage(), selection); + if (!areContactWritesEnabled()) { + // Returning 0, no rows were updated as writes are disabled + Log.w(TAG, "Blocked update with uri [" + uri + "]. Contact writes not enabled " + + "for the user"); + return 0; + } if (mapsToProfileDb(uri)) { switchToProfileMode(); updates = mProfileProvider.update(uri, values, selection, selectionArgs); @@ -2370,6 +2381,12 @@ public class ContactsProvider2 extends AbstractContactsProvider mContactsHelper.validateSql(getCallingPackage(), selection); + if (!areContactWritesEnabled()) { + // Returning 0, no rows were deleted as writes are disabled + Log.w(TAG, "Blocked delete with uri [" + uri + "]. Contact writes not enabled " + + "for the user"); + return 0; + } if (mapsToProfileDb(uri)) { switchToProfileMode(); deletes = mProfileProvider.delete(uri, selection, selectionArgs); @@ -2389,6 +2406,11 @@ public class ContactsProvider2 extends AbstractContactsProvider @Override public Bundle call(String method, String arg, Bundle extras) { waitForAccess(mReadAccessLatch); + if (!areContactWritesEnabled()) { + // Returning EMPTY Bundle since the call was rejected and no rows were affected + Log.w(TAG, "Blocked call to method [" + method + "], not enabled for the user"); + return Bundle.EMPTY; + } switchToContactMode(); if (Authorization.AUTHORIZATION_METHOD.equals(method)) { Uri uri = extras.getParcelable(Authorization.KEY_URI_TO_AUTHORIZE); @@ -2607,6 +2629,12 @@ public class ContactsProvider2 extends AbstractContactsProvider @Override public int bulkInsert(Uri uri, ContentValues[] values) { waitForAccess(mWriteAccessLatch); + if (!areContactWritesEnabled()) { + // Returning 0, no rows were affected since writes are disabled + Log.w(TAG, "Blocked bulkInsert with uri [" + uri + "]. Contact writes not enabled " + + "for the user"); + return 0; + } return super.bulkInsert(uri, values); } @@ -5655,6 +5683,17 @@ public class ContactsProvider2 extends AbstractContactsProvider || context.checkCallingPermission(INTERACT_ACROSS_USERS) == PERMISSION_GRANTED; } + /** + * Returns true if the contact writes are enabled for the current instance of ContactsProvider. + * The ContactsProvider instance running in the clone profile should block inserts, updates + * and deletes and hence should return false. + */ + private boolean areContactWritesEnabled() { + // TODO(b/253449368) - The condition below should only be checked behind the app-cloning + // feature flag + return !UserUtils.shouldUseParentsContacts(getContext()); + } + private Cursor queryDirectoryIfNecessary(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder, CancellationSignal cancellationSignal) { String directory = getQueryParameter(uri, ContactsContract.DIRECTORY_PARAM_KEY); @@ -8702,6 +8741,11 @@ public class ContactsProvider2 extends AbstractContactsProvider public AssetFileDescriptor openAssetFile(Uri uri, String mode) throws FileNotFoundException { boolean success = false; try { + if (!mode.equals("r") && !areContactWritesEnabled()) { + Log.w(TAG, "Blocked openAssetFile with uri [" + uri + "]. Contact writes not " + + "enabled for the user"); + return null; + } if (!isDirectoryParamValid(uri)){ return null; } diff --git a/src/com/android/providers/contacts/util/UserUtils.java b/src/com/android/providers/contacts/util/UserUtils.java index 31ea41aa..54fc8de3 100644 --- a/src/com/android/providers/contacts/util/UserUtils.java +++ b/src/com/android/providers/contacts/util/UserUtils.java @@ -17,9 +17,11 @@ package com.android.providers.contacts.util; import com.android.providers.contacts.ContactsProvider2; +import android.annotation.SuppressLint; import android.app.admin.DevicePolicyManager; import android.content.Context; import android.content.pm.UserInfo; +import android.content.pm.UserProperties; import android.os.UserManager; import android.util.Log; @@ -78,4 +80,11 @@ public final class UserUtils { final UserInfo ui = getCorpUserInfo(context); return ui == null ? -1 : ui.id; } + + @SuppressLint("AndroidFrameworkRequiresPermission") + public static boolean shouldUseParentsContacts(Context context) { + final UserManager userManager = getUserManager(context); + final UserProperties userProperties = userManager.getUserProperties(context.getUser()); + return userProperties.getUseParentsContacts(); + } } diff --git a/tests/src/com/android/providers/contacts/ContactsActor.java b/tests/src/com/android/providers/contacts/ContactsActor.java index e3c606e2..639380fb 100644 --- a/tests/src/com/android/providers/contacts/ContactsActor.java +++ b/tests/src/com/android/providers/contacts/ContactsActor.java @@ -25,6 +25,7 @@ import android.accounts.AccountManagerFuture; import android.accounts.AuthenticatorException; import android.accounts.OnAccountsUpdateListener; import android.accounts.OperationCanceledException; +import android.annotation.NonNull; import android.content.ContentProvider; import android.content.ContentResolver; import android.content.ContentUris; @@ -37,6 +38,7 @@ import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.pm.ProviderInfo; import android.content.pm.UserInfo; +import android.content.pm.UserProperties; import android.content.res.Configuration; import android.content.res.Resources; import android.database.Cursor; @@ -253,6 +255,11 @@ public class ContactsActor { public boolean isUserRunning(int userId) { return true; } + + @Override + public UserProperties getUserProperties(@NonNull UserHandle userHandle) { + return new UserProperties.Builder().build(); + } } private MockTelephonyManager mMockTelephonyManager; -- cgit v1.2.3 From e7b04b1b4197b0ef5b8f8dd89b4e2abee2ecf0f8 Mon Sep 17 00:00:00 2001 From: Jigar Thakkar Date: Thu, 3 Nov 2022 15:19:27 +0000 Subject: Add tests for ContactsProvider in clone profile The CP2 in the clone profile has a different behaviour than the one in the primary profile. This change adds support for testing the clone CP2 behaviour to ensure writes done through this provider are disabled. Bug: 253449539 Test: atest CloneContactsProvider2Test Change-Id: Id36349211df4c42d080766987b6cacac0209e0e8 --- .../providers/contacts/ContactsProvider2.java | 3 +- .../contacts/BaseContactsProvider2Test.java | 19 ++ .../contacts/CloneContactsProvider2Test.java | 282 +++++++++++++++++++++ .../android/providers/contacts/ContactsActor.java | 22 ++ .../providers/contacts/ContactsProvider2Test.java | 20 -- .../providers/contacts/util/UserUtilsTest.java | 33 +++ 6 files changed, 358 insertions(+), 21 deletions(-) create mode 100644 tests/src/com/android/providers/contacts/CloneContactsProvider2Test.java diff --git a/src/com/android/providers/contacts/ContactsProvider2.java b/src/com/android/providers/contacts/ContactsProvider2.java index fb5b30fd..d6b1cbe6 100644 --- a/src/com/android/providers/contacts/ContactsProvider2.java +++ b/src/com/android/providers/contacts/ContactsProvider2.java @@ -5688,7 +5688,8 @@ public class ContactsProvider2 extends AbstractContactsProvider * The ContactsProvider instance running in the clone profile should block inserts, updates * and deletes and hence should return false. */ - private boolean areContactWritesEnabled() { + @VisibleForTesting + protected boolean areContactWritesEnabled() { // TODO(b/253449368) - The condition below should only be checked behind the app-cloning // feature flag return !UserUtils.shouldUseParentsContacts(getContext()); diff --git a/tests/src/com/android/providers/contacts/BaseContactsProvider2Test.java b/tests/src/com/android/providers/contacts/BaseContactsProvider2Test.java index 54984d29..20d6a15e 100644 --- a/tests/src/com/android/providers/contacts/BaseContactsProvider2Test.java +++ b/tests/src/com/android/providers/contacts/BaseContactsProvider2Test.java @@ -1381,6 +1381,25 @@ public abstract class BaseContactsProvider2Test extends PhotoLoadingTestCase { assertEquals(timeStamp, time); } + /** + * Asserts the equality of two Uri objects, ignoring the order of the query parameters. + */ + protected static void assertUriEquals(Uri expected, Uri actual) { + assertEquals(expected.getScheme(), actual.getScheme()); + assertEquals(expected.getAuthority(), actual.getAuthority()); + assertEquals(expected.getPath(), actual.getPath()); + assertEquals(expected.getFragment(), actual.getFragment()); + Set expectedParameterNames = expected.getQueryParameterNames(); + Set actualParameterNames = actual.getQueryParameterNames(); + assertEquals(expectedParameterNames.size(), actualParameterNames.size()); + assertTrue(expectedParameterNames.containsAll(actualParameterNames)); + for (String parameterName : expectedParameterNames) { + assertEquals(expected.getQueryParameter(parameterName), + actual.getQueryParameter(parameterName)); + } + + } + protected void setTimeForTest(Long time) { Uri uri = Calls.CONTENT_URI.buildUpon() .appendQueryParameter(CallLogProvider.PARAM_KEY_QUERY_FOR_TESTING, "1") diff --git a/tests/src/com/android/providers/contacts/CloneContactsProvider2Test.java b/tests/src/com/android/providers/contacts/CloneContactsProvider2Test.java new file mode 100644 index 00000000..18c6cb76 --- /dev/null +++ b/tests/src/com/android/providers/contacts/CloneContactsProvider2Test.java @@ -0,0 +1,282 @@ +/* + * Copyright (C) 2022 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.ContactsActor.MockUserManager.CLONE_PROFILE_USER; + +import android.content.ContentProviderOperation; +import android.content.ContentProviderResult; +import android.content.ContentUris; +import android.content.ContentValues; +import android.content.OperationApplicationException; +import android.database.Cursor; +import android.net.Uri; +import android.os.Build; +import android.os.Bundle; +import android.os.RemoteException; +import android.provider.ContactsContract; +import androidx.test.filters.MediumTest; +import androidx.test.filters.SdkSuppress; + +import java.util.ArrayList; + +@MediumTest +@SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake") +public class CloneContactsProvider2Test extends BaseContactsProvider2Test { + + private ContactsActor mCloneContactsActor; + + private SynchronousContactsProvider2 getCloneContactsProvider() { + return (SynchronousContactsProvider2) mCloneContactsActor.provider; + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + mCloneContactsActor = new ContactsActor( + new ContactsActor.AlteringUserContext(getContext(), CLONE_PROFILE_USER.id), + getContextPackageName(), SynchronousContactsProvider2.class, getAuthority()); + mActor.mockUserManager.setUsers(ContactsActor.MockUserManager.PRIMARY_USER, + CLONE_PROFILE_USER); + mCloneContactsActor.mockUserManager.setUsers(ContactsActor.MockUserManager.PRIMARY_USER, + CLONE_PROFILE_USER); + mCloneContactsActor.mockUserManager.myUser = CLONE_PROFILE_USER.id; + getCloneContactsProvider().wipeData(); + } + + private ContentValues getSampleContentValues() { + ContentValues values = new ContentValues(); + values.put(ContactsContract.RawContacts.ACCOUNT_NAME, "test@test.com"); + values.put(ContactsContract.RawContacts.ACCOUNT_TYPE, "test.com"); + values.put(ContactsContract.RawContacts.CUSTOM_RINGTONE, "custom"); + values.put(ContactsContract.RawContacts.STARRED, "1"); + return values; + } + + private void assertEqualContentValues(ContentValues contentValues, Cursor cursor) { + assertEquals(contentValues.get(ContactsContract.RawContacts.ACCOUNT_NAME), + cursor.getString(cursor.getColumnIndex(ContactsContract.RawContacts.ACCOUNT_NAME))); + assertEquals(contentValues.get(ContactsContract.RawContacts.ACCOUNT_TYPE), + cursor.getString(cursor.getColumnIndex(ContactsContract.RawContacts.ACCOUNT_TYPE))); + assertEquals(contentValues.get(ContactsContract.RawContacts.CUSTOM_RINGTONE), + cursor.getString(cursor.getColumnIndex( + ContactsContract.RawContacts.CUSTOM_RINGTONE))); + assertEquals(contentValues.get(ContactsContract.RawContacts.STARRED), + cursor.getString(cursor.getColumnIndex( + ContactsContract.RawContacts.STARRED))); + } + + private void assertRejectedApplyBatchResults(ContentProviderResult[] res, + ArrayList ops) { + assertEquals(ops.size(), res.length); + for (int i = 0;i < ops.size();i++) { + Uri expectedUri = ops.get(i).getUri() + .buildUpon() + .appendPath("0") + .build(); + assertUriEquals(expectedUri, res[i].uri); + } + } + + /** + * Asserts that no contacts are returned when queried by the given contacts actor + */ + private void assertContactsProviderEmpty(ContactsActor contactsActor) { + Cursor cursor = contactsActor.resolver.query(ContactsContract.RawContacts.CONTENT_URI, + new String[]{ContactsContract.RawContactsEntity._ID}, + null /* queryArgs */, null /* cancellationSignal */); + assertNotNull(cursor); + assertEquals(cursor.getCount(), 0); + } + + private long insertRawContactsThroughPrimaryProvider(ContentValues values) { + Uri resultUri = mActor.resolver.insert(ContactsContract.RawContacts.CONTENT_URI, + values); + assertNotNull(resultUri); + return ContentUris.parseId(resultUri); + } + + public void testAreContactWritesEnabled() { + // Check that writes are disabled for clone CP2 + ContactsProvider2 cloneContactsProvider = + (ContactsProvider2) mCloneContactsActor.provider; + assertFalse(cloneContactsProvider.areContactWritesEnabled()); + + // Check that writes are enabled for primary CP2 + ContactsProvider2 primaryContactsProvider = (ContactsProvider2) getProvider(); + assertTrue(primaryContactsProvider.areContactWritesEnabled()); + } + + public void testCloneContactsProviderInsert() { + Uri resultUri = + mCloneContactsActor.resolver.insert(ContactsContract.RawContacts.CONTENT_URI, + getSampleContentValues()); + + // Here we expect a fakeUri returned to fail silently + Uri expectedUri = ContactsContract.RawContacts.CONTENT_URI.buildUpon() + .appendPath("0") + .build(); + assertUriEquals(expectedUri, resultUri); + assertContactsProviderEmpty(mCloneContactsActor); + } + + public void testPrimaryContactsProviderInsert() { + ContentValues inputContentValues = getSampleContentValues(); + long rawContactId = insertRawContactsThroughPrimaryProvider(inputContentValues); + Cursor cursor = mActor.resolver.query(ContentUris.withAppendedId( + ContactsContract.RawContacts.CONTENT_URI, rawContactId), + null /* projection */, null /* queryArgs */, null /* cancellationSignal */); + assertNotNull(cursor); + assertEquals(1, cursor.getCount()); + assertTrue(cursor.moveToFirst()); + assertEquals(rawContactId, + cursor.getLong(cursor.getColumnIndex(ContactsContract.RawContacts._ID))); + assertEqualContentValues(inputContentValues, cursor); + } + + public void testCloneContactsProviderUpdate() { + // Insert contact through the primary clone provider + ContentValues inputContentValues = getSampleContentValues(); + long rawContactId = insertRawContactsThroughPrimaryProvider(inputContentValues); + + // Update display name in the input content values + ContentValues updatedContentValues = getSampleContentValues(); + updatedContentValues.put(ContactsContract.RawContacts.STARRED, + "0"); + updatedContentValues.put(ContactsContract.RawContacts.CUSTOM_RINGTONE, + "beethoven5"); + + // Call clone contacts provider update method to update the raw contact inserted earlier + int updateResult = mCloneContactsActor.resolver.update( + ContentUris.withAppendedId(ContactsContract.RawContacts.CONTENT_URI, rawContactId), + updatedContentValues, null /* extras */); + + // Check results, no rows should have been affected + assertEquals(0, updateResult); + + // Check values associated with rawContactId by querying the database + Cursor cursor = mActor.resolver.query(ContentUris.withAppendedId( + ContactsContract.RawContacts.CONTENT_URI, rawContactId), + null /* projection */, null /* queryArgs */, null /* cancellationSignal */); + assertNotNull(cursor); + assertEquals(1, cursor.getCount()); + assertTrue(cursor.moveToFirst()); + assertEqualContentValues(inputContentValues, cursor); + } + + public void testCloneContactsProviderDelete() { + // Insert contact through the primary clone provider + ContentValues inputContentValues = getSampleContentValues(); + long rawContactId = insertRawContactsThroughPrimaryProvider(inputContentValues); + + // Delete the inserted row through clone provider + int deleteResult = mCloneContactsActor.resolver.delete( + ContentUris.withAppendedId(ContactsContract.RawContacts.CONTENT_URI, rawContactId), + null); + + // Check results, no rows should have been affected + assertEquals(0, deleteResult); + + // Check that contact is present in the primary CP2 database + Cursor cursor = mActor.resolver.query(ContentUris.withAppendedId( + ContactsContract.RawContacts.CONTENT_URI, rawContactId), + null /* projection */, null /* queryArgs */, null /* cancellationSignal */); + assertNotNull(cursor); + assertEquals(1, cursor.getCount()); + assertTrue(cursor.moveToFirst()); + assertEqualContentValues(inputContentValues, cursor); + } + + public void testCloneContactsProviderBulkInsert() { + int bulkInsertResult = + mCloneContactsActor.resolver.bulkInsert(ContactsContract.RawContacts.CONTENT_URI, + new ContentValues[]{ getSampleContentValues() }); + + // Check results, no rows should have been affected + assertEquals(0, bulkInsertResult); + assertContactsProviderEmpty(mCloneContactsActor); + } + + public void testCloneContactsApplyBatch() + throws RemoteException, OperationApplicationException { + ArrayList ops = new ArrayList<>(); + + ops.add(ContentProviderOperation.newInsert(ContactsContract.RawContacts.CONTENT_URI) + .withValue(ContactsContract.RawContacts.ACCOUNT_TYPE, null /* value */) + .withValue(ContactsContract.RawContacts.ACCOUNT_NAME, null /* value */).build()); + + // Phone Number + ops.add(ContentProviderOperation + .newInsert(ContactsContract.Data.CONTENT_URI) + .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0) + .withValue(ContactsContract.Data.MIMETYPE, + ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE) + .withValue(ContactsContract.CommonDataKinds.Phone.NUMBER, "7XXXXXXXXXX") + .withValue(ContactsContract.Data.MIMETYPE, + ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE) + .withValue(ContactsContract.CommonDataKinds.Phone.TYPE, "1").build()); + + // Display name/Contact name + ops.add(ContentProviderOperation + .newInsert(ContactsContract.Data.CONTENT_URI) + .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0) + .withValue(ContactsContract.Data.MIMETYPE, + ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE) + .withValue(ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME, "Name") + .build()); + + // Check results, fake uris should be returned for each of the insert operation + ContentProviderResult[] res = mCloneContactsActor.resolver.applyBatch( + ContactsContract.AUTHORITY, ops); + assertRejectedApplyBatchResults(res, ops); + + // No contacts should be present in both clone and primary providers + assertContactsProviderEmpty(mCloneContactsActor); + assertContactsProviderEmpty(mActor); + } + + public void testCloneContactsCallOperation() { + // Query Account Operation + Bundle response = mCloneContactsActor.resolver.call(ContactsContract.AUTHORITY_URI, + ContactsContract.Settings.QUERY_DEFAULT_ACCOUNT_METHOD, null /* arg */, + null /* extras */); + assertNotNull(response); + assertEquals(Bundle.EMPTY, response); + + // Set account operation + Bundle bundle = new Bundle(); + bundle.putString(ContactsContract.Settings.ACCOUNT_NAME, "test@test.com"); + bundle.putString(ContactsContract.Settings.ACCOUNT_TYPE, "test.com"); + Bundle setAccountResponse = + mCloneContactsActor.resolver.call(ContactsContract.AUTHORITY_URI, + ContactsContract.Settings.SET_DEFAULT_ACCOUNT_METHOD, null /* arg */, bundle); + assertNotNull(setAccountResponse); + assertEquals(Bundle.EMPTY, response); + + // Authorization URI + Uri testUri = ContentUris.withAppendedId(ContactsContract.Contacts.CONTENT_URI, 1); + final Bundle uriBundle = new Bundle(); + uriBundle.putParcelable(ContactsContract.Authorization.KEY_URI_TO_AUTHORIZE, testUri); + final Bundle authResponse = mCloneContactsActor.resolver.call( + ContactsContract.AUTHORITY_URI, + ContactsContract.Authorization.AUTHORIZATION_METHOD, + null /* arg */, + uriBundle); + assertNotNull(authResponse); + assertEquals(Bundle.EMPTY, authResponse); + } +} diff --git a/tests/src/com/android/providers/contacts/ContactsActor.java b/tests/src/com/android/providers/contacts/ContactsActor.java index 639380fb..0d7d9b3b 100644 --- a/tests/src/com/android/providers/contacts/ContactsActor.java +++ b/tests/src/com/android/providers/contacts/ContactsActor.java @@ -16,6 +16,10 @@ package com.android.providers.contacts; +import static android.content.pm.UserProperties.SHOW_IN_LAUNCHER_WITH_PARENT; +import static com.android.providers.contacts.ContactsActor.MockUserManager.CLONE_PROFILE_USER; +import static com.android.providers.contacts.ContactsActor.MockUserManager.PRIMARY_USER; + import static org.mockito.Mockito.when; import android.accounts.Account; @@ -172,6 +176,8 @@ public class ContactsActor { public static final UserInfo CORP_USER = createUserInfo("corp", 10, 0, UserInfo.FLAG_MANAGED_PROFILE); public static final UserInfo SECONDARY_USER = createUserInfo("2nd", 11, 11, 0); + public static final UserInfo CLONE_PROFILE_USER = createUserInfo("clone", 12, 0, + UserInfo.FLAG_PROFILE); /** "My" user. Set it to change the current user. */ public int myUser = DEFAULT_USER_ID; @@ -258,6 +264,13 @@ public class ContactsActor { @Override public UserProperties getUserProperties(@NonNull UserHandle userHandle) { + if (CLONE_PROFILE_USER.getUserHandle().equals(userHandle)) { + return new UserProperties.Builder() + .setUseParentsContacts(true) + .setShowInLauncher(SHOW_IN_LAUNCHER_WITH_PARENT) + .setStartWithParent(true) + .build(); + } return new UserProperties.Builder().build(); } } @@ -417,6 +430,15 @@ public class ContactsActor { } } + @Override + public UserHandle getUser() { + if (mockUserManager != null && + mockUserManager.getProcessUserId() == CLONE_PROFILE_USER.id) { + return CLONE_PROFILE_USER.getUserHandle(); + } + return PRIMARY_USER.getUserHandle(); + } + @Override public void sendBroadcast(Intent intent, String receiverPermission) { // Ignore. diff --git a/tests/src/com/android/providers/contacts/ContactsProvider2Test.java b/tests/src/com/android/providers/contacts/ContactsProvider2Test.java index 09ea19fc..b625ac4a 100644 --- a/tests/src/com/android/providers/contacts/ContactsProvider2Test.java +++ b/tests/src/com/android/providers/contacts/ContactsProvider2Test.java @@ -9908,24 +9908,4 @@ public class ContactsProvider2Test extends BaseContactsProvider2Test { } return false; } - - - /** - * Asserts the equality of two Uri objects, ignoring the order of the query parameters. - */ - public static void assertUriEquals(Uri expected, Uri actual) { - assertEquals(expected.getScheme(), actual.getScheme()); - assertEquals(expected.getAuthority(), actual.getAuthority()); - assertEquals(expected.getPath(), actual.getPath()); - assertEquals(expected.getFragment(), actual.getFragment()); - Set expectedParameterNames = expected.getQueryParameterNames(); - Set actualParameterNames = actual.getQueryParameterNames(); - assertEquals(expectedParameterNames.size(), actualParameterNames.size()); - assertTrue(expectedParameterNames.containsAll(actualParameterNames)); - for (String parameterName : expectedParameterNames) { - assertEquals(expected.getQueryParameter(parameterName), - actual.getQueryParameter(parameterName)); - } - - } } diff --git a/tests/src/com/android/providers/contacts/util/UserUtilsTest.java b/tests/src/com/android/providers/contacts/util/UserUtilsTest.java index 93613cf5..072df377 100644 --- a/tests/src/com/android/providers/contacts/util/UserUtilsTest.java +++ b/tests/src/com/android/providers/contacts/util/UserUtilsTest.java @@ -77,5 +77,38 @@ public class UserUtilsTest extends FixedAndroidTestCase { um.myUser = MockUserManager.SECONDARY_USER.id; assertEquals(-1, UserUtils.getCorpUserId(c)); + + // Primary + clone + corp + um.setUsers(MockUserManager.PRIMARY_USER, MockUserManager.CLONE_PROFILE_USER, + MockUserManager.CORP_USER); + + um.myUser = MockUserManager.PRIMARY_USER.id; + assertEquals(MockUserManager.CORP_USER.id, UserUtils.getCorpUserId(c)); + + um.myUser = MockUserManager.CLONE_PROFILE_USER.id; + assertEquals(-1, UserUtils.getCorpUserId(c)); + + um.myUser = MockUserManager.CORP_USER.id; + assertEquals(-1, UserUtils.getCorpUserId(c)); + } + + public void testShouldUseParentsContacts() { + final Context c = mActor.getProviderContext(); + final MockUserManager um = mActor.mockUserManager; + + um.setUsers(MockUserManager.PRIMARY_USER, MockUserManager.SECONDARY_USER, + MockUserManager.CLONE_PROFILE_USER, MockUserManager.CORP_USER); + + um.myUser = MockUserManager.PRIMARY_USER.id; + assertFalse(UserUtils.shouldUseParentsContacts(c)); + + um.myUser = MockUserManager.SECONDARY_USER.id; + assertFalse(UserUtils.shouldUseParentsContacts(c)); + + um.myUser = MockUserManager.CORP_USER.id; + assertFalse(UserUtils.shouldUseParentsContacts(c)); + + um.myUser = MockUserManager.CLONE_PROFILE_USER.id; + assertTrue(UserUtils.shouldUseParentsContacts(c)); } } -- cgit v1.2.3 From af4cf9743543fc7627068d0087b92e9dcb0b8c78 Mon Sep 17 00:00:00 2001 From: Julien Desprez Date: Mon, 12 Dec 2022 09:15:29 -0800 Subject: Update preparers to align across everything Carry the exact same options. Test: presubmit Bug: 261855411 Change-Id: Icdc854215deba14127039e867557bd9df65c8784 --- tests/AndroidTest.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/AndroidTest.xml b/tests/AndroidTest.xml index cdf0c7da..967614cc 100644 --- a/tests/AndroidTest.xml +++ b/tests/AndroidTest.xml @@ -14,7 +14,7 @@ limitations under the License. --> - + -- cgit v1.2.3 From e77b23fad8090764a4477aa21ada57fc5112cdff Mon Sep 17 00:00:00 2001 From: Jigar Thakkar Date: Mon, 21 Nov 2022 17:57:40 +0000 Subject: Redirect queries from clone to parent ContactsProvider This change adds support to forward the contacts provider query from clone user profile to the contacts provider in the parent profile. The cross-profile query would only be allowed for calls coming from apps that are cloned by the users. Test: atest ContactsProvider2Test Bug: 259964585 Change-Id: Id78e7662cf16aac221e3b0d0ea412c737476bb7d --- .../providers/contacts/ContactsProvider2.java | 126 +++++++++++++++++++-- .../android/providers/contacts/util/UserUtils.java | 44 ++++++- 2 files changed, 158 insertions(+), 12 deletions(-) diff --git a/src/com/android/providers/contacts/ContactsProvider2.java b/src/com/android/providers/contacts/ContactsProvider2.java index 2b90f8bc..d2d1e169 100644 --- a/src/com/android/providers/contacts/ContactsProvider2.java +++ b/src/com/android/providers/contacts/ContactsProvider2.java @@ -27,6 +27,7 @@ import static com.android.providers.contacts.util.PhoneAccountHandleMigrationUti import android.accounts.Account; import android.accounts.AccountManager; import android.accounts.OnAccountsUpdateListener; +import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.WorkerThread; import android.app.AppOpsManager; @@ -48,6 +49,7 @@ import android.content.UriMatcher; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.ProviderInfo; +import android.content.pm.UserInfo; import android.content.res.AssetFileDescriptor; import android.content.res.Resources; import android.content.res.Resources.NotFoundException; @@ -5620,10 +5622,17 @@ public class ContactsProvider2 extends AbstractContactsProvider // If caller does not come from same profile, Check if it's privileged or allowed by // enterprise policy - if (!queryAllowedByEnterprisePolicy(uri)) { + if (!isCrossUserQueryAllowed(uri)) { return null; } + // TODO(b/253449368) - The call below should be gated by the app-cloning feature flag. + if (UserUtils.shouldUseParentsContacts(getContext()) && + isAppAllowedToUseParentUsersContacts(getCallingPackage())) { + return queryParentProfileContactsProvider(uri, projection, selection, selectionArgs, + sortOrder, cancellationSignal); + } + // Query the profile DB if appropriate. if (mapsToProfileDb(uri)) { switchToProfileMode(); @@ -5643,7 +5652,21 @@ public class ContactsProvider2 extends AbstractContactsProvider } } - private boolean queryAllowedByEnterprisePolicy(Uri uri) { + private boolean isAppAllowedToUseParentUsersContacts(@Nullable String packageName) { + try { + for (String appName: getContext().getResources() + .getStringArray(com.android.internal.R.array.cloneable_apps)) { + if (packageName != null && packageName.equals(appName)) { + return true; + } + } + } catch (NotFoundException nfe) { + Log.w(TAG, "Resource corresponding to list of cloneable apps not found"); + } + return false; + } + + private boolean isCrossUserQueryAllowed(Uri uri) { if (isCallerFromSameUser()) { // Caller is on the same user; query allowed. return true; @@ -5651,12 +5674,21 @@ public class ContactsProvider2 extends AbstractContactsProvider 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.) + // But for now we don't allow it. (We never allowed 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 + // The caller is the other CP2 (which has INTERACT_ACROSS_USERS), meaning the request // is on behalf of a "real" client app. + + // TODO(b/253449368) - The condition below should only be checked behind the app-cloning + // feature flag + if (doesCallingProviderUseCurrentUsersContacts()) { + // The caller is the other CP2 (which has INTERACT_ACROSS_USERS), from the child + // user (of the current user) profile with the property of using parent's contacts + // set. + return true; + } // Consult the enterprise policy. return mEnterprisePolicyGuard.isCrossProfileAllowed(uri); } @@ -5667,6 +5699,22 @@ public class ContactsProvider2 extends AbstractContactsProvider return UserHandle.getUserId(Binder.getCallingUid()) == UserHandle.myUserId(); } + /** + * Returns true if calling contacts provider instance uses current users contacts. + * This can happen when the current user is the parent of the calling user and the calling user + * has the corresponding user property to use parent's contacts set. Please note that this + * cross-profile contact access will only be allowed if the call is redirected from the child + * user's CP2. + */ + private boolean doesCallingProviderUseCurrentUsersContacts() { + UserHandle callingUserHandle = UserHandle.getUserHandleForUid(Binder.getCallingUid()); + UserHandle currentUserHandle = android.os.Process.myUserHandle(); + boolean isCallerFromSameUser = callingUserHandle.equals(currentUserHandle); + return isCallerFromSameUser || + (UserUtils.shouldUseParentsContacts(getContext(), callingUserHandle) && + UserUtils.isParentUser(getContext(), currentUserHandle, callingUserHandle)); + } + /** * Returns true if called by a different user's CP2. */ @@ -5862,11 +5910,7 @@ public class ContactsProvider2 extends AbstractContactsProvider return createEmptyCursor(localUri, projection); } // Make sure authority is CP2 not other providers - if (!ContactsContract.AUTHORITY.equals(localUri.getAuthority())) { - Log.w(TAG, "Invalid authority: " + localUri.getAuthority()); - throw new IllegalArgumentException( - "Authority " + localUri.getAuthority() + " is not a valid CP2 authority."); - } + validateAuthority(localUri.getAuthority()); // Add the "user-id @" to the URI, and also pass the caller package name. final Uri remoteUri = maybeAddUserId(localUri, corpUserId).buildUpon() .appendQueryParameter(Directory.CALLER_PACKAGE_PARAM_KEY, getCallingPackage()) @@ -5879,6 +5923,58 @@ public class ContactsProvider2 extends AbstractContactsProvider return cursor; } + private Uri getParentProviderUri(Uri uri, @NonNull UserInfo parentUserInfo) { + // Add the "user-id @" of the parent to the URI + final Builder remoteUriBuilder = + maybeAddUserId(uri, parentUserInfo.getUserHandle().getIdentifier()) + .buildUpon(); + // Pass the caller package name query param and build the uri + return remoteUriBuilder + .appendQueryParameter(Directory.CALLER_PACKAGE_PARAM_KEY, + getRealCallerPackageName(uri)) + .build(); + } + + protected AssetFileDescriptor openAssetFileThroughParentProvider(Uri uri, String mode) + throws FileNotFoundException { + final UserInfo parentUserInfo = UserUtils.getProfileParentUser(getContext()); + if (parentUserInfo == null) { + return null; + } + validateAuthority(uri.getAuthority()); + final Uri remoteUri = getParentProviderUri(uri, parentUserInfo); + return getContext().getContentResolver().openAssetFile(remoteUri, mode, null); + } + + /** + * A helper function to query parent CP2, should only be called from users that are allowed to + * use parents contacts + */ + private Cursor queryParentProfileContactsProvider(Uri uri, String[] projection, + String selection, String[] selectionArgs, String sortOrder, + CancellationSignal cancellationSignal) { + final UserInfo parentUserInfo = UserUtils.getProfileParentUser(getContext()); + if (parentUserInfo == null) { + return createEmptyCursor(uri, projection); + } + final Uri remoteUri = getParentProviderUri(uri, parentUserInfo); + Cursor cursor = getContext().getContentResolver().query(remoteUri, projection, selection, + selectionArgs, sortOrder, cancellationSignal); + if (cursor == null) { + Log.w(TAG, "null cursor returned from primary CP2"); + return createEmptyCursor(uri, projection); + } + return cursor; + } + + private void validateAuthority(String authority) { + if (!ContactsContract.AUTHORITY.equals(authority)) { + Log.w(TAG, "Invalid authority: " + authority); + throw new IllegalArgumentException( + "Authority " + authority + " is not a valid CP2 authority."); + } + } + private Cursor addSnippetExtrasToCursor(Uri uri, Cursor cursor) { // If the cursor doesn't contain a snippet column, don't bother wrapping it. @@ -8771,10 +8867,20 @@ public class ContactsProvider2 extends AbstractContactsProvider if (!isDirectoryParamValid(uri)){ return null; } - if (!queryAllowedByEnterprisePolicy(uri)) { + if (!isCrossUserQueryAllowed(uri)) { return null; } waitForAccess(mode.equals("r") ? mReadAccessLatch : mWriteAccessLatch); + + // Redirect reads to parent provider if the corresponding user property is set and app + // is allow-listed to access parent's contacts + // TODO(b/253449368) - The call below should be gated by the app-cloning feature flag + if (mode.equals("r") && + UserUtils.shouldUseParentsContacts(getContext()) && + isAppAllowedToUseParentUsersContacts(getCallingPackage())) { + return openAssetFileThroughParentProvider(uri, mode); + } + final AssetFileDescriptor ret; if (mapsToProfileDb(uri)) { switchToProfileMode(); diff --git a/src/com/android/providers/contacts/util/UserUtils.java b/src/com/android/providers/contacts/util/UserUtils.java index 54fc8de3..effe8abe 100644 --- a/src/com/android/providers/contacts/util/UserUtils.java +++ b/src/com/android/providers/contacts/util/UserUtils.java @@ -22,6 +22,7 @@ import android.app.admin.DevicePolicyManager; import android.content.Context; import android.content.pm.UserInfo; import android.content.pm.UserProperties; +import android.os.UserHandle; import android.os.UserManager; import android.util.Log; @@ -83,8 +84,47 @@ public final class UserUtils { @SuppressLint("AndroidFrameworkRequiresPermission") public static boolean shouldUseParentsContacts(Context context) { + try { + final UserManager userManager = getUserManager(context); + final UserProperties userProperties = userManager.getUserProperties(context.getUser()); + return userProperties.getUseParentsContacts(); + } catch (IllegalArgumentException e) { + Log.w(TAG, "Trying to fetch user properties for non-existing/partial user " + + context.getUser()); + return false; + } + } + + @SuppressLint("AndroidFrameworkRequiresPermission") + public static boolean shouldUseParentsContacts(Context context, UserHandle userHandle) { + try { + final UserManager userManager = getUserManager(context); + final UserProperties userProperties = userManager.getUserProperties(userHandle); + return userProperties.getUseParentsContacts(); + } catch (IllegalArgumentException e) { + Log.w(TAG, "Trying to fetch user properties for non-existing/partial user " + + userHandle); + return false; + } + } + + /** + * Checks if the input profile user is the parent of the other user + * @return True if user1 is the parent profile of user2, false otherwise + */ + @SuppressLint("AndroidFrameworkRequiresPermission") + public static boolean isParentUser(Context context, UserHandle user1, UserHandle user2) { + if (user1 == null || user2 == null) return false; + final UserManager userManager = getUserManager(context); + UserInfo parentUserInfo = userManager.getProfileParent(user2.getIdentifier()); + return parentUserInfo != null + && parentUserInfo.getUserHandle() != null + && parentUserInfo.getUserHandle().equals(user1); + } + + @SuppressLint("AndroidFrameworkRequiresPermission") + public static UserInfo getProfileParentUser(Context context) { final UserManager userManager = getUserManager(context); - final UserProperties userProperties = userManager.getUserProperties(context.getUser()); - return userProperties.getUseParentsContacts(); + return userManager.getProfileParent(context.getUserId()); } } -- cgit v1.2.3 From a5fbbbda6c950365ff56ddfa5f9f5bb9f8b5bc2f Mon Sep 17 00:00:00 2001 From: Jigar Thakkar Date: Wed, 23 Nov 2022 02:47:44 +0000 Subject: Add unit tests for clone ContactsProvider reads Test: atest CloneContactsProvider2Tests Bug: 257967994 Change-Id: I643a97041c86ad27ec78195405f143593e5ec2d5 --- .../providers/contacts/ContactsProvider2.java | 25 ++- .../contacts/BaseContactsProvider2Test.java | 67 +++++++ .../contacts/CloneContactsProvider2Test.java | 195 +++++++++++++++++++-- .../providers/contacts/ContactsProvider2Test.java | 65 ------- .../providers/contacts/util/UserUtilsTest.java | 45 +++++ 5 files changed, 315 insertions(+), 82 deletions(-) diff --git a/src/com/android/providers/contacts/ContactsProvider2.java b/src/com/android/providers/contacts/ContactsProvider2.java index d2d1e169..e7c00f86 100644 --- a/src/com/android/providers/contacts/ContactsProvider2.java +++ b/src/com/android/providers/contacts/ContactsProvider2.java @@ -5652,7 +5652,8 @@ public class ContactsProvider2 extends AbstractContactsProvider } } - private boolean isAppAllowedToUseParentUsersContacts(@Nullable String packageName) { + @VisibleForTesting + protected boolean isAppAllowedToUseParentUsersContacts(@Nullable String packageName) { try { for (String appName: getContext().getResources() .getStringArray(com.android.internal.R.array.cloneable_apps)) { @@ -5745,7 +5746,8 @@ public class ContactsProvider2 extends AbstractContactsProvider return !UserUtils.shouldUseParentsContacts(getContext()); } - private Cursor queryDirectoryIfNecessary(Uri uri, String[] projection, String selection, + @VisibleForTesting + protected Cursor queryDirectoryIfNecessary(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder, CancellationSignal cancellationSignal) { String directory = getQueryParameter(uri, ContactsContract.DIRECTORY_PARAM_KEY); final long directoryId = @@ -5950,16 +5952,18 @@ public class ContactsProvider2 extends AbstractContactsProvider * A helper function to query parent CP2, should only be called from users that are allowed to * use parents contacts */ - private Cursor queryParentProfileContactsProvider(Uri uri, String[] projection, + @VisibleForTesting + protected Cursor queryParentProfileContactsProvider(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder, CancellationSignal cancellationSignal) { final UserInfo parentUserInfo = UserUtils.getProfileParentUser(getContext()); if (parentUserInfo == null) { return createEmptyCursor(uri, projection); } - final Uri remoteUri = getParentProviderUri(uri, parentUserInfo); - Cursor cursor = getContext().getContentResolver().query(remoteUri, projection, selection, - selectionArgs, sortOrder, cancellationSignal); + // Make sure authority is CP2 not other providers + validateAuthority(uri.getAuthority()); + Cursor cursor = queryContactsProviderForUser(uri, projection, selection, selectionArgs, + sortOrder, cancellationSignal, parentUserInfo); if (cursor == null) { Log.w(TAG, "null cursor returned from primary CP2"); return createEmptyCursor(uri, projection); @@ -5967,6 +5971,15 @@ public class ContactsProvider2 extends AbstractContactsProvider return cursor; } + @VisibleForTesting + protected Cursor queryContactsProviderForUser(Uri uri, String[] projection, String selection, + String[] selectionArgs, String sortOrder, CancellationSignal cancellationSignal, + UserInfo parentUserInfo) { + final Uri remoteUri = getParentProviderUri(uri, parentUserInfo); + return getContext().getContentResolver().query(remoteUri, projection, selection, + selectionArgs, sortOrder, cancellationSignal); + } + private void validateAuthority(String authority) { if (!ContactsContract.AUTHORITY.equals(authority)) { Log.w(TAG, "Invalid authority: " + authority); diff --git a/tests/src/com/android/providers/contacts/BaseContactsProvider2Test.java b/tests/src/com/android/providers/contacts/BaseContactsProvider2Test.java index 20d6a15e..e8920530 100644 --- a/tests/src/com/android/providers/contacts/BaseContactsProvider2Test.java +++ b/tests/src/com/android/providers/contacts/BaseContactsProvider2Test.java @@ -70,6 +70,8 @@ import com.android.providers.contacts.util.Hex; import com.android.providers.contacts.util.MockClock; import com.google.android.collect.Sets; +import java.io.FileInputStream; +import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.BitSet; @@ -1419,6 +1421,71 @@ public abstract class BaseContactsProvider2Test extends PhotoLoadingTestCase { getContactsProvider().getProfileProviderForTest().getDatabaseHelper(), values); } + protected class VCardTestUriCreator { + private String mLookup1; + private String mLookup2; + + public VCardTestUriCreator(String lookup1, String lookup2) { + super(); + mLookup1 = lookup1; + mLookup2 = lookup2; + } + + public Uri getUri1() { + return Uri.withAppendedPath(Contacts.CONTENT_VCARD_URI, mLookup1); + } + + public Uri getUri2() { + return Uri.withAppendedPath(Contacts.CONTENT_VCARD_URI, mLookup2); + } + + public Uri getCombinedUri() { + return Uri.withAppendedPath(Contacts.CONTENT_MULTI_VCARD_URI, + Uri.encode(mLookup1 + ":" + mLookup2)); + } + } + + protected VCardTestUriCreator createVCardTestContacts() { + final long rawContactId1 = RawContactUtil.createRawContact(mResolver, mAccount, + RawContacts.SOURCE_ID, "4:12"); + DataUtil.insertStructuredName(mResolver, rawContactId1, "John", "Doe"); + + final long rawContactId2 = RawContactUtil.createRawContact(mResolver, mAccount, + RawContacts.SOURCE_ID, "3:4%121"); + DataUtil.insertStructuredName(mResolver, rawContactId2, "Jane", "Doh"); + + final long contactId1 = queryContactId(rawContactId1); + final long contactId2 = queryContactId(rawContactId2); + final Uri contact1Uri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId1); + final Uri contact2Uri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId2); + final String lookup1 = + Uri.encode(Contacts.getLookupUri(mResolver, contact1Uri).getPathSegments().get(2)); + final String lookup2 = + Uri.encode(Contacts.getLookupUri(mResolver, contact2Uri).getPathSegments().get(2)); + return new VCardTestUriCreator(lookup1, lookup2); + } + + protected String readToEnd(FileInputStream inputStream) { + try { + System.out.println("DECLARED INPUT STREAM LENGTH: " + inputStream.available()); + int ch; + StringBuilder stringBuilder = new StringBuilder(); + int index = 0; + while (true) { + ch = inputStream.read(); + System.out.println("READ CHARACTER: " + index + " " + ch); + if (ch == -1) { + break; + } + stringBuilder.append((char)ch); + index++; + } + return stringBuilder.toString(); + } catch (IOException e) { + return null; + } + } + /** * A contact in the database, and the attributes used to create it. Construct using * {@link GoldenContactBuilder#build()}. diff --git a/tests/src/com/android/providers/contacts/CloneContactsProvider2Test.java b/tests/src/com/android/providers/contacts/CloneContactsProvider2Test.java index 18c6cb76..48a00da2 100644 --- a/tests/src/com/android/providers/contacts/CloneContactsProvider2Test.java +++ b/tests/src/com/android/providers/contacts/CloneContactsProvider2Test.java @@ -17,28 +17,50 @@ package com.android.providers.contacts; import static com.android.providers.contacts.ContactsActor.MockUserManager.CLONE_PROFILE_USER; +import static com.android.providers.contacts.ContactsActor.MockUserManager.PRIMARY_USER; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.spy; import android.content.ContentProviderOperation; import android.content.ContentProviderResult; import android.content.ContentUris; import android.content.ContentValues; import android.content.OperationApplicationException; +import android.content.res.AssetFileDescriptor; import android.database.Cursor; import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.os.RemoteException; +import android.provider.CallLog; import android.provider.ContactsContract; +import android.util.Log; + import androidx.test.filters.MediumTest; import androidx.test.filters.SdkSuppress; +import com.android.providers.contacts.testutil.DataUtil; +import com.android.providers.contacts.testutil.RawContactUtil; + +import org.junit.Assert; + +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; import java.util.ArrayList; +import java.util.Set; @MediumTest @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake") public class CloneContactsProvider2Test extends BaseContactsProvider2Test { private ContactsActor mCloneContactsActor; + private SynchronousContactsProvider2 mCloneContactsProvider; private SynchronousContactsProvider2 getCloneContactsProvider() { return (SynchronousContactsProvider2) mCloneContactsActor.provider; @@ -55,7 +77,8 @@ public class CloneContactsProvider2Test extends BaseContactsProvider2Test { mCloneContactsActor.mockUserManager.setUsers(ContactsActor.MockUserManager.PRIMARY_USER, CLONE_PROFILE_USER); mCloneContactsActor.mockUserManager.myUser = CLONE_PROFILE_USER.id; - getCloneContactsProvider().wipeData(); + mCloneContactsProvider = spy(getCloneContactsProvider()); + mCloneContactsProvider.wipeData(); } private ContentValues getSampleContentValues() { @@ -67,17 +90,43 @@ public class CloneContactsProvider2Test extends BaseContactsProvider2Test { return values; } + private void getCloneContactsProviderWithMockedCallToParent(Uri uri) { + Cursor primaryProfileCursor = mActor.provider.query(uri, + null /* projection */, null /* queryArgs */, null /* cancellationSignal */); + assertNotNull(primaryProfileCursor); + doReturn(primaryProfileCursor).when(mCloneContactsProvider) + .queryContactsProviderForUser(eq(uri), any(), any(), any(), any(), + any(), eq(PRIMARY_USER)); + } + + private void getCloneContactsProviderWithMockedOpenAssetFileCall(Uri uri) + throws FileNotFoundException { + AssetFileDescriptor fileDescriptor = mActor.provider.openAssetFile(uri, "r"); + doReturn(fileDescriptor).when(mCloneContactsProvider) + .openAssetFileThroughParentProvider(eq(uri), eq("r")); + } + + private String getCursorValue(Cursor c, String columnName) { + return c.getString(c.getColumnIndex(columnName)); + } + private void assertEqualContentValues(ContentValues contentValues, Cursor cursor) { - assertEquals(contentValues.get(ContactsContract.RawContacts.ACCOUNT_NAME), - cursor.getString(cursor.getColumnIndex(ContactsContract.RawContacts.ACCOUNT_NAME))); - assertEquals(contentValues.get(ContactsContract.RawContacts.ACCOUNT_TYPE), - cursor.getString(cursor.getColumnIndex(ContactsContract.RawContacts.ACCOUNT_TYPE))); - assertEquals(contentValues.get(ContactsContract.RawContacts.CUSTOM_RINGTONE), - cursor.getString(cursor.getColumnIndex( - ContactsContract.RawContacts.CUSTOM_RINGTONE))); - assertEquals(contentValues.get(ContactsContract.RawContacts.STARRED), - cursor.getString(cursor.getColumnIndex( - ContactsContract.RawContacts.STARRED))); + for (String key: contentValues.getValues().keySet()) { + assertEquals(contentValues.get(key), getCursorValue(cursor, key)); + } + } + + private void assertRawContactsCursorEquals(Cursor expectedCursor, Cursor actualCursor, + Set columnNames) { + assertNotNull(actualCursor); + assertEquals(expectedCursor.getCount(), actualCursor.getCount()); + while (actualCursor.moveToNext()) { + expectedCursor.moveToNext(); + for (String key: columnNames) { + assertEquals(getCursorValue(expectedCursor, key), + getCursorValue(actualCursor, key)); + } + } } private void assertRejectedApplyBatchResults(ContentProviderResult[] res, @@ -279,4 +328,128 @@ public class CloneContactsProvider2Test extends BaseContactsProvider2Test { assertNotNull(authResponse); assertEquals(Bundle.EMPTY, authResponse); } + + public void testCloneContactsProviderReads_callerNotInAllowlist() { + // Insert raw contact through the primary clone provider + ContentValues inputContentValues = getSampleContentValues(); + long rawContactId = insertRawContactsThroughPrimaryProvider(inputContentValues); + Uri uri = ContentUris.withAppendedId(ContactsContract.RawContacts.CONTENT_URI, + rawContactId); + + // Mock call to parent profile contacts provider to return the correct result containing all + // contacts in the parent profile. + getCloneContactsProviderWithMockedCallToParent(uri); + + // Mock call to ensure the caller package is not in the app-cloning allowlist + doReturn(false) + .when(mCloneContactsProvider).isAppAllowedToUseParentUsersContacts(any()); + + // Test clone contacts provider read with the uri of the contact added above + mCloneContactsProvider.query(uri, + null /* projection */, null /* queryArgs */, null /* cancellationSignal */); + + // Check that the call passed through to the local query instead of redirecting to the + // parent provider + verify(mCloneContactsProvider, times(1)) + .queryDirectoryIfNecessary(any(), any(), any(), any(), any(), any()); + } + + public void testContactsProviderReads_callerInAllowlist() { + // Insert raw contact through the primary clone provider + ContentValues inputContentValues = getSampleContentValues(); + long rawContactId = insertRawContactsThroughPrimaryProvider(inputContentValues); + Uri uri = ContentUris.withAppendedId(ContactsContract.RawContacts.CONTENT_URI, + rawContactId); + + // Mock call to parent profile contacts provider to return the correct result containing all + // contacts in the parent profile. + getCloneContactsProviderWithMockedCallToParent(uri); + + // Mock call to ensure the caller package is in the app-cloning allowlist + doReturn(true) + .when(mCloneContactsProvider).isAppAllowedToUseParentUsersContacts(any()); + + // Test clone contacts provider read with the uri of the contact added above + Cursor cursor = mCloneContactsProvider.query(uri, + null /* projection */, null /* queryArgs */, null /* cancellationSignal */); + + // Check that the call did not pass through to the local query and instead redirected to the + // parent provider + verify(mCloneContactsProvider, times(0)) + .queryDirectoryIfNecessary(any(), any(), any(), any(), any(), any()); + assertNotNull(cursor); + Cursor primaryProfileCursor = mActor.provider.query(uri, + null /* projection */, null /* queryArgs */, null /* cancellationSignal */); + assertNotNull(primaryProfileCursor); + assertRawContactsCursorEquals(primaryProfileCursor, cursor, + inputContentValues.getValues().keySet()); + } + + public void testQueryPrimaryProfileProvider_callingFromParentUser() { + ContentValues inputContentValues = getSampleContentValues(); + long rawContactId = insertRawContactsThroughPrimaryProvider(inputContentValues); + Uri uri = ContentUris.withAppendedId(ContactsContract.RawContacts.CONTENT_URI, + rawContactId); + + // Fetch primary contacts provider and call method to redirect to parent provider + final ContactsProvider2 primaryCP2 = (ContactsProvider2) getProvider(); + Cursor cursor = primaryCP2.queryParentProfileContactsProvider(uri, + null /* projection */, null /* selection */, null /* selectionArgs */, + null /* sortOrder */, null /* cancellationSignal */); + + // Assert that empty cursor is returned + assertNotNull(cursor); + assertEquals(0, cursor.getCount()); + } + + public void testQueryPrimaryProfileProvider_incorrectAuthority() { + ContentValues inputContentValues = getSampleContentValues(); + insertRawContactsThroughPrimaryProvider(inputContentValues); + + Assert.assertThrows(IllegalArgumentException.class, () -> + mCloneContactsProvider.queryParentProfileContactsProvider(CallLog.CONTENT_URI, + null /* projection */, null /* selection */, null /* selectionArgs */, + null /* sortOrder */, null /* cancellationSignal */)); + } + + public void testOpenAssetFileMultiVCard() throws IOException { + final VCardTestUriCreator contacts = createVCardTestContacts(); + + // Mock call to parent profile contacts provider to return the correct asset file + getCloneContactsProviderWithMockedOpenAssetFileCall(contacts.getCombinedUri()); + + // Mock call to ensure the caller package is in the app-cloning allowlist + doReturn(true) + .when(mCloneContactsProvider).isAppAllowedToUseParentUsersContacts(any()); + + final AssetFileDescriptor descriptor = + mCloneContactsProvider.openAssetFile(contacts.getCombinedUri(), "r"); + final FileInputStream inputStream = descriptor.createInputStream(); + String data = readToEnd(inputStream); + inputStream.close(); + descriptor.close(); + + // Ensure that the resulting VCard has both contacts + assertTrue(data.contains("N:Doe;John;;;")); + assertTrue(data.contains("N:Doh;Jane;;;")); + } + + public void testOpenAssetFileMultiVCard_callerNotInAllowlist() throws IOException { + final VCardTestUriCreator contacts = createVCardTestContacts(); + + // Mock call to parent profile contacts provider to return the correct asset file + getCloneContactsProviderWithMockedOpenAssetFileCall(contacts.getCombinedUri()); + + // Mock call to ensure the caller package is not in the app-cloning allowlist + doReturn(false) + .when(mCloneContactsProvider).isAppAllowedToUseParentUsersContacts(any()); + + final AssetFileDescriptor descriptor = + mCloneContactsProvider.openAssetFile(contacts.getCombinedUri(), "r"); + + // Check that the call passed through to the local call instead of redirecting to the + // parent provider + verify(mCloneContactsProvider, times(1)) + .openAssetFile(eq(contacts.getCombinedUri()), any()); + } } diff --git a/tests/src/com/android/providers/contacts/ContactsProvider2Test.java b/tests/src/com/android/providers/contacts/ContactsProvider2Test.java index b625ac4a..69ae0fb2 100644 --- a/tests/src/com/android/providers/contacts/ContactsProvider2Test.java +++ b/tests/src/com/android/providers/contacts/ContactsProvider2Test.java @@ -8163,50 +8163,6 @@ public class ContactsProvider2Test extends BaseContactsProvider2Test { assertEquals("default", helper.getProperty("existent1", "default")); } - private class VCardTestUriCreator { - private String mLookup1; - private String mLookup2; - - public VCardTestUriCreator(String lookup1, String lookup2) { - super(); - mLookup1 = lookup1; - mLookup2 = lookup2; - } - - public Uri getUri1() { - return Uri.withAppendedPath(Contacts.CONTENT_VCARD_URI, mLookup1); - } - - public Uri getUri2() { - return Uri.withAppendedPath(Contacts.CONTENT_VCARD_URI, mLookup2); - } - - public Uri getCombinedUri() { - return Uri.withAppendedPath(Contacts.CONTENT_MULTI_VCARD_URI, - Uri.encode(mLookup1 + ":" + mLookup2)); - } - } - - private VCardTestUriCreator createVCardTestContacts() { - final long rawContactId1 = RawContactUtil.createRawContact(mResolver, mAccount, - RawContacts.SOURCE_ID, "4:12"); - DataUtil.insertStructuredName(mResolver, rawContactId1, "John", "Doe"); - - final long rawContactId2 = RawContactUtil.createRawContact(mResolver, mAccount, - RawContacts.SOURCE_ID, "3:4%121"); - DataUtil.insertStructuredName(mResolver, rawContactId2, "Jane", "Doh"); - - final long contactId1 = queryContactId(rawContactId1); - final long contactId2 = queryContactId(rawContactId2); - final Uri contact1Uri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId1); - final Uri contact2Uri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId2); - final String lookup1 = - Uri.encode(Contacts.getLookupUri(mResolver, contact1Uri).getPathSegments().get(2)); - final String lookup2 = - Uri.encode(Contacts.getLookupUri(mResolver, contact2Uri).getPathSegments().get(2)); - return new VCardTestUriCreator(lookup1, lookup2); - } - public void testQueryMultiVCard() { // No need to create any contacts here, because the query for multiple vcards // does not go into the database at all @@ -9688,27 +9644,6 @@ public class ContactsProvider2Test extends BaseContactsProvider2Test { return c; } - private String readToEnd(FileInputStream inputStream) { - try { - System.out.println("DECLARED INPUT STREAM LENGTH: " + inputStream.available()); - int ch; - StringBuilder stringBuilder = new StringBuilder(); - int index = 0; - while (true) { - ch = inputStream.read(); - System.out.println("READ CHARACTER: " + index + " " + ch); - if (ch == -1) { - break; - } - stringBuilder.append((char)ch); - index++; - } - return stringBuilder.toString(); - } catch (IOException e) { - return null; - } - } - private void assertQueryParameter(String uriString, String parameter, String expectedValue) { assertEquals(expectedValue, ContactsProvider2.getQueryParameter( Uri.parse(uriString), parameter)); diff --git a/tests/src/com/android/providers/contacts/util/UserUtilsTest.java b/tests/src/com/android/providers/contacts/util/UserUtilsTest.java index 072df377..c672697a 100644 --- a/tests/src/com/android/providers/contacts/util/UserUtilsTest.java +++ b/tests/src/com/android/providers/contacts/util/UserUtilsTest.java @@ -18,6 +18,7 @@ package com.android.providers.contacts.util; import static com.android.providers.contacts.ContactsActor.PACKAGE_GREY; import android.content.Context; +import android.os.UserHandle; import android.provider.ContactsContract; import android.test.suitebuilder.annotation.SmallTest; @@ -101,14 +102,58 @@ public class UserUtilsTest extends FixedAndroidTestCase { um.myUser = MockUserManager.PRIMARY_USER.id; assertFalse(UserUtils.shouldUseParentsContacts(c)); + assertFalse(UserUtils.shouldUseParentsContacts(c, + MockUserManager.PRIMARY_USER.getUserHandle())); um.myUser = MockUserManager.SECONDARY_USER.id; assertFalse(UserUtils.shouldUseParentsContacts(c)); + assertFalse(UserUtils.shouldUseParentsContacts(c, + MockUserManager.SECONDARY_USER.getUserHandle())); um.myUser = MockUserManager.CORP_USER.id; assertFalse(UserUtils.shouldUseParentsContacts(c)); + assertFalse(UserUtils.shouldUseParentsContacts(c, + MockUserManager.CORP_USER.getUserHandle())); um.myUser = MockUserManager.CLONE_PROFILE_USER.id; assertTrue(UserUtils.shouldUseParentsContacts(c)); + assertTrue(UserUtils.shouldUseParentsContacts(c, + MockUserManager.CLONE_PROFILE_USER.getUserHandle())); + + } + + public void testIsParentUser() { + final Context c = mActor.getProviderContext(); + final MockUserManager um = mActor.mockUserManager; + um.setUsers(MockUserManager.PRIMARY_USER, MockUserManager.SECONDARY_USER, + MockUserManager.CLONE_PROFILE_USER, MockUserManager.CORP_USER); + + UserHandle primaryProfileUserHandle = MockUserManager.PRIMARY_USER.getUserHandle(); + UserHandle cloneUserHandle = MockUserManager.CLONE_PROFILE_USER.getUserHandle(); + UserHandle corpUserHandle = MockUserManager.CORP_USER.getUserHandle(); + + assertTrue(UserUtils.isParentUser(c, primaryProfileUserHandle, cloneUserHandle)); + assertTrue(UserUtils.isParentUser(c, primaryProfileUserHandle, corpUserHandle)); + assertFalse(UserUtils.isParentUser(c, primaryProfileUserHandle, primaryProfileUserHandle)); + assertFalse(UserUtils.isParentUser(c, cloneUserHandle, cloneUserHandle)); + assertFalse(UserUtils.isParentUser(c, cloneUserHandle, primaryProfileUserHandle)); + assertFalse(UserUtils.isParentUser(c, corpUserHandle, primaryProfileUserHandle)); + } + + public void testGetProfileParent() { + final Context c = mActor.getProviderContext(); + final MockUserManager um = mActor.mockUserManager; + + um.setUsers(MockUserManager.PRIMARY_USER, MockUserManager.SECONDARY_USER, + MockUserManager.CLONE_PROFILE_USER, MockUserManager.CORP_USER); + + um.myUser = MockUserManager.PRIMARY_USER.id; + assertNull(UserUtils.getProfileParentUser(c)); + + um.myUser = MockUserManager.CLONE_PROFILE_USER.id; + assertEquals(MockUserManager.PRIMARY_USER, UserUtils.getProfileParentUser(c)); + + um.myUser = MockUserManager.CORP_USER.id; + assertEquals(MockUserManager.PRIMARY_USER, UserUtils.getProfileParentUser(c)); } } -- cgit v1.2.3 From f021dbcd60deed89ec8ff77b8dc3c9df4095f24b Mon Sep 17 00:00:00 2001 From: Liahav Eitan Date: Fri, 11 Nov 2022 19:29:08 +0000 Subject: Support listing all work profile contacts in personal CP2. This includes the existing Phone.ENTERPRISE_CONTENT_URI that is now becoming public, and the new Contacts.ENTERPRISE_CONTENT_URI. See ag/20580387 for the API changes. Test: atest ManagedProfileContactsTest, EnterprisePolicyGuardTest Bug: 240954287 Change-Id: I8ac33c236d95954f4fc16027669b67cc204fcbdb --- .../providers/contacts/ContactsProvider2.java | 65 ++++++++++++++-------- .../contacts/enterprise/EnterprisePolicyGuard.java | 29 ++++++++-- .../enterprise/EnterprisePolicyGuardTest.java | 27 +++++++-- 3 files changed, 87 insertions(+), 34 deletions(-) diff --git a/src/com/android/providers/contacts/ContactsProvider2.java b/src/com/android/providers/contacts/ContactsProvider2.java index 3f61a15a..3f8b891c 100644 --- a/src/com/android/providers/contacts/ContactsProvider2.java +++ b/src/com/android/providers/contacts/ContactsProvider2.java @@ -75,6 +75,7 @@ import android.os.RemoteException; import android.os.StrictMode; import android.os.SystemClock; import android.os.UserHandle; +import android.os.UserManager; import android.preference.PreferenceManager; import android.provider.BaseColumns; import android.provider.ContactsContract; @@ -341,6 +342,7 @@ public class ContactsProvider2 extends AbstractContactsProvider public static final int CONTACTS_ID_PHOTO_CORP = 1027; public static final int CONTACTS_ID_DISPLAY_PHOTO_CORP = 1028; public static final int CONTACTS_FILTER_ENTERPRISE = 1029; + public static final int CONTACTS_ENTERPRISE = 1030; public static final int RAW_CONTACTS = 2002; public static final int RAW_CONTACTS_ID = 2003; @@ -1206,6 +1208,7 @@ public class ContactsProvider2 extends AbstractContactsProvider matcher.addURI(ContactsContract.AUTHORITY, "contacts/frequent", CONTACTS_FREQUENT); matcher.addURI(ContactsContract.AUTHORITY, "contacts/delete_usage", CONTACTS_DELETE_USAGE); + matcher.addURI(ContactsContract.AUTHORITY, "contacts/enterprise", CONTACTS_ENTERPRISE); matcher.addURI(ContactsContract.AUTHORITY, "contacts/filter_enterprise", CONTACTS_FILTER_ENTERPRISE); matcher.addURI(ContactsContract.AUTHORITY, "contacts/filter_enterprise/*", @@ -5628,7 +5631,7 @@ public class ContactsProvider2 extends AbstractContactsProvider // 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 mEnterprisePolicyGuard.isCrossProfileAllowed(uri, getRealCallerPackageName(uri)); } return true; } @@ -5703,14 +5706,14 @@ public class ContactsProvider2 extends AbstractContactsProvider final String passedPackage = queryUri.getQueryParameter( Directory.CALLER_PACKAGE_PARAM_KEY); if (TextUtils.isEmpty(passedPackage)) { - Log.wtfStack(TAG, - "Cross-profile query with no " + Directory.CALLER_PACKAGE_PARAM_KEY); - return "UNKNOWN"; + throw new IllegalArgumentException( + "Cross-profile query with no " + Directory.CALLER_PACKAGE_PARAM_KEY + + " param. Uri: " + queryUri); } return passedPackage; } else { // Otherwise, just return the real calling package name. - return getCallingPackage(); + return getCallingPackageUnchecked(); } } @@ -6341,8 +6344,6 @@ public class ContactsProvider2 extends AbstractContactsProvider new Object[] {getMaxDisplayPhotoDim(), getMaxThumbnailDim()}); } case PHONES_ENTERPRISE: { - ContactsPermissions.enforceCallingOrSelfPermission(getContext(), - INTERACT_ACROSS_USERS); return queryMergedDataPhones(uri, projection, selection, selectionArgs, sortOrder, cancellationSignal); } @@ -6510,6 +6511,9 @@ public class ContactsProvider2 extends AbstractContactsProvider } break; } + case CONTACTS_ENTERPRISE: + return queryMergedContacts(projection, selection, selectionArgs, sortOrder, + cancellationSignal); case PHONES_FILTER_ENTERPRISE: case CALLABLES_FILTER_ENTERPRISE: case EMAILS_FILTER_ENTERPRISE: @@ -7273,8 +7277,7 @@ public class ContactsProvider2 extends AbstractContactsProvider final Cursor[] cursorArray = new Cursor[] { primaryCursor, rewriteCorpDirectories(corpCursor) }; - final MergeCursor mergeCursor = new MergeCursor(cursorArray); - return mergeCursor; + return new MergeCursor(cursorArray); } catch (Throwable th) { if (primaryCursor != null) { primaryCursor.close(); @@ -7287,6 +7290,29 @@ public class ContactsProvider2 extends AbstractContactsProvider } } + /** + * Handles {@link Contacts#ENTERPRISE_CONTENT_URI}. + */ + private Cursor queryMergedContacts(String[] projection, String selection, + String[] selectionArgs, String sortOrder, CancellationSignal cancellationSignal) { + final Uri localUri = Contacts.CONTENT_URI; + try (final Cursor primaryCursor = queryLocal(localUri, projection, selection, selectionArgs, + sortOrder, Directory.DEFAULT, cancellationSignal)) {; + final int managedUserId = UserUtils.getCorpUserId(getContext()); + if (managedUserId < 0) { + // No managed profile or policy not allowed + return primaryCursor; + } + final Cursor managedCursor = queryCorpContacts(localUri, projection, selection, + selectionArgs, sortOrder, new String[] {Contacts._ID}, + Directory.ENTERPRISE_DEFAULT, cancellationSignal); + final Cursor[] cursorArray = new Cursor[] { + primaryCursor, managedCursor + }; + return new MergeCursor(cursorArray); + } + } + /** * Handles {@link Phone#ENTERPRISE_CONTENT_URI}. */ @@ -7310,8 +7336,6 @@ public class ContactsProvider2 extends AbstractContactsProvider final Cursor primaryCursor = queryLocal(localUri, projection, selection, selectionArgs, sortOrder, directoryId, null); try { - // PHONES_ENTERPRISE should not be guarded by EnterprisePolicyGuard as Bluetooth app is - // responsible to guard it. final int corpUserId = UserUtils.getCorpUserId(getContext()); if (corpUserId < 0) { // No Corp user or policy not allowed @@ -7321,21 +7345,10 @@ public class ContactsProvider2 extends AbstractContactsProvider final Cursor managedCursor = queryCorpContacts(localUri, projection, selection, selectionArgs, sortOrder, new String[] {RawContacts.CONTACT_ID}, null, cancellationSignal); - if (managedCursor == null) { - // No corp results. Just return the local result. - return primaryCursor; - } final Cursor[] cursorArray = new Cursor[] { primaryCursor, managedCursor }; - // Sort order is not supported yet, will be fixed in M when we have - // merged provider - // MergeCursor will copy all the contacts from two cursors, which may - // cause OOM if there's a lot of contacts. But it's only used by - // Bluetooth, and Bluetooth will loop through the Cursor and put all - // content in ArrayList anyway, so we ignore OOM issue here for now - final MergeCursor mergeCursor = new MergeCursor(cursorArray); - return mergeCursor; + return new MergeCursor(cursorArray); } catch (Throwable th) { if (primaryCursor != null) { primaryCursor.close(); @@ -9022,6 +9035,8 @@ public class ContactsProvider2 extends AbstractContactsProvider builder.encodedPath(uri.getEncodedPath()); builder.appendQueryParameter(ContactsContract.DIRECTORY_PARAM_KEY, String.valueOf(directoryId - Directory.ENTERPRISE_DIRECTORY_ID_BASE)); + builder.appendQueryParameter(Directory.CALLER_PACKAGE_PARAM_KEY, + getRealCallerPackageName(uri)); addQueryParametersFromUri(builder, uri, MODIFIED_KEY_SET_FOR_ENTERPRISE_FILTER); // If work profile is not available, it will throw FileNotFoundException @@ -9075,12 +9090,14 @@ public class ContactsProvider2 extends AbstractContactsProvider throw new FileNotFoundException(uri.toString()); } // Convert the URI into: - // content://USER@com.android.contacts/contacts_corp/ID/{photo,display_photo} + // content://USER@com.android.contacts/contacts/ID/{photo,display_photo} // If work profile is not available, it will throw FileNotFoundException final Uri corpUri = maybeAddUserId( ContentUris.appendId(Contacts.CONTENT_URI.buildUpon(), contactId) .appendPath(displayPhoto ? Contacts.Photo.DISPLAY_PHOTO : Contacts.Photo.CONTENT_DIRECTORY) + .appendQueryParameter(Directory.CALLER_PACKAGE_PARAM_KEY, + getRealCallerPackageName(uri)) .build(), corpUserId); // TODO Make sure it doesn't leak any FDs. diff --git a/src/com/android/providers/contacts/enterprise/EnterprisePolicyGuard.java b/src/com/android/providers/contacts/enterprise/EnterprisePolicyGuard.java index 46841050..db952d66 100644 --- a/src/com/android/providers/contacts/enterprise/EnterprisePolicyGuard.java +++ b/src/com/android/providers/contacts/enterprise/EnterprisePolicyGuard.java @@ -19,6 +19,7 @@ package com.android.providers.contacts.enterprise; import android.annotation.NonNull; import android.app.admin.DevicePolicyManager; import android.content.Context; +import android.content.pm.PackageManager; import android.net.Uri; import android.os.UserHandle; import android.provider.ContactsContract; @@ -29,8 +30,11 @@ import android.util.Log; import com.android.providers.contacts.ContactsProvider2; import com.android.providers.contacts.ProfileAwareUriMatcher; import com.android.providers.contacts.util.UserUtils; + import com.google.common.annotations.VisibleForTesting; +import static android.Manifest.permission.INTERACT_ACROSS_USERS; +import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.provider.Settings.Secure.MANAGED_PROFILE_CONTACT_REMOTE_SEARCH; /** @@ -44,22 +48,25 @@ public class EnterprisePolicyGuard { final private Context mContext; final private DevicePolicyManager mDpm; + final private PackageManager mPm; public EnterprisePolicyGuard(Context context) { mContext = context; mDpm = context.getSystemService(DevicePolicyManager.class); + mPm = context.getPackageManager(); } /** * Check if cross profile query is allowed for the given uri * * @param uri Uri that we want to check. + * @param callingPackage Name of the client package that called CP2 in the other profile * @return True if cross profile query is allowed for this uri */ - public boolean isCrossProfileAllowed(@NonNull Uri uri) { + public boolean isCrossProfileAllowed(@NonNull Uri uri, @NonNull String callingPackage) { final int uriCode = sUriMatcher.match(uri); final UserHandle currentHandle = new UserHandle(UserUtils.getCurrentUserHandle(mContext)); - if (uriCode == -1 || currentHandle == null) { + if (uriCode == -1) { return false; } @@ -67,6 +74,7 @@ public class EnterprisePolicyGuard { return true; } + // TODO(b/240954287): replace these by new DPM APIs that take package name into account final boolean isCallerIdEnabled = !mDpm.getCrossProfileCallerIdDisabled(currentHandle); final boolean isContactsSearchPolicyEnabled = !mDpm.getCrossProfileContactsSearchDisabled(currentHandle); @@ -95,10 +103,17 @@ public class EnterprisePolicyGuard { } } + final boolean isAllowedByCallerIdPolicy = isCallerIdGuarded(uriCode) && isCallerIdEnabled; + final boolean isAllowedByContactSearchPolicy = + isContactsSearchGuarded(uriCode) && isContactsSearchPolicyEnabled; + final boolean isAllowedByBluetoothSharingPolicy = + isBluetoothContactSharing(uriCode) && isBluetoothContactSharingEnabled + // Only allow apps with INTERACT_ACROSS_USERS to access the bluetooth APIs + && mPm.checkPermission(INTERACT_ACROSS_USERS, callingPackage) + == PERMISSION_GRANTED; // If either guard policy allows access, return true. - return (isCallerIdGuarded(uriCode) && isCallerIdEnabled) - || (isContactsSearchGuarded(uriCode) && isContactsSearchPolicyEnabled) - || (isBluetoothContactSharing(uriCode) && isBluetoothContactSharingEnabled); + return isAllowedByCallerIdPolicy || isAllowedByContactSearchPolicy + || isAllowedByBluetoothSharingPolicy; } private boolean isUriWhitelisted(int uriCode) { @@ -148,7 +163,9 @@ public class EnterprisePolicyGuard { switch (uriCode) { case ContactsProvider2.PHONE_LOOKUP_ENTERPRISE: case ContactsProvider2.EMAILS_LOOKUP_ENTERPRISE: + case ContactsProvider2.CONTACTS_ENTERPRISE: case ContactsProvider2.CONTACTS_FILTER_ENTERPRISE: + case ContactsProvider2.PHONES_ENTERPRISE: case ContactsProvider2.PHONES_FILTER_ENTERPRISE: case ContactsProvider2.CALLABLES_FILTER_ENTERPRISE: case ContactsProvider2.EMAILS_FILTER_ENTERPRISE: @@ -180,8 +197,10 @@ public class EnterprisePolicyGuard { switch(uriCode) { case ContactsProvider2.DIRECTORIES: case ContactsProvider2.DIRECTORIES_ID: + case ContactsProvider2.CONTACTS: case ContactsProvider2.CONTACTS_FILTER: case ContactsProvider2.CALLABLES_FILTER: + case ContactsProvider2.PHONES: case ContactsProvider2.PHONES_FILTER: case ContactsProvider2.EMAILS_FILTER: case ContactsProvider2.CONTACTS_ID_PHOTO: diff --git a/tests/src/com/android/providers/contacts/enterprise/EnterprisePolicyGuardTest.java b/tests/src/com/android/providers/contacts/enterprise/EnterprisePolicyGuardTest.java index 2e5241fb..9b5fca1a 100644 --- a/tests/src/com/android/providers/contacts/enterprise/EnterprisePolicyGuardTest.java +++ b/tests/src/com/android/providers/contacts/enterprise/EnterprisePolicyGuardTest.java @@ -17,6 +17,7 @@ package com.android.providers.contacts.enterprise; import android.app.admin.DevicePolicyManager; import android.content.Context; +import android.content.pm.PackageManager; import android.content.pm.UserInfo; import android.net.Uri; import android.os.UserHandle; @@ -31,6 +32,9 @@ import org.mockito.Matchers; import java.util.Arrays; import java.util.List; +import static android.Manifest.permission.INTERACT_ACROSS_USERS; +import static android.content.pm.PackageManager.PERMISSION_GRANTED; + import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -49,6 +53,7 @@ public class EnterprisePolicyGuardTest extends FixedAndroidTestCase { private static final String CONTACT_EMAIL = "david.green@android.com"; private static final String CONTACT_PHONE = "+1234567890"; private static final long DIRECTORY_ID = Directory.ENTERPRISE_DEFAULT; + private static final String CALLING_PACKAGE = "package"; private static final Uri URI_CONTACTS_ID_PHOTO = Uri.parse("content://com.android.contacts/contacts/" + CONTACT_ID + "/photo"); @@ -231,13 +236,13 @@ public class EnterprisePolicyGuardTest extends FixedAndroidTestCase { } } - private static void checkCrossProfile(EnterprisePolicyGuard guard, Uri uri, boolean expected) { + private void checkCrossProfile(EnterprisePolicyGuard guard, Uri uri, boolean expected) { if (expected) { assertTrue("Expected true but got false for uri: " + uri, - guard.isCrossProfileAllowed(uri)); + guard.isCrossProfileAllowed(uri, CALLING_PACKAGE)); } else { assertFalse("Expected false but got true for uri: " + uri, - guard.isCrossProfileAllowed(uri)); + guard.isCrossProfileAllowed(uri, CALLING_PACKAGE)); } } @@ -269,7 +274,11 @@ public class EnterprisePolicyGuardTest extends FixedAndroidTestCase { when(mockUm.getProfiles(Matchers.anyInt())).thenReturn(userInfos); when(mockUm.getProfileParent(WORK_USER_ID)).thenReturn(CURRENT_USER_INFO); - Context mockContext = new TestMockContext(getContext(), mockDpm, mockUm); + PackageManager mockPm = mock(PackageManager.class); + when(mockPm.checkPermission(INTERACT_ACROSS_USERS, CALLING_PACKAGE)) + .thenReturn(PERMISSION_GRANTED); + + Context mockContext = new TestMockContext(getContext(), mockDpm, mockUm, mockPm); return mockContext; } @@ -278,11 +287,19 @@ public class EnterprisePolicyGuardTest extends FixedAndroidTestCase { private Context mRealContext; private DevicePolicyManager mDpm; private UserManager mUm; + private PackageManager mPm; - public TestMockContext(Context realContext, DevicePolicyManager dpm, UserManager um) { + public TestMockContext( + Context realContext, DevicePolicyManager dpm, UserManager um, PackageManager pm) { mRealContext = realContext; mDpm = dpm; mUm = um; + mPm = pm; + } + + @Override + public PackageManager getPackageManager() { + return mPm; } public Object getSystemService(String name) { -- cgit v1.2.3 From 6050d0bfd1ba0b09ae6209a0ae1c1c0c54368967 Mon Sep 17 00:00:00 2001 From: Bill Yi Date: Wed, 14 Dec 2022 14:59:44 -0800 Subject: Import translations. DO NOT MERGE ANYWHERE Auto-generated-cl: translation import Change-Id: Id9e937879fee512981edbefdc1b18ad2fe71db19 --- res/values-te/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/res/values-te/strings.xml b/res/values-te/strings.xml index 2b6fa1aa..e7fd9c66 100644 --- a/res/values-te/strings.xml +++ b/res/values-te/strings.xml @@ -28,7 +28,7 @@ "కాంటాక్ట్‌ల డేటాబేస్‌ను కాపీ చేయండి" "మీరు 1) అన్ని కాంటాక్ట్‌లకు సంబంధించిన సమాచారాన్ని మరియు మొత్తం కాల్ లాగ్‌ను కలిగి ఉండే మీ డేటాబేస్ యొక్క కాపీని అంతర్గత స్టోరేజ్‌లో రూపొందించి 2) దాన్ని ఈమెయిల్‌ చేయబోతున్నారు. మీరు దాన్ని పరికరం నుండి విజయవంతంగా కాపీ చేసిన తర్వాత లేదా ఈమెయిల్‌ను స్వీకరించిన తర్వాత కాపీని తొలగించడం మర్చిపోవద్దు." "ఇప్పుడే తొలగించు" - "ప్రారంభించు" + "ప్రారంభించండి" "మీ పైల్‌ను పంపడానికి ప్రోగ్రామ్‌ను ఎంచుకోండి" "కాంటాక్ట్‌ల Db జోడించబడింది" "జోడింపు నా అన్ని కాంటాక్ట్‌ల సమాచారాన్ని కలిగి ఉన్న నా కాంటాక్ట్‌ల డేటాబేస్. జాగ్రత్తగా వ్యవహరించండి." -- cgit v1.2.3 From 3249f591ae9bd92bbcdb27243baa41f752cac782 Mon Sep 17 00:00:00 2001 From: Bill Yi Date: Wed, 14 Dec 2022 15:00:07 -0800 Subject: Import translations. DO NOT MERGE ANYWHERE Auto-generated-cl: translation import Change-Id: I72db1db58242974cedd95a08ca4cae1da30a17fa --- res/values-te/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/res/values-te/strings.xml b/res/values-te/strings.xml index 2b6fa1aa..e7fd9c66 100644 --- a/res/values-te/strings.xml +++ b/res/values-te/strings.xml @@ -28,7 +28,7 @@ "కాంటాక్ట్‌ల డేటాబేస్‌ను కాపీ చేయండి" "మీరు 1) అన్ని కాంటాక్ట్‌లకు సంబంధించిన సమాచారాన్ని మరియు మొత్తం కాల్ లాగ్‌ను కలిగి ఉండే మీ డేటాబేస్ యొక్క కాపీని అంతర్గత స్టోరేజ్‌లో రూపొందించి 2) దాన్ని ఈమెయిల్‌ చేయబోతున్నారు. మీరు దాన్ని పరికరం నుండి విజయవంతంగా కాపీ చేసిన తర్వాత లేదా ఈమెయిల్‌ను స్వీకరించిన తర్వాత కాపీని తొలగించడం మర్చిపోవద్దు." "ఇప్పుడే తొలగించు" - "ప్రారంభించు" + "ప్రారంభించండి" "మీ పైల్‌ను పంపడానికి ప్రోగ్రామ్‌ను ఎంచుకోండి" "కాంటాక్ట్‌ల Db జోడించబడింది" "జోడింపు నా అన్ని కాంటాక్ట్‌ల సమాచారాన్ని కలిగి ఉన్న నా కాంటాక్ట్‌ల డేటాబేస్. జాగ్రత్తగా వ్యవహరించండి." -- cgit v1.2.3 From f52aff5bc50a835554db152688d213603f910aa3 Mon Sep 17 00:00:00 2001 From: Bill Yi Date: Wed, 21 Dec 2022 06:41:23 -0800 Subject: Import translations. DO NOT MERGE ANYWHERE Auto-generated-cl: translation import Change-Id: I4d0f3e866ab536b0398976b6e0124d49cef97513 --- res/values-b+sr+Latn/strings.xml | 32 ++++++++++++++++---------------- res/values-te/strings.xml | 2 +- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/res/values-b+sr+Latn/strings.xml b/res/values-b+sr+Latn/strings.xml index b9427e4c..8230513a 100644 --- a/res/values-b+sr+Latn/strings.xml +++ b/res/values-b+sr+Latn/strings.xml @@ -16,20 +16,20 @@ - "Android Core aplikacije" - "Skladište kontakata" - "Kontakti" - "Za ažuriranje kontakata potrebno je više memorije." - "Nadograđivanje memorije za kontakte" - "Dodirnite da biste dovršili nadogradnju." - "Kontakti" - "Drugo" - "Govorna pošta od " - "Kopiranje baze podataka sa kontaktima" - "Upravo ćete 1) napraviti kopiju baze podataka koja sadrži sve informacije u vezi sa kontaktima i celokupnu evidenciju poziva u internoj memoriji i 2) poslati je imejlom. Ne zaboravite da izbrišete kopiju čim je budete kopirali sa uređaja ili čim budete primili imejl." - "Izbriši odmah" - "Pokreni" - "Izaberite program za slanje datoteke" - "Baza podataka sa kontaktima je u prilogu" - "U prilogu je baza podataka sa mojim kontaktima i svim informacijama o njima. Rukujte pažljivo." + "Android Core апликације" + "Складиште контаката" + "Контакти" + "За ажурирање контаката потребно је више меморије." + "Надограђивање меморије за контакте" + "Додирните да бисте довршили надоградњу." + "Контакти" + "Другo" + "Говорна пошта од " + "Копирање базе података са контактима" + "Управо ћете 1) направити копију базе података која садржи све информације у вези са контактима и целокупну евиденцију позива у интерној меморији и 2) послати је имејлом. Не заборавите да избришете копију чим је будете копирали са уређаја или чим будете примили имејл." + "Избриши одмах" + "Покрени" + "Изаберите програм за слање датотеке" + "База података са контактима је у прилогу" + "У прилогу је база података са мојим контактима и свим информацијама о њима. Рукујте пажљиво." diff --git a/res/values-te/strings.xml b/res/values-te/strings.xml index e7fd9c66..951bad9c 100644 --- a/res/values-te/strings.xml +++ b/res/values-te/strings.xml @@ -27,7 +27,7 @@ "దీని నుండి వాయిస్ మెయిల్ " "కాంటాక్ట్‌ల డేటాబేస్‌ను కాపీ చేయండి" "మీరు 1) అన్ని కాంటాక్ట్‌లకు సంబంధించిన సమాచారాన్ని మరియు మొత్తం కాల్ లాగ్‌ను కలిగి ఉండే మీ డేటాబేస్ యొక్క కాపీని అంతర్గత స్టోరేజ్‌లో రూపొందించి 2) దాన్ని ఈమెయిల్‌ చేయబోతున్నారు. మీరు దాన్ని పరికరం నుండి విజయవంతంగా కాపీ చేసిన తర్వాత లేదా ఈమెయిల్‌ను స్వీకరించిన తర్వాత కాపీని తొలగించడం మర్చిపోవద్దు." - "ఇప్పుడే తొలగించు" + "ఇప్పుడే తొలగించండి" "ప్రారంభించండి" "మీ పైల్‌ను పంపడానికి ప్రోగ్రామ్‌ను ఎంచుకోండి" "కాంటాక్ట్‌ల Db జోడించబడింది" -- cgit v1.2.3 From 9ce2e86aa952bf4fb9d168c3d9bc4a92d7155e5a Mon Sep 17 00:00:00 2001 From: Bill Yi Date: Wed, 21 Dec 2022 06:41:46 -0800 Subject: Import translations. DO NOT MERGE ANYWHERE Auto-generated-cl: translation import Change-Id: Ideef75dcc5702eb145accb13bb6236ac42d91a82 --- res/values-b+sr+Latn/strings.xml | 32 ++++++++++++++++---------------- res/values-te/strings.xml | 2 +- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/res/values-b+sr+Latn/strings.xml b/res/values-b+sr+Latn/strings.xml index b9427e4c..8230513a 100644 --- a/res/values-b+sr+Latn/strings.xml +++ b/res/values-b+sr+Latn/strings.xml @@ -16,20 +16,20 @@ - "Android Core aplikacije" - "Skladište kontakata" - "Kontakti" - "Za ažuriranje kontakata potrebno je više memorije." - "Nadograđivanje memorije za kontakte" - "Dodirnite da biste dovršili nadogradnju." - "Kontakti" - "Drugo" - "Govorna pošta od " - "Kopiranje baze podataka sa kontaktima" - "Upravo ćete 1) napraviti kopiju baze podataka koja sadrži sve informacije u vezi sa kontaktima i celokupnu evidenciju poziva u internoj memoriji i 2) poslati je imejlom. Ne zaboravite da izbrišete kopiju čim je budete kopirali sa uređaja ili čim budete primili imejl." - "Izbriši odmah" - "Pokreni" - "Izaberite program za slanje datoteke" - "Baza podataka sa kontaktima je u prilogu" - "U prilogu je baza podataka sa mojim kontaktima i svim informacijama o njima. Rukujte pažljivo." + "Android Core апликације" + "Складиште контаката" + "Контакти" + "За ажурирање контаката потребно је више меморије." + "Надограђивање меморије за контакте" + "Додирните да бисте довршили надоградњу." + "Контакти" + "Другo" + "Говорна пошта од " + "Копирање базе података са контактима" + "Управо ћете 1) направити копију базе података која садржи све информације у вези са контактима и целокупну евиденцију позива у интерној меморији и 2) послати је имејлом. Не заборавите да избришете копију чим је будете копирали са уређаја или чим будете примили имејл." + "Избриши одмах" + "Покрени" + "Изаберите програм за слање датотеке" + "База података са контактима је у прилогу" + "У прилогу је база података са мојим контактима и свим информацијама о њима. Рукујте пажљиво." diff --git a/res/values-te/strings.xml b/res/values-te/strings.xml index e7fd9c66..951bad9c 100644 --- a/res/values-te/strings.xml +++ b/res/values-te/strings.xml @@ -27,7 +27,7 @@ "దీని నుండి వాయిస్ మెయిల్ " "కాంటాక్ట్‌ల డేటాబేస్‌ను కాపీ చేయండి" "మీరు 1) అన్ని కాంటాక్ట్‌లకు సంబంధించిన సమాచారాన్ని మరియు మొత్తం కాల్ లాగ్‌ను కలిగి ఉండే మీ డేటాబేస్ యొక్క కాపీని అంతర్గత స్టోరేజ్‌లో రూపొందించి 2) దాన్ని ఈమెయిల్‌ చేయబోతున్నారు. మీరు దాన్ని పరికరం నుండి విజయవంతంగా కాపీ చేసిన తర్వాత లేదా ఈమెయిల్‌ను స్వీకరించిన తర్వాత కాపీని తొలగించడం మర్చిపోవద్దు." - "ఇప్పుడే తొలగించు" + "ఇప్పుడే తొలగించండి" "ప్రారంభించండి" "మీ పైల్‌ను పంపడానికి ప్రోగ్రామ్‌ను ఎంచుకోండి" "కాంటాక్ట్‌ల Db జోడించబడింది" -- cgit v1.2.3 From 08ea67a0af6fea39a0b09bf8bc1090733015783f Mon Sep 17 00:00:00 2001 From: Liahav Eitan Date: Fri, 23 Dec 2022 15:11:55 +0000 Subject: Fix bug where CP2 tries to access a closed cursor Patchset 5 of ag/20470472 used try-with-resources in queryMergedContacts. That introduced the issue in b/263386251#comment2. The problem is that the cursor is closed even when there is no exception, which means that the method never returns a useful cursor. This CL returns to the version of the code from previous patchsets. Bug: 263386251 Test: adb shell content query --uri content://com.android.contacts/contacts/enterprise Change-Id: I5bf688446e399eb9a15ffea20d06af4dad5fd594 --- src/com/android/providers/contacts/ContactsProvider2.java | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/com/android/providers/contacts/ContactsProvider2.java b/src/com/android/providers/contacts/ContactsProvider2.java index 145b062a..3f289c8c 100644 --- a/src/com/android/providers/contacts/ContactsProvider2.java +++ b/src/com/android/providers/contacts/ContactsProvider2.java @@ -7466,8 +7466,9 @@ public class ContactsProvider2 extends AbstractContactsProvider private Cursor queryMergedContacts(String[] projection, String selection, String[] selectionArgs, String sortOrder, CancellationSignal cancellationSignal) { final Uri localUri = Contacts.CONTENT_URI; - try (final Cursor primaryCursor = queryLocal(localUri, projection, selection, selectionArgs, - sortOrder, Directory.DEFAULT, cancellationSignal)) {; + final Cursor primaryCursor = queryLocal(localUri, projection, selection, selectionArgs, + sortOrder, Directory.DEFAULT, cancellationSignal); + try { final int managedUserId = UserUtils.getCorpUserId(getContext()); if (managedUserId < 0) { // No managed profile or policy not allowed @@ -7480,6 +7481,11 @@ public class ContactsProvider2 extends AbstractContactsProvider primaryCursor, managedCursor }; return new MergeCursor(cursorArray); + } catch (Throwable th) { + if (primaryCursor != null) { + primaryCursor.close(); + } + throw th; } } -- cgit v1.2.3 From 0f43896cc4635a8df49ca9ae3f904198c705d722 Mon Sep 17 00:00:00 2001 From: Bill Yi Date: Thu, 29 Dec 2022 22:50:08 -0800 Subject: Import translations. DO NOT MERGE ANYWHERE Auto-generated-cl: translation import Change-Id: I31ec0c0838fa8b8e8f158d15eec1fe449b72bb51 --- res/values-b+sr+Latn/strings.xml | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/res/values-b+sr+Latn/strings.xml b/res/values-b+sr+Latn/strings.xml index 8230513a..b9427e4c 100644 --- a/res/values-b+sr+Latn/strings.xml +++ b/res/values-b+sr+Latn/strings.xml @@ -16,20 +16,20 @@ - "Android Core апликације" - "Складиште контаката" - "Контакти" - "За ажурирање контаката потребно је више меморије." - "Надограђивање меморије за контакте" - "Додирните да бисте довршили надоградњу." - "Контакти" - "Другo" - "Говорна пошта од " - "Копирање базе података са контактима" - "Управо ћете 1) направити копију базе података која садржи све информације у вези са контактима и целокупну евиденцију позива у интерној меморији и 2) послати је имејлом. Не заборавите да избришете копију чим је будете копирали са уређаја или чим будете примили имејл." - "Избриши одмах" - "Покрени" - "Изаберите програм за слање датотеке" - "База података са контактима је у прилогу" - "У прилогу је база података са мојим контактима и свим информацијама о њима. Рукујте пажљиво." + "Android Core aplikacije" + "Skladište kontakata" + "Kontakti" + "Za ažuriranje kontakata potrebno je više memorije." + "Nadograđivanje memorije za kontakte" + "Dodirnite da biste dovršili nadogradnju." + "Kontakti" + "Drugo" + "Govorna pošta od " + "Kopiranje baze podataka sa kontaktima" + "Upravo ćete 1) napraviti kopiju baze podataka koja sadrži sve informacije u vezi sa kontaktima i celokupnu evidenciju poziva u internoj memoriji i 2) poslati je imejlom. Ne zaboravite da izbrišete kopiju čim je budete kopirali sa uređaja ili čim budete primili imejl." + "Izbriši odmah" + "Pokreni" + "Izaberite program za slanje datoteke" + "Baza podataka sa kontaktima je u prilogu" + "U prilogu je baza podataka sa mojim kontaktima i svim informacijama o njima. Rukujte pažljivo." -- cgit v1.2.3 From f4275f4371b2b52be11249e36108f26748230a20 Mon Sep 17 00:00:00 2001 From: Bill Yi Date: Wed, 4 Jan 2023 07:39:12 -0800 Subject: Import translations. DO NOT MERGE ANYWHERE Auto-generated-cl: translation import Change-Id: Iffef6bf628103bd897a4b506d2d5355747d57a44 --- res/values-b+sr+Latn/strings.xml | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/res/values-b+sr+Latn/strings.xml b/res/values-b+sr+Latn/strings.xml index 8230513a..b9427e4c 100644 --- a/res/values-b+sr+Latn/strings.xml +++ b/res/values-b+sr+Latn/strings.xml @@ -16,20 +16,20 @@ - "Android Core апликације" - "Складиште контаката" - "Контакти" - "За ажурирање контаката потребно је више меморије." - "Надограђивање меморије за контакте" - "Додирните да бисте довршили надоградњу." - "Контакти" - "Другo" - "Говорна пошта од " - "Копирање базе података са контактима" - "Управо ћете 1) направити копију базе података која садржи све информације у вези са контактима и целокупну евиденцију позива у интерној меморији и 2) послати је имејлом. Не заборавите да избришете копију чим је будете копирали са уређаја или чим будете примили имејл." - "Избриши одмах" - "Покрени" - "Изаберите програм за слање датотеке" - "База података са контактима је у прилогу" - "У прилогу је база података са мојим контактима и свим информацијама о њима. Рукујте пажљиво." + "Android Core aplikacije" + "Skladište kontakata" + "Kontakti" + "Za ažuriranje kontakata potrebno je više memorije." + "Nadograđivanje memorije za kontakte" + "Dodirnite da biste dovršili nadogradnju." + "Kontakti" + "Drugo" + "Govorna pošta od " + "Kopiranje baze podataka sa kontaktima" + "Upravo ćete 1) napraviti kopiju baze podataka koja sadrži sve informacije u vezi sa kontaktima i celokupnu evidenciju poziva u internoj memoriji i 2) poslati je imejlom. Ne zaboravite da izbrišete kopiju čim je budete kopirali sa uređaja ili čim budete primili imejl." + "Izbriši odmah" + "Pokreni" + "Izaberite program za slanje datoteke" + "Baza podataka sa kontaktima je u prilogu" + "U prilogu je baza podataka sa mojim kontaktima i svim informacijama o njima. Rukujte pažljivo." -- cgit v1.2.3 From 93a8e4b728062b6254143f897baa695378c6908d Mon Sep 17 00:00:00 2001 From: Jigar Thakkar Date: Wed, 14 Dec 2022 15:32:30 +0000 Subject: Add feature flag to control app-cloning contact sharing changes Adding changes to use AppCloningDeviceConfigHelper cache to query the feature flag to control app cloning building blocks. This flag is then used to gate contact sharing changes in clone contacts provider. Turn the flag on: adb shell device_config put app_cloning enable_app_cloning_building_blocks true Turn the flag off: adb shell device_config put app_cloning enable_app_cloning_building_blocks false Bug: 253449368 Test: atest CloneContactsProvider2Tests Change-Id: Ia9e77d494d5e183993c6d670bfb55401be705a75 --- .../providers/contacts/ContactsProvider2.java | 50 ++++++++++++++++------ .../contacts/SynchronousContactsProvider2.java | 5 +++ 2 files changed, 41 insertions(+), 14 deletions(-) diff --git a/src/com/android/providers/contacts/ContactsProvider2.java b/src/com/android/providers/contacts/ContactsProvider2.java index 3f289c8c..b2627bed 100644 --- a/src/com/android/providers/contacts/ContactsProvider2.java +++ b/src/com/android/providers/contacts/ContactsProvider2.java @@ -79,7 +79,6 @@ import android.os.RemoteException; import android.os.StrictMode; import android.os.SystemClock; import android.os.UserHandle; -import android.os.UserManager; import android.preference.PreferenceManager; import android.provider.BaseColumns; import android.provider.ContactsContract; @@ -138,6 +137,7 @@ import android.util.Log; import com.android.common.content.ProjectionMap; import com.android.common.content.SyncStateContentProviderHelper; import com.android.common.io.MoreCloseables; +import com.android.internal.config.appcloning.AppCloningDeviceConfigHelper; import com.android.internal.util.ArrayUtils; import com.android.providers.contacts.ContactLookupKey.LookupKeySegment; import com.android.providers.contacts.ContactsDatabaseHelper.AccountsColumns; @@ -1500,6 +1500,8 @@ public class ContactsProvider2 extends AbstractContactsProvider private Set mMigratedPhoneAccountHandles; + private AppCloningDeviceConfigHelper mAppCloningDeviceConfigHelper; + /** * Subscription change will trigger ACTION_PHONE_ACCOUNT_REGISTERED that broadcasts new * PhoneAccountHandle that is created based on the new subscription. This receiver is used @@ -1575,6 +1577,7 @@ public class ContactsProvider2 extends AbstractContactsProvider mFastScrollingIndexCache = FastScrollingIndexCache.getInstance(getContext()); mSubscriptionManager = getContext().getSystemService(SubscriptionManager.class); + mAppCloningDeviceConfigHelper = AppCloningDeviceConfigHelper.getInstance(getContext()); mContactsHelper = getDatabaseHelper(); mDbHelper.set(mContactsHelper); @@ -2144,6 +2147,19 @@ public class ContactsProvider2 extends AbstractContactsProvider return mProfilePhotoStore; } + /** + * Returned whether the feature flag for contacts sharing for clone profile is set. If true, + * the clone contacts provider would use the parent contacts providers contacts data to serve + * its requests. + * @return true/false if contact sharing is enabled/disabled + */ + @VisibleForTesting + protected boolean isContactSharingEnabledForCloneProfile() { + // TODO(b/253449368): This method should also check for the config controlling + // all app-cloning features. + return mAppCloningDeviceConfigHelper.getEnableAppCloningBuildingBlocks(); + } + /** * Maximum dimension (height or width) of photo thumbnails. */ @@ -5629,9 +5645,7 @@ public class ContactsProvider2 extends AbstractContactsProvider return null; } - // TODO(b/253449368) - The call below should be gated by the app-cloning feature flag. - if (UserUtils.shouldUseParentsContacts(getContext()) && - isAppAllowedToUseParentUsersContacts(getCallingPackage())) { + if (shouldRedirectQueryToParentProvider()) { return queryParentProfileContactsProvider(uri, projection, selection, selectionArgs, sortOrder, cancellationSignal); } @@ -5655,6 +5669,19 @@ public class ContactsProvider2 extends AbstractContactsProvider } } + /** + * Check if the query should be redirected to the parent profile's contacts provider. + */ + private boolean shouldRedirectQueryToParentProvider() { + return isContactSharingEnabledForCloneProfile() && + UserUtils.shouldUseParentsContacts(getContext()) && + isAppAllowedToUseParentUsersContacts(getCallingPackage()); + } + + /** + * Check if the app with the given package name is allowed to use parent user's contacts to + * serve the contacts read queries. + */ @VisibleForTesting protected boolean isAppAllowedToUseParentUsersContacts(@Nullable String packageName) { try { @@ -5685,9 +5712,8 @@ public class ContactsProvider2 extends AbstractContactsProvider // The caller is the other CP2 (which has INTERACT_ACROSS_USERS), meaning the request // is on behalf of a "real" client app. - // TODO(b/253449368) - The condition below should only be checked behind the app-cloning - // feature flag - if (doesCallingProviderUseCurrentUsersContacts()) { + if (isContactSharingEnabledForCloneProfile() && + doesCallingProviderUseCurrentUsersContacts()) { // The caller is the other CP2 (which has INTERACT_ACROSS_USERS), from the child // user (of the current user) profile with the property of using parent's contacts // set. @@ -5744,9 +5770,8 @@ public class ContactsProvider2 extends AbstractContactsProvider */ @VisibleForTesting protected boolean areContactWritesEnabled() { - // TODO(b/253449368) - The condition below should only be checked behind the app-cloning - // feature flag - return !UserUtils.shouldUseParentsContacts(getContext()); + return !isContactSharingEnabledForCloneProfile() || + !UserUtils.shouldUseParentsContacts(getContext()); } @VisibleForTesting @@ -8906,10 +8931,7 @@ public class ContactsProvider2 extends AbstractContactsProvider // Redirect reads to parent provider if the corresponding user property is set and app // is allow-listed to access parent's contacts - // TODO(b/253449368) - The call below should be gated by the app-cloning feature flag - if (mode.equals("r") && - UserUtils.shouldUseParentsContacts(getContext()) && - isAppAllowedToUseParentUsersContacts(getCallingPackage())) { + if (mode.equals("r") && shouldRedirectQueryToParentProvider()) { return openAssetFileThroughParentProvider(uri, mode); } diff --git a/tests/src/com/android/providers/contacts/SynchronousContactsProvider2.java b/tests/src/com/android/providers/contacts/SynchronousContactsProvider2.java index 848c2379..ca8cb669 100644 --- a/tests/src/com/android/providers/contacts/SynchronousContactsProvider2.java +++ b/tests/src/com/android/providers/contacts/SynchronousContactsProvider2.java @@ -178,6 +178,11 @@ public class SynchronousContactsProvider2 extends ContactsProvider2 { return Locale.US; } + @Override + protected boolean isContactSharingEnabledForCloneProfile() { + return true; + } + @Override public boolean isWritableAccountWithDataSet(String accountType) { return !READ_ONLY_ACCOUNT_TYPE.equals(accountType); -- cgit v1.2.3 From b915bb73ad6905bf339f5fcf8d47a8f487b0f889 Mon Sep 17 00:00:00 2001 From: Kunal Malhotra Date: Fri, 13 Jan 2023 23:07:30 +0000 Subject: Adding a fix for trimming any large fields before they are inserted into the DB Test: manual testing done by attempting to add an imported contact with a very large field in it Change-Id: I52814fee6c5e137809020f16f332800ef9d79099 Bug: b/262595156 b/262594744 --- src/com/android/providers/contacts/DataRowHandlerForNickname.java | 6 ++++++ .../android/providers/contacts/DataRowHandlerForOrganization.java | 8 ++++++++ 2 files changed, 14 insertions(+) diff --git a/src/com/android/providers/contacts/DataRowHandlerForNickname.java b/src/com/android/providers/contacts/DataRowHandlerForNickname.java index 03b96a3a..2806cf34 100644 --- a/src/com/android/providers/contacts/DataRowHandlerForNickname.java +++ b/src/com/android/providers/contacts/DataRowHandlerForNickname.java @@ -35,9 +35,14 @@ public class DataRowHandlerForNickname extends DataRowHandlerForCommonDataKind { Nickname.LABEL); } + private void applySimpleFieldMaxSize(ContentValues cv) { + applySimpleFieldMaxSize(cv, Nickname.NAME); + } + @Override public long insert(SQLiteDatabase db, TransactionContext txContext, long rawContactId, ContentValues values) { + applySimpleFieldMaxSize(values); String nickname = values.getAsString(Nickname.NAME); long dataId = super.insert(db, txContext, rawContactId, values); @@ -53,6 +58,7 @@ public class DataRowHandlerForNickname extends DataRowHandlerForCommonDataKind { @Override public boolean update(SQLiteDatabase db, TransactionContext txContext, ContentValues values, Cursor c, boolean callerIsSyncAdapter) { + applySimpleFieldMaxSize(values); long dataId = c.getLong(DataUpdateQuery._ID); long rawContactId = c.getLong(DataUpdateQuery.RAW_CONTACT_ID); diff --git a/src/com/android/providers/contacts/DataRowHandlerForOrganization.java b/src/com/android/providers/contacts/DataRowHandlerForOrganization.java index 66a3b1bd..74f22595 100644 --- a/src/com/android/providers/contacts/DataRowHandlerForOrganization.java +++ b/src/com/android/providers/contacts/DataRowHandlerForOrganization.java @@ -22,6 +22,7 @@ import android.database.DatabaseUtils; import android.database.sqlite.SQLiteDatabase; 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.AbstractContactAggregator; @@ -37,9 +38,15 @@ public class DataRowHandlerForOrganization extends DataRowHandlerForCommonDataKi Organization.CONTENT_ITEM_TYPE, Organization.TYPE, Organization.LABEL); } + private void applySimpleFieldMaxSize(ContentValues cv) { + applySimpleFieldMaxSize(cv, Organization.COMPANY); + applySimpleFieldMaxSize(cv, Organization.TITLE); + } + @Override public long insert(SQLiteDatabase db, TransactionContext txContext, long rawContactId, ContentValues values) { + applySimpleFieldMaxSize(values); String company = values.getAsString(Organization.COMPANY); String title = values.getAsString(Organization.TITLE); @@ -52,6 +59,7 @@ public class DataRowHandlerForOrganization extends DataRowHandlerForCommonDataKi @Override public boolean update(SQLiteDatabase db, TransactionContext txContext, ContentValues values, Cursor c, boolean callerIsSyncAdapter) { + applySimpleFieldMaxSize(values); if (!super.update(db, txContext, values, c, callerIsSyncAdapter)) { return false; } -- cgit v1.2.3 From 242bb9f25b210fbfe36a384088221b54b2602b34 Mon Sep 17 00:00:00 2001 From: Pete Varvarezis Date: Wed, 25 Jan 2023 20:44:55 +0000 Subject: Migrate to new package-level profile access checks Replace deprecated API calls with newer methods: getCrossProfileCallerIdDisabled to hasManagedProfileCallerIdAccess getCrossProfileContactsSearchDisabled to hasManagedProfileContactsAccess Prevent querying remote directory when managed profile is suspended Test: atest ManagedProfileContactsTest EnterprisePolicyGuardTest Bug: 264879018 Bug: 265934326 Change-Id: I8a0a07ac149c198ae9e2a61636165299edbceacd --- .../contacts/enterprise/EnterprisePolicyGuard.java | 15 +++++--- .../enterprise/EnterprisePolicyGuardTest.java | 42 +++++++++++++++++++--- 2 files changed, 49 insertions(+), 8 deletions(-) diff --git a/src/com/android/providers/contacts/enterprise/EnterprisePolicyGuard.java b/src/com/android/providers/contacts/enterprise/EnterprisePolicyGuard.java index db952d66..12a03053 100644 --- a/src/com/android/providers/contacts/enterprise/EnterprisePolicyGuard.java +++ b/src/com/android/providers/contacts/enterprise/EnterprisePolicyGuard.java @@ -22,6 +22,7 @@ import android.content.Context; import android.content.pm.PackageManager; import android.net.Uri; import android.os.UserHandle; +import android.os.UserManager; import android.provider.ContactsContract; import android.provider.ContactsContract.Directory; import android.provider.Settings; @@ -74,12 +75,14 @@ public class EnterprisePolicyGuard { return true; } - // TODO(b/240954287): replace these by new DPM APIs that take package name into account - final boolean isCallerIdEnabled = !mDpm.getCrossProfileCallerIdDisabled(currentHandle); + final boolean isCallerIdEnabled = + mDpm.hasManagedProfileCallerIdAccess(currentHandle, callingPackage); final boolean isContactsSearchPolicyEnabled = - !mDpm.getCrossProfileContactsSearchDisabled(currentHandle); + mDpm.hasManagedProfileContactsAccess(currentHandle, callingPackage); final boolean isBluetoothContactSharingEnabled = !mDpm.getBluetoothContactSharingDisabled(currentHandle); + final boolean isManagedProfileEnabled = !UserUtils.getUserManager(mContext) + .isQuietModeEnabled(new UserHandle(UserUtils.getCorpUserId(mContext))); final boolean isContactRemoteSearchUserEnabled = isContactRemoteSearchUserSettingEnabled(); final String directory = uri.getQueryParameter(ContactsContract.DIRECTORY_PARAM_KEY); @@ -89,16 +92,19 @@ public class EnterprisePolicyGuard { Log.v(TAG, "isContactsSearchPolicyEnabled: " + isContactsSearchPolicyEnabled); Log.v(TAG, "isBluetoothContactSharingEnabled: " + isBluetoothContactSharingEnabled); Log.v(TAG, "isContactRemoteSearchUserEnabled: " + isContactRemoteSearchUserEnabled); + Log.v(TAG, "isManagedProfileEnabled: " + isManagedProfileEnabled); } // If it is a remote directory, it is allowed only when // (i) The uri supports directory // (ii) User enables it in settings + // (iii) The managed profile is enabled if (directory != null) { final long directoryId = Long.parseLong(directory); if (Directory.isRemoteDirectoryId(directoryId) && !(isCrossProfileDirectorySupported(uri) - && isContactRemoteSearchUserEnabled)) { + && isContactRemoteSearchUserEnabled + && isManagedProfileEnabled)) { return false; } } @@ -227,4 +233,5 @@ public class EnterprisePolicyGuard { mContext.getContentResolver(), MANAGED_PROFILE_CONTACT_REMOTE_SEARCH, 0) == 1; } + } diff --git a/tests/src/com/android/providers/contacts/enterprise/EnterprisePolicyGuardTest.java b/tests/src/com/android/providers/contacts/enterprise/EnterprisePolicyGuardTest.java index 9b5fca1a..5aedd8d0 100644 --- a/tests/src/com/android/providers/contacts/enterprise/EnterprisePolicyGuardTest.java +++ b/tests/src/com/android/providers/contacts/enterprise/EnterprisePolicyGuardTest.java @@ -184,6 +184,32 @@ public class EnterprisePolicyGuardTest extends FixedAndroidTestCase { checkCrossProfile(guard, URI_CONTACTS_ID_PHOTO, false); checkCrossProfile(guard, URI_CONTACTS_ID_DISPLAY_PHOTO, false); checkCrossProfile(guard, URI_OTHER, false); + + // ManagedProfile is paused + context = getMockContext(true, true, false); + guard = new EnterprisePolicyGuardTestable(context, true); + checkCrossProfile(guard, appendRemoteDirectoryId(URI_PHONE_LOOKUP), false); + checkCrossProfile(guard, appendRemoteDirectoryId(URI_EMAILS_LOOKUP), false); + checkCrossProfile(guard, appendRemoteDirectoryId(URI_CONTACTS_FILTER), false); + checkCrossProfile(guard, appendRemoteDirectoryId(URI_PHONES_FILTER), false); + checkCrossProfile(guard, appendRemoteDirectoryId(URI_CALLABLES_FILTER), false); + checkCrossProfile(guard, appendRemoteDirectoryId(URI_EMAILS_FILTER), false); + checkCrossProfile(guard, URI_DIRECTORY_FILE, false); + + // Always allow uri with no directory support. + checkCrossProfile(guard, URI_DIRECTORIES, true); + checkCrossProfile(guard, URI_DIRECTORIES_ID, true); + checkCrossProfile(guard, URI_CONTACTS_ID_PHOTO, true); + checkCrossProfile(guard, URI_CONTACTS_ID_DISPLAY_PHOTO, true); + checkCrossProfile(guard, URI_OTHER, false); + + // Always allow uri with no remote directory id. + checkCrossProfile(guard, URI_PHONE_LOOKUP, true); + checkCrossProfile(guard, URI_EMAILS_LOOKUP, true); + checkCrossProfile(guard, URI_CONTACTS_FILTER, true); + checkCrossProfile(guard, URI_PHONES_FILTER, true); + checkCrossProfile(guard, URI_CALLABLES_FILTER, true); + checkCrossProfile(guard, URI_EMAILS_FILTER, true); } public void testCrossProfile_userSettingOff() { @@ -215,6 +241,7 @@ public class EnterprisePolicyGuardTest extends FixedAndroidTestCase { checkCrossProfile(guard, URI_EMAILS_FILTER, true); } + private static Uri appendRemoteDirectoryId(Uri uri) { return appendDirectoryId(uri, REMOTE_DIRECTORY_ID); } @@ -261,11 +288,16 @@ public class EnterprisePolicyGuardTest extends FixedAndroidTestCase { private Context getMockContext(boolean isCallerIdEnabled, boolean isContactsSearchEnabled) { + return getMockContext(isCallerIdEnabled, isContactsSearchEnabled, true); + } + + private Context getMockContext(boolean isCallerIdEnabled, boolean isContactsSearchEnabled, + boolean isManagedProfileEnabled) { DevicePolicyManager mockDpm = mock(DevicePolicyManager.class); - when(mockDpm.getCrossProfileCallerIdDisabled(Matchers.any())) - .thenReturn(!isCallerIdEnabled); - when(mockDpm.getCrossProfileContactsSearchDisabled(Matchers.any())) - .thenReturn(!isContactsSearchEnabled); + when(mockDpm.hasManagedProfileCallerIdAccess(Matchers.any(),Matchers.any())) + .thenReturn(isCallerIdEnabled); + when(mockDpm.hasManagedProfileContactsAccess(Matchers.any(),Matchers.any())) + .thenReturn(isContactsSearchEnabled); List userInfos = MANAGED_USERINFO_LIST; UserManager mockUm = mock(UserManager.class); @@ -273,6 +305,8 @@ public class EnterprisePolicyGuardTest extends FixedAndroidTestCase { when(mockUm.getUsers()).thenReturn(userInfos); when(mockUm.getProfiles(Matchers.anyInt())).thenReturn(userInfos); when(mockUm.getProfileParent(WORK_USER_ID)).thenReturn(CURRENT_USER_INFO); + when(mockUm.isQuietModeEnabled(UserHandle.of(WORK_USER_ID))) + .thenReturn(!isManagedProfileEnabled); PackageManager mockPm = mock(PackageManager.class); when(mockPm.checkPermission(INTERACT_ACROSS_USERS, CALLING_PACKAGE)) -- cgit v1.2.3 From 5adde96958c3e8c94c317225513824b89942760f Mon Sep 17 00:00:00 2001 From: Michael Groover Date: Thu, 9 Mar 2023 23:00:19 -0600 Subject: Correct broadcast action for ContactsProvider2 receiver The runtime receiver in ContactsProvider2 was reported as part of a lint check due to registering for a non-system broadcast. The action for which it is registering is expected as an extra in the ACTION_PHONE_ACCOUNT_REGISTERED broadcast; since the receiver in this class has a check for this action, this commit updates the IntentFilter for this receiver to use this broadcast action instead. Bug: 234659204 Test: ContactsProvider2Test Change-Id: I6d3591521243f2932f5b6ef8a0f35ab894bdd331 --- src/com/android/providers/contacts/ContactsProvider2.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/com/android/providers/contacts/ContactsProvider2.java b/src/com/android/providers/contacts/ContactsProvider2.java index b2627bed..9efab3a4 100644 --- a/src/com/android/providers/contacts/ContactsProvider2.java +++ b/src/com/android/providers/contacts/ContactsProvider2.java @@ -1589,7 +1589,7 @@ public class ContactsProvider2 extends AbstractContactsProvider if (mContactsHelper.getPhoneAccountHandleMigrationUtils() .isPhoneAccountMigrationPending()) { - IntentFilter filter = new IntentFilter(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE); + IntentFilter filter = new IntentFilter(TelecomManager.ACTION_PHONE_ACCOUNT_REGISTERED); getContext().registerReceiver(mBroadcastReceiver, filter); } -- cgit v1.2.3 From ed53d3d7df188b11c1e8ea3cada8c174e7265f9c Mon Sep 17 00:00:00 2001 From: Jigar Thakkar Date: Mon, 6 Mar 2023 00:02:27 +0000 Subject: Restrict contact access to launchable cloned apps Earlier, we used the cloneable_apps.xml as an allowlist to restrict cloned apps to access cross-profile contacts. This made it difficult to maintain CTS tests for clone contacts provider as the allowlist is empty by default. This change moves the allowlisting logic to use package manager call to identify launchable activities and use them to restrict access to cross-profile contacts. The mLaunchableCloneAppsCache is also added to ensure the results for a package are cached. Since, app uids are used as a key every cache entry, the entries are refreshed after 60s to ensure the uid values are not stale. The cache is also cleaned up once every 7 days to remove outdated entries using additional space (apps that have been removed). Bug: 271752108 Test: atest CloneContactsProvider2Test - added a couple of test case corresponding to the app allowlist check Change-Id: I6af0a69ac0c84a85e6cb39b2d573dec78a050a81 Change-Id: Id7282c9bf9b58fd32cf64cb0842d5e34c9ca2f18 --- .../providers/contacts/ContactsProvider2.java | 96 ++++++++++++++++++++-- .../contacts/CloneContactsProvider2Test.java | 71 +++++++++++++--- .../contacts/ContactsMockPackageManager.java | 7 ++ 3 files changed, 154 insertions(+), 20 deletions(-) diff --git a/src/com/android/providers/contacts/ContactsProvider2.java b/src/com/android/providers/contacts/ContactsProvider2.java index b2627bed..d601fe68 100644 --- a/src/com/android/providers/contacts/ContactsProvider2.java +++ b/src/com/android/providers/contacts/ContactsProvider2.java @@ -29,6 +29,7 @@ import android.accounts.AccountManager; import android.accounts.OnAccountsUpdateListener; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.SuppressLint; import android.annotation.WorkerThread; import android.app.AppOpsManager; import android.app.SearchManager; @@ -133,10 +134,12 @@ import android.text.TextUtils; import android.util.ArrayMap; import android.util.ArraySet; import android.util.Log; +import android.util.SparseArray; import com.android.common.content.ProjectionMap; import com.android.common.content.SyncStateContentProviderHelper; import com.android.common.io.MoreCloseables; +import com.android.internal.annotations.GuardedBy; import com.android.internal.config.appcloning.AppCloningDeviceConfigHelper; import com.android.internal.util.ArrayUtils; import com.android.providers.contacts.ContactLookupKey.LookupKeySegment; @@ -286,6 +289,14 @@ public class ContactsProvider2 extends AbstractContactsProvider /** Rate limit (in milliseconds) for dangling contacts cleanup. Do it at most once per day. */ private static final int DANGLING_CONTACTS_CLEANUP_RATE_LIMIT = 24 * 60 * 60 * 1000; + /** Time after which an entry in the launchable clone packages cache is invalidated and needs to + * be refreshed. + */ + private static final int LAUNCHABLE_CLONE_APPS_CACHE_ENTRY_REFRESH_INTERVAL = 10 * 60 * 1000; + + /** This value indicates the frequency of cleanup of the launchable clone apps cache */ + private static final int LAUNCHABLE_CLONE_APPS_CACHE_CLEANUP_LIMIT = 7 * 24 * 60 * 60 * 1000; + /** Maximum length of a phone number that can be inserted into the database */ private static final int PHONE_NUMBER_LENGTH_LIMIT = 1000; @@ -1375,6 +1386,18 @@ public class ContactsProvider2 extends AbstractContactsProvider long groupId; } + @VisibleForTesting + protected static class LaunchableCloneAppsCacheEntry { + boolean doesAppHaveLaunchableActivity; + long lastUpdatedAt; + + public LaunchableCloneAppsCacheEntry(boolean doesAppHaveLaunchableActivity, + long lastUpdatedAt) { + this.doesAppHaveLaunchableActivity = doesAppHaveLaunchableActivity; + this.lastUpdatedAt = lastUpdatedAt; + } + } + /** * The thread-local holder of the active transaction. Shared between this and the profile * provider, to keep transactions on both databases synchronized. @@ -1437,6 +1460,18 @@ public class ContactsProvider2 extends AbstractContactsProvider */ private ArrayMap> mGroupIdCache = new ArrayMap<>(); + /** + * Cache to store info on whether a cloned app has a launchable activity. This will be used to + * provide it access to query cross-profile contacts. + * The key to this map is the uid of the cloned app. The entries in the cache are refreshed + * after {@link LAUNCHABLE_CLONE_APPS_CACHE_ENTRY_REFRESH_INTERVAL} to ensure the uid values + * stored in the cache aren't stale. + */ + @VisibleForTesting + @GuardedBy("mLaunchableCloneAppsCache") + protected final SparseArray mLaunchableCloneAppsCache = + new SparseArray<>(); + /** * Sub-provider for handling profile requests against the profile database. */ @@ -1488,6 +1523,9 @@ public class ContactsProvider2 extends AbstractContactsProvider private long mLastDanglingContactsCleanup = 0; + @GuardedBy("mLaunchableCloneAppsCache") + private long mLastLaunchableCloneAppsCacheCleanup = 0; + private FastScrollingIndexCache mFastScrollingIndexCache; // Stats about FastScrollingIndex. @@ -1730,6 +1768,13 @@ public class ContactsProvider2 extends AbstractContactsProvider return new PhotoPriorityResolver(context); } + @VisibleForTesting + protected SparseArray getLaunchableCloneAppsCacheForTesting() { + synchronized (mLaunchableCloneAppsCache) { + return mLaunchableCloneAppsCache; + } + } + protected void scheduleBackgroundTask(int task) { scheduleBackgroundTask(task, null); } @@ -5684,17 +5729,50 @@ public class ContactsProvider2 extends AbstractContactsProvider */ @VisibleForTesting protected boolean isAppAllowedToUseParentUsersContacts(@Nullable String packageName) { - try { - for (String appName: getContext().getResources() - .getStringArray(com.android.internal.R.array.cloneable_apps)) { - if (packageName != null && packageName.equals(appName)) { - return true; - } + final int callingUid = Binder.getCallingUid(); + final UserHandle user = Binder.getCallingUserHandle(); + + synchronized (mLaunchableCloneAppsCache) { + maybeCleanupLaunchableCloneAppsCacheLocked(); + + final long now = System.currentTimeMillis(); + LaunchableCloneAppsCacheEntry cacheEntry = mLaunchableCloneAppsCache.get(callingUid); + if (cacheEntry == null || ((now - cacheEntry.lastUpdatedAt) + >= LAUNCHABLE_CLONE_APPS_CACHE_ENTRY_REFRESH_INTERVAL)) { + boolean result = doesPackageHaveALauncherActivity(packageName, user); + mLaunchableCloneAppsCache.put(callingUid, + new LaunchableCloneAppsCacheEntry(result, now)); } - } catch (NotFoundException nfe) { - Log.w(TAG, "Resource corresponding to list of cloneable apps not found"); + return mLaunchableCloneAppsCache.get(callingUid).doesAppHaveLaunchableActivity; } - return false; + } + + /** + * Clean up the launchable clone apps cache to ensure it doesn't have any stale entries taking + * up additional space. The frequency of the cleanup is governed by {@link + * LAUNCHABLE_CLONE_APPS_CACHE_CLEANUP_LIMIT}. + */ + @GuardedBy("mLaunchableCloneAppsCache") + private void maybeCleanupLaunchableCloneAppsCacheLocked() { + long now = System.currentTimeMillis(); + if (now - mLastLaunchableCloneAppsCacheCleanup + >= LAUNCHABLE_CLONE_APPS_CACHE_CLEANUP_LIMIT) { + mLaunchableCloneAppsCache.clear(); + mLastLaunchableCloneAppsCacheCleanup = now; + } + } + + @VisibleForTesting + @SuppressLint("AndroidFrameworkRequiresPermission") + protected boolean doesPackageHaveALauncherActivity(String packageName, UserHandle user) { + Intent launcherCategoryIntent = new Intent(Intent.ACTION_MAIN) + .addCategory(Intent.CATEGORY_LAUNCHER) + .setPackage(packageName); + final PackageManager pm = getContext().getPackageManager(); + return !pm.queryIntentActivitiesAsUser(launcherCategoryIntent, + PackageManager.ResolveInfoFlags.of(PackageManager.GET_ACTIVITIES), + user) + .isEmpty(); } private boolean isCrossUserQueryAllowed(Uri uri) { diff --git a/tests/src/com/android/providers/contacts/CloneContactsProvider2Test.java b/tests/src/com/android/providers/contacts/CloneContactsProvider2Test.java index 48a00da2..26a78143 100644 --- a/tests/src/com/android/providers/contacts/CloneContactsProvider2Test.java +++ b/tests/src/com/android/providers/contacts/CloneContactsProvider2Test.java @@ -34,19 +34,17 @@ import android.content.OperationApplicationException; import android.content.res.AssetFileDescriptor; import android.database.Cursor; import android.net.Uri; +import android.os.Binder; import android.os.Build; import android.os.Bundle; import android.os.RemoteException; import android.provider.CallLog; import android.provider.ContactsContract; -import android.util.Log; +import android.util.SparseArray; import androidx.test.filters.MediumTest; import androidx.test.filters.SdkSuppress; -import com.android.providers.contacts.testutil.DataUtil; -import com.android.providers.contacts.testutil.RawContactUtil; - import org.junit.Assert; import java.io.FileInputStream; @@ -142,10 +140,10 @@ public class CloneContactsProvider2Test extends BaseContactsProvider2Test { } /** - * Asserts that no contacts are returned when queried by the given contacts actor + * Asserts that no contacts are returned when queried by the given contacts provider */ - private void assertContactsProviderEmpty(ContactsActor contactsActor) { - Cursor cursor = contactsActor.resolver.query(ContactsContract.RawContacts.CONTENT_URI, + private void assertContactsProviderEmpty(ContactsProvider2 contactsProvider2) { + Cursor cursor = contactsProvider2.query(ContactsContract.RawContacts.CONTENT_URI, new String[]{ContactsContract.RawContactsEntity._ID}, null /* queryArgs */, null /* cancellationSignal */); assertNotNull(cursor); @@ -180,7 +178,12 @@ public class CloneContactsProvider2Test extends BaseContactsProvider2Test { .appendPath("0") .build(); assertUriEquals(expectedUri, resultUri); - assertContactsProviderEmpty(mCloneContactsActor); + // No contacts should be present in both clone and primary providers + assertContactsProviderEmpty(getContactsProvider()); + doReturn(false) + .when(mCloneContactsProvider).isAppAllowedToUseParentUsersContacts(any()); + assertContactsProviderEmpty(mCloneContactsProvider); + } public void testPrimaryContactsProviderInsert() { @@ -257,7 +260,11 @@ public class CloneContactsProvider2Test extends BaseContactsProvider2Test { // Check results, no rows should have been affected assertEquals(0, bulkInsertResult); - assertContactsProviderEmpty(mCloneContactsActor); + // No contacts should be present in both clone and primary providers + assertContactsProviderEmpty(getContactsProvider()); + doReturn(false) + .when(mCloneContactsProvider).isAppAllowedToUseParentUsersContacts(any()); + assertContactsProviderEmpty(mCloneContactsProvider); } public void testCloneContactsApplyBatch() @@ -294,8 +301,10 @@ public class CloneContactsProvider2Test extends BaseContactsProvider2Test { assertRejectedApplyBatchResults(res, ops); // No contacts should be present in both clone and primary providers - assertContactsProviderEmpty(mCloneContactsActor); - assertContactsProviderEmpty(mActor); + assertContactsProviderEmpty(getContactsProvider()); + doReturn(false) + .when(mCloneContactsProvider).isAppAllowedToUseParentUsersContacts(any()); + assertContactsProviderEmpty(mCloneContactsProvider); } public void testCloneContactsCallOperation() { @@ -452,4 +461,44 @@ public class CloneContactsProvider2Test extends BaseContactsProvider2Test { verify(mCloneContactsProvider, times(1)) .openAssetFile(eq(contacts.getCombinedUri()), any()); } + + public void testIsAppAllowedToUseParentUsersContacts_AppInAllowlistCacheEmpty() + throws InterruptedException { + String testPackageName = mCloneContactsActor.packageName; + int processUid = Binder.getCallingUid(); + doReturn(true) + .when(mCloneContactsProvider) + .doesPackageHaveALauncherActivity(eq(testPackageName), any()); + + SparseArray launchableCloneAppsCache = + mCloneContactsProvider.getLaunchableCloneAppsCacheForTesting(); + launchableCloneAppsCache.clear(); + boolean appAllowedToUseParentUsersContacts = + mCloneContactsProvider.isAppAllowedToUseParentUsersContacts(testPackageName); + assertTrue(appAllowedToUseParentUsersContacts); + + // Check that the cache has been updated with an entry corresponding to current app uid + ContactsProvider2.LaunchableCloneAppsCacheEntry cacheEntry = + launchableCloneAppsCache.get(processUid); + assertNotNull(cacheEntry); + assertEquals(1, launchableCloneAppsCache.size()); + assertTrue(cacheEntry.doesAppHaveLaunchableActivity); + } + + public void testIsAppAllowedToUseParentUsersContacts_AppNotInAllowlistCacheEmtpy() { + String testPackageName = mCloneContactsActor.packageName; + int processUid = Binder.getCallingUid(); + + SparseArray launchableCloneAppsCache = + mCloneContactsProvider.getLaunchableCloneAppsCacheForTesting(); + launchableCloneAppsCache.clear(); + assertFalse(mCloneContactsProvider.isAppAllowedToUseParentUsersContacts(testPackageName)); + + // Check that the cache has been updated with an entry corresponding to current app uid + ContactsProvider2.LaunchableCloneAppsCacheEntry cacheEntry = + launchableCloneAppsCache.get(processUid); + assertNotNull(cacheEntry); + assertEquals(1, launchableCloneAppsCache.size()); + assertFalse(cacheEntry.doesAppHaveLaunchableActivity); + } } diff --git a/tests/src/com/android/providers/contacts/ContactsMockPackageManager.java b/tests/src/com/android/providers/contacts/ContactsMockPackageManager.java index fea63b23..c583e1df 100644 --- a/tests/src/com/android/providers/contacts/ContactsMockPackageManager.java +++ b/tests/src/com/android/providers/contacts/ContactsMockPackageManager.java @@ -24,6 +24,7 @@ import android.content.pm.ProviderInfo; import android.content.pm.ResolveInfo; import android.content.res.Resources; import android.os.Binder; +import android.os.UserHandle; import android.test.mock.MockPackageManager; import java.util.ArrayList; @@ -154,4 +155,10 @@ public class ContactsMockPackageManager extends MockPackageManager { public int checkPermission(String permission, String packageName) { return PERMISSION_GRANTED; } + + @Override + public List queryIntentActivitiesAsUser(Intent intent, ResolveInfoFlags flags, + UserHandle user) { + return new ArrayList<>(); + } } -- cgit v1.2.3 From 4107ad15218cc2e89fe3138bdcf6cceddc322e26 Mon Sep 17 00:00:00 2001 From: Bill Yi Date: Mon, 17 Apr 2023 14:23:35 -0700 Subject: Import translations. DO NOT MERGE ANYWHERE Auto-generated-cl: translation import Change-Id: I76cfd87728983255f94a967ae7891ad4390a53fc --- res/values-as/strings.xml | 2 +- res/values-en-rCA/strings.xml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/res/values-as/strings.xml b/res/values-as/strings.xml index dff8c453..7843081c 100644 --- a/res/values-as/strings.xml +++ b/res/values-as/strings.xml @@ -16,7 +16,7 @@ - "Android Core এপসমূহ" + "Android Core এপ্‌সমূহ" "সম্পৰ্কসমূহৰ ষ্ট’ৰেজ" "সম্পর্কবোৰ" "সম্পৰ্কসূচী আপগ্ৰেড কৰিবলৈ অধিক মেম\'ৰিৰ প্ৰয়োজন।" diff --git a/res/values-en-rCA/strings.xml b/res/values-en-rCA/strings.xml index c62b699a..4b621199 100644 --- a/res/values-en-rCA/strings.xml +++ b/res/values-en-rCA/strings.xml @@ -19,7 +19,7 @@ "Android Core Apps" "Contacts Storage" "Contacts" - "Contact upgrade needs more memory." + "Contacts upgrade needs more memory." "Upgrading storage for contacts" "Tap to complete the upgrade." "Contacts" @@ -29,7 +29,7 @@ "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." "Delete now" "Start" - "Choose a programme to send your file" + "Choose a program to send your file" "Contacts Db attached" "Attached is my contacts database with all my contacts information. Handle with care." -- cgit v1.2.3 From b83684eea48a5877169db21aa058b2f7a9f9c337 Mon Sep 17 00:00:00 2001 From: Bill Yi Date: Mon, 17 Apr 2023 14:24:02 -0700 Subject: Import translations. DO NOT MERGE ANYWHERE Auto-generated-cl: translation import Change-Id: I452f1e8b22d65d76df0c54d1c7ed7098ee2d6866 --- res/values-as/strings.xml | 2 +- res/values-en-rCA/strings.xml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/res/values-as/strings.xml b/res/values-as/strings.xml index dff8c453..7843081c 100644 --- a/res/values-as/strings.xml +++ b/res/values-as/strings.xml @@ -16,7 +16,7 @@ - "Android Core এপসমূহ" + "Android Core এপ্‌সমূহ" "সম্পৰ্কসমূহৰ ষ্ট’ৰেজ" "সম্পর্কবোৰ" "সম্পৰ্কসূচী আপগ্ৰেড কৰিবলৈ অধিক মেম\'ৰিৰ প্ৰয়োজন।" diff --git a/res/values-en-rCA/strings.xml b/res/values-en-rCA/strings.xml index c62b699a..4b621199 100644 --- a/res/values-en-rCA/strings.xml +++ b/res/values-en-rCA/strings.xml @@ -19,7 +19,7 @@ "Android Core Apps" "Contacts Storage" "Contacts" - "Contact upgrade needs more memory." + "Contacts upgrade needs more memory." "Upgrading storage for contacts" "Tap to complete the upgrade." "Contacts" @@ -29,7 +29,7 @@ "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." "Delete now" "Start" - "Choose a programme to send your file" + "Choose a program to send your file" "Contacts Db attached" "Attached is my contacts database with all my contacts information. Handle with care." -- cgit v1.2.3 From dc389701e32120d49852f9cedb31b40d1e040399 Mon Sep 17 00:00:00 2001 From: Bill Yi Date: Mon, 17 Apr 2023 14:24:27 -0700 Subject: Import translations. DO NOT MERGE ANYWHERE Auto-generated-cl: translation import Change-Id: Ice4e6c24bb90012afbfad1a930f0b30699e1982b --- res/values-as/strings.xml | 2 +- res/values-en-rCA/strings.xml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/res/values-as/strings.xml b/res/values-as/strings.xml index dff8c453..7843081c 100644 --- a/res/values-as/strings.xml +++ b/res/values-as/strings.xml @@ -16,7 +16,7 @@ - "Android Core এপসমূহ" + "Android Core এপ্‌সমূহ" "সম্পৰ্কসমূহৰ ষ্ট’ৰেজ" "সম্পর্কবোৰ" "সম্পৰ্কসূচী আপগ্ৰেড কৰিবলৈ অধিক মেম\'ৰিৰ প্ৰয়োজন।" diff --git a/res/values-en-rCA/strings.xml b/res/values-en-rCA/strings.xml index c62b699a..4b621199 100644 --- a/res/values-en-rCA/strings.xml +++ b/res/values-en-rCA/strings.xml @@ -19,7 +19,7 @@ "Android Core Apps" "Contacts Storage" "Contacts" - "Contact upgrade needs more memory." + "Contacts upgrade needs more memory." "Upgrading storage for contacts" "Tap to complete the upgrade." "Contacts" @@ -29,7 +29,7 @@ "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." "Delete now" "Start" - "Choose a programme to send your file" + "Choose a program to send your file" "Contacts Db attached" "Attached is my contacts database with all my contacts information. Handle with care." -- cgit v1.2.3 From 38d26e6c64e7d732305448cf81b09fbe9d0d0b60 Mon Sep 17 00:00:00 2001 From: Gyanesh Mittal Date: Wed, 26 Apr 2023 18:09:09 +0000 Subject: Set flag to skip old duplicated broadcasts Set delivery group policy as "DELIVERY_GROUP_POLICY_MOST_RECENT" and delivery policy as "DEFERRAL_POLICY_UNTIL_ACTIVE" for broadcast "android.provider.action.SIM_ACCOUNTS_CHANGED", Then only the most recent broadcast in the delivery group need to be delivered after the receiver back to active and the rest can be dropped. Bug: 279111903 Change-Id: Ie014b3f16def45f8bea807fe74c41fc143e5f509 --- .../android/providers/contacts/ContactsProvider2.java | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/com/android/providers/contacts/ContactsProvider2.java b/src/com/android/providers/contacts/ContactsProvider2.java index 4f496dfd..e77526f0 100644 --- a/src/com/android/providers/contacts/ContactsProvider2.java +++ b/src/com/android/providers/contacts/ContactsProvider2.java @@ -32,6 +32,7 @@ import android.annotation.Nullable; import android.annotation.SuppressLint; import android.annotation.WorkerThread; import android.app.AppOpsManager; +import android.app.BroadcastOptions; import android.app.SearchManager; import android.content.BroadcastReceiver; import android.content.ContentProviderOperation; @@ -2472,6 +2473,17 @@ public class ContactsProvider2 extends AbstractContactsProvider } } + private void notifySimAccountsChanged() { + // This allows us to discard older broadcasts still waiting to be delivered. + final Bundle options = BroadcastOptions.makeBasic() + .setDeliveryGroupPolicy(BroadcastOptions.DELIVERY_GROUP_POLICY_MOST_RECENT) + .setDeferralPolicy(BroadcastOptions.DEFERRAL_POLICY_UNTIL_ACTIVE) + .toBundle(); + + getContext().sendBroadcast(new Intent(SimContacts.ACTION_SIM_ACCOUNTS_CHANGED), null, + options); + } + @Override public Bundle call(String method, String arg, Bundle extras) { waitForAccess(mReadAccessLatch); @@ -2529,7 +2541,7 @@ public class ContactsProvider2 extends AbstractContactsProvider } finally { db.endTransaction(); } - getContext().sendBroadcast(new Intent(SimContacts.ACTION_SIM_ACCOUNTS_CHANGED)); + notifySimAccountsChanged(); return response; } else if (SimContacts.REMOVE_SIM_ACCOUNT_METHOD.equals(method)) { ContactsPermissions.enforceCallingOrSelfPermission(getContext(), @@ -2549,7 +2561,7 @@ public class ContactsProvider2 extends AbstractContactsProvider } finally { db.endTransaction(); } - getContext().sendBroadcast(new Intent(SimContacts.ACTION_SIM_ACCOUNTS_CHANGED)); + notifySimAccountsChanged(); return response; } else if (SimContacts.QUERY_SIM_ACCOUNTS_METHOD.equals(method)) { ContactsPermissions.enforceCallingOrSelfPermission(getContext(), READ_PERMISSION); -- cgit v1.2.3 From de593df5b54d75478f5ede312abeac8d7008003d Mon Sep 17 00:00:00 2001 From: Jigar Thakkar Date: Wed, 19 Apr 2023 19:44:33 +0000 Subject: Add check for clone building blocks config Bug: 253449368 Test: Tested by flashing on test device Change-Id: Ie411ccfe24d4735277726f4ee04b025cde5ec7fa --- src/com/android/providers/contacts/ContactsProvider2.java | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/com/android/providers/contacts/ContactsProvider2.java b/src/com/android/providers/contacts/ContactsProvider2.java index e77526f0..f4ce5c74 100644 --- a/src/com/android/providers/contacts/ContactsProvider2.java +++ b/src/com/android/providers/contacts/ContactsProvider2.java @@ -140,6 +140,7 @@ import android.util.SparseArray; import com.android.common.content.ProjectionMap; import com.android.common.content.SyncStateContentProviderHelper; import com.android.common.io.MoreCloseables; +import com.android.internal.R; import com.android.internal.annotations.GuardedBy; import com.android.internal.config.appcloning.AppCloningDeviceConfigHelper; import com.android.internal.util.ArrayUtils; @@ -2194,16 +2195,17 @@ public class ContactsProvider2 extends AbstractContactsProvider } /** - * Returned whether the feature flag for contacts sharing for clone profile is set. If true, - * the clone contacts provider would use the parent contacts providers contacts data to serve - * its requests. + * Returns whether contacts sharing is enabled allowing the clone contacts provider to use the + * parent contacts providers contacts data to serve its requests. The method returns true if + * the device supports clone profile contacts sharing and the feature flag for the same is + * turned on. + * * @return true/false if contact sharing is enabled/disabled */ @VisibleForTesting protected boolean isContactSharingEnabledForCloneProfile() { - // TODO(b/253449368): This method should also check for the config controlling - // all app-cloning features. - return mAppCloningDeviceConfigHelper.getEnableAppCloningBuildingBlocks(); + return getContext().getResources().getBoolean(R.bool.config_enableAppCloningBuildingBlocks) + && mAppCloningDeviceConfigHelper.getEnableAppCloningBuildingBlocks(); } /** -- cgit v1.2.3 From 7bbfefea1ae86daa7a9898f040a190b212beca11 Mon Sep 17 00:00:00 2001 From: Bill Yi Date: Tue, 9 May 2023 16:15:01 -0700 Subject: Import translations. DO NOT MERGE ANYWHERE Auto-generated-cl: translation import Change-Id: Iea111d4a5b558d3f124ae6293899621d0daa27d0 --- res/values-et/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/res/values-et/strings.xml b/res/values-et/strings.xml index d5dbf950..bcaedf7a 100644 --- a/res/values-et/strings.xml +++ b/res/values-et/strings.xml @@ -19,7 +19,7 @@ "Androidi tuumrakendused" "Kontaktiruum" "Kontaktid" - "Kontaktisikute uuendamiseks on vaja rohkem mälu" + "Kontaktisikute uuendamiseks on vaja rohkem mäluruumi." "Kontaktide salvestusruumi uuendamine" "Puudutage täiendamise lõpetamiseks." "Kontaktid" -- cgit v1.2.3 From 6a9bb9f914e66a6cc6b24ba1c53eaddeb84aab36 Mon Sep 17 00:00:00 2001 From: Bill Yi Date: Tue, 9 May 2023 16:15:21 -0700 Subject: Import translations. DO NOT MERGE ANYWHERE Auto-generated-cl: translation import Change-Id: I9ad7e35beaa492f738e6c107c3ebfb03e2c39c00 --- res/values-et/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/res/values-et/strings.xml b/res/values-et/strings.xml index d5dbf950..bcaedf7a 100644 --- a/res/values-et/strings.xml +++ b/res/values-et/strings.xml @@ -19,7 +19,7 @@ "Androidi tuumrakendused" "Kontaktiruum" "Kontaktid" - "Kontaktisikute uuendamiseks on vaja rohkem mälu" + "Kontaktisikute uuendamiseks on vaja rohkem mäluruumi." "Kontaktide salvestusruumi uuendamine" "Puudutage täiendamise lõpetamiseks." "Kontaktid" -- cgit v1.2.3 From 07e93ac3b474b5777a7e273a84c194c0cca57b4a Mon Sep 17 00:00:00 2001 From: Bill Yi Date: Tue, 9 May 2023 16:15:58 -0700 Subject: Import translations. DO NOT MERGE ANYWHERE Auto-generated-cl: translation import Change-Id: Id9250995315d44b44eefa8ef63155b79b2ad544f --- res/values-et/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/res/values-et/strings.xml b/res/values-et/strings.xml index d5dbf950..bcaedf7a 100644 --- a/res/values-et/strings.xml +++ b/res/values-et/strings.xml @@ -19,7 +19,7 @@ "Androidi tuumrakendused" "Kontaktiruum" "Kontaktid" - "Kontaktisikute uuendamiseks on vaja rohkem mälu" + "Kontaktisikute uuendamiseks on vaja rohkem mäluruumi." "Kontaktide salvestusruumi uuendamine" "Puudutage täiendamise lõpetamiseks." "Kontaktid" -- cgit v1.2.3 From 6ce8cae63d71a9e46aac338044ee0bd23b8293a8 Mon Sep 17 00:00:00 2001 From: Jigar Thakkar Date: Wed, 17 May 2023 21:35:12 +0000 Subject: Remove app-cloning device config flags With this change we remove the device config feature flag check in the contacts provider codebase. Test: atest ContactsSharingTest Bug: 250961871 Change-Id: I86ca2863e11d42d2bc8daae2d19025becd8f3963 --- src/com/android/providers/contacts/ContactsProvider2.java | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/com/android/providers/contacts/ContactsProvider2.java b/src/com/android/providers/contacts/ContactsProvider2.java index f4ce5c74..239eab8a 100644 --- a/src/com/android/providers/contacts/ContactsProvider2.java +++ b/src/com/android/providers/contacts/ContactsProvider2.java @@ -142,7 +142,6 @@ import com.android.common.content.SyncStateContentProviderHelper; import com.android.common.io.MoreCloseables; import com.android.internal.R; import com.android.internal.annotations.GuardedBy; -import com.android.internal.config.appcloning.AppCloningDeviceConfigHelper; import com.android.internal.util.ArrayUtils; import com.android.providers.contacts.ContactLookupKey.LookupKeySegment; import com.android.providers.contacts.ContactsDatabaseHelper.AccountsColumns; @@ -1540,8 +1539,6 @@ public class ContactsProvider2 extends AbstractContactsProvider private Set mMigratedPhoneAccountHandles; - private AppCloningDeviceConfigHelper mAppCloningDeviceConfigHelper; - /** * Subscription change will trigger ACTION_PHONE_ACCOUNT_REGISTERED that broadcasts new * PhoneAccountHandle that is created based on the new subscription. This receiver is used @@ -1617,7 +1614,6 @@ public class ContactsProvider2 extends AbstractContactsProvider mFastScrollingIndexCache = FastScrollingIndexCache.getInstance(getContext()); mSubscriptionManager = getContext().getSystemService(SubscriptionManager.class); - mAppCloningDeviceConfigHelper = AppCloningDeviceConfigHelper.getInstance(getContext()); mContactsHelper = getDatabaseHelper(); mDbHelper.set(mContactsHelper); @@ -2204,8 +2200,7 @@ public class ContactsProvider2 extends AbstractContactsProvider */ @VisibleForTesting protected boolean isContactSharingEnabledForCloneProfile() { - return getContext().getResources().getBoolean(R.bool.config_enableAppCloningBuildingBlocks) - && mAppCloningDeviceConfigHelper.getEnableAppCloningBuildingBlocks(); + return getContext().getResources().getBoolean(R.bool.config_enableAppCloningBuildingBlocks); } /** -- cgit v1.2.3 From fd22f4fcf420786669eeeead4ef1858f72428aa0 Mon Sep 17 00:00:00 2001 From: Makoto Onuki Date: Thu, 18 May 2023 09:20:31 -0700 Subject: Revert "Adding in permission check for directory queries done in CP2 to prevent directories without permissions from accessing GAL data" This reverts commit 99175a2d960ad49d16d396da9f6d8c720ff6d178. Test: atest ContactsProviderTests CtsContactsProviderTestCases ContactsContract_DirectoryTest Bug: 161166785 Change-Id: If455d675b86f3a3a7ae55b8117dbb39d312b8e65 --- .../providers/contacts/ContactsProvider2.java | 26 +++------------------- .../contacts/ContactDirectoryManagerTest.java | 18 --------------- .../contacts/ContactsMockPackageManager.java | 5 ----- 3 files changed, 3 insertions(+), 46 deletions(-) diff --git a/src/com/android/providers/contacts/ContactsProvider2.java b/src/com/android/providers/contacts/ContactsProvider2.java index f4ce5c74..c38cc364 100644 --- a/src/com/android/providers/contacts/ContactsProvider2.java +++ b/src/com/android/providers/contacts/ContactsProvider2.java @@ -18,8 +18,6 @@ package com.android.providers.contacts; import static android.Manifest.permission.INTERACT_ACROSS_USERS; import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL; -import static android.Manifest.permission.READ_CALL_LOG; -import static android.Manifest.permission.WRITE_CONTACTS; import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static com.android.providers.contacts.util.PhoneAccountHandleMigrationUtils.TELEPHONY_COMPONENT_NAME; @@ -189,6 +187,7 @@ import com.android.providers.contacts.util.DbQueryUtils; import com.android.providers.contacts.util.LogFields; import com.android.providers.contacts.util.LogUtils; import com.android.providers.contacts.util.NeededForTesting; +import com.android.providers.contacts.util.PhoneAccountHandleMigrationUtils; import com.android.providers.contacts.util.UserUtils; import com.android.vcard.VCardComposer; import com.android.vcard.VCardConfig; @@ -1374,7 +1373,6 @@ public class ContactsProvider2 extends AbstractContactsProvider String authority; String accountName; String accountType; - String packageName; } /** @@ -4323,6 +4321,7 @@ public class ContactsProvider2 extends AbstractContactsProvider @Override protected int updateInTransaction( Uri uri, ContentValues values, String selection, String[] selectionArgs) { + if (VERBOSE_LOGGING) { Log.v(TAG, "updateInTransaction: uri=" + uri + " selection=[" + selection + "] args=" + Arrays.toString(selectionArgs) + @@ -5972,22 +5971,6 @@ public class ContactsProvider2 extends AbstractContactsProvider " Caller=" + getCallingPackage() + " User=" + UserUtils.getCurrentUserHandle(getContext())); } - final String packageName = directoryInfo.packageName; - // enforce permissions - final int queryType = sUriMatcher.match(uri); - final PackageManager pm = getContext().getPackageManager(); - if (queryType == PHONE_LOOKUP || queryType == PHONES_FILTER) { - if (pm.checkPermission(READ_CALL_LOG, packageName) != PERMISSION_GRANTED) { - Log.w(TAG, "Package " + packageName - + " does not have permission for phone lookup queries."); - return null; - } - } - if (pm.checkPermission(WRITE_CONTACTS, packageName) != PERMISSION_GRANTED) { - Log.w(TAG, "Package " + packageName - + " does not have permission for contact lookup queries."); - return null; - } cursor = getContext().getContentResolver().query( directoryUri, projection, selection, selectionArgs, sortOrder); if (cursor == null) { @@ -6149,8 +6132,7 @@ public class ContactsProvider2 extends AbstractContactsProvider Directory._ID, Directory.DIRECTORY_AUTHORITY, Directory.ACCOUNT_NAME, - Directory.ACCOUNT_TYPE, - Directory.PACKAGE_NAME, + Directory.ACCOUNT_TYPE }; public static final int DIRECTORY_ID = 0; @@ -6176,8 +6158,6 @@ public class ContactsProvider2 extends AbstractContactsProvider info.authority = cursor.getString(DirectoryQuery.AUTHORITY); info.accountName = cursor.getString(DirectoryQuery.ACCOUNT_NAME); info.accountType = cursor.getString(DirectoryQuery.ACCOUNT_TYPE); - info.packageName = - cursor.getString(cursor.getColumnIndex(Directory.PACKAGE_NAME)); mDirectoryCache.put(id, info); } } finally { diff --git a/tests/src/com/android/providers/contacts/ContactDirectoryManagerTest.java b/tests/src/com/android/providers/contacts/ContactDirectoryManagerTest.java index 7e8a7ad5..a4165ce5 100644 --- a/tests/src/com/android/providers/contacts/ContactDirectoryManagerTest.java +++ b/tests/src/com/android/providers/contacts/ContactDirectoryManagerTest.java @@ -27,7 +27,6 @@ import android.database.Cursor; import android.database.MatrixCursor; import android.net.Uri; import android.os.Bundle; -import android.os.Process; import android.provider.ContactsContract; import android.provider.ContactsContract.AggregationExceptions; import android.provider.ContactsContract.Contacts; @@ -552,11 +551,6 @@ public class ContactDirectoryManagerTest extends BaseContactsProvider2Test { mPackageManager.setInstalledPackages( Lists.newArrayList(createProviderPackage("test.package1", "authority1"), createPackage("test.packageX", "authorityX", false))); -// mPackageManager.grantRuntimePermission("test.package1", "android.permission.WRITE_CONTACTS", -// Process.myUserHandle()); -// mPackageManager.grantRuntimePermission("test.package1", "android.permission.READ_CALL_LOG", -// Process.myUserHandle()); - MockContactDirectoryProvider provider1 = (MockContactDirectoryProvider) addProvider( MockContactDirectoryProvider.class, "authority1"); @@ -591,20 +585,12 @@ public class ContactDirectoryManagerTest extends BaseContactsProvider2Test { assertEquals("account-name1", cursor.getString(cursor.getColumnIndex("accountName"))); assertEquals("account-type1", cursor.getString(cursor.getColumnIndex("accountType"))); cursor.close(); -// mPackageManager.revokeRuntimePermission("test.package1", "android.permission.WRITE_CONTACTS", -// Process.myUserHandle()); -// mPackageManager.revokeRuntimePermission("test.package1", "android.permission.READ_CALL_LOG", -// Process.myUserHandle()); } public void testProjectionPopulated() throws Exception { mPackageManager.setInstalledPackages( Lists.newArrayList(createProviderPackage("test.package1", "authority1"), createPackage("test.packageX", "authorityX", false))); -// mPackageManager.grantRuntimePermission("test.package1", "android.permission.WRITE_CONTACTS", -// Process.myUserHandle()); -// mPackageManager.grantRuntimePermission("test.package1", "android.permission.READ_CALL_LOG", -// Process.myUserHandle()); MockContactDirectoryProvider provider1 = (MockContactDirectoryProvider) addProvider( MockContactDirectoryProvider.class, "authority1"); @@ -633,10 +619,6 @@ public class ContactDirectoryManagerTest extends BaseContactsProvider2Test { AggregationExceptions.RAW_CONTACT_ID1, AggregationExceptions.RAW_CONTACT_ID2, }); -// mPackageManager.revokeRuntimePermission("test.package1", "android.permission.WRITE_CONTACTS", -// Process.myUserHandle()); -// mPackageManager.revokeRuntimePermission("test.package1", "android.permission.READ_CALL_LOG", -// Process.myUserHandle()); } /** diff --git a/tests/src/com/android/providers/contacts/ContactsMockPackageManager.java b/tests/src/com/android/providers/contacts/ContactsMockPackageManager.java index c583e1df..6210229e 100644 --- a/tests/src/com/android/providers/contacts/ContactsMockPackageManager.java +++ b/tests/src/com/android/providers/contacts/ContactsMockPackageManager.java @@ -151,11 +151,6 @@ public class ContactsMockPackageManager extends MockPackageManager { return ret; } - @Override - public int checkPermission(String permission, String packageName) { - return PERMISSION_GRANTED; - } - @Override public List queryIntentActivitiesAsUser(Intent intent, ResolveInfoFlags flags, UserHandle user) { -- cgit v1.2.3 From 1686ea04fc12eeafc72d3b17eac321d589b0142f Mon Sep 17 00:00:00 2001 From: Liana Kazanova Date: Fri, 19 May 2023 20:07:21 +0000 Subject: Revert "Remove app-cloning device config flags" Revert submission 23289956-remove-flags Reason for revert: b/283509181 Reverted changes: /q/submissionid:23289956-remove-flags Change-Id: I1ee72eda14c0b0548d74e95789bf38ae6dee46ba --- src/com/android/providers/contacts/ContactsProvider2.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/com/android/providers/contacts/ContactsProvider2.java b/src/com/android/providers/contacts/ContactsProvider2.java index 239eab8a..f4ce5c74 100644 --- a/src/com/android/providers/contacts/ContactsProvider2.java +++ b/src/com/android/providers/contacts/ContactsProvider2.java @@ -142,6 +142,7 @@ import com.android.common.content.SyncStateContentProviderHelper; import com.android.common.io.MoreCloseables; import com.android.internal.R; import com.android.internal.annotations.GuardedBy; +import com.android.internal.config.appcloning.AppCloningDeviceConfigHelper; import com.android.internal.util.ArrayUtils; import com.android.providers.contacts.ContactLookupKey.LookupKeySegment; import com.android.providers.contacts.ContactsDatabaseHelper.AccountsColumns; @@ -1539,6 +1540,8 @@ public class ContactsProvider2 extends AbstractContactsProvider private Set mMigratedPhoneAccountHandles; + private AppCloningDeviceConfigHelper mAppCloningDeviceConfigHelper; + /** * Subscription change will trigger ACTION_PHONE_ACCOUNT_REGISTERED that broadcasts new * PhoneAccountHandle that is created based on the new subscription. This receiver is used @@ -1614,6 +1617,7 @@ public class ContactsProvider2 extends AbstractContactsProvider mFastScrollingIndexCache = FastScrollingIndexCache.getInstance(getContext()); mSubscriptionManager = getContext().getSystemService(SubscriptionManager.class); + mAppCloningDeviceConfigHelper = AppCloningDeviceConfigHelper.getInstance(getContext()); mContactsHelper = getDatabaseHelper(); mDbHelper.set(mContactsHelper); @@ -2200,7 +2204,8 @@ public class ContactsProvider2 extends AbstractContactsProvider */ @VisibleForTesting protected boolean isContactSharingEnabledForCloneProfile() { - return getContext().getResources().getBoolean(R.bool.config_enableAppCloningBuildingBlocks); + return getContext().getResources().getBoolean(R.bool.config_enableAppCloningBuildingBlocks) + && mAppCloningDeviceConfigHelper.getEnableAppCloningBuildingBlocks(); } /** -- cgit v1.2.3 From 37ebcd9c8c07070203c3bbad8302124dbba7601c Mon Sep 17 00:00:00 2001 From: Makoto Onuki Date: Thu, 18 May 2023 11:34:26 -0700 Subject: Log GAL provider calls Test: atest ContactsProviderTests CtsContactsProviderTestCases Bug: 161166785 Change-Id: I9e6b46ccd936bc31c1c5f55504a628944c8aecc7 --- .../providers/contacts/ContactsProvider2.java | 35 ++++++++++++++++++---- .../android/providers/contacts/util/LogFields.java | 14 +++++++++ .../android/providers/contacts/util/LogUtils.java | 4 +++ .../contacts/ContactsMockPackageManager.java | 5 ++++ 4 files changed, 52 insertions(+), 6 deletions(-) diff --git a/src/com/android/providers/contacts/ContactsProvider2.java b/src/com/android/providers/contacts/ContactsProvider2.java index c38cc364..56162916 100644 --- a/src/com/android/providers/contacts/ContactsProvider2.java +++ b/src/com/android/providers/contacts/ContactsProvider2.java @@ -1373,6 +1373,7 @@ public class ContactsProvider2 extends AbstractContactsProvider String authority; String accountName; String accountType; + String packageName; } /** @@ -2366,7 +2367,8 @@ public class ContactsProvider2 extends AbstractContactsProvider .setUriType(sUriMatcher.match(uri)) .setCallerIsSyncAdapter(readBooleanQueryParameter( uri, ContactsContract.CALLER_IS_SYNCADAPTER, false)) - .setStartNanos(SystemClock.elapsedRealtimeNanos()); + .setStartNanos(SystemClock.elapsedRealtimeNanos()) + .setUid(Binder.getCallingUid()); Uri resultUri = null; try { @@ -2405,7 +2407,8 @@ public class ContactsProvider2 extends AbstractContactsProvider .setUriType(sUriMatcher.match(uri)) .setCallerIsSyncAdapter(readBooleanQueryParameter( uri, ContactsContract.CALLER_IS_SYNCADAPTER, false)) - .setStartNanos(SystemClock.elapsedRealtimeNanos()); + .setStartNanos(SystemClock.elapsedRealtimeNanos()) + .setUid(Binder.getCallingUid()); int updates = 0; try { @@ -2443,7 +2446,8 @@ public class ContactsProvider2 extends AbstractContactsProvider .setUriType(sUriMatcher.match(uri)) .setCallerIsSyncAdapter(readBooleanQueryParameter( uri, ContactsContract.CALLER_IS_SYNCADAPTER, false)) - .setStartNanos(SystemClock.elapsedRealtimeNanos()); + .setStartNanos(SystemClock.elapsedRealtimeNanos()) + .setUid(Binder.getCallingUid()); int deletes = 0; try { @@ -5661,7 +5665,8 @@ public class ContactsProvider2 extends AbstractContactsProvider .setUriType(sUriMatcher.match(uri)) .setCallerIsSyncAdapter(readBooleanQueryParameter( uri, ContactsContract.CALLER_IS_SYNCADAPTER, false)) - .setStartNanos(SystemClock.elapsedRealtimeNanos()); + .setStartNanos(SystemClock.elapsedRealtimeNanos()) + .setUid(Binder.getCallingUid()); Cursor cursor = null; try { @@ -5960,8 +5965,20 @@ public class ContactsProvider2 extends AbstractContactsProvider if (projection == null) { projection = getDefaultProjection(uri); } + int galUid = -1; + try { + galUid = getContext().getPackageManager().getPackageUid(directoryInfo.packageName, + PackageManager.MATCH_ALL); + } catch (NameNotFoundException e) { + // Shouldn't happen, but just in case. + Log.w(TAG, "getPackageUid() failed", e); + } + final LogFields.Builder logBuilder = LogFields.Builder.aLogFields() + .setApiType(LogUtils.ApiType.GAL_CALL) + .setUriType(sUriMatcher.match(uri)) + .setUid(galUid); - Cursor cursor; + Cursor cursor = null; try { if (VERBOSE_LOGGING) { Log.v(TAG, "Making directory query: uri=" + directoryUri + @@ -5979,6 +5996,9 @@ public class ContactsProvider2 extends AbstractContactsProvider } catch (RuntimeException e) { Log.w(TAG, "Directory query failed", e); return null; + } finally { + LogUtils.log( + logBuilder.setResultCount(cursor == null ? 0 : cursor.getCount()).build()); } if (cursor.getCount() > 0) { @@ -6132,7 +6152,8 @@ public class ContactsProvider2 extends AbstractContactsProvider Directory._ID, Directory.DIRECTORY_AUTHORITY, Directory.ACCOUNT_NAME, - Directory.ACCOUNT_TYPE + Directory.ACCOUNT_TYPE, + Directory.PACKAGE_NAME }; public static final int DIRECTORY_ID = 0; @@ -6158,6 +6179,8 @@ public class ContactsProvider2 extends AbstractContactsProvider info.authority = cursor.getString(DirectoryQuery.AUTHORITY); info.accountName = cursor.getString(DirectoryQuery.ACCOUNT_NAME); info.accountType = cursor.getString(DirectoryQuery.ACCOUNT_TYPE); + info.packageName = + cursor.getString(cursor.getColumnIndex(Directory.PACKAGE_NAME)); mDirectoryCache.put(id, info); } } finally { diff --git a/src/com/android/providers/contacts/util/LogFields.java b/src/com/android/providers/contacts/util/LogFields.java index fc05c847..1672d3db 100644 --- a/src/com/android/providers/contacts/util/LogFields.java +++ b/src/com/android/providers/contacts/util/LogFields.java @@ -37,6 +37,8 @@ public final class LogFields { private int resultCount; + private int uid; + public LogFields( int apiType, int uriType, int taskType, boolean callerIsSyncAdapter, long startNanos) { this.apiType = apiType; @@ -78,6 +80,10 @@ public final class LogFields { return resultCount; } + public int getUid() { + return uid; + } + public static final class Builder { private int apiType; private int uriType; @@ -88,6 +94,8 @@ public final class LogFields { private Uri resultUri; private int resultCount; + private int uid; + private Builder() { } @@ -135,12 +143,18 @@ public final class LogFields { return this; } + public Builder setUid(int uid) { + this.uid = uid; + return this; + } + public LogFields build() { LogFields logFields = new LogFields(apiType, uriType, taskType, callerIsSyncAdapter, startNanos); logFields.resultCount = this.resultCount; logFields.exception = this.exception; logFields.resultUri = this.resultUri; + logFields.uid = this.uid; return logFields; } } diff --git a/src/com/android/providers/contacts/util/LogUtils.java b/src/com/android/providers/contacts/util/LogUtils.java index a2301006..41409645 100644 --- a/src/com/android/providers/contacts/util/LogUtils.java +++ b/src/com/android/providers/contacts/util/LogUtils.java @@ -37,6 +37,8 @@ public class LogUtils { int INSERT = 2; int UPDATE = 3; int DELETE = 4; + int CALL = 5; + int GAL_CALL = 6; } // Keep in sync with ContactsProviderStatus#TaskType in @@ -66,6 +68,8 @@ public class LogUtils { .writeInt(logFields.getResultCount()) .writeLong(getLatencyMicros(logFields.getStartNanos())) .writeInt(logFields.getTaskType()) + .writeInt(0) // Not used yet. + .writeInt(logFields.getUid()) .usePooledBuffer() .build()); } diff --git a/tests/src/com/android/providers/contacts/ContactsMockPackageManager.java b/tests/src/com/android/providers/contacts/ContactsMockPackageManager.java index 6210229e..62f17eab 100644 --- a/tests/src/com/android/providers/contacts/ContactsMockPackageManager.java +++ b/tests/src/com/android/providers/contacts/ContactsMockPackageManager.java @@ -156,4 +156,9 @@ public class ContactsMockPackageManager extends MockPackageManager { UserHandle user) { return new ArrayList<>(); } + + @Override + public int getPackageUid(String packageName, int flags) throws NameNotFoundException { + return 123; + } } -- cgit v1.2.3 From f2629060dd20acd6182757504cf4ae7656a25706 Mon Sep 17 00:00:00 2001 From: Bill Yi Date: Mon, 12 Jun 2023 11:48:58 -0700 Subject: Import translations. DO NOT MERGE ANYWHERE Auto-generated-cl: translation import Change-Id: Ic9a9e3c91a46d522f8f606a5968ebd5a7c7aaede --- res/values-ne/strings.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/res/values-ne/strings.xml b/res/values-ne/strings.xml index fd716daf..4fd36c1c 100644 --- a/res/values-ne/strings.xml +++ b/res/values-ne/strings.xml @@ -18,11 +18,11 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "एन्ड्रोइड कोर एपहरू" "सम्पर्कहरू भण्डारण" - "सम्पर्कहरू" + "कन्ट्याक्टहरू" "सम्पर्क अद्यावधिकका लागि अझै धेरै मेमोरी चाहिन्छ।" "सम्पर्कका लागि भणडारण अद्यावधिक गर्दै" "स्तरवृद्धि पूरा गर्न ट्याप गर्नुहोस्।" - "सम्पर्कहरू" + "कन्ट्याक्टहरू" "अन्य" "बाट भ्वाइसमेल " "सम्पर्क डेटाबेस प्रतिलिप गर्नुहोस्" -- cgit v1.2.3 From c01d15752ca57ea35a9032d0b88cfa800855ee02 Mon Sep 17 00:00:00 2001 From: Bill Yi Date: Mon, 12 Jun 2023 11:49:16 -0700 Subject: Import translations. DO NOT MERGE ANYWHERE Auto-generated-cl: translation import Change-Id: I65c5c3e198e33d88cfb40069ac4deae453faf7b0 --- res/values-ne/strings.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/res/values-ne/strings.xml b/res/values-ne/strings.xml index fd716daf..4fd36c1c 100644 --- a/res/values-ne/strings.xml +++ b/res/values-ne/strings.xml @@ -18,11 +18,11 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "एन्ड्रोइड कोर एपहरू" "सम्पर्कहरू भण्डारण" - "सम्पर्कहरू" + "कन्ट्याक्टहरू" "सम्पर्क अद्यावधिकका लागि अझै धेरै मेमोरी चाहिन्छ।" "सम्पर्कका लागि भणडारण अद्यावधिक गर्दै" "स्तरवृद्धि पूरा गर्न ट्याप गर्नुहोस्।" - "सम्पर्कहरू" + "कन्ट्याक्टहरू" "अन्य" "बाट भ्वाइसमेल " "सम्पर्क डेटाबेस प्रतिलिप गर्नुहोस्" -- cgit v1.2.3 From e8afe56b5db8d7b32bca56894218efc43b0368bc Mon Sep 17 00:00:00 2001 From: Bill Yi Date: Wed, 30 Aug 2023 13:32:04 -0700 Subject: Import translations. DO NOT MERGE ANYWHERE Auto-generated-cl: translation import Change-Id: I0a92ef586db9c5a383f70274839daca17fd74f6d --- res/values-et/strings.xml | 2 +- res/values-ne/strings.xml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/res/values-et/strings.xml b/res/values-et/strings.xml index d5dbf950..bcaedf7a 100644 --- a/res/values-et/strings.xml +++ b/res/values-et/strings.xml @@ -19,7 +19,7 @@ "Androidi tuumrakendused" "Kontaktiruum" "Kontaktid" - "Kontaktisikute uuendamiseks on vaja rohkem mälu" + "Kontaktisikute uuendamiseks on vaja rohkem mäluruumi." "Kontaktide salvestusruumi uuendamine" "Puudutage täiendamise lõpetamiseks." "Kontaktid" diff --git a/res/values-ne/strings.xml b/res/values-ne/strings.xml index fd716daf..4fd36c1c 100644 --- a/res/values-ne/strings.xml +++ b/res/values-ne/strings.xml @@ -18,11 +18,11 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "एन्ड्रोइड कोर एपहरू" "सम्पर्कहरू भण्डारण" - "सम्पर्कहरू" + "कन्ट्याक्टहरू" "सम्पर्क अद्यावधिकका लागि अझै धेरै मेमोरी चाहिन्छ।" "सम्पर्कका लागि भणडारण अद्यावधिक गर्दै" "स्तरवृद्धि पूरा गर्न ट्याप गर्नुहोस्।" - "सम्पर्कहरू" + "कन्ट्याक्टहरू" "अन्य" "बाट भ्वाइसमेल " "सम्पर्क डेटाबेस प्रतिलिप गर्नुहोस्" -- cgit v1.2.3