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 enforceWritePermissionAndPrimaryUser(); 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 enforceWritePermissionAndPrimaryUser(); 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 enforceWritePermissionAndPrimaryUser(); 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 enforceReadPermissionAndPrimaryUser(); 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 enforceReadPermissionAndPrimaryUser(); 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 enforceReadPermissionAndPrimaryUser(); 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 enforceWritePermissionAndPrimaryUser(); 328 329 res.putInt(BlockedNumberContract.RES_NUM_ROWS_DELETED, unblock(arg)); 330 break; 331 case SystemContract.METHOD_NOTIFY_EMERGENCY_CONTACT: 332 enforceSystemWritePermissionAndPrimaryUser(); 333 334 notifyEmergencyContact(); 335 break; 336 case SystemContract.METHOD_END_BLOCK_SUPPRESSION: 337 enforceSystemWritePermissionAndPrimaryUser(); 338 339 endBlockSuppression(); 340 break; 341 case SystemContract.METHOD_GET_BLOCK_SUPPRESSION_STATUS: 342 enforceSystemReadPermissionAndPrimaryUser(); 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 enforceSystemReadPermissionAndPrimaryUser(); 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 enforceSystemReadPermissionAndPrimaryUser(); 358 res.putBoolean(BlockedNumberContract.RES_SHOW_EMERGENCY_CALL_NOTIFICATION, 359 shouldShowEmergencyCallNotification()); 360 break; 361 case SystemContract.METHOD_GET_ENHANCED_BLOCK_SETTING: 362 enforceSystemReadPermissionAndPrimaryUser(); 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 enforceSystemWritePermissionAndPrimaryUser(); 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 enforceReadPermissionAndPrimaryUser(); 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 return tm.isEmergencyNumber(phoneNumber) || tm.isEmergencyNumber(e164Number); 415 } 416 isBlocked(String phoneNumber)417 private boolean isBlocked(String phoneNumber) { 418 if (TextUtils.isEmpty(phoneNumber)) { 419 Log.i(TAG, "isBlocked: NOT BLOCKED; empty #"); 420 return false; 421 } 422 423 final String inE164 = Utils.getE164Number(getContext(), phoneNumber, null); // may be empty. 424 425 final Cursor c = mDbHelper.getReadableDatabase().rawQuery( 426 "SELECT " + 427 BlockedNumberContract.BlockedNumbers.COLUMN_ORIGINAL_NUMBER + "," + 428 BlockedNumberContract.BlockedNumbers.COLUMN_E164_NUMBER + 429 " FROM " + BlockedNumberDatabaseHelper.Tables.BLOCKED_NUMBERS + 430 " WHERE " + BlockedNumberContract.BlockedNumbers.COLUMN_ORIGINAL_NUMBER + "=?1" + 431 " OR (?2 != '' AND " + 432 BlockedNumberContract.BlockedNumbers.COLUMN_E164_NUMBER + "=?2)", 433 new String[] {phoneNumber, inE164} 434 ); 435 try { 436 while (c.moveToNext()) { 437 final String original = c.getString(0); 438 final String e164 = c.getString(1); 439 Log.i(TAG, String.format("isBlocked: BLOCKED; number=%s, e164=%s, foundOrig=%s, " 440 + "foundE164=%s", 441 piiHandle(phoneNumber), 442 piiHandle(inE164), 443 piiHandle(original), 444 piiHandle(e164))); 445 return true; 446 } 447 } finally { 448 c.close(); 449 } 450 // No match found. 451 Log.i(TAG, String.format("isBlocked: NOT BLOCKED; number=%s, e164=%s", 452 piiHandle(phoneNumber), piiHandle(inE164))); 453 return false; 454 } 455 canCurrentUserBlockUsers()456 private boolean canCurrentUserBlockUsers() { 457 int currentUserId = getContext().getUserId(); 458 UserManager userManager = getContext().getSystemService(UserManager.class); 459 // Allow USER_SYSTEM and managed profile to block users 460 return (currentUserId == UserHandle.USER_SYSTEM || 461 (userManager != null && userManager.isManagedProfile(currentUserId))); 462 } 463 notifyEmergencyContact()464 private void notifyEmergencyContact() { 465 long sec = getBlockSuppressSecondsFromCarrierConfig(); 466 long millisToWrite = sec < 0 467 ? BLOCKING_DISABLED_FOREVER : System.currentTimeMillis() + (sec * 1000); 468 writeBlockSuppressionExpiryTimePref(millisToWrite); 469 writeEmergencyCallNotificationPref(true); 470 notifyBlockSuppressionStateChange(); 471 } 472 473 private void endBlockSuppression() { 474 // Nothing to do if blocks are not being suppressed. 475 if (getBlockSuppressionStatus().isSuppressed) { 476 writeBlockSuppressionExpiryTimePref(0); 477 writeEmergencyCallNotificationPref(false); 478 notifyBlockSuppressionStateChange(); 479 } 480 } 481 482 private SystemContract.BlockSuppressionStatus getBlockSuppressionStatus() { 483 SharedPreferences pref = getContext().getSharedPreferences(PREF_FILE, Context.MODE_PRIVATE); 484 long blockSuppressionExpiryTimeMillis = pref.getLong(BLOCK_SUPPRESSION_EXPIRY_TIME_PREF, 0); 485 boolean isSuppressed = blockSuppressionExpiryTimeMillis == BLOCKING_DISABLED_FOREVER 486 || System.currentTimeMillis() < blockSuppressionExpiryTimeMillis; 487 return new SystemContract.BlockSuppressionStatus(isSuppressed, 488 blockSuppressionExpiryTimeMillis); 489 } 490 491 private int shouldSystemBlockNumber(String phoneNumber, Bundle extras) { 492 if (getBlockSuppressionStatus().isSuppressed) { 493 return BlockedNumberContract.STATUS_NOT_BLOCKED; 494 } 495 if (isEmergencyNumber(phoneNumber)) { 496 return BlockedNumberContract.STATUS_NOT_BLOCKED; 497 } 498 499 int blockReason = BlockedNumberContract.STATUS_NOT_BLOCKED; 500 if (extras != null && !extras.isEmpty()) { 501 // check enhanced blocking setting 502 boolean contactExist = extras.getBoolean(BlockedNumberContract.EXTRA_CONTACT_EXIST); 503 int presentation = extras.getInt(BlockedNumberContract.EXTRA_CALL_PRESENTATION); 504 switch (presentation) { 505 case TelecomManager.PRESENTATION_ALLOWED: 506 if (getEnhancedBlockSetting( 507 SystemContract.ENHANCED_SETTING_KEY_BLOCK_UNREGISTERED) 508 && !contactExist) { 509 blockReason = BlockedNumberContract.STATUS_BLOCKED_NOT_IN_CONTACTS; 510 } 511 break; 512 case TelecomManager.PRESENTATION_RESTRICTED: 513 if (getEnhancedBlockSetting( 514 SystemContract.ENHANCED_SETTING_KEY_BLOCK_PRIVATE)) { 515 blockReason = BlockedNumberContract.STATUS_BLOCKED_RESTRICTED; 516 } 517 break; 518 case TelecomManager.PRESENTATION_PAYPHONE: 519 if (getEnhancedBlockSetting( 520 SystemContract.ENHANCED_SETTING_KEY_BLOCK_PAYPHONE)) { 521 blockReason = BlockedNumberContract.STATUS_BLOCKED_PAYPHONE; 522 } 523 break; 524 case TelecomManager.PRESENTATION_UNKNOWN: 525 if (getEnhancedBlockSetting( 526 SystemContract.ENHANCED_SETTING_KEY_BLOCK_UNKNOWN)) { 527 blockReason = BlockedNumberContract.STATUS_BLOCKED_UNKNOWN_NUMBER; 528 } 529 break; 530 case TelecomManager.PRESENTATION_UNAVAILABLE: 531 if (getEnhancedBlockSetting( 532 SystemContract.ENHANCED_SETTING_KEY_BLOCK_UNKNOWN)) { 533 blockReason = BlockedNumberContract.STATUS_BLOCKED_UNAVAILABLE; 534 } 535 break; 536 default: 537 break; 538 } 539 } 540 if (blockReason == BlockedNumberContract.STATUS_NOT_BLOCKED && isBlocked(phoneNumber)) { 541 blockReason = BlockedNumberContract.STATUS_BLOCKED_IN_LIST; 542 } 543 return blockReason; 544 } 545 546 private boolean shouldShowEmergencyCallNotification() { 547 return isEnhancedCallBlockingEnabledByPlatform() 548 && (isShowCallBlockingDisabledNotificationAlways() 549 || isAnyEnhancedBlockingSettingEnabled()) 550 && getBlockSuppressionStatus().isSuppressed 551 && getEnhancedBlockSetting( 552 SystemContract.ENHANCED_SETTING_KEY_SHOW_EMERGENCY_CALL_NOTIFICATION); 553 } 554 555 private PersistableBundle getCarrierConfig() { 556 CarrierConfigManager configManager = (CarrierConfigManager) getContext().getSystemService( 557 Context.CARRIER_CONFIG_SERVICE); 558 PersistableBundle carrierConfig = configManager.getConfig(); 559 if (carrierConfig == null) { 560 carrierConfig = configManager.getDefaultConfig(); 561 } 562 return carrierConfig; 563 } 564 565 private boolean isEnhancedCallBlockingEnabledByPlatform() { 566 return getCarrierConfig().getBoolean( 567 CarrierConfigManager.KEY_SUPPORT_ENHANCED_CALL_BLOCKING_BOOL); 568 } 569 570 private boolean isShowCallBlockingDisabledNotificationAlways() { 571 return getCarrierConfig().getBoolean( 572 CarrierConfigManager.KEY_SHOW_CALL_BLOCKING_DISABLED_NOTIFICATION_ALWAYS_BOOL); 573 } 574 575 private boolean isAnyEnhancedBlockingSettingEnabled() { 576 return getEnhancedBlockSetting(SystemContract.ENHANCED_SETTING_KEY_BLOCK_UNREGISTERED) 577 || getEnhancedBlockSetting(SystemContract.ENHANCED_SETTING_KEY_BLOCK_PRIVATE) 578 || getEnhancedBlockSetting(SystemContract.ENHANCED_SETTING_KEY_BLOCK_PAYPHONE) 579 || getEnhancedBlockSetting(SystemContract.ENHANCED_SETTING_KEY_BLOCK_UNKNOWN); 580 } 581 582 private boolean getEnhancedBlockSetting(String key) { 583 SharedPreferences pref = getContext().getSharedPreferences(PREF_FILE, Context.MODE_PRIVATE); 584 return pref.getBoolean(key, false); 585 } 586 587 private void setEnhancedBlockSetting(String key, boolean value) { 588 SharedPreferences pref = getContext().getSharedPreferences(PREF_FILE, Context.MODE_PRIVATE); 589 SharedPreferences.Editor editor = pref.edit(); 590 editor.putBoolean(key, value); 591 editor.apply(); 592 } 593 594 private void writeEmergencyCallNotificationPref(boolean show) { 595 if (!isEnhancedCallBlockingEnabledByPlatform()) { 596 return; 597 } 598 setEnhancedBlockSetting( 599 SystemContract.ENHANCED_SETTING_KEY_SHOW_EMERGENCY_CALL_NOTIFICATION, show); 600 } 601 602 private void writeBlockSuppressionExpiryTimePref(long expiryTimeMillis) { 603 SharedPreferences pref = getContext().getSharedPreferences(PREF_FILE, Context.MODE_PRIVATE); 604 SharedPreferences.Editor editor = pref.edit(); 605 editor.putLong(BLOCK_SUPPRESSION_EXPIRY_TIME_PREF, expiryTimeMillis); 606 editor.apply(); 607 } 608 609 private long getBlockSuppressSecondsFromCarrierConfig() { 610 CarrierConfigManager carrierConfigManager = 611 getContext().getSystemService(CarrierConfigManager.class); 612 int carrierConfigValue = carrierConfigManager.getConfig().getInt 613 (CarrierConfigManager.KEY_DURATION_BLOCKING_DISABLED_AFTER_EMERGENCY_INT); 614 boolean isValidValue = carrierConfigValue <= MAX_BLOCKING_DISABLED_DURATION_SECONDS; 615 return isValidValue ? carrierConfigValue : CarrierConfigManager.getDefaultConfig().getInt( 616 CarrierConfigManager.KEY_DURATION_BLOCKING_DISABLED_AFTER_EMERGENCY_INT); 617 } 618 619 /** 620 * Returns {@code false} when the caller is not root, the user selected dialer, the 621 * default SMS app or a carrier app. 622 */ 623 private boolean checkForPrivilegedApplications() { 624 if (Binder.getCallingUid() == Process.ROOT_UID) { 625 return true; 626 } 627 628 final String callingPackage = getCallingPackage(); 629 if (TextUtils.isEmpty(callingPackage)) { 630 Log.w(TAG, "callingPackage not accessible"); 631 } else { 632 final TelecomManager telecom = getContext().getSystemService(TelecomManager.class); 633 634 if (callingPackage.equals(telecom.getDefaultDialerPackage()) 635 || callingPackage.equals(telecom.getSystemDialerPackage())) { 636 return true; 637 } 638 final AppOpsManager appOps = getContext().getSystemService(AppOpsManager.class); 639 if (appOps.noteOp(AppOpsManager.OP_WRITE_SMS, 640 Binder.getCallingUid(), callingPackage) == AppOpsManager.MODE_ALLOWED) { 641 return true; 642 } 643 644 final TelephonyManager telephonyManager = 645 getContext().getSystemService(TelephonyManager.class); 646 final long token = Binder.clearCallingIdentity(); 647 try { 648 return telephonyManager.checkCarrierPrivilegesForPackageAnyPhone(callingPackage) == 649 TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS; 650 } finally { 651 Binder.restoreCallingIdentity(token); 652 } 653 } 654 return false; 655 } 656 657 private void notifyBlockSuppressionStateChange() { 658 Intent intent = new Intent(SystemContract.ACTION_BLOCK_SUPPRESSION_STATE_CHANGED); 659 getContext().sendBroadcast(intent, Manifest.permission.READ_BLOCKED_NUMBERS); 660 } 661 662 private void enforceReadPermission() { 663 checkForPermission(android.Manifest.permission.READ_BLOCKED_NUMBERS); 664 } 665 666 private void enforceReadPermissionAndPrimaryUser() { 667 checkForPermissionAndPrimaryUser(android.Manifest.permission.READ_BLOCKED_NUMBERS); 668 } 669 670 private void enforceWritePermissionAndPrimaryUser() { 671 checkForPermissionAndPrimaryUser(android.Manifest.permission.WRITE_BLOCKED_NUMBERS); 672 } 673 674 private void checkForPermissionAndPrimaryUser(String permission) { 675 checkForPermission(permission); 676 if (!canCurrentUserBlockUsers()) { 677 throwCurrentUserNotPermittedSecurityException(); 678 } 679 } 680 681 private void checkForPermission(String permission) { 682 boolean permitted = passesSystemPermissionCheck(permission) 683 || checkForPrivilegedApplications() || isSelf(); 684 if (!permitted) { 685 throwSecurityException(); 686 } 687 } 688 689 private void enforceSystemReadPermissionAndPrimaryUser() { 690 enforceSystemPermissionAndUser(android.Manifest.permission.READ_BLOCKED_NUMBERS); 691 } 692 693 private void enforceSystemWritePermissionAndPrimaryUser() { 694 enforceSystemPermissionAndUser(android.Manifest.permission.WRITE_BLOCKED_NUMBERS); 695 } 696 697 private void enforceSystemPermissionAndUser(String permission) { 698 if (!canCurrentUserBlockUsers()) { 699 throwCurrentUserNotPermittedSecurityException(); 700 } 701 702 if (!passesSystemPermissionCheck(permission)) { 703 throwSecurityException(); 704 } 705 } 706 707 private boolean passesSystemPermissionCheck(String permission) { 708 return getContext().checkCallingPermission(permission) 709 == PackageManager.PERMISSION_GRANTED; 710 } 711 712 private boolean isSelf() { 713 return ALLOW_SELF_CALL && Binder.getCallingPid() == Process.myPid(); 714 } 715 716 private void throwSecurityException() { 717 throw new SecurityException("Caller must be system, default dialer or default SMS app"); 718 } 719 720 private void throwCurrentUserNotPermittedSecurityException() { 721 throw new SecurityException("The current user cannot perform this operation"); 722 } 723 } 724