/*
 * Copyright (C) 2020 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 "NativeCodecUnitTest"
#include <log/log.h>

#include <jni.h>
#include <media/NdkMediaExtractor.h>
#include <sys/stat.h>

#include <thread>

#include "NativeCodecTestBase.h"
#include "NativeMediaCommon.h"

class NativeCodecUnitTest final : CodecTestBase {
  private:
    AMediaFormat* mFormat;
    bool enqueueInput(size_t bufferIndex) override;
    bool dequeueOutput(size_t bufferIndex, AMediaCodecBufferInfo* bufferInfo) override;

    const long kStallTimeMs = 1000;

  public:
    explicit NativeCodecUnitTest(const char* mediaType);
    ~NativeCodecUnitTest();

    bool setupCodec(bool isAudio, bool isEncoder);

    bool testConfigureCodecForIncompleteFormat(bool isAudio, bool isEncoder);
    bool testConfigureCodecForBadFlags(bool isEncoder);
    bool testConfigureInInitState();
    bool testConfigureInRunningState();
    bool testConfigureInUnInitState();
    bool testDequeueInputBufferInInitState();
    bool testDequeueInputBufferInRunningState();
    bool testDequeueInputBufferInUnInitState();
    bool testDequeueOutputBufferInInitState();
    bool testDequeueOutputBufferInRunningState();
    bool testDequeueOutputBufferInUnInitState();
    bool testFlushInInitState();
    bool testFlushInRunningState();
    bool testFlushInUnInitState();
    bool testGetNameInInitState();
    bool testGetNameInRunningState();
    bool testGetNameInUnInitState();
    bool testSetAsyncNotifyCallbackInInitState();
    bool testSetAsyncNotifyCallbackInRunningState();
    bool testSetAsyncNotifyCallbackInUnInitState();
    bool testGetInputBufferInInitState();
    bool testGetInputBufferInRunningState();
    bool testGetInputBufferInUnInitState();
    bool testGetInputFormatInInitState();
    bool testGetInputFormatInRunningState();
    bool testGetInputFormatInUnInitState();
    bool testGetOutputBufferInInitState();
    bool testGetOutputBufferInRunningState();
    bool testGetOutputBufferInUnInitState();
    bool testGetOutputFormatInInitState();
    bool testGetOutputFormatInRunningState();
    bool testGetOutputFormatInUnInitState();
    bool testSetParametersInInitState();
    bool testSetParametersInRunningState();
    bool testSetParametersInUnInitState();
    bool testStartInRunningState();
    bool testStartInUnInitState();
    bool testStopInInitState();
    bool testStopInRunningState();
    bool testStopInUnInitState();
    bool testQueueInputBufferInInitState();
    bool testQueueInputBufferWithBadIndex();
    bool testQueueInputBufferWithBadSize();
    bool testQueueInputBufferWithBadBuffInfo();
    bool testQueueInputBufferWithBadOffset();
    bool testQueueInputBufferInUnInitState();
    bool testReleaseOutputBufferInInitState();
    bool testReleaseOutputBufferInRunningState();
    bool testReleaseOutputBufferInUnInitState();
    bool testGetBufferFormatInInitState();
    bool testGetBufferFormatInRunningState();
    bool testGetBufferFormatInUnInitState();
};

NativeCodecUnitTest::NativeCodecUnitTest(const char* mediaType) : CodecTestBase(mediaType) {
    mFormat = nullptr;
}

NativeCodecUnitTest::~NativeCodecUnitTest() {
    if (mFormat) AMediaFormat_delete(mFormat);
    mFormat = nullptr;
}

bool NativeCodecUnitTest::enqueueInput(size_t bufferIndex) {
    (void)bufferIndex;
    return false;
}

bool NativeCodecUnitTest::dequeueOutput(size_t bufferIndex, AMediaCodecBufferInfo* info) {
    if ((info->flags & AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM) != 0) {
        mSawOutputEOS = true;
    }
    RETURN_IF_FAIL(AMediaCodec_releaseOutputBuffer(mCodec, bufferIndex, false),
                   "AMediaCodec_releaseOutputBuffer failed")
    return !hasSeenError();
}

AMediaFormat* getSampleAudioFormat() {
    AMediaFormat* format = AMediaFormat_new();
    AMediaFormat_setString(format, AMEDIAFORMAT_KEY_MIME, AMEDIA_MIMETYPE_AUDIO_AAC);
    AMediaFormat_setInt32(format, AMEDIAFORMAT_KEY_BIT_RATE, 64000);
    AMediaFormat_setInt32(format, AMEDIAFORMAT_KEY_SAMPLE_RATE, 16000);
    AMediaFormat_setInt32(format, AMEDIAFORMAT_KEY_CHANNEL_COUNT, 1);
    return format;
}

AMediaFormat* getSampleVideoFormat() {
    AMediaFormat* format = AMediaFormat_new();
    AMediaFormat_setString(format, AMEDIAFORMAT_KEY_MIME, AMEDIA_MIMETYPE_VIDEO_AVC);
    AMediaFormat_setInt32(format, AMEDIAFORMAT_KEY_BIT_RATE, 512000);
    AMediaFormat_setInt32(format, AMEDIAFORMAT_KEY_WIDTH, 352);
    AMediaFormat_setInt32(format, AMEDIAFORMAT_KEY_HEIGHT, 288);
    AMediaFormat_setInt32(format, AMEDIAFORMAT_KEY_FRAME_RATE, 30);
    AMediaFormat_setFloat(format, AMEDIAFORMAT_KEY_I_FRAME_INTERVAL, 1.0F);
    AMediaFormat_setInt32(format, AMEDIAFORMAT_KEY_COLOR_FORMAT, COLOR_FormatYUV420Flexible);
    return format;
}

bool NativeCodecUnitTest::setupCodec(bool isAudio, bool isEncoder) {
    bool isPass = true;
    mFormat = isAudio ? getSampleAudioFormat() : getSampleVideoFormat();
    const char* mediaType = nullptr;
    AMediaFormat_getString(mFormat, AMEDIAFORMAT_KEY_MIME, &mediaType);
    mCodec = isEncoder ? AMediaCodec_createEncoderByType(mediaType)
                       : AMediaCodec_createDecoderByType(mediaType);
    if (!mCodec) {
        ALOGE("unable to create codec %s", mediaType);
        isPass = false;
    }
    return isPass;
}

/* Structure to keep format key and their value to initialize format. Value can be of type
 * string(stringVal) or int(intVal). At once, only one of stringVal or intVal is initialise with
 * valid value. */
struct formatKey {
    const char* key = nullptr;
    const char* stringVal = nullptr;
    int32_t intVal = 0;
};

void setUpDefaultFormatElementsList(std::vector<formatKey*>& vec, bool isAudio, bool isEncoder) {
    if (isAudio) {
        vec.push_back(new formatKey{AMEDIAFORMAT_KEY_MIME, AMEDIA_MIMETYPE_AUDIO_AAC, -1});
        vec.push_back(new formatKey{AMEDIAFORMAT_KEY_SAMPLE_RATE, nullptr, 16000});
        vec.push_back(new formatKey{AMEDIAFORMAT_KEY_CHANNEL_COUNT, nullptr, 1});
        if (isEncoder) {
            vec.push_back(new formatKey{AMEDIAFORMAT_KEY_BIT_RATE, nullptr, 64000});
        }
    } else {
        vec.push_back(new formatKey{AMEDIAFORMAT_KEY_MIME, AMEDIA_MIMETYPE_VIDEO_AVC, -1});
        vec.push_back(new formatKey{AMEDIAFORMAT_KEY_WIDTH, nullptr, 176});
        vec.push_back(new formatKey{AMEDIAFORMAT_KEY_HEIGHT, nullptr, 144});
        if (isEncoder) {
            vec.push_back(new formatKey{AMEDIAFORMAT_KEY_FRAME_RATE, nullptr, 24});
            vec.push_back(new formatKey{AMEDIAFORMAT_KEY_I_FRAME_INTERVAL, nullptr, 1});
            vec.push_back(new formatKey{AMEDIAFORMAT_KEY_BIT_RATE, nullptr, 256000});
            vec.push_back(new formatKey{AMEDIAFORMAT_KEY_COLOR_FORMAT, nullptr,
                                        COLOR_FormatYUV420Flexible});
        }
    }
}

void deleteDefaultFormatElementsList(std::vector<formatKey*>& vector) {
    for (auto& i : vector) delete i;
}

AMediaFormat* getSampleFormat(std::vector<formatKey*> vector, int skipIndex) {
    AMediaFormat* format = AMediaFormat_new();
    for (int i = 0; i < vector.size(); i++) {
        if (skipIndex == i) continue;
        formatKey* element = vector.at(i);
        if (element->stringVal) {
            AMediaFormat_setString(format, element->key, element->stringVal);
        } else {
            AMediaFormat_setInt32(format, element->key, element->intVal);
        }
    }
    return format;
}

bool NativeCodecUnitTest::testConfigureCodecForIncompleteFormat(bool isAudio, bool isEncoder) {
    const char* mediaType = isAudio ? AMEDIA_MIMETYPE_AUDIO_AAC : AMEDIA_MIMETYPE_VIDEO_AVC;
    mCodec = isEncoder ? AMediaCodec_createEncoderByType(mediaType)
                       : AMediaCodec_createDecoderByType(mediaType);
    if (!mCodec) {
        ALOGE("unable to create codec for media Type %s", mediaType);
        return false;
    }
    std::vector<formatKey*> vector;
    bool isPass = true;
    setUpDefaultFormatElementsList(vector, isAudio, isEncoder);
    AMediaFormat* format = nullptr;
    int i;
    for (i = 0; i < vector.size(); i++) {
        if (!isPass) break;
        format = getSampleFormat(vector, i);
        if (AMEDIA_OK ==
            AMediaCodec_configure(mCodec, format, nullptr, nullptr,
                                  isEncoder ? AMEDIACODEC_CONFIGURE_FLAG_ENCODE : 0)) {
            ALOGE("codec configure succeeds for format with missing key %s", vector.at(i)->key);
            isPass = false;
        }
        AMediaFormat_delete(format);
    }
    format = getSampleFormat(vector, i);
    if (AMEDIA_OK !=
        AMediaCodec_configure(mCodec, format, nullptr, nullptr,
                              isEncoder ? AMEDIACODEC_CONFIGURE_FLAG_ENCODE : 0)) {
        ALOGE("codec configure fails for valid format %s", AMediaFormat_toString(format));
        isPass = false;
    }
    AMediaFormat_delete(format);
    deleteDefaultFormatElementsList(vector);
    return isPass;
}

bool NativeCodecUnitTest::testConfigureCodecForBadFlags(bool isEncoder) {
    bool isAudio = true;
    if (!setupCodec(isAudio, isEncoder)) return false;
    bool isPass = true;
    if (AMEDIA_OK ==
        AMediaCodec_configure(mCodec, mFormat, nullptr, nullptr,
                              isEncoder ? 0 : AMEDIACODEC_CONFIGURE_FLAG_ENCODE)) {
        isPass = false;
        ALOGE("codec configure succeeds with bad configure flag");
    }
    AMediaCodec_stop(mCodec);
    return isPass;
}

bool NativeCodecUnitTest::testConfigureInInitState() {
    bool isAudio = true;
    bool isEncoder = true;
    if (!setupCodec(isAudio, isEncoder)) return false;
    const bool boolStates[]{true, false};
    for (auto isAsync : boolStates) {
        if (!configureCodec(mFormat, isAsync, true, isEncoder)) return false;
        if (AMEDIA_OK ==
            AMediaCodec_configure(mCodec, mFormat, nullptr, nullptr,
                                  isEncoder ? AMEDIACODEC_CONFIGURE_FLAG_ENCODE : 0)) {
            ALOGE("codec configure succeeds in initialized state");
            return false;
        }
        RETURN_IF_FAIL(AMediaCodec_stop(mCodec), "AMediaCodec_stop failed")
    }
    return !hasSeenError();
}

bool NativeCodecUnitTest::testConfigureInRunningState() {
    bool isAudio = true;
    bool isEncoder = true;
    if (!setupCodec(isAudio, isEncoder)) return false;
    const bool boolStates[]{true, false};
    for (auto isAsync : boolStates) {
        if (!configureCodec(mFormat, isAsync, false, isEncoder)) return false;
        RETURN_IF_FAIL(AMediaCodec_start(mCodec), "AMediaCodec_start failed")
        if (AMEDIA_OK ==
            AMediaCodec_configure(mCodec, mFormat, nullptr, nullptr,
                                  isEncoder ? AMEDIACODEC_CONFIGURE_FLAG_ENCODE : 0)) {
            ALOGE("codec configure succeeds in initialized state");
            return false;
        }
        if (!flushCodec()) return false;
        if (AMEDIA_OK ==
            AMediaCodec_configure(mCodec, mFormat, nullptr, nullptr,
                                  isEncoder ? AMEDIACODEC_CONFIGURE_FLAG_ENCODE : 0)) {
            ALOGE("codec configure succeeds in flush state");
            return false;
        }
        if (mIsCodecInAsyncMode) {
            RETURN_IF_FAIL(AMediaCodec_start(mCodec), "AMediaCodec_start failed")
        }
        if (!queueEOS()) return false;
        if (AMEDIA_OK ==
            AMediaCodec_configure(mCodec, mFormat, nullptr, nullptr,
                                  isEncoder ? AMEDIACODEC_CONFIGURE_FLAG_ENCODE : 0)) {
            ALOGE("codec configure succeeds in running state");
            return false;
        }
        if (!waitForAllOutputs()) return false;
        if (AMEDIA_OK ==
            AMediaCodec_configure(mCodec, mFormat, nullptr, nullptr,
                                  isEncoder ? AMEDIACODEC_CONFIGURE_FLAG_ENCODE : 0)) {
            ALOGE("codec configure succeeds in eos state");
            return false;
        }
        RETURN_IF_FAIL(AMediaCodec_stop(mCodec), "AMediaCodec_stop failed")
    }
    return !hasSeenError();
}

bool NativeCodecUnitTest::testConfigureInUnInitState() {
    bool isAudio = true;
    bool isEncoder = true;
    if (!setupCodec(isAudio, isEncoder)) return false;
    const bool boolStates[]{true, false};
    for (auto isAsync : boolStates) {
        if (!configureCodec(mFormat, isAsync, false, isEncoder)) return false;
        RETURN_IF_FAIL(AMediaCodec_stop(mCodec), "AMediaCodec_stop failed")
        RETURN_IF_FAIL(AMediaCodec_configure(mCodec, mFormat, nullptr, nullptr,
                                             isEncoder ? AMEDIACODEC_CONFIGURE_FLAG_ENCODE : 0),
                       "codec configure fails in uninitialized state")
        RETURN_IF_FAIL(AMediaCodec_stop(mCodec), "AMediaCodec_stop failed")
    }
    return !hasSeenError();
}

bool NativeCodecUnitTest::testDequeueInputBufferInInitState() {
    bool isAudio = true;
    bool isEncoder = true;
    if (!setupCodec(isAudio, isEncoder)) return false;
    const bool boolStates[]{true, false};
    for (auto isAsync : boolStates) {
        if (!configureCodec(mFormat, isAsync, false, isEncoder)) return false;
        if (AMediaCodec_dequeueInputBuffer(mCodec, kQDeQTimeOutUs) >=
            AMEDIACODEC_INFO_TRY_AGAIN_LATER) {
            ALOGE("dequeue input buffer succeeds in uninitialized state");
            return false;
        }
        RETURN_IF_FAIL(AMediaCodec_stop(mCodec), "AMediaCodec_stop failed")
    }
    return !hasSeenError();
}

bool NativeCodecUnitTest::testDequeueInputBufferInRunningState() {
    bool isAudio = true;
    bool isEncoder = true;
    if (!setupCodec(isAudio, isEncoder)) return false;
    const bool boolStates[]{true, false};
    for (auto isAsync : boolStates) {
        if (!configureCodec(mFormat, isAsync, false, isEncoder)) return false;
        RETURN_IF_FAIL(AMediaCodec_start(mCodec), "AMediaCodec_start failed")
        if (mIsCodecInAsyncMode) {
            if (AMediaCodec_dequeueInputBuffer(mCodec, kQDeQTimeOutUs) >=
                AMEDIACODEC_INFO_TRY_AGAIN_LATER) {
                ALOGE("dequeue input buffer succeeds in running state in async mode");
                return false;
            }
        }
        if (!queueEOS()) return false;
        if (!waitForAllOutputs()) return false;
        RETURN_IF_FAIL(AMediaCodec_stop(mCodec), "AMediaCodec_stop failed")
    }
    return !hasSeenError();
}

bool NativeCodecUnitTest::testDequeueInputBufferInUnInitState() {
    bool isAudio = true;
    bool isEncoder = true;
    if (!setupCodec(isAudio, isEncoder)) return false;
    const bool boolStates[]{true, false};
    for (auto isAsync : boolStates) {
        if (AMediaCodec_dequeueInputBuffer(mCodec, kQDeQTimeOutUs) >=
            AMEDIACODEC_INFO_TRY_AGAIN_LATER) {
            ALOGE("dequeue input buffer succeeds in uninitialized state");
            return false;
        }
        if (!configureCodec(mFormat, isAsync, false, isEncoder)) return false;
        RETURN_IF_FAIL(AMediaCodec_start(mCodec), "AMediaCodec_start failed")
        RETURN_IF_FAIL(AMediaCodec_stop(mCodec), "AMediaCodec_stop failed")
        if (AMediaCodec_dequeueInputBuffer(mCodec, kQDeQTimeOutUs) >= -1) {
            ALOGE("dequeue input buffer succeeds in stopped state");
            return false;
        }
    }
    return !hasSeenError();
}

bool NativeCodecUnitTest::testDequeueOutputBufferInInitState() {
    bool isAudio = true;
    bool isEncoder = true;
    if (!setupCodec(isAudio, isEncoder)) return false;
    const bool boolStates[]{true, false};
    for (auto isAsync : boolStates) {
        if (!configureCodec(mFormat, isAsync, false, isEncoder)) return false;
        AMediaCodecBufferInfo outInfo;
        if (AMediaCodec_dequeueOutputBuffer(mCodec, &outInfo, kQDeQTimeOutUs) >=
            AMEDIACODEC_INFO_OUTPUT_BUFFERS_CHANGED) {
            ALOGE("dequeue output buffer succeeds in uninitialized state");
            return false;
        }
        RETURN_IF_FAIL(AMediaCodec_stop(mCodec), "AMediaCodec_stop failed")
    }
    return !hasSeenError();
}

bool NativeCodecUnitTest::testDequeueOutputBufferInRunningState() {
    bool isAudio = true;
    bool isEncoder = true;
    if (!setupCodec(isAudio, isEncoder)) return false;
    const bool boolStates[]{true, false};
    for (auto isAsync : boolStates) {
        if (!configureCodec(mFormat, isAsync, false, isEncoder)) return false;
        RETURN_IF_FAIL(AMediaCodec_start(mCodec), "AMediaCodec_start failed")
        if (mIsCodecInAsyncMode) {
            AMediaCodecBufferInfo outInfo;
            if (AMediaCodec_dequeueOutputBuffer(mCodec, &outInfo, kQDeQTimeOutUs) >=
                AMEDIACODEC_INFO_OUTPUT_BUFFERS_CHANGED) {
                ALOGE("dequeue output buffer succeeds in running state in async mode");
                return false;
            }
        }
        if (!queueEOS()) return false;
        if (!waitForAllOutputs()) return false;
        RETURN_IF_FAIL(AMediaCodec_stop(mCodec), "AMediaCodec_stop failed")
    }
    return !hasSeenError();
}

bool NativeCodecUnitTest::testDequeueOutputBufferInUnInitState() {
    bool isAudio = true;
    bool isEncoder = true;
    if (!setupCodec(isAudio, isEncoder)) return false;
    const bool boolStates[]{true, false};
    for (auto isAsync : boolStates) {
        AMediaCodecBufferInfo outInfo;
        if (AMediaCodec_dequeueOutputBuffer(mCodec, &outInfo, kQDeQTimeOutUs) >=
            AMEDIACODEC_INFO_OUTPUT_BUFFERS_CHANGED) {
            ALOGE("dequeue output buffer succeeds in uninitialized state");
            return false;
        }
        if (!configureCodec(mFormat, isAsync, false, isEncoder)) return false;
        RETURN_IF_FAIL(AMediaCodec_start(mCodec), "AMediaCodec_start failed")
        RETURN_IF_FAIL(AMediaCodec_stop(mCodec), "AMediaCodec_stop failed")
        if (AMediaCodec_dequeueOutputBuffer(mCodec, &outInfo, kQDeQTimeOutUs) >=
            AMEDIACODEC_INFO_OUTPUT_BUFFERS_CHANGED) {
            ALOGE("dequeue output buffer succeeds in stopped state");
            return false;
        }
    }
    return !hasSeenError();
}

bool NativeCodecUnitTest::testFlushInInitState() {
    bool isAudio = true;
    bool isEncoder = true;
    if (!setupCodec(isAudio, isEncoder)) return false;
    const bool boolStates[]{true, false};
    for (auto isAsync : boolStates) {
        if (!configureCodec(mFormat, isAsync, false, isEncoder)) return false;
        if (flushCodec()) {
            ALOGE("codec flush succeeds in uninitialized state");
            return false;
        }
        RETURN_IF_FAIL(AMediaCodec_stop(mCodec), "AMediaCodec_stop failed")
    }
    return !hasSeenError();
}

bool NativeCodecUnitTest::testFlushInRunningState() {
    bool isAudio = true;
    bool isEncoder = true;
    if (!setupCodec(isAudio, isEncoder)) return false;
    bool isAsync = true;
    if (!configureCodec(mFormat, isAsync, false, isEncoder)) return false;
    RETURN_IF_FAIL(AMediaCodec_start(mCodec), "AMediaCodec_start failed")
    if (!flushCodec()) return false;
    std::this_thread::sleep_for(std::chrono::milliseconds(kStallTimeMs));
    if (!mAsyncHandle.isInputQueueEmpty()) {
        ALOGE("received input buffer callback before start");
        return false;
    }
    RETURN_IF_FAIL(AMediaCodec_start(mCodec), "AMediaCodec_start failed")
    std::this_thread::sleep_for(std::chrono::milliseconds(kStallTimeMs));
    if (mAsyncHandle.isInputQueueEmpty()) {
        ALOGE("did not receive input buffer callback after start");
        return false;
    }
    RETURN_IF_FAIL(AMediaCodec_stop(mCodec), "AMediaCodec_stop failed")
    return !hasSeenError();
}

bool NativeCodecUnitTest::testFlushInUnInitState() {
    bool isAudio = true;
    bool isEncoder = true;
    if (!setupCodec(isAudio, isEncoder)) return false;
    const bool boolStates[]{true, false};
    for (auto isAsync : boolStates) {
        if (flushCodec()) {
            ALOGE("codec flush succeeds in uninitialized state");
            return false;
        }
        if (!configureCodec(mFormat, isAsync, false, isEncoder)) return false;
        RETURN_IF_FAIL(AMediaCodec_start(mCodec), "AMediaCodec_start failed")
        RETURN_IF_FAIL(AMediaCodec_stop(mCodec), "AMediaCodec_stop failed")
        if (flushCodec()) {
            ALOGE("codec flush succeeds in uninitialized state");
            return false;
        }
    }
    return !hasSeenError();
}

bool NativeCodecUnitTest::testGetNameInInitState() {
    bool isAudio = true;
    bool isEncoder = true;
    if (!setupCodec(isAudio, isEncoder)) return false;
    const bool boolStates[]{true, false};
    for (auto isAsync : boolStates) {
        if (!configureCodec(mFormat, isAsync, false, isEncoder)) return false;
        char* name = nullptr;
        if (AMEDIA_OK != AMediaCodec_getName(mCodec, &name) || !name) {
            ALOGE("codec get metadata call fails in initialized state");
            if (name) AMediaCodec_releaseName(mCodec, name);
            return false;
        }
        if (name) AMediaCodec_releaseName(mCodec, name);
        RETURN_IF_FAIL(AMediaCodec_stop(mCodec), "AMediaCodec_stop failed")
    }
    return !hasSeenError();
}

bool NativeCodecUnitTest::testGetNameInRunningState() {
    bool isAudio = true;
    bool isEncoder = true;
    if (!setupCodec(isAudio, isEncoder)) return false;
    const bool boolStates[]{true, false};
    for (auto isAsync : boolStates) {
        if (!configureCodec(mFormat, isAsync, false, isEncoder)) return false;
        RETURN_IF_FAIL(AMediaCodec_start(mCodec), "AMediaCodec_start failed")
        char* name = nullptr;
        if (AMEDIA_OK != AMediaCodec_getName(mCodec, &name) || !name) {
            ALOGE("codec get metadata call fails in running state");
            if (name) AMediaCodec_releaseName(mCodec, name);
            return false;
        }
        if (name) AMediaCodec_releaseName(mCodec, name);
        name = nullptr;
        if (!queueEOS()) return false;
        if (!waitForAllOutputs()) return false;
        if (AMEDIA_OK != AMediaCodec_getName(mCodec, &name) || !name) {
            ALOGE("codec get metadata call fails in running state");
            if (name) AMediaCodec_releaseName(mCodec, name);
            return false;
        }
        if (name) AMediaCodec_releaseName(mCodec, name);
        RETURN_IF_FAIL(AMediaCodec_stop(mCodec), "AMediaCodec_stop failed")
    }
    return !hasSeenError();
}

bool NativeCodecUnitTest::testGetNameInUnInitState() {
    bool isAudio = true;
    bool isEncoder = true;
    if (!setupCodec(isAudio, isEncoder)) return false;
    const bool boolStates[]{true, false};
    char* name = nullptr;
    if (AMEDIA_OK != AMediaCodec_getName(mCodec, &name) || !name) {
        ALOGE("codec get metadata call fails in uninitialized state");
        if (name) AMediaCodec_releaseName(mCodec, name);
        return false;
    }
    if (name) AMediaCodec_releaseName(mCodec, name);
    name = nullptr;
    for (auto isAsync : boolStates) {
        if (!configureCodec(mFormat, isAsync, false, isEncoder)) return false;
        RETURN_IF_FAIL(AMediaCodec_start(mCodec), "AMediaCodec_start failed")
        RETURN_IF_FAIL(AMediaCodec_stop(mCodec), "AMediaCodec_stop failed")
        if (AMEDIA_OK != AMediaCodec_getName(mCodec, &name) || !name) {
            ALOGE("codec get metadata call fails in uninitialized state");
            if (name) AMediaCodec_releaseName(mCodec, name);
            return false;
        }
        if (name) AMediaCodec_releaseName(mCodec, name);
    }
    return !hasSeenError();
}

bool NativeCodecUnitTest::testSetAsyncNotifyCallbackInInitState() {
    bool isAudio = true;
    bool isEncoder = true;
    if (!setupCodec(isAudio, isEncoder)) return false;
    bool isAsync = true;

    // configure component in sync mode
    if (!configureCodec(mFormat, !isAsync, false, isEncoder)) return false;
    // setCallBack in async mode
    RETURN_IF_FAIL(mAsyncHandle.setCallBack(mCodec, isAsync),
                   "AMediaCodec_setAsyncNotifyCallback failed")
    mIsCodecInAsyncMode = true;
    RETURN_IF_FAIL(AMediaCodec_start(mCodec), "AMediaCodec_start failed")
    if (!queueEOS()) return false;
    if (!waitForAllOutputs()) return false;
    RETURN_IF_FAIL(AMediaCodec_stop(mCodec), "AMediaCodec_stop failed")

    // configure component in async mode
    if (!configureCodec(mFormat, isAsync, false, isEncoder)) return false;
    RETURN_IF_FAIL(AMediaCodec_start(mCodec), "AMediaCodec_start failed")
    if (!queueEOS()) return false;
    if (!waitForAllOutputs()) return false;
    RETURN_IF_FAIL(AMediaCodec_stop(mCodec), "AMediaCodec_stop failed")

    // configure component in async mode
    if (!configureCodec(mFormat, isAsync, false, isEncoder)) return false;
    RETURN_IF_FAIL(AMediaCodec_stop(mCodec), "AMediaCodec_stop failed")
    // configure component in sync mode
    if (!reConfigureCodec(mFormat, !isAsync, false, isEncoder)) return false;
    RETURN_IF_FAIL(AMediaCodec_start(mCodec), "AMediaCodec_start failed")
    if (!queueEOS()) return false;
    if (!waitForAllOutputs()) return false;
    RETURN_IF_FAIL(AMediaCodec_stop(mCodec), "AMediaCodec_stop failed")
    return !hasSeenError();
}

bool NativeCodecUnitTest::testSetAsyncNotifyCallbackInRunningState() {
    bool isAudio = true;
    bool isEncoder = true;
    if (!setupCodec(isAudio, isEncoder)) return false;
    const bool boolStates[]{true, false};
    for (auto isAsync : boolStates) {
        if (!configureCodec(mFormat, isAsync, false, isEncoder)) return false;
        RETURN_IF_FAIL(AMediaCodec_start(mCodec), "AMediaCodec_start failed")
        // setCallBack in async mode
        if (AMEDIA_OK == mAsyncHandle.setCallBack(mCodec, true)) {
            ALOGE("setAsyncNotifyCallback call succeeds in running state");
            return false;
        }
        if (!queueEOS()) return false;
        if (!waitForAllOutputs()) return false;
        RETURN_IF_FAIL(AMediaCodec_stop(mCodec), "AMediaCodec_stop failed")
    }
    return !hasSeenError();
}

bool NativeCodecUnitTest::testSetAsyncNotifyCallbackInUnInitState() {
    bool isAudio = true;
    bool isEncoder = true;
    if (!setupCodec(isAudio, isEncoder)) return false;
    bool isAsync = true;
    // setCallBack in async mode
    RETURN_IF_FAIL(mAsyncHandle.setCallBack(mCodec, isAsync),
                   "AMediaCodec_setAsyncNotifyCallback fails in uninitialized state")
    RETURN_IF_FAIL(AMediaCodec_stop(mCodec), "AMediaCodec_stop failed")
    // configure component in sync mode
    if (!reConfigureCodec(mFormat, !isAsync, false, isEncoder)) return false;
    RETURN_IF_FAIL(AMediaCodec_start(mCodec), "AMediaCodec_start failed")
    if (!queueEOS()) return false;
    if (!waitForAllOutputs()) return false;
    RETURN_IF_FAIL(AMediaCodec_stop(mCodec), "AMediaCodec_stop failed")

    // setCallBack in async mode
    RETURN_IF_FAIL(mAsyncHandle.setCallBack(mCodec, isAsync),
                   "AMediaCodec_setAsyncNotifyCallback fails in stopped state")
    RETURN_IF_FAIL(AMediaCodec_stop(mCodec), "AMediaCodec_stop failed")
    // configure component in sync mode
    if (!reConfigureCodec(mFormat, !isAsync, false, isEncoder)) return false;
    RETURN_IF_FAIL(AMediaCodec_start(mCodec), "AMediaCodec_start failed")
    if (!queueEOS()) return false;
    if (!waitForAllOutputs()) return false;
    RETURN_IF_FAIL(AMediaCodec_stop(mCodec), "AMediaCodec_stop failed")
    return !hasSeenError();
}

bool NativeCodecUnitTest::testGetInputBufferInInitState() {
    bool isAudio = true;
    bool isEncoder = true;
    if (!setupCodec(isAudio, isEncoder)) return false;
    const bool boolStates[]{true, false};
    for (auto isAsync : boolStates) {
        if (!configureCodec(mFormat, isAsync, false, isEncoder)) return false;
        size_t bufSize;
        uint8_t* buf = AMediaCodec_getInputBuffer(mCodec, 0, &bufSize);
        if (buf != nullptr) {
            ALOGE("getInputBuffer succeeds in initialized state");
            return false;
        }
        RETURN_IF_FAIL(AMediaCodec_stop(mCodec), "AMediaCodec_stop failed")
    }
    return !hasSeenError();
}

bool NativeCodecUnitTest::testGetInputBufferInRunningState() {
    bool isAudio = true;
    bool isEncoder = true;
    if (!setupCodec(isAudio, isEncoder)) return false;
    const bool boolStates[]{true, false};
    for (auto isAsync : boolStates) {
        if (!configureCodec(mFormat, isAsync, false, isEncoder)) return false;
        RETURN_IF_FAIL(AMediaCodec_start(mCodec), "AMediaCodec_start failed")
        size_t bufSize;
        if (AMediaCodec_getInputBuffer(mCodec, -1, &bufSize) != nullptr) {
            ALOGE("getInputBuffer succeeds for bad buffer index -1");
            return false;
        }
        int bufferIndex = mIsCodecInAsyncMode ? mAsyncHandle.getInput().bufferIndex
                                              : AMediaCodec_dequeueInputBuffer(mCodec, -1);
        uint8_t* buf = AMediaCodec_getInputBuffer(mCodec, bufferIndex, &bufSize);
        if (buf == nullptr) {
            ALOGE("getInputBuffer fails for valid index");
            return false;
        }
        if (!enqueueEOS(bufferIndex)) return false;
        if (!waitForAllOutputs()) return false;
        RETURN_IF_FAIL(AMediaCodec_stop(mCodec), "AMediaCodec_stop failed")
    }
    return !hasSeenError();
}

bool NativeCodecUnitTest::testGetInputBufferInUnInitState() {
    bool isAudio = true;
    bool isEncoder = true;
    if (!setupCodec(isAudio, isEncoder)) return false;
    const bool boolStates[]{true, false};
    for (auto isAsync : boolStates) {
        size_t bufSize;
        if (AMediaCodec_getInputBuffer(mCodec, 0, &bufSize) != nullptr) {
            ALOGE("getInputBuffer succeeds in uninitialized state");
            return false;
        }
        if (!configureCodec(mFormat, isAsync, false, isEncoder)) return false;
        RETURN_IF_FAIL(AMediaCodec_start(mCodec), "AMediaCodec_start failed")
        RETURN_IF_FAIL(AMediaCodec_stop(mCodec), "AMediaCodec_stop failed")
        if (AMediaCodec_getInputBuffer(mCodec, 0, &bufSize) != nullptr) {
            ALOGE("getInputBuffer succeeds in stopped state");
            return false;
        }
    }
    return !hasSeenError();
}

bool NativeCodecUnitTest::testGetInputFormatInInitState() {
    bool isAudio = true;
    bool isEncoder = false;
    if (!setupCodec(isAudio, isEncoder)) return false;
    const bool boolStates[]{true, false};
    const char* mediaType = nullptr;
    AMediaFormat_getString(mFormat, AMEDIAFORMAT_KEY_MIME, &mediaType);
    for (auto isAsync : boolStates) {
        if (!configureCodec(mFormat, isAsync, false, isEncoder)) return false;
        AMediaFormat* dupFormat = AMediaCodec_getInputFormat(mCodec);
        const char* dupMediaType = nullptr;
        AMediaFormat_getString(dupFormat, AMEDIAFORMAT_KEY_MIME, &dupMediaType);
        if (!dupMediaType || strcmp(dupMediaType, mediaType) != 0) {
            AMediaFormat_delete(dupFormat);
            ALOGE("getInputFormat fails in initialized state");
            return false;
        }
        AMediaFormat_delete(dupFormat);
        RETURN_IF_FAIL(AMediaCodec_stop(mCodec), "AMediaCodec_stop failed")
    }
    return !hasSeenError();
}

bool NativeCodecUnitTest::testGetInputFormatInRunningState() {
    bool isAudio = true;
    bool isEncoder = false;
    if (!setupCodec(isAudio, isEncoder)) return false;
    const bool boolStates[]{true, false};
    const char* mediaType = nullptr;
    AMediaFormat_getString(mFormat, AMEDIAFORMAT_KEY_MIME, &mediaType);
    for (auto isAsync : boolStates) {
        if (!configureCodec(mFormat, isAsync, false, isEncoder)) return false;
        RETURN_IF_FAIL(AMediaCodec_start(mCodec), "AMediaCodec_start failed")
        AMediaFormat* dupFormat = AMediaCodec_getInputFormat(mCodec);
        const char* dupMediaType = nullptr;
        AMediaFormat_getString(dupFormat, AMEDIAFORMAT_KEY_MIME, &dupMediaType);
        if (!dupMediaType || strcmp(dupMediaType, mediaType) != 0) {
            AMediaFormat_delete(dupFormat);
            ALOGE("getInputFormat fails in running state");
            return false;
        }
        AMediaFormat_delete(dupFormat);
        RETURN_IF_FAIL(AMediaCodec_stop(mCodec), "AMediaCodec_stop failed")
    }
    return !hasSeenError();
}

bool NativeCodecUnitTest::testGetInputFormatInUnInitState() {
    bool isAudio = true;
    bool isEncoder = true;
    if (!setupCodec(isAudio, isEncoder)) return false;
    const bool boolStates[]{true, false};
    const char* mediaType = nullptr;
    AMediaFormat_getString(mFormat, AMEDIAFORMAT_KEY_MIME, &mediaType);
    for (auto isAsync : boolStates) {
        AMediaFormat* dupFormat = AMediaCodec_getInputFormat(mCodec);
        const char* dupMediaType = nullptr;
        AMediaFormat_getString(dupFormat, AMEDIAFORMAT_KEY_MIME, &dupMediaType);
        if (dupMediaType) {
            AMediaFormat_delete(dupFormat);
            ALOGE("getInputFormat succeeds in uninitialized state");
            return false;
        }
        AMediaFormat_delete(dupFormat);
        if (!configureCodec(mFormat, isAsync, false, isEncoder)) return false;
        RETURN_IF_FAIL(AMediaCodec_start(mCodec), "AMediaCodec_start failed")
        RETURN_IF_FAIL(AMediaCodec_stop(mCodec), "AMediaCodec_stop failed")
        dupFormat = AMediaCodec_getInputFormat(mCodec);
        dupMediaType = nullptr;
        AMediaFormat_getString(dupFormat, AMEDIAFORMAT_KEY_MIME, &dupMediaType);
        if (dupMediaType) {
            AMediaFormat_delete(dupFormat);
            ALOGE("getInputFormat succeeds in stopped state");
            return false;
        }
        AMediaFormat_delete(dupFormat);
    }
    return !hasSeenError();
}

bool NativeCodecUnitTest::testGetOutputBufferInInitState() {
    bool isAudio = true;
    bool isEncoder = true;
    if (!setupCodec(isAudio, isEncoder)) return false;
    const bool boolStates[]{true, false};
    for (auto isAsync : boolStates) {
        if (!configureCodec(mFormat, isAsync, false, isEncoder)) return false;
        size_t bufSize;
        if (AMediaCodec_getOutputBuffer(mCodec, 0, &bufSize) != nullptr) {
            ALOGE("GetOutputBuffer succeeds in initialized state");
            return false;
        }
        RETURN_IF_FAIL(AMediaCodec_stop(mCodec), "AMediaCodec_stop failed")
    }
    return true;
}

bool NativeCodecUnitTest::testGetOutputBufferInRunningState() {
    bool isAudio = true;
    bool isEncoder = true;
    if (!setupCodec(isAudio, isEncoder)) return false;
    AMediaCodecBufferInfo outInfo;
    const bool boolStates[]{true, false};
    for (auto isAsync : boolStates) {
        if (!configureCodec(mFormat, isAsync, false, isEncoder)) return false;
        RETURN_IF_FAIL(AMediaCodec_start(mCodec), "AMediaCodec_start failed")
        size_t bufSize;
        if (AMediaCodec_getOutputBuffer(mCodec, -1, &bufSize)) {
            ALOGE("GetOutputBuffer succeeds for bad buffer index -1");
            return false;
        }
        if (!queueEOS()) return false;
        bool isOk = true;
        if (!hasSeenError()) {
            int bufferIndex = 0;
            size_t buffSize;
            while (!mSawOutputEOS && isOk) {
                if (mIsCodecInAsyncMode) {
                    callbackObject element = mAsyncHandle.getOutput();
                    bufferIndex = element.bufferIndex;
                    if (element.bufferIndex >= 0) {
                        if (!AMediaCodec_getOutputBuffer(mCodec, bufferIndex, &buffSize)) {
                            ALOGE("GetOutputBuffer fails for valid buffer index");
                            return false;
                        }
                        isOk = dequeueOutput(element.bufferIndex, &element.bufferInfo);
                    }
                } else {
                    bufferIndex = AMediaCodec_dequeueOutputBuffer(mCodec, &outInfo, kQDeQTimeOutUs);
                    if (bufferIndex >= 0) {
                        if (!AMediaCodec_getOutputBuffer(mCodec, bufferIndex, &buffSize)) {
                            ALOGE("GetOutputBuffer fails for valid buffer index");
                            return false;
                        }
                        isOk = dequeueOutput(bufferIndex, &outInfo);
                    }
                }
                if (hasSeenError() || !isOk) {
                    ALOGE("Got unexpected error");
                    return false;
                }
            }
            if (AMediaCodec_getOutputBuffer(mCodec, bufferIndex, &bufSize) != nullptr) {
                ALOGE("getOutputBuffer succeeds for buffer index not owned by client");
                return false;
            }
        } else {
            ALOGE("Got unexpected error");
            return false;
        }
        RETURN_IF_FAIL(AMediaCodec_stop(mCodec), "AMediaCodec_stop failed")
    }
    return !hasSeenError();
}

bool NativeCodecUnitTest::testGetOutputBufferInUnInitState() {
    bool isAudio = true;
    bool isEncoder = true;
    if (!setupCodec(isAudio, isEncoder)) return false;
    const bool boolStates[]{true, false};
    for (auto isAsync : boolStates) {
        size_t bufSize;
        if (AMediaCodec_getOutputBuffer(mCodec, 0, &bufSize) != nullptr) {
            ALOGE("GetOutputBuffer succeeds in uninitialized state");
            return false;
        }
        if (!configureCodec(mFormat, isAsync, false, isEncoder)) return false;
        RETURN_IF_FAIL(AMediaCodec_start(mCodec), "AMediaCodec_start failed")
        RETURN_IF_FAIL(AMediaCodec_stop(mCodec), "AMediaCodec_stop failed")
        if (AMediaCodec_getOutputBuffer(mCodec, 0, &bufSize) != nullptr) {
            ALOGE("GetOutputBuffer succeeds in stopped state");
            return false;
        }
    }
    return !hasSeenError();
}

bool NativeCodecUnitTest::testGetOutputFormatInInitState() {
    bool isAudio = true;
    bool isEncoder = true;
    if (!setupCodec(isAudio, isEncoder)) return false;
    const char* mediaType = nullptr;
    AMediaFormat_getString(mFormat, AMEDIAFORMAT_KEY_MIME, &mediaType);
    const bool boolStates[]{true, false};
    for (auto isAsync : boolStates) {
        if (!configureCodec(mFormat, isAsync, false, isEncoder)) return false;
        AMediaFormat* dupFormat = AMediaCodec_getOutputFormat(mCodec);
        const char* dupMediaType = nullptr;
        AMediaFormat_getString(dupFormat, AMEDIAFORMAT_KEY_MIME, &dupMediaType);
        if (!dupMediaType || strcmp(dupMediaType, mediaType) != 0) {
            AMediaFormat_delete(dupFormat);
            ALOGE("getOutputFormat fails in initialized state");
            return false;
        }
        AMediaFormat_delete(dupFormat);
        RETURN_IF_FAIL(AMediaCodec_stop(mCodec), "AMediaCodec_stop failed")
    }
    return !hasSeenError();
}

bool NativeCodecUnitTest::testGetOutputFormatInRunningState() {
    bool isAudio = true;
    bool isEncoder = true;
    if (!setupCodec(isAudio, isEncoder)) return false;
    const char* mediaType = nullptr;
    AMediaFormat_getString(mFormat, AMEDIAFORMAT_KEY_MIME, &mediaType);
    const bool boolStates[]{true, false};
    for (auto isAsync : boolStates) {
        if (!configureCodec(mFormat, isAsync, false, isEncoder)) return false;
        RETURN_IF_FAIL(AMediaCodec_start(mCodec), "AMediaCodec_start failed")
        AMediaFormat* dupFormat = AMediaCodec_getOutputFormat(mCodec);
        const char* dupMediaType = nullptr;
        AMediaFormat_getString(dupFormat, AMEDIAFORMAT_KEY_MIME, &dupMediaType);
        if (!dupMediaType || strcmp(dupMediaType, mediaType) != 0) {
            AMediaFormat_delete(dupFormat);
            ALOGE("getOutputFormat fails in running state");
            return false;
        }
        AMediaFormat_delete(dupFormat);
        RETURN_IF_FAIL(AMediaCodec_stop(mCodec), "AMediaCodec_stop failed")
    }
    return !hasSeenError();
}

bool NativeCodecUnitTest::testGetOutputFormatInUnInitState() {
    bool isAudio = true;
    bool isEncoder = true;
    if (!setupCodec(isAudio, isEncoder)) return false;
    const bool boolStates[]{true, false};
    for (auto isAsync : boolStates) {
        AMediaFormat* dupFormat = AMediaCodec_getOutputFormat(mCodec);
        const char* dupMediaType = nullptr;
        AMediaFormat_getString(dupFormat, AMEDIAFORMAT_KEY_MIME, &dupMediaType);
        if (dupMediaType) {
            AMediaFormat_delete(dupFormat);
            ALOGE("getOutputFormat succeeds in uninitialized state");
            return false;
        }
        AMediaFormat_delete(dupFormat);
        if (!configureCodec(mFormat, isAsync, false, isEncoder)) return false;
        RETURN_IF_FAIL(AMediaCodec_start(mCodec), "AMediaCodec_start failed")
        RETURN_IF_FAIL(AMediaCodec_stop(mCodec), "AMediaCodec_stop failed")
        dupFormat = AMediaCodec_getOutputFormat(mCodec);
        dupMediaType = nullptr;
        AMediaFormat_getString(dupFormat, AMEDIAFORMAT_KEY_MIME, &dupMediaType);
        if (dupMediaType) {
            AMediaFormat_delete(dupFormat);
            ALOGE("getOutputFormat succeeds in stopped state");
            return false;
        }
        AMediaFormat_delete(dupFormat);
    }
    return !hasSeenError();
}

bool NativeCodecUnitTest::testSetParametersInInitState() {
    bool isAudio = false;
    bool isEncoder = true;
    if (!setupCodec(isAudio, isEncoder)) return false;
    const bool boolStates[]{true, false};
    for (auto isAsync : boolStates) {
        if (!configureCodec(mFormat, isAsync, false, isEncoder)) return false;
        int bitrate;
        AMediaFormat_getInt32(mFormat, AMEDIAFORMAT_KEY_BIT_RATE, &bitrate);
        AMediaFormat* params = AMediaFormat_new();
        AMediaFormat_setInt32(params, TBD_AMEDIACODEC_PARAMETER_KEY_VIDEO_BITRATE, bitrate >> 1);
        if (AMEDIA_OK == AMediaCodec_setParameters(mCodec, params)) {
            ALOGE("SetParameters succeeds in initialized state");
            AMediaFormat_delete(params);
            return false;
        }
        AMediaFormat_delete(params);
        RETURN_IF_FAIL(AMediaCodec_stop(mCodec), "AMediaCodec_stop failed")
    }
    return !hasSeenError();
}

bool NativeCodecUnitTest::testSetParametersInRunningState() {
    bool isAudio = false;
    bool isEncoder = true;
    if (!setupCodec(isAudio, isEncoder)) return false;
    const bool boolStates[]{true, false};
    int bitrate;
    AMediaFormat_getInt32(mFormat, AMEDIAFORMAT_KEY_BIT_RATE, &bitrate);
    for (auto isAsync : boolStates) {
        if (!configureCodec(mFormat, isAsync, false, isEncoder)) return false;
        RETURN_IF_FAIL(AMediaCodec_start(mCodec), "AMediaCodec_start failed")
        // behaviour of setParams with null argument is acceptable according to SDK
        AMediaCodec_setParameters(mCodec, nullptr);
        AMediaFormat* params = AMediaFormat_new();
        AMediaFormat_setInt32(params, TBD_AMEDIACODEC_PARAMETER_KEY_VIDEO_BITRATE, bitrate >> 1);
        if (AMEDIA_OK != AMediaCodec_setParameters(mCodec, params)) {
            ALOGE("SetParameters fails in running state");
            AMediaFormat_delete(params);
            return false;
        }
        if (!queueEOS()) return false;
        AMediaCodec_setParameters(mCodec, nullptr);
        AMediaFormat_setInt32(mFormat, TBD_AMEDIACODEC_PARAMETER_KEY_VIDEO_BITRATE, bitrate << 1);
        if (AMEDIA_OK != AMediaCodec_setParameters(mCodec, mFormat)) {
            ALOGE("SetParameters fails in running state");
            AMediaFormat_delete(params);
            return false;
        }
        if (!waitForAllOutputs()) return false;
        AMediaCodec_setParameters(mCodec, nullptr);
        AMediaFormat_setInt32(mFormat, TBD_AMEDIACODEC_PARAMETER_KEY_VIDEO_BITRATE, bitrate);
        if (AMEDIA_OK != AMediaCodec_setParameters(mCodec, mFormat)) {
            ALOGE("SetParameters fails in running state");
            AMediaFormat_delete(params);
            return false;
        }
        AMediaFormat_delete(params);
        RETURN_IF_FAIL(AMediaCodec_stop(mCodec), "AMediaCodec_stop failed")
    }
    return !hasSeenError();
}

bool NativeCodecUnitTest::testSetParametersInUnInitState() {
    bool isAudio = false;
    bool isEncoder = true;
    if (!setupCodec(isAudio, isEncoder)) return false;
    const bool boolStates[]{true, false};
    for (auto isAsync : boolStates) {
        if (!configureCodec(mFormat, isAsync, false, isEncoder)) return false;
        RETURN_IF_FAIL(AMediaCodec_start(mCodec), "AMediaCodec_start failed")
        RETURN_IF_FAIL(AMediaCodec_stop(mCodec), "AMediaCodec_stop failed")
        int bitrate;
        AMediaFormat_getInt32(mFormat, AMEDIAFORMAT_KEY_BIT_RATE, &bitrate);
        AMediaFormat* params = AMediaFormat_new();
        AMediaFormat_setInt32(params, TBD_AMEDIACODEC_PARAMETER_KEY_VIDEO_BITRATE, bitrate >> 1);
        if (AMEDIA_OK == AMediaCodec_setParameters(mCodec, params)) {
            ALOGE("SetParameters succeeds in stopped state");
            AMediaFormat_delete(params);
            return false;
        }
        AMediaFormat_delete(params);
    }
    return !hasSeenError();
}

bool NativeCodecUnitTest::testStartInRunningState() {
    bool isAudio = true;
    bool isEncoder = true;
    if (!setupCodec(isAudio, isEncoder)) return false;
    if (!configureCodec(mFormat, false, false, isEncoder)) return false;
    RETURN_IF_FAIL(AMediaCodec_start(mCodec), "AMediaCodec_start failed")
    if (AMEDIA_OK == AMediaCodec_start(mCodec)) {
        ALOGE("Start succeeds in running state");
        return false;
    }
    RETURN_IF_FAIL(AMediaCodec_stop(mCodec), "AMediaCodec_stop failed")
    return !hasSeenError();
}

bool NativeCodecUnitTest::testStartInUnInitState() {
    bool isAudio = true;
    bool isEncoder = true;
    if (!setupCodec(isAudio, isEncoder)) return false;
    if (AMEDIA_OK == AMediaCodec_start(mCodec)) {
        ALOGE("codec start succeeds before initialization");
        return false;
    }
    if (!configureCodec(mFormat, false, false, isEncoder)) return false;
    RETURN_IF_FAIL(AMediaCodec_start(mCodec), "AMediaCodec_start failed")
    RETURN_IF_FAIL(AMediaCodec_stop(mCodec), "AMediaCodec_stop failed")
    if (AMEDIA_OK == AMediaCodec_start(mCodec)) {
        ALOGE("codec start succeeds in stopped state");
        return false;
    }
    return !hasSeenError();
}

bool NativeCodecUnitTest::testStopInInitState() {
    bool isAudio = true;
    bool isEncoder = true;
    if (!setupCodec(isAudio, isEncoder)) return false;
    const bool boolStates[]{true, false};
    for (auto isAsync : boolStates) {
        if (!configureCodec(mFormat, isAsync, false, isEncoder)) return false;
        RETURN_IF_FAIL(AMediaCodec_stop(mCodec), "Stop fails in initialized state")
    }
    return !hasSeenError();
}

bool NativeCodecUnitTest::testStopInRunningState() {
    bool isAudio = true;
    bool isEncoder = true;
    if (!setupCodec(isAudio, isEncoder)) return false;
    const bool boolStates[]{true, false};
    for (auto isAsync : boolStates) {
        if (!configureCodec(mFormat, isAsync, false, isEncoder)) return false;
        RETURN_IF_FAIL(AMediaCodec_start(mCodec), "AMediaCodec_start failed")
        if (!queueEOS()) return false;
        RETURN_IF_FAIL(AMediaCodec_stop(mCodec), "Stop fails in running state")
    }
    return !hasSeenError();
}

bool NativeCodecUnitTest::testStopInUnInitState() {
    bool isAudio = true;
    bool isEncoder = true;
    if (!setupCodec(isAudio, isEncoder)) return false;
    const bool boolStates[]{true, false};
    for (auto isAsync : boolStates) {
        if (!configureCodec(mFormat, isAsync, false, isEncoder)) return false;
        RETURN_IF_FAIL(AMediaCodec_start(mCodec), "AMediaCodec_start failed")
        RETURN_IF_FAIL(AMediaCodec_stop(mCodec), "AMediaCodec_stop failed")
        RETURN_IF_FAIL(AMediaCodec_stop(mCodec), "Stop fails in stopped state")
    }
    return !hasSeenError();
}

bool NativeCodecUnitTest::testQueueInputBufferInInitState() {
    bool isAudio = true;
    bool isEncoder = true;
    if (!setupCodec(isAudio, isEncoder)) return false;
    const bool boolStates[]{true, false};
    for (auto isAsync : boolStates) {
        if (!configureCodec(mFormat, isAsync, false, isEncoder)) return false;
        if (AMEDIA_OK ==
            AMediaCodec_queueInputBuffer(mCodec, 0, 0, 0, 0,
                                         AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM)) {
            ALOGE("queueInputBuffer succeeds in initialized state");
            return false;
        }
        RETURN_IF_FAIL(AMediaCodec_stop(mCodec), "AMediaCodec_stop failed")
    }
    return !hasSeenError();
}

bool NativeCodecUnitTest::testQueueInputBufferWithBadIndex() {
    bool isAudio = true;
    bool isEncoder = true;
    if (!setupCodec(isAudio, isEncoder)) return false;
    const bool boolStates[]{true, false};
    for (auto isAsync : boolStates) {
        if (!configureCodec(mFormat, isAsync, false, isEncoder)) return false;
        RETURN_IF_FAIL(AMediaCodec_start(mCodec), "AMediaCodec_start failed")
        if (AMEDIA_OK ==
            AMediaCodec_queueInputBuffer(mCodec, -1, 0, 0, 0,
                                         AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM)) {
            ALOGE("queueInputBuffer succeeds with bad buffer index :: -1");
            return false;
        }
        RETURN_IF_FAIL(AMediaCodec_stop(mCodec), "AMediaCodec_stop failed")
    }
    return !hasSeenError();
}

bool NativeCodecUnitTest::testQueueInputBufferWithBadSize() {
    bool isAudio = true;
    bool isEncoder = true;
    if (!setupCodec(isAudio, isEncoder)) return false;
    const bool boolStates[]{true, false};
    for (auto isAsync : boolStates) {
        if (!configureCodec(mFormat, isAsync, false, isEncoder)) return false;
        RETURN_IF_FAIL(AMediaCodec_start(mCodec), "AMediaCodec_start failed")
        int bufferIndex = mIsCodecInAsyncMode ? mAsyncHandle.getInput().bufferIndex
                                              : AMediaCodec_dequeueInputBuffer(mCodec, -1);
        size_t bufSize;
        uint8_t* buf = AMediaCodec_getInputBuffer(mCodec, bufferIndex, &bufSize);
        if (buf == nullptr) {
            ALOGE("getInputBuffer fails for valid index");
            return false;
        } else {
            if (AMEDIA_OK ==
                AMediaCodec_queueInputBuffer(mCodec, 0, 0, bufSize + 100, 0,
                                             AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM)) {
                ALOGE("queueInputBuffer succeeds for bad size %zu, buffer capacity %zu ",
                      bufSize + 100, bufSize);
                return false;
            }
        }
        RETURN_IF_FAIL(AMediaCodec_stop(mCodec), "AMediaCodec_stop failed")
    }
    return !hasSeenError();
}

bool NativeCodecUnitTest::testQueueInputBufferWithBadBuffInfo() {
    bool isAudio = true;
    bool isEncoder = true;
    if (!setupCodec(isAudio, isEncoder)) return false;
    const bool boolStates[]{true, false};
    for (auto isAsync : boolStates) {
        if (!configureCodec(mFormat, isAsync, false, isEncoder)) return false;
        RETURN_IF_FAIL(AMediaCodec_start(mCodec), "AMediaCodec_start failed")
        int bufferIndex = mIsCodecInAsyncMode ? mAsyncHandle.getInput().bufferIndex
                                              : AMediaCodec_dequeueInputBuffer(mCodec, -1);
        size_t bufSize;
        uint8_t* buf = AMediaCodec_getInputBuffer(mCodec, bufferIndex, &bufSize);
        if (buf == nullptr) {
            ALOGE("getInputBuffer fails for valid index");
            return false;
        } else {
            if (AMEDIA_OK ==
                AMediaCodec_queueInputBuffer(mCodec, 0, 16, bufSize, 0,
                                             AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM)) {
                ALOGE("queueInputBuffer succeeds with bad offset and size param");
                return false;
            }
        }
        RETURN_IF_FAIL(AMediaCodec_stop(mCodec), "AMediaCodec_stop failed")
    }
    return !hasSeenError();
}

bool NativeCodecUnitTest::testQueueInputBufferWithBadOffset() {
    bool isAudio = true;
    bool isEncoder = true;
    if (!setupCodec(isAudio, isEncoder)) return false;
    const bool boolStates[]{true, false};
    for (auto isAsync : boolStates) {
        if (!configureCodec(mFormat, isAsync, false, isEncoder)) return false;
        RETURN_IF_FAIL(AMediaCodec_start(mCodec), "AMediaCodec_start failed")
        if (AMEDIA_OK ==
            AMediaCodec_queueInputBuffer(mCodec, 0, -1, 0, 0,
                                         AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM)) {
            ALOGE("queueInputBuffer succeeds with bad buffer offset :: -1");
            return false;
        }
        RETURN_IF_FAIL(AMediaCodec_stop(mCodec), "AMediaCodec_stop failed")
    }
    return !hasSeenError();
}

bool NativeCodecUnitTest::testQueueInputBufferInUnInitState() {
    bool isAudio = true;
    bool isEncoder = true;
    if (!setupCodec(isAudio, isEncoder)) return false;
    const bool boolStates[]{true, false};
    for (auto isAsync : boolStates) {
        if (AMEDIA_OK ==
            AMediaCodec_queueInputBuffer(mCodec, 0, 0, 0, 0,
                                         AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM)) {
            ALOGE("queueInputBuffer succeeds in uninitialized state");
            return false;
        }
        if (!configureCodec(mFormat, isAsync, false, isEncoder)) return false;
        RETURN_IF_FAIL(AMediaCodec_start(mCodec), "AMediaCodec_start failed")
        RETURN_IF_FAIL(AMediaCodec_stop(mCodec), "AMediaCodec_stop failed")
        if (AMEDIA_OK ==
            AMediaCodec_queueInputBuffer(mCodec, 0, 0, 0, 0,
                                         AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM)) {
            ALOGE("queueInputBuffer succeeds in stopped state");
            return false;
        }
    }
    return !hasSeenError();
}

bool NativeCodecUnitTest::testReleaseOutputBufferInInitState() {
    bool isAudio = true;
    bool isEncoder = true;
    if (!setupCodec(isAudio, isEncoder)) return false;
    const bool boolStates[]{true, false};
    for (auto isAsync : boolStates) {
        if (!configureCodec(mFormat, isAsync, false, isEncoder)) return false;
        if (AMEDIA_OK == AMediaCodec_releaseOutputBuffer(mCodec, 0, false)) {
            ALOGE("ReleaseOutputBuffer succeeds in initialized state");
            return false;
        }
        RETURN_IF_FAIL(AMediaCodec_stop(mCodec), "AMediaCodec_stop failed")
    }
    return !hasSeenError();
}

bool NativeCodecUnitTest::testReleaseOutputBufferInRunningState() {
    bool isAudio = true;
    bool isEncoder = true;
    if (!setupCodec(isAudio, isEncoder)) return false;
    AMediaCodecBufferInfo outInfo;
    const bool boolStates[]{true, false};
    for (auto isAsync : boolStates) {
        if (!configureCodec(mFormat, isAsync, false, isEncoder)) return false;
        RETURN_IF_FAIL(AMediaCodec_start(mCodec), "AMediaCodec_start failed")
        if (AMEDIA_OK == AMediaCodec_releaseOutputBuffer(mCodec, -1, false)) {
            ALOGE("ReleaseOutputBuffer succeeds for bad buffer index -1");
            return false;
        }
        if (!queueEOS()) return false;
        if (!hasSeenError()) {
            int bufferIndex = 0;
            size_t buffSize;
            bool isOk = true;
            while (!mSawOutputEOS && isOk) {
                if (mIsCodecInAsyncMode) {
                    callbackObject element = mAsyncHandle.getOutput();
                    bufferIndex = element.bufferIndex;
                    if (element.bufferIndex >= 0) {
                        if (!AMediaCodec_getOutputBuffer(mCodec, bufferIndex, &buffSize)) {
                            ALOGE("GetOutputBuffer fails for valid buffer index");
                            return false;
                        }
                        isOk = dequeueOutput(element.bufferIndex, &element.bufferInfo);
                    }
                } else {
                    bufferIndex = AMediaCodec_dequeueOutputBuffer(mCodec, &outInfo, kQDeQTimeOutUs);
                    if (bufferIndex >= 0) {
                        if (!AMediaCodec_getOutputBuffer(mCodec, bufferIndex, &buffSize)) {
                            ALOGE("GetOutputBuffer fails for valid buffer index");
                            return false;
                        }
                        isOk = dequeueOutput(bufferIndex, &outInfo);
                    }
                }
                if (hasSeenError() || !isOk) {
                    ALOGE("Got unexpected error");
                    return false;
                }
            }
            if (AMEDIA_OK == AMediaCodec_releaseOutputBuffer(mCodec, bufferIndex, false)) {
                ALOGE("ReleaseOutputBuffer succeeds for buffer index not owned by client");
                return false;
            }
        } else {
            ALOGE("Got unexpected error");
            return false;
        }
        RETURN_IF_FAIL(AMediaCodec_stop(mCodec), "AMediaCodec_stop failed")
    }
    return !hasSeenError();
}

bool NativeCodecUnitTest::testReleaseOutputBufferInUnInitState() {
    bool isAudio = true;
    bool isEncoder = true;
    if (!setupCodec(isAudio, isEncoder)) return false;
    const bool boolStates[]{true, false};
    for (auto isAsync : boolStates) {
        if (AMEDIA_OK == AMediaCodec_releaseOutputBuffer(mCodec, 0, false)) {
            ALOGE("ReleaseOutputBuffer succeeds in uninitialized state");
            return false;
        }
        if (!configureCodec(mFormat, isAsync, false, isEncoder)) return false;
        RETURN_IF_FAIL(AMediaCodec_start(mCodec), "AMediaCodec_start failed")
        RETURN_IF_FAIL(AMediaCodec_stop(mCodec), "AMediaCodec_stop failed")
        if (AMEDIA_OK == AMediaCodec_releaseOutputBuffer(mCodec, 0, false)) {
            ALOGE("ReleaseOutputBuffer succeeds in stopped state");
            return false;
        }
    }
    return !hasSeenError();
}

bool NativeCodecUnitTest::testGetBufferFormatInInitState() {
    bool isAudio = true;
    bool isEncoder = true;
    if (!setupCodec(isAudio, isEncoder)) return false;
    const bool boolStates[]{true, false};
    for (auto isAsync : boolStates) {
        if (!configureCodec(mFormat, isAsync, false, isEncoder)) return false;
        AMediaFormat* dupFormat = AMediaCodec_getBufferFormat(mCodec, 0);
        const char* dupMediaType = nullptr;
        AMediaFormat_getString(dupFormat, AMEDIAFORMAT_KEY_MIME, &dupMediaType);
        if (dupMediaType) {
            AMediaFormat_delete(dupFormat);
            ALOGE("GetBufferFormat succeeds in initialized state");
            return false;
        }
        AMediaFormat_delete(dupFormat);
        RETURN_IF_FAIL(AMediaCodec_stop(mCodec), "AMediaCodec_stop failed")
    }
    return !hasSeenError();
}

bool NativeCodecUnitTest::testGetBufferFormatInRunningState() {
    bool isAudio = true;
    bool isEncoder = true;
    if (!setupCodec(isAudio, isEncoder)) return false;
    const char* mediaType = nullptr;
    AMediaFormat_getString(mFormat, AMEDIAFORMAT_KEY_MIME, &mediaType);
    AMediaCodecBufferInfo outInfo;
    const bool boolStates[]{true, false};
    for (auto isAsync : boolStates) {
        if (!configureCodec(mFormat, isAsync, false, isEncoder)) return false;
        RETURN_IF_FAIL(AMediaCodec_start(mCodec), "AMediaCodec_start failed")
        AMediaFormat* dupFormat = AMediaCodec_getBufferFormat(mCodec, -1);
        const char* dupMediaType = nullptr;
        AMediaFormat_getString(dupFormat, AMEDIAFORMAT_KEY_MIME, &dupMediaType);
        if (dupMediaType) {
            AMediaFormat_delete(dupFormat);
            ALOGE("GetBufferFormat succeeds for bad buffer index -1");
            return false;
        }
        AMediaFormat_delete(dupFormat);
        if (!queueEOS()) return false;
        if (!hasSeenError()) {
            int bufferIndex = 0;
            bool isOk = true;
            while (!mSawOutputEOS && isOk) {
                if (mIsCodecInAsyncMode) {
                    callbackObject element = mAsyncHandle.getOutput();
                    bufferIndex = element.bufferIndex;
                    if (element.bufferIndex >= 0) {
                        dupFormat = AMediaCodec_getBufferFormat(mCodec, bufferIndex);
                        dupMediaType = nullptr;
                        AMediaFormat_getString(dupFormat, AMEDIAFORMAT_KEY_MIME, &dupMediaType);
                        if (!dupMediaType || strcmp(dupMediaType, mediaType) != 0) {
                            AMediaFormat_delete(dupFormat);
                            ALOGE("GetBufferFormat fails in running state");
                            return false;
                        }
                        AMediaFormat_delete(dupFormat);
                        isOk = dequeueOutput(element.bufferIndex, &element.bufferInfo);
                    }
                } else {
                    bufferIndex = AMediaCodec_dequeueOutputBuffer(mCodec, &outInfo, kQDeQTimeOutUs);
                    if (bufferIndex >= 0) {
                        dupFormat = AMediaCodec_getBufferFormat(mCodec, bufferIndex);
                        dupMediaType = nullptr;
                        AMediaFormat_getString(dupFormat, AMEDIAFORMAT_KEY_MIME, &dupMediaType);
                        if (!dupMediaType || strcmp(dupMediaType, mediaType) != 0) {
                            AMediaFormat_delete(dupFormat);
                            ALOGE("GetBufferFormat fails in running state");
                            return false;
                        }
                        AMediaFormat_delete(dupFormat);
                        isOk = dequeueOutput(bufferIndex, &outInfo);
                    }
                }
                if (hasSeenError() || !isOk) {
                    ALOGE("Got unexpected error");
                    return false;
                }
            }
            dupFormat = AMediaCodec_getBufferFormat(mCodec, bufferIndex);
            dupMediaType = nullptr;
            AMediaFormat_getString(dupFormat, AMEDIAFORMAT_KEY_MIME, &dupMediaType);
            if (dupMediaType) {
                AMediaFormat_delete(dupFormat);
                ALOGE("GetBufferFormat succeeds for buffer index not owned by client");
                return false;
            }
            AMediaFormat_delete(dupFormat);
        } else {
            ALOGE("Got unexpected error");
            return false;
        }
        RETURN_IF_FAIL(AMediaCodec_stop(mCodec), "AMediaCodec_stop failed")
    }
    return !hasSeenError();
}

bool NativeCodecUnitTest::testGetBufferFormatInUnInitState() {
    bool isAudio = true;
    bool isEncoder = true;
    if (!setupCodec(isAudio, isEncoder)) return false;
    const bool boolStates[]{true, false};
    for (auto isAsync : boolStates) {
        AMediaFormat* dupFormat = AMediaCodec_getBufferFormat(mCodec, 0);
        const char* dupMediaType = nullptr;
        AMediaFormat_getString(dupFormat, AMEDIAFORMAT_KEY_MIME, &dupMediaType);
        if (dupMediaType) {
            AMediaFormat_delete(dupFormat);
            ALOGE("GetBufferFormat succeeds in uninitialized state");
            return false;
        }
        AMediaFormat_delete(dupFormat);
        if (!configureCodec(mFormat, isAsync, false, isEncoder)) return false;
        RETURN_IF_FAIL(AMediaCodec_start(mCodec), "AMediaCodec_start failed")
        RETURN_IF_FAIL(AMediaCodec_stop(mCodec), "AMediaCodec_stop failed")
        dupFormat = AMediaCodec_getBufferFormat(mCodec, 0);
        dupMediaType = nullptr;
        AMediaFormat_getString(dupFormat, AMEDIAFORMAT_KEY_MIME, &dupMediaType);
        if (dupMediaType) {
            AMediaFormat_delete(dupFormat);
            ALOGE("GetBufferFormat succeeds in stopped state");
            return false;
        }
        AMediaFormat_delete(dupFormat);
    }
    return !hasSeenError();
}

static jboolean nativeTestCreateByCodecNameForNull(JNIEnv*, jobject) {
    bool isPass = true;
    AMediaCodec* codec = AMediaCodec_createCodecByName(nullptr);
    if (codec) {
        AMediaCodec_delete(codec);
        ALOGE("AMediaCodec_createCodecByName succeeds with null argument");
        isPass = false;
    }
    return static_cast<jboolean>(isPass);
}

static jboolean nativeTestCreateByCodecNameForInvalidName(JNIEnv*, jobject) {
    bool isPass = true;
    AMediaCodec* codec = AMediaCodec_createCodecByName("invalid name");
    if (codec) {
        AMediaCodec_delete(codec);
        ALOGE("AMediaCodec_createCodecByName succeeds with invalid name");
        isPass = false;
    }
    return static_cast<jboolean>(isPass);
}

static jboolean nativeTestCreateDecoderByTypeForNull(JNIEnv*, jobject) {
    bool isPass = true;
    AMediaCodec* codec = AMediaCodec_createDecoderByType(nullptr);
    if (codec) {
        AMediaCodec_delete(codec);
        ALOGE("AMediaCodec_createDecoderByType succeeds with null argument");
        isPass = false;
    }
    return static_cast<jboolean>(isPass);
}

static jboolean nativeTestCreateDecoderByTypeForInvalidMediaType(JNIEnv*, jobject) {
    bool isPass = true;
    AMediaCodec* codec = AMediaCodec_createDecoderByType("invalid name");
    if (codec) {
        AMediaCodec_delete(codec);
        ALOGE("AMediaCodec_createDecoderByType succeeds with invalid name");
        isPass = false;
    }
    return static_cast<jboolean>(isPass);
}

static jboolean nativeTestCreateEncoderByTypeForNull(JNIEnv*, jobject) {
    bool isPass = true;
    AMediaCodec* codec = AMediaCodec_createEncoderByType(nullptr);
    if (codec) {
        AMediaCodec_delete(codec);
        ALOGE("AMediaCodec_createEncoderByType succeeds with null argument");
        isPass = false;
    }
    return static_cast<jboolean>(isPass);
}

static jboolean nativeTestCreateEncoderByTypeForInvalidMediaType(JNIEnv*, jobject) {
    bool isPass = true;
    AMediaCodec* codec = AMediaCodec_createEncoderByType("invalid name");
    if (codec) {
        AMediaCodec_delete(codec);
        ALOGE("AMediaCodec_createEncoderByType succeeds with invalid name");
        isPass = false;
    }
    return static_cast<jboolean>(isPass);
}

static jboolean nativeTestConfigureForNullFormat(JNIEnv*, jobject) {
    AMediaCodec* codec = AMediaCodec_createEncoderByType(AMEDIA_MIMETYPE_AUDIO_AAC);
    if (!codec) {
        ALOGE("unable to create codec %s", AMEDIA_MIMETYPE_AUDIO_AAC);
        return static_cast<jboolean>(false);
    }
    bool isPass = (AMEDIA_OK !=
                   AMediaCodec_configure(codec, nullptr, nullptr, nullptr,
                                         AMEDIACODEC_CONFIGURE_FLAG_ENCODE));
    if (!isPass) {
        ALOGE("codec configure succeeds with null format");
    }
    AMediaCodec_delete(codec);
    return static_cast<jboolean>(isPass);
}

static jboolean nativeTestConfigureForEmptyFormat(JNIEnv*, jobject) {
    AMediaCodec* codec = AMediaCodec_createEncoderByType(AMEDIA_MIMETYPE_AUDIO_AAC);
    if (!codec) {
        ALOGE("unable to create codec %s", AMEDIA_MIMETYPE_AUDIO_AAC);
        return static_cast<jboolean>(false);
    }
    AMediaFormat* format = AMediaFormat_new();
    bool isPass = (AMEDIA_OK !=
                   AMediaCodec_configure(codec, format, nullptr, nullptr,
                                         AMEDIACODEC_CONFIGURE_FLAG_ENCODE));
    if (!isPass) {
        ALOGE("codec configure succeeds with empty format");
    }
    AMediaFormat_delete(format);
    AMediaCodec_delete(codec);
    return static_cast<jboolean>(isPass);
}

static jboolean nativeTestConfigureCodecForIncompleteFormat(JNIEnv*, jobject, jboolean isAudio,
                                                            jboolean isEncoder) {
    auto* nativeCodecUnitTest = new NativeCodecUnitTest(AMEDIA_MIMETYPE_AUDIO_AAC);
    bool isPass = nativeCodecUnitTest->testConfigureCodecForIncompleteFormat(isAudio, isEncoder);
    delete nativeCodecUnitTest;
    return static_cast<jboolean>(isPass);
}

static jboolean nativeTestConfigureEncoderForBadFlags(JNIEnv*, jobject) {
    auto* nativeCodecUnitTest = new NativeCodecUnitTest(AMEDIA_MIMETYPE_AUDIO_AAC);
    bool isEncoder = true;
    bool isPass = nativeCodecUnitTest->testConfigureCodecForBadFlags(isEncoder);
    delete nativeCodecUnitTest;
    return static_cast<jboolean>(isPass);
}

static jboolean nativeTestConfigureDecoderForBadFlags(JNIEnv*, jobject) {
    auto* nativeCodecUnitTest = new NativeCodecUnitTest(AMEDIA_MIMETYPE_AUDIO_AAC);
    bool isEncoder = false;
    bool isPass = nativeCodecUnitTest->testConfigureCodecForBadFlags(isEncoder);
    delete nativeCodecUnitTest;
    return static_cast<jboolean>(isPass);
}

static jboolean nativeTestConfigureInInitState(JNIEnv*, jobject) {
    auto* nativeCodecUnitTest = new NativeCodecUnitTest(AMEDIA_MIMETYPE_AUDIO_AAC);
    bool isPass = nativeCodecUnitTest->testConfigureInInitState();
    delete nativeCodecUnitTest;
    return static_cast<jboolean>(isPass);
}

static jboolean nativeTestConfigureInRunningState(JNIEnv*, jobject) {
    auto* nativeCodecUnitTest = new NativeCodecUnitTest(AMEDIA_MIMETYPE_AUDIO_AAC);
    bool isPass = nativeCodecUnitTest->testConfigureInRunningState();
    delete nativeCodecUnitTest;
    return static_cast<jboolean>(isPass);
}

static jboolean nativeTestConfigureInUnInitState(JNIEnv*, jobject) {
    auto* nativeCodecUnitTest = new NativeCodecUnitTest(AMEDIA_MIMETYPE_AUDIO_AAC);
    bool isPass = nativeCodecUnitTest->testConfigureInUnInitState();
    delete nativeCodecUnitTest;
    return static_cast<jboolean>(isPass);
}

static jboolean nativeTestDequeueInputBufferInInitState(JNIEnv*, jobject) {
    auto* nativeCodecUnitTest = new NativeCodecUnitTest(AMEDIA_MIMETYPE_AUDIO_AAC);
    bool isPass = nativeCodecUnitTest->testDequeueInputBufferInInitState();
    delete nativeCodecUnitTest;
    return static_cast<jboolean>(isPass);
}

static jboolean nativeTestDequeueInputBufferInRunningState(JNIEnv*, jobject) {
    auto* nativeCodecUnitTest = new NativeCodecUnitTest(AMEDIA_MIMETYPE_AUDIO_AAC);
    bool isPass = nativeCodecUnitTest->testDequeueInputBufferInRunningState();
    delete nativeCodecUnitTest;
    return static_cast<jboolean>(isPass);
}

static jboolean nativeTestDequeueInputBufferInUnInitState(JNIEnv*, jobject) {
    auto* nativeCodecUnitTest = new NativeCodecUnitTest(AMEDIA_MIMETYPE_AUDIO_AAC);
    bool isPass = nativeCodecUnitTest->testDequeueInputBufferInUnInitState();
    delete nativeCodecUnitTest;
    return static_cast<jboolean>(isPass);
}

static jboolean nativeTestDequeueOutputBufferInInitState(JNIEnv*, jobject) {
    auto* nativeCodecUnitTest = new NativeCodecUnitTest(AMEDIA_MIMETYPE_AUDIO_AAC);
    bool isPass = nativeCodecUnitTest->testDequeueOutputBufferInInitState();
    delete nativeCodecUnitTest;
    return static_cast<jboolean>(isPass);
}

static jboolean nativeTestDequeueOutputBufferInRunningState(JNIEnv*, jobject) {
    auto* nativeCodecUnitTest = new NativeCodecUnitTest(AMEDIA_MIMETYPE_AUDIO_AAC);
    bool isPass = nativeCodecUnitTest->testDequeueOutputBufferInRunningState();
    delete nativeCodecUnitTest;
    return static_cast<jboolean>(isPass);
}

static jboolean nativeTestDequeueOutputBufferInUnInitState(JNIEnv*, jobject) {
    auto* nativeCodecUnitTest = new NativeCodecUnitTest(AMEDIA_MIMETYPE_AUDIO_AAC);
    bool isPass = nativeCodecUnitTest->testDequeueOutputBufferInUnInitState();
    delete nativeCodecUnitTest;
    return static_cast<jboolean>(isPass);
}

static jboolean nativeTestFlushInInitState(JNIEnv*, jobject) {
    auto* nativeCodecUnitTest = new NativeCodecUnitTest(AMEDIA_MIMETYPE_AUDIO_AAC);
    bool isPass = nativeCodecUnitTest->testFlushInInitState();
    delete nativeCodecUnitTest;
    return static_cast<jboolean>(isPass);
}

static jboolean nativeTestFlushInRunningState(JNIEnv*, jobject) {
    auto* nativeCodecUnitTest = new NativeCodecUnitTest(AMEDIA_MIMETYPE_AUDIO_AAC);
    bool isPass = nativeCodecUnitTest->testFlushInRunningState();
    delete nativeCodecUnitTest;
    return static_cast<jboolean>(isPass);
}

static jboolean nativeTestFlushInUnInitState(JNIEnv*, jobject) {
    auto* nativeCodecUnitTest = new NativeCodecUnitTest(AMEDIA_MIMETYPE_AUDIO_AAC);
    bool isPass = nativeCodecUnitTest->testFlushInUnInitState();
    delete nativeCodecUnitTest;
    return static_cast<jboolean>(isPass);
}

static jboolean nativeTestGetNameInInitState(JNIEnv*, jobject) {
    auto* nativeCodecUnitTest = new NativeCodecUnitTest(AMEDIA_MIMETYPE_AUDIO_AAC);
    bool isPass = nativeCodecUnitTest->testGetNameInInitState();
    delete nativeCodecUnitTest;
    return static_cast<jboolean>(isPass);
}

static jboolean nativeTestGetNameInRunningState(JNIEnv*, jobject) {
    auto* nativeCodecUnitTest = new NativeCodecUnitTest(AMEDIA_MIMETYPE_AUDIO_AAC);
    bool isPass = nativeCodecUnitTest->testGetNameInRunningState();
    delete nativeCodecUnitTest;
    return static_cast<jboolean>(isPass);
}

static jboolean nativeTestGetNameInUnInitState(JNIEnv*, jobject) {
    auto* nativeCodecUnitTest = new NativeCodecUnitTest(AMEDIA_MIMETYPE_AUDIO_AAC);
    bool isPass = nativeCodecUnitTest->testGetNameInUnInitState();
    delete nativeCodecUnitTest;
    return static_cast<jboolean>(isPass);
}

static jboolean nativeTestSetAsyncNotifyCallbackInInitState(JNIEnv*, jobject) {
    auto* nativeCodecUnitTest = new NativeCodecUnitTest(AMEDIA_MIMETYPE_AUDIO_AAC);
    bool isPass = nativeCodecUnitTest->testSetAsyncNotifyCallbackInInitState();
    delete nativeCodecUnitTest;
    return static_cast<jboolean>(isPass);
}

static jboolean nativeTestSetAsyncNotifyCallbackInRunningState(JNIEnv*, jobject) {
    auto* nativeCodecUnitTest = new NativeCodecUnitTest(AMEDIA_MIMETYPE_AUDIO_AAC);
    bool isPass = nativeCodecUnitTest->testSetAsyncNotifyCallbackInRunningState();
    delete nativeCodecUnitTest;
    return static_cast<jboolean>(isPass);
}

static jboolean nativeTestSetAsyncNotifyCallbackInUnInitState(JNIEnv*, jobject) {
    auto* nativeCodecUnitTest = new NativeCodecUnitTest(AMEDIA_MIMETYPE_AUDIO_AAC);
    bool isPass = nativeCodecUnitTest->testSetAsyncNotifyCallbackInUnInitState();
    delete nativeCodecUnitTest;
    return static_cast<jboolean>(isPass);
}

static jboolean nativeTestGetInputBufferInInitState(JNIEnv*, jobject) {
    auto* nativeCodecUnitTest = new NativeCodecUnitTest(AMEDIA_MIMETYPE_AUDIO_AAC);
    bool isPass = nativeCodecUnitTest->testGetInputBufferInInitState();
    delete nativeCodecUnitTest;
    return static_cast<jboolean>(isPass);
}

static jboolean nativeTestGetInputBufferInRunningState(JNIEnv*, jobject) {
    auto* nativeCodecUnitTest = new NativeCodecUnitTest(AMEDIA_MIMETYPE_AUDIO_AAC);
    bool isPass = nativeCodecUnitTest->testGetInputBufferInRunningState();
    delete nativeCodecUnitTest;
    return static_cast<jboolean>(isPass);
}

static jboolean nativeTestGetInputBufferInUnInitState(JNIEnv*, jobject) {
    auto* nativeCodecUnitTest = new NativeCodecUnitTest(AMEDIA_MIMETYPE_AUDIO_AAC);
    bool isPass = nativeCodecUnitTest->testGetInputBufferInUnInitState();
    delete nativeCodecUnitTest;
    return static_cast<jboolean>(isPass);
}

static jboolean nativeTestGetInputFormatInInitState(JNIEnv*, jobject) {
    auto* nativeCodecUnitTest = new NativeCodecUnitTest(AMEDIA_MIMETYPE_AUDIO_AAC);
    bool isPass = nativeCodecUnitTest->testGetInputFormatInInitState();
    delete nativeCodecUnitTest;
    return static_cast<jboolean>(isPass);
}

static jboolean nativeTestGetInputFormatInRunningState(JNIEnv*, jobject) {
    auto* nativeCodecUnitTest = new NativeCodecUnitTest(AMEDIA_MIMETYPE_AUDIO_AAC);
    bool isPass = nativeCodecUnitTest->testGetInputFormatInRunningState();
    delete nativeCodecUnitTest;
    return static_cast<jboolean>(isPass);
}

static jboolean nativeTestGetInputFormatInUnInitState(JNIEnv*, jobject) {
    auto* nativeCodecUnitTest = new NativeCodecUnitTest(AMEDIA_MIMETYPE_AUDIO_AAC);
    bool isPass = nativeCodecUnitTest->testGetInputFormatInUnInitState();
    delete nativeCodecUnitTest;
    return static_cast<jboolean>(isPass);
}

static jboolean nativeTestGetOutputBufferInInitState(JNIEnv*, jobject) {
    auto* nativeCodecUnitTest = new NativeCodecUnitTest(AMEDIA_MIMETYPE_AUDIO_AAC);
    bool isPass = nativeCodecUnitTest->testGetOutputBufferInInitState();
    delete nativeCodecUnitTest;
    return static_cast<jboolean>(isPass);
}

static jboolean nativeTestGetOutputBufferInRunningState(JNIEnv*, jobject) {
    auto* nativeCodecUnitTest = new NativeCodecUnitTest(AMEDIA_MIMETYPE_AUDIO_AAC);
    bool isPass = nativeCodecUnitTest->testGetOutputBufferInRunningState();
    delete nativeCodecUnitTest;
    return static_cast<jboolean>(isPass);
}

static jboolean nativeTestGetOutputBufferInUnInitState(JNIEnv*, jobject) {
    auto* nativeCodecUnitTest = new NativeCodecUnitTest(AMEDIA_MIMETYPE_AUDIO_AAC);
    bool isPass = nativeCodecUnitTest->testGetOutputBufferInUnInitState();
    delete nativeCodecUnitTest;
    return static_cast<jboolean>(isPass);
}

static jboolean nativeTestGetOutputFormatInInitState(JNIEnv*, jobject) {
    auto* nativeCodecUnitTest = new NativeCodecUnitTest(AMEDIA_MIMETYPE_AUDIO_AAC);
    bool isPass = nativeCodecUnitTest->testGetOutputFormatInInitState();
    delete nativeCodecUnitTest;
    return static_cast<jboolean>(isPass);
}

static jboolean nativeTestGetOutputFormatInRunningState(JNIEnv*, jobject) {
    auto* nativeCodecUnitTest = new NativeCodecUnitTest(AMEDIA_MIMETYPE_AUDIO_AAC);
    bool isPass = nativeCodecUnitTest->testGetOutputFormatInRunningState();
    delete nativeCodecUnitTest;
    return static_cast<jboolean>(isPass);
}

static jboolean nativeTestGetOutputFormatInUnInitState(JNIEnv*, jobject) {
    auto* nativeCodecUnitTest = new NativeCodecUnitTest(AMEDIA_MIMETYPE_AUDIO_AAC);
    bool isPass = nativeCodecUnitTest->testGetOutputFormatInUnInitState();
    delete nativeCodecUnitTest;
    return static_cast<jboolean>(isPass);
}

static jboolean nativeTestSetParametersInInitState(JNIEnv*, jobject) {
    auto* nativeCodecUnitTest = new NativeCodecUnitTest(AMEDIA_MIMETYPE_AUDIO_AAC);
    bool isPass = nativeCodecUnitTest->testSetParametersInInitState();
    delete nativeCodecUnitTest;
    return static_cast<jboolean>(isPass);
}

static jboolean nativeTestSetParametersInRunningState(JNIEnv*, jobject) {
    auto* nativeCodecUnitTest = new NativeCodecUnitTest(AMEDIA_MIMETYPE_AUDIO_AAC);
    bool isPass = nativeCodecUnitTest->testSetParametersInRunningState();
    delete nativeCodecUnitTest;
    return static_cast<jboolean>(isPass);
}

static jboolean nativeTestSetParametersInUnInitState(JNIEnv*, jobject) {
    auto* nativeCodecUnitTest = new NativeCodecUnitTest(AMEDIA_MIMETYPE_AUDIO_AAC);
    bool isPass = nativeCodecUnitTest->testSetParametersInUnInitState();
    delete nativeCodecUnitTest;
    return static_cast<jboolean>(isPass);
}

static jboolean nativeTestStartInRunningState(JNIEnv*, jobject) {
    auto* nativeCodecUnitTest = new NativeCodecUnitTest(AMEDIA_MIMETYPE_AUDIO_AAC);
    bool isPass = nativeCodecUnitTest->testStartInRunningState();
    delete nativeCodecUnitTest;
    return static_cast<jboolean>(isPass);
}

static jboolean nativeTestStartInUnInitState(JNIEnv*, jobject) {
    auto* nativeCodecUnitTest = new NativeCodecUnitTest(AMEDIA_MIMETYPE_AUDIO_AAC);
    bool isPass = nativeCodecUnitTest->testStartInUnInitState();
    delete nativeCodecUnitTest;
    return static_cast<jboolean>(isPass);
}

static jboolean nativeTestStopInInitState(JNIEnv*, jobject) {
    auto* nativeCodecUnitTest = new NativeCodecUnitTest(AMEDIA_MIMETYPE_AUDIO_AAC);
    bool isPass = nativeCodecUnitTest->testStopInInitState();
    delete nativeCodecUnitTest;
    return static_cast<jboolean>(isPass);
}

static jboolean nativeTestStopInRunningState(JNIEnv*, jobject) {
    auto* nativeCodecUnitTest = new NativeCodecUnitTest(AMEDIA_MIMETYPE_AUDIO_AAC);
    bool isPass = nativeCodecUnitTest->testStopInRunningState();
    delete nativeCodecUnitTest;
    return static_cast<jboolean>(isPass);
}

static jboolean nativeTestStopInUnInitState(JNIEnv*, jobject) {
    auto* nativeCodecUnitTest = new NativeCodecUnitTest(AMEDIA_MIMETYPE_AUDIO_AAC);
    bool isPass = nativeCodecUnitTest->testStopInUnInitState();
    delete nativeCodecUnitTest;
    return static_cast<jboolean>(isPass);
}

static jboolean nativeTestQueueInputBufferInInitState(JNIEnv*, jobject) {
    auto* nativeCodecUnitTest = new NativeCodecUnitTest(AMEDIA_MIMETYPE_AUDIO_AAC);
    bool isPass = nativeCodecUnitTest->testQueueInputBufferInInitState();
    delete nativeCodecUnitTest;
    return static_cast<jboolean>(isPass);
}

static jboolean nativeTestQueueInputBufferWithBadIndex(JNIEnv*, jobject) {
    auto* nativeCodecUnitTest = new NativeCodecUnitTest(AMEDIA_MIMETYPE_AUDIO_AAC);
    bool isPass = nativeCodecUnitTest->testQueueInputBufferWithBadIndex();
    delete nativeCodecUnitTest;
    return static_cast<jboolean>(isPass);
}

static jboolean nativeTestQueueInputBufferWithBadSize(JNIEnv*, jobject) {
    auto* nativeCodecUnitTest = new NativeCodecUnitTest(AMEDIA_MIMETYPE_AUDIO_AAC);
    bool isPass = nativeCodecUnitTest->testQueueInputBufferWithBadSize();
    delete nativeCodecUnitTest;
    return static_cast<jboolean>(isPass);
}

static jboolean nativeTestQueueInputBufferWithBadBuffInfo(JNIEnv*, jobject) {
    auto* nativeCodecUnitTest = new NativeCodecUnitTest(AMEDIA_MIMETYPE_AUDIO_AAC);
    bool isPass = nativeCodecUnitTest->testQueueInputBufferWithBadBuffInfo();
    delete nativeCodecUnitTest;
    return static_cast<jboolean>(isPass);
}

static jboolean nativeTestQueueInputBufferWithBadOffset(JNIEnv*, jobject) {
    auto* nativeCodecUnitTest = new NativeCodecUnitTest(AMEDIA_MIMETYPE_AUDIO_AAC);
    bool isPass = nativeCodecUnitTest->testQueueInputBufferWithBadOffset();
    delete nativeCodecUnitTest;
    return static_cast<jboolean>(isPass);
}

static jboolean nativeTestQueueInputBufferInUnInitState(JNIEnv*, jobject) {
    auto* nativeCodecUnitTest = new NativeCodecUnitTest(AMEDIA_MIMETYPE_AUDIO_AAC);
    bool isPass = nativeCodecUnitTest->testQueueInputBufferInUnInitState();
    delete nativeCodecUnitTest;
    return static_cast<jboolean>(isPass);
}

static jboolean nativeTestReleaseOutputBufferInInitState(JNIEnv*, jobject) {
    auto* nativeCodecUnitTest = new NativeCodecUnitTest(AMEDIA_MIMETYPE_AUDIO_AAC);
    bool isPass = nativeCodecUnitTest->testReleaseOutputBufferInInitState();
    delete nativeCodecUnitTest;
    return static_cast<jboolean>(isPass);
}

static jboolean nativeTestReleaseOutputBufferInRunningState(JNIEnv*, jobject) {
    auto* nativeCodecUnitTest = new NativeCodecUnitTest(AMEDIA_MIMETYPE_AUDIO_AAC);
    bool isPass = nativeCodecUnitTest->testReleaseOutputBufferInRunningState();
    delete nativeCodecUnitTest;
    return static_cast<jboolean>(isPass);
}

static jboolean nativeTestReleaseOutputBufferInUnInitState(JNIEnv*, jobject) {
    auto* nativeCodecUnitTest = new NativeCodecUnitTest(AMEDIA_MIMETYPE_AUDIO_AAC);
    bool isPass = nativeCodecUnitTest->testReleaseOutputBufferInUnInitState();
    delete nativeCodecUnitTest;
    return static_cast<jboolean>(isPass);
}

static jboolean nativeTestGetBufferFormatInInitState(JNIEnv*, jobject) {
    auto* nativeCodecUnitTest = new NativeCodecUnitTest(AMEDIA_MIMETYPE_AUDIO_AAC);
    bool isPass = nativeCodecUnitTest->testGetBufferFormatInInitState();
    delete nativeCodecUnitTest;
    return static_cast<jboolean>(isPass);
}

static jboolean nativeTestGetBufferFormatInRunningState(JNIEnv*, jobject) {
    auto* nativeCodecUnitTest = new NativeCodecUnitTest(AMEDIA_MIMETYPE_AUDIO_AAC);
    bool isPass = nativeCodecUnitTest->testGetBufferFormatInRunningState();
    delete nativeCodecUnitTest;
    return static_cast<jboolean>(isPass);
}

static jboolean nativeTestGetBufferFormatInUnInitState(JNIEnv*, jobject) {
    auto* nativeCodecUnitTest = new NativeCodecUnitTest(AMEDIA_MIMETYPE_AUDIO_AAC);
    bool isPass = nativeCodecUnitTest->testGetBufferFormatInUnInitState();
    delete nativeCodecUnitTest;
    return static_cast<jboolean>(isPass);
}

int registerAndroidMediaV2CtsCodecUnitTest(JNIEnv* env) {
    const JNINativeMethod methodTable[] = {
            {"nativeTestCreateByCodecNameForNull", "()Z",
             (void*)nativeTestCreateByCodecNameForNull},
            {"nativeTestCreateByCodecNameForInvalidName", "()Z",
             (void*)nativeTestCreateByCodecNameForInvalidName},
            {"nativeTestCreateDecoderByTypeForNull", "()Z",
             (void*)nativeTestCreateDecoderByTypeForNull},
            {"nativeTestCreateDecoderByTypeForInvalidMediaType", "()Z",
             (void*)nativeTestCreateDecoderByTypeForInvalidMediaType},
            {"nativeTestCreateEncoderByTypeForNull", "()Z",
             (void*)nativeTestCreateEncoderByTypeForNull},
            {"nativeTestCreateEncoderByTypeForInvalidMediaType", "()Z",
             (void*)nativeTestCreateEncoderByTypeForInvalidMediaType},
            {"nativeTestConfigureForNullFormat", "()Z", (void*)nativeTestConfigureForNullFormat},
            {"nativeTestConfigureForEmptyFormat", "()Z", (void*)nativeTestConfigureForEmptyFormat},
            {"nativeTestConfigureCodecForIncompleteFormat", "(ZZ)Z",
             (void*)nativeTestConfigureCodecForIncompleteFormat},
            {"nativeTestConfigureEncoderForBadFlags", "()Z",
             (void*)nativeTestConfigureEncoderForBadFlags},
            {"nativeTestConfigureDecoderForBadFlags", "()Z",
             (void*)nativeTestConfigureDecoderForBadFlags},
            {"nativeTestConfigureInInitState", "()Z", (void*)nativeTestConfigureInInitState},
            {"nativeTestConfigureInRunningState", "()Z", (void*)nativeTestConfigureInRunningState},
            {"nativeTestConfigureInUnInitState", "()Z", (void*)nativeTestConfigureInUnInitState},
            {"nativeTestDequeueInputBufferInInitState", "()Z",
             (void*)nativeTestDequeueInputBufferInInitState},
            {"nativeTestDequeueInputBufferInRunningState", "()Z",
             (void*)nativeTestDequeueInputBufferInRunningState},
            {"nativeTestDequeueInputBufferInUnInitState", "()Z",
             (void*)nativeTestDequeueInputBufferInUnInitState},
            {"nativeTestDequeueOutputBufferInInitState", "()Z",
             (void*)nativeTestDequeueOutputBufferInInitState},
            {"nativeTestDequeueOutputBufferInRunningState", "()Z",
             (void*)nativeTestDequeueOutputBufferInRunningState},
            {"nativeTestDequeueOutputBufferInUnInitState", "()Z",
             (void*)nativeTestDequeueOutputBufferInUnInitState},
            {"nativeTestFlushInInitState", "()Z", (void*)nativeTestFlushInInitState},
            {"nativeTestFlushInRunningState", "()Z", (void*)nativeTestFlushInRunningState},
            {"nativeTestFlushInUnInitState", "()Z", (void*)nativeTestFlushInUnInitState},
            {"nativeTestGetNameInInitState", "()Z", (void*)nativeTestGetNameInInitState},
            {"nativeTestGetNameInRunningState", "()Z", (void*)nativeTestGetNameInRunningState},
            {"nativeTestGetNameInUnInitState", "()Z", (void*)nativeTestGetNameInUnInitState},
            {"nativeTestSetAsyncNotifyCallbackInInitState", "()Z",
             (void*)nativeTestSetAsyncNotifyCallbackInInitState},
            {"nativeTestSetAsyncNotifyCallbackInRunningState", "()Z",
             (void*)nativeTestSetAsyncNotifyCallbackInRunningState},
            {"nativeTestSetAsyncNotifyCallbackInUnInitState", "()Z",
             (void*)nativeTestSetAsyncNotifyCallbackInUnInitState},
            {"nativeTestGetInputBufferInInitState", "()Z",
             (void*)nativeTestGetInputBufferInInitState},
            {"nativeTestGetInputBufferInRunningState", "()Z",
             (void*)nativeTestGetInputBufferInRunningState},
            {"nativeTestGetInputBufferInUnInitState", "()Z",
             (void*)nativeTestGetInputBufferInUnInitState},
            {"nativeTestGetInputFormatInInitState", "()Z",
             (void*)nativeTestGetInputFormatInInitState},
            {"nativeTestGetInputFormatInRunningState", "()Z",
             (void*)nativeTestGetInputFormatInRunningState},
            {"nativeTestGetInputFormatInUnInitState", "()Z",
             (void*)nativeTestGetInputFormatInUnInitState},
            {"nativeTestGetOutputBufferInInitState", "()Z",
             (void*)nativeTestGetOutputBufferInInitState},
            {"nativeTestGetOutputBufferInRunningState", "()Z",
             (void*)nativeTestGetOutputBufferInRunningState},
            {"nativeTestGetOutputBufferInUnInitState", "()Z",
             (void*)nativeTestGetOutputBufferInUnInitState},
            {"nativeTestGetOutputFormatInInitState", "()Z",
             (void*)nativeTestGetOutputFormatInInitState},
            {"nativeTestGetOutputFormatInRunningState", "()Z",
             (void*)nativeTestGetOutputFormatInRunningState},
            {"nativeTestGetOutputFormatInUnInitState", "()Z",
             (void*)nativeTestGetOutputFormatInUnInitState},
            {"nativeTestSetParametersInInitState", "()Z",
             (void*)nativeTestSetParametersInInitState},
            {"nativeTestSetParametersInRunningState", "()Z",
             (void*)nativeTestSetParametersInRunningState},
            {"nativeTestSetParametersInUnInitState", "()Z",
             (void*)nativeTestSetParametersInUnInitState},
            {"nativeTestStartInRunningState", "()Z", (void*)nativeTestStartInRunningState},
            {"nativeTestStartInUnInitState", "()Z", (void*)nativeTestStartInUnInitState},
            {"nativeTestStopInInitState", "()Z", (void*)nativeTestStopInInitState},
            {"nativeTestStopInRunningState", "()Z", (void*)nativeTestStopInRunningState},
            {"nativeTestStopInUnInitState", "()Z", (void*)nativeTestStopInUnInitState},
            {"nativeTestQueueInputBufferInInitState", "()Z",
             (void*)nativeTestQueueInputBufferInInitState},
            {"nativeTestQueueInputBufferWithBadIndex", "()Z",
             (void*)nativeTestQueueInputBufferWithBadIndex},
            {"nativeTestQueueInputBufferWithBadSize", "()Z",
             (void*)nativeTestQueueInputBufferWithBadSize},
            {"nativeTestQueueInputBufferWithBadBuffInfo", "()Z",
             (void*)nativeTestQueueInputBufferWithBadBuffInfo},
            {"nativeTestQueueInputBufferWithBadOffset", "()Z",
             (void*)nativeTestQueueInputBufferWithBadOffset},
            {"nativeTestQueueInputBufferInUnInitState", "()Z",
             (void*)nativeTestQueueInputBufferInUnInitState},
            {"nativeTestReleaseOutputBufferInInitState", "()Z",
             (void*)nativeTestReleaseOutputBufferInInitState},
            {"nativeTestReleaseOutputBufferInRunningState", "()Z",
             (void*)nativeTestReleaseOutputBufferInRunningState},
            {"nativeTestReleaseOutputBufferInUnInitState", "()Z",
             (void*)nativeTestReleaseOutputBufferInUnInitState},
            {"nativeTestGetBufferFormatInInitState", "()Z",
             (void*)nativeTestGetBufferFormatInInitState},
            {"nativeTestGetBufferFormatInRunningState", "()Z",
             (void*)nativeTestGetBufferFormatInRunningState},
            {"nativeTestGetBufferFormatInUnInitState", "()Z",
             (void*)nativeTestGetBufferFormatInUnInitState},

    };
    jclass c = env->FindClass("android/mediav2/cts/CodecUnitTest$TestApiNative");
    return env->RegisterNatives(c, methodTable, sizeof(methodTable) / sizeof(JNINativeMethod));
}

extern "C" JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void*) {
    JNIEnv* env;
    if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) return JNI_ERR;
    if (registerAndroidMediaV2CtsCodecUnitTest(env) != JNI_OK) return JNI_ERR;
    return JNI_VERSION_1_6;
}
