androidpermissionstelephonymanager

Wrong permission dialog when registering CallStateListener


My app wants to stop audio processing when the phone receives a call.

When the app starts, Android thinks, that the app wants to make and manage phone calls, which is wrong.Wrong Request

// in constructor
requestPermissionLauncher = activity.registerForActivityResult(
    new ActivityResultContracts.RequestPermission(), 
    this::registerTelephonyCallback);

// during init
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
    if(ActivityCompat.checkSelfPermission(activity.getContext(), Manifest.permission.READ_PHONE_STATE) == PackageManager.PERMISSION_GRANTED){
        registerTelephonyCallback(true);
    } else {
        requestPermissionLauncher.launch(Manifest.permission.READ_PHONE_STATE);
    }
} else {
    //noinspection deprecation
    // use deprecated TelephonyManager.listen()
}

Here is the method registerTelephonyCallback():

private void registerTelephonyCallback(boolean phoneStateCanBeRead){
    if (!phoneStateCanBeRead){
        return;
    }
    TelephonyManager telephonyManager = (TelephonyManager) activity.getContext().getSystemService(Context.TELEPHONY_SERVICE);
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
        Executor mainExecutor = ContextCompat.getMainExecutor((Activity)activity);
        telephonyManager.registerTelephonyCallback(INCLUDE_LOCATION_DATA_NONE, mainExecutor, new PhoneCallback());
    } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
        Executor mainExecutor = ContextCompat.getMainExecutor((Activity)activity);
        telephonyManager.registerTelephonyCallback(mainExecutor, new PhoneCallback());
    }
}

This is my TelephonyCallback implementation:

@RequiresApi(api = Build.VERSION_CODES.S)
private class PhoneCallback extends TelephonyCallback implements TelephonyCallback.CallStateListener {
    @Override
    public void onCallStateChanged(int state) {
        if (!audioServiceIsRegistered()) {
            return;
        }
        if (state == TelephonyManager.CALL_STATE_IDLE) {
            if (isExternallyPaused) {
                playAudio();
            }
            isExternallyPaused = false;
        } else {
            pauseAudio();
            isExternallyPaused = true;
        }
    }
}

Solution

  • According to documentation, some permissions have permission groups, and when you request permission, the confirmation dialog shown to the user only relates to group.

    In your case, the group of android.permission.READ_PHONE_STATE is android.permission-group.PHONE. so when you request permission, the details will contain the details of Phone permission group.

    According to google, the purpose is to minimize the number of possible dialog:

    Permission groups help the system minimize the number of system dialogs that are presented to the user when an app requests closely related permissions. When a user is presented with a prompt to grant permissions for an application, permissions belonging to the same group are presented in the same interface. However, permissions can change groups without notice, so don't assume that a particular permission is grouped with any other permission.

    I work on an app that only requests receiving SMS, and the dialog said the app wants to send and receive SMS.

    Seems like it's just how the system is built.