| /* |
| * 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(); |
| } |
| |
| 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; |
| } |
| |
| /**************************************************************************** |
| * 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); |
| } |
| |
| /******************************************************************************** |
| * 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 */ |