[libcamera-devel,v7,5/9] android: Add infrastructure for determining capabilities and hardware level
diff mbox series

Message ID 20210730103536.81117-6-paul.elder@ideasonboard.com
State Accepted
Headers show
Series
  • android: Support capability and hardware level detection
Related show

Commit Message

Paul Elder July 30, 2021, 10:35 a.m. UTC
Add the infrastructure for checking and reporting capabilities. Use
these capabilities to determine the hardware level as well.

Bug: https://bugs.libcamera.org/show_bug.cgi?id=55
Signed-off-by: Paul Elder <paul.elder@ideasonboard.com>

---
Changes in v7:
- use entryContains for the single-presence checks
- go back to set
- use the staticMetadata_ member variable instead of passing it
- return false in the capaibilities detector, with a todo item

Changes in v6:
- make all args const
- convert the caps list from set to vector

Changes in v5:
- change the whole thing to turn on capabilities after they are all
  confirmed, instead of disabling them as conditions are not met

Changes in v4:
- rebase on camera capabilities refactoring
- switch to std::set from std::map
- make hwlevel similar to capabilities

Changes in v3:
- fix some compiler errors
- go ahead and initialize the capabilities to true, update the commit
  message accordingly

Changes in v2:
- add a flag for FULL, since there are a few requirements that are not
  obtained from capabilities alone
- add burst capture capability, since that is required for FULL as well
---
 src/android/camera_capabilities.cpp | 202 +++++++++++++++++++++++++---
 src/android/camera_capabilities.h   |  11 ++
 2 files changed, 198 insertions(+), 15 deletions(-)

Comments

Laurent Pinchart Aug. 2, 2021, 1:19 a.m. UTC | #1
Hi Paul,

Thank you for the patch.

On Fri, Jul 30, 2021 at 07:35:32PM +0900, Paul Elder wrote:
> Add the infrastructure for checking and reporting capabilities. Use
> these capabilities to determine the hardware level as well.
> 
> Bug: https://bugs.libcamera.org/show_bug.cgi?id=55
> Signed-off-by: Paul Elder <paul.elder@ideasonboard.com>
> 
> ---
> Changes in v7:
> - use entryContains for the single-presence checks
> - go back to set
> - use the staticMetadata_ member variable instead of passing it
> - return false in the capaibilities detector, with a todo item
> 
> Changes in v6:
> - make all args const
> - convert the caps list from set to vector
> 
> Changes in v5:
> - change the whole thing to turn on capabilities after they are all
>   confirmed, instead of disabling them as conditions are not met
> 
> Changes in v4:
> - rebase on camera capabilities refactoring
> - switch to std::set from std::map
> - make hwlevel similar to capabilities
> 
> Changes in v3:
> - fix some compiler errors
> - go ahead and initialize the capabilities to true, update the commit
>   message accordingly
> 
> Changes in v2:
> - add a flag for FULL, since there are a few requirements that are not
>   obtained from capabilities alone
> - add burst capture capability, since that is required for FULL as well
> ---
>  src/android/camera_capabilities.cpp | 202 +++++++++++++++++++++++++---
>  src/android/camera_capabilities.h   |  11 ++
>  2 files changed, 198 insertions(+), 15 deletions(-)
> 
> diff --git a/src/android/camera_capabilities.cpp b/src/android/camera_capabilities.cpp
> index 3596b136..b59a854f 100644
> --- a/src/android/camera_capabilities.cpp
> +++ b/src/android/camera_capabilities.cpp
> @@ -7,8 +7,10 @@
>  
>  #include "camera_capabilities.h"
>  
> +#include <algorithm>
>  #include <array>
>  #include <cmath>
> +#include <map>
>  
>  #include <hardware/camera3.h>
>  
> @@ -114,8 +116,180 @@ const std::map<int, const Camera3Format> camera3FormatsMap = {
>  	},
>  };
>  
> +const std::map<camera_metadata_enum_android_info_supported_hardware_level, std::string>
> +hwLevelStrings = {
> +	{ ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED,  "LIMITED" },
> +	{ ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL_FULL,     "FULL" },
> +	{ ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY,   "LEGACY" },
> +	{ ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL_3,        "LEVEL_3" },
> +	{ ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL_EXTERNAL, "EXTERNAL" },
> +};
> +
>  } /* namespace */
>  
> +bool CameraCapabilities::validateManualSensorCapability()
> +{
> +	const char *noMode = "Manual sensor capability unavailable: ";
> +
> +	if (!staticMetadata_->entryContains<uint8_t>(ANDROID_CONTROL_AE_AVAILABLE_MODES,
> +						     ANDROID_CONTROL_AE_MODE_OFF)) {
> +		LOG(HAL, Info) << noMode << "missing AE mode off";
> +		return false;
> +	}
> +
> +	if (!staticMetadata_->entryContains<uint8_t>(ANDROID_CONTROL_AE_LOCK_AVAILABLE,
> +						     ANDROID_CONTROL_AE_LOCK_AVAILABLE_TRUE)) {
> +		LOG(HAL, Info) << noMode << "missing AE lock";
> +		return false;
> +	}
> +
> +	/*
> +	 * \todo Return true here after we satisfy all the requirements:
> +	 * https://developer.android.com/reference/android/hardware/camera2/CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR
> +	 * Manual frame duration control
> +	 *     android.sensor.frameDuration
> +	 *     android.sensor.info.maxFrameDuration
> +	 * Manual exposure control
> +	 *     android.sensor.exposureTime
> +	 *     android.sensor.info.exposureTimeRange
> +	 * Manual sensitivity control
> +	 *     android.sensor.sensitivity
> +	 *     android.sensor.info.sensitivityRange
> +	 * Manual lens control (if the lens is adjustable)
> +	 *     android.lens.*
> +	 * Manual flash control (if a flash unit is present)
> +	 *     android.flash.*
> +	 * Manual black level locking
> +	 *     android.blackLevel.lock
> +	 * Auto exposure lock
> +	 *     android.control.aeLock
> +	 */
> +	return false;
> +}
> +
> +bool CameraCapabilities::validateManualPostProcessingCapability()
> +{
> +	const char *noMode = "Manual post processing capability unavailable: ";
> +
> +	if (!staticMetadata_->entryContains<uint8_t>(ANDROID_CONTROL_AWB_AVAILABLE_MODES,
> +						     ANDROID_CONTROL_AWB_MODE_OFF)) {
> +		LOG(HAL, Info) << noMode << "missing AWB mode off";
> +		return false;
> +	}
> +
> +	if (!staticMetadata_->entryContains<uint8_t>(ANDROID_CONTROL_AWB_LOCK_AVAILABLE,
> +						     ANDROID_CONTROL_AWB_LOCK_AVAILABLE_TRUE)) {
> +		LOG(HAL, Info) << noMode << "missing AWB lock";
> +		return false;
> +	}
> +
> +	/*
> +	 * \todo return true here after we satisfy all the requirements:
> +	 * https://developer.android.com/reference/android/hardware/camera2/CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_MANUAL_POST_PROCESSING
> +	 * Manual tonemap control
> +	 *     android.tonemap.curve
> +	 *     android.tonemap.mode
> +	 *     android.tonemap.maxCurvePoints
> +	 *     android.tonemap.gamma
> +	 *     android.tonemap.presetCurve
> +	 * Manual white balance control
> +	 *     android.colorCorrection.transform
> +	 *     android.colorCorrection.gains
> +	 * Manual lens shading map control
> +	 *     android.shading.mode
> +	 *     android.statistics.lensShadingMapMode
> +	 *     android.statistics.lensShadingMap
> +	 *     android.lens.info.shadingMapSize
> +	 * Manual aberration correction control (if aberration correction is supported)
> +	 *     android.colorCorrection.aberrationMode
> +	 *     android.colorCorrection.availableAberrationModes
> +	 * Auto white balance lock
> +	 *     android.control.awbLock
> +	 */
> +	return false;
> +}
> +
> +bool CameraCapabilities::validateBurstCaptureCapability()
> +{
> +	camera_metadata_ro_entry_t entry;
> +	bool found;
> +
> +	const char *noMode = "Burst capture capability unavailable: ";
> +
> +	if (!staticMetadata_->entryContains<uint8_t>(ANDROID_CONTROL_AE_LOCK_AVAILABLE,
> +						     ANDROID_CONTROL_AE_LOCK_AVAILABLE_TRUE)) {
> +		LOG(HAL, Info) << noMode << "missing AE lock";
> +		return false;
> +	}
> +
> +	if (!staticMetadata_->entryContains<uint8_t>(ANDROID_CONTROL_AWB_LOCK_AVAILABLE,
> +						     ANDROID_CONTROL_AWB_LOCK_AVAILABLE_TRUE)) {
> +		LOG(HAL, Info) << noMode << "missing AWB lock";
> +		return false;
> +	}
> +
> +	found = staticMetadata_->getEntry(ANDROID_SYNC_MAX_LATENCY, &entry);
> +	if (!found || *entry.data.i32 < 0 || 4 < *entry.data.i32) {

Not sure if you've seen my last e-mail about this in v5 :-)

> +		LOG(HAL, Info)
> +			<< noMode << "max sync latency is "
> +			<< (found ? std::to_string(*entry.data.i32) : "not present");
> +		return false;
> +	}
> +
> +	/*
> +	 * \todo return true here after we satisfy all the requirements
> +	 * https://developer.android.com/reference/android/hardware/camera2/CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE
> +	 */
> +	return false;
> +}
> +
> +std::set<camera_metadata_enum_android_request_available_capabilities>
> +CameraCapabilities::computeCapabilities()
> +{
> +	std::set<camera_metadata_enum_android_request_available_capabilities>
> +		capabilities;
> +
> +	capabilities.insert(ANDROID_REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE);
> +
> +	if (validateManualSensorCapability())
> +		capabilities.insert(ANDROID_REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR);
> +
> +	if (validateManualPostProcessingCapability())
> +		capabilities.insert(ANDROID_REQUEST_AVAILABLE_CAPABILITIES_MANUAL_POST_PROCESSING);
> +
> +	if (validateBurstCaptureCapability())
> +		capabilities.insert(ANDROID_REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE);
> +
> +	if (rawStreamAvailable_)
> +		capabilities.insert(ANDROID_REQUEST_AVAILABLE_CAPABILITIES_RAW);
> +
> +	return capabilities;
> +}
> +
> +void CameraCapabilities::computeHwLevel(
> +	const std::set<camera_metadata_enum_android_request_available_capabilities> &caps)
> +{
> +	camera_metadata_ro_entry_t entry;
> +	bool found;
> +	camera_metadata_enum_android_info_supported_hardware_level
> +		hwLevel = ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL_FULL;
> +
> +	if (!caps.count(ANDROID_REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR))
> +		hwLevel = ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED;
> +
> +	if (!caps.count(ANDROID_REQUEST_AVAILABLE_CAPABILITIES_MANUAL_POST_PROCESSING))
> +		hwLevel = ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED;
> +
> +	if (!caps.count(ANDROID_REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE))
> +		hwLevel = ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED;
> +
> +	found = staticMetadata_->getEntry(ANDROID_SYNC_MAX_LATENCY, &entry);
> +	if (!found || *entry.data.i32 != 0)
> +		hwLevel = ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED;
> +
> +	hwLevel_ = hwLevel;

You could use hwLevel_ through the function and drop the local variable,
up to you.

> +}
> +
>  int CameraCapabilities::initialize(std::shared_ptr<libcamera::Camera> camera,
>  				   int orientation, int facing)
>  {
> @@ -851,11 +1025,6 @@ int CameraCapabilities::initializeStaticMetadata()
>  	uint8_t croppingType = ANDROID_SCALER_CROPPING_TYPE_CENTER_ONLY;
>  	staticMetadata_->addEntry(ANDROID_SCALER_CROPPING_TYPE, croppingType);
>  
> -	/* Info static metadata. */
> -	uint8_t supportedHWLevel = ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED;
> -	staticMetadata_->addEntry(ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL,
> -				  supportedHWLevel);
> -
>  	/* Request static metadata. */
>  	int32_t partialResultCount = 1;
>  	staticMetadata_->addEntry(ANDROID_REQUEST_PARTIAL_RESULT_COUNT,
> @@ -876,21 +1045,24 @@ int CameraCapabilities::initializeStaticMetadata()
>  	staticMetadata_->addEntry(ANDROID_REQUEST_MAX_NUM_INPUT_STREAMS,
>  				  maxNumInputStreams);
>  
> -	std::vector<uint8_t> availableCapabilities = {
> -		ANDROID_REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE,
> -	};
> -
> -	/* Report if camera supports RAW. */
> -	if (rawStreamAvailable_)
> -		availableCapabilities.push_back(ANDROID_REQUEST_AVAILABLE_CAPABILITIES_RAW);
> -
>  	/* Number of { RAW, YUV, JPEG } supported output streams */
>  	int32_t numOutStreams[] = { rawStreamAvailable_, 2, 1 };
>  	staticMetadata_->addEntry(ANDROID_REQUEST_MAX_NUM_OUTPUT_STREAMS,
>  				  numOutStreams);
>  
> -	staticMetadata_->addEntry(ANDROID_REQUEST_AVAILABLE_CAPABILITIES,
> -				  availableCapabilities);
> +	/* Check capabilities */
> +	std::set<camera_metadata_enum_android_request_available_capabilities>
> +		capabilities = computeCapabilities();
> +	std::vector<camera_metadata_enum_android_request_available_capabilities>
> +		capsVec(capabilities.begin(), capabilities.end());
> +	staticMetadata_->addEntry(ANDROID_REQUEST_AVAILABLE_CAPABILITIES, capsVec);
> +
> +	computeHwLevel(capabilities);
> +	staticMetadata_->addEntry(ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL, hwLevel_);
> +
> +	LOG(HAL, Info)
> +		<< "Hardware level: "
> +		<< hwLevelStrings.find(hwLevel_)->second;

The last two lines hold on a single line.

With these comments addressed or not,

Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>

>  
>  	std::vector<int32_t> availableCharacteristicsKeys = {
>  		ANDROID_COLOR_CORRECTION_AVAILABLE_ABERRATION_MODES,
> diff --git a/src/android/camera_capabilities.h b/src/android/camera_capabilities.h
> index 42a976d3..c7a966ce 100644
> --- a/src/android/camera_capabilities.h
> +++ b/src/android/camera_capabilities.h
> @@ -42,6 +42,16 @@ private:
>  		int androidFormat;
>  	};
>  
> +	bool validateManualSensorCapability();
> +	bool validateManualPostProcessingCapability();
> +	bool validateBurstCaptureCapability();
> +
> +	std::set<camera_metadata_enum_android_request_available_capabilities>
> +		computeCapabilities();
> +
> +	void computeHwLevel(
> +		const std::set<camera_metadata_enum_android_request_available_capabilities> &caps);
> +
>  	std::vector<libcamera::Size>
>  	initializeYUVResolutions(const libcamera::PixelFormat &pixelFormat,
>  				 const std::vector<libcamera::Size> &resolutions);
> @@ -56,6 +66,7 @@ private:
>  	int facing_;
>  	int orientation_;
>  	bool rawStreamAvailable_;
> +	camera_metadata_enum_android_info_supported_hardware_level hwLevel_;
>  
>  	std::vector<Camera3StreamConfiguration> streamConfigurations_;
>  	std::map<int, libcamera::PixelFormat> formatsMap_;

Patch
diff mbox series

diff --git a/src/android/camera_capabilities.cpp b/src/android/camera_capabilities.cpp
index 3596b136..b59a854f 100644
--- a/src/android/camera_capabilities.cpp
+++ b/src/android/camera_capabilities.cpp
@@ -7,8 +7,10 @@ 
 
 #include "camera_capabilities.h"
 
+#include <algorithm>
 #include <array>
 #include <cmath>
+#include <map>
 
 #include <hardware/camera3.h>
 
@@ -114,8 +116,180 @@  const std::map<int, const Camera3Format> camera3FormatsMap = {
 	},
 };
 
+const std::map<camera_metadata_enum_android_info_supported_hardware_level, std::string>
+hwLevelStrings = {
+	{ ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED,  "LIMITED" },
+	{ ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL_FULL,     "FULL" },
+	{ ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY,   "LEGACY" },
+	{ ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL_3,        "LEVEL_3" },
+	{ ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL_EXTERNAL, "EXTERNAL" },
+};
+
 } /* namespace */
 
+bool CameraCapabilities::validateManualSensorCapability()
+{
+	const char *noMode = "Manual sensor capability unavailable: ";
+
+	if (!staticMetadata_->entryContains<uint8_t>(ANDROID_CONTROL_AE_AVAILABLE_MODES,
+						     ANDROID_CONTROL_AE_MODE_OFF)) {
+		LOG(HAL, Info) << noMode << "missing AE mode off";
+		return false;
+	}
+
+	if (!staticMetadata_->entryContains<uint8_t>(ANDROID_CONTROL_AE_LOCK_AVAILABLE,
+						     ANDROID_CONTROL_AE_LOCK_AVAILABLE_TRUE)) {
+		LOG(HAL, Info) << noMode << "missing AE lock";
+		return false;
+	}
+
+	/*
+	 * \todo Return true here after we satisfy all the requirements:
+	 * https://developer.android.com/reference/android/hardware/camera2/CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR
+	 * Manual frame duration control
+	 *     android.sensor.frameDuration
+	 *     android.sensor.info.maxFrameDuration
+	 * Manual exposure control
+	 *     android.sensor.exposureTime
+	 *     android.sensor.info.exposureTimeRange
+	 * Manual sensitivity control
+	 *     android.sensor.sensitivity
+	 *     android.sensor.info.sensitivityRange
+	 * Manual lens control (if the lens is adjustable)
+	 *     android.lens.*
+	 * Manual flash control (if a flash unit is present)
+	 *     android.flash.*
+	 * Manual black level locking
+	 *     android.blackLevel.lock
+	 * Auto exposure lock
+	 *     android.control.aeLock
+	 */
+	return false;
+}
+
+bool CameraCapabilities::validateManualPostProcessingCapability()
+{
+	const char *noMode = "Manual post processing capability unavailable: ";
+
+	if (!staticMetadata_->entryContains<uint8_t>(ANDROID_CONTROL_AWB_AVAILABLE_MODES,
+						     ANDROID_CONTROL_AWB_MODE_OFF)) {
+		LOG(HAL, Info) << noMode << "missing AWB mode off";
+		return false;
+	}
+
+	if (!staticMetadata_->entryContains<uint8_t>(ANDROID_CONTROL_AWB_LOCK_AVAILABLE,
+						     ANDROID_CONTROL_AWB_LOCK_AVAILABLE_TRUE)) {
+		LOG(HAL, Info) << noMode << "missing AWB lock";
+		return false;
+	}
+
+	/*
+	 * \todo return true here after we satisfy all the requirements:
+	 * https://developer.android.com/reference/android/hardware/camera2/CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_MANUAL_POST_PROCESSING
+	 * Manual tonemap control
+	 *     android.tonemap.curve
+	 *     android.tonemap.mode
+	 *     android.tonemap.maxCurvePoints
+	 *     android.tonemap.gamma
+	 *     android.tonemap.presetCurve
+	 * Manual white balance control
+	 *     android.colorCorrection.transform
+	 *     android.colorCorrection.gains
+	 * Manual lens shading map control
+	 *     android.shading.mode
+	 *     android.statistics.lensShadingMapMode
+	 *     android.statistics.lensShadingMap
+	 *     android.lens.info.shadingMapSize
+	 * Manual aberration correction control (if aberration correction is supported)
+	 *     android.colorCorrection.aberrationMode
+	 *     android.colorCorrection.availableAberrationModes
+	 * Auto white balance lock
+	 *     android.control.awbLock
+	 */
+	return false;
+}
+
+bool CameraCapabilities::validateBurstCaptureCapability()
+{
+	camera_metadata_ro_entry_t entry;
+	bool found;
+
+	const char *noMode = "Burst capture capability unavailable: ";
+
+	if (!staticMetadata_->entryContains<uint8_t>(ANDROID_CONTROL_AE_LOCK_AVAILABLE,
+						     ANDROID_CONTROL_AE_LOCK_AVAILABLE_TRUE)) {
+		LOG(HAL, Info) << noMode << "missing AE lock";
+		return false;
+	}
+
+	if (!staticMetadata_->entryContains<uint8_t>(ANDROID_CONTROL_AWB_LOCK_AVAILABLE,
+						     ANDROID_CONTROL_AWB_LOCK_AVAILABLE_TRUE)) {
+		LOG(HAL, Info) << noMode << "missing AWB lock";
+		return false;
+	}
+
+	found = staticMetadata_->getEntry(ANDROID_SYNC_MAX_LATENCY, &entry);
+	if (!found || *entry.data.i32 < 0 || 4 < *entry.data.i32) {
+		LOG(HAL, Info)
+			<< noMode << "max sync latency is "
+			<< (found ? std::to_string(*entry.data.i32) : "not present");
+		return false;
+	}
+
+	/*
+	 * \todo return true here after we satisfy all the requirements
+	 * https://developer.android.com/reference/android/hardware/camera2/CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE
+	 */
+	return false;
+}
+
+std::set<camera_metadata_enum_android_request_available_capabilities>
+CameraCapabilities::computeCapabilities()
+{
+	std::set<camera_metadata_enum_android_request_available_capabilities>
+		capabilities;
+
+	capabilities.insert(ANDROID_REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE);
+
+	if (validateManualSensorCapability())
+		capabilities.insert(ANDROID_REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR);
+
+	if (validateManualPostProcessingCapability())
+		capabilities.insert(ANDROID_REQUEST_AVAILABLE_CAPABILITIES_MANUAL_POST_PROCESSING);
+
+	if (validateBurstCaptureCapability())
+		capabilities.insert(ANDROID_REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE);
+
+	if (rawStreamAvailable_)
+		capabilities.insert(ANDROID_REQUEST_AVAILABLE_CAPABILITIES_RAW);
+
+	return capabilities;
+}
+
+void CameraCapabilities::computeHwLevel(
+	const std::set<camera_metadata_enum_android_request_available_capabilities> &caps)
+{
+	camera_metadata_ro_entry_t entry;
+	bool found;
+	camera_metadata_enum_android_info_supported_hardware_level
+		hwLevel = ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL_FULL;
+
+	if (!caps.count(ANDROID_REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR))
+		hwLevel = ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED;
+
+	if (!caps.count(ANDROID_REQUEST_AVAILABLE_CAPABILITIES_MANUAL_POST_PROCESSING))
+		hwLevel = ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED;
+
+	if (!caps.count(ANDROID_REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE))
+		hwLevel = ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED;
+
+	found = staticMetadata_->getEntry(ANDROID_SYNC_MAX_LATENCY, &entry);
+	if (!found || *entry.data.i32 != 0)
+		hwLevel = ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED;
+
+	hwLevel_ = hwLevel;
+}
+
 int CameraCapabilities::initialize(std::shared_ptr<libcamera::Camera> camera,
 				   int orientation, int facing)
 {
@@ -851,11 +1025,6 @@  int CameraCapabilities::initializeStaticMetadata()
 	uint8_t croppingType = ANDROID_SCALER_CROPPING_TYPE_CENTER_ONLY;
 	staticMetadata_->addEntry(ANDROID_SCALER_CROPPING_TYPE, croppingType);
 
-	/* Info static metadata. */
-	uint8_t supportedHWLevel = ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED;
-	staticMetadata_->addEntry(ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL,
-				  supportedHWLevel);
-
 	/* Request static metadata. */
 	int32_t partialResultCount = 1;
 	staticMetadata_->addEntry(ANDROID_REQUEST_PARTIAL_RESULT_COUNT,
@@ -876,21 +1045,24 @@  int CameraCapabilities::initializeStaticMetadata()
 	staticMetadata_->addEntry(ANDROID_REQUEST_MAX_NUM_INPUT_STREAMS,
 				  maxNumInputStreams);
 
-	std::vector<uint8_t> availableCapabilities = {
-		ANDROID_REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE,
-	};
-
-	/* Report if camera supports RAW. */
-	if (rawStreamAvailable_)
-		availableCapabilities.push_back(ANDROID_REQUEST_AVAILABLE_CAPABILITIES_RAW);
-
 	/* Number of { RAW, YUV, JPEG } supported output streams */
 	int32_t numOutStreams[] = { rawStreamAvailable_, 2, 1 };
 	staticMetadata_->addEntry(ANDROID_REQUEST_MAX_NUM_OUTPUT_STREAMS,
 				  numOutStreams);
 
-	staticMetadata_->addEntry(ANDROID_REQUEST_AVAILABLE_CAPABILITIES,
-				  availableCapabilities);
+	/* Check capabilities */
+	std::set<camera_metadata_enum_android_request_available_capabilities>
+		capabilities = computeCapabilities();
+	std::vector<camera_metadata_enum_android_request_available_capabilities>
+		capsVec(capabilities.begin(), capabilities.end());
+	staticMetadata_->addEntry(ANDROID_REQUEST_AVAILABLE_CAPABILITIES, capsVec);
+
+	computeHwLevel(capabilities);
+	staticMetadata_->addEntry(ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL, hwLevel_);
+
+	LOG(HAL, Info)
+		<< "Hardware level: "
+		<< hwLevelStrings.find(hwLevel_)->second;
 
 	std::vector<int32_t> availableCharacteristicsKeys = {
 		ANDROID_COLOR_CORRECTION_AVAILABLE_ABERRATION_MODES,
diff --git a/src/android/camera_capabilities.h b/src/android/camera_capabilities.h
index 42a976d3..c7a966ce 100644
--- a/src/android/camera_capabilities.h
+++ b/src/android/camera_capabilities.h
@@ -42,6 +42,16 @@  private:
 		int androidFormat;
 	};
 
+	bool validateManualSensorCapability();
+	bool validateManualPostProcessingCapability();
+	bool validateBurstCaptureCapability();
+
+	std::set<camera_metadata_enum_android_request_available_capabilities>
+		computeCapabilities();
+
+	void computeHwLevel(
+		const std::set<camera_metadata_enum_android_request_available_capabilities> &caps);
+
 	std::vector<libcamera::Size>
 	initializeYUVResolutions(const libcamera::PixelFormat &pixelFormat,
 				 const std::vector<libcamera::Size> &resolutions);
@@ -56,6 +66,7 @@  private:
 	int facing_;
 	int orientation_;
 	bool rawStreamAvailable_;
+	camera_metadata_enum_android_info_supported_hardware_level hwLevel_;
 
 	std::vector<Camera3StreamConfiguration> streamConfigurations_;
 	std::map<int, libcamera::PixelFormat> formatsMap_;