Detect lack of earpiece (speakerphone only) on an Android device

Viewed 3002

I've got an app which displays a speakerphone toggle button when used on a phone. The toggle switches audio routing between the phone's earpiece and the speakerphone. However, when the app is run on a tablet (or any device which lacks an earpiece), I'd like to remove the toggle, since all audio is routed through the speakerphone.

Ideally, I'd like to use some kind of isEarpiecePresent() call, or maybe check a flag on some configuration object to find this information, but I can't find anything of the sort in the API.

I attempted to work around the issue by calling AudioManager.setSpeakerphoneOn(false), then checking AudioManager.isSpeakerphoneOn(), hoping that it would still return true and I could key off of that. The system returned false, even though audio is still routing through the speaker.

I'm currently thinking of checking for telephony capability, even though that doesn't exactly fit. Any other ideas?

6 Answers

starting with Android S/12 (API 31) we have new methods for checking built-in earpiece presence. comprehensive copy-pasteable method below, on older system versions it is using reflection from @milosmns answer (thanks!)

private static Boolean hasEarpiece = null;

public static boolean hasEarpiece(Context context) {
    if (hasEarpiece != null) return hasEarpiece;

    AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);

    if (Build.VERSION.SDK_INT >= 31) {
        List<AudioDeviceInfo> devices = audioManager.getAvailableCommunicationDevices();
        for (AudioDeviceInfo device : devices) {
            if (device.getType() == AudioDeviceInfo.TYPE_BUILTIN_EARPIECE) {
                hasEarpiece = true;
                break;
            }
        }
        if (hasEarpiece == null) {
            // just for safety
            AudioDeviceInfo currDevice = audioManager.getCommunicationDevice();
            hasEarpiece = currDevice != null &&
                    currDevice.getType() == AudioDeviceInfo.TYPE_BUILTIN_EARPIECE;
        }
        Log.i("EarpieceBuiltInChecker", "hasEarpiece detected:%s", hasEarpiece);
        return hasEarpiece;
    }

    try {
        Method method = AudioManager.class.getMethod("getDevicesForStream", Integer.TYPE);
        Field field = AudioManager.class.getField("DEVICE_OUT_EARPIECE");
        int earpieceFlag = field.getInt(null);
        int bitmaskResult = (int) method.invoke(audioManager, AudioManager.STREAM_VOICE_CALL);

        hasEarpiece = (bitmaskResult & earpieceFlag) == earpieceFlag;
        Log.i("EarpieceBuiltInChecker", "hasEarpiece detected:" + hasEarpiece);
    } catch (Throwable error) {
        hasEarpiece = false;
        Log.i("EarpieceBuiltInChecker", "hasEarpiece detection FAILED!");
    }

    return hasEarpiece;
}

my streams are AudioManager.STREAM_VOICE_CALL type (where applicable)

Related