Search code examples
javaandroidbroadcastreceivertelephonymanager

TelephonyManager call state still set at CALL_STATE_RINGING after call has been ended


I'm currently building a spam-call blocking app as a fun project to get better with Android development, and am running into this odd bug.

I have a Broadcast Receiver setup to listen for changes in the call state, and logic to check if the call is incoming or not, and basically end the call if the number is not in the user's contact list or a "whitelist" of approved numbers held in a database. However, even after the call has been ended, the receiver continues to be called multiple times (sometimes 8 or more times) and will often be called with Call State set as ringing (again, despite the fact that the call has been ended).

Basically, I'm trying to keep track of how many times each unknown number calls the phone, but for each single call it records it as anywhere from 2 - 6 calls.

Here is the onReceive function I have set up:

public void onReceive(final Context con, Intent intent) {
    System.out.println(intent);

    final TelephonyManager telephony = (TelephonyManager) con.getSystemService(Context.TELEPHONY_SERVICE);
    telephony.listen(new PhoneStateListener() {
        @RequiresApi(api = Build.VERSION_CODES.P)
        @Override
        public void onCallStateChanged(int state, String incomingNumber) {
            super.onCallStateChanged(state, incomingNumber);
            System.out.print(state);
            //Toast.makeText(con, incomingNumber, Toast.LENGTH_LONG).show();
            if (TelephonyManager.CALL_STATE_RINGING == state) {
                checkIfNumberInContacts(con, incomingNumber, telephony, state);
            }
        }
    }, PhoneStateListener.LISTEN_CALL_STATE);
}

and here is the custom function that checks if the incoming number is in contacts/whitelisted and ends the call:

@RequiresApi(api = Build.VERSION_CODES.P)
   public void checkIfNumberInContacts(Context con, String incomingNumber, TelephonyManager telephony, 
   int state) {
    db = Room.databaseBuilder(con, SpamBlockerDB.class, 
    "spamblocker.db").createFromAsset("databases/spamblocker.db").allowMainThreadQueries().build();

    FilteredCalls call = new FilteredCalls();
    call.calltime = Calendar.getInstance().getTime().toString();
    call.deleted = 0;
    call.number = incomingNumber;
    call.whitelisted = 0;
    call.callcount = 1;

    List<FilteredCalls> allCalls = db.callsDao().getAll();
    boolean callerExistsInDB = false;
    for (FilteredCalls item : allCalls) {

        if (incomingNumber.equals(item.number)) {
            callerExistsInDB = true;
            String updatedTime = Calendar.getInstance().getTime().toString();
            db.callsDao().updateCallTime(updatedTime, item.recID);
        }
        if (TelephonyManager.CALL_STATE_RINGING == state) {
            int currentCount = db.callsDao().getCallCount(item.recID);
            db.callsDao().updateCallCount(currentCount + 1, item.recID);
        }
    }

    ContentResolver contentResolver = con.getContentResolver();
    Uri uri = Uri.withAppendedPath(ContactsContract.PhoneLookup.CONTENT_FILTER_URI, Uri.encode(incomingNumber));

    String contact = "";

    Cursor c = contentResolver.query(uri, new String[]{ContactsContract.PhoneLookup.HAS_PHONE_NUMBER}, null, null);
    if (c != null) {
        if (c.moveToFirst()) {
            do {
                contact = c.getString(c.getColumnIndex("NUMBER"));
            } while (c.moveToNext());
        }
    }

    if (contact == "" && !checkIfNumberIsWhitelisted(con, incomingNumber)) {
        TelecomManager telecomManager = (TelecomManager) con.getSystemService(Context.TELECOM_SERVICE);
        telecomManager.endCall();

        if (!callerExistsInDB) {
            db.callsDao().insert(call);
        }
    }
}

Any ideas why the BroadCast receiver is called more than twice (once for incoming, once for hanging up), and why when it's called the subsequent times it still thinks the phone is ringing?

Appreciate the help in advance.


Solution

  • There are two ways to get callbacks when the phone state changes: PhoneStateListener and a BroadcastReceiver that listens to android.intent.action.PHONE_STATE.

    It looks like you're mixing the two methods together, which means that whenever your BroadcastReceiver is called, you're registering another new PhoneStateListener, so you keep adding more and more listeners that do the same thing.

    I would recommend not using PhoneStateListener at all, and go with the BroadcastReceiver approach only, which can be setup via AndroidManifest which allows it to be called even when the app is asleep.

    See this tutorial: https://medium.com/@saishaddai/how-to-know-when-a-device-is-ringing-in-android-57e516d0ab42