Changes to BlockedNumberProvider:

1. Remove STRIPPED_PHONE_NUMBER column.
2. Temporarily permit system apps to access provider.
3. Notify changes to content so that ListViews work well with the provider.
4. Use DE storage.

BUG: 26232372
Change-Id: Ib7fad2c3971a7b7b74c86c46fe3514330c22334c
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index d9bd2c9..dd8522c 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -18,17 +18,12 @@
         package="com.android.providers.blockednumber"
         android:sharedUserId="android.uid.shared">
 
-<!--
-    TODO: Make it DE. Add the following to <application>.  See go/android-fbe-apis
-        android:encryptionAware=”true”
-        android:forceDeviceEncrypted=”true”
--->
-
     <application
         android:process="android.process.acore"
         android:label="@string/app_label"
-        android:allowBackup="false"
-        android:usesCleartextTraffic="false">
+        android:usesCleartextTraffic="false"
+        android:encryptionAware="true"
+        android:forceDeviceEncrypted="true">
 
         <provider android:name="BlockedNumberProvider"
             android:authorities="com.android.blockednumber"
diff --git a/src/com/android/providers/blockednumber/BlockedNumberDatabaseHelper.java b/src/com/android/providers/blockednumber/BlockedNumberDatabaseHelper.java
index 005c918..ac3e94d 100644
--- a/src/com/android/providers/blockednumber/BlockedNumberDatabaseHelper.java
+++ b/src/com/android/providers/blockednumber/BlockedNumberDatabaseHelper.java
@@ -24,7 +24,7 @@
 import com.android.internal.util.Preconditions;
 
 public class BlockedNumberDatabaseHelper {
-    private static final int DATABASE_VERSION = 1;
+    private static final int DATABASE_VERSION = 2;
 
     private static final String DATABASE_NAME = "blockednumbers.db";
 
@@ -46,22 +46,29 @@
 
         @Override
         public void onCreate(SQLiteDatabase db) {
-            db.execSQL("CREATE TABLE " + Tables.BLOCKED_NUMBERS + " (" +
-                    BlockedNumbers.COLUMN_ID + " INTEGER PRIMARY KEY AUTOINCREMENT," +
-                    BlockedNumbers.COLUMN_ORIGINAL_NUMBER + " TEXT NOT NULL UNIQUE," +
-                    BlockedNumbers.COLUMN_STRIPPED_NUMBER + " TEXT NOT NULL," +
-                    BlockedNumbers.COLUMN_E164_NUMBER + " TEXT" +
-                    ")");
-
-            db.execSQL("CREATE INDEX blocked_number_idx_stripped ON " + Tables.BLOCKED_NUMBERS +
-                    " (" + BlockedNumbers.COLUMN_STRIPPED_NUMBER + ");");
-            db.execSQL("CREATE INDEX blocked_number_idx_e164 ON " + Tables.BLOCKED_NUMBERS + " (" +
-                    BlockedNumbers.COLUMN_E164_NUMBER +
-                    ");");
+            createTables(db);
         }
 
         @Override
         public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
+            if (oldVersion < 2) {
+                db.execSQL("DROP TABLE IF EXISTS blocked");
+                createTables(db);
+            }
+        }
+
+        private void createTables(SQLiteDatabase db) {
+            db.execSQL("CREATE TABLE " + Tables.BLOCKED_NUMBERS + " (" +
+                    BlockedNumbers.COLUMN_ID + " INTEGER PRIMARY KEY AUTOINCREMENT," +
+                    BlockedNumbers.COLUMN_ORIGINAL_NUMBER + " TEXT NOT NULL UNIQUE," +
+                    BlockedNumbers.COLUMN_E164_NUMBER + " TEXT" +
+                    ")");
+
+            db.execSQL("CREATE INDEX blocked_number_idx_original ON " + Tables.BLOCKED_NUMBERS +
+                    " (" + BlockedNumbers.COLUMN_ORIGINAL_NUMBER + ");");
+            db.execSQL("CREATE INDEX blocked_number_idx_e164 ON " + Tables.BLOCKED_NUMBERS + " (" +
+                    BlockedNumbers.COLUMN_E164_NUMBER +
+                    ");");
         }
     }
 
diff --git a/src/com/android/providers/blockednumber/BlockedNumberProvider.java b/src/com/android/providers/blockednumber/BlockedNumberProvider.java
index 72a0c93..2c8cffe 100644
--- a/src/com/android/providers/blockednumber/BlockedNumberProvider.java
+++ b/src/com/android/providers/blockednumber/BlockedNumberProvider.java
@@ -19,6 +19,8 @@
 import android.annotation.Nullable;
 import android.app.AppOpsManager;
 import android.content.*;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
 import android.database.Cursor;
 import android.database.sqlite.SQLiteDatabase;
 import android.database.sqlite.SQLiteQueryBuilder;
@@ -58,7 +60,6 @@
     private static final ProjectionMap sBlockedNumberColumns = ProjectionMap.builder()
             .add(BlockedNumberContract.BlockedNumbers.COLUMN_ID)
             .add(BlockedNumberContract.BlockedNumbers.COLUMN_ORIGINAL_NUMBER)
-            .add(BlockedNumberContract.BlockedNumbers.COLUMN_STRIPPED_NUMBER)
             .add(BlockedNumberContract.BlockedNumbers.COLUMN_E164_NUMBER)
             .build();
 
@@ -112,7 +113,9 @@
         final int match = sUriMatcher.match(uri);
         switch (match) {
             case BLOCKED_LIST:
-                return insertBlockedNumber(values);
+                Uri blockedUri = insertBlockedNumber(values);
+                getContext().getContentResolver().notifyChange(blockedUri, null);
+                return blockedUri;
             default:
                 throw new IllegalArgumentException("Unsupported URI: " + uri);
         }
@@ -123,7 +126,6 @@
      */
     private Uri insertBlockedNumber(ContentValues cv) {
         throwIfSpecified(cv, BlockedNumberContract.BlockedNumbers.COLUMN_ID);
-        throwIfSpecified(cv, BlockedNumberContract.BlockedNumbers.COLUMN_STRIPPED_NUMBER);
 
         final String phoneNumber = cv.getAsString(
                 BlockedNumberContract.BlockedNumbers.COLUMN_ORIGINAL_NUMBER);
@@ -133,14 +135,15 @@
                     BlockedNumberContract.BlockedNumbers.COLUMN_ORIGINAL_NUMBER);
         }
 
-        // Sanitize the input and fill in with autogenerated columns.
-        final String strippedNumber = Utils.stripPhoneNumber(phoneNumber);
-        final String e164Number = Utils.getE164Number(getContext(), strippedNumber,
+        // Fill in with autogenerated columns.
+        final String e164Number = Utils.getE164Number(getContext(), phoneNumber,
                 cv.getAsString(BlockedNumberContract.BlockedNumbers.COLUMN_E164_NUMBER));
-
-        cv.put(BlockedNumberContract.BlockedNumbers.COLUMN_STRIPPED_NUMBER, strippedNumber);
         cv.put(BlockedNumberContract.BlockedNumbers.COLUMN_E164_NUMBER, e164Number);
 
+        if (DEBUG) {
+            Log.d(TAG, String.format("inserted blocked number: %s", cv));
+        }
+
         // Then insert.
         final long id = mDbHelper.getWritableDatabase().insertOrThrow(
                 BlockedNumberDatabaseHelper.Tables.BLOCKED_NUMBERS, null, cv);
@@ -176,14 +179,19 @@
         enforceWritePermission();
 
         final int match = sUriMatcher.match(uri);
+        int numRows;
         switch (match) {
             case BLOCKED_LIST:
-                return deleteBlockedNumber(selection, selectionArgs);
+                numRows = deleteBlockedNumber(selection, selectionArgs);
+                break;
             case BLOCKED_ID:
-                return deleteBlockedNumberWithId(ContentUris.parseId(uri), selection);
+                numRows = deleteBlockedNumberWithId(ContentUris.parseId(uri), selection);
+                break;
             default:
                 throw new IllegalArgumentException("Unsupported URI: " + uri);
         }
+        getContext().getContentResolver().notifyChange(uri, null);
+        return numRows;
     }
 
     /**
@@ -228,16 +236,22 @@
         enforceReadPermission();
 
         final int match = sUriMatcher.match(uri);
+        Cursor cursor;
         switch (match) {
             case BLOCKED_LIST:
-                return queryBlockedList(projection, selection, selectionArgs, sortOrder,
+                cursor = queryBlockedList(projection, selection, selectionArgs, sortOrder,
                         cancellationSignal);
+                break;
             case BLOCKED_ID:
-                return queryBlockedListWithId(ContentUris.parseId(uri), projection, selection,
+                cursor = queryBlockedListWithId(ContentUris.parseId(uri), projection, selection,
                         cancellationSignal);
+                break;
             default:
                 throw new IllegalArgumentException("Unsupported URI: " + uri);
         }
+        // Tell the cursor what uri to watch, so it knows when its source data changes
+        cursor.setNotificationUri(getContext().getContentResolver(), uri);
+        return cursor;
     }
 
     /**
@@ -295,38 +309,33 @@
     }
 
     private boolean isBlocked(String phoneNumber) {
-        final String inStripped = Utils.stripPhoneNumber(phoneNumber);
-        if (TextUtils.isEmpty(inStripped)) {
+        if (TextUtils.isEmpty(phoneNumber)) {
             return false;
         }
 
-        final String inE164 = Utils.getE164Number(getContext(), inStripped, null); // may be empty.
+        final String inE164 = Utils.getE164Number(getContext(), phoneNumber, null); // may be empty.
 
         if (DEBUG) {
-            Log.d(TAG, String.format("isBlocked: in=%s, stripped=%s, e164=%s", phoneNumber,
-                    inStripped, inE164));
+            Log.d(TAG, String.format("isBlocked: in=%s, e164=%s", phoneNumber, inE164));
         }
 
         final Cursor c = mDbHelper.getReadableDatabase().rawQuery(
                 "SELECT " +
                 BlockedNumberContract.BlockedNumbers.COLUMN_ORIGINAL_NUMBER + "," +
-                BlockedNumberContract.BlockedNumbers.COLUMN_STRIPPED_NUMBER + "," +
                 BlockedNumberContract.BlockedNumbers.COLUMN_E164_NUMBER +
                 " FROM " + BlockedNumberDatabaseHelper.Tables.BLOCKED_NUMBERS +
-                " WHERE " + BlockedNumberContract.BlockedNumbers.COLUMN_STRIPPED_NUMBER + "=?1" +
+                " WHERE " + BlockedNumberContract.BlockedNumbers.COLUMN_ORIGINAL_NUMBER + "=?1" +
                 " OR (?2 != '' AND " +
                         BlockedNumberContract.BlockedNumbers.COLUMN_E164_NUMBER + "=?2)",
-                new String[] {inStripped, inE164}
+                new String[] {phoneNumber, inE164}
                 );
         try {
             while (c.moveToNext()) {
                 if (DEBUG) {
                     final String original = c.getString(0);
-                    final String stripped = c.getString(1);
-                    final String e164 = c.getString(2);
+                    final String e164 = c.getString(1);
 
-                    Log.d(TAG, String.format("  match found: original=%s, stripped=%s, e164=%s",
-                            original, stripped, e164));
+                    Log.d(TAG, String.format("match found: original=%s, e164=%s", original, e164));
                 }
                 return true;
             }
@@ -378,6 +387,17 @@
                     Binder.getCallingUid(), callingPackage) == AppOpsManager.MODE_ALLOWED) {
                 return;
             }
+
+            // 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);
+            }
         }
         throw new SecurityException("Caller must be system, default dialer or default SMS app");
     }
diff --git a/src/com/android/providers/blockednumber/Utils.java b/src/com/android/providers/blockednumber/Utils.java
index e890634..6aaf178 100644
--- a/src/com/android/providers/blockednumber/Utils.java
+++ b/src/com/android/providers/blockednumber/Utils.java
@@ -48,23 +48,6 @@
     }
 
     /**
-     * Strip formatting characters and the non-phone number portion from a phone number.  e.g.
-     * "+1-408-123-4444;123" to "+14081234444".
-     *
-     * <p>Special case: if a number contains '@', it's considered as an email address and returned
-     * unmodified.
-     */
-    public static @NonNull String stripPhoneNumber(@Nullable String phoneNumber) {
-        if (TextUtils.isEmpty(phoneNumber)) {
-            return "";
-        }
-        if (phoneNumber.contains("@")) {
-            return phoneNumber;
-        }
-        return PhoneNumberUtils.extractNetworkPortion(phoneNumber);
-    }
-
-    /**
      * Converts a phone number to an E164 number, assuming the current country.  If {@code
      * incomingE16Number} is provided, it'll just strip it and returns.  If the number is not valid,
      * it'll return "".
@@ -78,7 +61,7 @@
             return rawNumber;
         }
         if (!TextUtils.isEmpty(incomingE16Number)) {
-            return stripPhoneNumber(incomingE16Number);
+            return incomingE16Number;
         }
         if (TextUtils.isEmpty(rawNumber)) {
             return "";
diff --git a/tests/src/com/android/providers/blockednumber/BlockedNumberProviderTest.java b/tests/src/com/android/providers/blockednumber/BlockedNumberProviderTest.java
index fb9a81a..0ceb684 100644
--- a/tests/src/com/android/providers/blockednumber/BlockedNumberProviderTest.java
+++ b/tests/src/com/android/providers/blockednumber/BlockedNumberProviderTest.java
@@ -19,6 +19,7 @@
 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;
@@ -29,6 +30,9 @@
 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
@@ -40,7 +44,9 @@
     private MyMockContext mMockContext;
     private ContentResolver mResolver;
 
-    /** Whether the country detector thinks the device is in USA. */
+    /**
+     * Whether the country detector thinks the device is in USA.
+     */
     private boolean mInUSA;
 
     @Override
@@ -66,7 +72,7 @@
         final ContentValues ret = new ContentValues();
         for (int i = 1; i < namesAndValues.length; i += 2) {
             final String name = namesAndValues[i - 1].toString();
-            final Object value =  namesAndValues[i];
+            final Object value = namesAndValues[i];
             if (value == null) {
                 ret.putNull(name);
             } else if (value instanceof String) {
@@ -104,7 +110,6 @@
         insertExpectingFailure(cv(BlockedNumbers.COLUMN_ORIGINAL_NUMBER, null));
         insertExpectingFailure(cv(BlockedNumbers.COLUMN_ORIGINAL_NUMBER, ""));
         insertExpectingFailure(cv(BlockedNumbers.COLUMN_ID, 1));
-        insertExpectingFailure(cv(BlockedNumbers.COLUMN_STRIPPED_NUMBER, 1));
         insertExpectingFailure(cv(BlockedNumbers.COLUMN_E164_NUMBER, "1"));
 
         insert(cv(BlockedNumbers.COLUMN_ORIGINAL_NUMBER, "123"));
@@ -128,6 +133,30 @@
         // TODO Check the table content.
     }
 
+    public void testChangesNotified() throws Exception {
+        Cursor c = mResolver.query(BlockedNumbers.CONTENT_URI, null, null, null, null);
+
+        final CountDownLatch latch = new CountDownLatch(2);
+        ContentObserver contentObserver = new ContentObserver(null) {
+            @Override
+            public void onChange(boolean selfChange) {
+                Assert.assertFalse(selfChange);
+                latch.notify();
+            }
+        };
+        c.registerContentObserver(contentObserver);
+
+        try {
+            Uri uri = insert(cv(BlockedNumbers.COLUMN_ORIGINAL_NUMBER, "14506507000"));
+            mResolver.delete(uri, null, null);
+            latch.await(10, TimeUnit.SECONDS);
+        } catch (Exception e) {
+            fail(e.toString());
+        } finally {
+            c.unregisterContentObserver(contentObserver);
+        }
+    }
+
     private Uri insert(ContentValues cv) {
         final Uri uri = mResolver.insert(BlockedNumbers.CONTENT_URI, cv);
         assertNotNull(uri);
@@ -210,16 +239,19 @@
         insert(cv(BlockedNumbers.COLUMN_ORIGINAL_NUMBER, "1-500-454-2222"));
 
         insert(cv(BlockedNumbers.COLUMN_ORIGINAL_NUMBER, "045-111-2222",
-                BlockedNumbers.COLUMN_E164_NUMBER, "+81-45-111-2222"));
+                BlockedNumbers.COLUMN_E164_NUMBER, "+81451112222"));
 
         insert(cv(BlockedNumbers.COLUMN_ORIGINAL_NUMBER, "abc.def@gmail.com"));
 
         // Check
+        assertIsBlocked(false, "");
+        assertIsBlocked(false, null);
         assertIsBlocked(true, "123");
         assertIsBlocked(false, "1234");
         assertIsBlocked(true, "+81451112222");
         assertIsBlocked(true, "+81 45 111 2222");
-        assertIsBlocked(true, "045 111 2222");
+        assertIsBlocked(true, "045-111-2222");
+        assertIsBlocked(false, "045 111 2222");
 
         if (mInUSA) {
             // Probably won't work outside of the +1 region.