platform_hardware_interfaces/camera/provider/2.4/default/ExternalCameraProvider.cpp
Yin-Chia Yeh bbc046ed67 Camera: support torch control on external camera
Claim torch API support though there is no device
actually has a flash unit. This is to workaround
an issue in camera service where it falls back
to legacy camera HAL1 control if the device does
not report torch API support (and external camera
HAL does not implement HAL1)

Test: CtsVerifier FlashLight test
Bug: 113336515
Change-Id: I945b35fecfed3b2698b1e9bb1d8faad8699258d2
2018-08-28 11:22:51 -07:00

298 lines
10 KiB
C++

/*
* Copyright (C) 2018 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.
*/
#define LOG_TAG "CamPvdr@2.4-external"
//#define LOG_NDEBUG 0
#include <log/log.h>
#include <regex>
#include <sys/inotify.h>
#include <errno.h>
#include <linux/videodev2.h>
#include "ExternalCameraProvider.h"
#include "ExternalCameraDevice_3_4.h"
namespace android {
namespace hardware {
namespace camera {
namespace provider {
namespace V2_4 {
namespace implementation {
namespace {
// "device@<version>/external/<id>"
const std::regex kDeviceNameRE("device@([0-9]+\\.[0-9]+)/external/(.+)");
const int kMaxDevicePathLen = 256;
const char* kDevicePath = "/dev/";
constexpr char kPrefix[] = "video";
constexpr int kPrefixLen = sizeof(kPrefix) - 1;
bool matchDeviceName(const hidl_string& deviceName, std::string* deviceVersion,
std::string* cameraId) {
std::string deviceNameStd(deviceName.c_str());
std::smatch sm;
if (std::regex_match(deviceNameStd, sm, kDeviceNameRE)) {
if (deviceVersion != nullptr) {
*deviceVersion = sm[1];
}
if (cameraId != nullptr) {
*cameraId = sm[2];
}
return true;
}
return false;
}
} // anonymous namespace
ExternalCameraProvider::ExternalCameraProvider() :
mCfg(ExternalCameraConfig::loadFromCfg()),
mHotPlugThread(this) {
mHotPlugThread.run("ExtCamHotPlug", PRIORITY_BACKGROUND);
}
ExternalCameraProvider::~ExternalCameraProvider() {
mHotPlugThread.requestExit();
}
Return<Status> ExternalCameraProvider::setCallback(
const sp<ICameraProviderCallback>& callback) {
{
Mutex::Autolock _l(mLock);
mCallbacks = callback;
}
if (mCallbacks == nullptr) {
return Status::OK;
}
// Send a callback for all devices to initialize
{
for (const auto& pair : mCameraStatusMap) {
mCallbacks->cameraDeviceStatusChange(pair.first, pair.second);
}
}
return Status::OK;
}
Return<void> ExternalCameraProvider::getVendorTags(getVendorTags_cb _hidl_cb) {
// No vendor tag support for USB camera
hidl_vec<VendorTagSection> zeroSections;
_hidl_cb(Status::OK, zeroSections);
return Void();
}
Return<void> ExternalCameraProvider::getCameraIdList(getCameraIdList_cb _hidl_cb) {
// External camera HAL always report 0 camera, and extra cameras
// are just reported via cameraDeviceStatusChange callbacks
hidl_vec<hidl_string> hidlDeviceNameList;
_hidl_cb(Status::OK, hidlDeviceNameList);
return Void();
}
Return<void> ExternalCameraProvider::isSetTorchModeSupported(
isSetTorchModeSupported_cb _hidl_cb) {
// setTorchMode API is supported, though right now no external camera device
// has a flash unit.
_hidl_cb (Status::OK, true);
return Void();
}
Return<void> ExternalCameraProvider::getCameraDeviceInterface_V1_x(
const hidl_string&,
getCameraDeviceInterface_V1_x_cb _hidl_cb) {
// External Camera HAL does not support HAL1
_hidl_cb(Status::OPERATION_NOT_SUPPORTED, nullptr);
return Void();
}
Return<void> ExternalCameraProvider::getCameraDeviceInterface_V3_x(
const hidl_string& cameraDeviceName,
getCameraDeviceInterface_V3_x_cb _hidl_cb) {
std::string cameraId, deviceVersion;
bool match = matchDeviceName(cameraDeviceName, &deviceVersion, &cameraId);
if (!match) {
_hidl_cb(Status::ILLEGAL_ARGUMENT, nullptr);
return Void();
}
if (mCameraStatusMap.count(cameraDeviceName) == 0 ||
mCameraStatusMap[cameraDeviceName] != CameraDeviceStatus::PRESENT) {
_hidl_cb(Status::ILLEGAL_ARGUMENT, nullptr);
return Void();
}
ALOGV("Constructing v3.4 external camera device");
sp<device::V3_2::ICameraDevice> device;
sp<device::V3_4::implementation::ExternalCameraDevice> deviceImpl =
new device::V3_4::implementation::ExternalCameraDevice(
cameraId, mCfg);
if (deviceImpl == nullptr || deviceImpl->isInitFailed()) {
ALOGE("%s: camera device %s init failed!", __FUNCTION__, cameraId.c_str());
device = nullptr;
_hidl_cb(Status::INTERNAL_ERROR, nullptr);
return Void();
}
device = deviceImpl;
_hidl_cb (Status::OK, device);
return Void();
}
void ExternalCameraProvider::addExternalCamera(const char* devName) {
ALOGI("ExtCam: adding %s to External Camera HAL!", devName);
Mutex::Autolock _l(mLock);
std::string deviceName = std::string("device@3.4/external/") + devName;
mCameraStatusMap[deviceName] = CameraDeviceStatus::PRESENT;
if (mCallbacks != nullptr) {
mCallbacks->cameraDeviceStatusChange(deviceName, CameraDeviceStatus::PRESENT);
}
}
void ExternalCameraProvider::deviceAdded(const char* devName) {
{
base::unique_fd fd(::open(devName, O_RDWR));
if (fd.get() < 0) {
ALOGE("%s open v4l2 device %s failed:%s", __FUNCTION__, devName, strerror(errno));
return;
}
struct v4l2_capability capability;
int ret = ioctl(fd.get(), VIDIOC_QUERYCAP, &capability);
if (ret < 0) {
ALOGE("%s v4l2 QUERYCAP %s failed", __FUNCTION__, devName);
return;
}
if (!(capability.device_caps & V4L2_CAP_VIDEO_CAPTURE)) {
ALOGW("%s device %s does not support VIDEO_CAPTURE", __FUNCTION__, devName);
return;
}
}
// See if we can initialize ExternalCameraDevice correctly
sp<device::V3_4::implementation::ExternalCameraDevice> deviceImpl =
new device::V3_4::implementation::ExternalCameraDevice(devName, mCfg);
if (deviceImpl == nullptr || deviceImpl->isInitFailed()) {
ALOGW("%s: Attempt to init camera device %s failed!", __FUNCTION__, devName);
return;
}
deviceImpl.clear();
addExternalCamera(devName);
return;
}
void ExternalCameraProvider::deviceRemoved(const char* devName) {
Mutex::Autolock _l(mLock);
std::string deviceName = std::string("device@3.4/external/") + devName;
if (mCameraStatusMap.find(deviceName) != mCameraStatusMap.end()) {
mCameraStatusMap.erase(deviceName);
if (mCallbacks != nullptr) {
mCallbacks->cameraDeviceStatusChange(deviceName, CameraDeviceStatus::NOT_PRESENT);
}
} else {
ALOGE("%s: cannot find camera device %s", __FUNCTION__, devName);
}
}
ExternalCameraProvider::HotplugThread::HotplugThread(ExternalCameraProvider* parent) :
Thread(/*canCallJava*/false),
mParent(parent),
mInternalDevices(parent->mCfg.mInternalDevices) {}
ExternalCameraProvider::HotplugThread::~HotplugThread() {}
bool ExternalCameraProvider::HotplugThread::threadLoop() {
// Find existing /dev/video* devices
DIR* devdir = opendir(kDevicePath);
if(devdir == 0) {
ALOGE("%s: cannot open %s! Exiting threadloop", __FUNCTION__, kDevicePath);
return false;
}
struct dirent* de;
while ((de = readdir(devdir)) != 0) {
// Find external v4l devices that's existing before we start watching and add them
if (!strncmp(kPrefix, de->d_name, kPrefixLen)) {
// TODO: This might reject some valid devices. Ex: internal is 33 and a device named 3
// is added.
std::string deviceId(de->d_name + kPrefixLen);
if (mInternalDevices.count(deviceId) == 0) {
ALOGV("Non-internal v4l device %s found", de->d_name);
char v4l2DevicePath[kMaxDevicePathLen];
snprintf(v4l2DevicePath, kMaxDevicePathLen,
"%s%s", kDevicePath, de->d_name);
mParent->deviceAdded(v4l2DevicePath);
}
}
}
closedir(devdir);
// Watch new video devices
mINotifyFD = inotify_init();
if (mINotifyFD < 0) {
ALOGE("%s: inotify init failed! Exiting threadloop", __FUNCTION__);
return true;
}
mWd = inotify_add_watch(mINotifyFD, kDevicePath, IN_CREATE | IN_DELETE);
if (mWd < 0) {
ALOGE("%s: inotify add watch failed! Exiting threadloop", __FUNCTION__);
return true;
}
ALOGI("%s start monitoring new V4L2 devices", __FUNCTION__);
bool done = false;
char eventBuf[512];
while (!done) {
int offset = 0;
int ret = read(mINotifyFD, eventBuf, sizeof(eventBuf));
if (ret >= (int)sizeof(struct inotify_event)) {
while (offset < ret) {
struct inotify_event* event = (struct inotify_event*)&eventBuf[offset];
if (event->wd == mWd) {
if (!strncmp(kPrefix, event->name, kPrefixLen)) {
std::string deviceId(event->name + kPrefixLen);
if (mInternalDevices.count(deviceId) == 0) {
char v4l2DevicePath[kMaxDevicePathLen];
snprintf(v4l2DevicePath, kMaxDevicePathLen,
"%s%s", kDevicePath, event->name);
if (event->mask & IN_CREATE) {
mParent->deviceAdded(v4l2DevicePath);
}
if (event->mask & IN_DELETE) {
mParent->deviceRemoved(v4l2DevicePath);
}
}
}
}
offset += sizeof(struct inotify_event) + event->len;
}
}
}
return true;
}
} // namespace implementation
} // namespace V2_4
} // namespace provider
} // namespace camera
} // namespace hardware
} // namespace android