564 lines
19 KiB
C++
Executable file
564 lines
19 KiB
C++
Executable file
/*
|
|
* 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();
|
|
}
|
|
|
|
waitForQemuSfFakeCameraPropertyAvailable();
|
|
|
|
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__);
|
|
}
|
|
}
|
|
|
|
ALOGE("%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;
|
|
}
|
|
|
|
void EmulatedCameraFactory::waitForQemuSfFakeCameraPropertyAvailable() {
|
|
// Camera service may start running before qemu-props sets qemu.sf.fake_camera to
|
|
// any of the follwing four values: "none,front,back,both"; so we need to wait.
|
|
// android/camera/camera-service.c
|
|
// bug: 30768229
|
|
int numAttempts = 100;
|
|
char prop[PROPERTY_VALUE_MAX];
|
|
bool timeout = true;
|
|
for (int i = 0; i < numAttempts; ++i) {
|
|
if (property_get("qemu.sf.fake_camera", prop, NULL) != 0 ) {
|
|
timeout = false;
|
|
break;
|
|
}
|
|
usleep(5000);
|
|
}
|
|
if (timeout) {
|
|
ALOGE("timeout (%dms) waiting for property qemu.sf.fake_camera to be set\n", 5 * numAttempts);
|
|
}
|
|
}
|
|
|
|
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 */
|