/* * 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 #include #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 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 */