From 3be73aa7c24134ff5601f45113682156bd5a7900 Mon Sep 17 00:00:00 2001 From: Bill Yi Date: Wed, 19 Jul 2017 23:39:49 -0700 Subject: Import translations. DO NOT MERGE Change-Id: I4127469f3092a54d0069175effe843e92ece643e Auto-generated-cl: translation import Exempt-From-Owner-Approval: translation import --- res/values-hi/strings.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/res/values-hi/strings.xml b/res/values-hi/strings.xml index 5fa4021e..a9b20257 100644 --- a/res/values-hi/strings.xml +++ b/res/values-hi/strings.xml @@ -25,8 +25,8 @@ "संपर्क" "अन्य" "इनका ध्‍वनि‍मेल: " - "संपर्क डेटाबेस की प्रतिलिपि बनाएं" - "आप 1) मोबाइल मेमोरी में अपने उस डेटाबेस की प्रतिलिपि बनाने वाले हैं जिसमें सभी संपर्कों संबंधी जानकारी और सभी कॉल लॉग शामिल हैं, और 2) उसे ईमेल करने वाले हैं. जैसे ही आप डिवाइस से इसकी प्रतिलिपि सफलतापूर्वक बना लें या ईमेल प्राप्त हो जाए तो प्रतिलिपि को हटाना न भूलें." + "संपर्क डेटाबेस की कॉपी बनाएं" + "आप 1) मोबाइल मेमोरी में अपने उस डेटाबेस की कॉपी बनाने वाले हैं जिसमें सभी संपर्कों संबंधी जानकारी और सभी कॉल लॉग शामिल हैं, और 2) उसे ईमेल करने वाले हैं. जैसे ही आप डिवाइस से इसकी कॉपी सफलतापूर्वक बना लें या ईमेल प्राप्त हो जाए तो कॉपी को हटाना न भूलें." "अभी हटाएं" "प्रारंभ करें" "अपनी फ़ाइल भेजने के लिए कोई प्रोग्राम चुनें" -- cgit v1.2.3 From 8abe147bceba17da35375370bceae17cf440af69 Mon Sep 17 00:00:00 2001 From: Bill Yi Date: Sat, 12 Aug 2017 03:03:24 -0700 Subject: Import translations. DO NOT MERGE Change-Id: I9422b6739e2fbc420876695c9ec021073de63a10 Auto-generated-cl: translation import Exempt-From-Owner-Approval: translation import --- res/values-en-rCA/strings.xml | 35 +++++++++++++++++++++++++++++++++++ res/values-en-rXC/strings.xml | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 70 insertions(+) create mode 100644 res/values-en-rCA/strings.xml create mode 100644 res/values-en-rXC/strings.xml diff --git a/res/values-en-rCA/strings.xml b/res/values-en-rCA/strings.xml new file mode 100644 index 00000000..c62b699a --- /dev/null +++ b/res/values-en-rCA/strings.xml @@ -0,0 +1,35 @@ + + + + + "Android Core Apps" + "Contacts Storage" + "Contacts" + "Contact upgrade needs more memory." + "Upgrading storage for contacts" + "Tap to complete the upgrade." + "Contacts" + "Other" + "Voicemail from " + "Copy contacts database" + "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" + "Contacts Db attached" + "Attached is my contacts database with all my contacts information. Handle with care." + diff --git a/res/values-en-rXC/strings.xml b/res/values-en-rXC/strings.xml new file mode 100644 index 00000000..b7681dd5 --- /dev/null +++ b/res/values-en-rXC/strings.xml @@ -0,0 +1,35 @@ + + + + + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‎‏‎‎‏‏‏‏‏‏‏‏‏‏‎‏‏‏‏‎‏‎‏‏‏‎‎‎‎‎‏‎‏‎‎‏‏‏‏‏‏‏‏‏‏‏‎‏‎‏‏‏‏‏‎‎‏‎‏‏‎‎‎‏‏‎‎‏‏‎‏‎‎‎‏‎Android Core Apps‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‎‏‎‎‏‏‏‏‏‏‏‎‏‎‏‏‏‏‎‎‎‎‏‎‏‏‏‎‎‎‏‎‎‏‎‏‏‏‏‏‏‎‏‏‏‏‎‎‏‏‏‏‎‎‎‎‏‏‎‏‏‎‎‎‎‎‏‏‎‏‏‏‎‎‎Contacts Storage‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‎‏‎‎‏‏‏‏‏‏‏‏‏‎‏‎‎‏‏‎‏‏‎‏‏‏‏‎‏‏‏‎‎‏‏‎‏‎‏‏‎‎‎‎‏‎‏‎‏‏‏‎‎‏‎‏‏‏‎‏‏‎‎‎‎‏‎‎‎‎‎‎‎‏‏‎Contacts‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‎‏‎‎‏‏‏‏‏‏‏‏‏‏‎‏‎‏‎‎‎‎‎‎‎‏‎‎‏‎‎‏‎‎‎‎‎‏‎‎‎‏‎‏‎‎‎‏‏‏‎‎‎‎‏‎‎‏‏‏‎‎‏‏‏‎‎‏‏‎‏‏‏‎‏‎Contacts upgrade needs more memory.‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‎‏‎‎‏‏‏‏‏‏‏‏‏‏‏‏‎‏‏‎‏‎‏‏‎‎‏‎‎‏‎‎‎‎‏‎‏‎‎‎‏‏‎‎‎‏‎‎‎‎‏‎‏‏‏‎‎‏‏‎‎‎‎‎‏‏‏‏‎‎‎‎‎‏‏‎Upgrading storage for contacts‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‎‏‎‎‏‏‏‏‏‏‏‎‏‎‎‎‏‏‏‏‎‏‎‏‎‎‏‎‎‎‎‎‏‎‎‏‏‎‎‎‎‏‏‎‏‏‏‏‎‏‏‎‎‎‎‏‎‎‎‏‏‏‏‏‎‎‏‏‏‎‎‎‎‎‎Tap to complete the upgrade.‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‎‏‎‎‏‏‏‏‏‎‎‏‏‎‏‎‎‏‏‎‏‏‏‎‏‎‎‎‏‏‎‎‏‏‎‏‎‏‏‎‏‏‏‎‏‎‎‎‏‏‏‏‎‎‏‎‏‎‏‏‎‏‏‏‏‎‏‏‎‎Contacts‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‎‏‎‎‏‏‏‏‏‏‎‎‏‎‎‏‏‏‎‎‏‎‎‏‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‏‎‎‏‏‎‏‎‏‎‏‏‎‏‎‏‎‎‏‎‏‎‎‎‏‎‎‏‏‎‎‎‎Other‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‎‏‎‎‏‏‏‏‏‎‏‏‏‏‎‎‎‎‎‏‏‎‎‎‎‎‎‏‎‎‎‎‏‏‏‎‏‏‎‎‏‏‏‎‏‏‎‎‏‎‎‏‏‏‎‏‎‎‏‏‎‏‎‎‏‏‎‎‏‎‎‎Voicemail from ‎‏‎‎‏‎ " + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‎‏‎‎‏‏‏‏‏‏‏‏‏‎‎‎‏‎‎‎‎‏‏‏‏‎‎‎‏‎‎‏‎‎‏‏‎‎‎‎‎‏‏‏‏‏‎‏‏‏‎‎‎‎‏‎‎‏‏‏‏‏‏‏‏‏‎‏‏‏‎‎‏‏‏‎Copy contacts database‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‎‏‎‎‏‏‏‏‏‎‏‏‏‎‏‏‎‏‎‎‎‏‏‏‏‏‏‎‏‎‏‏‏‎‎‏‎‎‎‏‏‏‏‏‏‏‎‎‏‎‎‏‏‏‏‏‏‏‎‏‏‏‎‏‏‏‎‎‎‏‎‎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 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 5377831c69a25581e9cd87cc7490669057e081c3 Mon Sep 17 00:00:00 2001 From: Bill Yi Date: Wed, 6 Sep 2017 09:07:21 -0700 Subject: Import translations. DO NOT MERGE Change-Id: I48de72db8541539a21453e0f3cfc57c917fb69dd Auto-generated-cl: translation import Exempt-From-Owner-Approval: translation import --- res/values-bn/strings.xml | 2 +- res/values-bs/strings.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/res/values-bn/strings.xml b/res/values-bn/strings.xml index dc803da9..49646f3f 100644 --- a/res/values-bn/strings.xml +++ b/res/values-bn/strings.xml @@ -19,7 +19,7 @@ "Android কোর অ্যাপ্লিকেশানগুলি" "পরিচিতিগুলির সংগ্রহস্থল" "পরিচিতিগুলি" - "পরিচিতিগুলি আপগ্রেড করার জন্য আরো সঞ্চয়স্থানের দরকার৷" + "পরিচিতিগুলি আপগ্রেড করার জন্য আরও সঞ্চয়স্থানের দরকার৷" "পরিচিতিগুলির জন্য সঞ্চয়স্থান আপগ্রেড করা হচ্ছে" "আপগ্রেড সম্পূর্ণ করতে আলতো চাপ দিন৷" "পরিচিতিগুলি" diff --git a/res/values-bs/strings.xml b/res/values-bs/strings.xml index 37bc1502..e1fae510 100644 --- a/res/values-bs/strings.xml +++ b/res/values-bs/strings.xml @@ -26,7 +26,7 @@ "Ostalo" "Govorna pošta od " "Kopiraj bazu podataka kontakata" - "Upravo ćete 1) napraviti kopiju svoje baze podataka koja sadrži sve informacije o kontaktima i sve popise poziva u unutrašnjoj pohrani i 2) poslati tu kopiju e-poštom. Ne zaboravite izbrisati kopiju čim je uspješno kopirate s uređaja ili čim primite poruku e-pošte." + "Upravo ćete 1) napraviti kopiju svoje baze podataka koja sadrži sve informacije o kontaktima i sve popise poziva u unutrašnjoj pohrani i 2) poslati tu kopiju e-poštom. Ne zaboravite izbrisati kopiju čim je uspješno kopirate s uređaja ili čim primite e-poruku." "Izbriši sada" "Započni" "Odaberite program za slanje fajla" -- cgit v1.2.3 From 6b2cd56c398cde2acacf8f24abb1b638bdb03546 Mon Sep 17 00:00:00 2001 From: Tingting Wang Date: Thu, 12 Oct 2017 17:19:37 -0700 Subject: Trigger FSA sync on favorite membership insert. Like other data item insert/update, when inserting favorite group membership to a raw contact, it should also mark the raw contact dirty and trigger FSA sync. Test: run adb shell am instrument -e class com.android.providers.contacts.ContactsProvider2Test -w \ com.android.providers.contacts.tests/android.test.InstrumentationTestRunner BUG 65121762 Change-Id: I37201d2084da4b0cba427781a0e37ff33dba2b71 --- .../providers/contacts/ContactsProvider2.java | 7 ++++++ .../providers/contacts/ContactsProvider2Test.java | 27 +++++++++++++++++++++- 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/src/com/android/providers/contacts/ContactsProvider2.java b/src/com/android/providers/contacts/ContactsProvider2.java index a5af51fe..701c7916 100644 --- a/src/com/android/providers/contacts/ContactsProvider2.java +++ b/src/com/android/providers/contacts/ContactsProvider2.java @@ -4649,6 +4649,9 @@ public class ContactsProvider2 extends AbstractContactsProvider if (flagExists(values, RawContacts.STARRED)) { if (!callerIsSyncAdapter) { updateFavoritesMembership(rawContactId, flagIsSet(values, RawContacts.STARRED)); + mTransactionContext.get().markRawContactDirtyAndChanged( + rawContactId, callerIsSyncAdapter); + mSyncToNetwork |= !callerIsSyncAdapter; } aggregator.updateStarred(rawContactId); aggregator.updatePinned(rawContactId); @@ -4662,6 +4665,9 @@ public class ContactsProvider2 extends AbstractContactsProvider SELECTION_STARRED_FROM_RAW_CONTACTS, new String[] {Long.toString(rawContactId)}); updateFavoritesMembership(rawContactId, starred); + mTransactionContext.get().markRawContactDirtyAndChanged( + rawContactId, callerIsSyncAdapter); + mSyncToNetwork |= !callerIsSyncAdapter; } } if (flagExists(values, RawContacts.SEND_TO_VOICEMAIL)) { @@ -4832,6 +4838,7 @@ public class ContactsProvider2 extends AbstractContactsProvider if (hasStarredValue) { updateFavoritesMembership(rawContactId, flagIsSet(values, RawContacts.STARRED)); + mSyncToNetwork |= !callerIsSyncAdapter; } if (hasStarredValue || hasPinnedValue || hasVoiceMailValue) { diff --git a/tests/src/com/android/providers/contacts/ContactsProvider2Test.java b/tests/src/com/android/providers/contacts/ContactsProvider2Test.java index 8930338e..23b42dae 100644 --- a/tests/src/com/android/providers/contacts/ContactsProvider2Test.java +++ b/tests/src/com/android/providers/contacts/ContactsProvider2Test.java @@ -7021,7 +7021,6 @@ public class ContactsProvider2Test extends BaseContactsProvider2Test { values.put(ContactsContract.RawContacts.SEND_TO_VOICEMAIL, 1); values.put(ContactsContract.RawContacts.AGGREGATION_MODE, RawContacts.AGGREGATION_MODE_IMMEDIATE); - values.put(ContactsContract.RawContacts.STARRED, 1); assertEquals(1, mResolver.update(uri, values, null, null)); assertEquals(version, getVersion(uri)); @@ -7872,6 +7871,11 @@ public class ContactsProvider2Test extends BaseContactsProvider2Test { Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId); assertStoredValue(contactUri, Contacts.STARRED, "0"); + assertDirty(rawContactUri1, true); + assertDirty(rawContactUri2, true); + clearDirty(rawContactUri1); + clearDirty(rawContactUri2); + ContentValues values = new ContentValues(); values.put(RawContacts.STARRED, "1"); @@ -7880,20 +7884,41 @@ public class ContactsProvider2Test extends BaseContactsProvider2Test { assertStoredValue(rawContactUri1, RawContacts.STARRED, "1"); assertStoredValue(rawContactUri2, RawContacts.STARRED, "0"); assertStoredValue(contactUri, Contacts.STARRED, "1"); + assertDirty(rawContactUri1, true); + assertNetworkNotified(true); + clearDirty(rawContactUri1); values.put(RawContacts.STARRED, "0"); mResolver.update(rawContactUri1, values, null, null); assertStoredValue(rawContactUri1, RawContacts.STARRED, "0"); assertStoredValue(rawContactUri2, RawContacts.STARRED, "0"); assertStoredValue(contactUri, Contacts.STARRED, "0"); + assertDirty(rawContactUri1, true); + assertNetworkNotified(true); + clearDirty(rawContactUri1); values.put(Contacts.STARRED, "1"); mResolver.update(contactUri, values, null, null); assertStoredValue(rawContactUri1, RawContacts.STARRED, "1"); assertStoredValue(rawContactUri2, RawContacts.STARRED, "1"); assertStoredValue(contactUri, Contacts.STARRED, "1"); + assertDirty(rawContactUri1, true); + assertNetworkNotified(true); + } + + public void testUpdateContactOptionsSetStarred() { + long rawContactId = RawContactUtil.createRawContact(mResolver); + long contactId = queryContactId(rawContactId); + String lookupKey = queryLookupKey(contactId); + ContentValues values =new ContentValues(); + values.put(Contacts.STARRED, 1); + + Uri contactLookupUri = ContentUris.withAppendedId( + Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI, lookupKey), contactId); + mResolver.update(contactLookupUri, values, null, null); + assertNetworkNotified(true); } public void testSetAndClearSuperPrimaryEmail() { -- cgit v1.2.3 From c28f7969399fcb667d02de4030fba0fc40ff9130 Mon Sep 17 00:00:00 2001 From: Ta-wei Yen Date: Mon, 16 Oct 2017 12:53:06 -0700 Subject: Implement bulkInsert for VoicemailProvider DbModifierWithNotification will only generate one aggregated notification during bulk insert. Test: VoicemailProviderTest Change-Id: Ie808400d666c073642bdda175f714f9f3e2c68d6 Fixes: 21281858 --- .../providers/contacts/CallLogProvider.java | 12 +- .../providers/contacts/DatabaseModifier.java | 6 + .../contacts/DbModifierWithNotification.java | 120 ++++++++----------- .../contacts/VoicemailContentProvider.java | 7 ++ .../providers/contacts/VoicemailContentTable.java | 41 +++++-- .../providers/contacts/VoicemailNotifier.java | 127 +++++++++++++++++++++ .../providers/contacts/VoicemailStatusTable.java | 24 ++-- .../android/providers/contacts/VoicemailTable.java | 2 + .../providers/contacts/VoicemailProviderTest.java | 40 ++++++- 9 files changed, 284 insertions(+), 95 deletions(-) create mode 100644 src/com/android/providers/contacts/VoicemailNotifier.java diff --git a/src/com/android/providers/contacts/CallLogProvider.java b/src/com/android/providers/contacts/CallLogProvider.java index 59e9b147..76e207a0 100644 --- a/src/com/android/providers/contacts/CallLogProvider.java +++ b/src/com/android/providers/contacts/CallLogProvider.java @@ -383,7 +383,7 @@ public class CallLogProvider extends ContentProvider { // Add the computed fields to the copied values. mCallLogInsertionHelper.addComputedValues(copiedValues); - long rowId = getDatabaseModifier(mCallsInserter).insert(copiedValues); + long rowId = createDatabaseModifier(mCallsInserter).insert(copiedValues); if (rowId > 0) { return ContentUris.withAppendedId(uri, rowId); } @@ -423,7 +423,7 @@ public class CallLogProvider extends ContentProvider { throw new UnsupportedOperationException("Cannot update URL: " + uri); } - return getDatabaseModifier(db).update(uri, Tables.CALLS, values, selectionBuilder.build(), + return createDatabaseModifier(db).update(uri, Tables.CALLS, values, selectionBuilder.build(), selectionArgs); } @@ -445,7 +445,7 @@ public class CallLogProvider extends ContentProvider { case CALLS: // TODO: Special case - We may want to forward the delete request on user 0 to the // shadow provider too. - return getDatabaseModifier(db).delete(Tables.CALLS, + return createDatabaseModifier(db).delete(Tables.CALLS, selectionBuilder.build(), selectionArgs); default: throw new UnsupportedOperationException("Cannot delete that URL: " + uri); @@ -460,15 +460,15 @@ public class CallLogProvider extends ContentProvider { * Returns a {@link DatabaseModifier} that takes care of sending necessary notifications * after the operation is performed. */ - private DatabaseModifier getDatabaseModifier(SQLiteDatabase db) { + private DatabaseModifier createDatabaseModifier(SQLiteDatabase db) { return new DbModifierWithNotification(Tables.CALLS, db, getContext()); } /** - * Same as {@link #getDatabaseModifier(SQLiteDatabase)} but used for insert helper operations + * Same as {@link #createDatabaseModifier(SQLiteDatabase)} but used for insert helper operations * only. */ - private DatabaseModifier getDatabaseModifier(DatabaseUtils.InsertHelper insertHelper) { + private DatabaseModifier createDatabaseModifier(DatabaseUtils.InsertHelper insertHelper) { return new DbModifierWithNotification(Tables.CALLS, insertHelper, getContext()); } diff --git a/src/com/android/providers/contacts/DatabaseModifier.java b/src/com/android/providers/contacts/DatabaseModifier.java index b11605b4..60f9c7f1 100644 --- a/src/com/android/providers/contacts/DatabaseModifier.java +++ b/src/com/android/providers/contacts/DatabaseModifier.java @@ -49,4 +49,10 @@ public interface DatabaseModifier { * {@link SQLiteDatabase#delete(String, String, String[])} method. */ public abstract int delete(String table, String whereClause, String[] whereArgs); + + void startBulkOperation(); + + void yieldBulkOperation(); + + void finishBulkOperation(); } diff --git a/src/com/android/providers/contacts/DbModifierWithNotification.java b/src/com/android/providers/contacts/DbModifierWithNotification.java index 7e7b3e17..36865fa1 100644 --- a/src/com/android/providers/contacts/DbModifierWithNotification.java +++ b/src/com/android/providers/contacts/DbModifierWithNotification.java @@ -17,7 +17,6 @@ package com.android.providers.contacts; -import static android.Manifest.permission.ADD_VOICEMAIL; import static android.Manifest.permission.READ_VOICEMAIL; import android.content.ComponentName; @@ -25,8 +24,6 @@ import android.content.ContentUris; import android.content.ContentValues; import android.content.Context; import android.content.Intent; -import android.content.pm.ActivityInfo; -import android.content.pm.ResolveInfo; import android.database.Cursor; import android.database.DatabaseUtils.InsertHelper; import android.database.sqlite.SQLiteDatabase; @@ -37,17 +34,15 @@ import android.provider.VoicemailContract; import android.provider.VoicemailContract.Status; import android.provider.VoicemailContract.Voicemails; import android.util.ArraySet; -import android.util.Log; import com.android.common.io.MoreCloseables; +import com.android.internal.annotations.VisibleForTesting; import com.android.providers.contacts.CallLogDatabaseHelper.Tables; import com.android.providers.contacts.util.DbQueryUtils; import com.google.android.collect.Lists; import com.google.common.collect.Iterables; -import java.util.ArrayList; import java.util.Collection; -import java.util.List; import java.util.Set; /** @@ -57,6 +52,7 @@ import java.util.Set; * of then got affected by the change. */ public class DbModifierWithNotification implements DatabaseModifier { + private static final String TAG = "DbModifierWithNotify"; private static final String[] PROJECTION = new String[] { @@ -73,8 +69,11 @@ public class DbModifierWithNotification implements DatabaseModifier { private final Context mContext; private final Uri mBaseUri; private final boolean mIsCallsTable; - private final VoicemailPermissions mVoicemailPermissions; + private final VoicemailNotifier mVoicemailNotifier; + + private boolean mIsBulkOperation = false; + private static VoicemailNotifier sVoicemailNotifierForTest; public DbModifierWithNotification(String tableName, SQLiteDatabase db, Context context) { this(tableName, db, null, context); @@ -94,7 +93,8 @@ public class DbModifierWithNotification implements DatabaseModifier { mBaseUri = mTableName.equals(Tables.VOICEMAIL_STATUS) ? Status.CONTENT_URI : Voicemails.CONTENT_URI; mIsCallsTable = mTableName.equals(Tables.CALLS); - mVoicemailPermissions = new VoicemailPermissions(mContext); + mVoicemailNotifier = sVoicemailNotifierForTest != null ? sVoicemailNotifierForTest + : new VoicemailNotifier(mContext, mBaseUri); } @Override @@ -143,13 +143,21 @@ public class DbModifierWithNotification implements DatabaseModifier { } } - private void notifyVoicemailChangeOnInsert(Uri notificationUri, Set packagesModified) { + private void notifyVoicemailChangeOnInsert( + Uri notificationUri, Set packagesModified) { if (mIsCallsTable) { - notifyVoicemailChange(notificationUri, packagesModified, - VoicemailContract.ACTION_NEW_VOICEMAIL, Intent.ACTION_PROVIDER_CHANGED); - } else { - notifyVoicemailChange(notificationUri, packagesModified, - Intent.ACTION_PROVIDER_CHANGED); + mVoicemailNotifier.addIntentActions(VoicemailContract.ACTION_NEW_VOICEMAIL); + } + notifyVoicemailChange(notificationUri, packagesModified); + } + + private void notifyVoicemailChange(Uri notificationUri, + Set modifiedPackages) { + mVoicemailNotifier.addUri(notificationUri); + mVoicemailNotifier.addModifiedPackages(modifiedPackages); + mVoicemailNotifier.addIntentActions(Intent.ACTION_PROVIDER_CHANGED); + if (!mIsBulkOperation) { + mVoicemailNotifier.sendNotification(); } } @@ -197,7 +205,7 @@ public class DbModifierWithNotification implements DatabaseModifier { int count = mDb.update(table, values, whereClause, whereArgs); if (count > 0 && isVoicemail) { - notifyVoicemailChange(mBaseUri, packagesModified, Intent.ACTION_PROVIDER_CHANGED); + notifyVoicemailChange(mBaseUri, packagesModified); } if (count > 0 && mIsCallsTable) { notifyCallLogChange(); @@ -247,7 +255,7 @@ public class DbModifierWithNotification implements DatabaseModifier { } if (count > 0 && isVoicemail) { - notifyVoicemailChange(mBaseUri, packagesModified, Intent.ACTION_PROVIDER_CHANGED); + notifyVoicemailChange(mBaseUri, packagesModified); } if (count > 0 && mIsCallsTable) { notifyCallLogChange(); @@ -255,6 +263,25 @@ public class DbModifierWithNotification implements DatabaseModifier { return count; } + @Override + public void startBulkOperation() { + mIsBulkOperation = true; + mDb.beginTransaction(); + } + + @Override + public void yieldBulkOperation() { + mDb.yieldIfContendedSafely(); + } + + @Override + public void finishBulkOperation() { + mDb.setTransactionSuccessful(); + mDb.endTransaction(); + mIsBulkOperation = false; + mVoicemailNotifier.sendNotification(); + } + /** * Returns the set of packages affected when a modify operation is run for the specified * where clause. When called from an insert operation an empty set returned by this method @@ -266,7 +293,7 @@ public class DbModifierWithNotification implements DatabaseModifier { Cursor cursor = mDb.query(mTableName, PROJECTION, DbQueryUtils.concatenateClauses(NON_NULL_SOURCE_PACKAGE_SELECTION, whereClause), whereArgs, null, null, null); - while(cursor.moveToNext()) { + while (cursor.moveToNext()) { modifiedPackages.add(cursor.getString(SOURCE_PACKAGE_COLUMN_INDEX)); } MoreCloseables.closeQuietly(cursor); @@ -281,7 +308,7 @@ public class DbModifierWithNotification implements DatabaseModifier { */ private Set getModifiedPackages(ContentValues values) { Set impactedPackages = new ArraySet<>(); - if(values.containsKey(VoicemailContract.SOURCE_PACKAGE_FIELD)) { + if (values.containsKey(VoicemailContract.SOURCE_PACKAGE_FIELD)) { impactedPackages.add(values.getAsString(VoicemailContract.SOURCE_PACKAGE_FIELD)); } return impactedPackages; @@ -304,58 +331,6 @@ public class DbModifierWithNotification implements DatabaseModifier { || callingPackages.contains(mContext.getPackageName())); } - private void notifyVoicemailChange(Uri notificationUri, Set modifiedPackages, - String... intentActions) { - // Notify the observers. - // Must be done only once, even if there are multiple broadcast intents. - mContext.getContentResolver().notifyChange(notificationUri, null, true); - Collection callingPackages = getCallingPackages(); - // Now fire individual intents. - for (String intentAction : intentActions) { - // self_change extra should be included only for provider_changed events. - boolean includeSelfChangeExtra = intentAction.equals(Intent.ACTION_PROVIDER_CHANGED); - for (ComponentName component : - getBroadcastReceiverComponents(intentAction, notificationUri)) { - // Ignore any package that is not affected by the change and don't have full access - // either. - if (!modifiedPackages.contains(component.getPackageName()) && - !mVoicemailPermissions.packageHasReadAccess( - component.getPackageName())) { - continue; - } - - Intent intent = new Intent(intentAction, notificationUri); - intent.setComponent(component); - if (includeSelfChangeExtra && callingPackages != null) { - intent.putExtra(VoicemailContract.EXTRA_SELF_CHANGE, - callingPackages.contains(component.getPackageName())); - } - String permissionNeeded = modifiedPackages.contains(component.getPackageName()) ? - ADD_VOICEMAIL : READ_VOICEMAIL; - mContext.sendBroadcast(intent, permissionNeeded); - Log.v(TAG, String.format("Sent intent. act:%s, url:%s, comp:%s, perm:%s," + - " self_change:%s", intent.getAction(), intent.getData(), - component.getClassName(), permissionNeeded, - intent.hasExtra(VoicemailContract.EXTRA_SELF_CHANGE) ? - intent.getBooleanExtra(VoicemailContract.EXTRA_SELF_CHANGE, false) : - null)); - } - } - } - - /** Determines the components that can possibly receive the specified intent. */ - private List getBroadcastReceiverComponents(String intentAction, Uri uri) { - Intent intent = new Intent(intentAction, uri); - List receiverComponents = new ArrayList(); - // For broadcast receivers ResolveInfo.activityInfo is the one that is populated. - for (ResolveInfo resolveInfo : - mContext.getPackageManager().queryBroadcastReceivers(intent, 0)) { - ActivityInfo activityInfo = resolveInfo.activityInfo; - receiverComponents.add(new ComponentName(activityInfo.packageName, activityInfo.name)); - } - return receiverComponents; - } - /** * Returns the package names of the calling process. If the calling process has more than * one packages, this returns them all @@ -393,4 +368,9 @@ public class DbModifierWithNotification implements DatabaseModifier { } return CallLogProvider.getTimeForTestMillis(); } + + @VisibleForTesting + static void setVoicemailNotifierForTest(VoicemailNotifier notifier){ + sVoicemailNotifierForTest = notifier; + } } diff --git a/src/com/android/providers/contacts/VoicemailContentProvider.java b/src/com/android/providers/contacts/VoicemailContentProvider.java index 160a1a99..01c10481 100644 --- a/src/com/android/providers/contacts/VoicemailContentProvider.java +++ b/src/com/android/providers/contacts/VoicemailContentProvider.java @@ -20,6 +20,7 @@ import static android.provider.VoicemailContract.SOURCE_PACKAGE_FIELD; import static com.android.providers.contacts.util.DbQueryUtils.concatenateClauses; import static com.android.providers.contacts.util.DbQueryUtils.getEqualityClause; +import android.annotation.NonNull; import android.app.AppOpsManager; import android.content.ContentProvider; import android.content.ContentResolver; @@ -156,6 +157,12 @@ public class VoicemailContentProvider extends ContentProvider return getTableDelegate(uriData).insert(uriData, values); } + @Override + public int bulkInsert(@NonNull Uri uri, @NonNull ContentValues[] values) { + UriData uriData = checkPermissionsAndCreateUriDataForWrite(uri, values); + return getTableDelegate(uriData).bulkInsert(uriData, values); + } + @Override public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { diff --git a/src/com/android/providers/contacts/VoicemailContentTable.java b/src/com/android/providers/contacts/VoicemailContentTable.java index 09a8c1f0..b295ac54 100644 --- a/src/com/android/providers/contacts/VoicemailContentTable.java +++ b/src/com/android/providers/contacts/VoicemailContentTable.java @@ -39,7 +39,6 @@ import com.android.providers.contacts.VoicemailContentProvider.UriData; import com.android.providers.contacts.util.CloseUtils; import com.google.common.collect.ImmutableSet; - import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; @@ -52,10 +51,12 @@ public class VoicemailContentTable implements VoicemailTable.Delegate { private static final String TAG = "VmContentProvider"; private final ProjectionMap mVoicemailProjectionMap; - /** The private directory in which to store the data associated with the voicemail. */ + /** + * The private directory in which to store the data associated with the voicemail. + */ private static final String DATA_DIRECTORY = "voicemail-data"; - private static final String[] FILENAME_ONLY_PROJECTION = new String[] { Voicemails._DATA }; + private static final String[] FILENAME_ONLY_PROJECTION = new String[] {Voicemails._DATA}; private static final ImmutableSet ALLOWED_COLUMNS = new ImmutableSet.Builder() .add(Voicemails._ID) @@ -83,6 +84,8 @@ public class VoicemailContentTable implements VoicemailTable.Delegate { .add(OpenableColumns.SIZE) .build(); + private static final int BULK_INSERTS_PER_YIELD_POINT = 50; + private final String mTableName; private final CallLogDatabaseHelper mDbHelper; private final Context mContext; @@ -138,6 +141,30 @@ public class VoicemailContentTable implements VoicemailTable.Delegate { @Override public Uri insert(UriData uriData, ContentValues values) { + DatabaseModifier modifier = createDatabaseModifier(mDbHelper.getWritableDatabase()); + Uri uri = insertRow(modifier, uriData, values); + return uri; + } + + @Override + public int bulkInsert(UriData uriData, ContentValues[] values) { + DatabaseModifier modifier = createDatabaseModifier(mDbHelper.getWritableDatabase()); + modifier.startBulkOperation(); + int count = 0; + for (ContentValues value : values) { + Uri uri = insertRow(modifier, uriData, value); + if (uri != null) { + count++; + } + if((count % BULK_INSERTS_PER_YIELD_POINT) == 0){ + modifier.yieldBulkOperation(); + } + } + modifier.finishBulkOperation(); + return count; + } + + private Uri insertRow(DatabaseModifier modifier, UriData uriData, ContentValues values) { checkForSupportedColumns(mVoicemailProjectionMap, values); ContentValues copiedValues = new ContentValues(values); checkInsertSupported(uriData); @@ -160,7 +187,7 @@ public class VoicemailContentTable implements VoicemailTable.Delegate { } SQLiteDatabase db = mDbHelper.getWritableDatabase(); - long rowId = getDatabaseModifier(db).insert(mTableName, null, copiedValues); + long rowId = modifier.insert(mTableName, null, copiedValues); if (rowId > 0) { Uri newUri = ContentUris.withAppendedId(uriData.getUri(), rowId); // Populate the 'voicemail_uri' field to be used by the call_log provider. @@ -228,7 +255,7 @@ public class VoicemailContentTable implements VoicemailTable.Delegate { } // Now delete the rows themselves. - return getDatabaseModifier(db).delete(mTableName, combinedClause, + return createDatabaseModifier(db).delete(mTableName, combinedClause, selectionArgs); } @@ -262,7 +289,7 @@ public class VoicemailContentTable implements VoicemailTable.Delegate { // URI that include message Id. I think we do want to support bulk update. String combinedClause = concatenateClauses(selection, uriData.getWhereClause(), getCallTypeClause()); - return getDatabaseModifier(db).update(uriData.getUri(), mTableName, values, combinedClause, + return createDatabaseModifier(db).update(uriData.getUri(), mTableName, values, combinedClause, selectionArgs); } @@ -298,7 +325,7 @@ public class VoicemailContentTable implements VoicemailTable.Delegate { return getEqualityClause(Calls.TYPE, Calls.VOICEMAIL_TYPE); } - private DatabaseModifier getDatabaseModifier(SQLiteDatabase db) { + private DatabaseModifier createDatabaseModifier(SQLiteDatabase db) { return new DbModifierWithNotification(mTableName, db, mContext); } diff --git a/src/com/android/providers/contacts/VoicemailNotifier.java b/src/com/android/providers/contacts/VoicemailNotifier.java new file mode 100644 index 00000000..04a9bd64 --- /dev/null +++ b/src/com/android/providers/contacts/VoicemailNotifier.java @@ -0,0 +1,127 @@ +package com.android.providers.contacts; + +import static android.Manifest.permission.ADD_VOICEMAIL; +import static android.Manifest.permission.READ_VOICEMAIL; + +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.pm.ActivityInfo; +import android.content.pm.ResolveInfo; +import android.net.Uri; +import android.os.Binder; +import android.provider.VoicemailContract; +import android.util.ArraySet; +import android.util.Log; + +import com.google.android.collect.Lists; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Set; + +/** + * Aggregates voicemail broadcasts from multiple operations in to a single one. The URIs will be + * {@link VoicemailContract.Voicemails#DIR_TYPE} instead of {@link + * VoicemailContract.Voicemails#ITEM_TYPE} if multiple URIs is notified. + */ +public class VoicemailNotifier { + + private final String TAG = "VoicemailNotifier"; + + private final Context mContext; + private final Uri mBaseUri; + + private final VoicemailPermissions mVoicemailPermissions; + + private final Set mIntentActions = new ArraySet<>(); + private final Set mModifiedPackages = new ArraySet<>(); + private final Set mUris = new ArraySet<>(); + + public VoicemailNotifier(Context context, Uri baseUri) { + mContext = context; + mBaseUri = baseUri; + mVoicemailPermissions = new VoicemailPermissions(mContext); + } + + public void addIntentActions(String action) { + mIntentActions.add(action); + } + + public void addModifiedPackages(Collection packages) { + mModifiedPackages.addAll(packages); + } + + public void addUri(Uri uri) { + mUris.add(uri); + } + + public void sendNotification() { + Uri uri = mUris.size() == 1 ? mUris.iterator().next() : mBaseUri; + mContext.getContentResolver().notifyChange(uri, null, true); + Collection callingPackages = getCallingPackages(); + // Now fire individual intents. + for (String intentAction : mIntentActions) { + // self_change extra should be included only for provider_changed events. + boolean includeSelfChangeExtra = intentAction.equals(Intent.ACTION_PROVIDER_CHANGED); + Log.i(TAG, "receivers for " + intentAction + " :" + getBroadcastReceiverComponents( + intentAction, uri)); + for (ComponentName component : + getBroadcastReceiverComponents(intentAction, uri)) { + // Ignore any package that is not affected by the change and don't have full access + // either. + if (!mModifiedPackages.contains(component.getPackageName()) && + !mVoicemailPermissions.packageHasReadAccess( + component.getPackageName())) { + continue; + } + + Intent intent = new Intent(intentAction, uri); + intent.setComponent(component); + if (includeSelfChangeExtra && callingPackages != null) { + intent.putExtra(VoicemailContract.EXTRA_SELF_CHANGE, + callingPackages.contains(component.getPackageName())); + } + String permissionNeeded = mModifiedPackages.contains(component.getPackageName()) ? + ADD_VOICEMAIL : READ_VOICEMAIL; + mContext.sendBroadcast(intent, permissionNeeded); + Log.v(TAG, String.format("Sent intent. act:%s, url:%s, comp:%s, perm:%s," + + " self_change:%s", intent.getAction(), intent.getData(), + component.getClassName(), permissionNeeded, + intent.hasExtra(VoicemailContract.EXTRA_SELF_CHANGE) ? + intent.getBooleanExtra(VoicemailContract.EXTRA_SELF_CHANGE, false) : + null)); + } + } + mIntentActions.clear(); + mModifiedPackages.clear(); + mUris.clear(); + } + + /** + * Returns the package names of the calling process. If the calling process has more than + * one packages, this returns them all + */ + private Collection getCallingPackages() { + int caller = Binder.getCallingUid(); + if (caller == 0) { + return null; + } + return Lists.newArrayList(mContext.getPackageManager().getPackagesForUid(caller)); + } + + /** + * Determines the components that can possibly receive the specified intent. + */ + private List getBroadcastReceiverComponents(String intentAction, Uri uri) { + Intent intent = new Intent(intentAction, uri); + List receiverComponents = new ArrayList(); + // For broadcast receivers ResolveInfo.activityInfo is the one that is populated. + for (ResolveInfo resolveInfo : + mContext.getPackageManager().queryBroadcastReceivers(intent, 0)) { + ActivityInfo activityInfo = resolveInfo.activityInfo; + receiverComponents.add(new ComponentName(activityInfo.packageName, activityInfo.name)); + } + return receiverComponents; + } +} diff --git a/src/com/android/providers/contacts/VoicemailStatusTable.java b/src/com/android/providers/contacts/VoicemailStatusTable.java index f3008c0e..5b03c8a6 100644 --- a/src/com/android/providers/contacts/VoicemailStatusTable.java +++ b/src/com/android/providers/contacts/VoicemailStatusTable.java @@ -32,8 +32,6 @@ import android.util.ArraySet; import com.android.common.content.ProjectionMap; import com.android.providers.contacts.VoicemailContentProvider.UriData; -import java.util.Set; - /** * Implementation of {@link VoicemailTable.Delegate} for the voicemail status table. * @@ -78,7 +76,7 @@ public class VoicemailStatusTable implements VoicemailTable.Delegate { SQLiteDatabase db = mDbHelper.getWritableDatabase(); // Try to update before insert. String combinedClause = uriData.getWhereClause(); - int rowsChanged = getDatabaseModifier(db) + int rowsChanged = createDatabaseModifier(db) .update(uriData.getUri(), mTableName, values, combinedClause, null); if (rowsChanged != 0) { final String[] selection = new String[] {Status._ID}; @@ -90,7 +88,7 @@ public class VoicemailStatusTable implements VoicemailTable.Delegate { } ContentValues copiedValues = new ContentValues(values); mDelegateHelper.checkAndAddSourcePackageIntoValues(uriData, copiedValues); - long rowId = getDatabaseModifier(db).insert(mTableName, null, copiedValues); + long rowId = createDatabaseModifier(db).insert(mTableName, null, copiedValues); if (rowId > 0) { return ContentUris.withAppendedId(uriData.getUri(), rowId); } else { @@ -99,12 +97,24 @@ public class VoicemailStatusTable implements VoicemailTable.Delegate { } } + @Override + public int bulkInsert(UriData uriData, ContentValues[] values) { + int count = 0; + for (ContentValues value : values) { + Uri uri = insert(uriData, value); + if (uri != null) { + count++; + } + } + return count; + } + @Override public int delete(UriData uriData, String selection, String[] selectionArgs) { synchronized (DATABASE_LOCK) { SQLiteDatabase db = mDbHelper.getWritableDatabase(); String combinedClause = concatenateClauses(selection, uriData.getWhereClause()); - return getDatabaseModifier(db).delete(mTableName, combinedClause, + return createDatabaseModifier(db).delete(mTableName, combinedClause, selectionArgs); } } @@ -135,7 +145,7 @@ public class VoicemailStatusTable implements VoicemailTable.Delegate { synchronized (DATABASE_LOCK) { SQLiteDatabase db = mDbHelper.getWritableDatabase(); String combinedClause = concatenateClauses(selection, uriData.getWhereClause()); - return getDatabaseModifier(db) + return createDatabaseModifier(db) .update(uriData.getUri(), mTableName, values, combinedClause, selectionArgs); } } @@ -154,7 +164,7 @@ public class VoicemailStatusTable implements VoicemailTable.Delegate { throw new UnsupportedOperationException("File operation is not supported for status table"); } - private DatabaseModifier getDatabaseModifier(SQLiteDatabase db) { + private DatabaseModifier createDatabaseModifier(SQLiteDatabase db) { return new DbModifierWithNotification(mTableName, db, mContext); } diff --git a/src/com/android/providers/contacts/VoicemailTable.java b/src/com/android/providers/contacts/VoicemailTable.java index fcb653ce..f71b50de 100644 --- a/src/com/android/providers/contacts/VoicemailTable.java +++ b/src/com/android/providers/contacts/VoicemailTable.java @@ -46,6 +46,8 @@ public interface VoicemailTable { public ParcelFileDescriptor openFile(UriData uriData, String mode) throws FileNotFoundException; public ArraySet getSourcePackages(); + + int bulkInsert(UriData uriData, ContentValues[] values); } /** diff --git a/tests/src/com/android/providers/contacts/VoicemailProviderTest.java b/tests/src/com/android/providers/contacts/VoicemailProviderTest.java index 4fa935fa..1cd6646d 100644 --- a/tests/src/com/android/providers/contacts/VoicemailProviderTest.java +++ b/tests/src/com/android/providers/contacts/VoicemailProviderTest.java @@ -20,7 +20,6 @@ import android.content.ContentUris; import android.content.ContentValues; import android.database.Cursor; import android.net.Uri; -import android.os.BatteryStats.Uid.Proc; import android.os.ParcelFileDescriptor; import android.os.Process; import android.provider.CallLog; @@ -32,6 +31,7 @@ import android.test.MoreAsserts; import android.test.suitebuilder.annotation.SmallTest; import com.android.common.io.MoreCloseables; +import org.mockito.Mockito; import java.io.FileNotFoundException; import java.io.IOException; @@ -40,6 +40,9 @@ import java.io.OutputStream; import java.util.Arrays; import java.util.List; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.mock; + /** * Unit tests for {@link VoicemailContentProvider}. * @@ -51,7 +54,12 @@ import java.util.List; // TODO: Test that calltype and voicemail_uri are auto populated by the provider. @SmallTest public class VoicemailProviderTest extends BaseVoicemailProviderTest { - /** Fields specific to call_log provider that should not be exposed by voicemail provider. */ + + private static final String SYSTEM_PROPERTY_DEXMAKER_DEXCACHE = "dexmaker.dexcache"; + + /** + * Fields specific to call_log provider that should not be exposed by voicemail provider. + */ private static final String[] CALLLOG_PROVIDER_SPECIFIC_COLUMNS = { Calls.CACHED_NAME, Calls.CACHED_NUMBER_LABEL, @@ -60,23 +68,37 @@ public class VoicemailProviderTest extends BaseVoicemailProviderTest { Calls.VOICEMAIL_URI, Calls.COUNTRY_ISO }; - /** Total number of columns exposed by voicemail provider. */ + /** + * Total number of columns exposed by voicemail provider. + */ private static final int NUM_VOICEMAIL_FIELDS = 24; @Override protected void setUp() throws Exception { super.setUp(); setUpForOwnPermission(); + System.setProperty(SYSTEM_PROPERTY_DEXMAKER_DEXCACHE, getContext().getCacheDir().getPath()); + Thread.currentThread().setContextClassLoader(VoicemailContentProvider.class.getClassLoader()); addProvider(CallLogProviderTestable.class, CallLog.AUTHORITY); } - /** Returns the appropriate /voicemail URI. */ + @Override + protected void tearDown() throws Exception { + System.clearProperty(SYSTEM_PROPERTY_DEXMAKER_DEXCACHE); + DbModifierWithNotification.setVoicemailNotifierForTest(null); + } + + /** + * Returns the appropriate /voicemail URI. + */ private Uri voicemailUri() { return mUseSourceUri ? Voicemails.buildSourceUri(mActor.packageName) : Voicemails.CONTENT_URI; } - /** Returns the appropriate /status URI. */ + /** + * Returns the appropriate /status URI. + */ private Uri statusUri() { return mUseSourceUri ? Status.buildSourceUri(mActor.packageName) : Status.CONTENT_URI; @@ -118,6 +140,14 @@ public class VoicemailProviderTest extends BaseVoicemailProviderTest { } } + public void testBulkInsert() { + VoicemailNotifier notifier = mock(VoicemailNotifier.class); + DbModifierWithNotification.setVoicemailNotifierForTest(notifier); + mResolver.bulkInsert(voicemailUri(), + new ContentValues[] {getTestVoicemailValues(), getTestVoicemailValues()}); + verify(notifier, Mockito.times(1)).sendNotification(); + } + // Test to ensure that media content can be written and read back. public void testFileContent() throws Exception { Uri uri = insertVoicemail(); -- cgit v1.2.3 From d6743da473850d7f9cc900cc490afc905900409f Mon Sep 17 00:00:00 2001 From: Bill Yi Date: Mon, 6 Nov 2017 22:07:09 -0800 Subject: Import translations. DO NOT MERGE Change-Id: I07c60dcfdabc11b034cc84b958b51c5bd2383f07 Auto-generated-cl: translation import Exempt-From-Owner-Approval: translation import --- res/values-mr/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/res/values-mr/strings.xml b/res/values-mr/strings.xml index 7e6d6054..a4f324e0 100644 --- a/res/values-mr/strings.xml +++ b/res/values-mr/strings.xml @@ -20,7 +20,7 @@ "संपर्क संचयन" "संपर्क" "संपर्क श्रेणीसुधारित करण्‍यास अधिक मेमरी आवश्‍यक आहे." - "संपर्कांसाठी संचयन श्रेणीसुधारित करीत आहे" + "संपर्कांसाठी संचयन श्रेणीसुधारित करत आहे" "श्रेणीसुधारणा पूर्ण करण्यासाठी टॅप करा." "संपर्क" "इतर" -- cgit v1.2.3 From db3f85c4ba3316c9b3514a490a6e3cb7bf7c5a8f Mon Sep 17 00:00:00 2001 From: Bill Yi Date: Mon, 20 Nov 2017 11:21:53 -0800 Subject: Import translations. DO NOT MERGE Change-Id: I84c6739eac0a0c67abff55268ef296d1bab3c7a2 Auto-generated-cl: translation import --- res/values-sw/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/res/values-sw/strings.xml b/res/values-sw/strings.xml index 920511ab..92bd391f 100644 --- a/res/values-sw/strings.xml +++ b/res/values-sw/strings.xml @@ -21,7 +21,7 @@ "Anwani" "Kupandishwa gredi kwa anwani kunahitaji kumbukumbu zaidi." "Inapandisha gredi hifadhi ya anwani" - "Gonga ili ukamilishe kusasisha anwani." + "Gusa ili ukamilishe kusasisha anwani." "Anwani" "Nyingineyo" "Barua ya sauti kutoka " -- cgit v1.2.3 From 78ce51eacb3c0da4ea8fe88541d040a85b1bb9e6 Mon Sep 17 00:00:00 2001 From: Ta-wei Yen Date: Thu, 30 Nov 2017 14:46:15 -0800 Subject: Implement DIRTY_RETAIN constant Previously the DIRTY flag can only be automatically or explicitly set. There are no option to express "This operation does not change the DIRTY flag". This causes issue when the voicemail source is downloading changes from the server instead of uploading and would clobber the flag. After this CL, if DIRTY is updated with DIRTY_RETAIN, the flag will not be updated for the operation. Change-Id: I23a23a11f7d2177d10b65bbc33875b7eb838f7c1 Fixes: 64371667 Test: CtsProviderTestCases#VoicemailContractTest --- .../contacts/DbModifierWithNotification.java | 62 ++++++++++++++-------- .../providers/contacts/VoicemailProviderTest.java | 56 +++++++++++++++++-- 2 files changed, 90 insertions(+), 28 deletions(-) diff --git a/src/com/android/providers/contacts/DbModifierWithNotification.java b/src/com/android/providers/contacts/DbModifierWithNotification.java index 36865fa1..0babe1b9 100644 --- a/src/com/android/providers/contacts/DbModifierWithNotification.java +++ b/src/com/android/providers/contacts/DbModifierWithNotification.java @@ -178,28 +178,21 @@ public class DbModifierWithNotification implements DatabaseModifier { updateLastModified(table, whereClause, whereArgs); } if (isVoicemail) { - // If a calling package is modifying its own entries, it means that the change came - // from the server and thus is synced or "clean". Otherwise, it means that a local - // change is being made to the database, so the entries should be marked as "dirty" - // so that the corresponding sync adapter knows they need to be synced. - int isDirty; - Integer callerSetDirty = values.getAsInteger(Voicemails.DIRTY); - if (callerSetDirty != null) { - // Respect the calling package if it sets the dirty flag - isDirty = callerSetDirty == 0 ? 0 : 1; - } else { - isDirty = isSelfModifyingOrInternal(packagesModified) ? 0 : 1; - } - values.put(VoicemailContract.Voicemails.DIRTY, isDirty); - - if (isDirty == 0 && values.containsKey(Calls.IS_READ) && getAsBoolean(values, - Calls.IS_READ)) { - // If the server has set the IS_READ, it should also unset the new flag - if (!values.containsKey(Calls.NEW)) { - values.put(Calls.NEW, 0); - hasMarkedRead = true; + if (updateDirtyFlag(values, packagesModified)) { + if (values.containsKey(Calls.IS_READ) + && getAsBoolean(values, + Calls.IS_READ)) { + // If the server has set the IS_READ, it should also unset the new flag + if (!values.containsKey(Calls.NEW)) { + values.put(Calls.NEW, 0); + hasMarkedRead = true; + } } } + // updateDirtyFlag might remove the value and leave values empty. + if(values.isEmpty()){ + return 0; + } } } @@ -221,6 +214,29 @@ public class DbModifierWithNotification implements DatabaseModifier { return count; } + private boolean updateDirtyFlag(ContentValues values, Set packagesModified) { + // If a calling package is modifying its own entries, it means that the change came + // from the server and thus is synced or "clean". Otherwise, it means that a local + // change is being made to the database, so the entries should be marked as "dirty" + // so that the corresponding sync adapter knows they need to be synced. + int isDirty; + Integer callerSetDirty = values.getAsInteger(Voicemails.DIRTY); + if (callerSetDirty != null) { + // Respect the calling package if it sets the dirty flag + if (callerSetDirty == Voicemails.DIRTY_RETAIN) { + values.remove(Voicemails.DIRTY); + return false; + } else { + isDirty = callerSetDirty == 0 ? 0 : 1; + } + } else { + isDirty = isSelfModifyingOrInternal(packagesModified) ? 0 : 1; + } + + values.put(Voicemails.DIRTY, isDirty); + return isDirty == 0; + } + private void updateLastModified(String table, String whereClause, String[] whereArgs) { ContentValues values = new ContentValues(); values.put(Calls.LAST_MODIFIED, getTimeMillis()); @@ -317,7 +333,7 @@ public class DbModifierWithNotification implements DatabaseModifier { /** * @param packagesModified source packages that inserted the voicemail that is being modified * @return {@code true} if the caller is modifying its own voicemail, or this is an internal - * transaction, {@code false} otherwise. + * transaction, {@code false} otherwise. */ private boolean isSelfModifyingOrInternal(Set packagesModified) { final Collection callingPackages = getCallingPackages(); @@ -328,7 +344,7 @@ public class DbModifierWithNotification implements DatabaseModifier { // but allows us to mock the results for testing. return packagesModified.size() == 1 && (callingPackages.contains( Iterables.getOnlyElement(packagesModified)) - || callingPackages.contains(mContext.getPackageName())); + || callingPackages.contains(mContext.getPackageName())); } /** @@ -370,7 +386,7 @@ public class DbModifierWithNotification implements DatabaseModifier { } @VisibleForTesting - static void setVoicemailNotifierForTest(VoicemailNotifier notifier){ + static void setVoicemailNotifierForTest(VoicemailNotifier notifier) { sVoicemailNotifierForTest = notifier; } } diff --git a/tests/src/com/android/providers/contacts/VoicemailProviderTest.java b/tests/src/com/android/providers/contacts/VoicemailProviderTest.java index 1cd6646d..1bf0d847 100644 --- a/tests/src/com/android/providers/contacts/VoicemailProviderTest.java +++ b/tests/src/com/android/providers/contacts/VoicemailProviderTest.java @@ -16,6 +16,9 @@ package com.android.providers.contacts; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; + import android.content.ContentUris; import android.content.ContentValues; import android.database.Cursor; @@ -31,7 +34,6 @@ import android.test.MoreAsserts; import android.test.suitebuilder.annotation.SmallTest; import com.android.common.io.MoreCloseables; -import org.mockito.Mockito; import java.io.FileNotFoundException; import java.io.IOException; @@ -39,9 +41,7 @@ import java.io.InputStream; import java.io.OutputStream; import java.util.Arrays; import java.util.List; - -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.mock; +import org.mockito.Mockito; /** * Unit tests for {@link VoicemailContentProvider}. @@ -202,7 +202,7 @@ public class VoicemailProviderTest extends BaseVoicemailProviderTest { public void testUpdateOwnPackageVoicemail_RemovesDirtyStatus() { ContentValues values = getTestVoicemailValues(); values.put(Voicemails.DIRTY, "1"); - final Uri uri = mResolver.insert(voicemailUri(), getTestVoicemailValues()); + final Uri uri = mResolver.insert(voicemailUri(), values); mResolver.update(uri, new ContentValues(), null, null); // At this point, the voicemail should be set back to not dirty. @@ -211,6 +211,52 @@ public class VoicemailProviderTest extends BaseVoicemailProviderTest { assertStoredValues(uri, newValues); } + public void testUpdateOwnPackageVoicemail_retainDirtyStatus_dirty() { + ContentValues values = getTestVoicemailValues(); + values.put(Voicemails.DIRTY, "1"); + final Uri uri = mResolver.insert(voicemailUri(), values); + + ContentValues retainDirty = new ContentValues(); + retainDirty.put(Voicemails.TRANSCRIPTION, "foo"); + retainDirty.put(Voicemails.DIRTY, Voicemails.DIRTY_RETAIN); + + mResolver.update(uri, retainDirty, null, null); + ContentValues newValues = getTestVoicemailValues(); + newValues.put(Voicemails.DIRTY, "1"); + newValues.put(Voicemails.TRANSCRIPTION, "foo"); + assertStoredValues(uri, newValues); + } + + public void testUpdateOwnPackageVoicemail_retainDirtyStatus_notDirty() { + ContentValues values = getTestVoicemailValues(); + values.put(Voicemails.DIRTY, "0"); + final Uri uri = mResolver.insert(voicemailUri(), values); + + ContentValues retainDirty = new ContentValues(); + retainDirty.put(Voicemails.TRANSCRIPTION, "foo"); + retainDirty.put(Voicemails.DIRTY, Voicemails.DIRTY_RETAIN); + + mResolver.update(uri, retainDirty, null, null); + ContentValues newValues = getTestVoicemailValues(); + newValues.put(Voicemails.DIRTY, "0"); + newValues.put(Voicemails.TRANSCRIPTION, "foo"); + assertStoredValues(uri, newValues); + } + + public void testUpdateOwnPackageVoicemail_retainDirtyStatus_noOtherValues() { + ContentValues values = getTestVoicemailValues(); + values.put(Voicemails.DIRTY, "1"); + final Uri uri = mResolver.insert(voicemailUri(), values); + + ContentValues retainDirty = new ContentValues(); + retainDirty.put(Voicemails.DIRTY, Voicemails.DIRTY_RETAIN); + + mResolver.update(uri, retainDirty, null, null); + ContentValues newValues = getTestVoicemailValues(); + newValues.put(Voicemails.DIRTY, "1"); + assertStoredValues(uri, newValues); + } + public void testDeleteOwnPackageVoicemail_DeletesRow() { setUpForFullPermission(); final Uri ownVoicemail = insertVoicemail(); -- cgit v1.2.3 From 028b73bebdb9cac09de3b93a82ccb72ad3238ed6 Mon Sep 17 00:00:00 2001 From: Ta-wei Yen Date: Tue, 28 Nov 2017 17:11:42 -0800 Subject: Implement preferred phone account columns Fixes: 69868483 Test: CtsProviderTestCases Change-Id: Ib8af85b5c618c1a2c62bc6c09daae057521de5d6 --- .../providers/contacts/ContactsDatabaseHelper.java | 24 ++++++++++++++++++++-- .../providers/contacts/ContactsProvider2.java | 2 ++ .../ContactsDatabaseHelperUpgradeTest.java | 11 ++-------- .../providers/contacts/ContactsProvider2Test.java | 15 +++++++++++++- 4 files changed, 40 insertions(+), 12 deletions(-) diff --git a/src/com/android/providers/contacts/ContactsDatabaseHelper.java b/src/com/android/providers/contacts/ContactsDatabaseHelper.java index 76fe173d..9414ece8 100644 --- a/src/com/android/providers/contacts/ContactsDatabaseHelper.java +++ b/src/com/android/providers/contacts/ContactsDatabaseHelper.java @@ -136,9 +136,10 @@ public class ContactsDatabaseHelper extends SQLiteOpenHelper { * 1000-1099 M * 1100-1199 N * 1200-1299 O + * 1300-1399 P * */ - static final int DATABASE_VERSION = 1202; + static final int DATABASE_VERSION = 1300; private static final int MINIMUM_SUPPORTED_VERSION = 700; @VisibleForTesting @@ -1440,7 +1441,9 @@ public class ContactsDatabaseHelper extends SQLiteOpenHelper { Data.SYNC2 + " TEXT, " + Data.SYNC3 + " TEXT, " + Data.SYNC4 + " TEXT, " + - Data.CARRIER_PRESENCE + " INTEGER NOT NULL DEFAULT 0 " + + Data.CARRIER_PRESENCE + " INTEGER NOT NULL DEFAULT 0, " + + Data.PREFERRED_PHONE_ACCOUNT_COMPONENT_NAME + " TEXT, " + + Data.PREFERRED_PHONE_ACCOUNT_ID + " TEXT " + ");"); db.execSQL("CREATE INDEX data_raw_contact_id ON " + Tables.DATA + " (" + @@ -1924,6 +1927,8 @@ public class ContactsDatabaseHelper extends SQLiteOpenHelper { + Data.DATA14 + ", " + Data.DATA15 + ", " + Data.CARRIER_PRESENCE + ", " + + Data.PREFERRED_PHONE_ACCOUNT_COMPONENT_NAME + ", " + + Data.PREFERRED_PHONE_ACCOUNT_ID + ", " + Data.SYNC1 + ", " + Data.SYNC2 + ", " + Data.SYNC3 + ", " @@ -2666,6 +2671,12 @@ public class ContactsDatabaseHelper extends SQLiteOpenHelper { oldVersion = 1202; } + if (isUpgradeRequired(oldVersion,newVersion, 1300)) { + upgradeToVersion1300(db); + upgradeViewsAndTriggers = true; + oldVersion = 1300; + } + // We extracted "calls" and "voicemail_status" at this point, but we can't remove them here // yet, until CallLogDatabaseHelper moves the data. @@ -3438,6 +3449,15 @@ public class ContactsDatabaseHelper extends SQLiteOpenHelper { + "last_time_used = 0"); } + public void upgradeToVersion1300(SQLiteDatabase db) { + try { + db.execSQL("ALTER TABLE data ADD preferred_phone_account_component_name " + + "TEXT;"); + db.execSQL("ALTER TABLE data ADD preferred_phone_account_id TEXT;"); + } catch (SQLiteException ignore) { + } + } + /** * This method is only used in upgradeToVersion1101 method, and should not be used in other * places now. Because data15 is not used to generate hash_id for photo, and the new generating diff --git a/src/com/android/providers/contacts/ContactsProvider2.java b/src/com/android/providers/contacts/ContactsProvider2.java index 701c7916..1d5681ac 100644 --- a/src/com/android/providers/contacts/ContactsProvider2.java +++ b/src/com/android/providers/contacts/ContactsProvider2.java @@ -754,6 +754,8 @@ public class ContactsProvider2 extends AbstractContactsProvider .add(Data.DATA14) .add(Data.DATA15) .add(Data.CARRIER_PRESENCE) + .add(Data.PREFERRED_PHONE_ACCOUNT_COMPONENT_NAME) + .add(Data.PREFERRED_PHONE_ACCOUNT_ID) .add(Data.DATA_VERSION) .add(Data.IS_PRIMARY) .add(Data.IS_SUPER_PRIMARY) diff --git a/tests/src/com/android/providers/contacts/ContactsDatabaseHelperUpgradeTest.java b/tests/src/com/android/providers/contacts/ContactsDatabaseHelperUpgradeTest.java index 185fa031..1832b4e4 100644 --- a/tests/src/com/android/providers/contacts/ContactsDatabaseHelperUpgradeTest.java +++ b/tests/src/com/android/providers/contacts/ContactsDatabaseHelperUpgradeTest.java @@ -16,7 +16,6 @@ package com.android.providers.contacts; -import static com.android.providers.contacts.TestUtils.createDatabaseSnapshot; import static com.android.providers.contacts.TestUtils.cv; import static com.android.providers.contacts.TestUtils.executeSqlFromAssetFile; @@ -67,14 +66,6 @@ import com.android.providers.contacts.ContactsDatabaseHelper.Tables; import com.android.providers.contacts.testutil.TestUtil; import com.android.providers.contacts.util.PropertyUtils; -import junit.framework.AssertionFailedError; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.util.HashMap; - /** * Unit tests for database create/upgrade operations in {@link ContactsDatabaseHelper}. * @@ -376,6 +367,8 @@ public class ContactsDatabaseHelperUpgradeTest extends BaseDatabaseHelperUpgrade new TableColumn(Data.SYNC3, TEXT, false, null), new TableColumn(Data.SYNC4, TEXT, false, null), new TableColumn(Data.CARRIER_PRESENCE, INTEGER, true, "0"), + new TableColumn(Data.PREFERRED_PHONE_ACCOUNT_COMPONENT_NAME, TEXT, false, null), + new TableColumn(Data.PREFERRED_PHONE_ACCOUNT_ID, TEXT, false, null), }; private static final TableColumn[] PHONE_LOOKUP_COLUMNS = new TableColumn[] { diff --git a/tests/src/com/android/providers/contacts/ContactsProvider2Test.java b/tests/src/com/android/providers/contacts/ContactsProvider2Test.java index 23b42dae..6c76709e 100644 --- a/tests/src/com/android/providers/contacts/ContactsProvider2Test.java +++ b/tests/src/com/android/providers/contacts/ContactsProvider2Test.java @@ -16,7 +16,6 @@ package com.android.providers.contacts; -import static com.android.providers.contacts.TestUtils.createDatabaseSnapshot; import static com.android.providers.contacts.TestUtils.cv; import static com.android.providers.contacts.TestUtils.dumpCursor; @@ -420,6 +419,8 @@ public class ContactsProvider2Test extends BaseContactsProvider2Test { Data.DATA14, Data.DATA15, Data.CARRIER_PRESENCE, + Data.PREFERRED_PHONE_ACCOUNT_COMPONENT_NAME, + Data.PREFERRED_PHONE_ACCOUNT_ID, Data.SYNC1, Data.SYNC2, Data.SYNC3, @@ -508,6 +509,8 @@ public class ContactsProvider2Test extends BaseContactsProvider2Test { Data.DATA14, Data.DATA15, Data.CARRIER_PRESENCE, + Data.PREFERRED_PHONE_ACCOUNT_COMPONENT_NAME, + Data.PREFERRED_PHONE_ACCOUNT_ID, Data.SYNC1, Data.SYNC2, Data.SYNC3, @@ -590,6 +593,8 @@ public class ContactsProvider2Test extends BaseContactsProvider2Test { Data.DATA14, Data.DATA15, Data.CARRIER_PRESENCE, + Data.PREFERRED_PHONE_ACCOUNT_COMPONENT_NAME, + Data.PREFERRED_PHONE_ACCOUNT_ID, Data.SYNC1, Data.SYNC2, Data.SYNC3, @@ -698,6 +703,8 @@ public class ContactsProvider2Test extends BaseContactsProvider2Test { Data.DATA14, Data.DATA15, Data.CARRIER_PRESENCE, + Data.PREFERRED_PHONE_ACCOUNT_COMPONENT_NAME, + Data.PREFERRED_PHONE_ACCOUNT_ID, Data.SYNC1, Data.SYNC2, Data.SYNC3, @@ -6485,6 +6492,8 @@ public class ContactsProvider2Test extends BaseContactsProvider2Test { values.put(Data.DATA14, "old14"); values.put(Data.DATA15, "old15"); values.put(Data.CARRIER_PRESENCE, 0); + values.put(Data.PREFERRED_PHONE_ACCOUNT_COMPONENT_NAME, "oldcomponentname"); + values.put(Data.PREFERRED_PHONE_ACCOUNT_ID, "oldid"); Uri uri = mResolver.insert(Data.CONTENT_URI, values); assertStoredValues(uri, values); assertNetworkNotified(true); @@ -6509,6 +6518,8 @@ public class ContactsProvider2Test extends BaseContactsProvider2Test { values.put(Data.DATA14, "new14"); values.put(Data.DATA15, "new15"); values.put(Data.CARRIER_PRESENCE, Data.CARRIER_PRESENCE_VT_CAPABLE); + values.put(Data.PREFERRED_PHONE_ACCOUNT_COMPONENT_NAME, "newcomponentname"); + values.put(Data.PREFERRED_PHONE_ACCOUNT_ID, "newid"); mResolver.update(Data.CONTENT_URI, values, Data.RAW_CONTACT_ID + "=" + rawContactId + " AND " + Data.MIMETYPE + "='testmimetype'", null); assertNetworkNotified(true); @@ -9909,6 +9920,8 @@ public class ContactsProvider2Test extends BaseContactsProvider2Test { values.put(Data.DATA14, "fourteen"); values.put(Data.DATA15, "fifteen".getBytes()); values.put(Data.CARRIER_PRESENCE, Data.CARRIER_PRESENCE_VT_CAPABLE); + values.put(Data.PREFERRED_PHONE_ACCOUNT_COMPONENT_NAME, "preferredcomponentname"); + values.put(Data.PREFERRED_PHONE_ACCOUNT_ID, "preferredid"); values.put(Data.SYNC1, "sync1"); values.put(Data.SYNC2, "sync2"); values.put(Data.SYNC3, "sync3"); -- cgit v1.2.3 From 702ce7a78cfca923997e84f953c30cdc24146d12 Mon Sep 17 00:00:00 2001 From: Paul Duffin Date: Fri, 8 Dec 2017 00:02:43 +0000 Subject: Stop statically including legacy-android-test Statically including legacy-android-test leads to duplicate classes which causes build time problems (with Proguard) and runtime problems on older SDK versions. This change: * Stops statically including legacy-android-test. * Adds compile time dependencies on andoid.test.base, android.test.mock and android.test.runner where necessary. * Adds to any affected package to ensure that the classes that were included by legacy-android-test are still available at runtime. That also adds a dependency on android.test.base and android.test.mock. The following change descriptions were generated automatically and so may be a little repetitive. They are provided to give the reviewer enough information to check the comments match what has actually been changed and check the reasoning behind the changes. * tests/Android.mk Removed legacy-android-test from LOCAL_STATIC_JAVA_LIBRARIES because statically including the classes in ContactsProviderTests results in duplicate classes which leads to build time and compile time issues. Added 'android.test.base' and 'android.test.mock' to LOCAL_JAVA_LIBRARIES because ContactsProviderTests's source depends on their classes and because of these changes they are no longer present on the compilation path. * tests2/Android.mk Removed legacy-android-test from LOCAL_STATIC_JAVA_LIBRARIES because statically including the classes in ContactsProviderTests2 results in duplicate classes which leads to build time and compile time issues. Added 'android.test.base' to LOCAL_JAVA_LIBRARIES because ContactsProviderTests2's source depends on its classes and because of these changes they are no longer present on the compilation path. Bug: 30188076 Test: make checkbuild Change-Id: Ib9e624d4e31d65e92f5c040916230e6df0a0e8cb --- tests/Android.mk | 5 ++--- tests2/Android.mk | 5 ++--- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/tests/Android.mk b/tests/Android.mk index 8df1d6d4..e0c62843 100644 --- a/tests/Android.mk +++ b/tests/Android.mk @@ -7,10 +7,9 @@ LOCAL_MODULE_TAGS := tests LOCAL_STATIC_JAVA_LIBRARIES := \ ContactsProviderTestUtils \ android-support-test \ - mockito-target-minus-junit4 \ - legacy-android-test + mockito-target-minus-junit4 -LOCAL_JAVA_LIBRARIES := android.test.runner +LOCAL_JAVA_LIBRARIES := android.test.runner android.test.base android.test.mock # Only compile source java files in this apk. LOCAL_SRC_FILES := $(call all-java-files-under, src) diff --git a/tests2/Android.mk b/tests2/Android.mk index bb4443f3..4a0fdfb0 100644 --- a/tests2/Android.mk +++ b/tests2/Android.mk @@ -22,10 +22,9 @@ LOCAL_MODULE_TAGS := tests LOCAL_STATIC_JAVA_LIBRARIES := \ ContactsProviderTestUtils \ android-support-test \ - mockito-target-minus-junit4 \ - legacy-android-test + mockito-target-minus-junit4 -LOCAL_JAVA_LIBRARIES := android.test.runner +LOCAL_JAVA_LIBRARIES := android.test.runner android.test.base LOCAL_SRC_FILES := $(call all-java-files-under, src) -- cgit v1.2.3 From d9059b71617b107899e7b56be46664b4b333cc1f Mon Sep 17 00:00:00 2001 From: Bill Yi Date: Tue, 19 Dec 2017 15:42:18 -0800 Subject: Import translations. DO NOT MERGE Auto-generated-cl: translation import Bug: 64712476 Change-Id: If392616769f5bf6795dd1b6876a88c5429712f54 --- res/values-mr/strings.xml | 2 +- res/values-sw/strings.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/res/values-mr/strings.xml b/res/values-mr/strings.xml index 7e6d6054..a4f324e0 100644 --- a/res/values-mr/strings.xml +++ b/res/values-mr/strings.xml @@ -20,7 +20,7 @@ "संपर्क संचयन" "संपर्क" "संपर्क श्रेणीसुधारित करण्‍यास अधिक मेमरी आवश्‍यक आहे." - "संपर्कांसाठी संचयन श्रेणीसुधारित करीत आहे" + "संपर्कांसाठी संचयन श्रेणीसुधारित करत आहे" "श्रेणीसुधारणा पूर्ण करण्यासाठी टॅप करा." "संपर्क" "इतर" diff --git a/res/values-sw/strings.xml b/res/values-sw/strings.xml index 920511ab..92bd391f 100644 --- a/res/values-sw/strings.xml +++ b/res/values-sw/strings.xml @@ -21,7 +21,7 @@ "Anwani" "Kupandishwa gredi kwa anwani kunahitaji kumbukumbu zaidi." "Inapandisha gredi hifadhi ya anwani" - "Gonga ili ukamilishe kusasisha anwani." + "Gusa ili ukamilishe kusasisha anwani." "Anwani" "Nyingineyo" "Barua ya sauti kutoka " -- cgit v1.2.3 From 7fff7b1de14d7bc2590c3ffe2e73b43699146fe7 Mon Sep 17 00:00:00 2001 From: Bill Yi Date: Tue, 19 Dec 2017 15:42:54 -0800 Subject: Import translations. DO NOT MERGE Auto-generated-cl: translation import Bug: 64712476 Change-Id: I8d47ccd4c44916c796e3457e9cca2c9d751f8a99 --- res/values-mr/strings.xml | 2 +- res/values-sw/strings.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/res/values-mr/strings.xml b/res/values-mr/strings.xml index 7e6d6054..a4f324e0 100644 --- a/res/values-mr/strings.xml +++ b/res/values-mr/strings.xml @@ -20,7 +20,7 @@ "संपर्क संचयन" "संपर्क" "संपर्क श्रेणीसुधारित करण्‍यास अधिक मेमरी आवश्‍यक आहे." - "संपर्कांसाठी संचयन श्रेणीसुधारित करीत आहे" + "संपर्कांसाठी संचयन श्रेणीसुधारित करत आहे" "श्रेणीसुधारणा पूर्ण करण्यासाठी टॅप करा." "संपर्क" "इतर" diff --git a/res/values-sw/strings.xml b/res/values-sw/strings.xml index 920511ab..92bd391f 100644 --- a/res/values-sw/strings.xml +++ b/res/values-sw/strings.xml @@ -21,7 +21,7 @@ "Anwani" "Kupandishwa gredi kwa anwani kunahitaji kumbukumbu zaidi." "Inapandisha gredi hifadhi ya anwani" - "Gonga ili ukamilishe kusasisha anwani." + "Gusa ili ukamilishe kusasisha anwani." "Anwani" "Nyingineyo" "Barua ya sauti kutoka " -- cgit v1.2.3 From 0c37d7518d22c34dc68a613b65a673c47f48f6a8 Mon Sep 17 00:00:00 2001 From: Ta-wei Yen Date: Fri, 5 Jan 2018 14:51:01 -0800 Subject: Don't update DIRTY flag when updating non-voicemail columns Caching contact data should not cause an upload. Fixes: 71605105 Test: VoicemailProviderTest Change-Id: I9301c00ccb14580c5c20268a3e4bbe1f0bbb52f5 --- .../contacts/DbModifierWithNotification.java | 20 ++++-- .../providers/contacts/VoicemailContentTable.java | 2 +- .../providers/contacts/VoicemailProviderTest.java | 73 +++++++++++++++------- 3 files changed, 66 insertions(+), 29 deletions(-) diff --git a/src/com/android/providers/contacts/DbModifierWithNotification.java b/src/com/android/providers/contacts/DbModifierWithNotification.java index 0babe1b9..05803391 100644 --- a/src/com/android/providers/contacts/DbModifierWithNotification.java +++ b/src/com/android/providers/contacts/DbModifierWithNotification.java @@ -167,7 +167,7 @@ public class DbModifierWithNotification implements DatabaseModifier { Set packagesModified = getModifiedPackages(whereClause, whereArgs); packagesModified.addAll(getModifiedPackages(values)); - boolean isVoicemail = packagesModified.size() != 0; + boolean isVoicemail = packagesModified.size() != 0 && isUpdatingVoicemailColumns(values); boolean hasMarkedRead = false; if (mIsCallsTable) { @@ -189,13 +189,12 @@ public class DbModifierWithNotification implements DatabaseModifier { } } } - // updateDirtyFlag might remove the value and leave values empty. - if(values.isEmpty()){ - return 0; - } } } - + // updateDirtyFlag might remove the value and leave values empty. + if (values.isEmpty()) { + return 0; + } int count = mDb.update(table, values, whereClause, whereArgs); if (count > 0 && isVoicemail) { notifyVoicemailChange(mBaseUri, packagesModified); @@ -237,6 +236,15 @@ public class DbModifierWithNotification implements DatabaseModifier { return isDirty == 0; } + private boolean isUpdatingVoicemailColumns(ContentValues values) { + for (String key : values.keySet()) { + if (VoicemailContentTable.ALLOWED_COLUMNS.contains(key)) { + return true; + } + } + return false; + } + private void updateLastModified(String table, String whereClause, String[] whereArgs) { ContentValues values = new ContentValues(); values.put(Calls.LAST_MODIFIED, getTimeMillis()); diff --git a/src/com/android/providers/contacts/VoicemailContentTable.java b/src/com/android/providers/contacts/VoicemailContentTable.java index b295ac54..7025bda5 100644 --- a/src/com/android/providers/contacts/VoicemailContentTable.java +++ b/src/com/android/providers/contacts/VoicemailContentTable.java @@ -58,7 +58,7 @@ public class VoicemailContentTable implements VoicemailTable.Delegate { private static final String[] FILENAME_ONLY_PROJECTION = new String[] {Voicemails._DATA}; - private static final ImmutableSet ALLOWED_COLUMNS = new ImmutableSet.Builder() + public static final ImmutableSet ALLOWED_COLUMNS = new ImmutableSet.Builder() .add(Voicemails._ID) .add(Voicemails.NUMBER) .add(Voicemails.DATE) diff --git a/tests/src/com/android/providers/contacts/VoicemailProviderTest.java b/tests/src/com/android/providers/contacts/VoicemailProviderTest.java index 1bf0d847..973d0490 100644 --- a/tests/src/com/android/providers/contacts/VoicemailProviderTest.java +++ b/tests/src/com/android/providers/contacts/VoicemailProviderTest.java @@ -78,12 +78,13 @@ public class VoicemailProviderTest extends BaseVoicemailProviderTest { super.setUp(); setUpForOwnPermission(); System.setProperty(SYSTEM_PROPERTY_DEXMAKER_DEXCACHE, getContext().getCacheDir().getPath()); - Thread.currentThread().setContextClassLoader(VoicemailContentProvider.class.getClassLoader()); + Thread.currentThread() + .setContextClassLoader(VoicemailContentProvider.class.getClassLoader()); addProvider(CallLogProviderTestable.class, CallLog.AUTHORITY); } @Override - protected void tearDown() throws Exception { + protected void tearDown() throws Exception { System.clearProperty(SYSTEM_PROPERTY_DEXMAKER_DEXCACHE); DbModifierWithNotification.setVoicemailNotifierForTest(null); } @@ -191,22 +192,47 @@ public class VoicemailProviderTest extends BaseVoicemailProviderTest { public void testUpdateOwnPackageVoicemail_NotDirty() { final Uri uri = mResolver.insert(voicemailUri(), getTestVoicemailValues()); - mResolver.update(uri, new ContentValues(), null, null); + ContentValues updateValues = new ContentValues(); + updateValues.put(Voicemails.TRANSCRIPTION, "foo"); + mResolver.update(uri, updateValues, null, null); // Updating a package's own voicemail should not make the voicemail dirty. - ContentValues values = getTestVoicemailValues(); - values.put(Voicemails.DIRTY, "0"); - assertStoredValues(uri, values); + try (Cursor cursor = mResolver + .query(uri, new String[] {Voicemails.DIRTY}, null, null, null)) { + cursor.moveToFirst(); + assertEquals(cursor.getInt(0), 0); + } + } + + public void testUpdateOtherPackageCallLog_NotDirty() { + setUpForFullPermission(); + final Uri uri = insertVoicemailForSourcePackage("another-package"); + // Clear the mapping for our own UID so that this doesn't look like an internal transaction. + mPackageManager.removePackage(Process.myUid()); + + ContentValues values = new ContentValues(); + values.put(Calls.CACHED_NAME, "foo"); + mResolver.update(ContentUris + .withAppendedId(CallLog.Calls.CONTENT_URI, ContentUris.parseId(uri)), + values, null, null); + + try (Cursor cursor = mResolver + .query(uri, new String[] {Voicemails.DIRTY}, null, null, null)) { + cursor.moveToFirst(); + assertEquals(cursor.getInt(0), 0); + } } public void testUpdateOwnPackageVoicemail_RemovesDirtyStatus() { ContentValues values = getTestVoicemailValues(); values.put(Voicemails.DIRTY, "1"); final Uri uri = mResolver.insert(voicemailUri(), values); - - mResolver.update(uri, new ContentValues(), null, null); + ContentValues updateValues = new ContentValues(); + updateValues.put(Voicemails.IS_READ, 1); + mResolver.update(uri, updateValues, null, null); // At this point, the voicemail should be set back to not dirty. ContentValues newValues = getTestVoicemailValues(); + newValues.put(Voicemails.IS_READ, 1); newValues.put(Voicemails.DIRTY, "0"); assertStoredValues(uri, newValues); } @@ -452,8 +478,9 @@ public class VoicemailProviderTest extends BaseVoicemailProviderTest { private Uri withSourcePackageParam(Uri uri) { return uri.buildUpon() - .appendQueryParameter(VoicemailContract.PARAM_KEY_SOURCE_PACKAGE, mActor.packageName) - .build(); + .appendQueryParameter(VoicemailContract.PARAM_KEY_SOURCE_PACKAGE, + mActor.packageName) + .build(); } public void testUriPermissions() { @@ -516,7 +543,7 @@ public class VoicemailProviderTest extends BaseVoicemailProviderTest { private void checkHasReadAccessToUri(final Uri uri) { Cursor cursor = null; try { - cursor = mResolver.query(uri, null, null ,null, null); + cursor = mResolver.query(uri, null, null, null, null); assertEquals(1, cursor.getCount()); try { ParcelFileDescriptor fd = mResolver.openFileDescriptor(uri, "r"); @@ -628,11 +655,11 @@ public class VoicemailProviderTest extends BaseVoicemailProviderTest { values.put(callLogColumn, "foo"); EvenMoreAsserts.assertThrows("Column: " + callLogColumn, IllegalArgumentException.class, new Runnable() { - @Override - public void run() { - mResolver.insert(voicemailUri(), values); - } - }); + @Override + public void run() { + mResolver.insert(voicemailUri(), values); + } + }); } } @@ -659,11 +686,11 @@ public class VoicemailProviderTest extends BaseVoicemailProviderTest { values.put(callLogColumn, "foo"); EvenMoreAsserts.assertThrows("Column: " + callLogColumn, IllegalArgumentException.class, new Runnable() { - @Override - public void run() { - mResolver.update(insertedUri, values, null, null); - } - }); + @Override + public void run() { + mResolver.update(insertedUri, values, null, null); + } + }); } } @@ -827,7 +854,9 @@ public class VoicemailProviderTest extends BaseVoicemailProviderTest { return mResolver.insert(voicemailUri(), getTestVoicemailValues()); } - /** Inserts a voicemail record for the specified source package. */ + /** + * Inserts a voicemail record for the specified source package. + */ private Uri insertVoicemailForSourcePackage(String sourcePackage) { ContentValues values = getTestVoicemailValues(); values.put(Voicemails.SOURCE_PACKAGE, sourcePackage); -- cgit v1.2.3 From 42cb997bed39c45a355957d51f1eff16ee381aa8 Mon Sep 17 00:00:00 2001 From: Jeff Sharkey Date: Fri, 5 Jan 2018 17:15:40 -0700 Subject: Add reserved disk GID to critical component. We recently created a new GID that can be granted to critical system processes, so that the system is usable enough for the user to free up disk space used by abusive apps. Test: builds, boots Bug: 62024591 Change-Id: I32fadfb42eb5fd08b13b9da0ee025ca54bfcf2e5 --- AndroidManifest.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/AndroidManifest.xml b/AndroidManifest.xml index aa1c396f..ca4264b2 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -15,6 +15,7 @@ + Date: Tue, 9 Jan 2018 17:01:56 -0800 Subject: Allow Voicemails to be inserted as not new. Inserting voicemails as new will cause dialer to send notifications to the user. For initial syncs this is undesirable. Previously the NEW column is only available to call log. Change-Id: Ib01720dcec841d2f17016eac4efab6e13e9b0d6c Fixes: 69482089 Test: CTS --- .../providers/contacts/VoicemailContentTable.java | 2 + .../providers/contacts/VoicemailProviderTest.java | 45 +++++++++++++--------- 2 files changed, 28 insertions(+), 19 deletions(-) diff --git a/src/com/android/providers/contacts/VoicemailContentTable.java b/src/com/android/providers/contacts/VoicemailContentTable.java index b295ac54..1b39e7fb 100644 --- a/src/com/android/providers/contacts/VoicemailContentTable.java +++ b/src/com/android/providers/contacts/VoicemailContentTable.java @@ -63,6 +63,7 @@ public class VoicemailContentTable implements VoicemailTable.Delegate { .add(Voicemails.NUMBER) .add(Voicemails.DATE) .add(Voicemails.DURATION) + .add(Voicemails.NEW) .add(Voicemails.IS_READ) .add(Voicemails.TRANSCRIPTION) .add(Voicemails.TRANSCRIPTION_STATE) @@ -104,6 +105,7 @@ public class VoicemailContentTable implements VoicemailTable.Delegate { .add(Voicemails.NUMBER) .add(Voicemails.DATE) .add(Voicemails.DURATION) + .add(Voicemails.NEW) .add(Voicemails.IS_READ) .add(Voicemails.TRANSCRIPTION) .add(Voicemails.TRANSCRIPTION_STATE) diff --git a/tests/src/com/android/providers/contacts/VoicemailProviderTest.java b/tests/src/com/android/providers/contacts/VoicemailProviderTest.java index 1bf0d847..52e2f09f 100644 --- a/tests/src/com/android/providers/contacts/VoicemailProviderTest.java +++ b/tests/src/com/android/providers/contacts/VoicemailProviderTest.java @@ -71,19 +71,20 @@ public class VoicemailProviderTest extends BaseVoicemailProviderTest { /** * Total number of columns exposed by voicemail provider. */ - private static final int NUM_VOICEMAIL_FIELDS = 24; + private static final int NUM_VOICEMAIL_FIELDS = 25; @Override protected void setUp() throws Exception { super.setUp(); setUpForOwnPermission(); System.setProperty(SYSTEM_PROPERTY_DEXMAKER_DEXCACHE, getContext().getCacheDir().getPath()); - Thread.currentThread().setContextClassLoader(VoicemailContentProvider.class.getClassLoader()); + Thread.currentThread() + .setContextClassLoader(VoicemailContentProvider.class.getClassLoader()); addProvider(CallLogProviderTestable.class, CallLog.AUTHORITY); } @Override - protected void tearDown() throws Exception { + protected void tearDown() throws Exception { System.clearProperty(SYSTEM_PROPERTY_DEXMAKER_DEXCACHE); DbModifierWithNotification.setVoicemailNotifierForTest(null); } @@ -118,14 +119,16 @@ public class VoicemailProviderTest extends BaseVoicemailProviderTest { public void testInsertReadMessageIsNotNew() throws Exception { ContentValues values = getTestReadVoicemailValues(); + values.remove(Voicemails.NEW); Uri uri = mResolver.insert(voicemailUri(), values); String[] projection = {Voicemails.NUMBER, Voicemails.DATE, Voicemails.DURATION, - Voicemails.TRANSCRIPTION, Voicemails.IS_READ, Voicemails.HAS_CONTENT, + Voicemails.TRANSCRIPTION, Voicemails.NEW, Voicemails.IS_READ, + Voicemails.HAS_CONTENT, Voicemails.SOURCE_DATA, Voicemails.STATE, Voicemails.BACKED_UP, Voicemails.RESTORED, Voicemails.ARCHIVED, Voicemails.IS_OMTP_VOICEMAIL }; - Cursor c = mResolver.query(uri, projection, Calls.NEW + "=0", null, + Cursor c = mResolver.query(uri, projection, Voicemails.NEW + "=0", null, null); try { assertEquals("Record count", 1, c.getCount()); @@ -452,8 +455,9 @@ public class VoicemailProviderTest extends BaseVoicemailProviderTest { private Uri withSourcePackageParam(Uri uri) { return uri.buildUpon() - .appendQueryParameter(VoicemailContract.PARAM_KEY_SOURCE_PACKAGE, mActor.packageName) - .build(); + .appendQueryParameter(VoicemailContract.PARAM_KEY_SOURCE_PACKAGE, + mActor.packageName) + .build(); } public void testUriPermissions() { @@ -516,7 +520,7 @@ public class VoicemailProviderTest extends BaseVoicemailProviderTest { private void checkHasReadAccessToUri(final Uri uri) { Cursor cursor = null; try { - cursor = mResolver.query(uri, null, null ,null, null); + cursor = mResolver.query(uri, null, null, null, null); assertEquals(1, cursor.getCount()); try { ParcelFileDescriptor fd = mResolver.openFileDescriptor(uri, "r"); @@ -628,11 +632,11 @@ public class VoicemailProviderTest extends BaseVoicemailProviderTest { values.put(callLogColumn, "foo"); EvenMoreAsserts.assertThrows("Column: " + callLogColumn, IllegalArgumentException.class, new Runnable() { - @Override - public void run() { - mResolver.insert(voicemailUri(), values); - } - }); + @Override + public void run() { + mResolver.insert(voicemailUri(), values); + } + }); } } @@ -659,11 +663,11 @@ public class VoicemailProviderTest extends BaseVoicemailProviderTest { values.put(callLogColumn, "foo"); EvenMoreAsserts.assertThrows("Column: " + callLogColumn, IllegalArgumentException.class, new Runnable() { - @Override - public void run() { - mResolver.update(insertedUri, values, null, null); - } - }); + @Override + public void run() { + mResolver.update(insertedUri, values, null, null); + } + }); } } @@ -827,7 +831,9 @@ public class VoicemailProviderTest extends BaseVoicemailProviderTest { return mResolver.insert(voicemailUri(), getTestVoicemailValues()); } - /** Inserts a voicemail record for the specified source package. */ + /** + * Inserts a voicemail record for the specified source package. + */ private Uri insertVoicemailForSourcePackage(String sourcePackage) { ContentValues values = getTestVoicemailValues(); values.put(Voicemails.SOURCE_PACKAGE, sourcePackage); @@ -839,6 +845,7 @@ public class VoicemailProviderTest extends BaseVoicemailProviderTest { values.put(Voicemails.NUMBER, "1-800-4664-411"); values.put(Voicemails.DATE, 1000); values.put(Voicemails.DURATION, 30); + values.put(Voicemails.NEW, 0); values.put(Voicemails.TRANSCRIPTION, "Testing 123"); values.put(Voicemails.IS_READ, 0); values.put(Voicemails.HAS_CONTENT, 0); -- cgit v1.2.3 From ae1f9edc0c877c2ccc32ae8b30de974271def420 Mon Sep 17 00:00:00 2001 From: Jeff Davidson Date: Mon, 12 Feb 2018 18:39:42 -0800 Subject: Allow carrier-privileged apps to access voicemail provider. Bug: 70041899 Test: TreeHugger + tests in CL topic Change-Id: Icd80caa6f755f549f2433ac4dcacb4a77c962077 Merged-In: Icd80caa6f755f549f2433ac4dcacb4a77c962077 (cherry picked from commit 9c57007fb2e6bbb3d0303320ce6938751240c1c7) --- AndroidManifest.xml | 6 +- .../contacts/VoicemailContentProvider.java | 36 +++++++++-- .../providers/contacts/VoicemailNotifier.java | 27 ++++---- .../providers/contacts/VoicemailPermissions.java | 35 ++++++++-- .../contacts/BaseVoicemailProviderTest.java | 11 ++++ .../android/providers/contacts/ContactsActor.java | 44 +++++++++++++ .../providers/contacts/VoicemailProviderTest.java | 75 ++++++++++++++-------- 7 files changed, 183 insertions(+), 51 deletions(-) diff --git a/AndroidManifest.xml b/AndroidManifest.xml index ca4264b2..559e60f1 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -66,11 +66,13 @@ android:writePermission="android.permission.MANAGE_USERS"> + + android:exported="true"> mGrantedPermissions = Sets.newHashSet(); private final Set mGrantedUriPermissions = Sets.newHashSet(); + private boolean mHasCarrierPrivileges; private List mAllProviders = new ArrayList<>(); @@ -242,6 +246,31 @@ public class ContactsActor { } } + private MockTelephonyManager mMockTelephonyManager; + + private class MockTelephonyManager extends TelephonyManager { + public MockTelephonyManager(Context context) { + super(context); + } + + @Override + public int checkCarrierPrivilegesForPackageAnyPhone(String packageName) { + if (TextUtils.equals(packageName, ContactsActor.this.packageName) + && mHasCarrierPrivileges) { + return TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS; + } + return TelephonyManager.CARRIER_PRIVILEGE_STATUS_NO_ACCESS; + } + + @Override + public List getPackagesWithCarrierPrivileges() { + if (!mHasCarrierPrivileges) { + return Collections.emptyList(); + } + return Collections.singletonList(packageName); + } + } + /** * A context wrapper that reports a different user id. * @@ -290,6 +319,9 @@ public class ContactsActor { if (Context.USER_SERVICE.equals(name)) { return mockUserManager; } + if (Context.TELEPHONY_SERVICE.equals(name)) { + return mMockTelephonyManager; + } // Use overallContext here; super.getSystemService() somehow won't return // DevicePolicyManager. return overallContext.getSystemService(name); @@ -334,6 +366,9 @@ public class ContactsActor { if (Context.USER_SERVICE.equals(name)) { return mockUserManager; } + if (Context.TELEPHONY_SERVICE.equals(name)) { + return mMockTelephonyManager; + } // Use overallContext here; super.getSystemService() somehow won't return // DevicePolicyManager. return overallContext.getSystemService(name); @@ -367,6 +402,7 @@ public class ContactsActor { mMockAccountManager = new MockAccountManager(mProviderContext); mockUserManager = new MockUserManager(mProviderContext); + mMockTelephonyManager = new MockTelephonyManager(mProviderContext); provider = addProvider(providerClass, authority); } @@ -427,6 +463,14 @@ public class ContactsActor { mGrantedUriPermissions.removeAll(Arrays.asList(uris)); } + public void grantCarrierPrivileges() { + mHasCarrierPrivileges = true; + } + + public void revokeCarrierPrivileges() { + mHasCarrierPrivileges = false; + } + /** * Mock {@link Context} that reports specific well-known values for testing * data protection. The creator can override the owner package name, and diff --git a/tests/src/com/android/providers/contacts/VoicemailProviderTest.java b/tests/src/com/android/providers/contacts/VoicemailProviderTest.java index d2bfa875..9f9ef002 100644 --- a/tests/src/com/android/providers/contacts/VoicemailProviderTest.java +++ b/tests/src/com/android/providers/contacts/VoicemailProviderTest.java @@ -35,13 +35,14 @@ import android.test.suitebuilder.annotation.SmallTest; import com.android.common.io.MoreCloseables; +import org.mockito.Mockito; + import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.Arrays; import java.util.List; -import org.mockito.Mockito; /** * Unit tests for {@link VoicemailContentProvider}. @@ -365,6 +366,12 @@ public class VoicemailProviderTest extends BaseVoicemailProviderTest { // no package URI specified). public void testMustUsePackageUriWithoutFullPermission() { setUpForOwnPermission(); + assertBaseUriThrowsSecurityExceptions(); + setUpForOwnPermissionViaCarrierPrivileges(); + assertBaseUriThrowsSecurityExceptions(); + } + + private void assertBaseUriThrowsSecurityExceptions() { EvenMoreAsserts.assertThrows(SecurityException.class, new Runnable() { @Override public void run() { @@ -403,24 +410,31 @@ public class VoicemailProviderTest extends BaseVoicemailProviderTest { // Now give away full permission and check that only 1 message is accessible. setUpForOwnPermission(); - assertEquals(1, getCount(voicemailUri(), null, null)); + assertOnlyOwnVoicemailsCanBeQueriedAndInserted(); + // Same as above, but with carrier privileges. + setUpForOwnPermissionViaCarrierPrivileges(); + assertOnlyOwnVoicemailsCanBeQueriedAndInserted(); - // Once again try to insert message for another package. This time - // it should fail. + setUpForNoPermission(); + mUseSourceUri = false; + // With the READ_ALL_VOICEMAIL permission, we should now be able to read all voicemails + mActor.addPermissions(READ_VOICEMAIL_PERMISSION); + assertEquals(2, getCount(voicemailUri(), null, null)); + + // An insert for another package should still fail EvenMoreAsserts.assertThrows(SecurityException.class, new Runnable() { @Override public void run() { insertVoicemailForSourcePackage("another-package"); } }); + } - setUpForNoPermission(); - mUseSourceUri = false; - // With the READ_ALL_VOICEMAIL permission, we should now be able to read all voicemails - mActor.addPermissions(READ_VOICEMAIL_PERMISSION); - assertEquals(2, getCount(voicemailUri(), null, null)); + private void assertOnlyOwnVoicemailsCanBeQueriedAndInserted() { + assertEquals(1, getCount(voicemailUri(), null, null)); - // An insert for another package should still fail + // Once again try to insert message for another package. This time + // it should fail. EvenMoreAsserts.assertThrows(SecurityException.class, new Runnable() { @Override public void run() { @@ -439,23 +453,9 @@ public class VoicemailProviderTest extends BaseVoicemailProviderTest { // Now give away full permission and check that we can update and delete only // the own voicemail. setUpForOwnPermission(); - mResolver.update(withSourcePackageParam(ownVoicemail), - getTestVoicemailValues(), null, null); - mResolver.delete(withSourcePackageParam(ownVoicemail), null, null); - - // However, attempting to update or delete another-package's voicemail should fail. - EvenMoreAsserts.assertThrows(SecurityException.class, new Runnable() { - @Override - public void run() { - mResolver.update(anotherVoicemail, null, null, null); - } - }); - EvenMoreAsserts.assertThrows(SecurityException.class, new Runnable() { - @Override - public void run() { - mResolver.delete(anotherVoicemail, null, null); - } - }); + assertOnlyOwnVoicemailsCanBeUpdatedAndDeleted(ownVoicemail, anotherVoicemail); + setUpForOwnPermissionViaCarrierPrivileges(); + assertOnlyOwnVoicemailsCanBeUpdatedAndDeleted(ownVoicemail, anotherVoicemail); // If we have the manage voicemail permission, we should be able to both update voicemails // from all packages. @@ -478,6 +478,27 @@ public class VoicemailProviderTest extends BaseVoicemailProviderTest { assertEquals(0, getCount(anotherVoicemail, null, null)); } + private void assertOnlyOwnVoicemailsCanBeUpdatedAndDeleted( + Uri ownVoicemail, Uri anotherVoicemail) { + mResolver.update(withSourcePackageParam(ownVoicemail), + getTestVoicemailValues(), null, null); + mResolver.delete(withSourcePackageParam(ownVoicemail), null, null); + + // However, attempting to update or delete another-package's voicemail should fail. + EvenMoreAsserts.assertThrows(SecurityException.class, new Runnable() { + @Override + public void run() { + mResolver.update(anotherVoicemail, null, null, null); + } + }); + EvenMoreAsserts.assertThrows(SecurityException.class, new Runnable() { + @Override + public void run() { + mResolver.delete(anotherVoicemail, null, null); + } + }); + } + private Uri withSourcePackageParam(Uri uri) { return uri.buildUpon() .appendQueryParameter(VoicemailContract.PARAM_KEY_SOURCE_PACKAGE, -- cgit v1.2.3 From 8b924e2e51c026e769343e883ee21a5c05d1eaa1 Mon Sep 17 00:00:00 2001 From: Makoto Onuki Date: Tue, 20 Mar 2018 21:17:02 -0700 Subject: Move provider access stats to frameworks/base Bug: 76037330 Test: boot and dumpsys activity provider ContactsProvider2 Change-Id: I2d10d69da9ddb5536ff3ef0aef2cfc3a711a20bd --- .../contacts/AbstractContactsProvider.java | 117 +++++---------------- .../providers/contacts/ContactsProvider2.java | 5 +- .../providers/contacts/ProfileProvider.java | 6 +- 3 files changed, 33 insertions(+), 95 deletions(-) diff --git a/src/com/android/providers/contacts/AbstractContactsProvider.java b/src/com/android/providers/contacts/AbstractContactsProvider.java index ebf0a1b9..e00bd862 100644 --- a/src/com/android/providers/contacts/AbstractContactsProvider.java +++ b/src/com/android/providers/contacts/AbstractContactsProvider.java @@ -16,10 +16,6 @@ package com.android.providers.contacts; -import com.android.providers.contacts.ContactsDatabaseHelper.AccountsColumns; -import com.android.providers.contacts.ContactsDatabaseHelper.RawContactsColumns; -import com.android.providers.contacts.ContactsDatabaseHelper.Tables; - import android.content.ContentProvider; import android.content.ContentProviderOperation; import android.content.ContentProviderResult; @@ -38,8 +34,11 @@ import android.provider.BaseColumns; import android.provider.ContactsContract.Data; import android.provider.ContactsContract.RawContacts; import android.util.Log; -import android.util.SparseBooleanArray; -import android.util.SparseLongArray; + +import com.android.internal.util.ProviderAccessStats; +import com.android.providers.contacts.ContactsDatabaseHelper.AccountsColumns; +import com.android.providers.contacts.ContactsDatabaseHelper.RawContactsColumns; +import com.android.providers.contacts.ContactsDatabaseHelper.Tables; import java.io.PrintWriter; import java.util.ArrayList; @@ -116,23 +115,8 @@ public abstract class AbstractContactsProvider extends ContentProvider */ private SQLiteTransactionListener mSerializedDbTransactionListener; - private final long mStartTime = SystemClock.elapsedRealtime(); - - private final Object mStatsLock = new Object(); - protected final SparseBooleanArray mAllCallingUids = new SparseBooleanArray(); - protected final SparseLongArray mQueryStats = new SparseLongArray(); - protected final SparseLongArray mBatchStats = new SparseLongArray(); - protected final SparseLongArray mInsertStats = new SparseLongArray(); - protected final SparseLongArray mUpdateStats = new SparseLongArray(); - protected final SparseLongArray mDeleteStats = new SparseLongArray(); - protected final SparseLongArray mInsertInBatchStats = new SparseLongArray(); - protected final SparseLongArray mUpdateInBatchStats = new SparseLongArray(); - protected final SparseLongArray mDeleteInBatchStats = new SparseLongArray(); - private final SparseLongArray mOperationDurationMicroStats = new SparseLongArray(); - - private final ThreadLocal mOperationNest = ThreadLocal.withInitial(() -> 0); - private final ThreadLocal mOperationStartNs = ThreadLocal.withInitial(() -> 0L); + protected final ProviderAccessStats mStats = new ProviderAccessStats(); @Override public boolean onCreate() { @@ -158,47 +142,19 @@ public abstract class AbstractContactsProvider extends ContentProvider mSerializedDbTransactionListener = listener; } - protected final void incrementStats(SparseLongArray stats) { - final int callingUid = Binder.getCallingUid(); - synchronized (mStatsLock) { - stats.put(callingUid, stats.get(callingUid) + 1); - mAllCallingUids.put(callingUid, true); - - final int nest = mOperationNest.get(); - mOperationNest.set(nest + 1); - if (nest == 0) { - mOperationStartNs.set(SystemClock.elapsedRealtimeNanos()); - } - } + public ContactsTransaction getCurrentTransaction() { + return mTransactionHolder.get(); } - protected final void incrementStats(SparseLongArray statsNonBatch, - SparseLongArray statsInBatch) { + private boolean isInBatch() { final ContactsTransaction t = mTransactionHolder.get(); - final boolean inBatch = t != null && t.isBatch(); - incrementStats(inBatch ? statsInBatch : statsNonBatch); - } - - protected void finishOperation() { - final int callingUid = Binder.getCallingUid(); - synchronized (mStatsLock) { - final int nest = mOperationNest.get(); - mOperationNest.set(nest - 1); - if (nest == 1) { - final long duration = SystemClock.elapsedRealtimeNanos() - mOperationStartNs.get(); - mOperationDurationMicroStats.put(callingUid, - mOperationDurationMicroStats.get(callingUid) + duration / 1000L); - } - } - } - - public ContactsTransaction getCurrentTransaction() { - return mTransactionHolder.get(); + return t != null && t.isBatch(); } @Override public Uri insert(Uri uri, ContentValues values) { - incrementStats(mInsertStats, mInsertInBatchStats); + final int callingUid = Binder.getCallingUid(); + mStats.incrementInsertStats(callingUid, isInBatch()); try { ContactsTransaction transaction = startTransaction(false); try { @@ -212,13 +168,14 @@ public abstract class AbstractContactsProvider extends ContentProvider endTransaction(false); } } finally { - finishOperation(); + mStats.finishOperation(callingUid); } } @Override public int delete(Uri uri, String selection, String[] selectionArgs) { - incrementStats(mDeleteStats, mDeleteInBatchStats); + final int callingUid = Binder.getCallingUid(); + mStats.incrementDeleteStats(callingUid, isInBatch()); try { ContactsTransaction transaction = startTransaction(false); try { @@ -232,13 +189,14 @@ public abstract class AbstractContactsProvider extends ContentProvider endTransaction(false); } } finally { - finishOperation(); + mStats.finishOperation(callingUid); } } @Override public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { - incrementStats(mUpdateStats, mUpdateInBatchStats); + final int callingUid = Binder.getCallingUid(); + mStats.incrementUpdateStats(callingUid, isInBatch()); try { ContactsTransaction transaction = startTransaction(false); try { @@ -252,13 +210,14 @@ public abstract class AbstractContactsProvider extends ContentProvider endTransaction(false); } } finally { - finishOperation(); + mStats.finishOperation(callingUid); } } @Override public int bulkInsert(Uri uri, ContentValues[] values) { - incrementStats(mBatchStats); + final int callingUid = Binder.getCallingUid(); + mStats.incrementBatchStats(callingUid); try { ContactsTransaction transaction = startTransaction(true); int numValues = values.length; @@ -282,14 +241,15 @@ public abstract class AbstractContactsProvider extends ContentProvider } return numValues; } finally { - finishOperation(); + mStats.finishOperation(callingUid); } } @Override public ContentProviderResult[] applyBatch(ArrayList operations) throws OperationApplicationException { - incrementStats(mBatchStats); + final int callingUid = Binder.getCallingUid(); + mStats.incrementBatchStats(callingUid); try { if (VERBOSE_LOGGING) { Log.v(TAG, "applyBatch: " + operations.size() + " ops"); @@ -331,7 +291,7 @@ public abstract class AbstractContactsProvider extends ContentProvider endTransaction(true); } } finally { - finishOperation(); + mStats.finishOperation(callingUid); } } @@ -462,32 +422,7 @@ public abstract class AbstractContactsProvider extends ContentProvider pw.print("Database: "); pw.println(dbName); - pw.print(" Uptime: "); - pw.print((SystemClock.elapsedRealtime() - mStartTime) / (60 * 1000)); - pw.println(" minutes"); - - synchronized (mStatsLock) { - pw.println(); - pw.println(" Client activities:"); - pw.println(" UID Query Insert Update Delete Batch Insert Update Delete" - + " Sec"); - for (int i = 0; i < mAllCallingUids.size(); i++) { - final int uid = mAllCallingUids.keyAt(i); - pw.println(String.format( - " %-9d %6d %6d %6d %6d %6d %6d %6d %6d %12.3f", - uid, - mQueryStats.get(uid), - mInsertStats.get(uid), - mUpdateStats.get(uid), - mDeleteStats.get(uid), - mBatchStats.get(uid), - mInsertInBatchStats.get(uid), - mUpdateInBatchStats.get(uid), - mDeleteInBatchStats.get(uid), - (mOperationDurationMicroStats.get(uid) / 1000000.0) - )); - } - } + mStats.dump(pw, " "); if (mDbHelper == null) { pw.println("mDbHelper is null"); diff --git a/src/com/android/providers/contacts/ContactsProvider2.java b/src/com/android/providers/contacts/ContactsProvider2.java index 1d5681ac..54b4a27d 100644 --- a/src/com/android/providers/contacts/ContactsProvider2.java +++ b/src/com/android/providers/contacts/ContactsProvider2.java @@ -5545,7 +5545,8 @@ public class ContactsProvider2 extends AbstractContactsProvider return mProfileProvider.query(uri, projection, selection, selectionArgs, sortOrder, cancellationSignal); } - incrementStats(mQueryStats); + final int callingUid = Binder.getCallingUid(); + mStats.incrementQueryStats(callingUid); try { // Otherwise proceed with a normal query against the contacts DB. switchToContactMode(); @@ -5553,7 +5554,7 @@ public class ContactsProvider2 extends AbstractContactsProvider return queryDirectoryIfNecessary(uri, projection, selection, selectionArgs, sortOrder, cancellationSignal); } finally { - finishOperation(); + mStats.finishOperation(callingUid); } } diff --git a/src/com/android/providers/contacts/ProfileProvider.java b/src/com/android/providers/contacts/ProfileProvider.java index 6c84e4b0..f3b6daf6 100644 --- a/src/com/android/providers/contacts/ProfileProvider.java +++ b/src/com/android/providers/contacts/ProfileProvider.java @@ -24,6 +24,7 @@ import android.content.res.AssetFileDescriptor; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.net.Uri; +import android.os.Binder; import android.os.CancellationSignal; import android.provider.ContactsContract.Intents; @@ -70,12 +71,13 @@ public class ProfileProvider extends AbstractContactsProvider { @Override public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder, CancellationSignal cancellationSignal) { - incrementStats(mQueryStats); + final int callingUid = Binder.getCallingUid(); + mStats.incrementQueryStats(callingUid); try { return mDelegate.queryLocal(uri, projection, selection, selectionArgs, sortOrder, -1, cancellationSignal); } finally { - finishOperation(); + mStats.finishOperation(callingUid); } } -- cgit v1.2.3 From eef856198870eb06a86d9f901a747f5259177965 Mon Sep 17 00:00:00 2001 From: Makoto Onuki Date: Mon, 26 Mar 2018 11:36:55 -0700 Subject: Client access stats for calllog This prints # of requests made by clients, per calling UID, with total time spent for their requests. Sample output: PROVIDER ContentProviderRecord{a16090d u0 com.android.providers.contacts/.CallLogProvider} pid=2417 Client: Process uptime: 4 minutes Client activities: UID Query Insert Update Delete Batch Insert Update Delete Sec 1000 1 4 0 0 0 0 0 0 0.161 10022 1 0 0 0 0 0 0 0 0.068 10062 336 2 0 0 0 0 0 0 5.361 Change-Id: I4b6f7e58dd8dc971408220edaae11ce6f24c43f5 Fix: 76037330 Bug: 76037330 Test: build & boot, dumpsys activity provider com.android.providers.contacts/.CallLogProvider --- .../providers/contacts/CallLogProvider.java | 110 ++++++++++++++++++++- 1 file changed, 106 insertions(+), 4 deletions(-) diff --git a/src/com/android/providers/contacts/CallLogProvider.java b/src/com/android/providers/contacts/CallLogProvider.java index 76e207a0..f71a7503 100644 --- a/src/com/android/providers/contacts/CallLogProvider.java +++ b/src/com/android/providers/contacts/CallLogProvider.java @@ -22,10 +22,13 @@ import static com.android.providers.contacts.util.DbQueryUtils.getInequalityClau import android.app.AppOpsManager; import android.content.ContentProvider; +import android.content.ContentProviderOperation; +import android.content.ContentProviderResult; import android.content.ContentResolver; import android.content.ContentUris; import android.content.ContentValues; import android.content.Context; +import android.content.OperationApplicationException; import android.content.UriMatcher; import android.database.Cursor; import android.database.DatabaseUtils; @@ -45,11 +48,15 @@ import android.util.ArrayMap; import android.util.Log; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.util.ProviderAccessStats; import com.android.providers.contacts.CallLogDatabaseHelper.DbProperties; import com.android.providers.contacts.CallLogDatabaseHelper.Tables; import com.android.providers.contacts.util.SelectionBuilder; import com.android.providers.contacts.util.UserUtils; +import java.io.FileDescriptor; +import java.io.PrintWriter; +import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.concurrent.CountDownLatch; @@ -175,6 +182,10 @@ public class CallLogProvider extends ContentProvider { private VoicemailPermissions mVoicemailPermissions; private CallLogInsertionHelper mCallLogInsertionHelper; + private final ThreadLocal mApplyingBatch = new ThreadLocal<>(); + private final ThreadLocal mCallingUid = new ThreadLocal<>(); + private final ProviderAccessStats mStats = new ProviderAccessStats(); + protected boolean isShadow() { return false; } @@ -228,9 +239,58 @@ public class CallLogProvider extends ContentProvider { return CallLogDatabaseHelper.getInstance(context); } + protected boolean applyingBatch() { + final Boolean applying = mApplyingBatch.get(); + return applying != null && applying; + } + + @Override + public ContentProviderResult[] applyBatch(ArrayList operations) + throws OperationApplicationException { + final int callingUid = Binder.getCallingUid(); + mCallingUid.set(callingUid); + + mStats.incrementBatchStats(callingUid); + mApplyingBatch.set(true); + try { + return super.applyBatch(operations); + } finally { + mApplyingBatch.set(false); + mStats.finishOperation(callingUid); + } + } + + @Override + public int bulkInsert(Uri uri, ContentValues[] values) { + final int callingUid = Binder.getCallingUid(); + mCallingUid.set(callingUid); + + mStats.incrementBatchStats(callingUid); + mApplyingBatch.set(true); + try { + return super.bulkInsert(uri, values); + } finally { + mApplyingBatch.set(false); + mStats.finishOperation(callingUid); + } + } + @Override public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { + // Note don't use mCallingUid here. That's only used by mutation functions. + final int callingUid = Binder.getCallingUid(); + + mStats.incrementQueryStats(callingUid); + try { + return queryInternal(uri, projection, selection, selectionArgs, sortOrder); + } finally { + mStats.finishOperation(callingUid); + } + } + + private Cursor queryInternal(Uri uri, String[] projection, String selection, + String[] selectionArgs, String sortOrder) { if (VERBOSE_LOGGING) { Log.v(TAG, "query: uri=" + uri + " projection=" + Arrays.toString(projection) + " selection=[" + selection + "] args=" + Arrays.toString(selectionArgs) + @@ -361,6 +421,44 @@ public class CallLogProvider extends ContentProvider { @Override public Uri insert(Uri uri, ContentValues values) { + final int callingUid = + applyingBatch() ? mCallingUid.get() : Binder.getCallingUid(); + + mStats.incrementInsertStats(callingUid, applyingBatch()); + try { + return insertInternal(uri, values); + } finally { + mStats.finishOperation(callingUid); + } + } + + @Override + public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { + final int callingUid = + applyingBatch() ? mCallingUid.get() : Binder.getCallingUid(); + + mStats.incrementInsertStats(callingUid, applyingBatch()); + try { + return updateInternal(uri, values, selection, selectionArgs); + } finally { + mStats.finishOperation(callingUid); + } + } + + @Override + public int delete(Uri uri, String selection, String[] selectionArgs) { + final int callingUid = + applyingBatch() ? mCallingUid.get() : Binder.getCallingUid(); + + mStats.incrementInsertStats(callingUid, applyingBatch()); + try { + return deleteInternal(uri, selection, selectionArgs); + } finally { + mStats.finishOperation(callingUid); + } + } + + private Uri insertInternal(Uri uri, ContentValues values) { if (VERBOSE_LOGGING) { Log.v(TAG, "insert: uri=" + uri + " values=[" + values + "]" + " CPID=" + Binder.getCallingPid()); @@ -390,8 +488,8 @@ public class CallLogProvider extends ContentProvider { return null; } - @Override - public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { + private int updateInternal(Uri uri, ContentValues values, + String selection, String[] selectionArgs) { if (VERBOSE_LOGGING) { Log.v(TAG, "update: uri=" + uri + " selection=[" + selection + "] args=" + Arrays.toString(selectionArgs) + @@ -427,8 +525,7 @@ public class CallLogProvider extends ContentProvider { selectionArgs); } - @Override - public int delete(Uri uri, String selection, String[] selectionArgs) { + private int deleteInternal(Uri uri, String selection, String[] selectionArgs) { if (VERBOSE_LOGGING) { Log.v(TAG, "delete: uri=" + uri + " selection=[" + selection + "] args=" + Arrays.toString(selectionArgs) + @@ -753,4 +850,9 @@ public class CallLogProvider extends ContentProvider { public void shutdown() { mTaskScheduler.shutdownForTest(); } + + @Override + public void dump(FileDescriptor fd, PrintWriter writer, String[] args) { + mStats.dump(writer, " "); + } } -- cgit v1.2.3 From 04efcfc5f3227f54dfc9138a0fcdbb8af3f052fc Mon Sep 17 00:00:00 2001 From: Makoto Onuki Date: Mon, 2 Apr 2018 14:15:08 -0700 Subject: Avoid stringbuilder explosion Change-Id: Ifd60098d5a5738063a5b5831a857802327f42774 Fixes: 76175892 Test: ContactsProviderTests --- .../providers/contacts/SearchIndexManager.java | 24 ++++--- .../contacts/util/CappedStringBuilder.java | 69 ++++++++++++++++++ .../contacts/util/CappedStringBuilderTest.java | 84 ++++++++++++++++++++++ 3 files changed, 167 insertions(+), 10 deletions(-) create mode 100644 src/com/android/providers/contacts/util/CappedStringBuilder.java create mode 100644 tests/src/com/android/providers/contacts/util/CappedStringBuilderTest.java diff --git a/src/com/android/providers/contacts/SearchIndexManager.java b/src/com/android/providers/contacts/SearchIndexManager.java index 14c78a77..e421654c 100644 --- a/src/com/android/providers/contacts/SearchIndexManager.java +++ b/src/com/android/providers/contacts/SearchIndexManager.java @@ -24,7 +24,6 @@ import android.provider.ContactsContract.CommonDataKinds.Nickname; import android.provider.ContactsContract.CommonDataKinds.Organization; import android.provider.ContactsContract.CommonDataKinds.StructuredPostal; import android.provider.ContactsContract.Data; -import android.provider.ContactsContract.ProviderStatus; import android.provider.ContactsContract.RawContacts; import android.text.TextUtils; import android.util.ArraySet; @@ -35,6 +34,8 @@ import com.android.providers.contacts.ContactsDatabaseHelper.MimetypesColumns; import com.android.providers.contacts.ContactsDatabaseHelper.RawContactsColumns; import com.android.providers.contacts.ContactsDatabaseHelper.SearchIndexColumns; import com.android.providers.contacts.ContactsDatabaseHelper.Tables; +import com.android.providers.contacts.util.CappedStringBuilder; + import com.google.android.collect.Lists; import com.google.common.annotations.VisibleForTesting; @@ -51,6 +52,8 @@ public class SearchIndexManager { private static final boolean VERBOSE_LOGGING = Log.isLoggable(TAG, Log.VERBOSE); + private static final int MAX_STRING_BUILDER_SIZE = 1024 * 10; + public static final String PROPERTY_SEARCH_INDEX_VERSION = "search_index"; private static final int SEARCH_INDEX_VERSION = 1; @@ -72,10 +75,11 @@ public class SearchIndexManager { public static final int SEPARATOR_SLASH = 2; public static final int SEPARATOR_COMMA = 3; - private StringBuilder mSbContent = new StringBuilder(); - private StringBuilder mSbName = new StringBuilder(); - private StringBuilder mSbTokens = new StringBuilder(); - private StringBuilder mSbElementContent = new StringBuilder(); + private CappedStringBuilder mSbContent = new CappedStringBuilder(MAX_STRING_BUILDER_SIZE); + private CappedStringBuilder mSbName = new CappedStringBuilder(MAX_STRING_BUILDER_SIZE); + private CappedStringBuilder mSbTokens = new CappedStringBuilder(MAX_STRING_BUILDER_SIZE); + private CappedStringBuilder mSbElementContent = new CappedStringBuilder( + MAX_STRING_BUILDER_SIZE); private ArraySet mUniqueElements = new ArraySet<>(); private Cursor mCursor; @@ -84,10 +88,10 @@ public class SearchIndexManager { } void reset() { - mSbContent.setLength(0); - mSbTokens.setLength(0); - mSbName.setLength(0); - mSbElementContent.setLength(0); + mSbContent.clear(); + mSbTokens.clear(); + mSbName.clear(); + mSbElementContent.clear(); mUniqueElements.clear(); } @@ -126,7 +130,7 @@ public class SearchIndexManager { mSbContent.append(content); mUniqueElements.add(content); } - mSbElementContent.setLength(0); + mSbElementContent.clear(); } } diff --git a/src/com/android/providers/contacts/util/CappedStringBuilder.java b/src/com/android/providers/contacts/util/CappedStringBuilder.java new file mode 100644 index 00000000..74b70cf5 --- /dev/null +++ b/src/com/android/providers/contacts/util/CappedStringBuilder.java @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2018 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.util; + +import android.util.Log; + +import com.android.providers.contacts.AbstractContactsProvider; + +public class CappedStringBuilder { + private final int mCapSize; + private boolean mOver; + private final StringBuilder mStringBuilder = new StringBuilder(); + + public CappedStringBuilder(int capSize) { + mCapSize = capSize; + } + + public void clear() { + mOver = false; + mStringBuilder.setLength(0); + } + + public int length() { + return mStringBuilder.length(); + } + + @Override + public String toString() { + return mStringBuilder.toString(); + } + + public CappedStringBuilder append(char ch) { + if (canAppend(mStringBuilder.length() + 1)) { + mStringBuilder.append(ch); + } + return this; + } + + public CappedStringBuilder append(String s) { + if (canAppend(mStringBuilder.length() + s.length())) { + mStringBuilder.append(s); + } + return this; + } + + private boolean canAppend(int length) { + if (mOver || length > mCapSize) { + if (!mOver && AbstractContactsProvider.VERBOSE_LOGGING) { + Log.w(AbstractContactsProvider.TAG, "String too long! new length=" + length); + } + mOver = true; + return false; + } + return true; + } +} diff --git a/tests/src/com/android/providers/contacts/util/CappedStringBuilderTest.java b/tests/src/com/android/providers/contacts/util/CappedStringBuilderTest.java new file mode 100644 index 00000000..d49b2630 --- /dev/null +++ b/tests/src/com/android/providers/contacts/util/CappedStringBuilderTest.java @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2018 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.util; + +import android.test.suitebuilder.annotation.SmallTest; + +import junit.framework.TestCase; + +/** + * Run with: + atest /android/pi-dev/packages/providers/ContactsProvider/tests/src/com/android/providers/contacts/util/CappedStringBuilderTest.java + */ +@SmallTest +public class CappedStringBuilderTest extends TestCase { + public void testCappedChar() { + CappedStringBuilder csb = new CappedStringBuilder(8); + + csb.append("abcd"); + csb.append("efgh"); + + csb.append('x'); + assertEquals("abcdefgh", csb.toString()); + + csb.append("y"); + csb.append("yz"); + + assertEquals("abcdefgh", csb.toString()); + } + + public void testCappedString() { + CappedStringBuilder csb = new CappedStringBuilder(8); + + csb.append("abcd"); + csb.append("efgh"); + + csb.append("x"); + assertEquals("abcdefgh", csb.toString()); + } + + public void testClear() { + CappedStringBuilder csb = new CappedStringBuilder(8); + + csb.append("abcd"); + csb.append("efgh"); + + csb.append("x"); + + assertEquals("abcdefgh", csb.toString()); + + csb.clear(); + + assertEquals("", csb.toString()); + + csb.append("abcd"); + assertEquals("abcd", csb.toString()); + } + + public void testAlreadyCapped() { + CappedStringBuilder csb = new CappedStringBuilder(4); + + csb.append("abc"); + + csb.append("xy"); + + // Once capped, further append() will all be blocked. + csb.append('z'); + csb.append("z"); + + assertEquals("abc", csb.toString()); + } +} -- cgit v1.2.3 From 96f7db6f6cd8dbd30175c787019901e0c849be38 Mon Sep 17 00:00:00 2001 From: Nicolas Geoffray Date: Fri, 6 Apr 2018 14:27:08 +0100 Subject: Exempt tests from hidden API checks. Bug: 64382372 Bug: 74963051 Test: m Change-Id: Icac45a517a911e58e656c9d73544cbe9f7786040 --- tests/AndroidTest.xml | 1 + tests2/AndroidTest.xml | 1 + 2 files changed, 2 insertions(+) diff --git a/tests/AndroidTest.xml b/tests/AndroidTest.xml index 46e97bc4..3c4a3abc 100644 --- a/tests/AndroidTest.xml +++ b/tests/AndroidTest.xml @@ -23,5 +23,6 @@ diff --git a/tests2/AndroidTest.xml b/tests2/AndroidTest.xml index 9f541e5d..29fdc83f 100644 --- a/tests2/AndroidTest.xml +++ b/tests2/AndroidTest.xml @@ -23,5 +23,6 @@ -- cgit v1.2.3 From 3a1cd52583cd59f8b36d48b91e995899e158128d Mon Sep 17 00:00:00 2001 From: Bill Yi Date: Fri, 6 Apr 2018 10:50:17 -0700 Subject: Import translations. DO NOT MERGE Auto-generated-cl: translation import Bug: 64712476 Change-Id: I5d0ebfa933066c3ed10f800ff15747f7aef7e132 --- res/values-da/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/res/values-da/strings.xml b/res/values-da/strings.xml index fde3a2f2..c77b4c54 100644 --- a/res/values-da/strings.xml +++ b/res/values-da/strings.xml @@ -26,7 +26,7 @@ "Andre" "Telefonsvarerbesked fra " "Kopiér database med kontaktpersoner" - "Du er ved at 1) lave en kopi af din database, som indeholder alle oplysninger om dine kontaktpersoner og alle opkaldslister, til det interne lager, og 2) sende den som e-mail. Husk at slette kopien, så snart du har kopieret den fra enheden, eller e-mailen er modtaget." + "Du er ved at 1) lave en kopi af din database, som indeholder alle oplysninger om dine kontaktpersoner og alle opkaldslister, til det interne lager, og 2) sende den som mail. Husk at slette kopien, så snart du har kopieret den fra enheden, eller mailen er modtaget." "Slet nu" "Start" "Vælg et program, for at sende din fil" -- cgit v1.2.3 From 757d589afbd3085fc21caa04fdc16435edaaa401 Mon Sep 17 00:00:00 2001 From: Bill Yi Date: Fri, 13 Apr 2018 12:29:52 -0700 Subject: Import translations. DO NOT MERGE Auto-generated-cl: translation import Bug: 64712476 Change-Id: Id17a2be0f6ad40bb92fbdda356e71fee47d35482 --- res/values-cs/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/res/values-cs/strings.xml b/res/values-cs/strings.xml index 489b5b7f..47fc9312 100644 --- a/res/values-cs/strings.xml +++ b/res/values-cs/strings.xml @@ -27,7 +27,7 @@ "Hlasová zpráva od uživatele " "Kopírování databáze kontaktů" "Chystáte se 1) vytvořit v interním úložišti kopii databáze obsahující všechny informace o kontaktech a veškerou historii hovorů a 2) odeslat ji e-mailem. Po úspěšném zkopírování ze zařízení nebo přijetí e-mailem ji nezapomeňte ihned odstranit." - "Smazat nyní" + "Smazat teď" "Spustit" "Vyberte program pro odeslání souboru." "Databáze kontaktů v příloze" -- cgit v1.2.3 From a3945ace06036cb43849c5b3b54549a008699b77 Mon Sep 17 00:00:00 2001 From: Bill Yi Date: Fri, 27 Apr 2018 20:54:39 -0700 Subject: Import translations. DO NOT MERGE Auto-generated-cl: translation import Bug: 64712476 Change-Id: I6fad0a4cde663428ed74cbf6421cdc05e0809ff8 --- res/values-zh-rTW/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/res/values-zh-rTW/strings.xml b/res/values-zh-rTW/strings.xml index f259d839..decb5b6a 100644 --- a/res/values-zh-rTW/strings.xml +++ b/res/values-zh-rTW/strings.xml @@ -26,7 +26,7 @@ "其他" "語音郵件寄件者: " "複製聯絡人資料庫" - "您即將要 1) 將您的資料庫 (包含所有聯絡人相關資訊及所有通話紀錄) 複製到內部儲存空間,以及 2) 透過電子郵件傳送副本。提醒您,當您順利複製裝置上的資料或收到電子郵件後,請儘快刪除副本。" + "您即將要 1) 將您的資料庫 (包含所有聯絡人相關資訊及所有通話記錄) 複製到內部儲存空間,以及 2) 透過電子郵件傳送副本。提醒您,當您順利複製裝置上的資料或收到電子郵件後,請儘快刪除副本。" "立即刪除" "開始" "選擇要傳送檔案的程式" -- cgit v1.2.3 From 5ce016b52567b23d7611bc076994904a6502f716 Mon Sep 17 00:00:00 2001 From: Bill Yi Date: Sun, 20 May 2018 03:39:17 -0700 Subject: Import translations. DO NOT MERGE Auto-generated-cl: translation import Bug: 64712476 Change-Id: I44815d70e47c31f302e60796b3aca24f1a10dc8d --- res/values-as/strings.xml | 35 +++++++++++++++++++++++++++++++++++ res/values-or/strings.xml | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 70 insertions(+) create mode 100644 res/values-as/strings.xml create mode 100644 res/values-or/strings.xml diff --git a/res/values-as/strings.xml b/res/values-as/strings.xml new file mode 100644 index 00000000..d7be97d2 --- /dev/null +++ b/res/values-as/strings.xml @@ -0,0 +1,35 @@ + + + + + "Android Core এপসমূহ" + "সম্পৰ্কসমূহৰ সঞ্চয়াগাৰ" + "সম্পর্কবোৰ" + "সম্পৰ্কসূচী আপগ্ৰেড কৰিবলৈ অধিক মেম\'ৰিৰ প্ৰয়োজন।" + "সম্পৰ্কসূচীৰ বাবে সঞ্চয়াগাৰ আপগ্ৰেড কৰিথকা হৈছে" + "আপগ্ৰেড প্ৰক্ৰিয়া সম্পূৰ্ণ কৰিবলৈ টিপক।" + "সম্পর্কসূচী" + "অন্যান্য" + "ইয়াৰ পৰা অহা ভইচমেল " + "সম্পৰ্কসূচীৰ ডেটাবেছ প্ৰতিলিপি কৰক" + "আপুনি এই কাৰ্যবোৰ কৰিবলৈ লৈছে ১) সকলো সম্পৰ্ক সম্বন্ধীয় তথ্য় আৰু কল লগ সন্নিবিষ্ট থকা আপোনাৰ ডেটাবেছক আভ্য়ন্তৰীণ সঞ্চয়াগাৰলৈ প্ৰতিলিপি কৰা আৰু ২) ইয়াক ইমেইল কৰা। ডিভাইচৰ পৰা সফলতাৰে প্ৰতিলিপি কৰাৰ বা ইমেইল পোৱাৰ পিছত উক্ত প্ৰতিলিপি মচিবলৈ নাপাহৰিব।" + "এতিয়াই মচক" + "আৰম্ভ কৰক" + "আপোনাৰ ফাইল পঠাবলৈ কোনো প্ৰ\'গ্ৰাম বাছনি কৰক" + "সম্পৰ্কসূচী Db সংলগ্ন কৰা হ\'ল" + "মোৰ সকলো সম্পৰ্ক তথ্য়সহ মোৰ সম্পৰ্কসূচী সংলগ্ন কৰা হ\'ল। সাবধানে চলাব।" + diff --git a/res/values-or/strings.xml b/res/values-or/strings.xml new file mode 100644 index 00000000..c6bfa215 --- /dev/null +++ b/res/values-or/strings.xml @@ -0,0 +1,35 @@ + + + + + "Android କୋର୍ ଆପ୍" + "ଯୋଗାଯୋଗ ଷ୍ଟୋରେଜ୍" + "ଯୋଗାଯୋଗ" + "ଯୋଗାଯୋଗ ଅପଗ୍ରେଡ୍ ପାଇଁ ଅଧିକ ମେମୋରୀ ଦରକାର।" + "ଯୋଗାଯୋଗ ପାଇଁ ଷ୍ଟୋରେଜ୍ ଅପଗ୍ରେଡ୍ କରାଯାଉଛି" + "ଅପଗ୍ରେଡ୍ ସମ୍ପୂର୍ଣ୍ଣ କରିବା ପାଇଁ ଟାପ୍ କରନ୍ତୁ।" + "ଯୋଗାଯୋଗ" + "ଅନ୍ୟାନ୍ୟ" + "ଏହାଙ୍କଠାରୁ ଭଏସମେଲ୍‌ " + "ଯୋଗାଯୋଗ ଡାଟାବେସ୍ କପୀ କରନ୍ତୁ" + "ଆପଣ 1) ଇଣ୍ଟର୍ନଲ ଷ୍ଟୋରେଜରେ ନିଜ ଡାଟାବେସ୍‌ର ଏକ କପୀ ବନାନ୍ତୁ, ଯେଉଁଥିରେ ଯୋଗାଯୋଗ ସମ୍ବନ୍ଧିତ ସମସ୍ତ ତଥ୍ୟ ଏବଂ କଲ୍ ଲଗ୍ ସାମିଲ ରହିବ ଏବଂ 2) ଏହାକୁ ଇମେଲ୍ କରନ୍ତୁ। ମନେରଖନ୍ତୁ, ଡିଭାଇସ୍‌ରୁ ସଫଳତାପୂର୍ବକ ଏହାର କପୀ କରିସାରିବା ପରେ କିମ୍ୱା ଇମେଲ୍ ପ୍ରାପ୍ତ ହୋଇସାରିବା ପରେ, ଏହି କପୀକୁ ଡିଲିଟ୍ କରିଦେବେ।" + "ବର୍ତ୍ତମାନ ଡିଲିଟ୍ କରନ୍ତୁ" + "ଆରମ୍ଭ କରନ୍ତୁ" + "ନିଜ ଫାଇଲ ପଠାଇବା ପାଇଁ ଗୋଟିଏ ପ୍ରୋଗ୍ରାମ୍ ବାଛନ୍ତୁ" + "ଯୋଗାଯୋଗ Db ଆଟାଚ୍ କରାଗଲା" + "ମୋର ସମସ୍ତ ଯୋଗାଯୋଗ ସୂଚନା ସହିତ ମୋର ଯୋଗାଯୋଗ ଡାଟାବେସ୍‌କୁ ଆଟାଚ୍ କରାଯାଇଛି। ସାବଧାନତାର ସହ କାମ କରନ୍ତୁ।" + -- cgit v1.2.3 From 5759b04e94a87749a7626ec9d01334bef0ac320b Mon Sep 17 00:00:00 2001 From: Bill Yi Date: Thu, 24 May 2018 18:07:25 -0700 Subject: Import translations. DO NOT MERGE Auto-generated-cl: translation import Bug: 64712476 Change-Id: I1bba3d5dc622a6823c826488845dc6571875138d --- res/values-mr/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/res/values-mr/strings.xml b/res/values-mr/strings.xml index a4f324e0..171febea 100644 --- a/res/values-mr/strings.xml +++ b/res/values-mr/strings.xml @@ -29,7 +29,7 @@ "आपण 1) आपल्‍या डेटाबेसची प्रत बनवणार आहात जिच्‍यामध्‍ये सर्व संपर्कांसंबंधी माहिती आणि अंतर्गत संचयनावरील कॉल लॉग समाविष्‍ट असतात आणि 2) ती ईमेल करणार आहात. आपण डिव्‍हाइसवरून यशस्‍वीरित्‍या प्रत कॉपी केल्‍यानंतर किंवा ईमेल प्राप्त केल्‍यानंतर लगेच ती हटविण्‍याचे लक्षात ठेवा." "आता हटवा" "प्रारंभ करा" - "आपली फाईल पाठविण्‍यासाठी एक प्रोग्राम निवडा" + "तुमची फाईल पाठविण्‍यासाठी एक प्रोग्राम निवडा" "संपर्क Db संलग्‍न केला" "संलग्‍न केलेला माझ्‍या सर्व संपर्क माहितीसह माझा संपर्क डेटाबेस आहे. काळजीपूर्वक हाताळणी करा." -- cgit v1.2.3 From 8c892903f8eac55db45cdbf232ec7c2bc697ff48 Mon Sep 17 00:00:00 2001 From: Ta-wei Yen Date: Fri, 1 Jun 2018 16:03:01 -0700 Subject: Fix VoicemailContentProvider not notifying status changes In ag/3423779 the VoicemailContentProvider is changed to only notify changes that touches columns in the voicemail content table, and voicemail status changes are no longer notified. Change-Id: Iff71b4de7dc050a6928a73feafccee9699b61dec Fixes: 80102094 Test: tradefed ContactsProviderTests --- .../contacts/DbModifierWithNotification.java | 7 ++++--- .../providers/contacts/VoicemailProviderTest.java | 24 ++++++++++++++++++++++ 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/src/com/android/providers/contacts/DbModifierWithNotification.java b/src/com/android/providers/contacts/DbModifierWithNotification.java index 05803391..852301d3 100644 --- a/src/com/android/providers/contacts/DbModifierWithNotification.java +++ b/src/com/android/providers/contacts/DbModifierWithNotification.java @@ -167,7 +167,8 @@ public class DbModifierWithNotification implements DatabaseModifier { Set packagesModified = getModifiedPackages(whereClause, whereArgs); packagesModified.addAll(getModifiedPackages(values)); - boolean isVoicemail = packagesModified.size() != 0 && isUpdatingVoicemailColumns(values); + boolean isVoicemailContent = + packagesModified.size() != 0 && isUpdatingVoicemailColumns(values); boolean hasMarkedRead = false; if (mIsCallsTable) { @@ -177,7 +178,7 @@ public class DbModifierWithNotification implements DatabaseModifier { } else { updateLastModified(table, whereClause, whereArgs); } - if (isVoicemail) { + if (isVoicemailContent) { if (updateDirtyFlag(values, packagesModified)) { if (values.containsKey(Calls.IS_READ) && getAsBoolean(values, @@ -196,7 +197,7 @@ public class DbModifierWithNotification implements DatabaseModifier { return 0; } int count = mDb.update(table, values, whereClause, whereArgs); - if (count > 0 && isVoicemail) { + if (count > 0 && isVoicemailContent || Tables.VOICEMAIL_STATUS.equals(table)) { notifyVoicemailChange(mBaseUri, packagesModified); } if (count > 0 && mIsCallsTable) { diff --git a/tests/src/com/android/providers/contacts/VoicemailProviderTest.java b/tests/src/com/android/providers/contacts/VoicemailProviderTest.java index 9f9ef002..d20b9b3c 100644 --- a/tests/src/com/android/providers/contacts/VoicemailProviderTest.java +++ b/tests/src/com/android/providers/contacts/VoicemailProviderTest.java @@ -21,8 +21,10 @@ import static org.mockito.Mockito.verify; import android.content.ContentUris; import android.content.ContentValues; +import android.database.ContentObserver; import android.database.Cursor; import android.net.Uri; +import android.os.Handler; import android.os.ParcelFileDescriptor; import android.os.Process; import android.provider.CallLog; @@ -739,6 +741,28 @@ public class VoicemailProviderTest extends BaseVoicemailProviderTest { assertStoredValues(uri, values); } + public void testStatusUpdate_observerNotified() throws Exception { + Uri uri = insertTestStatusEntry(); + ContentValues values = getTestStatusValues(); + values.put(Status.DATA_CHANNEL_STATE, Status.DATA_CHANNEL_STATE_NO_CONNECTION); + values.put(Status.NOTIFICATION_CHANNEL_STATE, + Status.NOTIFICATION_CHANNEL_STATE_MESSAGE_WAITING); + values.put(Status.SOURCE_TYPE, + "vvm_type_test2"); + Boolean[] observerTriggered = new Boolean[]{false}; + mResolver.registerContentObserver(Status.CONTENT_URI, true, + new ContentObserver(new Handler()) { + @Override + public void onChange(boolean selfChange, Uri uri) { + observerTriggered[0] = true; + } + }); + + mResolver.update(uri, values, null, null); + + assertTrue(observerTriggered[0]); + } + public void testStatusUpsert() throws Exception { ContentValues values = getTestStatusValues(); mResolver.insert(statusUri(), values); -- cgit v1.2.3 From 5e2cd1e3d1f6b8618ff3335bfef4df150b011977 Mon Sep 17 00:00:00 2001 From: Bill Yi Date: Sat, 4 Aug 2018 08:29:33 -0700 Subject: Import translations. DO NOT MERGE Change-Id: I87492629ab63a22f4a73f9eec15ca6ecd9d48a22 Auto-generated-cl: translation import --- res/values-cs/strings.xml | 2 +- res/values-da/strings.xml | 2 +- res/values-fa/strings.xml | 2 +- res/values-mr/strings.xml | 2 +- res/values-zh-rTW/strings.xml | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/res/values-cs/strings.xml b/res/values-cs/strings.xml index 489b5b7f..47fc9312 100644 --- a/res/values-cs/strings.xml +++ b/res/values-cs/strings.xml @@ -27,7 +27,7 @@ "Hlasová zpráva od uživatele " "Kopírování databáze kontaktů" "Chystáte se 1) vytvořit v interním úložišti kopii databáze obsahující všechny informace o kontaktech a veškerou historii hovorů a 2) odeslat ji e-mailem. Po úspěšném zkopírování ze zařízení nebo přijetí e-mailem ji nezapomeňte ihned odstranit." - "Smazat nyní" + "Smazat teď" "Spustit" "Vyberte program pro odeslání souboru." "Databáze kontaktů v příloze" diff --git a/res/values-da/strings.xml b/res/values-da/strings.xml index fde3a2f2..c77b4c54 100644 --- a/res/values-da/strings.xml +++ b/res/values-da/strings.xml @@ -26,7 +26,7 @@ "Andre" "Telefonsvarerbesked fra " "Kopiér database med kontaktpersoner" - "Du er ved at 1) lave en kopi af din database, som indeholder alle oplysninger om dine kontaktpersoner og alle opkaldslister, til det interne lager, og 2) sende den som e-mail. Husk at slette kopien, så snart du har kopieret den fra enheden, eller e-mailen er modtaget." + "Du er ved at 1) lave en kopi af din database, som indeholder alle oplysninger om dine kontaktpersoner og alle opkaldslister, til det interne lager, og 2) sende den som mail. Husk at slette kopien, så snart du har kopieret den fra enheden, eller mailen er modtaget." "Slet nu" "Start" "Vælg et program, for at sende din fil" diff --git a/res/values-fa/strings.xml b/res/values-fa/strings.xml index 3266571b..4af29ec9 100644 --- a/res/values-fa/strings.xml +++ b/res/values-fa/strings.xml @@ -26,7 +26,7 @@ "سایر موارد" "پست صوتی از " "کپی پایگاه داده مخاطبین" - "شما در شرف ۱) ایجاد یک کپی از پایگاه داده‌ در حافظه داخلی هستید، این کپی حاوی همه اطلاعات مربوط به مخاطبین و همه گزارشات تماس است و همچنین می‌خواهید ۲) آنرا رایانامه کنید. به خاطر داشته باشید که به محض کپی کردن این نسخه در دستگاه یا دریافت رایانامه، آنرا حذف کنید." + "شما در شرف ۱) ایجاد یک کپی از پایگاه داده‌ در حافظه داخلی هستید، این کپی حاوی همه اطلاعات مربوط به مخاطبین و همه گزارشات تماس است و همچنین می‌خواهید ۲) آنرا ایمیل کنید. به خاطر داشته باشید که به محض کپی کردن این نسخه در دستگاه یا دریافت ایمیل، آنرا حذف کنید." "اکنون حذف شود" "شروع" "یک برنامه را برای ارسال فایل خود انتخاب کنید" diff --git a/res/values-mr/strings.xml b/res/values-mr/strings.xml index a4f324e0..171febea 100644 --- a/res/values-mr/strings.xml +++ b/res/values-mr/strings.xml @@ -29,7 +29,7 @@ "आपण 1) आपल्‍या डेटाबेसची प्रत बनवणार आहात जिच्‍यामध्‍ये सर्व संपर्कांसंबंधी माहिती आणि अंतर्गत संचयनावरील कॉल लॉग समाविष्‍ट असतात आणि 2) ती ईमेल करणार आहात. आपण डिव्‍हाइसवरून यशस्‍वीरित्‍या प्रत कॉपी केल्‍यानंतर किंवा ईमेल प्राप्त केल्‍यानंतर लगेच ती हटविण्‍याचे लक्षात ठेवा." "आता हटवा" "प्रारंभ करा" - "आपली फाईल पाठविण्‍यासाठी एक प्रोग्राम निवडा" + "तुमची फाईल पाठविण्‍यासाठी एक प्रोग्राम निवडा" "संपर्क Db संलग्‍न केला" "संलग्‍न केलेला माझ्‍या सर्व संपर्क माहितीसह माझा संपर्क डेटाबेस आहे. काळजीपूर्वक हाताळणी करा." diff --git a/res/values-zh-rTW/strings.xml b/res/values-zh-rTW/strings.xml index f259d839..decb5b6a 100644 --- a/res/values-zh-rTW/strings.xml +++ b/res/values-zh-rTW/strings.xml @@ -26,7 +26,7 @@ "其他" "語音郵件寄件者: " "複製聯絡人資料庫" - "您即將要 1) 將您的資料庫 (包含所有聯絡人相關資訊及所有通話紀錄) 複製到內部儲存空間,以及 2) 透過電子郵件傳送副本。提醒您,當您順利複製裝置上的資料或收到電子郵件後,請儘快刪除副本。" + "您即將要 1) 將您的資料庫 (包含所有聯絡人相關資訊及所有通話記錄) 複製到內部儲存空間,以及 2) 透過電子郵件傳送副本。提醒您,當您順利複製裝置上的資料或收到電子郵件後,請儘快刪除副本。" "立即刪除" "開始" "選擇要傳送檔案的程式" -- cgit v1.2.3