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