From 3450ba7879be6522ea46a56c5e66e5382f5dd5ba Mon Sep 17 00:00:00 2001 From: Ruben Brunk Date: Tue, 16 Jun 2015 11:00:37 -0700 Subject: camera2: Fix native binder interface and add tests. - Add CameraBinderTests for limited coverage of native camera2 binder interfaces for the camera service. - Fix several bugs in the native binder interfaces. Bug: 18468810 Change-Id: Iab2d81a5cacd20daf7454aeeed033cc13d88452c --- camera/ICameraService.cpp | 3 + camera/ICameraServiceListener.cpp | 14 +- camera/camera2/ICameraDeviceUser.cpp | 8 +- camera/camera2/OutputConfiguration.cpp | 5 + camera/tests/Android.mk | 3 +- camera/tests/CameraBinderTests.cpp | 484 ++++++++++++++++++++++++++++++ camera/tests/VendorTagDescriptorTests.cpp | 9 +- 7 files changed, 511 insertions(+), 15 deletions(-) create mode 100644 camera/tests/CameraBinderTests.cpp (limited to 'camera') diff --git a/camera/ICameraService.cpp b/camera/ICameraService.cpp index 7bb24ee..7c9720f 100644 --- a/camera/ICameraService.cpp +++ b/camera/ICameraService.cpp @@ -285,6 +285,7 @@ public: } Parcel data, reply; + data.writeInterfaceToken(ICameraService::getInterfaceDescriptor()); data.writeInt32(cameraId); remote()->transact(BnCameraService::GET_LEGACY_PARAMETERS, data, &reply); @@ -304,6 +305,7 @@ public: virtual status_t supportsCameraApi(int cameraId, int apiVersion) { Parcel data, reply; + data.writeInterfaceToken(ICameraService::getInterfaceDescriptor()); data.writeInt32(cameraId); data.writeInt32(apiVersion); remote()->transact(BnCameraService::SUPPORTS_CAMERA_API, data, &reply); @@ -315,6 +317,7 @@ public: virtual void notifySystemEvent(int32_t eventId, const int32_t* args, size_t len) { Parcel data, reply; + data.writeInterfaceToken(ICameraService::getInterfaceDescriptor()); data.writeInt32(eventId); data.writeInt32Array(len, args); remote()->transact(BnCameraService::NOTIFY_SYSTEM_EVENT, data, &reply, diff --git a/camera/ICameraServiceListener.cpp b/camera/ICameraServiceListener.cpp index 90a8bc2..0010325 100644 --- a/camera/ICameraServiceListener.cpp +++ b/camera/ICameraServiceListener.cpp @@ -45,8 +45,7 @@ public: virtual void onStatusChanged(Status status, int32_t cameraId) { Parcel data, reply; - data.writeInterfaceToken( - ICameraServiceListener::getInterfaceDescriptor()); + data.writeInterfaceToken(ICameraServiceListener::getInterfaceDescriptor()); data.writeInt32(static_cast(status)); data.writeInt32(cameraId); @@ -60,8 +59,7 @@ public: virtual void onTorchStatusChanged(TorchStatus status, const String16 &cameraId) { Parcel data, reply; - data.writeInterfaceToken( - ICameraServiceListener::getInterfaceDescriptor()); + data.writeInterfaceToken(ICameraServiceListener::getInterfaceDescriptor()); data.writeInt32(static_cast(status)); data.writeString16(cameraId); @@ -73,14 +71,12 @@ public: } }; -IMPLEMENT_META_INTERFACE(CameraServiceListener, - "android.hardware.ICameraServiceListener"); +IMPLEMENT_META_INTERFACE(CameraServiceListener, "android.hardware.ICameraServiceListener"); // ---------------------------------------------------------------------- -status_t BnCameraServiceListener::onTransact( - uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) -{ +status_t BnCameraServiceListener::onTransact(uint32_t code, const Parcel& data, Parcel* reply, + uint32_t flags) { switch(code) { case STATUS_CHANGED: { CHECK_INTERFACE(ICameraServiceListener, data, reply); diff --git a/camera/camera2/ICameraDeviceUser.cpp b/camera/camera2/ICameraDeviceUser.cpp index a7549f2..ffe974b 100644 --- a/camera/camera2/ICameraDeviceUser.cpp +++ b/camera/camera2/ICameraDeviceUser.cpp @@ -82,7 +82,7 @@ public: reply.readExceptionCode(); } - virtual status_t submitRequest(sp request, bool repeating, + virtual int submitRequest(sp request, bool repeating, int64_t *lastFrameNumber) { Parcel data, reply; @@ -111,13 +111,13 @@ public: } } - if ((res < NO_ERROR) || (resFrameNumber != NO_ERROR)) { + if (res < 0 || (resFrameNumber != NO_ERROR)) { res = FAILED_TRANSACTION; } return res; } - virtual status_t submitRequestList(List > requestList, bool repeating, + virtual int submitRequestList(List > requestList, bool repeating, int64_t *lastFrameNumber) { Parcel data, reply; @@ -151,7 +151,7 @@ public: resFrameNumber = reply.readInt64(lastFrameNumber); } } - if ((res < NO_ERROR) || (resFrameNumber != NO_ERROR)) { + if (res < 0 || (resFrameNumber != NO_ERROR)) { res = FAILED_TRANSACTION; } return res; diff --git a/camera/camera2/OutputConfiguration.cpp b/camera/camera2/OutputConfiguration.cpp index 24acaa0..20a23e0 100644 --- a/camera/camera2/OutputConfiguration.cpp +++ b/camera/camera2/OutputConfiguration.cpp @@ -65,6 +65,11 @@ OutputConfiguration::OutputConfiguration(const Parcel& parcel) { gbp.get(), String8(name).string()); } +OutputConfiguration::OutputConfiguration(sp& gbp, int rotation) { + mGbp = gbp; + mRotation = rotation; +} + status_t OutputConfiguration::writeToParcel(Parcel& parcel) const { parcel.writeInt32(mRotation); diff --git a/camera/tests/Android.mk b/camera/tests/Android.mk index 5d37f9e..3777d94 100644 --- a/camera/tests/Android.mk +++ b/camera/tests/Android.mk @@ -17,7 +17,8 @@ include $(CLEAR_VARS) LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk LOCAL_SRC_FILES:= \ - VendorTagDescriptorTests.cpp + VendorTagDescriptorTests.cpp \ + CameraBinderTests.cpp LOCAL_SHARED_LIBRARIES := \ libutils \ diff --git a/camera/tests/CameraBinderTests.cpp b/camera/tests/CameraBinderTests.cpp new file mode 100644 index 0000000..572fb72 --- /dev/null +++ b/camera/tests/CameraBinderTests.cpp @@ -0,0 +1,484 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_NDEBUG 0 +#define LOG_TAG "CameraBinderTests" + +#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 + +using namespace android; + +#define ASSERT_NOT_NULL(x) \ + ASSERT_TRUE((x) != nullptr) + +#define SETUP_TIMEOUT 2000000000 // ns +#define IDLE_TIMEOUT 2000000000 // ns + +// Stub listener implementation +class TestCameraServiceListener : public BnCameraServiceListener { + std::map mCameraTorchStatuses; + std::map mCameraStatuses; + mutable Mutex mLock; + mutable Condition mCondition; + mutable Condition mTorchCondition; +public: + virtual ~TestCameraServiceListener() {}; + + virtual void onStatusChanged(Status status, int32_t cameraId) { + Mutex::Autolock l(mLock); + mCameraStatuses[cameraId] = status; + mCondition.broadcast(); + }; + + virtual void onTorchStatusChanged(TorchStatus status, const String16& cameraId) { + Mutex::Autolock l(mLock); + mCameraTorchStatuses[cameraId] = status; + mTorchCondition.broadcast(); + }; + + bool waitForNumCameras(size_t num) const { + Mutex::Autolock l(mLock); + + if (mCameraStatuses.size() == num) { + return true; + } + + while (mCameraStatuses.size() < num) { + if (mCondition.waitRelative(mLock, SETUP_TIMEOUT) != OK) { + return false; + } + } + return true; + }; + + bool waitForTorchState(TorchStatus status, int32_t cameraId) const { + Mutex::Autolock l(mLock); + + const auto& iter = mCameraTorchStatuses.find(String16(String8::format("%d", cameraId))); + if (iter != mCameraTorchStatuses.end() && iter->second == status) { + return true; + } + + bool foundStatus = false; + while (!foundStatus) { + if (mTorchCondition.waitRelative(mLock, SETUP_TIMEOUT) != OK) { + return false; + } + const auto& iter = + mCameraTorchStatuses.find(String16(String8::format("%d", cameraId))); + foundStatus = (iter != mCameraTorchStatuses.end() && iter->second == status); + } + return true; + }; + + TorchStatus getTorchStatus(int32_t cameraId) const { + Mutex::Autolock l(mLock); + const auto& iter = mCameraTorchStatuses.find(String16(String8::format("%d", cameraId))); + if (iter == mCameraTorchStatuses.end()) { + return ICameraServiceListener::TORCH_STATUS_UNKNOWN; + } + return iter->second; + }; + + Status getStatus(int32_t cameraId) const { + Mutex::Autolock l(mLock); + const auto& iter = mCameraStatuses.find(cameraId); + if (iter == mCameraStatuses.end()) { + return ICameraServiceListener::STATUS_UNKNOWN; + } + return iter->second; + }; +}; + +// Callback implementation +class TestCameraDeviceCallbacks : public BnCameraDeviceCallbacks { +public: + enum Status { + IDLE, + ERROR, + PREPARED, + RUNNING, + SENT_RESULT, + UNINITIALIZED + }; + +protected: + bool mError; + Status mLastStatus; + mutable std::vector mStatusesHit; + mutable Mutex mLock; + mutable Condition mStatusCondition; +public: + TestCameraDeviceCallbacks() : mError(false), mLastStatus(UNINITIALIZED) {} + + virtual ~TestCameraDeviceCallbacks() {} + + virtual void onDeviceError(CameraErrorCode errorCode, + const CaptureResultExtras& resultExtras) { + ALOGE("%s: onDeviceError occurred with: %d", __FUNCTION__, static_cast(errorCode)); + Mutex::Autolock l(mLock); + mError = true; + mLastStatus = ERROR; + mStatusesHit.push_back(mLastStatus); + mStatusCondition.broadcast(); + } + + virtual void onDeviceIdle() { + Mutex::Autolock l(mLock); + mLastStatus = IDLE; + mStatusesHit.push_back(mLastStatus); + mStatusCondition.broadcast(); + } + + virtual void onCaptureStarted(const CaptureResultExtras& resultExtras, + int64_t timestamp) { + Mutex::Autolock l(mLock); + mLastStatus = RUNNING; + mStatusesHit.push_back(mLastStatus); + mStatusCondition.broadcast(); + } + + + virtual void onResultReceived(const CameraMetadata& metadata, + const CaptureResultExtras& resultExtras) { + Mutex::Autolock l(mLock); + mLastStatus = SENT_RESULT; + mStatusesHit.push_back(mLastStatus); + mStatusCondition.broadcast(); + } + + virtual void onPrepared(int streamId) { + Mutex::Autolock l(mLock); + mLastStatus = PREPARED; + mStatusesHit.push_back(mLastStatus); + mStatusCondition.broadcast(); + } + + // Test helper functions: + + bool hadError() const { + Mutex::Autolock l(mLock); + return mError; + } + + bool waitForStatus(Status status) const { + Mutex::Autolock l(mLock); + if (mLastStatus == status) { + return true; + } + + while (std::find(mStatusesHit.begin(), mStatusesHit.end(), status) + == mStatusesHit.end()) { + + if (mStatusCondition.waitRelative(mLock, IDLE_TIMEOUT) != OK) { + mStatusesHit.clear(); + return false; + } + } + mStatusesHit.clear(); + + return true; + + } + + void clearStatus() const { + Mutex::Autolock l(mLock); + mStatusesHit.clear(); + } + + bool waitForIdle() const { + return waitForStatus(IDLE); + } + +}; + +// Exercise basic binder calls for the camera service +TEST(CameraServiceBinderTest, CheckBinderCameraService) { + ProcessState::self()->startThreadPool(); + sp sm = defaultServiceManager(); + sp binder = sm->getService(String16("media.camera")); + ASSERT_NOT_NULL(binder); + sp service = interface_cast(binder); + + + int32_t numCameras = service->getNumberOfCameras(); + EXPECT_LE(0, numCameras); + + // Check listener binder calls + sp listener(new TestCameraServiceListener()); + EXPECT_EQ(OK, service->addListener(listener)); + + EXPECT_TRUE(listener->waitForNumCameras(numCameras)); + + for (int32_t i = 0; i < numCameras; i++) { + // We only care about binder calls for the Camera2 API. Camera1 is deprecated. + status_t camera2Support = service->supportsCameraApi(i, ICameraService::API_VERSION_2); + if (camera2Support != OK) { + EXPECT_EQ(-EOPNOTSUPP, camera2Support); + continue; + } + + // Check metadata binder call + CameraMetadata metadata; + EXPECT_EQ(OK, service->getCameraCharacteristics(i, &metadata)); + EXPECT_FALSE(metadata.isEmpty()); + + // Make sure we're available, or skip device tests otherwise + ICameraServiceListener::Status s = listener->getStatus(i); + EXPECT_EQ(ICameraServiceListener::STATUS_AVAILABLE, s); + if (s != ICameraServiceListener::STATUS_AVAILABLE) { + continue; + } + + // Check connect binder calls + sp callbacks(new TestCameraDeviceCallbacks()); + sp device; + EXPECT_EQ(OK, service->connectDevice(callbacks, i, String16("meeeeeeeee!"), + ICameraService::USE_CALLING_UID, /*out*/device)); + ASSERT_NE(nullptr, device.get()); + device->disconnect(); + EXPECT_FALSE(callbacks->hadError()); + + ICameraServiceListener::TorchStatus torchStatus = listener->getTorchStatus(i); + if (torchStatus == ICameraServiceListener::TORCH_STATUS_AVAILABLE_OFF) { + // Check torch calls + EXPECT_EQ(OK, service->setTorchMode(String16(String8::format("%d", i)), + /*enabled*/true, callbacks)); + EXPECT_TRUE(listener->waitForTorchState( + ICameraServiceListener::TORCH_STATUS_AVAILABLE_ON, i)); + EXPECT_EQ(OK, service->setTorchMode(String16(String8::format("%d", i)), + /*enabled*/false, callbacks)); + EXPECT_TRUE(listener->waitForTorchState( + ICameraServiceListener::TORCH_STATUS_AVAILABLE_OFF, i)); + } + } + + EXPECT_EQ(OK, service->removeListener(listener)); +} + +// Test fixture for client focused binder tests +class CameraClientBinderTest : public testing::Test { +protected: + sp service; + int32_t numCameras; + std::vector, sp>> openDeviceList; + sp serviceListener; + + std::pair, sp> openNewDevice(int deviceId) { + + sp callbacks(new TestCameraDeviceCallbacks()); + sp device; + { + SCOPED_TRACE("openNewDevice"); + EXPECT_EQ(OK, service->connectDevice(callbacks, deviceId, String16("meeeeeeeee!"), + ICameraService::USE_CALLING_UID, /*out*/device)); + } + auto p = std::make_pair(callbacks, device); + openDeviceList.push_back(p); + return p; + } + + void closeDevice(std::pair, sp>& p) { + if (p.second.get() != nullptr) { + p.second->disconnect(); + { + SCOPED_TRACE("closeDevice"); + EXPECT_FALSE(p.first->hadError()); + } + } + auto iter = std::find(openDeviceList.begin(), openDeviceList.end(), p); + if (iter != openDeviceList.end()) { + openDeviceList.erase(iter); + } + } + + virtual void SetUp() { + ProcessState::self()->startThreadPool(); + sp sm = defaultServiceManager(); + sp binder = sm->getService(String16("media.camera")); + service = interface_cast(binder); + serviceListener = new TestCameraServiceListener(); + service->addListener(serviceListener); + numCameras = service->getNumberOfCameras(); + } + + virtual void TearDown() { + service = nullptr; + numCameras = 0; + for (auto& p : openDeviceList) { + closeDevice(p); + } + } + +}; + +TEST_F(CameraClientBinderTest, CheckBinderCameraDeviceUser) { + ASSERT_NOT_NULL(service); + + EXPECT_TRUE(serviceListener->waitForNumCameras(numCameras)); + for (int32_t i = 0; i < numCameras; i++) { + // Make sure we're available, or skip device tests otherwise + ICameraServiceListener::Status s = serviceListener->getStatus(i); + EXPECT_EQ(ICameraServiceListener::STATUS_AVAILABLE, s); + if (s != ICameraServiceListener::STATUS_AVAILABLE) { + continue; + } + + auto p = openNewDevice(i); + sp callbacks = p.first; + sp device = p.second; + + // Setup a buffer queue; I'm just using the vendor opaque format here as that is + // guaranteed to be present + sp gbProducer; + sp gbConsumer; + BufferQueue::createBufferQueue(&gbProducer, &gbConsumer); + sp opaqueConsumer = new BufferItemConsumer(gbConsumer, + GRALLOC_USAGE_SW_READ_NEVER, /*maxImages*/2, /*controlledByApp*/true); + EXPECT_TRUE(opaqueConsumer.get() != nullptr); + opaqueConsumer->setName(String8("nom nom nom")); + + // Set to VGA dimens for default, as that is guaranteed to be present + EXPECT_EQ(OK, gbConsumer->setDefaultBufferSize(640, 480)); + EXPECT_EQ(OK, gbConsumer->setDefaultBufferFormat(HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED)); + + sp surface(new Surface(gbProducer, /*controlledByApp*/false)); + + OutputConfiguration output(gbProducer, /*rotation*/0); + + // Can we configure? + EXPECT_EQ(OK, device->beginConfigure()); + status_t streamId = device->createStream(output); + EXPECT_LE(0, streamId); + EXPECT_EQ(OK, device->endConfigure()); + EXPECT_FALSE(callbacks->hadError()); + + // Can we make requests? + CameraMetadata requestTemplate; + EXPECT_EQ(OK, device->createDefaultRequest(/*preview template*/1, + /*out*/&requestTemplate)); + sp request(new CaptureRequest()); + request->mMetadata = requestTemplate; + request->mSurfaceList.add(surface); + request->mIsReprocess = false; + int64_t lastFrameNumber = 0; + int64_t lastFrameNumberPrev = 0; + callbacks->clearStatus(); + int requestId = device->submitRequest(request, /*streaming*/true, /*out*/&lastFrameNumber); + EXPECT_TRUE(callbacks->waitForStatus(TestCameraDeviceCallbacks::SENT_RESULT)); + EXPECT_LE(0, requestId); + + // Can we stop requests? + EXPECT_EQ(OK, device->cancelRequest(requestId, /*out*/&lastFrameNumber)); + EXPECT_TRUE(callbacks->waitForIdle()); + EXPECT_FALSE(callbacks->hadError()); + + // Can we do it again? + lastFrameNumberPrev = lastFrameNumber; + lastFrameNumber = 0; + requestTemplate.clear(); + EXPECT_EQ(OK, device->createDefaultRequest(/*preview template*/1, + /*out*/&requestTemplate)); + sp request2(new CaptureRequest()); + request2->mMetadata = requestTemplate; + request2->mSurfaceList.add(surface); + request2->mIsReprocess = false; + callbacks->clearStatus(); + int requestId2 = device->submitRequest(request2, /*streaming*/true, + /*out*/&lastFrameNumber); + EXPECT_EQ(-1, lastFrameNumber); + lastFrameNumber = 0; + EXPECT_TRUE(callbacks->waitForStatus(TestCameraDeviceCallbacks::SENT_RESULT)); + EXPECT_LE(0, requestId2); + EXPECT_EQ(OK, device->cancelRequest(requestId2, /*out*/&lastFrameNumber)); + EXPECT_TRUE(callbacks->waitForIdle()); + EXPECT_LE(lastFrameNumberPrev, lastFrameNumber); + sleep(/*second*/1); // allow some time for errors to show up, if any + EXPECT_FALSE(callbacks->hadError()); + + // Can we do it with a request list? + lastFrameNumberPrev = lastFrameNumber; + lastFrameNumber = 0; + requestTemplate.clear(); + CameraMetadata requestTemplate2; + EXPECT_EQ(OK, device->createDefaultRequest(/*preview template*/1, + /*out*/&requestTemplate)); + EXPECT_EQ(OK, device->createDefaultRequest(/*preview template*/1, + /*out*/&requestTemplate2)); + sp request3(new CaptureRequest()); + sp request4(new CaptureRequest()); + request3->mMetadata = requestTemplate; + request3->mSurfaceList.add(surface); + request3->mIsReprocess = false; + request4->mMetadata = requestTemplate2; + request4->mSurfaceList.add(surface); + request4->mIsReprocess = false; + List> requestList; + requestList.push_back(request3); + requestList.push_back(request4); + + callbacks->clearStatus(); + int requestId3 = device->submitRequestList(requestList, /*streaming*/false, + /*out*/&lastFrameNumber); + EXPECT_TRUE(callbacks->waitForStatus(TestCameraDeviceCallbacks::SENT_RESULT)); + EXPECT_TRUE(callbacks->waitForIdle()); + EXPECT_LE(lastFrameNumberPrev, lastFrameNumber); + sleep(/*second*/1); // allow some time for errors to show up, if any + EXPECT_FALSE(callbacks->hadError()); + + // Can we unconfigure? + EXPECT_EQ(OK, device->beginConfigure()); + EXPECT_EQ(OK, device->deleteStream(streamId)); + EXPECT_EQ(OK, device->endConfigure()); + sleep(/*second*/1); // allow some time for errors to show up, if any + EXPECT_FALSE(callbacks->hadError()); + + closeDevice(p); + } + +}; diff --git a/camera/tests/VendorTagDescriptorTests.cpp b/camera/tests/VendorTagDescriptorTests.cpp index 6624e79..9082dbf 100644 --- a/camera/tests/VendorTagDescriptorTests.cpp +++ b/camera/tests/VendorTagDescriptorTests.cpp @@ -53,6 +53,10 @@ static bool ContainsTag(uint32_t* tagArray, size_t size, uint32_t tag) { extern "C" { +static int zero_get_tag_count(const vendor_tag_ops_t* vOps) { + return 0; +} + static int default_get_tag_count(const vendor_tag_ops_t* vOps) { return VENDOR_TAG_COUNT_ERR; } @@ -173,10 +177,13 @@ TEST(VendorTagDescriptorTest, ErrorConditions) { vendor_tag_ops_t vOps; FillWithDefaults(&vOps); + // Make empty tag count + vOps.get_tag_count = zero_get_tag_count; + // Ensure create fails when using null vOps EXPECT_EQ(BAD_VALUE, VendorTagDescriptor::createDescriptorFromOps(/*vOps*/NULL, vDesc)); - // Ensure create works when there are no vtags defined in a well-formed vOps + // Ensure creat succeeds for empty vendor tag ops ASSERT_EQ(OK, VendorTagDescriptor::createDescriptorFromOps(&vOps, vDesc)); // Ensure defaults are returned when no vtags are defined, or tag is unknown -- cgit v1.1