[libcamera-devel] [PATCH v4 1/1] android: hal: Add Camera3 HAL

Jacopo Mondi jacopo at jmondi.org
Mon Aug 12 10:41:46 CEST 2019


Add libcamera Android Camera HALv3 implementation.

The initial camera HAL implementation supports the LIMITED hardware
level and uses statically defined metadata and camera characteristics.

Add a build option named 'android' and adjust the build system to
selectively compile the Android camera HAL and link it against the
required Android libraries.

Signed-off-by: Jacopo Mondi <jacopo at jmondi.org>
---
 meson_options.txt                  |   5 +
 src/android/camera3_hal.cpp        | 111 ++++
 src/android/camera_device.cpp      | 831 +++++++++++++++++++++++++++++
 src/android/camera_device.h        |  71 +++
 src/android/camera_hal_manager.cpp | 138 +++++
 src/android/camera_hal_manager.h   |  47 ++
 src/android/camera_proxy.cpp       | 194 +++++++
 src/android/camera_proxy.h         |  45 ++
 src/android/meson.build            |   8 +
 src/android/thread_rpc.cpp         |  42 ++
 src/android/thread_rpc.h           |  54 ++
 src/libcamera/meson.build          |   9 +
 src/meson.build                    |   5 +-
 13 files changed, 1559 insertions(+), 1 deletion(-)
 create mode 100644 src/android/camera3_hal.cpp
 create mode 100644 src/android/camera_device.cpp
 create mode 100644 src/android/camera_device.h
 create mode 100644 src/android/camera_hal_manager.cpp
 create mode 100644 src/android/camera_hal_manager.h
 create mode 100644 src/android/camera_proxy.cpp
 create mode 100644 src/android/camera_proxy.h
 create mode 100644 src/android/thread_rpc.cpp
 create mode 100644 src/android/thread_rpc.h

diff --git a/meson_options.txt b/meson_options.txt
index 97efc85b4412..2d78b8d91f9c 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -1,3 +1,8 @@
+option('android',
+        type : 'boolean',
+        value : false,
+        description : 'Compile libcamera with Android Camera3 HAL interface')
+
 option('documentation',
         type : 'boolean',
         description : 'Generate the project documentation')
diff --git a/src/android/camera3_hal.cpp b/src/android/camera3_hal.cpp
new file mode 100644
index 000000000000..8d2629ca356c
--- /dev/null
+++ b/src/android/camera3_hal.cpp
@@ -0,0 +1,111 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (C) 2019, Google Inc.
+ *
+ * camera3_hal.cpp - Android Camera HALv3 module
+ */
+
+#include <hardware/camera_common.h>
+
+#include "camera_hal_manager.h"
+#include "camera_proxy.h"
+#include "log.h"
+
+using namespace libcamera;
+
+LOG_DEFINE_CATEGORY(HAL)
+
+static CameraHalManager cameraManager;
+
+/*------------------------------------------------------------------------------
+ * Android Camera HAL callbacks
+ */
+
+static int hal_get_number_of_cameras(void)
+{
+	return cameraManager.numCameras();
+}
+
+static int hal_get_camera_info(int id, struct camera_info *info)
+{
+	return cameraManager.getCameraInfo(id, info);
+}
+
+static int hal_set_callbacks(const camera_module_callbacks_t *callbacks)
+{
+	return 0;
+}
+
+static int hal_open_legacy(const struct hw_module_t *module, const char *id,
+			   uint32_t halVersion, struct hw_device_t **device)
+{
+	return -ENOSYS;
+}
+
+static int hal_set_torch_mode(const char *camera_id, bool enabled)
+{
+	return -ENOSYS;
+}
+
+/*
+ * First entry point of the camera HAL module.
+ *
+ * Initialize the HAL but does not open any camera device yet (see hal_dev_open)
+ */
+static int hal_init()
+{
+	LOG(HAL, Info) << "Initialising Android camera HAL";
+
+	cameraManager.init();
+
+	return 0;
+}
+
+/*------------------------------------------------------------------------------
+ * Android Camera Device
+ */
+
+static int hal_dev_open(const hw_module_t *module, const char *name,
+			hw_device_t **device)
+{
+	LOG(HAL, Debug) << "Open camera " << name;
+
+	int id = atoi(name);
+	CameraProxy *proxy = cameraManager.open(id, module);
+	if (!proxy) {
+		LOG(HAL, Error)
+			<< "Failed to open camera module '" << id << "'";
+		return -ENODEV;
+	}
+
+	*device = &proxy->camera3Device()->common;
+
+	return 0;
+}
+
+static struct hw_module_methods_t hal_module_methods = {
+	.open = hal_dev_open,
+};
+
+camera_module_t HAL_MODULE_INFO_SYM = {
+	.common = {
+		.tag = HARDWARE_MODULE_TAG,
+		.module_api_version = CAMERA_MODULE_API_VERSION_2_4,
+		.hal_api_version = HARDWARE_HAL_API_VERSION,
+		.id = CAMERA_HARDWARE_MODULE_ID,
+		.name = "libcamera camera HALv3 module",
+		.author = "libcamera",
+		.methods = &hal_module_methods,
+		.dso = nullptr,
+		.reserved = {},
+	},
+
+	.get_number_of_cameras = hal_get_number_of_cameras,
+	.get_camera_info = hal_get_camera_info,
+	.set_callbacks = hal_set_callbacks,
+	.get_vendor_tag_ops = nullptr,
+	.open_legacy = hal_open_legacy,
+	.set_torch_mode = hal_set_torch_mode,
+	.init = hal_init,
+	.reserved = {},
+};
diff --git a/src/android/camera_device.cpp b/src/android/camera_device.cpp
new file mode 100644
index 000000000000..e2c1f2a246c8
--- /dev/null
+++ b/src/android/camera_device.cpp
@@ -0,0 +1,831 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2019, Google Inc.
+ *
+ * camera_device.cpp - libcamera Android Camera Device
+ */
+
+#include "camera_device.h"
+
+#include <system/camera_metadata.h>
+
+#include "log.h"
+
+#include "thread_rpc.h"
+
+using namespace libcamera;
+
+LOG_DECLARE_CATEGORY(HAL);
+
+/*
+ * \struct Camera3RequestDescriptor
+ *
+ * A utility structure that groups information about a capture request to be
+ * later re-used at request complete time to notify the framework.
+ */
+
+CameraDevice::Camera3RequestDescriptor::Camera3RequestDescriptor(
+		unsigned int frameNumber, unsigned int numBuffers)
+	: frameNumber(frameNumber), numBuffers(numBuffers)
+{
+	buffers = new camera3_stream_buffer_t[numBuffers];
+}
+
+CameraDevice::Camera3RequestDescriptor::~Camera3RequestDescriptor()
+{
+	delete[] buffers;
+}
+
+/*
+ * \class CameraDevice
+ *
+ * The CameraDevice class wraps a libcamera::Camera instance, and implements
+ * the camera_device_t interface by handling RPC requests received from its
+ * associated CameraProxy.
+ *
+ * It translate parameters and operations from Camera HALv3 API to the libcamera
+ * ones to provide static information for a Camera, create request templates
+ * for it, process capture requests and then deliver capture results back
+ * to the framework using the designated callbacks.
+ */
+
+CameraDevice::CameraDevice(unsigned int id, std::shared_ptr<Camera> &camera)
+	: running_(false), camera_(camera), staticMetadata_(nullptr),
+	  requestTemplate_(nullptr)
+{
+	camera_->requestCompleted.connect(this, &CameraDevice::requestComplete);
+}
+
+CameraDevice::~CameraDevice()
+{
+	if (staticMetadata_)
+		free_camera_metadata(staticMetadata_);
+	staticMetadata_ = nullptr;
+
+	if (requestTemplate_)
+		free_camera_metadata(requestTemplate_);
+	requestTemplate_ = nullptr;
+}
+
+/*
+ * Handle RPC request received from the associated proxy.
+ */
+void CameraDevice::message(Message *message)
+{
+	if (message->type() != ThreadRpcMessage::type())
+		return Object::message(message);
+
+	ThreadRpcMessage *rpcMessage = static_cast<ThreadRpcMessage *>(message);
+	ThreadRpc *rpc = rpcMessage->rpc;
+
+	switch (rpc->tag) {
+	case ThreadRpc::ProcessCaptureRequest:
+		processCaptureRequest(rpc->request);
+		break;
+	case ThreadRpc::Close:
+		close();
+		break;
+	default:
+		LOG(HAL, Error) << "Unknown RPC operation: " << rpc->tag;
+	}
+
+	rpc->notifyReception();
+}
+
+int CameraDevice::open()
+{
+	int ret = camera_->acquire();
+	if (ret) {
+		LOG(HAL, Error) << "Failed to acquire the camera";
+		return ret;
+	}
+
+	return 0;
+}
+
+void CameraDevice::close()
+{
+	camera_->stop();
+
+	camera_->freeBuffers();
+	camera_->release();
+
+	running_ = false;
+}
+
+void CameraDevice::setCallbacks(const camera3_callback_ops_t *callbacks)
+{
+	callbacks_ = callbacks;
+}
+
+/*
+ * Return static information for the camera.
+ */
+camera_metadata_t *CameraDevice::getStaticMetadata()
+{
+	int ret;
+
+	if (staticMetadata_)
+		return staticMetadata_;
+
+	/*
+	 * The here reported metadata are enough to implement a basic capture
+	 * example application, but a real camera implementation will require
+	 * more.
+	 */
+
+	/* \todo Use correct sizes */
+	#define STATIC_ENTRY_CAP 256
+	#define STATIC_DATA_CAP 6688
+	camera_metadata_t *staticMetadata =
+		allocate_camera_metadata(STATIC_ENTRY_CAP, STATIC_DATA_CAP);
+
+	/* Sensor static metadata. */
+	int32_t pixelArraySize[] = {
+		2592, 1944,
+	};
+	ret = add_camera_metadata_entry(staticMetadata,
+				ANDROID_SENSOR_INFO_PIXEL_ARRAY_SIZE,
+				&pixelArraySize, 2);
+	METADATA_ASSERT(ret);
+
+	int32_t sensorSizes[] = {
+		0, 0, 2560, 1920,
+	};
+	ret = add_camera_metadata_entry(staticMetadata,
+				ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE,
+				&sensorSizes, 4);
+	METADATA_ASSERT(ret);
+
+	int32_t sensitivityRange[] = {
+		32, 2400,
+	};
+	ret = add_camera_metadata_entry(staticMetadata,
+				ANDROID_SENSOR_INFO_SENSITIVITY_RANGE,
+				&sensitivityRange, 2);
+	METADATA_ASSERT(ret);
+
+	uint16_t filterArr = ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_GRBG;
+	ret = add_camera_metadata_entry(staticMetadata,
+				ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT,
+				&filterArr, 1);
+	METADATA_ASSERT(ret);
+
+	int64_t exposureTimeRange[] = {
+		100000, 200000000,
+	};
+	ret = add_camera_metadata_entry(staticMetadata,
+				ANDROID_SENSOR_INFO_EXPOSURE_TIME_RANGE,
+				&exposureTimeRange, 2);
+	METADATA_ASSERT(ret);
+
+	int32_t orientation = 0;
+	ret = add_camera_metadata_entry(staticMetadata,
+				ANDROID_SENSOR_ORIENTATION,
+				&orientation, 1);
+	METADATA_ASSERT(ret);
+
+	/* Flash static metadata. */
+	char flashAvailable = ANDROID_FLASH_INFO_AVAILABLE_FALSE;
+	ret = add_camera_metadata_entry(staticMetadata,
+			ANDROID_FLASH_INFO_AVAILABLE,
+			&flashAvailable, 1);
+	METADATA_ASSERT(ret);
+
+	/* Lens static metadata. */
+	float fn = 2.53 / 100;
+	ret = add_camera_metadata_entry(staticMetadata,
+				ANDROID_LENS_INFO_AVAILABLE_APERTURES, &fn, 1);
+	METADATA_ASSERT(ret);
+
+	/* Control metadata. */
+	char controlMetadata = ANDROID_CONTROL_MODE_AUTO;
+	ret = add_camera_metadata_entry(staticMetadata,
+			ANDROID_CONTROL_AVAILABLE_MODES,
+			&controlMetadata, 1);
+	METADATA_ASSERT(ret);
+
+	char availableAntiBandingModes[] = {
+		ANDROID_CONTROL_AE_ANTIBANDING_MODE_OFF,
+		ANDROID_CONTROL_AE_ANTIBANDING_MODE_50HZ,
+		ANDROID_CONTROL_AE_ANTIBANDING_MODE_60HZ,
+		ANDROID_CONTROL_AE_ANTIBANDING_MODE_AUTO,
+	};
+	ret = add_camera_metadata_entry(staticMetadata,
+			ANDROID_CONTROL_AE_AVAILABLE_ANTIBANDING_MODES,
+			availableAntiBandingModes, 4);
+	METADATA_ASSERT(ret);
+
+	char aeAvailableModes[] = {
+		ANDROID_CONTROL_AE_MODE_ON,
+		ANDROID_CONTROL_AE_MODE_OFF,
+	};
+	ret = add_camera_metadata_entry(staticMetadata,
+			ANDROID_CONTROL_AE_AVAILABLE_MODES,
+			aeAvailableModes, 2);
+	METADATA_ASSERT(ret);
+
+	controlMetadata = ANDROID_CONTROL_AE_LOCK_AVAILABLE_TRUE;
+	ret = add_camera_metadata_entry(staticMetadata,
+			ANDROID_CONTROL_AE_LOCK_AVAILABLE,
+			&controlMetadata, 1);
+	METADATA_ASSERT(ret);
+
+	uint8_t awbLockAvailable = ANDROID_CONTROL_AWB_LOCK_AVAILABLE_FALSE;
+	ret = add_camera_metadata_entry(staticMetadata,
+			ANDROID_CONTROL_AWB_LOCK_AVAILABLE,
+			&awbLockAvailable, 1);
+
+	/* Scaler static metadata. */
+	std::vector<uint32_t> availableStreamFormats = {
+		ANDROID_SCALER_AVAILABLE_FORMATS_BLOB,
+		ANDROID_SCALER_AVAILABLE_FORMATS_YCbCr_420_888,
+		ANDROID_SCALER_AVAILABLE_FORMATS_IMPLEMENTATION_DEFINED,
+	};
+	ret = add_camera_metadata_entry(staticMetadata,
+			ANDROID_SCALER_AVAILABLE_FORMATS,
+			availableStreamFormats.data(),
+			availableStreamFormats.size());
+	METADATA_ASSERT(ret);
+
+	std::vector<uint32_t> availableStreamConfigurations = {
+		ANDROID_SCALER_AVAILABLE_FORMATS_BLOB, 2560, 1920,
+		ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT,
+		ANDROID_SCALER_AVAILABLE_FORMATS_YCbCr_420_888, 2560, 1920,
+		ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT,
+		ANDROID_SCALER_AVAILABLE_FORMATS_IMPLEMENTATION_DEFINED, 2560, 1920,
+		ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT,
+	};
+	ret = add_camera_metadata_entry(staticMetadata,
+			ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS,
+			availableStreamConfigurations.data(),
+			availableStreamConfigurations.size());
+	METADATA_ASSERT(ret);
+
+	std::vector<int64_t> availableStallDurations = {
+		ANDROID_SCALER_AVAILABLE_FORMATS_BLOB, 2560, 1920, 33333333,
+	};
+	ret = add_camera_metadata_entry(staticMetadata,
+			ANDROID_SCALER_AVAILABLE_STALL_DURATIONS,
+			availableStallDurations.data(),
+			availableStallDurations.size());
+	METADATA_ASSERT(ret);
+
+	std::vector<int64_t> minFrameDurations = {
+		ANDROID_SCALER_AVAILABLE_FORMATS_BLOB, 2560, 1920, 33333333,
+		ANDROID_SCALER_AVAILABLE_FORMATS_IMPLEMENTATION_DEFINED, 2560, 1920, 33333333,
+		ANDROID_SCALER_AVAILABLE_FORMATS_YCbCr_420_888, 2560, 1920, 33333333,
+	};
+	ret = add_camera_metadata_entry(staticMetadata,
+			ANDROID_SCALER_AVAILABLE_MIN_FRAME_DURATIONS,
+			minFrameDurations.data(), minFrameDurations.size());
+	METADATA_ASSERT(ret);
+
+	/* Info static metadata. */
+	uint8_t supportedHWLevel = ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED;
+	ret = add_camera_metadata_entry(staticMetadata,
+			ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL,
+			&supportedHWLevel, 1);
+
+	return staticMetadata;
+}
+
+/*
+ * Produce a metadata pack to be used as template for a capture request.
+ */
+const camera_metadata_t *CameraDevice::constructDefaultRequestSettings(int type)
+{
+	int ret;
+
+	/*
+	 * \todo Inspect type and pick the right metadata pack.
+	 * As of now just use a single one for all templates.
+	 */
+	uint8_t captureIntent;
+	switch (type) {
+	case CAMERA3_TEMPLATE_PREVIEW:
+		captureIntent = ANDROID_CONTROL_CAPTURE_INTENT_PREVIEW;
+		break;
+	case CAMERA3_TEMPLATE_STILL_CAPTURE:
+		captureIntent = ANDROID_CONTROL_CAPTURE_INTENT_STILL_CAPTURE;
+		break;
+	case CAMERA3_TEMPLATE_VIDEO_RECORD:
+		captureIntent = ANDROID_CONTROL_CAPTURE_INTENT_VIDEO_RECORD;
+		break;
+	case CAMERA3_TEMPLATE_VIDEO_SNAPSHOT:
+		captureIntent = ANDROID_CONTROL_CAPTURE_INTENT_VIDEO_SNAPSHOT;
+		break;
+	case CAMERA3_TEMPLATE_ZERO_SHUTTER_LAG:
+		captureIntent = ANDROID_CONTROL_CAPTURE_INTENT_ZERO_SHUTTER_LAG;
+		break;
+	case CAMERA3_TEMPLATE_MANUAL:
+		captureIntent = ANDROID_CONTROL_CAPTURE_INTENT_MANUAL;
+		break;
+	default:
+		LOG(HAL, Error) << "Invalid template request type: " << type;
+		return nullptr;
+	}
+
+	if (requestTemplate_)
+		return requestTemplate_;
+
+	/* \todo Use correct sizes */
+	#define REQUEST_TEMPLATE_ENTRIES	  30
+	#define REQUEST_TEMPLATE_DATA		2048
+	requestTemplate_ = allocate_camera_metadata(REQUEST_TEMPLATE_ENTRIES,
+						    REQUEST_TEMPLATE_DATA);
+	if (!requestTemplate_) {
+		LOG(HAL, Error) << "Failed to allocate template metadata";
+		return nullptr;
+	}
+
+	/* Set to 0 the number of 'processed and stalling' streams (ie JPEG). */
+	int32_t maxOutStream[] = { 0, 2, 0 };
+	ret = add_camera_metadata_entry(requestTemplate_,
+			ANDROID_REQUEST_MAX_NUM_OUTPUT_STREAMS,
+			maxOutStream, 3);
+	METADATA_ASSERT(ret);
+
+	uint8_t maxPipelineDepth = 5;
+	ret = add_camera_metadata_entry(requestTemplate_,
+			ANDROID_REQUEST_PIPELINE_MAX_DEPTH,
+			&maxPipelineDepth, 1);
+	METADATA_ASSERT(ret);
+
+	int32_t inputStreams = 0;
+	ret = add_camera_metadata_entry(requestTemplate_,
+			ANDROID_REQUEST_MAX_NUM_INPUT_STREAMS,
+			&inputStreams, 1);
+	METADATA_ASSERT(ret);
+
+	int32_t partialResultCount = 1;
+	ret = add_camera_metadata_entry(requestTemplate_,
+			ANDROID_REQUEST_PARTIAL_RESULT_COUNT,
+			&partialResultCount, 1);
+	METADATA_ASSERT(ret);
+
+	uint8_t availableCapabilities[] = {
+		ANDROID_REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE,
+	};
+	ret = add_camera_metadata_entry(requestTemplate_,
+			ANDROID_REQUEST_AVAILABLE_CAPABILITIES,
+			availableCapabilities, 1);
+	METADATA_ASSERT(ret);
+
+	uint8_t aeMode = ANDROID_CONTROL_AE_MODE_ON;
+	ret = add_camera_metadata_entry(requestTemplate_,
+			ANDROID_CONTROL_AE_MODE,
+			&aeMode, 1);
+	METADATA_ASSERT(ret);
+
+	int32_t aeExposureCompensation = 0;
+	ret = add_camera_metadata_entry(requestTemplate_,
+			ANDROID_CONTROL_AE_EXPOSURE_COMPENSATION,
+			&aeExposureCompensation, 1);
+	METADATA_ASSERT(ret);
+
+	uint8_t aePrecaptureTrigger = ANDROID_CONTROL_AE_PRECAPTURE_TRIGGER_IDLE;
+	ret = add_camera_metadata_entry(requestTemplate_,
+			ANDROID_CONTROL_AE_PRECAPTURE_TRIGGER,
+			&aePrecaptureTrigger, 1);
+	METADATA_ASSERT(ret);
+
+	uint8_t aeLock = ANDROID_CONTROL_AE_LOCK_OFF;
+	ret = add_camera_metadata_entry(requestTemplate_,
+			ANDROID_CONTROL_AE_LOCK,
+			&aeLock, 1);
+	METADATA_ASSERT(ret);
+
+	uint8_t afTrigger = ANDROID_CONTROL_AF_TRIGGER_IDLE;
+	ret = add_camera_metadata_entry(requestTemplate_,
+			ANDROID_CONTROL_AF_TRIGGER,
+			&afTrigger, 1);
+	METADATA_ASSERT(ret);
+
+	uint8_t awbMode = ANDROID_CONTROL_AWB_MODE_AUTO;
+	ret = add_camera_metadata_entry(requestTemplate_,
+			ANDROID_CONTROL_AWB_MODE,
+			&awbMode, 1);
+	METADATA_ASSERT(ret);
+
+	uint8_t awbLock = ANDROID_CONTROL_AWB_LOCK_OFF;
+	ret = add_camera_metadata_entry(requestTemplate_,
+			ANDROID_CONTROL_AWB_LOCK,
+			&awbLock, 1);
+	METADATA_ASSERT(ret);
+
+	uint8_t awbLockAvailable = ANDROID_CONTROL_AWB_LOCK_AVAILABLE_FALSE;
+	ret = add_camera_metadata_entry(requestTemplate_,
+			ANDROID_CONTROL_AWB_LOCK_AVAILABLE,
+			&awbLockAvailable, 1);
+	METADATA_ASSERT(ret);
+
+	uint8_t flashMode = ANDROID_FLASH_MODE_OFF;
+	ret = add_camera_metadata_entry(requestTemplate_,
+			ANDROID_FLASH_MODE,
+			&flashMode, 1);
+	METADATA_ASSERT(ret);
+
+	uint8_t faceDetectMode = ANDROID_STATISTICS_FACE_DETECT_MODE_OFF;
+	ret = add_camera_metadata_entry(requestTemplate_,
+			ANDROID_STATISTICS_FACE_DETECT_MODE,
+			&faceDetectMode, 1);
+	METADATA_ASSERT(ret);
+
+	ret = add_camera_metadata_entry(requestTemplate_,
+			ANDROID_CONTROL_CAPTURE_INTENT,
+			&captureIntent, 1);
+	METADATA_ASSERT(ret);
+
+	/*
+	 * This is quite hard to list at the moment wihtout knowing what
+	 * we could control.
+	 *
+	 * For now, just list in the available Request keys and in the available
+	 * result keys the control and reporting of the AE algorithm.
+	 */
+	std::vector<int32_t> availableRequestKeys = {
+		ANDROID_CONTROL_AE_MODE,
+		ANDROID_CONTROL_AE_EXPOSURE_COMPENSATION,
+		ANDROID_CONTROL_AE_PRECAPTURE_TRIGGER,
+		ANDROID_CONTROL_AE_LOCK,
+		ANDROID_CONTROL_AF_TRIGGER,
+		ANDROID_CONTROL_AWB_MODE,
+		ANDROID_CONTROL_AWB_LOCK,
+		ANDROID_CONTROL_AWB_LOCK_AVAILABLE,
+		ANDROID_CONTROL_CAPTURE_INTENT,
+		ANDROID_FLASH_MODE,
+		ANDROID_STATISTICS_FACE_DETECT_MODE,
+	};
+
+	ret = add_camera_metadata_entry(requestTemplate_,
+			ANDROID_REQUEST_AVAILABLE_REQUEST_KEYS,
+			availableRequestKeys.data(),
+			availableRequestKeys.size());
+	METADATA_ASSERT(ret);
+
+	std::vector<int32_t> availableResultKeys = {
+		ANDROID_CONTROL_AE_MODE,
+		ANDROID_CONTROL_AE_EXPOSURE_COMPENSATION,
+		ANDROID_CONTROL_AE_PRECAPTURE_TRIGGER,
+		ANDROID_CONTROL_AE_LOCK,
+		ANDROID_CONTROL_AF_TRIGGER,
+		ANDROID_CONTROL_AWB_MODE,
+		ANDROID_CONTROL_AWB_LOCK,
+		ANDROID_CONTROL_AWB_LOCK_AVAILABLE,
+		ANDROID_CONTROL_CAPTURE_INTENT,
+		ANDROID_FLASH_MODE,
+		ANDROID_STATISTICS_FACE_DETECT_MODE,
+	};
+	ret = add_camera_metadata_entry(requestTemplate_,
+			ANDROID_REQUEST_AVAILABLE_RESULT_KEYS,
+			availableResultKeys.data(),
+			availableResultKeys.size());
+	METADATA_ASSERT(ret);
+
+	/*
+	 * \todo The available characteristics are be the tags reported
+	 * as part of the static metadata reported at hal_get_camera_info()
+	 * time. As of now, report an empty list.
+	 */
+	std::vector<int32_t> availableCharacteristicsKeys = {};
+	ret = add_camera_metadata_entry(requestTemplate_,
+			ANDROID_REQUEST_AVAILABLE_CHARACTERISTICS_KEYS,
+			availableCharacteristicsKeys.data(),
+			availableCharacteristicsKeys.size());
+	METADATA_ASSERT(ret);
+
+	return requestTemplate_;
+}
+
+/*
+ * Inspect the stream_list to produce a list of StreamConfiguration to
+ * be use to configure the Camera.
+ */
+int CameraDevice::configureStreams(camera3_stream_configuration_t *stream_list)
+{
+	for (unsigned int i = 0; i < stream_list->num_streams; ++i) {
+		camera3_stream_t *stream = stream_list->streams[i];
+
+		LOG(HAL, Info) << "Stream #" << i
+			       << ", direction: " << stream->stream_type
+			       << ", width: " << stream->width
+			       << ", height: " << stream->height
+			       << ", format: " << std::hex << stream->format;
+	}
+
+	/* Hardcode viewfinder role, collecting sizes from the stream config. */
+	if (stream_list->num_streams != 1) {
+		LOG(HAL, Error) << "Only one stream supported";
+		return -EINVAL;
+	}
+
+	StreamRoles roles = { StreamRole::Viewfinder };
+	config_ = camera_->generateConfiguration(roles);
+	if (!config_ || config_->empty()) {
+		LOG(HAL, Error) << "Failed to generate camera configuration";
+		return -EINVAL;
+	}
+
+	/* Only one stream is supported. */
+	camera3_stream_t *camera3Stream = stream_list->streams[0];
+	StreamConfiguration *streamConfiguration = &config_->at(0);
+	streamConfiguration->size.width = camera3Stream->width;
+	streamConfiguration->size.height = camera3Stream->height;
+	streamConfiguration->memoryType = ExternalMemory;
+
+	/*
+	 * \todo We'll need to translate from Android defined pixel format codes
+	 * to the libcamera image format codes. For now, do not change the
+	 * format returned from Camera::generateConfiguration().
+	 */
+
+	switch (config_->validate()) {
+	case CameraConfiguration::Valid:
+		break;
+	case CameraConfiguration::Adjusted:
+		LOG(HAL, Info) << "Camera configuration adjusted";
+		config_.reset();
+		return -EINVAL;
+	case CameraConfiguration::Invalid:
+		LOG(HAL, Info) << "Camera configuration invalid";
+		config_.reset();
+		return -EINVAL;
+	}
+
+	camera3Stream->max_buffers = streamConfiguration->bufferCount;
+
+	/*
+	 * Once the CameraConfiguration has been adjusted/validated
+	 * it can be applied to the camera.
+	 */
+	int ret = camera_->configure(config_.get());
+	if (ret) {
+		LOG(HAL, Error) << "Failed to configure camera '"
+				<< camera_->name() << "'";
+		return ret;
+	}
+
+	return 0;
+}
+
+int CameraDevice::processCaptureRequest(camera3_capture_request_t *camera3Request)
+{
+	StreamConfiguration *streamConfiguration = &config_->at(0);
+	Stream *stream = streamConfiguration->stream();
+
+	if (camera3Request->num_output_buffers != 1) {
+		LOG(HAL, Error) << "Invalid number of output buffers: "
+				<< camera3Request->num_output_buffers;
+		return -EINVAL;
+	}
+
+	/* Start the camera if that's the first request we handle. */
+	if (!running_) {
+		int ret = camera_->allocateBuffers();
+		if (ret) {
+			LOG(HAL, Error) << "Failed to allocate buffers";
+			return ret;
+		}
+
+		ret = camera_->start();
+		if (ret) {
+			LOG(HAL, Error) << "Failed to start camera";
+			camera_->freeBuffers();
+			return ret;
+		}
+
+		running_ = true;
+	}
+
+	/*
+	 * Queue a request for the Camera with the provided dmabuf file
+	 * descriptors.
+	 */
+	const camera3_stream_buffer_t *camera3Buffers =
+					camera3Request->output_buffers;
+
+	/*
+	 * Save the request descriptors for use at completion time.
+	 * The descriptor and the associated memory reserved here are freed
+	 * at request complete time.
+	 */
+	Camera3RequestDescriptor *descriptor =
+		new Camera3RequestDescriptor(camera3Request->frame_number,
+					     camera3Request->num_output_buffers);
+	for (unsigned int i = 0; i < descriptor->numBuffers; ++i) {
+		/*
+		 * Keep track of which stream the request belongs to and store
+		 * the native buffer handles.
+		 *
+		 * \todo Currently we only support one capture buffer. Copy
+		 * all of them to be ready once we'll support more.
+		 */
+		descriptor->buffers[i].stream = camera3Buffers[i].stream;
+		descriptor->buffers[i].buffer = camera3Buffers[i].buffer;
+	}
+
+	/*
+	 * Create a libcamera buffer using the dmabuf descriptors of the first
+	 * and (currently) only supported request buffer.
+	 */
+	const buffer_handle_t camera3Handle = *camera3Buffers[0].buffer;
+	std::array<int, 3> fds = {
+		camera3Handle->data[0],
+		camera3Handle->data[1],
+		camera3Handle->data[2],
+	};
+
+	std::unique_ptr<Buffer> buffer = stream->createBuffer(fds);
+	if (!buffer) {
+		LOG(HAL, Error) << "Failed to create buffer";
+		delete descriptor;
+		return -EINVAL;
+	}
+
+	Request *request =
+		camera_->createRequest(reinterpret_cast<uint64_t>(descriptor));
+	request->addBuffer(std::move(buffer));
+
+	int ret = camera_->queueRequest(request);
+	if (ret) {
+		LOG(HAL, Error) << "Failed to queue request";
+		goto error;
+	}
+
+	return 0;
+
+error:
+	delete request;
+	delete descriptor;
+
+	return ret;
+}
+
+void CameraDevice::requestComplete(Request *request,
+				   const std::map<Stream *, Buffer *> &buffers)
+{
+	Buffer *libcameraBuffer = buffers.begin()->second;
+	camera3_buffer_status status = CAMERA3_BUFFER_STATUS_OK;
+	camera_metadata_t *resultMetadata = nullptr;
+
+	if (request->status() != Request::RequestComplete) {
+		LOG(HAL, Error) << "Request not succesfully completed: "
+				<< request->status();
+		status = CAMERA3_BUFFER_STATUS_ERROR;
+	}
+
+	/* Prepare to call back the Android camera stack. */
+	Camera3RequestDescriptor *descriptor =
+		reinterpret_cast<Camera3RequestDescriptor *>(request->cookie());
+
+	camera3_capture_result_t captureResult = {};
+	captureResult.frame_number = descriptor->frameNumber;
+	captureResult.num_output_buffers = descriptor->numBuffers;
+	for (unsigned int i = 0; i < descriptor->numBuffers; ++i) {
+		/*
+		 * \todo Currently we only support one capture buffer. Prepare
+		 * all of them to be ready once we'll support more.
+		 */
+		descriptor->buffers[i].acquire_fence = -1;
+		descriptor->buffers[i].release_fence = -1;
+		descriptor->buffers[i].status = status;
+	}
+	captureResult.output_buffers =
+		const_cast<const camera3_stream_buffer_t *>(descriptor->buffers);
+
+	if (status == CAMERA3_BUFFER_STATUS_ERROR) {
+		/* \todo Improve error handling. */
+		notifyError(descriptor->frameNumber,
+			    descriptor->buffers[0].stream);
+	} else {
+		notifyShutter(descriptor->frameNumber,
+			      libcameraBuffer->timestamp());
+
+		captureResult.partial_result = 1;
+		resultMetadata = getResultMetadata(descriptor->frameNumber,
+						   libcameraBuffer->timestamp());
+		captureResult.result = resultMetadata;
+	}
+
+	callbacks_->process_capture_result(callbacks_, &captureResult);
+
+	delete descriptor;
+	if (resultMetadata)
+		free_camera_metadata(resultMetadata);
+
+	return;
+}
+
+void CameraDevice::notifyShutter(uint32_t frameNumber, uint64_t timestamp)
+{
+	camera3_notify_msg_t notify = {};
+
+	notify.type = CAMERA3_MSG_SHUTTER;
+	notify.message.shutter.frame_number = frameNumber;
+	notify.message.shutter.timestamp = timestamp;
+
+	callbacks_->notify(callbacks_, &notify);
+}
+
+void CameraDevice::notifyError(uint32_t frameNumber, camera3_stream_t *stream)
+{
+	camera3_notify_msg_t notify = {};
+
+	notify.type = CAMERA3_MSG_ERROR;
+	notify.message.error.error_stream = stream;
+	notify.message.error.frame_number = frameNumber;
+	notify.message.error.error_code = CAMERA3_MSG_ERROR_REQUEST;
+
+	callbacks_->notify(callbacks_, &notify);
+}
+
+/*
+ * Produce a set of fixed result metadata.
+ */
+camera_metadata_t *CameraDevice::getResultMetadata(int frame_number,
+						   int64_t timestamp)
+{
+	int ret;
+
+	/* \todo Use correct sizes */
+	#define RESULT_ENTRY_CAP 256
+	#define RESULT_DATA_CAP 6688
+	camera_metadata_t *resultMetadata =
+		allocate_camera_metadata(STATIC_ENTRY_CAP, STATIC_DATA_CAP);
+
+	const uint8_t ae_state = ANDROID_CONTROL_AE_STATE_CONVERGED;
+	ret = add_camera_metadata_entry(resultMetadata, ANDROID_CONTROL_AE_STATE,
+					&ae_state, 1);
+	METADATA_ASSERT(ret);
+
+	const uint8_t ae_lock = ANDROID_CONTROL_AE_LOCK_OFF;
+	ret = add_camera_metadata_entry(resultMetadata, ANDROID_CONTROL_AE_LOCK,
+					&ae_lock, 1);
+	METADATA_ASSERT(ret);
+
+	uint8_t af_state = ANDROID_CONTROL_AF_STATE_INACTIVE;
+	ret = add_camera_metadata_entry(resultMetadata, ANDROID_CONTROL_AF_STATE,
+					&af_state, 1);
+	METADATA_ASSERT(ret);
+
+	const uint8_t awb_state = ANDROID_CONTROL_AWB_STATE_CONVERGED;
+	ret = add_camera_metadata_entry(resultMetadata,
+					ANDROID_CONTROL_AWB_STATE,
+					&awb_state, 1);
+	METADATA_ASSERT(ret);
+
+	const uint8_t awb_lock = ANDROID_CONTROL_AWB_LOCK_OFF;
+	ret = add_camera_metadata_entry(resultMetadata,
+					ANDROID_CONTROL_AWB_LOCK,
+					&awb_lock, 1);
+	METADATA_ASSERT(ret);
+
+	const uint8_t lens_state = ANDROID_LENS_STATE_STATIONARY;
+	ret = add_camera_metadata_entry(resultMetadata,
+					ANDROID_LENS_STATE,
+					&lens_state, 1);
+	METADATA_ASSERT(ret);
+
+	int32_t sensorSizes[] = {
+		0, 0, 2560, 1920,
+	};
+	ret = add_camera_metadata_entry(resultMetadata,
+					ANDROID_SCALER_CROP_REGION,
+					sensorSizes, 4);
+	METADATA_ASSERT(ret);
+
+	ret = add_camera_metadata_entry(resultMetadata,
+					ANDROID_SENSOR_TIMESTAMP,
+					&timestamp, 1);
+	METADATA_ASSERT(ret);
+
+	/* 33.3 msec */
+	const int64_t rolling_shutter_skew = 33300000;
+	ret = add_camera_metadata_entry(resultMetadata,
+					ANDROID_SENSOR_ROLLING_SHUTTER_SKEW,
+					&rolling_shutter_skew, 1);
+	METADATA_ASSERT(ret);
+
+	/* 16.6 msec */
+	const int64_t exposure_time = 16600000;
+	ret = add_camera_metadata_entry(resultMetadata,
+					ANDROID_SENSOR_EXPOSURE_TIME,
+					&exposure_time, 1);
+	METADATA_ASSERT(ret);
+
+	const uint8_t lens_shading_map_mode =
+				ANDROID_STATISTICS_LENS_SHADING_MAP_MODE_OFF;
+	ret = add_camera_metadata_entry(resultMetadata,
+					ANDROID_STATISTICS_LENS_SHADING_MAP_MODE,
+					&lens_shading_map_mode, 1);
+	METADATA_ASSERT(ret);
+
+	const uint8_t scene_flicker = ANDROID_STATISTICS_SCENE_FLICKER_NONE;
+	ret = add_camera_metadata_entry(resultMetadata,
+					ANDROID_STATISTICS_SCENE_FLICKER,
+					&scene_flicker, 1);
+	METADATA_ASSERT(ret);
+
+	return resultMetadata;
+}
diff --git a/src/android/camera_device.h b/src/android/camera_device.h
new file mode 100644
index 000000000000..ac5b95c95104
--- /dev/null
+++ b/src/android/camera_device.h
@@ -0,0 +1,71 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2019, Google Inc.
+ *
+ * camera_device.h - libcamera Android Camera Device
+ */
+#ifndef __ANDROID_CAMERA_DEVICE_H__
+#define __ANDROID_CAMERA_DEVICE_H__
+
+#include <memory>
+
+#include <hardware/camera3.h>
+
+#include <libcamera/buffer.h>
+#include <libcamera/camera.h>
+#include <libcamera/object.h>
+#include <libcamera/request.h>
+#include <libcamera/stream.h>
+
+#include "message.h"
+
+#define METADATA_ASSERT(_r)		\
+	do {				\
+		if (!(_r)) break;	\
+		LOG(HAL, Error) << "Error: " << __func__ << ":" << __LINE__; \
+		return nullptr;		\
+	} while(0);
+
+class CameraDevice : public libcamera::Object
+{
+public:
+	CameraDevice(unsigned int id, std::shared_ptr<libcamera::Camera> &camera);
+	~CameraDevice();
+
+	void message(libcamera::Message *message);
+
+	int open();
+	void close();
+	void setCallbacks(const camera3_callback_ops_t *callbacks);
+	camera_metadata_t *getStaticMetadata();
+	const camera_metadata_t *constructDefaultRequestSettings(int type);
+	int configureStreams(camera3_stream_configuration_t *stream_list);
+	int processCaptureRequest(camera3_capture_request_t *request);
+	void requestComplete(libcamera::Request *request,
+			     const std::map<libcamera::Stream *, libcamera::Buffer *> &buffers);
+
+private:
+	struct Camera3RequestDescriptor {
+		Camera3RequestDescriptor(unsigned int frameNumber,
+					 unsigned int numBuffers);
+		~Camera3RequestDescriptor();
+
+		uint32_t frameNumber;
+		uint32_t numBuffers;
+		camera3_stream_buffer_t *buffers;
+	};
+
+	void notifyShutter(uint32_t frameNumber, uint64_t timestamp);
+	void notifyError(uint32_t frameNumber, camera3_stream_t *stream);
+	camera_metadata_t *getResultMetadata(int frame_number, int64_t timestamp);
+
+	bool running_;
+	std::shared_ptr<libcamera::Camera> camera_;
+	std::unique_ptr<libcamera::CameraConfiguration> config_;
+
+	camera_metadata_t *staticMetadata_;
+	camera_metadata_t *requestTemplate_;
+	const camera3_callback_ops_t *callbacks_;
+};
+
+#endif /* __ANDROID_CAMERA_DEVICE_H__ */
diff --git a/src/android/camera_hal_manager.cpp b/src/android/camera_hal_manager.cpp
new file mode 100644
index 000000000000..08c759dfd8a1
--- /dev/null
+++ b/src/android/camera_hal_manager.cpp
@@ -0,0 +1,138 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2019, Google Inc.
+ *
+ * camera_hal_manager.cpp - libcamera Android Camera Manager
+ */
+
+#include "camera_hal_manager.h"
+
+#include <libcamera/camera.h>
+
+#include "log.h"
+
+#include "camera_device.h"
+#include "camera_proxy.h"
+
+using namespace libcamera;
+
+LOG_DECLARE_CATEGORY(HAL);
+
+/*
+ * \class CameraHalManager
+ *
+ * The HAL camera manager is initializated at camera_module_t 'hal_init()' time
+ * and spawns its own thread where libcamera related events are dispatched to.
+ * It wraps the libcamera CameraManager operations and provides helpers for the
+ * camera_module_t operations, to retrieve the number of cameras in the system,
+ * their static information and to open and close camera devices.
+ */
+
+int CameraHalManager::init()
+{
+	/*
+	 * Start the camera HAL manager thread and wait until its
+	 * initialisation completes to be fully operational before
+	 * receiving calls from the camera stack.
+	 */
+	start();
+
+	MutexLocker locker(mutex_);
+	cv_.wait(locker);
+
+	return 0;
+}
+
+void CameraHalManager::run()
+{
+	/*
+	 * All the libcamera components must be initialised here, in
+	 * order to bind them to the camera HAL manager thread that
+	 * executes the event dispatcher.
+	 */
+	cameraManager_ = libcamera::CameraManager::instance();
+
+	int ret = cameraManager_->start();
+	if (ret) {
+		LOG(HAL, Error) << "Failed to start camera manager: "
+				<< strerror(-ret);
+		return;
+	}
+
+	/*
+	 * For each Camera registered in the system, a CameraProxy
+	 * gets created here to wraps a camera device.
+	 *
+	 * \todo Support camera hotplug.
+	 */
+	unsigned int index = 0;
+	for (auto &camera : cameraManager_->cameras()) {
+		CameraProxy *proxy = new CameraProxy(index, camera);
+		proxies_.emplace_back(proxy);
+
+		++index;
+	}
+
+	/*
+	 * libcamera has been initialized. Unlock the init() caller
+	 * as we're now ready to handle calls from the framework.
+	 */
+	cv_.notify_one();
+
+	/* Now start processing events and messages. */
+	exec();
+}
+
+CameraProxy *CameraHalManager::open(unsigned int id,
+				    const hw_module_t *hardwareModule)
+{
+	if (id < 0 || id >= numCameras()) {
+		LOG(HAL, Error) << "Invalid camera id '" << id << "'";
+		return nullptr;
+	}
+
+	CameraProxy *proxy = proxies_[id].get();
+	if (proxy->open(hardwareModule))
+		return nullptr;
+
+	LOG(HAL, Info) << "Open camera '" << id << "'";
+
+	return proxy;
+}
+
+int CameraHalManager::close(CameraProxy *proxy)
+{
+	proxy->close();
+	LOG(HAL, Info) << "Close camera '" << proxy->id() << "'";
+
+	return 0;
+}
+
+unsigned int CameraHalManager::numCameras() const
+{
+	return cameraManager_->cameras().size();
+}
+
+int CameraHalManager::getCameraInfo(int id, struct camera_info *info)
+{
+	if (!info)
+		return -EINVAL;
+
+	if (id >= numCameras() || id < 0) {
+		LOG(HAL, Error) << "Invalid camera id '" << id << "'";
+		return -EINVAL;
+	}
+
+	CameraProxy *proxy = proxies_[id].get();
+
+	/* \todo Get these info dynamically inspecting the camera module. */
+	info->facing = id ? CAMERA_FACING_FRONT : CAMERA_FACING_BACK;
+	info->orientation = 0;
+	info->device_version = 0;
+	info->resource_cost = 0;
+	info->static_camera_characteristics = proxy->getStaticMetadata();
+	info->conflicting_devices = nullptr;
+	info->conflicting_devices_length = 0;
+
+	return 0;
+}
diff --git a/src/android/camera_hal_manager.h b/src/android/camera_hal_manager.h
new file mode 100644
index 000000000000..8004aaf660f5
--- /dev/null
+++ b/src/android/camera_hal_manager.h
@@ -0,0 +1,47 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2019, Google Inc.
+ *
+ * camera_hal_manager.h - libcamera Android Camera Manager
+ */
+#ifndef __ANDROID_CAMERA_MANAGER_H__
+#define __ANDROID_CAMERA_MANAGER_H__
+
+#include <condition_variable>
+#include <mutex>
+#include <vector>
+
+#include <hardware/hardware.h>
+#include <system/camera_metadata.h>
+
+#include <libcamera/camera_manager.h>
+
+#include "thread.h"
+
+class CameraDevice;
+class CameraProxy;
+
+class CameraHalManager : public libcamera::Thread
+{
+public:
+	int init();
+
+	CameraProxy *open(unsigned int id, const hw_module_t *module);
+	int close(CameraProxy *proxy);
+
+	unsigned int numCameras() const;
+	int getCameraInfo(int id, struct camera_info *info);
+
+private:
+	void run() override;
+	camera_metadata_t *getStaticMetadata(unsigned int id);
+
+	libcamera::CameraManager *cameraManager_;
+
+	std::mutex mutex_;
+	std::condition_variable cv_;
+
+	std::vector<std::unique_ptr<CameraProxy>> proxies_;
+};
+
+#endif /* __ANDROID_CAMERA_MANAGER_H__ */
diff --git a/src/android/camera_proxy.cpp b/src/android/camera_proxy.cpp
new file mode 100644
index 000000000000..f0cacac8025b
--- /dev/null
+++ b/src/android/camera_proxy.cpp
@@ -0,0 +1,194 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2019, Google Inc.
+ *
+ * camera_proxy.cpp - Proxy to camera devices
+ */
+
+#include "camera_proxy.h"
+
+#include <system/camera_metadata.h>
+
+#include "log.h"
+#include "message.h"
+#include "utils.h"
+
+#include "camera_device.h"
+#include "thread_rpc.h"
+
+using namespace libcamera;
+
+LOG_DECLARE_CATEGORY(HAL);
+
+/*
+ * \class CameraProxy
+ *
+ * The CameraProxy wraps a CameraDevice and implements the camera3_device_t
+ * API, bridging calls received from the camera framework to the CameraDevice.
+ *
+ * Bridging operation calls between the framework and the CameraDevice is
+ * required as the two run in two different threads and certain operations,
+ * such as queueing a new capture request to the camera, shall be called in
+ * the thread that dispatches events. Other operations do not require any
+ * bridging and resolve to direct function calls on the CameraDevice instance
+ * instead.
+ */
+
+static int hal_dev_initialize(const struct camera3_device *dev,
+			      const camera3_callback_ops_t *callback_ops)
+{
+	if (!dev)
+		return -EINVAL;
+
+	CameraProxy *proxy = reinterpret_cast<CameraProxy *>(dev->priv);
+	proxy->initialize(callback_ops);
+
+	return 0;
+}
+
+static int hal_dev_configure_streams(const struct camera3_device *dev,
+				     camera3_stream_configuration_t *stream_list)
+{
+	if (!dev)
+		return -EINVAL;
+
+	CameraProxy *proxy = reinterpret_cast<CameraProxy *>(dev->priv);
+	return proxy->configureStreams(stream_list);
+}
+
+static const camera_metadata_t *
+hal_dev_construct_default_request_settings(const struct camera3_device *dev,
+					   int type)
+{
+	if (!dev)
+		return nullptr;
+
+	CameraProxy *proxy = reinterpret_cast<CameraProxy *>(dev->priv);
+	return proxy->constructDefaultRequestSettings(type);
+}
+
+static int hal_dev_process_capture_request(const struct camera3_device *dev,
+					   camera3_capture_request_t *request)
+{
+	if (!dev)
+		return -EINVAL;
+
+	CameraProxy *proxy = reinterpret_cast<CameraProxy *>(dev->priv);
+	return proxy->processCaptureRequest(request);
+}
+
+static void hal_dev_dump(const struct camera3_device *dev, int fd)
+{
+}
+
+static int hal_dev_flush(const struct camera3_device *dev)
+{
+	return 0;
+}
+
+static int hal_dev_close(hw_device_t *hw_device)
+{
+	if (!hw_device)
+		return -EINVAL;
+
+	camera3_device_t *dev = reinterpret_cast<camera3_device_t *>(hw_device);
+	CameraProxy *proxy = reinterpret_cast<CameraProxy *>(dev->priv);
+
+	proxy->close();
+
+	return 0;
+}
+
+static camera3_device_ops hal_dev_ops = {
+	.initialize = hal_dev_initialize,
+	.configure_streams = hal_dev_configure_streams,
+	.register_stream_buffers = nullptr,
+	.construct_default_request_settings = hal_dev_construct_default_request_settings,
+	.process_capture_request = hal_dev_process_capture_request,
+	.get_metadata_vendor_tag_ops = nullptr,
+	.dump = hal_dev_dump,
+	.flush = hal_dev_flush,
+	.reserved = { nullptr },
+};
+
+CameraProxy::CameraProxy(unsigned int id, std::shared_ptr<Camera> camera)
+	: id_(id)
+{
+	cameraDevice_ = new CameraDevice(id, camera);
+}
+
+CameraProxy::~CameraProxy()
+{
+	delete cameraDevice_;
+}
+
+int CameraProxy::open(const hw_module_t *hardwareModule)
+{
+	int ret = cameraDevice_->open();
+	if (ret)
+		return ret;
+
+	/* Initialize the hw_device_t in the instance camera3_module_t. */
+	camera3Device_.common.tag = HARDWARE_DEVICE_TAG;
+	camera3Device_.common.version = CAMERA_DEVICE_API_VERSION_3_3;
+	camera3Device_.common.module = (hw_module_t *)hardwareModule;
+	camera3Device_.common.close = hal_dev_close;
+
+	/*
+	 * The camera device operations. These actually implement
+	 * the Android Camera HALv3 interface.
+	 */
+	camera3Device_.ops = &hal_dev_ops;
+	camera3Device_.priv = this;
+
+	return 0;
+}
+
+void CameraProxy::close()
+{
+	ThreadRpc rpcRequest;
+	rpcRequest.tag = ThreadRpc::Close;
+
+	threadRpcCall(rpcRequest);
+}
+
+void CameraProxy::initialize(const camera3_callback_ops_t *callbacks)
+{
+	cameraDevice_->setCallbacks(callbacks);
+}
+
+const camera_metadata_t *CameraProxy::getStaticMetadata()
+{
+	return cameraDevice_->getStaticMetadata();
+}
+
+const camera_metadata_t *CameraProxy::constructDefaultRequestSettings(int type)
+{
+	return cameraDevice_->constructDefaultRequestSettings(type);
+}
+
+int CameraProxy::configureStreams(camera3_stream_configuration_t *stream_list)
+{
+	return cameraDevice_->configureStreams(stream_list);
+}
+
+int CameraProxy::processCaptureRequest(camera3_capture_request_t *request)
+{
+	ThreadRpc rpcRequest;
+	rpcRequest.tag = ThreadRpc::ProcessCaptureRequest;
+	rpcRequest.request = request;
+
+	threadRpcCall(rpcRequest);
+
+	return 0;
+}
+
+void CameraProxy::threadRpcCall(ThreadRpc &rpcRequest)
+{
+	std::unique_ptr<ThreadRpcMessage> message =
+				utils::make_unique<ThreadRpcMessage>();
+	message->rpc = &rpcRequest;
+
+	cameraDevice_->postMessage(std::move(message));
+	rpcRequest.waitDelivery();
+}
diff --git a/src/android/camera_proxy.h b/src/android/camera_proxy.h
new file mode 100644
index 000000000000..da63bfa79fc9
--- /dev/null
+++ b/src/android/camera_proxy.h
@@ -0,0 +1,45 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2019, Google Inc.
+ *
+ * camera_proxy.h - Proxy to camera devices
+ */
+#ifndef __ANDROID_CAMERA_PROXY_H__
+#define __ANDROID_CAMERA_PROXY_H__
+
+#include <memory>
+
+#include <hardware/camera3.h>
+
+#include <libcamera/camera.h>
+
+class CameraDevice;
+class ThreadRpc;
+
+class CameraProxy
+{
+public:
+	CameraProxy(unsigned int id, std::shared_ptr<libcamera::Camera> camera);
+	~CameraProxy();
+
+	int open(const hw_module_t *hardwareModule);
+	void close();
+
+	void initialize(const camera3_callback_ops_t *callbacks);
+	const camera_metadata_t *getStaticMetadata();
+	const camera_metadata_t *constructDefaultRequestSettings(int type);
+	int configureStreams(camera3_stream_configuration_t *stream_list);
+	int processCaptureRequest(camera3_capture_request_t *request);
+
+	unsigned int id() const { return id_; }
+	camera3_device_t *camera3Device() { return &camera3Device_; }
+
+private:
+	void threadRpcCall(ThreadRpc &rpcRequest);
+
+	unsigned int id_;
+	CameraDevice *cameraDevice_;
+	camera3_device_t camera3Device_;
+};
+
+#endif /* __ANDROID_CAMERA_PROXY_H__ */
diff --git a/src/android/meson.build b/src/android/meson.build
index 1f242953db37..26537794bc29 100644
--- a/src/android/meson.build
+++ b/src/android/meson.build
@@ -1,3 +1,11 @@
+android_hal_sources = files([
+    'camera3_hal.cpp',
+    'camera_hal_manager.cpp',
+    'camera_device.cpp',
+    'camera_proxy.cpp',
+    'thread_rpc.cpp'
+])
+
 android_camera_metadata_sources = files([
     'metadata/camera_metadata.c',
 ])
diff --git a/src/android/thread_rpc.cpp b/src/android/thread_rpc.cpp
new file mode 100644
index 000000000000..295a05d7c676
--- /dev/null
+++ b/src/android/thread_rpc.cpp
@@ -0,0 +1,42 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2019, Google Inc.
+ *
+ * thread_rpc.cpp - Inter-thread procedure call
+ */
+
+#include "thread_rpc.h"
+
+#include "message.h"
+
+using namespace libcamera;
+
+libcamera::Message::Type ThreadRpcMessage::rpcType_ = Message::Type::None;
+
+ThreadRpcMessage::ThreadRpcMessage()
+	: Message(type())
+{
+}
+
+void ThreadRpc::notifyReception()
+{
+	{
+		libcamera::MutexLocker locker(mutex_);
+		delivered_ = true;
+	}
+	cv_.notify_one();
+}
+
+void ThreadRpc::waitDelivery()
+{
+	libcamera::MutexLocker locker(mutex_);
+	cv_.wait(locker, [&] { return delivered_; });
+}
+
+Message::Type ThreadRpcMessage::type()
+{
+	if (ThreadRpcMessage::rpcType_ == Message::Type::None)
+		rpcType_ = Message::registerMessageType();
+
+	return rpcType_;
+}
diff --git a/src/android/thread_rpc.h b/src/android/thread_rpc.h
new file mode 100644
index 000000000000..6d8992839d0b
--- /dev/null
+++ b/src/android/thread_rpc.h
@@ -0,0 +1,54 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2019, Google Inc.
+ *
+ * thread_rpc.h - Inter-thread procedure call
+ */
+#ifndef __ANDROID_THREAD_RPC_H__
+#define __ANDROID_THREAD_RPC_H__
+
+#include <condition_variable>
+#include <mutex>
+
+#include <hardware/camera3.h>
+
+#include "message.h"
+#include "thread.h"
+
+class ThreadRpc
+{
+public:
+	enum RpcTag {
+		ProcessCaptureRequest,
+		Close,
+	};
+
+	ThreadRpc()
+		: delivered_(false) {}
+
+	void notifyReception();
+	void waitDelivery();
+
+	RpcTag tag;
+
+	camera3_capture_request_t *request;
+
+private:
+	bool delivered_;
+	std::mutex mutex_;
+	std::condition_variable cv_;
+};
+
+class ThreadRpcMessage : public libcamera::Message
+{
+public:
+	ThreadRpcMessage();
+	ThreadRpc *rpc;
+
+	static Message::Type type();
+
+private:
+	static libcamera::Message::Type rpcType_;
+};
+
+#endif /* __ANDROID_THREAD_RPC_H__ */
diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build
index a09b23d60022..7d5d3c04fba0 100644
--- a/src/libcamera/meson.build
+++ b/src/libcamera/meson.build
@@ -103,9 +103,18 @@ libcamera_deps = [
     dependency('threads'),
 ]
 
+libcamera_link_with = []
+
+if get_option('android')
+    libcamera_sources += android_hal_sources
+    includes += android_includes
+    libcamera_link_with += android_camera_metadata
+endif
+
 libcamera = shared_library('camera',
                            libcamera_sources,
                            install : true,
+                           link_with : libcamera_link_with,
                            include_directories : includes,
                            dependencies : libcamera_deps)
 
diff --git a/src/meson.build b/src/meson.build
index 7148baee3eda..67ad20aab86b 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -1,4 +1,7 @@
-subdir('android')
+if get_option('android')
+    subdir('android')
+endif
+
 subdir('libcamera')
 subdir('ipa')
 subdir('cam')
-- 
2.22.0



More information about the libcamera-devel mailing list