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