diff --git a/src/android/camera_capabilities.cpp b/src/android/camera_capabilities.cpp
index be55db88..c7994800 100644
--- a/src/android/camera_capabilities.cpp
+++ b/src/android/camera_capabilities.cpp
@@ -10,6 +10,7 @@
 #include <array>
 #include <cmath>
 #include <map>
+#include <type_traits>
 
 #include <hardware/camera3.h>
 
@@ -124,6 +125,142 @@ hwLevelStrings = {
 	{ ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL_EXTERNAL, "EXTERNAL" },
 };
 
+enum class ControlRange {
+	Min,
+	Def,
+	Max,
+};
+
+/**
+ * \brief Set android metadata from libcamera ControlInfo
+ * \tparam T Type of the control in android
+ * \tparam V Type of the control in libcamera
+ * \param[in] metadata Android metadata pack to add the control value to
+ * \param[in] tag Android metadata tag
+ * \param[in] controlsInfo libcamera ControlInfoMap from which to find the control info
+ * \param[in] control libcamera ControlId to find from \a controlsInfo
+ * \param[in] controlRange Whether to use the min, def, or max value from the control info
+ *
+ * Set the android metadata entry in \a metadata with tag \a tag based on the
+ * control info found for the libcamera control \a control in the libcamera
+ * ControlInfoMap \a controlsInfo. If no libcamera ControlInfo is found, then
+ * the function returns without modifying anything.
+ *
+ * This function is for scalar values.
+ */
+template<typename T,
+	 std::enable_if_t<std::is_arithmetic_v<T>> * = nullptr,
+	 typename V>
+void setMetadata(CameraMetadata *metadata, uint32_t tag,
+		 const ControlInfoMap &controlsInfo, const ControlId *control,
+		 enum ControlRange controlRange)
+{
+	const auto &info = controlsInfo.find(control);
+	if (info == controlsInfo.end())
+		return;
+
+	T ret;
+	switch (controlRange) {
+	case ControlRange::Min:
+		ret = info->second.min().get<V>();
+		break;
+	case ControlRange::Def:
+		ret = info->second.def().get<V>();
+		break;
+	case ControlRange::Max:
+		ret = info->second.max().get<V>();
+		break;
+	}
+
+	metadata->addEntry(tag, ret);
+	return;
+}
+
+/**
+ * \brief Set android metadata from libcamera ControlInfo or a default value
+ * \tparam T Type of the control in android
+ * \tparam U Type of the control in libcamera
+ * \param[in] metadata Android metadata pack to add the control value to
+ * \param[in] tag Android metadata tag
+ * \param[in] controlsInfo libcamera ControlInfoMap from which to find the control info
+ * \param[in] control libcamera ControlId to find from \a controlsInfo
+ * \param[in] controlRange Whether to use the min, def, or max value from the control info
+ * \param[in] defaultValue The value to set in \a metadata if \a control is not found
+ *
+ * Set the android metadata entry in \a metadata with tag \a tag based on the
+ * control info found for the libcamera control \a control in the libcamera
+ * ControlInfoMap \a controlsInfo. If no libcamera ControlInfo is found, then
+ * the android metadata entry is set to \a defaultValue.
+ *
+ * This function is for scalar values.
+ */
+template<typename T,
+	 typename U,
+	 typename V,
+	 std::enable_if_t<std::is_arithmetic_v<V> ||
+			  std::is_enum_v<V>> * = nullptr>
+T setMetadata(CameraMetadata *metadata, uint32_t tag,
+	      const ControlInfoMap &controlsInfo, const ControlId *control,
+	      enum ControlRange controlRange, const V defaultValue)
+{
+	T ret = defaultValue;
+
+	const auto &info = controlsInfo.find(control);
+	if (info != controlsInfo.end()) {
+		switch (controlRange) {
+		case ControlRange::Min:
+			ret = info->second.min().get<U>();
+			break;
+		case ControlRange::Def:
+			ret = info->second.def().get<U>();
+			break;
+		case ControlRange::Max:
+			ret = info->second.max().get<U>();
+			break;
+		}
+	}
+
+	metadata->addEntry(tag, ret);
+	return ret;
+}
+
+/**
+ * \brief Set android metadata from libcamera ControlInfo or a default value
+ * \tparam T Type of the control in android
+ * \tparam V Type of the control in libcamera
+ * \param[in] metadata Android metadata pack to add the control value to
+ * \param[in] tag Android metadata tag
+ * \param[in] controlsInfo libcamera ControlInfoMap from which to find the control info
+ * \param[in] control libcamera ControlId to find from \a controlsInfo
+ * \param[in] defaultVector The value to set in \a metadata if \a control is not found
+ *
+ * Set the android metadata entry in \a metadata with tag \a tag based on the
+ * control info found for the libcamera control \a control in the libcamera
+ * ControlInfoMap \a controlsInfo. If no libcamera ControlInfo is found, then
+ * the android metadata entry is set to \a defaultVector.
+ *
+ * This function is for vector values.
+ */
+template<typename T, typename V>
+std::vector<T> setMetadata(CameraMetadata *metadata, uint32_t tag,
+			   const ControlInfoMap &controlsInfo,
+			   const ControlId *control,
+			   const std::vector<T> &defaultVector)
+{
+	const auto &info = controlsInfo.find(control);
+	if (info == controlsInfo.end()) {
+		metadata->addEntry(tag, defaultVector);
+		return defaultVector;
+	}
+
+	std::vector<T> ret(info->second.values().size());
+	for (const auto &value : info->second.values())
+		ret.push_back(value.get<V>());
+	metadata->addEntry(tag, ret);
+
+	return ret;
+}
+
 } /* namespace */
 
 bool CameraCapabilities::validateManualSensorCapability(CameraMetadata *staticMetadata)
