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@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 { 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 { 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, STATUS_BLOCKED_UNAVAILABLE}) 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 code used with {@link #RES_BLOCK_STATUS} to indicate that a call was blocked 281 * because it is from a number not available. 282 * @hide 283 */ 284 public static final int STATUS_BLOCKED_UNAVAILABLE = 6; 285 286 /** 287 * Integer reason indicating whether a call was blocked, and if so why. 288 * @hide 289 */ 290 public static final String RES_BLOCK_STATUS = "block_status"; 291 292 /** @hide */ 293 public static final String RES_NUM_ROWS_DELETED = "num_deleted"; 294 295 /** @hide */ 296 public static final String METHOD_CAN_CURRENT_USER_BLOCK_NUMBERS = 297 "can_current_user_block_numbers"; 298 299 /** @hide */ 300 public static final String RES_CAN_BLOCK_NUMBERS = "can_block"; 301 302 /** @hide */ 303 public static final String RES_ENHANCED_SETTING_IS_ENABLED = "enhanced_setting_enabled"; 304 305 /** @hide */ 306 public static final String RES_SHOW_EMERGENCY_CALL_NOTIFICATION = 307 "show_emergency_call_notification"; 308 309 /** @hide */ 310 public static final String EXTRA_ENHANCED_SETTING_KEY = "extra_enhanced_setting_key"; 311 312 /** @hide */ 313 public static final String EXTRA_ENHANCED_SETTING_VALUE = "extra_enhanced_setting_value"; 314 315 /** @hide */ 316 public static final String EXTRA_CONTACT_EXIST = "extra_contact_exist"; 317 318 /** @hide */ 319 public static final String EXTRA_CALL_PRESENTATION = "extra_call_presentation"; 320 321 /** 322 * Returns whether a given number is in the blocked list. 323 * 324 * <p> This matches the {@code phoneNumber} against the 325 * {@link BlockedNumbers#COLUMN_ORIGINAL_NUMBER} column, and the E164 representation of the 326 * {@code phoneNumber} with the {@link BlockedNumbers#COLUMN_E164_NUMBER} column. 327 * 328 * <p> Note that if the {@link #canCurrentUserBlockNumbers} is {@code false} for the user 329 * context {@code context}, this method will throw a {@link SecurityException}. 330 * 331 * @return {@code true} if the {@code phoneNumber} is blocked. 332 */ 333 @WorkerThread isBlocked(Context context, String phoneNumber)334 public static boolean isBlocked(Context context, String phoneNumber) { 335 try { 336 final Bundle res = context.getContentResolver().call( 337 AUTHORITY_URI, METHOD_IS_BLOCKED, phoneNumber, null); 338 boolean isBlocked = res != null && res.getBoolean(RES_NUMBER_IS_BLOCKED, false); 339 Log.d(LOG_TAG, "isBlocked: phoneNumber=%s, isBlocked=%b", Log.piiHandle(phoneNumber), 340 isBlocked); 341 return isBlocked; 342 } catch (NullPointerException | IllegalArgumentException ex) { 343 // The content resolver can throw an NPE or IAE; we don't want to crash Telecom if 344 // either of these happen. 345 Log.w(null, "isBlocked: provider not ready."); 346 return false; 347 } 348 } 349 350 /** 351 * Unblocks the {@code phoneNumber} if it is blocked. 352 * 353 * <p> This deletes all rows where the {@code phoneNumber} matches the 354 * {@link BlockedNumbers#COLUMN_ORIGINAL_NUMBER} column or the E164 representation of the 355 * {@code phoneNumber} matches the {@link BlockedNumbers#COLUMN_E164_NUMBER} column. 356 * 357 * <p>To delete rows based on exact match with specific columns such as 358 * {@link BlockedNumbers#COLUMN_ID} use 359 * {@link android.content.ContentProvider#delete(Uri, String, String[])} with 360 * {@link BlockedNumbers#CONTENT_URI} URI. 361 * 362 * <p> Note that if the {@link #canCurrentUserBlockNumbers} is {@code false} for the user 363 * context {@code context}, this method will throw a {@link SecurityException}. 364 * 365 * @return the number of rows deleted in the blocked number provider as a result of unblock. 366 */ 367 @WorkerThread unblock(Context context, String phoneNumber)368 public static int unblock(Context context, String phoneNumber) { 369 Log.d(LOG_TAG, "unblock: phoneNumber=%s", Log.piiHandle(phoneNumber)); 370 final Bundle res = context.getContentResolver().call( 371 AUTHORITY_URI, METHOD_UNBLOCK, phoneNumber, null); 372 return res.getInt(RES_NUM_ROWS_DELETED, 0); 373 } 374 375 /** 376 * Checks if blocking numbers is supported for the current user. 377 * <p> Typically, blocking numbers is only supported for one user at a time. 378 * 379 * @return {@code true} if the current user can block numbers. 380 */ canCurrentUserBlockNumbers(Context context)381 public static boolean canCurrentUserBlockNumbers(Context context) { 382 try { 383 final Bundle res = context.getContentResolver().call( 384 AUTHORITY_URI, METHOD_CAN_CURRENT_USER_BLOCK_NUMBERS, null, null); 385 return res != null && res.getBoolean(RES_CAN_BLOCK_NUMBERS, false); 386 } catch (NullPointerException | IllegalArgumentException ex) { 387 // The content resolver can throw an NPE or IAE; we don't want to crash Telecom if 388 // either of these happen. 389 Log.w(null, "canCurrentUserBlockNumbers: provider not ready."); 390 return false; 391 } 392 } 393 394 /** 395 * <p> 396 * The contract between the blockednumber provider and the system. 397 * </p> 398 * <p>This is a wrapper over {@link BlockedNumberContract} that also manages the blocking 399 * behavior when the user contacts emergency services. See 400 * {@link #notifyEmergencyContact(Context)} for details. All methods are protected by 401 * {@link android.Manifest.permission#READ_BLOCKED_NUMBERS} and 402 * {@link android.Manifest.permission#WRITE_BLOCKED_NUMBERS} appropriately which ensure that 403 * only system can access the methods defined here. 404 * </p> 405 * @hide 406 */ 407 public static class SystemContract { 408 /** 409 * A protected broadcast intent action for letting components with 410 * {@link android.Manifest.permission#READ_BLOCKED_NUMBERS} know that the block suppression 411 * status as returned by {@link #getBlockSuppressionStatus(Context)} has been updated. 412 */ 413 public static final String ACTION_BLOCK_SUPPRESSION_STATE_CHANGED = 414 "android.provider.action.BLOCK_SUPPRESSION_STATE_CHANGED"; 415 416 public static final String METHOD_NOTIFY_EMERGENCY_CONTACT = "notify_emergency_contact"; 417 418 public static final String METHOD_END_BLOCK_SUPPRESSION = "end_block_suppression"; 419 420 public static final String METHOD_SHOULD_SYSTEM_BLOCK_NUMBER = "should_system_block_number"; 421 422 public static final String METHOD_GET_BLOCK_SUPPRESSION_STATUS = 423 "get_block_suppression_status"; 424 425 public static final String METHOD_SHOULD_SHOW_EMERGENCY_CALL_NOTIFICATION = 426 "should_show_emergency_call_notification"; 427 428 public static final String RES_IS_BLOCKING_SUPPRESSED = "blocking_suppressed"; 429 430 public static final String RES_BLOCKING_SUPPRESSED_UNTIL_TIMESTAMP = 431 "blocking_suppressed_until_timestamp"; 432 433 public static final String METHOD_GET_ENHANCED_BLOCK_SETTING = "get_enhanced_block_setting"; 434 public static final String METHOD_SET_ENHANCED_BLOCK_SETTING = "set_enhanced_block_setting"; 435 436 /* Preference key of block numbers not in contacts setting. */ 437 public static final String ENHANCED_SETTING_KEY_BLOCK_UNREGISTERED = 438 "block_numbers_not_in_contacts_setting"; 439 /* Preference key of block private number calls setting. */ 440 public static final String ENHANCED_SETTING_KEY_BLOCK_PRIVATE = 441 "block_private_number_calls_setting"; 442 /* Preference key of block payphone calls setting. */ 443 public static final String ENHANCED_SETTING_KEY_BLOCK_PAYPHONE = 444 "block_payphone_calls_setting"; 445 /* Preference key of block unknown calls setting. */ 446 public static final String ENHANCED_SETTING_KEY_BLOCK_UNKNOWN = 447 "block_unknown_calls_setting"; 448 /* Preference key for whether should show an emergency call notification. */ 449 public static final String ENHANCED_SETTING_KEY_SHOW_EMERGENCY_CALL_NOTIFICATION = 450 "show_emergency_call_notification"; 451 /* Preference key of block unavailable calls setting. */ 452 public static final String ENHANCED_SETTING_KEY_BLOCK_UNAVAILABLE = 453 "block_unavailable_calls_setting"; 454 455 /** 456 * Notifies the provider that emergency services were contacted by the user. 457 * <p> This results in {@link #shouldSystemBlockNumber} returning {@code false} independent 458 * of the contents of the provider for a duration defined by 459 * {@link android.telephony.CarrierConfigManager#KEY_DURATION_BLOCKING_DISABLED_AFTER_EMERGENCY_INT} 460 * the provider unless {@link #endBlockSuppression(Context)} is called. 461 */ notifyEmergencyContact(Context context)462 public static void notifyEmergencyContact(Context context) { 463 try { 464 Log.i(LOG_TAG, "notifyEmergencyContact; caller=%s", context.getOpPackageName()); 465 context.getContentResolver().call( 466 AUTHORITY_URI, METHOD_NOTIFY_EMERGENCY_CONTACT, null, null); 467 } catch (NullPointerException | IllegalArgumentException ex) { 468 // The content resolver can throw an NPE or IAE; we don't want to crash Telecom if 469 // either of these happen. 470 Log.w(null, "notifyEmergencyContact: provider not ready."); 471 } 472 } 473 474 /** 475 * Notifies the provider to disable suppressing blocking. If emergency services were not 476 * contacted recently at all, calling this method is a no-op. 477 */ endBlockSuppression(Context context)478 public static void endBlockSuppression(Context context) { 479 String caller = context.getOpPackageName(); 480 Log.i(LOG_TAG, "endBlockSuppression: caller=%s", caller); 481 context.getContentResolver().call( 482 AUTHORITY_URI, METHOD_END_BLOCK_SUPPRESSION, null, null); 483 } 484 485 /** 486 * Returns {@code true} if {@code phoneNumber} is blocked taking 487 * {@link #notifyEmergencyContact(Context)} into consideration. If emergency services 488 * have not been contacted recently and enhanced call blocking not been enabled, this 489 * method is equivalent to {@link #isBlocked(Context, String)}. 490 * 491 * @param context the context of the caller. 492 * @param phoneNumber the number to check. 493 * @param extras the extra attribute of the number. 494 * @return result code indicating if the number should be blocked, and if so why. 495 * Valid values are: {@link #STATUS_NOT_BLOCKED}, {@link #STATUS_BLOCKED_IN_LIST}, 496 * {@link #STATUS_BLOCKED_NOT_IN_CONTACTS}, {@link #STATUS_BLOCKED_PAYPHONE}, 497 * {@link #STATUS_BLOCKED_RESTRICTED}, {@link #STATUS_BLOCKED_UNKNOWN_NUMBER}. 498 */ shouldSystemBlockNumber(Context context, String phoneNumber, Bundle extras)499 public static int shouldSystemBlockNumber(Context context, String phoneNumber, 500 Bundle extras) { 501 try { 502 String caller = context.getOpPackageName(); 503 final Bundle res = context.getContentResolver().call( 504 AUTHORITY_URI, METHOD_SHOULD_SYSTEM_BLOCK_NUMBER, phoneNumber, extras); 505 int blockResult = res != null ? res.getInt(RES_BLOCK_STATUS, STATUS_NOT_BLOCKED) : 506 BlockedNumberContract.STATUS_NOT_BLOCKED; 507 Log.d(LOG_TAG, "shouldSystemBlockNumber: number=%s, caller=%s, result=%s", 508 Log.piiHandle(phoneNumber), caller, blockStatusToString(blockResult)); 509 return blockResult; 510 } catch (NullPointerException | IllegalArgumentException ex) { 511 // The content resolver can throw an NPE or IAE; we don't want to crash Telecom if 512 // either of these happen. 513 Log.w(null, "shouldSystemBlockNumber: provider not ready."); 514 return BlockedNumberContract.STATUS_NOT_BLOCKED; 515 } 516 } 517 518 /** 519 * Returns the current status of block suppression. 520 */ getBlockSuppressionStatus(Context context)521 public static BlockSuppressionStatus getBlockSuppressionStatus(Context context) { 522 final Bundle res = context.getContentResolver().call( 523 AUTHORITY_URI, METHOD_GET_BLOCK_SUPPRESSION_STATUS, null, null); 524 BlockSuppressionStatus blockSuppressionStatus = new BlockSuppressionStatus( 525 res.getBoolean(RES_IS_BLOCKING_SUPPRESSED, false), 526 res.getLong(RES_BLOCKING_SUPPRESSED_UNTIL_TIMESTAMP, 0)); 527 Log.d(LOG_TAG, "getBlockSuppressionStatus: caller=%s, status=%s", 528 context.getOpPackageName(), blockSuppressionStatus); 529 return blockSuppressionStatus; 530 } 531 532 /** 533 * Check whether should show the emergency call notification. 534 * 535 * @param context the context of the caller. 536 * @return {@code true} if should show emergency call notification. {@code false} otherwise. 537 */ shouldShowEmergencyCallNotification(Context context)538 public static boolean shouldShowEmergencyCallNotification(Context context) { 539 try { 540 final Bundle res = context.getContentResolver().call( 541 AUTHORITY_URI, METHOD_SHOULD_SHOW_EMERGENCY_CALL_NOTIFICATION, null, null); 542 return res != null && res.getBoolean(RES_SHOW_EMERGENCY_CALL_NOTIFICATION, false); 543 } catch (NullPointerException | IllegalArgumentException ex) { 544 // The content resolver can throw an NPE or IAE; we don't want to crash Telecom if 545 // either of these happen. 546 Log.w(null, "shouldShowEmergencyCallNotification: provider not ready."); 547 return false; 548 } 549 } 550 551 /** 552 * Check whether the enhanced block setting is enabled. 553 * 554 * @param context the context of the caller. 555 * @param key the key of the setting to check, can be 556 * {@link #ENHANCED_SETTING_KEY_BLOCK_UNREGISTERED} 557 * {@link #ENHANCED_SETTING_KEY_BLOCK_PRIVATE} 558 * {@link #ENHANCED_SETTING_KEY_BLOCK_PAYPHONE} 559 * {@link #ENHANCED_SETTING_KEY_BLOCK_UNKNOWN} 560 * {@link #ENHANCED_SETTING_KEY_BLOCK_UNAVAILABLE} 561 * {@link #ENHANCED_SETTING_KEY_SHOW_EMERGENCY_CALL_NOTIFICATION} 562 * @return {@code true} if the setting is enabled. {@code false} otherwise. 563 */ getEnhancedBlockSetting(Context context, String key)564 public static boolean getEnhancedBlockSetting(Context context, String key) { 565 Bundle extras = new Bundle(); 566 extras.putString(EXTRA_ENHANCED_SETTING_KEY, key); 567 try { 568 final Bundle res = context.getContentResolver().call( 569 AUTHORITY_URI, METHOD_GET_ENHANCED_BLOCK_SETTING, null, extras); 570 return res != null && res.getBoolean(RES_ENHANCED_SETTING_IS_ENABLED, false); 571 } catch (NullPointerException | IllegalArgumentException ex) { 572 // The content resolver can throw an NPE or IAE; we don't want to crash Telecom if 573 // either of these happen. 574 Log.w(null, "getEnhancedBlockSetting: provider not ready."); 575 return false; 576 } 577 } 578 579 /** 580 * Set the enhanced block setting enabled status. 581 * 582 * @param context the context of the caller. 583 * @param key the key of the setting to set, can be 584 * {@link #ENHANCED_SETTING_KEY_BLOCK_UNREGISTERED} 585 * {@link #ENHANCED_SETTING_KEY_BLOCK_PRIVATE} 586 * {@link #ENHANCED_SETTING_KEY_BLOCK_PAYPHONE} 587 * {@link #ENHANCED_SETTING_KEY_BLOCK_UNKNOWN} 588 * {@link #ENHANCED_SETTING_KEY_BLOCK_UNAVAILABLE} 589 * {@link #ENHANCED_SETTING_KEY_SHOW_EMERGENCY_CALL_NOTIFICATION} 590 * @param value the enabled statue of the setting to set. 591 */ setEnhancedBlockSetting(Context context, String key, boolean value)592 public static void setEnhancedBlockSetting(Context context, String key, boolean value) { 593 Bundle extras = new Bundle(); 594 extras.putString(EXTRA_ENHANCED_SETTING_KEY, key); 595 extras.putBoolean(EXTRA_ENHANCED_SETTING_VALUE, value); 596 context.getContentResolver().call(AUTHORITY_URI, METHOD_SET_ENHANCED_BLOCK_SETTING, 597 null, extras); 598 } 599 600 /** 601 * Converts a block status constant to a string equivalent for logging. 602 * @hide 603 */ blockStatusToString(int blockStatus)604 public static String blockStatusToString(int blockStatus) { 605 switch (blockStatus) { 606 case STATUS_NOT_BLOCKED: 607 return "not blocked"; 608 case STATUS_BLOCKED_IN_LIST: 609 return "blocked - in list"; 610 case STATUS_BLOCKED_RESTRICTED: 611 return "blocked - restricted"; 612 case STATUS_BLOCKED_UNKNOWN_NUMBER: 613 return "blocked - unknown"; 614 case STATUS_BLOCKED_PAYPHONE: 615 return "blocked - payphone"; 616 case STATUS_BLOCKED_NOT_IN_CONTACTS: 617 return "blocked - not in contacts"; 618 case STATUS_BLOCKED_UNAVAILABLE: 619 return "blocked - unavailable"; 620 } 621 return "unknown"; 622 } 623 624 /** 625 * Represents the current status of 626 * {@link #shouldSystemBlockNumber(Context, String, Bundle)}. If emergency services 627 * have been contacted recently, {@link #isSuppressed} is {@code true}, and blocking 628 * is disabled until the timestamp {@link #untilTimestampMillis}. 629 */ 630 public static class BlockSuppressionStatus { 631 public final boolean isSuppressed; 632 /** 633 * Timestamp in milliseconds from epoch. 634 */ 635 public final long untilTimestampMillis; 636 BlockSuppressionStatus(boolean isSuppressed, long untilTimestampMillis)637 public BlockSuppressionStatus(boolean isSuppressed, long untilTimestampMillis) { 638 this.isSuppressed = isSuppressed; 639 this.untilTimestampMillis = untilTimestampMillis; 640 } 641 642 @Override toString()643 public String toString() { 644 return "[BlockSuppressionStatus; isSuppressed=" + isSuppressed + ", until=" 645 + untilTimestampMillis + "]"; 646 } 647 } 648 } 649 } 650