| {{> header}} |
| {{#if (chip_has_client_clusters)}} |
| |
| #include <app-common/zap-generated/cluster-objects.h> |
| #include <app/data-model/DecodableList.h> |
| #include <zap-generated/CHIPClusters.h> |
| #include <zap-generated/CHIPClientCallbacks.h> |
| |
| #include <lib/support/CHIPJNIError.h> |
| #include <lib/support/JniReferences.h> |
| #include <lib/support/JniTypeWrappers.h> |
| #include <jni.h> |
| #include <lib/core/CHIPSafeCasts.h> |
| #include <lib/support/CodeUtils.h> |
| #include <lib/support/Span.h> |
| #include <platform/PlatformManager.h> |
| |
| #define JNI_METHOD(RETURN, CLASS_NAME, METHOD_NAME) \ |
| extern "C" JNIEXPORT RETURN JNICALL Java_chip_devicecontroller_ChipClusters_00024##CLASS_NAME##_##METHOD_NAME |
| |
| using namespace chip; |
| using namespace chip::Controller; |
| |
| static CHIP_ERROR CreateChipClusterException(JNIEnv * env, jint errorCode, jthrowable & outEx); |
| static CHIP_ERROR CreateIllegalStateException(JNIEnv * env, const char message[], ChipError errorCode, jthrowable & outEx); |
| static void ReturnIllegalStateException(JNIEnv * env, jobject callback, const char message[], ChipError errorCode); |
| |
| CHIP_ERROR CreateChipClusterException(JNIEnv * env, jint errorCode, jthrowable & outEx) { |
| CHIP_ERROR err = CHIP_NO_ERROR; |
| jmethodID exceptionConstructor; |
| jclass clusterExceptionCls; |
| |
| err = JniReferences::GetInstance().GetClassRef(env, "chip/devicecontroller/ChipClusterException", clusterExceptionCls); |
| VerifyOrReturnError(err == CHIP_NO_ERROR, CHIP_JNI_ERROR_TYPE_NOT_FOUND); |
| JniClass clusterExceptionJniCls(clusterExceptionCls); |
| |
| exceptionConstructor = env->GetMethodID(clusterExceptionCls, "<init>", "(I)V"); |
| VerifyOrReturnError(exceptionConstructor != nullptr, CHIP_JNI_ERROR_TYPE_NOT_FOUND); |
| |
| outEx = (jthrowable) env->NewObject(clusterExceptionCls, exceptionConstructor, errorCode); |
| VerifyOrReturnError(outEx != nullptr, CHIP_JNI_ERROR_TYPE_NOT_FOUND); |
| |
| return err; |
| } |
| |
| CHIP_ERROR CreateIllegalStateException(JNIEnv * env, const char message[], ChipError errorCode, jthrowable & outEx) { |
| CHIP_ERROR err = CHIP_NO_ERROR; |
| jmethodID exceptionConstructor; |
| jclass exceptionClass; |
| jstring errStr; |
| |
| err = JniReferences::GetInstance().GetClassRef(env, "java/lang/IllegalStateException", exceptionClass); |
| VerifyOrReturnError(err == CHIP_NO_ERROR, CHIP_JNI_ERROR_TYPE_NOT_FOUND); |
| JniClass exceptionJniClass(exceptionClass); |
| |
| exceptionConstructor = env->GetMethodID(exceptionClass, "<init>", "(Ljava/lang/String;)V"); |
| VerifyOrReturnError(exceptionConstructor != nullptr, CHIP_JNI_ERROR_TYPE_NOT_FOUND); |
| |
| char buf[CHIP_CONFIG_LOG_MESSAGE_MAX_SIZE]; |
| snprintf(buf, sizeof(buf), "%s: %d", message, errorCode.AsInteger()); |
| errStr = env->NewStringUTF(buf); |
| |
| outEx = (jthrowable) env->NewObject(exceptionClass, exceptionConstructor, errStr); |
| VerifyOrReturnError(outEx != nullptr, CHIP_JNI_ERROR_TYPE_NOT_FOUND); |
| |
| return err; |
| } |
| |
| void ReturnIllegalStateException(JNIEnv * env, jobject callback, const char message[], ChipError errorCode) { |
| VerifyOrReturn(callback == nullptr, ChipLogDetail(Zcl, "Callback is null in ReturnIllegalStateException(), exiting early")); |
| |
| CHIP_ERROR err = CHIP_NO_ERROR; |
| jmethodID method; |
| err = JniReferences::GetInstance().FindMethod(env, callback, "onError", "(Ljava/lang/Exception;)V", &method); |
| if (err != CHIP_NO_ERROR) { |
| ChipLogError(Zcl, "Error throwing IllegalStateException %d", err.AsInteger()); |
| return; |
| } |
| |
| jthrowable exception; |
| err = CreateIllegalStateException(env, message, errorCode, exception); |
| if (err != CHIP_NO_ERROR) { |
| ChipLogError(Zcl, "Error throwing IllegalStateException %d", err.AsInteger()); |
| return; |
| } |
| env->CallVoidMethod(callback, method, exception); |
| } |
| |
| // TODO(#8773): Clean up callbacks. |
| class CHIPDefaultSuccessCallback : public Callback::Callback<DefaultSuccessCallback> { |
| public: |
| CHIPDefaultSuccessCallback(jobject javaCallback): Callback::Callback<DefaultSuccessCallback>(CallbackFn, this) |
| { |
| JNIEnv * env = JniReferences::GetInstance().GetEnvForCurrentThread(); |
| if (env == nullptr) { |
| ChipLogError(Zcl, "Could not create global reference for Java callback"); |
| return; |
| } |
| javaCallbackRef = env->NewGlobalRef(javaCallback); |
| if (javaCallbackRef == nullptr) { |
| ChipLogError(Zcl, "Could not create global reference for Java callback"); |
| } |
| } |
| |
| ~CHIPDefaultSuccessCallback() |
| { |
| JNIEnv * env = JniReferences::GetInstance().GetEnvForCurrentThread(); |
| if (env == nullptr) { |
| ChipLogError(Zcl, "Could not create global reference for Java callback"); |
| return; |
| } |
| env->DeleteGlobalRef(javaCallbackRef); |
| }; |
| |
| static void CallbackFn(void * context) |
| { |
| chip::DeviceLayer::StackUnlock unlock; |
| CHIP_ERROR err = CHIP_NO_ERROR; |
| jmethodID javaMethod; |
| JNIEnv * env = JniReferences::GetInstance().GetEnvForCurrentThread(); |
| jobject javaCallbackRef; |
| CHIPDefaultSuccessCallback * cppCallback = nullptr; |
| |
| VerifyOrExit(env != nullptr, err = CHIP_JNI_ERROR_NO_ENV); |
| |
| cppCallback = reinterpret_cast<CHIPDefaultSuccessCallback *>(context); |
| VerifyOrExit(cppCallback != nullptr, err = CHIP_ERROR_INCORRECT_STATE); |
| |
| // It's valid for javaCallbackRef to be nullptr if the Java code passed in a null callback. |
| javaCallbackRef = cppCallback->javaCallbackRef; |
| VerifyOrExit(javaCallbackRef != nullptr, err = CHIP_NO_ERROR); |
| |
| err = JniReferences::GetInstance().FindMethod(env, javaCallbackRef, "onSuccess", "()V", &javaMethod); |
| SuccessOrExit(err); |
| |
| env->ExceptionClear(); |
| env->CallVoidMethod(javaCallbackRef, javaMethod); |
| |
| exit: |
| if (err != CHIP_NO_ERROR) { |
| ChipLogError(Zcl, "Error invoking Java callback: %" CHIP_ERROR_FORMAT, err.Format()); |
| } |
| if (cppCallback != nullptr) { |
| cppCallback->Cancel(); |
| delete cppCallback; |
| } |
| } |
| |
| private: |
| jobject javaCallbackRef; |
| }; |
| |
| class CHIPDefaultFailureCallback : public Callback::Callback<DefaultFailureCallback> { |
| public: |
| CHIPDefaultFailureCallback(jobject javaCallback): Callback::Callback<DefaultFailureCallback>(CallbackFn, this) |
| { |
| JNIEnv * env = JniReferences::GetInstance().GetEnvForCurrentThread(); |
| if (env == nullptr) { |
| ChipLogError(Zcl, "Could not create global reference for Java callback"); |
| return; |
| } |
| javaCallbackRef = env->NewGlobalRef(javaCallback); |
| if (javaCallbackRef == nullptr) { |
| ChipLogError(Zcl, "Could not create global reference for Java callback"); |
| } |
| } |
| |
| ~CHIPDefaultFailureCallback() |
| { |
| JNIEnv * env = JniReferences::GetInstance().GetEnvForCurrentThread(); |
| if (env == nullptr) { |
| ChipLogError(Zcl, "Could not create global reference for Java callback"); |
| return; |
| } |
| env->DeleteGlobalRef(javaCallbackRef); |
| }; |
| |
| static void CallbackFn(void * context, uint8_t status) |
| { |
| chip::DeviceLayer::StackUnlock unlock; |
| CHIP_ERROR err = CHIP_NO_ERROR; |
| jmethodID javaMethod; |
| JNIEnv * env = JniReferences::GetInstance().GetEnvForCurrentThread(); |
| jobject javaCallbackRef; |
| jthrowable exception; |
| CHIPDefaultFailureCallback * cppCallback = nullptr; |
| |
| VerifyOrExit(env != nullptr, err = CHIP_JNI_ERROR_NO_ENV); |
| |
| cppCallback = reinterpret_cast<CHIPDefaultFailureCallback *>(context); |
| VerifyOrExit(cppCallback != nullptr, err = CHIP_ERROR_INCORRECT_STATE); |
| |
| // It's valid for javaCallbackRef to be nullptr if the Java code passed in a null callback. |
| javaCallbackRef = cppCallback->javaCallbackRef; |
| VerifyOrExit(javaCallbackRef != nullptr, err = CHIP_NO_ERROR); |
| |
| err = JniReferences::GetInstance().FindMethod(env, javaCallbackRef, "onError", "(Ljava/lang/Exception;)V", &javaMethod); |
| SuccessOrExit(err); |
| |
| err = CreateChipClusterException(env, status, exception); |
| SuccessOrExit(err); |
| |
| env->ExceptionClear(); |
| env->CallVoidMethod(javaCallbackRef, javaMethod, exception); |
| exit: |
| if (err != CHIP_NO_ERROR) { |
| ChipLogError(Zcl, "Error invoking Java callback: %" CHIP_ERROR_FORMAT, err.Format()); |
| } |
| if (cppCallback != nullptr) { |
| cppCallback->Cancel(); |
| delete cppCallback; |
| } |
| } |
| |
| private: |
| jobject javaCallbackRef; |
| }; |
| |
| {{#chip_server_global_responses}} |
| |
| class CHIP{{chipCallback.name}}AttributeCallback : public Callback::Callback<{{chipCallback.name}}AttributeCallback> { |
| public: |
| CHIP{{chipCallback.name}}AttributeCallback(jobject javaCallback, bool keepAlive = false): Callback::Callback<{{chipCallback.name}}AttributeCallback>(CallbackFn, this) |
| , keepAlive(keepAlive) |
| { |
| JNIEnv * env = JniReferences::GetInstance().GetEnvForCurrentThread(); |
| if (env == nullptr) { |
| ChipLogError(Zcl, "Could not create global reference for Java callback"); |
| return; |
| } |
| javaCallbackRef = env->NewGlobalRef(javaCallback); |
| if (javaCallbackRef == nullptr) { |
| ChipLogError(Zcl, "Could not create global reference for Java callback"); |
| } |
| } |
| |
| static void maybeDestroy(CHIP{{chipCallback.name}}AttributeCallback * callback) { |
| if (!callback->keepAlive) { |
| callback->Cancel(); |
| delete callback; |
| } |
| } |
| |
| static void CallbackFn(void * context, {{chipCallback.type}} value) |
| { |
| chip::DeviceLayer::StackUnlock unlock; |
| CHIP_ERROR err = CHIP_NO_ERROR; |
| |
| JNIEnv * env = JniReferences::GetInstance().GetEnvForCurrentThread(); |
| VerifyOrReturn(env != nullptr, ChipLogError(Zcl, "Could not get JNI env")); |
| |
| std::unique_ptr<CHIP{{chipCallback.name}}AttributeCallback, decltype(&maybeDestroy)> cppCallback(reinterpret_cast<CHIP{{chipCallback.name}}AttributeCallback *>(context), maybeDestroy); |
| |
| // It's valid for javaCallbackRef to be nullptr if the Java code passed in a null callback. |
| jobject javaCallbackRef = cppCallback.get()->javaCallbackRef; |
| VerifyOrReturn(javaCallbackRef != nullptr, ChipLogDetail(Zcl, "Early return from attribute callback since Java callback is null")); |
| |
| jmethodID javaMethod; |
| {{#unless (isStrEqual chipCallback.name "OctetString")}} |
| {{#unless (isStrEqual chipCallback.name "CharString")}} |
| err = JniReferences::GetInstance().FindMethod(env, javaCallbackRef, "onSuccess", "({{convertCTypeToJniSignature chipCallback.type}})V", &javaMethod); |
| VerifyOrReturn(err == CHIP_NO_ERROR, ChipLogError(Zcl, "Could not find onSuccess method")); |
| env->CallVoidMethod(javaCallbackRef, javaMethod, static_cast<{{convertBasicCTypeToJniType chipCallback.type}}>(value)); |
| {{/unless}} |
| {{/unless}} |
| |
| {{#if (isStrEqual chipCallback.name "OctetString")}} |
| err = JniReferences::GetInstance().FindMethod(env, javaCallbackRef, "onSuccess", "([B)V", &javaMethod); |
| VerifyOrReturn(err == CHIP_NO_ERROR, ChipLogError(Zcl, "Could not find onSuccess method")); |
| |
| jbyteArray valueArr = env->NewByteArray(value.size()); |
| env->ExceptionClear(); |
| env->SetByteArrayRegion(valueArr, 0, value.size(), reinterpret_cast<const jbyte *>(value.data())); |
| |
| env->CallVoidMethod(javaCallbackRef, javaMethod, valueArr); |
| {{/if}} |
| |
| {{#if (isStrEqual chipCallback.name "CharString")}} |
| err = JniReferences::GetInstance().FindMethod(env, javaCallbackRef, "onSuccess", "(Ljava/lang/String;)V", &javaMethod); |
| VerifyOrReturn(err == CHIP_NO_ERROR, ChipLogError(Zcl, "Could not find onSuccess method")); |
| |
| UtfString valueStr(env, value); |
| env->CallVoidMethod(javaCallbackRef, javaMethod, valueStr.jniValue()); |
| {{/if}} |
| } |
| |
| private: |
| jobject javaCallbackRef; |
| bool keepAlive; |
| }; |
| {{/chip_server_global_responses}} |
| |
| {{#chip_client_clusters}} |
| {{#chip_cluster_responses}} |
| class CHIP{{asUpperCamelCase parent.name}}Cluster{{asUpperCamelCase name}}Callback : public Callback::Callback<{{asUpperCamelCase parent.name}}Cluster{{asUpperCamelCase name}}Callback> |
| { |
| public: |
| CHIP{{asUpperCamelCase parent.name}}Cluster{{asUpperCamelCase name}}Callback(jobject javaCallback): Callback::Callback<{{asUpperCamelCase parent.name}}Cluster{{asUpperCamelCase name}}Callback>(CallbackFn, this) |
| { |
| JNIEnv * env = JniReferences::GetInstance().GetEnvForCurrentThread(); |
| if (env == nullptr) { |
| ChipLogError(Zcl, "Could not create global reference for Java callback"); |
| return; |
| } |
| |
| javaCallbackRef = env->NewGlobalRef(javaCallback); |
| if (javaCallbackRef == nullptr) { |
| ChipLogError(Zcl, "Could not create global reference for Java callback"); |
| } |
| } |
| ~CHIP{{asUpperCamelCase parent.name}}Cluster{{asUpperCamelCase name}}Callback() |
| { |
| JNIEnv * env = JniReferences::GetInstance().GetEnvForCurrentThread(); |
| if (env == nullptr) { |
| ChipLogError(Zcl, "Could not create global reference for Java callback"); |
| return; |
| } |
| env->DeleteGlobalRef(javaCallbackRef); |
| }; |
| |
| static void CallbackFn(void * context{{#chip_cluster_response_arguments}}, {{asUnderlyingZclType type}} {{asSymbol label}}{{/chip_cluster_response_arguments}}) |
| { |
| chip::DeviceLayer::StackUnlock unlock; |
| CHIP_ERROR err = CHIP_NO_ERROR; |
| JNIEnv * env = JniReferences::GetInstance().GetEnvForCurrentThread(); |
| jobject javaCallbackRef; |
| jmethodID javaMethod; |
| CHIP{{asUpperCamelCase parent.name}}Cluster{{asUpperCamelCase name}}Callback * cppCallback = nullptr; |
| {{#chip_cluster_response_arguments}} |
| {{#if (isOctetString type)}} |
| jbyteArray {{asSymbol label}}Arr; |
| {{else if (isShortString type)}} |
| UtfString {{asSymbol label}}Str(env, {{asSymbol label}}); |
| {{/if}} |
| {{/chip_cluster_response_arguments}} |
| |
| VerifyOrExit(env != nullptr, err = CHIP_JNI_ERROR_NO_ENV); |
| |
| cppCallback = reinterpret_cast<CHIP{{asUpperCamelCase parent.name}}Cluster{{asUpperCamelCase name}}Callback *>(context); |
| VerifyOrExit(cppCallback != nullptr, err = CHIP_JNI_ERROR_NULL_OBJECT); |
| |
| javaCallbackRef = cppCallback->javaCallbackRef; |
| VerifyOrExit(javaCallbackRef != nullptr, err = CHIP_NO_ERROR); |
| |
| err = JniReferences::GetInstance().FindMethod(env, javaCallbackRef, "onSuccess", "({{#chip_cluster_response_arguments}}{{#if isArray}}{{else if (isOctetString type)}}[B{{else if (isShortString type)}}Ljava/lang/String;{{else}}{{asJniSignature type}}{{/if}}{{/chip_cluster_response_arguments}})V", &javaMethod); |
| SuccessOrExit(err); |
| |
| {{#chip_cluster_response_arguments}} |
| {{#if (isOctetString type)}} |
| {{asSymbol label}}Arr = env->NewByteArray({{asSymbol label}}.size()); |
| VerifyOrExit({{asSymbol label}}Arr != nullptr, err = CHIP_ERROR_NO_MEMORY); |
| env->ExceptionClear(); |
| env->SetByteArrayRegion({{asSymbol label}}Arr, 0, {{asSymbol label}}.size(), reinterpret_cast<const jbyte *>({{asSymbol label}}.data())); |
| VerifyOrExit(!env->ExceptionCheck(), err = CHIP_JNI_ERROR_EXCEPTION_THROWN); |
| {{/if}} |
| {{/chip_cluster_response_arguments}} |
| |
| env->CallVoidMethod(javaCallbackRef, javaMethod |
| {{#chip_cluster_response_arguments}} |
| {{#if isArray}} |
| // {{asSymbol label}}: {{asUnderlyingZclType type}} |
| // Conversion from this type to Java is not properly implemented yet |
| {{else if (isOctetString type)}} |
| , {{asSymbol label}}Arr |
| {{else if (isShortString type)}} |
| , {{asSymbol label}}Str.jniValue() |
| {{else}} |
| , static_cast<{{asJniBasicTypeForZclType type}}>({{asSymbol label}}) |
| {{/if}} |
| {{/chip_cluster_response_arguments}} |
| ); |
| |
| {{#chip_cluster_response_arguments}} |
| {{#if (isOctetString type)}} |
| env->DeleteLocalRef({{asSymbol label}}Arr); |
| {{/if}} |
| {{/chip_cluster_response_arguments}} |
| |
| exit: |
| if (err != CHIP_NO_ERROR) { |
| ChipLogError(Zcl, "Error invoking Java callback: %" CHIP_ERROR_FORMAT, err.Format()); |
| } |
| if (cppCallback != nullptr) { |
| cppCallback->Cancel(); |
| delete cppCallback; |
| } |
| } |
| |
| private: |
| jobject javaCallbackRef; |
| }; |
| |
| {{/chip_cluster_responses}} |
| {{/chip_client_clusters}} |
| |
| {{#chip_client_clusters}} |
| {{#chip_server_cluster_attributes}} |
| {{#if isList}} |
| class CHIP{{asUpperCamelCase parent.name}}{{asUpperCamelCase name}}AttributeCallback : public Callback::Callback<{{asUpperCamelCase parent.name}}{{asUpperCamelCase name}}ListAttributeCallback> |
| { |
| public: |
| CHIP{{asUpperCamelCase parent.name}}{{asUpperCamelCase name}}AttributeCallback(jobject javaCallback): Callback::Callback<{{asUpperCamelCase parent.name}}{{asUpperCamelCase name}}ListAttributeCallback>(CallbackFn, this) |
| { |
| JNIEnv * env = JniReferences::GetInstance().GetEnvForCurrentThread(); |
| if (env == nullptr) { |
| ChipLogError(Zcl, "Could not create global reference for Java callback"); |
| return; |
| } |
| |
| javaCallbackRef = env->NewGlobalRef(javaCallback); |
| if (javaCallbackRef == nullptr) { |
| ChipLogError(Zcl, "Could not create global reference for Java callback"); |
| } |
| } |
| |
| static void CallbackFn(void * context, {{zapTypeToDecodableClusterObjectType type ns=parent.name isArgument=true}} list) |
| { |
| chip::DeviceLayer::StackUnlock unlock; |
| CHIP_ERROR err = CHIP_NO_ERROR; |
| JNIEnv * env = JniReferences::GetInstance().GetEnvForCurrentThread(); |
| jobject javaCallbackRef; |
| |
| VerifyOrReturn(env != nullptr, ChipLogError(Zcl, "Could not get JNI env")); |
| |
| std::unique_ptr<CHIP{{asUpperCamelCase parent.name}}{{asUpperCamelCase name}}AttributeCallback> cppCallback(reinterpret_cast<CHIP{{asUpperCamelCase parent.name}}{{asUpperCamelCase name}}AttributeCallback *>(context)); |
| |
| // It's valid for javaCallbackRef to be nullptr if the Java code passed in a null callback. |
| javaCallbackRef = cppCallback.get()->javaCallbackRef; |
| VerifyOrReturn(javaCallbackRef != nullptr, ChipLogProgress(Zcl, "Early return from attribute callback since Java callback is null")); |
| |
| jclass arrayListClass; |
| err = JniReferences::GetInstance().GetClassRef(env, "java/util/ArrayList", arrayListClass); |
| VerifyOrReturn(err == CHIP_NO_ERROR, ChipLogError(Zcl, "Error using Java ArrayList")); |
| JniClass arrayListJniClass(arrayListClass); |
| jmethodID arrayListCtor = env->GetMethodID(arrayListClass, "<init>", "()V"); |
| jmethodID arrayListAddMethod = env->GetMethodID(arrayListClass, "add", "(Ljava/lang/Object;)Z"); |
| VerifyOrReturn(arrayListCtor != nullptr && arrayListAddMethod != nullptr, ChipLogError(Zcl, "Error finding Java ArrayList methods")); |
| jobject arrayListObj = env->NewObject(arrayListClass, arrayListCtor); |
| VerifyOrReturn(arrayListObj != nullptr, ChipLogError(Zcl, "Error creating Java ArrayList")); |
| |
| jmethodID javaMethod; |
| err = JniReferences::GetInstance().FindMethod(env, javaCallbackRef, "onSuccess", "(Ljava/util/List;)V", &javaMethod); |
| VerifyOrReturn(err == CHIP_NO_ERROR, ChipLogError(Zcl, "Could not find onSuccess() method")); |
| |
| {{#if isStruct}} |
| jclass attributeClass; |
| err = JniReferences::GetInstance().GetClassRef(env, "chip/devicecontroller/ChipClusters${{asUpperCamelCase parent.name}}Cluster${{asUpperCamelCase name}}Attribute", attributeClass); |
| VerifyOrReturn(err == CHIP_NO_ERROR, ChipLogError(Zcl, "Could not find class chip/devicecontroller/ChipClusters${{asUpperCamelCase parent.name}}Cluster${{asUpperCamelCase name}}Attribute")); |
| JniClass attributeJniClass(attributeClass); |
| jmethodID attributeCtor = env->GetMethodID(attributeClass, "<init>" |
| , "({{#chip_attribute_list_entryTypes}}{{#if isOptional}}{{! TODO: Add support for optional types here }}{{else if isNullable}}{{! TODO: Add support for nullable types here }}{{else if isArray}}{{! TODO: Add support for lists here }}{{else if isStruct}}{{! TODO: Add support for structs here }}{{else if (isString type)}}{{#if (isOctetString type)}}[B{{else}}Ljava/lang/String;{{/if}}{{else}}{{asJniSignature type}}{{/if}}{{/chip_attribute_list_entryTypes}})V"); |
| VerifyOrReturn(attributeCtor != nullptr, ChipLogError(Zcl, "Could not find {{asUpperCamelCase name}}Attribute constructor")); |
| {{/if}} |
| |
| auto iter = list.begin(); |
| while (iter.Next()) |
| { |
| auto & entry = iter.GetValue(); |
| {{#if isStruct}} |
| (void)entry; {{! In case all our struct members are not supported yet }} |
| {{#chip_attribute_list_entryTypes}} |
| {{#if isOptional}} |
| {{! TODO: Add support for optional types here }} |
| {{else if isNullable}} |
| {{! TODO: Add support for nullable types here }} |
| {{else if isArray}} |
| {{! TODO: Add support for lists here }} |
| {{else if isStruct}} |
| {{! TODO: Add support for structs here }} |
| {{else if (isOctetString type)}} |
| jbyteArray {{asLowerCamelCase name}} = env->NewByteArray(entry.{{asLowerCamelCase name}}.size()); |
| env->SetByteArrayRegion({{asLowerCamelCase name}}, 0, entry.{{asLowerCamelCase name}}.size(), reinterpret_cast<const jbyte *>(entry.{{asLowerCamelCase name}}.data())); |
| {{else if (isCharString type)}} |
| UtfString {{asLowerCamelCase name}}Str(env, entry.{{asLowerCamelCase name}}); |
| jstring {{asLowerCamelCase name}}({{asLowerCamelCase name}}Str.jniValue()); |
| {{else}} |
| {{asJniBasicType type}} {{asLowerCamelCase name}} = entry.{{asLowerCamelCase name}}; |
| {{/if}} |
| {{/chip_attribute_list_entryTypes}} |
| |
| jobject attributeObj = env->NewObject(attributeClass, attributeCtor |
| {{#chip_attribute_list_entryTypes}} |
| {{#if isOptional}} |
| {{! TODO: Add support for optional types here }} |
| {{else if isNullable}} |
| {{! TODO: Add support for nullable types here }} |
| {{else if isArray}} |
| {{! TODO: Add support for lists here }} |
| {{else if isStruct}} |
| {{! TODO: Add support for structs here }} |
| {{else}} |
| , {{asLowerCamelCase name}} |
| {{/if}} |
| {{/chip_attribute_list_entryTypes}} |
| ); |
| VerifyOrReturn(attributeObj != nullptr, ChipLogError(Zcl, "Could not create {{asUpperCamelCase name}}Attribute object")); |
| |
| env->CallBooleanMethod(arrayListObj, arrayListAddMethod, attributeObj); |
| {{else}} |
| {{#if (isOctetString type)}} |
| jbyteArray {{asLowerCamelCase name}} = env->NewByteArray(entry.size()); |
| env->SetByteArrayRegion({{asLowerCamelCase name}}, 0, entry.size(), reinterpret_cast<const jbyte *>(entry.data())); |
| {{else if (isCharString type)}} |
| UtfString {{asLowerCamelCase name}}Str(env, entry); |
| jstring {{asLowerCamelCase name}}({{asLowerCamelCase name}}Str.jniValue()); |
| {{else}} |
| jclass entryTypeCls; |
| JniReferences::GetInstance().GetClassRef(env, "java/lang/{{asJavaBasicTypeForZclType type true}}", entryTypeCls); |
| jmethodID entryTypeCtor = env->GetMethodID(entryTypeCls, "<init>", "({{asJniSignature type}})V"); |
| jobject {{asLowerCamelCase name}} = env->NewObject(entryTypeCls, entryTypeCtor, entry); |
| {{/if}} |
| env->CallBooleanMethod(arrayListObj, arrayListAddMethod, {{asLowerCamelCase name}}); |
| {{/if}} |
| } |
| VerifyOrReturn(iter.GetStatus() == CHIP_NO_ERROR, ChipLogError(Zcl, "Error decoding {{asUpperCamelCase name}}Attribute value: %" CHIP_ERROR_FORMAT, iter.GetStatus().Format())); |
| |
| env->ExceptionClear(); |
| env->CallVoidMethod(javaCallbackRef, javaMethod, arrayListObj); |
| } |
| |
| private: |
| jobject javaCallbackRef; |
| }; |
| |
| {{/if}} |
| {{/chip_server_cluster_attributes}} |
| {{/chip_client_clusters}} |
| |
| JNI_METHOD(void, BaseChipCluster, deleteCluster)(JNIEnv * env, jobject self, jlong clusterPtr) |
| { |
| chip::DeviceLayer::StackLock lock; |
| ClusterBase * cluster = reinterpret_cast<ClusterBase *>(clusterPtr); |
| if (cluster != nullptr) { |
| delete cluster; |
| } |
| } |
| |
| {{#chip_client_clusters}} |
| JNI_METHOD(jlong, {{asUpperCamelCase name}}Cluster, initWithDevice)(JNIEnv * env, jobject self, jlong devicePtr, jint endpointId) |
| { |
| chip::DeviceLayer::StackLock lock; |
| {{asUpperCamelCase name}}Cluster * cppCluster = new {{asUpperCamelCase name}}Cluster(); |
| |
| cppCluster->Associate(reinterpret_cast<Device *>(devicePtr), endpointId); |
| return reinterpret_cast<jlong>(cppCluster); |
| } |
| |
| {{#chip_cluster_commands}} |
| JNI_METHOD(void, {{asUpperCamelCase ../name}}Cluster, {{asLowerCamelCase name}})(JNIEnv * env, jobject self, jlong clusterPtr, jobject callback{{#chip_cluster_command_arguments_with_structs_expanded}}, {{asJniBasicType type}} {{asLowerCamelCase label}}{{/chip_cluster_command_arguments_with_structs_expanded}}) |
| { |
| chip::DeviceLayer::StackLock lock; |
| CHIP_ERROR err = CHIP_NO_ERROR; |
| {{asUpperCamelCase ../name}}Cluster * cppCluster; |
| |
| {{#chip_cluster_command_arguments_with_structs_expanded}} |
| {{#if (isOctetString type)}} |
| JniByteArray {{asLowerCamelCase label}}Arr(env, {{asLowerCamelCase label}}); |
| {{else if (isCharString type)}} |
| JniUtfString {{asLowerCamelCase label}}Str(env, {{asLowerCamelCase label}}); |
| {{/if}} |
| {{/chip_cluster_command_arguments_with_structs_expanded}} |
| |
| {{#if hasSpecificResponse}} |
| std::unique_ptr<CHIP{{asCamelCased parent.name false}}Cluster{{asCamelCased responseName false}}Callback, void (*)(CHIP{{asCamelCased parent.name false}}Cluster{{asCamelCased responseName false}}Callback *)> onSuccess( |
| Platform::New<CHIP{{asCamelCased parent.name false}}Cluster{{asCamelCased responseName false}}Callback>(callback), Platform::Delete<CHIP{{asCamelCased parent.name false}}Cluster{{asCamelCased responseName false}}Callback>); |
| {{else}} |
| std::unique_ptr<CHIPDefaultSuccessCallback, void (*)(CHIPDefaultSuccessCallback *)> onSuccess(Platform::New<CHIPDefaultSuccessCallback>(callback), Platform::Delete<CHIPDefaultSuccessCallback>); |
| {{/if}} |
| std::unique_ptr<CHIPDefaultFailureCallback, void (*)(CHIPDefaultFailureCallback *)> onFailure(Platform::New<CHIPDefaultFailureCallback>(callback), Platform::Delete<CHIPDefaultFailureCallback>); |
| VerifyOrExit(onSuccess.get() != nullptr, err = CHIP_ERROR_INCORRECT_STATE); |
| VerifyOrExit(onFailure.get() != nullptr, err = CHIP_ERROR_INCORRECT_STATE); |
| |
| cppCluster = reinterpret_cast<{{asUpperCamelCase ../name}}Cluster *>(clusterPtr); |
| VerifyOrExit(cppCluster != nullptr, err = CHIP_ERROR_INCORRECT_STATE); |
| |
| err = cppCluster->{{asCamelCased name false}}(onSuccess->Cancel(), onFailure->Cancel(){{#chip_cluster_command_arguments_with_structs_expanded}}, {{#if_chip_enum type}}static_cast<{{chipType}}>({{asLowerCamelCase label}}){{else if (isOctetString type)}}{{asUnderlyingZclType type}}((const uint8_t*) {{asLowerCamelCase label}}Arr.data(), {{asLowerCamelCase label}}Arr.size()){{else if (isCharString type)}}chip::CharSpan({{asLowerCamelCase label}}Str.c_str(), strlen({{asLowerCamelCase label}}Str.c_str())){{else}}{{asLowerCamelCase label}}{{/if_chip_enum}}{{/chip_cluster_command_arguments_with_structs_expanded}}); |
| SuccessOrExit(err); |
| |
| exit: |
| if (err != CHIP_NO_ERROR) { |
| jthrowable exception; |
| jmethodID method; |
| |
| err = JniReferences::GetInstance().FindMethod(env, callback, "onError", "(Ljava/lang/Exception;)V", &method); |
| if (err != CHIP_NO_ERROR) { |
| ChipLogError(Zcl, "Error throwing IllegalStateException %" CHIP_ERROR_FORMAT, err.Format()); |
| return; |
| } |
| |
| err = CreateIllegalStateException(env, "Error invoking cluster", err, exception); |
| if (err != CHIP_NO_ERROR) { |
| ChipLogError(Zcl, "Error throwing IllegalStateException %" CHIP_ERROR_FORMAT, err.Format()); |
| return; |
| } |
| env->CallVoidMethod(callback, method, exception); |
| } else { |
| onSuccess.release(); |
| onFailure.release(); |
| } |
| } |
| {{/chip_cluster_commands}} |
| {{#chip_server_cluster_attributes}} |
| |
| JNI_METHOD(void, {{asUpperCamelCase ../name}}Cluster, read{{asUpperCamelCase name}}Attribute)(JNIEnv * env, jobject self, jlong clusterPtr, jobject callback) |
| { |
| chip::DeviceLayer::StackLock lock; |
| {{#if isList}} |
| std::unique_ptr<CHIP{{asCamelCased parent.name false}}{{asCamelCased name false}}AttributeCallback, void (*)(CHIP{{asCamelCased parent.name false}}{{asCamelCased name false}}AttributeCallback *)> onSuccess( |
| Platform::New<CHIP{{asCamelCased parent.name false}}{{asCamelCased name false}}AttributeCallback>(callback), Platform::Delete<CHIP{{asCamelCased parent.name false}}{{asCamelCased name false}}AttributeCallback>); |
| {{else}} |
| std::unique_ptr<CHIP{{chipCallback.name}}AttributeCallback, void (*)(CHIP{{chipCallback.name}}AttributeCallback *)> onSuccess(Platform::New<CHIP{{chipCallback.name}}AttributeCallback>(callback{{#if (isString type)}}, |
| {{#if (isOctetString type)}}true{{else}}false{{/if}}{{/if}}), Platform::Delete<CHIP{{chipCallback.name}}AttributeCallback>); |
| {{/if}} |
| VerifyOrReturn(onSuccess.get() != nullptr, ReturnIllegalStateException(env, callback, "Error creating native success callback", CHIP_ERROR_NO_MEMORY)); |
| |
| std::unique_ptr<CHIPDefaultFailureCallback, void (*)(CHIPDefaultFailureCallback *)> onFailure(Platform::New<CHIPDefaultFailureCallback>(callback), Platform::Delete<CHIPDefaultFailureCallback>); |
| VerifyOrReturn(onFailure.get() != nullptr, ReturnIllegalStateException(env, callback, "Error creating native failure callback", CHIP_ERROR_NO_MEMORY)); |
| |
| CHIP_ERROR err = CHIP_NO_ERROR; |
| {{asCamelCased ../name false}}Cluster * cppCluster = reinterpret_cast<{{asCamelCased ../name false}}Cluster *>(clusterPtr); |
| VerifyOrReturn(cppCluster != nullptr, ReturnIllegalStateException(env, callback, "Could not get native cluster", CHIP_ERROR_INCORRECT_STATE)); |
| |
| err = cppCluster->ReadAttribute{{asCamelCased name false}}(onSuccess->Cancel(), onFailure->Cancel()); |
| VerifyOrReturn(err == CHIP_NO_ERROR, ReturnIllegalStateException(env, callback, "Error reading attribute", err)); |
| |
| onSuccess.release(); |
| onFailure.release(); |
| } |
| {{#if isWritableAttribute}} |
| |
| JNI_METHOD(void, {{asUpperCamelCase ../name}}Cluster, write{{asUpperCamelCase name}}Attribute)(JNIEnv * env, jobject self, jlong clusterPtr, jobject callback, {{asJniBasicType type}} value) |
| { |
| chip::DeviceLayer::StackLock lock; |
| std::unique_ptr<CHIPDefaultSuccessCallback, void (*)(CHIPDefaultSuccessCallback *)> onSuccess(Platform::New<CHIPDefaultSuccessCallback>(callback), Platform::Delete<CHIPDefaultSuccessCallback>); |
| VerifyOrReturn(onSuccess.get() != nullptr, ReturnIllegalStateException(env, callback, "Error creating native success callback", CHIP_ERROR_NO_MEMORY)); |
| |
| std::unique_ptr<CHIPDefaultFailureCallback, void (*)(CHIPDefaultFailureCallback *)> onFailure(Platform::New<CHIPDefaultFailureCallback>(callback), Platform::Delete<CHIPDefaultFailureCallback>); |
| VerifyOrReturn(onFailure.get() != nullptr, ReturnIllegalStateException(env, callback, "Error creating native failure callback", CHIP_ERROR_NO_MEMORY)); |
| |
| CHIP_ERROR err = CHIP_NO_ERROR; |
| {{asCamelCased ../name false}}Cluster * cppCluster = reinterpret_cast<{{asCamelCased ../name false}}Cluster *>(clusterPtr); |
| VerifyOrReturn(cppCluster != nullptr, ReturnIllegalStateException(env, callback, "Could not get native cluster", CHIP_ERROR_INCORRECT_STATE)); |
| |
| {{#if (isOctetString type)}} |
| JniByteArray jniArr(env, value); |
| err = cppCluster->WriteAttribute{{asUpperCamelCase name}}(onSuccess->Cancel(), onFailure->Cancel(), chip::ByteSpan((const uint8_t*) jniArr.data(), jniArr.size())); |
| {{else if (isCharString type)}} |
| JniUtfString valueStr(env, value); |
| err = cppCluster->WriteAttribute{{asUpperCamelCase name}}(onSuccess->Cancel(), onFailure->Cancel(), chip::CharSpan(valueStr.c_str(), strlen(valueStr.c_str()))); |
| {{else}} |
| err = cppCluster->WriteAttribute{{asUpperCamelCase name}}(onSuccess->Cancel(), onFailure->Cancel(), static_cast<{{chipType}}>(value)); |
| {{/if}} |
| VerifyOrReturn(err == CHIP_NO_ERROR, ReturnIllegalStateException(env, callback, "Error writing attribute", err)); |
| |
| onSuccess.release(); |
| onFailure.release(); |
| } |
| {{/if}} |
| {{#if isReportableAttribute}} |
| |
| JNI_METHOD(void, {{asCamelCased ../name false}}Cluster, subscribe{{asCamelCased name false}}Attribute)(JNIEnv * env, jobject self, jlong clusterPtr, jobject callback, jint minInterval, jint maxInterval) |
| { |
| chip::DeviceLayer::StackLock lock; |
| std::unique_ptr<CHIPDefaultSuccessCallback, void (*)(CHIPDefaultSuccessCallback *)> onSuccess(Platform::New<CHIPDefaultSuccessCallback>(callback), Platform::Delete<CHIPDefaultSuccessCallback>); |
| VerifyOrReturn(onSuccess.get() != nullptr, ReturnIllegalStateException(env, callback, "Error creating native success callback", CHIP_ERROR_NO_MEMORY)); |
| |
| std::unique_ptr<CHIPDefaultFailureCallback, void (*)(CHIPDefaultFailureCallback *)> onFailure(Platform::New<CHIPDefaultFailureCallback>(callback), Platform::Delete<CHIPDefaultFailureCallback>); |
| VerifyOrReturn(onFailure.get() != nullptr, ReturnIllegalStateException(env, callback, "Error creating native failure callback", CHIP_ERROR_NO_MEMORY)); |
| |
| CHIP_ERROR err = CHIP_NO_ERROR; |
| {{asCamelCased ../name false}}Cluster * cppCluster = reinterpret_cast<{{asCamelCased ../name false}}Cluster *>(clusterPtr); |
| VerifyOrReturn(cppCluster != nullptr, ReturnIllegalStateException(env, callback, "Could not get native cluster", CHIP_ERROR_INCORRECT_STATE)); |
| |
| err = cppCluster->SubscribeAttribute{{asCamelCased name false}}(onSuccess->Cancel(), onFailure->Cancel(), static_cast<uint16_t>(minInterval), static_cast<uint16_t>(maxInterval)); |
| VerifyOrReturn(err == CHIP_NO_ERROR, ReturnIllegalStateException(env, callback, "Error subscribing to attribute", err)); |
| |
| onSuccess.release(); |
| onFailure.release(); |
| } |
| |
| JNI_METHOD(void, {{asCamelCased ../name false}}Cluster, report{{asCamelCased name false}}Attribute)(JNIEnv * env, jobject self, jlong clusterPtr, jobject callback) |
| { |
| chip::DeviceLayer::StackLock lock; |
| std::unique_ptr<CHIP{{chipCallback.name}}AttributeCallback, void (*)(CHIP{{chipCallback.name}}AttributeCallback *)> onReport(Platform::New<CHIP{{chipCallback.name}}AttributeCallback>(callback{{#if (isString type)}},{{#if (isOctetString type)}}true{{else}}false{{/if}}{{/if}}, true), Platform::Delete<CHIP{{chipCallback.name}}AttributeCallback>); |
| VerifyOrReturn(onReport.get() != nullptr, ReturnIllegalStateException(env, callback, "Error creating native report callback", CHIP_ERROR_NO_MEMORY)); |
| |
| CHIP_ERROR err = CHIP_NO_ERROR; |
| {{asCamelCased ../name false}}Cluster * cppCluster = reinterpret_cast<{{asCamelCased ../name false}}Cluster *>(clusterPtr); |
| VerifyOrReturn(cppCluster != nullptr, ReturnIllegalStateException(env, callback, "Could not get native cluster", CHIP_ERROR_INCORRECT_STATE)); |
| |
| err = cppCluster->ReportAttribute{{asCamelCased name false}}(onReport->Cancel()); |
| VerifyOrReturn(err == CHIP_NO_ERROR, ReturnIllegalStateException(env, callback, "Error registering for attribute reporting", err)); |
| |
| onReport.release(); |
| } |
| {{/if}} |
| {{/chip_server_cluster_attributes}} |
| {{/chip_client_clusters}} |
| {{/if}} |