{"id":1737,"url":"https://patchwork.libcamera.org/api/1.1/patches/1737/?format=json","web_url":"https://patchwork.libcamera.org/patch/1737/","project":{"id":1,"url":"https://patchwork.libcamera.org/api/1.1/projects/1/?format=json","name":"libcamera","link_name":"libcamera","list_id":"libcamera_core","list_email":"libcamera-devel@lists.libcamera.org","web_url":"","scm_url":"","webscm_url":""},"msgid":"<20190806195518.16739-7-jacopo@jmondi.org>","date":"2019-08-06T19:55:18","name":"[libcamera-devel,v2,6/6] android: hal: Add Camera3 HAL","commit_ref":null,"pull_url":null,"state":"superseded","archived":false,"hash":"803e69c501626408e62e32755a2ea514b21e96ff","submitter":{"id":3,"url":"https://patchwork.libcamera.org/api/1.1/people/3/?format=json","name":"Jacopo Mondi","email":"jacopo@jmondi.org"},"delegate":null,"mbox":"https://patchwork.libcamera.org/patch/1737/mbox/","series":[{"id":446,"url":"https://patchwork.libcamera.org/api/1.1/series/446/?format=json","web_url":"https://patchwork.libcamera.org/project/libcamera/list/?series=446","date":"2019-08-06T19:55:12","name":"android: Add initial Camera HAL implementation","version":2,"mbox":"https://patchwork.libcamera.org/series/446/mbox/"}],"comments":"https://patchwork.libcamera.org/api/patches/1737/comments/","check":"pending","checks":"https://patchwork.libcamera.org/api/patches/1737/checks/","tags":{},"headers":{"Return-Path":"<jacopo@jmondi.org>","Received":["from relay5-d.mail.gandi.net (relay5-d.mail.gandi.net\n\t[217.70.183.197])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 74AF06161B\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tTue,  6 Aug 2019 21:54:26 +0200 (CEST)","from uno.homenet.telecomitalia.it\n\t(host150-24-dynamic.51-79-r.retail.telecomitalia.it [79.51.24.150])\n\t(Authenticated sender: jacopo@jmondi.org)\n\tby relay5-d.mail.gandi.net (Postfix) with ESMTPSA id 421F41C0005;\n\tTue,  6 Aug 2019 19:54:23 +0000 (UTC)"],"X-Originating-IP":"79.51.24.150","From":"Jacopo Mondi <jacopo@jmondi.org>","To":"libcamera-devel@lists.libcamera.org","Date":"Tue,  6 Aug 2019 21:55:18 +0200","Message-Id":"<20190806195518.16739-7-jacopo@jmondi.org>","X-Mailer":"git-send-email 2.22.0","In-Reply-To":"<20190806195518.16739-1-jacopo@jmondi.org>","References":"<20190806195518.16739-1-jacopo@jmondi.org>","MIME-Version":"1.0","Content-Transfer-Encoding":"8bit","Subject":"[libcamera-devel] [PATCH v2 6/6] android: hal: Add Camera3 HAL","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.23","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","X-List-Received-Date":"Tue, 06 Aug 2019 19:54:26 -0000"},"content":"Add libcamera Android Camera HALv3 implementation.\n\nThe initial camera HAL implementation supports the LIMITED hardware\nlevel and uses statically defined metadata and camera characteristics.\n\nAdd a build option named 'android' and adjust the build system to\nselectively compile the Android camera HAL and link it against the\nrequired Android libraries.\n\nSigned-off-by: Jacopo Mondi <jacopo@jmondi.org>\n---\n meson_options.txt                  |   5 +\n src/android/camera3_hal.cpp        | 130 +++++\n src/android/camera_hal_manager.cpp | 173 +++++++\n src/android/camera_hal_manager.h   |  56 ++\n src/android/camera_module.cpp      | 799 +++++++++++++++++++++++++++++\n src/android/camera_module.h        |  75 +++\n src/android/camera_proxy.cpp       | 181 +++++++\n src/android/camera_proxy.h         |  41 ++\n src/android/meson.build            |   8 +\n src/android/thread_rpc.cpp         |  41 ++\n src/android/thread_rpc.h           |  56 ++\n src/libcamera/meson.build          |   9 +\n src/meson.build                    |   5 +-\n 13 files changed, 1578 insertions(+), 1 deletion(-)\n create mode 100644 src/android/camera3_hal.cpp\n create mode 100644 src/android/camera_hal_manager.cpp\n create mode 100644 src/android/camera_hal_manager.h\n create mode 100644 src/android/camera_module.cpp\n create mode 100644 src/android/camera_module.h\n create mode 100644 src/android/camera_proxy.cpp\n create mode 100644 src/android/camera_proxy.h\n create mode 100644 src/android/thread_rpc.cpp\n create mode 100644 src/android/thread_rpc.h","diff":"diff --git a/meson_options.txt b/meson_options.txt\nindex 97efc85b4412..2d78b8d91f9c 100644\n--- a/meson_options.txt\n+++ b/meson_options.txt\n@@ -1,3 +1,8 @@\n+option('android',\n+        type : 'boolean',\n+        value : false,\n+        description : 'Compile libcamera with Android Camera3 HAL interface')\n+\n option('documentation',\n         type : 'boolean',\n         description : 'Generate the project documentation')\ndiff --git a/src/android/camera3_hal.cpp b/src/android/camera3_hal.cpp\nnew file mode 100644\nindex 000000000000..0a97a9333d20\n--- /dev/null\n+++ b/src/android/camera3_hal.cpp\n@@ -0,0 +1,130 @@\n+/* SPDX-License-Identifier: GPL-2.0-or-later */\n+/*\n+ * Copyright (C) 2019, Google Inc.\n+ *\n+ * camera3_hal.cpp - Android Camera3 HAL module\n+ */\n+\n+#include <iostream>\n+#include <memory>\n+#include <vector>\n+\n+#include <hardware/hardware.h>\n+#include <system/camera_metadata.h>\n+\n+#include <libcamera/libcamera.h>\n+\n+#include \"log.h\"\n+\n+#include \"camera_hal_manager.h\"\n+#include \"camera_module.h\"\n+#include \"camera_proxy.h\"\n+\n+using namespace libcamera;\n+\n+LOG_DEFINE_CATEGORY(HAL)\n+\n+static CameraHalManager cameraManager;\n+\n+/*------------------------------------------------------------------------------\n+ * Android Camera HAL callbacks\n+ */\n+\n+static int hal_get_number_of_cameras(void)\n+{\n+\treturn cameraManager.numCameras();\n+}\n+\n+static int hal_get_camera_info(int id, struct camera_info *info)\n+{\n+\treturn cameraManager.getCameraInfo(id, info);\n+}\n+\n+static int hal_set_callbacks(const camera_module_callbacks_t *callbacks)\n+{\n+\treturn 0;\n+}\n+\n+static int hal_open_legacy(const struct hw_module_t *module, const char *id,\n+\t\t\t   uint32_t halVersion, struct hw_device_t **device)\n+{\n+\treturn -ENOSYS;\n+}\n+\n+static int hal_set_torch_mode(const char *camera_id, bool enabled)\n+{\n+\treturn -ENOSYS;\n+}\n+\n+/*\n+ * First entry point of the camera HAL module.\n+ *\n+ * Initialize the HAL but does not open any camera device yet (see hal_dev_open)\n+ */\n+static int hal_init()\n+{\n+\tLOG(HAL, Info) << \"Initialising Android camera HAL\";\n+\n+\tcameraManager.initialize();\n+\n+\treturn 0;\n+}\n+\n+/*------------------------------------------------------------------------------\n+ * Android Camera Device\n+ */\n+\n+static int hal_dev_close(hw_device_t *hw_device)\n+{\n+\tif (!hw_device)\n+\t\treturn -EINVAL;\n+\n+\tcamera3_device_t *dev = reinterpret_cast<camera3_device_t *>(hw_device);\n+\tCameraProxy *cameraModule = reinterpret_cast<CameraProxy *>(dev->priv);\n+\n+\treturn cameraManager.close(cameraModule);\n+}\n+\n+static int hal_dev_open(const hw_module_t* module, const char* name,\n+\t\t\thw_device_t** device)\n+{\n+\tLOG(HAL, Debug) << \"Open camera: \" << name;\n+\n+\tint id = atoi(name);\n+\tCameraProxy *proxy = cameraManager.open(id, module);\n+\tif (!proxy) {\n+\t\tLOG(HAL, Error) << \"Failed to open camera module \" << id;\n+\t\treturn -ENODEV;\n+\t}\n+\tproxy->device()->common.close = hal_dev_close;\n+\t*device = &proxy->device()->common;\n+\n+\treturn 0;\n+}\n+\n+static struct hw_module_methods_t hal_module_methods = {\n+\t.open = hal_dev_open,\n+};\n+\n+camera_module_t HAL_MODULE_INFO_SYM = {\n+\t.common = {\n+\t\t.tag = HARDWARE_MODULE_TAG,\n+\t\t.module_api_version = CAMERA_MODULE_API_VERSION_2_4,\n+\t\t.hal_api_version = HARDWARE_HAL_API_VERSION,\n+\t\t.id = CAMERA_HARDWARE_MODULE_ID,\n+\t\t.name = \"Libcamera Camera3HAL Module\",\n+\t\t.author = \"libcamera\",\n+\t\t.methods = &hal_module_methods,\n+\t\t.dso = nullptr,\n+\t\t.reserved = {},\n+\t},\n+\n+\t.get_number_of_cameras = hal_get_number_of_cameras,\n+\t.get_camera_info = hal_get_camera_info,\n+\t.set_callbacks = hal_set_callbacks,\n+\t.get_vendor_tag_ops = nullptr,\n+\t.open_legacy = hal_open_legacy,\n+\t.set_torch_mode = hal_set_torch_mode,\n+\t.init = hal_init,\n+\t.reserved = {},\n+};\ndiff --git a/src/android/camera_hal_manager.cpp b/src/android/camera_hal_manager.cpp\nnew file mode 100644\nindex 000000000000..734b5eebd9a5\n--- /dev/null\n+++ b/src/android/camera_hal_manager.cpp\n@@ -0,0 +1,173 @@\n+/* SPDX-License-Identifier: LGPL-2.1-or-later */\n+/*\n+ * Copyright (C) 2019, Google Inc.\n+ *\n+ * camera_hal_manager.cpp - Libcamera Android Camera Manager\n+ */\n+\n+#include \"camera_hal_manager.h\"\n+\n+#include <map>\n+\n+#include <hardware/hardware.h>\n+#include <system/camera_metadata.h>\n+\n+#include <libcamera/libcamera.h>\n+#include <libcamera/camera.h>\n+\n+#include \"log.h\"\n+\n+#include \"camera_module.h\"\n+#include \"camera_proxy.h\"\n+\n+using namespace libcamera;\n+LOG_DECLARE_CATEGORY(HAL);\n+\n+CameraHalManager::~CameraHalManager()\n+{\n+\tfor (auto metadata : staticMetadataMap_)\n+\t\tfree_camera_metadata(metadata.second);\n+}\n+\n+int CameraHalManager::initialize()\n+{\n+\t/*\n+\t * Thread::start() will create a std::thread which will execute\n+\t * CameraHalManager::run()\n+\t */\n+\tstart();\n+\n+\t/*\n+\t * Wait for run() to notify us before returning to the caller to\n+\t * make sure libcamera is fully initialized before we start handling\n+\t * calls from the camera stack.\n+\t */\n+\tMutexLocker locker(mutex_);\n+\tcv_.wait(locker);\n+\n+\treturn 0;\n+}\n+\n+void CameraHalManager::run()\n+{\n+\t/*\n+\t * The run() operation is scheduled in its own thread;\n+\t * It exec() to run the even dispatcher loop and initialize libcamera.\n+\t *\n+\t * All the libcamera components initialized from here will be tied to\n+\t * the same thread as the here below run event dispatcher.\n+\t */\n+\tlibcameraManager_ = libcamera::CameraManager::instance();\n+\n+\tint ret = libcameraManager_->start();\n+\tif (ret) {\n+\t\tLOG(HAL, Error) << \"Failed to start camera manager: \"\n+\t\t\t\t<< strerror(-ret);\n+\t\treturn;\n+\t}\n+\n+\t/*\n+\t * For each Camera registered in the system, a CameraModule\n+\t * get created here with an associated Camera and a proxy.\n+\t *\n+\t * \\todo Support camera hotplug.\n+\t */\n+\tunsigned int cameraIndex = 0;\n+\tfor (auto camera : libcameraManager_->cameras()) {\n+\t\tcameras_.push_back(camera);\n+\n+\t\tCameraModule *module = new CameraModule(cameraIndex,\n+\t\t\t\t\t\t\tcamera.get());\n+\t\tmodules_.emplace_back(module);\n+\n+\t\tCameraProxy *proxy = new CameraProxy(cameraIndex,\n+\t\t\t\t\t\t     module);\n+\t\tproxies_.emplace_back(proxy);\n+\n+\t\t++cameraIndex;\n+\t}\n+\n+\t/*\n+\t * libcamera has been initialized! Unlock the initialize() caller\n+\t * as we're now ready to handle calls from the framework.\n+\t */\n+\tcv_.notify_one();\n+\n+\t/* Now start processing events and messages! */\n+\texec();\n+}\n+\n+CameraProxy *CameraHalManager::open(unsigned int id,\n+\t\t\t\t    const hw_module_t *hardwareModule)\n+{\n+\tif (id < 0 || id >= numCameras()) {\n+\t\tLOG(HAL, Error) << \"Invalid camera id '\" << id << \"'\";\n+\t\treturn nullptr;\n+\t}\n+\n+\tCameraProxy *proxy = proxies_[id].get();\n+\tif (proxy->open(hardwareModule))\n+\t\treturn proxy;\n+\n+\tLOG(HAL, Info) << \"Camera: '\" << id << \"' opened\";\n+\n+\treturn proxy;\n+}\n+\n+int CameraHalManager::close(CameraProxy *proxy)\n+{\n+\tproxy->close();\n+\tLOG(HAL, Info) << \"Close camera: \" << proxy->id();\n+\n+\treturn 0;\n+}\n+\n+unsigned int CameraHalManager::numCameras() const\n+{\n+\treturn libcameraManager_->cameras().size();\n+}\n+\n+/*\n+ * Before the camera framework even tries to open a camera device with\n+ * hal_dev_open() it requires the camera HAL to report a list of static\n+ * informations on the camera device with id \\a id in the hal_get_camera_info()\n+ * method.\n+ *\n+ * The static metadata should be then generated probably from a\n+ * platform-specific module, as we cannot operate on the camera at this time as\n+ * it has not yet been open by the framework.\n+ *\n+ * This is what the Intel XML file is used for, it is parsed and the data there\n+ * combined with informations from the PSL service (which I -think- it's what\n+ * our IPA is) to generate a list of static metadata per-camera device.\n+ */\n+int CameraHalManager::getCameraInfo(int id, struct camera_info *info)\n+{\n+\tint cameras = numCameras();\n+\tif (id >= cameras || id < 0 || !info) {\n+\t\tLOG(HAL, Error) << \"Invalid camera id: \" << id;\n+\t\treturn -EINVAL;\n+\t}\n+\n+\tcamera_metadata_t *staticMetadata;\n+\tauto it = staticMetadataMap_.find(id);\n+\tif (it != staticMetadataMap_.end()) {\n+\t\tstaticMetadata = it->second;\n+\t} else {\n+\t\tCameraModule *cameraModule = modules_[id].get();\n+\n+\t\tstaticMetadata = cameraModule->getStaticMetadata();\n+\t\tstaticMetadataMap_[id] = staticMetadata;\n+\t}\n+\n+\t/* \\todo: Get these info dynamically inspecting the camera module. */\n+\tinfo->facing = id ? CAMERA_FACING_FRONT : CAMERA_FACING_BACK;\n+\tinfo->orientation = 0;\n+\tinfo->device_version = 0;\n+\tinfo->resource_cost = 0;\n+\tinfo->static_camera_characteristics = staticMetadata;\n+\tinfo->conflicting_devices = nullptr;\n+\tinfo->conflicting_devices_length = 0;\n+\n+\treturn 0;\n+}\ndiff --git a/src/android/camera_hal_manager.h b/src/android/camera_hal_manager.h\nnew file mode 100644\nindex 000000000000..65d6fa3150ef\n--- /dev/null\n+++ b/src/android/camera_hal_manager.h\n@@ -0,0 +1,56 @@\n+/* SPDX-License-Identifier: LGPL-2.1-or-later */\n+/*\n+ * Copyright (C) 2019, Google Inc.\n+ *\n+ * camera_hal_manager.h - Libcamera Android Camera Manager\n+ */\n+#ifndef __ANDROID_CAMERA_MANAGER_H__\n+#define __ANDROID_CAMERA_MANAGER_H__\n+\n+#include <condition_variable>\n+#include <cstddef>\n+#include <map>\n+#include <memory>\n+#include <vector>\n+\n+#include <hardware/hardware.h>\n+#include <system/camera_metadata.h>\n+\n+#include <libcamera/libcamera.h>\n+\n+#include \"thread.h\"\n+#include \"thread_rpc.h\"\n+\n+class CameraModule;\n+class CameraProxy;\n+\n+class CameraHalManager : public libcamera::Thread\n+{\n+public:\n+\t~CameraHalManager();\n+\n+\tint initialize();\n+\n+\tCameraProxy *open(unsigned int id, const hw_module_t *module);\n+\tint close(CameraProxy *proxy);\n+\n+\tunsigned int numCameras() const;\n+\tint getCameraInfo(int id, struct camera_info *info);\n+\n+private:\n+\tvoid run() override;\n+\tcamera_metadata_t *getStaticMetadata(unsigned int id);\n+\n+\tlibcamera::CameraManager *libcameraManager_;\n+\n+\tstd::mutex mutex_;\n+\tstd::condition_variable cv_;\n+\n+\tstd::vector<std::shared_ptr<libcamera::Camera>> cameras_;\n+\tstd::vector<std::unique_ptr<CameraModule>> modules_;\n+\tstd::vector<std::unique_ptr<CameraProxy>> proxies_;\n+\n+\tstd::map<unsigned int, camera_metadata_t *> staticMetadataMap_;\n+};\n+\n+#endif /* __ANDROID_CAMERA_MANAGER_H__ */\ndiff --git a/src/android/camera_module.cpp b/src/android/camera_module.cpp\nnew file mode 100644\nindex 000000000000..b64e377fb439\n--- /dev/null\n+++ b/src/android/camera_module.cpp\n@@ -0,0 +1,799 @@\n+/* SPDX-License-Identifier: LGPL-2.1-or-later */\n+/*\n+ * Copyright (C) 2019, Google Inc.\n+ *\n+ * camera_module.cpp - Libcamera Android Camera Module\n+ */\n+\n+#include \"camera_module.h\"\n+\n+#include <iostream>\n+#include <memory>\n+\n+#include <system/camera_metadata.h>\n+\n+#include <hardware/camera3.h>\n+#include <libcamera/libcamera.h>\n+\n+#include \"message.h\"\n+\n+#include \"log.h\"\n+\n+using namespace libcamera;\n+\n+LOG_DECLARE_CATEGORY(HAL);\n+\n+CameraModule::CameraModule(unsigned int id, libcamera::Camera *camera)\n+\t: id_(id), camera_(camera), cameraState_(CameraStopped),\n+\t  requestTemplate_(nullptr)\n+{\n+\tcamera_->requestCompleted.connect(this,\n+\t\t\t\t\t  &CameraModule::requestComplete);\n+}\n+\n+CameraModule::~CameraModule()\n+{\n+\tif (requestTemplate_)\n+\t\tfree_camera_metadata(requestTemplate_);\n+\trequestTemplate_ = nullptr;\n+}\n+\n+void CameraModule::message(Message *message)\n+{\n+\tThreadRpcMessage *rpcMessage = static_cast<ThreadRpcMessage *>\n+\t\t\t\t       (message);\n+\n+\tif (rpcMessage->type() != ThreadRpcMessage::type())\n+\t\treturn Object::message(message);\n+\n+\tThreadRpc *rpc = rpcMessage->rpc;\n+\n+\tswitch (rpc->tag) {\n+\tcase ThreadRpc::ProcessCaptureRequest:\n+\t\tprocessCaptureRequest(rpc->request);\n+\t\tbreak;\n+\tcase ThreadRpc::Close:\n+\t\tclose();\n+\t\tbreak;\n+\tdefault:\n+\t\tLOG(HAL, Error) << \"Unknown RPC operation: \" << rpc->tag;\n+\t}\n+\n+\trpc->notifyReception();\n+}\n+\n+int CameraModule::open()\n+{\n+\tint ret = camera_->acquire();\n+\tif (ret) {\n+\t\tLOG(HAL, Error) << \"Failed to acquire the camera\";\n+\t\treturn ret;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+void CameraModule::close()\n+{\n+\tcamera_->stop();\n+\n+\tcamera_->freeBuffers();\n+\tcamera_->release();\n+\n+\tcameraState_ = CameraStopped;\n+}\n+\n+void CameraModule::setCallbacks(const struct camera3_device *camera3Device,\n+\t\t\t\tconst camera3_callback_ops_t *callbacks)\n+{\n+\tcamera3Device_ = camera3Device;\n+\tcallbacks_ = callbacks;\n+}\n+\n+camera_metadata_t *CameraModule::getStaticMetadata()\n+{\n+\tint ret;\n+\n+\t/*\n+\t * The below metadata has been added by inspecting the Intel XML\n+\t * file and it's associated parsing routines in the Intel IPU3 HAL.\n+\t *\n+\t * The here below metadata are enough to satisfy the camera3-test\n+\t * provided by ChromeOS, but a real camera implementation might require\n+\t * more.\n+\t *\n+\t * See CameraConf::AiqConf class implementation in the Intel HAL\n+\t * to get a list of the static metadata registered by parsing the\n+\t * XML config file in AiqConf::fillStaticMetadataFromCMC\n+\t */\n+\n+\t/*\n+\t * FIXME: this sizes come from the Intel IPU3 HAL, and are way too large for\n+\t * this intial HAL version.\n+\t */\n+\t#define STATIC_ENTRY_CAP 256\n+\t#define STATIC_DATA_CAP 6688\n+\tcamera_metadata_t *staticMetadata =\n+\t\tallocate_camera_metadata(STATIC_ENTRY_CAP, STATIC_DATA_CAP);\n+\n+\t/* Sensor static metadata. */\n+\tint32_t pixelArraySize[] = {\n+\t\t2592, 1944,\n+\t};\n+\tret = add_camera_metadata_entry(staticMetadata,\n+\t\t\t\tANDROID_SENSOR_INFO_PIXEL_ARRAY_SIZE,\n+\t\t\t\t&pixelArraySize, 2);\n+\tMETADATA_ASSERT(ret);\n+\n+\tint32_t sensorSizes[] = {\n+\t\t0, 0, 2560, 1920,\n+\t};\n+\tret = add_camera_metadata_entry(staticMetadata,\n+\t\t\t\tANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE,\n+\t\t\t\t&sensorSizes, 4);\n+\tMETADATA_ASSERT(ret);\n+\n+\tint32_t sensitivityRange[] = {\n+\t\t32, 2400,\n+\t};\n+\tret = add_camera_metadata_entry(staticMetadata,\n+\t\t\t\tANDROID_SENSOR_INFO_SENSITIVITY_RANGE,\n+\t\t\t\t&sensitivityRange, 2);\n+\tMETADATA_ASSERT(ret);\n+\n+\tuint16_t filterArr = ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_GRBG;\n+\tret = add_camera_metadata_entry(staticMetadata,\n+\t\t\t\tANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT,\n+\t\t\t\t&filterArr, 1);\n+\tMETADATA_ASSERT(ret);\n+\n+\tint64_t exposureTimeRange[] = {\n+\t\t100000, 200000000,\n+\t};\n+\tret = add_camera_metadata_entry(staticMetadata,\n+\t\t\t\tANDROID_SENSOR_INFO_EXPOSURE_TIME_RANGE,\n+\t\t\t\t&exposureTimeRange, 2);\n+\tMETADATA_ASSERT(ret);\n+\n+\tint32_t orientation = 0;\n+\tret = add_camera_metadata_entry(staticMetadata,\n+\t\t\t\tANDROID_SENSOR_ORIENTATION,\n+\t\t\t\t&orientation, 1);\n+\tMETADATA_ASSERT(ret);\n+\n+\t/* Flash static metadata. */\n+\tchar flashAvailable = ANDROID_FLASH_INFO_AVAILABLE_FALSE;\n+\tret = add_camera_metadata_entry(staticMetadata,\n+\t\t\tANDROID_FLASH_INFO_AVAILABLE,\n+\t\t\t&flashAvailable, 1);\n+\tMETADATA_ASSERT(ret);\n+\n+\t/* Lens static metadata. */\n+\tfloat fn = 2.53 / 100;\n+\tret = add_camera_metadata_entry(staticMetadata,\n+\t\t\t\tANDROID_LENS_INFO_AVAILABLE_APERTURES, &fn, 1);\n+\tMETADATA_ASSERT(ret);\n+\n+\t/* Control metadata. */\n+\tchar controlMetadata = ANDROID_CONTROL_MODE_AUTO;\n+\tret = add_camera_metadata_entry(staticMetadata,\n+\t\t\tANDROID_CONTROL_AVAILABLE_MODES,\n+\t\t\t&controlMetadata, 1);\n+\tMETADATA_ASSERT(ret);\n+\n+\tchar availableAntiBandingModes[] = {\n+\t\tANDROID_CONTROL_AE_ANTIBANDING_MODE_OFF,\n+\t\tANDROID_CONTROL_AE_ANTIBANDING_MODE_50HZ,\n+\t\tANDROID_CONTROL_AE_ANTIBANDING_MODE_60HZ,\n+\t\tANDROID_CONTROL_AE_ANTIBANDING_MODE_AUTO,\n+\t};\n+\tret = add_camera_metadata_entry(staticMetadata,\n+\t\t\tANDROID_CONTROL_AE_AVAILABLE_ANTIBANDING_MODES,\n+\t\t\tavailableAntiBandingModes, 4);\n+\tMETADATA_ASSERT(ret);\n+\n+\tchar aeAvailableModes[] = {\n+\t\tANDROID_CONTROL_AE_MODE_ON,\n+\t\tANDROID_CONTROL_AE_MODE_OFF,\n+\t};\n+\tret = add_camera_metadata_entry(staticMetadata,\n+\t\t\tANDROID_CONTROL_AE_AVAILABLE_MODES,\n+\t\t\taeAvailableModes, 2);\n+\tMETADATA_ASSERT(ret);\n+\n+\tcontrolMetadata = ANDROID_CONTROL_AE_LOCK_AVAILABLE_TRUE;\n+\tret = add_camera_metadata_entry(staticMetadata,\n+\t\t\tANDROID_CONTROL_AE_LOCK_AVAILABLE,\n+\t\t\t&controlMetadata, 1);\n+\tMETADATA_ASSERT(ret);\n+\n+\tuint8_t awbLockAvailable = ANDROID_CONTROL_AWB_LOCK_AVAILABLE_FALSE;\n+\tret = add_camera_metadata_entry(staticMetadata,\n+\t\t\tANDROID_CONTROL_AWB_LOCK_AVAILABLE,\n+\t\t\t&awbLockAvailable, 1);\n+\n+\t/* Scaler static metadata. */\n+\tstd::vector<uint32_t> availableStreamFormats = {\n+\t\tANDROID_SCALER_AVAILABLE_FORMATS_BLOB,\n+\t\tANDROID_SCALER_AVAILABLE_FORMATS_YCbCr_420_888,\n+\t\tANDROID_SCALER_AVAILABLE_FORMATS_IMPLEMENTATION_DEFINED,\n+\t};\n+\tret = add_camera_metadata_entry(staticMetadata,\n+\t\t\tANDROID_SCALER_AVAILABLE_FORMATS,\n+\t\t\tavailableStreamFormats.data(),\n+\t\t\tavailableStreamFormats.size());\n+\tMETADATA_ASSERT(ret);\n+\n+\tstd::vector<uint32_t> availableStreamConfigurations = {\n+\t\tANDROID_SCALER_AVAILABLE_FORMATS_BLOB, 2560, 1920,\n+\t\tANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT,\n+\t\tANDROID_SCALER_AVAILABLE_FORMATS_YCbCr_420_888, 2560, 1920,\n+\t\tANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT,\n+\t\tANDROID_SCALER_AVAILABLE_FORMATS_IMPLEMENTATION_DEFINED, 2560, 1920,\n+\t\tANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT,\n+\t};\n+\tret = add_camera_metadata_entry(staticMetadata,\n+\t\t\tANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS,\n+\t\t\tavailableStreamConfigurations.data(),\n+\t\t\tavailableStreamConfigurations.size());\n+\tMETADATA_ASSERT(ret);\n+\n+\tstd::vector<int64_t> availableStallDurations = {\n+\t\tANDROID_SCALER_AVAILABLE_FORMATS_BLOB, 2560, 1920, 33333333,\n+\t};\n+\tret = add_camera_metadata_entry(staticMetadata,\n+\t\t\tANDROID_SCALER_AVAILABLE_STALL_DURATIONS,\n+\t\t\tavailableStallDurations.data(),\n+\t\t\tavailableStallDurations.size());\n+\tMETADATA_ASSERT(ret);\n+\n+\tstd::vector<int64_t> minFrameDurations = {\n+\t\tANDROID_SCALER_AVAILABLE_FORMATS_BLOB, 2560, 1920, 33333333,\n+\t\tANDROID_SCALER_AVAILABLE_FORMATS_IMPLEMENTATION_DEFINED, 2560, 1920, 33333333,\n+\t\tANDROID_SCALER_AVAILABLE_FORMATS_YCbCr_420_888, 2560, 1920, 33333333,\n+\t};\n+\tret = add_camera_metadata_entry(staticMetadata,\n+\t\t\tANDROID_SCALER_AVAILABLE_MIN_FRAME_DURATIONS,\n+\t\t\tminFrameDurations.data(), minFrameDurations.size());\n+\tMETADATA_ASSERT(ret);\n+\n+\t/* Info static metadata. */\n+\tuint8_t supportedHWLevel = ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED;\n+\tret = add_camera_metadata_entry(staticMetadata,\n+\t\t\tANDROID_INFO_SUPPORTED_HARDWARE_LEVEL,\n+\t\t\t&supportedHWLevel, 1);\n+\n+\treturn staticMetadata;\n+}\n+\n+const camera_metadata_t *CameraModule::constructDefaultRequestMetadata(int type)\n+{\n+\tint ret;\n+\n+\t/*\n+\t * TODO: inspect type and pick the right metadata pack.\n+\t * As of now just use a single one for all templates\n+\t */\n+\tuint8_t captureIntent;\n+\tswitch (type) {\n+\tcase CAMERA3_TEMPLATE_PREVIEW:\n+\t\tcaptureIntent = ANDROID_CONTROL_CAPTURE_INTENT_PREVIEW;\n+\t\tbreak;\n+\tcase CAMERA3_TEMPLATE_STILL_CAPTURE:\n+\t\tcaptureIntent = ANDROID_CONTROL_CAPTURE_INTENT_STILL_CAPTURE;\n+\t\tbreak;\n+\tcase CAMERA3_TEMPLATE_VIDEO_RECORD:\n+\t\tcaptureIntent = ANDROID_CONTROL_CAPTURE_INTENT_VIDEO_RECORD;\n+\t\tbreak;\n+\tcase CAMERA3_TEMPLATE_VIDEO_SNAPSHOT:\n+\t\tcaptureIntent = ANDROID_CONTROL_CAPTURE_INTENT_VIDEO_SNAPSHOT;\n+\t\tbreak;\n+\tcase CAMERA3_TEMPLATE_ZERO_SHUTTER_LAG:\n+\t\tcaptureIntent = ANDROID_CONTROL_CAPTURE_INTENT_ZERO_SHUTTER_LAG;\n+\t\tbreak;\n+\tcase CAMERA3_TEMPLATE_MANUAL:\n+\t\tcaptureIntent = ANDROID_CONTROL_CAPTURE_INTENT_MANUAL;\n+\t\tbreak;\n+\tdefault:\n+\t\tLOG(HAL, Error) << \"Invalid template request type: \" << type;\n+\t\treturn nullptr;\n+\t}\n+\n+\tif (requestTemplate_)\n+\t\treturn requestTemplate_;\n+\n+\t/* FIXME: this sizes are quite arbitrary ones */\n+\t#define REQUEST_TEMPLATE_ENTRIES\t  30\n+\t#define REQUEST_TEMPLATE_DATA\t\t2048\n+\trequestTemplate_ = allocate_camera_metadata(REQUEST_TEMPLATE_ENTRIES,\n+\t\t\t\t\t\t    REQUEST_TEMPLATE_DATA);\n+\tif (!requestTemplate_) {\n+\t\tLOG(HAL, Error) << \"Failed to allocate template metadata\";\n+\t\treturn nullptr;\n+\t}\n+\n+\t/*\n+\t * Fill in the required Request metadata.\n+\t *\n+\t * Part (most?) of this entries comes from inspecting the Intel's IPU3\n+\t * HAL xml configuration file.\n+\t */\n+\n+\t/* Set to 0 the number of 'processed and stalling' streams (ie JPEG). */\n+\tint32_t maxOutStream[] = { 0, 2, 0 };\n+\tret = add_camera_metadata_entry(requestTemplate_,\n+\t\t\tANDROID_REQUEST_MAX_NUM_OUTPUT_STREAMS,\n+\t\t\tmaxOutStream, 3);\n+\tMETADATA_ASSERT(ret);\n+\n+\tuint8_t maxPipelineDepth = 5;\n+\tret = add_camera_metadata_entry(requestTemplate_,\n+\t\t\tANDROID_REQUEST_PIPELINE_MAX_DEPTH,\n+\t\t\t&maxPipelineDepth, 1);\n+\tMETADATA_ASSERT(ret);\n+\n+\tint32_t inputStreams = 0;\n+\tret = add_camera_metadata_entry(requestTemplate_,\n+\t\t\tANDROID_REQUEST_MAX_NUM_INPUT_STREAMS,\n+\t\t\t&inputStreams, 1);\n+\tMETADATA_ASSERT(ret);\n+\n+\tint32_t partialResultCount = 1;\n+\tret = add_camera_metadata_entry(requestTemplate_,\n+\t\t\tANDROID_REQUEST_PARTIAL_RESULT_COUNT,\n+\t\t\t&partialResultCount, 1);\n+\tMETADATA_ASSERT(ret);\n+\n+\tuint8_t availableCapabilities[] = {\n+\t\tANDROID_REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE,\n+\t};\n+\tret = add_camera_metadata_entry(requestTemplate_,\n+\t\t\tANDROID_REQUEST_AVAILABLE_CAPABILITIES,\n+\t\t\tavailableCapabilities, 1);\n+\tMETADATA_ASSERT(ret);\n+\n+\tuint8_t aeMode = ANDROID_CONTROL_AE_MODE_ON;\n+\tret = add_camera_metadata_entry(requestTemplate_,\n+\t\t\tANDROID_CONTROL_AE_MODE,\n+\t\t\t&aeMode, 1);\n+\tMETADATA_ASSERT(ret);\n+\n+\tint32_t aeExposureCompensation = 0;\n+\tret = add_camera_metadata_entry(requestTemplate_,\n+\t\t\tANDROID_CONTROL_AE_EXPOSURE_COMPENSATION,\n+\t\t\t&aeExposureCompensation, 1);\n+\tMETADATA_ASSERT(ret);\n+\n+\tuint8_t aePrecaptureTrigger = ANDROID_CONTROL_AE_PRECAPTURE_TRIGGER_IDLE;\n+\tret = add_camera_metadata_entry(requestTemplate_,\n+\t\t\tANDROID_CONTROL_AE_PRECAPTURE_TRIGGER,\n+\t\t\t&aePrecaptureTrigger, 1);\n+\tMETADATA_ASSERT(ret);\n+\n+\tuint8_t aeLock = ANDROID_CONTROL_AE_LOCK_OFF;\n+\tret = add_camera_metadata_entry(requestTemplate_,\n+\t\t\tANDROID_CONTROL_AE_LOCK,\n+\t\t\t&aeLock, 1);\n+\tMETADATA_ASSERT(ret);\n+\n+\tuint8_t afTrigger = ANDROID_CONTROL_AF_TRIGGER_IDLE;\n+\tret = add_camera_metadata_entry(requestTemplate_,\n+\t\t\tANDROID_CONTROL_AF_TRIGGER,\n+\t\t\t&afTrigger, 1);\n+\tMETADATA_ASSERT(ret);\n+\n+\tuint8_t awbMode = ANDROID_CONTROL_AWB_MODE_AUTO;\n+\tret = add_camera_metadata_entry(requestTemplate_,\n+\t\t\tANDROID_CONTROL_AWB_MODE,\n+\t\t\t&awbMode, 1);\n+\tMETADATA_ASSERT(ret);\n+\n+\tuint8_t awbLock = ANDROID_CONTROL_AWB_LOCK_OFF;\n+\tret = add_camera_metadata_entry(requestTemplate_,\n+\t\t\tANDROID_CONTROL_AWB_LOCK,\n+\t\t\t&awbLock, 1);\n+\tMETADATA_ASSERT(ret);\n+\n+\tuint8_t awbLockAvailable = ANDROID_CONTROL_AWB_LOCK_AVAILABLE_FALSE;\n+\tret = add_camera_metadata_entry(requestTemplate_,\n+\t\t\tANDROID_CONTROL_AWB_LOCK_AVAILABLE,\n+\t\t\t&awbLockAvailable, 1);\n+\tMETADATA_ASSERT(ret);\n+\n+\tuint8_t flashMode = ANDROID_FLASH_MODE_OFF;\n+\tret = add_camera_metadata_entry(requestTemplate_,\n+\t\t\tANDROID_FLASH_MODE,\n+\t\t\t&flashMode, 1);\n+\tMETADATA_ASSERT(ret);\n+\n+\tuint8_t faceDetectMode = ANDROID_STATISTICS_FACE_DETECT_MODE_OFF;\n+\tret = add_camera_metadata_entry(requestTemplate_,\n+\t\t\tANDROID_STATISTICS_FACE_DETECT_MODE,\n+\t\t\t&faceDetectMode, 1);\n+\tMETADATA_ASSERT(ret);\n+\n+\tret = add_camera_metadata_entry(requestTemplate_,\n+\t\t\tANDROID_CONTROL_CAPTURE_INTENT,\n+\t\t\t&captureIntent, 1);\n+\tMETADATA_ASSERT(ret);\n+\n+\t/*\n+\t * This is quite hard to list at the moment wihtout knowing what\n+\t * we could control.\n+\t *\n+\t * For now, just list in the available Request keys and in the available\n+\t * result keys the control and reporting of the AE algorithm.\n+\t */\n+\tstd::vector<int32_t> availableRequestKeys = {\n+\t\tANDROID_CONTROL_AE_MODE,\n+\t\tANDROID_CONTROL_AE_EXPOSURE_COMPENSATION,\n+\t\tANDROID_CONTROL_AE_PRECAPTURE_TRIGGER,\n+\t\tANDROID_CONTROL_AE_LOCK,\n+\t\tANDROID_CONTROL_AF_TRIGGER,\n+\t\tANDROID_CONTROL_AWB_MODE,\n+\t\tANDROID_CONTROL_AWB_LOCK,\n+\t\tANDROID_CONTROL_AWB_LOCK_AVAILABLE,\n+\t\tANDROID_CONTROL_CAPTURE_INTENT,\n+\t\tANDROID_FLASH_MODE,\n+\t\tANDROID_STATISTICS_FACE_DETECT_MODE,\n+\t};\n+\n+\tret = add_camera_metadata_entry(requestTemplate_,\n+\t\t\tANDROID_REQUEST_AVAILABLE_REQUEST_KEYS,\n+\t\t\tavailableRequestKeys.data(),\n+\t\t\tavailableRequestKeys.size());\n+\tMETADATA_ASSERT(ret);\n+\n+\tstd::vector<int32_t> availableResultKeys = {\n+\t\tANDROID_CONTROL_AE_MODE,\n+\t\tANDROID_CONTROL_AE_EXPOSURE_COMPENSATION,\n+\t\tANDROID_CONTROL_AE_PRECAPTURE_TRIGGER,\n+\t\tANDROID_CONTROL_AE_LOCK,\n+\t\tANDROID_CONTROL_AF_TRIGGER,\n+\t\tANDROID_CONTROL_AWB_MODE,\n+\t\tANDROID_CONTROL_AWB_LOCK,\n+\t\tANDROID_CONTROL_AWB_LOCK_AVAILABLE,\n+\t\tANDROID_CONTROL_CAPTURE_INTENT,\n+\t\tANDROID_FLASH_MODE,\n+\t\tANDROID_STATISTICS_FACE_DETECT_MODE,\n+\t};\n+\tret = add_camera_metadata_entry(requestTemplate_,\n+\t\t\tANDROID_REQUEST_AVAILABLE_RESULT_KEYS,\n+\t\t\tavailableResultKeys.data(),\n+\t\t\tavailableResultKeys.size());\n+\tMETADATA_ASSERT(ret);\n+\n+\t/*\n+\t * The available characteristics should be the tags reported\n+\t * as part of the static metadata reported at hal_get_camera_info()\n+\t * time. The xml file sets those to 0 though.\n+\t */\n+\tstd::vector<int32_t> availableCharacteristicsKeys = {};\n+\tret = add_camera_metadata_entry(requestTemplate_,\n+\t\t\tANDROID_REQUEST_AVAILABLE_CHARACTERISTICS_KEYS,\n+\t\t\tavailableCharacteristicsKeys.data(),\n+\t\t\tavailableCharacteristicsKeys.size());\n+\tMETADATA_ASSERT(ret);\n+\n+\treturn requestTemplate_;\n+}\n+\n+/**\n+ * Inspect the stream_list to produce a list of StreamConfiguration to\n+ * be use to configure the Camera.\n+ */\n+int CameraModule::configureStreams(camera3_stream_configuration_t *stream_list)\n+{\n+\n+\tfor (unsigned int i = 0; i < stream_list->num_streams; ++i) {\n+\t\tcamera3_stream_t *stream = stream_list->streams[i];\n+\n+\t\tLOG(HAL, Info) << \"Stream #\" << i\n+\t\t\t       << \": direction: \" << stream->stream_type\n+\t\t\t       << \" - width: \" << stream->width\n+\t\t\t       << \" - height: \" << stream->height\n+\t\t\t       << \" - format: \" << std::hex << stream->format;\n+\t}\n+\n+\t/* Hardcode viewfinder role, collecting sizes from the stream config. */\n+\tif (stream_list->num_streams != 1) {\n+\t\tLOG(HAL, Error) << \"Only one stream supported\";\n+\t\treturn -EINVAL;\n+\t}\n+\n+\tStreamRoles roles = {};\n+\troles.push_back(StreamRole::StillCapture);\n+\n+\tconfig_ = camera_->generateConfiguration(roles);\n+\tif (!config_ || config_->empty()) {\n+\t\tLOG(HAL, Error) << \"Failed to generate camera configuration\";\n+\t\treturn -EINVAL;\n+\t}\n+\n+\t/* Only one stream is supported. */\n+\tcamera3_stream_t *camera3Stream = stream_list->streams[0];\n+\tStreamConfiguration *streamConfiguration = &config_->at(0);\n+\tstreamConfiguration->size.width = camera3Stream->width;\n+\tstreamConfiguration->size.height = camera3Stream->height;\n+\tstreamConfiguration->memoryType = ExternalMemory;\n+\n+\t/*\n+\t * FIXME: do not change the pixel format from the one returned\n+\t * from the Camera::generateConfiguration();\n+\t *\n+\t * We'll need to translate from Android defined pixel format codes\n+\t * to whatever libcamera will use.\n+\t */\n+\n+\tswitch (config_->validate()) {\n+\tcase CameraConfiguration::Valid:\n+\t\tbreak;\n+\tcase CameraConfiguration::Adjusted:\n+\t\tLOG(HAL, Info) << \"Camera configuration adjusted\";\n+\t\treturn -EINVAL;\n+\tcase CameraConfiguration::Invalid:\n+\t\tLOG(HAL, Info) << \"Camera configuration invalid\";\n+\t\tconfig_.reset();\n+\t\treturn -EINVAL;\n+\t}\n+\n+\tcamera3Stream->max_buffers = streamConfiguration->bufferCount;\n+\n+\t/*\n+\t * Once the CameraConfiguration has been adjusted/validated\n+\t * it can be applied to the camera.\n+\t */\n+\tint ret = camera_->configure(config_.get());\n+\tif (ret) {\n+\t\tLOG(HAL, Error) << \"Failed to configure camera '\"\n+\t\t\t\t<< camera_->name() << \"'\";\n+\t\treturn ret;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+int CameraModule::processCaptureRequest(camera3_capture_request_t *camera3Request)\n+{\n+\tStreamConfiguration *streamConfiguration = &config_->at(0);\n+\tStream *stream = streamConfiguration->stream();\n+\n+\tif (camera3Request->num_output_buffers != 1) {\n+\t\tLOG(HAL, Error) << \"Invalid number of output buffers: \"\n+\t\t\t\t<< camera3Request->num_output_buffers;\n+\t\treturn -EINVAL;\n+\t}\n+\n+\t/* Start the camera if that's the first request we handle. */\n+\tif (cameraState_ == CameraStopped) {\n+\t\tint ret = camera_->allocateBuffers();\n+\t\tif (ret) {\n+\t\t\tLOG(HAL, Error) << \"Failed to allocate buffers\";\n+\t\t\treturn ret;\n+\t\t}\n+\n+\t\tret = camera_->start();\n+\t\tif (ret) {\n+\t\t\tLOG(HAL, Error) << \"Failed to start camera\";\n+\t\t\tcamera_->freeBuffers();\n+\t\t\treturn ret;\n+\t\t}\n+\n+\t\tcameraState_ = CameraRunning;\n+\t}\n+\n+\t/*\n+\t * Queue a request for the Camera with the provided dmabuf file\n+\t * descriptors.\n+\t */\n+\tconst camera3_stream_buffer_t *camera3Buffer =\n+\t\t\t\t\t&camera3Request->output_buffers[0];\n+\tconst buffer_handle_t camera3Handle = *camera3Buffer->buffer;\n+\n+\tstd::array<int, 3> fds = {\n+\t\tcamera3Handle->data[0],\n+\t\tcamera3Handle->data[1],\n+\t\tcamera3Handle->data[2],\n+\t};\n+\tstd::unique_ptr<Buffer> buffer = stream->createBuffer(fds);\n+\tif (!buffer) {\n+\t\tLOG(HAL, Error) << \"Failed to create buffer\";\n+\t\treturn -EINVAL;\n+\t}\n+\n+\tRequest *request = camera_->createRequest();\n+\trequest->addBuffer(std::move(buffer));\n+\tint ret = camera_->queueRequest(request);\n+\tif (ret) {\n+\t\tLOG(HAL, Error) << \"Failed to queue request\";\n+\t\treturn ret;\n+\t}\n+\n+\t/* Save the request descriptors for use at completion time. */\n+\tCamera3RequestDescriptor descriptor = {};\n+\tdescriptor.frameNumber = camera3Request->frame_number,\n+\tdescriptor.numBuffers = camera3Request->num_output_buffers,\n+\tdescriptor.buffers = new camera3_stream_buffer_t[descriptor.numBuffers];\n+\tfor (unsigned int i = 0; i < descriptor.numBuffers; ++i) {\n+\t\tcamera3_stream_buffer_t &buffer = descriptor.buffers[i];\n+\t\tbuffer = camera3Request->output_buffers[i];\n+\t}\n+\n+\trequestMap_[request] = descriptor;\n+\n+\treturn 0;\n+}\n+\n+void CameraModule::requestComplete(Request *request,\n+\t\t\t\t   const std::map<Stream *, Buffer *> &buffers)\n+{\n+\tBuffer *libcameraBuffer = buffers.begin()->second;\n+\tcamera3_buffer_status status = CAMERA3_BUFFER_STATUS_OK;\n+\tcamera_metadata_t *resultMetadata = nullptr;\n+\n+\tif (request->status() != Request::RequestComplete) {\n+\t\tLOG(HAL, Error) << \"Request not succesfully completed: \"\n+\t\t\t\t<< request->status();\n+\t\tstatus = CAMERA3_BUFFER_STATUS_ERROR;\n+\t}\n+\n+\t/* Prepare to call back the Android camera stack. */\n+\tCamera3RequestDescriptor &descriptor = requestMap_[request];\n+\n+\tcamera3_capture_result_t captureResult = {};\n+\tcaptureResult.frame_number = descriptor.frameNumber;\n+\tcaptureResult.num_output_buffers = descriptor.numBuffers;\n+\n+\tcamera3_stream_buffer_t *resultBuffers =\n+\t\tnew camera3_stream_buffer_t[descriptor.numBuffers];\n+\tfor (unsigned int i = 0; i < descriptor.numBuffers; ++i) {\n+\t\tcamera3_stream_buffer_t resultBuffer;\n+\n+\t\tresultBuffer = descriptor.buffers[i];\n+\t\tresultBuffer.acquire_fence = -1;\n+\t\tresultBuffer.release_fence = -1;\n+\t\tresultBuffer.status = status;\n+\n+\t\tresultBuffers[i] = resultBuffer;\n+\t}\n+\tcaptureResult.output_buffers =\n+\t\tconst_cast<const camera3_stream_buffer_t *>(resultBuffers);\n+\n+\tif (status == CAMERA3_BUFFER_STATUS_ERROR) {\n+\t\t/* \\todo Improve error handling. */\n+\t\tnotifyError(descriptor.frameNumber, resultBuffers->stream);\n+\t} else {\n+\t\tnotifyShutter(descriptor.frameNumber, libcameraBuffer->timestamp());\n+\n+\t\tcaptureResult.partial_result = 1;\n+\t\tresultMetadata = getResultMetadata(descriptor.frameNumber,\n+\t\t\t\t\t\t   libcameraBuffer->timestamp());\n+\t\tcaptureResult.result = resultMetadata;\n+\t}\n+\n+\tcallbacks_->process_capture_result(callbacks_, &captureResult);\n+\n+\tif (resultMetadata)\n+\t\tfree_camera_metadata(resultMetadata);\n+\n+\tdelete[] resultBuffers;\n+\tdelete[] descriptor.buffers;\n+\trequestMap_.erase(request);\n+\n+\treturn;\n+}\n+\n+void CameraModule::notifyShutter(uint32_t frameNumber, uint64_t timestamp)\n+{\n+\tcamera3_notify_msg_t notify = {};\n+\n+\tnotify.type = CAMERA3_MSG_SHUTTER;\n+\tnotify.message.shutter.frame_number = frameNumber;\n+\tnotify.message.shutter.timestamp = timestamp;\n+\n+\tcallbacks_->notify(callbacks_, &notify);\n+}\n+\n+void CameraModule::notifyError(uint32_t frameNumber, camera3_stream_t *stream)\n+{\n+\tcamera3_notify_msg_t notify = {};\n+\n+\tnotify.type = CAMERA3_MSG_ERROR;\n+\tnotify.message.error.error_stream = stream;\n+\tnotify.message.error.frame_number = frameNumber;\n+\tnotify.message.error.error_code = CAMERA3_MSG_ERROR_REQUEST;\n+\n+\tcallbacks_->notify(callbacks_, &notify);\n+}\n+\n+/*\n+ * Fixed result metadata, mostly imported from the UVC camera HAL, which\n+ * does not produces metadata and thus needs to generate a fixed set.\n+ */\n+camera_metadata_t *CameraModule::getResultMetadata(int frame_number,\n+\t\t\t\t\t\t   int64_t timestamp)\n+{\n+\tint ret;\n+\n+\t/* FIXME: random \"big enough\" values. */\n+\t#define RESULT_ENTRY_CAP 256\n+\t#define RESULT_DATA_CAP 6688\n+\tcamera_metadata_t *resultMetadata =\n+\t\tallocate_camera_metadata(STATIC_ENTRY_CAP, STATIC_DATA_CAP);\n+\n+\tconst uint8_t ae_state = ANDROID_CONTROL_AE_STATE_CONVERGED;\n+\tret = add_camera_metadata_entry(resultMetadata, ANDROID_CONTROL_AE_STATE,\n+\t\t\t\t\t&ae_state, 1);\n+\tMETADATA_ASSERT(ret);\n+\n+\tconst uint8_t ae_lock = ANDROID_CONTROL_AE_LOCK_OFF;\n+\tret = add_camera_metadata_entry(resultMetadata, ANDROID_CONTROL_AE_LOCK,\n+\t\t\t\t\t&ae_lock, 1);\n+\tMETADATA_ASSERT(ret);\n+\n+\tuint8_t af_state = ANDROID_CONTROL_AF_STATE_INACTIVE;\n+\tret = add_camera_metadata_entry(resultMetadata, ANDROID_CONTROL_AF_STATE,\n+\t\t\t\t\t&af_state, 1);\n+\tMETADATA_ASSERT(ret);\n+\n+\tconst uint8_t awb_state = ANDROID_CONTROL_AWB_STATE_CONVERGED;\n+\tret = add_camera_metadata_entry(resultMetadata,\n+\t\t\t\t\tANDROID_CONTROL_AWB_STATE,\n+\t\t\t\t\t&awb_state, 1);\n+\tMETADATA_ASSERT(ret);\n+\n+\tconst uint8_t awb_lock = ANDROID_CONTROL_AWB_LOCK_OFF;\n+\tret = add_camera_metadata_entry(resultMetadata,\n+\t\t\t\t\tANDROID_CONTROL_AWB_LOCK,\n+\t\t\t\t\t&awb_lock, 1);\n+\tMETADATA_ASSERT(ret);\n+\n+\tconst uint8_t lens_state = ANDROID_LENS_STATE_STATIONARY;\n+\tret = add_camera_metadata_entry(resultMetadata,\n+\t\t\t\t\tANDROID_LENS_STATE,\n+\t\t\t\t\t&lens_state, 1);\n+\tMETADATA_ASSERT(ret);\n+\n+\tint32_t sensorSizes[] = {\n+\t\t0, 0, 2560, 1920,\n+\t};\n+\tret = add_camera_metadata_entry(resultMetadata,\n+\t\t\t\t\tANDROID_SCALER_CROP_REGION,\n+\t\t\t\t\tsensorSizes, 4);\n+\tMETADATA_ASSERT(ret);\n+\n+\tret = add_camera_metadata_entry(resultMetadata,\n+\t\t\t\t\tANDROID_SENSOR_TIMESTAMP,\n+\t\t\t\t\t&timestamp, 1);\n+\n+\tMETADATA_ASSERT(ret);\n+\n+\t/* 33.3 msec */\n+\tconst int64_t rolling_shutter_skew = 33300000;\n+\tret = add_camera_metadata_entry(resultMetadata,\n+\t\t\t\t\tANDROID_SENSOR_ROLLING_SHUTTER_SKEW,\n+\t\t\t\t\t&rolling_shutter_skew, 1);\n+\tMETADATA_ASSERT(ret);\n+\n+\t/* 16.6 msec */\n+\tconst int64_t exposure_time = 16600000;\n+\tret = add_camera_metadata_entry(resultMetadata,\n+\t\t\t\t\tANDROID_SENSOR_EXPOSURE_TIME,\n+\t\t\t\t\t&exposure_time, 1);\n+\tMETADATA_ASSERT(ret);\n+\n+\tconst uint8_t lens_shading_map_mode =\n+\t\t\t\tANDROID_STATISTICS_LENS_SHADING_MAP_MODE_OFF;\n+\tret = add_camera_metadata_entry(resultMetadata,\n+\t\t\t\t\tANDROID_STATISTICS_LENS_SHADING_MAP_MODE,\n+\t\t\t\t\t&lens_shading_map_mode, 1);\n+\tMETADATA_ASSERT(ret);\n+\n+\tconst uint8_t scene_flicker = ANDROID_STATISTICS_SCENE_FLICKER_NONE;\n+\tret = add_camera_metadata_entry(resultMetadata,\n+\t\t\t\t\tANDROID_STATISTICS_SCENE_FLICKER,\n+\t\t\t\t\t&scene_flicker, 1);\n+\tMETADATA_ASSERT(ret);\n+\n+\treturn resultMetadata;\n+}\ndiff --git a/src/android/camera_module.h b/src/android/camera_module.h\nnew file mode 100644\nindex 000000000000..67488d5940b1\n--- /dev/null\n+++ b/src/android/camera_module.h\n@@ -0,0 +1,75 @@\n+/* SPDX-License-Identifier: LGPL-2.1-or-later */\n+/*\n+ * Copyright (C) 2019, Google Inc.\n+ *\n+ * camera_module.h - Libcamera Android Camera Module\n+ */\n+#ifndef __ANDROID_CAMERA_MODULE_H__\n+#define __ANDROID_CAMERA_MODULE_H__\n+\n+#include <memory>\n+\n+#include <hardware/camera3.h>\n+#include <libcamera/libcamera.h>\n+\n+#include \"message.h\"\n+\n+#include \"camera_hal_manager.h\"\n+\n+#define METADATA_ASSERT(_r)\t\t\\\n+\tdo {\t\t\t\t\\\n+\t\tif (!(_r)) break;\t\\\n+\t\tLOG(HAL, Error) << \"Error: \" << __func__ << \":\" << __LINE__; \\\n+\t\treturn nullptr;\t\t\\\n+\t} while(0);\n+\n+class CameraModule : public libcamera::Object\n+{\n+public:\n+\tCameraModule(unsigned int id, libcamera::Camera *camera);\n+\t~CameraModule();\n+\n+\tvoid message(libcamera::Message *message);\n+\n+\tint open();\n+\tvoid close();\n+\tvoid setCallbacks(const struct camera3_device *cameraDevice,\n+\t\t\t  const camera3_callback_ops_t *callbacks);\n+\tcamera_metadata_t *getStaticMetadata();\n+\tconst camera_metadata_t *constructDefaultRequestMetadata(int type);\n+\tint configureStreams(camera3_stream_configuration_t *stream_list);\n+\tint processCaptureRequest(camera3_capture_request_t *request);\n+\tvoid requestComplete(libcamera::Request *request,\n+\t\t\t     const std::map<libcamera::Stream *, libcamera::Buffer *> &buffers);\n+\n+\tunsigned int id() const { return id_; }\n+\n+private:\n+\tstruct Camera3RequestDescriptor {\n+\t\tuint32_t frameNumber;\n+\t\tuint32_t numBuffers;\n+\t\tcamera3_stream_buffer_t *buffers;\n+\t};\n+\n+\tenum CameraState {\n+\t\tCameraStopped,\n+\t\tCameraRunning,\n+\t};\n+\n+\tvoid notifyShutter(uint32_t frameNumber, uint64_t timestamp);\n+\tvoid notifyError(uint32_t frameNumber, camera3_stream_t *stream);\n+\tcamera_metadata_t *getResultMetadata(int frame_number, int64_t timestamp);\n+\n+\tunsigned int id_;\n+\tlibcamera::Camera *camera_;\n+\tstd::unique_ptr<libcamera::CameraConfiguration> config_;\n+\tCameraState cameraState_;\n+\n+\tcamera_metadata_t *requestTemplate_;\n+\tconst camera3_callback_ops_t *callbacks_;\n+\tconst struct camera3_device *camera3Device_;\n+\n+\tstd::map<libcamera::Request *, Camera3RequestDescriptor> requestMap_;\n+};\n+\n+#endif /* __ANDROID_CAMERA_MODULE_H__ */\ndiff --git a/src/android/camera_proxy.cpp b/src/android/camera_proxy.cpp\nnew file mode 100644\nindex 000000000000..af7817f29137\n--- /dev/null\n+++ b/src/android/camera_proxy.cpp\n@@ -0,0 +1,181 @@\n+/* SPDX-License-Identifier: LGPL-2.1-or-later */\n+/*\n+ * Copyright (C) 2019, Google Inc.\n+ *\n+ * camera_proxy.cpp - Proxy to camera module instances\n+ */\n+\n+#include \"camera_proxy.h\"\n+\n+#include <hardware/camera3.h>\n+#include <libcamera/libcamera.h>\n+#include <system/camera_metadata.h>\n+\n+#include <memory>\n+\n+#include \"log.h\"\n+#include \"message.h\"\n+#include \"thread_rpc.h\"\n+#include \"utils.h\"\n+\n+using namespace libcamera;\n+\n+LOG_DECLARE_CATEGORY(HAL);\n+\n+#define LIBCAMERA_HAL_FUNC_DBG\t\\\n+\tLOG(HAL, Debug);\n+\n+static int hal_dev_initialize(const struct camera3_device *dev,\n+\t\t\t      const camera3_callback_ops_t *callback_ops)\n+{\n+\tLIBCAMERA_HAL_FUNC_DBG\n+\n+\tif (!dev)\n+\t\treturn -EINVAL;\n+\n+\tCameraProxy *proxy = reinterpret_cast<CameraProxy *>(dev->priv);\n+\tproxy->setCallbacks(callback_ops);\n+\n+\treturn 0;\n+}\n+\n+static int hal_dev_configure_streams(const struct camera3_device *dev,\n+\t\t\t\t     camera3_stream_configuration_t *stream_list)\n+{\n+\tLIBCAMERA_HAL_FUNC_DBG\n+\n+\tif (!dev)\n+\t\treturn -EINVAL;\n+\n+\tCameraProxy *proxy = reinterpret_cast<CameraProxy *>(dev->priv);\n+\tproxy->configureStreams(stream_list);\n+\n+\treturn 0;\n+}\n+\n+static const camera_metadata_t *\n+hal_dev_construct_default_request_settings(const struct camera3_device *dev,\n+\t\t\t\t\t   int type)\n+{\n+\tLIBCAMERA_HAL_FUNC_DBG\n+\n+\tif (!dev)\n+\t\treturn nullptr;\n+\n+\tCameraProxy *proxy = reinterpret_cast<CameraProxy *>(dev->priv);\n+\treturn proxy->constructDefaultRequest(type);\n+}\n+\n+static int hal_dev_process_capture_request(const struct camera3_device *dev,\n+\t\t\t\t\t   camera3_capture_request_t *request)\n+{\n+\tLIBCAMERA_HAL_FUNC_DBG\n+\n+\tif (!dev)\n+\t\treturn -EINVAL;\n+\n+\tCameraProxy *proxy = reinterpret_cast<CameraProxy *>(dev->priv);\n+\treturn proxy->processCaptureRequest(request);\n+}\n+\n+static void hal_dev_dump(const struct camera3_device *dev, int fd)\n+{\n+\tLIBCAMERA_HAL_FUNC_DBG\n+}\n+\n+static int hal_dev_flush(const struct camera3_device *dev)\n+{\n+\tLIBCAMERA_HAL_FUNC_DBG\n+\n+\treturn 0;\n+}\n+\n+static camera3_device_ops hal_dev_ops = {\n+\t.initialize = hal_dev_initialize,\n+\t.configure_streams = hal_dev_configure_streams,\n+\t.register_stream_buffers = nullptr,\n+\t.construct_default_request_settings = hal_dev_construct_default_request_settings,\n+\t.process_capture_request = hal_dev_process_capture_request,\n+\t.get_metadata_vendor_tag_ops = nullptr,\n+\t.dump = hal_dev_dump,\n+\t.flush = hal_dev_flush,\n+\t.reserved = { 0 },\n+};\n+\n+CameraProxy::CameraProxy(unsigned int id, CameraModule *cameraModule)\n+\t: open_(false), id_(id), cameraModule_(cameraModule)\n+{\n+}\n+\n+int CameraProxy::open(const hw_module_t *hardwareModule)\n+{\n+\tint ret = cameraModule_->open();\n+\tif (ret)\n+\t\treturn ret;\n+\n+\t/* Initialize the hw_device_t in the instance camera3_module_t. */\n+\tcameraDevice_.common.tag = HARDWARE_DEVICE_TAG;\n+\tcameraDevice_.common.version = CAMERA_DEVICE_API_VERSION_3_3;\n+\tcameraDevice_.common.module = (hw_module_t *)hardwareModule;\n+\n+\t/*\n+\t * The camera device operations. These actually implement\n+\t * the Android Camera HALv3 interface.\n+\t */\n+\tcameraDevice_.ops = &hal_dev_ops;\n+\tcameraDevice_.priv = this;\n+\n+\topen_ = true;\n+\n+\treturn 0;\n+}\n+\n+void CameraProxy::close()\n+{\n+\tLIBCAMERA_HAL_FUNC_DBG\n+\n+\tThreadRpc rpcRequest;\n+\trpcRequest.tag = ThreadRpc::Close;\n+\n+\tthreadRpcCall(rpcRequest);\n+\n+\topen_ = false;\n+}\n+\n+void CameraProxy::setCallbacks(const camera3_callback_ops_t *callbacks)\n+{\n+\tLIBCAMERA_HAL_FUNC_DBG\n+\n+\tcameraModule_->setCallbacks(&cameraDevice_, callbacks);\n+}\n+\n+const camera_metadata_t *CameraProxy::constructDefaultRequest(int type)\n+{\n+\treturn cameraModule_->constructDefaultRequestMetadata(type);\n+}\n+\n+int CameraProxy::configureStreams(camera3_stream_configuration_t *stream_list)\n+{\n+\treturn cameraModule_->configureStreams(stream_list);\n+}\n+\n+int CameraProxy::processCaptureRequest(camera3_capture_request_t *request)\n+{\n+\tThreadRpc rpcRequest;\n+\trpcRequest.tag = ThreadRpc::ProcessCaptureRequest;\n+\trpcRequest.request = request;\n+\n+\tthreadRpcCall(rpcRequest);\n+\n+\treturn 0;\n+}\n+\n+void CameraProxy::threadRpcCall(ThreadRpc &rpcRequest)\n+{\n+\tstd::unique_ptr<ThreadRpcMessage> message =\n+\t\t\t\tutils::make_unique<ThreadRpcMessage>();\n+\tmessage->rpc = &rpcRequest;\n+\n+\tcameraModule_->postMessage(std::move(message));\n+\trpcRequest.waitDelivery();\n+}\ndiff --git a/src/android/camera_proxy.h b/src/android/camera_proxy.h\nnew file mode 100644\nindex 000000000000..69e6878c4352\n--- /dev/null\n+++ b/src/android/camera_proxy.h\n@@ -0,0 +1,41 @@\n+/* SPDX-License-Identifier: LGPL-2.1-or-later */\n+/*\n+ * Copyright (C) 2019, Google Inc.\n+ *\n+ * camera_proxy.h - Proxy to camera module instances\n+ */\n+#ifndef __ANDROID_CAMERA_PROXY_H__\n+#define __ANDROID_CAMERA_PROXY_H__\n+\n+#include <hardware/camera3.h>\n+#include <libcamera/libcamera.h>\n+\n+#include \"camera_module.h\"\n+\n+class CameraProxy\n+{\n+public:\n+\tCameraProxy(unsigned int id, CameraModule *cameraModule);\n+\n+\tint open(const hw_module_t *hardwareModule);\n+\tvoid close();\n+\n+\tvoid setCallbacks(const camera3_callback_ops_t *callbacks);\n+\tconst camera_metadata_t *constructDefaultRequest(int type);\n+\tint configureStreams(camera3_stream_configuration_t *stream_list);\n+\tint processCaptureRequest(camera3_capture_request_t *request);\n+\n+\tbool isOpen() const { return open_; }\n+\tunsigned int id() const { return id_; }\n+\tcamera3_device_t *device() { return &cameraDevice_; }\n+\n+private:\n+\tvoid threadRpcCall(ThreadRpc &rpcRequest);\n+\n+\tbool open_;\n+\tunsigned int id_;\n+\tCameraModule *cameraModule_;\n+\tcamera3_device_t cameraDevice_;\n+};\n+\n+#endif /* __ANDROID_CAMERA_PROXY_H__ */\ndiff --git a/src/android/meson.build b/src/android/meson.build\nindex 1f242953db37..63b45832c0d2 100644\n--- a/src/android/meson.build\n+++ b/src/android/meson.build\n@@ -1,3 +1,11 @@\n+android_hal_sources = files([\n+    'camera3_hal.cpp',\n+    'camera_hal_manager.cpp',\n+    'camera_module.cpp',\n+    'camera_proxy.cpp',\n+    'thread_rpc.cpp'\n+])\n+\n android_camera_metadata_sources = files([\n     'metadata/camera_metadata.c',\n ])\ndiff --git a/src/android/thread_rpc.cpp b/src/android/thread_rpc.cpp\nnew file mode 100644\nindex 000000000000..f13db59d0d75\n--- /dev/null\n+++ b/src/android/thread_rpc.cpp\n@@ -0,0 +1,41 @@\n+/* SPDX-License-Identifier: LGPL-2.1-or-later */\n+/*\n+ * Copyright (C) 2019, Google Inc.\n+ *\n+ * thread_call.cpp - Inter-thread procedure call\n+ */\n+\n+#include \"message.h\"\n+#include \"thread_rpc.h\"\n+\n+using namespace libcamera;\n+\n+libcamera::Message::Type ThreadRpcMessage::rpcType_ = Message::Type::None;\n+\n+ThreadRpcMessage::ThreadRpcMessage()\n+\t: Message(type())\n+{\n+}\n+\n+void ThreadRpc::notifyReception()\n+{\n+\t{\n+\t\tlibcamera::MutexLocker locker(mutex_);\n+\t\tdelivered_ = true;\n+\t}\n+\tcv_.notify_one();\n+}\n+\n+void ThreadRpc::waitDelivery()\n+{\n+\tlibcamera::MutexLocker locker(mutex_);\n+\tcv_.wait(locker, [&]{ return delivered_; });\n+}\n+\n+Message::Type ThreadRpcMessage::type()\n+{\n+\tif (ThreadRpcMessage::rpcType_ == Message::Type::None)\n+\t\trpcType_ = Message::registerMessageType();\n+\n+\treturn rpcType_;\n+}\ndiff --git a/src/android/thread_rpc.h b/src/android/thread_rpc.h\nnew file mode 100644\nindex 000000000000..173caba1a515\n--- /dev/null\n+++ b/src/android/thread_rpc.h\n@@ -0,0 +1,56 @@\n+/* SPDX-License-Identifier: LGPL-2.1-or-later */\n+/*\n+ * Copyright (C) 2019, Google Inc.\n+ *\n+ * thread_call.h - Inter-thread procedure call\n+ */\n+#ifndef __ANDROID_THREAD_CALL_H__\n+#define __ANDROID_THREAD_CALL_H__\n+\n+#include <condition_variable>\n+#include <string>\n+\n+#include <hardware/camera3.h>\n+#include <libcamera/libcamera.h>\n+\n+#include \"message.h\"\n+#include \"thread.h\"\n+\n+class ThreadRpc\n+{\n+public:\n+\tenum RpcTag {\n+\t\tProcessCaptureRequest,\n+\t\tClose,\n+\t};\n+\n+\tThreadRpc()\n+\t\t: delivered_(false) {}\n+\n+\tvoid notifyReception();\n+\tvoid waitDelivery();\n+\n+\tRpcTag tag;\n+\n+\tcamera3_capture_request_t *request;\n+\n+private:\n+\tbool delivered_;\n+\tstd::mutex mutex_;\n+\tstd::condition_variable cv_;\n+};\n+\n+class ThreadRpcMessage : public libcamera::Message\n+{\n+public:\n+\tThreadRpcMessage();\n+\tThreadRpc *rpc;\n+\n+\tstatic Message::Type type();\n+\n+private:\n+\tstatic libcamera::Message::Type rpcType_;\n+};\n+\n+#endif /* __ANDROID_THREAD_CALL_H__ */\n+\ndiff --git a/src/libcamera/meson.build b/src/libcamera/meson.build\nindex a09b23d60022..7d5d3c04fba0 100644\n--- a/src/libcamera/meson.build\n+++ b/src/libcamera/meson.build\n@@ -103,9 +103,18 @@ libcamera_deps = [\n     dependency('threads'),\n ]\n \n+libcamera_link_with = []\n+\n+if get_option('android')\n+    libcamera_sources += android_hal_sources\n+    includes += android_includes\n+    libcamera_link_with += android_camera_metadata\n+endif\n+\n libcamera = shared_library('camera',\n                            libcamera_sources,\n                            install : true,\n+                           link_with : libcamera_link_with,\n                            include_directories : includes,\n                            dependencies : libcamera_deps)\n \ndiff --git a/src/meson.build b/src/meson.build\nindex 7148baee3eda..67ad20aab86b 100644\n--- a/src/meson.build\n+++ b/src/meson.build\n@@ -1,4 +1,7 @@\n-subdir('android')\n+if get_option('android')\n+    subdir('android')\n+endif\n+\n subdir('libcamera')\n subdir('ipa')\n subdir('cam')\n","prefixes":["libcamera-devel","v2","6/6"]}