From a5fbbbda6c950365ff56ddfa5f9f5bb9f8b5bc2f Mon Sep 17 00:00:00 2001 From: Jigar Thakkar Date: Wed, 23 Nov 2022 02:47:44 +0000 Subject: Add unit tests for clone ContactsProvider reads Test: atest CloneContactsProvider2Tests Bug: 257967994 Change-Id: I643a97041c86ad27ec78195405f143593e5ec2d5 --- .../contacts/BaseContactsProvider2Test.java | 67 +++++++ .../contacts/CloneContactsProvider2Test.java | 195 +++++++++++++++++++-- .../providers/contacts/ContactsProvider2Test.java | 65 ------- .../providers/contacts/util/UserUtilsTest.java | 45 +++++ 4 files changed, 296 insertions(+), 76 deletions(-) (limited to 'tests') diff --git a/tests/src/com/android/providers/contacts/BaseContactsProvider2Test.java b/tests/src/com/android/providers/contacts/BaseContactsProvider2Test.java index 20d6a15e..e8920530 100644 --- a/tests/src/com/android/providers/contacts/BaseContactsProvider2Test.java +++ b/tests/src/com/android/providers/contacts/BaseContactsProvider2Test.java @@ -70,6 +70,8 @@ import com.android.providers.contacts.util.Hex; import com.android.providers.contacts.util.MockClock; import com.google.android.collect.Sets; +import java.io.FileInputStream; +import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.BitSet; @@ -1419,6 +1421,71 @@ public abstract class BaseContactsProvider2Test extends PhotoLoadingTestCase { getContactsProvider().getProfileProviderForTest().getDatabaseHelper(), values); } + protected class VCardTestUriCreator { + private String mLookup1; + private String mLookup2; + + public VCardTestUriCreator(String lookup1, String lookup2) { + super(); + mLookup1 = lookup1; + mLookup2 = lookup2; + } + + public Uri getUri1() { + return Uri.withAppendedPath(Contacts.CONTENT_VCARD_URI, mLookup1); + } + + public Uri getUri2() { + return Uri.withAppendedPath(Contacts.CONTENT_VCARD_URI, mLookup2); + } + + public Uri getCombinedUri() { + return Uri.withAppendedPath(Contacts.CONTENT_MULTI_VCARD_URI, + Uri.encode(mLookup1 + ":" + mLookup2)); + } + } + + protected VCardTestUriCreator createVCardTestContacts() { + final long rawContactId1 = RawContactUtil.createRawContact(mResolver, mAccount, + RawContacts.SOURCE_ID, "4:12"); + DataUtil.insertStructuredName(mResolver, rawContactId1, "John", "Doe"); + + final long rawContactId2 = RawContactUtil.createRawContact(mResolver, mAccount, + RawContacts.SOURCE_ID, "3:4%121"); + DataUtil.insertStructuredName(mResolver, rawContactId2, "Jane", "Doh"); + + final long contactId1 = queryContactId(rawContactId1); + final long contactId2 = queryContactId(rawContactId2); + final Uri contact1Uri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId1); + final Uri contact2Uri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId2); + final String lookup1 = + Uri.encode(Contacts.getLookupUri(mResolver, contact1Uri).getPathSegments().get(2)); + final String lookup2 = + Uri.encode(Contacts.getLookupUri(mResolver, contact2Uri).getPathSegments().get(2)); + return new VCardTestUriCreator(lookup1, lookup2); + } + + protected String readToEnd(FileInputStream inputStream) { + try { + System.out.println("DECLARED INPUT STREAM LENGTH: " + inputStream.available()); + int ch; + StringBuilder stringBuilder = new StringBuilder(); + int index = 0; + while (true) { + ch = inputStream.read(); + System.out.println("READ CHARACTER: " + index + " " + ch); + if (ch == -1) { + break; + } + stringBuilder.append((char)ch); + index++; + } + return stringBuilder.toString(); + } catch (IOException e) { + return null; + } + } + /** * A contact in the database, and the attributes used to create it. Construct using * {@link GoldenContactBuilder#build()}. diff --git a/tests/src/com/android/providers/contacts/CloneContactsProvider2Test.java b/tests/src/com/android/providers/contacts/CloneContactsProvider2Test.java index 18c6cb76..48a00da2 100644 --- a/tests/src/com/android/providers/contacts/CloneContactsProvider2Test.java +++ b/tests/src/com/android/providers/contacts/CloneContactsProvider2Test.java @@ -17,28 +17,50 @@ package com.android.providers.contacts; import static com.android.providers.contacts.ContactsActor.MockUserManager.CLONE_PROFILE_USER; +import static com.android.providers.contacts.ContactsActor.MockUserManager.PRIMARY_USER; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.spy; import android.content.ContentProviderOperation; import android.content.ContentProviderResult; import android.content.ContentUris; import android.content.ContentValues; import android.content.OperationApplicationException; +import android.content.res.AssetFileDescriptor; import android.database.Cursor; import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.os.RemoteException; +import android.provider.CallLog; import android.provider.ContactsContract; +import android.util.Log; + import androidx.test.filters.MediumTest; import androidx.test.filters.SdkSuppress; +import com.android.providers.contacts.testutil.DataUtil; +import com.android.providers.contacts.testutil.RawContactUtil; + +import org.junit.Assert; + +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; import java.util.ArrayList; +import java.util.Set; @MediumTest @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake") public class CloneContactsProvider2Test extends BaseContactsProvider2Test { private ContactsActor mCloneContactsActor; + private SynchronousContactsProvider2 mCloneContactsProvider; private SynchronousContactsProvider2 getCloneContactsProvider() { return (SynchronousContactsProvider2) mCloneContactsActor.provider; @@ -55,7 +77,8 @@ public class CloneContactsProvider2Test extends BaseContactsProvider2Test { mCloneContactsActor.mockUserManager.setUsers(ContactsActor.MockUserManager.PRIMARY_USER, CLONE_PROFILE_USER); mCloneContactsActor.mockUserManager.myUser = CLONE_PROFILE_USER.id; - getCloneContactsProvider().wipeData(); + mCloneContactsProvider = spy(getCloneContactsProvider()); + mCloneContactsProvider.wipeData(); } private ContentValues getSampleContentValues() { @@ -67,17 +90,43 @@ public class CloneContactsProvider2Test extends BaseContactsProvider2Test { return values; } + private void getCloneContactsProviderWithMockedCallToParent(Uri uri) { + Cursor primaryProfileCursor = mActor.provider.query(uri, + null /* projection */, null /* queryArgs */, null /* cancellationSignal */); + assertNotNull(primaryProfileCursor); + doReturn(primaryProfileCursor).when(mCloneContactsProvider) + .queryContactsProviderForUser(eq(uri), any(), any(), any(), any(), + any(), eq(PRIMARY_USER)); + } + + private void getCloneContactsProviderWithMockedOpenAssetFileCall(Uri uri) + throws FileNotFoundException { + AssetFileDescriptor fileDescriptor = mActor.provider.openAssetFile(uri, "r"); + doReturn(fileDescriptor).when(mCloneContactsProvider) + .openAssetFileThroughParentProvider(eq(uri), eq("r")); + } + + private String getCursorValue(Cursor c, String columnName) { + return c.getString(c.getColumnIndex(columnName)); + } + private void assertEqualContentValues(ContentValues contentValues, Cursor cursor) { - assertEquals(contentValues.get(ContactsContract.RawContacts.ACCOUNT_NAME), - cursor.getString(cursor.getColumnIndex(ContactsContract.RawContacts.ACCOUNT_NAME))); - assertEquals(contentValues.get(ContactsContract.RawContacts.ACCOUNT_TYPE), - cursor.getString(cursor.getColumnIndex(ContactsContract.RawContacts.ACCOUNT_TYPE))); - assertEquals(contentValues.get(ContactsContract.RawContacts.CUSTOM_RINGTONE), - cursor.getString(cursor.getColumnIndex( - ContactsContract.RawContacts.CUSTOM_RINGTONE))); - assertEquals(contentValues.get(ContactsContract.RawContacts.STARRED), - cursor.getString(cursor.getColumnIndex( - ContactsContract.RawContacts.STARRED))); + for (String key: contentValues.getValues().keySet()) { + assertEquals(contentValues.get(key), getCursorValue(cursor, key)); + } + } + + private void assertRawContactsCursorEquals(Cursor expectedCursor, Cursor actualCursor, + Set columnNames) { + assertNotNull(actualCursor); + assertEquals(expectedCursor.getCount(), actualCursor.getCount()); + while (actualCursor.moveToNext()) { + expectedCursor.moveToNext(); + for (String key: columnNames) { + assertEquals(getCursorValue(expectedCursor, key), + getCursorValue(actualCursor, key)); + } + } } private void assertRejectedApplyBatchResults(ContentProviderResult[] res, @@ -279,4 +328,128 @@ public class CloneContactsProvider2Test extends BaseContactsProvider2Test { assertNotNull(authResponse); assertEquals(Bundle.EMPTY, authResponse); } + + public void testCloneContactsProviderReads_callerNotInAllowlist() { + // Insert raw contact through the primary clone provider + ContentValues inputContentValues = getSampleContentValues(); + long rawContactId = insertRawContactsThroughPrimaryProvider(inputContentValues); + Uri uri = ContentUris.withAppendedId(ContactsContract.RawContacts.CONTENT_URI, + rawContactId); + + // Mock call to parent profile contacts provider to return the correct result containing all + // contacts in the parent profile. + getCloneContactsProviderWithMockedCallToParent(uri); + + // Mock call to ensure the caller package is not in the app-cloning allowlist + doReturn(false) + .when(mCloneContactsProvider).isAppAllowedToUseParentUsersContacts(any()); + + // Test clone contacts provider read with the uri of the contact added above + mCloneContactsProvider.query(uri, + null /* projection */, null /* queryArgs */, null /* cancellationSignal */); + + // Check that the call passed through to the local query instead of redirecting to the + // parent provider + verify(mCloneContactsProvider, times(1)) + .queryDirectoryIfNecessary(any(), any(), any(), any(), any(), any()); + } + + public void testContactsProviderReads_callerInAllowlist() { + // Insert raw contact through the primary clone provider + ContentValues inputContentValues = getSampleContentValues(); + long rawContactId = insertRawContactsThroughPrimaryProvider(inputContentValues); + Uri uri = ContentUris.withAppendedId(ContactsContract.RawContacts.CONTENT_URI, + rawContactId); + + // Mock call to parent profile contacts provider to return the correct result containing all + // contacts in the parent profile. + getCloneContactsProviderWithMockedCallToParent(uri); + + // Mock call to ensure the caller package is in the app-cloning allowlist + doReturn(true) + .when(mCloneContactsProvider).isAppAllowedToUseParentUsersContacts(any()); + + // Test clone contacts provider read with the uri of the contact added above + Cursor cursor = mCloneContactsProvider.query(uri, + null /* projection */, null /* queryArgs */, null /* cancellationSignal */); + + // Check that the call did not pass through to the local query and instead redirected to the + // parent provider + verify(mCloneContactsProvider, times(0)) + .queryDirectoryIfNecessary(any(), any(), any(), any(), any(), any()); + assertNotNull(cursor); + Cursor primaryProfileCursor = mActor.provider.query(uri, + null /* projection */, null /* queryArgs */, null /* cancellationSignal */); + assertNotNull(primaryProfileCursor); + assertRawContactsCursorEquals(primaryProfileCursor, cursor, + inputContentValues.getValues().keySet()); + } + + public void testQueryPrimaryProfileProvider_callingFromParentUser() { + ContentValues inputContentValues = getSampleContentValues(); + long rawContactId = insertRawContactsThroughPrimaryProvider(inputContentValues); + Uri uri = ContentUris.withAppendedId(ContactsContract.RawContacts.CONTENT_URI, + rawContactId); + + // Fetch primary contacts provider and call method to redirect to parent provider + final ContactsProvider2 primaryCP2 = (ContactsProvider2) getProvider(); + Cursor cursor = primaryCP2.queryParentProfileContactsProvider(uri, + null /* projection */, null /* selection */, null /* selectionArgs */, + null /* sortOrder */, null /* cancellationSignal */); + + // Assert that empty cursor is returned + assertNotNull(cursor); + assertEquals(0, cursor.getCount()); + } + + public void testQueryPrimaryProfileProvider_incorrectAuthority() { + ContentValues inputContentValues = getSampleContentValues(); + insertRawContactsThroughPrimaryProvider(inputContentValues); + + Assert.assertThrows(IllegalArgumentException.class, () -> + mCloneContactsProvider.queryParentProfileContactsProvider(CallLog.CONTENT_URI, + null /* projection */, null /* selection */, null /* selectionArgs */, + null /* sortOrder */, null /* cancellationSignal */)); + } + + public void testOpenAssetFileMultiVCard() throws IOException { + final VCardTestUriCreator contacts = createVCardTestContacts(); + + // Mock call to parent profile contacts provider to return the correct asset file + getCloneContactsProviderWithMockedOpenAssetFileCall(contacts.getCombinedUri()); + + // Mock call to ensure the caller package is in the app-cloning allowlist + doReturn(true) + .when(mCloneContactsProvider).isAppAllowedToUseParentUsersContacts(any()); + + final AssetFileDescriptor descriptor = + mCloneContactsProvider.openAssetFile(contacts.getCombinedUri(), "r"); + final FileInputStream inputStream = descriptor.createInputStream(); + String data = readToEnd(inputStream); + inputStream.close(); + descriptor.close(); + + // Ensure that the resulting VCard has both contacts + assertTrue(data.contains("N:Doe;John;;;")); + assertTrue(data.contains("N:Doh;Jane;;;")); + } + + public void testOpenAssetFileMultiVCard_callerNotInAllowlist() throws IOException { + final VCardTestUriCreator contacts = createVCardTestContacts(); + + // Mock call to parent profile contacts provider to return the correct asset file + getCloneContactsProviderWithMockedOpenAssetFileCall(contacts.getCombinedUri()); + + // Mock call to ensure the caller package is not in the app-cloning allowlist + doReturn(false) + .when(mCloneContactsProvider).isAppAllowedToUseParentUsersContacts(any()); + + final AssetFileDescriptor descriptor = + mCloneContactsProvider.openAssetFile(contacts.getCombinedUri(), "r"); + + // Check that the call passed through to the local call instead of redirecting to the + // parent provider + verify(mCloneContactsProvider, times(1)) + .openAssetFile(eq(contacts.getCombinedUri()), any()); + } } diff --git a/tests/src/com/android/providers/contacts/ContactsProvider2Test.java b/tests/src/com/android/providers/contacts/ContactsProvider2Test.java index b625ac4a..69ae0fb2 100644 --- a/tests/src/com/android/providers/contacts/ContactsProvider2Test.java +++ b/tests/src/com/android/providers/contacts/ContactsProvider2Test.java @@ -8163,50 +8163,6 @@ public class ContactsProvider2Test extends BaseContactsProvider2Test { assertEquals("default", helper.getProperty("existent1", "default")); } - private class VCardTestUriCreator { - private String mLookup1; - private String mLookup2; - - public VCardTestUriCreator(String lookup1, String lookup2) { - super(); - mLookup1 = lookup1; - mLookup2 = lookup2; - } - - public Uri getUri1() { - return Uri.withAppendedPath(Contacts.CONTENT_VCARD_URI, mLookup1); - } - - public Uri getUri2() { - return Uri.withAppendedPath(Contacts.CONTENT_VCARD_URI, mLookup2); - } - - public Uri getCombinedUri() { - return Uri.withAppendedPath(Contacts.CONTENT_MULTI_VCARD_URI, - Uri.encode(mLookup1 + ":" + mLookup2)); - } - } - - private VCardTestUriCreator createVCardTestContacts() { - final long rawContactId1 = RawContactUtil.createRawContact(mResolver, mAccount, - RawContacts.SOURCE_ID, "4:12"); - DataUtil.insertStructuredName(mResolver, rawContactId1, "John", "Doe"); - - final long rawContactId2 = RawContactUtil.createRawContact(mResolver, mAccount, - RawContacts.SOURCE_ID, "3:4%121"); - DataUtil.insertStructuredName(mResolver, rawContactId2, "Jane", "Doh"); - - final long contactId1 = queryContactId(rawContactId1); - final long contactId2 = queryContactId(rawContactId2); - final Uri contact1Uri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId1); - final Uri contact2Uri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId2); - final String lookup1 = - Uri.encode(Contacts.getLookupUri(mResolver, contact1Uri).getPathSegments().get(2)); - final String lookup2 = - Uri.encode(Contacts.getLookupUri(mResolver, contact2Uri).getPathSegments().get(2)); - return new VCardTestUriCreator(lookup1, lookup2); - } - public void testQueryMultiVCard() { // No need to create any contacts here, because the query for multiple vcards // does not go into the database at all @@ -9688,27 +9644,6 @@ public class ContactsProvider2Test extends BaseContactsProvider2Test { return c; } - private String readToEnd(FileInputStream inputStream) { - try { - System.out.println("DECLARED INPUT STREAM LENGTH: " + inputStream.available()); - int ch; - StringBuilder stringBuilder = new StringBuilder(); - int index = 0; - while (true) { - ch = inputStream.read(); - System.out.println("READ CHARACTER: " + index + " " + ch); - if (ch == -1) { - break; - } - stringBuilder.append((char)ch); - index++; - } - return stringBuilder.toString(); - } catch (IOException e) { - return null; - } - } - private void assertQueryParameter(String uriString, String parameter, String expectedValue) { assertEquals(expectedValue, ContactsProvider2.getQueryParameter( Uri.parse(uriString), parameter)); diff --git a/tests/src/com/android/providers/contacts/util/UserUtilsTest.java b/tests/src/com/android/providers/contacts/util/UserUtilsTest.java index 072df377..c672697a 100644 --- a/tests/src/com/android/providers/contacts/util/UserUtilsTest.java +++ b/tests/src/com/android/providers/contacts/util/UserUtilsTest.java @@ -18,6 +18,7 @@ package com.android.providers.contacts.util; import static com.android.providers.contacts.ContactsActor.PACKAGE_GREY; import android.content.Context; +import android.os.UserHandle; import android.provider.ContactsContract; import android.test.suitebuilder.annotation.SmallTest; @@ -101,14 +102,58 @@ public class UserUtilsTest extends FixedAndroidTestCase { um.myUser = MockUserManager.PRIMARY_USER.id; assertFalse(UserUtils.shouldUseParentsContacts(c)); + assertFalse(UserUtils.shouldUseParentsContacts(c, + MockUserManager.PRIMARY_USER.getUserHandle())); um.myUser = MockUserManager.SECONDARY_USER.id; assertFalse(UserUtils.shouldUseParentsContacts(c)); + assertFalse(UserUtils.shouldUseParentsContacts(c, + MockUserManager.SECONDARY_USER.getUserHandle())); um.myUser = MockUserManager.CORP_USER.id; assertFalse(UserUtils.shouldUseParentsContacts(c)); + assertFalse(UserUtils.shouldUseParentsContacts(c, + MockUserManager.CORP_USER.getUserHandle())); um.myUser = MockUserManager.CLONE_PROFILE_USER.id; assertTrue(UserUtils.shouldUseParentsContacts(c)); + assertTrue(UserUtils.shouldUseParentsContacts(c, + MockUserManager.CLONE_PROFILE_USER.getUserHandle())); + + } + + public void testIsParentUser() { + final Context c = mActor.getProviderContext(); + final MockUserManager um = mActor.mockUserManager; + um.setUsers(MockUserManager.PRIMARY_USER, MockUserManager.SECONDARY_USER, + MockUserManager.CLONE_PROFILE_USER, MockUserManager.CORP_USER); + + UserHandle primaryProfileUserHandle = MockUserManager.PRIMARY_USER.getUserHandle(); + UserHandle cloneUserHandle = MockUserManager.CLONE_PROFILE_USER.getUserHandle(); + UserHandle corpUserHandle = MockUserManager.CORP_USER.getUserHandle(); + + assertTrue(UserUtils.isParentUser(c, primaryProfileUserHandle, cloneUserHandle)); + assertTrue(UserUtils.isParentUser(c, primaryProfileUserHandle, corpUserHandle)); + assertFalse(UserUtils.isParentUser(c, primaryProfileUserHandle, primaryProfileUserHandle)); + assertFalse(UserUtils.isParentUser(c, cloneUserHandle, cloneUserHandle)); + assertFalse(UserUtils.isParentUser(c, cloneUserHandle, primaryProfileUserHandle)); + assertFalse(UserUtils.isParentUser(c, corpUserHandle, primaryProfileUserHandle)); + } + + public void testGetProfileParent() { + final Context c = mActor.getProviderContext(); + final MockUserManager um = mActor.mockUserManager; + + um.setUsers(MockUserManager.PRIMARY_USER, MockUserManager.SECONDARY_USER, + MockUserManager.CLONE_PROFILE_USER, MockUserManager.CORP_USER); + + um.myUser = MockUserManager.PRIMARY_USER.id; + assertNull(UserUtils.getProfileParentUser(c)); + + um.myUser = MockUserManager.CLONE_PROFILE_USER.id; + assertEquals(MockUserManager.PRIMARY_USER, UserUtils.getProfileParentUser(c)); + + um.myUser = MockUserManager.CORP_USER.id; + assertEquals(MockUserManager.PRIMARY_USER, UserUtils.getProfileParentUser(c)); } } -- cgit v1.2.3