Skip to content

Regex compiled per call in maskNumber() causes ANR on Android #97

@gargalino-mobile

Description

@gargalino-mobile

Problem

FlutterLibphonenumberPlugin.kt in flutter_libphonenumber_android (v2.0.0) creates a new Regex object on every call to maskNumber():

// line 108
private fun maskNumber(phoneNumber: String, phoneCode: String) =
    phoneNumber.replace(Regex("""[\d]"""), "0")

maskNumber() is called 4 times per region inside getAllSupportedRegions() (twice for mobile example, twice for fixed line). With ~250 supported regions, that's 1000+ Regex object instantiations in a tight loop — each one compiling a pattern, building an NFA/DFA, and allocating memory.

On low-end Android devices, this contributes to ANR (Application Not Responding) reports. We're seeing ~875 ANR reports attributed to RegExp compilation on the main thread in our Play Console vitals.

Additional issues in getAllSupportedRegions()

  1. PhoneNumberUtil.getInstance() called ~6 times per region instead of once before the loop. That's ~1500 synchronized lock acquisitions for no reason.

  2. Raw Thread(Runnable { ... }).start() (line 76) — unmanaged thread with no cancellation support. If the Dart side awaits the method channel result, the main thread can stall.

Suggested fix

1. Cache the Regex as a companion object constant

companion object {
    private val DIGIT_REGEX = Regex("""\d""")
}

private fun maskNumber(phoneNumber: String, phoneCode: String) =
    phoneNumber.replace(DIGIT_REGEX, "0")

2. Hoist PhoneNumberUtil.getInstance() outside the loop

val phoneUtil = PhoneNumberUtil.getInstance()
for (region in phoneUtil.supportedRegions) {
    val phoneCode = phoneUtil.getCountryCodeForRegion(region).toString()
    // ...
}

3. Consider using a coroutine or executor instead of raw Thread

The raw Thread doesn't integrate with Android lifecycle management and can't be cancelled.

Environment

  • flutter_libphonenumber: 2.7.0
  • flutter_libphonenumber_android: 2.0.0
  • Android (Play Console vitals — ANR reports)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions