From 3068d73c6c7e1f44523b1466b903a9c82408b258 Mon Sep 17 00:00:00 2001 From: Chien-Yu Chen Date: Mon, 9 Feb 2015 13:29:57 -0800 Subject: camera: implement flashlight control Implement flashlight API for module v2.4 by calling module APIs and by for hal v2 and v3 by using CameraDeviceBase. Bug: 2682206 Change-Id: Ib8b77f6fd462489d672f27e14fe37801d35b7544 --- camera/ICameraService.cpp | 24 + camera/ICameraServiceListener.cpp | 27 +- camera/tests/ProCameraTests.cpp | 6 + include/camera/ICameraService.h | 7 + include/camera/ICameraServiceListener.h | 24 + services/camera/libcameraservice/Android.mk | 1 + .../camera/libcameraservice/CameraFlashlight.cpp | 520 +++++++++++++++++++++ .../camera/libcameraservice/CameraFlashlight.h | 149 ++++++ services/camera/libcameraservice/CameraService.cpp | 176 ++++++- services/camera/libcameraservice/CameraService.h | 34 ++ 10 files changed, 965 insertions(+), 3 deletions(-) create mode 100644 services/camera/libcameraservice/CameraFlashlight.cpp create mode 100644 services/camera/libcameraservice/CameraFlashlight.h diff --git a/camera/ICameraService.cpp b/camera/ICameraService.cpp index fc3e437..a75cb48 100644 --- a/camera/ICameraService.cpp +++ b/camera/ICameraService.cpp @@ -209,6 +209,20 @@ public: return status; } + virtual status_t setTorchMode(const String16& cameraId, bool enabled, + const sp& clientBinder) + { + Parcel data, reply; + data.writeInterfaceToken(ICameraService::getInterfaceDescriptor()); + data.writeString16(cameraId); + data.writeInt32(enabled ? 1 : 0); + data.writeStrongBinder(clientBinder); + remote()->transact(BnCameraService::SET_TORCH_MODE, data, &reply); + + if (readExceptionCode(reply)) return -EPROTO; + return reply.readInt32(); + } + // connect to camera service (pro client) virtual status_t connectPro(const sp& cameraCb, int cameraId, const String16 &clientPackageName, int clientUid, @@ -490,6 +504,16 @@ status_t BnCameraService::onTransact( } return NO_ERROR; } break; + case SET_TORCH_MODE: { + CHECK_INTERFACE(ICameraService, data, reply); + String16 cameraId = data.readString16(); + bool enabled = data.readInt32() != 0 ? true : false; + const sp clientBinder = data.readStrongBinder(); + status_t status = setTorchMode(cameraId, enabled, clientBinder); + reply->writeNoException(); + reply->writeInt32(status); + return NO_ERROR; + } break; default: return BBinder::onTransact(code, data, reply, flags); } diff --git a/camera/ICameraServiceListener.cpp b/camera/ICameraServiceListener.cpp index b2f1729..90a8bc2 100644 --- a/camera/ICameraServiceListener.cpp +++ b/camera/ICameraServiceListener.cpp @@ -29,6 +29,7 @@ namespace android { namespace { enum { STATUS_CHANGED = IBinder::FIRST_CALL_TRANSACTION, + TORCH_STATUS_CHANGED, }; }; // namespace anonymous @@ -54,8 +55,21 @@ public: data, &reply, IBinder::FLAG_ONEWAY); + } - reply.readExceptionCode(); + virtual void onTorchStatusChanged(TorchStatus status, const String16 &cameraId) + { + Parcel data, reply; + data.writeInterfaceToken( + ICameraServiceListener::getInterfaceDescriptor()); + + data.writeInt32(static_cast(status)); + data.writeString16(cameraId); + + remote()->transact(TORCH_STATUS_CHANGED, + data, + &reply, + IBinder::FLAG_ONEWAY); } }; @@ -75,7 +89,16 @@ status_t BnCameraServiceListener::onTransact( int32_t cameraId = data.readInt32(); onStatusChanged(status, cameraId); - reply->writeNoException(); + + return NO_ERROR; + } break; + case TORCH_STATUS_CHANGED: { + CHECK_INTERFACE(ICameraServiceListener, data, reply); + + TorchStatus status = static_cast(data.readInt32()); + String16 cameraId = data.readString16(); + + onTorchStatusChanged(status, cameraId); return NO_ERROR; } break; diff --git a/camera/tests/ProCameraTests.cpp b/camera/tests/ProCameraTests.cpp index 1f5867a..6212678 100644 --- a/camera/tests/ProCameraTests.cpp +++ b/camera/tests/ProCameraTests.cpp @@ -89,6 +89,12 @@ struct ServiceListener : public BnCameraServiceListener { mCondition.broadcast(); } + void onTorchStatusChanged(TorchStatus status, const String16& cameraId) { + dout << "On torch status changed: 0x" << std::hex + << (unsigned int) status << " cameraId " << cameraId.string() + << std::endl; + } + status_t waitForStatusChange(Status& newStatus) { Mutex::Autolock al(mMutex); diff --git a/include/camera/ICameraService.h b/include/camera/ICameraService.h index f7f06bb..cc41efe 100644 --- a/include/camera/ICameraService.h +++ b/include/camera/ICameraService.h @@ -53,6 +53,7 @@ public: GET_LEGACY_PARAMETERS, SUPPORTS_CAMERA_API, CONNECT_LEGACY, + SET_TORCH_MODE, }; enum { @@ -142,6 +143,12 @@ public: int clientUid, /*out*/ sp& device) = 0; + + /** + * Turn on or off a camera's torch mode. + */ + virtual status_t setTorchMode(const String16& cameraId, bool enabled, + const sp& clientBinder) = 0; }; // ---------------------------------------------------------------------------- diff --git a/include/camera/ICameraServiceListener.h b/include/camera/ICameraServiceListener.h index 0a0e43a..9e8b912 100644 --- a/include/camera/ICameraServiceListener.h +++ b/include/camera/ICameraServiceListener.h @@ -66,9 +66,33 @@ public: STATUS_UNKNOWN = 0xFFFFFFFF, }; + /** + * The torch mode status of a camera. + * + * Initial status will be transmitted with onTorchStatusChanged immediately + * after this listener is added to the service listener list. + */ + enum TorchStatus { + // The camera's torch mode has become available to use via + // setTorchMode(). + TORCH_STATUS_AVAILABLE = TORCH_MODE_STATUS_AVAILABLE, + // The camera's torch mode has become not available to use via + // setTorchMode(). + TORCH_STATUS_NOT_AVAILABLE = TORCH_MODE_STATUS_RESOURCE_BUSY, + // The camera's torch mode has been turned off by setTorchMode(). + TORCH_STATUS_OFF = TORCH_MODE_STATUS_OFF, + // The camera's torch mode has been turned on by setTorchMode(). + TORCH_STATUS_ON = 0x80000000, + + // Use to initialize variables only + TORCH_STATUS_UNKNOWN = 0xFFFFFFFF, + }; + DECLARE_META_INTERFACE(CameraServiceListener); virtual void onStatusChanged(Status status, int32_t cameraId) = 0; + + virtual void onTorchStatusChanged(TorchStatus status, const String16& cameraId) = 0; }; // ---------------------------------------------------------------------------- diff --git a/services/camera/libcameraservice/Android.mk b/services/camera/libcameraservice/Android.mk index de9551d..5d6423a 100644 --- a/services/camera/libcameraservice/Android.mk +++ b/services/camera/libcameraservice/Android.mk @@ -23,6 +23,7 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES:= \ CameraService.cpp \ CameraDeviceFactory.cpp \ + CameraFlashlight.cpp \ common/Camera2ClientBase.cpp \ common/CameraDeviceBase.cpp \ common/CameraModule.cpp \ diff --git a/services/camera/libcameraservice/CameraFlashlight.cpp b/services/camera/libcameraservice/CameraFlashlight.cpp new file mode 100644 index 0000000..00a70eb --- /dev/null +++ b/services/camera/libcameraservice/CameraFlashlight.cpp @@ -0,0 +1,520 @@ +/* + * Copyright (C) 2015 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 "CameraFlashlight" +#define ATRACE_TAG ATRACE_TAG_CAMERA +#define LOG_NDEBUG 0 + +#include +#include +#include + +#include "camera/CameraMetadata.h" +#include "CameraFlashlight.h" +#include "gui/IGraphicBufferConsumer.h" +#include "gui/BufferQueue.h" +#include "camera/camera2/CaptureRequest.h" +#include "CameraDeviceFactory.h" + + +namespace android { + +CameraFlashlight::CameraFlashlight(CameraModule& cameraModule, + const camera_module_callbacks_t& callbacks) : + mCameraModule(&cameraModule), + mCallbacks(&callbacks) { +} + +CameraFlashlight::~CameraFlashlight() { +} + +status_t CameraFlashlight::createFlashlightControl(const String16& cameraId) { + ALOGV("%s: creating a flash light control for camera %s", __FUNCTION__, + cameraId.string()); + if (mFlashControl != NULL) { + return INVALID_OPERATION; + } + + status_t res = OK; + + if (mCameraModule->getRawModule()->module_api_version >= + CAMERA_MODULE_API_VERSION_2_4) { + mFlashControl = new FlashControl(*mCameraModule, *mCallbacks); + if (mFlashControl == NULL) { + ALOGV("%s: cannot create flash control for module api v2.4+", + __FUNCTION__); + return NO_MEMORY; + } + } else { + uint32_t deviceVersion = CAMERA_DEVICE_API_VERSION_1_0; + + if (mCameraModule->getRawModule()->module_api_version >= + CAMERA_MODULE_API_VERSION_2_0) { + camera_info info; + res = mCameraModule->getCameraInfo( + atoi(String8(cameraId).string()), &info); + if (res) { + ALOGV("%s: failed to get camera info for camera %s", + __FUNCTION__, cameraId.string()); + return res; + } + deviceVersion = info.device_version; + } + + if (deviceVersion >= CAMERA_DEVICE_API_VERSION_2_0) { + CameraDeviceClientFlashControl *flashControl = + new CameraDeviceClientFlashControl(*mCameraModule, + *mCallbacks); + if (!flashControl) { + return NO_MEMORY; + } + + mFlashControl = flashControl; + } + else { + // todo: implement for device api 1 + return INVALID_OPERATION; + } + } + + return OK; +} + +status_t CameraFlashlight::setTorchMode(const String16& cameraId, bool enabled) { + if (!mCameraModule) { + return NO_INIT; + } + + ALOGV("%s: set torch mode of camera %s to %d", __FUNCTION__, + cameraId.string(), enabled); + + status_t res = OK; + Mutex::Autolock l(mLock); + + if (mFlashControl == NULL) { + res = createFlashlightControl(cameraId); + if (res) { + return res; + } + res = mFlashControl->setTorchMode(cameraId, enabled); + return res; + } + + // if flash control already exists, turning on torch mode may fail if it's + // tied to another camera device for module v2.3 and below. + res = mFlashControl->setTorchMode(cameraId, enabled); + if (res == BAD_INDEX) { + // flash control is tied to another camera device, need to close it and + // try again. + mFlashControl.clear(); + res = createFlashlightControl(cameraId); + if (res) { + return res; + } + res = mFlashControl->setTorchMode(cameraId, enabled); + } + + return res; +} + +bool CameraFlashlight::hasFlashUnit(const String16& cameraId) { + status_t res; + + Mutex::Autolock l(mLock); + + if (mFlashControl == NULL) { + res = createFlashlightControl(cameraId); + if (res) { + ALOGE("%s: failed to create flash control for %s ", + __FUNCTION__, cameraId.string()); + return false; + } + } + + bool flashUnit = false; + + // if flash control already exists, querying if a camera device has a flash + // unit may fail if it's module v1 + res = mFlashControl->hasFlashUnit(cameraId, &flashUnit); + if (res == BAD_INDEX) { + // need to close the flash control before query. + mFlashControl.clear(); + res = createFlashlightControl(cameraId); + if (res) { + ALOGE("%s: failed to create flash control for %s ", __FUNCTION__, + cameraId.string()); + return false; + } + res = mFlashControl->hasFlashUnit(cameraId, &flashUnit); + if (res) { + flashUnit = false; + } + } + + return flashUnit; +} + +status_t CameraFlashlight::prepareDeviceOpen() { + ALOGV("%s: prepare for device open", __FUNCTION__); + + Mutex::Autolock l(mLock); + + if (mCameraModule && mCameraModule->getRawModule()->module_api_version < + CAMERA_MODULE_API_VERSION_2_4) { + // framework is going to open a camera device, all flash light control + // should be closed for backward compatible support. + if (mFlashControl != NULL) { + mFlashControl.clear(); + } + } + + return OK; +} + + +FlashControlBase::~FlashControlBase() { +} + + +FlashControl::FlashControl(CameraModule& cameraModule, + const camera_module_callbacks_t& callbacks) : + mCameraModule(&cameraModule) { +} + +FlashControl::~FlashControl() { +} + +status_t FlashControl::hasFlashUnit(const String16& cameraId, bool *hasFlash) { + if (!hasFlash) { + return BAD_VALUE; + } + + *hasFlash = false; + + Mutex::Autolock l(mLock); + + if (!mCameraModule) { + return NO_INIT; + } + + camera_info info; + status_t res = mCameraModule->getCameraInfo(atoi(String8(cameraId).string()), + &info); + if (res != 0) { + return res; + } + + CameraMetadata metadata; + metadata = info.static_camera_characteristics; + camera_metadata_entry flashAvailable = + metadata.find(ANDROID_FLASH_INFO_AVAILABLE); + if (flashAvailable.count == 1 && flashAvailable.data.u8[0] == 1) { + *hasFlash = true; + } + + return OK; +} + +status_t FlashControl::setTorchMode(const String16& cameraId, bool enabled) { + ALOGV("%s: set camera %s torch mode to %d", __FUNCTION__, + cameraId.string(), enabled); + + Mutex::Autolock l(mLock); + if (!mCameraModule) { + return NO_INIT; + } + + return mCameraModule->setTorchMode(String8(cameraId).string(), enabled); +} + +CameraDeviceClientFlashControl::CameraDeviceClientFlashControl( + CameraModule& cameraModule, + const camera_module_callbacks_t& callbacks) : + mCameraModule(&cameraModule), + mCallbacks(&callbacks), + mTorchEnabled(false), + mMetadata(NULL) { +} + +CameraDeviceClientFlashControl::~CameraDeviceClientFlashControl() { + if (mDevice != NULL) { + mDevice->flush(); + mDevice->deleteStream(mStreamId); + mDevice.clear(); + } + if (mMetadata) { + delete mMetadata; + } + + mAnw.clear(); + mSurfaceTexture.clear(); + mProducer.clear(); + mConsumer.clear(); + + if (mTorchEnabled) { + if (mCallbacks) { + ALOGV("%s: notify the framework that torch was turned off", + __FUNCTION__); + mCallbacks->torch_mode_status_change(mCallbacks, + String8(mCameraId).string(), TORCH_MODE_STATUS_OFF); + } + } +} + +status_t CameraDeviceClientFlashControl::initializeSurface(int32_t width, + int32_t height) { + status_t res; + BufferQueue::createBufferQueue(&mProducer, &mConsumer); + + mSurfaceTexture = new GLConsumer(mConsumer, 0, GLConsumer::TEXTURE_EXTERNAL, + true, true); + if (mSurfaceTexture == NULL) { + return NO_MEMORY; + } + + int32_t format = HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED; + res = mSurfaceTexture->setDefaultBufferSize(width, height); + if (res) { + return res; + } + res = mSurfaceTexture->setDefaultBufferFormat(format); + if (res) { + return res; + } + + bool useAsync = false; + int32_t consumerUsage; + res = mProducer->query(NATIVE_WINDOW_CONSUMER_USAGE_BITS, &consumerUsage); + if (res) { + return res; + } + + if (consumerUsage & GraphicBuffer::USAGE_HW_TEXTURE) { + useAsync = true; + } + + mAnw = new Surface(mProducer, useAsync); + if (mAnw == NULL) { + return NO_MEMORY; + } + res = mDevice->createStream(mAnw, width, height, format, &mStreamId); + if (res) { + return res; + } + + res = mDevice->configureStreams(); + if (res) { + return res; + } + + return res; +} + +status_t CameraDeviceClientFlashControl::getSmallestSurfaceSize( + const camera_info& info, int32_t *width, int32_t *height) { + if (!width || !height) { + return BAD_VALUE; + } + + int32_t w = INT32_MAX; + int32_t h = 1; + + CameraMetadata metadata; + metadata = info.static_camera_characteristics; + camera_metadata_entry streamConfigs = + metadata.find(ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS); + for (size_t i = 0; i < streamConfigs.count; i += 4) { + int32_t fmt = streamConfigs.data.i32[i]; + if (fmt == ANDROID_SCALER_AVAILABLE_FORMATS_IMPLEMENTATION_DEFINED) { + int32_t ww = streamConfigs.data.i32[i + 1]; + int32_t hh = streamConfigs.data.i32[i + 2]; + + if (w* h > ww * hh) { + w = ww; + h = hh; + } + } + } + + if (w == INT32_MAX) { + return NAME_NOT_FOUND; + } + + *width = w; + *height = h; + + return OK; +} + +status_t CameraDeviceClientFlashControl::connectCameraDevice( + const String16& cameraId) { + String8 id = String8(cameraId); + camera_info info; + status_t res = mCameraModule->getCameraInfo(atoi(id.string()), &info); + if (res != 0) { + ALOGE("%s: failed to get camera info for camera %s", __FUNCTION__, + mCameraId.string()); + return res; + } + + mDevice = CameraDeviceFactory::createDevice(atoi(id.string())); + if (mDevice == NULL) { + return NO_MEMORY; + } + + res = mDevice->initialize(mCameraModule); + if (res) { + goto fail; + } + + int32_t width, height; + res = getSmallestSurfaceSize(info, &width, &height); + if (res) { + return res; + } + res = initializeSurface(width, height); + if (res) { + goto fail; + } + + mCameraId = cameraId; + + return OK; + +fail: + mDevice.clear(); + return res; +} + + +status_t CameraDeviceClientFlashControl::hasFlashUnit(const String16& cameraId, + bool *hasFlash) { + ALOGV("%s: checking if camera %s has a flash unit", __FUNCTION__, + cameraId.string()); + + Mutex::Autolock l(mLock); + return hasFlashUnitLocked(cameraId, hasFlash); + +} + +status_t CameraDeviceClientFlashControl::hasFlashUnitLocked( + const String16& cameraId, bool *hasFlash) { + if (!mCameraModule) { + ALOGE("%s: camera module is NULL", __FUNCTION__); + return NO_INIT; + } + + if (!hasFlash) { + return BAD_VALUE; + } + + camera_info info; + status_t res = mCameraModule->getCameraInfo( + atoi(String8(cameraId).string()), &info); + if (res != 0) { + ALOGE("%s: failed to get camera info for camera %s", __FUNCTION__, + cameraId.string()); + return res; + } + + CameraMetadata metadata; + metadata = info.static_camera_characteristics; + camera_metadata_entry flashAvailable = + metadata.find(ANDROID_FLASH_INFO_AVAILABLE); + if (flashAvailable.count == 1 && flashAvailable.data.u8[0] == 1) { + *hasFlash = true; + } + + return OK; +} + +status_t CameraDeviceClientFlashControl::submitTorchRequest(bool enabled) { + status_t res; + + if (mMetadata == NULL) { + mMetadata = new CameraMetadata(); + if (mMetadata == NULL) { + return NO_MEMORY; + } + res = mDevice->createDefaultRequest( + CAMERA3_TEMPLATE_PREVIEW, mMetadata); + if (res) { + return res; + } + } + + uint8_t torchOn = enabled ? ANDROID_FLASH_MODE_TORCH : + ANDROID_FLASH_MODE_OFF; + + mMetadata->update(ANDROID_FLASH_MODE, &torchOn, 1); + mMetadata->update(ANDROID_REQUEST_OUTPUT_STREAMS, &mStreamId, 1); + + int32_t requestId = 0; + mMetadata->update(ANDROID_REQUEST_ID, &requestId, 1); + + List metadataRequestList; + metadataRequestList.push_back(*mMetadata); + + int64_t lastFrameNumber = 0; + res = mDevice->captureList(metadataRequestList, &lastFrameNumber); + + return res; +} + + +status_t CameraDeviceClientFlashControl::setTorchMode( + const String16& cameraId, bool enabled) { + bool hasFlash = false; + + Mutex::Autolock l(mLock); + status_t res = hasFlashUnitLocked(cameraId, &hasFlash); + + // pre-check + if (enabled) { + // invalid camera? + if (res) { + return -EINVAL; + } + // no flash unit? + if (!hasFlash) { + return -ENOSYS; + } + // already opened for a different device? + if (mDevice != NULL && cameraId != mCameraId) { + return BAD_INDEX; + } + } else if (mDevice == NULL || cameraId != mCameraId) { + // disabling the torch mode of an un-opened or different device. + return OK; + } + + if (mDevice == NULL) { + res = connectCameraDevice(cameraId); + if (res) { + return res; + } + } + + res = submitTorchRequest(enabled); + if (res) { + return res; + } + + mTorchEnabled = enabled; + return OK; +} + +} diff --git a/services/camera/libcameraservice/CameraFlashlight.h b/services/camera/libcameraservice/CameraFlashlight.h new file mode 100644 index 0000000..a0de0b0 --- /dev/null +++ b/services/camera/libcameraservice/CameraFlashlight.h @@ -0,0 +1,149 @@ +/* + * Copyright (C) 2015 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. + */ + +#ifndef ANDROID_SERVERS_CAMERA_CAMERAFLASHLIGHT_H +#define ANDROID_SERVERS_CAMERA_CAMERAFLASHLIGHT_H + +#include "hardware/camera_common.h" +#include "utils/KeyedVector.h" +#include "gui/GLConsumer.h" +#include "gui/Surface.h" +#include "common/CameraDeviceBase.h" + +namespace android { + +/** + * FlashControlBase is a base class for flash control. It defines the functions + * that a flash control for each camera module/device version should implement. + */ +class FlashControlBase : public virtual VirtualLightRefBase { + public: + virtual ~FlashControlBase(); + + // Whether a camera device has a flash unit. Calling this function may + // cause the torch mode to be turned off in HAL v1 devices. If + // previously-on torch mode is turned off, + // callbacks.torch_mode_status_change() should be invoked. + virtual status_t hasFlashUnit(const String16& cameraId, + bool *hasFlash) = 0; + + // set the torch mode to on or off. + virtual status_t setTorchMode(const String16& cameraId, + bool enabled) = 0; +}; + +/** + * CameraFlashlight can be used by camera service to control flashflight. + */ +class CameraFlashlight : public virtual VirtualLightRefBase { + public: + CameraFlashlight(CameraModule& cameraModule, + const camera_module_callbacks_t& callbacks); + virtual ~CameraFlashlight(); + + // set the torch mode to on or off. + status_t setTorchMode(const String16& cameraId, bool enabled); + + // Whether a camera device has a flash unit. Calling this function may + // cause the torch mode to be turned off in HAL v1 devices. + bool hasFlashUnit(const String16& cameraId); + + // Notify CameraFlashlight that camera service is going to open a camera + // device. CameraFlashlight will free the resources that may cause the + // camera open to fail. Camera service must call this function before + // opening a camera device. + status_t prepareDeviceOpen(); + + private: + // create flashlight control based on camera module API and camera + // device API versions. + status_t createFlashlightControl(const String16& cameraId); + + sp mFlashControl; + CameraModule *mCameraModule; + const camera_module_callbacks_t *mCallbacks; + + Mutex mLock; +}; + +/** + * Flash control for camera module v2.4 and above. + */ +class FlashControl : public FlashControlBase { + public: + FlashControl(CameraModule& cameraModule, + const camera_module_callbacks_t& callbacks); + virtual ~FlashControl(); + + // FlashControlBase + status_t hasFlashUnit(const String16& cameraId, bool *hasFlash); + status_t setTorchMode(const String16& cameraId, bool enabled); + + private: + CameraModule *mCameraModule; + + Mutex mLock; +}; + +/** + * Flash control for camera module <= v2.3 and camera HAL v2-v3 + */ +class CameraDeviceClientFlashControl : public FlashControlBase { + public: + CameraDeviceClientFlashControl(CameraModule& cameraModule, + const camera_module_callbacks_t& callbacks); + virtual ~CameraDeviceClientFlashControl(); + + // FlashControlBase + status_t setTorchMode(const String16& cameraId, bool enabled); + status_t hasFlashUnit(const String16& cameraId, bool *hasFlash); + + private: + // connect to a camera device + status_t connectCameraDevice(const String16& cameraId); + + // initialize a surface + status_t initializeSurface(int32_t width, int32_t height); + + // submit a request with the given torch mode + status_t submitTorchRequest(bool enabled); + + // get the smallest surface size of IMPLEMENTATION_DEFINED + status_t getSmallestSurfaceSize(const camera_info& info, int32_t *width, + int32_t *height); + + status_t hasFlashUnitLocked(const String16& cameraId, bool *hasFlash); + + CameraModule *mCameraModule; + const camera_module_callbacks_t *mCallbacks; + String16 mCameraId; + bool mTorchEnabled; + CameraMetadata *mMetadata; + + sp mDevice; + + sp mProducer; + sp mConsumer; + sp mSurfaceTexture; + sp mAnw; + int32_t mStreamId; + + Mutex mLock; +}; + +} // namespace android + +#endif diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp index 485b979..488fc42 100644 --- a/services/camera/libcameraservice/CameraService.cpp +++ b/services/camera/libcameraservice/CameraService.cpp @@ -86,6 +86,38 @@ static void camera_device_status_change( camera_id, new_status); } + +static void torch_mode_status_change( + const struct camera_module_callbacks* callbacks, + const char* camera_id, + int new_status) { + if (!callbacks || !camera_id) { + ALOGE("%s invalid parameters. callbacks %p, camera_id %p", __FUNCTION__, + callbacks, camera_id); + } + sp cs = const_cast( + static_cast(callbacks)); + + ICameraServiceListener::TorchStatus status; + switch (new_status) { + case TORCH_MODE_STATUS_AVAILABLE: + status = ICameraServiceListener::TORCH_STATUS_AVAILABLE; + break; + case TORCH_MODE_STATUS_RESOURCE_BUSY: + status = ICameraServiceListener::TORCH_STATUS_NOT_AVAILABLE; + break; + case TORCH_MODE_STATUS_OFF: + status = ICameraServiceListener::TORCH_STATUS_OFF; + break; + default: + ALOGE("Unknown torch status %d", new_status); + return; + } + + cs->onTorchStatusChanged( + String16(camera_id), + status); +} } // extern "C" // ---------------------------------------------------------------------------- @@ -95,7 +127,7 @@ static void camera_device_status_change( static CameraService *gCameraService; CameraService::CameraService() - :mSoundRef(0), mModule(0) + :mSoundRef(0), mModule(0), mFlashlight(0) { ALOGI("CameraService started (pid=%d)", getpid()); gCameraService = this; @@ -105,6 +137,8 @@ CameraService::CameraService() } this->camera_device_status_change = android::camera_device_status_change; + this->torch_mode_status_change = android::torch_mode_status_change; + } void CameraService::onFirstRef() @@ -121,6 +155,8 @@ void CameraService::onFirstRef() } else { mModule = new CameraModule(rawModule); + mFlashlight = new CameraFlashlight(*mModule, *this); + const hw_module_t *common = mModule->getRawModule(); ALOGI("Loaded \"%s\" camera module", common->name); mNumberOfCameras = mModule->getNumberOfCameras(); @@ -131,6 +167,12 @@ void CameraService::onFirstRef() } for (int i = 0; i < mNumberOfCameras; i++) { setCameraFree(i); + + String16 cameraName = String16(String8::format("%d", i)); + if (mFlashlight->hasFlashUnit(cameraName)) { + mTorchStatusMap.add(cameraName, + ICameraServiceListener::TORCH_STATUS_AVAILABLE); + } } if (common->module_api_version >= CAMERA_MODULE_API_VERSION_2_1) { @@ -225,6 +267,37 @@ void CameraService::onDeviceStatusChanged(int cameraId, } +void CameraService::onTorchStatusChanged(const String16& cameraId, + ICameraServiceListener::TorchStatus newStatus) { + Mutex::Autolock al(mTorchStatusMutex); + onTorchStatusChangedLocked(cameraId, newStatus); +} + +void CameraService::onTorchStatusChangedLocked(const String16& cameraId, + ICameraServiceListener::TorchStatus newStatus) { + ALOGI("%s: Torch status changed for cameraId=%s, newStatus=%d", + __FUNCTION__, cameraId.string(), newStatus); + + if (getTorchStatusLocked(cameraId) == newStatus) { + ALOGE("%s: Torch state transition to the same status 0x%x not allowed", + __FUNCTION__, (uint32_t)newStatus); + return; + } + + status_t res = setTorchStatusLocked(cameraId, newStatus); + if (res) { + ALOGE("%s: Failed to set the torch status", __FUNCTION__, + (uint32_t)newStatus); + return; + } + + Vector >::const_iterator it; + for (it = mListenerList.begin(); it != mListenerList.end(); ++it) { + (*it)->onTorchStatusChanged(newStatus, cameraId); + } +} + + int32_t CameraService::getNumberOfCameras() { return mNumberOfCameras; } @@ -676,6 +749,9 @@ status_t CameraService::connectHelperLocked( int halVersion, bool legacyMode) { + // give flashlight a chance to close devices if necessary. + mFlashlight->prepareDeviceOpen(); + int facing = -1; int deviceVersion = getDeviceVersion(cameraId, &facing); @@ -852,6 +928,47 @@ status_t CameraService::connectLegacy( return OK; } +status_t CameraService::setTorchMode(const String16& cameraId, bool enabled, + const sp& clientBinder) { + if (enabled && clientBinder == NULL) { + ALOGE("%s: torch client binder is NULL", __FUNCTION__); + return -ENOSYS; + } + + Mutex::Autolock al(mTorchStatusMutex); + status_t res = mFlashlight->setTorchMode(cameraId, enabled); + if (res) { + ALOGE("%s: setting torch mode of camera %s to %d failed", __FUNCTION__, + cameraId.string(), enabled); + return res; + } + + // update the link to client's death + ssize_t index = mTorchClientMap.indexOfKey(cameraId); + if (enabled) { + if (index == NAME_NOT_FOUND) { + mTorchClientMap.add(cameraId, clientBinder); + } else { + const sp oldBinder = mTorchClientMap.valueAt(index); + oldBinder->unlinkToDeath(this); + + mTorchClientMap.replaceValueAt(index, clientBinder); + } + clientBinder->linkToDeath(this); + } else if (index != NAME_NOT_FOUND) { + sp oldBinder = mTorchClientMap.valueAt(index); + oldBinder->unlinkToDeath(this); + } + + // notify the listeners the change. + ICameraServiceListener::TorchStatus status = enabled ? + ICameraServiceListener::TORCH_STATUS_ON : + ICameraServiceListener::TORCH_STATUS_OFF; + onTorchStatusChangedLocked(cameraId, status); + + return OK; +} + status_t CameraService::connectFinishUnsafe(const sp& client, const sp& remoteCallback) { status_t status = client->initialize(mModule); @@ -977,6 +1094,9 @@ status_t CameraService::connectDevice( int facing = -1; int deviceVersion = getDeviceVersion(cameraId, &facing); + // give flashlight a chance to close devices if necessary. + mFlashlight->prepareDeviceOpen(); + switch(deviceVersion) { case CAMERA_DEVICE_API_VERSION_1_0: ALOGW("Camera using old HAL version: %d", deviceVersion); @@ -1048,6 +1168,16 @@ status_t CameraService::addListener( } } + /* Immediately signal current torch status to this listener only */ + { + Mutex::Autolock al(mTorchStatusMutex); + for (size_t i = 0; i < mTorchStatusMap.size(); i++ ) { + listener->onTorchStatusChanged(mTorchStatusMap.valueAt(i), + mTorchStatusMap.keyAt(i)); + } + + } + return OK; } status_t CameraService::removeListener( @@ -1727,6 +1857,23 @@ status_t CameraService::dump(int fd, const Vector& args) { return NO_ERROR; } +void CameraService::handleTorchClientBinderDied(const wp &who) { + Mutex::Autolock al(mTorchStatusMutex); + for (size_t i = 0; i < mTorchClientMap.size(); i++) { + if (mTorchClientMap[i] == who) { + // turn off the torch mode that was turned on by dead client + String16 cameraId = mTorchClientMap.keyAt(i); + mFlashlight->setTorchMode(cameraId, false); + mTorchClientMap.removeItemsAt(i); + + // notify torch mode was turned off + onTorchStatusChangedLocked(cameraId, + ICameraServiceListener::TORCH_STATUS_OFF); + break; + } + } +} + /*virtual*/void CameraService::binderDied( const wp &who) { @@ -1737,6 +1884,10 @@ status_t CameraService::dump(int fd, const Vector& args) { ALOGV("java clients' binder died"); + // check torch client + handleTorchClientBinderDied(who); + + // check camera device client sp cameraClient = getClientByRemote(who); if (cameraClient == 0) { @@ -1830,4 +1981,27 @@ ICameraServiceListener::Status CameraService::getStatus(int cameraId) const { return mStatusList[cameraId]; } +ICameraServiceListener::TorchStatus CameraService::getTorchStatusLocked( + const String16& cameraId) const { + ssize_t index = mTorchStatusMap.indexOfKey(cameraId); + if (index == NAME_NOT_FOUND) { + return ICameraServiceListener::TORCH_STATUS_NOT_AVAILABLE; + } + + return mTorchStatusMap.valueAt(index); +} + +status_t CameraService::setTorchStatusLocked(const String16& cameraId, + ICameraServiceListener::TorchStatus status) { + ssize_t index = mTorchStatusMap.indexOfKey(cameraId); + if (index == NAME_NOT_FOUND) { + return BAD_VALUE; + } + ICameraServiceListener::TorchStatus& item = + mTorchStatusMap.editValueAt(index); + item = status; + + return OK; +} + }; // namespace android diff --git a/services/camera/libcameraservice/CameraService.h b/services/camera/libcameraservice/CameraService.h index 7d0df3a..84bcdb8 100644 --- a/services/camera/libcameraservice/CameraService.h +++ b/services/camera/libcameraservice/CameraService.h @@ -36,6 +36,8 @@ #include #include +#include "CameraFlashlight.h" + #include "common/CameraModule.h" @@ -70,6 +72,9 @@ public: // HAL Callbacks virtual void onDeviceStatusChanged(int cameraId, int newStatus); + virtual void onTorchStatusChanged(const String16& cameraId, + ICameraServiceListener::TorchStatus + newStatus); ///////////////////////////////////////////////////////////////////// // ICameraService @@ -112,6 +117,9 @@ public: /*out*/ String16* parameters); + virtual status_t setTorchMode(const String16& cameraId, bool enabled, + const sp& clientBinder); + // OK = supports api of that version, -EOPNOTSUPP = does not support virtual status_t supportsCameraApi( int cameraId, int apiVersion); @@ -408,6 +416,32 @@ private: int32_t cameraId, const StatusVector *rejectSourceStates = NULL); + // flashlight control + sp mFlashlight; + // guard mTorchStatusMap and mTorchClientMap + Mutex mTorchStatusMutex; + // camera id -> torch status + KeyedVector mTorchStatusMap; + // camera id -> torch client binder + // only store the last client that turns on each camera's torch mode + KeyedVector > mTorchClientMap; + + // check and handle if torch client's process has died + void handleTorchClientBinderDied(const wp &who); + + // handle torch mode status change and invoke callbacks. mTorchStatusMutex + // should be locked. + void onTorchStatusChangedLocked(const String16& cameraId, + ICameraServiceListener::TorchStatus newStatus); + + // get a camera's torch status. mTorchStatusMutex should be locked. + ICameraServiceListener::TorchStatus getTorchStatusLocked( + const String16 &cameraId) const; + + // set a camera's torch status. mTorchStatusMutex should be locked. + status_t setTorchStatusLocked(const String16 &cameraId, + ICameraServiceListener::TorchStatus status); + // IBinder::DeathRecipient implementation virtual void binderDied(const wp &who); -- cgit v1.1