--- /dev/null
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Contains implementation of a class EmulatedCameraFactory that manages cameras
+ * available for emulation.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "EmulatedCamera_Factory"
+#include <cutils/log.h>
+#include <cutils/properties.h>
+#include "EmulatedQemuCamera.h"
+#include "EmulatedFakeCamera.h"
+#include "EmulatedFakeCamera2.h"
+#include "EmulatedFakeCamera3.h"
+#include "EmulatedCameraHotplugThread.h"
+#include "EmulatedCameraFactory.h"
+
+extern camera_module_t HAL_MODULE_INFO_SYM;
+
+/* A global instance of EmulatedCameraFactory is statically instantiated and
+ * initialized when camera emulation HAL is loaded.
+ */
+android::EmulatedCameraFactory gEmulatedCameraFactory;
+
+namespace android {
+
+EmulatedCameraFactory::EmulatedCameraFactory()
+ : mQemuClient(),
+ mEmulatedCameras(NULL),
+ mEmulatedCameraNum(0),
+ mFakeCameraNum(0),
+ mConstructedOK(false),
+ mCallbacks(NULL)
+{
+ status_t res;
+ /* Connect to the factory service in the emulator, and create Qemu cameras. */
+ if (mQemuClient.connectClient(NULL) == NO_ERROR) {
+ /* Connection has succeeded. Create emulated cameras for each camera
+ * device, reported by the service. */
+ createQemuCameras();
+ }
+
+ if (isBackFakeCameraEmulationOn()) {
+ /* Camera ID. */
+ const int camera_id = mEmulatedCameraNum;
+ /* Use fake camera to emulate back-facing camera. */
+ mEmulatedCameraNum++;
+
+ /* Make sure that array is allocated (in case there were no 'qemu'
+ * cameras created. Note that we preallocate the array so it may contain
+ * two fake cameras: one facing back, and another facing front. */
+ if (mEmulatedCameras == NULL) {
+ mEmulatedCameras = new EmulatedBaseCamera*[mEmulatedCameraNum + 1];
+ if (mEmulatedCameras == NULL) {
+ ALOGE("%s: Unable to allocate emulated camera array for %d entries",
+ __FUNCTION__, mEmulatedCameraNum);
+ return;
+ }
+ memset(mEmulatedCameras, 0,
+ (mEmulatedCameraNum + 1) * sizeof(EmulatedBaseCamera*));
+ }
+
+ /* Create, and initialize the fake camera */
+ switch (getBackCameraHalVersion()) {
+ case 1:
+ mEmulatedCameras[camera_id] =
+ new EmulatedFakeCamera(camera_id, true,
+ &HAL_MODULE_INFO_SYM.common);
+ break;
+ case 2:
+ mEmulatedCameras[camera_id] =
+ new EmulatedFakeCamera2(camera_id, true,
+ &HAL_MODULE_INFO_SYM.common);
+ break;
+ case 3:
+ mEmulatedCameras[camera_id] =
+ new EmulatedFakeCamera3(camera_id, true,
+ &HAL_MODULE_INFO_SYM.common);
+ break;
+ default:
+ ALOGE("%s: Unknown back camera hal version requested: %d", __FUNCTION__,
+ getBackCameraHalVersion());
+ }
+ if (mEmulatedCameras[camera_id] != NULL) {
+ ALOGV("%s: Back camera device version is %d", __FUNCTION__,
+ getBackCameraHalVersion());
+ res = mEmulatedCameras[camera_id]->Initialize();
+ if (res != NO_ERROR) {
+ ALOGE("%s: Unable to intialize back camera %d: %s (%d)",
+ __FUNCTION__, camera_id, strerror(-res), res);
+ delete mEmulatedCameras[camera_id];
+ mEmulatedCameraNum--;
+ }
+ } else {
+ mEmulatedCameraNum--;
+ ALOGE("%s: Unable to instantiate fake camera class", __FUNCTION__);
+ }
+ }
+
+ if (isFrontFakeCameraEmulationOn()) {
+ /* Camera ID. */
+ const int camera_id = mEmulatedCameraNum;
+ /* Use fake camera to emulate front-facing camera. */
+ mEmulatedCameraNum++;
+
+ /* Make sure that array is allocated (in case there were no 'qemu'
+ * cameras created. */
+ if (mEmulatedCameras == NULL) {
+ mEmulatedCameras = new EmulatedBaseCamera*[mEmulatedCameraNum];
+ if (mEmulatedCameras == NULL) {
+ ALOGE("%s: Unable to allocate emulated camera array for %d entries",
+ __FUNCTION__, mEmulatedCameraNum);
+ return;
+ }
+ memset(mEmulatedCameras, 0,
+ mEmulatedCameraNum * sizeof(EmulatedBaseCamera*));
+ }
+
+ /* Create, and initialize the fake camera */
+ switch (getFrontCameraHalVersion()) {
+ case 1:
+ mEmulatedCameras[camera_id] =
+ new EmulatedFakeCamera(camera_id, false,
+ &HAL_MODULE_INFO_SYM.common);
+ break;
+ case 2:
+ mEmulatedCameras[camera_id] =
+ new EmulatedFakeCamera2(camera_id, false,
+ &HAL_MODULE_INFO_SYM.common);
+ break;
+ case 3:
+ mEmulatedCameras[camera_id] =
+ new EmulatedFakeCamera3(camera_id, false,
+ &HAL_MODULE_INFO_SYM.common);
+ break;
+ default:
+ ALOGE("%s: Unknown front camera hal version requested: %d",
+ __FUNCTION__,
+ getFrontCameraHalVersion());
+ }
+ if (mEmulatedCameras[camera_id] != NULL) {
+ ALOGV("%s: Front camera device version is %d", __FUNCTION__,
+ getFrontCameraHalVersion());
+ res = mEmulatedCameras[camera_id]->Initialize();
+ if (res != NO_ERROR) {
+ ALOGE("%s: Unable to intialize front camera %d: %s (%d)",
+ __FUNCTION__, camera_id, strerror(-res), res);
+ delete mEmulatedCameras[camera_id];
+ mEmulatedCameraNum--;
+ }
+ } else {
+ mEmulatedCameraNum--;
+ ALOGE("%s: Unable to instantiate fake camera class", __FUNCTION__);
+ }
+ }
+
+ ALOGV("%d cameras are being emulated. %d of them are fake cameras.",
+ mEmulatedCameraNum, mFakeCameraNum);
+
+ /* Create hotplug thread */
+ {
+ Vector<int> cameraIdVector;
+ for (int i = 0; i < mEmulatedCameraNum; ++i) {
+ cameraIdVector.push_back(i);
+ }
+ mHotplugThread = new EmulatedCameraHotplugThread(&cameraIdVector[0],
+ mEmulatedCameraNum);
+ mHotplugThread->run("EmulatedCameraHotplugThread");
+ }
+
+ mConstructedOK = true;
+}
+
+EmulatedCameraFactory::~EmulatedCameraFactory()
+{
+ if (mEmulatedCameras != NULL) {
+ for (int n = 0; n < mEmulatedCameraNum; n++) {
+ if (mEmulatedCameras[n] != NULL) {
+ delete mEmulatedCameras[n];
+ }
+ }
+ delete[] mEmulatedCameras;
+ }
+
+ if (mHotplugThread != NULL) {
+ mHotplugThread->requestExit();
+ mHotplugThread->join();
+ }
+}
+
+/****************************************************************************
+ * Camera HAL API handlers.
+ *
+ * Each handler simply verifies existence of an appropriate EmulatedBaseCamera
+ * instance, and dispatches the call to that instance.
+ *
+ ***************************************************************************/
+
+int EmulatedCameraFactory::cameraDeviceOpen(int camera_id, hw_device_t** device)
+{
+ ALOGV("%s: id = %d", __FUNCTION__, camera_id);
+
+ *device = NULL;
+
+ if (!isConstructedOK()) {
+ ALOGE("%s: EmulatedCameraFactory has failed to initialize", __FUNCTION__);
+ return -EINVAL;
+ }
+
+ if (camera_id < 0 || camera_id >= getEmulatedCameraNum()) {
+ ALOGE("%s: Camera id %d is out of bounds (%d)",
+ __FUNCTION__, camera_id, getEmulatedCameraNum());
+ return -ENODEV;
+ }
+
+ return mEmulatedCameras[camera_id]->connectCamera(device);
+}
+
+int EmulatedCameraFactory::getCameraInfo(int camera_id, struct camera_info* info)
+{
+ ALOGV("%s: id = %d", __FUNCTION__, camera_id);
+
+ if (!isConstructedOK()) {
+ ALOGE("%s: EmulatedCameraFactory has failed to initialize", __FUNCTION__);
+ return -EINVAL;
+ }
+
+ if (camera_id < 0 || camera_id >= getEmulatedCameraNum()) {
+ ALOGE("%s: Camera id %d is out of bounds (%d)",
+ __FUNCTION__, camera_id, getEmulatedCameraNum());
+ return -ENODEV;
+ }
+
+ return mEmulatedCameras[camera_id]->getCameraInfo(info);
+}
+
+int EmulatedCameraFactory::setCallbacks(
+ const camera_module_callbacks_t *callbacks)
+{
+ ALOGV("%s: callbacks = %p", __FUNCTION__, callbacks);
+
+ mCallbacks = callbacks;
+
+ return OK;
+}
+
+void EmulatedCameraFactory::getVendorTagOps(vendor_tag_ops_t* ops) {
+ ALOGV("%s: ops = %p", __FUNCTION__, ops);
+
+ // No vendor tags defined for emulator yet, so not touching ops
+}
+
+/****************************************************************************
+ * Camera HAL API callbacks.
+ ***************************************************************************/
+
+int EmulatedCameraFactory::device_open(const hw_module_t* module,
+ const char* name,
+ hw_device_t** device)
+{
+ /*
+ * Simply verify the parameters, and dispatch the call inside the
+ * EmulatedCameraFactory instance.
+ */
+
+ if (module != &HAL_MODULE_INFO_SYM.common) {
+ ALOGE("%s: Invalid module %p expected %p",
+ __FUNCTION__, module, &HAL_MODULE_INFO_SYM.common);
+ return -EINVAL;
+ }
+ if (name == NULL) {
+ ALOGE("%s: NULL name is not expected here", __FUNCTION__);
+ return -EINVAL;
+ }
+
+ return gEmulatedCameraFactory.cameraDeviceOpen(atoi(name), device);
+}
+
+int EmulatedCameraFactory::get_number_of_cameras(void)
+{
+ return gEmulatedCameraFactory.getEmulatedCameraNum();
+}
+
+int EmulatedCameraFactory::get_camera_info(int camera_id,
+ struct camera_info* info)
+{
+ return gEmulatedCameraFactory.getCameraInfo(camera_id, info);
+}
+
+int EmulatedCameraFactory::set_callbacks(
+ const camera_module_callbacks_t *callbacks)
+{
+ return gEmulatedCameraFactory.setCallbacks(callbacks);
+}
+
+void EmulatedCameraFactory::get_vendor_tag_ops(vendor_tag_ops_t* ops)
+{
+ gEmulatedCameraFactory.getVendorTagOps(ops);
+}
+
+int EmulatedCameraFactory::open_legacy(const struct hw_module_t* module,
+ const char* id, uint32_t halVersion, struct hw_device_t** device) {
+ // Not supporting legacy open
+ return -ENOSYS;
+}
+
+/********************************************************************************
+ * Internal API
+ *******************************************************************************/
+
+/*
+ * Camera information tokens passed in response to the "list" factory query.
+ */
+
+/* Device name token. */
+static const char lListNameToken[] = "name=";
+/* Frame dimensions token. */
+static const char lListDimsToken[] = "framedims=";
+/* Facing direction token. */
+static const char lListDirToken[] = "dir=";
+
+void EmulatedCameraFactory::createQemuCameras()
+{
+ /* Obtain camera list. */
+ char* camera_list = NULL;
+ status_t res = mQemuClient.listCameras(&camera_list);
+ /* Empty list, or list containing just an EOL means that there were no
+ * connected cameras found. */
+ if (res != NO_ERROR || camera_list == NULL || *camera_list == '\0' ||
+ *camera_list == '\n') {
+ if (camera_list != NULL) {
+ free(camera_list);
+ }
+ return;
+ }
+
+ /*
+ * Calculate number of connected cameras. Number of EOLs in the camera list
+ * is the number of the connected cameras.
+ */
+
+ int num = 0;
+ const char* eol = strchr(camera_list, '\n');
+ while (eol != NULL) {
+ num++;
+ eol = strchr(eol + 1, '\n');
+ }
+
+ /* Allocate the array for emulated camera instances. Note that we allocate
+ * two more entries for back and front fake camera emulation. */
+ mEmulatedCameras = new EmulatedBaseCamera*[num + 2];
+ if (mEmulatedCameras == NULL) {
+ ALOGE("%s: Unable to allocate emulated camera array for %d entries",
+ __FUNCTION__, num + 1);
+ free(camera_list);
+ return;
+ }
+ memset(mEmulatedCameras, 0, sizeof(EmulatedBaseCamera*) * (num + 1));
+
+ /*
+ * Iterate the list, creating, and initializin emulated qemu cameras for each
+ * entry (line) in the list.
+ */
+
+ int index = 0;
+ char* cur_entry = camera_list;
+ while (cur_entry != NULL && *cur_entry != '\0' && index < num) {
+ /* Find the end of the current camera entry, and terminate it with zero
+ * for simpler string manipulation. */
+ char* next_entry = strchr(cur_entry, '\n');
+ if (next_entry != NULL) {
+ *next_entry = '\0';
+ next_entry++; // Start of the next entry.
+ }
+
+ /* Find 'name', 'framedims', and 'dir' tokens that are required here. */
+ char* name_start = strstr(cur_entry, lListNameToken);
+ char* dim_start = strstr(cur_entry, lListDimsToken);
+ char* dir_start = strstr(cur_entry, lListDirToken);
+ if (name_start != NULL && dim_start != NULL && dir_start != NULL) {
+ /* Advance to the token values. */
+ name_start += strlen(lListNameToken);
+ dim_start += strlen(lListDimsToken);
+ dir_start += strlen(lListDirToken);
+
+ /* Terminate token values with zero. */
+ char* s = strchr(name_start, ' ');
+ if (s != NULL) {
+ *s = '\0';
+ }
+ s = strchr(dim_start, ' ');
+ if (s != NULL) {
+ *s = '\0';
+ }
+ s = strchr(dir_start, ' ');
+ if (s != NULL) {
+ *s = '\0';
+ }
+
+ /* Create and initialize qemu camera. */
+ EmulatedQemuCamera* qemu_cam =
+ new EmulatedQemuCamera(index, &HAL_MODULE_INFO_SYM.common);
+ if (NULL != qemu_cam) {
+ res = qemu_cam->Initialize(name_start, dim_start, dir_start);
+ if (res == NO_ERROR) {
+ mEmulatedCameras[index] = qemu_cam;
+ index++;
+ } else {
+ delete qemu_cam;
+ }
+ } else {
+ ALOGE("%s: Unable to instantiate EmulatedQemuCamera",
+ __FUNCTION__);
+ }
+ } else {
+ ALOGW("%s: Bad camera information: %s", __FUNCTION__, cur_entry);
+ }
+
+ cur_entry = next_entry;
+ }
+
+ mEmulatedCameraNum = index;
+}
+
+bool EmulatedCameraFactory::isBackFakeCameraEmulationOn()
+{
+ /* Defined by 'qemu.sf.fake_camera' boot property: if property exist, and
+ * is set to 'both', or 'back', then fake camera is used to emulate back
+ * camera. */
+ char prop[PROPERTY_VALUE_MAX];
+ if ((property_get("qemu.sf.fake_camera", prop, NULL) > 0) &&
+ (!strcmp(prop, "both") || !strcmp(prop, "back"))) {
+ return true;
+ } else {
+ return false;
+ }
+}
+
+int EmulatedCameraFactory::getBackCameraHalVersion()
+{
+ /* Defined by 'qemu.sf.back_camera_hal_version' boot property: if the
+ * property doesn't exist, it is assumed to be 1. */
+ char prop[PROPERTY_VALUE_MAX];
+ if (property_get("qemu.sf.back_camera_hal", prop, NULL) > 0) {
+ char *prop_end = prop;
+ int val = strtol(prop, &prop_end, 10);
+ if (*prop_end == '\0') {
+ return val;
+ }
+ // Badly formatted property, should just be a number
+ ALOGE("qemu.sf.back_camera_hal is not a number: %s", prop);
+ }
+ return 1;
+}
+
+bool EmulatedCameraFactory::isFrontFakeCameraEmulationOn()
+{
+ /* Defined by 'qemu.sf.fake_camera' boot property: if property exist, and
+ * is set to 'both', or 'front', then fake camera is used to emulate front
+ * camera. */
+ char prop[PROPERTY_VALUE_MAX];
+ if ((property_get("qemu.sf.fake_camera", prop, NULL) > 0) &&
+ (!strcmp(prop, "both") || !strcmp(prop, "front"))) {
+ return true;
+ } else {
+ return false;
+ }
+}
+
+int EmulatedCameraFactory::getFrontCameraHalVersion()
+{
+ /* Defined by 'qemu.sf.front_camera_hal_version' boot property: if the
+ * property doesn't exist, it is assumed to be 1. */
+ char prop[PROPERTY_VALUE_MAX];
+ if (property_get("qemu.sf.front_camera_hal", prop, NULL) > 0) {
+ char *prop_end = prop;
+ int val = strtol(prop, &prop_end, 10);
+ if (*prop_end == '\0') {
+ return val;
+ }
+ // Badly formatted property, should just be a number
+ ALOGE("qemu.sf.front_camera_hal is not a number: %s", prop);
+ }
+ return 1;
+}
+
+void EmulatedCameraFactory::onStatusChanged(int cameraId, int newStatus) {
+
+ EmulatedBaseCamera *cam = mEmulatedCameras[cameraId];
+ if (!cam) {
+ ALOGE("%s: Invalid camera ID %d", __FUNCTION__, cameraId);
+ return;
+ }
+
+ /**
+ * (Order is important)
+ * Send the callback first to framework, THEN close the camera.
+ */
+
+ if (newStatus == cam->getHotplugStatus()) {
+ ALOGW("%s: Ignoring transition to the same status", __FUNCTION__);
+ return;
+ }
+
+ const camera_module_callbacks_t* cb = mCallbacks;
+ if (cb != NULL && cb->camera_device_status_change != NULL) {
+ cb->camera_device_status_change(cb, cameraId, newStatus);
+ }
+
+ if (newStatus == CAMERA_DEVICE_STATUS_NOT_PRESENT) {
+ cam->unplugCamera();
+ } else if (newStatus == CAMERA_DEVICE_STATUS_PRESENT) {
+ cam->plugCamera();
+ }
+
+}
+
+/********************************************************************************
+ * Initializer for the static member structure.
+ *******************************************************************************/
+
+/* Entry point for camera HAL API. */
+struct hw_module_methods_t EmulatedCameraFactory::mCameraModuleMethods = {
+ open: EmulatedCameraFactory::device_open
+};
+
+}; /* namespace android */