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;
+}
+