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.IntDef;
19 import android.annotation.WorkerThread;
20 import android.content.Context;
21 import android.net.Uri;
22 import android.os.Bundle;
23 import android.telecom.Log;
24 
25 import java.lang.annotation.Retention;
26 import java.lang.annotation.RetentionPolicy;
27 
28 /**
29  * <p>
30  * The contract between the blockednumber provider and applications. Contains definitions for
31  * the supported URIs and columns.
32  * </p>
33  *
34  * <h3> Overview </h3>
35  * <p>
36  * The content provider exposes a table containing blocked numbers. The columns and URIs for
37  * accessing this table are defined by the {@link BlockedNumbers} class. Messages, and calls from
38  * blocked numbers are discarded by the platform. Notifications upon provider changes can be
39  * received using a {@link android.database.ContentObserver}.
40  * </p>
41  * <p>
42  * The platform will not block messages, and calls from emergency numbers as defined by
43  * {@link android.telephony.PhoneNumberUtils#isEmergencyNumber(String)}. If the user contacts
44  * emergency services, number blocking is disabled by the platform for a duration defined by
45  * {@link android.telephony.CarrierConfigManager#KEY_DURATION_BLOCKING_DISABLED_AFTER_EMERGENCY_INT}.
46  * </p>
47  *
48  * <h3> Permissions </h3>
49  * <p>
50  * Only the system, the default SMS application, and the default phone app
51  * (See {@link android.telecom.TelecomManager#getDefaultDialerPackage()}), and carrier apps
52  * (See {@link android.service.carrier.CarrierService}) can read, and write to the blockednumber
53  * provider. However, {@link #canCurrentUserBlockNumbers(Context)} can be accessed by any
54  * application.
55  * </p>
56  *
57  * <h3> Data </h3>
58  * <p>
59  * Other than regular phone numbers, the blocked number provider can also store addresses (such
60  * as email) from which a user can receive messages, and calls. The blocked numbers are stored
61  * in the {@link BlockedNumbers#COLUMN_ORIGINAL_NUMBER} column. A normalized version of phone
62  * numbers (if normalization is possible) is stored in {@link BlockedNumbers#COLUMN_E164_NUMBER}
63  * column. The platform blocks calls, and messages from an address if it is present in in the
64  * {@link BlockedNumbers#COLUMN_ORIGINAL_NUMBER} column or if the E164 version of the address
65  * matches the {@link BlockedNumbers#COLUMN_E164_NUMBER} column.
66  * </p>
67  *
68  * <h3> Operations </h3>
69  * <dl>
70  * <dt><b>Insert</b></dt>
71  * <dd>
72  * <p>
73  * {@link BlockedNumbers#COLUMN_ORIGINAL_NUMBER} is a required column that needs to be populated.
74  * Apps can optionally provide the {@link BlockedNumbers#COLUMN_E164_NUMBER} which is the phone
75  * number's E164 representation. The provider automatically populates this column if the app does
76  * not provide it. Note that this column is not populated if normalization fails or if the address
77  * is not a phone number (eg: email).
78  * <p>
79  * Attempting to insert an existing blocked number (same
80  * {@link BlockedNumbers#COLUMN_ORIGINAL_NUMBER} column) will result in replacing the existing
81  * blocked number.
82  * <p>
83  * Examples:
84  * <pre>
85  * ContentValues values = new ContentValues();
86  * values.put(BlockedNumbers.COLUMN_ORIGINAL_NUMBER, "1234567890");
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, "1234567890");
92  * values.put(BlockedNumbers.COLUMN_E164_NUMBER, "+11234567890");
93  * Uri uri = getContentResolver().insert(BlockedNumbers.CONTENT_URI, values);
94  * </pre>
95  * <pre>
96  * ContentValues values = new ContentValues();
97  * values.put(BlockedNumbers.COLUMN_ORIGINAL_NUMBER, "12345@abdcde.com");
98  * Uri uri = getContentResolver().insert(BlockedNumbers.CONTENT_URI, values);
99  * </pre>
100  * </p>
101  * </dd>
102  * <dt><b>Update</b></dt>
103  * <dd>
104  * <p>
105  * Updates are not supported. Use Delete, and Insert instead.
106  * </p>
107  * </dd>
108  * <dt><b>Delete</b></dt>
109  * <dd>
110  * <p>
111  * Deletions can be performed as follows:
112  * <pre>
113  * ContentValues values = new ContentValues();
114  * values.put(BlockedNumbers.COLUMN_ORIGINAL_NUMBER, "1234567890");
115  * Uri uri = getContentResolver().insert(BlockedNumbers.CONTENT_URI, values);
116  * getContentResolver().delete(uri, null, null);
117  * </pre>
118  * To check if a particular number is blocked, use the method
119  * {@link #isBlocked(Context, String)}.
120  * </p>
121  * </dd>
122  * <dt><b>Query</b></dt>
123  * <dd>
124  * <p>
125  * All blocked numbers can be enumerated as follows:
126  * <pre>
127  * Cursor c = getContentResolver().query(BlockedNumbers.CONTENT_URI,
128  *          new String[]{BlockedNumbers.COLUMN_ID, BlockedNumbers.COLUMN_ORIGINAL_NUMBER,
129  *          BlockedNumbers.COLUMN_E164_NUMBER}, null, null, null);
130  * </pre>
131  * </p>
132  * </dd>
133  * <dt><b>Unblock</b></dt>
134  * <dd>
135  * <p>
136  * Use the method {@link #unblock(Context, String)} to unblock numbers.
137  * </p>
138  * </dd>
139  *
140  * <h3> Multi-user </h3>
141  * <p>
142  * Apps must use the method {@link #canCurrentUserBlockNumbers(Context)} before performing any
143  * operation on the blocked number provider. If {@link #canCurrentUserBlockNumbers(Context)} returns
144  * {@code false}, all operations on the provider will fail with a {@link SecurityException}. The
145  * platform will block calls, and messages from numbers in the provider independent of the current
146  * user.
147  * </p>
148  */
149 public class BlockedNumberContract {
BlockedNumberContract()150     private BlockedNumberContract() {
151     }
152 
153     /** The authority for the blocked number provider */
154     public static final String AUTHORITY = "com.android.blockednumber";
155 
156     /** A content:// style uri to the authority for the blocked number provider */
157     public static final Uri AUTHORITY_URI = Uri.parse("content://" + AUTHORITY);
158 
159     private static final String LOG_TAG = BlockedNumberContract.class.getSimpleName();
160 
161     /**
162      * Constants to interact with the blocked numbers list.
163      */
164     public static class BlockedNumbers {
BlockedNumbers()165         private BlockedNumbers() {
166         }
167 
168         /**
169          * Content URI for the blocked numbers.
170          * <h3> Supported operations </h3>
171          * <p> blocked
172          * <ul>
173          * <li> query
174          * <li> delete
175          * <li> insert
176          * </ul>
177          * <p> blocked/ID
178          * <ul>
179          * <li> query (selection is not supported)
180          * <li> delete (selection is not supported)
181          * </ul>
182          */
183         public static final Uri CONTENT_URI = Uri.withAppendedPath(AUTHORITY_URI, "blocked");
184 
185         /**
186          * The MIME type of {@link #CONTENT_URI} itself providing a directory of blocked phone
187          * numbers.
188          */
189         public static final String CONTENT_TYPE = "vnd.android.cursor.dir/blocked_number";
190 
191         /**
192          * The MIME type of a blocked phone number under {@link #CONTENT_URI}.
193          */
194         public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/blocked_number";
195 
196         /**
197          * Auto-generated ID field which monotonically increases.
198          * <p>TYPE: long</p>
199          */
200         public static final String COLUMN_ID = "_id";
201 
202         /**
203          * Phone number to block.
204          * <p>Must be specified in {@code insert}.
205          * <p>TYPE: String</p>
206          */
207         public static final String COLUMN_ORIGINAL_NUMBER = "original_number";
208 
209         /**
210          * Phone number to block.  The system generates it from {@link #COLUMN_ORIGINAL_NUMBER}
211          * by removing all formatting characters.
212          * <p>Optional in {@code insert}.  When not specified, the system tries to generate it
213          * assuming the current country. (Which will still be null if the number is not valid.)
214          * <p>TYPE: String</p>
215          */
216         public static final String COLUMN_E164_NUMBER = "e164_number";
217     }
218 
219     /** @hide */
220     public static final String METHOD_IS_BLOCKED = "is_blocked";
221 
222     /** @hide */
223     public static final String METHOD_UNBLOCK= "unblock";
224 
225     /** @hide */
226     public static final String RES_NUMBER_IS_BLOCKED = "blocked";
227 
228     /** @hide */
229     @Retention(RetentionPolicy.SOURCE)
230     @IntDef(
231             prefix = { "STATUS_" },
232             value = {STATUS_NOT_BLOCKED, STATUS_BLOCKED_IN_LIST, STATUS_BLOCKED_RESTRICTED,
233                     STATUS_BLOCKED_UNKNOWN_NUMBER, STATUS_BLOCKED_PAYPHONE,
234                     STATUS_BLOCKED_NOT_IN_CONTACTS, STATUS_BLOCKED_UNAVAILABLE})
235     public @interface BlockStatus {}
236 
237     /**
238      * Integer reason code used with {@link #RES_BLOCK_STATUS} to indicate that a call was not
239      * blocked.
240      * @hide
241      */
242     public static final int STATUS_NOT_BLOCKED = 0;
243 
244     /**
245      * Integer reason code used with {@link #RES_BLOCK_STATUS} to indicate that a call was blocked
246      * because it is in the list of blocked numbers maintained by the provider.
247      * @hide
248      */
249     public static final int STATUS_BLOCKED_IN_LIST = 1;
250 
251     /**
252      * Integer reason code used with {@link #RES_BLOCK_STATUS} to indicate that a call was blocked
253      * because it is from a restricted number.
254      * @hide
255      */
256     public static final int STATUS_BLOCKED_RESTRICTED = 2;
257 
258     /**
259      * Integer reason code used with {@link #RES_BLOCK_STATUS} to indicate that a call was blocked
260      * because it is from an unknown number.
261      * @hide
262      */
263     public static final int STATUS_BLOCKED_UNKNOWN_NUMBER = 3;
264 
265     /**
266      * Integer reason code used with {@link #RES_BLOCK_STATUS} to indicate that a call was blocked
267      * because it is from a pay phone.
268      * @hide
269      */
270     public static final int STATUS_BLOCKED_PAYPHONE = 4;
271 
272     /**
273      * Integer reason code used with {@link #RES_BLOCK_STATUS} to indicate that a call was blocked
274      * because it is from a number not in the users contacts.
275      * @hide
276      */
277     public static final int STATUS_BLOCKED_NOT_IN_CONTACTS = 5;
278 
279     /**
280      * Integer reason code used with {@link #RES_BLOCK_STATUS} to indicate that a call was blocked
281      * because it is from a number not available.
282      * @hide
283      */
284     public static final int STATUS_BLOCKED_UNAVAILABLE = 6;
285 
286     /**
287      * Integer reason indicating whether a call was blocked, and if so why.
288      * @hide
289      */
290     public static final String RES_BLOCK_STATUS = "block_status";
291 
292     /** @hide */
293     public static final String RES_NUM_ROWS_DELETED = "num_deleted";
294 
295     /** @hide */
296     public static final String METHOD_CAN_CURRENT_USER_BLOCK_NUMBERS =
297             "can_current_user_block_numbers";
298 
299     /** @hide */
300     public static final String RES_CAN_BLOCK_NUMBERS = "can_block";
301 
302     /** @hide */
303     public static final String RES_ENHANCED_SETTING_IS_ENABLED = "enhanced_setting_enabled";
304 
305     /** @hide */
306     public static final String RES_SHOW_EMERGENCY_CALL_NOTIFICATION =
307             "show_emergency_call_notification";
308 
309     /** @hide */
310     public static final String EXTRA_ENHANCED_SETTING_KEY = "extra_enhanced_setting_key";
311 
312     /** @hide */
313     public static final String EXTRA_ENHANCED_SETTING_VALUE = "extra_enhanced_setting_value";
314 
315     /** @hide */
316     public static final String EXTRA_CONTACT_EXIST = "extra_contact_exist";
317 
318     /** @hide */
319     public static final String EXTRA_CALL_PRESENTATION = "extra_call_presentation";
320 
321     /**
322      * Returns whether a given number is in the blocked list.
323      *
324      * <p> This matches the {@code phoneNumber} against the
325      * {@link BlockedNumbers#COLUMN_ORIGINAL_NUMBER} column, and the E164 representation of the
326      * {@code phoneNumber} with the {@link BlockedNumbers#COLUMN_E164_NUMBER} column.
327      *
328      * <p> Note that if the {@link #canCurrentUserBlockNumbers} is {@code false} for the user
329      * context {@code context}, this method will throw a {@link SecurityException}.
330      *
331      * @return {@code true} if the {@code phoneNumber} is blocked.
332      */
333     @WorkerThread
isBlocked(Context context, String phoneNumber)334     public static boolean isBlocked(Context context, String phoneNumber) {
335         try {
336             final Bundle res = context.getContentResolver().call(
337                     AUTHORITY_URI, METHOD_IS_BLOCKED, phoneNumber, null);
338             boolean isBlocked = res != null && res.getBoolean(RES_NUMBER_IS_BLOCKED, false);
339             Log.d(LOG_TAG, "isBlocked: phoneNumber=%s, isBlocked=%b", Log.piiHandle(phoneNumber),
340                     isBlocked);
341             return isBlocked;
342         } catch (NullPointerException | IllegalArgumentException ex) {
343             // The content resolver can throw an NPE or IAE; we don't want to crash Telecom if
344             // either of these happen.
345             Log.w(null, "isBlocked: provider not ready.");
346             return false;
347         }
348     }
349 
350     /**
351      * Unblocks the {@code phoneNumber} if it is blocked.
352      *
353      * <p> This deletes all rows where the {@code phoneNumber} matches the
354      * {@link BlockedNumbers#COLUMN_ORIGINAL_NUMBER} column or the E164 representation of the
355      * {@code phoneNumber} matches the {@link BlockedNumbers#COLUMN_E164_NUMBER} column.
356      *
357      * <p>To delete rows based on exact match with specific columns such as
358      * {@link BlockedNumbers#COLUMN_ID} use
359      * {@link android.content.ContentProvider#delete(Uri, String, String[])} with
360      * {@link BlockedNumbers#CONTENT_URI} URI.
361      *
362      * <p> Note that if the {@link #canCurrentUserBlockNumbers} is {@code false} for the user
363      * context {@code context}, this method will throw a {@link SecurityException}.
364      *
365      * @return the number of rows deleted in the blocked number provider as a result of unblock.
366      */
367     @WorkerThread
unblock(Context context, String phoneNumber)368     public static int unblock(Context context, String phoneNumber) {
369         Log.d(LOG_TAG, "unblock: phoneNumber=%s", Log.piiHandle(phoneNumber));
370         final Bundle res = context.getContentResolver().call(
371                 AUTHORITY_URI, METHOD_UNBLOCK, phoneNumber, null);
372         return res.getInt(RES_NUM_ROWS_DELETED, 0);
373     }
374 
375     /**
376      * Checks if blocking numbers is supported for the current user.
377      * <p> Typically, blocking numbers is only supported for one user at a time.
378      *
379      * @return {@code true} if the current user can block numbers.
380      */
canCurrentUserBlockNumbers(Context context)381     public static boolean canCurrentUserBlockNumbers(Context context) {
382         try {
383             final Bundle res = context.getContentResolver().call(
384                     AUTHORITY_URI, METHOD_CAN_CURRENT_USER_BLOCK_NUMBERS, null, null);
385             return res != null && res.getBoolean(RES_CAN_BLOCK_NUMBERS, false);
386         } catch (NullPointerException | IllegalArgumentException ex) {
387             // The content resolver can throw an NPE or IAE; we don't want to crash Telecom if
388             // either of these happen.
389             Log.w(null, "canCurrentUserBlockNumbers: provider not ready.");
390             return false;
391         }
392     }
393 
394     /**
395      * <p>
396      * The contract between the blockednumber provider and the system.
397      * </p>
398      * <p>This is a wrapper over {@link BlockedNumberContract} that also manages the blocking
399      * behavior when the user contacts emergency services. See
400      * {@link #notifyEmergencyContact(Context)} for details. All methods are protected by
401      * {@link android.Manifest.permission#READ_BLOCKED_NUMBERS} and
402      * {@link android.Manifest.permission#WRITE_BLOCKED_NUMBERS} appropriately which ensure that
403      * only system can access the methods defined here.
404      * </p>
405      * @hide
406      */
407     public static class SystemContract {
408         /**
409          * A protected broadcast intent action for letting components with
410          * {@link android.Manifest.permission#READ_BLOCKED_NUMBERS} know that the block suppression
411          * status as returned by {@link #getBlockSuppressionStatus(Context)} has been updated.
412          */
413         public static final String ACTION_BLOCK_SUPPRESSION_STATE_CHANGED =
414                 "android.provider.action.BLOCK_SUPPRESSION_STATE_CHANGED";
415 
416         public static final String METHOD_NOTIFY_EMERGENCY_CONTACT = "notify_emergency_contact";
417 
418         public static final String METHOD_END_BLOCK_SUPPRESSION = "end_block_suppression";
419 
420         public static final String METHOD_SHOULD_SYSTEM_BLOCK_NUMBER = "should_system_block_number";
421 
422         public static final String METHOD_GET_BLOCK_SUPPRESSION_STATUS =
423                 "get_block_suppression_status";
424 
425         public static final String METHOD_SHOULD_SHOW_EMERGENCY_CALL_NOTIFICATION =
426                 "should_show_emergency_call_notification";
427 
428         public static final String RES_IS_BLOCKING_SUPPRESSED = "blocking_suppressed";
429 
430         public static final String RES_BLOCKING_SUPPRESSED_UNTIL_TIMESTAMP =
431                 "blocking_suppressed_until_timestamp";
432 
433         public static final String METHOD_GET_ENHANCED_BLOCK_SETTING = "get_enhanced_block_setting";
434         public static final String METHOD_SET_ENHANCED_BLOCK_SETTING = "set_enhanced_block_setting";
435 
436         /* Preference key of block numbers not in contacts setting. */
437         public static final String ENHANCED_SETTING_KEY_BLOCK_UNREGISTERED =
438                 "block_numbers_not_in_contacts_setting";
439         /* Preference key of block private number calls setting. */
440         public static final String ENHANCED_SETTING_KEY_BLOCK_PRIVATE =
441                 "block_private_number_calls_setting";
442         /* Preference key of block payphone calls setting. */
443         public static final String ENHANCED_SETTING_KEY_BLOCK_PAYPHONE =
444                 "block_payphone_calls_setting";
445         /* Preference key of block unknown calls setting. */
446         public static final String ENHANCED_SETTING_KEY_BLOCK_UNKNOWN =
447                 "block_unknown_calls_setting";
448         /* Preference key for whether should show an emergency call notification. */
449         public static final String ENHANCED_SETTING_KEY_SHOW_EMERGENCY_CALL_NOTIFICATION =
450                 "show_emergency_call_notification";
451         /* Preference key of block unavailable calls setting. */
452         public static final String ENHANCED_SETTING_KEY_BLOCK_UNAVAILABLE =
453                 "block_unavailable_calls_setting";
454 
455         /**
456          * Notifies the provider that emergency services were contacted by the user.
457          * <p> This results in {@link #shouldSystemBlockNumber} returning {@code false} independent
458          * of the contents of the provider for a duration defined by
459          * {@link android.telephony.CarrierConfigManager#KEY_DURATION_BLOCKING_DISABLED_AFTER_EMERGENCY_INT}
460          * the provider unless {@link #endBlockSuppression(Context)} is called.
461          */
notifyEmergencyContact(Context context)462         public static void notifyEmergencyContact(Context context) {
463             try {
464                 Log.i(LOG_TAG, "notifyEmergencyContact; caller=%s", context.getOpPackageName());
465                 context.getContentResolver().call(
466                         AUTHORITY_URI, METHOD_NOTIFY_EMERGENCY_CONTACT, null, null);
467             } catch (NullPointerException | IllegalArgumentException ex) {
468                 // The content resolver can throw an NPE or IAE; we don't want to crash Telecom if
469                 // either of these happen.
470                 Log.w(null, "notifyEmergencyContact: provider not ready.");
471             }
472         }
473 
474         /**
475          * Notifies the provider to disable suppressing blocking. If emergency services were not
476          * contacted recently at all, calling this method is a no-op.
477          */
endBlockSuppression(Context context)478         public static void endBlockSuppression(Context context) {
479             String caller = context.getOpPackageName();
480             Log.i(LOG_TAG, "endBlockSuppression: caller=%s", caller);
481             context.getContentResolver().call(
482                     AUTHORITY_URI, METHOD_END_BLOCK_SUPPRESSION, null, null);
483         }
484 
485         /**
486          * Returns {@code true} if {@code phoneNumber} is blocked taking
487          * {@link #notifyEmergencyContact(Context)} into consideration. If emergency services
488          * have not been contacted recently and enhanced call blocking not been enabled, this
489          * method is equivalent to {@link #isBlocked(Context, String)}.
490          *
491          * @param context the context of the caller.
492          * @param phoneNumber the number to check.
493          * @param extras the extra attribute of the number.
494          * @return result code indicating if the number should be blocked, and if so why.
495          *         Valid values are: {@link #STATUS_NOT_BLOCKED}, {@link #STATUS_BLOCKED_IN_LIST},
496          *         {@link #STATUS_BLOCKED_NOT_IN_CONTACTS}, {@link #STATUS_BLOCKED_PAYPHONE},
497          *         {@link #STATUS_BLOCKED_RESTRICTED}, {@link #STATUS_BLOCKED_UNKNOWN_NUMBER}.
498          */
shouldSystemBlockNumber(Context context, String phoneNumber, Bundle extras)499         public static int shouldSystemBlockNumber(Context context, String phoneNumber,
500                 Bundle extras) {
501             try {
502                 String caller = context.getOpPackageName();
503                 final Bundle res = context.getContentResolver().call(
504                         AUTHORITY_URI, METHOD_SHOULD_SYSTEM_BLOCK_NUMBER, phoneNumber, extras);
505                 int blockResult = res != null ? res.getInt(RES_BLOCK_STATUS, STATUS_NOT_BLOCKED) :
506                         BlockedNumberContract.STATUS_NOT_BLOCKED;
507                 Log.d(LOG_TAG, "shouldSystemBlockNumber: number=%s, caller=%s, result=%s",
508                         Log.piiHandle(phoneNumber), caller, blockStatusToString(blockResult));
509                 return blockResult;
510             } catch (NullPointerException | IllegalArgumentException ex) {
511                 // The content resolver can throw an NPE or IAE; we don't want to crash Telecom if
512                 // either of these happen.
513                 Log.w(null, "shouldSystemBlockNumber: provider not ready.");
514                 return BlockedNumberContract.STATUS_NOT_BLOCKED;
515             }
516         }
517 
518         /**
519          * Returns the current status of block suppression.
520          */
getBlockSuppressionStatus(Context context)521         public static BlockSuppressionStatus getBlockSuppressionStatus(Context context) {
522             final Bundle res = context.getContentResolver().call(
523                     AUTHORITY_URI, METHOD_GET_BLOCK_SUPPRESSION_STATUS, null, null);
524             BlockSuppressionStatus blockSuppressionStatus = new BlockSuppressionStatus(
525                     res.getBoolean(RES_IS_BLOCKING_SUPPRESSED, false),
526                     res.getLong(RES_BLOCKING_SUPPRESSED_UNTIL_TIMESTAMP, 0));
527             Log.d(LOG_TAG, "getBlockSuppressionStatus: caller=%s, status=%s",
528                     context.getOpPackageName(), blockSuppressionStatus);
529             return blockSuppressionStatus;
530         }
531 
532         /**
533          * Check whether should show the emergency call notification.
534          *
535          * @param context the context of the caller.
536          * @return {@code true} if should show emergency call notification. {@code false} otherwise.
537          */
shouldShowEmergencyCallNotification(Context context)538         public static boolean shouldShowEmergencyCallNotification(Context context) {
539             try {
540                 final Bundle res = context.getContentResolver().call(
541                         AUTHORITY_URI, METHOD_SHOULD_SHOW_EMERGENCY_CALL_NOTIFICATION, null, null);
542                 return res != null && res.getBoolean(RES_SHOW_EMERGENCY_CALL_NOTIFICATION, false);
543             } catch (NullPointerException | IllegalArgumentException ex) {
544                 // The content resolver can throw an NPE or IAE; we don't want to crash Telecom if
545                 // either of these happen.
546                 Log.w(null, "shouldShowEmergencyCallNotification: provider not ready.");
547                 return false;
548             }
549         }
550 
551         /**
552          * Check whether the enhanced block setting is enabled.
553          *
554          * @param context the context of the caller.
555          * @param key the key of the setting to check, can be
556          *        {@link #ENHANCED_SETTING_KEY_BLOCK_UNREGISTERED}
557          *        {@link #ENHANCED_SETTING_KEY_BLOCK_PRIVATE}
558          *        {@link #ENHANCED_SETTING_KEY_BLOCK_PAYPHONE}
559          *        {@link #ENHANCED_SETTING_KEY_BLOCK_UNKNOWN}
560          *        {@link #ENHANCED_SETTING_KEY_BLOCK_UNAVAILABLE}
561          *        {@link #ENHANCED_SETTING_KEY_SHOW_EMERGENCY_CALL_NOTIFICATION}
562          * @return {@code true} if the setting is enabled. {@code false} otherwise.
563          */
getEnhancedBlockSetting(Context context, String key)564         public static boolean getEnhancedBlockSetting(Context context, String key) {
565             Bundle extras = new Bundle();
566             extras.putString(EXTRA_ENHANCED_SETTING_KEY, key);
567             try {
568                 final Bundle res = context.getContentResolver().call(
569                         AUTHORITY_URI, METHOD_GET_ENHANCED_BLOCK_SETTING, null, extras);
570                 return res != null && res.getBoolean(RES_ENHANCED_SETTING_IS_ENABLED, false);
571             } catch (NullPointerException | IllegalArgumentException ex) {
572                 // The content resolver can throw an NPE or IAE; we don't want to crash Telecom if
573                 // either of these happen.
574                 Log.w(null, "getEnhancedBlockSetting: provider not ready.");
575                 return false;
576             }
577         }
578 
579         /**
580          * Set the enhanced block setting enabled status.
581          *
582          * @param context the context of the caller.
583          * @param key the key of the setting to set, can be
584          *        {@link #ENHANCED_SETTING_KEY_BLOCK_UNREGISTERED}
585          *        {@link #ENHANCED_SETTING_KEY_BLOCK_PRIVATE}
586          *        {@link #ENHANCED_SETTING_KEY_BLOCK_PAYPHONE}
587          *        {@link #ENHANCED_SETTING_KEY_BLOCK_UNKNOWN}
588          *        {@link #ENHANCED_SETTING_KEY_BLOCK_UNAVAILABLE}
589          *        {@link #ENHANCED_SETTING_KEY_SHOW_EMERGENCY_CALL_NOTIFICATION}
590          * @param value the enabled statue of the setting to set.
591          */
setEnhancedBlockSetting(Context context, String key, boolean value)592         public static void setEnhancedBlockSetting(Context context, String key, boolean value) {
593             Bundle extras = new Bundle();
594             extras.putString(EXTRA_ENHANCED_SETTING_KEY, key);
595             extras.putBoolean(EXTRA_ENHANCED_SETTING_VALUE, value);
596             context.getContentResolver().call(AUTHORITY_URI, METHOD_SET_ENHANCED_BLOCK_SETTING,
597                     null, extras);
598         }
599 
600         /**
601          * Converts a block status constant to a string equivalent for logging.
602          * @hide
603          */
blockStatusToString(int blockStatus)604         public static String blockStatusToString(int blockStatus) {
605             switch (blockStatus) {
606                 case STATUS_NOT_BLOCKED:
607                     return "not blocked";
608                 case STATUS_BLOCKED_IN_LIST:
609                     return "blocked - in list";
610                 case STATUS_BLOCKED_RESTRICTED:
611                     return "blocked - restricted";
612                 case STATUS_BLOCKED_UNKNOWN_NUMBER:
613                     return "blocked - unknown";
614                 case STATUS_BLOCKED_PAYPHONE:
615                     return "blocked - payphone";
616                 case STATUS_BLOCKED_NOT_IN_CONTACTS:
617                     return "blocked - not in contacts";
618                 case STATUS_BLOCKED_UNAVAILABLE:
619                     return "blocked - unavailable";
620             }
621             return "unknown";
622         }
623 
624         /**
625          * Represents the current status of
626          * {@link #shouldSystemBlockNumber(Context, String, Bundle)}. If emergency services
627          * have been contacted recently, {@link #isSuppressed} is {@code true}, and blocking
628          * is disabled until the timestamp {@link #untilTimestampMillis}.
629          */
630         public static class BlockSuppressionStatus {
631             public final boolean isSuppressed;
632             /**
633              * Timestamp in milliseconds from epoch.
634              */
635             public final long untilTimestampMillis;
636 
BlockSuppressionStatus(boolean isSuppressed, long untilTimestampMillis)637             public BlockSuppressionStatus(boolean isSuppressed, long untilTimestampMillis) {
638                 this.isSuppressed = isSuppressed;
639                 this.untilTimestampMillis = untilTimestampMillis;
640             }
641 
642             @Override
toString()643             public String toString() {
644                 return "[BlockSuppressionStatus; isSuppressed=" + isSuppressed + ", until="
645                         + untilTimestampMillis + "]";
646             }
647         }
648     }
649 }
650