blob: 780ad8ff7366559c046f5de6594544286923645f [file] [log] [blame]
#include <jni.h>
#include <lib/core/CHIPError.h>
#include <lib/core/TLV.h>
#include <lib/support/JniReferences.h>
#include <lib/support/JniTypeWrappers.h>
#define JNI_METHOD(RETURN, CLASS_NAME, METHOD_NAME) \
extern "C" JNIEXPORT RETURN JNICALL Java_chip_devicecontroller_ChipClusters_00024##CLASS_NAME##_##METHOD_NAME
#define CHIP_TLV_WRITER_BUFFER_SIZE 1024
jobject decodeValueFromTLV(JNIEnv * env, chip::TLV::TLVReader * data);
static CHIP_ERROR encodeTLVFromValue(JNIEnv * env, jobject jObject, chip::TLV::TLVWriter & writer, chip::TLV::Tag tag);
jobject decodeValueFromTLV(JNIEnv * env, chip::TLV::TLVReader * data)
{
chip::TLV::TLVType dataTLVType = data->GetType();
switch (dataTLVType)
{
case chip::TLV::kTLVType_SignedInteger: {
int64_t val;
CHIP_ERROR err = data->Get(val);
VerifyOrReturnValue(
err == CHIP_NO_ERROR, nullptr,
ChipLogError(Controller, "Error: TLV signed integer decoding failed : %" CHIP_ERROR_FORMAT, err.Format()));
jclass intTypeCls = env->FindClass("chip/devicecontroller/ChipTLVType$IntType");
jmethodID constructor = env->GetMethodID(intTypeCls, "<init>", "(J)V");
return env->NewObject(intTypeCls, constructor, static_cast<jlong>(val));
}
case chip::TLV::kTLVType_UnsignedInteger: {
uint64_t val;
CHIP_ERROR err = data->Get(val);
VerifyOrReturnValue(
err == CHIP_NO_ERROR, nullptr,
ChipLogError(Controller, "Error: TLV unsigned integer decoding failed : %" CHIP_ERROR_FORMAT, err.Format()));
jclass uintTypeCls = env->FindClass("chip/devicecontroller/ChipTLVType$UIntType");
jmethodID constructor = env->GetMethodID(uintTypeCls, "<init>", "(J)V");
return env->NewObject(uintTypeCls, constructor, static_cast<jlong>(val));
}
case chip::TLV::kTLVType_Boolean: {
bool val;
CHIP_ERROR err = data->Get(val);
VerifyOrReturnValue(err == CHIP_NO_ERROR, nullptr,
ChipLogError(Controller, "Error: TLV boolean decoding failed : %" CHIP_ERROR_FORMAT, err.Format()));
jclass booleanTypeCls = env->FindClass("chip/devicecontroller/ChipTLVType$BooleanType");
jmethodID constructor = env->GetMethodID(booleanTypeCls, "<init>", "(Z)V");
return env->NewObject(booleanTypeCls, constructor, static_cast<jboolean>(val));
}
case chip::TLV::kTLVType_FloatingPointNumber: {
// Try float first
float floatValue;
CHIP_ERROR err = data->Get(floatValue);
if (err == CHIP_NO_ERROR)
{
jclass floatTypeCls = env->FindClass("chip/devicecontroller/ChipTLVType$FloatType");
jmethodID constructor = env->GetMethodID(floatTypeCls, "<init>", "(F)V");
return env->NewObject(floatTypeCls, constructor, static_cast<jfloat>(floatValue));
}
double val;
err = data->Get(val);
VerifyOrReturnValue(
err == CHIP_NO_ERROR, nullptr,
ChipLogError(Controller, "Error: TLV floating point decoding failed : %" CHIP_ERROR_FORMAT, err.Format()));
jclass doubleTypeCls = env->FindClass("chip/devicecontroller/ChipTLVType$DoubleType");
jmethodID doubleConstructor = env->GetMethodID(doubleTypeCls, "<init>", "(D)V");
return env->NewObject(doubleTypeCls, doubleConstructor, static_cast<jdouble>(val));
}
case chip::TLV::kTLVType_UTF8String: {
chip::CharSpan stringValue;
CHIP_ERROR err = data->Get(stringValue);
VerifyOrReturnValue(err == CHIP_NO_ERROR, nullptr,
ChipLogError(Controller, "Error: TLV UTF8String decoding failed : %" CHIP_ERROR_FORMAT, err.Format()));
chip::UtfString stringObj(env, stringValue);
jclass stringTypeCls = env->FindClass("chip/devicecontroller/ChipTLVType$StringType");
jmethodID constructor = env->GetMethodID(stringTypeCls, "<init>", "(Ljava/lang/String;)V");
return env->NewObject(stringTypeCls, constructor, stringObj.jniValue());
}
case chip::TLV::kTLVType_ByteString: {
chip::ByteSpan bytesValue;
CHIP_ERROR err = data->Get(bytesValue);
VerifyOrReturnValue(err == CHIP_NO_ERROR, nullptr,
ChipLogError(Controller, "Error: TLV ByteString decoding failed : %" CHIP_ERROR_FORMAT, err.Format()));
chip::ByteArray byteArrayObj(env, bytesValue);
jclass stringTypeCls = env->FindClass("chip/devicecontroller/ChipTLVType$ByteArrayType");
jmethodID constructor = env->GetMethodID(stringTypeCls, "<init>", "([B)V");
return env->NewObject(stringTypeCls, constructor, byteArrayObj.jniValue());
}
case chip::TLV::kTLVType_Null: {
jclass nullTypeCls = env->FindClass("chip/devicecontroller/ChipTLVType$NullType");
jmethodID constructor = env->GetMethodID(nullTypeCls, "<init>", "()V");
return env->NewObject(nullTypeCls, constructor);
}
case chip::TLV::kTLVType_Structure:
case chip::TLV::kTLVType_Array: {
chip::TLV::TLVType tlvType;
CHIP_ERROR err = data->EnterContainer(tlvType);
VerifyOrReturnValue(err == CHIP_NO_ERROR, nullptr,
ChipLogError(Controller, "Error: TLV container entering failed : %" CHIP_ERROR_FORMAT, err.Format()));
jobject arrayLists;
err = chip::JniReferences::GetInstance().CreateArrayList(arrayLists);
VerifyOrReturnValue(err == CHIP_NO_ERROR, nullptr,
ChipLogError(Controller, "Error: Create ArrayList object failed : %" CHIP_ERROR_FORMAT, err.Format()));
while ((err = data->Next()) == CHIP_NO_ERROR)
{
chip::TLV::Tag tag = data->GetTag();
jobject value = decodeValueFromTLV(env, data);
VerifyOrReturnValue(
value != nullptr, nullptr,
ChipLogError(Controller, "Error when decoding TLV container of type : %" CHIP_ERROR_FORMAT, err.Format()));
jobject jValue = nullptr;
if (dataTLVType == chip::TLV::kTLVType_Structure)
{
uint64_t tagNum = TagNumFromTag(tag);
jclass structElementCls = env->FindClass("chip/devicecontroller/ChipTLVType$StructElement");
jmethodID constructor =
env->GetMethodID(structElementCls, "<init>", "(JLchip/devicecontroller/ChipTLVType$BaseTLVType;)V");
jValue = env->NewObject(structElementCls, constructor, static_cast<jlong>(tagNum), value);
}
else
{
jValue = value;
}
err = chip::JniReferences::GetInstance().AddToList(arrayLists, jValue);
VerifyOrReturnValue(err == CHIP_NO_ERROR, nullptr,
ChipLogError(Controller, "Error: Add ArrayList object failed : %" CHIP_ERROR_FORMAT, err.Format()));
}
if (err != CHIP_END_OF_TLV)
{
ChipLogError(Controller, "Error: TLV container decoding failed: %" CHIP_ERROR_FORMAT, err.Format());
return nullptr;
}
err = data->ExitContainer(tlvType);
VerifyOrReturnValue(err == CHIP_NO_ERROR, nullptr,
ChipLogError(Controller, "Error: TLV container exiting failed: %" CHIP_ERROR_FORMAT, err.Format()));
jclass typeCls = nullptr;
if (dataTLVType == chip::TLV::kTLVType_Structure)
{
typeCls = env->FindClass("chip/devicecontroller/ChipTLVType$StructType");
}
else
{
typeCls = env->FindClass("chip/devicecontroller/ChipTLVType$ArrayType");
}
jmethodID constructor = env->GetMethodID(typeCls, "<init>", "(Ljava/util/ArrayList;)V");
return env->NewObject(typeCls, constructor, arrayLists);
}
default:
ChipLogError(Controller, "Error: Unsupported TLV type for conversion : %u", data->GetType());
return nullptr;
}
}
static bool isEqualTLVType(JNIEnv * env, const char * typeName, jobject tlvType)
{
jclass tlvEnum = env->FindClass("chip/devicecontroller/ChipTLVType$TLVType");
jfieldID enumFieldID = env->GetStaticFieldID(tlvEnum, typeName, "Lchip/devicecontroller/ChipTLVType$TLVType;");
jobject enumObj = env->GetStaticObjectField(tlvEnum, enumFieldID);
jmethodID equalsMethodID = env->GetMethodID(tlvEnum, "equals", "(Ljava/lang/Object;)Z");
return (env->CallBooleanMethod(enumObj, equalsMethodID, tlvType) == JNI_TRUE);
}
static CHIP_ERROR encodeTLVFromValue(JNIEnv * env, jobject jValue, chip::TLV::TLVWriter & writer, chip::TLV::Tag tag)
{
jmethodID getTypeMethod = nullptr;
jmethodID getValueMethod = nullptr;
ReturnLogErrorOnFailure(chip::JniReferences::GetInstance().FindMethod(
env, jValue, "type", "()Lchip/devicecontroller/ChipTLVType$TLVType;", &getTypeMethod));
jobject jType = env->CallObjectMethod(jValue, getTypeMethod);
if (jType == nullptr)
{
ChipLogError(Controller, "Error: Object to encode is corrupt");
return CHIP_ERROR_INVALID_ARGUMENT;
}
if (isEqualTLVType(env, "UInt", jType))
{
jclass typeClass = env->FindClass("chip/devicecontroller/ChipTLVType$UIntType");
VerifyOrReturnError(typeClass != nullptr, CHIP_JNI_ERROR_TYPE_NOT_FOUND);
getValueMethod = env->GetMethodID(typeClass, "value", "()J");
VerifyOrReturnError(getValueMethod != nullptr, CHIP_JNI_ERROR_METHOD_NOT_FOUND);
jlong value = env->CallLongMethod(jValue, getValueMethod);
VerifyOrReturnLogError(!env->ExceptionCheck(), CHIP_JNI_ERROR_EXCEPTION_THROWN);
return writer.Put(tag, static_cast<uint64_t>(value));
}
if (isEqualTLVType(env, "Int", jType))
{
jclass typeClass = env->FindClass("chip/devicecontroller/ChipTLVType$IntType");
VerifyOrReturnError(typeClass != nullptr, CHIP_JNI_ERROR_TYPE_NOT_FOUND);
getValueMethod = env->GetMethodID(typeClass, "value", "()J");
VerifyOrReturnError(getValueMethod != nullptr, CHIP_JNI_ERROR_METHOD_NOT_FOUND);
jlong value = env->CallLongMethod(jValue, getValueMethod);
VerifyOrReturnLogError(!env->ExceptionCheck(), CHIP_JNI_ERROR_EXCEPTION_THROWN);
return writer.Put(tag, static_cast<int64_t>(value));
}
if (isEqualTLVType(env, "Boolean", jType))
{
jclass typeClass = env->FindClass("chip/devicecontroller/ChipTLVType$BooleanType");
VerifyOrReturnError(typeClass != nullptr, CHIP_JNI_ERROR_TYPE_NOT_FOUND);
getValueMethod = env->GetMethodID(typeClass, "value", "()Z");
VerifyOrReturnError(getValueMethod != nullptr, CHIP_JNI_ERROR_METHOD_NOT_FOUND);
jboolean value = env->CallBooleanMethod(jValue, getValueMethod);
VerifyOrReturnLogError(!env->ExceptionCheck(), CHIP_JNI_ERROR_EXCEPTION_THROWN);
return writer.Put(tag, static_cast<bool>(value));
}
if (isEqualTLVType(env, "Float", jType))
{
jclass typeClass = env->FindClass("chip/devicecontroller/ChipTLVType$FloatType");
VerifyOrReturnError(typeClass != nullptr, CHIP_JNI_ERROR_TYPE_NOT_FOUND);
getValueMethod = env->GetMethodID(typeClass, "value", "()F");
VerifyOrReturnError(getValueMethod != nullptr, CHIP_JNI_ERROR_METHOD_NOT_FOUND);
jfloat value = env->CallFloatMethod(jValue, getValueMethod);
VerifyOrReturnLogError(!env->ExceptionCheck(), CHIP_JNI_ERROR_EXCEPTION_THROWN);
return writer.Put(tag, static_cast<float>(value));
}
if (isEqualTLVType(env, "Double", jType))
{
jclass typeClass = env->FindClass("chip/devicecontroller/ChipTLVType$DoubleType");
VerifyOrReturnError(typeClass != nullptr, CHIP_JNI_ERROR_TYPE_NOT_FOUND);
getValueMethod = env->GetMethodID(typeClass, "value", "()D");
VerifyOrReturnError(getValueMethod != nullptr, CHIP_JNI_ERROR_METHOD_NOT_FOUND);
jdouble value = env->CallDoubleMethod(jValue, getValueMethod);
VerifyOrReturnLogError(!env->ExceptionCheck(), CHIP_JNI_ERROR_EXCEPTION_THROWN);
return writer.Put(tag, static_cast<double>(value));
}
if (isEqualTLVType(env, "Null", jType))
{
return writer.PutNull(tag);
}
if (isEqualTLVType(env, "String", jType))
{
jclass typeClass = env->FindClass("chip/devicecontroller/ChipTLVType$StringType");
VerifyOrReturnError(typeClass != nullptr, CHIP_JNI_ERROR_TYPE_NOT_FOUND);
getValueMethod = env->GetMethodID(typeClass, "value", "()Ljava/lang/String;");
VerifyOrReturnError(getValueMethod != nullptr, CHIP_JNI_ERROR_METHOD_NOT_FOUND);
jstring value = (jstring) env->CallObjectMethod(jValue, getValueMethod);
VerifyOrReturnLogError(!env->ExceptionCheck(), CHIP_JNI_ERROR_EXCEPTION_THROWN);
chip::JniUtfString jniString(env, value);
return writer.PutString(tag, jniString.c_str());
}
if (isEqualTLVType(env, "ByteArray", jType))
{
jclass typeClass = env->FindClass("chip/devicecontroller/ChipTLVType$ByteArrayType");
VerifyOrReturnError(typeClass != nullptr, CHIP_JNI_ERROR_TYPE_NOT_FOUND);
getValueMethod = env->GetMethodID(typeClass, "value", "()[B");
VerifyOrReturnError(getValueMethod != nullptr, CHIP_JNI_ERROR_METHOD_NOT_FOUND);
jbyteArray value = (jbyteArray) env->CallObjectMethod(jValue, getValueMethod);
VerifyOrReturnLogError(!env->ExceptionCheck(), CHIP_JNI_ERROR_EXCEPTION_THROWN);
chip::JniByteArray jniByteArray(env, value);
return writer.Put(tag, jniByteArray.byteSpan());
}
if (isEqualTLVType(env, "Struct", jType))
{
jmethodID getSizeMethod = nullptr;
jclass typeClass = env->FindClass("chip/devicecontroller/ChipTLVType$StructType");
VerifyOrReturnError(typeClass != nullptr, CHIP_JNI_ERROR_TYPE_NOT_FOUND);
getSizeMethod = env->GetMethodID(typeClass, "size", "()I");
VerifyOrReturnError(getSizeMethod != nullptr, CHIP_JNI_ERROR_METHOD_NOT_FOUND);
getValueMethod = env->GetMethodID(typeClass, "value", "(I)Lchip/devicecontroller/ChipTLVType$StructElement;");
VerifyOrReturnError(getValueMethod != nullptr, CHIP_JNI_ERROR_METHOD_NOT_FOUND);
jclass elementClass = env->FindClass("chip/devicecontroller/ChipTLVType$StructElement");
VerifyOrReturnError(elementClass != nullptr, CHIP_JNI_ERROR_TYPE_NOT_FOUND);
jmethodID getcontextTagNumMethod = env->GetMethodID(elementClass, "contextTagNum", "()J");
VerifyOrReturnError(getcontextTagNumMethod != nullptr, CHIP_JNI_ERROR_METHOD_NOT_FOUND);
jmethodID getElementValueMethod =
env->GetMethodID(elementClass, "value", "()Lchip/devicecontroller/ChipTLVType$BaseTLVType;");
VerifyOrReturnError(getElementValueMethod != nullptr, CHIP_JNI_ERROR_METHOD_NOT_FOUND);
jint size = env->CallIntMethod(jValue, getSizeMethod);
VerifyOrReturnLogError(!env->ExceptionCheck(), CHIP_JNI_ERROR_EXCEPTION_THROWN);
chip::TLV::TLVType outer;
ReturnErrorOnFailure(writer.StartContainer(tag, chip::TLV::kTLVType_Structure, outer));
for (int i = 0; i < static_cast<int>(size); i++)
{
jobject eachElement = env->CallObjectMethod(jValue, getValueMethod, static_cast<jint>(i));
VerifyOrReturnLogError(!env->ExceptionCheck(), CHIP_JNI_ERROR_EXCEPTION_THROWN);
jlong jEachTag = env->CallLongMethod(eachElement, getcontextTagNumMethod);
VerifyOrReturnLogError(!env->ExceptionCheck(), CHIP_JNI_ERROR_EXCEPTION_THROWN);
jobject jEachValue = env->CallObjectMethod(eachElement, getElementValueMethod);
VerifyOrReturnLogError(!env->ExceptionCheck(), CHIP_JNI_ERROR_EXCEPTION_THROWN);
uint64_t tagValue = static_cast<uint64_t>(jEachTag);
chip::TLV::Tag innerTag = chip::TLV::ContextTag(static_cast<uint8_t>(tagValue));
ReturnErrorOnFailure(encodeTLVFromValue(env, jEachValue, writer, innerTag));
}
ReturnErrorOnFailure(writer.EndContainer(outer));
return CHIP_NO_ERROR;
}
if (isEqualTLVType(env, "Array", jType))
{
jmethodID getSizeMethod = nullptr;
jclass typeClass = env->FindClass("chip/devicecontroller/ChipTLVType$ArrayType");
VerifyOrReturnError(typeClass != nullptr, CHIP_JNI_ERROR_TYPE_NOT_FOUND);
getSizeMethod = env->GetMethodID(typeClass, "size", "()I");
VerifyOrReturnError(getSizeMethod != nullptr, CHIP_JNI_ERROR_METHOD_NOT_FOUND);
getValueMethod = env->GetMethodID(typeClass, "value", "(I)Lchip/devicecontroller/ChipTLVType$BaseTLVType;");
VerifyOrReturnError(getValueMethod != nullptr, CHIP_JNI_ERROR_METHOD_NOT_FOUND);
jint size = env->CallIntMethod(jValue, getSizeMethod);
VerifyOrReturnLogError(!env->ExceptionCheck(), CHIP_JNI_ERROR_EXCEPTION_THROWN);
chip::TLV::TLVType outer;
ReturnErrorOnFailure(writer.StartContainer(tag, chip::TLV::kTLVType_Array, outer));
for (int i = 0; i < static_cast<int>(size); i++)
{
jobject eachValue = env->CallObjectMethod(jValue, getValueMethod, static_cast<jint>(i));
VerifyOrReturnLogError(!env->ExceptionCheck(), CHIP_JNI_ERROR_EXCEPTION_THROWN);
ReturnErrorOnFailure(encodeTLVFromValue(env, eachValue, writer, chip::TLV::AnonymousTag()));
}
ReturnErrorOnFailure(writer.EndContainer(outer));
return CHIP_NO_ERROR;
}
if (isEqualTLVType(env, "Empty", jType))
{
// For optional Value
return CHIP_NO_ERROR;
}
ChipLogError(Controller, "Error: Unsupported type to encode");
return CHIP_ERROR_INVALID_ARGUMENT;
}
JNI_METHOD(jbyteArray, BaseChipCluster, encodeToTlv)(JNIEnv * env, jclass clazz, jobject value)
{
VerifyOrReturnValue(value != nullptr, nullptr, ChipLogError(Controller, "invalid parameter: value is null"));
chip::TLV::TLVWriter writer;
uint8_t buffer[CHIP_TLV_WRITER_BUFFER_SIZE];
jbyteArray tlv = nullptr;
writer.Init(buffer, sizeof(buffer));
CHIP_ERROR err = encodeTLVFromValue(env, value, writer, chip::TLV::AnonymousTag());
VerifyOrReturnValue(err == CHIP_NO_ERROR, nullptr, ChipLogError(Controller, "Encode Error: %" CHIP_ERROR_FORMAT, err.Format()));
err = chip::JniReferences::GetInstance().N2J_ByteArray(env, buffer, writer.GetLengthWritten(), tlv);
VerifyOrReturnValue(err == CHIP_NO_ERROR, nullptr, ChipLogError(Controller, "JNI Error: %" CHIP_ERROR_FORMAT, err.Format()));
return tlv;
}
JNI_METHOD(jobject, BaseChipCluster, decodeFromTlv)(JNIEnv * env, jclass clazz, jbyteArray tlvBytesObj)
{
VerifyOrReturnValue(tlvBytesObj != nullptr, nullptr, ChipLogError(Controller, "invalid parameter: tlvBytesObj is null"));
chip::JniByteArray tlvBytesObjBytes(env, tlvBytesObj);
chip::TLV::TLVReader reader;
reader.Init(tlvBytesObjBytes.byteSpan());
VerifyOrReturnValue(reader.Next() == CHIP_NO_ERROR, nullptr, ChipLogError(Controller, "TLV Parsing is wrong"));
return decodeValueFromTLV(env, &reader);
}