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