Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 56c2e79d authored by Emilian Peev's avatar Emilian Peev
Browse files

Camera: Add camera capture request VTS tests

Use single camera capture requests to verify basic
'processCaptureRequest' functionality:
- 'processCaptureRequestSinglePreview' will generate
a valid preview capture request. The result needs to
include both valid stream Id and frame number.
- 'processCaptureRequestInvalidSinglePreview' will
omit the settings from the first capture request. Hal
should handle this by returning an appropriate error.
- 'processCaptureRequestInvalidSingleSnapshot' will
have a valid blob request but no valid output buffers.
Hal should again return appropriate error in this case.

BUG: 32022758
Test: compile and run the gtest binary on device
Change-Id: I021dd150b12d4be39fae47e13ba82d3db105bfa3
parent 378f8aae
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -26,7 +26,8 @@ cc_test {
        "libutils",
        "android.hardware.camera.provider@2.4",
        "android.hardware.camera.device@3.2",
        "libcamera_metadata"
        "libcamera_metadata",
        "libui"
    ],
    static_libs: ["libgtest"],
    cflags: [
+368 −0
Original line number Diff line number Diff line
@@ -18,11 +18,17 @@
#include <android/hardware/camera/provider/2.4/ICameraProvider.h>
#include <android/hardware/camera/device/3.2/ICameraDevice.h>
#include <android/log.h>
#include <ui/GraphicBuffer.h>
#include <gtest/gtest.h>
#include <regex>
#include "system/camera_metadata.h"
#include <hardware/gralloc.h>
#include <hardware/gralloc1.h>
#include <unordered_map>
#include <mutex>
#include <condition_variable>
#include <chrono>
#include <inttypes.h>

using ::android::hardware::Return;
using ::android::hardware::Void;
@@ -30,6 +36,7 @@ using ::android::hardware::hidl_handle;
using ::android::hardware::hidl_string;
using ::android::hardware::hidl_vec;
using ::android::sp;
using ::android::GraphicBuffer;
using ::android::hardware::graphics::common::V1_0::PixelFormat;
using ::android::hardware::camera::common::V1_0::Status;
using ::android::hardware::camera::common::V1_0::CameraDeviceStatus;
@@ -50,12 +57,15 @@ using ::android::hardware::camera::device::V3_2::StreamConfiguration;
using ::android::hardware::camera::device::V3_2::StreamConfigurationMode;
using ::android::hardware::camera::device::V3_2::CameraMetadata;
using ::android::hardware::camera::device::V3_2::HalStreamConfiguration;
using ::android::hardware::camera::device::V3_2::BufferStatus;
using ::android::hardware::camera::device::V3_2::StreamBuffer;

#define CAMERA_PASSTHROUGH_SERVICE_NAME "legacy/0"
#define MAX_PREVIEW_WIDTH  1920
#define MAX_PREVIEW_HEIGHT 1080
#define MAX_VIDEO_WIDTH    4096
#define MAX_VIDEO_HEIGHT   2160
#define STREAM_BUFFER_TIMEOUT 3  // sec.

struct AvailableStream {
    int32_t width;
@@ -152,6 +162,15 @@ public:
        }
    };

    struct DeviceCb : public ICameraDeviceCallback {
        DeviceCb(CameraHidlTest *parent) : mParent(parent) {}
        Return<void> processCaptureResult(const CaptureResult& result) override;
        Return<void> notify(const NotifyMsg& msg) override;

     private:
        CameraHidlTest *mParent;               // Parent object
    };

    static Status getAvailableOutputStreams(camera_metadata_t *staticMeta,
            std::vector<AvailableStream> &outputStreams,
            AvailableStream *threshold = nullptr);
@@ -164,8 +183,49 @@ public:
    static Status findLargestSize(
            const std::vector<AvailableStream> &streamSizes,
            int32_t format, AvailableStream &result);

protected:
    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<StreamBuffer> mResultBuffers;  // Holds stream buffers from capture result
};

Return<void> CameraHidlTest::DeviceCb::processCaptureResult(
        const CaptureResult& result) {
    if (nullptr == mParent) {
        return Void();
    }

    std::unique_lock<std::mutex> l(mParent->mLock);

    if(mParent->mResultFrameNumber != result.frameNumber) {
        ALOGE("%s: Unexpected frame number! Expected: %u received: %u",
              __func__, mParent->mResultFrameNumber, result.frameNumber);
        ADD_FAILURE();
    }

    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();

    return Void();
}

Return<void> CameraHidlTest::DeviceCb::notify(
        const NotifyMsg& /*msg*/) {
    // TODO(epeev): Pending implementation.
    ALOGI("notify callback");
    return Void();
}

hidl_vec<hidl_string> CameraHidlTest::getCameraDeviceNames() {
    CameraHidlEnvironment* env = CameraHidlEnvironment::Instance();
    hidl_vec<hidl_string> cameraDeviceNames;
@@ -1066,6 +1126,314 @@ TEST_F(CameraHidlTest, configureStreamsVideoStillOutputs) {
    }
}

// Generate and verify a camera capture request
TEST_F(CameraHidlTest, processCaptureRequestPreview) {
    CameraHidlEnvironment* env = CameraHidlEnvironment::Instance();
    hidl_vec<hidl_string> cameraDeviceNames = getCameraDeviceNames();
    std::vector<AvailableStream> outputPreviewStreams;
    AvailableStream previewThreshold = {MAX_PREVIEW_WIDTH, MAX_PREVIEW_HEIGHT,
            static_cast<int32_t>(PixelFormat::IMPLEMENTATION_DEFINED)};
    int32_t streamId = 0;
    uint64_t bufferId = 1;
    uint32_t frameNumber = 1;
    ::android::hardware::hidl_vec<uint8_t> settings;

    for (const auto& name : cameraDeviceNames) {
        if (getCameraDeviceVersion(name) == CAMERA_DEVICE_API_VERSION_3_2) {
            ::android::sp<::android::hardware::camera::device::V3_2::ICameraDevice> device3_2;
            ALOGI("configureStreams: Testing camera device %s", name.c_str());
            env->mProvider->getCameraDeviceInterface_V3_x(
                name,
                [&](auto status, const auto& device) {
                    ALOGI("getCameraDeviceInterface_V3_x returns status:%d", (int)status);
                    ASSERT_EQ(Status::OK, status);
                    ASSERT_NE(device, nullptr);
                    device3_2 = device;
                });

            sp<DeviceCb> cb = new DeviceCb(this);
            sp<ICameraDeviceSession> session;
            device3_2->open(
                cb,
                [&](auto status, const auto& newSession) {
                    ALOGI("device::open returns status:%d", (int)status);
                    ASSERT_EQ(Status::OK, status);
                    ASSERT_NE(newSession, nullptr);
                    session = newSession;
                });

            camera_metadata_t *staticMeta;
            device3_2->getCameraCharacteristics([&] (Status s,
                    CameraMetadata metadata) {
                ASSERT_EQ(Status::OK, s);
                staticMeta =
                        reinterpret_cast<camera_metadata_t*>(metadata.data());
            });

            outputPreviewStreams.clear();
            ASSERT_EQ(Status::OK, getAvailableOutputStreams(staticMeta,
                    outputPreviewStreams, &previewThreshold));
            ASSERT_NE(0u, outputPreviewStreams.size());

            HalStreamConfiguration halStreamConfig;
            Stream previewStream = {streamId, StreamType::OUTPUT,
                    static_cast<uint32_t> (outputPreviewStreams[0].width),
                    static_cast<uint32_t> (outputPreviewStreams[0].height),
                    static_cast<PixelFormat> (outputPreviewStreams[0].format),
                    0, 0, StreamRotation::ROTATION_0};
            ::android::hardware::hidl_vec<Stream> streams = {previewStream};
            StreamConfiguration config = {streams,
                    StreamConfigurationMode::NORMAL_MODE};
            session->configureStreams(config, [&] (Status s,
                    HalStreamConfiguration halConfig) {
                ASSERT_EQ(Status::OK, s);
                ASSERT_EQ(1u, halConfig.streams.size());
                halStreamConfig = halConfig;
            });

            RequestTemplate reqTemplate = RequestTemplate::PREVIEW;
            session->constructDefaultRequestSettings(reqTemplate,
                [&](auto status, const auto& req) {
                    ASSERT_EQ(Status::OK, status);
                    settings = req; });

            sp<GraphicBuffer> gb = new GraphicBuffer(previewStream.width,
                    previewStream.height,
                    static_cast<int32_t> (halStreamConfig.streams[0].overrideFormat),
                    1, halStreamConfig.streams[0].producerUsage,
                    halStreamConfig.streams[0].consumerUsage);
            ASSERT_NE(nullptr, gb.get());
            StreamBuffer outputBuffer = {halStreamConfig.streams[0].id,
                    bufferId, hidl_handle(gb->getNativeBuffer()->handle),
                    BufferStatus::OK, nullptr, nullptr};
            ::android::hardware::hidl_vec<StreamBuffer> outputBuffers = {
                    outputBuffer};
            CaptureRequest request = {frameNumber, settings,
                    {-1, 0, nullptr, BufferStatus::ERROR, nullptr, nullptr},
                    outputBuffers};

            std::unique_lock<std::mutex> l(mLock);
            mResultBuffers.clear();
            mResultFrameNumber = frameNumber;
            l.unlock();

            ASSERT_EQ(Status::OK, session->processCaptureRequest(request));

            l.lock();
            while (0 == mResultBuffers.size()) {
                auto timeout = std::chrono::system_clock::now() +
                        std::chrono::seconds(STREAM_BUFFER_TIMEOUT);
                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);

            request.frameNumber++;
            //Empty settings should be supported after the first call
            //for repeating requests.
            request.settings.setToExternal(nullptr, 0, true);
            mResultBuffers.clear();
            mResultFrameNumber++;
            l.unlock();

            ASSERT_EQ(Status::OK, session->processCaptureRequest(request));

            l.lock();
            while (0 == mResultBuffers.size()) {
                auto timeout = std::chrono::system_clock::now() +
                        std::chrono::seconds(STREAM_BUFFER_TIMEOUT);
                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);

            session->close();
        }
    }
}

// Test whether an incorrect capture request with missing settings will
// be reported correctly.
TEST_F(CameraHidlTest, processCaptureRequestInvalidSinglePreview) {
    CameraHidlEnvironment* env = CameraHidlEnvironment::Instance();
    hidl_vec<hidl_string> cameraDeviceNames = getCameraDeviceNames();
    std::vector<AvailableStream> outputPreviewStreams;
    AvailableStream previewThreshold = {MAX_PREVIEW_WIDTH, MAX_PREVIEW_HEIGHT,
            static_cast<int32_t>(PixelFormat::IMPLEMENTATION_DEFINED)};
    int32_t streamId = 0;
    uint64_t bufferId = 1;
    uint32_t frameNumber = 1;
    ::android::hardware::hidl_vec<uint8_t> settings;

    for (const auto& name : cameraDeviceNames) {
        if (getCameraDeviceVersion(name) == CAMERA_DEVICE_API_VERSION_3_2) {
            ::android::sp<::android::hardware::camera::device::V3_2::ICameraDevice> device3_2;
            ALOGI("configureStreams: Testing camera device %s", name.c_str());
            env->mProvider->getCameraDeviceInterface_V3_x(
                name,
                [&](auto status, const auto& device) {
                    ALOGI("getCameraDeviceInterface_V3_x returns status:%d", (int)status);
                    ASSERT_EQ(Status::OK, status);
                    ASSERT_NE(device, nullptr);
                    device3_2 = device;
                });

            sp<DeviceCb> cb = new DeviceCb(this);
            sp<ICameraDeviceSession> session;
            device3_2->open(
                cb,
                [&](auto status, const auto& newSession) {
                    ALOGI("device::open returns status:%d", (int)status);
                    ASSERT_EQ(Status::OK, status);
                    ASSERT_NE(newSession, nullptr);
                    session = newSession;
                });

            camera_metadata_t *staticMeta;
            device3_2->getCameraCharacteristics([&] (Status s,
                    CameraMetadata metadata) {
                ASSERT_EQ(Status::OK, s);
                staticMeta =
                        reinterpret_cast<camera_metadata_t*>(metadata.data());
            });

            outputPreviewStreams.clear();
            ASSERT_EQ(Status::OK, getAvailableOutputStreams(staticMeta,
                    outputPreviewStreams, &previewThreshold));
            ASSERT_NE(0u, outputPreviewStreams.size());

            HalStreamConfiguration halStreamConfig;
            Stream previewStream = {streamId, StreamType::OUTPUT,
                    static_cast<uint32_t> (outputPreviewStreams[0].width),
                    static_cast<uint32_t> (outputPreviewStreams[0].height),
                    static_cast<PixelFormat> (outputPreviewStreams[0].format),
                    0, 0, StreamRotation::ROTATION_0};
            ::android::hardware::hidl_vec<Stream> streams = {previewStream};
            StreamConfiguration config = {streams,
                    StreamConfigurationMode::NORMAL_MODE};
            session->configureStreams(config, [&] (Status s,
                    HalStreamConfiguration halConfig) {
                ASSERT_EQ(Status::OK, s);
                ASSERT_EQ(1u, halConfig.streams.size());
                halStreamConfig = halConfig;
            });

            sp<GraphicBuffer> gb = new GraphicBuffer(previewStream.width,
                    previewStream.height,
                    static_cast<int32_t> (halStreamConfig.streams[0].overrideFormat),
                    1, halStreamConfig.streams[0].producerUsage,
                    halStreamConfig.streams[0].consumerUsage);

            StreamBuffer outputBuffer = {halStreamConfig.streams[0].id,
                    bufferId, hidl_handle(gb->getNativeBuffer()->handle),
                    BufferStatus::OK, nullptr, nullptr};
            ::android::hardware::hidl_vec<StreamBuffer> outputBuffers = {
                    outputBuffer};
            CaptureRequest request = {frameNumber, settings,
                    {-1, 0, nullptr, BufferStatus::ERROR, nullptr, nullptr},
                    outputBuffers};

            //Settings were not correctly initialized, we should fail here
            ASSERT_EQ(Status::INTERNAL_ERROR,
                    session->processCaptureRequest(request));

            session->close();
        }
    }
}

// Check whether an invalid capture request with missing output buffers
// will be reported correctly.
TEST_F(CameraHidlTest, processCaptureRequestInvalidSingleSnapshot) {
    CameraHidlEnvironment* env = CameraHidlEnvironment::Instance();
    hidl_vec<hidl_string> cameraDeviceNames = getCameraDeviceNames();
    std::vector<AvailableStream> outputBlobStreams;
    AvailableStream blobThreshold = {INT32_MAX, INT32_MAX,
            static_cast<int32_t>(PixelFormat::BLOB)};
    int32_t streamId = 0;
    uint64_t bufferId = 1;
    uint32_t frameNumber = 1;
    ::android::hardware::hidl_vec<uint8_t> settings;

    for (const auto& name : cameraDeviceNames) {
        if (getCameraDeviceVersion(name) == CAMERA_DEVICE_API_VERSION_3_2) {
            ::android::sp<::android::hardware::camera::device::V3_2::ICameraDevice> device3_2;
            ALOGI("configureStreams: Testing camera device %s", name.c_str());
            env->mProvider->getCameraDeviceInterface_V3_x(
                name,
                [&](auto status, const auto& device) {
                    ALOGI("getCameraDeviceInterface_V3_x returns status:%d", (int)status);
                    ASSERT_EQ(Status::OK, status);
                    ASSERT_NE(device, nullptr);
                    device3_2 = device;
                });

            sp<DeviceCb> cb = new DeviceCb(this);
            sp<ICameraDeviceSession> session;
            device3_2->open(
                cb,
                [&](auto status, const auto& newSession) {
                    ALOGI("device::open returns status:%d", (int)status);
                    ASSERT_EQ(Status::OK, status);
                    ASSERT_NE(newSession, nullptr);
                    session = newSession;
                });

            camera_metadata_t *staticMeta;
            device3_2->getCameraCharacteristics([&] (Status s,
                    CameraMetadata metadata) {
                ASSERT_EQ(Status::OK, s);
                staticMeta =
                        reinterpret_cast<camera_metadata_t*>(metadata.data());
            });

            outputBlobStreams.clear();
            ASSERT_EQ(Status::OK, getAvailableOutputStreams(staticMeta,
                    outputBlobStreams, &blobThreshold));
            ASSERT_NE(0u, outputBlobStreams.size());

            HalStreamConfiguration halStreamConfig;
            Stream previewStream = {streamId, StreamType::OUTPUT,
                    static_cast<uint32_t> (outputBlobStreams[0].width),
                    static_cast<uint32_t> (outputBlobStreams[0].height),
                    static_cast<PixelFormat> (outputBlobStreams[0].format),
                    0, 0, StreamRotation::ROTATION_0};
            ::android::hardware::hidl_vec<Stream> streams = {previewStream};
            StreamConfiguration config = {streams,
                    StreamConfigurationMode::NORMAL_MODE};
            session->configureStreams(config, [&] (Status s,
                    HalStreamConfiguration halConfig) {
                ASSERT_EQ(Status::OK, s);
                ASSERT_EQ(1u, halConfig.streams.size());
                halStreamConfig = halConfig;
            });

            RequestTemplate reqTemplate = RequestTemplate::STILL_CAPTURE;
            session->constructDefaultRequestSettings(reqTemplate,
                [&](auto status, const auto& req) {
                    ASSERT_EQ(Status::OK, status);
                    settings = req; });

            StreamBuffer outputBuffer = {halStreamConfig.streams[0].id,
                    bufferId, hidl_handle(nullptr), BufferStatus::OK,
                    nullptr, nullptr};
            ::android::hardware::hidl_vec<StreamBuffer> outputBuffers;
            CaptureRequest request = {frameNumber, settings,
                    {-1, 0, nullptr, BufferStatus::ERROR, nullptr, nullptr},
                    outputBuffers};

            //Output buffers are missing, we should fail here
            ASSERT_EQ(Status::INTERNAL_ERROR,
                    session->processCaptureRequest(request));

            session->close();
        }
    }
}

// Retrieve all valid output stream resolutions from the camera
// static characteristics.
Status CameraHidlTest::getAvailableOutputStreams(camera_metadata_t *staticMeta,