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