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 23 /** 24 * <p> 25 * The contract between the blockednumber provider and applications. Contains definitions for 26 * the supported URIs and columns. 27 * </p> 28 * 29 * <h3> Overview </h3> 30 * <p> 31 * The content provider exposes a table containing blocked numbers. The columns and URIs for 32 * accessing this table are defined by the {@link BlockedNumbers} class. Messages, and calls from 33 * blocked numbers are discarded by the platform. Notifications upon provider changes can be 34 * received using a {@link android.database.ContentObserver}. 35 * </p> 36 * <p> 37 * The platform will not block messages, and calls from emergency numbers as defined by 38 * {@link android.telephony.PhoneNumberUtils#isEmergencyNumber(String)}. If the user contacts 39 * emergency services, number blocking is disabled by the platform for a duration defined by 40 * {@link android.telephony.CarrierConfigManager#KEY_DURATION_BLOCKING_DISABLED_AFTER_EMERGENCY_INT}. 41 * </p> 42 * 43 * <h3> Permissions </h3> 44 * <p> 45 * Only the system, the default SMS application, and the default phone app 46 * (See {@link android.telecom.TelecomManager#getDefaultDialerPackage()}), and carrier apps 47 * (See {@link android.service.carrier.CarrierService}) can read, and write to the blockednumber 48 * provider. However, {@link #canCurrentUserBlockNumbers(Context)} can be accessed by any 49 * application. 50 * </p> 51 * 52 * <h3> Data </h3> 53 * <p> 54 * Other than regular phone numbers, the blocked number provider can also store addresses (such 55 * as email) from which a user can receive messages, and calls. The blocked numbers are stored 56 * in the {@link BlockedNumbers#COLUMN_ORIGINAL_NUMBER} column. A normalized version of phone 57 * numbers (if normalization is possible) is stored in {@link BlockedNumbers#COLUMN_E164_NUMBER} 58 * column. The platform blocks calls, and messages from an address if it is present in in the 59 * {@link BlockedNumbers#COLUMN_ORIGINAL_NUMBER} column or if the E164 version of the address 60 * matches the {@link BlockedNumbers#COLUMN_E164_NUMBER} column. 61 * </p> 62 * 63 * <h3> Operations </h3> 64 * <dl> 65 * <dt><b>Insert</b></dt> 66 * <dd> 67 * <p> 68 * {@link BlockedNumbers#COLUMN_ORIGINAL_NUMBER} is a required column that needs to be populated. 69 * Apps can optionally provide the {@link BlockedNumbers#COLUMN_E164_NUMBER} which is the phone 70 * number's E164 representation. The provider automatically populates this column if the app does 71 * not provide it. Note that this column is not populated if normalization fails or if the address 72 * is not a phone number (eg: email). 73 * <p> 74 * Attempting to insert an existing blocked number (same 75 * {@link BlockedNumbers#COLUMN_ORIGINAL_NUMBER} column) will result in replacing the existing 76 * blocked number. 77 * <p> 78 * Examples: 79 * <pre> 80 * ContentValues values = new ContentValues(); 81 * values.put(BlockedNumbers.COLUMN_ORIGINAL_NUMBER, "1234567890"); 82 * Uri uri = getContentResolver().insert(BlockedNumbers.CONTENT_URI, values); 83 * </pre> 84 * <pre> 85 * ContentValues values = new ContentValues(); 86 * values.put(BlockedNumbers.COLUMN_ORIGINAL_NUMBER, "1234567890"); 87 * values.put(BlockedNumbers.COLUMN_E164_NUMBER, "+11234567890"); 88 * Uri uri = getContentResolver().insert(BlockedNumbers.CONTENT_URI, values); 89 * </pre> 90 * <pre> 91 * ContentValues values = new ContentValues(); 92 * values.put(BlockedNumbers.COLUMN_ORIGINAL_NUMBER, "12345 (at) abdcde.com"); 93 * Uri uri = getContentResolver().insert(BlockedNumbers.CONTENT_URI, values); 94 * </pre> 95 * </p> 96 * </dd> 97 * <dt><b>Update</b></dt> 98 * <dd> 99 * <p> 100 * Updates are not supported. Use Delete, and Insert instead. 101 * </p> 102 * </dd> 103 * <dt><b>Delete</b></dt> 104 * <dd> 105 * <p> 106 * Deletions can be performed as follows: 107 * <pre> 108 * ContentValues values = new ContentValues(); 109 * values.put(BlockedNumbers.COLUMN_ORIGINAL_NUMBER, "1234567890"); 110 * Uri uri = getContentResolver().insert(BlockedNumbers.CONTENT_URI, values); 111 * getContentResolver().delete(uri, null, null); 112 * </pre> 113 * To check if a particular number is blocked, use the method 114 * {@link #isBlocked(Context, String)}. 115 * </p> 116 * </dd> 117 * <dt><b>Query</b></dt> 118 * <dd> 119 * <p> 120 * All blocked numbers can be enumerated as follows: 121 * <pre> 122 * Cursor c = getContentResolver().query(BlockedNumbers.CONTENT_URI, 123 * new String[]{BlockedNumbers.COLUMN_ID, BlockedNumbers.COLUMN_ORIGINAL_NUMBER, 124 * BlockedNumbers.COLUMN_E164_NUMBER}, null, null, null); 125 * </pre> 126 * </p> 127 * </dd> 128 * <dt><b>Unblock</b></dt> 129 * <dd> 130 * <p> 131 * Use the method {@link #unblock(Context, String)} to unblock numbers. 132 * </p> 133 * </dd> 134 * 135 * <h3> Multi-user </h3> 136 * <p> 137 * Apps must use the method {@link #canCurrentUserBlockNumbers(Context)} before performing any 138 * operation on the blocked number provider. If {@link #canCurrentUserBlockNumbers(Context)} returns 139 * {@code false}, all operations on the provider will fail with a {@link SecurityException}. The 140 * platform will block calls, and messages from numbers in the provider independent of the current 141 * user. 142 * </p> 143 */ 144 public class BlockedNumberContract { 145 private BlockedNumberContract() { 146 } 147 148 /** The authority for the blocked number provider */ 149 public static final String AUTHORITY = "com.android.blockednumber"; 150 151 /** A content:// style uri to the authority for the blocked number provider */ 152 public static final Uri AUTHORITY_URI = Uri.parse("content://" + AUTHORITY); 153 154 /** 155 * Constants to interact with the blocked numbers list. 156 */ 157 public static class BlockedNumbers { 158 private BlockedNumbers() { 159 } 160 161 /** 162 * Content URI for the blocked numbers. 163 * <h3> Supported operations </h3> 164 * <p> blocked 165 * <ul> 166 * <li> query 167 * <li> delete 168 * <li> insert 169 * </ul> 170 * <p> blocked/ID 171 * <ul> 172 * <li> query (selection is not supported) 173 * <li> delete (selection is not supported) 174 * </ul> 175 */ 176 public static final Uri CONTENT_URI = Uri.withAppendedPath(AUTHORITY_URI, "blocked"); 177 178 /** 179 * The MIME type of {@link #CONTENT_URI} itself providing a directory of blocked phone 180 * numbers. 181 */ 182 public static final String CONTENT_TYPE = "vnd.android.cursor.dir/blocked_number"; 183 184 /** 185 * The MIME type of a blocked phone number under {@link #CONTENT_URI}. 186 */ 187 public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/blocked_number"; 188 189 /** 190 * Auto-generated ID field which monotonically increases. 191 * <p>TYPE: long</p> 192 */ 193 public static final String COLUMN_ID = "_id"; 194 195 /** 196 * Phone number to block. 197 * <p>Must be specified in {@code insert}. 198 * <p>TYPE: String</p> 199 */ 200 public static final String COLUMN_ORIGINAL_NUMBER = "original_number"; 201 202 /** 203 * Phone number to block. The system generates it from {@link #COLUMN_ORIGINAL_NUMBER} 204 * by removing all formatting characters. 205 * <p>Optional in {@code insert}. When not specified, the system tries to generate it 206 * assuming the current country. (Which will still be null if the number is not valid.) 207 * <p>TYPE: String</p> 208 */ 209 public static final String COLUMN_E164_NUMBER = "e164_number"; 210 } 211 212 /** @hide */ 213 public static final String METHOD_IS_BLOCKED = "is_blocked"; 214 215 /** @hide */ 216 public static final String METHOD_UNBLOCK= "unblock"; 217 218 /** @hide */ 219 public static final String RES_NUMBER_IS_BLOCKED = "blocked"; 220 221 /** @hide */ 222 public static final String RES_NUM_ROWS_DELETED = "num_deleted"; 223 224 /** @hide */ 225 public static final String METHOD_CAN_CURRENT_USER_BLOCK_NUMBERS = 226 "can_current_user_block_numbers"; 227 228 /** @hide */ 229 public static final String RES_CAN_BLOCK_NUMBERS = "can_block"; 230 231 /** 232 * Returns whether a given number is in the blocked list. 233 * 234 * <p> This matches the {@code phoneNumber} against the 235 * {@link BlockedNumbers#COLUMN_ORIGINAL_NUMBER} column, and the E164 representation of the 236 * {@code phoneNumber} with the {@link BlockedNumbers#COLUMN_E164_NUMBER} column. 237 * 238 * <p> Note that if the {@link #canCurrentUserBlockNumbers} is {@code false} for the user 239 * context {@code context}, this method will throw a {@link SecurityException}. 240 * 241 * @return {@code true} if the {@code phoneNumber} is blocked. 242 */ 243 @WorkerThread 244 public static boolean isBlocked(Context context, String phoneNumber) { 245 final Bundle res = context.getContentResolver().call( 246 AUTHORITY_URI, METHOD_IS_BLOCKED, phoneNumber, null); 247 return res != null && res.getBoolean(RES_NUMBER_IS_BLOCKED, false); 248 } 249 250 /** 251 * Unblocks the {@code phoneNumber} if it is blocked. 252 * 253 * <p> This deletes all rows where the {@code phoneNumber} matches the 254 * {@link BlockedNumbers#COLUMN_ORIGINAL_NUMBER} column or the E164 representation of the 255 * {@code phoneNumber} matches the {@link BlockedNumbers#COLUMN_E164_NUMBER} column. 256 * 257 * <p>To delete rows based on exact match with specific columns such as 258 * {@link BlockedNumbers#COLUMN_ID} use 259 * {@link android.content.ContentProvider#delete(Uri, String, String[])} with 260 * {@link BlockedNumbers#CONTENT_URI} URI. 261 * 262 * <p> Note that if the {@link #canCurrentUserBlockNumbers} is {@code false} for the user 263 * context {@code context}, this method will throw a {@link SecurityException}. 264 * 265 * @return the number of rows deleted in the blocked number provider as a result of unblock. 266 */ 267 @WorkerThread 268 public static int unblock(Context context, String phoneNumber) { 269 final Bundle res = context.getContentResolver().call( 270 AUTHORITY_URI, METHOD_UNBLOCK, phoneNumber, null); 271 return res.getInt(RES_NUM_ROWS_DELETED, 0); 272 } 273 274 /** 275 * Checks if blocking numbers is supported for the current user. 276 * <p> Typically, blocking numbers is only supported for one user at a time. 277 * 278 * @return {@code true} if the current user can block numbers. 279 */ 280 public static boolean canCurrentUserBlockNumbers(Context context) { 281 final Bundle res = context.getContentResolver().call( 282 AUTHORITY_URI, METHOD_CAN_CURRENT_USER_BLOCK_NUMBERS, null, null); 283 return res != null && res.getBoolean(RES_CAN_BLOCK_NUMBERS, false); 284 } 285 286 /** 287 * <p> 288 * The contract between the blockednumber provider and the system. 289 * </p> 290 * <p>This is a wrapper over {@link BlockedNumberContract} that also manages the blocking 291 * behavior when the user contacts emergency services. See 292 * {@link #notifyEmergencyContact(Context)} for details. All methods are protected by 293 * {@link android.Manifest.permission#READ_BLOCKED_NUMBERS} and 294 * {@link android.Manifest.permission#WRITE_BLOCKED_NUMBERS} appropriately which ensure that 295 * only system can access the methods defined here. 296 * </p> 297 * @hide 298 */ 299 public static class SystemContract { 300 /** 301 * A protected broadcast intent action for letting components with 302 * {@link android.Manifest.permission#READ_BLOCKED_NUMBERS} know that the block suppression 303 * status as returned by {@link #getBlockSuppressionStatus(Context)} has been updated. 304 */ 305 public static final String ACTION_BLOCK_SUPPRESSION_STATE_CHANGED = 306 "android.provider.action.BLOCK_SUPPRESSION_STATE_CHANGED"; 307 308 public static final String METHOD_NOTIFY_EMERGENCY_CONTACT = "notify_emergency_contact"; 309 310 public static final String METHOD_END_BLOCK_SUPPRESSION = "end_block_suppression"; 311 312 public static final String METHOD_SHOULD_SYSTEM_BLOCK_NUMBER = "should_system_block_number"; 313 314 public static final String METHOD_GET_BLOCK_SUPPRESSION_STATUS = 315 "get_block_suppression_status"; 316 317 public static final String RES_IS_BLOCKING_SUPPRESSED = "blocking_suppressed"; 318 319 public static final String RES_BLOCKING_SUPPRESSED_UNTIL_TIMESTAMP = 320 "blocking_suppressed_until_timestamp"; 321 322 /** 323 * Notifies the provider that emergency services were contacted by the user. 324 * <p> This results in {@link #shouldSystemBlockNumber} returning {@code false} independent 325 * of the contents of the provider for a duration defined by 326 * {@link android.telephony.CarrierConfigManager#KEY_DURATION_BLOCKING_DISABLED_AFTER_EMERGENCY_INT} 327 * the provider unless {@link #endBlockSuppression(Context)} is called. 328 */ 329 public static void notifyEmergencyContact(Context context) { 330 context.getContentResolver().call( 331 AUTHORITY_URI, METHOD_NOTIFY_EMERGENCY_CONTACT, null, null); 332 } 333 334 /** 335 * Notifies the provider to disable suppressing blocking. If emergency services were not 336 * contacted recently at all, calling this method is a no-op. 337 */ 338 public static void endBlockSuppression(Context context) { 339 context.getContentResolver().call( 340 AUTHORITY_URI, METHOD_END_BLOCK_SUPPRESSION, null, null); 341 } 342 343 /** 344 * Returns {@code true} if {@code phoneNumber} is blocked taking 345 * {@link #notifyEmergencyContact(Context)} into consideration. If emergency services have 346 * not been contacted recently, this method is equivalent to 347 * {@link #isBlocked(Context, String)}. 348 */ 349 public static boolean shouldSystemBlockNumber(Context context, String phoneNumber) { 350 final Bundle res = context.getContentResolver().call( 351 AUTHORITY_URI, METHOD_SHOULD_SYSTEM_BLOCK_NUMBER, phoneNumber, null); 352 return res != null && res.getBoolean(RES_NUMBER_IS_BLOCKED, false); 353 } 354 355 /** 356 * Returns the current status of block suppression. 357 */ 358 public static BlockSuppressionStatus getBlockSuppressionStatus(Context context) { 359 final Bundle res = context.getContentResolver().call( 360 AUTHORITY_URI, METHOD_GET_BLOCK_SUPPRESSION_STATUS, null, null); 361 return new BlockSuppressionStatus(res.getBoolean(RES_IS_BLOCKING_SUPPRESSED, false), 362 res.getLong(RES_BLOCKING_SUPPRESSED_UNTIL_TIMESTAMP, 0)); 363 } 364 365 /** 366 * Represents the current status of {@link #shouldSystemBlockNumber(Context, String)}. If 367 * emergency services have been contacted recently, {@link #isSuppressed} is {@code true}, 368 * and blocking is disabled until the timestamp {@link #untilTimestampMillis}. 369 */ 370 public static class BlockSuppressionStatus { 371 public final boolean isSuppressed; 372 /** 373 * Timestamp in milliseconds from epoch. 374 */ 375 public final long untilTimestampMillis; 376 377 public BlockSuppressionStatus(boolean isSuppressed, long untilTimestampMillis) { 378 this.isSuppressed = isSuppressed; 379 this.untilTimestampMillis = untilTimestampMillis; 380 } 381 } 382 } 383 } 384