blob: 9acdbd2a7f4bbd0973226890e7706796adf23b7e [file] [log] [blame]
/*
* 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 "EmulatedCameraFactory.h"
#include "EmulatedCameraHotplugThread.h"
#include "EmulatedFakeCamera.h"
#include "EmulatedFakeCamera2.h"
#include "EmulatedFakeCamera3.h"
#include "EmulatedQemuCamera.h"
#include "EmulatedQemuCamera3.h"
#include <log/log.h>
#include <cutils/properties.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(),
mConstructedOK(false),
mGBM(&GraphicBufferMapper::get()),
mCallbacks(nullptr) {
/*
* Figure out how many cameras need to be created, so we can allocate the
* array of emulated cameras before populating it.
*/
// QEMU Cameras
std::vector<QemuCameraInfo> qemuCameras;
if (mQemuClient.connectClient(nullptr) == NO_ERROR) {
findQemuCameras(&qemuCameras);
}
int fakeCameraNum = 0;
waitForQemuSfFakeCameraPropertyAvailable();
// Fake Cameras
if (isFakeCameraEmulationOn(/* backCamera */ true)) {
fakeCameraNum++;
}
if (isFakeCameraEmulationOn(/* backCamera */ false)) {
fakeCameraNum++;
}
/*
* We have the number of cameras we need to create, now allocate space for
* them.
*/
mEmulatedCameras.reserve(qemuCameras.size() + fakeCameraNum);
createQemuCameras(qemuCameras);
// Create fake cameras, if enabled.
if (isFakeCameraEmulationOn(/* backCamera */ true)) {
createFakeCamera(/* backCamera */ true);
}
if (isFakeCameraEmulationOn(/* backCamera */ false)) {
createFakeCamera(/* backCamera */ false);
}
ALOGE("%zu cameras are being emulated. %d of them are fake cameras.",
mEmulatedCameras.size(), fakeCameraNum);
// Create hotplug thread.
{
std::vector<int> cameraIdVector;
for (const auto &camera: mEmulatedCameras) {
cameraIdVector.push_back(camera->getCameraId());
}
mHotplugThread = new EmulatedCameraHotplugThread(std::move(cameraIdVector));
mHotplugThread->run("EmulatedCameraHotplugThread");
}
mConstructedOK = true;
}
EmulatedCameraFactory::~EmulatedCameraFactory() {
mEmulatedCameras.clear();
if (mHotplugThread != nullptr) {
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 cameraId,
hw_device_t **device) {
ALOGV("%s: id = %d", __FUNCTION__, cameraId);
*device = nullptr;
if (!isConstructedOK()) {
ALOGE("%s: EmulatedCameraFactory has failed to initialize",
__FUNCTION__);
return -EINVAL;
}
if (cameraId < 0 || cameraId >= getEmulatedCameraNum()) {
ALOGE("%s: Camera id %d is out of bounds (%d)",
__FUNCTION__, cameraId, getEmulatedCameraNum());
return -ENODEV;
}
return mEmulatedCameras[cameraId]->connectCamera(device);
}
int EmulatedCameraFactory::getCameraInfo(int cameraId,
struct camera_info *info) {
ALOGV("%s: id = %d", __FUNCTION__, cameraId);
if (!isConstructedOK()) {
ALOGE("%s: EmulatedCameraFactory has failed to initialize",
__FUNCTION__);
return -EINVAL;
}
if (cameraId < 0 || cameraId >= getEmulatedCameraNum()) {
ALOGE("%s: Camera id %d is out of bounds (%d)",
__FUNCTION__, cameraId, getEmulatedCameraNum());
return -ENODEV;
}
return mEmulatedCameras[cameraId]->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 == nullptr) {
ALOGE("%s: NULL name is not expected here", __FUNCTION__);
return -EINVAL;
}
return gEmulatedCameraFactory.cameraDeviceOpen(atoi(name), device);
}
int EmulatedCameraFactory::get_number_of_cameras() {
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 *kListNameToken = "name=";
// Frame dimensions token.
static const char *kListDimsToken = "framedims=";
// Facing direction token.
static const char *kListDirToken = "dir=";
bool EmulatedCameraFactory::getTokenValue(const char *token,
const std::string &s, char **value) {
// Find the start of the token.
size_t tokenStart = s.find(token);
if (tokenStart == std::string::npos) {
return false;
}
// Advance to the beginning of the token value.
size_t valueStart = tokenStart + strlen(token);
// Find the length of the token value.
size_t valueLength = s.find(' ', valueStart) - valueStart;
// Extract the value substring.
std::string valueStr = s.substr(valueStart, valueLength);
// Convert to char*.
*value = new char[valueStr.length() + 1];
if (*value == nullptr) {
return false;
}
strcpy(*value, valueStr.c_str());
ALOGV("%s: Parsed value is \"%s\"", __FUNCTION__, *value);
return true;
}
void EmulatedCameraFactory::findQemuCameras(
std::vector<QemuCameraInfo> *qemuCameras) {
// Obtain camera list.
char *cameraList = nullptr;
status_t res = mQemuClient.listCameras(&cameraList);
/*
* Empty list, or list containing just an EOL means that there were no
* connected cameras found.
*/
if (res != NO_ERROR || cameraList == nullptr || *cameraList == '\0' ||
*cameraList == '\n') {
if (cameraList != nullptr) {
free(cameraList);
}
return;
}
/*
* Calculate number of connected cameras. Number of EOLs in the camera list
* is the number of the connected cameras.
*/
std::string cameraListStr(cameraList);
free(cameraList);
size_t lineBegin = 0;
size_t lineEnd = cameraListStr.find('\n');
while (lineEnd != std::string::npos) {
std::string cameraStr = cameraListStr.substr(lineBegin, lineEnd - lineBegin);
// Parse the 'name', 'framedims', and 'dir' tokens.
char *name, *frameDims, *dir;
if (getTokenValue(kListNameToken, cameraStr, &name) &&
getTokenValue(kListDimsToken, cameraStr, &frameDims) &&
getTokenValue(kListDirToken, cameraStr, &dir)) {
// Push the camera info if it was all successfully parsed.
qemuCameras->push_back(QemuCameraInfo{
.name = name,
.frameDims = frameDims,
.dir = dir,
});
} else {
ALOGW("%s: Bad camera information: %s", __FUNCTION__,
cameraStr.c_str());
}
// Skip over the newline for the beginning of the next line.
lineBegin = lineEnd + 1;
lineEnd = cameraListStr.find('\n', lineBegin);
}
}
std::unique_ptr<EmulatedBaseCamera>
EmulatedCameraFactory::createQemuCameraImpl(int halVersion,
const QemuCameraInfo& camInfo,
int cameraId,
struct hw_module_t* module) {
status_t res;
switch (halVersion) {
case 1: {
auto camera = std::make_unique<EmulatedQemuCamera>(cameraId, module, mGBM);
res = camera->Initialize(camInfo.name, camInfo.frameDims, camInfo.dir);
if (res == NO_ERROR) {
return camera;
}
}
break;
case 3: {
auto camera = std::make_unique<EmulatedQemuCamera3>(cameraId, module, mGBM);
res = camera->Initialize(camInfo.name, camInfo.frameDims, camInfo.dir);
if (res == NO_ERROR) {
return camera;
}
}
break;
default:
ALOGE("%s: QEMU support for camera hal version %d is not "
"implemented", __func__, halVersion);
break;
}
return nullptr;
}
void EmulatedCameraFactory::createQemuCameras(
const std::vector<QemuCameraInfo> &qemuCameras) {
/*
* Iterate the list, creating, and initializing emulated QEMU cameras for each
* entry in the list.
*/
/*
* We use this index only for determining which direction the webcam should
* face. Otherwise, mEmulatedCameraNum represents the camera ID and the
* index into mEmulatedCameras.
*/
int qemuIndex = 0;
for (const auto &cameraInfo : qemuCameras) {
/*
* Here, we're assuming the first webcam is intended to be the back
* camera and any other webcams are front cameras.
*/
const bool isBackcamera = (qemuIndex == 0);
const int halVersion = getCameraHalVersion(isBackcamera);
std::unique_ptr<EmulatedBaseCamera> camera =
createQemuCameraImpl(halVersion,
cameraInfo,
mEmulatedCameras.size(),
&HAL_MODULE_INFO_SYM.common);
if (camera) {
mEmulatedCameras.push_back(std::move(camera));
}
qemuIndex++;
}
}
std::unique_ptr<EmulatedBaseCamera>
EmulatedCameraFactory::createFakeCameraImpl(bool backCamera,
int halVersion,
int cameraId,
struct hw_module_t* module) {
switch (halVersion) {
case 1:
return std::make_unique<EmulatedFakeCamera>(cameraId, backCamera, module, mGBM);
case 2:
return std::make_unique<EmulatedFakeCamera2>(cameraId, backCamera, module, mGBM);
case 3: {
static const char key[] = "ro.kernel.qemu.camera.fake.rotating";
char prop[PROPERTY_VALUE_MAX];
if (property_get(key, prop, nullptr) > 0) {
return std::make_unique<EmulatedFakeCamera>(cameraId, backCamera, module, mGBM);
} else {
return std::make_unique<EmulatedFakeCamera3>(cameraId, backCamera, module, mGBM);
}
}
default:
ALOGE("%s: Unknown %s camera hal version requested: %d",
__func__, backCamera ? "back" : "front", halVersion);
return nullptr;
}
}
void EmulatedCameraFactory::createFakeCamera(bool backCamera) {
const int halVersion = getCameraHalVersion(backCamera);
std::unique_ptr<EmulatedBaseCamera> camera = createFakeCameraImpl(
backCamera, halVersion, mEmulatedCameras.size(),
&HAL_MODULE_INFO_SYM.common);
status_t res = camera->Initialize();
if (res == NO_ERROR) {
mEmulatedCameras.push_back(std::move(camera));
} else {
ALOGE("%s: Unable to initialize %s camera %zu: %s (%d)",
__func__, backCamera ? "back" : "front",
mEmulatedCameras.size(), strerror(-res), res);
}
}
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, nullptr) != 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::isFakeCameraEmulationOn(bool backCamera) {
/*
* Defined by 'qemu.sf.fake_camera' boot property. If the property exists,
* and if it's set to 'both', then fake cameras are used to emulate both
* sides. If it's set to 'back' or 'front', then a fake camera is used only
* to emulate the back or front camera, respectively.
*/
char prop[PROPERTY_VALUE_MAX];
if ((property_get("qemu.sf.fake_camera", prop, nullptr) > 0) &&
(!strcmp(prop, "both") ||
!strcmp(prop, backCamera ? "back" : "front"))) {
return true;
} else {
return false;
}
}
int EmulatedCameraFactory::getCameraHalVersion(bool backCamera) {
/*
* Defined by 'qemu.sf.front_camera_hal_version' and
* 'qemu.sf.back_camera_hal_version' boot properties. If the property
* doesn't exist, it is assumed we are working with HAL v1.
*/
char prop[PROPERTY_VALUE_MAX];
const char *propQuery = backCamera ?
"qemu.sf.back_camera_hal" :
"qemu.sf.front_camera_hal";
if (property_get(propQuery, prop, nullptr) > 0) {
char *propEnd = prop;
int val = strtol(prop, &propEnd, 10);
if (*propEnd == '\0') {
return val;
}
// Badly formatted property. It should just be a number.
ALOGE("qemu.sf.back_camera_hal is not a number: %s", prop);
}
return 3;
}
void EmulatedCameraFactory::onStatusChanged(int cameraId, int newStatus) {
EmulatedBaseCamera *cam = mEmulatedCameras[cameraId].get();
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 != nullptr && cb->camera_device_status_change != nullptr) {
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
};
}; // end of namespace android