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 static com.android.providers.blockednumber.Utils.piiHandle; 19 20 import android.Manifest; 21 import android.annotation.NonNull; 22 import android.annotation.Nullable; 23 import android.app.AppOpsManager; 24 import android.app.backup.BackupManager; 25 import android.content.ContentProvider; 26 import android.content.ContentUris; 27 import android.content.ContentValues; 28 import android.content.Context; 29 import android.content.Intent; 30 import android.content.SharedPreferences; 31 import android.content.UriMatcher; 32 import android.content.pm.PackageManager; 33 import android.database.Cursor; 34 import android.database.sqlite.SQLiteDatabase; 35 import android.database.sqlite.SQLiteQueryBuilder; 36 import android.net.Uri; 37 import android.os.Binder; 38 import android.os.Bundle; 39 import android.os.CancellationSignal; 40 import android.os.PersistableBundle; 41 import android.os.Process; 42 import android.os.UserHandle; 43 import android.os.UserManager; 44 import android.provider.BlockedNumberContract; 45 import android.provider.BlockedNumberContract.SystemContract; 46 import android.telecom.TelecomManager; 47 import android.telephony.CarrierConfigManager; 48 import android.telephony.TelephonyManager; 49 import android.text.TextUtils; 50 import android.util.Log; 51 52 import com.android.common.content.ProjectionMap; 53 import com.android.internal.annotations.VisibleForTesting; 54 import com.android.providers.blockednumber.BlockedNumberDatabaseHelper.Tables; 55 56 import java.util.Arrays; 57 58 /** 59 * Blocked phone number provider. 60 * 61 * <p>Note the provider allows emergency numbers. The caller (telecom) should never call it with 62 * emergency numbers. 63 */ 64 public class BlockedNumberProvider extends ContentProvider { 65 static final String TAG = "BlockedNumbers"; 66 67 private static final boolean DEBUG = false; // DO NOT SUBMIT WITH TRUE. 68 69 private static final int BLOCKED_LIST = 1000; 70 private static final int BLOCKED_ID = 1001; 71 72 private static final UriMatcher sUriMatcher; 73 74 private static final String PREF_FILE = "block_number_provider_prefs"; 75 private static final String BLOCK_SUPPRESSION_EXPIRY_TIME_PREF = 76 "block_suppression_expiry_time_pref"; 77 private static final int MAX_BLOCKING_DISABLED_DURATION_SECONDS = 7 * 24 * 3600; // 1 week 78 private static final long BLOCKING_DISABLED_FOREVER = -1; 79 // Normally, we allow calls from self, *except* in unit tests, where we clear this flag 80 // to emulate calls from other apps. 81 @VisibleForTesting 82 static boolean ALLOW_SELF_CALL = true; 83 84 static { 85 sUriMatcher = new UriMatcher(0); sUriMatcher.addURI(BlockedNumberContract.AUTHORITY, "blocked", BLOCKED_LIST)86 sUriMatcher.addURI(BlockedNumberContract.AUTHORITY, "blocked", BLOCKED_LIST); sUriMatcher.addURI(BlockedNumberContract.AUTHORITY, "blocked/#", BLOCKED_ID)87 sUriMatcher.addURI(BlockedNumberContract.AUTHORITY, "blocked/#", BLOCKED_ID); 88 } 89 90 private static final ProjectionMap sBlockedNumberColumns = ProjectionMap.builder() 91 .add(BlockedNumberContract.BlockedNumbers.COLUMN_ID) 92 .add(BlockedNumberContract.BlockedNumbers.COLUMN_ORIGINAL_NUMBER) 93 .add(BlockedNumberContract.BlockedNumbers.COLUMN_E164_NUMBER) 94 .build(); 95 96 private static final String ID_SELECTION = 97 BlockedNumberContract.BlockedNumbers.COLUMN_ID + "=?"; 98 99 private static final String ORIGINAL_NUMBER_SELECTION = 100 BlockedNumberContract.BlockedNumbers.COLUMN_ORIGINAL_NUMBER + "=?"; 101 102 private static final String E164_NUMBER_SELECTION = 103 BlockedNumberContract.BlockedNumbers.COLUMN_E164_NUMBER + "=?"; 104 105 @VisibleForTesting 106 protected BlockedNumberDatabaseHelper mDbHelper; 107 @VisibleForTesting 108 protected BackupManager mBackupManager; 109 protected AppOpsManager mAppOpsManager; 110 111 @Override onCreate()112 public boolean onCreate() { 113 mDbHelper = BlockedNumberDatabaseHelper.getInstance(getContext()); 114 mBackupManager = new BackupManager(getContext()); 115 mAppOpsManager = getAppOpsManager(); 116 return true; 117 } 118 119 @Override getType(@onNull Uri uri)120 public String getType(@NonNull Uri uri) { 121 final int match = sUriMatcher.match(uri); 122 switch (match) { 123 case BLOCKED_LIST: 124 return BlockedNumberContract.BlockedNumbers.CONTENT_TYPE; 125 case BLOCKED_ID: 126 return BlockedNumberContract.BlockedNumbers.CONTENT_ITEM_TYPE; 127 default: 128 throw new IllegalArgumentException("Unsupported URI: " + uri); 129 } 130 } 131 132 @Override insert(@onNull Uri uri, @Nullable ContentValues values)133 public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) { 134 enforceWritePermissionAndMainUser(); 135 136 final int match = sUriMatcher.match(uri); 137 switch (match) { 138 case BLOCKED_LIST: 139 Uri blockedUri = insertBlockedNumber(values); 140 getContext().getContentResolver().notifyChange(blockedUri, null); 141 mBackupManager.dataChanged(); 142 return blockedUri; 143 default: 144 throw new IllegalArgumentException("Unsupported URI: " + uri); 145 } 146 } 147 148 /** 149 * Implements the "blocked/" insert. 150 */ insertBlockedNumber(ContentValues cv)151 private Uri insertBlockedNumber(ContentValues cv) { 152 throwIfSpecified(cv, BlockedNumberContract.BlockedNumbers.COLUMN_ID); 153 154 final String phoneNumber = cv.getAsString( 155 BlockedNumberContract.BlockedNumbers.COLUMN_ORIGINAL_NUMBER); 156 157 if (TextUtils.isEmpty(phoneNumber)) { 158 throw new IllegalArgumentException("Missing a required column " + 159 BlockedNumberContract.BlockedNumbers.COLUMN_ORIGINAL_NUMBER); 160 } 161 162 // Fill in with autogenerated columns. 163 final String e164Number = Utils.getE164Number(getContext(), phoneNumber, 164 cv.getAsString(BlockedNumberContract.BlockedNumbers.COLUMN_E164_NUMBER)); 165 cv.put(BlockedNumberContract.BlockedNumbers.COLUMN_E164_NUMBER, e164Number); 166 167 if (DEBUG) { 168 Log.d(TAG, String.format("inserted blocked number: %s", cv)); 169 } 170 171 // Then insert. 172 final long id = mDbHelper.getWritableDatabase().insertWithOnConflict( 173 BlockedNumberDatabaseHelper.Tables.BLOCKED_NUMBERS, null, cv, 174 SQLiteDatabase.CONFLICT_REPLACE); 175 176 return ContentUris.withAppendedId(BlockedNumberContract.BlockedNumbers.CONTENT_URI, id); 177 } 178 throwIfSpecified(ContentValues cv, String column)179 private static void throwIfSpecified(ContentValues cv, String column) { 180 if (cv.containsKey(column)) { 181 throw new IllegalArgumentException("Column " + column + " must not be specified"); 182 } 183 } 184 185 @Override update(@onNull Uri uri, @Nullable ContentValues values, @Nullable String selection, @Nullable String[] selectionArgs)186 public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection, 187 @Nullable String[] selectionArgs) { 188 enforceWritePermissionAndMainUser(); 189 190 throw new UnsupportedOperationException( 191 "Update is not supported. Use delete + insert instead"); 192 } 193 194 @Override delete(@onNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs)195 public int delete(@NonNull Uri uri, @Nullable String selection, 196 @Nullable String[] selectionArgs) { 197 enforceWritePermissionAndMainUser(); 198 199 final int match = sUriMatcher.match(uri); 200 int numRows; 201 switch (match) { 202 case BLOCKED_LIST: 203 numRows = deleteBlockedNumber(selection, selectionArgs); 204 break; 205 case BLOCKED_ID: 206 numRows = deleteBlockedNumberWithId(ContentUris.parseId(uri), selection); 207 break; 208 default: 209 throw new IllegalArgumentException("Unsupported URI: " + uri); 210 } 211 getContext().getContentResolver().notifyChange(uri, null); 212 mBackupManager.dataChanged(); 213 return numRows; 214 } 215 216 /** 217 * Implements the "blocked/#" delete. 218 */ deleteBlockedNumberWithId(long id, String selection)219 private int deleteBlockedNumberWithId(long id, String selection) { 220 throwForNonEmptySelection(selection); 221 222 return deleteBlockedNumber(ID_SELECTION, new String[]{Long.toString(id)}); 223 } 224 225 /** 226 * Implements the "blocked/" delete. 227 */ deleteBlockedNumber(String selection, String[] selectionArgs)228 private int deleteBlockedNumber(String selection, String[] selectionArgs) { 229 final SQLiteDatabase db = mDbHelper.getWritableDatabase(); 230 231 // When selection is specified, compile it within (...) to detect SQL injection. 232 if (!TextUtils.isEmpty(selection)) { 233 db.validateSql("select 1 FROM " + Tables.BLOCKED_NUMBERS + " WHERE " + 234 Utils.wrapSelectionWithParens(selection), 235 /* cancellationSignal =*/ null); 236 } 237 238 return db.delete( 239 BlockedNumberDatabaseHelper.Tables.BLOCKED_NUMBERS, 240 selection, selectionArgs); 241 } 242 243 @Override query(@onNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder)244 public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, 245 @Nullable String[] selectionArgs, @Nullable String sortOrder) { 246 enforceReadPermissionAndMainUser(); 247 248 return query(uri, projection, selection, selectionArgs, sortOrder, null); 249 } 250 251 @Override query(@onNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder, @Nullable CancellationSignal cancellationSignal)252 public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, 253 @Nullable String[] selectionArgs, @Nullable String sortOrder, 254 @Nullable CancellationSignal cancellationSignal) { 255 enforceReadPermissionAndMainUser(); 256 257 final int match = sUriMatcher.match(uri); 258 Cursor cursor; 259 switch (match) { 260 case BLOCKED_LIST: 261 cursor = queryBlockedList(projection, selection, selectionArgs, sortOrder, 262 cancellationSignal); 263 break; 264 case BLOCKED_ID: 265 cursor = queryBlockedListWithId(ContentUris.parseId(uri), projection, selection, 266 cancellationSignal); 267 break; 268 default: 269 throw new IllegalArgumentException("Unsupported URI: " + uri); 270 } 271 // Tell the cursor what uri to watch, so it knows when its source data changes 272 cursor.setNotificationUri(getContext().getContentResolver(), uri); 273 return cursor; 274 } 275 276 /** 277 * Implements the "blocked/#" query. 278 */ queryBlockedListWithId(long id, String[] projection, String selection, CancellationSignal cancellationSignal)279 private Cursor queryBlockedListWithId(long id, String[] projection, String selection, 280 CancellationSignal cancellationSignal) { 281 throwForNonEmptySelection(selection); 282 283 return queryBlockedList(projection, ID_SELECTION, new String[]{Long.toString(id)}, 284 null, cancellationSignal); 285 } 286 287 /** 288 * Implements the "blocked/" query. 289 */ queryBlockedList(String[] projection, String selection, String[] selectionArgs, String sortOrder, CancellationSignal cancellationSignal)290 private Cursor queryBlockedList(String[] projection, String selection, String[] selectionArgs, 291 String sortOrder, CancellationSignal cancellationSignal) { 292 SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); 293 qb.setStrict(true); 294 qb.setTables(BlockedNumberDatabaseHelper.Tables.BLOCKED_NUMBERS); 295 qb.setProjectionMap(sBlockedNumberColumns); 296 297 return qb.query(mDbHelper.getReadableDatabase(), projection, selection, selectionArgs, 298 /* groupBy =*/ null, /* having =*/null, sortOrder, 299 /* limit =*/ null, cancellationSignal); 300 } 301 throwForNonEmptySelection(String selection)302 private void throwForNonEmptySelection(String selection) { 303 if (!TextUtils.isEmpty(selection)) { 304 throw new IllegalArgumentException( 305 "When ID is specified in URI, selection must be null"); 306 } 307 } 308 309 @Override call(@onNull String method, @Nullable String arg, @Nullable Bundle extras)310 public Bundle call(@NonNull String method, @Nullable String arg, @Nullable Bundle extras) { 311 final Bundle res = new Bundle(); 312 switch (method) { 313 case BlockedNumberContract.METHOD_IS_BLOCKED: 314 enforceReadPermissionAndMainUser(); 315 boolean isBlocked = isBlocked(arg); 316 res.putBoolean(BlockedNumberContract.RES_NUMBER_IS_BLOCKED, isBlocked); 317 res.putInt(BlockedNumberContract.RES_BLOCK_STATUS, 318 isBlocked ? BlockedNumberContract.STATUS_BLOCKED_IN_LIST 319 : BlockedNumberContract.STATUS_NOT_BLOCKED); 320 break; 321 case BlockedNumberContract.METHOD_CAN_CURRENT_USER_BLOCK_NUMBERS: 322 // No permission checks: any app should be able to access this API. 323 res.putBoolean( 324 BlockedNumberContract.RES_CAN_BLOCK_NUMBERS, canCurrentUserBlockUsers()); 325 break; 326 case BlockedNumberContract.METHOD_UNBLOCK: 327 enforceWritePermissionAndMainUser(); 328 329 res.putInt(BlockedNumberContract.RES_NUM_ROWS_DELETED, unblock(arg)); 330 break; 331 case SystemContract.METHOD_NOTIFY_EMERGENCY_CONTACT: 332 enforceSystemWritePermissionAndMainUser(); 333 334 notifyEmergencyContact(); 335 break; 336 case SystemContract.METHOD_END_BLOCK_SUPPRESSION: 337 enforceSystemWritePermissionAndMainUser(); 338 339 endBlockSuppression(); 340 break; 341 case SystemContract.METHOD_GET_BLOCK_SUPPRESSION_STATUS: 342 enforceSystemReadPermissionAndMainUser(); 343 344 SystemContract.BlockSuppressionStatus status = getBlockSuppressionStatus(); 345 res.putBoolean(SystemContract.RES_IS_BLOCKING_SUPPRESSED, status.isSuppressed); 346 res.putLong(SystemContract.RES_BLOCKING_SUPPRESSED_UNTIL_TIMESTAMP, 347 status.untilTimestampMillis); 348 break; 349 case SystemContract.METHOD_SHOULD_SYSTEM_BLOCK_NUMBER: 350 enforceSystemReadPermissionAndMainUser(); 351 int blockReason = shouldSystemBlockNumber(arg, extras); 352 res.putBoolean(BlockedNumberContract.RES_NUMBER_IS_BLOCKED, 353 blockReason != BlockedNumberContract.STATUS_NOT_BLOCKED); 354 res.putInt(BlockedNumberContract.RES_BLOCK_STATUS, blockReason); 355 break; 356 case SystemContract.METHOD_SHOULD_SHOW_EMERGENCY_CALL_NOTIFICATION: 357 enforceSystemReadPermissionAndMainUser(); 358 res.putBoolean(BlockedNumberContract.RES_SHOW_EMERGENCY_CALL_NOTIFICATION, 359 shouldShowEmergencyCallNotification()); 360 break; 361 case SystemContract.METHOD_GET_ENHANCED_BLOCK_SETTING: 362 enforceSystemReadPermissionAndMainUser(); 363 if (extras != null) { 364 String key = extras.getString(BlockedNumberContract.EXTRA_ENHANCED_SETTING_KEY); 365 boolean value = getEnhancedBlockSetting(key); 366 res.putBoolean(BlockedNumberContract.RES_ENHANCED_SETTING_IS_ENABLED, value); 367 } 368 break; 369 case SystemContract.METHOD_SET_ENHANCED_BLOCK_SETTING: 370 enforceSystemWritePermissionAndMainUser(); 371 if (extras != null) { 372 String key = extras.getString(BlockedNumberContract.EXTRA_ENHANCED_SETTING_KEY); 373 boolean value = extras.getBoolean( 374 BlockedNumberContract.EXTRA_ENHANCED_SETTING_VALUE, false); 375 setEnhancedBlockSetting(key, value); 376 } 377 break; 378 default: 379 enforceReadPermissionAndMainUser(); 380 381 throw new IllegalArgumentException("Unsupported method " + method); 382 } 383 return res; 384 } 385 unblock(String phoneNumber)386 private int unblock(String phoneNumber) { 387 if (TextUtils.isEmpty(phoneNumber)) { 388 return 0; 389 } 390 391 StringBuilder selectionBuilder = new StringBuilder(ORIGINAL_NUMBER_SELECTION); 392 String[] selectionArgs = new String[]{phoneNumber}; 393 final String e164Number = Utils.getE164Number(getContext(), phoneNumber, null); 394 if (!TextUtils.isEmpty(e164Number)) { 395 selectionBuilder.append(" or " + E164_NUMBER_SELECTION); 396 selectionArgs = new String[]{phoneNumber, e164Number}; 397 } 398 String selection = selectionBuilder.toString(); 399 if (DEBUG) { 400 Log.d(TAG, String.format("Unblocking numbers using selection: %s, args: %s", 401 selection, Arrays.toString(selectionArgs))); 402 } 403 return deleteBlockedNumber(selection, selectionArgs); 404 } 405 isEmergencyNumber(String phoneNumber)406 private boolean isEmergencyNumber(String phoneNumber) { 407 if (TextUtils.isEmpty(phoneNumber)) { 408 return false; 409 } 410 411 Context context = getContext(); 412 final String e164Number = Utils.getE164Number(context, phoneNumber, null); 413 TelephonyManager tm = context.getSystemService(TelephonyManager.class); 414 415 if (tm == null) { 416 return false; 417 } 418 try { 419 return tm.isEmergencyNumber(phoneNumber) || tm.isEmergencyNumber(e164Number); 420 } catch (UnsupportedOperationException | IllegalStateException e) { 421 return false; 422 } 423 } 424 isBlocked(String phoneNumber)425 private boolean isBlocked(String phoneNumber) { 426 if (TextUtils.isEmpty(phoneNumber)) { 427 Log.i(TAG, "isBlocked: NOT BLOCKED; empty #"); 428 return false; 429 } 430 431 final String inE164 = Utils.getE164Number(getContext(), phoneNumber, null); // may be empty. 432 433 final Cursor c = mDbHelper.getReadableDatabase().rawQuery( 434 "SELECT " + 435 BlockedNumberContract.BlockedNumbers.COLUMN_ORIGINAL_NUMBER + "," + 436 BlockedNumberContract.BlockedNumbers.COLUMN_E164_NUMBER + 437 " FROM " + BlockedNumberDatabaseHelper.Tables.BLOCKED_NUMBERS + 438 " WHERE " + BlockedNumberContract.BlockedNumbers.COLUMN_ORIGINAL_NUMBER + "=?1" + 439 " OR (?2 != '' AND " + 440 BlockedNumberContract.BlockedNumbers.COLUMN_E164_NUMBER + "=?2)", 441 new String[] {phoneNumber, inE164} 442 ); 443 try { 444 while (c.moveToNext()) { 445 final String original = c.getString(0); 446 final String e164 = c.getString(1); 447 Log.i(TAG, String.format("isBlocked: BLOCKED; number=%s, e164=%s, foundOrig=%s, " 448 + "foundE164=%s", 449 piiHandle(phoneNumber), 450 piiHandle(inE164), 451 piiHandle(original), 452 piiHandle(e164))); 453 return true; 454 } 455 } finally { 456 c.close(); 457 } 458 // No match found. 459 Log.i(TAG, String.format("isBlocked: NOT BLOCKED; number=%s, e164=%s", 460 piiHandle(phoneNumber), piiHandle(inE164))); 461 return false; 462 } 463 canCurrentUserBlockUsers()464 private boolean canCurrentUserBlockUsers() { 465 int currentUserId = getContext().getUserId(); 466 467 if (!android.multiuser.Flags.allowMainUserToAccessBlockedNumberProvider()) { 468 UserManager userManager = getContext().getSystemService(UserManager.class); 469 // Allow USER_SYSTEM and managed profile to block users 470 return (currentUserId == UserHandle.USER_SYSTEM || 471 (userManager != null && userManager.isManagedProfile(currentUserId))); 472 } else { 473 // Allow SYSTEM user and users with messaging support to block users 474 return (currentUserId == UserHandle.USER_SYSTEM 475 || isMainUserOrManagedProfile(currentUserId)); 476 } 477 } 478 isMainUserOrManagedProfile(int currentUserId)479 private boolean isMainUserOrManagedProfile(int currentUserId) { 480 UserManager userManager = getContext().getSystemService(UserManager.class); 481 // Only MAIN User and Managed profile users can have full messaging support. 482 return userManager != null 483 && (userManager.isMainUser() || userManager.isManagedProfile(currentUserId)); 484 } 485 notifyEmergencyContact()486 private void notifyEmergencyContact() { 487 long sec = getBlockSuppressSecondsFromCarrierConfig(); 488 long millisToWrite = sec < 0 489 ? BLOCKING_DISABLED_FOREVER : System.currentTimeMillis() + (sec * 1000); 490 writeBlockSuppressionExpiryTimePref(millisToWrite); 491 writeEmergencyCallNotificationPref(true); 492 notifyBlockSuppressionStateChange(); 493 } 494 495 private void endBlockSuppression() { 496 // Nothing to do if blocks are not being suppressed. 497 if (getBlockSuppressionStatus().isSuppressed) { 498 writeBlockSuppressionExpiryTimePref(0); 499 writeEmergencyCallNotificationPref(false); 500 notifyBlockSuppressionStateChange(); 501 } 502 } 503 504 private SystemContract.BlockSuppressionStatus getBlockSuppressionStatus() { 505 SharedPreferences pref = getContext().getSharedPreferences(PREF_FILE, Context.MODE_PRIVATE); 506 long blockSuppressionExpiryTimeMillis = pref.getLong(BLOCK_SUPPRESSION_EXPIRY_TIME_PREF, 0); 507 boolean isSuppressed = blockSuppressionExpiryTimeMillis == BLOCKING_DISABLED_FOREVER 508 || System.currentTimeMillis() < blockSuppressionExpiryTimeMillis; 509 return new SystemContract.BlockSuppressionStatus(isSuppressed, 510 blockSuppressionExpiryTimeMillis); 511 } 512 513 private int shouldSystemBlockNumber(String phoneNumber, Bundle extras) { 514 if (getBlockSuppressionStatus().isSuppressed) { 515 return BlockedNumberContract.STATUS_NOT_BLOCKED; 516 } 517 if (isEmergencyNumber(phoneNumber)) { 518 return BlockedNumberContract.STATUS_NOT_BLOCKED; 519 } 520 521 int blockReason = BlockedNumberContract.STATUS_NOT_BLOCKED; 522 if (extras != null && !extras.isEmpty()) { 523 // check enhanced blocking setting 524 boolean contactExist = extras.getBoolean(BlockedNumberContract.EXTRA_CONTACT_EXIST); 525 int presentation = extras.getInt(BlockedNumberContract.EXTRA_CALL_PRESENTATION); 526 switch (presentation) { 527 case TelecomManager.PRESENTATION_ALLOWED: 528 if (getEnhancedBlockSetting( 529 SystemContract.ENHANCED_SETTING_KEY_BLOCK_UNREGISTERED) 530 && !contactExist) { 531 blockReason = BlockedNumberContract.STATUS_BLOCKED_NOT_IN_CONTACTS; 532 } 533 break; 534 case TelecomManager.PRESENTATION_RESTRICTED: 535 if (getEnhancedBlockSetting( 536 SystemContract.ENHANCED_SETTING_KEY_BLOCK_PRIVATE)) { 537 blockReason = BlockedNumberContract.STATUS_BLOCKED_RESTRICTED; 538 } 539 break; 540 case TelecomManager.PRESENTATION_PAYPHONE: 541 if (getEnhancedBlockSetting( 542 SystemContract.ENHANCED_SETTING_KEY_BLOCK_PAYPHONE)) { 543 blockReason = BlockedNumberContract.STATUS_BLOCKED_PAYPHONE; 544 } 545 break; 546 case TelecomManager.PRESENTATION_UNKNOWN: 547 if (getEnhancedBlockSetting( 548 SystemContract.ENHANCED_SETTING_KEY_BLOCK_UNKNOWN)) { 549 blockReason = BlockedNumberContract.STATUS_BLOCKED_UNKNOWN_NUMBER; 550 } 551 break; 552 case TelecomManager.PRESENTATION_UNAVAILABLE: 553 if (getEnhancedBlockSetting( 554 SystemContract.ENHANCED_SETTING_KEY_BLOCK_UNKNOWN)) { 555 blockReason = BlockedNumberContract.STATUS_BLOCKED_UNAVAILABLE; 556 } 557 break; 558 default: 559 break; 560 } 561 } 562 if (blockReason == BlockedNumberContract.STATUS_NOT_BLOCKED && isBlocked(phoneNumber)) { 563 blockReason = BlockedNumberContract.STATUS_BLOCKED_IN_LIST; 564 } 565 return blockReason; 566 } 567 568 private boolean shouldShowEmergencyCallNotification() { 569 return isEnhancedCallBlockingEnabledByPlatform() 570 && (isShowCallBlockingDisabledNotificationAlways() 571 || isAnyEnhancedBlockingSettingEnabled()) 572 && getBlockSuppressionStatus().isSuppressed 573 && getEnhancedBlockSetting( 574 SystemContract.ENHANCED_SETTING_KEY_SHOW_EMERGENCY_CALL_NOTIFICATION); 575 } 576 577 private PersistableBundle getCarrierConfig() { 578 CarrierConfigManager configManager = (CarrierConfigManager) getContext().getSystemService( 579 Context.CARRIER_CONFIG_SERVICE); 580 PersistableBundle carrierConfig = configManager.getConfig(); 581 if (carrierConfig == null) { 582 carrierConfig = configManager.getDefaultConfig(); 583 } 584 return carrierConfig; 585 } 586 587 private boolean isEnhancedCallBlockingEnabledByPlatform() { 588 return getCarrierConfig().getBoolean( 589 CarrierConfigManager.KEY_SUPPORT_ENHANCED_CALL_BLOCKING_BOOL); 590 } 591 592 private boolean isShowCallBlockingDisabledNotificationAlways() { 593 return getCarrierConfig().getBoolean( 594 CarrierConfigManager.KEY_SHOW_CALL_BLOCKING_DISABLED_NOTIFICATION_ALWAYS_BOOL); 595 } 596 597 private boolean isAnyEnhancedBlockingSettingEnabled() { 598 return getEnhancedBlockSetting(SystemContract.ENHANCED_SETTING_KEY_BLOCK_UNREGISTERED) 599 || getEnhancedBlockSetting(SystemContract.ENHANCED_SETTING_KEY_BLOCK_PRIVATE) 600 || getEnhancedBlockSetting(SystemContract.ENHANCED_SETTING_KEY_BLOCK_PAYPHONE) 601 || getEnhancedBlockSetting(SystemContract.ENHANCED_SETTING_KEY_BLOCK_UNKNOWN); 602 } 603 604 private boolean getEnhancedBlockSetting(String key) { 605 SharedPreferences pref = getContext().getSharedPreferences(PREF_FILE, Context.MODE_PRIVATE); 606 return pref.getBoolean(key, false); 607 } 608 609 private void setEnhancedBlockSetting(String key, boolean value) { 610 SharedPreferences pref = getContext().getSharedPreferences(PREF_FILE, Context.MODE_PRIVATE); 611 SharedPreferences.Editor editor = pref.edit(); 612 editor.putBoolean(key, value); 613 editor.apply(); 614 } 615 616 private void writeEmergencyCallNotificationPref(boolean show) { 617 if (!isEnhancedCallBlockingEnabledByPlatform()) { 618 return; 619 } 620 setEnhancedBlockSetting( 621 SystemContract.ENHANCED_SETTING_KEY_SHOW_EMERGENCY_CALL_NOTIFICATION, show); 622 } 623 624 private void writeBlockSuppressionExpiryTimePref(long expiryTimeMillis) { 625 SharedPreferences pref = getContext().getSharedPreferences(PREF_FILE, Context.MODE_PRIVATE); 626 SharedPreferences.Editor editor = pref.edit(); 627 editor.putLong(BLOCK_SUPPRESSION_EXPIRY_TIME_PREF, expiryTimeMillis); 628 editor.apply(); 629 } 630 631 private long getBlockSuppressSecondsFromCarrierConfig() { 632 CarrierConfigManager carrierConfigManager = 633 getContext().getSystemService(CarrierConfigManager.class); 634 int carrierConfigValue = carrierConfigManager.getConfig().getInt 635 (CarrierConfigManager.KEY_DURATION_BLOCKING_DISABLED_AFTER_EMERGENCY_INT); 636 boolean isValidValue = carrierConfigValue <= MAX_BLOCKING_DISABLED_DURATION_SECONDS; 637 return isValidValue ? carrierConfigValue : CarrierConfigManager.getDefaultConfig().getInt( 638 CarrierConfigManager.KEY_DURATION_BLOCKING_DISABLED_AFTER_EMERGENCY_INT); 639 } 640 641 /** 642 * Returns {@code false} when the caller is not root, the user selected dialer, the 643 * default SMS app or a carrier app. 644 */ 645 private boolean checkForPrivilegedApplications() { 646 if (Binder.getCallingUid() == Process.ROOT_UID) { 647 return true; 648 } 649 650 final String callingPackage = getCallingPackage(); 651 if (TextUtils.isEmpty(callingPackage)) { 652 Log.w(TAG, "callingPackage not accessible"); 653 } else { 654 final TelecomManager telecom = getContext().getSystemService(TelecomManager.class); 655 656 if (callingPackage.equals(telecom.getDefaultDialerPackage()) 657 || callingPackage.equals(telecom.getSystemDialerPackage())) { 658 return true; 659 } 660 final AppOpsManager appOps = getContext().getSystemService(AppOpsManager.class); 661 if (appOps.noteOp(AppOpsManager.OP_WRITE_SMS, 662 Binder.getCallingUid(), callingPackage) == AppOpsManager.MODE_ALLOWED) { 663 return true; 664 } 665 666 final TelephonyManager telephonyManager = 667 getContext().getSystemService(TelephonyManager.class); 668 final long token = Binder.clearCallingIdentity(); 669 try { 670 return telephonyManager.checkCarrierPrivilegesForPackageAnyPhone(callingPackage) == 671 TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS; 672 } finally { 673 Binder.restoreCallingIdentity(token); 674 } 675 } 676 return false; 677 } 678 679 private void notifyBlockSuppressionStateChange() { 680 Intent intent = new Intent(SystemContract.ACTION_BLOCK_SUPPRESSION_STATE_CHANGED); 681 getContext().sendBroadcast(intent, Manifest.permission.READ_BLOCKED_NUMBERS); 682 } 683 684 private void enforceReadPermission() { 685 checkForPermission(android.Manifest.permission.READ_BLOCKED_NUMBERS); 686 } 687 688 private void enforceReadPermissionAndMainUser() { 689 checkForPermissionAndMainUser(android.Manifest.permission.READ_BLOCKED_NUMBERS); 690 } 691 692 private void enforceWritePermissionAndMainUser() { 693 checkForPermissionAndMainUser(android.Manifest.permission.WRITE_BLOCKED_NUMBERS); 694 } 695 696 private void checkForPermissionAndMainUser(String permission) { 697 checkForPermission(permission); 698 if (!canCurrentUserBlockUsers()) { 699 throwCurrentUserNotPermittedSecurityException(); 700 } 701 } 702 703 private void checkForPermission(String permission) { 704 boolean permitted = passesSystemPermissionCheck(permission) 705 || checkForPrivilegedApplications() || isSelf(); 706 if (!permitted) { 707 throwSecurityException(); 708 } 709 } 710 711 private void enforceSystemReadPermissionAndMainUser() { 712 enforceSystemPermissionAndUser(android.Manifest.permission.READ_BLOCKED_NUMBERS); 713 } 714 715 private void enforceSystemWritePermissionAndMainUser() { 716 enforceSystemPermissionAndUser(android.Manifest.permission.WRITE_BLOCKED_NUMBERS); 717 } 718 719 private void enforceSystemPermissionAndUser(String permission) { 720 if (!canCurrentUserBlockUsers()) { 721 throwCurrentUserNotPermittedSecurityException(); 722 } 723 724 if (!passesSystemPermissionCheck(permission)) { 725 throwSecurityException(); 726 } 727 } 728 729 private boolean passesSystemPermissionCheck(String permission) { 730 return getContext().checkCallingPermission(permission) 731 == PackageManager.PERMISSION_GRANTED; 732 } 733 734 private boolean isSelf() { 735 return ALLOW_SELF_CALL && Binder.getCallingPid() == Process.myPid(); 736 } 737 738 private void throwSecurityException() { 739 throw new SecurityException("Caller must be system, default dialer or default SMS app"); 740 } 741 742 private void throwCurrentUserNotPermittedSecurityException() { 743 throw new SecurityException("The current user cannot perform this operation"); 744 } 745 } 746