| /* |
| * |
| * 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 <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; |
| |
| 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; |
| #ifdef USE_JAVA_TLV_ENCODE_DECODE |
| TLV::TLVReader readerForJavaObject; |
| readerForJavaObject.Init(*apData); |
| |
| value = DecodeAttributeValue(aPath, readerForJavaObject, &err); |
| // If we don't know this attribute, suppress it. |
| if (err == CHIP_ERROR_IM_MALFORMED_ATTRIBUTE_PATH_IB) |
| { |
| 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()); |
| #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; |
| #ifdef USE_JAVA_TLV_ENCODE_DECODE |
| TLV::TLVReader readerForJavaObject; |
| readerForJavaObject.Init(*apData); |
| 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) |
| { |
| 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()); |
| #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); |
| } |
| } |
| |
| 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(err == CHIP_NO_ERROR, ChipLogError(Controller, "Unable to create Java InvokeElement: %s", ErrorStr(err))); |
| 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()); |
| } |
| |
| 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); |
| } |
| |
| } // namespace Controller |
| } // namespace chip |