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