Support enhanced call blocking function

- Add new method to get/set the call blocking enabled status.
- Improve the method of checking whether a number is block number
by supporting extras.

Bug: 28189985
Test: Manual
Change-Id: I22c40827390417b22ea40513cc0eb28798765764
diff --git a/src/com/android/providers/blockednumber/BlockedNumberProvider.java b/src/com/android/providers/blockednumber/BlockedNumberProvider.java
index 6d96188..44a5f84 100644
--- a/src/com/android/providers/blockednumber/BlockedNumberProvider.java
+++ b/src/com/android/providers/blockednumber/BlockedNumberProvider.java
@@ -35,6 +35,7 @@
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.CancellationSignal;
+import android.os.PersistableBundle;
 import android.os.Process;
 import android.os.UserManager;
 import android.provider.BlockedNumberContract;
@@ -72,6 +73,7 @@
     private static final String BLOCK_SUPPRESSION_EXPIRY_TIME_PREF =
             "block_suppression_expiry_time_pref";
     private static final int MAX_BLOCKING_DISABLED_DURATION_SECONDS = 7 * 24 * 3600; // 1 week
+    private static final long BLOCKING_DISABLED_FOREVER = -1;
     // Normally, we allow calls from self, *except* in unit tests, where we clear this flag
     // to emulate calls from other apps.
     @VisibleForTesting
@@ -339,8 +341,30 @@
                 break;
             case SystemContract.METHOD_SHOULD_SYSTEM_BLOCK_NUMBER:
                 enforceSystemReadPermissionAndPrimaryUser();
-                res.putBoolean(
-                        BlockedNumberContract.RES_NUMBER_IS_BLOCKED, shouldSystemBlockNumber(arg));
+                res.putBoolean(BlockedNumberContract.RES_NUMBER_IS_BLOCKED,
+                        shouldSystemBlockNumber(arg, extras));
+                break;
+            case SystemContract.METHOD_SHOULD_SHOW_EMERGENCY_CALL_NOTIFICATION:
+                enforceSystemReadPermissionAndPrimaryUser();
+                res.putBoolean(BlockedNumberContract.RES_SHOW_EMERGENCY_CALL_NOTIFICATION,
+                        shouldShowEmergencyCallNotification());
+                break;
+            case SystemContract.METHOD_GET_ENHANCED_BLOCK_SETTING:
+                enforceSystemReadPermissionAndPrimaryUser();
+                if (extras != null) {
+                    String key = extras.getString(BlockedNumberContract.EXTRA_ENHANCED_SETTING_KEY);
+                    boolean value = getEnhancedBlockSetting(key);
+                    res.putBoolean(BlockedNumberContract.RES_ENHANCED_SETTING_IS_ENABLED, value);
+                }
+                break;
+            case SystemContract.METHOD_SET_ENHANCED_BLOCK_SETTING:
+                enforceSystemWritePermissionAndPrimaryUser();
+                if (extras != null) {
+                    String key = extras.getString(BlockedNumberContract.EXTRA_ENHANCED_SETTING_KEY);
+                    boolean value = extras.getBoolean(
+                            BlockedNumberContract.EXTRA_ENHANCED_SETTING_VALUE, false);
+                    setEnhancedBlockSetting(key, value);
+                }
                 break;
             default:
                 enforceReadPermissionAndPrimaryUser();
@@ -424,8 +448,11 @@
     }
 
     private void notifyEmergencyContact() {
-        writeBlockSuppressionExpiryTimePref(System.currentTimeMillis() +
-                getBlockSuppressSecondsFromCarrierConfig() * 1000);
+        long sec = getBlockSuppressSecondsFromCarrierConfig();
+        long millisToWrite = sec < 0
+                ? BLOCKING_DISABLED_FOREVER : System.currentTimeMillis() + (sec * 1000);
+        writeBlockSuppressionExpiryTimePref(millisToWrite);
+        writeEmergencyCallNotificationPref(true);
         notifyBlockSuppressionStateChange();
     }
 
@@ -433,6 +460,7 @@
         // Nothing to do if blocks are not being suppressed.
         if (getBlockSuppressionStatus().isSuppressed) {
             writeBlockSuppressionExpiryTimePref(0);
+            writeEmergencyCallNotificationPref(false);
             notifyBlockSuppressionStateChange();
         }
     }
@@ -440,18 +468,94 @@
     private SystemContract.BlockSuppressionStatus getBlockSuppressionStatus() {
         SharedPreferences pref = getContext().getSharedPreferences(PREF_FILE, Context.MODE_PRIVATE);
         long blockSuppressionExpiryTimeMillis = pref.getLong(BLOCK_SUPPRESSION_EXPIRY_TIME_PREF, 0);
-        return new SystemContract.BlockSuppressionStatus(System.currentTimeMillis() <
-                blockSuppressionExpiryTimeMillis, blockSuppressionExpiryTimeMillis);
+        boolean isSuppressed = blockSuppressionExpiryTimeMillis == BLOCKING_DISABLED_FOREVER
+                || System.currentTimeMillis() < blockSuppressionExpiryTimeMillis;
+        return new SystemContract.BlockSuppressionStatus(isSuppressed,
+                blockSuppressionExpiryTimeMillis);
     }
 
-    private boolean shouldSystemBlockNumber(String phoneNumber) {
+    private boolean shouldSystemBlockNumber(String phoneNumber, Bundle extras) {
         if (getBlockSuppressionStatus().isSuppressed) {
             return false;
         }
         if (isEmergencyNumber(phoneNumber)) {
             return false;
         }
-        return isBlocked(phoneNumber);
+
+        boolean isBlocked = false;
+        if (extras != null && !extras.isEmpty()) {
+            // check enhanced blocking setting
+            boolean contactExist = extras.getBoolean(BlockedNumberContract.EXTRA_CONTACT_EXIST);
+            int presentation = extras.getInt(BlockedNumberContract.EXTRA_CALL_PRESENTATION);
+            switch (presentation) {
+                case TelecomManager.PRESENTATION_ALLOWED:
+                    isBlocked = getEnhancedBlockSetting(
+                            SystemContract.ENHANCED_SETTING_KEY_BLOCK_UNREGISTERED)
+                                    && !contactExist;
+                    break;
+                case TelecomManager.PRESENTATION_RESTRICTED:
+                    isBlocked = getEnhancedBlockSetting(
+                            SystemContract.ENHANCED_SETTING_KEY_BLOCK_PRIVATE);
+                    break;
+                case TelecomManager.PRESENTATION_PAYPHONE:
+                    isBlocked = getEnhancedBlockSetting(
+                            SystemContract.ENHANCED_SETTING_KEY_BLOCK_PAYPHONE);
+                    break;
+                case TelecomManager.PRESENTATION_UNKNOWN:
+                    isBlocked = getEnhancedBlockSetting(
+                            SystemContract.ENHANCED_SETTING_KEY_BLOCK_UNKNOWN);
+                    break;
+                default:
+                    break;
+            }
+        }
+        return isBlocked || isBlocked(phoneNumber);
+    }
+
+    private boolean shouldShowEmergencyCallNotification() {
+        return isEnhancedCallBlockingEnabledByPlatform()
+                && isAnyEnhancedBlockingSettingEnabled()
+                && getBlockSuppressionStatus().isSuppressed
+                && getEnhancedBlockSetting(
+                        SystemContract.ENHANCED_SETTING_KEY_SHOW_EMERGENCY_CALL_NOTIFICATION);
+    }
+
+    private boolean isEnhancedCallBlockingEnabledByPlatform() {
+        CarrierConfigManager configManager = (CarrierConfigManager) getContext().getSystemService(
+                Context.CARRIER_CONFIG_SERVICE);
+        PersistableBundle carrierConfig = configManager.getConfig();
+        if (carrierConfig == null) {
+            carrierConfig = configManager.getDefaultConfig();
+        }
+        return carrierConfig.getBoolean(
+                CarrierConfigManager.KEY_SUPPORT_ENHANCED_CALL_BLOCKING_BOOL);
+    }
+
+    private boolean isAnyEnhancedBlockingSettingEnabled() {
+        return getEnhancedBlockSetting(SystemContract.ENHANCED_SETTING_KEY_BLOCK_UNREGISTERED)
+                || getEnhancedBlockSetting(SystemContract.ENHANCED_SETTING_KEY_BLOCK_PRIVATE)
+                || getEnhancedBlockSetting(SystemContract.ENHANCED_SETTING_KEY_BLOCK_PAYPHONE)
+                || getEnhancedBlockSetting(SystemContract.ENHANCED_SETTING_KEY_BLOCK_UNKNOWN);
+    }
+
+    private boolean getEnhancedBlockSetting(String key) {
+        SharedPreferences pref = getContext().getSharedPreferences(PREF_FILE, Context.MODE_PRIVATE);
+        return pref.getBoolean(key, false);
+    }
+
+    private void setEnhancedBlockSetting(String key, boolean value) {
+        SharedPreferences pref = getContext().getSharedPreferences(PREF_FILE, Context.MODE_PRIVATE);
+        SharedPreferences.Editor editor = pref.edit();
+        editor.putBoolean(key, value);
+        editor.apply();
+    }
+
+    private void writeEmergencyCallNotificationPref(boolean show) {
+        if (!isEnhancedCallBlockingEnabledByPlatform()) {
+            return;
+        }
+        setEnhancedBlockSetting(
+                SystemContract.ENHANCED_SETTING_KEY_SHOW_EMERGENCY_CALL_NOTIFICATION, show);
     }
 
     private void writeBlockSuppressionExpiryTimePref(long expiryTimeMillis) {
@@ -466,8 +570,7 @@
                 getContext().getSystemService(CarrierConfigManager.class);
         int carrierConfigValue = carrierConfigManager.getConfig().getInt
                 (CarrierConfigManager.KEY_DURATION_BLOCKING_DISABLED_AFTER_EMERGENCY_INT);
-        boolean isValidValue = carrierConfigValue >=0 && carrierConfigValue <=
-                MAX_BLOCKING_DISABLED_DURATION_SECONDS;
+        boolean isValidValue = carrierConfigValue <= MAX_BLOCKING_DISABLED_DURATION_SECONDS;
         return isValidValue ? carrierConfigValue : CarrierConfigManager.getDefaultConfig().getInt(
                 CarrierConfigManager.KEY_DURATION_BLOCKING_DISABLED_AFTER_EMERGENCY_INT);
     }
diff --git a/tests/src/com/android/providers/blockednumber/BlockedNumberProviderTest.java b/tests/src/com/android/providers/blockednumber/BlockedNumberProviderTest.java
index 6c703b8..91d2430 100644
--- a/tests/src/com/android/providers/blockednumber/BlockedNumberProviderTest.java
+++ b/tests/src/com/android/providers/blockednumber/BlockedNumberProviderTest.java
@@ -35,11 +35,13 @@
 import android.database.sqlite.SQLiteException;
 import android.location.Country;
 import android.net.Uri;
+import android.os.Bundle;
 import android.os.PersistableBundle;
 import android.os.SystemProperties;
 import android.provider.BlockedNumberContract;
 import android.provider.BlockedNumberContract.BlockedNumbers;
 import android.provider.BlockedNumberContract.SystemContract;
+import android.telecom.TelecomManager;
 import android.telephony.CarrierConfigManager;
 import android.telephony.TelephonyManager;
 import android.test.AndroidTestCase;
@@ -267,14 +269,14 @@
 
         // No emergency contact: Blocks should not be suppressed.
         assertIsBlocked(true, phoneNumber);
-        assertShouldSystemBlock(true, phoneNumber);
+        assertShouldSystemBlock(true, phoneNumber, null);
         verifyBlocksNotSuppressed();
         assertTrue(mMockContext.mIntentsBroadcasted.isEmpty());
 
         // No emergency contact yet: Ending block suppression should be a no-op.
         SystemContract.endBlockSuppression(mMockContext);
         assertIsBlocked(true, phoneNumber);
-        assertShouldSystemBlock(true, phoneNumber);
+        assertShouldSystemBlock(true, phoneNumber, null);
         verifyBlocksNotSuppressed();
         assertTrue(mMockContext.mIntentsBroadcasted.isEmpty());
 
@@ -282,7 +284,7 @@
         long timestampMillisBeforeEmergencyContact = System.currentTimeMillis();
         SystemContract.notifyEmergencyContact(mMockContext);
         assertIsBlocked(true, phoneNumber);
-        assertShouldSystemBlock(false, phoneNumber);
+        assertShouldSystemBlock(false, phoneNumber, null);
         SystemContract.BlockSuppressionStatus status =
                 SystemContract.getBlockSuppressionStatus(mMockContext);
         assertTrue(status.isSuppressed);
@@ -296,7 +298,7 @@
         // Ending block suppression should work.
         SystemContract.endBlockSuppression(mMockContext);
         assertIsBlocked(true, phoneNumber);
-        assertShouldSystemBlock(true, phoneNumber);
+        assertShouldSystemBlock(true, phoneNumber, null);
         verifyBlocksNotSuppressed();
         assertEquals(1, mMockContext.mIntentsBroadcasted.size());
         assertEquals(SystemContract.ACTION_BLOCK_SUPPRESSION_STATE_CHANGED,
@@ -314,7 +316,7 @@
         long timestampMillisBeforeEmergencyContact = System.currentTimeMillis();
         SystemContract.notifyEmergencyContact(mMockContext);
         assertIsBlocked(true, phoneNumber);
-        assertShouldSystemBlock(false, phoneNumber);
+        assertShouldSystemBlock(false, phoneNumber, null);
         SystemContract.BlockSuppressionStatus status =
                 SystemContract.getBlockSuppressionStatus(mMockContext);
         assertTrue(status.isSuppressed);
@@ -325,6 +327,96 @@
                 mMockContext.mIntentsBroadcasted.get(0));
     }
 
+    public void testEnhancedBlock() {
+        String phoneNumber = "5004541111";
+
+        // Check whether block numbers not in contacts setting works well
+        setEnhancedBlockSetting(SystemContract.ENHANCED_SETTING_KEY_BLOCK_UNREGISTERED, true);
+        assertShouldSystemBlock(true, phoneNumber,
+                createBundleForEnhancedBlocking(TelecomManager.PRESENTATION_ALLOWED, false));
+        assertShouldSystemBlock(false, phoneNumber,
+                createBundleForEnhancedBlocking(TelecomManager.PRESENTATION_ALLOWED, true));
+        setEnhancedBlockSetting(SystemContract.ENHANCED_SETTING_KEY_BLOCK_UNREGISTERED, false);
+        assertShouldSystemBlock(false, phoneNumber,
+                createBundleForEnhancedBlocking(TelecomManager.PRESENTATION_ALLOWED, false));
+
+        // Check whether block private number calls setting works well
+        setEnhancedBlockSetting(SystemContract.ENHANCED_SETTING_KEY_BLOCK_PRIVATE, true);
+        assertShouldSystemBlock(true, phoneNumber,
+                createBundleForEnhancedBlocking(TelecomManager.PRESENTATION_RESTRICTED, false));
+        setEnhancedBlockSetting(SystemContract.ENHANCED_SETTING_KEY_BLOCK_PRIVATE, false);
+        assertShouldSystemBlock(false, phoneNumber,
+                createBundleForEnhancedBlocking(TelecomManager.PRESENTATION_RESTRICTED, false));
+
+        // Check whether block payphone calls setting works well
+        setEnhancedBlockSetting(SystemContract.ENHANCED_SETTING_KEY_BLOCK_PAYPHONE, true);
+        assertShouldSystemBlock(true, phoneNumber,
+                createBundleForEnhancedBlocking(TelecomManager.PRESENTATION_PAYPHONE, false));
+        setEnhancedBlockSetting(SystemContract.ENHANCED_SETTING_KEY_BLOCK_PAYPHONE, false);
+        assertShouldSystemBlock(false, phoneNumber,
+                createBundleForEnhancedBlocking(TelecomManager.PRESENTATION_PAYPHONE, false));
+
+        // Check whether block unknown calls setting works well
+        setEnhancedBlockSetting(SystemContract.ENHANCED_SETTING_KEY_BLOCK_UNKNOWN, true);
+        assertShouldSystemBlock(true, phoneNumber,
+                createBundleForEnhancedBlocking(TelecomManager.PRESENTATION_UNKNOWN, false));
+        setEnhancedBlockSetting(SystemContract.ENHANCED_SETTING_KEY_BLOCK_UNKNOWN, false);
+        assertShouldSystemBlock(false, phoneNumber,
+                createBundleForEnhancedBlocking(TelecomManager.PRESENTATION_UNKNOWN, false));
+    }
+
+    public void testEnhancedBlockSuppressionAfterEmergencyContact() {
+        String phoneNumber = "5004541111";
+
+        int blockSuppressionSeconds = 1000;
+        when(mMockContext.mCarrierConfigManager.getConfig())
+                .thenReturn(getBundleWithInt(blockSuppressionSeconds));
+
+        setEnhancedBlockSetting(SystemContract.ENHANCED_SETTING_KEY_BLOCK_UNREGISTERED, true);
+        setEnhancedBlockSetting(SystemContract.ENHANCED_SETTING_KEY_BLOCK_PRIVATE, true);
+        setEnhancedBlockSetting(SystemContract.ENHANCED_SETTING_KEY_BLOCK_PAYPHONE, true);
+        setEnhancedBlockSetting(SystemContract.ENHANCED_SETTING_KEY_BLOCK_UNKNOWN, true);
+
+        // After emergency contact blocks should be suppressed.
+        long timestampMillisBeforeEmergencyContact = System.currentTimeMillis();
+        SystemContract.notifyEmergencyContact(mMockContext);
+
+        assertShouldSystemBlock(false, phoneNumber,
+                createBundleForEnhancedBlocking(TelecomManager.PRESENTATION_ALLOWED, false));
+        assertShouldSystemBlock(false, phoneNumber,
+                createBundleForEnhancedBlocking(TelecomManager.PRESENTATION_RESTRICTED, false));
+        assertShouldSystemBlock(false, phoneNumber,
+                createBundleForEnhancedBlocking(TelecomManager.PRESENTATION_PAYPHONE, false));
+        assertShouldSystemBlock(false, phoneNumber,
+                createBundleForEnhancedBlocking(TelecomManager.PRESENTATION_UNKNOWN, false));
+
+        SystemContract.BlockSuppressionStatus status =
+                SystemContract.getBlockSuppressionStatus(mMockContext);
+        assertTrue(status.isSuppressed);
+        assertValidBlockSuppressionExpiration(timestampMillisBeforeEmergencyContact,
+                blockSuppressionSeconds, status.untilTimestampMillis);
+        assertEquals(1, mMockContext.mIntentsBroadcasted.size());
+        assertEquals(SystemContract.ACTION_BLOCK_SUPPRESSION_STATE_CHANGED,
+                mMockContext.mIntentsBroadcasted.get(0));
+        mMockContext.mIntentsBroadcasted.clear();
+
+        // Ending block suppression should work.
+        SystemContract.endBlockSuppression(mMockContext);
+        assertShouldSystemBlock(true, phoneNumber,
+                createBundleForEnhancedBlocking(TelecomManager.PRESENTATION_ALLOWED, false));
+        assertShouldSystemBlock(true, phoneNumber,
+                createBundleForEnhancedBlocking(TelecomManager.PRESENTATION_RESTRICTED, false));
+        assertShouldSystemBlock(true, phoneNumber,
+                createBundleForEnhancedBlocking(TelecomManager.PRESENTATION_PAYPHONE, false));
+        assertShouldSystemBlock(true, phoneNumber,
+                createBundleForEnhancedBlocking(TelecomManager.PRESENTATION_UNKNOWN, false));
+
+        verifyBlocksNotSuppressed();
+        assertEquals(1, mMockContext.mIntentsBroadcasted.size());
+        assertEquals(SystemContract.ACTION_BLOCK_SUPPRESSION_STATE_CHANGED,
+                mMockContext.mIntentsBroadcasted.get(0));
+    }
+
     public void testRegularAppCannotAccessApis() {
         doReturn(PackageManager.PERMISSION_DENIED)
                 .when(mMockContext).checkCallingPermission(anyString());
@@ -372,7 +464,7 @@
         }
 
         try {
-            SystemContract.shouldSystemBlockNumber(mMockContext, "123");
+            SystemContract.shouldSystemBlockNumber(mMockContext, "123", null);
             fail("SecurityException expected");
         } catch (SecurityException expected) {
         }
@@ -451,7 +543,7 @@
         }
 
         try {
-            SystemContract.shouldSystemBlockNumber(mMockContext, "123");
+            SystemContract.shouldSystemBlockNumber(mMockContext, "123", null);
             fail("SecurityException expected");
         } catch (SecurityException expected) {
         }
@@ -519,7 +611,20 @@
         insert(cv(BlockedNumbers.COLUMN_ORIGINAL_NUMBER, emergencyNumber));
 
         assertIsBlocked(true, emergencyNumber);
-        assertFalse(SystemContract.shouldSystemBlockNumber(mMockContext, emergencyNumber));
+        assertFalse(SystemContract.shouldSystemBlockNumber(mMockContext, emergencyNumber, null));
+
+        setEnhancedBlockSetting(SystemContract.ENHANCED_SETTING_KEY_BLOCK_UNREGISTERED, true);
+        assertFalse(SystemContract.shouldSystemBlockNumber(mMockContext, emergencyNumber,
+                createBundleForEnhancedBlocking(TelecomManager.PRESENTATION_ALLOWED, false)));
+        setEnhancedBlockSetting(SystemContract.ENHANCED_SETTING_KEY_BLOCK_PRIVATE, true);
+        assertFalse(SystemContract.shouldSystemBlockNumber(mMockContext, emergencyNumber,
+                createBundleForEnhancedBlocking(TelecomManager.PRESENTATION_RESTRICTED, false)));
+        setEnhancedBlockSetting(SystemContract.ENHANCED_SETTING_KEY_BLOCK_PAYPHONE, true);
+        assertFalse(SystemContract.shouldSystemBlockNumber(mMockContext, emergencyNumber,
+                createBundleForEnhancedBlocking(TelecomManager.PRESENTATION_PAYPHONE, false)));
+        setEnhancedBlockSetting(SystemContract.ENHANCED_SETTING_KEY_BLOCK_UNKNOWN, true);
+        assertFalse(SystemContract.shouldSystemBlockNumber(mMockContext, emergencyNumber,
+                createBundleForEnhancedBlocking(TelecomManager.PRESENTATION_UNKNOWN, false)));
     }
 
     public void testPrivilegedAppAccessingApisAsSecondaryUser() {
@@ -601,8 +706,20 @@
         assertEquals(expected, BlockedNumberContract.isBlocked(mMockContext, phoneNumber));
     }
 
-    private void assertShouldSystemBlock(boolean expected, String phoneNumber) {
-        assertEquals(expected, SystemContract.shouldSystemBlockNumber(mMockContext, phoneNumber));
+    private void assertShouldSystemBlock(boolean expected, String phoneNumber, Bundle extras) {
+        assertEquals(expected, SystemContract.shouldSystemBlockNumber(mMockContext, phoneNumber,
+                extras));
+    }
+
+    private void setEnhancedBlockSetting(String key, boolean value) {
+        SystemContract.setEnhancedBlockSetting(mMockContext, key, value);
+    }
+
+    private Bundle createBundleForEnhancedBlocking(int presentation, boolean contactExist) {
+        Bundle extras = new Bundle();
+        extras.putInt(BlockedNumberContract.EXTRA_CALL_PRESENTATION, presentation);
+        extras.putBoolean(BlockedNumberContract.EXTRA_CONTACT_EXIST, contactExist);
+        return extras;
     }
 
     private PersistableBundle getBundleWithInt(int value) {