/* * * 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 #include #ifdef USE_JAVA_TLV_ENCODE_DECODE #include #include #endif #include #include #include #include #include #include #include #include #include #include #include #include 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(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(device)); VerifyOrReturn(!env->ExceptionCheck(), env->ExceptionDescribe()); } void GetConnectedDeviceCallback::OnDeviceConnectionFailureFn(void * context, const OperationalSessionSetup::ConnectionFailureInfo & failureInfo) { JNIEnv * env = JniReferences::GetInstance().GetEnvForCurrentThread(); VerifyOrReturn(env != nullptr, ChipLogError(Controller, "Could not get JNIEnv for current thread")); auto * self = static_cast(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 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::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 buffer = std::unique_ptr(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( "java/lang/Integer", "(I)V", static_cast(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(aPath.mEndpointId), static_cast(aPath.mClusterId), static_cast(aPath.mAttributeId), static_cast(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 buffer = std::unique_ptr(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(buffer.get()), static_cast(size)); // Convert TLV to JSON std::string json; err = ConvertReportTlvToJson(static_cast(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(aPath.mEndpointId), static_cast(aPath.mClusterId), static_cast(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 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(lastConcreteClusterPath.mEndpointId), static_cast(lastConcreteClusterPath.mClusterId), static_cast(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( "java/lang/Integer", "(I)V", static_cast(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(aEventHeader.mPath.mEndpointId), static_cast(aEventHeader.mPath.mClusterId), static_cast(aEventHeader.mPath.mEventId), static_cast(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(aEventHeader.mEventNumber); jint priorityLevel = static_cast(aEventHeader.mPriorityLevel); jlong timestampValue = static_cast(aEventHeader.mTimestamp.mValue); jint timestampType = 0; if (aEventHeader.mTimestamp.mType == app::Timestamp::Type::kSystem) { timestampType = static_cast(MILLIS_SINCE_BOOT); } else if (aEventHeader.mTimestamp.mType == app::Timestamp::Type::kEpoch) { timestampType = static_cast(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 buffer = std::unique_ptr(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(buffer.get()), static_cast(size)); // Convert TLV to JSON std::string json; err = ConvertReportTlvToJson(static_cast(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(aEventHeader.mPath.mEndpointId), static_cast(aEventHeader.mPath.mClusterId), static_cast(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(aTerminationCause.AsInteger()), static_cast(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>(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(kInvalidEndpointId); jlong attributeClusterId = static_cast(kInvalidClusterId); jlong attributeId = static_cast(kInvalidAttributeId); jboolean isEventPath = JNI_FALSE; jint eventEndpointId = static_cast(kInvalidEndpointId); jlong eventClusterId = static_cast(kInvalidClusterId); jlong eventId = static_cast(kInvalidAttributeId); if (attributePath != nullptr) { isAttributePath = JNI_TRUE; attributeEndpointId = static_cast(attributePath->mEndpointId); attributeClusterId = static_cast(attributePath->mClusterId); attributeId = static_cast(attributePath->mAttributeId); } if (eventPath != nullptr) { isEventPath = JNI_TRUE; eventEndpointId = static_cast(eventPath->mEndpointId); eventClusterId = static_cast(eventPath->mClusterId); eventId = static_cast(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( "java/lang/Integer", "(I)V", static_cast(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(aPath.mEndpointId), static_cast(aPath.mClusterId), static_cast(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>(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(kInvalidEndpointId); jlong attributeClusterId = static_cast(kInvalidClusterId); jlong attributeId = static_cast(kInvalidAttributeId); if (attributePath != nullptr) { isAttributePath = JNI_TRUE; attributeEndpointId = static_cast(attributePath->mEndpointId); attributeClusterId = static_cast(attributePath->mClusterId); attributeId = static_cast(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 buffer = std::unique_ptr(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(buffer.get()), static_cast(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(aPath.mEndpointId), static_cast(aPath.mClusterId), static_cast(aPath.mCommandId), jniByteArray.jniValue(), jsonString.jniValue(), aStatusIB.mClusterStatus.HasValue() ? static_cast(aStatusIB.mClusterStatus.Value()) : static_cast(Protocols::InteractionModel::Status::Success)); } else { env->CallVoidMethod(wrapperCallbackRef, onResponseMethod, static_cast(aPath.mEndpointId), static_cast(aPath.mClusterId), static_cast(aPath.mCommandId), nullptr, nullptr, aStatusIB.mClusterStatus.HasValue() ? static_cast(aStatusIB.mClusterStatus.Value()) : static_cast(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>(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("java/lang/Long", "(J)V", static_cast(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("java/lang/Long", "(J)V", static_cast(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("java/lang/Boolean", "(Z)V", static_cast(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("java/lang/Double", "(D)V", static_cast(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 buffer = std::unique_ptr(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 buffer = std::unique_ptr(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(bufferLen)); env->SetByteArrayRegion(valueByteArray, 0, static_cast(bufferLen), reinterpret_cast(buffer.get())); return static_cast(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( "java/lang/Integer", "(I)V", static_cast(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 buffer = std::unique_ptr(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(buffer.get()), static_cast(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(aResponseData.path.mEndpointId), static_cast(aResponseData.path.mClusterId), static_cast(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( "java/lang/Integer", "(I)V", static_cast(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(aResponseData.path.mEndpointId), static_cast(aResponseData.path.mClusterId), static_cast(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(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(self, callback); return reinterpret_cast(connectedDeviceCallback); } void deleteConnectedDeviceCallback(JNIEnv * env, jobject self, jlong callbackHandle) { chip::DeviceLayer::StackLock lock; GetConnectedDeviceCallback * connectedDeviceCallback = reinterpret_cast(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( self, subscriptionEstablishedCallbackJava, resubscriptionAttemptCallbackJava, nodeStateClassSignature); return reinterpret_cast(reportCallback); } void deleteReportCallback(JNIEnv * env, jobject self, jlong callbackHandle) { chip::DeviceLayer::StackLock lock; ReportCallback * reportCallback = reinterpret_cast(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(self); return reinterpret_cast(writeAttributesCallback); } void deleteWriteAttributesCallback(JNIEnv * env, jobject self, jlong callbackHandle) { chip::DeviceLayer::StackLock lock; WriteAttributesCallback * writeAttributesCallback = reinterpret_cast(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(self); return reinterpret_cast(invokeCallback); } void deleteInvokeCallback(JNIEnv * env, jobject self, jlong callbackHandle) { chip::DeviceLayer::StackLock lock; InvokeCallback * invokeCallback = reinterpret_cast(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(self); return reinterpret_cast(invokeCallback); } void deleteExtendableInvokeCallback(JNIEnv * env, jobject self, jlong callbackHandle) { chip::DeviceLayer::StackLock lock; ExtendableInvokeCallback * invokeCallback = reinterpret_cast(callbackHandle); VerifyOrReturn(invokeCallback != nullptr, ChipLogError(Controller, "ExtendableInvokeCallback handle is nullptr")); chip::Platform::Delete(invokeCallback); } } // namespace Controller } // namespace chip