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