Home | History | Annotate | Download | only in provider
      1 /*
      2  * Copyright (C) 2016 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License
     15  */
     16 package android.provider;
     17 
     18 import android.annotation.IntDef;
     19 import android.annotation.WorkerThread;
     20 import android.content.Context;
     21 import android.net.Uri;
     22 import android.os.Bundle;
     23 import android.telecom.Log;
     24 
     25 import java.lang.annotation.Retention;
     26 import java.lang.annotation.RetentionPolicy;
     27 
     28 /**
     29  * <p>
     30  * The contract between the blockednumber provider and applications. Contains definitions for
     31  * the supported URIs and columns.
     32  * </p>
     33  *
     34  * <h3> Overview </h3>
     35  * <p>
     36  * The content provider exposes a table containing blocked numbers. The columns and URIs for
     37  * accessing this table are defined by the {@link BlockedNumbers} class. Messages, and calls from
     38  * blocked numbers are discarded by the platform. Notifications upon provider changes can be
     39  * received using a {@link android.database.ContentObserver}.
     40  * </p>
     41  * <p>
     42  * The platform will not block messages, and calls from emergency numbers as defined by
     43  * {@link android.telephony.PhoneNumberUtils#isEmergencyNumber(String)}. If the user contacts
     44  * emergency services, number blocking is disabled by the platform for a duration defined by
     45  * {@link android.telephony.CarrierConfigManager#KEY_DURATION_BLOCKING_DISABLED_AFTER_EMERGENCY_INT}.
     46  * </p>
     47  *
     48  * <h3> Permissions </h3>
     49  * <p>
     50  * Only the system, the default SMS application, and the default phone app
     51  * (See {@link android.telecom.TelecomManager#getDefaultDialerPackage()}), and carrier apps
     52  * (See {@link android.service.carrier.CarrierService}) can read, and write to the blockednumber
     53  * provider. However, {@link #canCurrentUserBlockNumbers(Context)} can be accessed by any
     54  * application.
     55  * </p>
     56  *
     57  * <h3> Data </h3>
     58  * <p>
     59  * Other than regular phone numbers, the blocked number provider can also store addresses (such
     60  * as email) from which a user can receive messages, and calls. The blocked numbers are stored
     61  * in the {@link BlockedNumbers#COLUMN_ORIGINAL_NUMBER} column. A normalized version of phone
     62  * numbers (if normalization is possible) is stored in {@link BlockedNumbers#COLUMN_E164_NUMBER}
     63  * column. The platform blocks calls, and messages from an address if it is present in in the
     64  * {@link BlockedNumbers#COLUMN_ORIGINAL_NUMBER} column or if the E164 version of the address
     65  * matches the {@link BlockedNumbers#COLUMN_E164_NUMBER} column.
     66  * </p>
     67  *
     68  * <h3> Operations </h3>
     69  * <dl>
     70  * <dt><b>Insert</b></dt>
     71  * <dd>
     72  * <p>
     73  * {@link BlockedNumbers#COLUMN_ORIGINAL_NUMBER} is a required column that needs to be populated.
     74  * Apps can optionally provide the {@link BlockedNumbers#COLUMN_E164_NUMBER} which is the phone
     75  * number's E164 representation. The provider automatically populates this column if the app does
     76  * not provide it. Note that this column is not populated if normalization fails or if the address
     77  * is not a phone number (eg: email).
     78  * <p>
     79  * Attempting to insert an existing blocked number (same
     80  * {@link BlockedNumbers#COLUMN_ORIGINAL_NUMBER} column) will result in replacing the existing
     81  * blocked number.
     82  * <p>
     83  * Examples:
     84  * <pre>
     85  * ContentValues values = new ContentValues();
     86  * values.put(BlockedNumbers.COLUMN_ORIGINAL_NUMBER, "1234567890");
     87  * Uri uri = getContentResolver().insert(BlockedNumbers.CONTENT_URI, values);
     88  * </pre>
     89  * <pre>
     90  * ContentValues values = new ContentValues();
     91  * values.put(BlockedNumbers.COLUMN_ORIGINAL_NUMBER, "1234567890");
     92  * values.put(BlockedNumbers.COLUMN_E164_NUMBER, "+11234567890");
     93  * Uri uri = getContentResolver().insert(BlockedNumbers.CONTENT_URI, values);
     94  * </pre>
     95  * <pre>
     96  * ContentValues values = new ContentValues();
     97  * values.put(BlockedNumbers.COLUMN_ORIGINAL_NUMBER, "12345 (at) abdcde.com");
     98  * Uri uri = getContentResolver().insert(BlockedNumbers.CONTENT_URI, values);
     99  * </pre>
    100  * </p>
    101  * </dd>
    102  * <dt><b>Update</b></dt>
    103  * <dd>
    104  * <p>
    105  * Updates are not supported. Use Delete, and Insert instead.
    106  * </p>
    107  * </dd>
    108  * <dt><b>Delete</b></dt>
    109  * <dd>
    110  * <p>
    111  * Deletions can be performed as follows:
    112  * <pre>
    113  * ContentValues values = new ContentValues();
    114  * values.put(BlockedNumbers.COLUMN_ORIGINAL_NUMBER, "1234567890");
    115  * Uri uri = getContentResolver().insert(BlockedNumbers.CONTENT_URI, values);
    116  * getContentResolver().delete(uri, null, null);
    117  * </pre>
    118  * To check if a particular number is blocked, use the method
    119  * {@link #isBlocked(Context, String)}.
    120  * </p>
    121  * </dd>
    122  * <dt><b>Query</b></dt>
    123  * <dd>
    124  * <p>
    125  * All blocked numbers can be enumerated as follows:
    126  * <pre>
    127  * Cursor c = getContentResolver().query(BlockedNumbers.CONTENT_URI,
    128  *          new String[]{BlockedNumbers.COLUMN_ID, BlockedNumbers.COLUMN_ORIGINAL_NUMBER,
    129  *          BlockedNumbers.COLUMN_E164_NUMBER}, null, null, null);
    130  * </pre>
    131  * </p>
    132  * </dd>
    133  * <dt><b>Unblock</b></dt>
    134  * <dd>
    135  * <p>
    136  * Use the method {@link #unblock(Context, String)} to unblock numbers.
    137  * </p>
    138  * </dd>
    139  *
    140  * <h3> Multi-user </h3>
    141  * <p>
    142  * Apps must use the method {@link #canCurrentUserBlockNumbers(Context)} before performing any
    143  * operation on the blocked number provider. If {@link #canCurrentUserBlockNumbers(Context)} returns
    144  * {@code false}, all operations on the provider will fail with a {@link SecurityException}. The
    145  * platform will block calls, and messages from numbers in the provider independent of the current
    146  * user.
    147  * </p>
    148  */
    149 public class BlockedNumberContract {
    150     private BlockedNumberContract() {
    151     }
    152 
    153     /** The authority for the blocked number provider */
    154     public static final String AUTHORITY = "com.android.blockednumber";
    155 
    156     /** A content:// style uri to the authority for the blocked number provider */
    157     public static final Uri AUTHORITY_URI = Uri.parse("content://" + AUTHORITY);
    158 
    159     private static final String LOG_TAG = BlockedNumberContract.class.getSimpleName();
    160 
    161     /**
    162      * Constants to interact with the blocked numbers list.
    163      */
    164     public static class BlockedNumbers {
    165         private BlockedNumbers() {
    166         }
    167 
    168         /**
    169          * Content URI for the blocked numbers.
    170          * <h3> Supported operations </h3>
    171          * <p> blocked
    172          * <ul>
    173          * <li> query
    174          * <li> delete
    175          * <li> insert
    176          * </ul>
    177          * <p> blocked/ID
    178          * <ul>
    179          * <li> query (selection is not supported)
    180          * <li> delete (selection is not supported)
    181          * </ul>
    182          */
    183         public static final Uri CONTENT_URI = Uri.withAppendedPath(AUTHORITY_URI, "blocked");
    184 
    185         /**
    186          * The MIME type of {@link #CONTENT_URI} itself providing a directory of blocked phone
    187          * numbers.
    188          */
    189         public static final String CONTENT_TYPE = "vnd.android.cursor.dir/blocked_number";
    190 
    191         /**
    192          * The MIME type of a blocked phone number under {@link #CONTENT_URI}.
    193          */
    194         public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/blocked_number";
    195 
    196         /**
    197          * Auto-generated ID field which monotonically increases.
    198          * <p>TYPE: long</p>
    199          */
    200         public static final String COLUMN_ID = "_id";
    201 
    202         /**
    203          * Phone number to block.
    204          * <p>Must be specified in {@code insert}.
    205          * <p>TYPE: String</p>
    206          */
    207         public static final String COLUMN_ORIGINAL_NUMBER = "original_number";
    208 
    209         /**
    210          * Phone number to block.  The system generates it from {@link #COLUMN_ORIGINAL_NUMBER}
    211          * by removing all formatting characters.
    212          * <p>Optional in {@code insert}.  When not specified, the system tries to generate it
    213          * assuming the current country. (Which will still be null if the number is not valid.)
    214          * <p>TYPE: String</p>
    215          */
    216         public static final String COLUMN_E164_NUMBER = "e164_number";
    217     }
    218 
    219     /** @hide */
    220     public static final String METHOD_IS_BLOCKED = "is_blocked";
    221 
    222     /** @hide */
    223     public static final String METHOD_UNBLOCK= "unblock";
    224 
    225     /** @hide */
    226     public static final String RES_NUMBER_IS_BLOCKED = "blocked";
    227 
    228     /** @hide */
    229     @Retention(RetentionPolicy.SOURCE)
    230     @IntDef(
    231             prefix = { "STATUS_" },
    232             value = {STATUS_NOT_BLOCKED, STATUS_BLOCKED_IN_LIST, STATUS_BLOCKED_RESTRICTED,
    233                     STATUS_BLOCKED_UNKNOWN_NUMBER, STATUS_BLOCKED_PAYPHONE,
    234                     STATUS_BLOCKED_NOT_IN_CONTACTS})
    235     public @interface BlockStatus {}
    236 
    237     /**
    238      * Integer reason code used with {@link #RES_BLOCK_STATUS} to indicate that a call was not
    239      * blocked.
    240      * @hide
    241      */
    242     public static final int STATUS_NOT_BLOCKED = 0;
    243 
    244     /**
    245      * Integer reason code used with {@link #RES_BLOCK_STATUS} to indicate that a call was blocked
    246      * because it is in the list of blocked numbers maintained by the provider.
    247      * @hide
    248      */
    249     public static final int STATUS_BLOCKED_IN_LIST = 1;
    250 
    251     /**
    252      * Integer reason code used with {@link #RES_BLOCK_STATUS} to indicate that a call was blocked
    253      * because it is from a restricted number.
    254      * @hide
    255      */
    256     public static final int STATUS_BLOCKED_RESTRICTED = 2;
    257 
    258     /**
    259      * Integer reason code used with {@link #RES_BLOCK_STATUS} to indicate that a call was blocked
    260      * because it is from an unknown number.
    261      * @hide
    262      */
    263     public static final int STATUS_BLOCKED_UNKNOWN_NUMBER = 3;
    264 
    265     /**
    266      * Integer reason code used with {@link #RES_BLOCK_STATUS} to indicate that a call was blocked
    267      * because it is from a pay phone.
    268      * @hide
    269      */
    270     public static final int STATUS_BLOCKED_PAYPHONE = 4;
    271 
    272     /**
    273      * Integer reason code used with {@link #RES_BLOCK_STATUS} to indicate that a call was blocked
    274      * because it is from a number not in the users contacts.
    275      * @hide
    276      */
    277     public static final int STATUS_BLOCKED_NOT_IN_CONTACTS = 5;
    278 
    279     /**
    280      * Integer reason indicating whether a call was blocked, and if so why.
    281      * @hide
    282      */
    283     public static final String RES_BLOCK_STATUS = "block_status";
    284 
    285     /** @hide */
    286     public static final String RES_NUM_ROWS_DELETED = "num_deleted";
    287 
    288     /** @hide */
    289     public static final String METHOD_CAN_CURRENT_USER_BLOCK_NUMBERS =
    290             "can_current_user_block_numbers";
    291 
    292     /** @hide */
    293     public static final String RES_CAN_BLOCK_NUMBERS = "can_block";
    294 
    295     /** @hide */
    296     public static final String RES_ENHANCED_SETTING_IS_ENABLED = "enhanced_setting_enabled";
    297 
    298     /** @hide */
    299     public static final String RES_SHOW_EMERGENCY_CALL_NOTIFICATION =
    300             "show_emergency_call_notification";
    301 
    302     /** @hide */
    303     public static final String EXTRA_ENHANCED_SETTING_KEY = "extra_enhanced_setting_key";
    304 
    305     /** @hide */
    306     public static final String EXTRA_ENHANCED_SETTING_VALUE = "extra_enhanced_setting_value";
    307 
    308     /** @hide */
    309     public static final String EXTRA_CONTACT_EXIST = "extra_contact_exist";
    310 
    311     /** @hide */
    312     public static final String EXTRA_CALL_PRESENTATION = "extra_call_presentation";
    313 
    314     /**
    315      * Returns whether a given number is in the blocked list.
    316      *
    317      * <p> This matches the {@code phoneNumber} against the
    318      * {@link BlockedNumbers#COLUMN_ORIGINAL_NUMBER} column, and the E164 representation of the
    319      * {@code phoneNumber} with the {@link BlockedNumbers#COLUMN_E164_NUMBER} column.
    320      *
    321      * <p> Note that if the {@link #canCurrentUserBlockNumbers} is {@code false} for the user
    322      * context {@code context}, this method will throw a {@link SecurityException}.
    323      *
    324      * @return {@code true} if the {@code phoneNumber} is blocked.
    325      */
    326     @WorkerThread
    327     public static boolean isBlocked(Context context, String phoneNumber) {
    328         try {
    329             final Bundle res = context.getContentResolver().call(
    330                     AUTHORITY_URI, METHOD_IS_BLOCKED, phoneNumber, null);
    331             boolean isBlocked = res != null && res.getBoolean(RES_NUMBER_IS_BLOCKED, false);
    332             Log.d(LOG_TAG, "isBlocked: phoneNumber=%s, isBlocked=%b", Log.piiHandle(phoneNumber),
    333                     isBlocked);
    334             return isBlocked;
    335         } catch (NullPointerException | IllegalArgumentException ex) {
    336             // The content resolver can throw an NPE or IAE; we don't want to crash Telecom if
    337             // either of these happen.
    338             Log.w(null, "isBlocked: provider not ready.");
    339             return false;
    340         }
    341     }
    342 
    343     /**
    344      * Unblocks the {@code phoneNumber} if it is blocked.
    345      *
    346      * <p> This deletes all rows where the {@code phoneNumber} matches the
    347      * {@link BlockedNumbers#COLUMN_ORIGINAL_NUMBER} column or the E164 representation of the
    348      * {@code phoneNumber} matches the {@link BlockedNumbers#COLUMN_E164_NUMBER} column.
    349      *
    350      * <p>To delete rows based on exact match with specific columns such as
    351      * {@link BlockedNumbers#COLUMN_ID} use
    352      * {@link android.content.ContentProvider#delete(Uri, String, String[])} with
    353      * {@link BlockedNumbers#CONTENT_URI} URI.
    354      *
    355      * <p> Note that if the {@link #canCurrentUserBlockNumbers} is {@code false} for the user
    356      * context {@code context}, this method will throw a {@link SecurityException}.
    357      *
    358      * @return the number of rows deleted in the blocked number provider as a result of unblock.
    359      */
    360     @WorkerThread
    361     public static int unblock(Context context, String phoneNumber) {
    362         Log.d(LOG_TAG, "unblock: phoneNumber=%s", Log.piiHandle(phoneNumber));
    363         final Bundle res = context.getContentResolver().call(
    364                 AUTHORITY_URI, METHOD_UNBLOCK, phoneNumber, null);
    365         return res.getInt(RES_NUM_ROWS_DELETED, 0);
    366     }
    367 
    368     /**
    369      * Checks if blocking numbers is supported for the current user.
    370      * <p> Typically, blocking numbers is only supported for one user at a time.
    371      *
    372      * @return {@code true} if the current user can block numbers.
    373      */
    374     public static boolean canCurrentUserBlockNumbers(Context context) {
    375         try {
    376             final Bundle res = context.getContentResolver().call(
    377                     AUTHORITY_URI, METHOD_CAN_CURRENT_USER_BLOCK_NUMBERS, null, null);
    378             return res != null && res.getBoolean(RES_CAN_BLOCK_NUMBERS, false);
    379         } catch (NullPointerException | IllegalArgumentException ex) {
    380             // The content resolver can throw an NPE or IAE; we don't want to crash Telecom if
    381             // either of these happen.
    382             Log.w(null, "canCurrentUserBlockNumbers: provider not ready.");
    383             return false;
    384         }
    385     }
    386 
    387     /**
    388      * <p>
    389      * The contract between the blockednumber provider and the system.
    390      * </p>
    391      * <p>This is a wrapper over {@link BlockedNumberContract} that also manages the blocking
    392      * behavior when the user contacts emergency services. See
    393      * {@link #notifyEmergencyContact(Context)} for details. All methods are protected by
    394      * {@link android.Manifest.permission#READ_BLOCKED_NUMBERS} and
    395      * {@link android.Manifest.permission#WRITE_BLOCKED_NUMBERS} appropriately which ensure that
    396      * only system can access the methods defined here.
    397      * </p>
    398      * @hide
    399      */
    400     public static class SystemContract {
    401         /**
    402          * A protected broadcast intent action for letting components with
    403          * {@link android.Manifest.permission#READ_BLOCKED_NUMBERS} know that the block suppression
    404          * status as returned by {@link #getBlockSuppressionStatus(Context)} has been updated.
    405          */
    406         public static final String ACTION_BLOCK_SUPPRESSION_STATE_CHANGED =
    407                 "android.provider.action.BLOCK_SUPPRESSION_STATE_CHANGED";
    408 
    409         public static final String METHOD_NOTIFY_EMERGENCY_CONTACT = "notify_emergency_contact";
    410 
    411         public static final String METHOD_END_BLOCK_SUPPRESSION = "end_block_suppression";
    412 
    413         public static final String METHOD_SHOULD_SYSTEM_BLOCK_NUMBER = "should_system_block_number";
    414 
    415         public static final String METHOD_GET_BLOCK_SUPPRESSION_STATUS =
    416                 "get_block_suppression_status";
    417 
    418         public static final String METHOD_SHOULD_SHOW_EMERGENCY_CALL_NOTIFICATION =
    419                 "should_show_emergency_call_notification";
    420 
    421         public static final String RES_IS_BLOCKING_SUPPRESSED = "blocking_suppressed";
    422 
    423         public static final String RES_BLOCKING_SUPPRESSED_UNTIL_TIMESTAMP =
    424                 "blocking_suppressed_until_timestamp";
    425 
    426         public static final String METHOD_GET_ENHANCED_BLOCK_SETTING = "get_enhanced_block_setting";
    427         public static final String METHOD_SET_ENHANCED_BLOCK_SETTING = "set_enhanced_block_setting";
    428 
    429         /* Preference key of block numbers not in contacts setting. */
    430         public static final String ENHANCED_SETTING_KEY_BLOCK_UNREGISTERED =
    431                 "block_numbers_not_in_contacts_setting";
    432         /* Preference key of block private number calls setting. */
    433         public static final String ENHANCED_SETTING_KEY_BLOCK_PRIVATE =
    434                 "block_private_number_calls_setting";
    435         /* Preference key of block payphone calls setting. */
    436         public static final String ENHANCED_SETTING_KEY_BLOCK_PAYPHONE =
    437                 "block_payphone_calls_setting";
    438         /* Preference key of block unknown calls setting. */
    439         public static final String ENHANCED_SETTING_KEY_BLOCK_UNKNOWN =
    440                 "block_unknown_calls_setting";
    441         /* Preference key for whether should show an emergency call notification. */
    442         public static final String ENHANCED_SETTING_KEY_SHOW_EMERGENCY_CALL_NOTIFICATION =
    443                 "show_emergency_call_notification";
    444 
    445         /**
    446          * Notifies the provider that emergency services were contacted by the user.
    447          * <p> This results in {@link #shouldSystemBlockNumber} returning {@code false} independent
    448          * of the contents of the provider for a duration defined by
    449          * {@link android.telephony.CarrierConfigManager#KEY_DURATION_BLOCKING_DISABLED_AFTER_EMERGENCY_INT}
    450          * the provider unless {@link #endBlockSuppression(Context)} is called.
    451          */
    452         public static void notifyEmergencyContact(Context context) {
    453             try {
    454                 Log.i(LOG_TAG, "notifyEmergencyContact; caller=%s", context.getOpPackageName());
    455                 context.getContentResolver().call(
    456                         AUTHORITY_URI, METHOD_NOTIFY_EMERGENCY_CONTACT, null, null);
    457             } catch (NullPointerException | IllegalArgumentException ex) {
    458                 // The content resolver can throw an NPE or IAE; we don't want to crash Telecom if
    459                 // either of these happen.
    460                 Log.w(null, "notifyEmergencyContact: provider not ready.");
    461             }
    462         }
    463 
    464         /**
    465          * Notifies the provider to disable suppressing blocking. If emergency services were not
    466          * contacted recently at all, calling this method is a no-op.
    467          */
    468         public static void endBlockSuppression(Context context) {
    469             String caller = context.getOpPackageName();
    470             Log.i(LOG_TAG, "endBlockSuppression: caller=%s", caller);
    471             context.getContentResolver().call(
    472                     AUTHORITY_URI, METHOD_END_BLOCK_SUPPRESSION, null, null);
    473         }
    474 
    475         /**
    476          * Returns {@code true} if {@code phoneNumber} is blocked taking
    477          * {@link #notifyEmergencyContact(Context)} into consideration. If emergency services
    478          * have not been contacted recently and enhanced call blocking not been enabled, this
    479          * method is equivalent to {@link #isBlocked(Context, String)}.
    480          *
    481          * @param context the context of the caller.
    482          * @param phoneNumber the number to check.
    483          * @param extras the extra attribute of the number.
    484          * @return result code indicating if the number should be blocked, and if so why.
    485          *         Valid values are: {@link #STATUS_NOT_BLOCKED}, {@link #STATUS_BLOCKED_IN_LIST},
    486          *         {@link #STATUS_BLOCKED_NOT_IN_CONTACTS}, {@link #STATUS_BLOCKED_PAYPHONE},
    487          *         {@link #STATUS_BLOCKED_RESTRICTED}, {@link #STATUS_BLOCKED_UNKNOWN_NUMBER}.
    488          */
    489         public static int shouldSystemBlockNumber(Context context, String phoneNumber,
    490                 Bundle extras) {
    491             try {
    492                 String caller = context.getOpPackageName();
    493                 final Bundle res = context.getContentResolver().call(
    494                         AUTHORITY_URI, METHOD_SHOULD_SYSTEM_BLOCK_NUMBER, phoneNumber, extras);
    495                 int blockResult = res != null ? res.getInt(RES_BLOCK_STATUS, STATUS_NOT_BLOCKED) :
    496                         BlockedNumberContract.STATUS_NOT_BLOCKED;
    497                 Log.d(LOG_TAG, "shouldSystemBlockNumber: number=%s, caller=%s, result=%s",
    498                         Log.piiHandle(phoneNumber), caller, blockStatusToString(blockResult));
    499                 return blockResult;
    500             } catch (NullPointerException | IllegalArgumentException ex) {
    501                 // The content resolver can throw an NPE or IAE; we don't want to crash Telecom if
    502                 // either of these happen.
    503                 Log.w(null, "shouldSystemBlockNumber: provider not ready.");
    504                 return BlockedNumberContract.STATUS_NOT_BLOCKED;
    505             }
    506         }
    507 
    508         /**
    509          * Returns the current status of block suppression.
    510          */
    511         public static BlockSuppressionStatus getBlockSuppressionStatus(Context context) {
    512             final Bundle res = context.getContentResolver().call(
    513                     AUTHORITY_URI, METHOD_GET_BLOCK_SUPPRESSION_STATUS, null, null);
    514             BlockSuppressionStatus blockSuppressionStatus = new BlockSuppressionStatus(
    515                     res.getBoolean(RES_IS_BLOCKING_SUPPRESSED, false),
    516                     res.getLong(RES_BLOCKING_SUPPRESSED_UNTIL_TIMESTAMP, 0));
    517             Log.d(LOG_TAG, "getBlockSuppressionStatus: caller=%s, status=%s",
    518                     context.getOpPackageName(), blockSuppressionStatus);
    519             return blockSuppressionStatus;
    520         }
    521 
    522         /**
    523          * Check whether should show the emergency call notification.
    524          *
    525          * @param context the context of the caller.
    526          * @return {@code true} if should show emergency call notification. {@code false} otherwise.
    527          */
    528         public static boolean shouldShowEmergencyCallNotification(Context context) {
    529             try {
    530                 final Bundle res = context.getContentResolver().call(
    531                         AUTHORITY_URI, METHOD_SHOULD_SHOW_EMERGENCY_CALL_NOTIFICATION, null, null);
    532                 return res != null && res.getBoolean(RES_SHOW_EMERGENCY_CALL_NOTIFICATION, false);
    533             } catch (NullPointerException | IllegalArgumentException ex) {
    534                 // The content resolver can throw an NPE or IAE; we don't want to crash Telecom if
    535                 // either of these happen.
    536                 Log.w(null, "shouldShowEmergencyCallNotification: provider not ready.");
    537                 return false;
    538             }
    539         }
    540 
    541         /**
    542          * Check whether the enhanced block setting is enabled.
    543          *
    544          * @param context the context of the caller.
    545          * @param key the key of the setting to check, can be
    546          *        {@link #ENHANCED_SETTING_KEY_BLOCK_UNREGISTERED}
    547          *        {@link #ENHANCED_SETTING_KEY_BLOCK_PRIVATE}
    548          *        {@link #ENHANCED_SETTING_KEY_BLOCK_PAYPHONE}
    549          *        {@link #ENHANCED_SETTING_KEY_BLOCK_UNKNOWN}
    550          *        {@link #ENHANCED_SETTING_KEY_EMERGENCY_CALL_NOTIFICATION_SHOWING}
    551          * @return {@code true} if the setting is enabled. {@code false} otherwise.
    552          */
    553         public static boolean getEnhancedBlockSetting(Context context, String key) {
    554             Bundle extras = new Bundle();
    555             extras.putString(EXTRA_ENHANCED_SETTING_KEY, key);
    556             try {
    557                 final Bundle res = context.getContentResolver().call(
    558                         AUTHORITY_URI, METHOD_GET_ENHANCED_BLOCK_SETTING, null, extras);
    559                 return res != null && res.getBoolean(RES_ENHANCED_SETTING_IS_ENABLED, false);
    560             } catch (NullPointerException | IllegalArgumentException ex) {
    561                 // The content resolver can throw an NPE or IAE; we don't want to crash Telecom if
    562                 // either of these happen.
    563                 Log.w(null, "getEnhancedBlockSetting: provider not ready.");
    564                 return false;
    565             }
    566         }
    567 
    568         /**
    569          * Set the enhanced block setting enabled status.
    570          *
    571          * @param context the context of the caller.
    572          * @param key the key of the setting to set, can be
    573          *        {@link #ENHANCED_SETTING_KEY_BLOCK_UNREGISTERED}
    574          *        {@link #ENHANCED_SETTING_KEY_BLOCK_PRIVATE}
    575          *        {@link #ENHANCED_SETTING_KEY_BLOCK_PAYPHONE}
    576          *        {@link #ENHANCED_SETTING_KEY_BLOCK_UNKNOWN}
    577          *        {@link #ENHANCED_SETTING_KEY_EMERGENCY_CALL_NOTIFICATION_SHOWING}
    578          * @param value the enabled statue of the setting to set.
    579          */
    580         public static void setEnhancedBlockSetting(Context context, String key, boolean value) {
    581             Bundle extras = new Bundle();
    582             extras.putString(EXTRA_ENHANCED_SETTING_KEY, key);
    583             extras.putBoolean(EXTRA_ENHANCED_SETTING_VALUE, value);
    584             context.getContentResolver().call(AUTHORITY_URI, METHOD_SET_ENHANCED_BLOCK_SETTING,
    585                     null, extras);
    586         }
    587 
    588         /**
    589          * Converts a block status constant to a string equivalent for logging.
    590          * @hide
    591          */
    592         public static String blockStatusToString(int blockStatus) {
    593             switch (blockStatus) {
    594                 case STATUS_NOT_BLOCKED:
    595                     return "not blocked";
    596                 case STATUS_BLOCKED_IN_LIST:
    597                     return "blocked - in list";
    598                 case STATUS_BLOCKED_RESTRICTED:
    599                     return "blocked - restricted";
    600                 case STATUS_BLOCKED_UNKNOWN_NUMBER:
    601                     return "blocked - unknown";
    602                 case STATUS_BLOCKED_PAYPHONE:
    603                     return "blocked - payphone";
    604                 case STATUS_BLOCKED_NOT_IN_CONTACTS:
    605                     return "blocked - not in contacts";
    606             }
    607             return "unknown";
    608         }
    609 
    610         /**
    611          * Represents the current status of
    612          * {@link #shouldSystemBlockNumber(Context, String, Bundle)}. If emergency services
    613          * have been contacted recently, {@link #isSuppressed} is {@code true}, and blocking
    614          * is disabled until the timestamp {@link #untilTimestampMillis}.
    615          */
    616         public static class BlockSuppressionStatus {
    617             public final boolean isSuppressed;
    618             /**
    619              * Timestamp in milliseconds from epoch.
    620              */
    621             public final long untilTimestampMillis;
    622 
    623             public BlockSuppressionStatus(boolean isSuppressed, long untilTimestampMillis) {
    624                 this.isSuppressed = isSuppressed;
    625                 this.untilTimestampMillis = untilTimestampMillis;
    626             }
    627 
    628             @Override
    629             public String toString() {
    630                 return "[BlockSuppressionStatus; isSuppressed=" + isSuppressed + ", until="
    631                         + untilTimestampMillis + "]";
    632             }
    633         }
    634     }
    635 }
    636