diff --git a/camera/provider/2.4/vts/functional/Android.bp b/camera/provider/2.4/vts/functional/Android.bp index 85312c1a5d..14d7c50f33 100644 --- a/camera/provider/2.4/vts/functional/Android.bp +++ b/camera/provider/2.4/vts/functional/Android.bp @@ -31,9 +31,14 @@ cc_test { "libcamera_metadata", "libbinder", "libgui", - "libui" + "libui", + "libfmq", + ], + static_libs: [ + "VtsHalHidlTargetTestBase", + "libgrallocusage", + "android.hardware.camera.common@1.0-helper", ], - static_libs: ["VtsHalHidlTargetTestBase", "libgrallocusage"], cflags: [ "-O0", "-g", diff --git a/camera/provider/2.4/vts/functional/VtsHalCameraProviderV2_4TargetTest.cpp b/camera/provider/2.4/vts/functional/VtsHalCameraProviderV2_4TargetTest.cpp index c350e31aea..0afc6158d0 100644 --- a/camera/provider/2.4/vts/functional/VtsHalCameraProviderV2_4TargetTest.cpp +++ b/camera/provider/2.4/vts/functional/VtsHalCameraProviderV2_4TargetTest.cpp @@ -15,29 +15,33 @@ */ #define LOG_TAG "camera_hidl_hal_test" -#include + +#include +#include +#include +#include +#include + +#include + #include #include #include -#include #include +#include +#include +#include #include #include #include #include #include #include -#include #include +#include #include -#include -#include -#include -#include -#include -#include -#include "CameraParameters.h" -#include "system/camera_metadata.h" + +#include using ::android::hardware::Return; using ::android::hardware::Void; @@ -89,6 +93,9 @@ using ::android::hardware::camera::device::V1_0::CameraFrameMetadata; using ::android::hardware::camera::device::V1_0::ICameraDevicePreviewCallback; using ::android::hardware::camera::device::V1_0::FrameCallbackFlag; using ::android::hardware::camera::device::V1_0::HandleTimestampMessage; +using ::android::hardware::MessageQueue; +using ::android::hardware::kSynchronizedReadWrite; +using ResultMetadataQueue = MessageQueue; const char kCameraPassthroughServiceName[] = "legacy/0"; const uint32_t kMaxPreviewWidth = 1920; @@ -253,9 +260,7 @@ struct PreviewWindowCb : public ICameraDevicePreviewCallback { size_t result = 1; result = 31 * result + buf->numFds; - result = 31 * result + buf->numInts; - int length = buf->numFds + buf->numInts; - for (int i = 0; i < length; i++) { + for (int i = 0; i < buf->numFds; i++) { result = 31 * result + buf->data[i]; } return result; @@ -265,10 +270,8 @@ struct PreviewWindowCb : public ICameraDevicePreviewCallback { struct BufferComparator { bool operator()(const buffer_handle_t& buf1, const buffer_handle_t& buf2) const { - if ((buf1->numFds == buf2->numFds) && - (buf1->numInts == buf2->numInts)) { - int length = buf1->numFds + buf1->numInts; - for (int i = 0; i < length; i++) { + if (buf1->numFds == buf2->numFds) { + for (int i = 0; i < buf1->numFds; i++) { if (buf1->data[i] != buf2->data[i]) { return false; } @@ -552,7 +555,9 @@ public: const AvailableStream *previewThreshold, sp *session /*out*/, Stream *previewStream /*out*/, - HalStreamConfiguration *halStreamConfig /*out*/); + HalStreamConfiguration *halStreamConfig /*out*/, + bool *supportsPartialResults /*out*/, + uint32_t *partialResultCount /*out*/); static Status getAvailableOutputStreams(camera_metadata_t *staticMeta, std::vector &outputStreams, const AvailableStream *threshold = nullptr); @@ -569,11 +574,75 @@ public: ::android::CameraParameters &cameraParams, const char *mode) ; protected: + + // In-flight queue for tracking completion of capture requests. + struct InFlightRequest { + // Set by notify() SHUTTER call. + nsecs_t shutterTimestamp; + + bool errorCodeValid; + ErrorCode errorCode; + + //Is partial result supported + bool usePartialResult; + + //Partial result count expected + uint32_t numPartialResults; + + // Message queue + std::shared_ptr resultQueue; + + // Set by process_capture_result call with valid metadata + bool haveResultMetadata; + + // Decremented by calls to process_capture_result with valid output + // and input buffers + ssize_t numBuffersLeft; + + // A 64bit integer to index the frame number associated with this result. + int64_t frameNumber; + + // The partial result count (index) for this capture result. + int32_t partialResultCount; + + // For buffer drop errors, the stream ID for the stream that lost a buffer. + // Otherwise -1. + int32_t errorStreamId; + + // If this request has any input buffer + bool hasInputBuffer; + + // Result metadata + ::android::hardware::camera::common::V1_0::helper::CameraMetadata collectedResult; + + // Buffers are added by process_capture_result when output buffers + // return from HAL but framework. + ::android::Vector resultOutputBuffers; + + InFlightRequest(ssize_t numBuffers, bool hasInput, + bool partialResults, uint32_t partialCount, + std::shared_ptr queue = nullptr) : + shutterTimestamp(0), + errorCodeValid(false), + errorCode(ErrorCode::ERROR_BUFFER), + usePartialResult(partialResults), + numPartialResults(partialCount), + resultQueue(queue), + haveResultMetadata(false), + numBuffersLeft(numBuffers), + frameNumber(0), + partialResultCount(0), + errorStreamId(-1), + hasInputBuffer(hasInput) {} + }; + + // Map from frame number to the in-flight request state + typedef ::android::KeyedVector InFlightMap; + std::mutex mLock; // Synchronize access to member variables std::condition_variable mResultCondition; // Condition variable for incoming results - uint32_t mResultFrameNumber; // Expected result frame number - std::vector mResultBuffers; // Holds stream buffers from capture result - std::vector mErrors; // Holds incoming error notifications + InFlightMap mInflightMap; // Map of all inflight requests + DataCallbackMsg mDataMessageTypeReceived; // Most recent message type received through data callbacks uint32_t mVideoBufferIndex; // Buffer index of the most recent video buffer uint32_t mVideoData; // Buffer data of the most recent video buffer @@ -696,45 +765,173 @@ Return CameraHidlTest::DeviceCb::processCaptureResult( return Void(); } + bool notify = false; std::unique_lock l(mParent->mLock); - const CaptureResult& result = results[0]; + for (size_t i = 0 ; i < results.size(); i++) { + uint32_t frameNumber = results[i].frameNumber; - if(mParent->mResultFrameNumber != result.frameNumber) { - ALOGE("%s: Unexpected frame number! Expected: %u received: %u", - __func__, mParent->mResultFrameNumber, result.frameNumber); - ADD_FAILURE(); + if ((results[i].result.size() == 0) && + (results[i].outputBuffers.size() == 0) && + (results[i].inputBuffer.buffer == nullptr) && + (results[i].fmqResultSize == 0)) { + ALOGE("%s: No result data provided by HAL for frame %d result count: %d", + __func__, frameNumber, (int) results[i].fmqResultSize); + ADD_FAILURE(); + break; + } + + ssize_t idx = mParent->mInflightMap.indexOfKey(frameNumber); + if (::android::NAME_NOT_FOUND == idx) { + ALOGE("%s: Unexpected frame number! received: %u", + __func__, frameNumber); + ADD_FAILURE(); + break; + } + + bool isPartialResult = false; + bool hasInputBufferInRequest = false; + InFlightRequest *request = mParent->mInflightMap.editValueAt(idx); + ::android::hardware::camera::device::V3_2::CameraMetadata resultMetadata; + size_t resultSize = 0; + if (results[i].fmqResultSize > 0) { + resultMetadata.resize(results[i].fmqResultSize); + if (request->resultQueue == nullptr) { + ADD_FAILURE(); + break; + } + if (!request->resultQueue->read(resultMetadata.data(), + results[i].fmqResultSize)) { + ALOGE("%s: Frame %d: Cannot read camera metadata from fmq," + "size = %" PRIu64, __func__, frameNumber, + results[i].fmqResultSize); + ADD_FAILURE(); + break; + } + resultSize = resultMetadata.size(); + } else if (results[i].result.size() > 0) { + resultMetadata.setToExternal(const_cast( + results[i].result.data()), results[i].result.size()); + resultSize = resultMetadata.size(); + } + + if (!request->usePartialResult && (resultSize > 0) && + (results[i].partialResult != 1)) { + ALOGE("%s: Result is malformed for frame %d: partial_result %u " + "must be 1 if partial result is not supported", __func__, + frameNumber, results[i].partialResult); + ADD_FAILURE(); + break; + } + + if (results[i].partialResult != 0) { + request->partialResultCount = results[i].partialResult; + } + + // Check if this result carries only partial metadata + if (request->usePartialResult && (resultSize > 0)) { + if ((results[i].partialResult > request->numPartialResults) || + (results[i].partialResult < 1)) { + ALOGE("%s: Result is malformed for frame %d: partial_result %u" + " must be in the range of [1, %d] when metadata is " + "included in the result", __func__, frameNumber, + results[i].partialResult, request->numPartialResults); + ADD_FAILURE(); + break; + } + request->collectedResult.append( + reinterpret_cast( + resultMetadata.data())); + + isPartialResult = + (results[i].partialResult < request->numPartialResults); + } + + hasInputBufferInRequest = request->hasInputBuffer; + + // Did we get the (final) result metadata for this capture? + if ((resultSize > 0) && !isPartialResult) { + if (request->haveResultMetadata) { + ALOGE("%s: Called multiple times with metadata for frame %d", + __func__, frameNumber); + ADD_FAILURE(); + break; + } + request->haveResultMetadata = true; + request->collectedResult.sort(); + } + + uint32_t numBuffersReturned = results[i].outputBuffers.size(); + if (results[i].inputBuffer.buffer != nullptr) { + if (hasInputBufferInRequest) { + numBuffersReturned += 1; + } else { + ALOGW("%s: Input buffer should be NULL if there is no input" + " buffer sent in the request", __func__); + } + } + request->numBuffersLeft -= numBuffersReturned; + if (request->numBuffersLeft < 0) { + ALOGE("%s: Too many buffers returned for frame %d", __func__, + frameNumber); + ADD_FAILURE(); + break; + } + + request->resultOutputBuffers.appendArray(results[i].outputBuffers.data(), + results[i].outputBuffers.size()); + // If shutter event is received notify the pending threads. + if (request->shutterTimestamp != 0) { + notify = true; + } } - size_t resultLength = result.outputBuffers.size(); - for (size_t i = 0; i < resultLength; i++) { - mParent->mResultBuffers.push_back(result.outputBuffers[i]); - } - - // TODO(epeev): Handle partial results in case client supports them and - // verify the result against request settings. - l.unlock(); - mParent->mResultCondition.notify_one(); + if (notify) { + mParent->mResultCondition.notify_one(); + } return Void(); } Return CameraHidlTest::DeviceCb::notify( const hidl_vec& messages) { - const NotifyMsg& message = messages[0]; + std::lock_guard l(mParent->mLock); - if (MsgType::ERROR == message.type) { - { - std::lock_guard l(mParent->mLock); - mParent->mErrors.push_back(message.msg.error); + for (size_t i = 0; i < messages.size(); i++) { + ssize_t idx = mParent->mInflightMap.indexOfKey( + messages[i].msg.shutter.frameNumber); + if (::android::NAME_NOT_FOUND == idx) { + ALOGE("%s: Unexpected frame number! received: %u", + __func__, messages[i].msg.shutter.frameNumber); + ADD_FAILURE(); + break; } + InFlightRequest *r = mParent->mInflightMap.editValueAt(idx); - if ((ErrorCode::ERROR_REQUEST == message.msg.error.errorCode) - || (ErrorCode::ERROR_BUFFER == message.msg.error.errorCode)) { - mParent->mResultCondition.notify_one(); + switch(messages[i].type) { + case MsgType::ERROR: + if (ErrorCode::ERROR_DEVICE == messages[i].msg.error.errorCode) { + ALOGE("%s: Camera reported serious device error", + __func__); + ADD_FAILURE(); + } else { + r->errorCodeValid = true; + r->errorCode = messages[i].msg.error.errorCode; + r->errorStreamId = messages[i].msg.error.errorStreamId; + } + break; + case MsgType::SHUTTER: + r->shutterTimestamp = messages[i].msg.shutter.timestamp; + break; + default: + ALOGE("%s: Unsupported notify message %d", __func__, + messages[i].type); + ADD_FAILURE(); + break; } } + mParent->mResultCondition.notify_one(); return Void(); } @@ -2471,9 +2668,31 @@ TEST_F(CameraHidlTest, processCaptureRequestPreview) { Stream previewStream; HalStreamConfiguration halStreamConfig; sp session; + bool supportsPartialResults = false; + uint32_t partialResultCount = 0; configurePreviewStream(name, env, &previewThreshold, &session /*out*/, &previewStream /*out*/, - &halStreamConfig /*out*/); + &halStreamConfig /*out*/, &supportsPartialResults /*out*/, + &partialResultCount/*out*/); + + std::shared_ptr resultQueue; + auto resultQueueRet = session->getCaptureResultMetadataQueue( + [&resultQueue](const auto& descriptor) { + resultQueue = std::make_shared( + descriptor); + if (!resultQueue->isValid() || + resultQueue->availableToWrite() <= 0) { + ALOGE("%s: HAL returns empty result metadata fmq," + " not use it", __func__); + resultQueue = nullptr; + // Don't use the queue onwards. + } + }); + ASSERT_TRUE(resultQueueRet.isOk()); + ASSERT_NE(nullptr, resultQueue); + + InFlightRequest inflightReq = {1, false, supportsPartialResults, + partialResultCount, resultQueue}; RequestTemplate reqTemplate = RequestTemplate::PREVIEW; Return ret; @@ -2502,8 +2721,8 @@ TEST_F(CameraHidlTest, processCaptureRequestPreview) { { std::unique_lock l(mLock); - mResultBuffers.clear(); - mResultFrameNumber = frameNumber; + mInflightMap.clear(); + mInflightMap.add(frameNumber, &inflightReq); } Status status = Status::INTERNAL_ERROR; @@ -2522,22 +2741,28 @@ TEST_F(CameraHidlTest, processCaptureRequestPreview) { { std::unique_lock l(mLock); - while (0 == mResultBuffers.size()) { + while (!inflightReq.errorCodeValid && + ((0 < inflightReq.numBuffersLeft) || + (!inflightReq.haveResultMetadata))) { auto timeout = std::chrono::system_clock::now() + std::chrono::seconds(kStreamBufferTimeoutSec); ASSERT_NE(std::cv_status::timeout, mResultCondition.wait_until(l, timeout)); } - ASSERT_EQ(BufferStatus::OK, mResultBuffers[0].status); - ASSERT_EQ(previewStream.id, mResultBuffers[0].streamId); + ASSERT_FALSE(inflightReq.errorCodeValid); + ASSERT_NE(inflightReq.resultOutputBuffers.size(), 0u); + ASSERT_EQ(previewStream.id, + inflightReq.resultOutputBuffers[0].streamId); request.frameNumber++; //Empty settings should be supported after the first call //for repeating requests. request.settings.setToExternal(nullptr, 0, true); - mResultBuffers.clear(); - mResultFrameNumber++; + mInflightMap.clear(); + inflightReq = {1, false, supportsPartialResults, + partialResultCount, resultQueue}; + mInflightMap.add(request.frameNumber, &inflightReq); } returnStatus = session->processCaptureRequest( @@ -2553,14 +2778,19 @@ TEST_F(CameraHidlTest, processCaptureRequestPreview) { { std::unique_lock l(mLock); - while (0 == mResultBuffers.size()) { + while (!inflightReq.errorCodeValid && + ((0 < inflightReq.numBuffersLeft) || + (!inflightReq.haveResultMetadata))) { auto timeout = std::chrono::system_clock::now() + std::chrono::seconds(kStreamBufferTimeoutSec); ASSERT_NE(std::cv_status::timeout, mResultCondition.wait_until(l, timeout)); } - ASSERT_EQ(BufferStatus::OK, mResultBuffers[0].status); - ASSERT_EQ(previewStream.id, mResultBuffers[0].streamId); + + ASSERT_FALSE(inflightReq.errorCodeValid); + ASSERT_NE(inflightReq.resultOutputBuffers.size(), 0u); + ASSERT_EQ(previewStream.id, + inflightReq.resultOutputBuffers[0].streamId); } ret = session->close(); @@ -2586,9 +2816,12 @@ TEST_F(CameraHidlTest, processCaptureRequestInvalidSinglePreview) { Stream previewStream; HalStreamConfiguration halStreamConfig; sp session; + bool supportsPartialResults = false; + uint32_t partialResultCount = 0; configurePreviewStream(name, env, &previewThreshold, &session /*out*/, &previewStream /*out*/, - &halStreamConfig /*out*/); + &halStreamConfig /*out*/, &supportsPartialResults /*out*/, + &partialResultCount /*out*/); sp gb = new GraphicBuffer( previewStream.width, previewStream.height, @@ -2644,9 +2877,12 @@ TEST_F(CameraHidlTest, processCaptureRequestInvalidBuffer) { Stream previewStream; HalStreamConfiguration halStreamConfig; sp session; + bool supportsPartialResults = false; + uint32_t partialResultCount = 0; configurePreviewStream(name, env, &previewThreshold, &session /*out*/, &previewStream /*out*/, - &halStreamConfig /*out*/); + &halStreamConfig /*out*/, &supportsPartialResults/*out*/, + &partialResultCount /*out*/); RequestTemplate reqTemplate = RequestTemplate::PREVIEW; Return ret; @@ -2699,10 +2935,31 @@ TEST_F(CameraHidlTest, flushPreviewRequest) { Stream previewStream; HalStreamConfiguration halStreamConfig; sp session; + bool supportsPartialResults = false; + uint32_t partialResultCount = 0; configurePreviewStream(name, env, &previewThreshold, &session /*out*/, &previewStream /*out*/, - &halStreamConfig /*out*/); + &halStreamConfig /*out*/, &supportsPartialResults /*out*/, + &partialResultCount /*out*/); + std::shared_ptr resultQueue; + auto resultQueueRet = session->getCaptureResultMetadataQueue( + [&resultQueue](const auto& descriptor) { + resultQueue = std::make_shared( + descriptor); + if (!resultQueue->isValid() || + resultQueue->availableToWrite() <= 0) { + ALOGE("%s: HAL returns empty result metadata fmq," + " not use it", __func__); + resultQueue = nullptr; + // Don't use the queue onwards. + } + }); + ASSERT_TRUE(resultQueueRet.isOk()); + ASSERT_NE(nullptr, resultQueue); + + InFlightRequest inflightReq = {1, false, supportsPartialResults, + partialResultCount, resultQueue}; RequestTemplate reqTemplate = RequestTemplate::PREVIEW; Return ret; ret = session->constructDefaultRequestSettings(reqTemplate, @@ -2730,9 +2987,8 @@ TEST_F(CameraHidlTest, flushPreviewRequest) { { std::unique_lock l(mLock); - mResultBuffers.clear(); - mErrors.clear(); - mResultFrameNumber = frameNumber; + mInflightMap.clear(); + mInflightMap.add(frameNumber, &inflightReq); } Status status = Status::INTERNAL_ERROR; @@ -2756,32 +3012,30 @@ TEST_F(CameraHidlTest, flushPreviewRequest) { { std::unique_lock l(mLock); - while ((0 == mResultBuffers.size()) && (0 == mErrors.size())) { + while (!inflightReq.errorCodeValid && + ((0 < inflightReq.numBuffersLeft) || + (!inflightReq.haveResultMetadata))) { auto timeout = std::chrono::system_clock::now() + std::chrono::seconds(kStreamBufferTimeoutSec); ASSERT_NE(std::cv_status::timeout, mResultCondition.wait_until(l, timeout)); } - if (mErrors.empty()) { - ASSERT_EQ(BufferStatus::OK, mResultBuffers[0].status); - ASSERT_EQ(previewStream.id, mResultBuffers[0].streamId); + if (!inflightReq.errorCodeValid) { + ASSERT_NE(inflightReq.resultOutputBuffers.size(), 0u); + ASSERT_EQ(previewStream.id, + inflightReq.resultOutputBuffers[0].streamId); } else { - for (auto &error : mErrors) { - switch (error.errorCode) { - case ErrorCode::ERROR_REQUEST: - case ErrorCode::ERROR_RESULT: - //Expected - break; - case ErrorCode::ERROR_BUFFER: - //Expected as well - ASSERT_EQ(frameNumber, error.frameNumber); - ASSERT_EQ(previewStream.id, error.errorStreamId); - break; - case ErrorCode::ERROR_DEVICE: - default: - FAIL() <<"Unexpected error:" << static_cast (error.errorCode); - } + switch (inflightReq.errorCode) { + case ErrorCode::ERROR_REQUEST: + case ErrorCode::ERROR_RESULT: + case ErrorCode::ERROR_BUFFER: + //Expected + break; + case ErrorCode::ERROR_DEVICE: + default: + FAIL() << "Unexpected error:" << static_cast ( + inflightReq.errorCode); } } } @@ -2805,16 +3059,12 @@ TEST_F(CameraHidlTest, flushEmpty) { Stream previewStream; HalStreamConfiguration halStreamConfig; sp session; + bool supportsPartialResults = false; + uint32_t partialResultCount = 0; configurePreviewStream(name, env, &previewThreshold, &session /*out*/, &previewStream /*out*/, - &halStreamConfig /*out*/); - - { - std::unique_lock l(mLock); - mResultBuffers.clear(); - mErrors.clear(); - mResultFrameNumber = 0; - } + &halStreamConfig /*out*/, &supportsPartialResults /*out*/, + &partialResultCount /*out*/); Return returnStatus = session->flush(); ASSERT_TRUE(returnStatus.isOk()); @@ -2826,8 +3076,6 @@ TEST_F(CameraHidlTest, flushEmpty) { std::chrono::milliseconds(kEmptyFlushTimeoutMSec); ASSERT_EQ(std::cv_status::timeout, mResultCondition.wait_until(l, timeout)); - ASSERT_TRUE(mErrors.empty()); - ASSERT_TRUE(mResultBuffers.empty()); } Return ret = session->close(); @@ -3025,11 +3273,15 @@ void CameraHidlTest::configurePreviewStream(const std::string &name, const AvailableStream *previewThreshold, sp *session /*out*/, Stream *previewStream /*out*/, - HalStreamConfiguration *halStreamConfig /*out*/) { + HalStreamConfiguration *halStreamConfig /*out*/, + bool *supportsPartialResults /*out*/, + uint32_t *partialResultCount /*out*/) { ASSERT_NE(nullptr, env); ASSERT_NE(nullptr, session); ASSERT_NE(nullptr, previewStream); ASSERT_NE(nullptr, halStreamConfig); + ASSERT_NE(nullptr, supportsPartialResults); + ASSERT_NE(nullptr, partialResultCount); std::vector outputPreviewStreams; ::android::sp device3_2; @@ -3067,6 +3319,14 @@ void CameraHidlTest::configurePreviewStream(const std::string &name, }); ASSERT_TRUE(ret.isOk()); + camera_metadata_ro_entry entry; + auto status = find_camera_metadata_ro_entry(staticMeta, + ANDROID_REQUEST_PARTIAL_RESULT_COUNT, &entry); + if ((0 == status) && (entry.count > 0)) { + *partialResultCount = entry.data.i32[0]; + *supportsPartialResults = (*partialResultCount > 1); + } + outputPreviewStreams.clear(); auto rc = getAvailableOutputStreams(staticMeta, outputPreviewStreams, previewThreshold);