blob: 65cba8d2d47e0be1ead17fe72614c51aff80e72a [file] [log] [blame]
/*
* Copyright (C) 2024 The LineageOS Project
*
* SPDX-License-Identifier: Apache-2.0
*/
#define LOG_TAG "CamDev-impl"
#include "CameraDevice.h"
#include "convert.h"
#include <cutils/trace.h>
namespace android {
namespace hardware {
namespace camera {
namespace device {
namespace implementation {
std::string CameraDevice::kDeviceVersion = "1.1";
CameraDevice::CameraDevice(
sp<SamsungCameraModule> module, const std::string& cameraId,
const SortedVector<std::pair<std::string, std::string>>& cameraDeviceNames)
: mModule(module),
mCameraId(cameraId),
mDisconnected(false),
mCameraDeviceNames(cameraDeviceNames) {
mCameraIdInt = atoi(mCameraId.c_str());
// Should not reach here as provider also validate ID
if (mCameraIdInt < 0) {
ALOGE("%s: Invalid camera id: %s", __FUNCTION__, mCameraId.c_str());
mInitFail = true;
} else if (mCameraIdInt >= mModule->getNumberOfCameras()) {
ALOGI("%s: Adding a new camera id: %s", __FUNCTION__, mCameraId.c_str());
}
mDeviceVersion = mModule->getDeviceVersion(mCameraIdInt);
if (mDeviceVersion < CAMERA_DEVICE_API_VERSION_3_2) {
ALOGE("%s: Camera id %s does not support HAL3.2+", __FUNCTION__, mCameraId.c_str());
mInitFail = true;
}
}
CameraDevice::~CameraDevice() {}
Status CameraDevice::initStatus() const {
Mutex::Autolock _l(mLock);
Status status = Status::OK;
if (mInitFail) {
status = Status::INTERNAL_ERROR;
} else if (mDisconnected) {
status = Status::CAMERA_DISCONNECTED;
}
return status;
}
ndk::ScopedAStatus CameraDevice::getCameraCharacteristics(CameraMetadata* _aidl_return) {
if (_aidl_return == nullptr) {
return fromStatus(Status::ILLEGAL_ARGUMENT);
}
Status status = initStatus();
if (status == Status::OK) {
// Module 2.1+ codepath.
struct camera_info info;
int ret = mModule->getCameraInfo(mCameraIdInt, &info);
if (ret == OK && info.static_camera_characteristics != NULL) {
common::helper::CameraMetadata metadata = (camera_metadata_t*)info.static_camera_characteristics;
camera_metadata_entry_t entry = metadata.find(ANDROID_FLASH_INFO_AVAILABLE);
if (entry.count > 0 && *entry.data.u8 != 0 && mModule->isSetTorchModeStrengthSupported()) {
// Samsung always has 5 supported torch strength levels
int32_t defaultTorchStrength = 1;
int32_t torchStrengthLevels = 5;
metadata.update(ANDROID_FLASH_INFO_STRENGTH_DEFAULT_LEVEL, &defaultTorchStrength, 1);
metadata.update(ANDROID_FLASH_INFO_STRENGTH_MAXIMUM_LEVEL, &torchStrengthLevels, 1);
}
convertToAidl(metadata.release(), _aidl_return);
} else {
ALOGE("%s: get camera info failed!", __FUNCTION__);
status = Status::INTERNAL_ERROR;
}
}
return fromStatus(status);
}
ndk::ScopedAStatus CameraDevice::getPhysicalCameraCharacteristics(
const std::string& in_physicalCameraId, CameraMetadata* _aidl_return) {
if (_aidl_return == nullptr) {
return fromStatus(Status::ILLEGAL_ARGUMENT);
}
Status status = initStatus();
if (status == Status::OK) {
// Require module 2.5+ version.
if (mModule->getModuleApiVersion() < CAMERA_MODULE_API_VERSION_2_5) {
ALOGE("%s: get_physical_camera_info must be called on camera module 2.5 or newer",
__FUNCTION__);
status = Status::INTERNAL_ERROR;
} else {
char* end;
errno = 0;
long id = strtol(in_physicalCameraId.c_str(), &end, 0);
if (id > INT_MAX || (errno == ERANGE && id == LONG_MAX) || id < INT_MIN ||
(errno == ERANGE && id == LONG_MIN) || *end != '\0') {
ALOGE("%s: Invalid physicalCameraId %s", __FUNCTION__, in_physicalCameraId.c_str());
status = Status::ILLEGAL_ARGUMENT;
} else {
camera_metadata_t* physicalInfo = nullptr;
int ret = mModule->getPhysicalCameraInfo((int)id, &physicalInfo);
if (ret == OK) {
convertToAidl(physicalInfo, _aidl_return);
} else if (ret == -EINVAL) {
ALOGE("%s: %s is not a valid physical camera Id outside of getCameraIdList()",
__FUNCTION__, in_physicalCameraId.c_str());
status = Status::ILLEGAL_ARGUMENT;
} else {
ALOGE("%s: Failed to get physical camera %s info: %s (%d)!", __FUNCTION__,
in_physicalCameraId.c_str(), strerror(-ret), ret);
status = Status::INTERNAL_ERROR;
}
}
}
}
return fromStatus(status);
}
void CameraDevice::setConnectionStatus(bool connected) {
Mutex::Autolock _l(mLock);
mDisconnected = !connected;
std::shared_ptr<CameraDeviceSession> session = mSession.lock();
if (session == nullptr) {
return;
}
// Only notify active session disconnect events.
// Users will need to re-open camera after disconnect event
if (!connected) {
session->disconnect();
}
return;
}
Status CameraDevice::getAidlStatus(int status) {
switch (status) {
case 0:
return Status::OK;
case -ENOSYS:
return Status::OPERATION_NOT_SUPPORTED;
case -EBUSY:
return Status::CAMERA_IN_USE;
case -EUSERS:
return Status::MAX_CAMERAS_IN_USE;
case -ENODEV:
return Status::INTERNAL_ERROR;
case -EINVAL:
return Status::ILLEGAL_ARGUMENT;
default:
ALOGE("%s: unknown HAL status code %d", __FUNCTION__, status);
return Status::INTERNAL_ERROR;
}
}
ndk::ScopedAStatus CameraDevice::getResourceCost(CameraResourceCost* _aidl_return) {
if (_aidl_return == nullptr) {
return fromStatus(Status::ILLEGAL_ARGUMENT);
}
Status status = initStatus();
if (status == Status::OK) {
int cost = 100;
std::vector<std::string> conflicting_devices;
struct camera_info info;
// If using post-2.4 module version, query the cost + conflicting devices from the HAL
if (mModule->getModuleApiVersion() >= CAMERA_MODULE_API_VERSION_2_4) {
int ret = mModule->getCameraInfo(mCameraIdInt, &info);
if (ret == OK) {
cost = info.resource_cost;
for (size_t i = 0; i < info.conflicting_devices_length; i++) {
std::string cameraId(info.conflicting_devices[i]);
for (const auto& pair : mCameraDeviceNames) {
if (cameraId == pair.first) {
conflicting_devices.push_back(pair.second);
}
}
}
} else {
status = Status::INTERNAL_ERROR;
}
}
if (status == Status::OK) {
_aidl_return->resourceCost = cost;
_aidl_return->conflictingDevices.resize(conflicting_devices.size());
for (size_t i = 0; i < conflicting_devices.size(); i++) {
_aidl_return->conflictingDevices[i] = conflicting_devices[i];
ALOGV("CamDevice %s is conflicting with camDevice %s", mCameraId.c_str(),
_aidl_return->conflictingDevices[i].c_str());
}
}
}
return fromStatus(status);
}
ndk::ScopedAStatus CameraDevice::isStreamCombinationSupported(const StreamConfiguration& in_streams,
bool* _aidl_return) {
Status status;
*_aidl_return = false;
// Require module 2.5+ version.
if (mModule->getModuleApiVersion() < CAMERA_MODULE_API_VERSION_2_5) {
ALOGE("%s: is_stream_combination_supported must be called on camera module 2.5 or "
"newer",
__FUNCTION__);
status = Status::INTERNAL_ERROR;
} else {
camera_stream_combination_t streamComb{};
streamComb.operation_mode = static_cast<uint32_t>(in_streams.operationMode);
streamComb.num_streams = in_streams.streams.size();
camera_stream_t* streamBuffer = new camera_stream_t[streamComb.num_streams];
size_t i = 0;
for (const auto& it : in_streams.streams) {
streamBuffer[i].stream_type = static_cast<int>(it.streamType);
streamBuffer[i].width = it.width;
streamBuffer[i].height = it.height;
streamBuffer[i].format = static_cast<int>(it.format);
streamBuffer[i].data_space = static_cast<android_dataspace_t>(it.dataSpace);
streamBuffer[i].usage = static_cast<uint32_t>(it.usage);
streamBuffer[i].physical_camera_id = it.physicalCameraId.c_str();
streamBuffer[i++].rotation = static_cast<int>(it.rotation);
}
streamComb.streams = streamBuffer;
auto res = mModule->isStreamCombinationSupported(mCameraIdInt, &streamComb);
switch (res) {
case NO_ERROR:
*_aidl_return = true;
status = Status::OK;
break;
case BAD_VALUE:
status = Status::OK;
break;
case INVALID_OPERATION:
status = Status::OPERATION_NOT_SUPPORTED;
break;
default:
ALOGE("%s: Unexpected error: %d", __FUNCTION__, res);
status = Status::INTERNAL_ERROR;
};
delete[] streamBuffer;
}
return fromStatus(status);
}
ndk::ScopedAStatus CameraDevice::open(const std::shared_ptr<ICameraDeviceCallback>& in_callback,
std::shared_ptr<ICameraDeviceSession>* _aidl_return) {
if (_aidl_return == nullptr) {
ALOGE("%s: cannot open camera %s. return session ptr is null!", __FUNCTION__,
mCameraId.c_str());
return fromStatus(Status::ILLEGAL_ARGUMENT);
}
Status status = initStatus();
std::shared_ptr<CameraDeviceSession> session = nullptr;
if (in_callback == nullptr) {
ALOGE("%s: cannot open camera %s. callback is null!", __FUNCTION__, mCameraId.c_str());
return fromStatus(Status::ILLEGAL_ARGUMENT);
}
if (status != Status::OK) {
// Provider will never pass initFailed device to client, so
// this must be a disconnected camera
ALOGE("%s: cannot open camera %s. camera is disconnected!", __FUNCTION__,
mCameraId.c_str());
return fromStatus(Status::CAMERA_DISCONNECTED);
} else {
mLock.lock();
ALOGV("%s: Initializing device for camera %d", __FUNCTION__, mCameraIdInt);
session = mSession.lock();
if (session != nullptr && !session->isClosed()) {
ALOGE("%s: cannot open an already opened camera!", __FUNCTION__);
mLock.unlock();
return fromStatus(Status::CAMERA_IN_USE);
}
/** Open HAL device */
status_t res;
camera3_device_t* device;
ATRACE_BEGIN("camera3->open");
res = mModule->open(mCameraId.c_str(), reinterpret_cast<hw_device_t**>(&device));
ATRACE_END();
if (res != OK) {
ALOGE("%s: cannot open camera %s!", __FUNCTION__, mCameraId.c_str());
mLock.unlock();
return fromStatus(getAidlStatus(res));
}
/** Cross-check device version */
if (device->common.version < CAMERA_DEVICE_API_VERSION_3_2) {
ALOGE("%s: Could not open camera: "
"Camera device should be at least %x, reports %x instead",
__FUNCTION__, CAMERA_DEVICE_API_VERSION_3_2, device->common.version);
device->common.close(&device->common);
mLock.unlock();
return fromStatus(Status::ILLEGAL_ARGUMENT);
}
struct camera_info info;
res = mModule->getCameraInfo(mCameraIdInt, &info);
if (res != OK) {
ALOGE("%s: Could not open camera: getCameraInfo failed", __FUNCTION__);
device->common.close(&device->common);
mLock.unlock();
return fromStatus(Status::ILLEGAL_ARGUMENT);
}
session = createSession(device, info.static_camera_characteristics, in_callback);
if (session == nullptr) {
ALOGE("%s: camera device session allocation failed", __FUNCTION__);
mLock.unlock();
return fromStatus(Status::INTERNAL_ERROR);
}
if (session->isInitFailed()) {
ALOGE("%s: camera device session init failed", __FUNCTION__);
session = nullptr;
mLock.unlock();
return fromStatus(Status::INTERNAL_ERROR);
}
mSession = session;
mLock.unlock();
}
*_aidl_return = session;
return fromStatus(Status::OK);
}
ndk::ScopedAStatus CameraDevice::openInjectionSession(const std::shared_ptr<ICameraDeviceCallback>&,
std::shared_ptr<ICameraInjectionSession>*) {
return fromStatus(Status::OPERATION_NOT_SUPPORTED);
}
ndk::ScopedAStatus CameraDevice::setTorchMode(bool in_on) {
if (!mModule->isSetTorchModeSupported()) {
return fromStatus(Status::OPERATION_NOT_SUPPORTED);
}
Status status = initStatus();
if (status == Status::OK) {
status = getAidlStatus(mModule->setTorchMode(mCameraId.c_str(), in_on));
if (status == Status::OK) {
mTorchStrengthLevel = 1;
}
}
return fromStatus(status);
}
ndk::ScopedAStatus CameraDevice::turnOnTorchWithStrengthLevel(int32_t in_torchStrength) {
if (!mModule->isSetTorchModeStrengthSupported()) {
return fromStatus(Status::OPERATION_NOT_SUPPORTED);
}
Status status = initStatus();
if (status == Status::OK) {
status = getAidlStatus(
mModule->setTorchModeStrength(mCameraId.c_str(), true, in_torchStrength));
if (status == Status::OK) {
mTorchStrengthLevel = in_torchStrength;
}
}
return fromStatus(status);
}
ndk::ScopedAStatus CameraDevice::getTorchStrengthLevel(int32_t* _aidl_return) {
if (!mModule->isSetTorchModeSupported()) {
return fromStatus(Status::OPERATION_NOT_SUPPORTED);
}
*_aidl_return = mTorchStrengthLevel;
return fromStatus(Status::OK);
}
std::shared_ptr<CameraDeviceSession> CameraDevice::createSession(
camera3_device_t* device, const camera_metadata_t* deviceInfo,
const std::shared_ptr<ICameraDeviceCallback>& callback) {
return ndk::SharedRefBase::make<CameraDeviceSession>(device, deviceInfo, callback);
}
binder_status_t CameraDevice::dump(int fd, const char** args, uint32_t numArgs) {
std::shared_ptr<CameraDeviceSession> session = mSession.lock();
if (session == nullptr) {
dprintf(fd, "No active camera device session instance\n");
return STATUS_OK;
}
return session->dump(fd, args, numArgs);
}
} // namespace implementation
} // namespace device
} // namespace camera
} // namespace hardware
} // namespace android