aboutsummaryrefslogtreecommitdiffstats
path: root/src/java/com/android/internal/telephony/util/BlacklistUtils.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/java/com/android/internal/telephony/util/BlacklistUtils.java')
-rw-r--r--src/java/com/android/internal/telephony/util/BlacklistUtils.java262
1 files changed, 262 insertions, 0 deletions
diff --git a/src/java/com/android/internal/telephony/util/BlacklistUtils.java b/src/java/com/android/internal/telephony/util/BlacklistUtils.java
new file mode 100644
index 000000000..f264f8d50
--- /dev/null
+++ b/src/java/com/android/internal/telephony/util/BlacklistUtils.java
@@ -0,0 +1,262 @@
+/*
+ * Copyright (C) 2013 The CyanogenMod 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 com.android.internal.telephony.util;
+
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.provider.Telephony.Blacklist;
+import android.telephony.PhoneNumberUtils;
+import android.telephony.TelephonyManager;
+import android.text.TextUtils;
+import android.util.Log;
+import android.util.Pair;
+
+import java.util.Locale;
+
+import com.android.internal.telephony.CallerInfo;
+
+/**
+ * Blacklist Utility Class
+ * @hide
+ */
+public class BlacklistUtils {
+ private static final String TAG = "BlacklistUtils";
+ private static final boolean DEBUG = false;
+
+ // Blacklist matching type
+ public final static int MATCH_NONE = 0;
+ public final static int MATCH_PRIVATE = 1;
+ public final static int MATCH_UNKNOWN = 2;
+ public final static int MATCH_LIST = 3;
+ public final static int MATCH_REGEX = 4;
+
+ public final static int BLOCK_CALLS =
+ Settings.System.BLACKLIST_BLOCK << Settings.System.BLACKLIST_PHONE_SHIFT;
+ public final static int BLOCK_MESSAGES =
+ Settings.System.BLACKLIST_BLOCK << Settings.System.BLACKLIST_MESSAGE_SHIFT;
+
+ public static boolean addOrUpdate(Context context, String number, int flags, int valid) {
+ ContentValues cv = new ContentValues();
+
+ if ((valid & BLOCK_CALLS) != 0) {
+ cv.put(Blacklist.PHONE_MODE, (flags & BLOCK_CALLS) != 0 ? 1 : 0);
+ }
+ if ((valid & BLOCK_MESSAGES) != 0) {
+ cv.put(Blacklist.MESSAGE_MODE, (flags & BLOCK_MESSAGES) != 0 ? 1 : 0);
+ }
+
+ Uri uri = Uri.withAppendedPath(Blacklist.CONTENT_FILTER_BYNUMBER_URI, number);
+ int count = context.getContentResolver().update(uri, cv, null, null);
+
+ return count > 0;
+ }
+
+ /**
+ * Check if the number is in the blacklist
+ * @param number: Number to check
+ * @return one of: MATCH_NONE, MATCH_PRIVATE, MATCH_UNKNOWN, MATCH_LIST or MATCH_REGEX
+ */
+ public static int isListed(Context context, String number, int mode) {
+ if (!isBlacklistEnabled(context)) {
+ return MATCH_NONE;
+ }
+
+ if (DEBUG) {
+ Log.d(TAG, "Checking number " + number + " against the Blacklist for mode " + mode);
+ }
+
+ final String type;
+
+ if (mode == BLOCK_CALLS) {
+ if (DEBUG) Log.d(TAG, "Checking if an incoming call should be blocked");
+ type = Blacklist.PHONE_MODE;
+ } else if (mode == BLOCK_MESSAGES) {
+ if (DEBUG) Log.d(TAG, "Checking if an incoming message should be blocked");
+ type = Blacklist.MESSAGE_MODE;
+ } else {
+ Log.e(TAG, "Invalid mode " + mode);
+ return MATCH_NONE;
+ }
+
+ if (isBlacklistUnknownNumberEnabled(context, mode)) {
+ CallerInfo ci = CallerInfo.getCallerInfo(context, number);
+ if (ci == null || !ci.contactExists) {
+ if (DEBUG) Log.d(TAG, "Blacklist matched due to unknown number");
+ return MATCH_UNKNOWN;
+ }
+ }
+
+ // Private and unknown number matching
+ if (TextUtils.isEmpty(number)) {
+ if (isBlacklistPrivateNumberEnabled(context, mode)) {
+ if (DEBUG) Log.d(TAG, "Blacklist matched due to private number");
+ return MATCH_PRIVATE;
+ }
+ return MATCH_NONE;
+ }
+
+ Uri.Builder builder = Blacklist.CONTENT_FILTER_BYNUMBER_URI.buildUpon();
+ builder.appendPath(number);
+ if (isBlacklistRegexEnabled(context)) {
+ builder.appendQueryParameter(Blacklist.REGEX_KEY, "1");
+ }
+
+ int result = MATCH_NONE;
+ Cursor c = context.getContentResolver().query(builder.build(),
+ new String[]{Blacklist.IS_REGEX, type}, null, null, null);
+
+ if (c != null) {
+ if (DEBUG) Log.d(TAG, "Blacklist query successful, " + c.getCount() + " matches");
+ int regexColumnIndex = c.getColumnIndexOrThrow(Blacklist.IS_REGEX);
+ int modeColumnIndex = c.getColumnIndexOrThrow(type);
+ boolean whitelisted = false;
+
+ c.moveToPosition(-1);
+ while (c.moveToNext()) {
+ boolean isRegex = c.getInt(regexColumnIndex) != 0;
+ boolean blocked = c.getInt(modeColumnIndex) != 0;
+
+ if (!isRegex) {
+ whitelisted = !blocked;
+ result = MATCH_LIST;
+ if (blocked) {
+ break;
+ }
+ } else if (blocked) {
+ result = MATCH_REGEX;
+ }
+ }
+ if (whitelisted) {
+ result = MATCH_NONE;
+ }
+ c.close();
+ }
+
+ if (DEBUG) Log.d(TAG, "Blacklist check result for number " + number + " is " + result);
+ return result;
+ }
+
+ public static boolean isBlacklistEnabled(Context context) {
+ return Settings.System.getIntForUser(context.getContentResolver(),
+ Settings.System.PHONE_BLACKLIST_ENABLED, 1,
+ UserHandle.USER_CURRENT_OR_SELF) != 0;
+ }
+
+ public static boolean isBlacklistNotifyEnabled(Context context) {
+ return Settings.System.getIntForUser(context.getContentResolver(),
+ Settings.System.PHONE_BLACKLIST_NOTIFY_ENABLED, 1,
+ UserHandle.USER_CURRENT_OR_SELF) != 0;
+ }
+
+ public static boolean isBlacklistPrivateNumberEnabled(Context context, int mode) {
+ return (Settings.System.getIntForUser(context.getContentResolver(),
+ Settings.System.PHONE_BLACKLIST_PRIVATE_NUMBER_MODE, 0,
+ UserHandle.USER_CURRENT_OR_SELF) & mode) != 0;
+ }
+
+ public static boolean isBlacklistUnknownNumberEnabled(Context context, int mode) {
+ return (Settings.System.getIntForUser(context.getContentResolver(),
+ Settings.System.PHONE_BLACKLIST_UNKNOWN_NUMBER_MODE, 0,
+ UserHandle.USER_CURRENT_OR_SELF) & mode) != 0;
+ }
+
+ public static boolean isBlacklistRegexEnabled(Context context) {
+ return Settings.System.getIntForUser(context.getContentResolver(),
+ Settings.System.PHONE_BLACKLIST_REGEX_ENABLED, 0,
+ UserHandle.USER_CURRENT_OR_SELF) != 0;
+ }
+
+ public static Pair<String, Boolean> isValidBlacklistInput(Context context, String number) {
+ final Pair<String, Boolean> normalizeResult = BlacklistUtils.normalizeNumber(
+ context, number);
+ final String normalizedNumber = normalizeResult.first;
+ boolean isRegex = normalizedNumber.indexOf('%') >= 0 ||
+ normalizedNumber.indexOf('_') >= 0;
+ // For non-regex numbers, apply additional validity checking if
+ // they didn't pass e164 normalization
+ if (!isRegex && !normalizeResult.second && !BlacklistUtils.isValidPhoneNumber(number)) {
+ // number was invalid
+ return new Pair<String, Boolean>(normalizedNumber, false);
+ }
+ return new Pair<String, Boolean>(normalizedNumber, true);
+ }
+
+ /**
+ * Normalizes the passed in number and tries to format it according to E164.
+ * Returns a pair of
+ * - normalized number
+ * - boolean indicating whether the number is a E164 number or not
+ */
+ public static Pair<String, Boolean> normalizeNumber(Context context, String number) {
+ int len = number.length();
+ StringBuilder ret = new StringBuilder(len);
+
+ for (int i = 0; i < len; i++) {
+ char c = number.charAt(i);
+ // Character.digit() supports ASCII and Unicode digits (fullwidth, Arabic-Indic, etc.)
+ int digit = Character.digit(c, 10);
+ if (digit != -1) {
+ ret.append(digit);
+ } else if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) {
+ String actualNumber = PhoneNumberUtils.convertKeypadLettersToDigits(number);
+ return normalizeNumber(context, actualNumber);
+ } else if (i == 0 && c == '+') {
+ ret.append(c);
+ } else if (c == '*') {
+ // replace regex match-multiple character by SQL equivalent
+ ret.append('%');
+ } else if (c == '.') {
+ // replace regex-match-single character by SQL equivalent
+ ret.append('_');
+ }
+ }
+
+ String normalizedNumber = ret.toString();
+ String e164Number = toE164Number(context, normalizedNumber);
+ return Pair.create(e164Number != null ? e164Number : normalizedNumber, e164Number != null);
+ }
+
+ public static String toE164Number(Context context, String src) {
+ // Try to retrieve the current ISO Country code
+ TelephonyManager tm = (TelephonyManager)
+ context.getSystemService(Context.TELEPHONY_SERVICE);
+ String countryCode = tm.getSimCountryIso();
+ Locale numberLocale = TextUtils.isEmpty(countryCode)
+ ? context.getResources().getConfiguration().locale
+ : new Locale("", countryCode);
+
+ return PhoneNumberUtils.formatNumberToE164(src, numberLocale.getCountry());
+ }
+
+ public static boolean isValidPhoneNumber(String address) {
+ for (int i = 0, count = address.length(); i < count; i++) {
+ if (!PhoneNumberUtils.isISODigit(address.charAt(i))) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public static boolean isInputRegex(String input) {
+ return input.indexOf('%') >= 0 ||
+ input.indexOf('_') >= 0;
+ }
+}