androidkotlintelephonymanager

TelephonyManager returning NULL and ACTUAL NUMBER


I'm working on an Android app that detects incoming calls using a BroadcastReceiver. When an incoming call is detected, I want to perform a task, but I'm facing an issue with handling null phone numbers.

Here's the current flow of my app:

  1. I have a BroadcastReceiver that listens for ACTION_PHONE_STATE_CHANGED intent action to detect incoming calls.
  2. In the onReceive method of the receiver, I retrieve the phone number from the intent.
  3. However, sometimes the phone number is null, and this is causing problems because I end up performing task for null numbers.

However, this approach leads to two same tasks being done for the same incoming call, which is undesirable.

FYI, I need to call the method while the Telephony state == TelephonyManager.EXTRA_STATE_RINGING, not after the call is ended.

My goal is to call logCall only once per incoming call, regardless of whether I receive a null value initially.

Here's what I've tried:

  1. I've modified my code to store both the null value and the actual number in an array.
  2. I only call logCall if the array contains a valid number or Unknown or Private.
  3. Permissions are working as I can still receive the number <receiving null prior to that>

CallDetectionService.kt

private val detectedNumbers = mutableListOf<String?>()

private val broadcastReceiver = object : BroadcastReceiver() {

    @RequiresApi(Build.VERSION_CODES.TIRAMISU)
    override fun onReceive(context: Context, intent: Intent) {
        if (intent.action == TelephonyManager.ACTION_PHONE_STATE_CHANGED) {
            val state = intent.getStringExtra(TelephonyManager.EXTRA_STATE)
            val number = intent.getStringExtra(TelephonyManager.EXTRA_INCOMING_NUMBER)
            if (state == TelephonyManager.EXTRA_STATE_RINGING) {
                Log.d(TAG, "Incoming call from $number at ${LocalDate.now()}")
                Log.d(TAG, "Sending broadcast to the activity from $CLASS")
                detectedNumbers.add(number)
                if (detectedNumbers.any { it != null && it.isNotBlank() }) {
                    logCall(number)
                }
            }
        }
    }
}


private fun logCall(number: String?) {
    // Update detected numbers array
    detectedNumbers.add(number)

    // Check if any actual number or specific values are present
    if (detectedNumbers.any { it != null && (it != "Unknown" && it != "Private") }) {
        // Call logCall only once with the first non-null, non-specific value
        val actualNumber = detectedNumbers.firstOrNull { it != null && (it != "Unknown" && it != "Private") }
        logCall(actualNumber ?: "Unknown") // Use "Unknown" as default if no valid number found

        // Clear the detected numbers array for the next call
        detectedNumbers.clear()
    }
}

I believe the issue might be related to the timing of receiving the broadcasts, but I'm not sure how to address it effectively


Solution

  • Your approach doesn't effectively handle the asynchronous nature of receiving call state and number information, leading to multiple log entries for the same call or nulls.

    you can try this to keep track of last number

      private var lastNumber: String? = null
    
      private val broadcastReceiver = object : BroadcastReceiver() {
         override fun onReceive(context: Context, intent: Intent) {
           if (TelephonyManager.ACTION_PHONE_STATE_CHANGED == intent.action) {
               val state = intent.getStringExtra(TelephonyManager.EXTRA_STATE)
               if (TelephonyManager.EXTRA_STATE_RINGING == state) {
                   val number = intent.getStringExtra(TelephonyManager.EXTRA_INCOMING_NUMBER)
                if (number != null && number != lastNumber) {
                    logCall(number)
                    lastNumber = number
                  }
               }
           }
        }
    }
    
    private fun logCall(number: String?) {
           // your logcall code 
    }