From b8afb569423106ad4cca2d24a4c986e3cd3af462 Mon Sep 17 00:00:00 2001 From: Andrew Gunnerson Date: Sat, 2 Mar 2024 23:59:31 -0500 Subject: [PATCH] PhoneNumber: Do not allow empty strings Otherwise, performing a contact lookup will fail with an obscure error due to a missing path component in the lookup URI. Fixes: #455 Signed-off-by: Andrew Gunnerson --- app/src/main/java/com/chiller3/bcr/Contact.kt | 9 ++++++--- app/src/main/java/com/chiller3/bcr/RecorderThread.kt | 7 ++++--- .../com/chiller3/bcr/extension/CallDetailsExtensions.kt | 8 +++++++- .../com/chiller3/bcr/output/CallMetadataCollector.kt | 9 +++++++-- app/src/main/java/com/chiller3/bcr/output/PhoneNumber.kt | 4 ++++ app/src/main/java/com/chiller3/bcr/rule/RecordRule.kt | 3 ++- 6 files changed, 30 insertions(+), 10 deletions(-) diff --git a/app/src/main/java/com/chiller3/bcr/Contact.kt b/app/src/main/java/com/chiller3/bcr/Contact.kt index 96f400df9..0f310ec57 100644 --- a/app/src/main/java/com/chiller3/bcr/Contact.kt +++ b/app/src/main/java/com/chiller3/bcr/Contact.kt @@ -5,6 +5,7 @@ import android.content.Context import android.net.Uri import android.provider.ContactsContract import androidx.annotation.RequiresPermission +import com.chiller3.bcr.output.PhoneNumber private val PROJECTION = arrayOf( ContactsContract.PhoneLookup.LOOKUP_KEY, @@ -17,12 +18,14 @@ data class ContactInfo( ) @RequiresPermission(Manifest.permission.READ_CONTACTS) -fun findContactsByPhoneNumber(context: Context, number: String): Iterator { +fun findContactsByPhoneNumber(context: Context, number: PhoneNumber): Iterator { + val rawNumber = number.toString() + // Same heuristic as InCallUI's PhoneNumberHelper.isUriNumber() - val numberIsSip = number.contains("@") || number.contains("%40") + val numberIsSip = rawNumber.contains("@") || rawNumber.contains("%40") val uri = ContactsContract.PhoneLookup.ENTERPRISE_CONTENT_FILTER_URI.buildUpon() - .appendPath(number) + .appendPath(rawNumber) .appendQueryParameter( ContactsContract.PhoneLookup.QUERY_PARAMETER_SIP_ADDRESS, numberIsSip.toString()) diff --git a/app/src/main/java/com/chiller3/bcr/RecorderThread.kt b/app/src/main/java/com/chiller3/bcr/RecorderThread.kt index e0850a23f..ade56033a 100644 --- a/app/src/main/java/com/chiller3/bcr/RecorderThread.kt +++ b/app/src/main/java/com/chiller3/bcr/RecorderThread.kt @@ -29,6 +29,7 @@ import com.chiller3.bcr.output.OutputDirUtils import com.chiller3.bcr.output.OutputFile import com.chiller3.bcr.output.OutputFilenameGenerator import com.chiller3.bcr.output.OutputPath +import com.chiller3.bcr.output.PhoneNumber import com.chiller3.bcr.output.Retention import com.chiller3.bcr.rule.RecordRule import org.json.JSONObject @@ -153,14 +154,14 @@ class RecorderThread( return } - val numbers = hashSetOf() + val numbers = hashSetOf() if (parentCall.details.hasProperty(Call.Details.PROPERTY_CONFERENCE)) { for (childCall in parentCall.children) { - childCall.details?.phoneNumber?.let { numbers.add(it.toString()) } + childCall.details?.phoneNumber?.let { numbers.add(it) } } } else { - parentCall.details?.phoneNumber?.let { numbers.add(it.toString()) } + parentCall.details?.phoneNumber?.let { numbers.add(it) } } Log.i(tag, "Evaluating record rules for ${numbers.size} phone number(s)") diff --git a/app/src/main/java/com/chiller3/bcr/extension/CallDetailsExtensions.kt b/app/src/main/java/com/chiller3/bcr/extension/CallDetailsExtensions.kt index fdb38fbb9..246d4581e 100644 --- a/app/src/main/java/com/chiller3/bcr/extension/CallDetailsExtensions.kt +++ b/app/src/main/java/com/chiller3/bcr/extension/CallDetailsExtensions.kt @@ -4,4 +4,10 @@ import android.telecom.Call import com.chiller3.bcr.output.PhoneNumber val Call.Details.phoneNumber: PhoneNumber? - get() = handle?.phoneNumber?.let { PhoneNumber(it) } + get() = handle?.phoneNumber?.let { + try { + PhoneNumber(it) + } catch (e: IllegalArgumentException) { + null + } + } diff --git a/app/src/main/java/com/chiller3/bcr/output/CallMetadataCollector.kt b/app/src/main/java/com/chiller3/bcr/output/CallMetadataCollector.kt index b41e43a38..e4916533c 100644 --- a/app/src/main/java/com/chiller3/bcr/output/CallMetadataCollector.kt +++ b/app/src/main/java/com/chiller3/bcr/output/CallMetadataCollector.kt @@ -55,7 +55,7 @@ class CallMetadataCollector( Log.d(TAG, "Performing manual contact lookup") - for (contact in findContactsByPhoneNumber(context, number.toString())) { + for (contact in findContactsByPhoneNumber(context, number)) { Log.d(TAG, "Found contact display name via manual lookup") return contact.displayName } @@ -144,7 +144,12 @@ class CallMetadataCollector( if (index != -1) { number = cursor.getStringOrNull(index)?.let { Log.d(TAG, "${prefix}Found call log phone number") - PhoneNumber(it) + try { + PhoneNumber(it) + } catch (e: IllegalArgumentException) { + Log.w(TAG, "${prefix}Invalid call log phone number", e) + null + } } } else { Log.d(TAG, "${prefix}Call log entry has no phone number") diff --git a/app/src/main/java/com/chiller3/bcr/output/PhoneNumber.kt b/app/src/main/java/com/chiller3/bcr/output/PhoneNumber.kt index 40d9972a3..8548d30e0 100644 --- a/app/src/main/java/com/chiller3/bcr/output/PhoneNumber.kt +++ b/app/src/main/java/com/chiller3/bcr/output/PhoneNumber.kt @@ -7,6 +7,10 @@ import android.util.Log import java.util.Locale data class PhoneNumber(private val number: String) { + init { + require(number.isNotEmpty()) { "Number cannot be empty" } + } + fun format(context: Context, format: Format) = when (format) { Format.DIGITS_ONLY -> number.filter { Character.digit(it, 10) != -1 } Format.COUNTRY_SPECIFIC -> { diff --git a/app/src/main/java/com/chiller3/bcr/rule/RecordRule.kt b/app/src/main/java/com/chiller3/bcr/rule/RecordRule.kt index e5ee448b4..e9332e47b 100644 --- a/app/src/main/java/com/chiller3/bcr/rule/RecordRule.kt +++ b/app/src/main/java/com/chiller3/bcr/rule/RecordRule.kt @@ -6,6 +6,7 @@ import android.content.SharedPreferences import android.content.pm.PackageManager import android.util.Log import com.chiller3.bcr.findContactsByPhoneNumber +import com.chiller3.bcr.output.PhoneNumber sealed class RecordRule { abstract val record: Boolean @@ -105,7 +106,7 @@ sealed class RecordRule { * @throws IllegalArgumentException if [rules] does not contain [AllCalls] */ fun evaluate(context: Context, rules: List, - numbers: Collection): Boolean { + numbers: Collection): Boolean { val contactsAllowed = context.checkSelfPermission(Manifest.permission.READ_CONTACTS) == PackageManager.PERMISSION_GRANTED val contactLookupKeys = if (contactsAllowed) {