blob: 0439fe247ee1d028ce6881f61de75d80a342de7d [file] [log] [blame]
Makoto Onukicbd0ed22016-01-11 14:05:15 -08001/*
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/**
Abhijith Shastrydc4535e2016-02-04 16:33:32 -080023 * <p>
24 * The contract between the blockednumber provider and applications. Contains definitions for
25 * the supported URIs and columns.
26 * </p>
Makoto Onukicbd0ed22016-01-11 14:05:15 -080027 *
Abhijith Shastrydc4535e2016-02-04 16:33:32 -080028 * <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
Abhijith Shastry575a9492016-02-29 14:32:57 -080032 * blocked numbers are discarded by the platform. If the user contacts emergency
33 * services, number blocking is disabled by the platform for a duration defined by
34 * {@link android.telephony.CarrierConfigManager#KEY_DURATION_BLOCKING_DISABLED_AFTER_EMERGENCY_INT}.
35 * Notifications upon provider changes can be received using a
36 * {@link android.database.ContentObserver}.
Abhijith Shastrydc4535e2016-02-04 16:33:32 -080037 * </p>
38 *
39 * <h3> Permissions </h3>
40 * <p>
41 * Only the system, the default SMS application, and the default phone app
42 * (See {@link android.telecom.TelecomManager#getDefaultDialerPackage()}), and carrier apps
43 * (See {@link android.service.carrier.CarrierService}) can read, and write to the blockednumber
44 * provider.
45 * </p>
46 *
47 * <h3> Data </h3>
48 * <p>
49 * Other than regular phone numbers, the blocked number provider can also store addresses (such
50 * as email) from which a user can receive messages, and calls. The blocked numbers are stored
51 * in the {@link BlockedNumbers#COLUMN_ORIGINAL_NUMBER} column. A normalized version of phone
52 * numbers (if normalization is possible) is stored in {@link BlockedNumbers#COLUMN_E164_NUMBER}
53 * column. The platform blocks calls, and messages from an address if it is present in in the
54 * {@link BlockedNumbers#COLUMN_ORIGINAL_NUMBER} column or if the E164 version of the address
55 * matches the {@link BlockedNumbers#COLUMN_E164_NUMBER} column.
56 * </p>
57 *
58 * <h3> Operations </h3>
59 * <dl>
60 * <dt><b>Insert</b></dt>
61 * <dd>
62 * <p>
63 * {@link BlockedNumbers#COLUMN_ORIGINAL_NUMBER} is a required column that needs to be populated.
64 * Apps can optionally provide the {@link BlockedNumbers#COLUMN_E164_NUMBER} which is the phone
65 * number's E164 representation. The provider automatically populates this column if the app does
66 * not provide it. Note that this column is not populated if normalization fails or if the address
67 * is not a phone number (eg: email). The provider enforces uniqueness constraint on this column.
68 * Examples:
69 * <pre>
70 * ContentValues values = new ContentValues();
71 * values.put(BlockedNumbers.COLUMN_ORIGINAL_NUMBER, "1234567890");
72 * Uri uri = getContentResolver().insert(BlockedNumbers.CONTENT_URI, values);
73 * </pre>
74 * <pre>
75 * ContentValues values = new ContentValues();
76 * values.put(BlockedNumbers.COLUMN_ORIGINAL_NUMBER, "1234567890");
77 * values.put(BlockedNumbers.COLUMN_E164_NUMBER, "+11234567890");
78 * Uri uri = getContentResolver().insert(BlockedNumbers.CONTENT_URI, values);
79 * </pre>
80 * <pre>
81 * ContentValues values = new ContentValues();
82 * values.put(BlockedNumbers.COLUMN_ORIGINAL_NUMBER, "12345@abdcde.com");
83 * Uri uri = getContentResolver().insert(BlockedNumbers.CONTENT_URI, values);
84 * </pre>
85 * </p>
86 * </dd>
87 * <dt><b>Update</b></dt>
88 * <dd>
89 * <p>
90 * Updates are not supported. Use Delete, and Insert instead.
91 * </p>
92 * </dd>
93 * <dt><b>Delete</b></dt>
94 * <dd>
95 * <p>
96 * Deletions can be performed as follows:
97 * <pre>
98 * ContentValues values = new ContentValues();
99 * values.put(BlockedNumbers.COLUMN_ORIGINAL_NUMBER, "1234567890");
100 * Uri uri = getContentResolver().insert(BlockedNumbers.CONTENT_URI, values);
101 * getContentResolver().delete(uri, null, null);
102 * </pre>
103 * </p>
104 * </dd>
105 * <dt><b>Query</b></dt>
106 * <dd>
107 * <p>
108 * All blocked numbers can be enumerated as follows:
109 * <pre>
110 * Cursor c = getContentResolver().query(BlockedNumbers.CONTENT_URI,
111 * new String[]{BlockedNumbers.COLUMN_ID, BlockedNumbers.COLUMN_ORIGINAL_NUMBER,
112 * BlockedNumbers.COLUMN_E164_NUMBER}, null, 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 *
119 * <h3> Multi-user </h3>
120 * <p>
121 * Apps must use the method {@link #canCurrentUserBlockNumbers(Context)} before performing any
122 * operation on the blocked number provider. If {@link #canCurrentUserBlockNumbers(Context)} returns
123 * {@code false}, all operations on the provider will fail with an
124 * {@link UnsupportedOperationException}. The platform will block calls, and messages from numbers
125 * in the provider independent of the current user.
126 * </p>
Makoto Onukicbd0ed22016-01-11 14:05:15 -0800127 */
128public class BlockedNumberContract {
129 private BlockedNumberContract() {
130 }
131
Abhijith Shastrydc4535e2016-02-04 16:33:32 -0800132 /** The authority for the blocked number provider */
Makoto Onukicbd0ed22016-01-11 14:05:15 -0800133 public static final String AUTHORITY = "com.android.blockednumber";
134
Abhijith Shastrydc4535e2016-02-04 16:33:32 -0800135 /** A content:// style uri to the authority for the blocked number provider */
Makoto Onukicbd0ed22016-01-11 14:05:15 -0800136 public static final Uri AUTHORITY_URI = Uri.parse("content://" + AUTHORITY);
137
138 /**
Abhijith Shastrydc4535e2016-02-04 16:33:32 -0800139 * Constants to interact with the blocked numbers list.
Makoto Onukicbd0ed22016-01-11 14:05:15 -0800140 */
141 public static class BlockedNumbers {
142 private BlockedNumbers() {
143 }
144
145 /**
Makoto Onukicbd0ed22016-01-11 14:05:15 -0800146 * Content URI for the blocked numbers.
Abhijith Shastry575a9492016-02-29 14:32:57 -0800147 * <h3> Supported operations </h3>
148 * <p> blocked
149 * <ul>
150 * <li> query
151 * <li> delete
152 * <li> insert
153 * </ul>
154 * <p> blocked/ID
155 * <ul>
156 * <li> query (selection is not supported)
157 * <li> delete (selection is not supported)
158 * </ul>
Makoto Onukicbd0ed22016-01-11 14:05:15 -0800159 */
Abhijith Shastry575a9492016-02-29 14:32:57 -0800160 public static final Uri CONTENT_URI = Uri.withAppendedPath(AUTHORITY_URI, "blocked");
Makoto Onukicbd0ed22016-01-11 14:05:15 -0800161
162 /**
163 * The MIME type of {@link #CONTENT_URI} itself providing a directory of blocked phone
164 * numbers.
165 */
Abhijith Shastry575a9492016-02-29 14:32:57 -0800166 public static final String CONTENT_TYPE = "vnd.android.cursor.dir/blocked_number";
Makoto Onukicbd0ed22016-01-11 14:05:15 -0800167
168 /**
169 * The MIME type of a blocked phone number under {@link #CONTENT_URI}.
170 */
171 public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/blocked_number";
172
173 /**
174 * Auto-generated ID field which monotonically increases.
175 * <p>TYPE: long</p>
176 */
177 public static final String COLUMN_ID = "_id";
178
179 /**
180 * Phone number to block.
181 * <p>Must be specified in {@code insert}.
182 * <p>TYPE: String</p>
183 */
184 public static final String COLUMN_ORIGINAL_NUMBER = "original_number";
185
186 /**
187 * Phone number to block. The system generates it from {@link #COLUMN_ORIGINAL_NUMBER}
188 * by removing all formatting characters.
Makoto Onukicbd0ed22016-01-11 14:05:15 -0800189 * <p>Optional in {@code insert}. When not specified, the system tries to generate it
190 * assuming the current country. (Which will still be null if the number is not valid.)
191 * <p>TYPE: String</p>
192 */
193 public static final String COLUMN_E164_NUMBER = "e164_number";
Makoto Onukicbd0ed22016-01-11 14:05:15 -0800194 }
195
196 /** @hide */
197 public static final String METHOD_IS_BLOCKED = "is_blocked";
198
199 /** @hide */
200 public static final String RES_NUMBER_IS_BLOCKED = "blocked";
201
Abhijith Shastryd907c642016-01-28 13:27:08 -0800202 /** @hide */
203 public static final String METHOD_CAN_CURRENT_USER_BLOCK_NUMBERS =
204 "can_current_user_block_numbers";
205
206 /** @hide */
207 public static final String RES_CAN_BLOCK_NUMBERS = "can_block";
208
Makoto Onukicbd0ed22016-01-11 14:05:15 -0800209 /**
210 * Returns whether a given number is in the blocked list.
Abhijith Shastryd907c642016-01-28 13:27:08 -0800211 * <p> Note that if the {@link #canCurrentUserBlockNumbers} is {@code false} for the user
212 * context {@code context}, this method will throw an {@link UnsupportedOperationException}.
Makoto Onukicbd0ed22016-01-11 14:05:15 -0800213 */
214 public static boolean isBlocked(Context context, String phoneNumber) {
Abhijith Shastrydc4535e2016-02-04 16:33:32 -0800215 final Bundle res = context.getContentResolver().call(
216 AUTHORITY_URI, METHOD_IS_BLOCKED, phoneNumber, null);
Makoto Onukicbd0ed22016-01-11 14:05:15 -0800217 return res != null && res.getBoolean(RES_NUMBER_IS_BLOCKED, false);
218 }
Abhijith Shastryd907c642016-01-28 13:27:08 -0800219
220 /**
221 * Returns {@code true} if blocking numbers is supported for the current user.
Abhijith Shastry575a9492016-02-29 14:32:57 -0800222 * <p> Typically, blocking numbers is only supported for one user at a time.
Abhijith Shastryd907c642016-01-28 13:27:08 -0800223 */
224 public static boolean canCurrentUserBlockNumbers(Context context) {
225 final Bundle res = context.getContentResolver().call(
226 AUTHORITY_URI, METHOD_CAN_CURRENT_USER_BLOCK_NUMBERS, null, null);
227 return res != null && res.getBoolean(RES_CAN_BLOCK_NUMBERS, false);
228 }
229
Abhijith Shastryb87c90c2016-02-10 11:15:08 -0800230 /**
231 * <p>
232 * The contract between the blockednumber provider and the system.
233 * </p>
234 * <p>This is a wrapper over {@link BlockedNumberContract} that also manages the blocking
235 * behavior when the user contacts emergency services. See
236 * {@link #notifyEmergencyContact(Context)} for details. All methods are protected by
237 * {@link android.Manifest.permission#READ_BLOCKED_NUMBERS} and
238 * {@link android.Manifest.permission#WRITE_BLOCKED_NUMBERS} appropriately which ensure that
239 * only system can access the methods defined here.
240 * </p>
241 * @hide
242 */
243 public static class SystemContract {
244 /**
245 * A protected broadcast intent action for letting components with
Abhijith Shastry4478fdc2016-02-22 16:07:14 -0800246 * {@link android.Manifest.permission#READ_BLOCKED_NUMBERS} know that the block suppression
247 * status as returned by {@link #getBlockSuppressionStatus(Context)} has been updated.
Abhijith Shastryb87c90c2016-02-10 11:15:08 -0800248 */
Abhijith Shastry4478fdc2016-02-22 16:07:14 -0800249 public static final String ACTION_BLOCK_SUPPRESSION_STATE_CHANGED =
250 "android.provider.action.BLOCK_SUPPRESSION_STATE_CHANGED";
Abhijith Shastryb87c90c2016-02-10 11:15:08 -0800251
252 public static final String METHOD_NOTIFY_EMERGENCY_CONTACT = "notify_emergency_contact";
253
Abhijith Shastry4478fdc2016-02-22 16:07:14 -0800254 public static final String METHOD_END_BLOCK_SUPPRESSION = "end_block_suppression";
Abhijith Shastryb87c90c2016-02-10 11:15:08 -0800255
256 public static final String METHOD_SHOULD_SYSTEM_BLOCK_NUMBER = "should_system_block_number";
257
Abhijith Shastry4478fdc2016-02-22 16:07:14 -0800258 public static final String METHOD_GET_BLOCK_SUPPRESSION_STATUS =
259 "get_block_suppression_status";
Abhijith Shastryb87c90c2016-02-10 11:15:08 -0800260
261 public static final String RES_IS_BLOCKING_SUPPRESSED = "blocking_suppressed";
262
263 public static final String RES_BLOCKING_SUPPRESSED_UNTIL_TIMESTAMP =
264 "blocking_suppressed_until_timestamp";
265
266 /**
267 * Notifies the provider that emergency services were contacted by the user.
268 * <p> This results in {@link #shouldSystemBlockNumber} returning {@code false} independent
269 * of the contents of the provider for a duration defined by
270 * {@link android.telephony.CarrierConfigManager#KEY_DURATION_BLOCKING_DISABLED_AFTER_EMERGENCY_INT}
Abhijith Shastry4478fdc2016-02-22 16:07:14 -0800271 * the provider unless {@link #endBlockSuppression(Context)} is called.
Abhijith Shastryb87c90c2016-02-10 11:15:08 -0800272 */
273 public static void notifyEmergencyContact(Context context) {
274 context.getContentResolver().call(
275 AUTHORITY_URI, METHOD_NOTIFY_EMERGENCY_CONTACT, null, null);
276 }
277
278 /**
279 * Notifies the provider to disable suppressing blocking. If emergency services were not
280 * contacted recently at all, calling this method is a no-op.
281 */
Abhijith Shastry4478fdc2016-02-22 16:07:14 -0800282 public static void endBlockSuppression(Context context) {
Abhijith Shastryb87c90c2016-02-10 11:15:08 -0800283 context.getContentResolver().call(
Abhijith Shastry4478fdc2016-02-22 16:07:14 -0800284 AUTHORITY_URI, METHOD_END_BLOCK_SUPPRESSION, null, null);
Abhijith Shastryb87c90c2016-02-10 11:15:08 -0800285 }
286
287 /**
288 * Returns {@code true} if {@code phoneNumber} is blocked taking
289 * {@link #notifyEmergencyContact(Context)} into consideration. If emergency services have
290 * not been contacted recently, this method is equivalent to
291 * {@link #isBlocked(Context, String)}.
292 */
293 public static boolean shouldSystemBlockNumber(Context context, String phoneNumber) {
294 final Bundle res = context.getContentResolver().call(
295 AUTHORITY_URI, METHOD_SHOULD_SYSTEM_BLOCK_NUMBER, phoneNumber, null);
296 return res != null && res.getBoolean(RES_NUMBER_IS_BLOCKED, false);
297 }
298
Abhijith Shastry575a9492016-02-29 14:32:57 -0800299 /**
300 * Returns the current status of block suppression.
301 */
Abhijith Shastry4478fdc2016-02-22 16:07:14 -0800302 public static BlockSuppressionStatus getBlockSuppressionStatus(Context context) {
Abhijith Shastryb87c90c2016-02-10 11:15:08 -0800303 final Bundle res = context.getContentResolver().call(
Abhijith Shastry4478fdc2016-02-22 16:07:14 -0800304 AUTHORITY_URI, METHOD_GET_BLOCK_SUPPRESSION_STATUS, null, null);
305 return new BlockSuppressionStatus(res.getBoolean(RES_IS_BLOCKING_SUPPRESSED, false),
Abhijith Shastryb87c90c2016-02-10 11:15:08 -0800306 res.getLong(RES_BLOCKING_SUPPRESSED_UNTIL_TIMESTAMP, 0));
307 }
308
309 /**
310 * Represents the current status of {@link #shouldSystemBlockNumber(Context, String)}. If
311 * emergency services have been contacted recently, {@link #isSuppressed} is {@code true},
Abhijith Shastry4478fdc2016-02-22 16:07:14 -0800312 * and blocking is disabled until the timestamp {@link #untilTimestampMillis}.
Abhijith Shastryb87c90c2016-02-10 11:15:08 -0800313 */
Abhijith Shastry4478fdc2016-02-22 16:07:14 -0800314 public static class BlockSuppressionStatus {
Abhijith Shastryb87c90c2016-02-10 11:15:08 -0800315 public final boolean isSuppressed;
316 /**
317 * Timestamp in milliseconds from epoch.
318 */
319 public final long untilTimestampMillis;
320
Abhijith Shastry4478fdc2016-02-22 16:07:14 -0800321 public BlockSuppressionStatus(boolean isSuppressed, long untilTimestampMillis) {
Abhijith Shastryb87c90c2016-02-10 11:15:08 -0800322 this.isSuppressed = isSuppressed;
323 this.untilTimestampMillis = untilTimestampMillis;
324 }
325 }
326 }
Makoto Onukicbd0ed22016-01-11 14:05:15 -0800327}