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