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