[Android]add json support for invoke and write using latest json/tlv conversion (#28759)
diff --git a/examples/java-matter-controller/java/src/com/matter/controller/commands/pairing/PairOnNetworkLongImInvokeCommand.kt b/examples/java-matter-controller/java/src/com/matter/controller/commands/pairing/PairOnNetworkLongImInvokeCommand.kt
index 79592ae..73eab9e 100644
--- a/examples/java-matter-controller/java/src/com/matter/controller/commands/pairing/PairOnNetworkLongImInvokeCommand.kt
+++ b/examples/java-matter-controller/java/src/com/matter/controller/commands/pairing/PairOnNetworkLongImInvokeCommand.kt
@@ -50,16 +50,28 @@
private inner class InternalInvokeCallback : InvokeCallback {
override fun onError(e: Exception) {
logger.log(Level.INFO, "Invoke receive onError" + e.message)
- setFailure("write failure")
+ setFailure("invoke failure")
}
override fun onResponse(element: InvokeElement?, successCode: Long) {
logger.log(Level.INFO, "Invoke receive OnResponse on ")
if (element != null) {
- logger.log(Level.INFO, element.toString())
+ logger.log(Level.INFO, element.toString() + element.getJsonString())
+ val clusterId = element.getClusterId().getId()
+ if (clusterId == CLUSTER_ID_IDENTIFY) {
+ logger.log(Level.INFO, "success code is $successCode")
+ setSuccess()
+ return
+ } else if (
+ clusterId == CLUSTER_ID_TEST && element.getJsonString().equals("""{"0:UINT":2}""")
+ ) {
+ logger.log(Level.INFO, "success code is $successCode")
+ setSuccess()
+ return
+ }
}
- logger.log(Level.INFO, "success code is$successCode")
- setSuccess()
+
+ setFailure("invoke failure")
}
}
@@ -76,20 +88,44 @@
override fun runCommand() {
val number: UShort = 1u
- val tlvWriter = TlvWriter()
- tlvWriter.startStructure(AnonymousTag)
- tlvWriter.put(ContextSpecificTag(0), number)
- tlvWriter.endStructure()
+ val tlvWriter1 = TlvWriter()
+ tlvWriter1.startStructure(AnonymousTag)
+ tlvWriter1.put(ContextSpecificTag(0), number)
+ tlvWriter1.endStructure()
- val element: InvokeElement =
+ val element1: InvokeElement =
InvokeElement.newInstance(
/* endpointId= */ 0,
CLUSTER_ID_IDENTIFY,
IDENTIFY_COMMAND,
- tlvWriter.getEncoded(),
+ tlvWriter1.getEncoded(),
null
)
+ val tlvWriter2 = TlvWriter()
+ tlvWriter2.startStructure(AnonymousTag)
+ tlvWriter2.put(ContextSpecificTag(0), number)
+ tlvWriter2.put(ContextSpecificTag(1), number)
+ tlvWriter2.endStructure()
+
+ val element2: InvokeElement =
+ InvokeElement.newInstance(
+ /* endpointId= */ 1,
+ CLUSTER_ID_TEST,
+ TEST_ADD_ARGUMENT_COMMAND,
+ tlvWriter2.getEncoded(),
+ null
+ )
+
+ val element3: InvokeElement =
+ InvokeElement.newInstance(
+ /* endpointId= */ 1,
+ CLUSTER_ID_IDENTIFY,
+ IDENTIFY_COMMAND,
+ null,
+ """{"0:UINT":1}"""
+ )
+
currentCommissioner()
.pairDeviceWithAddress(
getNodeId(),
@@ -104,7 +140,13 @@
currentCommissioner()
.getConnectedDevicePointer(getNodeId(), InternalGetConnectedDeviceCallback())
clear()
- currentCommissioner().invoke(InternalInvokeCallback(), devicePointer, element, 0, 0)
+ currentCommissioner().invoke(InternalInvokeCallback(), devicePointer, element1, 0, 0)
+ waitCompleteMs(getTimeoutMillis())
+ clear()
+ currentCommissioner().invoke(InternalInvokeCallback(), devicePointer, element2, 0, 0)
+ waitCompleteMs(getTimeoutMillis())
+ clear()
+ currentCommissioner().invoke(InternalInvokeCallback(), devicePointer, element3, 0, 0)
waitCompleteMs(getTimeoutMillis())
}
@@ -114,5 +156,7 @@
private const val MATTER_PORT = 5540
private const val CLUSTER_ID_IDENTIFY = 0x0003L
private const val IDENTIFY_COMMAND = 0L
+ private const val CLUSTER_ID_TEST = 0xFFF1FC05L
+ private const val TEST_ADD_ARGUMENT_COMMAND = 0X04L
}
}
diff --git a/examples/java-matter-controller/java/src/com/matter/controller/commands/pairing/PairOnNetworkLongImWriteCommand.kt b/examples/java-matter-controller/java/src/com/matter/controller/commands/pairing/PairOnNetworkLongImWriteCommand.kt
index 1492950..10945a4 100644
--- a/examples/java-matter-controller/java/src/com/matter/controller/commands/pairing/PairOnNetworkLongImWriteCommand.kt
+++ b/examples/java-matter-controller/java/src/com/matter/controller/commands/pairing/PairOnNetworkLongImWriteCommand.kt
@@ -52,7 +52,7 @@
}
override fun onResponse(attributePath: ChipAttributePath?) {
- logger.log(Level.INFO, "Write receve OnResponse on ")
+ logger.log(Level.INFO, "Write receive OnResponse on ")
if (attributePath != null) {
logger.log(Level.INFO, attributePath.toString())
}
@@ -72,15 +72,25 @@
}
override fun runCommand() {
- val tlvWriter = TlvWriter()
- tlvWriter.put(AnonymousTag, true)
- val attributeList =
+ val tlvWriter1 = TlvWriter()
+ tlvWriter1.put(AnonymousTag, true)
+ val attributeList1 =
listOf(
AttributeWriteRequest.newInstance(
/* endpointId= */ 0,
CLUSTER_ID_BASIC,
ATTR_ID_LOCAL_CONFIG_DISABLED,
- tlvWriter.getEncoded(),
+ tlvWriter1.getEncoded()
+ )
+ )
+
+ val attributeList2 =
+ listOf(
+ AttributeWriteRequest.newInstance(
+ /* endpointId= */ 0,
+ CLUSTER_ID_BASIC,
+ ATTR_ID_LOCAL_CONFIG_DISABLED,
+ """{"40:BOOL":false}"""
)
)
@@ -99,7 +109,11 @@
.getConnectedDevicePointer(getNodeId(), InternalGetConnectedDeviceCallback())
clear()
currentCommissioner()
- .write(InternalWriteAttributesCallback(), devicePointer, attributeList, 0, 0)
+ .write(InternalWriteAttributesCallback(), devicePointer, attributeList1, 0, 0)
+ waitCompleteMs(getTimeoutMillis())
+ clear()
+ currentCommissioner()
+ .write(InternalWriteAttributesCallback(), devicePointer, attributeList2, 0, 0)
waitCompleteMs(getTimeoutMillis())
}
diff --git a/src/controller/java/AndroidCallbacks.cpp b/src/controller/java/AndroidCallbacks.cpp
index f6d9499..daa7f9e 100644
--- a/src/controller/java/AndroidCallbacks.cpp
+++ b/src/controller/java/AndroidCallbacks.cpp
@@ -1,6 +1,6 @@
/*
*
- * Copyright (c) 2020-2021 Project CHIP Authors
+ * Copyright (c) 2020-2023 Project CHIP Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -28,7 +28,9 @@
#include <lib/support/ErrorStr.h>
#include <lib/support/JniReferences.h>
#include <lib/support/JniTypeWrappers.h>
+#include <lib/support/jsontlv/JsonToTlv.h>
#include <lib/support/jsontlv/TlvJson.h>
+#include <lib/support/jsontlv/TlvToJson.h>
#include <lib/support/logging/CHIPLogging.h>
#include <platform/PlatformManager.h>
#include <type_traits>
@@ -488,15 +490,12 @@
TLV::TLVReader readerForJavaTLV;
TLV::TLVReader readerForJson;
readerForJavaTLV.Init(*apData);
- readerForJson.Init(*apData);
// Create TLV byte array to pass to Java layer
size_t bufferLen = readerForJavaTLV.GetRemainingLength() + readerForJavaTLV.GetLengthRead();
std::unique_ptr<uint8_t[]> buffer = std::unique_ptr<uint8_t[]>(new uint8_t[bufferLen]);
uint32_t size = 0;
- // The TLVReader's read head is not pointing to the first element in the container, instead of the container itself, use
- // a TLVWriter to get a TLV with a normalized TLV buffer (Wrapped with an anonymous tag, no extra "end of container" tag
- // at the end.)
+
TLV::TLVWriter writer;
writer.Init(buffer.get(), bufferLen);
err = writer.CopyElement(TLV::AnonymousTag(), readerForJavaTLV);
@@ -505,11 +504,13 @@
chip::ByteArray jniByteArray(env, reinterpret_cast<jbyte *>(buffer.get()), size);
// Convert TLV to JSON
- Json::Value json;
+ std::string json;
+ readerForJson.Init(buffer.get(), size);
+ err = readerForJson.Next();
+ ReturnErrorOnFailure(err);
err = TlvToJson(readerForJson, json);
ReturnErrorOnFailure(err);
-
- UtfString jsonString(env, JsonToString(json).c_str());
+ UtfString jsonString(env, json.c_str());
outObj = env->CallStaticObjectMethod(invokeElementCls, invokeElementCtor, static_cast<jint>(aPath.mEndpointId),
static_cast<jlong>(aPath.mClusterId), static_cast<jlong>(aPath.mCommandId),
jniByteArray.jniValue(), jsonString.jniValue());
@@ -614,6 +615,7 @@
CHIP_ERROR err = CHIP_NO_ERROR;
JNIEnv * env = JniReferences::GetInstance().GetEnvForCurrentThread();
+ ChipLogError(Controller, "ReportCallback::ReportError is called with %u", errorCode);
jthrowable exception;
err = AndroidControllerExceptions::GetInstance().CreateAndroidControllerException(env, message, errorCode, exception);
VerifyOrReturn(
@@ -722,7 +724,7 @@
CHIP_ERROR err = CHIP_NO_ERROR;
JNIEnv * env = JniReferences::GetInstance().GetEnvForCurrentThread();
- ChipLogError(Controller, "WriteAttributesCallback ReportError is called");
+ ChipLogError(Controller, "WriteAttributesCallback::ReportError is called with %u", errorCode);
jthrowable exception;
err = AndroidControllerExceptions::GetInstance().CreateAndroidControllerException(env, message, errorCode, exception);
VerifyOrReturn(err == CHIP_NO_ERROR,
@@ -825,7 +827,7 @@
CHIP_ERROR err = CHIP_NO_ERROR;
JNIEnv * env = JniReferences::GetInstance().GetEnvForCurrentThread();
- ChipLogError(Controller, "InvokeCallback ReportError is called");
+ ChipLogError(Controller, "InvokeCallback::ReportError is called with %u", errorCode);
jthrowable exception;
err = AndroidControllerExceptions::GetInstance().CreateAndroidControllerException(env, message, errorCode, exception);
VerifyOrReturn(
diff --git a/src/controller/java/CHIPDeviceController-JNI.cpp b/src/controller/java/CHIPDeviceController-JNI.cpp
index 5d93b30..be7bfa6 100644
--- a/src/controller/java/CHIPDeviceController-JNI.cpp
+++ b/src/controller/java/CHIPDeviceController-JNI.cpp
@@ -46,6 +46,8 @@
#include <lib/support/ErrorStr.h>
#include <lib/support/SafeInt.h>
#include <lib/support/ThreadOperationalDataset.h>
+#include <lib/support/jsontlv/JsonToTlv.h>
+#include <lib/support/jsontlv/TlvToJson.h>
#include <lib/support/logging/CHIPLogging.h>
#include <platform/KeyValueStoreManager.h>
#include <protocols/Protocols.h>
@@ -1864,6 +1866,8 @@
auto callback = reinterpret_cast<WriteAttributesCallback *>(callbackHandle);
app::WriteClient * writeClient = nullptr;
uint16_t convertedTimedRequestTimeoutMs = static_cast<uint16_t>(timedRequestTimeoutMs);
+ bool hasValidTlv = false;
+ bool hasValidJson = false;
ChipLogDetail(Controller, "IM write() called");
@@ -1889,15 +1893,15 @@
jmethodID hasDataVersionMethod = nullptr;
jmethodID getDataVersionMethod = nullptr;
jmethodID getTlvByteArrayMethod = nullptr;
+ jmethodID getJsonStringMethod = nullptr;
jobject endpointIdObj = nullptr;
jobject clusterIdObj = nullptr;
jobject attributeIdObj = nullptr;
jbyteArray tlvBytesObj = nullptr;
bool hasDataVersion = false;
Optional<DataVersion> dataVersion = Optional<DataVersion>();
- ;
- jbyte * tlvBytesObjBytes = nullptr;
- jsize length = 0;
+ uint8_t * tlvBytes = nullptr;
+ size_t length = 0;
TLV::TLVReader reader;
SuccessOrExit(err = JniReferences::GetInstance().GetListItem(attributeList, i, attributeItem));
@@ -1942,14 +1946,47 @@
tlvBytesObj = static_cast<jbyteArray>(env->CallObjectMethod(attributeItem, getTlvByteArrayMethod));
VerifyOrExit(!env->ExceptionCheck(), err = CHIP_JNI_ERROR_EXCEPTION_THROWN);
- VerifyOrExit(tlvBytesObj != nullptr, err = CHIP_ERROR_INVALID_ARGUMENT);
+ if (tlvBytesObj != nullptr)
+ {
+ jbyte * tlvBytesObjBytes = env->GetByteArrayElements(tlvBytesObj, nullptr);
+ VerifyOrExit(!env->ExceptionCheck(), err = CHIP_JNI_ERROR_EXCEPTION_THROWN);
+ length = static_cast<size_t>(env->GetArrayLength(tlvBytesObj));
+ VerifyOrExit(!env->ExceptionCheck(), err = CHIP_JNI_ERROR_EXCEPTION_THROWN);
+ tlvBytes = reinterpret_cast<uint8_t *>(tlvBytesObjBytes);
+ hasValidTlv = true;
+ }
+ else
+ {
+ SuccessOrExit(err = JniReferences::GetInstance().FindMethod(env, attributeItem, "getJsonString", "()Ljava/lang/String;",
+ &getJsonStringMethod));
+ jstring jsonJniString = static_cast<jstring>(env->CallObjectMethod(attributeItem, getJsonStringMethod));
+ VerifyOrExit(!env->ExceptionCheck(), err = CHIP_JNI_ERROR_EXCEPTION_THROWN);
+ if (jsonJniString != nullptr)
+ {
+ JniUtfString jsonUtfJniString(env, jsonJniString);
+ uint8_t bufWithStruct[chip::app::kMaxSecureSduLengthBytes] = { 0 };
+ uint8_t buf[chip::app::kMaxSecureSduLengthBytes] = { 0 };
+ TLV::TLVReader tlvReader;
+ TLV::TLVWriter tlvWrite;
+ TLV::TLVType outerContainer = TLV::kTLVType_Structure;
+ MutableByteSpan dataWithStruct{ bufWithStruct };
+ MutableByteSpan data{ buf };
+ SuccessOrExit(err = JsonToTlv(std::string(jsonUtfJniString.c_str(), jsonUtfJniString.size()), dataWithStruct));
+ tlvReader.Init(dataWithStruct);
+ SuccessOrExit(err = tlvReader.Next(TLV::kTLVType_Structure, TLV::AnonymousTag()));
+ SuccessOrExit(err = tlvReader.EnterContainer(outerContainer));
+ SuccessOrExit(err = tlvReader.Next());
+ tlvWrite.Init(data);
+ SuccessOrExit(err = tlvWrite.CopyElement(TLV::AnonymousTag(), tlvReader));
+ SuccessOrExit(err = tlvWrite.Finalize());
+ tlvBytes = buf;
+ length = tlvWrite.GetLengthWritten();
+ hasValidJson = true;
+ }
+ }
+ VerifyOrExit(hasValidTlv || hasValidJson, err = CHIP_ERROR_INVALID_ARGUMENT);
- tlvBytesObjBytes = env->GetByteArrayElements(tlvBytesObj, nullptr);
- VerifyOrExit(!env->ExceptionCheck(), err = CHIP_JNI_ERROR_EXCEPTION_THROWN);
- length = env->GetArrayLength(tlvBytesObj);
- VerifyOrExit(!env->ExceptionCheck(), err = CHIP_JNI_ERROR_EXCEPTION_THROWN);
-
- reader.Init(reinterpret_cast<const uint8_t *>(tlvBytesObjBytes), static_cast<size_t>(length));
+ reader.Init(tlvBytes, length);
reader.Next();
SuccessOrExit(
err = writeClient->PutPreencodedAttribute(
@@ -1964,7 +2001,6 @@
callback->mWriteClient = writeClient;
exit:
-
if (err != CHIP_NO_ERROR)
{
ChipLogError(Controller, "JNI IM Write Error: %s", err.AsString());
@@ -2000,14 +2036,17 @@
jmethodID getClusterIdMethod = nullptr;
jmethodID getCommandIdMethod = nullptr;
jmethodID getTlvByteArrayMethod = nullptr;
+ jmethodID getJsonStringMethod = nullptr;
jobject endpointIdObj = nullptr;
jobject clusterIdObj = nullptr;
jobject commandIdObj = nullptr;
jbyteArray tlvBytesObj = nullptr;
- jbyte * tlvBytesObjBytes = nullptr;
- jsize length = 0;
TLV::TLVReader reader;
TLV::TLVWriter * writer = nullptr;
+ uint8_t * tlvBytes = nullptr;
+ size_t length = 0;
+ bool hasValidTlv = false;
+ bool hasValidJson = false;
uint16_t convertedTimedRequestTimeoutMs = static_cast<uint16_t>(timedRequestTimeoutMs);
ChipLogDetail(Controller, "IM invoke() called");
@@ -2045,12 +2084,34 @@
tlvBytesObj = static_cast<jbyteArray>(env->CallObjectMethod(invokeElement, getTlvByteArrayMethod));
VerifyOrExit(!env->ExceptionCheck(), err = CHIP_JNI_ERROR_EXCEPTION_THROWN);
- VerifyOrExit(tlvBytesObj != nullptr, err = CHIP_ERROR_INVALID_ARGUMENT);
+ if (tlvBytesObj != nullptr)
+ {
+ jbyte * tlvBytesObjBytes = env->GetByteArrayElements(tlvBytesObj, nullptr);
+ VerifyOrExit(!env->ExceptionCheck(), err = CHIP_JNI_ERROR_EXCEPTION_THROWN);
+ length = static_cast<size_t>(env->GetArrayLength(tlvBytesObj));
+ VerifyOrExit(!env->ExceptionCheck(), err = CHIP_JNI_ERROR_EXCEPTION_THROWN);
+ tlvBytes = reinterpret_cast<uint8_t *>(tlvBytesObjBytes);
+ hasValidTlv = true;
+ }
+ else
+ {
+ SuccessOrExit(err = JniReferences::GetInstance().FindMethod(env, invokeElement, "getJsonString", "()Ljava/lang/String;",
+ &getJsonStringMethod));
+ jstring jsonJniString = static_cast<jstring>(env->CallObjectMethod(invokeElement, getJsonStringMethod));
+ VerifyOrExit(!env->ExceptionCheck(), err = CHIP_JNI_ERROR_EXCEPTION_THROWN);
+ if (jsonJniString != nullptr)
+ {
+ JniUtfString jsonUtfJniString(env, jsonJniString);
+ uint8_t buf[chip::app::kMaxSecureSduLengthBytes] = { 0 };
+ MutableByteSpan tlvEncodingLocal{ buf };
+ SuccessOrExit(err = JsonToTlv(std::string(jsonUtfJniString.c_str(), jsonUtfJniString.size()), tlvEncodingLocal));
+ tlvBytes = tlvEncodingLocal.data();
+ length = tlvEncodingLocal.size();
+ hasValidJson = true;
+ }
+ }
+ VerifyOrExit(hasValidTlv || hasValidJson, err = CHIP_ERROR_INVALID_ARGUMENT);
- tlvBytesObjBytes = env->GetByteArrayElements(tlvBytesObj, nullptr);
- VerifyOrExit(!env->ExceptionCheck(), err = CHIP_JNI_ERROR_EXCEPTION_THROWN);
- length = env->GetArrayLength(tlvBytesObj);
- VerifyOrExit(!env->ExceptionCheck(), err = CHIP_JNI_ERROR_EXCEPTION_THROWN);
SuccessOrExit(err = commandSender->PrepareCommand(app::CommandPathParams(static_cast<EndpointId>(endpointId), /* group id */ 0,
static_cast<ClusterId>(clusterId),
static_cast<CommandId>(commandId),
@@ -2059,7 +2120,7 @@
writer = commandSender->GetCommandDataIBTLVWriter();
VerifyOrExit(writer != nullptr, err = CHIP_ERROR_INCORRECT_STATE);
- reader.Init(reinterpret_cast<const uint8_t *>(tlvBytesObjBytes), static_cast<size_t>(length));
+ reader.Init(tlvBytes, static_cast<size_t>(length));
reader.Next();
SuccessOrExit(err = writer->CopyContainer(TLV::ContextTag(app::CommandDataIB::Tag::kFields), reader));
SuccessOrExit(err = commandSender->FinishCommand(convertedTimedRequestTimeoutMs != 0