From 6df26071a5b581a90a6719a6dc03c212073dca71 Mon Sep 17 00:00:00 2001 From: Jayant Chowdhary Date: Tue, 6 Nov 2018 23:55:12 -0800 Subject: [PATCH] Create a vendor available version of libcamera2ndk. Bug: 110364143 Test: mm -j64 Test: AImageReaderVendorTest Change-Id: Ic9343cc7fb10eb374197945b72be59e7ce8282fe Signed-off-by: Jayant Chowdhary --- camera/ndk/Android.bp | 78 + camera/ndk/NdkCameraDevice.cpp | 22 +- camera/ndk/NdkCameraManager.cpp | 7 +- camera/ndk/NdkCaptureRequest.cpp | 2 +- camera/ndk/impl/ACameraCaptureSession.cpp | 20 +- camera/ndk/impl/ACameraCaptureSession.h | 20 +- camera/ndk/impl/ACameraDevice.cpp | 6 +- camera/ndk/impl/ACameraDevice.h | 6 +- camera/ndk/impl/ACameraManager.cpp | 4 +- camera/ndk/impl/ACameraManager.h | 6 +- camera/ndk/impl/ACameraMetadata.h | 6 + camera/ndk/impl/ACaptureRequest.h | 8 +- .../include/camera/NdkCameraCaptureSession.h | 4 +- camera/ndk/include/camera/NdkCameraDevice.h | 10 +- .../ndk/include/camera/NdkCameraWindowType.h | 53 + camera/ndk/include/camera/NdkCaptureRequest.h | 4 +- .../impl/ACameraCaptureSessionVendor.h | 45 + camera/ndk/ndk_vendor/impl/ACameraDevice.cpp | 1594 +++++++++++++++++ camera/ndk/ndk_vendor/impl/ACameraDevice.h | 356 ++++ camera/ndk/ndk_vendor/impl/ACameraManager.cpp | 458 +++++ camera/ndk/ndk_vendor/impl/ACameraManager.h | 209 +++ .../ndk_vendor/impl/ACaptureRequestVendor.h | 36 + camera/ndk/ndk_vendor/impl/utils.cpp | 197 ++ camera/ndk/ndk_vendor/impl/utils.h | 174 ++ .../tests/AImageReaderVendorTest.cpp | 525 ++++++ 25 files changed, 3805 insertions(+), 45 deletions(-) create mode 100644 camera/ndk/include/camera/NdkCameraWindowType.h create mode 100644 camera/ndk/ndk_vendor/impl/ACameraCaptureSessionVendor.h create mode 100644 camera/ndk/ndk_vendor/impl/ACameraDevice.cpp create mode 100644 camera/ndk/ndk_vendor/impl/ACameraDevice.h create mode 100644 camera/ndk/ndk_vendor/impl/ACameraManager.cpp create mode 100644 camera/ndk/ndk_vendor/impl/ACameraManager.h create mode 100644 camera/ndk/ndk_vendor/impl/ACaptureRequestVendor.h create mode 100644 camera/ndk/ndk_vendor/impl/utils.cpp create mode 100644 camera/ndk/ndk_vendor/impl/utils.h create mode 100644 camera/ndk/ndk_vendor/tests/AImageReaderVendorTest.cpp diff --git a/camera/ndk/Android.bp b/camera/ndk/Android.bp index e84eeef90e..60c9f851b8 100644 --- a/camera/ndk/Android.bp +++ b/camera/ndk/Android.bp @@ -70,3 +70,81 @@ cc_library_shared { ], version_script: "libcamera2ndk.map.txt", } + +cc_library_shared { + name: "libcamera2ndk_vendor", + vendor_available: true, + srcs: [ + "ndk_vendor/impl/ACameraDevice.cpp", + "ndk_vendor/impl/ACameraManager.cpp", + "ndk_vendor/impl/utils.cpp", + "impl/ACameraMetadata.cpp", + "impl/ACameraCaptureSession.cpp", + "NdkCameraMetadata.cpp", + "NdkCameraCaptureSession.cpp", + "NdkCameraManager.cpp", + "NdkCameraDevice.cpp", + "NdkCaptureRequest.cpp", + ], + + export_include_dirs: ["include"], + export_shared_lib_headers: [ + "libcutils", + ], + local_include_dirs: [ + ".", + "include", + "impl", + ], + cflags: [ + "-fvisibility=hidden", + "-DEXPORT=__attribute__((visibility(\"default\")))", + "-D__ANDROID_VNDK__", + ], + + shared_libs: [ + "libhwbinder", + "libfmq", + "libhidlbase", + "libhardware", + "libnativewindow", + "liblog", + "libutils", + "libstagefright_foundation", + "libcutils", + "libcamera_metadata", + "libmediandk", + "android.frameworks.cameraservice.device@2.0", + "android.frameworks.cameraservice.common@2.0", + "android.frameworks.cameraservice.service@2.0", + ], + + static_libs: [ + "android.hardware.camera.common@1.0-helper", + "libarect", + ], + product_variables: { + pdk: { + enabled: false, + }, + }, +} + +cc_test { + name: "AImageReaderVendorTest", + vendor: true, + srcs: ["ndk_vendor/tests/AImageReaderVendorTest.cpp"], + shared_libs: [ + "libhwbinder", + "libcamera2ndk_vendor", + "libmediandk", + "libnativewindow", + "libutils", + "libui", + "libcutils", + "liblog", + ], + cflags: [ + "-D__ANDROID_VNDK__", + ], +} diff --git a/camera/ndk/NdkCameraDevice.cpp b/camera/ndk/NdkCameraDevice.cpp index 812a312c54..ef05e0b80f 100644 --- a/camera/ndk/NdkCameraDevice.cpp +++ b/camera/ndk/NdkCameraDevice.cpp @@ -24,7 +24,15 @@ #include #include "impl/ACameraCaptureSession.h" -using namespace android; +using namespace android::acam; + +bool areWindowTypesEqual(ACameraWindowType *a, ACameraWindowType *b) { +#ifdef __ANDROID_VNDK__ + return utils::isWindowNativeHandleEqual(a, b); +#else + return a == b; +#endif +} EXPORT camera_status_t ACameraDevice_close(ACameraDevice* device) { @@ -96,7 +104,7 @@ void ACaptureSessionOutputContainer_free(ACaptureSessionOutputContainer* contain EXPORT camera_status_t ACaptureSessionOutput_create( - ANativeWindow* window, /*out*/ACaptureSessionOutput** out) { + ACameraWindowType* window, /*out*/ACaptureSessionOutput** out) { ATRACE_CALL(); if (window == nullptr || out == nullptr) { ALOGE("%s: Error: bad argument. window %p, out %p", @@ -109,7 +117,7 @@ camera_status_t ACaptureSessionOutput_create( EXPORT camera_status_t ACaptureSessionSharedOutput_create( - ANativeWindow* window, /*out*/ACaptureSessionOutput** out) { + ACameraWindowType* window, /*out*/ACaptureSessionOutput** out) { ATRACE_CALL(); if (window == nullptr || out == nullptr) { ALOGE("%s: Error: bad argument. window %p, out %p", @@ -122,7 +130,7 @@ camera_status_t ACaptureSessionSharedOutput_create( EXPORT camera_status_t ACaptureSessionSharedOutput_add(ACaptureSessionOutput *out, - ANativeWindow* window) { + ACameraWindowType* window) { ATRACE_CALL(); if ((window == nullptr) || (out == nullptr)) { ALOGE("%s: Error: bad argument. window %p, out %p", @@ -134,7 +142,7 @@ camera_status_t ACaptureSessionSharedOutput_add(ACaptureSessionOutput *out, __FUNCTION__); return ACAMERA_ERROR_INVALID_OPERATION; } - if (out->mWindow == window) { + if (areWindowTypesEqual(out->mWindow, window)) { ALOGE("%s: Error trying to add the same window associated with the output configuration", __FUNCTION__); return ACAMERA_ERROR_INVALID_PARAMETER; @@ -147,7 +155,7 @@ camera_status_t ACaptureSessionSharedOutput_add(ACaptureSessionOutput *out, EXPORT camera_status_t ACaptureSessionSharedOutput_remove(ACaptureSessionOutput *out, - ANativeWindow* window) { + ACameraWindowType* window) { ATRACE_CALL(); if ((window == nullptr) || (out == nullptr)) { ALOGE("%s: Error: bad argument. window %p, out %p", @@ -159,7 +167,7 @@ camera_status_t ACaptureSessionSharedOutput_remove(ACaptureSessionOutput *out, __FUNCTION__); return ACAMERA_ERROR_INVALID_OPERATION; } - if (out->mWindow == window) { + if (areWindowTypesEqual(out->mWindow, window)) { ALOGE("%s: Error trying to remove the same window associated with the output configuration", __FUNCTION__); return ACAMERA_ERROR_INVALID_PARAMETER; diff --git a/camera/ndk/NdkCameraManager.cpp b/camera/ndk/NdkCameraManager.cpp index f14a4c9757..8742d9c97e 100644 --- a/camera/ndk/NdkCameraManager.cpp +++ b/camera/ndk/NdkCameraManager.cpp @@ -22,10 +22,15 @@ #include #include + +#ifdef __ANDROID_VNDK__ +#include "ndk_vendor/impl/ACameraManager.h" +#else #include "impl/ACameraManager.h" +#endif #include "impl/ACameraMetadata.h" -using namespace android; +using namespace android::acam; EXPORT ACameraManager* ACameraManager_create() { diff --git a/camera/ndk/NdkCaptureRequest.cpp b/camera/ndk/NdkCaptureRequest.cpp index ddb69d77ac..c64de3ee4d 100644 --- a/camera/ndk/NdkCaptureRequest.cpp +++ b/camera/ndk/NdkCaptureRequest.cpp @@ -27,7 +27,7 @@ EXPORT camera_status_t ACameraOutputTarget_create( - ANativeWindow* window, ACameraOutputTarget** out) { + ACameraWindowType* window, ACameraOutputTarget** out) { ATRACE_CALL(); if (window == nullptr) { ALOGE("%s: Error: input window is null", __FUNCTION__); diff --git a/camera/ndk/impl/ACameraCaptureSession.cpp b/camera/ndk/impl/ACameraCaptureSession.cpp index f60e5fd9cd..fb72bdbb3c 100644 --- a/camera/ndk/impl/ACameraCaptureSession.cpp +++ b/camera/ndk/impl/ACameraCaptureSession.cpp @@ -23,7 +23,7 @@ using namespace android; ACameraCaptureSession::~ACameraCaptureSession() { ALOGV("~ACameraCaptureSession: %p notify device end of life", this); - sp dev = getDeviceSp(); + sp dev = getDeviceSp(); if (dev != nullptr && !dev->isClosed()) { dev->lockDeviceForSessionOps(); { @@ -48,7 +48,7 @@ ACameraCaptureSession::closeByApp() { mClosedByApp = true; } - sp dev = getDeviceSp(); + sp dev = getDeviceSp(); if (dev != nullptr) { dev->lockDeviceForSessionOps(); } @@ -73,7 +73,7 @@ ACameraCaptureSession::closeByApp() { camera_status_t ACameraCaptureSession::stopRepeating() { - sp dev = getDeviceSp(); + sp dev = getDeviceSp(); if (dev == nullptr) { ALOGE("Error: Device associated with session %p has been closed!", this); return ACAMERA_ERROR_SESSION_CLOSED; @@ -91,7 +91,7 @@ ACameraCaptureSession::stopRepeating() { camera_status_t ACameraCaptureSession::abortCaptures() { - sp dev = getDeviceSp(); + sp dev = getDeviceSp(); if (dev == nullptr) { ALOGE("Error: Device associated with session %p has been closed!", this); return ACAMERA_ERROR_SESSION_CLOSED; @@ -112,7 +112,7 @@ ACameraCaptureSession::setRepeatingRequest( /*optional*/ACameraCaptureSession_captureCallbacks* cbs, int numRequests, ACaptureRequest** requests, /*optional*/int* captureSequenceId) { - sp dev = getDeviceSp(); + sp dev = getDeviceSp(); if (dev == nullptr) { ALOGE("Error: Device associated with session %p has been closed!", this); return ACAMERA_ERROR_SESSION_CLOSED; @@ -133,7 +133,7 @@ camera_status_t ACameraCaptureSession::capture( /*optional*/ACameraCaptureSession_captureCallbacks* cbs, int numRequests, ACaptureRequest** requests, /*optional*/int* captureSequenceId) { - sp dev = getDeviceSp(); + sp dev = getDeviceSp(); if (dev == nullptr) { ALOGE("Error: Device associated with session %p has been closed!", this); return ACAMERA_ERROR_SESSION_CLOSED; @@ -149,7 +149,7 @@ camera_status_t ACameraCaptureSession::capture( } camera_status_t ACameraCaptureSession::updateOutputConfiguration(ACaptureSessionOutput *output) { - sp dev = getDeviceSp(); + sp dev = getDeviceSp(); if (dev == nullptr) { ALOGE("Error: Device associated with session %p has been closed!", this); return ACAMERA_ERROR_SESSION_CLOSED; @@ -168,7 +168,7 @@ camera_status_t ACameraCaptureSession::updateOutputConfiguration(ACaptureSession ACameraDevice* ACameraCaptureSession::getDevice() { Mutex::Autolock _l(mSessionLock); - sp dev = getDeviceSp(); + sp dev = getDeviceSp(); if (dev == nullptr) { ALOGE("Error: Device associated with session %p has been closed!", this); return nullptr; @@ -182,9 +182,9 @@ ACameraCaptureSession::closeByDevice() { mIsClosed = true; } -sp +sp ACameraCaptureSession::getDeviceSp() { - sp device = mDevice.promote(); + sp device = mDevice.promote(); if (device == nullptr || device->isClosed()) { ALOGW("Device is closed but session %d is not notified", mId); return nullptr; diff --git a/camera/ndk/impl/ACameraCaptureSession.h b/camera/ndk/impl/ACameraCaptureSession.h index a2068e71b1..133c2c8325 100644 --- a/camera/ndk/impl/ACameraCaptureSession.h +++ b/camera/ndk/impl/ACameraCaptureSession.h @@ -19,12 +19,17 @@ #include #include #include + +#ifdef __ANDROID_VNDK__ +#include "ndk_vendor/impl/ACameraDevice.h" +#include "ndk_vendor/impl/ACameraCaptureSessionVendor.h" +#else #include "ACameraDevice.h" using namespace android; struct ACaptureSessionOutput { - explicit ACaptureSessionOutput(ANativeWindow* window, bool isShared = false) : + explicit ACaptureSessionOutput(ACameraWindowType* window, bool isShared = false) : mWindow(window), mIsShared(isShared) {}; bool operator == (const ACaptureSessionOutput& other) const { @@ -40,11 +45,12 @@ struct ACaptureSessionOutput { return mWindow > other.mWindow; } - ANativeWindow* mWindow; - std::set mSharedWindows; + ACameraWindowType* mWindow; + std::set mSharedWindows; bool mIsShared; int mRotation = CAMERA3_STREAM_ROTATION_0; }; +#endif struct ACaptureSessionOutputContainer { std::set mOutputs; @@ -60,7 +66,7 @@ struct ACameraCaptureSession : public RefBase { int id, const ACaptureSessionOutputContainer* outputs, const ACameraCaptureSession_stateCallbacks* cb, - CameraDevice* device) : + android::acam::CameraDevice* device) : mId(id), mOutput(*outputs), mUserSessionCallback(*cb), mDevice(device) {} @@ -97,18 +103,18 @@ struct ACameraCaptureSession : public RefBase { ACameraDevice* getDevice(); private: - friend class CameraDevice; + friend class android::acam::CameraDevice; // Close session because app close camera device, camera device got ERROR_DISCONNECTED, // or a new session is replacing this session. void closeByDevice(); - sp getDeviceSp(); + sp getDeviceSp(); const int mId; const ACaptureSessionOutputContainer mOutput; const ACameraCaptureSession_stateCallbacks mUserSessionCallback; - const wp mDevice; + const wp mDevice; bool mIsClosed = false; bool mClosedByApp = false; Mutex mSessionLock; diff --git a/camera/ndk/impl/ACameraDevice.cpp b/camera/ndk/impl/ACameraDevice.cpp index ac3be25856..00da54e6dd 100644 --- a/camera/ndk/impl/ACameraDevice.cpp +++ b/camera/ndk/impl/ACameraDevice.cpp @@ -27,9 +27,9 @@ #include "ACaptureRequest.h" #include "ACameraCaptureSession.h" -using namespace android; - namespace android { +namespace acam { + // Static member definitions const char* CameraDevice::kContextKey = "Context"; const char* CameraDevice::kDeviceKey = "Device"; @@ -1513,5 +1513,5 @@ CameraDevice::ServiceCallback::onRepeatingRequestError( return ret; } - +} // namespace acam } // namespace android diff --git a/camera/ndk/impl/ACameraDevice.h b/camera/ndk/impl/ACameraDevice.h index 7d64081ae1..8f56d3f2e2 100644 --- a/camera/ndk/impl/ACameraDevice.h +++ b/camera/ndk/impl/ACameraDevice.h @@ -41,6 +41,7 @@ #include "ACameraMetadata.h" namespace android { +namespace acam { // Wrap ACameraCaptureFailure so it can be ref-counted struct CameraCaptureFailure : public RefBase, public ACameraCaptureFailure {}; @@ -286,6 +287,7 @@ class CameraDevice final : public RefBase { }; +} // namespace acam; } // namespace android; /** @@ -295,7 +297,7 @@ class CameraDevice final : public RefBase { struct ACameraDevice { ACameraDevice(const char* id, ACameraDevice_StateCallbacks* cb, sp chars) : - mDevice(new CameraDevice(id, cb, chars, this)) {} + mDevice(new android::acam::CameraDevice(id, cb, chars, this)) {} ~ACameraDevice() {}; @@ -331,7 +333,7 @@ struct ACameraDevice { } private: - android::sp mDevice; + android::sp mDevice; }; #endif // _ACAMERA_DEVICE_H diff --git a/camera/ndk/impl/ACameraManager.cpp b/camera/ndk/impl/ACameraManager.cpp index ee67677e9b..7d6ecacbb4 100644 --- a/camera/ndk/impl/ACameraManager.cpp +++ b/camera/ndk/impl/ACameraManager.cpp @@ -26,9 +26,10 @@ #include #include -using namespace android; +using namespace android::acam; namespace android { +namespace acam { // Static member definitions const char* CameraManagerGlobal::kCameraIdKey = "CameraId"; const char* CameraManagerGlobal::kCallbackFpKey = "CallbackFp"; @@ -345,6 +346,7 @@ void CameraManagerGlobal::onStatusChangedLocked( } } +} // namespace acam } // namespace android /** diff --git a/camera/ndk/impl/ACameraManager.h b/camera/ndk/impl/ACameraManager.h index ce65769cc8..55bfa7ea54 100644 --- a/camera/ndk/impl/ACameraManager.h +++ b/camera/ndk/impl/ACameraManager.h @@ -35,6 +35,7 @@ #include namespace android { +namespace acam { /** * Per-process singleton instance of CameraManger. Shared by all ACameraManager @@ -172,6 +173,7 @@ class CameraManagerGlobal final : public RefBase { ~CameraManagerGlobal(); }; +} // namespace acam; } // namespace android; /** @@ -180,7 +182,7 @@ class CameraManagerGlobal final : public RefBase { */ struct ACameraManager { ACameraManager() : - mGlobalManager(&(android::CameraManagerGlobal::getInstance())) {} + mGlobalManager(&(android::acam::CameraManagerGlobal::getInstance())) {} ~ACameraManager(); camera_status_t getCameraIdList(ACameraIdList** cameraIdList); static void deleteCameraIdList(ACameraIdList* cameraIdList); @@ -196,7 +198,7 @@ struct ACameraManager { kCameraIdListNotInit = -1 }; android::Mutex mLock; - android::sp mGlobalManager; + android::sp mGlobalManager; }; #endif //_ACAMERA_MANAGER_H diff --git a/camera/ndk/impl/ACameraMetadata.h b/camera/ndk/impl/ACameraMetadata.h index e76b80cef7..f21dbaf1ba 100644 --- a/camera/ndk/impl/ACameraMetadata.h +++ b/camera/ndk/impl/ACameraMetadata.h @@ -22,7 +22,13 @@ #include #include #include + +#ifdef __ANDROID_VNDK__ +#include +using CameraMetadata = android::hardware::camera::common::V1_0::helper::CameraMetadata; +#else #include +#endif #include diff --git a/camera/ndk/impl/ACaptureRequest.h b/camera/ndk/impl/ACaptureRequest.h index b11dafb093..5c82ab7fa7 100644 --- a/camera/ndk/impl/ACaptureRequest.h +++ b/camera/ndk/impl/ACaptureRequest.h @@ -21,8 +21,11 @@ using namespace android; +#ifdef __ANDROID_VNDK__ +#include "ndk_vendor/impl/ACaptureRequestVendor.h" +#else struct ACameraOutputTarget { - explicit ACameraOutputTarget(ANativeWindow* window) : mWindow(window) {}; + explicit ACameraOutputTarget(ACameraWindowType* window) : mWindow(window) {}; bool operator == (const ACameraOutputTarget& other) const { return mWindow == other.mWindow; @@ -37,8 +40,9 @@ struct ACameraOutputTarget { return mWindow > other.mWindow; } - ANativeWindow* mWindow; + ACameraWindowType* mWindow; }; +#endif struct ACameraOutputTargets { std::set mOutputs; diff --git a/camera/ndk/include/camera/NdkCameraCaptureSession.h b/camera/ndk/include/camera/NdkCameraCaptureSession.h index 5e0db60c17..1244582364 100644 --- a/camera/ndk/include/camera/NdkCameraCaptureSession.h +++ b/camera/ndk/include/camera/NdkCameraCaptureSession.h @@ -35,10 +35,10 @@ #include #include -#include #include "NdkCameraError.h" #include "NdkCameraMetadata.h" #include "NdkCaptureRequest.h" +#include "NdkCameraWindowType.h" #ifndef _NDK_CAMERA_CAPTURE_SESSION_H #define _NDK_CAMERA_CAPTURE_SESSION_H @@ -246,7 +246,7 @@ typedef void (*ACameraCaptureSession_captureCallback_sequenceAbort)( */ typedef void (*ACameraCaptureSession_captureCallback_bufferLost)( void* context, ACameraCaptureSession* session, - ACaptureRequest* request, ANativeWindow* window, int64_t frameNumber); + ACaptureRequest* request, ACameraWindowType* window, int64_t frameNumber); typedef struct ACameraCaptureSession_captureCallbacks { /// optional application context. diff --git a/camera/ndk/include/camera/NdkCameraDevice.h b/camera/ndk/include/camera/NdkCameraDevice.h index 7c13b34a82..4fe43d5278 100644 --- a/camera/ndk/include/camera/NdkCameraDevice.h +++ b/camera/ndk/include/camera/NdkCameraDevice.h @@ -34,10 +34,10 @@ */ #include -#include #include "NdkCameraError.h" #include "NdkCaptureRequest.h" #include "NdkCameraCaptureSession.h" +#include "NdkCameraWindowType.h" #ifndef _NDK_CAMERA_DEVICE_H #define _NDK_CAMERA_DEVICE_H @@ -345,7 +345,7 @@ void ACaptureSessionOutputContainer_free(ACaptureSessionOutputContain * @see ACaptureSessionOutputContainer_add */ camera_status_t ACaptureSessionOutput_create( - ANativeWindow* anw, /*out*/ACaptureSessionOutput** output) __INTRODUCED_IN(24); + ACameraWindowType* anw, /*out*/ACaptureSessionOutput** output) __INTRODUCED_IN(24); /** * Free a ACaptureSessionOutput object. @@ -694,7 +694,7 @@ camera_status_t ACameraDevice_createCaptureSession( * @see ACaptureSessionOutputContainer_add */ camera_status_t ACaptureSessionSharedOutput_create( - ANativeWindow* anw, /*out*/ACaptureSessionOutput** output) __INTRODUCED_IN(28); + ACameraWindowType* anw, /*out*/ACaptureSessionOutput** output) __INTRODUCED_IN(28); /** * Add a native window to shared ACaptureSessionOutput. @@ -712,7 +712,7 @@ camera_status_t ACaptureSessionSharedOutput_create( * ACaptureSessionOutput. */ camera_status_t ACaptureSessionSharedOutput_add(ACaptureSessionOutput *output, - ANativeWindow *anw) __INTRODUCED_IN(28); + ACameraWindowType *anw) __INTRODUCED_IN(28); /** * Remove a native window from shared ACaptureSessionOutput. @@ -728,7 +728,7 @@ camera_status_t ACaptureSessionSharedOutput_add(ACaptureSessionOutput *output, * ACaptureSessionOutput. */ camera_status_t ACaptureSessionSharedOutput_remove(ACaptureSessionOutput *output, - ANativeWindow* anw) __INTRODUCED_IN(28); + ACameraWindowType* anw) __INTRODUCED_IN(28); /** * Create a new camera capture session similar to {@link ACameraDevice_createCaptureSession}. This diff --git a/camera/ndk/include/camera/NdkCameraWindowType.h b/camera/ndk/include/camera/NdkCameraWindowType.h new file mode 100644 index 0000000000..99f67e9a5a --- /dev/null +++ b/camera/ndk/include/camera/NdkCameraWindowType.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _NDK_CAMERA_WINDOW_TYPE_H +#define _NDK_CAMERA_WINDOW_TYPE_H + +/** + * @addtogroup Camera + * @{ + */ + +/** + * @file NdkCameraWindowType.h + */ + +/* + * This file defines an NDK API. + * Do not remove methods. + * Do not change method signatures. + * Do not change the value of constants. + * Do not change the size of any of the classes defined in here. + * Do not reference types that are not part of the NDK. + * Do not #include files that aren't part of the NDK. + */ + +/** + * This file defines the window type used by NDK and the VNDK variants of the + * camera2 NDK. This enables us to share the api definition headers and avoid + * code duplication (since the VNDK variant doesn't use ANativeWindow unlike the + * NDK variant). + */ +#ifdef __ANDROID_VNDK__ +#include +typedef native_handle_t ACameraWindowType; +#else +#include +typedef ANativeWindow ACameraWindowType; +#endif + +#endif //_NDK_CAMERA_WINDOW_TYPE_H diff --git a/camera/ndk/include/camera/NdkCaptureRequest.h b/camera/ndk/include/camera/NdkCaptureRequest.h index 5340e76044..136989a338 100644 --- a/camera/ndk/include/camera/NdkCaptureRequest.h +++ b/camera/ndk/include/camera/NdkCaptureRequest.h @@ -35,9 +35,9 @@ #include -#include #include "NdkCameraError.h" #include "NdkCameraMetadata.h" +#include "NdkCameraWindowType.h" #ifndef _NDK_CAPTURE_REQUEST_H #define _NDK_CAPTURE_REQUEST_H @@ -101,7 +101,7 @@ typedef struct ACaptureRequest ACaptureRequest; * * @see ACaptureRequest_addTarget */ -camera_status_t ACameraOutputTarget_create(ANativeWindow* window, +camera_status_t ACameraOutputTarget_create(ACameraWindowType* window, ACameraOutputTarget** output) __INTRODUCED_IN(24); /** diff --git a/camera/ndk/ndk_vendor/impl/ACameraCaptureSessionVendor.h b/camera/ndk/ndk_vendor/impl/ACameraCaptureSessionVendor.h new file mode 100644 index 0000000000..8d9e90cac5 --- /dev/null +++ b/camera/ndk/ndk_vendor/impl/ACameraCaptureSessionVendor.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "utils.h" + +struct ACaptureSessionOutput { + explicit ACaptureSessionOutput(native_handle_t* window, bool isShared = false) : + mWindow(window), mIsShared(isShared) {}; + + bool operator == (const ACaptureSessionOutput& other) const { + return (mWindow == other.mWindow); + } + + bool operator != (const ACaptureSessionOutput& other) const { + return mWindow != other.mWindow; + } + + bool operator < (const ACaptureSessionOutput& other) const { + return mWindow < other.mWindow; + } + + bool operator > (const ACaptureSessionOutput& other) const { + return mWindow > other.mWindow; + } + + android::acam::utils::native_handle_ptr_wrapper mWindow; + std::set mSharedWindows; + bool mIsShared; + int mRotation = CAMERA3_STREAM_ROTATION_0; +}; + + diff --git a/camera/ndk/ndk_vendor/impl/ACameraDevice.cpp b/camera/ndk/ndk_vendor/impl/ACameraDevice.cpp new file mode 100644 index 0000000000..b86f854e04 --- /dev/null +++ b/camera/ndk/ndk_vendor/impl/ACameraDevice.cpp @@ -0,0 +1,1594 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//#define LOG_NDEBUG 0 +#define LOG_TAG "ACameraDeviceVendor" + +#include +#include +#include +#include +#include + +#include "ndk_vendor/impl/ACameraDevice.h" +#include "ACameraCaptureSession.h" +#include "ACameraMetadata.h" +#include "ACaptureRequest.h" +#include "utils.h" + +using namespace android; + +namespace android { +namespace acam { + +using HCameraMetadata = frameworks::cameraservice::device::V2_0::CameraMetadata; +using OutputConfiguration = frameworks::cameraservice::device::V2_0::OutputConfiguration; +using hardware::Void; + +// Static member definitions +const char* CameraDevice::kContextKey = "Context"; +const char* CameraDevice::kDeviceKey = "Device"; +const char* CameraDevice::kErrorCodeKey = "ErrorCode"; +const char* CameraDevice::kCallbackFpKey = "Callback"; +const char* CameraDevice::kSessionSpKey = "SessionSp"; +const char* CameraDevice::kCaptureRequestKey = "CaptureRequest"; +const char* CameraDevice::kTimeStampKey = "TimeStamp"; +const char* CameraDevice::kCaptureResultKey = "CaptureResult"; +const char* CameraDevice::kCaptureFailureKey = "CaptureFailure"; +const char* CameraDevice::kSequenceIdKey = "SequenceId"; +const char* CameraDevice::kFrameNumberKey = "FrameNumber"; +const char* CameraDevice::kAnwKey = "Anw"; + +/** + * CameraDevice Implementation + */ +CameraDevice::CameraDevice( + const char* id, + ACameraDevice_StateCallbacks* cb, + sp chars, + ACameraDevice* wrapper) : + mCameraId(id), + mAppCallbacks(*cb), + mChars(std::move(chars)), + mServiceCallback(new ServiceCallback(this)), + mWrapper(wrapper), + mInError(false), + mError(ACAMERA_OK), + mIdle(true), + mCurrentSession(nullptr) { + mClosing = false; + // Setup looper thread to perfrom device callbacks to app + mCbLooper = new ALooper; + mCbLooper->setName("C2N-dev-looper"); + status_t err = mCbLooper->start( + /*runOnCallingThread*/false, + /*canCallJava*/ true, + PRIORITY_DEFAULT); + if (err != OK) { + ALOGE("%s: Unable to start camera device callback looper: %s (%d)", + __FUNCTION__, strerror(-err), err); + setCameraDeviceErrorLocked(ACAMERA_ERROR_CAMERA_DEVICE); + } + mHandler = new CallbackHandler(); + mCbLooper->registerHandler(mHandler); + + const CameraMetadata& metadata = mChars->getInternalData(); + camera_metadata_ro_entry entry = metadata.find(ANDROID_REQUEST_PARTIAL_RESULT_COUNT); + if (entry.count != 1) { + ALOGW("%s: bad count %zu for partial result count", __FUNCTION__, entry.count); + mPartialResultCount = 1; + } else { + mPartialResultCount = entry.data.i32[0]; + } + + entry = metadata.find(ANDROID_LENS_INFO_SHADING_MAP_SIZE); + if (entry.count != 2) { + ALOGW("%s: bad count %zu for shading map size", __FUNCTION__, entry.count); + mShadingMapSize[0] = 0; + mShadingMapSize[1] = 0; + } else { + mShadingMapSize[0] = entry.data.i32[0]; + mShadingMapSize[1] = entry.data.i32[1]; + } +} + +// Device close implementaiton +CameraDevice::~CameraDevice() { + sp session = mCurrentSession.promote(); + { + Mutex::Autolock _l(mDeviceLock); + if (!isClosed()) { + disconnectLocked(session); + } + mCurrentSession = nullptr; + if (mCbLooper != nullptr) { + mCbLooper->unregisterHandler(mHandler->id()); + mCbLooper->stop(); + } + } + mCbLooper.clear(); + mHandler.clear(); +} + +void +CameraDevice::postSessionMsgAndCleanup(sp& msg) { + msg->post(); + msg.clear(); + sp cleanupMsg = new AMessage(kWhatCleanUpSessions, mHandler); + cleanupMsg->post(); +} + +// TODO: cached created request? +camera_status_t +CameraDevice::createCaptureRequest( + ACameraDevice_request_template templateId, + ACaptureRequest** request) const { + Mutex::Autolock _l(mDeviceLock); + camera_status_t ret = checkCameraClosedOrErrorLocked(); + if (ret != ACAMERA_OK) { + return ret; + } + if (mRemote == nullptr) { + return ACAMERA_ERROR_CAMERA_DISCONNECTED; + } + CameraMetadata rawRequest; + Status status = Status::NO_ERROR; + auto remoteRet = mRemote->createDefaultRequest( + utils::convertToHidl(templateId), + [&status, &rawRequest](auto s, const hidl_vec &metadata) { + status = s; + if (status == Status::NO_ERROR && utils::convertFromHidlCloned(metadata, &rawRequest)) { + } else { + ALOGE("%s: Couldn't create default request", __FUNCTION__); + } + }); + if (!remoteRet.isOk()) { + ALOGE("%s: Transaction error while trying to create default request %s", __FUNCTION__, + remoteRet.description().c_str()); + return ACAMERA_ERROR_UNKNOWN; + } + if (status != Status::NO_ERROR) { + return utils::convertFromHidl(status); + } + ACaptureRequest* outReq = new ACaptureRequest(); + outReq->settings = new ACameraMetadata(rawRequest.release(), ACameraMetadata::ACM_REQUEST); + outReq->targets = new ACameraOutputTargets(); + *request = outReq; + return ACAMERA_OK; +} + +camera_status_t +CameraDevice::createCaptureSession( + const ACaptureSessionOutputContainer* outputs, + const ACaptureRequest* sessionParameters, + const ACameraCaptureSession_stateCallbacks* callbacks, + /*out*/ACameraCaptureSession** session) { + sp currentSession = mCurrentSession.promote(); + Mutex::Autolock _l(mDeviceLock); + camera_status_t ret = checkCameraClosedOrErrorLocked(); + if (ret != ACAMERA_OK) { + return ret; + } + + if (currentSession != nullptr) { + currentSession->closeByDevice(); + stopRepeatingLocked(); + } + + // Create new session + ret = configureStreamsLocked(outputs, sessionParameters); + if (ret != ACAMERA_OK) { + ALOGE("Fail to create new session. cannot configure streams"); + return ret; + } + + ACameraCaptureSession* newSession = new ACameraCaptureSession( + mNextSessionId++, outputs, callbacks, this); + + // set new session as current session + newSession->incStrong((void *) ACameraDevice_createCaptureSession); + mCurrentSession = newSession; + mFlushing = false; + *session = newSession; + return ACAMERA_OK; +} + +camera_status_t +CameraDevice::captureLocked( + sp session, + /*optional*/ACameraCaptureSession_captureCallbacks* cbs, + int numRequests, ACaptureRequest** requests, + /*optional*/int* captureSequenceId) { + return submitRequestsLocked( + session, cbs, numRequests, requests, captureSequenceId, /*isRepeating*/false); +} + +camera_status_t +CameraDevice::setRepeatingRequestsLocked( + sp session, + /*optional*/ACameraCaptureSession_captureCallbacks* cbs, + int numRequests, ACaptureRequest** requests, + /*optional*/int* captureSequenceId) { + return submitRequestsLocked( + session, cbs, numRequests, requests, captureSequenceId, /*isRepeating*/true); +} + +void addRequestSettingsMetadata(ACaptureRequest *aCaptureRequest, + sp &req) { + CameraMetadata metadataCopy = aCaptureRequest->settings->getInternalData(); + const camera_metadata_t *camera_metadata = metadataCopy.getAndLock(); + HCameraMetadata hCameraMetadata; + utils::convertToHidl(camera_metadata, &hCameraMetadata); + metadataCopy.unlock(camera_metadata); + req->mPhysicalCameraSettings.resize(1); + req->mPhysicalCameraSettings[0].settings.metadata(std::move(hCameraMetadata)); +} + +camera_status_t +CameraDevice::submitRequestsLocked( + sp session, + /*optional*/ACameraCaptureSession_captureCallbacks* cbs, + int numRequests, ACaptureRequest** requests, + /*optional*/int* captureSequenceId, + bool isRepeating) { + camera_status_t ret = checkCameraClosedOrErrorLocked(); + if (ret != ACAMERA_OK) { + ALOGE("Camera %s submit capture request failed! ret %d", getId(), ret); + return ret; + } + + // Form two vectors of capture request, one for internal tracking + std::vector requestList; + Vector> requestsV; + requestsV.setCapacity(numRequests); + for (int i = 0; i < numRequests; i++) { + sp req; + ret = allocateCaptureRequest(requests[i], req); + // We need to call this method since after submitRequestList is called, + // the request metadata queue might have removed the capture request + // metadata. Therefore we simply add the metadata to its wrapper class, + // so that it can be retrived later. + addRequestSettingsMetadata(requests[i], req); + if (ret != ACAMERA_OK) { + ALOGE("Convert capture request to internal format failure! ret %d", ret); + return ret; + } + if (req->mCaptureRequest.streamAndWindowIds.size() == 0) { + ALOGE("Capture request without output target cannot be submitted!"); + return ACAMERA_ERROR_INVALID_PARAMETER; + } + requestList.push_back(utils::convertToHidl(req.get())); + requestsV.push_back(req); + } + if (isRepeating) { + ret = stopRepeatingLocked(); + if (ret != ACAMERA_OK) { + ALOGE("Camera %s stop repeating failed! ret %d", getId(), ret); + return ret; + } + } + + SubmitInfo info; + Status status; + auto remoteRet = mRemote->submitRequestList(requestList, isRepeating, + [&status, &info](auto s, auto &submitInfo) { + status = s; + info = submitInfo; + }); + if (!remoteRet.isOk()) { + ALOGE("%s: Transaction error for submitRequestList call: %s", __FUNCTION__, + remoteRet.description().c_str()); + } + if (status != Status::NO_ERROR) { + return utils::convertFromHidl(status); + } + int32_t sequenceId = info.requestId; + int64_t lastFrameNumber = info.lastFrameNumber; + if (sequenceId < 0) { + ALOGE("Camera %s submit request remote failure: ret %d", getId(), sequenceId); + return ACAMERA_ERROR_UNKNOWN; + } + + CallbackHolder cbHolder(session, requestsV, isRepeating, cbs); + mSequenceCallbackMap.insert(std::make_pair(sequenceId, cbHolder)); + + if (isRepeating) { + // stopRepeating above should have cleanup repeating sequence id + if (mRepeatingSequenceId != REQUEST_ID_NONE) { + setCameraDeviceErrorLocked(ACAMERA_ERROR_CAMERA_DEVICE); + return ACAMERA_ERROR_CAMERA_DEVICE; + } + mRepeatingSequenceId = sequenceId; + } else { + mSequenceLastFrameNumberMap.insert(std::make_pair(sequenceId, lastFrameNumber)); + } + + if (mIdle) { + sp msg = new AMessage(kWhatSessionStateCb, mHandler); + msg->setPointer(kContextKey, session->mUserSessionCallback.context); + msg->setObject(kSessionSpKey, session); + msg->setPointer(kCallbackFpKey, (void*) session->mUserSessionCallback.onActive); + postSessionMsgAndCleanup(msg); + } + mIdle = false; + mBusySession = session; + + if (captureSequenceId) { + *captureSequenceId = sequenceId; + } + return ACAMERA_OK; +} + +camera_status_t CameraDevice::updateOutputConfigurationLocked(ACaptureSessionOutput *output) { + camera_status_t ret = checkCameraClosedOrErrorLocked(); + if (ret != ACAMERA_OK) { + return ret; + } + + if (output == nullptr) { + return ACAMERA_ERROR_INVALID_PARAMETER; + } + + if (!output->mIsShared) { + ALOGE("Error output configuration is not shared"); + return ACAMERA_ERROR_INVALID_OPERATION; + } + + int32_t streamId = -1; + for (auto& kvPair : mConfiguredOutputs) { + if (utils::isWindowNativeHandleEqual(kvPair.second.first, output->mWindow)) { + streamId = kvPair.first; + break; + } + } + if (streamId < 0) { + ALOGE("Error: Invalid output configuration"); + return ACAMERA_ERROR_INVALID_PARAMETER; + } + + OutputConfigurationWrapper outConfigW; + OutputConfiguration &outConfig = outConfigW.mOutputConfiguration; + outConfig.rotation = utils::convertToHidl(output->mRotation); + outConfig.windowGroupId = -1; // ndk doesn't support inter OutputConfiguration buffer sharing. + outConfig.windowHandles.resize(output->mSharedWindows.size() + 1); + outConfig.windowHandles[0] = output->mWindow; + int i = 1; + for (auto& anw : output->mSharedWindows) { + outConfig.windowHandles[i++] = anw; + } + + auto remoteRet = mRemote->updateOutputConfiguration(streamId, outConfig); + if (!remoteRet.isOk()) { + ALOGE("%s: Transaction error in updating OutputConfiguration: %s", __FUNCTION__, + remoteRet.description().c_str()); + return ACAMERA_ERROR_UNKNOWN; + } + + switch (remoteRet) { + case Status::NO_ERROR: + break; + case Status::INVALID_OPERATION: + ALOGE("Camera device %s invalid operation", getId()); + return ACAMERA_ERROR_INVALID_OPERATION; + case Status::ALREADY_EXISTS: + ALOGE("Camera device %s output surface already exists", getId()); + return ACAMERA_ERROR_INVALID_PARAMETER; + case Status::ILLEGAL_ARGUMENT: + ALOGE("Camera device %s invalid input argument", getId()); + return ACAMERA_ERROR_INVALID_PARAMETER; + default: + ALOGE("Camera device %s failed to add shared output", getId()); + return ACAMERA_ERROR_UNKNOWN; + } + + mConfiguredOutputs[streamId] = std::make_pair(output->mWindow, outConfigW); + + return ACAMERA_OK; +} + +camera_status_t +CameraDevice::allocateCaptureRequest( + const ACaptureRequest* request, /*out*/sp &outReq) { + sp req(new CaptureRequest()); + req->mCaptureRequest.physicalCameraSettings.resize(1); + req->mCaptureRequest.physicalCameraSettings[0].id = mCameraId; + // TODO: Do we really need to copy the metadata here ? + CameraMetadata metadataCopy = request->settings->getInternalData(); + const camera_metadata_t *cameraMetadata = metadataCopy.getAndLock(); + HCameraMetadata hCameraMetadata; + utils::convertToHidl(cameraMetadata, &hCameraMetadata); + metadataCopy.unlock(cameraMetadata); + if (request->settings != nullptr) { + if (hCameraMetadata.data() != nullptr && + mCaptureRequestMetadataQueue != nullptr && + mCaptureRequestMetadataQueue->write( + reinterpret_cast(hCameraMetadata.data()), + hCameraMetadata.size())) { + // The metadata field of the union would've been destructued, so no need + // to re-size it. + req->mCaptureRequest.physicalCameraSettings[0].settings.fmqMetadataSize( + hCameraMetadata.size()); + } else { + ALOGE("Fmq write capture result failed, falling back to hwbinder"); + req->mCaptureRequest.physicalCameraSettings[0].settings.metadata( + std::move(hCameraMetadata)); + } + } + + std::vector requestStreamIdxList; + std::vector requestSurfaceIdxList; + for (auto outputTarget : request->targets->mOutputs) { + native_handle_t* anw = outputTarget.mWindow; + bool found = false; + req->mSurfaceList.push_back(anw); + // lookup stream/surface ID + for (const auto& kvPair : mConfiguredOutputs) { + int streamId = kvPair.first; + const OutputConfigurationWrapper& outConfig = kvPair.second.second; + const auto& windowHandles = outConfig.mOutputConfiguration.windowHandles; + for (int surfaceId = 0; surfaceId < (int) windowHandles.size(); surfaceId++) { + // If two native handles are equivalent, so are their surfaces. + if (utils::isWindowNativeHandleEqual(windowHandles[surfaceId].getNativeHandle(), + anw)) { + found = true; + requestStreamIdxList.push_back(streamId); + requestSurfaceIdxList.push_back(surfaceId); + break; + } + } + if (found) { + break; + } + } + if (!found) { + ALOGE("Unconfigured output target %p in capture request!", anw); + return ACAMERA_ERROR_INVALID_PARAMETER; + } + } + req->mCaptureRequest.streamAndWindowIds.resize(requestStreamIdxList.size()); + for (int i = 0; i < requestStreamIdxList.size(); i++) { + req->mCaptureRequest.streamAndWindowIds[i].streamId = requestStreamIdxList[i]; + req->mCaptureRequest.streamAndWindowIds[i].windowId = requestSurfaceIdxList[i]; + } + outReq = req; + return ACAMERA_OK; +} + +ACaptureRequest* +CameraDevice::allocateACaptureRequest(sp& req) { + ACaptureRequest* pRequest = new ACaptureRequest(); + CameraMetadata clone; + utils::convertFromHidlCloned(req->mPhysicalCameraSettings[0].settings.metadata(), &clone); + pRequest->settings = new ACameraMetadata(clone.release(), ACameraMetadata::ACM_REQUEST); + pRequest->targets = new ACameraOutputTargets(); + for (size_t i = 0; i < req->mSurfaceList.size(); i++) { + native_handle_t* anw = req->mSurfaceList[i]; + ACameraOutputTarget outputTarget(anw); + pRequest->targets->mOutputs.insert(outputTarget); + } + return pRequest; +} + +void +CameraDevice::freeACaptureRequest(ACaptureRequest* req) { + if (req == nullptr) { + return; + } + req->settings.clear(); + delete req->targets; + delete req; +} + +void +CameraDevice::notifySessionEndOfLifeLocked(ACameraCaptureSession* session) { + if (isClosed()) { + // Device is closing already. do nothing + return; + } + + if (mCurrentSession != session) { + // Session has been replaced by other seesion or device is closed + return; + } + mCurrentSession = nullptr; + + // Should not happen + if (!session->mIsClosed) { + ALOGE("Error: unclosed session %p reaches end of life!", session); + setCameraDeviceErrorLocked(ACAMERA_ERROR_CAMERA_DEVICE); + return; + } + + // No new session, unconfigure now + camera_status_t ret = configureStreamsLocked(nullptr, nullptr); + if (ret != ACAMERA_OK) { + ALOGE("Unconfigure stream failed. Device might still be configured! ret %d", ret); + } +} + +void +CameraDevice::disconnectLocked(sp& session) { + if (mClosing.exchange(true)) { + // Already closing, just return + ALOGW("Camera device %s is already closing.", getId()); + return; + } + + if (mRemote != nullptr) { + auto ret = mRemote->disconnect(); + if (!ret.isOk()) { + ALOGE("%s: Transaction error while disconnecting device %s", __FUNCTION__, + ret.description().c_str()); + } + } + mRemote = nullptr; + + if (session != nullptr) { + session->closeByDevice(); + } +} + +camera_status_t +CameraDevice::stopRepeatingLocked() { + camera_status_t ret = checkCameraClosedOrErrorLocked(); + if (ret != ACAMERA_OK) { + ALOGE("Camera %s stop repeating failed! ret %d", getId(), ret); + return ret; + } + if (mRepeatingSequenceId != REQUEST_ID_NONE) { + int repeatingSequenceId = mRepeatingSequenceId; + mRepeatingSequenceId = REQUEST_ID_NONE; + + int64_t lastFrameNumber; + Status status = Status::NO_ERROR; + auto remoteRet = mRemote->cancelRepeatingRequest( + [&status, &lastFrameNumber](Status s, auto frameNumber) { + status = s; + lastFrameNumber = frameNumber; + }); + if (!remoteRet.isOk() || status != Status::NO_ERROR) { + ALOGE("%s: Unable to cancel active repeating request", __FUNCTION__); + return utils::convertFromHidl(status); + } + checkRepeatingSequenceCompleteLocked(repeatingSequenceId, lastFrameNumber); + } + return ACAMERA_OK; +} + +camera_status_t +CameraDevice::flushLocked(ACameraCaptureSession* session) { + camera_status_t ret = checkCameraClosedOrErrorLocked(); + if (ret != ACAMERA_OK) { + ALOGE("Camera %s abort captures failed! ret %d", getId(), ret); + return ret; + } + + // This should never happen because creating a new session will close + // previous one and thus reject any API call from previous session. + // But still good to check here in case something unexpected happen. + if (mCurrentSession != session) { + ALOGE("Camera %s session %p is not current active session!", getId(), session); + return ACAMERA_ERROR_INVALID_OPERATION; + } + + if (mFlushing) { + ALOGW("Camera %s is already aborting captures", getId()); + return ACAMERA_OK; + } + + mFlushing = true; + + // Send onActive callback to guarantee there is always active->ready transition + sp msg = new AMessage(kWhatSessionStateCb, mHandler); + msg->setPointer(kContextKey, session->mUserSessionCallback.context); + msg->setObject(kSessionSpKey, session); + msg->setPointer(kCallbackFpKey, (void*) session->mUserSessionCallback.onActive); + postSessionMsgAndCleanup(msg); + + // If device is already idling, send callback and exit early + if (mIdle) { + sp msg = new AMessage(kWhatSessionStateCb, mHandler); + msg->setPointer(kContextKey, session->mUserSessionCallback.context); + msg->setObject(kSessionSpKey, session); + msg->setPointer(kCallbackFpKey, (void*) session->mUserSessionCallback.onReady); + postSessionMsgAndCleanup(msg); + mFlushing = false; + return ACAMERA_OK; + } + + int64_t lastFrameNumber; + Status status; + auto remoteRet = mRemote->flush([&status, &lastFrameNumber](auto s, auto frameNumber) { + status = s; + lastFrameNumber = frameNumber; + }); + if (!remoteRet.isOk() || status != Status::NO_ERROR) { + ALOGE("%s: Abort captures failed", __FUNCTION__); + return utils::convertFromHidl(status); + } + if (mRepeatingSequenceId != REQUEST_ID_NONE) { + checkRepeatingSequenceCompleteLocked(mRepeatingSequenceId, lastFrameNumber); + } + return ACAMERA_OK; +} + +camera_status_t +CameraDevice::waitUntilIdleLocked() { + camera_status_t ret = checkCameraClosedOrErrorLocked(); + if (ret != ACAMERA_OK) { + ALOGE("Wait until camera %s idle failed! ret %d", getId(), ret); + return ret; + } + + if (mRepeatingSequenceId != REQUEST_ID_NONE) { + ALOGE("Camera device %s won't go to idle when there is repeating request!", getId()); + return ACAMERA_ERROR_INVALID_OPERATION; + } + + auto remoteRet = mRemote->waitUntilIdle(); + if (!remoteRet.isOk()) { + ALOGE("%s: Transaction waitUntilIdle failed", __FUNCTION__); + return utils::convertFromHidl(remoteRet); + } + return ACAMERA_OK; +} + +camera_status_t +CameraDevice::configureStreamsLocked(const ACaptureSessionOutputContainer* outputs, + const ACaptureRequest* sessionParameters) { + ACaptureSessionOutputContainer emptyOutput; + if (outputs == nullptr) { + outputs = &emptyOutput; + } + + camera_status_t ret = checkCameraClosedOrErrorLocked(); + if (ret != ACAMERA_OK) { + return ret; + } + + std::set> outputSet; + for (auto outConfig : outputs->mOutputs) { + native_handle_t* anw = outConfig.mWindow; + OutputConfigurationWrapper outConfigInsertW; + OutputConfiguration &outConfigInsert = outConfigInsertW.mOutputConfiguration; + outConfigInsert.rotation = utils::convertToHidl(outConfig.mRotation); + outConfigInsert.windowGroupId = -1; + outConfigInsert.windowHandles.resize(outConfig.mSharedWindows.size() + 1); + outConfigInsert.windowHandles[0] = anw; + native_handle_ptr_wrapper wrap(anw); + outputSet.insert(std::make_pair(anw, outConfigInsertW)); + } + std::set> addSet = outputSet; + std::vector deleteList; + + // Determine which streams need to be created, which to be deleted + for (auto& kvPair : mConfiguredOutputs) { + int32_t streamId = kvPair.first; + auto& outputPair = kvPair.second; + if (outputSet.count(outputPair)) { + deleteList.push_back(streamId); // Need to delete a no longer needed stream + } else { + addSet.erase(outputPair); // No need to add already existing stream + } + } + + ret = stopRepeatingLocked(); + if (ret != ACAMERA_OK) { + ALOGE("Camera device %s stop repeating failed, ret %d", getId(), ret); + return ret; + } + + ret = waitUntilIdleLocked(); + if (ret != ACAMERA_OK) { + ALOGE("Camera device %s wait until idle failed, ret %d", getId(), ret); + return ret; + } + + // Send onReady to previous session + // CurrentSession will be updated after configureStreamLocked, so here + // mCurrentSession is the session to be replaced by a new session + if (!mIdle && mCurrentSession != nullptr) { + if (mBusySession != mCurrentSession) { + ALOGE("Current session != busy session"); + setCameraDeviceErrorLocked(ACAMERA_ERROR_CAMERA_DEVICE); + return ACAMERA_ERROR_CAMERA_DEVICE; + } + sp msg = new AMessage(kWhatSessionStateCb, mHandler); + msg->setPointer(kContextKey, mBusySession->mUserSessionCallback.context); + msg->setObject(kSessionSpKey, mBusySession); + msg->setPointer(kCallbackFpKey, (void*) mBusySession->mUserSessionCallback.onReady); + mBusySession.clear(); + postSessionMsgAndCleanup(msg); + } + mIdle = true; + + auto remoteRet = mRemote->beginConfigure(); + if (!remoteRet.isOk()|| remoteRet != Status::NO_ERROR) { + ALOGE("Camera device %s begin configure failed", getId()); + return utils::convertFromHidl(remoteRet); + } + + // delete to-be-deleted streams + for (auto streamId : deleteList) { + remoteRet = mRemote->deleteStream(streamId); + if (!remoteRet.isOk() || remoteRet != Status::NO_ERROR) { + ALOGE("Camera device %s failed to remove stream %d", getId(), streamId); + return utils::convertFromHidl(remoteRet); + } + mConfiguredOutputs.erase(streamId); + } + + // add new streams + for (auto outputPair : addSet) { + int streamId; + Status status; + auto ret = mRemote->createStream(outputPair.second, + [&status, &streamId](Status s, auto stream_id) { + status = s; + streamId = stream_id; + }); + if (!remoteRet.isOk() || status != Status::NO_ERROR) { + ALOGE("Camera device %s failed to create stream", getId()); + return utils::convertFromHidl(status); + } + mConfiguredOutputs.insert(std::make_pair(streamId, outputPair)); + } + + CameraMetadata params; + HCameraMetadata hidlParams; + if ((sessionParameters != nullptr) && (sessionParameters->settings != nullptr)) { + params.append(sessionParameters->settings->getInternalData()); + const camera_metadata_t *params_metadata = params.getAndLock(); + utils::convertToHidl(params_metadata, &hidlParams); + params.unlock(params_metadata); + remoteRet = mRemote->endConfigure(StreamConfigurationMode::NORMAL_MODE, hidlParams); + } + if (!remoteRet.isOk()) { + ALOGE("Transaction error: endConfigure failed %s", remoteRet.description().c_str()); + } + + return utils::convertFromHidl(remoteRet); +} + +void +CameraDevice::setRemoteDevice(sp remote) { + Mutex::Autolock _l(mDeviceLock); + mRemote = remote; +} + +bool +CameraDevice::setDeviceMetadataQueues() { + if (mRemote == nullptr) { + ALOGE("mRemote must not be null while trying to fetch metadata queues"); + return false; + } + std::shared_ptr &reqQueue = mCaptureRequestMetadataQueue; + auto ret = + mRemote->getCaptureRequestMetadataQueue( + [&reqQueue](const auto &mqDescriptor) { + reqQueue = std::make_shared(mqDescriptor); + if (!reqQueue->isValid() || reqQueue->availableToWrite() <=0) { + ALOGE("Empty fmq from cameraserver"); + reqQueue = nullptr; + } + }); + if (!ret.isOk()) { + ALOGE("Transaction error trying to get capture request metadata queue"); + return false; + } + std::shared_ptr &resQueue = mCaptureResultMetadataQueue; + ret = + mRemote->getCaptureResultMetadataQueue( + [&resQueue](const auto &mqDescriptor) { + resQueue = std::make_shared(mqDescriptor); + if (!resQueue->isValid() || resQueue->availableToWrite() <=0) { + ALOGE("Empty fmq from cameraserver"); + } + }); + if (!ret.isOk()) { + ALOGE("Transaction error trying to get capture result metadata queue"); + return false; + } + return true; +} + +camera_status_t +CameraDevice::checkCameraClosedOrErrorLocked() const { + if (mRemote == nullptr) { + ALOGE("%s: camera device already closed", __FUNCTION__); + return ACAMERA_ERROR_CAMERA_DISCONNECTED; + } + if (mInError) {// triggered by onDeviceError + ALOGE("%s: camera device has encountered a serious error", __FUNCTION__); + return mError; + } + return ACAMERA_OK; +} + +void +CameraDevice::setCameraDeviceErrorLocked(camera_status_t error) { + mInError = true; + mError = error; + return; +} + +void +CameraDevice::FrameNumberTracker::updateTracker(int64_t frameNumber, bool isError) { + ALOGV("updateTracker frame %" PRId64 " isError %d", frameNumber, isError); + if (isError) { + mFutureErrorSet.insert(frameNumber); + } else if (frameNumber <= mCompletedFrameNumber) { + ALOGE("Frame number %" PRId64 " decreased! current fn %" PRId64, + frameNumber, mCompletedFrameNumber); + return; + } else { + if (frameNumber != mCompletedFrameNumber + 1) { + ALOGE("Frame number out of order. Expect %" PRId64 " but get %" PRId64, + mCompletedFrameNumber + 1, frameNumber); + // Do not assert as in java implementation + } + mCompletedFrameNumber = frameNumber; + } + update(); +} + +void +CameraDevice::FrameNumberTracker::update() { + for (auto it = mFutureErrorSet.begin(); it != mFutureErrorSet.end();) { + int64_t errorFrameNumber = *it; + if (errorFrameNumber == mCompletedFrameNumber + 1) { + mCompletedFrameNumber++; + it = mFutureErrorSet.erase(it); + } else if (errorFrameNumber <= mCompletedFrameNumber) { + // This should not happen, but deal with it anyway + ALOGE("Completd frame number passed through current frame number!"); + // erase the old error since it's no longer useful + it = mFutureErrorSet.erase(it); + } else { + // Normal requests hasn't catched up error frames, just break + break; + } + } + ALOGV("Update complete frame %" PRId64, mCompletedFrameNumber); +} + +void +CameraDevice::onCaptureErrorLocked( + ErrorCode errorCode, + const CaptureResultExtras& resultExtras) { + int sequenceId = resultExtras.requestId; + int64_t frameNumber = resultExtras.frameNumber; + int32_t burstId = resultExtras.burstId; + auto it = mSequenceCallbackMap.find(sequenceId); + if (it == mSequenceCallbackMap.end()) { + ALOGE("%s: Error: capture sequence index %d not found!", + __FUNCTION__, sequenceId); + setCameraDeviceErrorLocked(ACAMERA_ERROR_CAMERA_SERVICE); + return; + } + + CallbackHolder cbh = (*it).second; + sp session = cbh.mSession; + if ((size_t) burstId >= cbh.mRequests.size()) { + ALOGE("%s: Error: request index %d out of bound (size %zu)", + __FUNCTION__, burstId, cbh.mRequests.size()); + setCameraDeviceErrorLocked(ACAMERA_ERROR_CAMERA_SERVICE); + return; + } + sp request = cbh.mRequests[burstId]; + + // Handle buffer error + if (errorCode == ErrorCode::CAMERA_BUFFER) { + int32_t streamId = resultExtras.errorStreamId; + ACameraCaptureSession_captureCallback_bufferLost onBufferLost = + cbh.mCallbacks.onCaptureBufferLost; + auto outputPairIt = mConfiguredOutputs.find(streamId); + if (outputPairIt == mConfiguredOutputs.end()) { + ALOGE("%s: Error: stream id %d does not exist", __FUNCTION__, streamId); + setCameraDeviceErrorLocked(ACAMERA_ERROR_CAMERA_SERVICE); + return; + } + + const auto& windowHandles = outputPairIt->second.second.mOutputConfiguration.windowHandles; + for (const auto& outHandle : windowHandles) { + for (auto streamAndWindowId : request->mCaptureRequest.streamAndWindowIds) { + int32_t windowId = streamAndWindowId.windowId; + if (utils::isWindowNativeHandleEqual(windowHandles[windowId],outHandle)) { + native_handle_t* anw = + const_cast(windowHandles[windowId].getNativeHandle()); + ALOGV("Camera %s Lost output buffer for ANW %p frame %" PRId64, + getId(), anw, frameNumber); + + sp msg = new AMessage(kWhatCaptureBufferLost, mHandler); + msg->setPointer(kContextKey, cbh.mCallbacks.context); + msg->setObject(kSessionSpKey, session); + msg->setPointer(kCallbackFpKey, (void*) onBufferLost); + msg->setObject(kCaptureRequestKey, request); + msg->setPointer(kAnwKey, (void*) anw); + msg->setInt64(kFrameNumberKey, frameNumber); + postSessionMsgAndCleanup(msg); + } + } + } + } else { // Handle other capture failures + // Fire capture failure callback if there is one registered + ACameraCaptureSession_captureCallback_failed onError = cbh.mCallbacks.onCaptureFailed; + sp failure(new CameraCaptureFailure()); + failure->frameNumber = frameNumber; + // TODO: refine this when implementing flush + failure->reason = CAPTURE_FAILURE_REASON_ERROR; + failure->sequenceId = sequenceId; + failure->wasImageCaptured = (errorCode == ErrorCode::CAMERA_RESULT); + + sp msg = new AMessage(kWhatCaptureFail, mHandler); + msg->setPointer(kContextKey, cbh.mCallbacks.context); + msg->setObject(kSessionSpKey, session); + msg->setPointer(kCallbackFpKey, (void*) onError); + msg->setObject(kCaptureRequestKey, request); + msg->setObject(kCaptureFailureKey, failure); + postSessionMsgAndCleanup(msg); + + // Update tracker + mFrameNumberTracker.updateTracker(frameNumber, /*isError*/true); + checkAndFireSequenceCompleteLocked(); + } + return; +} + +void CameraDevice::CallbackHandler::onMessageReceived( + const sp &msg) { + switch (msg->what()) { + case kWhatOnDisconnected: + case kWhatOnError: + case kWhatSessionStateCb: + case kWhatCaptureStart: + case kWhatCaptureResult: + case kWhatCaptureFail: + case kWhatCaptureSeqEnd: + case kWhatCaptureSeqAbort: + case kWhatCaptureBufferLost: + ALOGV("%s: Received msg %d", __FUNCTION__, msg->what()); + break; + case kWhatCleanUpSessions: + mCachedSessions.clear(); + return; + default: + ALOGE("%s:Error: unknown device callback %d", __FUNCTION__, msg->what()); + return; + } + // Check the common part of all message + void* context; + bool found = msg->findPointer(kContextKey, &context); + if (!found) { + ALOGE("%s: Cannot find callback context!", __FUNCTION__); + return; + } + switch (msg->what()) { + case kWhatOnDisconnected: + { + ACameraDevice* dev; + found = msg->findPointer(kDeviceKey, (void**) &dev); + if (!found || dev == nullptr) { + ALOGE("%s: Cannot find device pointer!", __FUNCTION__); + return; + } + ACameraDevice_StateCallback onDisconnected; + found = msg->findPointer(kCallbackFpKey, (void**) &onDisconnected); + if (!found) { + ALOGE("%s: Cannot find onDisconnected!", __FUNCTION__); + return; + } + if (onDisconnected == nullptr) { + return; + } + (*onDisconnected)(context, dev); + break; + } + case kWhatOnError: + { + ACameraDevice* dev; + found = msg->findPointer(kDeviceKey, (void**) &dev); + if (!found || dev == nullptr) { + ALOGE("%s: Cannot find device pointer!", __FUNCTION__); + return; + } + ACameraDevice_ErrorStateCallback onError; + found = msg->findPointer(kCallbackFpKey, (void**) &onError); + if (!found) { + ALOGE("%s: Cannot find onError!", __FUNCTION__); + return; + } + int errorCode; + found = msg->findInt32(kErrorCodeKey, &errorCode); + if (!found) { + ALOGE("%s: Cannot find error code!", __FUNCTION__); + return; + } + if (onError == nullptr) { + return; + } + (*onError)(context, dev, errorCode); + break; + } + case kWhatSessionStateCb: + case kWhatCaptureStart: + case kWhatCaptureResult: + case kWhatCaptureFail: + case kWhatCaptureSeqEnd: + case kWhatCaptureSeqAbort: + case kWhatCaptureBufferLost: + { + sp obj; + found = msg->findObject(kSessionSpKey, &obj); + if (!found || obj == nullptr) { + ALOGE("%s: Cannot find session pointer!", __FUNCTION__); + return; + } + sp session(static_cast(obj.get())); + mCachedSessions.push(session); + sp requestSp = nullptr; + switch (msg->what()) { + case kWhatCaptureStart: + case kWhatCaptureResult: + case kWhatCaptureFail: + case kWhatCaptureBufferLost: + found = msg->findObject(kCaptureRequestKey, &obj); + if (!found) { + ALOGE("%s: Cannot find capture request!", __FUNCTION__); + return; + } + requestSp = static_cast(obj.get()); + break; + } + + switch (msg->what()) { + case kWhatSessionStateCb: + { + ACameraCaptureSession_stateCallback onState; + found = msg->findPointer(kCallbackFpKey, (void**) &onState); + if (!found) { + ALOGE("%s: Cannot find state callback!", __FUNCTION__); + return; + } + if (onState == nullptr) { + return; + } + (*onState)(context, session.get()); + break; + } + case kWhatCaptureStart: + { + ACameraCaptureSession_captureCallback_start onStart; + found = msg->findPointer(kCallbackFpKey, (void**) &onStart); + if (!found) { + ALOGE("%s: Cannot find capture start callback!", __FUNCTION__); + return; + } + if (onStart == nullptr) { + return; + } + int64_t timestamp; + found = msg->findInt64(kTimeStampKey, ×tamp); + if (!found) { + ALOGE("%s: Cannot find timestamp!", __FUNCTION__); + return; + } + ACaptureRequest* request = allocateACaptureRequest(requestSp); + (*onStart)(context, session.get(), request, timestamp); + freeACaptureRequest(request); + break; + } + case kWhatCaptureResult: + { + ACameraCaptureSession_captureCallback_result onResult; + found = msg->findPointer(kCallbackFpKey, (void**) &onResult); + if (!found) { + ALOGE("%s: Cannot find capture result callback!", __FUNCTION__); + return; + } + if (onResult == nullptr) { + return; + } + + found = msg->findObject(kCaptureResultKey, &obj); + if (!found) { + ALOGE("%s: Cannot find capture result!", __FUNCTION__); + return; + } + sp result(static_cast(obj.get())); + ACaptureRequest* request = allocateACaptureRequest(requestSp); + (*onResult)(context, session.get(), request, result.get()); + freeACaptureRequest(request); + break; + } + case kWhatCaptureFail: + { + ACameraCaptureSession_captureCallback_failed onFail; + found = msg->findPointer(kCallbackFpKey, (void**) &onFail); + if (!found) { + ALOGE("%s: Cannot find capture fail callback!", __FUNCTION__); + return; + } + if (onFail == nullptr) { + return; + } + + found = msg->findObject(kCaptureFailureKey, &obj); + if (!found) { + ALOGE("%s: Cannot find capture failure!", __FUNCTION__); + return; + } + sp failureSp( + static_cast(obj.get())); + ACameraCaptureFailure* failure = + static_cast(failureSp.get()); + ACaptureRequest* request = allocateACaptureRequest(requestSp); + (*onFail)(context, session.get(), request, failure); + freeACaptureRequest(request); + break; + } + case kWhatCaptureSeqEnd: + { + ACameraCaptureSession_captureCallback_sequenceEnd onSeqEnd; + found = msg->findPointer(kCallbackFpKey, (void**) &onSeqEnd); + if (!found) { + ALOGE("%s: Cannot find sequence end callback!", __FUNCTION__); + return; + } + if (onSeqEnd == nullptr) { + return; + } + int seqId; + found = msg->findInt32(kSequenceIdKey, &seqId); + if (!found) { + ALOGE("%s: Cannot find frame number!", __FUNCTION__); + return; + } + int64_t frameNumber; + found = msg->findInt64(kFrameNumberKey, &frameNumber); + if (!found) { + ALOGE("%s: Cannot find frame number!", __FUNCTION__); + return; + } + (*onSeqEnd)(context, session.get(), seqId, frameNumber); + break; + } + case kWhatCaptureSeqAbort: + { + ACameraCaptureSession_captureCallback_sequenceAbort onSeqAbort; + found = msg->findPointer(kCallbackFpKey, (void**) &onSeqAbort); + if (!found) { + ALOGE("%s: Cannot find sequence end callback!", __FUNCTION__); + return; + } + if (onSeqAbort == nullptr) { + return; + } + int seqId; + found = msg->findInt32(kSequenceIdKey, &seqId); + if (!found) { + ALOGE("%s: Cannot find frame number!", __FUNCTION__); + return; + } + (*onSeqAbort)(context, session.get(), seqId); + break; + } + case kWhatCaptureBufferLost: + { + ACameraCaptureSession_captureCallback_bufferLost onBufferLost; + found = msg->findPointer(kCallbackFpKey, (void**) &onBufferLost); + if (!found) { + ALOGE("%s: Cannot find buffer lost callback!", __FUNCTION__); + return; + } + if (onBufferLost == nullptr) { + return; + } + + native_handle_t* anw; + found = msg->findPointer(kAnwKey, (void**) &anw); + if (!found) { + ALOGE("%s: Cannot find native_handle_t!", __FUNCTION__); + return; + } + + int64_t frameNumber; + found = msg->findInt64(kFrameNumberKey, &frameNumber); + if (!found) { + ALOGE("%s: Cannot find frame number!", __FUNCTION__); + return; + } + + ACaptureRequest* request = allocateACaptureRequest(requestSp); + (*onBufferLost)(context, session.get(), request, anw, frameNumber); + freeACaptureRequest(request); + break; + } + } + break; + } + } +} + +CameraDevice::CallbackHolder::CallbackHolder( + sp session, + const Vector >& requests, + bool isRepeating, + ACameraCaptureSession_captureCallbacks* cbs) : + mSession(session), mRequests(requests), + mIsRepeating(isRepeating), mCallbacks(fillCb(cbs)) {} + +void +CameraDevice::checkRepeatingSequenceCompleteLocked( + const int sequenceId, const int64_t lastFrameNumber) { + ALOGV("Repeating seqId %d lastFrameNumer %" PRId64, sequenceId, lastFrameNumber); + if (lastFrameNumber == NO_FRAMES_CAPTURED) { + if (mSequenceCallbackMap.count(sequenceId) == 0) { + ALOGW("No callback found for sequenceId %d", sequenceId); + return; + } + // remove callback holder from callback map + auto cbIt = mSequenceCallbackMap.find(sequenceId); + CallbackHolder cbh = cbIt->second; + mSequenceCallbackMap.erase(cbIt); + // send seq aborted callback + sp msg = new AMessage(kWhatCaptureSeqAbort, mHandler); + msg->setPointer(kContextKey, cbh.mCallbacks.context); + msg->setObject(kSessionSpKey, cbh.mSession); + msg->setPointer(kCallbackFpKey, (void*) cbh.mCallbacks.onCaptureSequenceAborted); + msg->setInt32(kSequenceIdKey, sequenceId); + postSessionMsgAndCleanup(msg); + } else { + // Use mSequenceLastFrameNumberMap to track + mSequenceLastFrameNumberMap.insert(std::make_pair(sequenceId, lastFrameNumber)); + + // Last frame might have arrived. Check now + checkAndFireSequenceCompleteLocked(); + } +} + +void +CameraDevice::checkAndFireSequenceCompleteLocked() { + int64_t completedFrameNumber = mFrameNumberTracker.getCompletedFrameNumber(); + auto it = mSequenceLastFrameNumberMap.begin(); + while (it != mSequenceLastFrameNumberMap.end()) { + int sequenceId = it->first; + int64_t lastFrameNumber = it->second; + bool seqCompleted = false; + bool hasCallback = true; + + if (mRemote == nullptr) { + ALOGW("Camera %s closed while checking sequence complete", getId()); + return; + } + + // Check if there is callback for this sequence + // This should not happen because we always register callback (with nullptr inside) + if (mSequenceCallbackMap.count(sequenceId) == 0) { + ALOGW("No callback found for sequenceId %d", sequenceId); + hasCallback = false; + } + + if (lastFrameNumber <= completedFrameNumber) { + ALOGV("seq %d reached last frame %" PRId64 ", completed %" PRId64, + sequenceId, lastFrameNumber, completedFrameNumber); + seqCompleted = true; + } + + if (seqCompleted && hasCallback) { + // remove callback holder from callback map + auto cbIt = mSequenceCallbackMap.find(sequenceId); + CallbackHolder cbh = cbIt->second; + mSequenceCallbackMap.erase(cbIt); + // send seq complete callback + sp msg = new AMessage(kWhatCaptureSeqEnd, mHandler); + msg->setPointer(kContextKey, cbh.mCallbacks.context); + msg->setObject(kSessionSpKey, cbh.mSession); + msg->setPointer(kCallbackFpKey, (void*) cbh.mCallbacks.onCaptureSequenceCompleted); + msg->setInt32(kSequenceIdKey, sequenceId); + msg->setInt64(kFrameNumberKey, lastFrameNumber); + + // Clear the session sp before we send out the message + // This will guarantee the rare case where the message is processed + // before cbh goes out of scope and causing we call the session + // destructor while holding device lock + cbh.mSession.clear(); + postSessionMsgAndCleanup(msg); + } + + // No need to track sequence complete if there is no callback registered + if (seqCompleted || !hasCallback) { + it = mSequenceLastFrameNumberMap.erase(it); + } else { + ++it; + } + } +} + +/** + * Camera service callback implementation + */ +android::hardware::Return +CameraDevice::ServiceCallback::onDeviceError( + ErrorCode errorCode, + const CaptureResultExtras& resultExtras) { + ALOGD("Device error received, code %d, frame number %" PRId64 ", request ID %d, subseq ID %d", + errorCode, resultExtras.frameNumber, resultExtras.requestId, resultExtras.burstId); + auto ret = Void(); + sp dev = mDevice.promote(); + if (dev == nullptr) { + return ret; // device has been closed + } + + sp session = dev->mCurrentSession.promote(); + Mutex::Autolock _l(dev->mDeviceLock); + if (dev->mRemote == nullptr) { + return ret; // device has been closed + } + switch (errorCode) { + case ErrorCode::CAMERA_DISCONNECTED: + { + // Camera is disconnected, close the session and expect no more callbacks + if (session != nullptr) { + session->closeByDevice(); + } + dev->mCurrentSession = nullptr; + sp msg = new AMessage(kWhatOnDisconnected, dev->mHandler); + msg->setPointer(kContextKey, dev->mAppCallbacks.context); + msg->setPointer(kDeviceKey, (void*) dev->getWrapper()); + msg->setPointer(kCallbackFpKey, (void*) dev->mAppCallbacks.onDisconnected); + msg->post(); + break; + } + default: + ALOGE("Unknown error from camera device: %d", errorCode); + [[fallthrough]]; + case ErrorCode::CAMERA_DEVICE: + case ErrorCode::CAMERA_SERVICE: + { + int32_t errorVal = ::ERROR_CAMERA_DEVICE; + // We keep this switch since this block might be encountered with + // more than just 2 states. The default fallthrough could have us + // handling more unmatched error cases. + switch (errorCode) { + case ErrorCode::CAMERA_DEVICE: + dev->setCameraDeviceErrorLocked(ACAMERA_ERROR_CAMERA_DEVICE); + break; + case ErrorCode::CAMERA_SERVICE: + dev->setCameraDeviceErrorLocked(ACAMERA_ERROR_CAMERA_SERVICE); + errorVal = ::ERROR_CAMERA_SERVICE; + break; + default: + dev->setCameraDeviceErrorLocked(ACAMERA_ERROR_UNKNOWN); + break; + } + sp msg = new AMessage(kWhatOnError, dev->mHandler); + msg->setPointer(kContextKey, dev->mAppCallbacks.context); + msg->setPointer(kDeviceKey, (void*) dev->getWrapper()); + msg->setPointer(kCallbackFpKey, (void*) dev->mAppCallbacks.onError); + msg->setInt32(kErrorCodeKey, errorVal); + msg->post(); + break; + } + case ErrorCode::CAMERA_REQUEST: + case ErrorCode::CAMERA_RESULT: + case ErrorCode::CAMERA_BUFFER: + dev->onCaptureErrorLocked(errorCode, resultExtras); + break; + } + return ret; +} + +android::hardware::Return +CameraDevice::ServiceCallback::onDeviceIdle() { + ALOGV("Camera is now idle"); + auto ret = Void(); + sp dev = mDevice.promote(); + if (dev == nullptr) { + return ret; // device has been closed + } + + Mutex::Autolock _l(dev->mDeviceLock); + if (dev->isClosed() || dev->mRemote == nullptr) { + return ret; + } + + if (dev->mIdle) { + // Already in idle state. Possibly other thread did waitUntilIdle + return ret; + } + + if (dev->mCurrentSession != nullptr) { + ALOGE("onDeviceIdle sending state cb"); + if (dev->mBusySession != dev->mCurrentSession) { + ALOGE("Current session != busy session"); + dev->setCameraDeviceErrorLocked(ACAMERA_ERROR_CAMERA_DEVICE); + return ret; + } + + sp msg = new AMessage(kWhatSessionStateCb, dev->mHandler); + msg->setPointer(kContextKey, dev->mBusySession->mUserSessionCallback.context); + msg->setObject(kSessionSpKey, dev->mBusySession); + msg->setPointer(kCallbackFpKey, (void*) dev->mBusySession->mUserSessionCallback.onReady); + // Make sure we clear the sp first so the session destructor can + // only happen on handler thread (where we don't hold device/session lock) + dev->mBusySession.clear(); + dev->postSessionMsgAndCleanup(msg); + } + dev->mIdle = true; + dev->mFlushing = false; + return ret; +} + +android::hardware::Return +CameraDevice::ServiceCallback::onCaptureStarted( + const CaptureResultExtras& resultExtras, + uint64_t timestamp) { + auto ret = Void(); + + sp dev = mDevice.promote(); + if (dev == nullptr) { + return ret; // device has been closed + } + Mutex::Autolock _l(dev->mDeviceLock); + if (dev->isClosed() || dev->mRemote == nullptr) { + return ret; + } + + int32_t sequenceId = resultExtras.requestId; + int32_t burstId = resultExtras.burstId; + + auto it = dev->mSequenceCallbackMap.find(sequenceId); + if (it != dev->mSequenceCallbackMap.end()) { + CallbackHolder cbh = (*it).second; + ACameraCaptureSession_captureCallback_start onStart = cbh.mCallbacks.onCaptureStarted; + sp session = cbh.mSession; + if ((size_t) burstId >= cbh.mRequests.size()) { + ALOGE("%s: Error: request index %d out of bound (size %zu)", + __FUNCTION__, burstId, cbh.mRequests.size()); + dev->setCameraDeviceErrorLocked(ACAMERA_ERROR_CAMERA_SERVICE); + } + sp request = cbh.mRequests[burstId]; + sp msg = new AMessage(kWhatCaptureStart, dev->mHandler); + msg->setPointer(kContextKey, cbh.mCallbacks.context); + msg->setObject(kSessionSpKey, session); + msg->setPointer(kCallbackFpKey, (void*) onStart); + msg->setObject(kCaptureRequestKey, request); + msg->setInt64(kTimeStampKey, timestamp); + dev->postSessionMsgAndCleanup(msg); + } + return ret; +} + +android::hardware::Return +CameraDevice::ServiceCallback::onResultReceived( + const FmqSizeOrMetadata& resultMetadata, + const CaptureResultExtras& resultExtras, + const hidl_vec& physicalResultInfos) { + (void) physicalResultInfos; + auto ret = Void(); + + sp dev = mDevice.promote(); + if (dev == nullptr) { + return ret; // device has been closed + } + int32_t sequenceId = resultExtras.requestId; + int64_t frameNumber = resultExtras.frameNumber; + int32_t burstId = resultExtras.burstId; + bool isPartialResult = (resultExtras.partialResultCount < dev->mPartialResultCount); + + if (!isPartialResult) { + ALOGV("SeqId %d frame %" PRId64 " result arrive.", sequenceId, frameNumber); + } + + Mutex::Autolock _l(dev->mDeviceLock); + if (dev->mRemote == nullptr) { + return ret; // device has been disconnected + } + + if (dev->isClosed()) { + if (!isPartialResult) { + dev->mFrameNumberTracker.updateTracker(frameNumber, /*isError*/false); + } + // early return to avoid callback sent to closed devices + return ret; + } + + CameraMetadata metadataCopy; + HCameraMetadata hCameraMetadata; + bool converted = false; + if (resultMetadata.getDiscriminator() == + FmqSizeOrMetadata::hidl_discriminator::fmqMetadataSize) { + hCameraMetadata.resize(resultMetadata.fmqMetadataSize()); + bool read = dev->mCaptureResultMetadataQueue->read(hCameraMetadata.data(), + resultMetadata.fmqMetadataSize()); + if (!read) { + ALOGE("%s capture request settings could't be read from fmq", + __FUNCTION__); + return ret; + } + // TODO: Do we actually need to clone here ? + converted = utils::convertFromHidlCloned(hCameraMetadata, &metadataCopy); + + } else { + converted = utils::convertFromHidlCloned(resultMetadata.metadata(), &metadataCopy); + } + + if (!converted) { + ALOGE("%s result metadata couldn't be converted", __FUNCTION__); + return ret; + } + + metadataCopy.update(ANDROID_LENS_INFO_SHADING_MAP_SIZE, dev->mShadingMapSize, /*data_count*/2); + metadataCopy.update(ANDROID_SYNC_FRAME_NUMBER, &frameNumber, /*data_count*/1); + + auto it = dev->mSequenceCallbackMap.find(sequenceId); + if (it != dev->mSequenceCallbackMap.end()) { + CallbackHolder cbh = (*it).second; + ACameraCaptureSession_captureCallback_result onResult = isPartialResult ? + cbh.mCallbacks.onCaptureProgressed : + cbh.mCallbacks.onCaptureCompleted; + sp session = cbh.mSession; + if ((size_t) burstId >= cbh.mRequests.size()) { + ALOGE("%s: Error: request index %d out of bound (size %zu)", + __FUNCTION__, burstId, cbh.mRequests.size()); + dev->setCameraDeviceErrorLocked(ACAMERA_ERROR_CAMERA_SERVICE); + } + sp request = cbh.mRequests[burstId]; + sp result(new ACameraMetadata( + metadataCopy.release(), ACameraMetadata::ACM_RESULT)); + + sp msg = new AMessage(kWhatCaptureResult, dev->mHandler); + msg->setPointer(kContextKey, cbh.mCallbacks.context); + msg->setObject(kSessionSpKey, session); + msg->setPointer(kCallbackFpKey, (void*) onResult); + msg->setObject(kCaptureRequestKey, request); + msg->setObject(kCaptureResultKey, result); + dev->postSessionMsgAndCleanup(msg); + } + + if (!isPartialResult) { + dev->mFrameNumberTracker.updateTracker(frameNumber, /*isError*/false); + dev->checkAndFireSequenceCompleteLocked(); + } + + return ret; +} + +android::hardware::Return +CameraDevice::ServiceCallback::onRepeatingRequestError( + uint64_t lastFrameNumber, int32_t stoppedSequenceId) { + auto ret = Void(); + + sp dev = mDevice.promote(); + if (dev == nullptr) { + return ret; // device has been closed + } + + Mutex::Autolock _l(dev->mDeviceLock); + + int repeatingSequenceId = dev->mRepeatingSequenceId; + if (stoppedSequenceId == repeatingSequenceId) { + dev->mRepeatingSequenceId = REQUEST_ID_NONE; + } + + dev->checkRepeatingSequenceCompleteLocked(repeatingSequenceId, lastFrameNumber); + + return ret; +} + +} // namespace acam +} // namespace android diff --git a/camera/ndk/ndk_vendor/impl/ACameraDevice.h b/camera/ndk/ndk_vendor/impl/ACameraDevice.h new file mode 100644 index 0000000000..01a219f01a --- /dev/null +++ b/camera/ndk/ndk_vendor/impl/ACameraDevice.h @@ -0,0 +1,356 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef _ACAMERA_DEVICE_H +#define _ACAMERA_DEVICE_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include "ACameraMetadata.h" +#include "utils.h" + +namespace android { +namespace acam { + +using ICameraDeviceCallback = frameworks::cameraservice::device::V2_0::ICameraDeviceCallback; +using ICameraDeviceUser = frameworks::cameraservice::device::V2_0::ICameraDeviceUser; +using CaptureResultExtras = frameworks::cameraservice::device::V2_0::CaptureResultExtras; +using PhysicalCaptureResultInfo = frameworks::cameraservice::device::V2_0::PhysicalCaptureResultInfo; +using PhysicalCameraSettings = frameworks::cameraservice::device::V2_0::PhysicalCameraSettings; +using SubmitInfo = frameworks::cameraservice::device::V2_0::SubmitInfo; +using CaptureResultExtras = frameworks::cameraservice::device::V2_0::CaptureResultExtras; +using ErrorCode = frameworks::cameraservice::device::V2_0::ErrorCode; +using FmqSizeOrMetadata = frameworks::cameraservice::device::V2_0::FmqSizeOrMetadata; +using StreamConfigurationMode = frameworks::cameraservice::device::V2_0::StreamConfigurationMode; +using Status = frameworks::cameraservice::common::V2_0::Status; +using ResultMetadataQueue = hardware::MessageQueue; +using RequestMetadataQueue = hardware::MessageQueue; +using CameraStatusAndId = frameworks::cameraservice::service::V2_0::CameraStatusAndId; + +using hardware::hidl_vec; +using hardware::hidl_string; +using utils::native_handle_ptr_wrapper; +using utils::CaptureRequest; +using utils::OutputConfigurationWrapper; + +// Wrap ACameraCaptureFailure so it can be ref-counted +struct CameraCaptureFailure : public RefBase, public ACameraCaptureFailure { }; + +class CameraDevice final : public RefBase { + public: + CameraDevice(const char* id, ACameraDevice_StateCallbacks* cb, + sp chars, + ACameraDevice* wrapper); + ~CameraDevice(); + + inline const char* getId() const { return mCameraId.c_str(); } + + camera_status_t createCaptureRequest( + ACameraDevice_request_template templateId, + ACaptureRequest** request) const; + + camera_status_t createCaptureSession( + const ACaptureSessionOutputContainer* outputs, + const ACaptureRequest* sessionParameters, + const ACameraCaptureSession_stateCallbacks* callbacks, + /*out*/ACameraCaptureSession** session); + + // Callbacks from camera service + class ServiceCallback : public ICameraDeviceCallback { + public: + explicit ServiceCallback(CameraDevice* device) : mDevice(device) {} + android::hardware::Return onDeviceError(ErrorCode errorCode, + const CaptureResultExtras& resultExtras) override; + android::hardware::Return onDeviceIdle() override; + android::hardware::Return onCaptureStarted(const CaptureResultExtras& resultExtras, + uint64_t timestamp) override; + android::hardware::Return onResultReceived(const FmqSizeOrMetadata& result, + const CaptureResultExtras& resultExtras, + const hidl_vec& physicalResultInfos) override; + android::hardware::Return onRepeatingRequestError(uint64_t lastFrameNumber, + int32_t stoppedSequenceId) override; + private: + const wp mDevice; + }; + inline sp getServiceCallback() { + return mServiceCallback; + }; + + // Camera device is only functional after remote being set + void setRemoteDevice(sp remote); + + bool setDeviceMetadataQueues(); + inline ACameraDevice* getWrapper() const { return mWrapper; }; + + private: + friend ACameraCaptureSession; + + camera_status_t checkCameraClosedOrErrorLocked() const; + + // device goes into fatal error state after this + void setCameraDeviceErrorLocked(camera_status_t error); + + void disconnectLocked(sp& session); // disconnect from camera service + + camera_status_t stopRepeatingLocked(); + + camera_status_t flushLocked(ACameraCaptureSession*); + + camera_status_t waitUntilIdleLocked(); + + + camera_status_t captureLocked(sp session, + /*optional*/ACameraCaptureSession_captureCallbacks* cbs, + int numRequests, ACaptureRequest** requests, + /*optional*/int* captureSequenceId); + + camera_status_t setRepeatingRequestsLocked(sp session, + /*optional*/ACameraCaptureSession_captureCallbacks* cbs, + int numRequests, ACaptureRequest** requests, + /*optional*/int* captureSequenceId); + + camera_status_t submitRequestsLocked( + sp session, + /*optional*/ACameraCaptureSession_captureCallbacks* cbs, + int numRequests, ACaptureRequest** requests, + /*out*/int* captureSequenceId, + bool isRepeating); + + camera_status_t updateOutputConfigurationLocked(ACaptureSessionOutput *output); + + camera_status_t allocateCaptureRequest( + const ACaptureRequest* request, sp& outReq); + + static ACaptureRequest* allocateACaptureRequest(sp& req); + static void freeACaptureRequest(ACaptureRequest*); + + // only For session to hold device lock + // Always grab device lock before grabbing session lock + void lockDeviceForSessionOps() const { mDeviceLock.lock(); }; + void unlockDevice() const { mDeviceLock.unlock(); }; + + // For capture session to notify its end of life + void notifySessionEndOfLifeLocked(ACameraCaptureSession* session); + + camera_status_t configureStreamsLocked(const ACaptureSessionOutputContainer* outputs, + const ACaptureRequest* sessionParameters); + + // Input message will be posted and cleared after this returns + void postSessionMsgAndCleanup(sp& msg); + + mutable Mutex mDeviceLock; + const hidl_string mCameraId; // Camera ID + const ACameraDevice_StateCallbacks mAppCallbacks; // Callback to app + const sp mChars; // Camera characteristics + const sp mServiceCallback; + ACameraDevice* mWrapper; + + // stream id -> pair of (ACameraWindowType* from application, OutputConfiguration used for + // camera service) + std::map> mConfiguredOutputs; + + // TODO: maybe a bool will suffice for synchronous implementation? + std::atomic_bool mClosing; + inline bool isClosed() { return mClosing; } + + bool mInError = false; + camera_status_t mError = ACAMERA_OK; + void onCaptureErrorLocked( + ErrorCode errorCode, + const CaptureResultExtras& resultExtras); + + bool mIdle = true; + // This will avoid a busy session being deleted before it's back to idle state + sp mBusySession; + + sp mRemote; + + // Looper thread to handle callback to app + sp mCbLooper; + // definition of handler and message + enum { + // Device state callbacks + kWhatOnDisconnected, // onDisconnected + kWhatOnError, // onError + // Session state callbacks + kWhatSessionStateCb, // onReady, onActive + // Capture callbacks + kWhatCaptureStart, // onCaptureStarted + kWhatCaptureResult, // onCaptureProgressed, onCaptureCompleted + kWhatCaptureFail, // onCaptureFailed + kWhatCaptureSeqEnd, // onCaptureSequenceCompleted + kWhatCaptureSeqAbort, // onCaptureSequenceAborted + kWhatCaptureBufferLost,// onCaptureBufferLost + // Internal cleanup + kWhatCleanUpSessions // Cleanup cached sp + }; + static const char* kContextKey; + static const char* kDeviceKey; + static const char* kErrorCodeKey; + static const char* kCallbackFpKey; + static const char* kSessionSpKey; + static const char* kCaptureRequestKey; + static const char* kTimeStampKey; + static const char* kCaptureResultKey; + static const char* kCaptureFailureKey; + static const char* kSequenceIdKey; + static const char* kFrameNumberKey; + static const char* kAnwKey; + + class CallbackHandler : public AHandler { + public: + void onMessageReceived(const sp &msg) override; + + private: + // This handler will cache all capture session sp until kWhatCleanUpSessions + // is processed. This is used to guarantee the last session reference is always + // being removed in callback thread without holding camera device lock + Vector> mCachedSessions; + }; + sp mHandler; + + /*********************************** + * Capture session related members * + ***********************************/ + // The current active session + wp mCurrentSession; + bool mFlushing = false; + + int mNextSessionId = 0; + // TODO: might need another looper/handler to handle callbacks from service + + static const int REQUEST_ID_NONE = -1; + int mRepeatingSequenceId = REQUEST_ID_NONE; + + // sequence id -> last frame number map + std::map mSequenceLastFrameNumberMap; + + struct CallbackHolder { + CallbackHolder(sp session, + const Vector>& requests, + bool isRepeating, + ACameraCaptureSession_captureCallbacks* cbs); + + static ACameraCaptureSession_captureCallbacks fillCb( + ACameraCaptureSession_captureCallbacks* cbs) { + if (cbs != nullptr) { + return *cbs; + } + return { nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr }; + } + + sp mSession; + Vector> mRequests; + const bool mIsRepeating; + ACameraCaptureSession_captureCallbacks mCallbacks; + }; + // sequence id -> callbacks map + std::map mSequenceCallbackMap; + + static const int64_t NO_FRAMES_CAPTURED = -1; + class FrameNumberTracker { + public: + // TODO: Called in onResultReceived and onCaptureErrorLocked + void updateTracker(int64_t frameNumber, bool isError); + inline int64_t getCompletedFrameNumber() { return mCompletedFrameNumber; } + private: + void update(); + void updateCompletedFrameNumber(int64_t frameNumber); + + int64_t mCompletedFrameNumber = NO_FRAMES_CAPTURED; + List mSkippedFrameNumbers; + std::set mFutureErrorSet; + }; + FrameNumberTracker mFrameNumberTracker; + + void checkRepeatingSequenceCompleteLocked(const int sequenceId, const int64_t lastFrameNumber); + void checkAndFireSequenceCompleteLocked(); + + // Misc variables + int32_t mShadingMapSize[2]; // const after constructor + int32_t mPartialResultCount; // const after constructor + std::shared_ptr mCaptureRequestMetadataQueue = nullptr; + std::shared_ptr mCaptureResultMetadataQueue = nullptr; +}; + +} // namespace acam; +} // namespace android; + +/** + * ACameraDevice opaque struct definition + * Leave outside of android namespace because it's NDK struct + */ +struct ACameraDevice { + ACameraDevice(const char* id, ACameraDevice_StateCallbacks* cb, + sp chars) : + mDevice(new android::acam::CameraDevice(id, cb, std::move(chars), this)) {} + + ~ACameraDevice() {}; + + /******************* + * NDK public APIs * + *******************/ + inline const char* getId() const { return mDevice->getId(); } + + camera_status_t createCaptureRequest( + ACameraDevice_request_template templateId, + ACaptureRequest** request) const { + return mDevice->createCaptureRequest(templateId, request); + } + + camera_status_t createCaptureSession( + const ACaptureSessionOutputContainer* outputs, + const ACaptureRequest* sessionParameters, + const ACameraCaptureSession_stateCallbacks* callbacks, + /*out*/ACameraCaptureSession** session) { + return mDevice->createCaptureSession(outputs, sessionParameters, callbacks, session); + } + + /*********************** + * Device interal APIs * + ***********************/ + inline android::sp getServiceCallback() { + return mDevice->getServiceCallback(); + }; + + // Camera device is only functional after remote being set + inline void setRemoteDevice(android::sp remote) { + mDevice->setRemoteDevice(remote); + } + inline bool setDeviceMetadataQueues() { + return mDevice->setDeviceMetadataQueues(); + } + private: + android::sp mDevice; +}; + +#endif // _ACAMERA_DEVICE_H diff --git a/camera/ndk/ndk_vendor/impl/ACameraManager.cpp b/camera/ndk/ndk_vendor/impl/ACameraManager.cpp new file mode 100644 index 0000000000..f395b443ee --- /dev/null +++ b/camera/ndk/ndk_vendor/impl/ACameraManager.cpp @@ -0,0 +1,458 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//#define LOG_NDEBUG 0 +#define LOG_TAG "ACameraManagerVendor" + +#include +#include "ndk_vendor/impl/ACameraManager.h" +#include "ACameraMetadata.h" +#include "ndk_vendor/impl/ACameraDevice.h" +#include "utils.h" + +#include +#include +#include + +#include + +using namespace android::acam; + +namespace android { +namespace acam { + +using CameraStatusAndId = frameworks::cameraservice::service::V2_0::CameraStatusAndId; + +// Static member definitions +const char* CameraManagerGlobal::kCameraIdKey = "CameraId"; +const char* CameraManagerGlobal::kCallbackFpKey = "CallbackFp"; +const char* CameraManagerGlobal::kContextKey = "CallbackContext"; +Mutex CameraManagerGlobal::sLock; +CameraManagerGlobal* CameraManagerGlobal::sInstance = nullptr; + +CameraManagerGlobal& +CameraManagerGlobal::getInstance() { + Mutex::Autolock _l(sLock); + CameraManagerGlobal* instance = sInstance; + if (instance == nullptr) { + instance = new CameraManagerGlobal(); + sInstance = instance; + } + return *instance; +} + +CameraManagerGlobal::~CameraManagerGlobal() { + // clear sInstance so next getInstance call knows to create a new one + Mutex::Autolock _sl(sLock); + sInstance = nullptr; + Mutex::Autolock _l(mLock); + if (mCameraService != nullptr) { + mCameraService->unlinkToDeath(mDeathNotifier); + mCameraService->removeListener(mCameraServiceListener); + } + mDeathNotifier.clear(); + if (mCbLooper != nullptr) { + mCbLooper->unregisterHandler(mHandler->id()); + mCbLooper->stop(); + } + mCbLooper.clear(); + mHandler.clear(); + mCameraServiceListener.clear(); + mCameraService.clear(); +} + +static bool isCameraServiceDisabled() { + char value[PROPERTY_VALUE_MAX]; + property_get("config.disable_cameraservice", value, "0"); + return (strncmp(value, "0", 2) != 0 && strncasecmp(value, "false", 6) != 0); +} + +// TODO: Add back when vendor tags are supported for libcamera2ndk_vendor when +// the HIDL interface supports querying by vendor id. + +sp CameraManagerGlobal::getCameraService() { + Mutex::Autolock _l(mLock); + if (mCameraService.get() == nullptr) { + if (isCameraServiceDisabled()) { + return mCameraService; + } + + sp cameraServiceBinder; + do { + cameraServiceBinder = ICameraService::getService(); + if (cameraServiceBinder != nullptr) { + break; + } + ALOGW("CameraService not published, waiting..."); + usleep(kCameraServicePollDelay); + } while(true); + if (mDeathNotifier == nullptr) { + mDeathNotifier = new DeathNotifier(this); + } + cameraServiceBinder->linkToDeath(mDeathNotifier, 0); + mCameraService = cameraServiceBinder; + + // Setup looper thread to perfrom availiability callbacks + if (mCbLooper == nullptr) { + mCbLooper = new ALooper; + mCbLooper->setName("C2N-mgr-looper"); + status_t err = mCbLooper->start( + /*runOnCallingThread*/false, + /*canCallJava*/ true, + PRIORITY_DEFAULT); + if (err != OK) { + ALOGE("%s: Unable to start camera service listener looper: %s (%d)", + __FUNCTION__, strerror(-err), err); + mCbLooper.clear(); + return nullptr; + } + if (mHandler == nullptr) { + mHandler = new CallbackHandler(); + } + mCbLooper->registerHandler(mHandler); + } + + // register ICameraServiceListener + if (mCameraServiceListener == nullptr) { + mCameraServiceListener = new CameraServiceListener(this); + } + hidl_vec cameraStatuses{}; + Status status = Status::NO_ERROR; + auto remoteRet = mCameraService->addListener(mCameraServiceListener, + [&status, &cameraStatuses](Status s, + auto &retStatuses) { + status = s; + cameraStatuses = retStatuses; + }); + if (!remoteRet.isOk() || status != Status::NO_ERROR) { + ALOGE("Failed to add listener to camera service %s", remoteRet.description().c_str()); + } + for (auto& c : cameraStatuses) { + onStatusChangedLocked(c); + } + } + return mCameraService; +} + +void CameraManagerGlobal::DeathNotifier::serviceDied(uint64_t cookie, const wp &who) { + (void) cookie; + (void) who; + ALOGE("Camera service binderDied!"); + sp cm = mCameraManager.promote(); + if (cm != nullptr) { + AutoMutex lock(cm->mLock); + for (auto& pair : cm->mDeviceStatusMap) { + CameraStatusAndId cameraStatusAndId; + cameraStatusAndId.cameraId = pair.first; + cameraStatusAndId.deviceStatus = pair.second; + cm->onStatusChangedLocked(cameraStatusAndId); + } + cm->mCameraService.clear(); + // TODO: consider adding re-connect call here? + } +} + +void CameraManagerGlobal::registerAvailabilityCallback( + const ACameraManager_AvailabilityCallbacks *callback) { + Mutex::Autolock _l(mLock); + Callback cb(callback); + auto pair = mCallbacks.insert(cb); + // Send initial callbacks if callback is newly registered + if (pair.second) { + for (auto& pair : mDeviceStatusMap) { + const hidl_string& cameraId = pair.first; + CameraDeviceStatus status = pair.second; + + sp msg = new AMessage(kWhatSendSingleCallback, mHandler); + ACameraManager_AvailabilityCallback cb = isStatusAvailable(status) ? + callback->onCameraAvailable : callback->onCameraUnavailable; + msg->setPointer(kCallbackFpKey, (void *) cb); + msg->setPointer(kContextKey, callback->context); + msg->setString(kCameraIdKey, AString(cameraId.c_str())); + msg->post(); + } + } +} + +void CameraManagerGlobal::unregisterAvailabilityCallback( + const ACameraManager_AvailabilityCallbacks *callback) { + Mutex::Autolock _l(mLock); + Callback cb(callback); + mCallbacks.erase(cb); +} + +void CameraManagerGlobal::getCameraIdList(std::vector* cameraIds) { + // Ensure that we have initialized/refreshed the list of available devices + auto cs = getCameraService(); + Mutex::Autolock _l(mLock); + + for(auto& deviceStatus : mDeviceStatusMap) { + if (deviceStatus.second == CameraDeviceStatus::STATUS_NOT_PRESENT || + deviceStatus.second == CameraDeviceStatus::STATUS_ENUMERATING) { + continue; + } + cameraIds->push_back(deviceStatus.first); + } +} + +bool CameraManagerGlobal::validStatus(CameraDeviceStatus status) { + switch (status) { + case CameraDeviceStatus::STATUS_NOT_PRESENT: + case CameraDeviceStatus::STATUS_PRESENT: + case CameraDeviceStatus::STATUS_ENUMERATING: + case CameraDeviceStatus::STATUS_NOT_AVAILABLE: + return true; + default: + return false; + } +} + +bool CameraManagerGlobal::isStatusAvailable(CameraDeviceStatus status) { + switch (status) { + case CameraDeviceStatus::STATUS_PRESENT: + return true; + default: + return false; + } +} + +void CameraManagerGlobal::CallbackHandler::onMessageReceived( + const sp &msg) { + switch (msg->what()) { + case kWhatSendSingleCallback: + { + ACameraManager_AvailabilityCallback cb; + void* context; + AString cameraId; + bool found = msg->findPointer(kCallbackFpKey, (void**) &cb); + if (!found) { + ALOGE("%s: Cannot find camera callback fp!", __FUNCTION__); + return; + } + found = msg->findPointer(kContextKey, &context); + if (!found) { + ALOGE("%s: Cannot find callback context!", __FUNCTION__); + return; + } + found = msg->findString(kCameraIdKey, &cameraId); + if (!found) { + ALOGE("%s: Cannot find camera ID!", __FUNCTION__); + return; + } + (*cb)(context, cameraId.c_str()); + break; + } + default: + ALOGE("%s: unknown message type %d", __FUNCTION__, msg->what()); + break; + } +} + +hardware::Return CameraManagerGlobal::CameraServiceListener::onStatusChanged( + const CameraStatusAndId &statusAndId) { + sp cm = mCameraManager.promote(); + if (cm != nullptr) { + cm->onStatusChanged(statusAndId); + } else { + ALOGE("Cannot deliver status change. Global camera manager died"); + } + return Void(); +} + +void CameraManagerGlobal::onStatusChanged( + const CameraStatusAndId &statusAndId) { + Mutex::Autolock _l(mLock); + onStatusChangedLocked(statusAndId); +} + +void CameraManagerGlobal::onStatusChangedLocked( + const CameraStatusAndId &statusAndId) { + hidl_string cameraId = statusAndId.cameraId; + CameraDeviceStatus status = statusAndId.deviceStatus; + if (!validStatus(status)) { + ALOGE("%s: Invalid status %d", __FUNCTION__, status); + return; + } + + bool firstStatus = (mDeviceStatusMap.count(cameraId) == 0); + CameraDeviceStatus oldStatus = firstStatus ? + status : // first status + mDeviceStatusMap[cameraId]; + + if (!firstStatus && + isStatusAvailable(status) == isStatusAvailable(oldStatus)) { + // No status update. No need to send callback + return; + } + + // Iterate through all registered callbacks + mDeviceStatusMap[cameraId] = status; + for (auto cb : mCallbacks) { + sp msg = new AMessage(kWhatSendSingleCallback, mHandler); + ACameraManager_AvailabilityCallback cbFp = isStatusAvailable(status) ? + cb.mAvailable : cb.mUnavailable; + msg->setPointer(kCallbackFpKey, (void *) cbFp); + msg->setPointer(kContextKey, cb.mContext); + msg->setString(kCameraIdKey, AString(cameraId.c_str())); + msg->post(); + } + if (status == CameraDeviceStatus::STATUS_NOT_PRESENT) { + mDeviceStatusMap.erase(cameraId); + } +} + +} // namespace acam +} // namespace android + +/** + * ACameraManger Implementation + */ +camera_status_t +ACameraManager::getCameraIdList(ACameraIdList** cameraIdList) { + Mutex::Autolock _l(mLock); + + std::vector idList; + CameraManagerGlobal::getInstance().getCameraIdList(&idList); + + int numCameras = idList.size(); + ACameraIdList *out = new ACameraIdList; + if (!out) { + ALOGE("Allocate memory for ACameraIdList failed!"); + return ACAMERA_ERROR_NOT_ENOUGH_MEMORY; + } + out->numCameras = numCameras; + out->cameraIds = new const char*[numCameras]; + if (!out->cameraIds) { + ALOGE("Allocate memory for ACameraIdList failed!"); + deleteCameraIdList(out); + return ACAMERA_ERROR_NOT_ENOUGH_MEMORY; + } + for (int i = 0; i < numCameras; i++) { + const char* src = idList[i].c_str(); + size_t dstSize = strlen(src) + 1; + char* dst = new char[dstSize]; + if (!dst) { + ALOGE("Allocate memory for ACameraIdList failed!"); + deleteCameraIdList(out); + return ACAMERA_ERROR_NOT_ENOUGH_MEMORY; + } + strlcpy(dst, src, dstSize); + out->cameraIds[i] = dst; + } + *cameraIdList = out; + return ACAMERA_OK; +} + +void +ACameraManager::deleteCameraIdList(ACameraIdList* cameraIdList) { + if (cameraIdList != nullptr) { + if (cameraIdList->cameraIds != nullptr) { + for (int i = 0; i < cameraIdList->numCameras; i ++) { + if (cameraIdList->cameraIds[i] != nullptr) { + delete[] cameraIdList->cameraIds[i]; + } + } + delete[] cameraIdList->cameraIds; + } + delete cameraIdList; + } +} + +camera_status_t ACameraManager::getCameraCharacteristics( + const char *cameraIdStr, sp *characteristics) { + Mutex::Autolock _l(mLock); + + sp cs = CameraManagerGlobal::getInstance().getCameraService(); + if (cs == nullptr) { + ALOGE("%s: Cannot reach camera service!", __FUNCTION__); + return ACAMERA_ERROR_CAMERA_DISCONNECTED; + } + CameraMetadata rawMetadata; + Status status = Status::NO_ERROR; + auto serviceRet = + cs->getCameraCharacteristics(cameraIdStr, + [&status, &rawMetadata] (auto s , + const hidl_vec &metadata) { + status = s; + if (status == Status::NO_ERROR) { + utils::convertFromHidlCloned(metadata, &rawMetadata); + } + }); + if (!serviceRet.isOk() || status != Status::NO_ERROR) { + ALOGE("Get camera characteristics from camera service failed"); + return ACAMERA_ERROR_UNKNOWN; // should not reach here + } + + *characteristics = new ACameraMetadata( + rawMetadata.release(), ACameraMetadata::ACM_CHARACTERISTICS); + return ACAMERA_OK; +} + +camera_status_t +ACameraManager::openCamera( + const char* cameraId, + ACameraDevice_StateCallbacks* callback, + /*out*/ACameraDevice** outDevice) { + sp rawChars; + camera_status_t ret = getCameraCharacteristics(cameraId, &rawChars); + Mutex::Autolock _l(mLock); + if (ret != ACAMERA_OK) { + ALOGE("%s: cannot get camera characteristics for camera %s. err %d", + __FUNCTION__, cameraId, ret); + return ACAMERA_ERROR_INVALID_PARAMETER; + } + + ACameraDevice* device = new ACameraDevice(cameraId, callback, std::move(rawChars)); + + sp cs = CameraManagerGlobal::getInstance().getCameraService(); + if (cs == nullptr) { + ALOGE("%s: Cannot reach camera service!", __FUNCTION__); + delete device; + return ACAMERA_ERROR_CAMERA_DISCONNECTED; + } + + sp callbacks = device->getServiceCallback(); + sp deviceRemote; + + // No way to get package name from native. + // Send a zero length package name and let camera service figure it out from UID + Status status = Status::NO_ERROR; + auto serviceRet = cs->connectDevice( + callbacks, cameraId, [&status, &deviceRemote](auto s, auto &device) { + status = s; + deviceRemote = device; + }); + + if (!serviceRet.isOk() || status != Status::NO_ERROR) { + ALOGE("%s: connect camera device failed", __FUNCTION__); + // TODO: Convert serviceRet to camera_status_t + delete device; + return ACAMERA_ERROR_UNKNOWN; + } + if (deviceRemote == nullptr) { + ALOGE("%s: connect camera device failed! remote device is null", __FUNCTION__); + delete device; + return ACAMERA_ERROR_CAMERA_DISCONNECTED; + } + device->setRemoteDevice(deviceRemote); + device->setDeviceMetadataQueues(); + *outDevice = device; + return ACAMERA_OK; +} + +ACameraManager::~ACameraManager() { + +} diff --git a/camera/ndk/ndk_vendor/impl/ACameraManager.h b/camera/ndk/ndk_vendor/impl/ACameraManager.h new file mode 100644 index 0000000000..c8d640ffe7 --- /dev/null +++ b/camera/ndk/ndk_vendor/impl/ACameraManager.h @@ -0,0 +1,209 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _ACAMERA_MANAGER_H +#define _ACAMERA_MANAGER_H + +#include + +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include +#include + +namespace android { +namespace acam { + +using ICameraService = frameworks::cameraservice::service::V2_0::ICameraService; +using CameraDeviceStatus = frameworks::cameraservice::service::V2_0::CameraDeviceStatus; +using ICameraServiceListener = frameworks::cameraservice::service::V2_0::ICameraServiceListener; +using CameraStatusAndId = frameworks::cameraservice::service::V2_0::CameraStatusAndId; +using Status = frameworks::cameraservice::common::V2_0::Status; +using VendorTagSection = frameworks::cameraservice::common::V2_0::VendorTagSection; +using VendorTag = frameworks::cameraservice::common::V2_0::VendorTag; +using IBase = android::hidl::base::V1_0::IBase; +using android::hardware::hidl_string; +using hardware::Void; + +/** + * Per-process singleton instance of CameraManger. Shared by all ACameraManager + * instances. Created when first ACameraManager is created and destroyed when + * all ACameraManager instances are deleted. + * + * TODO: maybe CameraManagerGlobal is better suited in libcameraclient? + */ +class CameraManagerGlobal final : public RefBase { + public: + static CameraManagerGlobal& getInstance(); + sp getCameraService(); + + void registerAvailabilityCallback( + const ACameraManager_AvailabilityCallbacks *callback); + void unregisterAvailabilityCallback( + const ACameraManager_AvailabilityCallbacks *callback); + + /** + * Return camera IDs that support camera2 + */ + void getCameraIdList(std::vector *cameraIds); + + private: + sp mCameraService; + const int kCameraServicePollDelay = 500000; // 0.5s + Mutex mLock; + class DeathNotifier : public android::hardware::hidl_death_recipient { + public: + explicit DeathNotifier(CameraManagerGlobal* cm) : mCameraManager(cm) {} + protected: + // IBinder::DeathRecipient implementation + virtual void serviceDied(uint64_t cookie, const wp &who); + private: + const wp mCameraManager; + }; + sp mDeathNotifier; + + class CameraServiceListener final : public ICameraServiceListener { + public: + explicit CameraServiceListener(CameraManagerGlobal* cm) : mCameraManager(cm) {} + android::hardware::Return onStatusChanged( + const CameraStatusAndId &statusAndId) override; + + private: + const wp mCameraManager; + }; + sp mCameraServiceListener; + + // Wrapper of ACameraManager_AvailabilityCallbacks so we can store it in std::set + struct Callback { + explicit Callback(const ACameraManager_AvailabilityCallbacks *callback) : + mAvailable(callback->onCameraAvailable), + mUnavailable(callback->onCameraUnavailable), + mContext(callback->context) {} + + bool operator == (const Callback& other) const { + return (mAvailable == other.mAvailable && + mUnavailable == other.mUnavailable && + mContext == other.mContext); + } + bool operator != (const Callback& other) const { + return !(*this == other); + } + bool operator < (const Callback& other) const { + if (*this == other) return false; + if (mContext != other.mContext) return mContext < other.mContext; + if (mAvailable != other.mAvailable) return mAvailable < other.mAvailable; + return mUnavailable < other.mUnavailable; + } + bool operator > (const Callback& other) const { + return (*this != other && !(*this < other)); + } + ACameraManager_AvailabilityCallback mAvailable; + ACameraManager_AvailabilityCallback mUnavailable; + void* mContext; + }; + std::set mCallbacks; + + // definition of handler and message + enum { + kWhatSendSingleCallback + }; + static const char* kCameraIdKey; + static const char* kCallbackFpKey; + static const char* kContextKey; + class CallbackHandler : public AHandler { + public: + CallbackHandler() {} + void onMessageReceived(const sp &msg) override; + }; + sp mHandler; + sp mCbLooper; // Looper thread where callbacks actually happen on + + void onStatusChanged(const CameraStatusAndId &statusAndId); + void onStatusChangedLocked(const CameraStatusAndId &statusAndId); + // Utils for status + static bool validStatus(CameraDeviceStatus status); + static bool isStatusAvailable(CameraDeviceStatus status); + + // The sort logic must match the logic in + // libcameraservice/common/CameraProviderManager.cpp::getAPI1CompatibleCameraDeviceIds + struct CameraIdComparator { + bool operator()(const hidl_string& a, const hidl_string& b) const { + uint32_t aUint = 0, bUint = 0; + bool aIsUint = base::ParseUint(a.c_str(), &aUint); + bool bIsUint = base::ParseUint(b.c_str(), &bUint); + + // Uint device IDs first + if (aIsUint && bIsUint) { + return aUint < bUint; + } else if (aIsUint) { + return true; + } else if (bIsUint) { + return false; + } + // Simple string compare if both id are not uint + return a < b; + } + }; + + // Map camera_id -> status + std::map mDeviceStatusMap; + + // For the singleton instance + static Mutex sLock; + static CameraManagerGlobal* sInstance; + CameraManagerGlobal() {}; + ~CameraManagerGlobal(); +}; + +} // namespace acam; +} // namespace android; + +/** + * ACameraManager opaque struct definition + * Leave outside of android namespace because it's NDK struct + */ +struct ACameraManager { + ACameraManager() : + mGlobalManager(&(android::acam::CameraManagerGlobal::getInstance())) {} + ~ACameraManager(); + camera_status_t getCameraIdList(ACameraIdList** cameraIdList); + static void deleteCameraIdList(ACameraIdList* cameraIdList); + + camera_status_t getCameraCharacteristics( + const char* cameraId, android::sp* characteristics); + + camera_status_t openCamera(const char* cameraId, + ACameraDevice_StateCallbacks* callback, + /*out*/ACameraDevice** device); + + private: + enum { + kCameraIdListNotInit = -1 + }; + android::Mutex mLock; + android::sp mGlobalManager; +}; + +#endif //_ACAMERA_MANAGER_H diff --git a/camera/ndk/ndk_vendor/impl/ACaptureRequestVendor.h b/camera/ndk/ndk_vendor/impl/ACaptureRequestVendor.h new file mode 100644 index 0000000000..ed67615186 --- /dev/null +++ b/camera/ndk/ndk_vendor/impl/ACaptureRequestVendor.h @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "utils.h" + +struct ACameraOutputTarget { + explicit ACameraOutputTarget(native_handle_t* window) : mWindow(window) {}; + + bool operator == (const ACameraOutputTarget& other) const { + return mWindow == other.mWindow; + } + bool operator != (const ACameraOutputTarget& other) const { + return mWindow != other.mWindow; + } + bool operator < (const ACameraOutputTarget& other) const { + return mWindow < other.mWindow; + } + bool operator > (const ACameraOutputTarget& other) const { + return mWindow > other.mWindow; + } + + android::acam::utils::native_handle_ptr_wrapper mWindow; +}; diff --git a/camera/ndk/ndk_vendor/impl/utils.cpp b/camera/ndk/ndk_vendor/impl/utils.cpp new file mode 100644 index 0000000000..7193006799 --- /dev/null +++ b/camera/ndk/ndk_vendor/impl/utils.cpp @@ -0,0 +1,197 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "ACameraVendorUtils" + +#include + +#include "utils.h" + +namespace android { +namespace acam { +namespace utils { + +// Convert CaptureRequest wrappable by sp<> to hidl CaptureRequest. +frameworks::cameraservice::device::V2_0::CaptureRequest +convertToHidl(const CaptureRequest *captureRequest) { + frameworks::cameraservice::device::V2_0::CaptureRequest hCaptureRequest; + hCaptureRequest.physicalCameraSettings = captureRequest->mCaptureRequest.physicalCameraSettings; + hCaptureRequest.streamAndWindowIds = captureRequest->mCaptureRequest.streamAndWindowIds; + return hCaptureRequest; +} + +HRotation convertToHidl(int rotation) { + HRotation hRotation = HRotation::R0; + switch(rotation) { + case CAMERA3_STREAM_ROTATION_90: + hRotation = HRotation::R90; + break; + case CAMERA3_STREAM_ROTATION_180: + hRotation = HRotation::R180; + break; + case CAMERA3_STREAM_ROTATION_270: + hRotation = HRotation::R270; + break; + default: + break; + } + return hRotation; +} + +bool convertFromHidlCloned(const HCameraMetadata &metadata, CameraMetadata *rawMetadata) { + const camera_metadata *buffer = (camera_metadata_t*)(metadata.data()); + size_t expectedSize = metadata.size(); + int ret = validate_camera_metadata_structure(buffer, &expectedSize); + if (ret == OK || ret == CAMERA_METADATA_VALIDATION_SHIFTED) { + *rawMetadata = buffer; + } else { + ALOGE("%s: Malformed camera metadata received from caller", __FUNCTION__); + return false; + } + return true; +} + +// Note: existing data in dst will be gone. Caller still owns the memory of src +void convertToHidl(const camera_metadata_t *src, HCameraMetadata* dst) { + if (src == nullptr) { + return; + } + size_t size = get_camera_metadata_size(src); + ALOGE("Converting metadata size: %d", (int)size); + dst->setToExternal((uint8_t *) src, size); + return; +} + +TemplateId convertToHidl(ACameraDevice_request_template templateId) { + switch(templateId) { + case TEMPLATE_STILL_CAPTURE: + return TemplateId::STILL_CAPTURE; + case TEMPLATE_RECORD: + return TemplateId::RECORD; + case TEMPLATE_VIDEO_SNAPSHOT: + return TemplateId::VIDEO_SNAPSHOT; + case TEMPLATE_ZERO_SHUTTER_LAG: + return TemplateId::ZERO_SHUTTER_LAG; + case TEMPLATE_MANUAL: + return TemplateId::MANUAL; + default: + return TemplateId::PREVIEW; + } +} + +camera_status_t convertFromHidl(Status status) { + camera_status_t ret = ACAMERA_OK; + switch(status) { + case Status::NO_ERROR: + break; + case Status::DISCONNECTED: + ret = ACAMERA_ERROR_CAMERA_DISCONNECTED; + break; + case Status::CAMERA_IN_USE: + ret = ACAMERA_ERROR_CAMERA_IN_USE; + break; + case Status::MAX_CAMERAS_IN_USE: + ret = ACAMERA_ERROR_MAX_CAMERA_IN_USE; + break; + case Status::ILLEGAL_ARGUMENT: + ret = ACAMERA_ERROR_INVALID_PARAMETER; + break; + case Status::DEPRECATED_HAL: + // Should not reach here since we filtered legacy HALs earlier + ret = ACAMERA_ERROR_INVALID_PARAMETER; + break; + case Status::DISABLED: + ret = ACAMERA_ERROR_CAMERA_DISABLED; + break; + case Status::PERMISSION_DENIED: + ret = ACAMERA_ERROR_PERMISSION_DENIED; + break; + case Status::INVALID_OPERATION: + ret = ACAMERA_ERROR_INVALID_OPERATION; + break; + default: + ret = ACAMERA_ERROR_UNKNOWN; + break; + } + return ret; +} + +bool isWindowNativeHandleEqual(const native_handle_t *nh1, const native_handle_t *nh2) { + if (nh1->numFds !=0 || nh2->numFds !=0) { + ALOGE("Invalid window native handles being compared"); + return false; + } + if (nh1->version != nh2->version || nh1->numFds != nh2->numFds || + nh1->numInts != nh2->numInts) { + return false; + } + for (int i = 0; i < nh1->numInts; i++) { + if(nh1->data[i] != nh2->data[i]) { + return false; + } + } + return true; +} + +bool isWindowNativeHandleLessThan(const native_handle_t *nh1, const native_handle_t *nh2) { + if (isWindowNativeHandleEqual(nh1, nh2)) { + return false; + } + if (nh1->numInts != nh2->numInts) { + return nh1->numInts < nh2->numInts; + } + + for (int i = 0; i < nh1->numInts; i++) { + if (nh1->data[i] != nh2->data[i]) { + return nh1->data[i] < nh2->data[i]; + } + } + return false; +} + +bool isWindowNativeHandleGreaterThan(const native_handle_t *nh1, const native_handle_t *nh2) { + return !isWindowNativeHandleLessThan(nh1, nh2) && !isWindowNativeHandleEqual(nh1, nh2); +} + +bool areWindowNativeHandlesEqual(hidl_vec handles1, hidl_vec handles2) { + if (handles1.size() != handles2.size()) { + return false; + } + for (int i = 0; i < handles1.size(); i++) { + if (!isWindowNativeHandleEqual(handles1[i], handles2[i])) { + return false; + } + } + return true; +} + +bool areWindowNativeHandlesLessThan(hidl_vec handles1, hidl_vechandles2) { + if (handles1.size() != handles2.size()) { + return handles1.size() < handles2.size(); + } + for (int i = 0; i < handles1.size(); i++) { + const native_handle_t *handle1 = handles1[i].getNativeHandle(); + const native_handle_t *handle2 = handles2[i].getNativeHandle(); + if (!isWindowNativeHandleEqual(handle1, handle2)) { + return isWindowNativeHandleLessThan(handle1, handle2); + } + } + return false; +} + +} // namespace utils +} // namespace acam +} // namespace android diff --git a/camera/ndk/ndk_vendor/impl/utils.h b/camera/ndk/ndk_vendor/impl/utils.h new file mode 100644 index 0000000000..2f1006d0eb --- /dev/null +++ b/camera/ndk/ndk_vendor/impl/utils.h @@ -0,0 +1,174 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include + +#ifndef CAMERA_NDK_VENDOR_H +#define CAMERA_NDK_VENDOR_H + +using android::hardware::hidl_vec; +using android::hardware::hidl_handle; + +namespace android { +namespace acam { +namespace utils { + +using CameraMetadata = hardware::camera::common::V1_0::helper::CameraMetadata; +using HCameraMetadata = frameworks::cameraservice::service::V2_0::CameraMetadata; +using Status = frameworks::cameraservice::common::V2_0::Status; +using TemplateId = frameworks::cameraservice::device::V2_0::TemplateId; +using PhysicalCameraSettings = frameworks::cameraservice::device::V2_0::PhysicalCameraSettings; +using HRotation = frameworks::cameraservice::device::V2_0::OutputConfiguration::Rotation; +using OutputConfiguration = frameworks::cameraservice::device::V2_0::OutputConfiguration; + +// Utility class so that CaptureRequest can be stored by sp<> +struct CaptureRequest : public RefBase { + frameworks::cameraservice::device::V2_0::CaptureRequest mCaptureRequest; + std::vector mSurfaceList; + //Physical camera settings metadata is stored here, since the capture request + //might not contain it. That's since, fmq might have consumed it. + hidl_vec mPhysicalCameraSettings; +}; + +bool areWindowNativeHandlesEqual(hidl_vec handles1, hidl_vechandles2); + +bool areWindowNativeHandlesLessThan(hidl_vec handles1, hidl_vechandles2); + +bool isWindowNativeHandleEqual(const native_handle_t *nh1, const native_handle_t *nh2); + +bool isWindowNativeHandleLessThan(const native_handle_t *nh1, const native_handle_t *nh2); + +// Convenience wrapper over isWindowNativeHandleLessThan and isWindowNativeHandleEqual +bool isWindowNativeHandleGreaterThan(const native_handle_t *nh1, const native_handle_t *nh2); + +// Utility class so the native_handle_t can be compared with its contents instead +// of just raw pointer comparisons. +struct native_handle_ptr_wrapper { + native_handle_t *mWindow = nullptr; + + native_handle_ptr_wrapper(native_handle_t *nh) : mWindow(nh) { } + + native_handle_ptr_wrapper() = default; + + operator native_handle_t *() const { return mWindow; } + + bool operator ==(const native_handle_ptr_wrapper other) const { + return isWindowNativeHandleEqual(mWindow, other.mWindow); + } + + bool operator != (const native_handle_ptr_wrapper& other) const { + return !isWindowNativeHandleEqual(mWindow, other.mWindow); + } + + bool operator < (const native_handle_ptr_wrapper& other) const { + return isWindowNativeHandleLessThan(mWindow, other.mWindow); + } + + bool operator > (const native_handle_ptr_wrapper& other) const { + return !isWindowNativeHandleGreaterThan(mWindow, other.mWindow); + } + +}; + +// Wrapper around OutputConfiguration. This is needed since HIDL +// OutputConfiguration is auto-generated and marked final. Therefore, operator +// overloads outside the class, will not get picked by clang while trying to +// store OutputConfiguration in maps/sets. +struct OutputConfigurationWrapper { + OutputConfiguration mOutputConfiguration; + + operator const OutputConfiguration &() const { + return mOutputConfiguration; + } + + OutputConfigurationWrapper() = default; + + OutputConfigurationWrapper(OutputConfiguration &outputConfiguration) + : mOutputConfiguration((outputConfiguration)) { } + + bool operator ==(const OutputConfiguration &other) const { + const OutputConfiguration &self = mOutputConfiguration; + return self.rotation == other.rotation && self.windowGroupId == other.windowGroupId && + self.physicalCameraId == other.physicalCameraId && self.width == other.width && + self.height == other.height && self.isDeferred == other.isDeferred && + areWindowNativeHandlesEqual(self.windowHandles, other.windowHandles); + } + + bool operator < (const OutputConfiguration &other) const { + if (*this == other) { + return false; + } + const OutputConfiguration &self = mOutputConfiguration; + if (self.windowGroupId != other.windowGroupId) { + return self.windowGroupId < other.windowGroupId; + } + + if (self.width != other.width) { + return self.width < other.width; + } + + if (self.height != other.height) { + return self.height < other.height; + } + + if (self.rotation != other.rotation) { + return static_cast(self.rotation) < static_cast(other.rotation); + } + + if (self.isDeferred != other.isDeferred) { + return self.isDeferred < other.isDeferred; + } + + if (self.physicalCameraId != other.physicalCameraId) { + return self.physicalCameraId < other.physicalCameraId; + } + return areWindowNativeHandlesLessThan(self.windowHandles, other.windowHandles); + } + + bool operator != (const OutputConfiguration &other) const { + return !(*this == other); + } + + bool operator > (const OutputConfiguration &other) const { + return (*this != other) && !(*this < other); + } +}; + +// Convert CaptureRequest wrappable by sp<> to hidl CaptureRequest. +frameworks::cameraservice::device::V2_0::CaptureRequest convertToHidl( + const CaptureRequest *captureRequest); + +HRotation convertToHidl(int rotation); + +bool convertFromHidlCloned(const HCameraMetadata &metadata, CameraMetadata *rawMetadata); + +// Note: existing data in dst will be gone. Caller still owns the memory of src +void convertToHidl(const camera_metadata_t *src, HCameraMetadata* dst); + +TemplateId convertToHidl(ACameraDevice_request_template templateId); + +camera_status_t convertFromHidl(Status status); + +} // namespace utils +} // namespace acam +} // namespace android + +#endif // CAMERA_NDK_VENDOR_H diff --git a/camera/ndk/ndk_vendor/tests/AImageReaderVendorTest.cpp b/camera/ndk/ndk_vendor/tests/AImageReaderVendorTest.cpp new file mode 100644 index 0000000000..b1e501b215 --- /dev/null +++ b/camera/ndk/ndk_vendor/tests/AImageReaderVendorTest.cpp @@ -0,0 +1,525 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "AImageReaderVendorTest" +//#define LOG_NDEBUG 0 + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +//#define ALOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__) +//#define ALOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__) +#define ALOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__) +#define ALOGW(...) __android_log_print(ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__) +#define ALOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__) + +namespace { + +static constexpr int kDummyFenceFd = -1; +static constexpr int kCaptureWaitUs = 100 * 1000; +static constexpr int kCaptureWaitRetry = 10; +static constexpr int kTestImageWidth = 640; +static constexpr int kTestImageHeight = 480; +static constexpr int kTestImageFormat = AIMAGE_FORMAT_YUV_420_888; + +class CameraHelper { + public: + CameraHelper(native_handle_t* imgReaderAnw) : mImgReaderAnw(imgReaderAnw) {} + ~CameraHelper() { closeCamera(); } + + int initCamera() { + if (mImgReaderAnw == nullptr) { + ALOGE("Cannot initialize camera before image reader get initialized."); + return -1; + } + int ret; + + mCameraManager = ACameraManager_create(); + if (mCameraManager == nullptr) { + ALOGE("Failed to create ACameraManager."); + return -1; + } + + ret = ACameraManager_getCameraIdList(mCameraManager, &mCameraIdList); + if (ret != AMEDIA_OK) { + ALOGE("Failed to get cameraIdList: ret=%d", ret); + return ret; + } + if (mCameraIdList->numCameras < 1) { + ALOGW("Device has no camera on board."); + return 0; + } + + // We always use the first camera. + mCameraId = mCameraIdList->cameraIds[0]; + if (mCameraId == nullptr) { + ALOGE("Failed to get cameraId."); + return -1; + } + + ret = ACameraManager_openCamera(mCameraManager, mCameraId, &mDeviceCb, &mDevice); + if (ret != AMEDIA_OK || mDevice == nullptr) { + ALOGE("Failed to open camera, ret=%d, mDevice=%p.", ret, mDevice); + return -1; + } + + ret = ACameraManager_getCameraCharacteristics(mCameraManager, mCameraId, &mCameraMetadata); + if (ret != ACAMERA_OK || mCameraMetadata == nullptr) { + ALOGE("Get camera %s characteristics failure. ret %d, metadata %p", mCameraId, ret, + mCameraMetadata); + return -1; + } + + if (!isCapabilitySupported(ACAMERA_REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE)) { + ALOGW("Camera does not support BACKWARD_COMPATIBLE."); + return 0; + } + + // Create capture session + ret = ACaptureSessionOutputContainer_create(&mOutputs); + if (ret != AMEDIA_OK) { + ALOGE("ACaptureSessionOutputContainer_create failed, ret=%d", ret); + return ret; + } + ret = ACaptureSessionOutput_create(mImgReaderAnw, &mImgReaderOutput); + if (ret != AMEDIA_OK) { + ALOGE("ACaptureSessionOutput_create failed, ret=%d", ret); + return ret; + } + ret = ACaptureSessionOutputContainer_add(mOutputs, mImgReaderOutput); + if (ret != AMEDIA_OK) { + ALOGE("ACaptureSessionOutputContainer_add failed, ret=%d", ret); + return ret; + } + ret = ACameraDevice_createCaptureSession(mDevice, mOutputs, &mSessionCb, &mSession); + if (ret != AMEDIA_OK) { + ALOGE("ACameraDevice_createCaptureSession failed, ret=%d", ret); + return ret; + } + + // Create capture request + ret = ACameraDevice_createCaptureRequest(mDevice, TEMPLATE_STILL_CAPTURE, &mStillRequest); + if (ret != AMEDIA_OK) { + ALOGE("ACameraDevice_createCaptureRequest failed, ret=%d", ret); + return ret; + } + ret = ACameraOutputTarget_create(mImgReaderAnw, &mReqImgReaderOutput); + if (ret != AMEDIA_OK) { + ALOGE("ACameraOutputTarget_create failed, ret=%d", ret); + return ret; + } + ret = ACaptureRequest_addTarget(mStillRequest, mReqImgReaderOutput); + if (ret != AMEDIA_OK) { + ALOGE("ACaptureRequest_addTarget failed, ret=%d", ret); + return ret; + } + + mIsCameraReady = true; + return 0; + } + + bool isCapabilitySupported(acamera_metadata_enum_android_request_available_capabilities_t cap) { + ACameraMetadata_const_entry entry; + ACameraMetadata_getConstEntry( + mCameraMetadata, ACAMERA_REQUEST_AVAILABLE_CAPABILITIES, &entry); + for (uint32_t i = 0; i < entry.count; i++) { + if (entry.data.u8[i] == cap) { + return true; + } + } + return false; + } + + bool isCameraReady() { return mIsCameraReady; } + + void closeCamera() { + // Destroy capture request + if (mReqImgReaderOutput) { + ACameraOutputTarget_free(mReqImgReaderOutput); + mReqImgReaderOutput = nullptr; + } + if (mStillRequest) { + ACaptureRequest_free(mStillRequest); + mStillRequest = nullptr; + } + // Destroy capture session + if (mSession != nullptr) { + ACameraCaptureSession_close(mSession); + mSession = nullptr; + } + if (mImgReaderOutput) { + ACaptureSessionOutput_free(mImgReaderOutput); + mImgReaderOutput = nullptr; + } + if (mOutputs) { + ACaptureSessionOutputContainer_free(mOutputs); + mOutputs = nullptr; + } + // Destroy camera device + if (mDevice) { + ACameraDevice_close(mDevice); + mDevice = nullptr; + } + if (mCameraMetadata) { + ACameraMetadata_free(mCameraMetadata); + mCameraMetadata = nullptr; + } + // Destroy camera manager + if (mCameraIdList) { + ACameraManager_deleteCameraIdList(mCameraIdList); + mCameraIdList = nullptr; + } + if (mCameraManager) { + ACameraManager_delete(mCameraManager); + mCameraManager = nullptr; + } + mIsCameraReady = false; + } + + int takePicture() { + int seqId; + return ACameraCaptureSession_capture(mSession, nullptr, 1, &mStillRequest, &seqId); + } + + static void onDeviceDisconnected(void* /*obj*/, ACameraDevice* /*device*/) {} + + static void onDeviceError(void* /*obj*/, ACameraDevice* /*device*/, int /*errorCode*/) {} + + static void onSessionClosed(void* /*obj*/, ACameraCaptureSession* /*session*/) {} + + static void onSessionReady(void* /*obj*/, ACameraCaptureSession* /*session*/) {} + + static void onSessionActive(void* /*obj*/, ACameraCaptureSession* /*session*/) {} + + private: + ACameraDevice_StateCallbacks mDeviceCb{this, onDeviceDisconnected, + onDeviceError}; + ACameraCaptureSession_stateCallbacks mSessionCb{ + this, onSessionClosed, onSessionReady, onSessionActive}; + + native_handle_t* mImgReaderAnw = nullptr; // not owned by us. + + // Camera manager + ACameraManager* mCameraManager = nullptr; + ACameraIdList* mCameraIdList = nullptr; + // Camera device + ACameraMetadata* mCameraMetadata = nullptr; + ACameraDevice* mDevice = nullptr; + // Capture session + ACaptureSessionOutputContainer* mOutputs = nullptr; + ACaptureSessionOutput* mImgReaderOutput = nullptr; + ACameraCaptureSession* mSession = nullptr; + // Capture request + ACaptureRequest* mStillRequest = nullptr; + ACameraOutputTarget* mReqImgReaderOutput = nullptr; + + bool mIsCameraReady = false; + const char* mCameraId; +}; + +class ImageReaderTestCase { + public: + ImageReaderTestCase(int32_t width, + int32_t height, + int32_t format, + uint64_t usage, + int32_t maxImages, + bool async) + : mWidth(width), + mHeight(height), + mFormat(format), + mUsage(usage), + mMaxImages(maxImages), + mAsync(async) {} + + ~ImageReaderTestCase() { + if (mImgReaderAnw) { + AImageReader_delete(mImgReader); + // No need to call native_handle_t_release on imageReaderAnw + } + } + + int initImageReader() { + if (mImgReader != nullptr || mImgReaderAnw != nullptr) { + ALOGE("Cannot re-initalize image reader, mImgReader=%p, mImgReaderAnw=%p", mImgReader, + mImgReaderAnw); + return -1; + } + + media_status_t ret = AImageReader_newWithUsage( + mWidth, mHeight, mFormat, mUsage, mMaxImages, &mImgReader); + if (ret != AMEDIA_OK || mImgReader == nullptr) { + ALOGE("Failed to create new AImageReader, ret=%d, mImgReader=%p", ret, mImgReader); + return -1; + } + + ret = AImageReader_setImageListener(mImgReader, &mReaderAvailableCb); + if (ret != AMEDIA_OK) { + ALOGE("Failed to set image available listener, ret=%d.", ret); + return ret; + } + + ret = AImageReader_setBufferRemovedListener(mImgReader, &mReaderDetachedCb); + if (ret != AMEDIA_OK) { + ALOGE("Failed to set buffer detaching listener, ret=%d.", ret); + return ret; + } + + ret = AImageReader_getWindowNativeHandle(mImgReader, &mImgReaderAnw); + if (ret != AMEDIA_OK || mImgReaderAnw == nullptr) { + ALOGE("Failed to get native_handle_t from AImageReader, ret=%d, mImgReaderAnw=%p.", ret, + mImgReaderAnw); + return -1; + } + + return 0; + } + + native_handle_t* getNativeWindow() { return mImgReaderAnw; } + + int getAcquiredImageCount() { + std::lock_guard lock(mMutex); + return mAcquiredImageCount; + } + + void HandleImageAvailable(AImageReader* reader) { + std::lock_guard lock(mMutex); + + AImage* outImage = nullptr; + media_status_t ret; + + // Make sure AImage will be deleted automatically when it goes out of + // scope. + auto imageDeleter = [this](AImage* img) { + if (mAsync) { + AImage_deleteAsync(img, kDummyFenceFd); + } else { + AImage_delete(img); + } + }; + std::unique_ptr img(nullptr, imageDeleter); + + if (mAsync) { + int outFenceFd = 0; + // Verity that outFenceFd's value will be changed by + // AImageReader_acquireNextImageAsync. + ret = AImageReader_acquireNextImageAsync(reader, &outImage, &outFenceFd); + if (ret != AMEDIA_OK || outImage == nullptr || outFenceFd == 0) { + ALOGE("Failed to acquire image, ret=%d, outIamge=%p, outFenceFd=%d.", ret, outImage, + outFenceFd); + return; + } + img.reset(outImage); + } else { + ret = AImageReader_acquireNextImage(reader, &outImage); + if (ret != AMEDIA_OK || outImage == nullptr) { + ALOGE("Failed to acquire image, ret=%d, outIamge=%p.", ret, outImage); + return; + } + img.reset(outImage); + } + + AHardwareBuffer* outBuffer = nullptr; + ret = AImage_getHardwareBuffer(img.get(), &outBuffer); + if (ret != AMEDIA_OK || outBuffer == nullptr) { + ALOGE("Faild to get hardware buffer, ret=%d, outBuffer=%p.", ret, outBuffer); + return; + } + + // No need to release AHardwareBuffer, since we don't acquire additional + // reference to it. + AHardwareBuffer_Desc outDesc; + AHardwareBuffer_describe(outBuffer, &outDesc); + int32_t imageWidth = 0; + int32_t imageHeight = 0; + int32_t bufferWidth = static_cast(outDesc.width); + int32_t bufferHeight = static_cast(outDesc.height); + + AImage_getWidth(outImage, &imageWidth); + AImage_getHeight(outImage, &imageHeight); + if (imageWidth != mWidth || imageHeight != mHeight) { + ALOGE("Mismatched output image dimension: expected=%dx%d, actual=%dx%d", mWidth, + mHeight, imageWidth, imageHeight); + return; + } + + if (mFormat == AIMAGE_FORMAT_RGBA_8888 || + mFormat == AIMAGE_FORMAT_RGBX_8888 || + mFormat == AIMAGE_FORMAT_RGB_888 || + mFormat == AIMAGE_FORMAT_RGB_565 || + mFormat == AIMAGE_FORMAT_RGBA_FP16 || + mFormat == AIMAGE_FORMAT_YUV_420_888 || + mFormat == AIMAGE_FORMAT_Y8) { + // Check output buffer dimension for certain formats. Don't do this for blob based + // formats. + if (bufferWidth != mWidth || bufferHeight != mHeight) { + ALOGE("Mismatched output buffer dimension: expected=%dx%d, actual=%dx%d", mWidth, + mHeight, bufferWidth, bufferHeight); + return; + } + } + + if ((outDesc.usage & mUsage) != mUsage) { + ALOGE("Mismatched output buffer usage: actual (%" PRIu64 "), expected (%" PRIu64 ")", + outDesc.usage, mUsage); + return; + } + + uint8_t* data = nullptr; + int dataLength = 0; + ret = AImage_getPlaneData(img.get(), 0, &data, &dataLength); + if (mUsage & AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN) { + // When we have CPU_READ_OFTEN usage bits, we can lock the image. + if (ret != AMEDIA_OK || data == nullptr || dataLength < 0) { + ALOGE("Failed to access CPU data, ret=%d, data=%p, dataLength=%d", ret, data, + dataLength); + return; + } + } else { + if (ret != AMEDIA_IMGREADER_CANNOT_LOCK_IMAGE || data != nullptr || dataLength != 0) { + ALOGE("Shouldn't be able to access CPU data, ret=%d, data=%p, dataLength=%d", ret, + data, dataLength); + return; + } + } + // Only increase mAcquiredImageCount if all checks pass. + mAcquiredImageCount++; + } + + static void onImageAvailable(void* obj, AImageReader* reader) { + ImageReaderTestCase* thiz = reinterpret_cast(obj); + thiz->HandleImageAvailable(reader); + } + + static void + onBufferRemoved(void* /*obj*/, AImageReader* /*reader*/, AHardwareBuffer* /*buffer*/) { + // No-op, just to check the listener can be set properly. + } + + private: + int32_t mWidth; + int32_t mHeight; + int32_t mFormat; + uint64_t mUsage; + int32_t mMaxImages; + bool mAsync; + + std::mutex mMutex; + int mAcquiredImageCount{0}; + + AImageReader* mImgReader = nullptr; + native_handle_t* mImgReaderAnw = nullptr; + + AImageReader_ImageListener mReaderAvailableCb{this, onImageAvailable}; + AImageReader_BufferRemovedListener mReaderDetachedCb{this, onBufferRemoved}; +}; + +int takePictures(uint64_t readerUsage, int readerMaxImages, bool readerAsync, int pictureCount) { + int ret = 0; + + ImageReaderTestCase testCase( + kTestImageWidth, kTestImageHeight, kTestImageFormat, readerUsage, readerMaxImages, + readerAsync); + ret = testCase.initImageReader(); + if (ret < 0) { + return ret; + } + + CameraHelper cameraHelper(testCase.getNativeWindow()); + ret = cameraHelper.initCamera(); + if (ret < 0) { + return ret; + } + + if (!cameraHelper.isCameraReady()) { + ALOGW("Camera is not ready after successful initialization. It's either due to camera on " + "board lacks BACKWARDS_COMPATIBLE capability or the device does not have camera on " + "board."); + return 0; + } + + for (int i = 0; i < pictureCount; i++) { + ret = cameraHelper.takePicture(); + if (ret < 0) { + return ret; + } + } + + // Sleep until all capture finished + for (int i = 0; i < kCaptureWaitRetry * pictureCount; i++) { + usleep(kCaptureWaitUs); + if (testCase.getAcquiredImageCount() == pictureCount) { + ALOGI("Session take ~%d ms to capture %d images", i * kCaptureWaitUs / 1000, + pictureCount); + break; + } + } + return testCase.getAcquiredImageCount() == pictureCount ? 0 : -1; +} + +class AImageReaderWindowHandleTest : public ::testing::Test { + public: + void SetUp() override { + } + void TearDown() override { + + } + +}; + +bool testTakePicturesNative() { + for (auto& readerUsage : + {AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN}) { + for (auto& readerMaxImages : {1, 4, 8}) { + for (auto& readerAsync : {true, false}) { + for (auto& pictureCount : {1, 4, 8}) { + if (takePictures(readerUsage, readerMaxImages, readerAsync, pictureCount)) { + ALOGE("Test takePictures failed for test case usage=%" PRIu64 ", maxImages=%d, " + "async=%d, pictureCount=%d", + readerUsage, readerMaxImages, readerAsync, pictureCount); + return false; + } + } + } + } + } + return true; +} + + +TEST_F(AImageReaderWindowHandleTest, CreateWindowNativeHandle) { + EXPECT_TRUE(testTakePicturesNative()); +} + +} // namespace