Camera2: Add DNG validation to DngCreatorTest
- Rename JNI library since it's more than just for NDK now
- Link against static version of dng_sdk with validate active
- Duplicate much of SDK dng_validate executable for validation
- Statically include dng_sdk dependencies as well
Bug: 23727371
Change-Id: Ifec2bae0f4b4d7acf3b17ee39c252e1046a9b7b9
diff --git a/tests/camera/libctscamera2jni/Android.mk b/tests/camera/libctscamera2jni/Android.mk
new file mode 100644
index 0000000..eac42f5
--- /dev/null
+++ b/tests/camera/libctscamera2jni/Android.mk
@@ -0,0 +1,53 @@
+# 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.
+#
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := libctscamera2_jni
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SRC_FILES := \
+ native-camera-jni.cpp \
+ dng-validate-jni.cpp
+
+LOCAL_C_INCLUDES := \
+ $(JNI_H_INCLUDE) \
+ system/core/include \
+ frameworks/av/include/camera/ndk \
+ frameworks/av/include/ndk \
+
+# Flags needed by DNG SDK
+LOCAL_CFLAGS := -DUNIX_ENV=1 -DqDNGBigEndian=0 -DqDNGThreadSafe=1 -DqDNGUseLibJPEG=1 -DqDNGUseXMP=0 -DqDNGValidate=1 -DqDNGValidateTarget=1 -DqAndroid=1 -fexceptions -Wsign-compare -Wno-reorder -Wframe-larger-than=20000
+
+# Flags to avoid warnings from DNG SDK
+LOCAL_CFLAGS += -Wno-unused-parameter
+
+LOCAL_STATIC_LIBRARIES := libdng_sdk_validate libjpeg_static libz
+LOCAL_SHARED_LIBRARIES := libandroid \
+ libnativehelper_compat_libc++ \
+ liblog \
+ libcamera2ndk \
+ libmediandk
+
+# NDK build, shared C++ runtime
+#LOCAL_SDK_VERSION := current
+#LOCAL_NDK_STL_VARIANT := c++_shared
+
+# Temporary workaround until camera2 NDK is active. See b/27102995.
+LOCAL_CXX_STL := libc++_static
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/tests/camera/libctscamera2jni/dng-validate-jni.cpp b/tests/camera/libctscamera2jni/dng-validate-jni.cpp
new file mode 100644
index 0000000..186cf3f
--- /dev/null
+++ b/tests/camera/libctscamera2jni/dng-validate-jni.cpp
@@ -0,0 +1,469 @@
+/*
+ * Copyright (C) 2016 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 "DngValidateCamera"
+#include <log/log.h>
+#include <jni.h>
+
+#include <string>
+#include <sstream>
+#include <iostream>
+
+/**
+ * Use DNG SDK to validate captured DNG file.
+ *
+ * This code is largely based on the dng_validate.cpp implementation included
+ * with the DNG SDK. The portions of this file that are from the DNG SDK are
+ * covered by the the DNG SDK license in /external/dng_sdk/LICENSE
+ */
+
+#include "dng_color_space.h"
+#include "dng_date_time.h"
+#include "dng_exceptions.h"
+#include "dng_file_stream.h"
+#include "dng_globals.h"
+#include "dng_host.h"
+#include "dng_ifd.h"
+#include "dng_image_writer.h"
+#include "dng_info.h"
+#include "dng_linearization_info.h"
+#include "dng_mosaic_info.h"
+#include "dng_negative.h"
+#include "dng_preview.h"
+#include "dng_render.h"
+#include "dng_simple_image.h"
+#include "dng_tag_codes.h"
+#include "dng_tag_types.h"
+#include "dng_tag_values.h"
+
+// Version of DNG validate referenced for this implementation
+#define kDNGValidateVersion "1.4"
+
+static bool gFourColorBayer = false;
+
+static int32 gMosaicPlane = -1;
+
+static uint32 gPreferredSize = 0;
+static uint32 gMinimumSize = 0;
+static uint32 gMaximumSize = 0;
+
+static uint32 gProxyDNGSize = 0;
+
+static const dng_color_space *gFinalSpace = &dng_space_sRGB::Get();
+
+static uint32 gFinalPixelType = ttByte;
+
+static dng_string gDumpStage1;
+static dng_string gDumpStage2;
+static dng_string gDumpStage3;
+static dng_string gDumpTIF;
+static dng_string gDumpDNG;
+
+/**
+ * Validate DNG file in provided buffer.
+ *
+ * Returns dng_error_none (0) on success, otherwise one of the
+ * dng_error_code enum values is returned.
+ *
+ * Warnings and errors found during validation are printed to stderr
+ */
+static dng_error_code dng_validate(const void* data, uint32_t count) {
+
+ ALOGI("Validating DNG buffer");
+
+ try {
+ dng_stream stream(data, count);
+
+ dng_host host;
+
+ host.SetPreferredSize(gPreferredSize);
+ host.SetMinimumSize(gMinimumSize);
+ host.SetMaximumSize(gMaximumSize);
+
+ host.ValidateSizes();
+
+ if (host.MinimumSize()) {
+ host.SetForPreview(true);
+ gDumpDNG.Clear();
+ }
+
+ if (gDumpDNG.NotEmpty()) {
+ host.SetSaveDNGVersion(dngVersion_SaveDefault);
+ host.SetSaveLinearDNG(false);
+ host.SetKeepOriginalFile(false);
+ }
+
+ // Read into the negative.
+
+ AutoPtr<dng_negative> negative;
+ {
+ dng_info info;
+ info.Parse(host, stream);
+ info.PostParse(host);
+ if (!info.IsValidDNG()) {
+ return dng_error_bad_format;
+ }
+
+ negative.Reset(host.Make_dng_negative());
+ negative->Parse(host, stream, info);
+ negative->PostParse(host, stream, info);
+
+ {
+ dng_timer timer("Raw image read time");
+ negative->ReadStage1Image(host, stream, info);
+ }
+
+ if (info.fMaskIndex != -1) {
+ dng_timer timer("Transparency mask read time");
+ negative->ReadTransparencyMask(host, stream, info);
+ }
+
+ negative->ValidateRawImageDigest(host);
+ }
+
+ // Option to write stage 1 image.
+
+ if (gDumpStage1.NotEmpty()) {
+ dng_file_stream stream2 (gDumpStage1.Get(), true);
+ const dng_image &stage1 = *negative->Stage1Image();
+ dng_image_writer writer;
+
+ writer.WriteTIFF(host,
+ stream2,
+ stage1,
+ stage1.Planes() >= 3 ? piRGB
+ : piBlackIsZero);
+
+ gDumpStage1.Clear();
+ }
+
+ // Metadata.
+
+ negative->SynchronizeMetadata();
+
+ // Four color Bayer option.
+
+ if (gFourColorBayer) {
+ negative->SetFourColorBayer();
+ }
+
+ // Build stage 2 image.
+
+ {
+ dng_timer timer("Linearization time");
+ negative->BuildStage2Image(host);
+ }
+
+ if (gDumpStage2.NotEmpty()) {
+ dng_file_stream stream2(gDumpStage2.Get(), true);
+ const dng_image &stage2 = *negative->Stage2Image();
+ dng_image_writer writer;
+
+ writer.WriteTIFF (host,
+ stream2,
+ stage2,
+ stage2.Planes() >= 3 ? piRGB
+ : piBlackIsZero);
+
+ gDumpStage2.Clear();
+ }
+
+ // Build stage 3 image.
+
+ {
+ dng_timer timer("Interpolate time");
+ negative->BuildStage3Image(host,
+ gMosaicPlane);
+ }
+
+ // Convert to proxy, if requested.
+
+ if (gProxyDNGSize) {
+ dng_timer timer("ConvertToProxy time");
+ dng_image_writer writer;
+
+ negative->ConvertToProxy(host,
+ writer,
+ gProxyDNGSize);
+ }
+
+ // Flatten transparency, if required.
+
+ if (negative->NeedFlattenTransparency(host)) {
+ dng_timer timer("FlattenTransparency time");
+ negative->FlattenTransparency(host);
+ }
+
+ if (gDumpStage3.NotEmpty()) {
+ dng_file_stream stream2(gDumpStage3.Get(), true);
+ const dng_image &stage3 = *negative->Stage3Image();
+ dng_image_writer writer;
+
+ writer.WriteTIFF (host,
+ stream2,
+ stage3,
+ stage3.Planes () >= 3 ? piRGB
+ : piBlackIsZero);
+
+ gDumpStage3.Clear();
+ }
+
+ // Output DNG file if requested.
+
+ if (gDumpDNG.NotEmpty()) {
+ // Build the preview list.
+ dng_preview_list previewList;
+ dng_date_time_info dateTimeInfo;
+ CurrentDateTimeAndZone(dateTimeInfo);
+
+ for (uint32 previewIndex = 0; previewIndex < 2; previewIndex++) {
+
+ // Skip preview if writing a compresssed main image to save space
+ // in this example code.
+ if (negative->RawJPEGImage() != NULL && previewIndex > 0) {
+ break;
+ }
+
+ // Report timing.
+ dng_timer timer(previewIndex == 0 ? "Build thumbnail time"
+ : "Build preview time");
+
+ // Render a preview sized image.
+ AutoPtr<dng_image> previewImage;
+
+ {
+ dng_render render (host, *negative);
+ render.SetFinalSpace (negative->IsMonochrome() ?
+ dng_space_GrayGamma22::Get() : dng_space_sRGB::Get());
+ render.SetFinalPixelType (ttByte);
+ render.SetMaximumSize (previewIndex == 0 ? 256 : 1024);
+
+ previewImage.Reset (render.Render());
+ }
+
+ // Don't write the preview if it is same size as thumbnail.
+
+ if (previewIndex > 0 &&
+ Max_uint32(previewImage->Bounds().W(),
+ previewImage->Bounds().H()) <= 256) {
+ break;
+ }
+
+ // If we have compressed JPEG data, create a compressed thumbnail. Otherwise
+ // save a uncompressed thumbnail.
+ bool useCompressedPreview = (negative->RawJPEGImage() != NULL) ||
+ (previewIndex > 0);
+
+ AutoPtr<dng_preview> preview (useCompressedPreview ?
+ (dng_preview *) new dng_jpeg_preview :
+ (dng_preview *) new dng_image_preview);
+
+ // Setup up preview info.
+
+ preview->fInfo.fApplicationName.Set("dng_validate");
+ preview->fInfo.fApplicationVersion.Set(kDNGValidateVersion);
+
+ preview->fInfo.fSettingsName.Set("Default");
+
+ preview->fInfo.fColorSpace = previewImage->Planes() == 1 ?
+ previewColorSpace_GrayGamma22 :
+ previewColorSpace_sRGB;
+
+ preview->fInfo.fDateTime = dateTimeInfo.Encode_ISO_8601();
+
+ if (!useCompressedPreview) {
+ dng_image_preview *imagePreview = static_cast<dng_image_preview *>(preview.Get());
+ imagePreview->fImage.Reset(previewImage.Release());
+ } else {
+ dng_jpeg_preview *jpegPreview = static_cast<dng_jpeg_preview *>(preview.Get());
+ int32 quality = (previewIndex == 0 ? 8 : 5);
+ dng_image_writer writer;
+ writer.EncodeJPEGPreview (host,
+ *previewImage,
+ *jpegPreview,
+ quality);
+ }
+ previewList.Append (preview);
+ }
+
+ // Write DNG file.
+
+ dng_file_stream stream2(gDumpDNG.Get(), true);
+
+ {
+ dng_timer timer("Write DNG time");
+ dng_image_writer writer;
+
+ writer.WriteDNG(host,
+ stream2,
+ *negative.Get(),
+ &previewList,
+ dngVersion_Current,
+ false);
+ }
+
+ gDumpDNG.Clear();
+ }
+
+ // Output TIF file if requested.
+ if (gDumpTIF.NotEmpty()) {
+
+ // Render final image.
+
+ dng_render render(host, *negative);
+
+ render.SetFinalSpace(*gFinalSpace );
+ render.SetFinalPixelType(gFinalPixelType);
+
+ if (host.MinimumSize()) {
+ dng_point stage3Size = negative->Stage3Image()->Size();
+ render.SetMaximumSize (Max_uint32(stage3Size.v,
+ stage3Size.h));
+ }
+
+ AutoPtr<dng_image> finalImage;
+
+ {
+ dng_timer timer("Render time");
+ finalImage.Reset(render.Render());
+ }
+
+ finalImage->Rotate(negative->Orientation());
+
+ // Now that Camera Raw supports non-raw formats, we should
+ // not keep any Camera Raw settings in the XMP around when
+ // writing rendered files.
+#if qDNGUseXMP
+ if (negative->GetXMP()) {
+ negative->GetXMP()->RemoveProperties(XMP_NS_CRS);
+ negative->GetXMP()->RemoveProperties(XMP_NS_CRSS);
+ }
+#endif
+
+ // Write TIF file.
+ dng_file_stream stream2(gDumpTIF.Get(), true);
+
+ {
+ dng_timer timer("Write TIFF time");
+ dng_image_writer writer;
+
+ writer.WriteTIFF(host,
+ stream2,
+ *finalImage.Get(),
+ finalImage->Planes() >= 3 ? piRGB
+ : piBlackIsZero,
+ ccUncompressed,
+ negative.Get(),
+ &render.FinalSpace());
+ }
+ gDumpTIF.Clear();
+ }
+ } catch (const dng_exception &except) {
+ return except.ErrorCode();
+ } catch (...) {
+ return dng_error_unknown;
+ }
+
+ ALOGI("DNG validation complete");
+
+ return dng_error_none;
+}
+
+extern "C" jboolean
+Java_android_hardware_camera2_cts_DngCreatorTest_validateDngNative(
+ JNIEnv* env, jclass /*clazz*/, jbyteArray dngBuffer) {
+
+ jbyte* buffer = env->GetByteArrayElements(dngBuffer, NULL);
+ jsize bufferCount = env->GetArrayLength(dngBuffer);
+ if (buffer == nullptr) {
+ ALOGE("Unable to map DNG buffer to native");
+ return JNI_FALSE;
+ }
+
+ // DNG parsing warnings/errors fprintfs are spread throughout the DNG SDK,
+ // guarded by the qDNGValidate define flag. To avoid modifying the SDK,
+ // redirect stderr to a pipe to capture output locally.
+
+ int pipeFds[2];
+ int err;
+
+ err = pipe(pipeFds);
+ if (err != 0) {
+ ALOGE("Error redirecting dng_validate output: %d", errno);
+ env->ReleaseByteArrayElements(dngBuffer, buffer, 0);
+ return JNI_FALSE;
+ }
+
+ int stderrFd = dup(fileno(stderr));
+ dup2(pipeFds[1], fileno(stderr));
+ close(pipeFds[1]);
+
+ // Actually run the validation
+ dng_error_code dng_err = dng_validate(buffer, bufferCount);
+
+ env->ReleaseByteArrayElements(dngBuffer, buffer, 0);
+
+ // Restore stderr and read out pipe
+ dup2(stderrFd, fileno(stderr));
+
+ std::stringstream errorStream;
+ const size_t BUF_SIZE = 256;
+ char readBuf[BUF_SIZE];
+
+ ssize_t count = 0;
+ while((count = read(pipeFds[0], readBuf, BUF_SIZE)) > 0) {
+ errorStream.write(readBuf, count);
+ }
+ if (count < 0) {
+ ALOGE("Error reading from dng_validate output pipe: %d", errno);
+ return JNI_FALSE;
+ }
+ close(pipeFds[1]);
+
+ std::string line;
+ int lineCount = 0;
+ ALOGI("Output from DNG validation:");
+ // dng_validate doesn't actually propagate all errors/warnings to the
+ // return error code, so look for an error pattern in output to detect
+ // problems. Also make sure the output is long enough since some non-error
+ // content should always be printed.
+ while(std::getline(errorStream, line, '\n')) {
+ lineCount++;
+ if ( (line.size() > 3) &&
+ (line[0] == line[1]) &&
+ (line[1] == line[2]) &&
+ (line[2] == '*') ) {
+ // Found a warning or error, so need to fail the test
+ if (dng_err == dng_error_none) {
+ dng_err = dng_error_bad_format;
+ }
+ ALOGE("**|%s", line.c_str());
+ } else {
+ ALOGI(" |%s", line.c_str());
+ }
+ }
+ // If no output is produced, assume something went wrong
+ if (lineCount < 3) {
+ ALOGE("Validation output less than expected!");
+ dng_err = dng_error_unknown;
+ }
+ if (dng_err != dng_error_none) {
+ ALOGE("DNG validation failed!");
+ }
+
+ return (dng_err == dng_error_none) ? JNI_TRUE : JNI_FALSE;
+}
diff --git a/tests/camera/libctscamera2jni/native-camera-jni.cpp b/tests/camera/libctscamera2jni/native-camera-jni.cpp
new file mode 100644
index 0000000..90c34db
--- /dev/null
+++ b/tests/camera/libctscamera2jni/native-camera-jni.cpp
@@ -0,0 +1,1844 @@
+/*
+ * 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 "NativeCamera"
+#include <log/log.h>
+
+#include <string>
+#include <map>
+#include <mutex>
+#include <unistd.h>
+#include <assert.h>
+#include <jni.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <android/native_window_jni.h>
+
+#include "NdkCameraError.h"
+#include "NdkCameraManager.h"
+#include "NdkCameraDevice.h"
+#include "NdkCameraCaptureSession.h"
+#include "NdkImage.h"
+#include "NdkImageReader.h"
+
+#define LOG_ERROR(buf, ...) sprintf(buf, __VA_ARGS__); \
+ ALOGE("%s", buf);
+
+namespace {
+ const int MAX_ERROR_STRING_LEN = 512;
+ char errorString[MAX_ERROR_STRING_LEN];
+}
+
+class CameraServiceListener {
+ public:
+ static void onAvailable(void* obj, const char* cameraId) {
+ ALOGV("Camera %s onAvailable", cameraId);
+ if (obj == nullptr) {
+ return;
+ }
+ CameraServiceListener* thiz = reinterpret_cast<CameraServiceListener*>(obj);
+ std::lock_guard<std::mutex> lock(thiz->mMutex);
+ thiz->mOnAvailableCount++;
+ thiz->mAvailableMap[cameraId] = true;
+ return;
+ }
+
+ static void onUnavailable(void* obj, const char* cameraId) {
+ ALOGV("Camera %s onUnavailable", cameraId);
+ if (obj == nullptr) {
+ return;
+ }
+ CameraServiceListener* thiz = reinterpret_cast<CameraServiceListener*>(obj);
+ std::lock_guard<std::mutex> lock(thiz->mMutex);
+ thiz->mOnUnavailableCount++;
+ thiz->mAvailableMap[cameraId] = false;
+ return;
+ }
+
+ void resetCount() {
+ std::lock_guard<std::mutex> lock(mMutex);
+ mOnAvailableCount = 0;
+ mOnUnavailableCount = 0;
+ return;
+ }
+
+ int getAvailableCount() {
+ std::lock_guard<std::mutex> lock(mMutex);
+ return mOnAvailableCount;
+ }
+
+ int getUnavailableCount() {
+ std::lock_guard<std::mutex> lock(mMutex);
+ return mOnUnavailableCount;
+ }
+
+ bool isAvailable(const char* cameraId) {
+ std::lock_guard<std::mutex> lock(mMutex);
+ if (mAvailableMap.count(cameraId) == 0) {
+ return false;
+ }
+ return mAvailableMap[cameraId];
+ }
+
+ private:
+ std::mutex mMutex;
+ int mOnAvailableCount = 0;
+ int mOnUnavailableCount = 0;
+ std::map<std::string, bool> mAvailableMap;
+};
+
+class CameraDeviceListener {
+ public:
+ static void onDisconnected(void* obj, ACameraDevice* device) {
+ ALOGV("Camera %s is disconnected!", ACameraDevice_getId(device));
+ if (obj == nullptr) {
+ return;
+ }
+ CameraDeviceListener* thiz = reinterpret_cast<CameraDeviceListener*>(obj);
+ std::lock_guard<std::mutex> lock(thiz->mMutex);
+ thiz->mOnDisconnect++;
+ return;
+ }
+
+ static void onError(void* obj, ACameraDevice* device, int errorCode) {
+ ALOGV("Camera %s receive error %d!", ACameraDevice_getId(device), errorCode);
+ if (obj == nullptr) {
+ return;
+ }
+ CameraDeviceListener* thiz = reinterpret_cast<CameraDeviceListener*>(obj);
+ std::lock_guard<std::mutex> lock(thiz->mMutex);
+ thiz->mOnError++;
+ thiz->mLatestError = errorCode;
+ return;
+ }
+
+ private:
+ std::mutex mMutex;
+ int mOnDisconnect = 0;
+ int mOnError = 0;
+ int mLatestError = 0;
+};
+
+class CaptureSessionListener {
+
+ public:
+ static void onClosed(void* obj, ACameraCaptureSession *session) {
+ // TODO: might want an API to query cameraId even session is closed?
+ ALOGV("Session %p is closed!", session);
+ if (obj == nullptr) {
+ return;
+ }
+ CaptureSessionListener* thiz = reinterpret_cast<CaptureSessionListener*>(obj);
+ std::lock_guard<std::mutex> lock(thiz->mMutex);
+ thiz->mIsClosed = true;
+ thiz->mOnClosed++; // Should never > 1
+ }
+
+ static void onReady(void* obj, ACameraCaptureSession *session) {
+ ALOGV("%s", __FUNCTION__);
+ if (obj == nullptr) {
+ return;
+ }
+ CaptureSessionListener* thiz = reinterpret_cast<CaptureSessionListener*>(obj);
+ std::lock_guard<std::mutex> lock(thiz->mMutex);
+ ACameraDevice* device = nullptr;
+ camera_status_t ret = ACameraCaptureSession_getDevice(session, &device);
+ // There will be one onReady fired after session closed
+ if (ret != ACAMERA_OK && !thiz->mIsClosed) {
+ ALOGE("%s Getting camera device from session callback failed!",
+ __FUNCTION__);
+ thiz->mInError = true;
+ }
+ ALOGV("Session for camera %s is ready!", ACameraDevice_getId(device));
+ thiz->mIsIdle = true;
+ thiz->mOnReady++;
+ }
+
+ static void onActive(void* obj, ACameraCaptureSession *session) {
+ ALOGV("%s", __FUNCTION__);
+ if (obj == nullptr) {
+ return;
+ }
+ CaptureSessionListener* thiz = reinterpret_cast<CaptureSessionListener*>(obj);
+ std::lock_guard<std::mutex> lock(thiz->mMutex);
+ ACameraDevice* device = nullptr;
+ camera_status_t ret = ACameraCaptureSession_getDevice(session, &device);
+ if (ret != ACAMERA_OK) {
+ ALOGE("%s Getting camera device from session callback failed!",
+ __FUNCTION__);
+ thiz->mInError = true;
+ }
+ ALOGV("Session for camera %s is busy!", ACameraDevice_getId(device));
+ thiz->mIsIdle = false;
+ thiz->mOnActive;
+ }
+
+ bool isClosed() {
+ std::lock_guard<std::mutex> lock(mMutex);
+ return mIsClosed;
+ }
+
+ bool isIdle() {
+ std::lock_guard<std::mutex> lock(mMutex);
+ return mIsIdle;
+ }
+
+ bool isInError() {
+ std::lock_guard<std::mutex> lock(mMutex);
+ return mInError;
+ }
+
+ int onClosedCount() {
+ std::lock_guard<std::mutex> lock(mMutex);
+ return mOnClosed;
+ }
+
+ int onReadyCount() {
+ std::lock_guard<std::mutex> lock(mMutex);
+ return mOnReady;
+ }
+
+ int onActiveCount() {
+ std::lock_guard<std::mutex> lock(mMutex);
+ return mOnActive;
+ }
+
+ void reset() {
+ std::lock_guard<std::mutex> lock(mMutex);
+ mIsClosed = false;
+ mIsIdle = true;
+ mInError = false;
+ mOnClosed = 0;
+ mOnReady = 0;
+ mOnActive = 0;
+ }
+
+ private:
+ std::mutex mMutex;
+ bool mIsClosed = false;
+ bool mIsIdle = true;
+ bool mInError = false; // should always stay false
+ int mOnClosed = 0;
+ int mOnReady = 0;
+ int mOnActive = 0;
+};
+
+class ImageReaderListener {
+ public:
+ static void onImageAvailable(void* obj, AImageReader* reader) {
+ ALOGV("%s", __FUNCTION__);
+ if (obj == nullptr) {
+ return;
+ }
+ ImageReaderListener* thiz = reinterpret_cast<ImageReaderListener*>(obj);
+ std::lock_guard<std::mutex> lock(thiz->mMutex);
+ thiz->mOnImageAvailableCount++;
+
+ AImage* img = nullptr;
+ media_status_t ret = AImageReader_acquireNextImage(reader, &img);
+ if (ret != AMEDIA_OK || img == nullptr) {
+ ALOGE("%s: acquire image from reader %p failed! ret: %d, img %p",
+ __FUNCTION__, reader, ret, img);
+ return;
+ }
+
+ // TODO: validate image content
+ int32_t format = -1;
+ ret = AImage_getFormat(img, &format);
+ if (ret != AMEDIA_OK || format == -1) {
+ ALOGE("%s: get format for image %p failed! ret: %d, format %d",
+ __FUNCTION__, img, ret, format);
+ }
+
+ // Save jpeg to SD card
+ if (thiz->mDumpFilePathBase && format == AIMAGE_FORMAT_JPEG) {
+ int32_t numPlanes = 0;
+ ret = AImage_getNumberOfPlanes(img, &numPlanes);
+ if (ret != AMEDIA_OK || numPlanes != 1) {
+ ALOGE("%s: get numPlanes for image %p failed! ret: %d, numPlanes %d",
+ __FUNCTION__, img, ret, numPlanes);
+ AImage_delete(img);
+ return;
+ }
+
+ int32_t width = -1, height = -1;
+ ret = AImage_getWidth(img, &width);
+ if (ret != AMEDIA_OK || width <= 0) {
+ ALOGE("%s: get width for image %p failed! ret: %d, width %d",
+ __FUNCTION__, img, ret, width);
+ AImage_delete(img);
+ return;
+ }
+
+ ret = AImage_getHeight(img, &height);
+ if (ret != AMEDIA_OK || height <= 0) {
+ ALOGE("%s: get height for image %p failed! ret: %d, height %d",
+ __FUNCTION__, img, ret, height);
+ AImage_delete(img);
+ return;
+ }
+
+ uint8_t* data = nullptr;
+ int dataLength = 0;
+ ret = AImage_getPlaneData(img, /*planeIdx*/0, &data, &dataLength);
+ if (ret != AMEDIA_OK || data == nullptr || dataLength <= 0) {
+ ALOGE("%s: get jpeg data for image %p failed! ret: %d, data %p, len %d",
+ __FUNCTION__, img, ret, data, dataLength);
+ AImage_delete(img);
+ return;
+ }
+
+#if 0
+ char dumpFilePath[512];
+ sprintf(dumpFilePath, "%s/%dx%d.jpg", thiz->mDumpFilePathBase, width, height);
+ ALOGI("Writing jpeg file to %s", dumpFilePath);
+ FILE* file = fopen(dumpFilePath,"w+");
+
+ if (file != nullptr) {
+ fwrite(data, 1, dataLength, file);
+ fflush(file);
+ fclose(file);
+ }
+#endif
+ }
+
+ AImage_delete(img);
+ }
+
+ int onImageAvailableCount() {
+ std::lock_guard<std::mutex> lock(mMutex);
+ return mOnImageAvailableCount;
+ }
+
+ void setDumpFilePathBase(const char* path) {
+ std::lock_guard<std::mutex> lock(mMutex);
+ mDumpFilePathBase = path;
+ }
+
+ void reset() {
+ std::lock_guard<std::mutex> lock(mMutex);
+ mOnImageAvailableCount = 0;
+ mDumpFilePathBase = nullptr;
+ }
+
+ private:
+ // TODO: add mReader to make sure each listener is associated to one reader?
+ std::mutex mMutex;
+ int mOnImageAvailableCount = 0;
+ const char* mDumpFilePathBase = nullptr;
+};
+
+class StaticInfo {
+ public:
+ StaticInfo(ACameraMetadata* chars) : mChars(chars) {}
+
+ bool isColorOutputSupported() {
+ return isCapabilitySupported(ACAMERA_REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE);
+ }
+
+ bool isCapabilitySupported(acamera_metadata_enum_android_request_available_capabilities_t cap) {
+ ACameraMetadata_const_entry entry;
+ ACameraMetadata_getConstEntry(mChars, ACAMERA_REQUEST_AVAILABLE_CAPABILITIES, &entry);
+ for (uint32_t i = 0; i < entry.count; i++) {
+ if (entry.data.u8[i] == cap) {
+ return true;
+ }
+ }
+ return false;
+ }
+ private:
+ const ACameraMetadata* mChars;
+};
+
+class PreviewTestCase {
+ public:
+ ~PreviewTestCase() {
+ resetCamera();
+ deInit();
+ if (mCameraManager) {
+ ACameraManager_delete(mCameraManager);
+ mCameraManager = nullptr;
+ }
+ }
+
+ PreviewTestCase() {
+ // create is guaranteed to succeed;
+ createManager();
+ }
+
+ // Free all resources except camera manager
+ void resetCamera() {
+ mReaderListener.reset();
+ mSessionListener.reset();
+ if (mSession) {
+ ACameraCaptureSession_close(mSession);
+ mSession = nullptr;
+ }
+ if (mDevice) {
+ ACameraDevice_close(mDevice);
+ mDevice = nullptr;
+ }
+ if (mImgReader) {
+ AImageReader_delete(mImgReader);
+ // No need to call ANativeWindow_release on imageReaderAnw
+ mImgReaderAnw = nullptr;
+ mImgReader = nullptr;
+ }
+ if (mPreviewAnw) {
+ ANativeWindow_release(mPreviewAnw);
+ mPreviewAnw = nullptr;
+ }
+ if (mOutputs) {
+ ACaptureSessionOutputContainer_free(mOutputs);
+ mOutputs = nullptr;
+ }
+ if (mPreviewOutput) {
+ ACaptureSessionOutput_free(mPreviewOutput);
+ mPreviewOutput = nullptr;
+ }
+ if (mImgReaderOutput) {
+ ACaptureSessionOutput_free(mImgReaderOutput);
+ mImgReaderOutput = nullptr;
+ }
+ if (mPreviewRequest) {
+ ACaptureRequest_free(mPreviewRequest);
+ mPreviewRequest = nullptr;
+ }
+ if (mStillRequest) {
+ ACaptureRequest_free(mStillRequest);
+ mStillRequest = nullptr;
+ }
+ if (mReqPreviewOutput) {
+ ACameraOutputTarget_free(mReqPreviewOutput);
+ mReqPreviewOutput = nullptr;
+ }
+ if (mReqImgReaderOutput) {
+ ACameraOutputTarget_free(mReqImgReaderOutput);
+ mReqImgReaderOutput = nullptr;
+ }
+
+ mImgReaderInited = false;
+ mPreviewInited = false;
+ }
+
+ camera_status_t initWithErrorLog() {
+ camera_status_t ret = ACameraManager_getCameraIdList(
+ mCameraManager, &mCameraIdList);
+ if (ret != ACAMERA_OK) {
+ LOG_ERROR(errorString, "Get camera id list failed: ret %d", ret);
+ return ret;
+ }
+ ret = ACameraManager_registerAvailabilityCallback(mCameraManager, &mServiceCb);
+ if (ret != ACAMERA_OK) {
+ LOG_ERROR(errorString, "Register availability callback failed: ret %d", ret);
+ return ret;
+ }
+ mMgrInited = true;
+ return ACAMERA_OK;
+ }
+
+ camera_status_t deInit () {
+ if (!mMgrInited) {
+ return ACAMERA_OK;
+ }
+
+ camera_status_t ret = ACameraManager_unregisterAvailabilityCallback(
+ mCameraManager, &mServiceCb);
+ if (ret != ACAMERA_OK) {
+ ALOGE("Unregister availability callback failed: ret %d", ret);
+ return ret;
+ }
+
+ if (mCameraIdList) {
+ ACameraManager_deleteCameraIdList(mCameraIdList);
+ mCameraIdList = nullptr;
+ }
+ mMgrInited = false;
+ return ACAMERA_OK;
+ }
+
+ int getNumCameras() {
+ if (!mMgrInited || !mCameraIdList) {
+ return -1;
+ }
+ return mCameraIdList->numCameras;
+ }
+
+ const char* getCameraId(int idx) {
+ if (!mMgrInited || !mCameraIdList || idx < 0 || idx >= mCameraIdList->numCameras) {
+ return nullptr;
+ }
+ return mCameraIdList->cameraIds[idx];
+ }
+
+ camera_status_t openCamera(const char* cameraId) {
+ if (mDevice) {
+ ALOGE("Cannot open camera before closing previously open one");
+ return ACAMERA_ERROR_INVALID_PARAMETER;
+ }
+ mCameraId = cameraId;
+ return ACameraManager_openCamera(mCameraManager, cameraId, &mDeviceCb, &mDevice);
+ }
+
+ camera_status_t closeCamera() {
+ camera_status_t ret = ACameraDevice_close(mDevice);
+ mDevice = nullptr;
+ return ret;
+ }
+
+ bool isCameraAvailable(const char* cameraId) {
+ if (!mMgrInited) {
+ ALOGE("Camera service listener has not been registered!");
+ }
+ return mServiceListener.isAvailable(cameraId);
+ }
+
+ media_status_t initImageReaderWithErrorLog(
+ int32_t width, int32_t height, int32_t format, int32_t maxImages) {
+ if (mImgReader || mImgReaderAnw) {
+ LOG_ERROR(errorString, "Cannot init image reader before closing existing one");
+ return AMEDIA_ERROR_UNKNOWN;
+ }
+
+ media_status_t ret = AImageReader_new(
+ width, height, format,
+ maxImages, &mImgReader);
+ if (ret != AMEDIA_OK) {
+ LOG_ERROR(errorString, "Create image reader. ret %d", ret);
+ return ret;
+ }
+ if (mImgReader == nullptr) {
+ LOG_ERROR(errorString, "null image reader created");
+ return AMEDIA_ERROR_UNKNOWN;
+ }
+
+ ret = AImageReader_setImageListener(
+ mImgReader, &mReaderCb);
+ if (ret != AMEDIA_OK) {
+ LOG_ERROR(errorString, "Set AImageReader listener failed. ret %d", ret);
+ return ret;
+ }
+
+ ret = AImageReader_getWindow(mImgReader, &mImgReaderAnw);
+ if (ret != AMEDIA_OK) {
+ LOG_ERROR(errorString, "AImageReader_getWindow failed. ret %d", ret);
+ return ret;
+ }
+ if (mImgReaderAnw == nullptr) {
+ LOG_ERROR(errorString, "Null ANW from AImageReader!");
+ return AMEDIA_ERROR_UNKNOWN;
+ }
+ mImgReaderInited = true;
+ return AMEDIA_OK;
+ }
+
+ ANativeWindow* initPreviewAnw(JNIEnv* env, jobject jSurface) {
+ if (mPreviewAnw) {
+ ALOGE("Cannot init preview twice!");
+ return nullptr;
+ }
+ mPreviewAnw = ANativeWindow_fromSurface(env, jSurface);
+ mPreviewInited = true;
+ return mPreviewAnw;
+ }
+
+ camera_status_t createCaptureSessionWithLog() {
+ if (mSession) {
+ LOG_ERROR(errorString, "Cannot create session before closing existing one");
+ return ACAMERA_ERROR_UNKNOWN;
+ }
+
+ if (!mMgrInited || (!mImgReaderInited && !mPreviewInited)) {
+ LOG_ERROR(errorString, "Cannot create session. mgrInit %d readerInit %d previewInit %d",
+ mMgrInited, mImgReaderInited, mPreviewInited);
+ return ACAMERA_ERROR_UNKNOWN;
+ }
+
+ camera_status_t ret = ACaptureSessionOutputContainer_create(&mOutputs);
+ if (ret != ACAMERA_OK) {
+ LOG_ERROR(errorString, "Create capture session output container failed. ret %d", ret);
+ return ret;
+ }
+
+ if (mImgReaderInited) {
+ ret = ACaptureSessionOutput_create(mImgReaderAnw, &mImgReaderOutput);
+ if (ret != ACAMERA_OK || mImgReaderOutput == nullptr) {
+ LOG_ERROR(errorString,
+ "Sesssion image reader output create fail! ret %d output %p",
+ ret, mImgReaderOutput);
+ if (ret == ACAMERA_OK) {
+ ret = ACAMERA_ERROR_UNKNOWN; // ret OK but output is null
+ }
+ return ret;
+ }
+
+ ret = ACaptureSessionOutputContainer_add(mOutputs, mImgReaderOutput);
+ if (ret != ACAMERA_OK) {
+ LOG_ERROR(errorString, "Sesssion image reader output add failed! ret %d", ret);
+ return ret;
+ }
+ }
+
+ if (mPreviewInited) {
+ ret = ACaptureSessionOutput_create(mPreviewAnw, &mPreviewOutput);
+ if (ret != ACAMERA_OK || mPreviewOutput == nullptr) {
+ LOG_ERROR(errorString,
+ "Sesssion preview output create fail! ret %d output %p",
+ ret, mPreviewOutput);
+ if (ret == ACAMERA_OK) {
+ ret = ACAMERA_ERROR_UNKNOWN; // ret OK but output is null
+ }
+ return ret;
+ }
+
+ ret = ACaptureSessionOutputContainer_add(mOutputs, mPreviewOutput);
+ if (ret != ACAMERA_OK) {
+ LOG_ERROR(errorString, "Sesssion preview output add failed! ret %d", ret);
+ return ret;
+ }
+ }
+
+ ret = ACameraDevice_createCaptureSession(
+ mDevice, mOutputs, &mSessionCb, &mSession);
+ if (ret != ACAMERA_OK || mSession == nullptr) {
+ LOG_ERROR(errorString, "Create session for camera %s failed. ret %d session %p",
+ mCameraId, ret, mSession);
+ if (ret == ACAMERA_OK) {
+ ret = ACAMERA_ERROR_UNKNOWN; // ret OK but session is null
+ }
+ return ret;
+ }
+
+ return ACAMERA_OK;
+ }
+
+ void closeSession() {
+ if (mSession != nullptr) {
+ ACameraCaptureSession_close(mSession);
+ }
+ if (mOutputs) {
+ ACaptureSessionOutputContainer_free(mOutputs);
+ mOutputs = nullptr;
+ }
+ if (mPreviewOutput) {
+ ACaptureSessionOutput_free(mPreviewOutput);
+ mPreviewOutput = nullptr;
+ }
+ if (mImgReaderOutput) {
+ ACaptureSessionOutput_free(mImgReaderOutput);
+ mImgReaderOutput = nullptr;
+ }
+ mSession = nullptr;
+ }
+
+ camera_status_t createRequestsWithErrorLog() {
+ if (mPreviewRequest || mStillRequest) {
+ LOG_ERROR(errorString, "Cannot create requests before deleteing existing one");
+ return ACAMERA_ERROR_UNKNOWN;
+ }
+
+ if (mDevice == nullptr || (!mPreviewInited && !mImgReaderInited)) {
+ LOG_ERROR(errorString,
+ "Cannot create request. device %p previewInit %d readeInit %d",
+ mDevice, mPreviewInited, mImgReaderInited);
+ return ACAMERA_ERROR_UNKNOWN;
+ }
+
+ camera_status_t ret;
+ if (mPreviewInited) {
+ ret = ACameraDevice_createCaptureRequest(
+ mDevice, TEMPLATE_PREVIEW, &mPreviewRequest);
+ if (ret != ACAMERA_OK) {
+ LOG_ERROR(errorString, "Camera %s create preview request failed. ret %d",
+ mCameraId, ret);
+ return ret;
+ }
+
+ ret = ACameraOutputTarget_create(mPreviewAnw, &mReqPreviewOutput);
+ if (ret != ACAMERA_OK) {
+ LOG_ERROR(errorString,
+ "Camera %s create request preview output target failed. ret %d",
+ mCameraId, ret);
+ return ret;
+ }
+
+ ret = ACaptureRequest_addTarget(mPreviewRequest, mReqPreviewOutput);
+ if (ret != ACAMERA_OK) {
+ LOG_ERROR(errorString, "Camera %s add preview request output failed. ret %d",
+ mCameraId, ret);
+ return ret;
+ }
+ } else {
+ ALOGI("Preview not inited. Will not create preview request!");
+ }
+
+ if (mImgReaderInited) {
+ ret = ACameraDevice_createCaptureRequest(
+ mDevice, TEMPLATE_STILL_CAPTURE, &mStillRequest);
+ if (ret != ACAMERA_OK) {
+ LOG_ERROR(errorString, "Camera %s create still request failed. ret %d",
+ mCameraId, ret);
+ return ret;
+ }
+
+ ret = ACameraOutputTarget_create(mImgReaderAnw, &mReqImgReaderOutput);
+ if (ret != ACAMERA_OK) {
+ LOG_ERROR(errorString,
+ "Camera %s create request reader output target failed. ret %d",
+ mCameraId, ret);
+ return ret;
+ }
+
+ ret = ACaptureRequest_addTarget(mStillRequest, mReqImgReaderOutput);
+ if (ret != ACAMERA_OK) {
+ LOG_ERROR(errorString, "Camera %s add still request output failed. ret %d",
+ mCameraId, ret);
+ return ret;
+ }
+
+ if (mPreviewInited) {
+ ret = ACaptureRequest_addTarget(mStillRequest, mReqPreviewOutput);
+ if (ret != ACAMERA_OK) {
+ LOG_ERROR(errorString,
+ "Camera %s add still request preview output failed. ret %d",
+ mCameraId, ret);
+ return ret;
+ }
+ }
+ } else {
+ ALOGI("AImageReader not inited. Will not create still request!");
+ }
+
+ return ACAMERA_OK;
+ }
+
+ camera_status_t startPreview() {
+ if (mSession == nullptr || mPreviewRequest == nullptr) {
+ ALOGE("Testcase cannot start preview: session %p, preview request %p",
+ mSession, mPreviewRequest);
+ return ACAMERA_ERROR_UNKNOWN;
+ }
+ int previewSeqId;
+ return ACameraCaptureSession_setRepeatingRequest(
+ mSession, nullptr, 1, &mPreviewRequest, &previewSeqId);
+ }
+
+ camera_status_t takePicture() {
+ if (mSession == nullptr || mStillRequest == nullptr) {
+ ALOGE("Testcase cannot take picture: session %p, still request %p",
+ mSession, mStillRequest);
+ return ACAMERA_ERROR_UNKNOWN;
+ }
+ int seqId;
+ return ACameraCaptureSession_capture(
+ mSession, nullptr, 1, &mStillRequest, &seqId);
+ }
+
+ int getReaderImageCount() {
+ return mReaderListener.onImageAvailableCount();
+ }
+
+ camera_status_t resetWithErrorLog() {
+ camera_status_t ret;
+
+ mReaderListener.reset();
+ closeSession();
+
+ for (int i = 0; i < 50; i++) {
+ usleep(100000); // sleep 100ms
+ if (mSessionListener.isClosed()) {
+ ALOGI("Session take ~%d ms to close", i*100);
+ break;
+ }
+ }
+
+ if (!mSessionListener.isClosed() || mSessionListener.onClosedCount() != 1) {
+ LOG_ERROR(errorString,
+ "Session for camera %s close error. isClosde %d close count %d",
+ mCameraId, mSessionListener.isClosed(), mSessionListener.onClosedCount());
+ return ACAMERA_ERROR_UNKNOWN;
+ }
+ mSessionListener.reset();
+
+ ret = closeCamera();
+ if (ret != ACAMERA_OK) {
+ LOG_ERROR(errorString, "Close camera device %s failure. ret %d", mCameraId, ret);
+ return ret;
+ }
+
+ resetCamera();
+ return ACAMERA_OK;
+ }
+
+ void setDumpFilePathBase(const char* path) {
+ mReaderListener.setDumpFilePathBase(path);
+ }
+
+ CaptureSessionListener* getSessionListener() {
+ return &mSessionListener;
+ }
+
+ private:
+ ACameraManager* createManager() {
+ if (!mCameraManager) {
+ mCameraManager = ACameraManager_create();
+ }
+ return mCameraManager;
+ }
+
+ CameraServiceListener mServiceListener;
+ ACameraManager_AvailabilityCallbacks mServiceCb {
+ &mServiceListener,
+ CameraServiceListener::onAvailable,
+ CameraServiceListener::onUnavailable
+ };
+ CameraDeviceListener mDeviceListener;
+ ACameraDevice_StateCallbacks mDeviceCb {
+ &mDeviceListener,
+ CameraDeviceListener::onDisconnected,
+ CameraDeviceListener::onError
+ };
+ CaptureSessionListener mSessionListener;
+ ACameraCaptureSession_stateCallbacks mSessionCb {
+ &mSessionListener,
+ CaptureSessionListener::onClosed,
+ CaptureSessionListener::onReady,
+ CaptureSessionListener::onActive
+ };
+
+ // TODO: capture listeners
+ ImageReaderListener mReaderListener;
+ AImageReader_ImageListener mReaderCb {
+ &mReaderListener,
+ ImageReaderListener::onImageAvailable
+ };
+
+ ACameraIdList* mCameraIdList = nullptr;
+ ACameraDevice* mDevice = nullptr;
+ AImageReader* mImgReader = nullptr;
+ ANativeWindow* mImgReaderAnw = nullptr;
+ ANativeWindow* mPreviewAnw = nullptr;
+ ACameraManager* mCameraManager = nullptr;
+ ACaptureSessionOutputContainer* mOutputs = nullptr;
+ ACaptureSessionOutput* mPreviewOutput = nullptr;
+ ACaptureSessionOutput* mImgReaderOutput = nullptr;
+ ACameraCaptureSession* mSession = nullptr;
+ ACaptureRequest* mPreviewRequest = nullptr;
+ ACaptureRequest* mStillRequest = nullptr;
+ ACameraOutputTarget* mReqPreviewOutput = nullptr;
+ ACameraOutputTarget* mReqImgReaderOutput = nullptr;
+ const char* mCameraId;
+
+ bool mMgrInited = false; // cameraId, serviceListener
+ bool mImgReaderInited = false;
+ bool mPreviewInited = false;
+};
+
+jint throwAssertionError(JNIEnv* env, const char* message)
+{
+ jclass assertionClass;
+ const char* className = "junit/framework/AssertionFailedError";
+
+ assertionClass = env->FindClass(className);
+ if (assertionClass == nullptr) {
+ ALOGE("Native throw error: cannot find class %s", className);
+ return -1;
+ }
+ return env->ThrowNew(assertionClass, message);
+}
+
+extern "C" jboolean
+Java_android_hardware_camera2_cts_NativeCameraManagerTest_\
+testCameraManagerGetAndCloseNative(
+ JNIEnv* env, jclass /*clazz*/) {
+ bool pass = false;
+ ALOGV("%s", __FUNCTION__);
+ ACameraManager* cameraManager2 = nullptr;
+ ACameraManager* cameraManager3 = nullptr;
+ ACameraManager* cameraManager4 = nullptr;
+ camera_status_t ret = ACAMERA_OK;
+ ACameraManager* cameraManager = ACameraManager_create();
+ if (cameraManager == nullptr) {
+ LOG_ERROR(errorString, "ACameraManager_create returns nullptr");
+ goto cleanup;
+ }
+ ACameraManager_delete(cameraManager);
+ cameraManager = nullptr;
+
+ // Test get/close multiple instances
+ cameraManager = ACameraManager_create();
+ cameraManager2 = ACameraManager_create();
+ if (cameraManager2 == nullptr) {
+ LOG_ERROR(errorString, "ACameraManager_create 2 returns nullptr");
+ goto cleanup;
+ }
+ ACameraManager_delete(cameraManager);
+ cameraManager = nullptr;
+ cameraManager3 = ACameraManager_create();
+ if (cameraManager3 == nullptr) {
+ LOG_ERROR(errorString, "ACameraManager_create 3 returns nullptr");
+ goto cleanup;
+ }
+ cameraManager4 = ACameraManager_create();
+ if (cameraManager4 == nullptr) {
+ LOG_ERROR(errorString, "ACameraManager_create 4 returns nullptr");
+ goto cleanup;
+ }
+ ACameraManager_delete(cameraManager3);
+ ACameraManager_delete(cameraManager2);
+ ACameraManager_delete(cameraManager4);
+
+ pass = true;
+cleanup:
+ if (cameraManager) {
+ ACameraManager_delete(cameraManager);
+ }
+ ALOGI("%s %s", __FUNCTION__, pass ? "pass" : "fail");
+ if (!pass) {
+ throwAssertionError(env, errorString);
+ }
+ return pass;
+}
+
+extern "C" jboolean
+Java_android_hardware_camera2_cts_NativeCameraManagerTest_\
+testCameraManagerGetCameraIdsNative(
+ JNIEnv* env, jclass /*clazz*/) {
+ ALOGV("%s", __FUNCTION__);
+ bool pass = false;
+ ACameraManager* mgr = ACameraManager_create();
+ ACameraIdList *cameraIdList = nullptr;
+ camera_status_t ret = ACameraManager_getCameraIdList(mgr, &cameraIdList);
+ if (ret != ACAMERA_OK || cameraIdList == nullptr) {
+ LOG_ERROR(errorString, "Get camera id list failed: ret %d, cameraIdList %p",
+ ret, cameraIdList);
+ goto cleanup;
+ }
+ ALOGI("Number of cameras: %d", cameraIdList->numCameras);
+ for (int i = 0; i < cameraIdList->numCameras; i++) {
+ ALOGI("Camera ID: %s", cameraIdList->cameraIds[i]);
+ }
+ ACameraManager_deleteCameraIdList(cameraIdList);
+ cameraIdList = nullptr;
+
+ pass = true;
+cleanup:
+ if (mgr) {
+ ACameraManager_delete(mgr);
+ }
+ if (cameraIdList) {
+ ACameraManager_deleteCameraIdList(cameraIdList);
+ }
+ ALOGI("%s %s", __FUNCTION__, pass ? "pass" : "fail");
+ if (!pass) {
+ throwAssertionError(env, errorString);
+ }
+ return pass;
+}
+
+extern "C" jboolean
+Java_android_hardware_camera2_cts_NativeCameraManagerTest_\
+testCameraManagerAvailabilityCallbackNative(
+ JNIEnv* env, jclass /*clazz*/) {
+ ALOGV("%s", __FUNCTION__);
+ bool pass = false;
+ ACameraManager* mgr = ACameraManager_create();
+ ACameraIdList *cameraIdList = nullptr;
+ camera_status_t ret = ACameraManager_getCameraIdList(mgr, &cameraIdList);
+ int numCameras = cameraIdList->numCameras;
+ CameraServiceListener listener;
+ ACameraManager_AvailabilityCallbacks cbs {
+ &listener,
+ CameraServiceListener::onAvailable,
+ CameraServiceListener::onUnavailable};
+ ret = ACameraManager_registerAvailabilityCallback(mgr, &cbs);
+ if (ret != ACAMERA_OK) {
+ LOG_ERROR(errorString, "Register availability callback failed: ret %d", ret);
+ goto cleanup;
+ }
+ sleep(1); // sleep a second to give some time for callbacks to happen
+
+ // Should at least get onAvailable for each camera once
+ if (listener.getAvailableCount() < numCameras) {
+ LOG_ERROR(errorString, "Expect at least %d available callback but only got %d",
+ numCameras, listener.getAvailableCount());
+ goto cleanup;
+ }
+
+ ret = ACameraManager_unregisterAvailabilityCallback(mgr, &cbs);
+ if (ret != ACAMERA_OK) {
+ LOG_ERROR(errorString, "Unregister availability callback failed: ret %d", ret);
+ goto cleanup;
+ }
+ pass = true;
+cleanup:
+ if (cameraIdList) {
+ ACameraManager_deleteCameraIdList(cameraIdList);
+ }
+ if (mgr) {
+ ACameraManager_delete(mgr);
+ }
+ ALOGI("%s %s", __FUNCTION__, pass ? "pass" : "failed");
+ if (!pass) {
+ throwAssertionError(env, errorString);
+ }
+ return pass;
+}
+
+extern "C" jboolean
+Java_android_hardware_camera2_cts_NativeCameraManagerTest_\
+testCameraManagerCharacteristicsNative(
+ JNIEnv* env, jclass /*clazz*/) {
+ ALOGV("%s", __FUNCTION__);
+ bool pass = false;
+ ACameraManager* mgr = ACameraManager_create();
+ ACameraIdList *cameraIdList = nullptr;
+ ACameraMetadata* chars = nullptr;
+ int numCameras = 0;
+ camera_status_t ret = ACameraManager_getCameraIdList(mgr, &cameraIdList);
+ if (ret != ACAMERA_OK || cameraIdList == nullptr) {
+ LOG_ERROR(errorString, "Get camera id list failed: ret %d, cameraIdList %p",
+ ret, cameraIdList);
+ goto cleanup;
+ }
+ numCameras = cameraIdList->numCameras;
+
+ for (int i = 0; i < numCameras; i++) {
+ ret = ACameraManager_getCameraCharacteristics(
+ mgr, cameraIdList->cameraIds[i], &chars);
+ if (ret != ACAMERA_OK) {
+ LOG_ERROR(errorString, "Get camera characteristics failed: ret %d", ret);
+ goto cleanup;
+ }
+
+ int32_t numTags = 0;
+ const uint32_t* tags = nullptr;
+ ret = ACameraMetadata_getAllTags(chars, &numTags, &tags);
+ if (ret != ACAMERA_OK) {
+ LOG_ERROR(errorString, "Get camera characteristics tags failed: ret %d", ret);
+ goto cleanup;
+ }
+
+ for (int tid = 0; tid < numTags; tid++) {
+ uint32_t tagId = tags[tid];
+ ALOGV("%s capture request contains key %u", __FUNCTION__, tagId);
+ uint32_t sectionId = tagId >> 16;
+ if (sectionId >= ACAMERA_SECTION_COUNT && sectionId < ACAMERA_VENDOR) {
+ LOG_ERROR(errorString, "Unknown tagId %u, sectionId %u", tagId, sectionId);
+ goto cleanup;
+ }
+ }
+
+ ACameraMetadata_const_entry entry;
+ ret = ACameraMetadata_getConstEntry(chars, ACAMERA_REQUEST_AVAILABLE_CAPABILITIES, &entry);
+ if (ret != ACAMERA_OK) {
+ LOG_ERROR(errorString, "Get const available capabilities key failed. ret %d", ret);
+ goto cleanup;
+ }
+
+ // Check the entry is actually legit
+ if (entry.tag != ACAMERA_REQUEST_AVAILABLE_CAPABILITIES ||
+ entry.count == 0 || entry.type != ACAMERA_TYPE_BYTE || entry.data.i32 == nullptr) {
+ LOG_ERROR(errorString,
+ "Bad available capabilities key: tag: %d (expected %d), count %u (expect > 0), "
+ "type %d (expected %d), data %p (expected not null)",
+ entry.tag, ACAMERA_REQUEST_AVAILABLE_CAPABILITIES, entry.count,
+ entry.type, ACAMERA_TYPE_BYTE, entry.data.i32);
+ goto cleanup;
+ }
+ // All camera supports BC except depth only cameras
+ bool supportBC = false, supportDepth = false;
+ for (uint32_t i = 0; i < entry.count; i++) {
+ if (entry.data.u8[i] == ACAMERA_REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE) {
+ supportBC = true;
+ }
+ if (entry.data.u8[i] == ACAMERA_REQUEST_AVAILABLE_CAPABILITIES_DEPTH_OUTPUT) {
+ supportDepth = true;
+ }
+ }
+ if (!(supportBC || supportDepth)) {
+ LOG_ERROR(errorString, "Error: camera device %s does not support either BC or DEPTH",
+ cameraIdList->cameraIds[i]);
+ goto cleanup;
+ }
+
+ // Check get unknown value fails
+ uint32_t badTag = (uint32_t) ACAMERA_VENDOR_START - 1;
+ ret = ACameraMetadata_getConstEntry(chars, ACAMERA_VENDOR_START, &entry);
+ if (ret == ACAMERA_OK) {
+ LOG_ERROR(errorString, "Error: get unknown tag should fail!");
+ goto cleanup;
+ }
+
+ ACameraMetadata_free(chars);
+ chars = nullptr;
+ }
+
+ pass = true;
+cleanup:
+ if (chars) {
+ ACameraMetadata_free(chars);
+ }
+ ACameraManager_deleteCameraIdList(cameraIdList);
+ ACameraManager_delete(mgr);
+ ALOGI("%s %s", __FUNCTION__, pass ? "pass" : "failed");
+ if (!pass) {
+ throwAssertionError(env, errorString);
+ }
+ return pass;
+}
+
+extern "C" jboolean
+Java_android_hardware_camera2_cts_NativeCameraDeviceTest_\
+testCameraDeviceOpenAndCloseNative(
+ JNIEnv* env, jclass /*clazz*/) {
+ ALOGV("%s", __FUNCTION__);
+ int numCameras = 0;
+ bool pass = false;
+ PreviewTestCase testCase;
+
+ camera_status_t ret = testCase.initWithErrorLog();
+ if (ret != ACAMERA_OK) {
+ // Don't log error here. testcase did it
+ goto cleanup;
+ }
+
+ numCameras = testCase.getNumCameras();
+ if (numCameras < 0) {
+ LOG_ERROR(errorString, "Testcase returned negavtive number of cameras: %d", numCameras);
+ goto cleanup;
+ }
+
+ for (int i = 0; i < numCameras; i++) {
+ const char* cameraId = testCase.getCameraId(i);
+ if (cameraId == nullptr) {
+ LOG_ERROR(errorString, "Testcase returned null camera id for camera %d", i);
+ goto cleanup;
+ }
+
+ ret = testCase.openCamera(cameraId);
+ if (ret != ACAMERA_OK) {
+ LOG_ERROR(errorString, "Open camera device %s failure. ret %d", cameraId, ret);
+ goto cleanup;
+ }
+
+ usleep(100000); // sleep to give some time for callbacks to happen
+
+ if (testCase.isCameraAvailable(cameraId)) {
+ LOG_ERROR(errorString, "Camera %s should be unavailable now", cameraId);
+ goto cleanup;
+ }
+
+ ret = testCase.closeCamera();
+ if (ret != ACAMERA_OK) {
+ LOG_ERROR(errorString, "Close camera device %s failure. ret %d", cameraId, ret);
+ goto cleanup;
+ }
+
+ usleep(100000); // sleep to give some time for callbacks to happen
+
+ if (!testCase.isCameraAvailable(cameraId)) {
+ LOG_ERROR(errorString, "Camera %s should be available now", cameraId);
+ goto cleanup;
+ }
+ }
+
+ ret = testCase.deInit();
+ if (ret != ACAMERA_OK) {
+ LOG_ERROR(errorString, "Testcase deInit failed: ret %d", ret);
+ goto cleanup;
+ }
+
+ pass = true;
+cleanup:
+ ALOGI("%s %s", __FUNCTION__, pass ? "pass" : "failed");
+ if (!pass) {
+ throwAssertionError(env, errorString);
+ }
+ return pass;
+}
+
+extern "C" jboolean
+Java_android_hardware_camera2_cts_NativeCameraDeviceTest_\
+testCameraDeviceCreateCaptureRequestNative(
+ JNIEnv* env, jclass /*clazz*/) {
+ ALOGV("%s", __FUNCTION__);
+ bool pass = false;
+ ACameraManager* mgr = ACameraManager_create();
+ ACameraIdList* cameraIdList = nullptr;
+ ACameraDevice* device = nullptr;
+ ACaptureRequest* request = nullptr;
+ ACameraMetadata* chars = nullptr;
+ camera_status_t ret = ACameraManager_getCameraIdList(mgr, &cameraIdList);
+
+ int numCameras = cameraIdList->numCameras;
+ for (int i = 0; i < numCameras; i++) {
+ CameraDeviceListener deviceListener;
+ const char* cameraId = cameraIdList->cameraIds[i];
+ ACameraDevice_StateCallbacks deviceCb {
+ &deviceListener,
+ CameraDeviceListener::onDisconnected,
+ CameraDeviceListener::onError
+ };
+ ret = ACameraManager_openCamera(mgr, cameraId, &deviceCb, &device);
+ if (ret != ACAMERA_OK) {
+ LOG_ERROR(errorString, "Open camera device %s failure. ret %d", cameraId, ret);
+ goto cleanup;
+ }
+
+ ret = ACameraManager_getCameraCharacteristics(mgr, cameraId, &chars);
+ if (ret != ACAMERA_OK || chars == nullptr) {
+ LOG_ERROR(errorString, "Get camera %s characteristics failure. ret %d, chars %p",
+ cameraId, ret, chars);
+ goto cleanup;
+ }
+ StaticInfo staticInfo(chars);
+
+ for (int t = TEMPLATE_PREVIEW; t <= TEMPLATE_MANUAL; t++) {
+ ACameraDevice_request_template templateId =
+ static_cast<ACameraDevice_request_template>(t);
+ ret = ACameraDevice_createCaptureRequest(device, templateId, &request);
+ if (ret == ACAMERA_ERROR_UNSUPPORTED) {
+ // template not supported. skip
+ continue;
+ }
+
+ if (ret != ACAMERA_OK) {
+ LOG_ERROR(errorString, "Create capture request failed!: ret %d", ret);
+ goto cleanup;
+ }
+
+ int32_t numTags = 0;
+ const uint32_t* tags = nullptr;
+ ret = ACaptureRequest_getAllTags(request, &numTags, &tags);
+ if (ret != ACAMERA_OK) {
+ LOG_ERROR(errorString, "Get capture request tags failed: ret %d", ret);
+ goto cleanup;
+ }
+
+ for (int tid = 0; tid < numTags; tid++) {
+ uint32_t tagId = tags[tid];
+ ALOGV("%s capture request contains key %u", __FUNCTION__, tagId);
+ uint32_t sectionId = tagId >> 16;
+ if (sectionId >= ACAMERA_SECTION_COUNT && sectionId < ACAMERA_VENDOR) {
+ LOG_ERROR(errorString, "Unknown tagId %u, sectionId %u", tagId, sectionId);
+ goto cleanup;
+ }
+ }
+
+ // try get/set capture request fields
+ ACameraMetadata_const_entry entry;
+ ret = ACaptureRequest_getConstEntry(request, ACAMERA_CONTROL_AE_MODE, &entry);
+ if (ret != ACAMERA_OK) {
+ LOG_ERROR(errorString, "Get AE mode key failed. ret %d", ret);
+ goto cleanup;
+ }
+
+ if (entry.tag != ACAMERA_CONTROL_AE_MODE || entry.type != ACAMERA_TYPE_BYTE ||\
+ entry.count != 1) {
+ LOG_ERROR(errorString,
+ "Bad AE mode key. tag 0x%x (expect 0x%x), type %d (expect %d), "
+ "count %d (expect %d)",
+ entry.tag, ACAMERA_CONTROL_AE_MODE, entry.type, ACAMERA_TYPE_BYTE,
+ entry.count, 1);
+ goto cleanup;
+ }
+ if (t == TEMPLATE_MANUAL) {
+ if (entry.data.u8[0] != ACAMERA_CONTROL_AE_MODE_OFF) {
+ LOG_ERROR(errorString, "Error: MANUAL template AE mode %d (expect %d)",
+ entry.data.u8[0], ACAMERA_CONTROL_AE_MODE_OFF);
+ goto cleanup;
+ }
+ // try set AE_MODE_ON
+ uint8_t aeMode = ACAMERA_CONTROL_AE_MODE_ON;
+ ret = ACaptureRequest_setEntry_u8(
+ request, ACAMERA_CONTROL_AE_MODE, /*count*/ 1, &aeMode);
+ if (ret != ACAMERA_OK) {
+ LOG_ERROR(errorString,
+ "Error: Camera %s template %d: update AE mode key fail. ret %d",
+ cameraId, t, ret);
+ goto cleanup;
+ }
+ ret = ACaptureRequest_getConstEntry(
+ request, ACAMERA_CONTROL_AE_MODE, &entry);
+ if (ret != ACAMERA_OK) {
+ LOG_ERROR(errorString, "Get AE mode key failed. ret %d", ret);
+ goto cleanup;
+ }
+ if (entry.data.u8[0] != aeMode) {
+ LOG_ERROR(errorString,
+ "Error: AE mode key is not updated. expect %d but get %d",
+ aeMode, entry.data.u8[0]);
+ goto cleanup;
+ }
+ } else {
+ if (staticInfo.isColorOutputSupported()) {
+ if (entry.data.u8[0] != ACAMERA_CONTROL_AE_MODE_ON) {
+ LOG_ERROR(errorString,
+ "Error: Template %d has wrong AE mode %d (expect %d)",
+ t, entry.data.u8[0], ACAMERA_CONTROL_AE_MODE_ON);
+ goto cleanup;
+ }
+ // try set AE_MODE_OFF
+ if (staticInfo.isCapabilitySupported(
+ ACAMERA_REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR)) {
+ uint8_t aeMode = ACAMERA_CONTROL_AE_MODE_OFF;
+ ret = ACaptureRequest_setEntry_u8(
+ request, ACAMERA_CONTROL_AE_MODE, /*count*/ 1, &aeMode);
+ if (ret != ACAMERA_OK) {
+ LOG_ERROR(errorString,
+ "Error: Camera %s template %d: update AE mode key fail. ret %d",
+ cameraId, t, ret);
+ goto cleanup;
+ }
+ ret = ACaptureRequest_getConstEntry(
+ request, ACAMERA_CONTROL_AE_MODE, &entry);
+ if (ret != ACAMERA_OK) {
+ LOG_ERROR(errorString, "Get AE mode key failed. ret %d", ret);
+ goto cleanup;
+ }
+ if (entry.data.u8[0] != aeMode) {
+ LOG_ERROR(errorString,
+ "Error: AE mode key is not updated. expect %d but get %d",
+ aeMode, entry.data.u8[0]);
+ goto cleanup;
+ }
+ }
+ }
+ }
+ ACaptureRequest_free(request);
+ request = nullptr;
+ }
+
+ ACameraMetadata_free(chars);
+ chars = nullptr;
+ ACameraDevice_close(device);
+ device = nullptr;
+ }
+
+ pass = true;
+cleanup:
+ if (cameraIdList) {
+ ACameraManager_deleteCameraIdList(cameraIdList);
+ }
+ if (request) {
+ ACaptureRequest_free(request);
+ }
+ if (chars) {
+ ACameraMetadata_free(chars);
+ }
+ if (device) {
+ ACameraDevice_close(device);
+ }
+ if (mgr) {
+ ACameraManager_delete(mgr);
+ }
+ ALOGI("%s %s", __FUNCTION__, pass ? "pass" : "failed");
+ if (!pass) {
+ throwAssertionError(env, errorString);
+ }
+ return pass;
+}
+
+extern "C" jboolean
+Java_android_hardware_camera2_cts_NativeCameraDeviceTest_\
+testCameraDeviceSessionOpenAndCloseNative(
+ JNIEnv* env, jclass /*clazz*/, jobject jPreviewSurface) {
+ ALOGV("%s", __FUNCTION__);
+ int numCameras = 0;
+ bool pass = false;
+ PreviewTestCase testCase;
+
+ camera_status_t ret = testCase.initWithErrorLog();
+ if (ret != ACAMERA_OK) {
+ // Don't log error here. testcase did it
+ goto cleanup;
+ }
+
+ numCameras = testCase.getNumCameras();
+ if (numCameras < 0) {
+ LOG_ERROR(errorString, "Testcase returned negavtive number of cameras: %d", numCameras);
+ goto cleanup;
+ }
+
+ for (int i = 0; i < numCameras; i++) {
+ const char* cameraId = testCase.getCameraId(i);
+ if (cameraId == nullptr) {
+ LOG_ERROR(errorString, "Testcase returned null camera id for camera %d", i);
+ goto cleanup;
+ }
+
+ ret = testCase.openCamera(cameraId);
+ if (ret != ACAMERA_OK) {
+ LOG_ERROR(errorString, "Open camera device %s failure. ret %d", cameraId, ret);
+ goto cleanup;
+ }
+
+ usleep(100000); // sleep to give some time for callbacks to happen
+
+ if (testCase.isCameraAvailable(cameraId)) {
+ LOG_ERROR(errorString, "Camera %s should be unavailable now", cameraId);
+ goto cleanup;
+ }
+
+ ANativeWindow* previewAnw = testCase.initPreviewAnw(env, jPreviewSurface);
+ if (previewAnw == nullptr) {
+ LOG_ERROR(errorString, "Null ANW from preview surface!");
+ goto cleanup;
+ }
+
+ CaptureSessionListener* sessionListener = testCase.getSessionListener();
+ if (sessionListener == nullptr) {
+ LOG_ERROR(errorString, "Session listener camera %s is null", cameraId);
+ goto cleanup;
+ }
+
+ // Try open/close session multiple times
+ for (int j = 0; j < 5; j++) {
+ ret = testCase.createCaptureSessionWithLog();
+ if (ret != ACAMERA_OK) {
+ // Don't log error here. testcase did it
+ goto cleanup;
+ }
+
+ usleep(100000); // sleep to give some time for callbacks to happen
+
+ if (!sessionListener->isIdle()) {
+ LOG_ERROR(errorString, "Session for camera %s should be idle right after creation",
+ cameraId);
+ goto cleanup;
+ }
+
+ testCase.closeSession();
+
+ usleep(100000); // sleep to give some time for callbacks to happen
+ if (!sessionListener->isClosed() || sessionListener->onClosedCount() != 1) {
+ LOG_ERROR(errorString,
+ "Session for camera %s close error. isClosde %d close count %d",
+ cameraId, sessionListener->isClosed(), sessionListener->onClosedCount());
+ goto cleanup;
+ }
+ sessionListener->reset();
+ }
+
+ // Try open/close really fast
+ ret = testCase.createCaptureSessionWithLog();
+ if (ret != ACAMERA_OK) {
+ LOG_ERROR(errorString, "Create session for camera %s failed. ret %d",
+ cameraId, ret);
+ goto cleanup;
+ }
+ testCase.closeSession();
+ usleep(100000); // sleep to give some time for callbacks to happen
+ if (!sessionListener->isClosed() || sessionListener->onClosedCount() != 1) {
+ LOG_ERROR(errorString,
+ "Session for camera %s close error. isClosde %d close count %d",
+ cameraId, sessionListener->isClosed(), sessionListener->onClosedCount());
+ goto cleanup;
+ }
+
+ ret = testCase.resetWithErrorLog();
+ if (ret != ACAMERA_OK) {
+ // Don't log error here. testcase did it
+ goto cleanup;
+ }
+
+ usleep(100000); // sleep to give some time for callbacks to happen
+
+ if (!testCase.isCameraAvailable(cameraId)) {
+ LOG_ERROR(errorString, "Camera %s should be available now", cameraId);
+ goto cleanup;
+ }
+ }
+
+ ret = testCase.deInit();
+ if (ret != ACAMERA_OK) {
+ LOG_ERROR(errorString, "Testcase deInit failed: ret %d", ret);
+ goto cleanup;
+ }
+
+ pass = true;
+cleanup:
+ ALOGI("%s %s", __FUNCTION__, pass ? "pass" : "failed");
+ if (!pass) {
+ throwAssertionError(env, errorString);
+ }
+ return pass;
+}
+
+extern "C" jboolean
+Java_android_hardware_camera2_cts_NativeCameraDeviceTest_\
+testCameraDeviceSimplePreviewNative(
+ JNIEnv* env, jclass /*clazz*/, jobject jPreviewSurface) {
+ ALOGV("%s", __FUNCTION__);
+ int numCameras = 0;
+ bool pass = false;
+ PreviewTestCase testCase;
+
+ camera_status_t ret = testCase.initWithErrorLog();
+ if (ret != ACAMERA_OK) {
+ // Don't log error here. testcase did it
+ goto cleanup;
+ }
+
+ numCameras = testCase.getNumCameras();
+ if (numCameras < 0) {
+ LOG_ERROR(errorString, "Testcase returned negavtive number of cameras: %d", numCameras);
+ goto cleanup;
+ }
+
+ for (int i = 0; i < numCameras; i++) {
+ const char* cameraId = testCase.getCameraId(i);
+ if (cameraId == nullptr) {
+ LOG_ERROR(errorString, "Testcase returned null camera id for camera %d", i);
+ goto cleanup;
+ }
+
+ ret = testCase.openCamera(cameraId);
+ if (ret != ACAMERA_OK) {
+ LOG_ERROR(errorString, "Open camera device %s failure. ret %d", cameraId, ret);
+ goto cleanup;
+ }
+
+ usleep(100000); // sleep to give some time for callbacks to happen
+
+ if (testCase.isCameraAvailable(cameraId)) {
+ LOG_ERROR(errorString, "Camera %s should be unavailable now", cameraId);
+ goto cleanup;
+ }
+
+ ANativeWindow* previewAnw = testCase.initPreviewAnw(env, jPreviewSurface);
+ if (previewAnw == nullptr) {
+ LOG_ERROR(errorString, "Null ANW from preview surface!");
+ goto cleanup;
+ }
+
+ ret = testCase.createCaptureSessionWithLog();
+ if (ret != ACAMERA_OK) {
+ // Don't log error here. testcase did it
+ goto cleanup;
+ }
+
+ ret = testCase.createRequestsWithErrorLog();
+ if (ret != ACAMERA_OK) {
+ // Don't log error here. testcase did it
+ goto cleanup;
+ }
+
+ ret = testCase.startPreview();
+ if (ret != ACAMERA_OK) {
+ LOG_ERROR(errorString, "Start preview failed!");
+ goto cleanup;
+ }
+
+ sleep(3);
+
+ ret = testCase.resetWithErrorLog();
+ if (ret != ACAMERA_OK) {
+ // Don't log error here. testcase did it
+ goto cleanup;
+ }
+
+ usleep(100000); // sleep to give some time for callbacks to happen
+
+ if (!testCase.isCameraAvailable(cameraId)) {
+ LOG_ERROR(errorString, "Camera %s should be available now", cameraId);
+ goto cleanup;
+ }
+ }
+
+ ret = testCase.deInit();
+ if (ret != ACAMERA_OK) {
+ LOG_ERROR(errorString, "Testcase deInit failed: ret %d", ret);
+ goto cleanup;
+ }
+
+ pass = true;
+cleanup:
+ ALOGI("%s %s", __FUNCTION__, pass ? "pass" : "failed");
+ if (!pass) {
+ throwAssertionError(env, errorString);
+ }
+ return pass;
+}
+
+extern "C" jboolean
+Java_android_hardware_camera2_cts_NativeImageReaderTest_\
+testJpegNative(
+ JNIEnv* env, jclass /*clazz*/, jstring jOutPath) {
+ ALOGV("%s", __FUNCTION__);
+ const int NUM_TEST_IMAGES = 10;
+ const int TEST_WIDTH = 640;
+ const int TEST_HEIGHT = 480;
+ media_status_t mediaRet = AMEDIA_ERROR_UNKNOWN;
+ int numCameras = 0;
+ bool pass = false;
+ PreviewTestCase testCase;
+
+ const char* outPath = env->GetStringUTFChars(jOutPath, nullptr);
+ testCase.setDumpFilePathBase(outPath);
+ ALOGI("%s: out path is %s", __FUNCTION__, outPath);
+
+ camera_status_t ret = testCase.initWithErrorLog();
+ if (ret != ACAMERA_OK) {
+ // Don't log error here. testcase did it
+ goto cleanup;
+ }
+
+ numCameras = testCase.getNumCameras();
+ if (numCameras < 0) {
+ LOG_ERROR(errorString, "Testcase returned negavtive number of cameras: %d", numCameras);
+ goto cleanup;
+ }
+
+ for (int i = 0; i < numCameras; i++) {
+ const char* cameraId = testCase.getCameraId(i);
+ if (cameraId == nullptr) {
+ LOG_ERROR(errorString, "Testcase returned null camera id for camera %d", i);
+ goto cleanup;
+ }
+
+ ret = testCase.openCamera(cameraId);
+ if (ret != ACAMERA_OK) {
+ LOG_ERROR(errorString, "Open camera device %s failure. ret %d", cameraId, ret);
+ goto cleanup;
+ }
+
+ usleep(100000); // sleep to give some time for callbacks to happen
+
+ if (testCase.isCameraAvailable(cameraId)) {
+ LOG_ERROR(errorString, "Camera %s should be unavailable now", cameraId);
+ goto cleanup;
+ }
+
+ mediaRet = testCase.initImageReaderWithErrorLog(
+ TEST_WIDTH, TEST_HEIGHT, AIMAGE_FORMAT_JPEG, NUM_TEST_IMAGES);
+ if (mediaRet != AMEDIA_OK) {
+ // Don't log error here. testcase did it
+ goto cleanup;
+ }
+
+ ret = testCase.createCaptureSessionWithLog();
+ if (ret != ACAMERA_OK) {
+ // Don't log error here. testcase did it
+ goto cleanup;
+ }
+
+ ret = testCase.createRequestsWithErrorLog();
+ if (ret != ACAMERA_OK) {
+ // Don't log error here. testcase did it
+ goto cleanup;
+ }
+
+ // Do some still capture
+ for (int capture = 0; capture < NUM_TEST_IMAGES; capture++) {
+ ret = testCase.takePicture();
+ if (ret != ACAMERA_OK) {
+ LOG_ERROR(errorString, "Camera %s capture(%d) failed. ret %d",
+ cameraId, capture, ret);
+ goto cleanup;
+ }
+ }
+
+ // wait until all capture finished
+ for (int i = 0; i < 50; i++) {
+ usleep(100000); // sleep 100ms
+ if (testCase.getReaderImageCount() == NUM_TEST_IMAGES) {
+ ALOGI("Session take ~%d ms to capture %d images",
+ i*100, NUM_TEST_IMAGES);
+ break;
+ }
+ }
+
+ if (testCase.getReaderImageCount() != NUM_TEST_IMAGES) {
+ LOG_ERROR(errorString, "Camera %s timeout capturing %d images. Got %d",
+ cameraId, NUM_TEST_IMAGES, testCase.getReaderImageCount());
+ goto cleanup;
+ }
+
+ ret = testCase.resetWithErrorLog();
+ if (ret != ACAMERA_OK) {
+ // Don't log error here. testcase did it
+ goto cleanup;
+ }
+
+ usleep(100000); // sleep to give some time for callbacks to happen
+
+ if (!testCase.isCameraAvailable(cameraId)) {
+ LOG_ERROR(errorString, "Camera %s should be available now", cameraId);
+ goto cleanup;
+ }
+ }
+
+ ret = testCase.deInit();
+ if (ret != ACAMERA_OK) {
+ LOG_ERROR(errorString, "Testcase deInit failed: ret %d", ret);
+ goto cleanup;
+ }
+
+ pass = true;
+
+cleanup:
+ env->ReleaseStringUTFChars(jOutPath, outPath);
+ ALOGI("%s %s", __FUNCTION__, pass ? "pass" : "failed");
+ if (!pass) {
+ throwAssertionError(env, errorString);
+ }
+ return pass;
+}
+
+
+extern "C" jboolean
+Java_android_hardware_camera2_cts_NativeStillCaptureTest_\
+testStillCaptureNative(
+ JNIEnv* env, jclass /*clazz*/, jstring jOutPath, jobject jPreviewSurface) {
+ ALOGV("%s", __FUNCTION__);
+ const int NUM_TEST_IMAGES = 10;
+ const int TEST_WIDTH = 640;
+ const int TEST_HEIGHT = 480;
+ media_status_t mediaRet = AMEDIA_ERROR_UNKNOWN;
+ int numCameras = 0;
+ bool pass = false;
+ PreviewTestCase testCase;
+
+ const char* outPath = env->GetStringUTFChars(jOutPath, nullptr);
+ testCase.setDumpFilePathBase(outPath);
+ ALOGI("%s: out path is %s", __FUNCTION__, outPath);
+
+ camera_status_t ret = testCase.initWithErrorLog();
+ if (ret != ACAMERA_OK) {
+ // Don't log error here. testcase did it
+ goto cleanup;
+ }
+
+ numCameras = testCase.getNumCameras();
+ if (numCameras < 0) {
+ LOG_ERROR(errorString, "Testcase returned negavtive number of cameras: %d", numCameras);
+ goto cleanup;
+ }
+
+ for (int i = 0; i < numCameras; i++) {
+ const char* cameraId = testCase.getCameraId(i);
+ if (cameraId == nullptr) {
+ LOG_ERROR(errorString, "Testcase returned null camera id for camera %d", i);
+ goto cleanup;
+ }
+
+ ret = testCase.openCamera(cameraId);
+ if (ret != ACAMERA_OK) {
+ LOG_ERROR(errorString, "Open camera device %s failure. ret %d", cameraId, ret);
+ goto cleanup;
+ }
+
+ usleep(100000); // sleep to give some time for callbacks to happen
+
+ if (testCase.isCameraAvailable(cameraId)) {
+ LOG_ERROR(errorString, "Camera %s should be unavailable now", cameraId);
+ goto cleanup;
+ }
+
+ mediaRet = testCase.initImageReaderWithErrorLog(
+ TEST_WIDTH, TEST_HEIGHT, AIMAGE_FORMAT_JPEG, NUM_TEST_IMAGES);
+ if (mediaRet != AMEDIA_OK) {
+ // Don't log error here. testcase did it
+ goto cleanup;
+ }
+
+ ANativeWindow* previewAnw = testCase.initPreviewAnw(env, jPreviewSurface);
+ if (previewAnw == nullptr) {
+ LOG_ERROR(errorString, "Null ANW from preview surface!");
+ goto cleanup;
+ }
+
+ ret = testCase.createCaptureSessionWithLog();
+ if (ret != ACAMERA_OK) {
+ // Don't log error here. testcase did it
+ goto cleanup;
+ }
+
+ ret = testCase.createRequestsWithErrorLog();
+ if (ret != ACAMERA_OK) {
+ // Don't log error here. testcase did it
+ goto cleanup;
+ }
+
+ ret = testCase.startPreview();
+ if (ret != ACAMERA_OK) {
+ LOG_ERROR(errorString, "Start preview failed!");
+ goto cleanup;
+ }
+
+ // Let preview run some time
+ sleep(3);
+
+ // Do some still capture
+ for (int capture = 0; capture < NUM_TEST_IMAGES; capture++) {
+ ret = testCase.takePicture();
+ if (ret != ACAMERA_OK) {
+ LOG_ERROR(errorString, "Camera %s capture(%d) failed. ret %d",
+ cameraId, capture, ret);
+ goto cleanup;
+ }
+ }
+
+ // wait until all capture finished
+ for (int i = 0; i < 50; i++) {
+ usleep(100000); // sleep 100ms
+ if (testCase.getReaderImageCount() == NUM_TEST_IMAGES) {
+ ALOGI("Session take ~%d ms to capture %d images",
+ i*100, NUM_TEST_IMAGES);
+ break;
+ }
+ }
+
+ if (testCase.getReaderImageCount() != NUM_TEST_IMAGES) {
+ LOG_ERROR(errorString, "Camera %s timeout capturing %d images. Got %d",
+ cameraId, NUM_TEST_IMAGES, testCase.getReaderImageCount());
+ goto cleanup;
+ }
+
+ ret = testCase.resetWithErrorLog();
+ if (ret != ACAMERA_OK) {
+ // Don't log error here. testcase did it
+ goto cleanup;
+ }
+
+ usleep(100000); // sleep to give some time for callbacks to happen
+
+ if (!testCase.isCameraAvailable(cameraId)) {
+ LOG_ERROR(errorString, "Camera %s should be available now", cameraId);
+ goto cleanup;
+ }
+ }
+
+ ret = testCase.deInit();
+ if (ret != ACAMERA_OK) {
+ LOG_ERROR(errorString, "Testcase deInit failed: ret %d", ret);
+ goto cleanup;
+ }
+
+ pass = true;
+cleanup:
+ env->ReleaseStringUTFChars(jOutPath, outPath);
+ ALOGI("%s %s", __FUNCTION__, pass ? "pass" : "failed");
+ if (!pass) {
+ throwAssertionError(env, errorString);
+ }
+ return pass;
+}
+