Camera lenses and capabilities

Note: This page refers to the Camera2 package. Unless your app requires specific, low-level features from Camera2, we recommend using CameraX. Both CameraX and Camera2 support Android 5.0 (API level 21) and higher.

Many modern Android devices have two or more cameras on the front, back, or both sides of the device. Each lens can have unique capabilities, such as burst capture, manual control, or motion tracking. An app for depositing checks might only use the first rear-facing camera, whereas a social media app might default to a front-facing camera, but give users the option to switch between all available lenses. It can also remember their choices.

This page covers how to list camera lenses and their capabilities so that you can make decisions within your app about which lens to use in a given situation. The following code snippet retrieves a list of all cameras and iterates over them:

try {
    val cameraIdList = cameraManager.cameraIdList // may be empty

    // iterate over available camera devices.
    for (cameraId in cameraIdList) {
        val characteristics = cameraManager.getCameraCharacteristics(cameraId)
        val cameraLensFacing = characteristics.get(CameraCharacteristics.LENS_FACING)
        val cameraCapabilities = characteristics.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES)

        // check if the selected camera device supports basic features
        // ensures backward compatibility with the original Camera API.
        val isBackwardCompatible = cameraCapabilities?.contains(
            CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE
        ) ?: false
        // ...
    }
} catch (e: CameraAccessException) {
    e.message?.let { Log.e(TAG, it) }
    // ...
}

The variable cameraLensFacing describes the direction that the camera faces relative to the device screen, and has one of the following values:

For more information about the lens-facing configuration, see CameraCharacteristics.LENS_FACING.

The variable cameraCapabilities from the preceding code sample contains information about miscellaneous capabilities, including whether the camera is able to produce standard frames as an output (as opposed to, for example, only depth sensor data). You can look for whether CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE is one of the camera's listed capabilities, which is stored as a flag in isBackwardCompatible.

Choose sensible defaults

In your app, you likely want to open a specific camera by default (if it's available). For example, a selfie app likely opens the front-facing camera, while an augmented reality app might start with the back camera. The following function returns the first camera that faces a given direction:

fun getFirstCameraIdFacing(
    cameraManager: CameraManager,
    facing: Int = CameraMetadata.LENS_FACING_BACK
): String? {
    try {
        // Get list of all compatible cameras.
        val cameraIds = cameraManager.cameraIdList.filter {
            val characteristics = cameraManager.getCameraCharacteristics(it)
            val capabilities = characteristics.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES)
            capabilities?.contains(
                CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE
            ) ?: false
        }

        // Iterate over the list of cameras and return the first one matching desired
        // lens-facing configuration.
        cameraIds.forEach {
            val characteristics = cameraManager.getCameraCharacteristics(it)
            if (characteristics.get(CameraCharacteristics.LENS_FACING) == facing) {
                return it
            }
        }

        // If no camera matched desired orientation, return the first one from the list.
        return cameraIds.firstOrNull()
    } catch (e: CameraAccessException) {
        e.message?.let { Log.e(TAG, it) }
        return null
    }
}

Enable switching cameras

Many camera apps give users the option to switch between cameras:

Google Camera app interface displaying the switch camera button.
Figure 1. Switch camera button in the Google Camera app

Many devices have multiple cameras that face the same direction. Some even have external USB cameras. To provide users with a UI that lets them switch between different facing cameras, choose the first available camera for each possible lens-facing configuration.

Although there is no universal logic for selecting the next camera, the following code works for most use cases:

fun filterCompatibleCameras(
    cameraIds: Array<String>,
    cameraManager: CameraManager
): List<String> {
    return cameraIds.filter {
        val characteristics = cameraManager.getCameraCharacteristics(it)
        characteristics.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES)?.contains(
            CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE
        ) ?: false
    }
}

fun filterCameraIdsFacing(
    cameraIds: List<String>,
    cameraManager: CameraManager,
    facing: Int
): List<String> {
    return cameraIds.filter {
        val characteristics = cameraManager.getCameraCharacteristics(it)
        characteristics.get(CameraCharacteristics.LENS_FACING) == facing
    }
}

fun getNextCameraId(cameraManager: CameraManager, currCameraId: String? = null): String? {
    // Get all front, back and external cameras in 3 separate lists.
    val cameraIds = filterCompatibleCameras(cameraManager.cameraIdList, cameraManager)
    val backCameras = filterCameraIdsFacing(
        cameraIds, cameraManager, CameraMetadata.LENS_FACING_BACK
    )
    val frontCameras = filterCameraIdsFacing(
        cameraIds, cameraManager, CameraMetadata.LENS_FACING_FRONT
    )
    val externalCameras = filterCameraIdsFacing(
        cameraIds, cameraManager, CameraMetadata.LENS_FACING_EXTERNAL
    )

    // The recommended order of iteration is: all external, first back, first front.
    val allCameras = (
        externalCameras + listOf(
            backCameras.firstOrNull(), frontCameras.firstOrNull()
        )
        ).filterNotNull()

    // Get the index of the currently selected camera in the list.
    val cameraIndex = allCameras.indexOf(currCameraId)

    // The selected camera may not be in the list, for example it could be an
    // external camera that has been removed by the user.
    return if (cameraIndex == -1) {
        // Return the first camera from the list.
        allCameras.getOrNull(0)
    } else {
        // Return the next camera from the list, wrap around if necessary.
        allCameras.getOrNull((cameraIndex + 1) % allCameras.size)
    }
}

This code works for a large set of devices with many different configurations. For more information on accounting for edge cases, see CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA.

Create compatible apps

For apps still using the deprecated Camera API, the number of cameras that Camera.getNumberOfCameras() returns depends on OEM implementation. If there is a logical multi-camera in the system, to maintain app backward compatibility, this method will only expose one camera for every logical camera and underlying physical cameras group. Use the Camera2 API to see all cameras.

For more background information on camera orientations, see Camera.CameraInfo.orientation.

In general, use the Camera.getCameraInfo() API to query all camera orientations, and expose only one camera for each available orientation to users that are switching between cameras.

Accommodate all device types

Don't assume that your app always runs on a handheld device with one or two cameras. Instead, choose the most appropriate cameras for the app. If you don't need a specific camera, select the first camera that faces the chosen direction. If the camera facing a certain direction isn't available, consider whether the user can complete their journey with a different camera. Don't restrict your app's availability on certain devices based on camera hardware. If an external camera is connected, the user probably prefers it as the default.