aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJeff Sharkey <jsharkey@android.com>2009-10-12 20:38:46 -0700
committerJeff Sharkey <jsharkey@android.com>2009-10-12 20:38:59 -0700
commit1d9c0e17216ff6df5f73fbc5e784b5965c5026bd (patch)
treeb385ec32f235cfb2966a4dac510b879771335a1e
parentfda634f3eeff6aed8e8dddca92fc07aa44befedd (diff)
downloadContactsProvider-1d9c0e17216ff6df5f73fbc5e784b5965c5026bd.tar.gz
Unit tests to verify IS_RESTRICTED security mechanisms.
Partially fixes http://b/2148997
-rw-r--r--tests/src/com/android/providers/contacts/ContactsActor.java119
-rw-r--r--tests/src/com/android/providers/contacts/RestrictionExceptionsTest.java466
2 files changed, 339 insertions, 246 deletions
diff --git a/tests/src/com/android/providers/contacts/ContactsActor.java b/tests/src/com/android/providers/contacts/ContactsActor.java
index d84f0e10..9b4f4bed 100644
--- a/tests/src/com/android/providers/contacts/ContactsActor.java
+++ b/tests/src/com/android/providers/contacts/ContactsActor.java
@@ -24,6 +24,7 @@ import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.res.Resources;
+import android.content.res.Resources.NotFoundException;
import android.database.Cursor;
import android.net.Uri;
import android.os.Binder;
@@ -33,12 +34,17 @@ import android.provider.ContactsContract.CommonDataKinds;
import android.provider.ContactsContract.Contacts;
import android.provider.ContactsContract.Data;
import android.provider.ContactsContract.RawContacts;
+import android.provider.ContactsContract.StatusUpdates;
+import android.provider.ContactsContract.CommonDataKinds.Email;
import android.provider.ContactsContract.CommonDataKinds.Phone;
import android.test.IsolatedContext;
import android.test.RenamingDelegatingContext;
import android.test.mock.MockContentResolver;
import android.test.mock.MockContext;
import android.test.mock.MockPackageManager;
+import android.test.mock.MockResources;
+import android.util.Log;
+import android.util.TypedValue;
import java.util.HashMap;
@@ -108,6 +114,7 @@ public class ContactsActor {
private final String mReportedPackageName;
private final RestrictionMockPackageManager mPackageManager;
private final ContentResolver mResolver;
+ private final Resources mRes;
/**
* Create a {@link Context} under the given package name.
@@ -117,11 +124,14 @@ public class ContactsActor {
mOverallContext = overallContext;
mReportedPackageName = reportedPackageName;
mResolver = resolver;
+
mPackageManager = new RestrictionMockPackageManager();
mPackageManager.addPackage(1000, PACKAGE_GREY);
mPackageManager.addPackage(2000, PACKAGE_RED);
mPackageManager.addPackage(3000, PACKAGE_GREEN);
mPackageManager.addPackage(4000, PACKAGE_BLUE);
+
+ mRes = new RestrictionMockResources(overallContext.getResources());
}
@Override
@@ -136,7 +146,7 @@ public class ContactsActor {
@Override
public Resources getResources() {
- return mOverallContext.getResources();
+ return mRes;
}
@Override
@@ -145,6 +155,56 @@ public class ContactsActor {
}
}
+ private static class RestrictionMockResources extends MockResources {
+ private static final String UNRESTRICTED = "unrestricted_packages";
+ private static final int UNRESTRICTED_ID = 1024;
+
+ private static final String[] UNRESTRICTED_LIST = new String[] {
+ PACKAGE_GREY
+ };
+
+ private final Resources mRes;
+
+ public RestrictionMockResources(Resources res) {
+ mRes = res;
+ }
+
+ @Override
+ public int getIdentifier(String name, String defType, String defPackage) {
+ if (UNRESTRICTED.equals(name)) {
+ return UNRESTRICTED_ID;
+ } else {
+ return mRes.getIdentifier(name, defType, defPackage);
+ }
+ }
+
+ @Override
+ public String[] getStringArray(int id) throws NotFoundException {
+ if (id == UNRESTRICTED_ID) {
+ return UNRESTRICTED_LIST;
+ } else {
+ return mRes.getStringArray(id);
+ }
+ }
+
+ @Override
+ public void getValue(int id, TypedValue outValue, boolean resolveRefs)
+ throws NotFoundException {
+ mRes.getValue(id, outValue, resolveRefs);
+ }
+
+ @Override
+ public String getString(int id) throws NotFoundException {
+ return mRes.getString(id);
+ }
+ }
+
+ private static String sCallingPackage = null;
+
+ void ensureCallingPackage() {
+ sCallingPackage = this.packageName;
+ }
+
/**
* Mock {@link PackageManager} that knows about a specific set of packages
* to help test security models. Because {@link Binder#getCallingUid()}
@@ -155,6 +215,9 @@ public class ContactsActor {
private final HashMap<Integer, String> mForward = new HashMap<Integer, String>();
private final HashMap<String, Integer> mReverse = new HashMap<String, Integer>();
+ public RestrictionMockPackageManager() {
+ }
+
/**
* Add a UID-to-package mapping, which is then stored internally.
*/
@@ -165,7 +228,7 @@ public class ContactsActor {
@Override
public String[] getPackagesForUid(int uid) {
- return new String[] { mForward.get(uid) };
+ return new String[] { sCallingPackage };
}
@Override
@@ -178,12 +241,14 @@ public class ContactsActor {
}
public long createContact(boolean isRestricted, String name) {
+ ensureCallingPackage();
long contactId = createContact(isRestricted);
createName(contactId, name);
return contactId;
}
public long createContact(boolean isRestricted) {
+ ensureCallingPackage();
final ContentValues values = new ContentValues();
if (isRestricted) {
values.put(RawContacts.IS_RESTRICTED, 1);
@@ -193,7 +258,16 @@ public class ContactsActor {
return ContentUris.parseId(contactUri);
}
+ public long createContactWithStatus(boolean isRestricted, String name, String address,
+ String status) {
+ final long rawContactId = createContact(isRestricted, name);
+ final long dataId = createEmail(rawContactId, address);
+ createStatus(dataId, status);
+ return rawContactId;
+ }
+
public long createName(long contactId, String name) {
+ ensureCallingPackage();
final ContentValues values = new ContentValues();
values.put(Data.RAW_CONTACT_ID, contactId);
values.put(Data.IS_PRIMARY, 1);
@@ -207,6 +281,7 @@ public class ContactsActor {
}
public long createPhone(long contactId, String phoneNumber) {
+ ensureCallingPackage();
final ContentValues values = new ContentValues();
values.put(Data.RAW_CONTACT_ID, contactId);
values.put(Data.IS_PRIMARY, 1);
@@ -221,11 +296,36 @@ public class ContactsActor {
return ContentUris.parseId(dataUri);
}
+ public long createEmail(long contactId, String address) {
+ ensureCallingPackage();
+ final ContentValues values = new ContentValues();
+ values.put(Data.RAW_CONTACT_ID, contactId);
+ values.put(Data.IS_PRIMARY, 1);
+ values.put(Data.IS_SUPER_PRIMARY, 1);
+ values.put(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE);
+ values.put(Email.TYPE, Email.TYPE_HOME);
+ values.put(Email.DATA, address);
+ Uri insertUri = Uri.withAppendedPath(ContentUris.withAppendedId(RawContacts.CONTENT_URI,
+ contactId), RawContacts.Data.CONTENT_DIRECTORY);
+ Uri dataUri = resolver.insert(insertUri, values);
+ return ContentUris.parseId(dataUri);
+ }
+
+ public long createStatus(long dataId, String status) {
+ ensureCallingPackage();
+ final ContentValues values = new ContentValues();
+ values.put(StatusUpdates.DATA_ID, dataId);
+ values.put(StatusUpdates.STATUS, status);
+ Uri dataUri = resolver.insert(StatusUpdates.CONTENT_URI, values);
+ return ContentUris.parseId(dataUri);
+ }
+
public void updateException(String packageProvider, String packageClient, boolean allowAccess) {
throw new UnsupportedOperationException("RestrictionExceptions are hard-coded");
}
public long getContactForRawContact(long rawContactId) {
+ ensureCallingPackage();
Uri contactUri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId);
final Cursor cursor = resolver.query(contactUri, Projections.PROJ_RAW_CONTACTS, null,
null, null);
@@ -239,6 +339,7 @@ public class ContactsActor {
}
public int getDataCountForContact(long contactId) {
+ ensureCallingPackage();
Uri contactUri = Uri.withAppendedPath(ContentUris.withAppendedId(Contacts.CONTENT_URI,
contactId), Contacts.Data.CONTENT_DIRECTORY);
final Cursor cursor = resolver.query(contactUri, Projections.PROJ_ID, null, null,
@@ -248,7 +349,19 @@ public class ContactsActor {
return count;
}
+ public int getDataCountForRawContact(long rawContactId) {
+ ensureCallingPackage();
+ Uri contactUri = Uri.withAppendedPath(ContentUris.withAppendedId(RawContacts.CONTENT_URI,
+ rawContactId), Contacts.Data.CONTENT_DIRECTORY);
+ final Cursor cursor = resolver.query(contactUri, Projections.PROJ_ID, null, null,
+ null);
+ final int count = cursor.getCount();
+ cursor.close();
+ return count;
+ }
+
public void setSuperPrimaryPhone(long dataId) {
+ ensureCallingPackage();
final ContentValues values = new ContentValues();
values.put(Data.IS_PRIMARY, 1);
values.put(Data.IS_SUPER_PRIMARY, 1);
@@ -257,6 +370,7 @@ public class ContactsActor {
}
public long createGroup(String groupName) {
+ ensureCallingPackage();
final ContentValues values = new ContentValues();
values.put(ContactsContract.Groups.RES_PACKAGE, packageName);
values.put(ContactsContract.Groups.TITLE, groupName);
@@ -265,6 +379,7 @@ public class ContactsActor {
}
public long createGroupMembership(long rawContactId, long groupId) {
+ ensureCallingPackage();
final ContentValues values = new ContentValues();
values.put(Data.RAW_CONTACT_ID, rawContactId);
values.put(Data.MIMETYPE, CommonDataKinds.GroupMembership.CONTENT_ITEM_TYPE);
diff --git a/tests/src/com/android/providers/contacts/RestrictionExceptionsTest.java b/tests/src/com/android/providers/contacts/RestrictionExceptionsTest.java
index c1e897f7..7928d224 100644
--- a/tests/src/com/android/providers/contacts/RestrictionExceptionsTest.java
+++ b/tests/src/com/android/providers/contacts/RestrictionExceptionsTest.java
@@ -16,46 +16,43 @@
package com.android.providers.contacts;
-import static com.android.providers.contacts.ContactsActor.PACKAGE_BLUE;
-import static com.android.providers.contacts.ContactsActor.PACKAGE_GREEN;
import static com.android.providers.contacts.ContactsActor.PACKAGE_GREY;
import static com.android.providers.contacts.ContactsActor.PACKAGE_RED;
import android.content.ContentUris;
+import android.content.ContentValues;
import android.content.Context;
+import android.content.Entity;
+import android.content.EntityIterator;
+import android.content.res.AssetFileDescriptor;
import android.database.Cursor;
import android.net.Uri;
-import android.provider.BaseColumns;
import android.provider.ContactsContract;
+import android.provider.LiveFolders;
import android.provider.ContactsContract.Contacts;
-import android.provider.ContactsContract.RawContacts;
import android.provider.ContactsContract.Data;
+import android.provider.ContactsContract.RawContacts;
+import android.provider.ContactsContract.CommonDataKinds.Email;
import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.LargeTest;
+import java.io.InputStream;
+
/**
- * Unit tests for {@link RestrictionExceptions}.
- *
- * Run the test like this:
- * <code>
- * adb shell am instrument -e class com.android.providers.contacts.RestrictionExceptionsTest -w \
- * com.android.providers.contacts.tests/android.test.InstrumentationTestRunner
- * </code>
+ * Unit tests for {@link RawContacts#IS_RESTRICTED}.
*/
@LargeTest
public class RestrictionExceptionsTest extends AndroidTestCase {
- private static final String TAG = "RestrictionExceptionsTest";
-
- private static ContactsActor mGrey;
- private static ContactsActor mRed;
- private static ContactsActor mGreen;
- private static ContactsActor mBlue;
+ private ContactsActor mGrey;
+ private ContactsActor mRed;
private static final String PHONE_GREY = "555-1111";
private static final String PHONE_RED = "555-2222";
- private static final String PHONE_GREEN = "555-3333";
- private static final String PHONE_BLUE = "555-4444";
+ private static final String EMAIL_GREY = "user@example.com";
+ private static final String EMAIL_RED = "user@example.org";
+
+ private static final String GENERIC_STATUS = "Status update";
private static final String GENERIC_NAME = "Smith";
public RestrictionExceptionsTest() {
@@ -72,275 +69,256 @@ public class RestrictionExceptionsTest extends AndroidTestCase {
SynchronousContactsProvider2.class, ContactsContract.AUTHORITY);
mRed = new ContactsActor(overallContext, PACKAGE_RED,
SynchronousContactsProvider2.class, ContactsContract.AUTHORITY);
- mGreen = new ContactsActor(overallContext, PACKAGE_GREEN,
- SynchronousContactsProvider2.class, ContactsContract.AUTHORITY);
- mBlue = new ContactsActor(overallContext, PACKAGE_BLUE,
- SynchronousContactsProvider2.class, ContactsContract.AUTHORITY);
// TODO make the provider wipe data automatically
((SynchronousContactsProvider2)mGrey.provider).wipeData();
}
/**
- * Create various contacts that are both open and restricted, and assert
- * that both {@link Contacts#IS_RESTRICTED} and
- * {@link RestrictionExceptions} are being applied correctly.
+ * Assert that {@link Contacts#CONTACT_STATUS} matches the given value, or
+ * that no rows are returned when null.
*/
- public void __testDataRestriction() {
-
- // Grey creates an unprotected contact
- long greyContact = mGrey.createContact(false);
- long greyData = mGrey.createPhone(greyContact, PHONE_GREY);
- long greyAgg = mGrey.getContactForRawContact(greyContact);
-
- // Assert that both Grey and Blue can read contact
- assertTrue("Owner of unrestricted contact unable to read",
- (mGrey.getDataCountForContact(greyAgg) == 1));
- assertTrue("Non-owner of unrestricted contact unable to read",
- (mBlue.getDataCountForContact(greyAgg) == 1));
-
- // Red grants protected access to itself
- mRed.updateException(PACKAGE_RED, PACKAGE_RED, true);
-
- // Red creates a protected contact
- long redContact = mRed.createContact(true);
- long redData = mRed.createPhone(redContact, PHONE_RED);
- long redAgg = mRed.getContactForRawContact(redContact);
-
- // Assert that only Red can read contact
- assertTrue("Owner of restricted contact unable to read",
- (mRed.getDataCountForContact(redAgg) == 1));
- assertTrue("Non-owner of restricted contact able to read",
- (mBlue.getDataCountForContact(redAgg) == 0));
- assertTrue("Non-owner of restricted contact able to read",
- (mGreen.getDataCountForContact(redAgg) == 0));
+ void assertStatus(ContactsActor actor, long aggId, String status) {
+ final Uri aggUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, aggId);
+ actor.ensureCallingPackage();
+ final Cursor cursor = actor.resolver.query(aggUri,
+ new String[] { Contacts.CONTACT_STATUS }, null, null, null);
try {
- // Blue tries to grant an exception for Red data, which should throw
- // exception. If it somehow worked, fail this test.
- mBlue.updateException(PACKAGE_RED, PACKAGE_BLUE, true);
- fail("Non-owner able to grant restriction exception");
-
- } catch (RuntimeException e) {
+ if (status == null) {
+ assertEquals(0, cursor.getCount());
+ } else {
+ while (cursor.moveToNext()) {
+ final String foundStatus = cursor.getString(0);
+ assertEquals(status, foundStatus);
+ }
+ }
+ } finally {
+ cursor.close();
}
+ }
- // Red grants exception to Blue for contact
- mRed.updateException(PACKAGE_RED, PACKAGE_BLUE, true);
-
- // Both Blue and Red can read Red contact, but still not Green
- assertTrue("Owner of restricted contact unable to read",
- (mRed.getDataCountForContact(redAgg) == 1));
- assertTrue("Non-owner with restriction exception unable to read",
- (mBlue.getDataCountForContact(redAgg) == 1));
- assertTrue("Non-owner of restricted contact able to read",
- (mGreen.getDataCountForContact(redAgg) == 0));
-
- // Red revokes exception to Blue
- mRed.updateException(PACKAGE_RED, PACKAGE_BLUE, false);
+ public void testRestrictedInsertRestrictedQuery() {
+ // Restricted query can read restricted data
+ final long rawContact = mGrey.createContact(true, GENERIC_NAME);
+ final int count = mGrey.getDataCountForRawContact(rawContact);
+ assertEquals(1, count);
+ }
- // Assert that only Red can read contact
- assertTrue("Owner of restricted contact unable to read",
- (mRed.getDataCountForContact(redAgg) == 1));
- assertTrue("Non-owner of restricted contact able to read",
- (mBlue.getDataCountForContact(redAgg) == 0));
- assertTrue("Non-owner of restricted contact able to read",
- (mGreen.getDataCountForContact(redAgg) == 0));
+ public void testRestrictedInsertGenericQuery() {
+ // Generic query is denied restricted data
+ final long rawContact = mGrey.createContact(true, GENERIC_NAME);
+ final int count = mRed.getDataCountForRawContact(rawContact);
+ assertEquals(0, count);
+ }
+ public void testGenericInsertRestrictedQuery() {
+ // Restricted query can read generic data
+ final long rawContact = mRed.createContact(false, GENERIC_NAME);
+ final int count = mGrey.getDataCountForRawContact(rawContact);
+ assertEquals(1, count);
}
- /**
- * Create an aggregate that has multiple contacts with various levels of
- * protected data, and ensure that {@link Contacts#CONTENT_URI}
- * details don't expose {@link Contacts#IS_RESTRICTED} data.
- */
- public void __testAggregateSummary() {
+ public void testGenericInsertGenericQuery() {
+ // Generic query can read generic data
+ final long rawContact = mRed.createContact(false, GENERIC_NAME);
+ final int count = mRed.getDataCountForRawContact(rawContact);
+ assertEquals(1, count);
+ }
- // Red grants exceptions to itself and Grey
- mRed.updateException(PACKAGE_RED, PACKAGE_RED, true);
- mRed.updateException(PACKAGE_RED, PACKAGE_GREY, true);
+ public void testMixedAggregateRestrictedQuery() {
+ // Create mixed aggregate with a restricted phone number
+ final long greyContact = mGrey.createContact(true, GENERIC_NAME);
+ final long greyPhone = mGrey.createPhone(greyContact, PHONE_GREY);
+ final long redContact = mRed.createContact(false, GENERIC_NAME);
+ final long redPhone = mRed.createPhone(redContact, PHONE_RED);
- // Red creates a protected contact
- long redContact = mRed.createContact(true);
- long redName = mRed.createName(redContact, GENERIC_NAME);
- long redPhone = mRed.createPhone(redContact, PHONE_RED);
+ // Make sure both aggregates were joined
+ final long greyAgg = mGrey.getContactForRawContact(greyContact);
+ final long redAgg = mRed.getContactForRawContact(redContact);
+ assertEquals(greyAgg, redAgg);
- // Blue grants exceptions to itself and Grey
- mBlue.updateException(PACKAGE_BLUE, PACKAGE_BLUE, true);
- mBlue.updateException(PACKAGE_BLUE, PACKAGE_GREY, true);
+ // Restricted reader should have access to both numbers
+ final int greyCount = mGrey.getDataCountForContact(greyAgg);
+ assertEquals(4, greyCount);
- // Blue creates a protected contact
- long blueContact = mBlue.createContact(true);
- long blueName = mBlue.createName(blueContact, GENERIC_NAME);
- long bluePhone = mBlue.createPhone(blueContact, PHONE_BLUE);
+ // Generic reader should have limited access
+ final int redCount = mRed.getDataCountForContact(redAgg);
+ assertEquals(2, redCount);
+ }
- // Set the super-primary phone number to Red
- mRed.setSuperPrimaryPhone(redPhone);
+ public void testUpdateRestricted() {
+ // Assert that we can't un-restrict something
+ final long greyContact = mGrey.createContact(true, GENERIC_NAME);
+ final long greyPhone = mGrey.createPhone(greyContact, PHONE_GREY);
- // Make sure both aggregates were joined
- long singleAgg;
- {
- long redAgg = mRed.getContactForRawContact(redContact);
- long blueAgg = mBlue.getContactForRawContact(blueContact);
- assertTrue("Two contacts with identical name not aggregated correctly",
- (redAgg == blueAgg));
- singleAgg = redAgg;
- }
+ int count = mRed.getDataCountForRawContact(greyContact);
+ assertEquals(0, count);
- /*
- // Grey and Red querying summary should see Red phone. Blue shouldn't
- // see any summary data, since it's own data is protected and it's not
- // the super-primary. Green shouldn't know this aggregate exists.
- assertTrue("Participant with restriction exception reading incorrect summary",
- (mGrey.getPrimaryPhoneId(singleAgg) == redPhone));
- assertTrue("Participant with super-primary restricted data reading incorrect summary",
- (mRed.getPrimaryPhoneId(singleAgg) == redPhone));
- assertTrue("Participant with non-super-primary restricted data reading incorrect summary",
- (mBlue.getPrimaryPhoneId(singleAgg) == 0));
- assertTrue("Non-participant able to discover aggregate existance",
- (mGreen.getPrimaryPhoneId(singleAgg) == 0));
-
- // Add an unprotected Grey contact into the mix
- long greyContact = mGrey.createContact(false);
- long greyName = mGrey.createName(greyContact, GENERIC_NAME);
- long greyPhone = mGrey.createPhone(greyContact, PHONE_GREY);
-
- // Set the super-primary phone number to Blue
- mBlue.setSuperPrimaryPhone(bluePhone);
-
- // Make sure all three aggregates were joined
- {
- long redAgg = mRed.getContactForRawContact(redContact);
- long blueAgg = mBlue.getContactForRawContact(blueContact);
- long greyAgg = mGrey.getContactForRawContact(greyContact);
- assertTrue("Three contacts with identical name not aggregated correctly",
- (redAgg == blueAgg) && (blueAgg == greyAgg));
- singleAgg = redAgg;
- }
+ // Try un-restricting that contact
+ final Uri greyUri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, greyContact);
+ final ContentValues values = new ContentValues();
+ values.put(RawContacts.IS_RESTRICTED, 0);
+ mRed.ensureCallingPackage();
+ mRed.provider.update(greyUri, values, null, null);
- // Grey and Blue querying summary should see Blue phone. Red should see
- // the Grey phone in its summary, since it's the unprotected fallback.
- // Red doesn't see its own phone number because it's not super-primary,
- // and is protected. Again, green shouldn't know this exists.
- assertTrue("Participant with restriction exception reading incorrect summary",
- (mGrey.getPrimaryPhoneId(singleAgg) == bluePhone));
- assertTrue("Participant with non-super-primary restricted data reading incorrect summary",
- (mRed.getPrimaryPhoneId(singleAgg) == greyPhone));
- assertTrue("Participant with super-primary restricted data reading incorrect summary",
- (mBlue.getPrimaryPhoneId(singleAgg) == bluePhone));
- assertTrue("Non-participant couldn't find unrestricted primary through summary",
- (mGreen.getPrimaryPhoneId(singleAgg) == greyPhone));
- */
+ count = mRed.getDataCountForRawContact(greyContact);
+ assertEquals(0, count);
}
- /**
- * Create a contact that is completely restricted and isolated in its own
- * aggregate, and make sure that another actor can't detect its existence.
- */
- public void __testRestrictionSilence() {
- Cursor cursor;
-
- // Green grants exception to itself
- mGreen.updateException(PACKAGE_GREEN, PACKAGE_GREEN, true);
-
- // Green creates a protected contact
- long greenContact = mGreen.createContact(true);
- long greenData = mGreen.createPhone(greenContact, PHONE_GREEN);
- long greenAgg = mGreen.getContactForRawContact(greenContact);
-
- // AGGREGATES
- cursor = mRed.resolver
- .query(Contacts.CONTENT_URI, Projections.PROJ_ID, null, null, null);
- while (cursor.moveToNext()) {
- assertTrue("Discovered restricted contact",
- (cursor.getLong(Projections.COL_ID) != greenAgg));
- }
- cursor.close();
-
- // AGGREGATES_ID
- cursor = mRed.resolver.query(ContentUris.withAppendedId(Contacts.CONTENT_URI, greenAgg),
- Projections.PROJ_ID, null, null, null);
- assertTrue("Discovered restricted contact", (cursor.getCount() == 0));
- cursor.close();
+ public void testExportVCard() throws Exception {
+ // Create mixed aggregate with a restricted phone number
+ final long greyContact = mGrey.createContact(true, GENERIC_NAME);
+ final long greyPhone = mGrey.createPhone(greyContact, PHONE_GREY);
+ final long redContact = mRed.createContact(false, GENERIC_NAME);
+ final long redPhone = mRed.createPhone(redContact, PHONE_RED);
- // AGGREGATES_DATA
- cursor = mRed.resolver.query(Uri.withAppendedPath(ContentUris.withAppendedId(
- Contacts.CONTENT_URI, greenAgg), Contacts.Data.CONTENT_DIRECTORY),
- Projections.PROJ_ID, null, null, null);
- assertTrue("Discovered restricted contact", (cursor.getCount() == 0));
+ // Make sure both aggregates were joined
+ final long greyAgg = mGrey.getContactForRawContact(greyContact);
+ final long redAgg = mRed.getContactForRawContact(redContact);
+ assertEquals(greyAgg, redAgg);
+
+ // Exported vCard shouldn't contain restricted phone
+ mRed.ensureCallingPackage();
+ final Uri aggUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, greyAgg);
+ final Cursor cursor = mRed.resolver.query(aggUri,
+ new String[] { Contacts.LOOKUP_KEY }, null, null, null);
+ assertTrue(cursor.moveToFirst());
+ final String lookupKey = cursor.getString(0);
cursor.close();
- // AGGREGATES_SUMMARY
- cursor = mRed.resolver.query(Contacts.CONTENT_URI, Projections.PROJ_ID, null,
- null, null);
- while (cursor.moveToNext()) {
- assertTrue("Discovered restricted contact",
- (cursor.getLong(Projections.COL_ID) != greenAgg));
- }
- cursor.close();
+ // Read vCard into buffer
+ final Uri shareUri = Uri.withAppendedPath(Contacts.CONTENT_VCARD_URI, lookupKey);
+ final AssetFileDescriptor file = mRed.resolver.openAssetFileDescriptor(shareUri, "r");
+ final InputStream in = file.createInputStream();
+ final byte[] buf = new byte[in.available()];
+ in.read(buf);
+ in.close();
+ final String card = new String(buf);
+
+ // Make sure that only unrestricted phones appear
+ assertTrue(card.indexOf(PHONE_RED) != -1);
+ assertTrue(card.indexOf(PHONE_GREY) == -1);
+ }
- // AGGREGATES_SUMMARY_ID
- cursor = mRed.resolver.query(ContentUris.withAppendedId(Contacts.CONTENT_URI,
- greenAgg), Projections.PROJ_ID, null, null, null);
- assertTrue("Discovered restricted contact", (cursor.getCount() == 0));
- cursor.close();
+ public void testContactsLiveFolder() {
+ final long greyContact = mGrey.createContact(true, GENERIC_NAME);
+ final long greyPhone = mGrey.createPhone(greyContact, PHONE_GREY);
- // TODO: AGGREGATES_SUMMARY_FILTER
- // TODO: =========================
+ // Protected contact should be omitted from live folder
+ mRed.ensureCallingPackage();
+ final Uri folderUri = Uri.withAppendedPath(ContactsContract.AUTHORITY_URI,
+ "live_folders/contacts_with_phones");
+ final Cursor cursor = mRed.resolver.query(folderUri,
+ new String[] { LiveFolders._ID }, null, null, null);
+ try {
+ while (cursor.moveToNext()) {
+ final long id = cursor.getLong(0);
+ assertFalse(id == greyContact);
+ }
+ } finally {
+ cursor.close();
+ }
+ }
- // TODO: AGGREGATION_SUGGESTIONS
- // TODO: =======================
+ public void testRestrictedQueryParam() throws Exception {
+ final long greyContact = mGrey.createContact(true, GENERIC_NAME);
+ final long greyPhone = mGrey.createPhone(greyContact, PHONE_GREY);
+
+ final Uri greyUri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, greyContact);
+ final Uri redUri = greyUri.buildUpon().appendQueryParameter(
+ ContactsContract.REQUESTING_PACKAGE_PARAM_KEY, mRed.packageName).build();
+
+ // When calling normally, we have access to protected
+ mGrey.ensureCallingPackage();
+ EntityIterator iterator = mGrey.resolver.queryEntities(greyUri, null, null, null);
+ while (iterator.hasNext()) {
+ final Entity entity = iterator.next();
+ final long rawContactId = entity.getEntityValues().getAsLong(RawContacts._ID);
+ assertTrue(rawContactId == greyContact);
+ }
- // CONTACTS
- cursor = mRed.resolver.query(RawContacts.CONTENT_URI, Projections.PROJ_ID,
- null, null, null);
- while (cursor.moveToNext()) {
- assertTrue("Discovered restricted contact",
- (cursor.getLong(Projections.COL_ID) != greenContact));
+ // When calling on behalf of another package, protected is omitted
+ mGrey.ensureCallingPackage();
+ iterator = mGrey.resolver.queryEntities(redUri, null, null, null);
+ while (iterator.hasNext()) {
+ final Entity entity = iterator.next();
+ final long rawContactId = entity.getEntityValues().getAsLong(RawContacts._ID);
+ assertTrue(rawContactId != greyContact);
}
- cursor.close();
+ }
- // CONTACTS_ID
- cursor = mRed.resolver.query(ContentUris
- .withAppendedId(RawContacts.CONTENT_URI, greenContact), Projections.PROJ_ID, null,
- null, null);
- assertTrue("Discovered restricted contact", (cursor.getCount() == 0));
- cursor.close();
+ public void testRestrictedEmailLookupRestricted() {
+ final long greyContact = mGrey.createContact(true, GENERIC_NAME);
+ final long greyEmail = mGrey.createEmail(greyContact, EMAIL_GREY);
- // CONTACTS_DATA
- cursor = mRed.resolver.query(Uri.withAppendedPath(ContentUris.withAppendedId(
- RawContacts.CONTENT_URI, greenContact), RawContacts.Data.CONTENT_DIRECTORY),
- Projections.PROJ_ID, null, null, null);
- assertTrue("Discovered restricted contact", (cursor.getCount() == 0));
- cursor.close();
+ // Restricted caller should see protected data
+ mGrey.ensureCallingPackage();
+ final Uri lookupUri = Uri.withAppendedPath(Email.CONTENT_LOOKUP_URI, EMAIL_GREY);
+ final Cursor cursor = mGrey.resolver.query(lookupUri,
+ new String[] { Data._ID }, null, null, null);
+ try {
+ while (cursor.moveToNext()) {
+ final long dataId = cursor.getLong(0);
+ assertTrue(dataId == greyEmail);
+ }
+ } finally {
+ cursor.close();
+ }
+ }
- // TODO: CONTACTS_FILTER_EMAIL
- // TODO: =====================
+ public void testRestrictedEmailLookupGeneric() {
+ final long greyContact = mGrey.createContact(true, GENERIC_NAME);
+ final long greyEmail = mGrey.createEmail(greyContact, EMAIL_GREY);
- // DATA
- cursor = mRed.resolver.query(Data.CONTENT_URI, Projections.PROJ_ID, null, null, null);
- while (cursor.moveToNext()) {
- assertTrue("Discovered restricted contact",
- (cursor.getLong(Projections.COL_ID) != greenData));
+ // Generic caller should never see protected data
+ mRed.ensureCallingPackage();
+ final Uri lookupUri = Uri.withAppendedPath(Email.CONTENT_LOOKUP_URI, EMAIL_GREY);
+ final Cursor cursor = mRed.resolver.query(lookupUri,
+ new String[] { Data._ID }, null, null, null);
+ try {
+ while (cursor.moveToNext()) {
+ final long dataId = cursor.getLong(0);
+ assertFalse(dataId == greyEmail);
+ }
+ } finally {
+ cursor.close();
}
- cursor.close();
+ }
- // DATA_ID
- cursor = mRed.resolver.query(ContentUris.withAppendedId(Data.CONTENT_URI, greenData),
- Projections.PROJ_ID, null, null, null);
- assertTrue("Discovered restricted contact", (cursor.getCount() == 0));
- cursor.close();
+ public void testStatusRestrictedInsertRestrictedQuery() {
+ final long rawContactId = mGrey.createContactWithStatus(true,
+ GENERIC_NAME, EMAIL_GREY, GENERIC_STATUS);
+ final long aggId = mGrey.getContactForRawContact(rawContactId);
+
+ // Restricted query can read restricted status
+ assertStatus(mGrey, aggId, GENERIC_STATUS);
+ }
- // TODO: PHONE_LOOKUP
- // TODO: ============
+ public void testStatusRestrictedInsertGenericQuery() {
+ final long rawContactId = mGrey.createContactWithStatus(true,
+ GENERIC_NAME, EMAIL_GREY, GENERIC_STATUS);
+ final long aggId = mGrey.getContactForRawContact(rawContactId);
+ // Generic query is denied restricted status
+ assertStatus(mRed, aggId, null);
}
- private interface Projections {
- static final String[] PROJ_ID = new String[] {
- BaseColumns._ID,
- };
+ public void testStatusGenericInsertRestrictedQuery() {
+ final long rawContactId = mRed.createContactWithStatus(false,
+ GENERIC_NAME, EMAIL_RED, GENERIC_STATUS);
+ final long aggId = mRed.getContactForRawContact(rawContactId);
- static final int COL_ID = 0;
+ // Restricted query can read generic status
+ assertStatus(mGrey, aggId, GENERIC_STATUS);
}
+ public void testStatusGenericInsertGenericQuery() {
+ final long rawContactId = mRed.createContactWithStatus(false,
+ GENERIC_NAME, EMAIL_RED, GENERIC_STATUS);
+ final long aggId = mRed.getContactForRawContact(rawContactId);
+
+ // Generic query can read generic status
+ assertStatus(mRed, aggId, GENERIC_STATUS);
+ }
}