blob: 8e0f5e9a684e6730ffb350dc884e6085f443e8eb [file] [log] [blame]
/*
*
* 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.
* 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.
*/
#include "AndroidCallbacks.h"
#include <controller/java/AndroidConnectionFailureExceptions.h>
#include <controller/java/AndroidControllerExceptions.h>
#ifdef USE_JAVA_TLV_ENCODE_DECODE
#include <controller/java/CHIPAttributeTLVValueDecoder.h>
#include <controller/java/CHIPEventTLVValueDecoder.h>
#endif
#include <app/EventLoggingTypes.h>
#include <jni.h>
#include <lib/core/ErrorStr.h>
#include <lib/support/CHIPJNIError.h>
#include <lib/support/CodeUtils.h>
#include <lib/support/JniReferences.h>
#include <lib/support/jsontlv/JsonToTlv.h>
#include <lib/support/jsontlv/TlvToJson.h>
#include <lib/support/logging/CHIPLogging.h>
#include <platform/PlatformManager.h>
#include <string>
#include <type_traits>
namespace chip {
namespace Controller {
static const int MILLIS_SINCE_BOOT = 0;
static const int MILLIS_SINCE_EPOCH = 1;
// Add the bytes for attribute tag(1:control + 8:tag + 8:length) and structure(1:struct + 1:close container)
static const int EXTRA_SPACE_FOR_ATTRIBUTE_TAG = 19;
jobject DecodeGeneralTLVValue(JNIEnv * env, TLV::TLVReader & readerForGeneralValueObject, CHIP_ERROR & err);
GetConnectedDeviceCallback::GetConnectedDeviceCallback(jobject wrapperCallback, jobject javaCallback,
const char * callbackClassSignature) :
mOnSuccess(OnDeviceConnectedFn, this),
mOnFailure(OnDeviceConnectionFailureFn, this), mCallbackClassSignature(callbackClassSignature)
{
VerifyOrReturn(mWrapperCallbackRef.Init(wrapperCallback) == CHIP_NO_ERROR,
ChipLogError(Controller, "Could not init mWrapperCallbackRef in %s", __func__));
VerifyOrReturn(mJavaCallbackRef.Init(javaCallback) == CHIP_NO_ERROR,
ChipLogError(Controller, "Could not init mJavaCallbackRef in %s", __func__));
}
GetConnectedDeviceCallback::~GetConnectedDeviceCallback() {}
void GetConnectedDeviceCallback::OnDeviceConnectedFn(void * context, Messaging::ExchangeManager & exchangeMgr,
const SessionHandle & sessionHandle)
{
JNIEnv * env = JniReferences::GetInstance().GetEnvForCurrentThread();
VerifyOrReturn(env != nullptr, ChipLogError(Controller, "Could not get JNIEnv for current thread"));
auto * self = static_cast<GetConnectedDeviceCallback *>(context);
VerifyOrReturn(self->mJavaCallbackRef.HasValidObjectRef(),
ChipLogError(Controller, "mJavaCallbackRef is not valid in %s", __func__));
jobject javaCallback = self->mJavaCallbackRef.ObjectRef();
JniLocalReferenceScope scope(env);
// Release wrapper's global ref so application can clean up the actual callback underneath.
VerifyOrReturn(self->mWrapperCallbackRef.HasValidObjectRef(),
ChipLogError(Controller, "mWrapperCallbackRef is not valid in %s", __func__));
JniGlobalReference globalRef(std::move(self->mWrapperCallbackRef));
jclass getConnectedDeviceCallbackCls = nullptr;
CHIP_ERROR err =
JniReferences::GetInstance().GetLocalClassRef(env, self->mCallbackClassSignature, getConnectedDeviceCallbackCls);
VerifyOrReturn(err == CHIP_NO_ERROR, ChipLogError(Controller, "GetLocalClassRef Error! : %" CHIP_ERROR_FORMAT, err.Format()));
VerifyOrReturn(getConnectedDeviceCallbackCls != nullptr,
ChipLogError(Controller, "Could not find GetConnectedDeviceCallback class"));
jmethodID successMethod;
err = JniReferences::GetInstance().FindMethod(env, javaCallback, "onDeviceConnected", "(J)V", &successMethod);
VerifyOrReturn(err == CHIP_NO_ERROR, ChipLogError(Controller, "FindMethod Error! : %" CHIP_ERROR_FORMAT, err.Format()));
VerifyOrReturn(successMethod != nullptr, ChipLogError(Controller, "Could not find onDeviceConnected method"));
static_assert(sizeof(jlong) >= sizeof(void *), "Need to store a pointer in a Java handle");
OperationalDeviceProxy * device = new OperationalDeviceProxy(&exchangeMgr, sessionHandle);
DeviceLayer::StackUnlock unlock;
env->CallVoidMethod(javaCallback, successMethod, reinterpret_cast<jlong>(device));
VerifyOrReturn(!env->ExceptionCheck(), env->ExceptionDescribe());
}
void GetConnectedDeviceCallback::OnDeviceConnectionFailureFn(void * context,
const OperationalSessionSetup::ConnnectionFailureInfo & failureInfo)
{
JNIEnv * env = JniReferences::GetInstance().GetEnvForCurrentThread();
VerifyOrReturn(env != nullptr, ChipLogError(Controller, "Could not get JNIEnv for current thread"));
auto * self = static_cast<GetConnectedDeviceCallback *>(context);
VerifyOrReturn(self->mJavaCallbackRef.HasValidObjectRef(),
ChipLogError(Controller, "mJavaCallbackRef is not valid in %s", __func__));
jobject javaCallback = self->mJavaCallbackRef.ObjectRef();
JniLocalReferenceScope scope(env);
jclass getConnectedDeviceCallbackCls = nullptr;
CHIP_ERROR err =
JniReferences::GetInstance().GetLocalClassRef(env, self->mCallbackClassSignature, getConnectedDeviceCallbackCls);
VerifyOrReturn(err == CHIP_NO_ERROR, ChipLogError(Controller, "GetLocalClassRef Error! : %" CHIP_ERROR_FORMAT, err.Format()));
VerifyOrReturn(getConnectedDeviceCallbackCls != nullptr,
ChipLogError(Controller, "Could not find GetConnectedDeviceCallback class"));
jmethodID failureMethod;
err = JniReferences::GetInstance().FindMethod(env, javaCallback, "onConnectionFailure", "(JLjava/lang/Exception;)V",
&failureMethod);
VerifyOrReturn(err == CHIP_NO_ERROR, ChipLogError(Controller, "FindMethod Error! : %" CHIP_ERROR_FORMAT, err.Format()));
VerifyOrReturn(failureMethod != nullptr, ChipLogError(Controller, "Could not find onConnectionFailure method"));
jthrowable exception;
err = AndroidConnectionFailureExceptions::GetInstance().CreateAndroidConnectionFailureException(
env, failureInfo.error.Format(), failureInfo.error.AsInteger(), failureInfo.sessionStage, exception);
VerifyOrReturn(
err == CHIP_NO_ERROR,
ChipLogError(Controller,
"Unable to create AndroidControllerException on GetConnectedDeviceCallback::OnDeviceConnectionFailureFn: %s",
ErrorStr(err)));
DeviceLayer::StackUnlock unlock;
env->CallVoidMethod(javaCallback, failureMethod, failureInfo.peerId.GetNodeId(), exception);
VerifyOrReturn(!env->ExceptionCheck(), env->ExceptionDescribe());
}
CHIP_ERROR CreateOptional(chip::Optional<uint8_t> value, jobject & outObj)
{
return CHIP_NO_ERROR;
}
jobject GetNodeStateObj(JNIEnv * env, const char * nodeStateClassSignature, jobject wrapperCallback)
{
jmethodID getNodeStateMethod;
CHIP_ERROR err =
JniReferences::GetInstance().FindMethod(env, wrapperCallback, "getNodeState", nodeStateClassSignature, &getNodeStateMethod);
VerifyOrReturnValue(err == CHIP_NO_ERROR, nullptr, ChipLogError(Controller, "Could not find getNodeState method"));
jobject ret = env->CallObjectMethod(wrapperCallback, getNodeStateMethod);
VerifyOrReturnValue(!env->ExceptionCheck(), nullptr, env->ExceptionDescribe());
return ret;
}
ReportCallback::ReportCallback(jobject wrapperCallback, jobject subscriptionEstablishedCallback,
jobject resubscriptionAttemptCallback, const char * nodeStateClassSignature) :
mClusterCacheAdapter(*this, Optional<EventNumber>::Missing(), false /*cacheData*/),
mNodeStateClassSignature(nodeStateClassSignature)
{
JNIEnv * env = JniReferences::GetInstance().GetEnvForCurrentThread();
VerifyOrReturn(env != nullptr, ChipLogError(Controller, "Could not get JNIEnv for current thread"));
if (subscriptionEstablishedCallback != nullptr)
{
VerifyOrReturn(mSubscriptionEstablishedCallbackRef.Init(subscriptionEstablishedCallback) == CHIP_NO_ERROR,
ChipLogError(Controller, "Could not init mSubscriptionEstablishedCallbackRef in %s", __func__));
}
VerifyOrReturn(mWrapperCallbackRef.Init(wrapperCallback) == CHIP_NO_ERROR,
ChipLogError(Controller, "Could not init mWrapperCallbackRef in %s", __func__));
if (resubscriptionAttemptCallback != nullptr)
{
VerifyOrReturn(mResubscriptionAttemptCallbackRef.Init(resubscriptionAttemptCallback) == CHIP_NO_ERROR,
ChipLogError(Controller, "Could not init mResubscriptionAttemptCallbackRef in %s", __func__));
}
}
ReportCallback::~ReportCallback()
{
if (mReadClient != nullptr)
{
Platform::Delete(mReadClient);
}
}
void ReportCallback::OnReportBegin()
{
JNIEnv * env = JniReferences::GetInstance().GetEnvForCurrentThread();
VerifyOrReturn(env != nullptr, ChipLogError(Controller, "Could not get JNIEnv for current thread"));
JniLocalReferenceScope scope(env);
VerifyOrReturn(mWrapperCallbackRef.HasValidObjectRef(),
ChipLogError(Controller, "mReportCallbackRef is not valid in %s", __func__));
jobject wrapperCallback = mWrapperCallbackRef.ObjectRef();
jmethodID onReportBeginMethod;
CHIP_ERROR err = JniReferences::GetInstance().FindMethod(env, wrapperCallback, "onReportBegin", "()V", &onReportBeginMethod);
VerifyOrReturn(err == CHIP_NO_ERROR, ChipLogError(Controller, "Could not find onReportBegin method"));
DeviceLayer::StackUnlock unlock;
env->CallVoidMethod(wrapperCallback, onReportBeginMethod);
VerifyOrReturn(!env->ExceptionCheck(), env->ExceptionDescribe());
}
void ReportCallback::OnDeallocatePaths(app::ReadPrepareParams && aReadPrepareParams)
{
if (aReadPrepareParams.mpAttributePathParamsList != nullptr && aReadPrepareParams.mAttributePathParamsListSize != 0)
{
delete[] aReadPrepareParams.mpAttributePathParamsList;
aReadPrepareParams.mpAttributePathParamsList = nullptr;
aReadPrepareParams.mAttributePathParamsListSize = 0;
}
if (aReadPrepareParams.mpEventPathParamsList != nullptr && aReadPrepareParams.mEventPathParamsListSize != 0)
{
delete[] aReadPrepareParams.mpEventPathParamsList;
aReadPrepareParams.mpEventPathParamsList = nullptr;
aReadPrepareParams.mEventPathParamsListSize = 0;
}
if (aReadPrepareParams.mpDataVersionFilterList != nullptr && aReadPrepareParams.mDataVersionFilterListSize != 0)
{
delete[] aReadPrepareParams.mpDataVersionFilterList;
aReadPrepareParams.mpDataVersionFilterList = nullptr;
aReadPrepareParams.mDataVersionFilterListSize = 0;
}
}
void ReportCallback::OnReportEnd()
{
UpdateClusterDataVersion();
// Transform C++ jobject pair list to a Java HashMap, and call onReport() on the Java callback.
CHIP_ERROR err = CHIP_NO_ERROR;
JNIEnv * env = JniReferences::GetInstance().GetEnvForCurrentThread();
VerifyOrReturn(env != nullptr, ChipLogError(Controller, "Could not get JNIEnv for current thread"));
JniLocalReferenceScope scope(env);
VerifyOrReturn(mWrapperCallbackRef.HasValidObjectRef(),
ChipLogError(Controller, "mWrapperCallbackRef is not valid in %s", __func__));
jobject wrapperCallback = mWrapperCallbackRef.ObjectRef();
jmethodID onReportEndMethod;
err = JniReferences::GetInstance().FindMethod(env, wrapperCallback, "onReportEnd", "()V", &onReportEndMethod);
VerifyOrReturn(err == CHIP_NO_ERROR, ChipLogError(Controller, "Could not find onReportEnd method"));
DeviceLayer::StackUnlock unlock;
env->CallVoidMethod(wrapperCallback, onReportEndMethod);
VerifyOrReturn(!env->ExceptionCheck(), env->ExceptionDescribe());
}
// Convert TLV blob to Json with structure, the element's tag is replaced with the actual attributeId.
CHIP_ERROR ConvertReportTlvToJson(const uint32_t id, TLV::TLVReader & data, std::string & json)
{
TLV::TLVWriter writer;
TLV::TLVReader readerForJavaTLV;
uint32_t size = 0;
readerForJavaTLV.Init(data);
size_t bufferLen = readerForJavaTLV.GetTotalLength() + EXTRA_SPACE_FOR_ATTRIBUTE_TAG;
std::unique_ptr<uint8_t[]> buffer = std::unique_ptr<uint8_t[]>(new uint8_t[bufferLen]);
writer.Init(buffer.get(), bufferLen);
TLV::TLVType outer;
ReturnErrorOnFailure(writer.StartContainer(TLV::AnonymousTag(), TLV::kTLVType_Structure, outer));
TLV::Tag tag;
ReturnErrorOnFailure(ConvertTlvTag(id, tag));
ReturnErrorOnFailure(writer.CopyElement(tag, readerForJavaTLV));
ReturnErrorOnFailure(writer.EndContainer(outer));
size = writer.GetLengthWritten();
TLV::TLVReader readerForJson;
readerForJson.Init(buffer.get(), size);
ReturnErrorOnFailure(readerForJson.Next());
// Convert TLV to JSON
return TlvToJson(readerForJson, json);
}
void ReportCallback::OnAttributeData(const app::ConcreteDataAttributePath & aPath, TLV::TLVReader * apData,
const app::StatusIB & aStatus)
{
DeviceLayer::StackUnlock unlock;
CHIP_ERROR err = CHIP_NO_ERROR;
JNIEnv * env = JniReferences::GetInstance().GetEnvForCurrentThread();
VerifyOrReturn(env != nullptr, ChipLogError(Controller, "Could not get JNIEnv for current thread"));
JniLocalReferenceScope scope(env);
VerifyOrReturn(!aPath.IsListItemOperation(), ChipLogError(Controller, "Expect non-list item operation"); aPath.LogPath());
VerifyOrReturn(mWrapperCallbackRef.HasValidObjectRef(),
ChipLogError(Controller, "mReportCallbackRef is not valid in %s", __func__));
jobject wrapperCallback = mWrapperCallbackRef.ObjectRef();
jobject nodeState = GetNodeStateObj(env, mNodeStateClassSignature, wrapperCallback);
{
// Add Attribute Status to wrapperCallback
jmethodID addAttributeStatusMethod = nullptr;
err = JniReferences::GetInstance().FindMethod(env, nodeState, "addAttributeStatus", "(IJJILjava/lang/Integer;)V",
&addAttributeStatusMethod);
VerifyOrReturn(
err == CHIP_NO_ERROR,
ChipLogError(Controller, "Could not find addAttributeStatus method with error %" CHIP_ERROR_FORMAT, err.Format()));
jobject jClusterState = nullptr;
if (aStatus.mClusterStatus.HasValue())
{
err = JniReferences::GetInstance().CreateBoxedObject<jint>(
"java/lang/Integer", "(I)V", static_cast<jint>(aStatus.mClusterStatus.Value()), jClusterState);
VerifyOrReturn(err == CHIP_NO_ERROR,
ChipLogError(Controller, "Could not CreateBoxedObject with error %" CHIP_ERROR_FORMAT, err.Format()));
}
env->CallVoidMethod(nodeState, addAttributeStatusMethod, static_cast<jint>(aPath.mEndpointId),
static_cast<jlong>(aPath.mClusterId), static_cast<jlong>(aPath.mAttributeId),
static_cast<jint>(aStatus.mStatus), jClusterState);
VerifyOrReturn(!env->ExceptionCheck(), env->ExceptionDescribe());
}
VerifyOrReturn(aStatus.IsSuccess(), ChipLogError(Controller, "Receive bad status %s", ErrorStr(aStatus.ToChipError()));
aPath.LogPath());
VerifyOrReturn(apData != nullptr, ChipLogError(Controller, "Receive empty apData"); aPath.LogPath());
TLV::TLVReader readerForJavaTLV;
readerForJavaTLV.Init(*apData);
jobject value = nullptr;
TLV::TLVReader readerForJavaObject;
readerForJavaObject.Init(*apData);
#ifdef USE_JAVA_TLV_ENCODE_DECODE
value = DecodeAttributeValue(aPath, readerForJavaObject, &err);
// If we don't know this attribute, suppress it.
if (err == CHIP_ERROR_IM_MALFORMED_ATTRIBUTE_PATH_IB)
{
TLV::TLVReader readerForGeneralValueObject;
readerForGeneralValueObject.Init(*apData);
value = DecodeGeneralTLVValue(env, readerForGeneralValueObject, err);
err = CHIP_NO_ERROR;
}
VerifyOrReturn(err == CHIP_NO_ERROR, ChipLogError(Controller, "Fail to decode attribute with error %s", ErrorStr(err));
aPath.LogPath());
VerifyOrReturn(!env->ExceptionCheck(), env->ExceptionDescribe());
#else
value = DecodeGeneralTLVValue(env, readerForJavaObject, err);
#endif
// 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);
VerifyOrReturn(err == CHIP_NO_ERROR, ChipLogError(Controller, "Fail to copy tlv element with error %s", ErrorStr(err));
aPath.LogPath());
size = writer.GetLengthWritten();
chip::ByteArray jniByteArray(env, reinterpret_cast<jbyte *>(buffer.get()), static_cast<jint>(size));
// Convert TLV to JSON
std::string json;
err = ConvertReportTlvToJson(static_cast<uint32_t>(aPath.mAttributeId), *apData, json);
VerifyOrReturn(err == CHIP_NO_ERROR,
ChipLogError(Controller, "Fail to convert report tlv to json with error %s", ErrorStr(err));
aPath.LogPath());
UtfString jsonString(env, json.c_str());
// Add AttributeState to wrapperCallback
jmethodID addAttributeMethod;
err = JniReferences::GetInstance().FindMethod(env, nodeState, "addAttribute", "(IJJLjava/lang/Object;[BLjava/lang/String;)V",
&addAttributeMethod);
VerifyOrReturn(err == CHIP_NO_ERROR,
ChipLogError(Controller, "Could not find addAttribute method with error %s", ErrorStr(err)));
env->CallVoidMethod(nodeState, addAttributeMethod, static_cast<jint>(aPath.mEndpointId), static_cast<jlong>(aPath.mClusterId),
static_cast<jlong>(aPath.mAttributeId), value, jniByteArray.jniValue(), jsonString.jniValue());
VerifyOrReturn(!env->ExceptionCheck(), env->ExceptionDescribe());
UpdateClusterDataVersion();
}
void ReportCallback::UpdateClusterDataVersion()
{
JNIEnv * env = JniReferences::GetInstance().GetEnvForCurrentThread();
VerifyOrReturn(env != nullptr, ChipLogError(Controller, "Could not get JNIEnv for current thread"));
chip::app::ConcreteClusterPath lastConcreteClusterPath;
if (mClusterCacheAdapter.GetLastReportDataPath(lastConcreteClusterPath) != CHIP_NO_ERROR)
{
return;
}
if (!lastConcreteClusterPath.IsValidConcreteClusterPath())
{
return;
}
chip::Optional<chip::DataVersion> committedDataVersion;
if (mClusterCacheAdapter.GetVersion(lastConcreteClusterPath, committedDataVersion) != CHIP_NO_ERROR)
{
return;
}
if (!committedDataVersion.HasValue())
{
return;
}
VerifyOrReturn(mWrapperCallbackRef.HasValidObjectRef(),
ChipLogError(Controller, "mReportCallbackRef is not valid in %s", __func__));
jobject wrapperCallback = mWrapperCallbackRef.ObjectRef();
jobject nodeState = GetNodeStateObj(env, mNodeStateClassSignature, wrapperCallback);
// SetDataVersion to NodeState
jmethodID setDataVersionMethod;
CHIP_ERROR err = JniReferences::GetInstance().FindMethod(env, nodeState, "setDataVersion", "(IJJ)V", &setDataVersionMethod);
VerifyOrReturn(err == CHIP_NO_ERROR, ChipLogError(Controller, "Could not find setDataVersion method"));
env->CallVoidMethod(nodeState, setDataVersionMethod, static_cast<jint>(lastConcreteClusterPath.mEndpointId),
static_cast<jlong>(lastConcreteClusterPath.mClusterId), static_cast<jlong>(committedDataVersion.Value()));
VerifyOrReturn(!env->ExceptionCheck(), env->ExceptionDescribe());
}
void ReportCallback::OnEventData(const app::EventHeader & aEventHeader, TLV::TLVReader * apData, const app::StatusIB * apStatus)
{
DeviceLayer::StackUnlock unlock;
CHIP_ERROR err = CHIP_NO_ERROR;
JNIEnv * env = JniReferences::GetInstance().GetEnvForCurrentThread();
VerifyOrReturn(env != nullptr, ChipLogError(Controller, "Could not get JNIEnv for current thread"));
VerifyOrReturn(mWrapperCallbackRef.HasValidObjectRef(),
ChipLogError(Controller, "mReportCallbackRef is not valid in %s", __func__));
jobject wrapperCallback = mWrapperCallbackRef.ObjectRef();
jobject nodeState = GetNodeStateObj(env, mNodeStateClassSignature, wrapperCallback);
if (apStatus != nullptr)
{
// Add Event Status to NodeState
jmethodID addEventStatusMethod;
err = JniReferences::GetInstance().FindMethod(env, nodeState, "addEventStatus", "(IJJILjava/lang/Integer;)V",
&addEventStatusMethod);
VerifyOrReturn(
err == CHIP_NO_ERROR,
ChipLogError(Controller, "Could not find addEventStatus method with error %" CHIP_ERROR_FORMAT, err.Format()));
jobject jClusterState = nullptr;
if (apStatus->mClusterStatus.HasValue())
{
err = JniReferences::GetInstance().CreateBoxedObject<jint>(
"java/lang/Integer", "(I)V", static_cast<jint>(apStatus->mClusterStatus.Value()), jClusterState);
VerifyOrReturn(err == CHIP_NO_ERROR,
ChipLogError(Controller, "Could not CreateBoxedObject with error %" CHIP_ERROR_FORMAT, err.Format()));
}
env->CallVoidMethod(nodeState, addEventStatusMethod, static_cast<jint>(aEventHeader.mPath.mEndpointId),
static_cast<jlong>(aEventHeader.mPath.mClusterId), static_cast<jlong>(aEventHeader.mPath.mEventId),
static_cast<jint>(apStatus->mStatus), jClusterState);
VerifyOrReturn(!env->ExceptionCheck(), env->ExceptionDescribe());
return;
}
VerifyOrReturn(apData != nullptr, ChipLogError(Controller, "Receive empty apData"); aEventHeader.LogPath());
TLV::TLVReader readerForJavaTLV;
readerForJavaTLV.Init(*apData);
jlong eventNumber = static_cast<jlong>(aEventHeader.mEventNumber);
jint priorityLevel = static_cast<jint>(aEventHeader.mPriorityLevel);
jlong timestampValue = static_cast<jlong>(aEventHeader.mTimestamp.mValue);
jint timestampType = 0;
if (aEventHeader.mTimestamp.mType == app::Timestamp::Type::kSystem)
{
timestampType = static_cast<jint>(MILLIS_SINCE_BOOT);
}
else if (aEventHeader.mTimestamp.mType == app::Timestamp::Type::kEpoch)
{
timestampType = static_cast<jint>(MILLIS_SINCE_EPOCH);
}
else
{
ChipLogError(Controller, "Unsupported event timestamp type");
aEventHeader.LogPath();
return;
}
jobject value = nullptr;
TLV::TLVReader readerForJavaObject;
readerForJavaObject.Init(*apData);
#ifdef USE_JAVA_TLV_ENCODE_DECODE
value = DecodeEventValue(aEventHeader.mPath, readerForJavaObject, &err);
// If we don't know this event, just skip it.
if (err == CHIP_ERROR_IM_MALFORMED_EVENT_PATH_IB)
{
TLV::TLVReader readerForGeneralValueObject;
readerForGeneralValueObject.Init(*apData);
value = DecodeGeneralTLVValue(env, readerForGeneralValueObject, err);
err = CHIP_NO_ERROR;
}
VerifyOrReturn(err == CHIP_NO_ERROR, ChipLogError(Controller, "Fail to decode event with error %s", ErrorStr(err));
aEventHeader.LogPath());
VerifyOrReturn(!env->ExceptionCheck(), env->ExceptionDescribe());
#else
value = DecodeGeneralTLVValue(env, readerForJavaObject, err);
#endif
// 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);
VerifyOrReturn(err == CHIP_NO_ERROR, ChipLogError(Controller, "Fail to copy element with error %s", ErrorStr(err));
aEventHeader.LogPath());
size = writer.GetLengthWritten();
chip::ByteArray jniByteArray(env, reinterpret_cast<jbyte *>(buffer.get()), static_cast<jint>(size));
// Convert TLV to JSON
std::string json;
err = ConvertReportTlvToJson(static_cast<uint32_t>(aEventHeader.mPath.mEventId), *apData, json);
VerifyOrReturn(err == CHIP_NO_ERROR,
ChipLogError(Controller, "Fail to convert report tlv to Json with error %s", ErrorStr(err));
aEventHeader.LogPath());
UtfString jsonString(env, json.c_str());
jmethodID addEventMethod;
err = JniReferences::GetInstance().FindMethod(env, nodeState, "addEvent", "(IJJJIIJLjava/lang/Object;[BLjava/lang/String;)V",
&addEventMethod);
VerifyOrReturn(err == CHIP_NO_ERROR, ChipLogError(Controller, "Could not find addEvent method with error %s", ErrorStr(err));
aEventHeader.LogPath());
env->CallVoidMethod(nodeState, addEventMethod, static_cast<jint>(aEventHeader.mPath.mEndpointId),
static_cast<jlong>(aEventHeader.mPath.mClusterId), static_cast<jlong>(aEventHeader.mPath.mEventId),
eventNumber, priorityLevel, timestampType, timestampValue, value, jniByteArray.jniValue(),
jsonString.jniValue());
VerifyOrReturn(!env->ExceptionCheck(), env->ExceptionDescribe(); aEventHeader.LogPath());
}
void ReportCallback::OnError(CHIP_ERROR aError)
{
ReportError(nullptr, nullptr, aError);
}
void ReportCallback::OnDone(app::ReadClient *)
{
CHIP_ERROR err = CHIP_NO_ERROR;
JNIEnv * env = JniReferences::GetInstance().GetEnvForCurrentThread();
VerifyOrReturn(env != nullptr, ChipLogError(Controller, "Could not get JNIEnv for current thread"));
JniLocalReferenceScope scope(env);
VerifyOrReturn(mWrapperCallbackRef.HasValidObjectRef(),
ChipLogError(Controller, "mWrapperCallbackRef is not valid in %s", __func__));
jmethodID onDoneMethod;
jobject wrapperCallback = mWrapperCallbackRef.ObjectRef();
JniGlobalReference globalRef(std::move(mWrapperCallbackRef));
err = JniReferences::GetInstance().FindMethod(env, wrapperCallback, "onDone", "()V", &onDoneMethod);
VerifyOrReturn(err == CHIP_NO_ERROR, ChipLogError(Controller, "Could not find onDone method"));
if (mReadClient != nullptr)
{
Platform::Delete(mReadClient);
}
mReadClient = nullptr;
DeviceLayer::StackUnlock unlock;
env->CallVoidMethod(wrapperCallback, onDoneMethod);
VerifyOrReturn(!env->ExceptionCheck(), env->ExceptionDescribe());
}
void ReportCallback::OnSubscriptionEstablished(SubscriptionId aSubscriptionId)
{
JNIEnv * env = JniReferences::GetInstance().GetEnvForCurrentThread();
VerifyOrReturn(env != nullptr, ChipLogError(Controller, "Could not get JNIEnv for current thread"));
JniLocalReferenceScope scope(env);
DeviceLayer::StackUnlock unlock;
VerifyOrReturn(mSubscriptionEstablishedCallbackRef.HasValidObjectRef(),
ChipLogError(Controller, " mSubscriptionEstablishedCallbackRef is not valid in %s", __func__));
CHIP_ERROR err =
JniReferences::GetInstance().CallSubscriptionEstablished(mSubscriptionEstablishedCallbackRef.ObjectRef(), aSubscriptionId);
VerifyOrReturn(err == CHIP_NO_ERROR,
ChipLogError(Controller, "CallSubscriptionEstablished error! : %" CHIP_ERROR_FORMAT, err.Format()));
}
CHIP_ERROR ReportCallback::OnResubscriptionNeeded(app::ReadClient * apReadClient, CHIP_ERROR aTerminationCause)
{
JNIEnv * env = JniReferences::GetInstance().GetEnvForCurrentThread();
VerifyOrReturnError(env != nullptr, CHIP_JNI_ERROR_NULL_OBJECT);
ReturnErrorOnFailure(app::ReadClient::Callback::OnResubscriptionNeeded(apReadClient, aTerminationCause));
JniLocalReferenceScope scope(env);
jmethodID onResubscriptionAttemptMethod;
VerifyOrReturnLogError(mResubscriptionAttemptCallbackRef.HasValidObjectRef(), CHIP_ERROR_INCORRECT_STATE);
jobject resubscriptionAttemptCallbackRef = mResubscriptionAttemptCallbackRef.ObjectRef();
ReturnLogErrorOnFailure(JniReferences::GetInstance().FindMethod(
env, resubscriptionAttemptCallbackRef, "onResubscriptionAttempt", "(JJ)V", &onResubscriptionAttemptMethod));
DeviceLayer::StackUnlock unlock;
env->CallVoidMethod(resubscriptionAttemptCallbackRef, onResubscriptionAttemptMethod,
static_cast<jlong>(aTerminationCause.AsInteger()),
static_cast<jlong>(apReadClient->ComputeTimeTillNextSubscription()));
VerifyOrReturnError(!env->ExceptionCheck(), CHIP_JNI_ERROR_EXCEPTION_THROWN);
return CHIP_NO_ERROR;
}
void ReportCallback::ReportError(const app::ConcreteAttributePath * attributePath, const app::ConcreteEventPath * eventPath,
CHIP_ERROR err)
{
ReportError(attributePath, eventPath, ErrorStr(err), err.AsInteger());
}
void ReportCallback::ReportError(const app::ConcreteAttributePath * attributePath, const app::ConcreteEventPath * eventPath,
Protocols::InteractionModel::Status status)
{
ReportError(attributePath, eventPath, "IM Status",
static_cast<std::underlying_type_t<Protocols::InteractionModel::Status>>(status));
}
void ReportCallback::ReportError(const app::ConcreteAttributePath * attributePath, const app::ConcreteEventPath * eventPath,
const char * message, ChipError::StorageType errorCode)
{
CHIP_ERROR err = CHIP_NO_ERROR;
JNIEnv * env = JniReferences::GetInstance().GetEnvForCurrentThread();
VerifyOrReturn(env != nullptr, ChipLogError(Controller, "Could not get JNIEnv for current thread"));
JniLocalReferenceScope scope(env);
VerifyOrReturn(mWrapperCallbackRef.HasValidObjectRef(),
ChipLogError(Controller, "mWrapperCallbackRef is not valid in %s", __func__));
jobject wrapperCallback = mWrapperCallbackRef.ObjectRef();
jthrowable exception;
err = AndroidControllerExceptions::GetInstance().CreateAndroidControllerException(env, message, errorCode, exception);
VerifyOrReturn(
err == CHIP_NO_ERROR,
ChipLogError(Controller, "Unable to create AndroidControllerException on ReportCallback::ReportError: %s", ErrorStr(err)));
jmethodID onErrorMethod;
err = JniReferences::GetInstance().FindMethod(env, wrapperCallback, "onError", "(ZIJJZIJJLjava/lang/Exception;)V",
&onErrorMethod);
VerifyOrReturn(err == CHIP_NO_ERROR, ChipLogError(Controller, "Unable to find onError method: %s", ErrorStr(err)));
DeviceLayer::StackUnlock unlock;
jboolean isAttributePath = JNI_FALSE;
jint attributeEndpointId = static_cast<jint>(kInvalidEndpointId);
jlong attributeClusterId = static_cast<jlong>(kInvalidClusterId);
jlong attributeId = static_cast<jlong>(kInvalidAttributeId);
jboolean isEventPath = JNI_FALSE;
jint eventEndpointId = static_cast<jint>(kInvalidEndpointId);
jlong eventClusterId = static_cast<jlong>(kInvalidClusterId);
jlong eventId = static_cast<jlong>(kInvalidAttributeId);
if (attributePath != nullptr)
{
isAttributePath = JNI_TRUE;
attributeEndpointId = static_cast<jint>(attributePath->mEndpointId);
attributeClusterId = static_cast<jlong>(attributePath->mClusterId);
attributeId = static_cast<jlong>(attributePath->mAttributeId);
}
if (eventPath != nullptr)
{
isEventPath = JNI_TRUE;
eventEndpointId = static_cast<jint>(eventPath->mEndpointId);
eventClusterId = static_cast<jlong>(eventPath->mClusterId);
eventId = static_cast<jlong>(eventPath->mEventId);
}
env->CallVoidMethod(wrapperCallback, onErrorMethod, isAttributePath, attributeEndpointId, attributeClusterId, attributeId,
isEventPath, eventEndpointId, eventClusterId, eventId, exception);
VerifyOrReturn(!env->ExceptionCheck(), env->ExceptionDescribe());
}
WriteAttributesCallback::WriteAttributesCallback(jobject wrapperCallback) : mChunkedWriteCallback(this)
{
VerifyOrReturn(mWrapperCallbackRef.Init(wrapperCallback) == CHIP_NO_ERROR,
ChipLogError(Controller, "Could not init mWrapperCallbackRef for WriteAttributesCallback"));
}
WriteAttributesCallback::~WriteAttributesCallback()
{
if (mWriteClient != nullptr)
{
Platform::Delete(mWriteClient);
}
}
void WriteAttributesCallback::OnResponse(const app::WriteClient * apWriteClient, const app::ConcreteDataAttributePath & aPath,
app::StatusIB aStatus)
{
CHIP_ERROR err = CHIP_NO_ERROR;
JNIEnv * env = JniReferences::GetInstance().GetEnvForCurrentThread();
VerifyOrReturn(env != nullptr, ChipLogError(Controller, "Could not get JNIEnv for current thread"));
JniLocalReferenceScope scope(env);
jmethodID onResponseMethod;
VerifyOrReturn(mWrapperCallbackRef.HasValidObjectRef(),
ChipLogError(Controller, "mWrapperCallbackRef is not valid in %s", __func__));
jobject wrapperCallback = mWrapperCallbackRef.ObjectRef();
err = JniReferences::GetInstance().FindMethod(env, wrapperCallback, "onResponse", "(IJJILjava/lang/Integer;)V",
&onResponseMethod);
VerifyOrReturn(err == CHIP_NO_ERROR, ChipLogError(Controller, "Unable to find onError method: %s", ErrorStr(err)));
jobject jClusterState = nullptr;
if (aStatus.mClusterStatus.HasValue())
{
err = JniReferences::GetInstance().CreateBoxedObject<jint>(
"java/lang/Integer", "(I)V", static_cast<jint>(aStatus.mClusterStatus.Value()), jClusterState);
VerifyOrReturn(err == CHIP_NO_ERROR,
ChipLogError(Controller, "Could not CreateBoxedObject with error %" CHIP_ERROR_FORMAT, err.Format()));
}
DeviceLayer::StackUnlock unlock;
env->CallVoidMethod(wrapperCallback, onResponseMethod, static_cast<jint>(aPath.mEndpointId),
static_cast<jlong>(aPath.mClusterId), static_cast<jlong>(aPath.mAttributeId), aStatus.mStatus,
jClusterState);
VerifyOrReturn(!env->ExceptionCheck(), env->ExceptionDescribe());
}
void WriteAttributesCallback::OnError(const app::WriteClient * apWriteClient, CHIP_ERROR aError)
{
ReportError(nullptr, aError);
}
void WriteAttributesCallback::OnDone(app::WriteClient *)
{
CHIP_ERROR err = CHIP_NO_ERROR;
JNIEnv * env = JniReferences::GetInstance().GetEnvForCurrentThread();
VerifyOrReturn(env != nullptr, ChipLogError(Controller, "Could not get JNIEnv for current thread"));
JniLocalReferenceScope scope(env);
VerifyOrReturn(mWrapperCallbackRef.HasValidObjectRef(),
ChipLogError(Controller, "mWrapperCallbackRef is not valid in %s", __func__));
jmethodID onDoneMethod;
jobject wrapperCallback = mWrapperCallbackRef.ObjectRef();
JniGlobalReference globalRef(std::move(mWrapperCallbackRef));
err = JniReferences::GetInstance().FindMethod(env, wrapperCallback, "onDone", "()V", &onDoneMethod);
VerifyOrReturn(err == CHIP_NO_ERROR, ChipLogError(Controller, "Could not find onDone method"));
DeviceLayer::StackUnlock unlock;
env->CallVoidMethod(wrapperCallback, onDoneMethod);
VerifyOrReturn(!env->ExceptionCheck(), env->ExceptionDescribe());
}
void WriteAttributesCallback::ReportError(const app::ConcreteAttributePath * attributePath, CHIP_ERROR err)
{
ReportError(attributePath, ErrorStr(err), err.AsInteger());
}
void WriteAttributesCallback::ReportError(const app::ConcreteAttributePath * attributePath,
Protocols::InteractionModel::Status status)
{
ReportError(attributePath, "IM Status", static_cast<std::underlying_type_t<Protocols::InteractionModel::Status>>(status));
}
void WriteAttributesCallback::ReportError(const app::ConcreteAttributePath * attributePath, const char * message,
ChipError::StorageType errorCode)
{
CHIP_ERROR err = CHIP_NO_ERROR;
JNIEnv * env = JniReferences::GetInstance().GetEnvForCurrentThread();
VerifyOrReturn(env != nullptr, ChipLogError(Controller, "Could not get JNIEnv for current thread"));
JniLocalReferenceScope scope(env);
ChipLogError(Controller, "WriteAttributesCallback::ReportError is called with %u", errorCode);
jthrowable exception;
err = AndroidControllerExceptions::GetInstance().CreateAndroidControllerException(env, message, errorCode, exception);
VerifyOrReturn(err == CHIP_NO_ERROR,
ChipLogError(Controller,
"Unable to create AndroidControllerException on WriteAttributesCallback::ReportError: %s",
ErrorStr(err)));
jmethodID onErrorMethod;
VerifyOrReturn(mWrapperCallbackRef.HasValidObjectRef(),
ChipLogError(Controller, "mWrapperCallbackRef is not valid in %s", __func__));
jobject wrapperCallback = mWrapperCallbackRef.ObjectRef();
err = JniReferences::GetInstance().FindMethod(env, wrapperCallback, "onError", "(ZIJJLjava/lang/Exception;)V", &onErrorMethod);
VerifyOrReturn(err == CHIP_NO_ERROR, ChipLogError(Controller, "Unable to find onError method: %s", ErrorStr(err)));
jboolean isAttributePath = JNI_FALSE;
jint attributeEndpointId = static_cast<jint>(kInvalidEndpointId);
jlong attributeClusterId = static_cast<jlong>(kInvalidClusterId);
jlong attributeId = static_cast<jlong>(kInvalidAttributeId);
if (attributePath != nullptr)
{
isAttributePath = JNI_TRUE;
attributeEndpointId = static_cast<jint>(attributePath->mEndpointId);
attributeClusterId = static_cast<jint>(attributePath->mClusterId);
attributeId = static_cast<jint>(attributePath->mAttributeId);
}
DeviceLayer::StackUnlock unlock;
env->CallVoidMethod(wrapperCallback, onErrorMethod, isAttributePath, attributeEndpointId, attributeClusterId, attributeId,
exception);
VerifyOrReturn(!env->ExceptionCheck(), env->ExceptionDescribe());
}
InvokeCallback::InvokeCallback(jobject wrapperCallback)
{
VerifyOrReturn(mWrapperCallbackRef.Init(wrapperCallback) == CHIP_NO_ERROR,
ChipLogError(Controller, "Could not init mWrapperCallbackRef for InvokeCallback"));
}
InvokeCallback::~InvokeCallback()
{
if (mCommandSender != nullptr)
{
Platform::Delete(mCommandSender);
mCommandSender = nullptr;
}
}
void InvokeCallback::OnResponse(app::CommandSender * apCommandSender, const app::ConcreteCommandPath & aPath,
const app::StatusIB & aStatusIB, TLV::TLVReader * apData)
{
CHIP_ERROR err = CHIP_NO_ERROR;
JNIEnv * env = JniReferences::GetInstance().GetEnvForCurrentThread();
VerifyOrReturn(env != nullptr, ChipLogError(Controller, "Could not get JNIEnv for current thread"));
jmethodID onResponseMethod;
JniLocalReferenceScope scope(env);
VerifyOrReturn(mWrapperCallbackRef.HasValidObjectRef(),
ChipLogError(Controller, "mWrapperCallbackRef is not valid in %s", __func__));
jobject wrapperCallbackRef = mWrapperCallbackRef.ObjectRef();
err = JniReferences::GetInstance().FindMethod(env, wrapperCallbackRef, "onResponse", "(IJJ[BLjava/lang/String;J)V",
&onResponseMethod);
VerifyOrReturn(err == CHIP_NO_ERROR, ChipLogError(Controller, "Unable to find onResponse method: %s", ErrorStr(err)));
DeviceLayer::StackUnlock unlock;
if (apData != nullptr)
{
TLV::TLVReader readerForJavaTLV;
TLV::TLVReader readerForJson;
readerForJavaTLV.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;
TLV::TLVWriter writer;
writer.Init(buffer.get(), bufferLen);
err = writer.CopyElement(TLV::AnonymousTag(), readerForJavaTLV);
VerifyOrReturn(err == CHIP_NO_ERROR, ChipLogError(Controller, "Failed CopyElement: %" CHIP_ERROR_FORMAT, err.Format()));
size = writer.GetLengthWritten();
chip::ByteArray jniByteArray(env, reinterpret_cast<jbyte *>(buffer.get()), static_cast<jint>(size));
// Convert TLV to JSON
std::string json;
readerForJson.Init(buffer.get(), size);
err = readerForJson.Next();
VerifyOrReturn(err == CHIP_NO_ERROR,
ChipLogError(Controller, "Failed readerForJson next: %" CHIP_ERROR_FORMAT, err.Format()));
err = TlvToJson(readerForJson, json);
VerifyOrReturn(err == CHIP_NO_ERROR, ChipLogError(Controller, "Failed TlvToJson: %" CHIP_ERROR_FORMAT, err.Format()));
UtfString jsonString(env, json.c_str());
env->CallVoidMethod(wrapperCallbackRef, onResponseMethod, static_cast<jint>(aPath.mEndpointId),
static_cast<jlong>(aPath.mClusterId), static_cast<jlong>(aPath.mCommandId), jniByteArray.jniValue(),
jsonString.jniValue(),
aStatusIB.mClusterStatus.HasValue() ? static_cast<jlong>(aStatusIB.mClusterStatus.Value())
: static_cast<jlong>(Protocols::InteractionModel::Status::Success));
}
else
{
env->CallVoidMethod(wrapperCallbackRef, onResponseMethod, static_cast<jint>(aPath.mEndpointId),
static_cast<jlong>(aPath.mClusterId), static_cast<jlong>(aPath.mCommandId), nullptr, nullptr,
aStatusIB.mClusterStatus.HasValue() ? static_cast<jlong>(aStatusIB.mClusterStatus.Value())
: static_cast<jlong>(Protocols::InteractionModel::Status::Success));
}
VerifyOrReturn(!env->ExceptionCheck(), env->ExceptionDescribe());
}
void InvokeCallback::OnError(const app::CommandSender * apCommandSender, CHIP_ERROR aError)
{
ReportError(aError);
}
void InvokeCallback::OnDone(app::CommandSender * apCommandSender)
{
CHIP_ERROR err = CHIP_NO_ERROR;
JNIEnv * env = JniReferences::GetInstance().GetEnvForCurrentThread();
VerifyOrReturn(env != nullptr, ChipLogError(Controller, "Could not get JNIEnv for current thread"));
JniLocalReferenceScope scope(env);
jmethodID onDoneMethod;
VerifyOrReturn(mWrapperCallbackRef.HasValidObjectRef(),
ChipLogError(Controller, "mWrapperCallbackRef is not valid in %s", __func__));
jobject wrapperCallback = mWrapperCallbackRef.ObjectRef();
JniGlobalReference globalRef(std::move(mWrapperCallbackRef));
err = JniReferences::GetInstance().FindMethod(env, wrapperCallback, "onDone", "()V", &onDoneMethod);
VerifyOrReturn(err == CHIP_NO_ERROR, ChipLogError(Controller, "Could not find onDone method"));
DeviceLayer::StackUnlock unlock;
env->CallVoidMethod(wrapperCallback, onDoneMethod);
VerifyOrReturn(!env->ExceptionCheck(), env->ExceptionDescribe());
}
void InvokeCallback::ReportError(CHIP_ERROR err)
{
ReportError(ErrorStr(err), err.AsInteger());
}
void InvokeCallback::ReportError(Protocols::InteractionModel::Status status)
{
ReportError("IM Status", static_cast<std::underlying_type_t<Protocols::InteractionModel::Status>>(status));
}
void InvokeCallback::ReportError(const char * message, ChipError::StorageType errorCode)
{
CHIP_ERROR err = CHIP_NO_ERROR;
JNIEnv * env = JniReferences::GetInstance().GetEnvForCurrentThread();
VerifyOrReturn(env != nullptr, ChipLogError(Controller, "Could not get JNIEnv for current thread"));
JniLocalReferenceScope scope(env);
ChipLogError(Controller, "InvokeCallback::ReportError is called with %u", errorCode);
jthrowable exception;
err = AndroidControllerExceptions::GetInstance().CreateAndroidControllerException(env, message, errorCode, exception);
VerifyOrReturn(
err == CHIP_NO_ERROR,
ChipLogError(Controller, "Unable to create AndroidControllerException: %s on InvokeCallback::ReportError", ErrorStr(err)));
jmethodID onErrorMethod;
VerifyOrReturn(mWrapperCallbackRef.HasValidObjectRef(),
ChipLogError(Controller, "mWrapperCallbackRef is not valid in %s", __func__));
jobject wrapperCallback = mWrapperCallbackRef.ObjectRef();
err = JniReferences::GetInstance().FindMethod(env, wrapperCallback, "onError", "(Ljava/lang/Exception;)V", &onErrorMethod);
VerifyOrReturn(err == CHIP_NO_ERROR, ChipLogError(Controller, "Unable to find onError method: %s", ErrorStr(err)));
DeviceLayer::StackUnlock unlock;
env->CallVoidMethod(wrapperCallback, onErrorMethod, exception);
VerifyOrReturn(!env->ExceptionCheck(), env->ExceptionDescribe());
}
jobject DecodeGeneralTLVValue(JNIEnv * env, TLV::TLVReader & readerForGeneralValueObject, CHIP_ERROR & err)
{
jobject retValue = nullptr;
switch (readerForGeneralValueObject.GetType())
{
case TLV::kTLVType_SignedInteger: {
int64_t signedValue = 0;
VerifyOrReturnValue(readerForGeneralValueObject.Get(signedValue) == CHIP_NO_ERROR, nullptr,
ChipLogProgress(Controller, "Get TLV Value fail!"));
err = JniReferences::GetInstance().CreateBoxedObject<jlong>("java/lang/Long", "(J)V", static_cast<jlong>(signedValue),
retValue);
VerifyOrReturnValue(err == CHIP_NO_ERROR, nullptr, ChipLogProgress(Controller, "Create Boxed Object fail!"));
return retValue;
}
case TLV::kTLVType_UnsignedInteger: {
uint64_t unsignedValue = 0;
VerifyOrReturnValue(readerForGeneralValueObject.Get(unsignedValue) == CHIP_NO_ERROR, nullptr,
ChipLogProgress(Controller, "Get TLV Value fail!"));
err = JniReferences::GetInstance().CreateBoxedObject<jlong>("java/lang/Long", "(J)V", static_cast<jlong>(unsignedValue),
retValue);
VerifyOrReturnValue(err == CHIP_NO_ERROR, nullptr, ChipLogProgress(Controller, "Create Boxed Object fail!"));
return retValue;
}
case TLV::kTLVType_Boolean: {
bool booleanValue = false;
VerifyOrReturnValue(readerForGeneralValueObject.Get(booleanValue) == CHIP_NO_ERROR, nullptr,
ChipLogProgress(Controller, "Get TLV Value fail!"));
err = JniReferences::GetInstance().CreateBoxedObject<jboolean>("java/lang/Boolean", "(Z)V",
static_cast<jboolean>(booleanValue), retValue);
VerifyOrReturnValue(err == CHIP_NO_ERROR, nullptr, ChipLogProgress(Controller, "Create Boxed Object fail!"));
return retValue;
}
case TLV::kTLVType_FloatingPointNumber: {
double doubleValue = 0.0;
VerifyOrReturnValue(readerForGeneralValueObject.Get(doubleValue) == CHIP_NO_ERROR, nullptr,
ChipLogProgress(Controller, "Get TLV Value fail!"));
err = JniReferences::GetInstance().CreateBoxedObject<jdouble>("java/lang/Double", "(D)V", static_cast<jdouble>(doubleValue),
retValue);
VerifyOrReturnValue(err == CHIP_NO_ERROR, nullptr, ChipLogProgress(Controller, "Create Boxed Object fail!"));
return retValue;
}
case TLV::kTLVType_UTF8String: {
uint32_t bufferLen = readerForGeneralValueObject.GetLength();
std::unique_ptr<char[]> buffer = std::unique_ptr<char[]>(new char[bufferLen + 1]);
err = readerForGeneralValueObject.GetString(buffer.get(), bufferLen + 1);
VerifyOrReturnValue(err == CHIP_NO_ERROR, nullptr, ChipLogProgress(Controller, "Get TLV Value fail!"));
chip::CharSpan valueSpan(buffer.get(), bufferLen);
chip::JniReferences::GetInstance().CharToStringUTF(valueSpan, retValue);
return retValue;
}
case TLV::kTLVType_ByteString: {
uint32_t bufferLen = readerForGeneralValueObject.GetLength();
std::unique_ptr<uint8_t[]> buffer = std::unique_ptr<uint8_t[]>(new uint8_t[bufferLen + 1]);
err = readerForGeneralValueObject.GetBytes(buffer.get(), bufferLen + 1);
VerifyOrReturnValue(err == CHIP_NO_ERROR, nullptr, ChipLogProgress(Controller, "Get TLV Value fail!"));
jbyteArray valueByteArray = env->NewByteArray(static_cast<jsize>(bufferLen));
env->SetByteArrayRegion(valueByteArray, 0, static_cast<jsize>(bufferLen), reinterpret_cast<const jbyte *>(buffer.get()));
return static_cast<jobject>(valueByteArray);
}
case TLV::kTLVType_Array: {
TLV::TLVType containerType;
err = readerForGeneralValueObject.EnterContainer(containerType);
VerifyOrReturnValue(err == CHIP_NO_ERROR, nullptr,
ChipLogProgress(Controller, "EnterContainer fail! : %" CHIP_ERROR_FORMAT, err.Format()));
err = chip::JniReferences::GetInstance().CreateArrayList(retValue);
VerifyOrReturnValue(err == CHIP_NO_ERROR, nullptr,
ChipLogProgress(Controller, "CreateArrayList fail! : %" CHIP_ERROR_FORMAT, err.Format()));
while (readerForGeneralValueObject.Next() == CHIP_NO_ERROR)
{
jobject inValue = DecodeGeneralTLVValue(env, readerForGeneralValueObject, err);
VerifyOrReturnValue(err == CHIP_NO_ERROR, nullptr,
ChipLogProgress(Controller, "Can't parse general value : %" CHIP_ERROR_FORMAT, err.Format()));
err = chip::JniReferences::GetInstance().AddToList(retValue, inValue);
}
return retValue;
}
case TLV::kTLVType_List: {
TLV::TLVType containerType;
err = readerForGeneralValueObject.EnterContainer(containerType);
VerifyOrReturnValue(err == CHIP_NO_ERROR, nullptr,
ChipLogProgress(Controller, "EnterContainer fail! : %" CHIP_ERROR_FORMAT, err.Format()));
err = chip::JniReferences::GetInstance().CreateArrayList(retValue);
VerifyOrReturnValue(err == CHIP_NO_ERROR, nullptr,
ChipLogProgress(Controller, "CreateArrayList fail! : %" CHIP_ERROR_FORMAT, err.Format()));
while (readerForGeneralValueObject.Next() == CHIP_NO_ERROR)
{
jobject inValue = DecodeGeneralTLVValue(env, readerForGeneralValueObject, err);
VerifyOrReturnValue(err == CHIP_NO_ERROR, nullptr,
ChipLogProgress(Controller, "Can't parse general value : %" CHIP_ERROR_FORMAT, err.Format()));
err = chip::JniReferences::GetInstance().AddToList(retValue, inValue);
}
return retValue;
}
case TLV::kTLVType_Null: {
return nullptr;
}
default:
err = CHIP_ERROR_WRONG_TLV_TYPE;
return nullptr;
}
}
ExtendableInvokeCallback::ExtendableInvokeCallback(jobject wrapperCallback)
{
VerifyOrReturn(mWrapperCallbackRef.Init(wrapperCallback) == CHIP_NO_ERROR,
ChipLogError(Controller, "Could not init mWrapperCallbackRef for ExtendableInvokeCallback"));
}
ExtendableInvokeCallback::~ExtendableInvokeCallback()
{
if (mCommandSender != nullptr)
{
Platform::Delete(mCommandSender);
mCommandSender = nullptr;
}
}
void ExtendableInvokeCallback::OnResponse(app::CommandSender * apCommandSender,
const app::CommandSender::ResponseData & aResponseData)
{
CHIP_ERROR err = CHIP_NO_ERROR;
JNIEnv * env = JniReferences::GetInstance().GetEnvForCurrentThread();
VerifyOrReturn(env != nullptr, ChipLogError(Controller, "Could not get JNIEnv for current thread"));
jmethodID onResponseMethod;
JniLocalReferenceScope scope(env);
VerifyOrReturn(mWrapperCallbackRef.HasValidObjectRef(),
ChipLogError(Controller, "mWrapperCallbackRef is not valid in %s", __func__));
jobject wrapperCallbackRef = mWrapperCallbackRef.ObjectRef();
DeviceLayer::StackUnlock unlock;
jobject jCommandRef = nullptr;
if (aResponseData.commandRef.HasValue())
{
err = JniReferences::GetInstance().CreateBoxedObject<jint>(
"java/lang/Integer", "(I)V", static_cast<jint>(aResponseData.commandRef.Value()), jCommandRef);
VerifyOrReturn(err == CHIP_NO_ERROR,
ChipLogError(Controller, "Could not CreateBoxedObject with error %" CHIP_ERROR_FORMAT, err.Format()));
}
if (aResponseData.data != nullptr)
{
TLV::TLVReader readerForJavaTLV;
TLV::TLVReader readerForJson;
readerForJavaTLV.Init(*(aResponseData.data));
// 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;
TLV::TLVWriter writer;
writer.Init(buffer.get(), bufferLen);
err = writer.CopyElement(TLV::AnonymousTag(), readerForJavaTLV);
VerifyOrReturn(err == CHIP_NO_ERROR, ChipLogError(Controller, "Failed CopyElement: %" CHIP_ERROR_FORMAT, err.Format()));
size = writer.GetLengthWritten();
chip::ByteArray jniByteArray(env, reinterpret_cast<jbyte *>(buffer.get()), static_cast<jint>(size));
// Convert TLV to JSON
std::string json;
readerForJson.Init(buffer.get(), size);
err = readerForJson.Next();
VerifyOrReturn(err == CHIP_NO_ERROR,
ChipLogError(Controller, "Failed readerForJson next: %" CHIP_ERROR_FORMAT, err.Format()));
err = TlvToJson(readerForJson, json);
VerifyOrReturn(err == CHIP_NO_ERROR, ChipLogError(Controller, "Failed TlvToJson: %" CHIP_ERROR_FORMAT, err.Format()));
UtfString jsonString(env, json.c_str());
err = JniReferences::GetInstance().FindMethod(env, wrapperCallbackRef, "onResponse",
"(IJJLjava/lang/Integer;[BLjava/lang/String;)V", &onResponseMethod);
VerifyOrReturn(err == CHIP_NO_ERROR, ChipLogError(Controller, "Unable to find onResponse method: %s", ErrorStr(err)));
env->CallVoidMethod(wrapperCallbackRef, onResponseMethod, static_cast<jint>(aResponseData.path.mEndpointId),
static_cast<jlong>(aResponseData.path.mClusterId), static_cast<jlong>(aResponseData.path.mCommandId),
jCommandRef, jniByteArray.jniValue(), jsonString.jniValue());
}
else
{
err = JniReferences::GetInstance().FindMethod(env, wrapperCallbackRef, "onResponse",
"(IJJLjava/lang/Integer;ILjava/lang/Integer;)V", &onResponseMethod);
VerifyOrReturn(err == CHIP_NO_ERROR, ChipLogError(Controller, "Unable to find onResponse method: %s", ErrorStr(err)));
jobject jClusterState = nullptr;
if (aResponseData.statusIB.mClusterStatus.HasValue())
{
err = JniReferences::GetInstance().CreateBoxedObject<jint>(
"java/lang/Integer", "(I)V", static_cast<jint>(aResponseData.statusIB.mClusterStatus.Value()), jClusterState);
VerifyOrReturn(err == CHIP_NO_ERROR,
ChipLogError(Controller, "Could not CreateBoxedObject with error %" CHIP_ERROR_FORMAT, err.Format()));
}
env->CallVoidMethod(wrapperCallbackRef, onResponseMethod, static_cast<jint>(aResponseData.path.mEndpointId),
static_cast<jlong>(aResponseData.path.mClusterId), static_cast<jlong>(aResponseData.path.mCommandId),
jCommandRef, aResponseData.statusIB.mStatus, jClusterState);
}
VerifyOrReturn(!env->ExceptionCheck(), env->ExceptionDescribe());
}
void ExtendableInvokeCallback::OnNoResponse(app::CommandSender * commandSender,
const app::CommandSender::NoResponseData & aNoResponseData)
{
CHIP_ERROR err = CHIP_NO_ERROR;
JNIEnv * env = JniReferences::GetInstance().GetEnvForCurrentThread();
VerifyOrReturn(env != nullptr, ChipLogError(Controller, "Could not get JNIEnv for current thread"));
jmethodID onNoResponseMethod;
JniLocalReferenceScope scope(env);
VerifyOrReturn(mWrapperCallbackRef.HasValidObjectRef(),
ChipLogError(Controller, "mWrapperCallbackRef is not valid in %s", __func__));
jobject wrapperCallbackRef = mWrapperCallbackRef.ObjectRef();
DeviceLayer::StackUnlock unlock;
err = JniReferences::GetInstance().FindMethod(env, wrapperCallbackRef, "onNoResponse", "(I)V", &onNoResponseMethod);
VerifyOrReturn(err == CHIP_NO_ERROR,
ChipLogError(Controller, "Unable to find onNoResponse method: %" CHIP_ERROR_FORMAT, err.Format()));
env->CallVoidMethod(wrapperCallbackRef, onNoResponseMethod, static_cast<jint>(aNoResponseData.commandRef));
VerifyOrReturn(!env->ExceptionCheck(), env->ExceptionDescribe());
}
void ExtendableInvokeCallback::OnError(const app::CommandSender * apCommandSender, const app::CommandSender::ErrorData & aErrorData)
{
CHIP_ERROR err = CHIP_NO_ERROR;
JNIEnv * env = JniReferences::GetInstance().GetEnvForCurrentThread();
VerifyOrReturn(env != nullptr, ChipLogError(Controller, "Could not get JNIEnv for current thread"));
JniLocalReferenceScope scope(env);
ChipLogError(Controller, "ExtendableInvokeCallback::OnError is called with %u", aErrorData.error.AsInteger());
jthrowable exception;
err = AndroidControllerExceptions::GetInstance().CreateAndroidControllerException(env, ErrorStr(aErrorData.error),
aErrorData.error.AsInteger(), exception);
VerifyOrReturn(
err == CHIP_NO_ERROR,
ChipLogError(Controller, "Unable to create AndroidControllerException with error: %" CHIP_ERROR_FORMAT, err.Format()));
jmethodID onErrorMethod;
VerifyOrReturn(mWrapperCallbackRef.HasValidObjectRef(),
ChipLogError(Controller, "mWrapperCallbackRef is not valid in %s", __func__));
jobject wrapperCallback = mWrapperCallbackRef.ObjectRef();
err = JniReferences::GetInstance().FindMethod(env, wrapperCallback, "onError", "(Ljava/lang/Exception;)V", &onErrorMethod);
VerifyOrReturn(err == CHIP_NO_ERROR,
ChipLogError(Controller, "Unable to find onError method: %" CHIP_ERROR_FORMAT, err.Format()));
DeviceLayer::StackUnlock unlock;
env->CallVoidMethod(wrapperCallback, onErrorMethod, exception);
VerifyOrReturn(!env->ExceptionCheck(), env->ExceptionDescribe());
}
void ExtendableInvokeCallback::OnDone(app::CommandSender * apCommandSender)
{
CHIP_ERROR err = CHIP_NO_ERROR;
JNIEnv * env = JniReferences::GetInstance().GetEnvForCurrentThread();
VerifyOrReturn(env != nullptr, ChipLogError(Controller, "Could not get JNIEnv for current thread"));
JniLocalReferenceScope scope(env);
jmethodID onDoneMethod;
VerifyOrReturn(mWrapperCallbackRef.HasValidObjectRef(),
ChipLogError(Controller, "mWrapperCallbackRef is not valid in %s", __func__));
jobject wrapperCallback = mWrapperCallbackRef.ObjectRef();
JniGlobalReference globalRef(std::move(mWrapperCallbackRef));
err = JniReferences::GetInstance().FindMethod(env, wrapperCallback, "onDone", "()V", &onDoneMethod);
VerifyOrReturn(err == CHIP_NO_ERROR, ChipLogError(Controller, "Could not find onDone method"));
DeviceLayer::StackUnlock unlock;
env->CallVoidMethod(wrapperCallback, onDoneMethod);
VerifyOrReturn(!env->ExceptionCheck(), env->ExceptionDescribe());
}
jlong newConnectedDeviceCallback(JNIEnv * env, jobject self, jobject callback)
{
chip::DeviceLayer::StackLock lock;
GetConnectedDeviceCallback * connectedDeviceCallback = chip::Platform::New<GetConnectedDeviceCallback>(self, callback);
return reinterpret_cast<jlong>(connectedDeviceCallback);
}
void deleteConnectedDeviceCallback(JNIEnv * env, jobject self, jlong callbackHandle)
{
chip::DeviceLayer::StackLock lock;
GetConnectedDeviceCallback * connectedDeviceCallback = reinterpret_cast<GetConnectedDeviceCallback *>(callbackHandle);
VerifyOrReturn(connectedDeviceCallback != nullptr, ChipLogError(Controller, "GetConnectedDeviceCallback handle is nullptr"));
chip::Platform::Delete(connectedDeviceCallback);
}
jlong newReportCallback(JNIEnv * env, jobject self, jobject subscriptionEstablishedCallbackJava,
jobject resubscriptionAttemptCallbackJava, const char * nodeStateClassSignature)
{
chip::DeviceLayer::StackLock lock;
ReportCallback * reportCallback = chip::Platform::New<ReportCallback>(
self, subscriptionEstablishedCallbackJava, resubscriptionAttemptCallbackJava, nodeStateClassSignature);
return reinterpret_cast<jlong>(reportCallback);
}
void deleteReportCallback(JNIEnv * env, jobject self, jlong callbackHandle)
{
chip::DeviceLayer::StackLock lock;
ReportCallback * reportCallback = reinterpret_cast<ReportCallback *>(callbackHandle);
VerifyOrReturn(reportCallback != nullptr, ChipLogError(Controller, "ReportCallback handle is nullptr"));
chip::Platform::Delete(reportCallback);
}
jlong newWriteAttributesCallback(JNIEnv * env, jobject self)
{
chip::DeviceLayer::StackLock lock;
WriteAttributesCallback * writeAttributesCallback = chip::Platform::New<WriteAttributesCallback>(self);
return reinterpret_cast<jlong>(writeAttributesCallback);
}
void deleteWriteAttributesCallback(JNIEnv * env, jobject self, jlong callbackHandle)
{
chip::DeviceLayer::StackLock lock;
WriteAttributesCallback * writeAttributesCallback = reinterpret_cast<WriteAttributesCallback *>(callbackHandle);
VerifyOrReturn(writeAttributesCallback != nullptr, ChipLogError(Controller, "WriteAttributesCallback handle is nullptr"));
chip::Platform::Delete(writeAttributesCallback);
}
jlong newInvokeCallback(JNIEnv * env, jobject self)
{
chip::DeviceLayer::StackLock lock;
InvokeCallback * invokeCallback = chip::Platform::New<InvokeCallback>(self);
return reinterpret_cast<jlong>(invokeCallback);
}
void deleteInvokeCallback(JNIEnv * env, jobject self, jlong callbackHandle)
{
chip::DeviceLayer::StackLock lock;
InvokeCallback * invokeCallback = reinterpret_cast<InvokeCallback *>(callbackHandle);
VerifyOrReturn(invokeCallback != nullptr, ChipLogError(Controller, "InvokeCallback handle is nullptr"));
chip::Platform::Delete(invokeCallback);
}
jlong newExtendableInvokeCallback(JNIEnv * env, jobject self)
{
chip::DeviceLayer::StackLock lock;
ExtendableInvokeCallback * invokeCallback = chip::Platform::New<ExtendableInvokeCallback>(self);
return reinterpret_cast<jlong>(invokeCallback);
}
void deleteExtendableInvokeCallback(JNIEnv * env, jobject self, jlong callbackHandle)
{
chip::DeviceLayer::StackLock lock;
ExtendableInvokeCallback * invokeCallback = reinterpret_cast<ExtendableInvokeCallback *>(callbackHandle);
VerifyOrReturn(invokeCallback != nullptr, ChipLogError(Controller, "ExtendableInvokeCallback handle is nullptr"));
chip::Platform::Delete(invokeCallback);
}
} // namespace Controller
} // namespace chip