Update BlockedNumberProvider permission checks as documented in the contract.
BUG: 26232372
Change-Id: I40ccdff1f3820c080d3a6361f17fe6cb6506aa37
diff --git a/src/com/android/providers/blockednumber/BlockedNumberProvider.java b/src/com/android/providers/blockednumber/BlockedNumberProvider.java
index 2774aa6..257756c 100644
--- a/src/com/android/providers/blockednumber/BlockedNumberProvider.java
+++ b/src/com/android/providers/blockednumber/BlockedNumberProvider.java
@@ -22,7 +22,6 @@
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.UriMatcher;
-import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
@@ -32,10 +31,10 @@
import android.os.Bundle;
import android.os.CancellationSignal;
import android.os.Process;
-import android.os.UserHandle;
import android.os.UserManager;
import android.provider.BlockedNumberContract;
import android.telecom.TelecomManager;
+import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.Log;
@@ -150,6 +149,8 @@
@Override
public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection,
@Nullable String[] selectionArgs) {
+ enforceWritePermission();
+
throw new UnsupportedOperationException(
"Update is not supported. Use delete + insert instead");
}
@@ -331,59 +332,56 @@
}
/**
- * Throws {@link SecurityException} when the caller is not root, system, the system dialer,
- * the user selected dialer, or the default SMS app.
+ * Returns {@code false} when the caller is not root, the user selected dialer, the
+ * default SMS app or a carrier app.
*/
- private void enforceReadPermission() {
+ private boolean checkForPrivilegedApplications() {
if (!canCurrentUserBlockUsers()) {
throw new UnsupportedOperationException();
}
- final int callingUid = Binder.getCallingUid();
-
- // System and root can always call it. (and myself)
- if (UserHandle.isSameApp(callingUid, android.os.Process.SYSTEM_UID)
- || (callingUid == Process.ROOT_UID)
- || (callingUid == Process.myUid())) {
- return;
+ if (Binder.getCallingUid() == Process.ROOT_UID) {
+ return true;
}
final String callingPackage = getCallingPackage();
if (TextUtils.isEmpty(callingPackage)) {
Log.w(TAG, "callingPackage not accessible");
} else {
-
final TelecomManager telecom = getContext().getSystemService(TelecomManager.class);
if (callingPackage.equals(telecom.getDefaultDialerPackage())
- || callingPackage.equals(telecom.getSystemDialerPackage())) {
- return;
+ || callingPackage.equals(telecom.getSystemDialerPackage())) {
+ return true;
}
-
- // Allow the default SMS app and the dialer app to access it.
final AppOpsManager appOps = getContext().getSystemService(AppOpsManager.class);
-
if (appOps.noteOp(AppOpsManager.OP_WRITE_SMS,
Binder.getCallingUid(), callingPackage) == AppOpsManager.MODE_ALLOWED) {
- return;
+ return true;
}
- // TODO: Add an explicit permission instead.
- try {
- ApplicationInfo applicationInfo = getContext().
- getPackageManager().getPackageInfo(callingPackage, 0).applicationInfo;
- if (applicationInfo.isPrivilegedApp() || applicationInfo.isSystemApp()) {
- return;
- }
- } catch (PackageManager.NameNotFoundException e) {
- Log.w(TAG, "package not found: " + e);
- }
+ final TelephonyManager telephonyManager =
+ getContext().getSystemService(TelephonyManager.class);
+ return telephonyManager.checkCarrierPrivilegesForPackage(callingPackage) ==
+ TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS;
}
- throw new SecurityException("Caller must be system, default dialer or default SMS app");
+ return false;
}
- public void enforceWritePermission() {
- // Same check as read.
- enforceReadPermission();
+ private void enforceReadPermission() {
+ checkForPermission(android.Manifest.permission.READ_BLOCKED_NUMBERS);
+ }
+
+ private void enforceWritePermission() {
+ checkForPermission(android.Manifest.permission.WRITE_BLOCKED_NUMBERS);
+ }
+
+ private void checkForPermission(String permission) {
+ boolean permitted = getContext().checkCallingPermission(permission)
+ == PackageManager.PERMISSION_GRANTED
+ || checkForPrivilegedApplications();
+ if (!permitted) {
+ throw new SecurityException("Caller must be system, default dialer or default SMS app");
+ }
}
}
diff --git a/tests/src/com/android/providers/blockednumber/BlockedNumberProviderTest.java b/tests/src/com/android/providers/blockednumber/BlockedNumberProviderTest.java
index 0ceb684..868735b 100644
--- a/tests/src/com/android/providers/blockednumber/BlockedNumberProviderTest.java
+++ b/tests/src/com/android/providers/blockednumber/BlockedNumberProviderTest.java
@@ -15,48 +15,52 @@
*/
package com.android.providers.blockednumber;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.when;
+
+import android.app.AppOpsManager;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.ContentValues;
-import android.content.Context;
import android.database.ContentObserver;
import android.database.Cursor;
import android.database.sqlite.SQLiteConstraintException;
import android.database.sqlite.SQLiteException;
+import android.location.Country;
import android.net.Uri;
import android.provider.BlockedNumberContract;
import android.provider.BlockedNumberContract.BlockedNumbers;
import android.test.AndroidTestCase;
import android.test.MoreAsserts;
+
import junit.framework.Assert;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
/**
- * m BlockedNumberProviderTest && adb install -r
- * ${ANDROID_PRODUCT_OUT}/data/app/BlockedNumberProviderTest/BlockedNumberProviderTest.apk && adb
- * shell am instrument -e class com.android.providers.blockednumber.BlockedNumberProviderTest \ -w
- * com.android.providers.blockednumber.tests/android.support.test.runner.AndroidJUnitRunner
+ * m BlockedNumberProviderTest && runtest --path packages/providers/BlockedNumberProvider/tests
*/
public class BlockedNumberProviderTest extends AndroidTestCase {
- private Context mRealTestContext;
private MyMockContext mMockContext;
private ContentResolver mResolver;
- /**
- * Whether the country detector thinks the device is in USA.
- */
- private boolean mInUSA;
-
@Override
protected void setUp() throws Exception {
super.setUp();
- mRealTestContext = getContext();
- mMockContext = new MyMockContext(mRealTestContext);
+ mMockContext = new MyMockContext();
+ mMockContext.init();
mResolver = mMockContext.getContentResolver();
- mInUSA = "US".equals(Utils.getCurrentCountryIso(mRealTestContext));
+
+ when(mMockContext.mUserManager.isPrimaryUser()).thenReturn(true);
+ when(mMockContext.mCountryDetector.detectCountry())
+ .thenReturn(new Country("US", Country.COUNTRY_SOURCE_LOCATION));
+ when(mMockContext.mAppOpsManager.noteOp(
+ eq(AppOpsManager.OP_WRITE_SMS), anyInt(), anyString()))
+ .thenReturn(AppOpsManager.MODE_ERRORED);
}
@Override
@@ -253,11 +257,8 @@
assertIsBlocked(true, "045-111-2222");
assertIsBlocked(false, "045 111 2222");
- if (mInUSA) {
- // Probably won't work outside of the +1 region.
- assertIsBlocked(true, "500-454 1111");
- assertIsBlocked(true, "500-454 2222");
- }
+ assertIsBlocked(true, "500-454 1111");
+ assertIsBlocked(true, "500-454 2222");
assertIsBlocked(true, "+1 500-454 1111");
assertIsBlocked(true, "1 500-454 1111");
diff --git a/tests/src/com/android/providers/blockednumber/MyMockContext.java b/tests/src/com/android/providers/blockednumber/MyMockContext.java
index 14d4f84..47fadef 100644
--- a/tests/src/com/android/providers/blockednumber/MyMockContext.java
+++ b/tests/src/com/android/providers/blockednumber/MyMockContext.java
@@ -15,57 +15,103 @@
*/
package com.android.providers.blockednumber;
+import android.app.AppOpsManager;
import android.content.ContentResolver;
import android.content.Context;
+import android.content.pm.PackageManager;
import android.content.pm.ProviderInfo;
import android.location.CountryDetector;
+import android.os.UserManager;
import android.provider.BlockedNumberContract;
+import android.telecom.TelecomManager;
+import android.telephony.TelephonyManager;
import android.test.mock.MockContentResolver;
import android.test.mock.MockContext;
+
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.util.HashMap;
+
public class MyMockContext extends MockContext {
- public final Context realTestContext;
-
@Mock
- CountryDetector countryDetector;
+ CountryDetector mCountryDetector;
+ @Mock
+ AppOpsManager mAppOpsManager;
+ @Mock
+ UserManager mUserManager;
+ @Mock
+ TelecomManager mTelecomManager;
+ @Mock
+ TelephonyManager mTelephonyManager;
- MockContentResolver resolver;
+ private final HashMap<Class<?>, String> mSupportedServiceNamesByClass =
+ new HashMap<Class<?>, String>();
+ private MockContentResolver mResolver;
+ private BlockedNumberProviderTestable mProvider;
- BlockedNumberProviderTestable provider;
-
- public MyMockContext(Context realTestContext) {
- this.realTestContext = realTestContext;
- MockitoAnnotations.initMocks(this);
-
- resolver = new MockContentResolver();
-
- provider = new BlockedNumberProviderTestable();
-
- final ProviderInfo info = new ProviderInfo();
- info.authority = BlockedNumberContract.AUTHORITY;
- provider.attachInfoForTesting(realTestContext, info);
-
- resolver.addProvider(BlockedNumberContract.AUTHORITY, provider);
+ @Override
+ public String getSystemServiceName(Class<?> serviceClass) {
+ if (mSupportedServiceNamesByClass.containsKey(serviceClass)) {
+ return mSupportedServiceNamesByClass.get(serviceClass);
+ }
+ throw new UnsupportedOperationException();
}
@Override
public Object getSystemService(String name) {
switch (name) {
case Context.COUNTRY_DETECTOR:
- return countryDetector;
+ return mCountryDetector;
+ case Context.APP_OPS_SERVICE:
+ return mAppOpsManager;
+ case Context.USER_SERVICE:
+ return mUserManager;
+ case Context.TELECOM_SERVICE:
+ return mTelecomManager;
+ case Context.TELEPHONY_SERVICE:
+ return mTelephonyManager;
}
- throw new UnsupportedOperationException();
+ throw new UnsupportedOperationException("Service not supported: " + name);
}
@Override
public ContentResolver getContentResolver() {
- return resolver;
+ return mResolver;
+ }
+
+ @Override
+ public int checkCallingPermission(String permission) {
+ return permission != null && (permission.equals("android.permission.READ_BLOCKED_NUMBERS")
+ || permission.equals("android.permission.WRITE_BLOCKED_NUMBERS"))
+ ? PackageManager.PERMISSION_GRANTED : PackageManager.PERMISSION_DENIED;
+ }
+
+ public void init() {
+ registerServices();
+ mResolver = new MockContentResolver();
+
+ mProvider = new BlockedNumberProviderTestable();
+
+ final ProviderInfo info = new ProviderInfo();
+ info.authority = BlockedNumberContract.AUTHORITY;
+ mProvider.attachInfoForTesting(this, info);
+
+ mResolver.addProvider(BlockedNumberContract.AUTHORITY, mProvider);
+ }
+
+ private void registerServices() {
+ MockitoAnnotations.initMocks(this);
+
+ mSupportedServiceNamesByClass.put(CountryDetector.class, Context.COUNTRY_DETECTOR);
+ mSupportedServiceNamesByClass.put(AppOpsManager.class, Context.APP_OPS_SERVICE);
+ mSupportedServiceNamesByClass.put(UserManager.class, Context.USER_SERVICE);
+ mSupportedServiceNamesByClass.put(TelecomManager.class, Context.TELECOM_SERVICE);
+ mSupportedServiceNamesByClass.put(TelephonyManager.class, Context.TELEPHONY_SERVICE);
}
public void shutdown() {
- provider.shutdown();
+ mProvider.shutdown();
}
}