1 /*
2  * Copyright (C) 2019 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 
17 package android.provider;
18 
19 import static android.provider.BlockedNumberContract.AUTHORITY_URI;
20 import static android.provider.BlockedNumberContract.EXTRA_ENHANCED_SETTING_KEY;
21 import static android.provider.BlockedNumberContract.EXTRA_ENHANCED_SETTING_VALUE;
22 import static android.provider.BlockedNumberContract.RES_BLOCK_STATUS;
23 import static android.provider.BlockedNumberContract.RES_ENHANCED_SETTING_IS_ENABLED;
24 import static android.provider.BlockedNumberContract.RES_SHOW_EMERGENCY_CALL_NOTIFICATION;
25 import static android.provider.BlockedNumberContract.STATUS_NOT_BLOCKED;
26 import static android.provider.BlockedNumberContract.SystemContract.METHOD_END_BLOCK_SUPPRESSION;
27 import static android.provider.BlockedNumberContract.SystemContract.METHOD_GET_BLOCK_SUPPRESSION_STATUS;
28 import static android.provider.BlockedNumberContract.SystemContract.METHOD_GET_ENHANCED_BLOCK_SETTING;
29 import static android.provider.BlockedNumberContract.SystemContract.METHOD_NOTIFY_EMERGENCY_CONTACT;
30 import static android.provider.BlockedNumberContract.SystemContract.METHOD_SET_ENHANCED_BLOCK_SETTING;
31 import static android.provider.BlockedNumberContract.SystemContract.METHOD_SHOULD_SHOW_EMERGENCY_CALL_NOTIFICATION;
32 import static android.provider.BlockedNumberContract.SystemContract.METHOD_SHOULD_SYSTEM_BLOCK_NUMBER;
33 import static android.provider.BlockedNumberContract.SystemContract.RES_BLOCKING_SUPPRESSED_UNTIL_TIMESTAMP;
34 import static android.provider.BlockedNumberContract.SystemContract.RES_IS_BLOCKING_SUPPRESSED;
35 
36 import android.Manifest;
37 import android.annotation.FlaggedApi;
38 import android.annotation.NonNull;
39 import android.annotation.RequiresPermission;
40 import android.annotation.SystemApi;
41 import android.content.Context;
42 import android.os.Bundle;
43 import android.telecom.Log;
44 import android.telecom.TelecomManager;
45 
46 import com.android.server.telecom.flags.Flags;
47 
48 /**
49  * Constants and methods to interact with the blocked numbers list. This class also serves as
50  * a mediator between the BlockedNumber provider and the system: it manages blocking behavior
51  * when the user contacts emergency services. Currently, this is only used internally by Telecom.
52  *
53  * Refer to {@link BlockedNumberContract} for more context.
54  * @hide
55  */
56 @SystemApi
57 @FlaggedApi(Flags.FLAG_TELECOM_MAINLINE_BLOCKED_NUMBERS_MANAGER)
58 public final class BlockedNumbersManager {
59     private static final String LOG_TAG = BlockedNumbersManager.class.getSimpleName();
60     private Context mContext;
61 
62     /**
63      * @hide
64      */
BlockedNumbersManager(Context context)65     public BlockedNumbersManager(Context context) {
66         mContext = context;
67     }
68 
69     /**
70      * A protected broadcast intent action for letting components with
71      * {@link android.Manifest.permission#READ_BLOCKED_NUMBERS} know that the block suppression
72      * status as returned by {@link #getBlockSuppressionStatus()} has been updated.
73      * @hide
74      */
75     @SystemApi
76     @FlaggedApi(Flags.FLAG_TELECOM_MAINLINE_BLOCKED_NUMBERS_MANAGER)
77     public static final String ACTION_BLOCK_SUPPRESSION_STATE_CHANGED =
78             "android.provider.action.BLOCK_SUPPRESSION_STATE_CHANGED";
79 
80     /**
81      * Preference key of block numbers not in contacts setting.
82      * @hide
83      */
84     @SystemApi
85     @FlaggedApi(Flags.FLAG_TELECOM_MAINLINE_BLOCKED_NUMBERS_MANAGER)
86     public static final String ENHANCED_SETTING_KEY_BLOCK_UNREGISTERED =
87             "block_numbers_not_in_contacts_setting";
88 
89     /**
90      * Preference key of block private number calls setting.
91      * @hide
92      */
93     @SystemApi
94     @FlaggedApi(Flags.FLAG_TELECOM_MAINLINE_BLOCKED_NUMBERS_MANAGER)
95     public static final String ENHANCED_SETTING_KEY_BLOCK_PRIVATE =
96             "block_private_number_calls_setting";
97 
98     /**
99      * Preference key of block payphone calls setting.
100      * @hide
101      */
102     @SystemApi
103     @FlaggedApi(Flags.FLAG_TELECOM_MAINLINE_BLOCKED_NUMBERS_MANAGER)
104     public static final String ENHANCED_SETTING_KEY_BLOCK_PAYPHONE =
105             "block_payphone_calls_setting";
106 
107     /**
108      * Preference key of block unknown calls setting.
109      * @hide
110      */
111     @SystemApi
112     @FlaggedApi(Flags.FLAG_TELECOM_MAINLINE_BLOCKED_NUMBERS_MANAGER)
113     public static final String ENHANCED_SETTING_KEY_BLOCK_UNKNOWN =
114             "block_unknown_calls_setting";
115 
116     /**
117      * Preference key for whether should show an emergency call notification.
118      * @hide
119      */
120     @SystemApi
121     @FlaggedApi(Flags.FLAG_TELECOM_MAINLINE_BLOCKED_NUMBERS_MANAGER)
122     public static final String ENHANCED_SETTING_KEY_SHOW_EMERGENCY_CALL_NOTIFICATION =
123             "show_emergency_call_notification";
124 
125     /**
126      * Preference key of block unavailable calls setting.
127      * @hide
128      */
129     @SystemApi
130     @FlaggedApi(Flags.FLAG_TELECOM_MAINLINE_BLOCKED_NUMBERS_MANAGER)
131     public static final String ENHANCED_SETTING_KEY_BLOCK_UNAVAILABLE =
132             "block_unavailable_calls_setting";
133 
134     /**
135      * Notifies the provider that emergency services were contacted by the user.
136      * <p> This results in {@link #shouldSystemBlockNumber} returning {@code false} independent
137      * of the contents of the provider for a duration defined by
138      * {@link android.telephony.CarrierConfigManager#KEY_DURATION_BLOCKING_DISABLED_AFTER_EMERGENCY_INT}
139      * the provider unless {@link #endBlockSuppression()} is called.
140      * @hide
141      */
142     @SystemApi
143     @RequiresPermission(allOf = {
144             android.Manifest.permission.READ_BLOCKED_NUMBERS,
145             android.Manifest.permission.WRITE_BLOCKED_NUMBERS
146     })
147     @FlaggedApi(Flags.FLAG_TELECOM_MAINLINE_BLOCKED_NUMBERS_MANAGER)
notifyEmergencyContact()148     public void notifyEmergencyContact() {
149         verifyBlockedNumbersPermission();
150         try {
151             Log.i(LOG_TAG, "notifyEmergencyContact; caller=%s", mContext.getOpPackageName());
152             mContext.getContentResolver().call(AUTHORITY_URI, METHOD_NOTIFY_EMERGENCY_CONTACT,
153                     null, null);
154         } catch (NullPointerException | IllegalArgumentException ex) {
155             // The content resolver can throw an NPE or IAE; we don't want to crash Telecom if
156             // either of these happen.
157             Log.w(null, "notifyEmergencyContact: provider not ready.");
158         }
159     }
160 
161     /**
162      * Notifies the provider to disable suppressing blocking. If emergency services were not
163      * contacted recently at all, calling this method is a no-op.
164      * @hide
165      */
166     @SystemApi
167     @RequiresPermission(allOf = {
168             android.Manifest.permission.READ_BLOCKED_NUMBERS,
169             android.Manifest.permission.WRITE_BLOCKED_NUMBERS
170     })
171     @FlaggedApi(Flags.FLAG_TELECOM_MAINLINE_BLOCKED_NUMBERS_MANAGER)
endBlockSuppression()172     public void endBlockSuppression() {
173         verifyBlockedNumbersPermission();
174         String caller = mContext.getOpPackageName();
175         Log.i(LOG_TAG, "endBlockSuppression: caller=%s", caller);
176         mContext.getContentResolver().call(AUTHORITY_URI, METHOD_END_BLOCK_SUPPRESSION, null, null);
177     }
178 
179     /**
180      * Returns {@code true} if {@code phoneNumber} is blocked taking
181      * {@link #notifyEmergencyContact()} into consideration. If emergency services
182      * have not been contacted recently and enhanced call blocking not been enabled, this
183      * method is equivalent to {@link BlockedNumberContract#isBlocked(Context, String)}.
184      *
185      * @param phoneNumber the number to check.
186      * @param numberPresentation the presentation code associated with the call.
187      * @param isNumberInContacts indicates if the provided number exists as a contact.
188      * @return result code indicating if the number should be blocked, and if so why.
189      *         Valid values are: {@link BlockedNumberContract#STATUS_NOT_BLOCKED},
190      *         {@link BlockedNumberContract#STATUS_BLOCKED_IN_LIST},
191      *         {@link BlockedNumberContract#STATUS_BLOCKED_NOT_IN_CONTACTS},
192      *         {@link BlockedNumberContract#STATUS_BLOCKED_PAYPHONE},
193      *         {@link BlockedNumberContract#STATUS_BLOCKED_RESTRICTED},
194      *         {@link BlockedNumberContract#STATUS_BLOCKED_UNKNOWN_NUMBER}.
195      * @hide
196      */
197     @SystemApi
198     @RequiresPermission(allOf = {
199             android.Manifest.permission.READ_BLOCKED_NUMBERS,
200             android.Manifest.permission.WRITE_BLOCKED_NUMBERS
201     })
202     @FlaggedApi(Flags.FLAG_TELECOM_MAINLINE_BLOCKED_NUMBERS_MANAGER)
shouldSystemBlockNumber(@onNull String phoneNumber, @TelecomManager.Presentation int numberPresentation, boolean isNumberInContacts)203     public int shouldSystemBlockNumber(@NonNull String phoneNumber,
204             @TelecomManager.Presentation int numberPresentation, boolean isNumberInContacts) {
205         verifyBlockedNumbersPermission();
206         try {
207             String caller = mContext.getOpPackageName();
208             Bundle extras = new Bundle();
209             extras.putInt(BlockedNumberContract.EXTRA_CALL_PRESENTATION, numberPresentation);
210             extras.putBoolean(BlockedNumberContract.EXTRA_CONTACT_EXIST, isNumberInContacts);
211             final Bundle res = mContext.getContentResolver().call(AUTHORITY_URI,
212                     METHOD_SHOULD_SYSTEM_BLOCK_NUMBER, phoneNumber, extras);
213             int blockResult = res != null ? res.getInt(RES_BLOCK_STATUS, STATUS_NOT_BLOCKED) :
214                     BlockedNumberContract.STATUS_NOT_BLOCKED;
215             Log.d(LOG_TAG, "shouldSystemBlockNumber: number=%s, caller=%s, result=%s",
216                     Log.piiHandle(phoneNumber), caller,
217                     BlockedNumberContract.SystemContract.blockStatusToString(blockResult));
218             return blockResult;
219         } catch (NullPointerException | IllegalArgumentException ex) {
220             // The content resolver can throw an NPE or IAE; we don't want to crash Telecom if
221             // either of these happen.
222             Log.w(null, "shouldSystemBlockNumber: provider not ready.");
223             return BlockedNumberContract.STATUS_NOT_BLOCKED;
224         }
225     }
226 
227     /**
228      * @return The current status of block suppression.
229      * @hide
230      */
231     @SystemApi
232     @RequiresPermission(allOf = {
233             android.Manifest.permission.READ_BLOCKED_NUMBERS,
234             android.Manifest.permission.WRITE_BLOCKED_NUMBERS
235     })
236     @FlaggedApi(Flags.FLAG_TELECOM_MAINLINE_BLOCKED_NUMBERS_MANAGER)
getBlockSuppressionStatus()237     public @NonNull BlockSuppressionStatus getBlockSuppressionStatus() {
238         verifyBlockedNumbersPermission();
239         final Bundle res = mContext.getContentResolver().call(
240                 AUTHORITY_URI, METHOD_GET_BLOCK_SUPPRESSION_STATUS, null, null);
241         BlockSuppressionStatus blockSuppressionStatus = new BlockSuppressionStatus(
242                 res.getBoolean(RES_IS_BLOCKING_SUPPRESSED, false),
243                 res.getLong(RES_BLOCKING_SUPPRESSED_UNTIL_TIMESTAMP, 0));
244         Log.d(LOG_TAG, "getBlockSuppressionStatus: caller=%s, status=%s",
245                 mContext.getOpPackageName(), blockSuppressionStatus);
246         return blockSuppressionStatus;
247     }
248 
249     /**
250      * Check whether should show the emergency call notification.
251      *
252      * @return {@code true} if should show emergency call notification. {@code false} otherwise.
253      * @hide
254      */
255     @SystemApi
256     @RequiresPermission(allOf = {
257             android.Manifest.permission.READ_BLOCKED_NUMBERS,
258             android.Manifest.permission.WRITE_BLOCKED_NUMBERS
259     })
260     @FlaggedApi(Flags.FLAG_TELECOM_MAINLINE_BLOCKED_NUMBERS_MANAGER)
shouldShowEmergencyCallNotification()261     public boolean shouldShowEmergencyCallNotification() {
262         verifyBlockedNumbersPermission();
263         try {
264             final Bundle res = mContext.getContentResolver().call(AUTHORITY_URI,
265                     METHOD_SHOULD_SHOW_EMERGENCY_CALL_NOTIFICATION, null, null);
266             return res != null && res.getBoolean(RES_SHOW_EMERGENCY_CALL_NOTIFICATION, false);
267         } catch (NullPointerException | IllegalArgumentException ex) {
268             // The content resolver can throw an NPE or IAE; we don't want to crash Telecom if
269             // either of these happen.
270             Log.w(null, "shouldShowEmergencyCallNotification: provider not ready.");
271             return false;
272         }
273     }
274 
275     /**
276      * Check whether the enhanced block setting is enabled.
277      *
278      * @param key the key of the setting to check, can be
279      *        {@link BlockedNumberContract.SystemContract#ENHANCED_SETTING_KEY_BLOCK_UNREGISTERED}
280      *        {@link BlockedNumberContract.SystemContract#ENHANCED_SETTING_KEY_BLOCK_PRIVATE}
281      *        {@link BlockedNumberContract.SystemContract#ENHANCED_SETTING_KEY_BLOCK_PAYPHONE}
282      *        {@link BlockedNumberContract.SystemContract#ENHANCED_SETTING_KEY_BLOCK_UNKNOWN}
283      *        {@link BlockedNumberContract.SystemContract#ENHANCED_SETTING_KEY_BLOCK_UNAVAILABLE}
284      *        {@link BlockedNumberContract.SystemContract
285      *               #ENHANCED_SETTING_KEY_SHOW_EMERGENCY_CALL_NOTIFICATION}
286      * @return {@code true} if the setting is enabled. {@code false} otherwise.
287      * @hide
288      */
289     @SystemApi
290     @RequiresPermission(allOf = {
291             android.Manifest.permission.READ_BLOCKED_NUMBERS,
292             android.Manifest.permission.WRITE_BLOCKED_NUMBERS
293     })
294     @FlaggedApi(Flags.FLAG_TELECOM_MAINLINE_BLOCKED_NUMBERS_MANAGER)
getBlockedNumberSetting(@onNull String key)295     public boolean getBlockedNumberSetting(@NonNull String key) {
296         verifyBlockedNumbersPermission();
297         Bundle extras = new Bundle();
298         extras.putString(EXTRA_ENHANCED_SETTING_KEY, key);
299         try {
300             final Bundle res = mContext.getContentResolver().call(AUTHORITY_URI,
301                     METHOD_GET_ENHANCED_BLOCK_SETTING, null, extras);
302             return res != null && res.getBoolean(RES_ENHANCED_SETTING_IS_ENABLED, false);
303         } catch (NullPointerException | IllegalArgumentException ex) {
304             // The content resolver can throw an NPE or IAE; we don't want to crash Telecom if
305             // either of these happen.
306             Log.w(null, "getEnhancedBlockSetting: provider not ready.");
307             return false;
308         }
309     }
310 
311     /**
312      * Set the enhanced block setting enabled status.
313      *
314      * @param key the key of the setting to set, can be
315      *        {@link BlockedNumberContract.SystemContract#ENHANCED_SETTING_KEY_BLOCK_UNREGISTERED}
316      *        {@link BlockedNumberContract.SystemContract#ENHANCED_SETTING_KEY_BLOCK_PRIVATE}
317      *        {@link BlockedNumberContract.SystemContract#ENHANCED_SETTING_KEY_BLOCK_PAYPHONE}
318      *        {@link BlockedNumberContract.SystemContract#ENHANCED_SETTING_KEY_BLOCK_UNKNOWN}
319      *        {@link BlockedNumberContract.SystemContract#ENHANCED_SETTING_KEY_BLOCK_UNAVAILABLE}
320      *        {@link BlockedNumberContract.SystemContract
321      *               #ENHANCED_SETTING_KEY_SHOW_EMERGENCY_CALL_NOTIFICATION}
322      * @param value the enabled statue of the setting to set.
323      * @hide
324      */
325     @SystemApi
326     @RequiresPermission(allOf = {
327             android.Manifest.permission.READ_BLOCKED_NUMBERS,
328             android.Manifest.permission.WRITE_BLOCKED_NUMBERS
329     })
330     @FlaggedApi(Flags.FLAG_TELECOM_MAINLINE_BLOCKED_NUMBERS_MANAGER)
setBlockedNumberSetting(@onNull String key, boolean value)331     public void setBlockedNumberSetting(@NonNull String key, boolean value) {
332         verifyBlockedNumbersPermission();
333         Bundle extras = new Bundle();
334         extras.putString(EXTRA_ENHANCED_SETTING_KEY, key);
335         extras.putBoolean(EXTRA_ENHANCED_SETTING_VALUE, value);
336         mContext.getContentResolver().call(AUTHORITY_URI, METHOD_SET_ENHANCED_BLOCK_SETTING,
337                 null, extras);
338     }
339 
340     /**
341      * Represents the current status of
342      * {@link #shouldSystemBlockNumber(String, int, boolean)}. If emergency services
343      * have been contacted recently, {@link #mIsSuppressed} is {@code true}, and blocking
344      * is disabled until the timestamp {@link #mUntilTimestampMillis}.
345      * @hide
346      */
347     @SystemApi
348     @FlaggedApi(Flags.FLAG_TELECOM_MAINLINE_BLOCKED_NUMBERS_MANAGER)
349     public static final class BlockSuppressionStatus {
350         /**
351          * Indicates if block suppression is enabled.
352          */
353         private boolean mIsSuppressed;
354 
355         /**
356          * Timestamp in milliseconds from epoch.
357          */
358         private long mUntilTimestampMillis;
359 
BlockSuppressionStatus(boolean isSuppressed, long untilTimestampMillis)360         public BlockSuppressionStatus(boolean isSuppressed, long untilTimestampMillis) {
361             this.mIsSuppressed = isSuppressed;
362             this.mUntilTimestampMillis = untilTimestampMillis;
363         }
364 
365         @Override
toString()366         public String toString() {
367             return "[BlockSuppressionStatus; isSuppressed=" + mIsSuppressed + ", until="
368                     + mUntilTimestampMillis + "]";
369         }
370 
371         /**
372          * @return mIsSuppressed Indicates whether or not block suppression is enabled.
373          */
getIsSuppressed()374         public boolean getIsSuppressed() {
375             return mIsSuppressed;
376         }
377 
378         /**
379          * @return mUntilTimestampMillis The timestamp until which block suppression would be
380          * enabled for
381          */
getUntilTimestampMillis()382         public long getUntilTimestampMillis() {
383             return mUntilTimestampMillis;
384         }
385     }
386 
387     /**
388      * Verifies that the caller holds both the
389      * {@link android.Manifest.permission#READ_BLOCKED_NUMBERS} permission and the
390      * {@link android.Manifest.permission#WRITE_BLOCKED_NUMBERS} permission.
391      *
392      * @throws SecurityException if the caller is missing the necessary permissions
393      */
verifyBlockedNumbersPermission()394     private void verifyBlockedNumbersPermission() {
395         mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_BLOCKED_NUMBERS,
396                 "Caller does not have the android.permission.READ_BLOCKED_NUMBERS permission");
397         mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_BLOCKED_NUMBERS,
398                 "Caller does not have the android.permission.WRITE_BLOCKED_NUMBERS permission");
399     }
400 }
401