summaryrefslogtreecommitdiffstats
path: root/camera
diff options
context:
space:
mode:
Diffstat (limited to 'camera')
-rw-r--r--camera/Android.mk20
-rw-r--r--camera/Camera.cpp142
-rw-r--r--camera/CameraBase.cpp218
-rw-r--r--camera/CameraMetadata.cpp411
-rw-r--r--camera/CameraParameters.cpp5
-rw-r--r--camera/ICamera.cpp30
-rw-r--r--camera/ICameraService.cpp76
-rw-r--r--camera/ICameraServiceListener.cpp86
-rw-r--r--camera/IProCameraCallbacks.cpp128
-rw-r--r--camera/IProCameraUser.cpp426
-rw-r--r--camera/ProCamera.cpp431
-rw-r--r--camera/tests/Android.mk38
-rw-r--r--camera/tests/ProCameraTests.cpp1281
-rw-r--r--camera/tests/main.cpp27
14 files changed, 3163 insertions, 156 deletions
diff --git a/camera/Android.mk b/camera/Android.mk
index 7286f92..fa518ff 100644
--- a/camera/Android.mk
+++ b/camera/Android.mk
@@ -1,22 +1,36 @@
-LOCAL_PATH:= $(call my-dir)
+CAMERA_CLIENT_LOCAL_PATH:= $(call my-dir)
+include $(call all-subdir-makefiles)
include $(CLEAR_VARS)
+LOCAL_PATH := $(CAMERA_CLIENT_LOCAL_PATH)
+
LOCAL_SRC_FILES:= \
Camera.cpp \
+ CameraMetadata.cpp \
CameraParameters.cpp \
ICamera.cpp \
ICameraClient.cpp \
ICameraService.cpp \
+ ICameraServiceListener.cpp \
ICameraRecordingProxy.cpp \
- ICameraRecordingProxyListener.cpp
+ ICameraRecordingProxyListener.cpp \
+ IProCameraUser.cpp \
+ IProCameraCallbacks.cpp \
+ ProCamera.cpp \
+ CameraBase.cpp \
LOCAL_SHARED_LIBRARIES := \
libcutils \
libutils \
+ liblog \
libbinder \
libhardware \
libui \
- libgui
+ libgui \
+ libcamera_metadata \
+
+LOCAL_C_INCLUDES += \
+ system/media/camera/include \
LOCAL_MODULE:= libcamera_client
diff --git a/camera/Camera.cpp b/camera/Camera.cpp
index d43cb0b..1b136de 100644
--- a/camera/Camera.cpp
+++ b/camera/Camera.cpp
@@ -19,6 +19,7 @@
#define LOG_TAG "Camera"
#include <utils/Log.h>
#include <utils/threads.h>
+#include <utils/String16.h>
#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>
#include <binder/IMemory.h>
@@ -26,46 +27,16 @@
#include <camera/Camera.h>
#include <camera/ICameraRecordingProxyListener.h>
#include <camera/ICameraService.h>
+#include <camera/ICamera.h>
-#include <gui/ISurfaceTexture.h>
+#include <gui/IGraphicBufferProducer.h>
#include <gui/Surface.h>
namespace android {
-// client singleton for camera service binder interface
-Mutex Camera::mLock;
-sp<ICameraService> Camera::mCameraService;
-sp<Camera::DeathNotifier> Camera::mDeathNotifier;
-
-// establish binder interface to camera service
-const sp<ICameraService>& Camera::getCameraService()
-{
- Mutex::Autolock _l(mLock);
- if (mCameraService.get() == 0) {
- sp<IServiceManager> sm = defaultServiceManager();
- sp<IBinder> binder;
- do {
- binder = sm->getService(String16("media.camera"));
- if (binder != 0)
- break;
- ALOGW("CameraService not published, waiting...");
- usleep(500000); // 0.5 s
- } while(true);
- if (mDeathNotifier == NULL) {
- mDeathNotifier = new DeathNotifier();
- }
- binder->linkToDeath(mDeathNotifier);
- mCameraService = interface_cast<ICameraService>(binder);
- }
- ALOGE_IF(mCameraService==0, "no CameraService!?");
- return mCameraService;
-}
-
-// ---------------------------------------------------------------------------
-
-Camera::Camera()
+Camera::Camera(int cameraId)
+ : CameraBase(cameraId)
{
- init();
}
// construct a camera client from an existing camera remote
@@ -77,7 +48,7 @@ sp<Camera> Camera::create(const sp<ICamera>& camera)
return 0;
}
- sp<Camera> c = new Camera();
+ sp<Camera> c = new Camera(-1);
if (camera->connect(c) == NO_ERROR) {
c->mStatus = NO_ERROR;
c->mCamera = camera;
@@ -87,11 +58,6 @@ sp<Camera> Camera::create(const sp<ICamera>& camera)
return 0;
}
-void Camera::init()
-{
- mStatus = UNKNOWN_ERROR;
-}
-
Camera::~Camera()
{
// We don't need to call disconnect() here because if the CameraService
@@ -102,45 +68,10 @@ Camera::~Camera()
// deadlock if we call any method of ICamera here.
}
-int32_t Camera::getNumberOfCameras()
-{
- const sp<ICameraService>& cs = getCameraService();
- if (cs == 0) return 0;
- return cs->getNumberOfCameras();
-}
-
-status_t Camera::getCameraInfo(int cameraId,
- struct CameraInfo* cameraInfo) {
- const sp<ICameraService>& cs = getCameraService();
- if (cs == 0) return UNKNOWN_ERROR;
- return cs->getCameraInfo(cameraId, cameraInfo);
-}
-
-sp<Camera> Camera::connect(int cameraId)
-{
- ALOGV("connect");
- sp<Camera> c = new Camera();
- const sp<ICameraService>& cs = getCameraService();
- if (cs != 0) {
- c->mCamera = cs->connect(c, cameraId);
- }
- if (c->mCamera != 0) {
- c->mCamera->asBinder()->linkToDeath(c);
- c->mStatus = NO_ERROR;
- } else {
- c.clear();
- }
- return c;
-}
-
-void Camera::disconnect()
+sp<Camera> Camera::connect(int cameraId, const String16& clientPackageName,
+ int clientUid)
{
- ALOGV("disconnect");
- if (mCamera != 0) {
- mCamera->disconnect();
- mCamera->asBinder()->unlinkToDeath(this);
- mCamera = 0;
- }
+ return CameraBaseT::connect(cameraId, clientPackageName, clientUid);
}
status_t Camera::reconnect()
@@ -151,11 +82,6 @@ status_t Camera::reconnect()
return c->connect(this);
}
-sp<ICamera> Camera::remote()
-{
- return mCamera;
-}
-
status_t Camera::lock()
{
sp <ICamera> c = mCamera;
@@ -170,32 +96,14 @@ status_t Camera::unlock()
return c->unlock();
}
-// pass the buffered Surface to the camera service
-status_t Camera::setPreviewDisplay(const sp<Surface>& surface)
-{
- ALOGV("setPreviewDisplay(%p)", surface.get());
- sp <ICamera> c = mCamera;
- if (c == 0) return NO_INIT;
- if (surface != 0) {
- return c->setPreviewDisplay(surface);
- } else {
- ALOGD("app passed NULL surface");
- return c->setPreviewDisplay(0);
- }
-}
-
-// pass the buffered ISurfaceTexture to the camera service
-status_t Camera::setPreviewTexture(const sp<ISurfaceTexture>& surfaceTexture)
+// pass the buffered IGraphicBufferProducer to the camera service
+status_t Camera::setPreviewTexture(const sp<IGraphicBufferProducer>& bufferProducer)
{
- ALOGV("setPreviewTexture(%p)", surfaceTexture.get());
+ ALOGV("setPreviewTexture(%p)", bufferProducer.get());
sp <ICamera> c = mCamera;
if (c == 0) return NO_INIT;
- if (surfaceTexture != 0) {
- return c->setPreviewTexture(surfaceTexture);
- } else {
- ALOGD("app passed NULL surface");
- return c->setPreviewTexture(0);
- }
+ ALOGD_IF(bufferProducer == 0, "app passed NULL surface");
+ return c->setPreviewTexture(bufferProducer);
}
// start preview mode
@@ -350,14 +258,7 @@ void Camera::setPreviewCallbackFlags(int flag)
// callback from camera service
void Camera::notifyCallback(int32_t msgType, int32_t ext1, int32_t ext2)
{
- sp<CameraListener> listener;
- {
- Mutex::Autolock _l(mLock);
- listener = mListener;
- }
- if (listener != NULL) {
- listener->notify(msgType, ext1, ext2);
- }
+ return CameraBaseT::notifyCallback(msgType, ext1, ext2);
}
// callback from camera service when frame or image is ready
@@ -395,6 +296,7 @@ void Camera::dataCallbackTimestamp(nsecs_t timestamp, int32_t msgType, const sp<
Mutex::Autolock _l(mLock);
listener = mListener;
}
+
if (listener != NULL) {
listener->postDataTimestamp(timestamp, msgType, dataPtr);
} else {
@@ -403,18 +305,6 @@ void Camera::dataCallbackTimestamp(nsecs_t timestamp, int32_t msgType, const sp<
}
}
-void Camera::binderDied(const wp<IBinder>& who) {
- ALOGW("ICamera died");
- notifyCallback(CAMERA_MSG_ERROR, CAMERA_ERROR_SERVER_DIED, 0);
-}
-
-void Camera::DeathNotifier::binderDied(const wp<IBinder>& who) {
- ALOGV("binderDied");
- Mutex::Autolock _l(Camera::mLock);
- Camera::mCameraService.clear();
- ALOGW("Camera server died!");
-}
-
sp<ICameraRecordingProxy> Camera::getRecordingProxy() {
ALOGV("getProxy");
return new RecordingProxy(this);
diff --git a/camera/CameraBase.cpp b/camera/CameraBase.cpp
new file mode 100644
index 0000000..c25c5fd
--- /dev/null
+++ b/camera/CameraBase.cpp
@@ -0,0 +1,218 @@
+/*
+**
+** Copyright (C) 2013, 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_NDEBUG 0
+#define LOG_TAG "CameraBase"
+#include <utils/Log.h>
+#include <utils/threads.h>
+#include <utils/Mutex.h>
+
+#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
+#include <binder/IMemory.h>
+
+#include <camera/CameraBase.h>
+#include <camera/ICameraService.h>
+
+// needed to instantiate
+#include <camera/ProCamera.h>
+#include <camera/Camera.h>
+
+#include <system/camera_metadata.h>
+
+namespace android {
+
+namespace {
+ sp<ICameraService> gCameraService;
+ const int kCameraServicePollDelay = 500000; // 0.5s
+ const char* kCameraServiceName = "media.camera";
+
+ Mutex gLock;
+
+ class DeathNotifier : public IBinder::DeathRecipient
+ {
+ public:
+ DeathNotifier() {
+ }
+
+ virtual void binderDied(const wp<IBinder>& who) {
+ ALOGV("binderDied");
+ Mutex::Autolock _l(gLock);
+ gCameraService.clear();
+ ALOGW("Camera service died!");
+ }
+ };
+
+ sp<DeathNotifier> gDeathNotifier;
+}; // namespace anonymous
+
+///////////////////////////////////////////////////////////
+// CameraBase definition
+///////////////////////////////////////////////////////////
+
+// establish binder interface to camera service
+template <typename TCam, typename TCamTraits>
+const sp<ICameraService>& CameraBase<TCam, TCamTraits>::getCameraService()
+{
+ Mutex::Autolock _l(gLock);
+ if (gCameraService.get() == 0) {
+ sp<IServiceManager> sm = defaultServiceManager();
+ sp<IBinder> binder;
+ do {
+ binder = sm->getService(String16(kCameraServiceName));
+ if (binder != 0) {
+ break;
+ }
+ ALOGW("CameraService not published, waiting...");
+ usleep(kCameraServicePollDelay);
+ } while(true);
+ if (gDeathNotifier == NULL) {
+ gDeathNotifier = new DeathNotifier();
+ }
+ binder->linkToDeath(gDeathNotifier);
+ gCameraService = interface_cast<ICameraService>(binder);
+ }
+ ALOGE_IF(gCameraService == 0, "no CameraService!?");
+ return gCameraService;
+}
+
+template <typename TCam, typename TCamTraits>
+sp<TCam> CameraBase<TCam, TCamTraits>::connect(int cameraId,
+ const String16& clientPackageName,
+ int clientUid)
+{
+ ALOGV("%s: connect", __FUNCTION__);
+ sp<TCam> c = new TCam(cameraId);
+ sp<TCamCallbacks> cl = c;
+ const sp<ICameraService>& cs = getCameraService();
+ if (cs != 0) {
+ c->mCamera = cs->connect(cl, cameraId, clientPackageName, clientUid);
+ }
+ if (c->mCamera != 0) {
+ c->mCamera->asBinder()->linkToDeath(c);
+ c->mStatus = NO_ERROR;
+ } else {
+ c.clear();
+ }
+ return c;
+}
+
+template <typename TCam, typename TCamTraits>
+void CameraBase<TCam, TCamTraits>::disconnect()
+{
+ ALOGV("%s: disconnect", __FUNCTION__);
+ if (mCamera != 0) {
+ mCamera->disconnect();
+ mCamera->asBinder()->unlinkToDeath(this);
+ mCamera = 0;
+ }
+ ALOGV("%s: disconnect (done)", __FUNCTION__);
+}
+
+template <typename TCam, typename TCamTraits>
+CameraBase<TCam, TCamTraits>::CameraBase(int cameraId) :
+ mStatus(UNKNOWN_ERROR),
+ mCameraId(cameraId)
+{
+}
+
+template <typename TCam, typename TCamTraits>
+CameraBase<TCam, TCamTraits>::~CameraBase()
+{
+}
+
+template <typename TCam, typename TCamTraits>
+sp<typename TCamTraits::TCamUser> CameraBase<TCam, TCamTraits>::remote()
+{
+ return mCamera;
+}
+
+template <typename TCam, typename TCamTraits>
+status_t CameraBase<TCam, TCamTraits>::getStatus()
+{
+ return mStatus;
+}
+
+template <typename TCam, typename TCamTraits>
+void CameraBase<TCam, TCamTraits>::binderDied(const wp<IBinder>& who) {
+ ALOGW("mediaserver's remote binder Camera object died");
+ notifyCallback(CAMERA_MSG_ERROR, CAMERA_ERROR_SERVER_DIED, /*ext2*/0);
+}
+
+template <typename TCam, typename TCamTraits>
+void CameraBase<TCam, TCamTraits>::setListener(const sp<TCamListener>& listener)
+{
+ Mutex::Autolock _l(mLock);
+ mListener = listener;
+}
+
+// callback from camera service
+template <typename TCam, typename TCamTraits>
+void CameraBase<TCam, TCamTraits>::notifyCallback(int32_t msgType,
+ int32_t ext1,
+ int32_t ext2)
+{
+ sp<TCamListener> listener;
+ {
+ Mutex::Autolock _l(mLock);
+ listener = mListener;
+ }
+ if (listener != NULL) {
+ listener->notify(msgType, ext1, ext2);
+ }
+}
+
+template <typename TCam, typename TCamTraits>
+int CameraBase<TCam, TCamTraits>::getNumberOfCameras() {
+ const sp<ICameraService> cs = getCameraService();
+
+ if (!cs.get()) {
+ // as required by the public Java APIs
+ return 0;
+ }
+ return cs->getNumberOfCameras();
+}
+
+// this can be in BaseCamera but it should be an instance method
+template <typename TCam, typename TCamTraits>
+status_t CameraBase<TCam, TCamTraits>::getCameraInfo(int cameraId,
+ struct CameraInfo* cameraInfo) {
+ const sp<ICameraService>& cs = getCameraService();
+ if (cs == 0) return UNKNOWN_ERROR;
+ return cs->getCameraInfo(cameraId, cameraInfo);
+}
+
+template <typename TCam, typename TCamTraits>
+status_t CameraBase<TCam, TCamTraits>::addServiceListener(
+ const sp<ICameraServiceListener>& listener) {
+ const sp<ICameraService>& cs = getCameraService();
+ if (cs == 0) return UNKNOWN_ERROR;
+ return cs->addListener(listener);
+}
+
+template <typename TCam, typename TCamTraits>
+status_t CameraBase<TCam, TCamTraits>::removeServiceListener(
+ const sp<ICameraServiceListener>& listener) {
+ const sp<ICameraService>& cs = getCameraService();
+ if (cs == 0) return UNKNOWN_ERROR;
+ return cs->removeListener(listener);
+}
+
+template class CameraBase<ProCamera>;
+template class CameraBase<Camera>;
+
+} // namespace android
diff --git a/camera/CameraMetadata.cpp b/camera/CameraMetadata.cpp
new file mode 100644
index 0000000..a8f9eff
--- /dev/null
+++ b/camera/CameraMetadata.cpp
@@ -0,0 +1,411 @@
+/*
+ * Copyright (C) 2012 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_NDEBUG 0
+
+#define LOG_TAG "Camera2-Metadata"
+#include <utils/Log.h>
+#include <utils/Errors.h>
+
+#include <camera/CameraMetadata.h>
+
+namespace android {
+
+CameraMetadata::CameraMetadata() :
+ mBuffer(NULL), mLocked(false) {
+}
+
+CameraMetadata::CameraMetadata(size_t entryCapacity, size_t dataCapacity) :
+ mLocked(false)
+{
+ mBuffer = allocate_camera_metadata(entryCapacity, dataCapacity);
+}
+
+CameraMetadata::CameraMetadata(const CameraMetadata &other) :
+ mLocked(false) {
+ mBuffer = clone_camera_metadata(other.mBuffer);
+}
+
+CameraMetadata::CameraMetadata(camera_metadata_t *buffer) :
+ mBuffer(NULL), mLocked(false) {
+ acquire(buffer);
+}
+
+CameraMetadata &CameraMetadata::operator=(const CameraMetadata &other) {
+ return operator=(other.mBuffer);
+}
+
+CameraMetadata &CameraMetadata::operator=(const camera_metadata_t *buffer) {
+ if (mLocked) {
+ ALOGE("%s: Assignment to a locked CameraMetadata!", __FUNCTION__);
+ return *this;
+ }
+
+ if (CC_LIKELY(buffer != mBuffer)) {
+ camera_metadata_t *newBuffer = clone_camera_metadata(buffer);
+ clear();
+ mBuffer = newBuffer;
+ }
+ return *this;
+}
+
+CameraMetadata::~CameraMetadata() {
+ mLocked = false;
+ clear();
+}
+
+const camera_metadata_t* CameraMetadata::getAndLock() {
+ mLocked = true;
+ return mBuffer;
+}
+
+status_t CameraMetadata::unlock(const camera_metadata_t *buffer) {
+ if (!mLocked) {
+ ALOGE("%s: Can't unlock a non-locked CameraMetadata!", __FUNCTION__);
+ return INVALID_OPERATION;
+ }
+ if (buffer != mBuffer) {
+ ALOGE("%s: Can't unlock CameraMetadata with wrong pointer!",
+ __FUNCTION__);
+ return BAD_VALUE;
+ }
+ mLocked = false;
+ return OK;
+}
+
+camera_metadata_t* CameraMetadata::release() {
+ if (mLocked) {
+ ALOGE("%s: CameraMetadata is locked", __FUNCTION__);
+ return NULL;
+ }
+ camera_metadata_t *released = mBuffer;
+ mBuffer = NULL;
+ return released;
+}
+
+void CameraMetadata::clear() {
+ if (mLocked) {
+ ALOGE("%s: CameraMetadata is locked", __FUNCTION__);
+ return;
+ }
+ if (mBuffer) {
+ free_camera_metadata(mBuffer);
+ mBuffer = NULL;
+ }
+}
+
+void CameraMetadata::acquire(camera_metadata_t *buffer) {
+ if (mLocked) {
+ ALOGE("%s: CameraMetadata is locked", __FUNCTION__);
+ return;
+ }
+ clear();
+ mBuffer = buffer;
+
+ ALOGE_IF(validate_camera_metadata_structure(mBuffer, /*size*/NULL) != OK,
+ "%s: Failed to validate metadata structure %p",
+ __FUNCTION__, buffer);
+}
+
+void CameraMetadata::acquire(CameraMetadata &other) {
+ if (mLocked) {
+ ALOGE("%s: CameraMetadata is locked", __FUNCTION__);
+ return;
+ }
+ acquire(other.release());
+}
+
+status_t CameraMetadata::append(const CameraMetadata &other) {
+ if (mLocked) {
+ ALOGE("%s: CameraMetadata is locked", __FUNCTION__);
+ return INVALID_OPERATION;
+ }
+ return append_camera_metadata(mBuffer, other.mBuffer);
+}
+
+size_t CameraMetadata::entryCount() const {
+ return (mBuffer == NULL) ? 0 :
+ get_camera_metadata_entry_count(mBuffer);
+}
+
+bool CameraMetadata::isEmpty() const {
+ return entryCount() == 0;
+}
+
+status_t CameraMetadata::sort() {
+ if (mLocked) {
+ ALOGE("%s: CameraMetadata is locked", __FUNCTION__);
+ return INVALID_OPERATION;
+ }
+ return sort_camera_metadata(mBuffer);
+}
+
+status_t CameraMetadata::checkType(uint32_t tag, uint8_t expectedType) {
+ int tagType = get_camera_metadata_tag_type(tag);
+ if ( CC_UNLIKELY(tagType == -1)) {
+ ALOGE("Update metadata entry: Unknown tag %d", tag);
+ return INVALID_OPERATION;
+ }
+ if ( CC_UNLIKELY(tagType != expectedType) ) {
+ ALOGE("Mismatched tag type when updating entry %s (%d) of type %s; "
+ "got type %s data instead ",
+ get_camera_metadata_tag_name(tag), tag,
+ camera_metadata_type_names[tagType],
+ camera_metadata_type_names[expectedType]);
+ return INVALID_OPERATION;
+ }
+ return OK;
+}
+
+status_t CameraMetadata::update(uint32_t tag,
+ const int32_t *data, size_t data_count) {
+ status_t res;
+ if (mLocked) {
+ ALOGE("%s: CameraMetadata is locked", __FUNCTION__);
+ return INVALID_OPERATION;
+ }
+ if ( (res = checkType(tag, TYPE_INT32)) != OK) {
+ return res;
+ }
+ return updateImpl(tag, (const void*)data, data_count);
+}
+
+status_t CameraMetadata::update(uint32_t tag,
+ const uint8_t *data, size_t data_count) {
+ status_t res;
+ if (mLocked) {
+ ALOGE("%s: CameraMetadata is locked", __FUNCTION__);
+ return INVALID_OPERATION;
+ }
+ if ( (res = checkType(tag, TYPE_BYTE)) != OK) {
+ return res;
+ }
+ return updateImpl(tag, (const void*)data, data_count);
+}
+
+status_t CameraMetadata::update(uint32_t tag,
+ const float *data, size_t data_count) {
+ status_t res;
+ if (mLocked) {
+ ALOGE("%s: CameraMetadata is locked", __FUNCTION__);
+ return INVALID_OPERATION;
+ }
+ if ( (res = checkType(tag, TYPE_FLOAT)) != OK) {
+ return res;
+ }
+ return updateImpl(tag, (const void*)data, data_count);
+}
+
+status_t CameraMetadata::update(uint32_t tag,
+ const int64_t *data, size_t data_count) {
+ status_t res;
+ if (mLocked) {
+ ALOGE("%s: CameraMetadata is locked", __FUNCTION__);
+ return INVALID_OPERATION;
+ }
+ if ( (res = checkType(tag, TYPE_INT64)) != OK) {
+ return res;
+ }
+ return updateImpl(tag, (const void*)data, data_count);
+}
+
+status_t CameraMetadata::update(uint32_t tag,
+ const double *data, size_t data_count) {
+ status_t res;
+ if (mLocked) {
+ ALOGE("%s: CameraMetadata is locked", __FUNCTION__);
+ return INVALID_OPERATION;
+ }
+ if ( (res = checkType(tag, TYPE_DOUBLE)) != OK) {
+ return res;
+ }
+ return updateImpl(tag, (const void*)data, data_count);
+}
+
+status_t CameraMetadata::update(uint32_t tag,
+ const camera_metadata_rational_t *data, size_t data_count) {
+ status_t res;
+ if (mLocked) {
+ ALOGE("%s: CameraMetadata is locked", __FUNCTION__);
+ return INVALID_OPERATION;
+ }
+ if ( (res = checkType(tag, TYPE_RATIONAL)) != OK) {
+ return res;
+ }
+ return updateImpl(tag, (const void*)data, data_count);
+}
+
+status_t CameraMetadata::update(uint32_t tag,
+ const String8 &string) {
+ status_t res;
+ if (mLocked) {
+ ALOGE("%s: CameraMetadata is locked", __FUNCTION__);
+ return INVALID_OPERATION;
+ }
+ if ( (res = checkType(tag, TYPE_BYTE)) != OK) {
+ return res;
+ }
+ return updateImpl(tag, (const void*)string.string(), string.size());
+}
+
+status_t CameraMetadata::updateImpl(uint32_t tag, const void *data,
+ size_t data_count) {
+ status_t res;
+ if (mLocked) {
+ ALOGE("%s: CameraMetadata is locked", __FUNCTION__);
+ return INVALID_OPERATION;
+ }
+ int type = get_camera_metadata_tag_type(tag);
+ if (type == -1) {
+ ALOGE("%s: Tag %d not found", __FUNCTION__, tag);
+ return BAD_VALUE;
+ }
+ size_t data_size = calculate_camera_metadata_entry_data_size(type,
+ data_count);
+
+ res = resizeIfNeeded(1, data_size);
+
+ if (res == OK) {
+ camera_metadata_entry_t entry;
+ res = find_camera_metadata_entry(mBuffer, tag, &entry);
+ if (res == NAME_NOT_FOUND) {
+ res = add_camera_metadata_entry(mBuffer,
+ tag, data, data_count);
+ } else if (res == OK) {
+ res = update_camera_metadata_entry(mBuffer,
+ entry.index, data, data_count, NULL);
+ }
+ }
+
+ if (res != OK) {
+ ALOGE("%s: Unable to update metadata entry %s.%s (%x): %s (%d)",
+ __FUNCTION__, get_camera_metadata_section_name(tag),
+ get_camera_metadata_tag_name(tag), tag, strerror(-res), res);
+ }
+
+ IF_ALOGV() {
+ ALOGE_IF(validate_camera_metadata_structure(mBuffer, /*size*/NULL) !=
+ OK,
+
+ "%s: Failed to validate metadata structure after update %p",
+ __FUNCTION__, mBuffer);
+ }
+
+ return res;
+}
+
+bool CameraMetadata::exists(uint32_t tag) const {
+ camera_metadata_ro_entry entry;
+ return find_camera_metadata_ro_entry(mBuffer, tag, &entry) == 0;
+}
+
+camera_metadata_entry_t CameraMetadata::find(uint32_t tag) {
+ status_t res;
+ camera_metadata_entry entry;
+ if (mLocked) {
+ ALOGE("%s: CameraMetadata is locked", __FUNCTION__);
+ entry.count = 0;
+ return entry;
+ }
+ res = find_camera_metadata_entry(mBuffer, tag, &entry);
+ if (CC_UNLIKELY( res != OK )) {
+ entry.count = 0;
+ entry.data.u8 = NULL;
+ }
+ return entry;
+}
+
+camera_metadata_ro_entry_t CameraMetadata::find(uint32_t tag) const {
+ status_t res;
+ camera_metadata_ro_entry entry;
+ res = find_camera_metadata_ro_entry(mBuffer, tag, &entry);
+ if (CC_UNLIKELY( res != OK )) {
+ entry.count = 0;
+ entry.data.u8 = NULL;
+ }
+ return entry;
+}
+
+status_t CameraMetadata::erase(uint32_t tag) {
+ camera_metadata_entry_t entry;
+ status_t res;
+ if (mLocked) {
+ ALOGE("%s: CameraMetadata is locked", __FUNCTION__);
+ return INVALID_OPERATION;
+ }
+ res = find_camera_metadata_entry(mBuffer, tag, &entry);
+ if (res == NAME_NOT_FOUND) {
+ return OK;
+ } else if (res != OK) {
+ ALOGE("%s: Error looking for entry %s.%s (%x): %s %d",
+ __FUNCTION__,
+ get_camera_metadata_section_name(tag),
+ get_camera_metadata_tag_name(tag), tag, strerror(-res), res);
+ return res;
+ }
+ res = delete_camera_metadata_entry(mBuffer, entry.index);
+ if (res != OK) {
+ ALOGE("%s: Error deleting entry %s.%s (%x): %s %d",
+ __FUNCTION__,
+ get_camera_metadata_section_name(tag),
+ get_camera_metadata_tag_name(tag), tag, strerror(-res), res);
+ }
+ return res;
+}
+
+void CameraMetadata::dump(int fd, int verbosity, int indentation) const {
+ dump_indented_camera_metadata(mBuffer, fd, verbosity, indentation);
+}
+
+status_t CameraMetadata::resizeIfNeeded(size_t extraEntries, size_t extraData) {
+ if (mBuffer == NULL) {
+ mBuffer = allocate_camera_metadata(extraEntries * 2, extraData * 2);
+ if (mBuffer == NULL) {
+ ALOGE("%s: Can't allocate larger metadata buffer", __FUNCTION__);
+ return NO_MEMORY;
+ }
+ } else {
+ size_t currentEntryCount = get_camera_metadata_entry_count(mBuffer);
+ size_t currentEntryCap = get_camera_metadata_entry_capacity(mBuffer);
+ size_t newEntryCount = currentEntryCount +
+ extraEntries;
+ newEntryCount = (newEntryCount > currentEntryCap) ?
+ newEntryCount * 2 : currentEntryCap;
+
+ size_t currentDataCount = get_camera_metadata_data_count(mBuffer);
+ size_t currentDataCap = get_camera_metadata_data_capacity(mBuffer);
+ size_t newDataCount = currentDataCount +
+ extraData;
+ newDataCount = (newDataCount > currentDataCap) ?
+ newDataCount * 2 : currentDataCap;
+
+ if (newEntryCount > currentEntryCap ||
+ newDataCount > currentDataCap) {
+ camera_metadata_t *oldBuffer = mBuffer;
+ mBuffer = allocate_camera_metadata(newEntryCount,
+ newDataCount);
+ if (mBuffer == NULL) {
+ ALOGE("%s: Can't allocate larger metadata buffer", __FUNCTION__);
+ return NO_MEMORY;
+ }
+ append_camera_metadata(mBuffer, oldBuffer);
+ free_camera_metadata(oldBuffer);
+ }
+ }
+ return OK;
+}
+
+}; // namespace android
diff --git a/camera/CameraParameters.cpp b/camera/CameraParameters.cpp
index 661f49c..c51f265 100644
--- a/camera/CameraParameters.cpp
+++ b/camera/CameraParameters.cpp
@@ -90,6 +90,7 @@ const char CameraParameters::KEY_RECORDING_HINT[] = "recording-hint";
const char CameraParameters::KEY_VIDEO_SNAPSHOT_SUPPORTED[] = "video-snapshot-supported";
const char CameraParameters::KEY_VIDEO_STABILIZATION[] = "video-stabilization";
const char CameraParameters::KEY_VIDEO_STABILIZATION_SUPPORTED[] = "video-stabilization-supported";
+const char CameraParameters::KEY_LIGHTFX[] = "light-fx";
const char CameraParameters::TRUE[] = "true";
const char CameraParameters::FALSE[] = "false";
@@ -167,6 +168,10 @@ const char CameraParameters::FOCUS_MODE_EDOF[] = "edof";
const char CameraParameters::FOCUS_MODE_CONTINUOUS_VIDEO[] = "continuous-video";
const char CameraParameters::FOCUS_MODE_CONTINUOUS_PICTURE[] = "continuous-picture";
+// Values for light fx settings
+const char CameraParameters::LIGHTFX_LOWLIGHT[] = "low-light";
+const char CameraParameters::LIGHTFX_HDR[] = "high-dynamic-range";
+
CameraParameters::CameraParameters()
: mMap()
{
diff --git a/camera/ICamera.cpp b/camera/ICamera.cpp
index 8d8408c..8900867 100644
--- a/camera/ICamera.cpp
+++ b/camera/ICamera.cpp
@@ -22,14 +22,13 @@
#include <sys/types.h>
#include <binder/Parcel.h>
#include <camera/ICamera.h>
-#include <gui/ISurfaceTexture.h>
+#include <gui/IGraphicBufferProducer.h>
#include <gui/Surface.h>
namespace android {
enum {
DISCONNECT = IBinder::FIRST_CALL_TRANSACTION,
- SET_PREVIEW_DISPLAY,
SET_PREVIEW_TEXTURE,
SET_PREVIEW_CALLBACK_FLAG,
START_PREVIEW,
@@ -68,24 +67,13 @@ public:
remote()->transact(DISCONNECT, data, &reply);
}
- // pass the buffered Surface to the camera service
- status_t setPreviewDisplay(const sp<Surface>& surface)
- {
- ALOGV("setPreviewDisplay");
- Parcel data, reply;
- data.writeInterfaceToken(ICamera::getInterfaceDescriptor());
- Surface::writeToParcel(surface, &data);
- remote()->transact(SET_PREVIEW_DISPLAY, data, &reply);
- return reply.readInt32();
- }
-
- // pass the buffered SurfaceTexture to the camera service
- status_t setPreviewTexture(const sp<ISurfaceTexture>& surfaceTexture)
+ // pass the buffered IGraphicBufferProducer to the camera service
+ status_t setPreviewTexture(const sp<IGraphicBufferProducer>& bufferProducer)
{
ALOGV("setPreviewTexture");
Parcel data, reply;
data.writeInterfaceToken(ICamera::getInterfaceDescriptor());
- sp<IBinder> b(surfaceTexture->asBinder());
+ sp<IBinder> b(bufferProducer->asBinder());
data.writeStrongBinder(b);
remote()->transact(SET_PREVIEW_TEXTURE, data, &reply);
return reply.readInt32();
@@ -282,17 +270,11 @@ status_t BnCamera::onTransact(
disconnect();
return NO_ERROR;
} break;
- case SET_PREVIEW_DISPLAY: {
- ALOGV("SET_PREVIEW_DISPLAY");
- CHECK_INTERFACE(ICamera, data, reply);
- sp<Surface> surface = Surface::readFromParcel(data);
- reply->writeInt32(setPreviewDisplay(surface));
- return NO_ERROR;
- } break;
case SET_PREVIEW_TEXTURE: {
ALOGV("SET_PREVIEW_TEXTURE");
CHECK_INTERFACE(ICamera, data, reply);
- sp<ISurfaceTexture> st = interface_cast<ISurfaceTexture>(data.readStrongBinder());
+ sp<IGraphicBufferProducer> st =
+ interface_cast<IGraphicBufferProducer>(data.readStrongBinder());
reply->writeInt32(setPreviewTexture(st));
return NO_ERROR;
} break;
diff --git a/camera/ICameraService.cpp b/camera/ICameraService.cpp
index f2d367e..134f7f0 100644
--- a/camera/ICameraService.cpp
+++ b/camera/ICameraService.cpp
@@ -23,6 +23,11 @@
#include <binder/IServiceManager.h>
#include <camera/ICameraService.h>
+#include <camera/ICameraServiceListener.h>
+#include <camera/IProCameraUser.h>
+#include <camera/IProCameraCallbacks.h>
+#include <camera/ICamera.h>
+#include <camera/ICameraClient.h>
namespace android {
@@ -56,15 +61,50 @@ public:
}
// connect to camera service
- virtual sp<ICamera> connect(const sp<ICameraClient>& cameraClient, int cameraId)
+ virtual sp<ICamera> connect(const sp<ICameraClient>& cameraClient, int cameraId,
+ const String16 &clientPackageName, int clientUid)
{
Parcel data, reply;
data.writeInterfaceToken(ICameraService::getInterfaceDescriptor());
data.writeStrongBinder(cameraClient->asBinder());
data.writeInt32(cameraId);
+ data.writeString16(clientPackageName);
+ data.writeInt32(clientUid);
remote()->transact(BnCameraService::CONNECT, data, &reply);
return interface_cast<ICamera>(reply.readStrongBinder());
}
+
+ // connect to camera service (pro client)
+ virtual sp<IProCameraUser> connect(const sp<IProCameraCallbacks>& cameraCb, int cameraId,
+ const String16 &clientPackageName, int clientUid)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(ICameraService::getInterfaceDescriptor());
+ data.writeStrongBinder(cameraCb->asBinder());
+ data.writeInt32(cameraId);
+ data.writeString16(clientPackageName);
+ data.writeInt32(clientUid);
+ remote()->transact(BnCameraService::CONNECT_PRO, data, &reply);
+ return interface_cast<IProCameraUser>(reply.readStrongBinder());
+ }
+
+ virtual status_t addListener(const sp<ICameraServiceListener>& listener)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(ICameraService::getInterfaceDescriptor());
+ data.writeStrongBinder(listener->asBinder());
+ remote()->transact(BnCameraService::ADD_LISTENER, data, &reply);
+ return reply.readInt32();
+ }
+
+ virtual status_t removeListener(const sp<ICameraServiceListener>& listener)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(ICameraService::getInterfaceDescriptor());
+ data.writeStrongBinder(listener->asBinder());
+ remote()->transact(BnCameraService::REMOVE_LISTENER, data, &reply);
+ return reply.readInt32();
+ }
};
IMPLEMENT_META_INTERFACE(CameraService, "android.hardware.ICameraService");
@@ -92,11 +132,41 @@ status_t BnCameraService::onTransact(
} break;
case CONNECT: {
CHECK_INTERFACE(ICameraService, data, reply);
- sp<ICameraClient> cameraClient = interface_cast<ICameraClient>(data.readStrongBinder());
- sp<ICamera> camera = connect(cameraClient, data.readInt32());
+ sp<ICameraClient> cameraClient =
+ interface_cast<ICameraClient>(data.readStrongBinder());
+ int32_t cameraId = data.readInt32();
+ const String16 clientName = data.readString16();
+ int32_t clientUid = data.readInt32();
+ sp<ICamera> camera = connect(cameraClient, cameraId,
+ clientName, clientUid);
+ reply->writeStrongBinder(camera->asBinder());
+ return NO_ERROR;
+ } break;
+ case CONNECT_PRO: {
+ CHECK_INTERFACE(ICameraService, data, reply);
+ sp<IProCameraCallbacks> cameraClient = interface_cast<IProCameraCallbacks>(data.readStrongBinder());
+ int32_t cameraId = data.readInt32();
+ const String16 clientName = data.readString16();
+ int32_t clientUid = data.readInt32();
+ sp<IProCameraUser> camera = connect(cameraClient, cameraId,
+ clientName, clientUid);
reply->writeStrongBinder(camera->asBinder());
return NO_ERROR;
} break;
+ case ADD_LISTENER: {
+ CHECK_INTERFACE(ICameraService, data, reply);
+ sp<ICameraServiceListener> listener =
+ interface_cast<ICameraServiceListener>(data.readStrongBinder());
+ reply->writeInt32(addListener(listener));
+ return NO_ERROR;
+ } break;
+ case REMOVE_LISTENER: {
+ CHECK_INTERFACE(ICameraService, data, reply);
+ sp<ICameraServiceListener> listener =
+ interface_cast<ICameraServiceListener>(data.readStrongBinder());
+ reply->writeInt32(removeListener(listener));
+ return NO_ERROR;
+ } break;
default:
return BBinder::onTransact(code, data, reply, flags);
}
diff --git a/camera/ICameraServiceListener.cpp b/camera/ICameraServiceListener.cpp
new file mode 100644
index 0000000..640ee35
--- /dev/null
+++ b/camera/ICameraServiceListener.cpp
@@ -0,0 +1,86 @@
+/*
+**
+** Copyright 2013, 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.
+*/
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <binder/Parcel.h>
+#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
+
+#include <camera/ICameraServiceListener.h>
+
+namespace android {
+
+namespace {
+ enum {
+ STATUS_CHANGED = IBinder::FIRST_CALL_TRANSACTION,
+ };
+}; // namespace anonymous
+
+class BpCameraServiceListener: public BpInterface<ICameraServiceListener>
+{
+
+public:
+ BpCameraServiceListener(const sp<IBinder>& impl)
+ : BpInterface<ICameraServiceListener>(impl)
+ {
+ }
+
+ virtual void onStatusChanged(Status status, int32_t cameraId)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(
+ ICameraServiceListener::getInterfaceDescriptor());
+
+ data.writeInt32(static_cast<int32_t>(status));
+ data.writeInt32(cameraId);
+
+ remote()->transact(STATUS_CHANGED,
+ data,
+ &reply,
+ IBinder::FLAG_ONEWAY);
+ }
+};
+
+IMPLEMENT_META_INTERFACE(CameraServiceListener,
+ "android.hardware.ICameraServiceListener");
+
+// ----------------------------------------------------------------------
+
+status_t BnCameraServiceListener::onTransact(
+ uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
+{
+ switch(code) {
+ case STATUS_CHANGED: {
+ CHECK_INTERFACE(ICameraServiceListener, data, reply);
+
+ Status status = static_cast<Status>(data.readInt32());
+ int32_t cameraId = data.readInt32();
+
+ onStatusChanged(status, cameraId);
+
+ return NO_ERROR;
+ } break;
+ default:
+ return BBinder::onTransact(code, data, reply, flags);
+ }
+}
+
+// ----------------------------------------------------------------------------
+
+}; // namespace android
diff --git a/camera/IProCameraCallbacks.cpp b/camera/IProCameraCallbacks.cpp
new file mode 100644
index 0000000..b9cd14d
--- /dev/null
+++ b/camera/IProCameraCallbacks.cpp
@@ -0,0 +1,128 @@
+/*
+**
+** Copyright 2013, 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_NDEBUG 0
+#define LOG_TAG "IProCameraCallbacks"
+#include <utils/Log.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <binder/Parcel.h>
+#include <gui/IGraphicBufferProducer.h>
+#include <gui/Surface.h>
+#include <utils/Mutex.h>
+
+#include <camera/IProCameraCallbacks.h>
+
+#include <system/camera_metadata.h>
+
+namespace android {
+
+enum {
+ NOTIFY_CALLBACK = IBinder::FIRST_CALL_TRANSACTION,
+ LOCK_STATUS_CHANGED,
+ RESULT_RECEIVED,
+};
+
+void readMetadata(const Parcel& data, camera_metadata_t** out);
+void writeMetadata(Parcel& data, camera_metadata_t* metadata);
+
+class BpProCameraCallbacks: public BpInterface<IProCameraCallbacks>
+{
+public:
+ BpProCameraCallbacks(const sp<IBinder>& impl)
+ : BpInterface<IProCameraCallbacks>(impl)
+ {
+ }
+
+ // generic callback from camera service to app
+ void notifyCallback(int32_t msgType, int32_t ext1, int32_t ext2)
+ {
+ ALOGV("notifyCallback");
+ Parcel data, reply;
+ data.writeInterfaceToken(IProCameraCallbacks::getInterfaceDescriptor());
+ data.writeInt32(msgType);
+ data.writeInt32(ext1);
+ data.writeInt32(ext2);
+ remote()->transact(NOTIFY_CALLBACK, data, &reply, IBinder::FLAG_ONEWAY);
+ }
+
+ void onLockStatusChanged(LockStatus newLockStatus) {
+ ALOGV("onLockStatusChanged");
+ Parcel data, reply;
+ data.writeInterfaceToken(IProCameraCallbacks::getInterfaceDescriptor());
+ data.writeInt32(newLockStatus);
+ remote()->transact(LOCK_STATUS_CHANGED, data, &reply,
+ IBinder::FLAG_ONEWAY);
+ }
+
+ void onResultReceived(int32_t frameId, camera_metadata* result) {
+ ALOGV("onResultReceived");
+ Parcel data, reply;
+ data.writeInterfaceToken(IProCameraCallbacks::getInterfaceDescriptor());
+ data.writeInt32(frameId);
+ writeMetadata(data, result);
+ remote()->transact(RESULT_RECEIVED, data, &reply, IBinder::FLAG_ONEWAY);
+ }
+};
+
+IMPLEMENT_META_INTERFACE(ProCameraCallbacks,
+ "android.hardware.IProCameraCallbacks");
+
+// ----------------------------------------------------------------------
+
+status_t BnProCameraCallbacks::onTransact(
+ uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
+{
+ ALOGV("onTransact - code = %d", code);
+ switch(code) {
+ case NOTIFY_CALLBACK: {
+ ALOGV("NOTIFY_CALLBACK");
+ CHECK_INTERFACE(IProCameraCallbacks, data, reply);
+ int32_t msgType = data.readInt32();
+ int32_t ext1 = data.readInt32();
+ int32_t ext2 = data.readInt32();
+ notifyCallback(msgType, ext1, ext2);
+ return NO_ERROR;
+ } break;
+ case LOCK_STATUS_CHANGED: {
+ ALOGV("LOCK_STATUS_CHANGED");
+ CHECK_INTERFACE(IProCameraCallbacks, data, reply);
+ LockStatus newLockStatus
+ = static_cast<LockStatus>(data.readInt32());
+ onLockStatusChanged(newLockStatus);
+ return NO_ERROR;
+ } break;
+ case RESULT_RECEIVED: {
+ ALOGV("RESULT_RECEIVED");
+ CHECK_INTERFACE(IProCameraCallbacks, data, reply);
+ int32_t frameId = data.readInt32();
+ camera_metadata_t *result = NULL;
+ readMetadata(data, &result);
+ onResultReceived(frameId, result);
+ return NO_ERROR;
+ break;
+ }
+ default:
+ return BBinder::onTransact(code, data, reply, flags);
+ }
+}
+
+// ----------------------------------------------------------------------------
+
+}; // namespace android
+
diff --git a/camera/IProCameraUser.cpp b/camera/IProCameraUser.cpp
new file mode 100644
index 0000000..4c4dec3
--- /dev/null
+++ b/camera/IProCameraUser.cpp
@@ -0,0 +1,426 @@
+/*
+**
+** Copyright 2013, 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_NDEBUG 0
+#define LOG_TAG "IProCameraUser"
+#include <utils/Log.h>
+#include <stdint.h>
+#include <sys/types.h>
+#include <binder/Parcel.h>
+#include <camera/IProCameraUser.h>
+#include <gui/IGraphicBufferProducer.h>
+#include <gui/Surface.h>
+#include <system/camera_metadata.h>
+
+namespace android {
+
+typedef Parcel::WritableBlob WritableBlob;
+typedef Parcel::ReadableBlob ReadableBlob;
+
+enum {
+ DISCONNECT = IBinder::FIRST_CALL_TRANSACTION,
+ CONNECT,
+ EXCLUSIVE_TRY_LOCK,
+ EXCLUSIVE_LOCK,
+ EXCLUSIVE_UNLOCK,
+ HAS_EXCLUSIVE_LOCK,
+ SUBMIT_REQUEST,
+ CANCEL_REQUEST,
+ DELETE_STREAM,
+ CREATE_STREAM,
+ CREATE_DEFAULT_REQUEST,
+ GET_CAMERA_INFO,
+};
+
+/**
+ * Caller becomes the owner of the new metadata
+ * 'const Parcel' doesnt prevent us from calling the read functions.
+ * which is interesting since it changes the internal state
+ *
+ * NULL can be returned when no metadata was sent, OR if there was an issue
+ * unpacking the serialized data (i.e. bad parcel or invalid structure).
+ */
+void readMetadata(const Parcel& data, camera_metadata_t** out) {
+
+ status_t err = OK;
+
+ camera_metadata_t* metadata = NULL;
+
+ if (out) {
+ *out = NULL;
+ }
+
+ // arg0 = metadataSize (int32)
+ int32_t metadataSizeTmp = -1;
+ if ((err = data.readInt32(&metadataSizeTmp)) != OK) {
+ ALOGE("%s: Failed to read metadata size (error %d %s)",
+ __FUNCTION__, err, strerror(-err));
+ return;
+ }
+ const size_t metadataSize = static_cast<size_t>(metadataSizeTmp);
+
+ if (metadataSize == 0) {
+ return;
+ }
+
+ // NOTE: this doesn't make sense to me. shouldnt the blob
+ // know how big it is? why do we have to specify the size
+ // to Parcel::readBlob ?
+
+ ReadableBlob blob;
+ // arg1 = metadata (blob)
+ do {
+ if ((err = data.readBlob(metadataSize, &blob)) != OK) {
+ ALOGE("%s: Failed to read metadata blob (sized %d). Possible "
+ " serialization bug. Error %d %s",
+ __FUNCTION__, metadataSize, err, strerror(-err));
+ break;
+ }
+ const camera_metadata_t* tmp =
+ reinterpret_cast<const camera_metadata_t*>(blob.data());
+
+ metadata = allocate_copy_camera_metadata_checked(tmp, metadataSize);
+ } while(0);
+ blob.release();
+
+ if (out) {
+ *out = metadata;
+ } else if (metadata != NULL) {
+ free_camera_metadata(metadata);
+ }
+}
+
+/**
+ * Caller retains ownership of metadata
+ * - Write 2 (int32 + blob) args in the current position
+ */
+void writeMetadata(Parcel& data, camera_metadata_t* metadata) {
+ // arg0 = metadataSize (int32)
+
+ if (metadata == NULL) {
+ data.writeInt32(0);
+ return;
+ }
+
+ const size_t metadataSize = get_camera_metadata_compact_size(metadata);
+ data.writeInt32(static_cast<int32_t>(metadataSize));
+
+ // arg1 = metadata (blob)
+ WritableBlob blob;
+ {
+ data.writeBlob(metadataSize, &blob);
+ copy_camera_metadata(blob.data(), metadataSize, metadata);
+
+ IF_ALOGV() {
+ if (validate_camera_metadata_structure(
+ (const camera_metadata_t*)blob.data(),
+ &metadataSize) != OK) {
+ ALOGV("%s: Failed to validate metadata %p after writing blob",
+ __FUNCTION__, blob.data());
+ } else {
+ ALOGV("%s: Metadata written to blob. Validation success",
+ __FUNCTION__);
+ }
+ }
+
+ // Not too big of a problem since receiving side does hard validation
+ if (validate_camera_metadata_structure(metadata, &metadataSize) != OK) {
+ ALOGW("%s: Failed to validate metadata %p before writing blob",
+ __FUNCTION__, metadata);
+ }
+
+ }
+ blob.release();
+}
+
+class BpProCameraUser: public BpInterface<IProCameraUser>
+{
+public:
+ BpProCameraUser(const sp<IBinder>& impl)
+ : BpInterface<IProCameraUser>(impl)
+ {
+ }
+
+ // disconnect from camera service
+ void disconnect()
+ {
+ ALOGV("disconnect");
+ Parcel data, reply;
+ data.writeInterfaceToken(IProCameraUser::getInterfaceDescriptor());
+ remote()->transact(DISCONNECT, data, &reply);
+ }
+
+ virtual status_t connect(const sp<IProCameraCallbacks>& cameraClient)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IProCameraUser::getInterfaceDescriptor());
+ data.writeStrongBinder(cameraClient->asBinder());
+ remote()->transact(CONNECT, data, &reply);
+ return reply.readInt32();
+ }
+
+ /* Shared ProCameraUser */
+
+ virtual status_t exclusiveTryLock()
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IProCameraUser::getInterfaceDescriptor());
+ remote()->transact(EXCLUSIVE_TRY_LOCK, data, &reply);
+ return reply.readInt32();
+ }
+ virtual status_t exclusiveLock()
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IProCameraUser::getInterfaceDescriptor());
+ remote()->transact(EXCLUSIVE_LOCK, data, &reply);
+ return reply.readInt32();
+ }
+
+ virtual status_t exclusiveUnlock()
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IProCameraUser::getInterfaceDescriptor());
+ remote()->transact(EXCLUSIVE_UNLOCK, data, &reply);
+ return reply.readInt32();
+ }
+
+ virtual bool hasExclusiveLock()
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IProCameraUser::getInterfaceDescriptor());
+ remote()->transact(HAS_EXCLUSIVE_LOCK, data, &reply);
+ return !!reply.readInt32();
+ }
+
+ virtual int submitRequest(camera_metadata_t* metadata, bool streaming)
+ {
+
+ Parcel data, reply;
+ data.writeInterfaceToken(IProCameraUser::getInterfaceDescriptor());
+
+ // arg0+arg1
+ writeMetadata(data, metadata);
+
+ // arg2 = streaming (bool)
+ data.writeInt32(streaming);
+
+ remote()->transact(SUBMIT_REQUEST, data, &reply);
+ return reply.readInt32();
+ }
+
+ virtual status_t cancelRequest(int requestId)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IProCameraUser::getInterfaceDescriptor());
+ data.writeInt32(requestId);
+
+ remote()->transact(CANCEL_REQUEST, data, &reply);
+ return reply.readInt32();
+ }
+
+ virtual status_t deleteStream(int streamId)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IProCameraUser::getInterfaceDescriptor());
+ data.writeInt32(streamId);
+
+ remote()->transact(DELETE_STREAM, data, &reply);
+ return reply.readInt32();
+ }
+
+ virtual status_t createStream(int width, int height, int format,
+ const sp<IGraphicBufferProducer>& bufferProducer,
+ /*out*/
+ int* streamId)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IProCameraUser::getInterfaceDescriptor());
+ data.writeInt32(width);
+ data.writeInt32(height);
+ data.writeInt32(format);
+
+ sp<IBinder> b(bufferProducer->asBinder());
+ data.writeStrongBinder(b);
+
+ remote()->transact(CREATE_STREAM, data, &reply);
+
+ int sId = reply.readInt32();
+ if (streamId) {
+ *streamId = sId;
+ }
+ return reply.readInt32();
+ }
+
+ // Create a request object from a template.
+ virtual status_t createDefaultRequest(int templateId,
+ /*out*/
+ camera_metadata** request)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IProCameraUser::getInterfaceDescriptor());
+ data.writeInt32(templateId);
+ remote()->transact(CREATE_DEFAULT_REQUEST, data, &reply);
+ readMetadata(reply, /*out*/request);
+ return reply.readInt32();
+ }
+
+
+ virtual status_t getCameraInfo(int cameraId, camera_metadata** info)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IProCameraUser::getInterfaceDescriptor());
+ data.writeInt32(cameraId);
+ remote()->transact(GET_CAMERA_INFO, data, &reply);
+ readMetadata(reply, /*out*/info);
+ return reply.readInt32();
+ }
+
+
+private:
+
+
+};
+
+IMPLEMENT_META_INTERFACE(ProCameraUser, "android.hardware.IProCameraUser");
+
+// ----------------------------------------------------------------------
+
+status_t BnProCameraUser::onTransact(
+ uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
+{
+ switch(code) {
+ case DISCONNECT: {
+ ALOGV("DISCONNECT");
+ CHECK_INTERFACE(IProCameraUser, data, reply);
+ disconnect();
+ return NO_ERROR;
+ } break;
+ case CONNECT: {
+ CHECK_INTERFACE(IProCameraUser, data, reply);
+ sp<IProCameraCallbacks> cameraClient =
+ interface_cast<IProCameraCallbacks>(data.readStrongBinder());
+ reply->writeInt32(connect(cameraClient));
+ return NO_ERROR;
+ } break;
+
+ /* Shared ProCameraUser */
+ case EXCLUSIVE_TRY_LOCK: {
+ CHECK_INTERFACE(IProCameraUser, data, reply);
+ reply->writeInt32(exclusiveTryLock());
+ return NO_ERROR;
+ } break;
+ case EXCLUSIVE_LOCK: {
+ CHECK_INTERFACE(IProCameraUser, data, reply);
+ reply->writeInt32(exclusiveLock());
+ return NO_ERROR;
+ } break;
+ case EXCLUSIVE_UNLOCK: {
+ CHECK_INTERFACE(IProCameraUser, data, reply);
+ reply->writeInt32(exclusiveUnlock());
+ return NO_ERROR;
+ } break;
+ case HAS_EXCLUSIVE_LOCK: {
+ CHECK_INTERFACE(IProCameraUser, data, reply);
+ reply->writeInt32(hasExclusiveLock());
+ return NO_ERROR;
+ } break;
+ case SUBMIT_REQUEST: {
+ CHECK_INTERFACE(IProCameraUser, data, reply);
+ camera_metadata_t* metadata;
+ readMetadata(data, /*out*/&metadata);
+
+ // arg2 = streaming (bool)
+ bool streaming = data.readInt32();
+
+ // return code: requestId (int32)
+ reply->writeInt32(submitRequest(metadata, streaming));
+
+ return NO_ERROR;
+ } break;
+ case CANCEL_REQUEST: {
+ CHECK_INTERFACE(IProCameraUser, data, reply);
+ int requestId = data.readInt32();
+ reply->writeInt32(cancelRequest(requestId));
+ return NO_ERROR;
+ } break;
+ case DELETE_STREAM: {
+ CHECK_INTERFACE(IProCameraUser, data, reply);
+ int streamId = data.readInt32();
+ reply->writeInt32(deleteStream(streamId));
+ return NO_ERROR;
+ } break;
+ case CREATE_STREAM: {
+ CHECK_INTERFACE(IProCameraUser, data, reply);
+ int width, height, format;
+
+ width = data.readInt32();
+ height = data.readInt32();
+ format = data.readInt32();
+
+ sp<IGraphicBufferProducer> bp =
+ interface_cast<IGraphicBufferProducer>(data.readStrongBinder());
+
+ int streamId = -1;
+ status_t ret;
+ ret = createStream(width, height, format, bp, &streamId);
+
+ reply->writeInt32(streamId);
+ reply->writeInt32(ret);
+
+ return NO_ERROR;
+ } break;
+
+ case CREATE_DEFAULT_REQUEST: {
+ CHECK_INTERFACE(IProCameraUser, data, reply);
+
+ int templateId = data.readInt32();
+
+ camera_metadata_t* request = NULL;
+ status_t ret;
+ ret = createDefaultRequest(templateId, &request);
+
+ writeMetadata(*reply, request);
+ reply->writeInt32(ret);
+
+ free_camera_metadata(request);
+
+ return NO_ERROR;
+ } break;
+ case GET_CAMERA_INFO: {
+ CHECK_INTERFACE(IProCameraUser, data, reply);
+
+ int cameraId = data.readInt32();
+
+ camera_metadata_t* info = NULL;
+ status_t ret;
+ ret = getCameraInfo(cameraId, &info);
+
+ writeMetadata(*reply, info);
+ reply->writeInt32(ret);
+
+ free_camera_metadata(info);
+
+ return NO_ERROR;
+ } break;
+ default:
+ return BBinder::onTransact(code, data, reply, flags);
+ }
+}
+
+// ----------------------------------------------------------------------------
+
+}; // namespace android
diff --git a/camera/ProCamera.cpp b/camera/ProCamera.cpp
new file mode 100644
index 0000000..fec5461
--- /dev/null
+++ b/camera/ProCamera.cpp
@@ -0,0 +1,431 @@
+/*
+**
+** Copyright (C) 2013, 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_NDEBUG 0
+#define LOG_TAG "ProCamera"
+#include <utils/Log.h>
+#include <utils/threads.h>
+#include <utils/Mutex.h>
+
+#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
+#include <binder/IMemory.h>
+
+#include <camera/ProCamera.h>
+#include <camera/ICameraService.h>
+#include <camera/IProCameraUser.h>
+#include <camera/IProCameraCallbacks.h>
+
+#include <gui/IGraphicBufferProducer.h>
+
+#include <system/camera_metadata.h>
+
+namespace android {
+
+sp<ProCamera> ProCamera::connect(int cameraId)
+{
+ return CameraBaseT::connect(cameraId, String16(),
+ ICameraService::USE_CALLING_UID);
+}
+
+ProCamera::ProCamera(int cameraId)
+ : CameraBase(cameraId)
+{
+}
+
+ProCamera::~ProCamera()
+{
+
+}
+
+/* IProCameraUser's implementation */
+
+// callback from camera service
+void ProCamera::notifyCallback(int32_t msgType, int32_t ext1, int32_t ext2)
+{
+ return CameraBaseT::notifyCallback(msgType, ext1, ext2);
+}
+
+void ProCamera::onLockStatusChanged(
+ IProCameraCallbacks::LockStatus newLockStatus)
+{
+ ALOGV("%s: newLockStatus = %d", __FUNCTION__, newLockStatus);
+
+ sp<ProCameraListener> listener;
+ {
+ Mutex::Autolock _l(mLock);
+ listener = mListener;
+ }
+ if (listener != NULL) {
+ switch (newLockStatus) {
+ case IProCameraCallbacks::LOCK_ACQUIRED:
+ listener->onLockAcquired();
+ break;
+ case IProCameraCallbacks::LOCK_RELEASED:
+ listener->onLockReleased();
+ break;
+ case IProCameraCallbacks::LOCK_STOLEN:
+ listener->onLockStolen();
+ break;
+ default:
+ ALOGE("%s: Unknown lock status: %d",
+ __FUNCTION__, newLockStatus);
+ }
+ }
+}
+
+void ProCamera::onResultReceived(int32_t frameId, camera_metadata* result) {
+ ALOGV("%s: frameId = %d, result = %p", __FUNCTION__, frameId, result);
+
+ sp<ProCameraListener> listener;
+ {
+ Mutex::Autolock _l(mLock);
+ listener = mListener;
+ }
+
+ CameraMetadata tmp(result);
+
+ // Unblock waitForFrame(id) callers
+ {
+ Mutex::Autolock al(mWaitMutex);
+ mMetadataReady = true;
+ mLatestMetadata = tmp; // make copy
+ mWaitCondition.broadcast();
+ }
+
+ result = tmp.release();
+
+ if (listener != NULL) {
+ listener->onResultReceived(frameId, result);
+ } else {
+ free_camera_metadata(result);
+ }
+
+}
+
+status_t ProCamera::exclusiveTryLock()
+{
+ sp <IProCameraUser> c = mCamera;
+ if (c == 0) return NO_INIT;
+
+ return c->exclusiveTryLock();
+}
+status_t ProCamera::exclusiveLock()
+{
+ sp <IProCameraUser> c = mCamera;
+ if (c == 0) return NO_INIT;
+
+ return c->exclusiveLock();
+}
+status_t ProCamera::exclusiveUnlock()
+{
+ sp <IProCameraUser> c = mCamera;
+ if (c == 0) return NO_INIT;
+
+ return c->exclusiveUnlock();
+}
+bool ProCamera::hasExclusiveLock()
+{
+ sp <IProCameraUser> c = mCamera;
+ if (c == 0) return NO_INIT;
+
+ return c->hasExclusiveLock();
+}
+
+// Note that the callee gets a copy of the metadata.
+int ProCamera::submitRequest(const struct camera_metadata* metadata,
+ bool streaming)
+{
+ sp <IProCameraUser> c = mCamera;
+ if (c == 0) return NO_INIT;
+
+ return c->submitRequest(const_cast<struct camera_metadata*>(metadata),
+ streaming);
+}
+
+status_t ProCamera::cancelRequest(int requestId)
+{
+ sp <IProCameraUser> c = mCamera;
+ if (c == 0) return NO_INIT;
+
+ return c->cancelRequest(requestId);
+}
+
+status_t ProCamera::deleteStream(int streamId)
+{
+ sp <IProCameraUser> c = mCamera;
+ if (c == 0) return NO_INIT;
+
+ status_t s = c->deleteStream(streamId);
+
+ mStreams.removeItem(streamId);
+
+ return s;
+}
+
+status_t ProCamera::createStream(int width, int height, int format,
+ const sp<Surface>& surface,
+ /*out*/
+ int* streamId)
+{
+ *streamId = -1;
+
+ ALOGV("%s: createStreamW %dx%d (fmt=0x%x)", __FUNCTION__, width, height,
+ format);
+
+ if (surface == 0) {
+ return BAD_VALUE;
+ }
+
+ return createStream(width, height, format,
+ surface->getIGraphicBufferProducer(),
+ streamId);
+}
+
+status_t ProCamera::createStream(int width, int height, int format,
+ const sp<IGraphicBufferProducer>& bufferProducer,
+ /*out*/
+ int* streamId) {
+ *streamId = -1;
+
+ ALOGV("%s: createStreamT %dx%d (fmt=0x%x)", __FUNCTION__, width, height,
+ format);
+
+ if (bufferProducer == 0) {
+ return BAD_VALUE;
+ }
+
+ sp <IProCameraUser> c = mCamera;
+ status_t stat = c->createStream(width, height, format, bufferProducer,
+ streamId);
+
+ if (stat == OK) {
+ StreamInfo s(*streamId);
+
+ mStreams.add(*streamId, s);
+ }
+
+ return stat;
+}
+
+status_t ProCamera::createStreamCpu(int width, int height, int format,
+ int heapCount,
+ /*out*/
+ sp<CpuConsumer>* cpuConsumer,
+ int* streamId) {
+ return createStreamCpu(width, height, format, heapCount,
+ /*synchronousMode*/true,
+ cpuConsumer, streamId);
+}
+
+status_t ProCamera::createStreamCpu(int width, int height, int format,
+ int heapCount,
+ bool synchronousMode,
+ /*out*/
+ sp<CpuConsumer>* cpuConsumer,
+ int* streamId)
+{
+ ALOGV("%s: createStreamW %dx%d (fmt=0x%x)", __FUNCTION__, width, height,
+ format);
+
+ *cpuConsumer = NULL;
+
+ sp <IProCameraUser> c = mCamera;
+ if (c == 0) return NO_INIT;
+
+ sp<CpuConsumer> cc = new CpuConsumer(heapCount, synchronousMode);
+ cc->setName(String8("ProCamera::mCpuConsumer"));
+
+ sp<Surface> stc = new Surface(
+ cc->getProducerInterface());
+
+ status_t s = createStream(width, height, format,
+ stc->getIGraphicBufferProducer(),
+ streamId);
+
+ if (s != OK) {
+ ALOGE("%s: Failure to create stream %dx%d (fmt=0x%x)", __FUNCTION__,
+ width, height, format);
+ return s;
+ }
+
+ sp<ProFrameListener> frameAvailableListener =
+ new ProFrameListener(this, *streamId);
+
+ getStreamInfo(*streamId).cpuStream = true;
+ getStreamInfo(*streamId).cpuConsumer = cc;
+ getStreamInfo(*streamId).synchronousMode = synchronousMode;
+ getStreamInfo(*streamId).stc = stc;
+ // for lifetime management
+ getStreamInfo(*streamId).frameAvailableListener = frameAvailableListener;
+
+ cc->setFrameAvailableListener(frameAvailableListener);
+
+ *cpuConsumer = cc;
+
+ return s;
+}
+
+camera_metadata* ProCamera::getCameraInfo(int cameraId) {
+ ALOGV("%s: cameraId = %d", __FUNCTION__, cameraId);
+
+ sp <IProCameraUser> c = mCamera;
+ if (c == 0) return NULL;
+
+ camera_metadata* ptr = NULL;
+ status_t status = c->getCameraInfo(cameraId, &ptr);
+
+ if (status != OK) {
+ ALOGE("%s: Failed to get camera info, error = %d", __FUNCTION__, status);
+ }
+
+ return ptr;
+}
+
+status_t ProCamera::createDefaultRequest(int templateId,
+ camera_metadata** request) const {
+ ALOGV("%s: templateId = %d", __FUNCTION__, templateId);
+
+ sp <IProCameraUser> c = mCamera;
+ if (c == 0) return NO_INIT;
+
+ return c->createDefaultRequest(templateId, request);
+}
+
+void ProCamera::onFrameAvailable(int streamId) {
+ ALOGV("%s: streamId = %d", __FUNCTION__, streamId);
+
+ sp<ProCameraListener> listener = mListener;
+ StreamInfo& stream = getStreamInfo(streamId);
+
+ if (listener.get() != NULL) {
+ listener->onFrameAvailable(streamId, stream.cpuConsumer);
+ }
+
+ // Unblock waitForFrame(id) callers
+ {
+ Mutex::Autolock al(mWaitMutex);
+ getStreamInfo(streamId).frameReady++;
+ mWaitCondition.broadcast();
+ }
+}
+
+int ProCamera::waitForFrameBuffer(int streamId) {
+ status_t stat = BAD_VALUE;
+ Mutex::Autolock al(mWaitMutex);
+
+ StreamInfo& si = getStreamInfo(streamId);
+
+ if (si.frameReady > 0) {
+ int numFrames = si.frameReady;
+ si.frameReady = 0;
+ return numFrames;
+ } else {
+ while (true) {
+ stat = mWaitCondition.waitRelative(mWaitMutex,
+ mWaitTimeout);
+ if (stat != OK) {
+ ALOGE("%s: Error while waiting for frame buffer: %d",
+ __FUNCTION__, stat);
+ return stat;
+ }
+
+ if (si.frameReady > 0) {
+ int numFrames = si.frameReady;
+ si.frameReady = 0;
+ return numFrames;
+ }
+ // else it was some other stream that got unblocked
+ }
+ }
+
+ return stat;
+}
+
+int ProCamera::dropFrameBuffer(int streamId, int count) {
+ StreamInfo& si = getStreamInfo(streamId);
+
+ if (!si.cpuStream) {
+ return BAD_VALUE;
+ } else if (count < 0) {
+ return BAD_VALUE;
+ }
+
+ if (!si.synchronousMode) {
+ ALOGW("%s: No need to drop frames on asynchronous streams,"
+ " as asynchronous mode only keeps 1 latest frame around.",
+ __FUNCTION__);
+ return BAD_VALUE;
+ }
+
+ int numDropped = 0;
+ for (int i = 0; i < count; ++i) {
+ CpuConsumer::LockedBuffer buffer;
+ if (si.cpuConsumer->lockNextBuffer(&buffer) != OK) {
+ break;
+ }
+
+ si.cpuConsumer->unlockBuffer(buffer);
+ numDropped++;
+ }
+
+ return numDropped;
+}
+
+status_t ProCamera::waitForFrameMetadata() {
+ status_t stat = BAD_VALUE;
+ Mutex::Autolock al(mWaitMutex);
+
+ if (mMetadataReady) {
+ return OK;
+ } else {
+ while (true) {
+ stat = mWaitCondition.waitRelative(mWaitMutex,
+ mWaitTimeout);
+
+ if (stat != OK) {
+ ALOGE("%s: Error while waiting for metadata: %d",
+ __FUNCTION__, stat);
+ return stat;
+ }
+
+ if (mMetadataReady) {
+ mMetadataReady = false;
+ return OK;
+ }
+ // else it was some other stream or metadata
+ }
+ }
+
+ return stat;
+}
+
+CameraMetadata ProCamera::consumeFrameMetadata() {
+ Mutex::Autolock al(mWaitMutex);
+
+ // Destructive: Subsequent calls return empty metadatas
+ CameraMetadata tmp = mLatestMetadata;
+ mLatestMetadata.clear();
+
+ return tmp;
+}
+
+ProCamera::StreamInfo& ProCamera::getStreamInfo(int streamId) {
+ return mStreams.editValueFor(streamId);
+}
+
+}; // namespace android
diff --git a/camera/tests/Android.mk b/camera/tests/Android.mk
new file mode 100644
index 0000000..ec13911
--- /dev/null
+++ b/camera/tests/Android.mk
@@ -0,0 +1,38 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+ main.cpp \
+ ProCameraTests.cpp \
+
+LOCAL_SHARED_LIBRARIES := \
+ libutils \
+ libcutils \
+ libstlport \
+ libcamera_metadata \
+ libcamera_client \
+ libgui \
+ libsync \
+ libui \
+ libdl \
+ libbinder
+
+LOCAL_STATIC_LIBRARIES := \
+ libgtest
+
+LOCAL_C_INCLUDES += \
+ bionic \
+ bionic/libstdc++/include \
+ external/gtest/include \
+ external/stlport/stlport \
+ system/media/camera/include \
+ frameworks/av/services/camera/libcameraservice \
+ frameworks/av/include/camera \
+ frameworks/native/include \
+
+LOCAL_CFLAGS += -Wall -Wextra
+
+LOCAL_MODULE:= camera_client_test
+LOCAL_MODULE_TAGS := tests
+
+include $(BUILD_NATIVE_TEST)
diff --git a/camera/tests/ProCameraTests.cpp b/camera/tests/ProCameraTests.cpp
new file mode 100644
index 0000000..f203949
--- /dev/null
+++ b/camera/tests/ProCameraTests.cpp
@@ -0,0 +1,1281 @@
+/*
+ * Copyright (C) 2013 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.
+ */
+
+#include <gtest/gtest.h>
+#include <iostream>
+
+#include <binder/IPCThreadState.h>
+#include <utils/Thread.h>
+
+#include "Camera.h"
+#include "ProCamera.h"
+#include <utils/Vector.h>
+#include <utils/Mutex.h>
+#include <utils/Condition.h>
+
+#include <gui/SurfaceComposerClient.h>
+#include <gui/Surface.h>
+
+#include <system/camera_metadata.h>
+#include <hardware/camera2.h> // for CAMERA2_TEMPLATE_PREVIEW only
+#include <camera/CameraMetadata.h>
+
+#include <camera/ICameraServiceListener.h>
+
+namespace android {
+namespace camera2 {
+namespace tests {
+namespace client {
+
+#define CAMERA_ID 0
+#define TEST_DEBUGGING 0
+
+#define TEST_LISTENER_TIMEOUT 1000000000 // 1 second listener timeout
+#define TEST_FORMAT HAL_PIXEL_FORMAT_Y16 //TODO: YUY2 instead
+
+#define TEST_FORMAT_MAIN HAL_PIXEL_FORMAT_Y8
+#define TEST_FORMAT_DEPTH HAL_PIXEL_FORMAT_Y16
+
+// defaults for display "test"
+#define TEST_DISPLAY_FORMAT HAL_PIXEL_FORMAT_Y8
+#define TEST_DISPLAY_WIDTH 320
+#define TEST_DISPLAY_HEIGHT 240
+
+#define TEST_CPU_FRAME_COUNT 2
+#define TEST_CPU_HEAP_COUNT 5
+
+#define TEST_FRAME_PROCESSING_DELAY_US 200000 // 200 ms
+
+#if TEST_DEBUGGING
+#define dout std::cerr
+#else
+#define dout if (0) std::cerr
+#endif
+
+#define EXPECT_OK(x) EXPECT_EQ(OK, (x))
+#define ASSERT_OK(x) ASSERT_EQ(OK, (x))
+
+class ProCameraTest;
+
+struct ServiceListener : public BnCameraServiceListener {
+
+ ServiceListener() :
+ mLatestStatus(STATUS_UNKNOWN),
+ mPrevStatus(STATUS_UNKNOWN)
+ {
+ }
+
+ void onStatusChanged(Status status, int32_t cameraId) {
+ dout << "On status changed: 0x" << std::hex
+ << (unsigned int) status << " cameraId " << cameraId
+ << std::endl;
+
+ Mutex::Autolock al(mMutex);
+
+ mLatestStatus = status;
+ mCondition.broadcast();
+ }
+
+ status_t waitForStatusChange(Status& newStatus) {
+ Mutex::Autolock al(mMutex);
+
+ if (mLatestStatus != mPrevStatus) {
+ newStatus = mLatestStatus;
+ mPrevStatus = mLatestStatus;
+ return OK;
+ }
+
+ status_t stat = mCondition.waitRelative(mMutex,
+ TEST_LISTENER_TIMEOUT);
+
+ if (stat == OK) {
+ newStatus = mLatestStatus;
+ mPrevStatus = mLatestStatus;
+ }
+
+ return stat;
+ }
+
+ Condition mCondition;
+ Mutex mMutex;
+
+ Status mLatestStatus;
+ Status mPrevStatus;
+};
+
+enum ProEvent {
+ UNKNOWN,
+ ACQUIRED,
+ RELEASED,
+ STOLEN,
+ FRAME_RECEIVED,
+ RESULT_RECEIVED,
+};
+
+inline int ProEvent_Mask(ProEvent e) {
+ return (1 << static_cast<int>(e));
+}
+
+typedef Vector<ProEvent> EventList;
+
+class ProCameraTestThread : public Thread
+{
+public:
+ ProCameraTestThread() {
+ }
+
+ virtual bool threadLoop() {
+ mProc = ProcessState::self();
+ mProc->startThreadPool();
+
+ IPCThreadState *ptr = IPCThreadState::self();
+
+ ptr->joinThreadPool();
+
+ return false;
+ }
+
+ sp<ProcessState> mProc;
+};
+
+class ProCameraTestListener : public ProCameraListener {
+
+public:
+ static const int EVENT_MASK_ALL = 0xFFFFFFFF;
+
+ ProCameraTestListener() {
+ mEventMask = EVENT_MASK_ALL;
+ mDropFrames = false;
+ }
+
+ status_t WaitForEvent() {
+ Mutex::Autolock cal(mConditionMutex);
+
+ {
+ Mutex::Autolock al(mListenerMutex);
+
+ if (mProEventList.size() > 0) {
+ return OK;
+ }
+ }
+
+ return mListenerCondition.waitRelative(mConditionMutex,
+ TEST_LISTENER_TIMEOUT);
+ }
+
+ /* Read events into out. Existing queue is flushed */
+ void ReadEvents(EventList& out) {
+ Mutex::Autolock al(mListenerMutex);
+
+ for (size_t i = 0; i < mProEventList.size(); ++i) {
+ out.push(mProEventList[i]);
+ }
+
+ mProEventList.clear();
+ }
+
+ /**
+ * Dequeue 1 event from the event queue.
+ * Returns UNKNOWN if queue is empty
+ */
+ ProEvent ReadEvent() {
+ Mutex::Autolock al(mListenerMutex);
+
+ if (mProEventList.size() == 0) {
+ return UNKNOWN;
+ }
+
+ ProEvent ev = mProEventList[0];
+ mProEventList.removeAt(0);
+
+ return ev;
+ }
+
+ void SetEventMask(int eventMask) {
+ Mutex::Autolock al(mListenerMutex);
+ mEventMask = eventMask;
+ }
+
+ // Automatically acquire/release frames as they are available
+ void SetDropFrames(bool dropFrames) {
+ Mutex::Autolock al(mListenerMutex);
+ mDropFrames = dropFrames;
+ }
+
+private:
+ void QueueEvent(ProEvent ev) {
+ bool eventAdded = false;
+ {
+ Mutex::Autolock al(mListenerMutex);
+
+ // Drop events not part of mask
+ if (ProEvent_Mask(ev) & mEventMask) {
+ mProEventList.push(ev);
+ eventAdded = true;
+ }
+ }
+
+ if (eventAdded) {
+ mListenerCondition.broadcast();
+ }
+ }
+
+protected:
+
+ //////////////////////////////////////////////////
+ ///////// ProCameraListener //////////////////////
+ //////////////////////////////////////////////////
+
+
+ // Lock has been acquired. Write operations now available.
+ virtual void onLockAcquired() {
+ QueueEvent(ACQUIRED);
+ }
+ // Lock has been released with exclusiveUnlock
+ virtual void onLockReleased() {
+ QueueEvent(RELEASED);
+ }
+
+ // Lock has been stolen by another client.
+ virtual void onLockStolen() {
+ QueueEvent(STOLEN);
+ }
+
+ // Lock free.
+ virtual void onTriggerNotify(int32_t ext1, int32_t ext2, int32_t ext3) {
+
+ dout << "Trigger notify: " << ext1 << " " << ext2
+ << " " << ext3 << std::endl;
+ }
+
+ virtual void onFrameAvailable(int streamId,
+ const sp<CpuConsumer>& consumer) {
+
+ QueueEvent(FRAME_RECEIVED);
+
+ Mutex::Autolock al(mListenerMutex);
+ if (mDropFrames) {
+ CpuConsumer::LockedBuffer buf;
+ status_t ret;
+
+ EXPECT_OK(ret);
+ if (OK == (ret = consumer->lockNextBuffer(&buf))) {
+
+ dout << "Frame received on streamId = " << streamId <<
+ ", dataPtr = " << (void*)buf.data <<
+ ", timestamp = " << buf.timestamp << std::endl;
+
+ EXPECT_OK(consumer->unlockBuffer(buf));
+ }
+ } else {
+ dout << "Frame received on streamId = " << streamId << std::endl;
+ }
+ }
+
+ virtual void onResultReceived(int32_t frameId,
+ camera_metadata* request) {
+ dout << "Result received frameId = " << frameId
+ << ", requestPtr = " << (void*)request << std::endl;
+ QueueEvent(RESULT_RECEIVED);
+ free_camera_metadata(request);
+ }
+
+ virtual void notify(int32_t msg, int32_t ext1, int32_t ext2) {
+ dout << "Notify received: msg " << std::hex << msg
+ << ", ext1: " << std::hex << ext1 << ", ext2: " << std::hex << ext2
+ << std::endl;
+ }
+
+ Vector<ProEvent> mProEventList;
+ Mutex mListenerMutex;
+ Mutex mConditionMutex;
+ Condition mListenerCondition;
+ int mEventMask;
+ bool mDropFrames;
+};
+
+class ProCameraTest : public ::testing::Test {
+
+public:
+ ProCameraTest() {
+ char* displaySecsEnv = getenv("TEST_DISPLAY_SECS");
+ if (displaySecsEnv != NULL) {
+ mDisplaySecs = atoi(displaySecsEnv);
+ if (mDisplaySecs < 0) {
+ mDisplaySecs = 0;
+ }
+ } else {
+ mDisplaySecs = 0;
+ }
+
+ char* displayFmtEnv = getenv("TEST_DISPLAY_FORMAT");
+ if (displayFmtEnv != NULL) {
+ mDisplayFmt = FormatFromString(displayFmtEnv);
+ } else {
+ mDisplayFmt = TEST_DISPLAY_FORMAT;
+ }
+
+ char* displayWidthEnv = getenv("TEST_DISPLAY_WIDTH");
+ if (displayWidthEnv != NULL) {
+ mDisplayW = atoi(displayWidthEnv);
+ if (mDisplayW < 0) {
+ mDisplayW = 0;
+ }
+ } else {
+ mDisplayW = TEST_DISPLAY_WIDTH;
+ }
+
+ char* displayHeightEnv = getenv("TEST_DISPLAY_HEIGHT");
+ if (displayHeightEnv != NULL) {
+ mDisplayH = atoi(displayHeightEnv);
+ if (mDisplayH < 0) {
+ mDisplayH = 0;
+ }
+ } else {
+ mDisplayH = TEST_DISPLAY_HEIGHT;
+ }
+ }
+
+ static void SetUpTestCase() {
+ // Binder Thread Pool Initialization
+ mTestThread = new ProCameraTestThread();
+ mTestThread->run("ProCameraTestThread");
+ }
+
+ virtual void SetUp() {
+ mCamera = ProCamera::connect(CAMERA_ID);
+ ASSERT_NE((void*)NULL, mCamera.get());
+
+ mListener = new ProCameraTestListener();
+ mCamera->setListener(mListener);
+ }
+
+ virtual void TearDown() {
+ ASSERT_NE((void*)NULL, mCamera.get());
+ mCamera->disconnect();
+ }
+
+protected:
+ sp<ProCamera> mCamera;
+ sp<ProCameraTestListener> mListener;
+
+ static sp<Thread> mTestThread;
+
+ int mDisplaySecs;
+ int mDisplayFmt;
+ int mDisplayW;
+ int mDisplayH;
+
+ sp<SurfaceComposerClient> mComposerClient;
+ sp<SurfaceControl> mSurfaceControl;
+
+ sp<SurfaceComposerClient> mDepthComposerClient;
+ sp<SurfaceControl> mDepthSurfaceControl;
+
+ int getSurfaceWidth() {
+ return 512;
+ }
+ int getSurfaceHeight() {
+ return 512;
+ }
+
+ void createOnScreenSurface(sp<Surface>& surface) {
+ mComposerClient = new SurfaceComposerClient;
+ ASSERT_EQ(NO_ERROR, mComposerClient->initCheck());
+
+ mSurfaceControl = mComposerClient->createSurface(
+ String8("ProCameraTest StreamingImage Surface"),
+ getSurfaceWidth(), getSurfaceHeight(),
+ PIXEL_FORMAT_RGB_888, 0);
+
+ mSurfaceControl->setPosition(0, 0);
+
+ ASSERT_TRUE(mSurfaceControl != NULL);
+ ASSERT_TRUE(mSurfaceControl->isValid());
+
+ SurfaceComposerClient::openGlobalTransaction();
+ ASSERT_EQ(NO_ERROR, mSurfaceControl->setLayer(0x7FFFFFFF));
+ ASSERT_EQ(NO_ERROR, mSurfaceControl->show());
+ SurfaceComposerClient::closeGlobalTransaction();
+
+ sp<ANativeWindow> window = mSurfaceControl->getSurface();
+ surface = mSurfaceControl->getSurface();
+
+ ASSERT_NE((void*)NULL, surface.get());
+ }
+
+ void createDepthOnScreenSurface(sp<Surface>& surface) {
+ mDepthComposerClient = new SurfaceComposerClient;
+ ASSERT_EQ(NO_ERROR, mDepthComposerClient->initCheck());
+
+ mDepthSurfaceControl = mDepthComposerClient->createSurface(
+ String8("ProCameraTest StreamingImage Surface"),
+ getSurfaceWidth(), getSurfaceHeight(),
+ PIXEL_FORMAT_RGB_888, 0);
+
+ mDepthSurfaceControl->setPosition(640, 0);
+
+ ASSERT_TRUE(mDepthSurfaceControl != NULL);
+ ASSERT_TRUE(mDepthSurfaceControl->isValid());
+
+ SurfaceComposerClient::openGlobalTransaction();
+ ASSERT_EQ(NO_ERROR, mDepthSurfaceControl->setLayer(0x7FFFFFFF));
+ ASSERT_EQ(NO_ERROR, mDepthSurfaceControl->show());
+ SurfaceComposerClient::closeGlobalTransaction();
+
+ sp<ANativeWindow> window = mDepthSurfaceControl->getSurface();
+ surface = mDepthSurfaceControl->getSurface();
+
+ ASSERT_NE((void*)NULL, surface.get());
+ }
+
+ template <typename T>
+ static bool ExistsItem(T needle, T* array, size_t count) {
+ if (!array) {
+ return false;
+ }
+
+ for (size_t i = 0; i < count; ++i) {
+ if (array[i] == needle) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+
+ static int FormatFromString(const char* str) {
+ std::string s(str);
+
+#define CMP_STR(x, y) \
+ if (s == #x) return HAL_PIXEL_FORMAT_ ## y;
+#define CMP_STR_SAME(x) CMP_STR(x, x)
+
+ CMP_STR_SAME( Y16);
+ CMP_STR_SAME( Y8);
+ CMP_STR_SAME( YV12);
+ CMP_STR(NV16, YCbCr_422_SP);
+ CMP_STR(NV21, YCrCb_420_SP);
+ CMP_STR(YUY2, YCbCr_422_I);
+ CMP_STR(RAW, RAW_SENSOR);
+ CMP_STR(RGBA, RGBA_8888);
+
+ std::cerr << "Unknown format string " << str << std::endl;
+ return -1;
+
+ }
+
+ /**
+ * Creating a streaming request for these output streams from a template,
+ * and submit it
+ */
+ void createSubmitRequestForStreams(uint8_t* streamIds, size_t count, int requestCount=-1) {
+
+ ASSERT_NE((void*)NULL, streamIds);
+ ASSERT_LT(0u, count);
+
+ camera_metadata_t *requestTmp = NULL;
+ EXPECT_OK(mCamera->createDefaultRequest(CAMERA2_TEMPLATE_PREVIEW,
+ /*out*/&requestTmp));
+ ASSERT_NE((void*)NULL, requestTmp);
+ CameraMetadata request(requestTmp);
+
+ // set the output streams. default is empty
+
+ uint32_t tag = static_cast<uint32_t>(ANDROID_REQUEST_OUTPUT_STREAMS);
+ request.update(tag, streamIds, count);
+
+ requestTmp = request.release();
+
+ if (requestCount < 0) {
+ EXPECT_OK(mCamera->submitRequest(requestTmp, /*streaming*/true));
+ } else {
+ for (int i = 0; i < requestCount; ++i) {
+ EXPECT_OK(mCamera->submitRequest(requestTmp,
+ /*streaming*/false));
+ }
+ }
+ request.acquire(requestTmp);
+ }
+};
+
+sp<Thread> ProCameraTest::mTestThread;
+
+TEST_F(ProCameraTest, AvailableFormats) {
+ if (HasFatalFailure()) {
+ return;
+ }
+
+ CameraMetadata staticInfo = mCamera->getCameraInfo(CAMERA_ID);
+ ASSERT_FALSE(staticInfo.isEmpty());
+
+ uint32_t tag = static_cast<uint32_t>(ANDROID_SCALER_AVAILABLE_FORMATS);
+ EXPECT_TRUE(staticInfo.exists(tag));
+ camera_metadata_entry_t entry = staticInfo.find(tag);
+
+ EXPECT_TRUE(ExistsItem<int32_t>(HAL_PIXEL_FORMAT_YV12,
+ entry.data.i32, entry.count));
+ EXPECT_TRUE(ExistsItem<int32_t>(HAL_PIXEL_FORMAT_YCrCb_420_SP,
+ entry.data.i32, entry.count));
+}
+
+// test around exclusiveTryLock (immediate locking)
+TEST_F(ProCameraTest, LockingImmediate) {
+
+ if (HasFatalFailure()) {
+ return;
+ }
+
+ mListener->SetEventMask(ProEvent_Mask(ACQUIRED) |
+ ProEvent_Mask(STOLEN) |
+ ProEvent_Mask(RELEASED));
+
+ EXPECT_FALSE(mCamera->hasExclusiveLock());
+ EXPECT_EQ(OK, mCamera->exclusiveTryLock());
+ // at this point we definitely have the lock
+
+ EXPECT_EQ(OK, mListener->WaitForEvent());
+ EXPECT_EQ(ACQUIRED, mListener->ReadEvent());
+
+ EXPECT_TRUE(mCamera->hasExclusiveLock());
+ EXPECT_EQ(OK, mCamera->exclusiveUnlock());
+
+ EXPECT_EQ(OK, mListener->WaitForEvent());
+ EXPECT_EQ(RELEASED, mListener->ReadEvent());
+
+ EXPECT_FALSE(mCamera->hasExclusiveLock());
+}
+
+// test around exclusiveLock (locking at some future point in time)
+TEST_F(ProCameraTest, LockingAsynchronous) {
+
+ if (HasFatalFailure()) {
+ return;
+ }
+
+
+ mListener->SetEventMask(ProEvent_Mask(ACQUIRED) |
+ ProEvent_Mask(STOLEN) |
+ ProEvent_Mask(RELEASED));
+
+ // TODO: Add another procamera that has a lock here.
+ // then we can be test that the lock wont immediately be acquired
+
+ EXPECT_FALSE(mCamera->hasExclusiveLock());
+ EXPECT_EQ(OK, mCamera->exclusiveTryLock());
+ // at this point we definitely have the lock
+
+ EXPECT_EQ(OK, mListener->WaitForEvent());
+ EXPECT_EQ(ACQUIRED, mListener->ReadEvent());
+
+ EXPECT_TRUE(mCamera->hasExclusiveLock());
+ EXPECT_EQ(OK, mCamera->exclusiveUnlock());
+
+ EXPECT_EQ(OK, mListener->WaitForEvent());
+ EXPECT_EQ(RELEASED, mListener->ReadEvent());
+
+ EXPECT_FALSE(mCamera->hasExclusiveLock());
+}
+
+// Stream directly to the screen.
+TEST_F(ProCameraTest, DISABLED_StreamingImageSingle) {
+ if (HasFatalFailure()) {
+ return;
+ }
+
+ sp<Surface> surface;
+ if (mDisplaySecs > 0) {
+ createOnScreenSurface(/*out*/surface);
+ }
+ else {
+ dout << "Skipping, will not render to screen" << std::endl;
+ return;
+ }
+
+ int depthStreamId = -1;
+
+ sp<ServiceListener> listener = new ServiceListener();
+ EXPECT_OK(ProCamera::addServiceListener(listener));
+
+ ServiceListener::Status currentStatus;
+
+ // when subscribing a new listener,
+ // we immediately get a callback to the current status
+ while (listener->waitForStatusChange(/*out*/currentStatus) != OK);
+ EXPECT_EQ(ServiceListener::STATUS_PRESENT, currentStatus);
+
+ dout << "Will now stream and resume infinitely..." << std::endl;
+ while (true) {
+
+ if (currentStatus == ServiceListener::STATUS_PRESENT) {
+
+ ASSERT_OK(mCamera->createStream(mDisplayW, mDisplayH, mDisplayFmt,
+ surface,
+ &depthStreamId));
+ EXPECT_NE(-1, depthStreamId);
+
+ EXPECT_OK(mCamera->exclusiveTryLock());
+
+ uint8_t streams[] = { depthStreamId };
+ ASSERT_NO_FATAL_FAILURE(createSubmitRequestForStreams(
+ streams,
+ /*count*/1));
+ }
+
+ ServiceListener::Status stat = ServiceListener::STATUS_UNKNOWN;
+
+ // TODO: maybe check for getch every once in a while?
+ while (listener->waitForStatusChange(/*out*/stat) != OK);
+
+ if (currentStatus != stat) {
+ if (stat == ServiceListener::STATUS_PRESENT) {
+ dout << "Reconnecting to camera" << std::endl;
+ mCamera = ProCamera::connect(CAMERA_ID);
+ } else if (stat == ServiceListener::STATUS_NOT_AVAILABLE) {
+ dout << "Disconnecting from camera" << std::endl;
+ mCamera->disconnect();
+ } else if (stat == ServiceListener::STATUS_NOT_PRESENT) {
+ dout << "Camera unplugged" << std::endl;
+ mCamera = NULL;
+ } else {
+ dout << "Unknown status change "
+ << std::hex << stat << std::endl;
+ }
+
+ currentStatus = stat;
+ }
+ }
+
+ EXPECT_OK(ProCamera::removeServiceListener(listener));
+ EXPECT_OK(mCamera->deleteStream(depthStreamId));
+ EXPECT_OK(mCamera->exclusiveUnlock());
+}
+
+// Stream directly to the screen.
+TEST_F(ProCameraTest, DISABLED_StreamingImageDual) {
+ if (HasFatalFailure()) {
+ return;
+ }
+ sp<Surface> surface;
+ sp<Surface> depthSurface;
+ if (mDisplaySecs > 0) {
+ createOnScreenSurface(/*out*/surface);
+ createDepthOnScreenSurface(/*out*/depthSurface);
+ }
+
+ int streamId = -1;
+ EXPECT_OK(mCamera->createStream(/*width*/1280, /*height*/960,
+ TEST_FORMAT_MAIN, surface, &streamId));
+ EXPECT_NE(-1, streamId);
+
+ int depthStreamId = -1;
+ EXPECT_OK(mCamera->createStream(/*width*/320, /*height*/240,
+ TEST_FORMAT_DEPTH, depthSurface, &depthStreamId));
+ EXPECT_NE(-1, depthStreamId);
+
+ EXPECT_OK(mCamera->exclusiveTryLock());
+ /*
+ */
+ /* iterate in a loop submitting requests every frame.
+ * what kind of requests doesnt really matter, just whatever.
+ */
+
+ // it would probably be better to use CameraMetadata from camera service.
+ camera_metadata_t *request = NULL;
+ EXPECT_OK(mCamera->createDefaultRequest(CAMERA2_TEMPLATE_PREVIEW,
+ /*out*/&request));
+ EXPECT_NE((void*)NULL, request);
+
+ /*FIXME: dont need this later, at which point the above should become an
+ ASSERT_NE*/
+ if(request == NULL) request = allocate_camera_metadata(10, 100);
+
+ // set the output streams to just this stream ID
+
+ // wow what a verbose API.
+ uint8_t allStreams[] = { streamId, depthStreamId };
+ // IMPORTANT. bad things will happen if its not a uint8.
+ size_t streamCount = sizeof(allStreams) / sizeof(allStreams[0]);
+ camera_metadata_entry_t entry;
+ uint32_t tag = static_cast<uint32_t>(ANDROID_REQUEST_OUTPUT_STREAMS);
+ int find = find_camera_metadata_entry(request, tag, &entry);
+ if (find == -ENOENT) {
+ if (add_camera_metadata_entry(request, tag, &allStreams,
+ /*data_count*/streamCount) != OK) {
+ camera_metadata_t *tmp = allocate_camera_metadata(1000, 10000);
+ ASSERT_OK(append_camera_metadata(tmp, request));
+ free_camera_metadata(request);
+ request = tmp;
+
+ ASSERT_OK(add_camera_metadata_entry(request, tag, &allStreams,
+ /*data_count*/streamCount));
+ }
+ } else {
+ ASSERT_OK(update_camera_metadata_entry(request, entry.index,
+ &allStreams, /*data_count*/streamCount, &entry));
+ }
+
+ EXPECT_OK(mCamera->submitRequest(request, /*streaming*/true));
+
+ dout << "will sleep now for " << mDisplaySecs << std::endl;
+ sleep(mDisplaySecs);
+
+ free_camera_metadata(request);
+
+ for (int i = 0; i < streamCount; ++i) {
+ EXPECT_OK(mCamera->deleteStream(allStreams[i]));
+ }
+ EXPECT_OK(mCamera->exclusiveUnlock());
+}
+
+TEST_F(ProCameraTest, CpuConsumerSingle) {
+ if (HasFatalFailure()) {
+ return;
+ }
+
+ mListener->SetEventMask(ProEvent_Mask(ACQUIRED) |
+ ProEvent_Mask(STOLEN) |
+ ProEvent_Mask(RELEASED) |
+ ProEvent_Mask(FRAME_RECEIVED));
+ mListener->SetDropFrames(true);
+
+ int streamId = -1;
+ sp<CpuConsumer> consumer;
+ EXPECT_OK(mCamera->createStreamCpu(/*width*/320, /*height*/240,
+ TEST_FORMAT_DEPTH, TEST_CPU_HEAP_COUNT, &consumer, &streamId));
+ EXPECT_NE(-1, streamId);
+
+ EXPECT_OK(mCamera->exclusiveTryLock());
+ EXPECT_EQ(OK, mListener->WaitForEvent());
+ EXPECT_EQ(ACQUIRED, mListener->ReadEvent());
+ /* iterate in a loop submitting requests every frame.
+ * what kind of requests doesnt really matter, just whatever.
+ */
+
+ // it would probably be better to use CameraMetadata from camera service.
+ camera_metadata_t *request = NULL;
+ EXPECT_OK(mCamera->createDefaultRequest(CAMERA2_TEMPLATE_PREVIEW,
+ /*out*/&request));
+ EXPECT_NE((void*)NULL, request);
+
+ /*FIXME: dont need this later, at which point the above should become an
+ ASSERT_NE*/
+ if(request == NULL) request = allocate_camera_metadata(10, 100);
+
+ // set the output streams to just this stream ID
+
+ uint8_t allStreams[] = { streamId };
+ camera_metadata_entry_t entry;
+ uint32_t tag = static_cast<uint32_t>(ANDROID_REQUEST_OUTPUT_STREAMS);
+ int find = find_camera_metadata_entry(request, tag, &entry);
+ if (find == -ENOENT) {
+ if (add_camera_metadata_entry(request, tag, &allStreams,
+ /*data_count*/1) != OK) {
+ camera_metadata_t *tmp = allocate_camera_metadata(1000, 10000);
+ ASSERT_OK(append_camera_metadata(tmp, request));
+ free_camera_metadata(request);
+ request = tmp;
+
+ ASSERT_OK(add_camera_metadata_entry(request, tag, &allStreams,
+ /*data_count*/1));
+ }
+ } else {
+ ASSERT_OK(update_camera_metadata_entry(request, entry.index,
+ &allStreams, /*data_count*/1, &entry));
+ }
+
+ EXPECT_OK(mCamera->submitRequest(request, /*streaming*/true));
+
+ // Consume a couple of frames
+ for (int i = 0; i < TEST_CPU_FRAME_COUNT; ++i) {
+ EXPECT_EQ(OK, mListener->WaitForEvent());
+ EXPECT_EQ(FRAME_RECEIVED, mListener->ReadEvent());
+ }
+
+ // Done: clean up
+ free_camera_metadata(request);
+ EXPECT_OK(mCamera->deleteStream(streamId));
+ EXPECT_OK(mCamera->exclusiveUnlock());
+}
+
+TEST_F(ProCameraTest, CpuConsumerDual) {
+ if (HasFatalFailure()) {
+ return;
+ }
+
+ mListener->SetEventMask(ProEvent_Mask(FRAME_RECEIVED));
+ mListener->SetDropFrames(true);
+
+ int streamId = -1;
+ sp<CpuConsumer> consumer;
+ EXPECT_OK(mCamera->createStreamCpu(/*width*/1280, /*height*/960,
+ TEST_FORMAT_MAIN, TEST_CPU_HEAP_COUNT, &consumer, &streamId));
+ EXPECT_NE(-1, streamId);
+
+ int depthStreamId = -1;
+ EXPECT_OK(mCamera->createStreamCpu(/*width*/320, /*height*/240,
+ TEST_FORMAT_DEPTH, TEST_CPU_HEAP_COUNT, &consumer, &depthStreamId));
+ EXPECT_NE(-1, depthStreamId);
+
+ EXPECT_OK(mCamera->exclusiveTryLock());
+ /*
+ */
+ /* iterate in a loop submitting requests every frame.
+ * what kind of requests doesnt really matter, just whatever.
+ */
+
+ // it would probably be better to use CameraMetadata from camera service.
+ camera_metadata_t *request = NULL;
+ EXPECT_OK(mCamera->createDefaultRequest(CAMERA2_TEMPLATE_PREVIEW,
+ /*out*/&request));
+ EXPECT_NE((void*)NULL, request);
+
+ if(request == NULL) request = allocate_camera_metadata(10, 100);
+
+ // set the output streams to just this stream ID
+
+ // wow what a verbose API.
+ uint8_t allStreams[] = { streamId, depthStreamId };
+ size_t streamCount = 2;
+ camera_metadata_entry_t entry;
+ uint32_t tag = static_cast<uint32_t>(ANDROID_REQUEST_OUTPUT_STREAMS);
+ int find = find_camera_metadata_entry(request, tag, &entry);
+ if (find == -ENOENT) {
+ if (add_camera_metadata_entry(request, tag, &allStreams,
+ /*data_count*/streamCount) != OK) {
+ camera_metadata_t *tmp = allocate_camera_metadata(1000, 10000);
+ ASSERT_OK(append_camera_metadata(tmp, request));
+ free_camera_metadata(request);
+ request = tmp;
+
+ ASSERT_OK(add_camera_metadata_entry(request, tag, &allStreams,
+ /*data_count*/streamCount));
+ }
+ } else {
+ ASSERT_OK(update_camera_metadata_entry(request, entry.index,
+ &allStreams, /*data_count*/streamCount, &entry));
+ }
+
+ EXPECT_OK(mCamera->submitRequest(request, /*streaming*/true));
+
+ // Consume a couple of frames
+ for (int i = 0; i < TEST_CPU_FRAME_COUNT; ++i) {
+ // stream id 1
+ EXPECT_EQ(OK, mListener->WaitForEvent());
+ EXPECT_EQ(FRAME_RECEIVED, mListener->ReadEvent());
+
+ // stream id 2
+ EXPECT_EQ(OK, mListener->WaitForEvent());
+ EXPECT_EQ(FRAME_RECEIVED, mListener->ReadEvent());
+
+ //TODO: events should be a struct with some data like the stream id
+ }
+
+ // Done: clean up
+ free_camera_metadata(request);
+ EXPECT_OK(mCamera->deleteStream(streamId));
+ EXPECT_OK(mCamera->exclusiveUnlock());
+}
+
+TEST_F(ProCameraTest, ResultReceiver) {
+ if (HasFatalFailure()) {
+ return;
+ }
+
+ mListener->SetEventMask(ProEvent_Mask(RESULT_RECEIVED));
+ mListener->SetDropFrames(true);
+ //FIXME: if this is run right after the previous test we get FRAME_RECEIVED
+ // need to filter out events at read time
+
+ int streamId = -1;
+ sp<CpuConsumer> consumer;
+ EXPECT_OK(mCamera->createStreamCpu(/*width*/1280, /*height*/960,
+ TEST_FORMAT_MAIN, TEST_CPU_HEAP_COUNT, &consumer, &streamId));
+ EXPECT_NE(-1, streamId);
+
+ EXPECT_OK(mCamera->exclusiveTryLock());
+ /*
+ */
+ /* iterate in a loop submitting requests every frame.
+ * what kind of requests doesnt really matter, just whatever.
+ */
+
+ camera_metadata_t *request = NULL;
+ EXPECT_OK(mCamera->createDefaultRequest(CAMERA2_TEMPLATE_PREVIEW,
+ /*out*/&request));
+ EXPECT_NE((void*)NULL, request);
+
+ /*FIXME*/
+ if(request == NULL) request = allocate_camera_metadata(10, 100);
+
+ // set the output streams to just this stream ID
+
+ uint8_t allStreams[] = { streamId };
+ size_t streamCount = 1;
+ camera_metadata_entry_t entry;
+ uint32_t tag = static_cast<uint32_t>(ANDROID_REQUEST_OUTPUT_STREAMS);
+ int find = find_camera_metadata_entry(request, tag, &entry);
+ if (find == -ENOENT) {
+ if (add_camera_metadata_entry(request, tag, &allStreams,
+ /*data_count*/streamCount) != OK) {
+ camera_metadata_t *tmp = allocate_camera_metadata(1000, 10000);
+ ASSERT_OK(append_camera_metadata(tmp, request));
+ free_camera_metadata(request);
+ request = tmp;
+
+ ASSERT_OK(add_camera_metadata_entry(request, tag, &allStreams,
+ /*data_count*/streamCount));
+ }
+ } else {
+ ASSERT_OK(update_camera_metadata_entry(request, entry.index,
+ &allStreams, /*data_count*/streamCount, &entry));
+ }
+
+ EXPECT_OK(mCamera->submitRequest(request, /*streaming*/true));
+
+ // Consume a couple of results
+ for (int i = 0; i < TEST_CPU_FRAME_COUNT; ++i) {
+ EXPECT_EQ(OK, mListener->WaitForEvent());
+ EXPECT_EQ(RESULT_RECEIVED, mListener->ReadEvent());
+ }
+
+ // Done: clean up
+ free_camera_metadata(request);
+ EXPECT_OK(mCamera->deleteStream(streamId));
+ EXPECT_OK(mCamera->exclusiveUnlock());
+}
+
+// FIXME: This is racy and sometimes fails on waitForFrameMetadata
+TEST_F(ProCameraTest, DISABLED_WaitForResult) {
+ if (HasFatalFailure()) {
+ return;
+ }
+
+ mListener->SetDropFrames(true);
+
+ int streamId = -1;
+ sp<CpuConsumer> consumer;
+ EXPECT_OK(mCamera->createStreamCpu(/*width*/1280, /*height*/960,
+ TEST_FORMAT_MAIN, TEST_CPU_HEAP_COUNT, &consumer, &streamId));
+ EXPECT_NE(-1, streamId);
+
+ EXPECT_OK(mCamera->exclusiveTryLock());
+
+ uint8_t streams[] = { streamId };
+ ASSERT_NO_FATAL_FAILURE(createSubmitRequestForStreams(streams, /*count*/1));
+
+ // Consume a couple of results
+ for (int i = 0; i < TEST_CPU_FRAME_COUNT; ++i) {
+ EXPECT_OK(mCamera->waitForFrameMetadata());
+ CameraMetadata meta = mCamera->consumeFrameMetadata();
+ EXPECT_FALSE(meta.isEmpty());
+ }
+
+ // Done: clean up
+ EXPECT_OK(mCamera->deleteStream(streamId));
+ EXPECT_OK(mCamera->exclusiveUnlock());
+}
+
+TEST_F(ProCameraTest, WaitForSingleStreamBuffer) {
+ if (HasFatalFailure()) {
+ return;
+ }
+
+ int streamId = -1;
+ sp<CpuConsumer> consumer;
+ EXPECT_OK(mCamera->createStreamCpu(/*width*/1280, /*height*/960,
+ TEST_FORMAT_MAIN, TEST_CPU_HEAP_COUNT, &consumer, &streamId));
+ EXPECT_NE(-1, streamId);
+
+ EXPECT_OK(mCamera->exclusiveTryLock());
+
+ uint8_t streams[] = { streamId };
+ ASSERT_NO_FATAL_FAILURE(createSubmitRequestForStreams(streams, /*count*/1,
+ /*requests*/TEST_CPU_FRAME_COUNT));
+
+ // Consume a couple of results
+ for (int i = 0; i < TEST_CPU_FRAME_COUNT; ++i) {
+ EXPECT_EQ(1, mCamera->waitForFrameBuffer(streamId));
+
+ CpuConsumer::LockedBuffer buf;
+ EXPECT_OK(consumer->lockNextBuffer(&buf));
+
+ dout << "Buffer synchronously received on streamId = " << streamId <<
+ ", dataPtr = " << (void*)buf.data <<
+ ", timestamp = " << buf.timestamp << std::endl;
+
+ EXPECT_OK(consumer->unlockBuffer(buf));
+ }
+
+ // Done: clean up
+ EXPECT_OK(mCamera->deleteStream(streamId));
+ EXPECT_OK(mCamera->exclusiveUnlock());
+}
+
+// FIXME: This is racy and sometimes fails on waitForFrameMetadata
+TEST_F(ProCameraTest, DISABLED_WaitForDualStreamBuffer) {
+ if (HasFatalFailure()) {
+ return;
+ }
+
+ const int REQUEST_COUNT = TEST_CPU_FRAME_COUNT * 10;
+
+ // 15 fps
+ int streamId = -1;
+ sp<CpuConsumer> consumer;
+ EXPECT_OK(mCamera->createStreamCpu(/*width*/1280, /*height*/960,
+ TEST_FORMAT_MAIN, TEST_CPU_HEAP_COUNT, &consumer, &streamId));
+ EXPECT_NE(-1, streamId);
+
+ // 30 fps
+ int depthStreamId = -1;
+ sp<CpuConsumer> depthConsumer;
+ EXPECT_OK(mCamera->createStreamCpu(/*width*/320, /*height*/240,
+ TEST_FORMAT_DEPTH, TEST_CPU_HEAP_COUNT, &depthConsumer, &depthStreamId));
+ EXPECT_NE(-1, depthStreamId);
+
+ EXPECT_OK(mCamera->exclusiveTryLock());
+
+ uint8_t streams[] = { streamId, depthStreamId };
+ ASSERT_NO_FATAL_FAILURE(createSubmitRequestForStreams(streams, /*count*/2,
+ /*requests*/REQUEST_COUNT));
+
+ int depthFrames = 0;
+ int greyFrames = 0;
+
+ // Consume two frames simultaneously. Unsynchronized by timestamps.
+ for (int i = 0; i < REQUEST_COUNT; ++i) {
+
+ // Exhaust event queue so it doesn't keep growing
+ while (mListener->ReadEvent() != UNKNOWN);
+
+ // Get the metadata
+ EXPECT_OK(mCamera->waitForFrameMetadata());
+ CameraMetadata meta = mCamera->consumeFrameMetadata();
+ EXPECT_FALSE(meta.isEmpty());
+
+ // Get the buffers
+
+ EXPECT_EQ(1, mCamera->waitForFrameBuffer(depthStreamId));
+
+ /**
+ * Guaranteed to be able to consume the depth frame,
+ * since we waited on it.
+ */
+ CpuConsumer::LockedBuffer depthBuffer;
+ EXPECT_OK(depthConsumer->lockNextBuffer(&depthBuffer));
+
+ dout << "Depth Buffer synchronously received on streamId = " <<
+ streamId <<
+ ", dataPtr = " << (void*)depthBuffer.data <<
+ ", timestamp = " << depthBuffer.timestamp << std::endl;
+
+ EXPECT_OK(depthConsumer->unlockBuffer(depthBuffer));
+
+ depthFrames++;
+
+
+ /** Consume Greyscale frames if there are any.
+ * There may not be since it runs at half FPS */
+ CpuConsumer::LockedBuffer greyBuffer;
+ while (consumer->lockNextBuffer(&greyBuffer) == OK) {
+
+ dout << "GRAY Buffer synchronously received on streamId = " <<
+ streamId <<
+ ", dataPtr = " << (void*)greyBuffer.data <<
+ ", timestamp = " << greyBuffer.timestamp << std::endl;
+
+ EXPECT_OK(consumer->unlockBuffer(greyBuffer));
+
+ greyFrames++;
+ }
+ }
+
+ dout << "Done, summary: depth frames " << std::dec << depthFrames
+ << ", grey frames " << std::dec << greyFrames << std::endl;
+
+ // Done: clean up
+ EXPECT_OK(mCamera->deleteStream(streamId));
+ EXPECT_OK(mCamera->exclusiveUnlock());
+}
+
+TEST_F(ProCameraTest, WaitForSingleStreamBufferAndDropFramesSync) {
+ if (HasFatalFailure()) {
+ return;
+ }
+
+ const int NUM_REQUESTS = 20 * TEST_CPU_FRAME_COUNT;
+
+ int streamId = -1;
+ sp<CpuConsumer> consumer;
+ EXPECT_OK(mCamera->createStreamCpu(/*width*/1280, /*height*/960,
+ TEST_FORMAT_MAIN, TEST_CPU_HEAP_COUNT,
+ /*synchronousMode*/true, &consumer, &streamId));
+ EXPECT_NE(-1, streamId);
+
+ EXPECT_OK(mCamera->exclusiveTryLock());
+
+ uint8_t streams[] = { streamId };
+ ASSERT_NO_FATAL_FAILURE(createSubmitRequestForStreams(streams, /*count*/1,
+ /*requests*/NUM_REQUESTS));
+
+ // Consume a couple of results
+ for (int i = 0; i < NUM_REQUESTS; ++i) {
+ int numFrames;
+ EXPECT_TRUE((numFrames = mCamera->waitForFrameBuffer(streamId)) > 0);
+
+ // Drop all but the newest framebuffer
+ EXPECT_EQ(numFrames-1, mCamera->dropFrameBuffer(streamId, numFrames-1));
+
+ dout << "Dropped " << (numFrames - 1) << " frames" << std::endl;
+
+ // Skip the counter ahead, don't try to consume these frames again
+ i += numFrames-1;
+
+ // "Consume" the buffer
+ CpuConsumer::LockedBuffer buf;
+ EXPECT_OK(consumer->lockNextBuffer(&buf));
+
+ dout << "Buffer synchronously received on streamId = " << streamId <<
+ ", dataPtr = " << (void*)buf.data <<
+ ", timestamp = " << buf.timestamp << std::endl;
+
+ // Process at 10fps, stream is at 15fps.
+ // This means we will definitely fill up the buffer queue with
+ // extra buffers and need to drop them.
+ usleep(TEST_FRAME_PROCESSING_DELAY_US);
+
+ EXPECT_OK(consumer->unlockBuffer(buf));
+ }
+
+ // Done: clean up
+ EXPECT_OK(mCamera->deleteStream(streamId));
+ EXPECT_OK(mCamera->exclusiveUnlock());
+}
+
+TEST_F(ProCameraTest, WaitForSingleStreamBufferAndDropFramesAsync) {
+ if (HasFatalFailure()) {
+ return;
+ }
+
+ const int NUM_REQUESTS = 20 * TEST_CPU_FRAME_COUNT;
+ const int CONSECUTIVE_FAILS_ASSUME_TIME_OUT = 5;
+
+ int streamId = -1;
+ sp<CpuConsumer> consumer;
+ EXPECT_OK(mCamera->createStreamCpu(/*width*/1280, /*height*/960,
+ TEST_FORMAT_MAIN, TEST_CPU_HEAP_COUNT,
+ /*synchronousMode*/false, &consumer, &streamId));
+ EXPECT_NE(-1, streamId);
+
+ EXPECT_OK(mCamera->exclusiveTryLock());
+
+ uint8_t streams[] = { streamId };
+ ASSERT_NO_FATAL_FAILURE(createSubmitRequestForStreams(streams, /*count*/1,
+ /*requests*/NUM_REQUESTS));
+
+ uint64_t lastFrameNumber = 0;
+ int numFrames;
+
+ // Consume a couple of results
+ int i;
+ for (i = 0; i < NUM_REQUESTS && lastFrameNumber < NUM_REQUESTS; ++i) {
+ EXPECT_LT(0, (numFrames = mCamera->waitForFrameBuffer(streamId)));
+
+ dout << "Dropped " << (numFrames - 1) << " frames" << std::endl;
+
+ // Skip the counter ahead, don't try to consume these frames again
+ i += numFrames-1;
+
+ // "Consume" the buffer
+ CpuConsumer::LockedBuffer buf;
+
+ EXPECT_EQ(OK, consumer->lockNextBuffer(&buf));
+
+ lastFrameNumber = buf.frameNumber;
+
+ dout << "Buffer asynchronously received on streamId = " << streamId <<
+ ", dataPtr = " << (void*)buf.data <<
+ ", timestamp = " << buf.timestamp <<
+ ", framenumber = " << buf.frameNumber << std::endl;
+
+ // Process at 10fps, stream is at 15fps.
+ // This means we will definitely fill up the buffer queue with
+ // extra buffers and need to drop them.
+ usleep(TEST_FRAME_PROCESSING_DELAY_US);
+
+ EXPECT_OK(consumer->unlockBuffer(buf));
+ }
+
+ dout << "Done after " << i << " iterations " << std::endl;
+
+ // Done: clean up
+ EXPECT_OK(mCamera->deleteStream(streamId));
+ EXPECT_OK(mCamera->exclusiveUnlock());
+}
+
+
+
+//TODO: refactor into separate file
+TEST_F(ProCameraTest, ServiceListenersSubscribe) {
+
+ ASSERT_EQ(4u, sizeof(ServiceListener::Status));
+
+ sp<ServiceListener> listener = new ServiceListener();
+
+ EXPECT_EQ(BAD_VALUE, ProCamera::removeServiceListener(listener));
+ EXPECT_OK(ProCamera::addServiceListener(listener));
+
+ EXPECT_EQ(ALREADY_EXISTS, ProCamera::addServiceListener(listener));
+ EXPECT_OK(ProCamera::removeServiceListener(listener));
+
+ EXPECT_EQ(BAD_VALUE, ProCamera::removeServiceListener(listener));
+}
+
+//TODO: refactor into separate file
+TEST_F(ProCameraTest, ServiceListenersFunctional) {
+
+ sp<ServiceListener> listener = new ServiceListener();
+
+ EXPECT_OK(ProCamera::addServiceListener(listener));
+
+ sp<Camera> cam = Camera::connect(CAMERA_ID,
+ /*clientPackageName*/String16(),
+ -1);
+ EXPECT_NE((void*)NULL, cam.get());
+
+ ServiceListener::Status stat = ServiceListener::STATUS_UNKNOWN;
+ EXPECT_OK(listener->waitForStatusChange(/*out*/stat));
+
+ EXPECT_EQ(ServiceListener::STATUS_NOT_AVAILABLE, stat);
+
+ if (cam.get()) {
+ cam->disconnect();
+ }
+
+ EXPECT_OK(listener->waitForStatusChange(/*out*/stat));
+ EXPECT_EQ(ServiceListener::STATUS_PRESENT, stat);
+
+ EXPECT_OK(ProCamera::removeServiceListener(listener));
+}
+
+
+
+}
+}
+}
+}
+
diff --git a/camera/tests/main.cpp b/camera/tests/main.cpp
new file mode 100644
index 0000000..8c8c515
--- /dev/null
+++ b/camera/tests/main.cpp
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2013 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.
+ */
+
+#include <gtest/gtest.h>
+
+
+int main(int argc, char **argv) {
+
+ ::testing::InitGoogleTest(&argc, argv);
+
+ int ret = RUN_ALL_TESTS();
+
+ return ret;
+}