| /* |
| * Copyright (C) 2019 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package android.provider; |
| |
| import static android.provider.BlockedNumberContract.AUTHORITY_URI; |
| import static android.provider.BlockedNumberContract.EXTRA_ENHANCED_SETTING_KEY; |
| import static android.provider.BlockedNumberContract.EXTRA_ENHANCED_SETTING_VALUE; |
| import static android.provider.BlockedNumberContract.RES_BLOCK_STATUS; |
| import static android.provider.BlockedNumberContract.RES_ENHANCED_SETTING_IS_ENABLED; |
| import static android.provider.BlockedNumberContract.RES_SHOW_EMERGENCY_CALL_NOTIFICATION; |
| import static android.provider.BlockedNumberContract.STATUS_NOT_BLOCKED; |
| import static android.provider.BlockedNumberContract.SystemContract.METHOD_END_BLOCK_SUPPRESSION; |
| import static android.provider.BlockedNumberContract.SystemContract.METHOD_GET_BLOCK_SUPPRESSION_STATUS; |
| import static android.provider.BlockedNumberContract.SystemContract.METHOD_GET_ENHANCED_BLOCK_SETTING; |
| import static android.provider.BlockedNumberContract.SystemContract.METHOD_NOTIFY_EMERGENCY_CONTACT; |
| import static android.provider.BlockedNumberContract.SystemContract.METHOD_SET_ENHANCED_BLOCK_SETTING; |
| import static android.provider.BlockedNumberContract.SystemContract.METHOD_SHOULD_SHOW_EMERGENCY_CALL_NOTIFICATION; |
| import static android.provider.BlockedNumberContract.SystemContract.METHOD_SHOULD_SYSTEM_BLOCK_NUMBER; |
| import static android.provider.BlockedNumberContract.SystemContract.RES_BLOCKING_SUPPRESSED_UNTIL_TIMESTAMP; |
| import static android.provider.BlockedNumberContract.SystemContract.RES_IS_BLOCKING_SUPPRESSED; |
| |
| import android.Manifest; |
| import android.annotation.FlaggedApi; |
| import android.annotation.NonNull; |
| import android.annotation.RequiresPermission; |
| import android.annotation.SystemApi; |
| import android.content.Context; |
| import android.os.Bundle; |
| import android.telecom.Log; |
| import android.telecom.TelecomManager; |
| |
| import com.android.server.telecom.flags.Flags; |
| |
| /** |
| * Constants and methods to interact with the blocked numbers list. This class also serves as |
| * a mediator between the BlockedNumber provider and the system: it manages blocking behavior |
| * when the user contacts emergency services. Currently, this is only used internally by Telecom. |
| * |
| * Refer to {@link BlockedNumberContract} for more context. |
| * @hide |
| */ |
| @SystemApi |
| @FlaggedApi(Flags.FLAG_TELECOM_MAINLINE_BLOCKED_NUMBERS_MANAGER) |
| public final class BlockedNumbersManager { |
| private static final String LOG_TAG = BlockedNumbersManager.class.getSimpleName(); |
| private Context mContext; |
| |
| /** |
| * @hide |
| */ |
| public BlockedNumbersManager(Context context) { |
| mContext = context; |
| } |
| |
| /** |
| * A protected broadcast intent action for letting components with |
| * {@link android.Manifest.permission#READ_BLOCKED_NUMBERS} know that the block suppression |
| * status as returned by {@link #getBlockSuppressionStatus()} has been updated. |
| * @hide |
| */ |
| @SystemApi |
| @FlaggedApi(Flags.FLAG_TELECOM_MAINLINE_BLOCKED_NUMBERS_MANAGER) |
| public static final String ACTION_BLOCK_SUPPRESSION_STATE_CHANGED = |
| "android.provider.action.BLOCK_SUPPRESSION_STATE_CHANGED"; |
| |
| /** |
| * Preference key of block numbers not in contacts setting. |
| * @hide |
| */ |
| @SystemApi |
| @FlaggedApi(Flags.FLAG_TELECOM_MAINLINE_BLOCKED_NUMBERS_MANAGER) |
| public static final String ENHANCED_SETTING_KEY_BLOCK_UNREGISTERED = |
| "block_numbers_not_in_contacts_setting"; |
| |
| /** |
| * Preference key of block private number calls setting. |
| * @hide |
| */ |
| @SystemApi |
| @FlaggedApi(Flags.FLAG_TELECOM_MAINLINE_BLOCKED_NUMBERS_MANAGER) |
| public static final String ENHANCED_SETTING_KEY_BLOCK_PRIVATE = |
| "block_private_number_calls_setting"; |
| |
| /** |
| * Preference key of block payphone calls setting. |
| * @hide |
| */ |
| @SystemApi |
| @FlaggedApi(Flags.FLAG_TELECOM_MAINLINE_BLOCKED_NUMBERS_MANAGER) |
| public static final String ENHANCED_SETTING_KEY_BLOCK_PAYPHONE = |
| "block_payphone_calls_setting"; |
| |
| /** |
| * Preference key of block unknown calls setting. |
| * @hide |
| */ |
| @SystemApi |
| @FlaggedApi(Flags.FLAG_TELECOM_MAINLINE_BLOCKED_NUMBERS_MANAGER) |
| public static final String ENHANCED_SETTING_KEY_BLOCK_UNKNOWN = |
| "block_unknown_calls_setting"; |
| |
| /** |
| * Preference key for whether should show an emergency call notification. |
| * @hide |
| */ |
| @SystemApi |
| @FlaggedApi(Flags.FLAG_TELECOM_MAINLINE_BLOCKED_NUMBERS_MANAGER) |
| public static final String ENHANCED_SETTING_KEY_SHOW_EMERGENCY_CALL_NOTIFICATION = |
| "show_emergency_call_notification"; |
| |
| /** |
| * Preference key of block unavailable calls setting. |
| * @hide |
| */ |
| @SystemApi |
| @FlaggedApi(Flags.FLAG_TELECOM_MAINLINE_BLOCKED_NUMBERS_MANAGER) |
| public static final String ENHANCED_SETTING_KEY_BLOCK_UNAVAILABLE = |
| "block_unavailable_calls_setting"; |
| |
| /** |
| * Notifies the provider that emergency services were contacted by the user. |
| * <p> This results in {@link #shouldSystemBlockNumber} returning {@code false} independent |
| * of the contents of the provider for a duration defined by |
| * {@link android.telephony.CarrierConfigManager#KEY_DURATION_BLOCKING_DISABLED_AFTER_EMERGENCY_INT} |
| * the provider unless {@link #endBlockSuppression()} is called. |
| * @hide |
| */ |
| @SystemApi |
| @RequiresPermission(allOf = { |
| android.Manifest.permission.READ_BLOCKED_NUMBERS, |
| android.Manifest.permission.WRITE_BLOCKED_NUMBERS |
| }) |
| @FlaggedApi(Flags.FLAG_TELECOM_MAINLINE_BLOCKED_NUMBERS_MANAGER) |
| public void notifyEmergencyContact() { |
| verifyBlockedNumbersPermission(); |
| try { |
| Log.i(LOG_TAG, "notifyEmergencyContact; caller=%s", mContext.getOpPackageName()); |
| mContext.getContentResolver().call(AUTHORITY_URI, METHOD_NOTIFY_EMERGENCY_CONTACT, |
| null, null); |
| } catch (NullPointerException | IllegalArgumentException ex) { |
| // The content resolver can throw an NPE or IAE; we don't want to crash Telecom if |
| // either of these happen. |
| Log.w(null, "notifyEmergencyContact: provider not ready."); |
| } |
| } |
| |
| /** |
| * Notifies the provider to disable suppressing blocking. If emergency services were not |
| * contacted recently at all, calling this method is a no-op. |
| * @hide |
| */ |
| @SystemApi |
| @RequiresPermission(allOf = { |
| android.Manifest.permission.READ_BLOCKED_NUMBERS, |
| android.Manifest.permission.WRITE_BLOCKED_NUMBERS |
| }) |
| @FlaggedApi(Flags.FLAG_TELECOM_MAINLINE_BLOCKED_NUMBERS_MANAGER) |
| public void endBlockSuppression() { |
| verifyBlockedNumbersPermission(); |
| String caller = mContext.getOpPackageName(); |
| Log.i(LOG_TAG, "endBlockSuppression: caller=%s", caller); |
| mContext.getContentResolver().call(AUTHORITY_URI, METHOD_END_BLOCK_SUPPRESSION, null, null); |
| } |
| |
| /** |
| * Returns {@code true} if {@code phoneNumber} is blocked taking |
| * {@link #notifyEmergencyContact()} into consideration. If emergency services |
| * have not been contacted recently and enhanced call blocking not been enabled, this |
| * method is equivalent to {@link BlockedNumberContract#isBlocked(Context, String)}. |
| * |
| * @param phoneNumber the number to check. |
| * @param numberPresentation the presentation code associated with the call. |
| * @param isNumberInContacts indicates if the provided number exists as a contact. |
| * @return result code indicating if the number should be blocked, and if so why. |
| * Valid values are: {@link BlockedNumberContract#STATUS_NOT_BLOCKED}, |
| * {@link BlockedNumberContract#STATUS_BLOCKED_IN_LIST}, |
| * {@link BlockedNumberContract#STATUS_BLOCKED_NOT_IN_CONTACTS}, |
| * {@link BlockedNumberContract#STATUS_BLOCKED_PAYPHONE}, |
| * {@link BlockedNumberContract#STATUS_BLOCKED_RESTRICTED}, |
| * {@link BlockedNumberContract#STATUS_BLOCKED_UNKNOWN_NUMBER}. |
| * @hide |
| */ |
| @SystemApi |
| @RequiresPermission(allOf = { |
| android.Manifest.permission.READ_BLOCKED_NUMBERS, |
| android.Manifest.permission.WRITE_BLOCKED_NUMBERS |
| }) |
| @FlaggedApi(Flags.FLAG_TELECOM_MAINLINE_BLOCKED_NUMBERS_MANAGER) |
| public int shouldSystemBlockNumber(@NonNull String phoneNumber, |
| @TelecomManager.Presentation int numberPresentation, boolean isNumberInContacts) { |
| verifyBlockedNumbersPermission(); |
| try { |
| String caller = mContext.getOpPackageName(); |
| Bundle extras = new Bundle(); |
| extras.putInt(BlockedNumberContract.EXTRA_CALL_PRESENTATION, numberPresentation); |
| extras.putBoolean(BlockedNumberContract.EXTRA_CONTACT_EXIST, isNumberInContacts); |
| final Bundle res = mContext.getContentResolver().call(AUTHORITY_URI, |
| METHOD_SHOULD_SYSTEM_BLOCK_NUMBER, phoneNumber, extras); |
| int blockResult = res != null ? res.getInt(RES_BLOCK_STATUS, STATUS_NOT_BLOCKED) : |
| BlockedNumberContract.STATUS_NOT_BLOCKED; |
| Log.d(LOG_TAG, "shouldSystemBlockNumber: number=%s, caller=%s, result=%s", |
| Log.piiHandle(phoneNumber), caller, |
| BlockedNumberContract.SystemContract.blockStatusToString(blockResult)); |
| return blockResult; |
| } catch (NullPointerException | IllegalArgumentException ex) { |
| // The content resolver can throw an NPE or IAE; we don't want to crash Telecom if |
| // either of these happen. |
| Log.w(null, "shouldSystemBlockNumber: provider not ready."); |
| return BlockedNumberContract.STATUS_NOT_BLOCKED; |
| } |
| } |
| |
| /** |
| * @return The current status of block suppression. |
| * @hide |
| */ |
| @SystemApi |
| @RequiresPermission(allOf = { |
| android.Manifest.permission.READ_BLOCKED_NUMBERS, |
| android.Manifest.permission.WRITE_BLOCKED_NUMBERS |
| }) |
| @FlaggedApi(Flags.FLAG_TELECOM_MAINLINE_BLOCKED_NUMBERS_MANAGER) |
| public @NonNull BlockSuppressionStatus getBlockSuppressionStatus() { |
| verifyBlockedNumbersPermission(); |
| final Bundle res = mContext.getContentResolver().call( |
| AUTHORITY_URI, METHOD_GET_BLOCK_SUPPRESSION_STATUS, null, null); |
| BlockSuppressionStatus blockSuppressionStatus = new BlockSuppressionStatus( |
| res.getBoolean(RES_IS_BLOCKING_SUPPRESSED, false), |
| res.getLong(RES_BLOCKING_SUPPRESSED_UNTIL_TIMESTAMP, 0)); |
| Log.d(LOG_TAG, "getBlockSuppressionStatus: caller=%s, status=%s", |
| mContext.getOpPackageName(), blockSuppressionStatus); |
| return blockSuppressionStatus; |
| } |
| |
| /** |
| * Check whether should show the emergency call notification. |
| * |
| * @return {@code true} if should show emergency call notification. {@code false} otherwise. |
| * @hide |
| */ |
| @SystemApi |
| @RequiresPermission(allOf = { |
| android.Manifest.permission.READ_BLOCKED_NUMBERS, |
| android.Manifest.permission.WRITE_BLOCKED_NUMBERS |
| }) |
| @FlaggedApi(Flags.FLAG_TELECOM_MAINLINE_BLOCKED_NUMBERS_MANAGER) |
| public boolean shouldShowEmergencyCallNotification() { |
| verifyBlockedNumbersPermission(); |
| try { |
| final Bundle res = mContext.getContentResolver().call(AUTHORITY_URI, |
| METHOD_SHOULD_SHOW_EMERGENCY_CALL_NOTIFICATION, null, null); |
| return res != null && res.getBoolean(RES_SHOW_EMERGENCY_CALL_NOTIFICATION, false); |
| } catch (NullPointerException | IllegalArgumentException ex) { |
| // The content resolver can throw an NPE or IAE; we don't want to crash Telecom if |
| // either of these happen. |
| Log.w(null, "shouldShowEmergencyCallNotification: provider not ready."); |
| return false; |
| } |
| } |
| |
| /** |
| * Check whether the enhanced block setting is enabled. |
| * |
| * @param key the key of the setting to check, can be |
| * {@link BlockedNumberContract.SystemContract#ENHANCED_SETTING_KEY_BLOCK_UNREGISTERED} |
| * {@link BlockedNumberContract.SystemContract#ENHANCED_SETTING_KEY_BLOCK_PRIVATE} |
| * {@link BlockedNumberContract.SystemContract#ENHANCED_SETTING_KEY_BLOCK_PAYPHONE} |
| * {@link BlockedNumberContract.SystemContract#ENHANCED_SETTING_KEY_BLOCK_UNKNOWN} |
| * {@link BlockedNumberContract.SystemContract#ENHANCED_SETTING_KEY_BLOCK_UNAVAILABLE} |
| * {@link BlockedNumberContract.SystemContract |
| * #ENHANCED_SETTING_KEY_SHOW_EMERGENCY_CALL_NOTIFICATION} |
| * @return {@code true} if the setting is enabled. {@code false} otherwise. |
| * @hide |
| */ |
| @SystemApi |
| @RequiresPermission(allOf = { |
| android.Manifest.permission.READ_BLOCKED_NUMBERS, |
| android.Manifest.permission.WRITE_BLOCKED_NUMBERS |
| }) |
| @FlaggedApi(Flags.FLAG_TELECOM_MAINLINE_BLOCKED_NUMBERS_MANAGER) |
| public boolean getBlockedNumberSetting(@NonNull String key) { |
| verifyBlockedNumbersPermission(); |
| Bundle extras = new Bundle(); |
| extras.putString(EXTRA_ENHANCED_SETTING_KEY, key); |
| try { |
| final Bundle res = mContext.getContentResolver().call(AUTHORITY_URI, |
| METHOD_GET_ENHANCED_BLOCK_SETTING, null, extras); |
| return res != null && res.getBoolean(RES_ENHANCED_SETTING_IS_ENABLED, false); |
| } catch (NullPointerException | IllegalArgumentException ex) { |
| // The content resolver can throw an NPE or IAE; we don't want to crash Telecom if |
| // either of these happen. |
| Log.w(null, "getEnhancedBlockSetting: provider not ready."); |
| return false; |
| } |
| } |
| |
| /** |
| * Set the enhanced block setting enabled status. |
| * |
| * @param key the key of the setting to set, can be |
| * {@link BlockedNumberContract.SystemContract#ENHANCED_SETTING_KEY_BLOCK_UNREGISTERED} |
| * {@link BlockedNumberContract.SystemContract#ENHANCED_SETTING_KEY_BLOCK_PRIVATE} |
| * {@link BlockedNumberContract.SystemContract#ENHANCED_SETTING_KEY_BLOCK_PAYPHONE} |
| * {@link BlockedNumberContract.SystemContract#ENHANCED_SETTING_KEY_BLOCK_UNKNOWN} |
| * {@link BlockedNumberContract.SystemContract#ENHANCED_SETTING_KEY_BLOCK_UNAVAILABLE} |
| * {@link BlockedNumberContract.SystemContract |
| * #ENHANCED_SETTING_KEY_SHOW_EMERGENCY_CALL_NOTIFICATION} |
| * @param value the enabled statue of the setting to set. |
| * @hide |
| */ |
| @SystemApi |
| @RequiresPermission(allOf = { |
| android.Manifest.permission.READ_BLOCKED_NUMBERS, |
| android.Manifest.permission.WRITE_BLOCKED_NUMBERS |
| }) |
| @FlaggedApi(Flags.FLAG_TELECOM_MAINLINE_BLOCKED_NUMBERS_MANAGER) |
| public void setBlockedNumberSetting(@NonNull String key, boolean value) { |
| verifyBlockedNumbersPermission(); |
| Bundle extras = new Bundle(); |
| extras.putString(EXTRA_ENHANCED_SETTING_KEY, key); |
| extras.putBoolean(EXTRA_ENHANCED_SETTING_VALUE, value); |
| mContext.getContentResolver().call(AUTHORITY_URI, METHOD_SET_ENHANCED_BLOCK_SETTING, |
| null, extras); |
| } |
| |
| /** |
| * Represents the current status of |
| * {@link #shouldSystemBlockNumber(String, int, boolean)}. If emergency services |
| * have been contacted recently, {@link #mIsSuppressed} is {@code true}, and blocking |
| * is disabled until the timestamp {@link #mUntilTimestampMillis}. |
| * @hide |
| */ |
| @SystemApi |
| @FlaggedApi(Flags.FLAG_TELECOM_MAINLINE_BLOCKED_NUMBERS_MANAGER) |
| public static final class BlockSuppressionStatus { |
| /** |
| * Indicates if block suppression is enabled. |
| */ |
| private boolean mIsSuppressed; |
| |
| /** |
| * Timestamp in milliseconds from epoch. |
| */ |
| private long mUntilTimestampMillis; |
| |
| public BlockSuppressionStatus(boolean isSuppressed, long untilTimestampMillis) { |
| this.mIsSuppressed = isSuppressed; |
| this.mUntilTimestampMillis = untilTimestampMillis; |
| } |
| |
| @Override |
| public String toString() { |
| return "[BlockSuppressionStatus; isSuppressed=" + mIsSuppressed + ", until=" |
| + mUntilTimestampMillis + "]"; |
| } |
| |
| /** |
| * @return mIsSuppressed Indicates whether or not block suppression is enabled. |
| */ |
| public boolean getIsSuppressed() { |
| return mIsSuppressed; |
| } |
| |
| /** |
| * @return mUntilTimestampMillis The timestamp until which block suppression would be |
| * enabled for |
| */ |
| public long getUntilTimestampMillis() { |
| return mUntilTimestampMillis; |
| } |
| } |
| |
| /** |
| * Verifies that the caller holds both the |
| * {@link android.Manifest.permission#READ_BLOCKED_NUMBERS} permission and the |
| * {@link android.Manifest.permission#WRITE_BLOCKED_NUMBERS} permission. |
| * |
| * @throws SecurityException if the caller is missing the necessary permissions |
| */ |
| private void verifyBlockedNumbersPermission() { |
| mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_BLOCKED_NUMBERS, |
| "Caller does not have the android.permission.READ_BLOCKED_NUMBERS permission"); |
| mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_BLOCKED_NUMBERS, |
| "Caller does not have the android.permission.WRITE_BLOCKED_NUMBERS permission"); |
| } |
| } |