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 com.android.providers.blockednumber;
17 
18 import android.Manifest;
19 import android.annotation.NonNull;
20 import android.annotation.Nullable;
21 import android.app.AppOpsManager;
22 import android.app.backup.BackupManager;
23 import android.content.ContentProvider;
24 import android.content.ContentUris;
25 import android.content.ContentValues;
26 import android.content.Context;
27 import android.content.Intent;
28 import android.content.SharedPreferences;
29 import android.content.UriMatcher;
30 import android.content.pm.PackageManager;
31 import android.database.Cursor;
32 import android.database.sqlite.SQLiteDatabase;
33 import android.database.sqlite.SQLiteQueryBuilder;
34 import android.net.Uri;
35 import android.os.Binder;
36 import android.os.Bundle;
37 import android.os.CancellationSignal;
38 import android.os.Process;
39 import android.os.UserManager;
40 import android.provider.BlockedNumberContract;
41 import android.provider.BlockedNumberContract.SystemContract;
42 import android.telecom.TelecomManager;
43 import android.telephony.CarrierConfigManager;
44 import android.telephony.PhoneNumberUtils;
45 import android.telephony.TelephonyManager;
46 import android.text.TextUtils;
47 import android.util.Log;
48 
49 import com.android.common.content.ProjectionMap;
50 import com.android.internal.annotations.VisibleForTesting;
51 import com.android.providers.blockednumber.BlockedNumberDatabaseHelper.Tables;
52 
53 import java.util.Arrays;
54 
55 /**
56  * Blocked phone number provider.
57  *
58  * <p>Note the provider allows emergency numbers.  The caller (telecom) should never call it with
59  * emergency numbers.
60  */
61 public class BlockedNumberProvider extends ContentProvider {
62     static final String TAG = "BlockedNumbers";
63 
64     private static final boolean DEBUG = false; // DO NOT SUBMIT WITH TRUE.
65 
66     private static final int BLOCKED_LIST = 1000;
67     private static final int BLOCKED_ID = 1001;
68 
69     private static final UriMatcher sUriMatcher;
70 
71     private static final String PREF_FILE = "block_number_provider_prefs";
72     private static final String BLOCK_SUPPRESSION_EXPIRY_TIME_PREF =
73             "block_suppression_expiry_time_pref";
74     private static final int MAX_BLOCKING_DISABLED_DURATION_SECONDS = 7 * 24 * 3600; // 1 week
75     // Normally, we allow calls from self, *except* in unit tests, where we clear this flag
76     // to emulate calls from other apps.
77     @VisibleForTesting
78     static boolean ALLOW_SELF_CALL = true;
79 
80     static {
81         sUriMatcher = new UriMatcher(0);
sUriMatcher.addURI(BlockedNumberContract.AUTHORITY, "blocked", BLOCKED_LIST)82         sUriMatcher.addURI(BlockedNumberContract.AUTHORITY, "blocked", BLOCKED_LIST);
sUriMatcher.addURI(BlockedNumberContract.AUTHORITY, "blocked/#", BLOCKED_ID)83         sUriMatcher.addURI(BlockedNumberContract.AUTHORITY, "blocked/#", BLOCKED_ID);
84     }
85 
86     private static final ProjectionMap sBlockedNumberColumns = ProjectionMap.builder()
87             .add(BlockedNumberContract.BlockedNumbers.COLUMN_ID)
88             .add(BlockedNumberContract.BlockedNumbers.COLUMN_ORIGINAL_NUMBER)
89             .add(BlockedNumberContract.BlockedNumbers.COLUMN_E164_NUMBER)
90             .build();
91 
92     private static final String ID_SELECTION =
93             BlockedNumberContract.BlockedNumbers.COLUMN_ID + "=?";
94 
95     private static final String ORIGINAL_NUMBER_SELECTION =
96             BlockedNumberContract.BlockedNumbers.COLUMN_ORIGINAL_NUMBER + "=?";
97 
98     private static final String E164_NUMBER_SELECTION =
99             BlockedNumberContract.BlockedNumbers.COLUMN_E164_NUMBER + "=?";
100 
101     @VisibleForTesting
102     protected BlockedNumberDatabaseHelper mDbHelper;
103     @VisibleForTesting
104     protected BackupManager mBackupManager;
105 
106     @Override
onCreate()107     public boolean onCreate() {
108         mDbHelper = BlockedNumberDatabaseHelper.getInstance(getContext());
109         mBackupManager = new BackupManager(getContext());
110         return true;
111     }
112 
113     @Override
getType(@onNull Uri uri)114     public String getType(@NonNull Uri uri) {
115         final int match = sUriMatcher.match(uri);
116         switch (match) {
117             case BLOCKED_LIST:
118                 return BlockedNumberContract.BlockedNumbers.CONTENT_TYPE;
119             case BLOCKED_ID:
120                 return BlockedNumberContract.BlockedNumbers.CONTENT_ITEM_TYPE;
121             default:
122                 throw new IllegalArgumentException("Unsupported URI: " + uri);
123         }
124     }
125 
126     @Override
insert(@onNull Uri uri, @Nullable ContentValues values)127     public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
128         enforceWritePermissionAndPrimaryUser();
129 
130         final int match = sUriMatcher.match(uri);
131         switch (match) {
132             case BLOCKED_LIST:
133                 Uri blockedUri = insertBlockedNumber(values);
134                 getContext().getContentResolver().notifyChange(blockedUri, null);
135                 mBackupManager.dataChanged();
136                 return blockedUri;
137             default:
138                 throw new IllegalArgumentException("Unsupported URI: " + uri);
139         }
140     }
141 
142     /**
143      * Implements the "blocked/" insert.
144      */
insertBlockedNumber(ContentValues cv)145     private Uri insertBlockedNumber(ContentValues cv) {
146         throwIfSpecified(cv, BlockedNumberContract.BlockedNumbers.COLUMN_ID);
147 
148         final String phoneNumber = cv.getAsString(
149                 BlockedNumberContract.BlockedNumbers.COLUMN_ORIGINAL_NUMBER);
150 
151         if (TextUtils.isEmpty(phoneNumber)) {
152             throw new IllegalArgumentException("Missing a required column " +
153                     BlockedNumberContract.BlockedNumbers.COLUMN_ORIGINAL_NUMBER);
154         }
155 
156         // Fill in with autogenerated columns.
157         final String e164Number = Utils.getE164Number(getContext(), phoneNumber,
158                 cv.getAsString(BlockedNumberContract.BlockedNumbers.COLUMN_E164_NUMBER));
159         cv.put(BlockedNumberContract.BlockedNumbers.COLUMN_E164_NUMBER, e164Number);
160 
161         if (DEBUG) {
162             Log.d(TAG, String.format("inserted blocked number: %s", cv));
163         }
164 
165         // Then insert.
166         final long id = mDbHelper.getWritableDatabase().insertWithOnConflict(
167                 BlockedNumberDatabaseHelper.Tables.BLOCKED_NUMBERS, null, cv,
168                 SQLiteDatabase.CONFLICT_REPLACE);
169 
170         return ContentUris.withAppendedId(BlockedNumberContract.BlockedNumbers.CONTENT_URI, id);
171     }
172 
throwIfSpecified(ContentValues cv, String column)173     private static void throwIfSpecified(ContentValues cv, String column) {
174         if (cv.containsKey(column)) {
175             throw new IllegalArgumentException("Column " + column + " must not be specified");
176         }
177     }
178 
179     @Override
update(@onNull Uri uri, @Nullable ContentValues values, @Nullable String selection, @Nullable String[] selectionArgs)180     public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection,
181             @Nullable String[] selectionArgs) {
182         enforceWritePermissionAndPrimaryUser();
183 
184         throw new UnsupportedOperationException(
185                 "Update is not supported.  Use delete + insert instead");
186     }
187 
188     @Override
delete(@onNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs)189     public int delete(@NonNull Uri uri, @Nullable String selection,
190             @Nullable String[] selectionArgs) {
191         enforceWritePermissionAndPrimaryUser();
192 
193         final int match = sUriMatcher.match(uri);
194         int numRows;
195         switch (match) {
196             case BLOCKED_LIST:
197                 numRows = deleteBlockedNumber(selection, selectionArgs);
198                 break;
199             case BLOCKED_ID:
200                 numRows = deleteBlockedNumberWithId(ContentUris.parseId(uri), selection);
201                 break;
202             default:
203                 throw new IllegalArgumentException("Unsupported URI: " + uri);
204         }
205         getContext().getContentResolver().notifyChange(uri, null);
206         mBackupManager.dataChanged();
207         return numRows;
208     }
209 
210     /**
211      * Implements the "blocked/#" delete.
212      */
deleteBlockedNumberWithId(long id, String selection)213     private int deleteBlockedNumberWithId(long id, String selection) {
214         throwForNonEmptySelection(selection);
215 
216         return deleteBlockedNumber(ID_SELECTION, new String[]{Long.toString(id)});
217     }
218 
219     /**
220      * Implements the "blocked/" delete.
221      */
deleteBlockedNumber(String selection, String[] selectionArgs)222     private int deleteBlockedNumber(String selection, String[] selectionArgs) {
223         final SQLiteDatabase db = mDbHelper.getWritableDatabase();
224 
225         // When selection is specified, compile it within (...) to detect SQL injection.
226         if (!TextUtils.isEmpty(selection)) {
227             db.validateSql("select 1 FROM " + Tables.BLOCKED_NUMBERS + " WHERE " +
228                     Utils.wrapSelectionWithParens(selection),
229                     /* cancellationSignal =*/ null);
230         }
231 
232         return db.delete(
233                 BlockedNumberDatabaseHelper.Tables.BLOCKED_NUMBERS,
234                 selection, selectionArgs);
235     }
236 
237     @Override
query(@onNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder)238     public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection,
239             @Nullable String[] selectionArgs, @Nullable String sortOrder) {
240         enforceReadPermissionAndPrimaryUser();
241 
242         return query(uri, projection, selection, selectionArgs, sortOrder, null);
243     }
244 
245     @Override
query(@onNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder, @Nullable CancellationSignal cancellationSignal)246     public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection,
247             @Nullable String[] selectionArgs, @Nullable String sortOrder,
248             @Nullable CancellationSignal cancellationSignal) {
249         enforceReadPermissionAndPrimaryUser();
250 
251         final int match = sUriMatcher.match(uri);
252         Cursor cursor;
253         switch (match) {
254             case BLOCKED_LIST:
255                 cursor = queryBlockedList(projection, selection, selectionArgs, sortOrder,
256                         cancellationSignal);
257                 break;
258             case BLOCKED_ID:
259                 cursor = queryBlockedListWithId(ContentUris.parseId(uri), projection, selection,
260                         cancellationSignal);
261                 break;
262             default:
263                 throw new IllegalArgumentException("Unsupported URI: " + uri);
264         }
265         // Tell the cursor what uri to watch, so it knows when its source data changes
266         cursor.setNotificationUri(getContext().getContentResolver(), uri);
267         return cursor;
268     }
269 
270     /**
271      * Implements the "blocked/#" query.
272      */
queryBlockedListWithId(long id, String[] projection, String selection, CancellationSignal cancellationSignal)273     private Cursor queryBlockedListWithId(long id, String[] projection, String selection,
274             CancellationSignal cancellationSignal) {
275         throwForNonEmptySelection(selection);
276 
277         return queryBlockedList(projection, ID_SELECTION, new String[]{Long.toString(id)},
278                 null, cancellationSignal);
279     }
280 
281     /**
282      * Implements the "blocked/" query.
283      */
queryBlockedList(String[] projection, String selection, String[] selectionArgs, String sortOrder, CancellationSignal cancellationSignal)284     private Cursor queryBlockedList(String[] projection, String selection, String[] selectionArgs,
285             String sortOrder, CancellationSignal cancellationSignal) {
286         SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
287         qb.setStrict(true);
288         qb.setTables(BlockedNumberDatabaseHelper.Tables.BLOCKED_NUMBERS);
289         qb.setProjectionMap(sBlockedNumberColumns);
290 
291         return qb.query(mDbHelper.getReadableDatabase(), projection, selection, selectionArgs,
292                 /* groupBy =*/ null, /* having =*/null, sortOrder,
293                 /* limit =*/ null, cancellationSignal);
294     }
295 
throwForNonEmptySelection(String selection)296     private void throwForNonEmptySelection(String selection) {
297         if (!TextUtils.isEmpty(selection)) {
298             throw new IllegalArgumentException(
299                     "When ID is specified in URI, selection must be null");
300         }
301     }
302 
303     @Override
call(@onNull String method, @Nullable String arg, @Nullable Bundle extras)304     public Bundle call(@NonNull String method, @Nullable String arg, @Nullable Bundle extras) {
305         final Bundle res = new Bundle();
306         switch (method) {
307             case BlockedNumberContract.METHOD_IS_BLOCKED:
308                 enforceReadPermissionAndPrimaryUser();
309 
310                 res.putBoolean(BlockedNumberContract.RES_NUMBER_IS_BLOCKED, isBlocked(arg));
311                 break;
312             case BlockedNumberContract.METHOD_CAN_CURRENT_USER_BLOCK_NUMBERS:
313                 // No permission checks: any app should be able to access this API.
314                 res.putBoolean(
315                         BlockedNumberContract.RES_CAN_BLOCK_NUMBERS, canCurrentUserBlockUsers());
316                 break;
317             case BlockedNumberContract.METHOD_UNBLOCK:
318                 enforceWritePermissionAndPrimaryUser();
319 
320                 res.putInt(BlockedNumberContract.RES_NUM_ROWS_DELETED, unblock(arg));
321                 break;
322             case SystemContract.METHOD_NOTIFY_EMERGENCY_CONTACT:
323                 enforceSystemWritePermissionAndPrimaryUser();
324 
325                 notifyEmergencyContact();
326                 break;
327             case SystemContract.METHOD_END_BLOCK_SUPPRESSION:
328                 enforceSystemWritePermissionAndPrimaryUser();
329 
330                 endBlockSuppression();
331                 break;
332             case SystemContract.METHOD_GET_BLOCK_SUPPRESSION_STATUS:
333                 enforceSystemReadPermissionAndPrimaryUser();
334 
335                 SystemContract.BlockSuppressionStatus status = getBlockSuppressionStatus();
336                 res.putBoolean(SystemContract.RES_IS_BLOCKING_SUPPRESSED, status.isSuppressed);
337                 res.putLong(SystemContract.RES_BLOCKING_SUPPRESSED_UNTIL_TIMESTAMP,
338                         status.untilTimestampMillis);
339                 break;
340             case SystemContract.METHOD_SHOULD_SYSTEM_BLOCK_NUMBER:
341                 enforceSystemReadPermissionAndPrimaryUser();
342                 res.putBoolean(
343                         BlockedNumberContract.RES_NUMBER_IS_BLOCKED, shouldSystemBlockNumber(arg));
344                 break;
345             default:
346                 enforceReadPermissionAndPrimaryUser();
347 
348                 throw new IllegalArgumentException("Unsupported method " + method);
349         }
350         return res;
351     }
352 
unblock(String phoneNumber)353     private int unblock(String phoneNumber) {
354         if (TextUtils.isEmpty(phoneNumber)) {
355             return 0;
356         }
357 
358         StringBuilder selectionBuilder = new StringBuilder(ORIGINAL_NUMBER_SELECTION);
359         String[] selectionArgs = new String[]{phoneNumber};
360         final String e164Number = Utils.getE164Number(getContext(), phoneNumber, null);
361         if (!TextUtils.isEmpty(e164Number)) {
362             selectionBuilder.append(" or " + E164_NUMBER_SELECTION);
363             selectionArgs = new String[]{phoneNumber, e164Number};
364         }
365         String selection = selectionBuilder.toString();
366         if (DEBUG) {
367             Log.d(TAG, String.format("Unblocking numbers using selection: %s, args: %s",
368                     selection, Arrays.toString(selectionArgs)));
369         }
370         return deleteBlockedNumber(selection, selectionArgs);
371     }
372 
isEmergencyNumber(String phoneNumber)373     private boolean isEmergencyNumber(String phoneNumber) {
374         if (TextUtils.isEmpty(phoneNumber)) {
375             return false;
376         }
377 
378         final String e164Number = Utils.getE164Number(getContext(), phoneNumber, null);
379         return PhoneNumberUtils.isEmergencyNumber(phoneNumber)
380                 || PhoneNumberUtils.isEmergencyNumber(e164Number);
381     }
382 
isBlocked(String phoneNumber)383     private boolean isBlocked(String phoneNumber) {
384         if (TextUtils.isEmpty(phoneNumber)) {
385             return false;
386         }
387 
388         final String inE164 = Utils.getE164Number(getContext(), phoneNumber, null); // may be empty.
389 
390         if (DEBUG) {
391             Log.d(TAG, String.format("isBlocked: in=%s, e164=%s", phoneNumber, inE164));
392         }
393 
394         final Cursor c = mDbHelper.getReadableDatabase().rawQuery(
395                 "SELECT " +
396                 BlockedNumberContract.BlockedNumbers.COLUMN_ORIGINAL_NUMBER + "," +
397                 BlockedNumberContract.BlockedNumbers.COLUMN_E164_NUMBER +
398                 " FROM " + BlockedNumberDatabaseHelper.Tables.BLOCKED_NUMBERS +
399                 " WHERE " + BlockedNumberContract.BlockedNumbers.COLUMN_ORIGINAL_NUMBER + "=?1" +
400                 " OR (?2 != '' AND " +
401                         BlockedNumberContract.BlockedNumbers.COLUMN_E164_NUMBER + "=?2)",
402                 new String[] {phoneNumber, inE164}
403                 );
404         try {
405             while (c.moveToNext()) {
406                 if (DEBUG) {
407                     final String original = c.getString(0);
408                     final String e164 = c.getString(1);
409 
410                     Log.d(TAG, String.format("match found: original=%s, e164=%s", original, e164));
411                 }
412                 return true;
413             }
414         } finally {
415             c.close();
416         }
417         // No match found.
418         return false;
419     }
420 
canCurrentUserBlockUsers()421     private boolean canCurrentUserBlockUsers() {
422         UserManager userManager = getContext().getSystemService(UserManager.class);
423         return userManager.isPrimaryUser();
424     }
425 
notifyEmergencyContact()426     private void notifyEmergencyContact() {
427         writeBlockSuppressionExpiryTimePref(System.currentTimeMillis() +
428                 getBlockSuppressSecondsFromCarrierConfig() * 1000);
429         notifyBlockSuppressionStateChange();
430     }
431 
endBlockSuppression()432     private void endBlockSuppression() {
433         // Nothing to do if blocks are not being suppressed.
434         if (getBlockSuppressionStatus().isSuppressed) {
435             writeBlockSuppressionExpiryTimePref(0);
436             notifyBlockSuppressionStateChange();
437         }
438     }
439 
getBlockSuppressionStatus()440     private SystemContract.BlockSuppressionStatus getBlockSuppressionStatus() {
441         SharedPreferences pref = getContext().getSharedPreferences(PREF_FILE, Context.MODE_PRIVATE);
442         long blockSuppressionExpiryTimeMillis = pref.getLong(BLOCK_SUPPRESSION_EXPIRY_TIME_PREF, 0);
443         return new SystemContract.BlockSuppressionStatus(System.currentTimeMillis() <
444                 blockSuppressionExpiryTimeMillis, blockSuppressionExpiryTimeMillis);
445     }
446 
shouldSystemBlockNumber(String phoneNumber)447     private boolean shouldSystemBlockNumber(String phoneNumber) {
448         if (getBlockSuppressionStatus().isSuppressed) {
449             return false;
450         }
451         if (isEmergencyNumber(phoneNumber)) {
452             return false;
453         }
454         return isBlocked(phoneNumber);
455     }
456 
writeBlockSuppressionExpiryTimePref(long expiryTimeMillis)457     private void writeBlockSuppressionExpiryTimePref(long expiryTimeMillis) {
458         SharedPreferences pref = getContext().getSharedPreferences(PREF_FILE, Context.MODE_PRIVATE);
459         SharedPreferences.Editor editor = pref.edit();
460         editor.putLong(BLOCK_SUPPRESSION_EXPIRY_TIME_PREF, expiryTimeMillis);
461         editor.apply();
462     }
463 
getBlockSuppressSecondsFromCarrierConfig()464     private long getBlockSuppressSecondsFromCarrierConfig() {
465         CarrierConfigManager carrierConfigManager =
466                 getContext().getSystemService(CarrierConfigManager.class);
467         int carrierConfigValue = carrierConfigManager.getConfig().getInt
468                 (CarrierConfigManager.KEY_DURATION_BLOCKING_DISABLED_AFTER_EMERGENCY_INT);
469         boolean isValidValue = carrierConfigValue >=0 && carrierConfigValue <=
470                 MAX_BLOCKING_DISABLED_DURATION_SECONDS;
471         return isValidValue ? carrierConfigValue : CarrierConfigManager.getDefaultConfig().getInt(
472                 CarrierConfigManager.KEY_DURATION_BLOCKING_DISABLED_AFTER_EMERGENCY_INT);
473     }
474 
475     /**
476      * Returns {@code false} when the caller is not root, the user selected dialer, the
477      * default SMS app or a carrier app.
478      */
checkForPrivilegedApplications()479     private boolean checkForPrivilegedApplications() {
480         if (Binder.getCallingUid() == Process.ROOT_UID) {
481             return true;
482         }
483 
484         final String callingPackage = getCallingPackage();
485         if (TextUtils.isEmpty(callingPackage)) {
486             Log.w(TAG, "callingPackage not accessible");
487         } else {
488             final TelecomManager telecom = getContext().getSystemService(TelecomManager.class);
489 
490             if (callingPackage.equals(telecom.getDefaultDialerPackage())
491                     || callingPackage.equals(telecom.getSystemDialerPackage())) {
492                 return true;
493             }
494             final AppOpsManager appOps = getContext().getSystemService(AppOpsManager.class);
495             if (appOps.noteOp(AppOpsManager.OP_WRITE_SMS,
496                     Binder.getCallingUid(), callingPackage) == AppOpsManager.MODE_ALLOWED) {
497                 return true;
498             }
499 
500             final TelephonyManager telephonyManager =
501                     getContext().getSystemService(TelephonyManager.class);
502             return telephonyManager.checkCarrierPrivilegesForPackage(callingPackage) ==
503                     TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS;
504         }
505         return false;
506     }
507 
notifyBlockSuppressionStateChange()508     private void notifyBlockSuppressionStateChange() {
509         Intent intent = new Intent(SystemContract.ACTION_BLOCK_SUPPRESSION_STATE_CHANGED);
510         getContext().sendBroadcast(intent, Manifest.permission.READ_BLOCKED_NUMBERS);
511     }
512 
enforceReadPermission()513     private void enforceReadPermission() {
514         checkForPermission(android.Manifest.permission.READ_BLOCKED_NUMBERS);
515     }
516 
enforceReadPermissionAndPrimaryUser()517     private void enforceReadPermissionAndPrimaryUser() {
518         checkForPermissionAndPrimaryUser(android.Manifest.permission.READ_BLOCKED_NUMBERS);
519     }
520 
enforceWritePermissionAndPrimaryUser()521     private void enforceWritePermissionAndPrimaryUser() {
522         checkForPermissionAndPrimaryUser(android.Manifest.permission.WRITE_BLOCKED_NUMBERS);
523     }
524 
checkForPermissionAndPrimaryUser(String permission)525     private void checkForPermissionAndPrimaryUser(String permission) {
526         checkForPermission(permission);
527         if (!canCurrentUserBlockUsers()) {
528             throwCurrentUserNotPermittedSecurityException();
529         }
530     }
531 
checkForPermission(String permission)532     private void checkForPermission(String permission) {
533         boolean permitted = passesSystemPermissionCheck(permission)
534                 || checkForPrivilegedApplications() || isSelf();
535         if (!permitted) {
536             throwSecurityException();
537         }
538     }
539 
enforceSystemReadPermissionAndPrimaryUser()540     private void enforceSystemReadPermissionAndPrimaryUser() {
541         enforceSystemPermissionAndUser(android.Manifest.permission.READ_BLOCKED_NUMBERS);
542     }
543 
enforceSystemWritePermissionAndPrimaryUser()544     private void enforceSystemWritePermissionAndPrimaryUser() {
545         enforceSystemPermissionAndUser(android.Manifest.permission.WRITE_BLOCKED_NUMBERS);
546     }
547 
enforceSystemPermissionAndUser(String permission)548     private void enforceSystemPermissionAndUser(String permission) {
549         if (!canCurrentUserBlockUsers()) {
550             throwCurrentUserNotPermittedSecurityException();
551         }
552 
553         if (!passesSystemPermissionCheck(permission)) {
554             throwSecurityException();
555         }
556     }
557 
passesSystemPermissionCheck(String permission)558     private boolean passesSystemPermissionCheck(String permission) {
559         return getContext().checkCallingPermission(permission)
560                 == PackageManager.PERMISSION_GRANTED;
561     }
562 
isSelf()563     private boolean isSelf() {
564         return ALLOW_SELF_CALL && Binder.getCallingPid() == Process.myPid();
565     }
566 
throwSecurityException()567     private void throwSecurityException() {
568         throw new SecurityException("Caller must be system, default dialer or default SMS app");
569     }
570 
throwCurrentUserNotPermittedSecurityException()571     private void throwCurrentUserNotPermittedSecurityException() {
572         throw new SecurityException("The current user cannot perform this operation");
573     }
574 }
575