Implement unblock API for blocked numbers.

BUG: 27790536
Change-Id: Ic8cd41f54a588b41ebfdce871c4f7961895be029
diff --git a/src/com/android/providers/blockednumber/BlockedNumberProvider.java b/src/com/android/providers/blockednumber/BlockedNumberProvider.java
index 04d463b..a5bccfc 100644
--- a/src/com/android/providers/blockednumber/BlockedNumberProvider.java
+++ b/src/com/android/providers/blockednumber/BlockedNumberProvider.java
@@ -50,6 +50,8 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.providers.blockednumber.BlockedNumberDatabaseHelper.Tables;
 
+import java.util.Arrays;
+
 /**
  * Blocked phone number provider.
  *
@@ -90,6 +92,12 @@
     private static final String ID_SELECTION =
             BlockedNumberContract.BlockedNumbers.COLUMN_ID + "=?";
 
+    private static final String ORIGINAL_NUMBER_SELECTION =
+            BlockedNumberContract.BlockedNumbers.COLUMN_ORIGINAL_NUMBER + "=?";
+
+    private static final String E164_NUMBER_SELECTION =
+            BlockedNumberContract.BlockedNumbers.COLUMN_E164_NUMBER + "=?";
+
     @VisibleForTesting
     protected BlockedNumberDatabaseHelper mDbHelper;
     @VisibleForTesting
@@ -306,6 +314,11 @@
                 res.putBoolean(
                         BlockedNumberContract.RES_CAN_BLOCK_NUMBERS, canCurrentUserBlockUsers());
                 break;
+            case BlockedNumberContract.METHOD_UNBLOCK:
+                enforceWritePermissionAndPrimaryUser();
+
+                res.putInt(BlockedNumberContract.RES_NUM_ROWS_DELETED, unblock(arg));
+                break;
             case SystemContract.METHOD_NOTIFY_EMERGENCY_CONTACT:
                 enforceSystemWritePermissionAndPrimaryUser();
 
@@ -337,6 +350,26 @@
         return res;
     }
 
+    private int unblock(String phoneNumber) {
+        if (TextUtils.isEmpty(phoneNumber)) {
+            return 0;
+        }
+
+        StringBuilder selectionBuilder = new StringBuilder(ORIGINAL_NUMBER_SELECTION);
+        String[] selectionArgs = new String[]{phoneNumber};
+        final String e164Number = Utils.getE164Number(getContext(), phoneNumber, null);
+        if (!TextUtils.isEmpty(e164Number)) {
+            selectionBuilder.append(" or " + E164_NUMBER_SELECTION);
+            selectionArgs = new String[]{phoneNumber, e164Number};
+        }
+        String selection = selectionBuilder.toString();
+        if (DEBUG) {
+            Log.d(TAG, String.format("Unblocking numbers using selection: %s, args: %s",
+                    selection, Arrays.toString(selectionArgs)));
+        }
+        return deleteBlockedNumber(selection, selectionArgs);
+    }
+
     private boolean isEmergencyNumber(String phoneNumber) {
         if (TextUtils.isEmpty(phoneNumber)) {
             return false;
diff --git a/tests/src/com/android/providers/blockednumber/BlockedNumberProviderTest.java b/tests/src/com/android/providers/blockednumber/BlockedNumberProviderTest.java
index 70f5170..19185be 100644
--- a/tests/src/com/android/providers/blockednumber/BlockedNumberProviderTest.java
+++ b/tests/src/com/android/providers/blockednumber/BlockedNumberProviderTest.java
@@ -21,8 +21,8 @@
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import android.app.AppOpsManager;
@@ -32,7 +32,6 @@
 import android.content.pm.PackageManager;
 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;
@@ -355,6 +354,12 @@
         }
 
         try {
+            BlockedNumberContract.unblock(mMockContext, "123");
+            fail("SecurityException expected");
+        } catch (SecurityException expected) {
+        }
+
+        try {
             SystemContract.notifyEmergencyContact(mMockContext);
             fail("SecurityException expected");
         } catch (SecurityException expected) {
@@ -493,6 +498,22 @@
         assertIsBlocked(false, "abcdef@gmail.com");
     }
 
+    public void testUnblock() {
+        insert(cv(BlockedNumbers.COLUMN_ORIGINAL_NUMBER, "+1-500-454-1111"));
+        insert(cv(BlockedNumbers.COLUMN_ORIGINAL_NUMBER, "1500-454-1111"));
+        insert(cv(BlockedNumbers.COLUMN_ORIGINAL_NUMBER, "abc.def@gmail.com"));
+
+        // Unblocking non-existent number is a no-op.
+        assertEquals(0, BlockedNumberContract.unblock(mMockContext, "12345"));
+
+        // Both rows which map to the same E164 number are deleted.
+        assertEquals(2, BlockedNumberContract.unblock(mMockContext, "5004541111"));
+        assertIsBlocked(false, "1-500-454-1111");
+
+        assertEquals(1, BlockedNumberContract.unblock(mMockContext, "abc.def@gmail.com"));
+        assertIsBlocked(false, "abc.def@gmail.com");
+    }
+
     public void testEmergencyNumbersAreNotBlockedBySystem() {
         String emergencyNumber = getEmergencyNumberFromSystemPropertiesOrDefault();
         insert(cv(BlockedNumbers.COLUMN_ORIGINAL_NUMBER, emergencyNumber));
@@ -529,6 +550,12 @@
             fail("UnsupportedOperationException expected");
         } catch (UnsupportedOperationException expected) {
         }
+
+        try {
+            BlockedNumberContract.unblock(mMockContext, "123");
+            fail("UnsupportedOperationException expected");
+        } catch (UnsupportedOperationException expected) {
+        }
     }
 
     public void testRegularAppAccessingApisAsSecondaryUser() {
@@ -561,6 +588,12 @@
             fail("SecurityException expected");
         } catch (SecurityException expected) {
         }
+
+        try {
+            BlockedNumberContract.unblock(mMockContext, "123");
+            fail("SecurityException expected");
+        } catch (SecurityException expected) {
+        }
     }
 
     private void assertIsBlocked(boolean expected, String phoneNumber) {