| {{> header}} |
| {{#if (chip_has_client_clusters)}} |
| #include "CHIPReadCallbacks.h" |
| |
| #include <zap-generated/CHIPClientCallbacks.h> |
| |
| #include <lib/support/JniReferences.h> |
| #include <lib/support/JniTypeWrappers.h> |
| #include <jni.h> |
| #include <lib/support/CodeUtils.h> |
| #include <platform/PlatformManager.h> |
| |
| {{#chip_server_global_responses}} |
| {{! TODO: Add support for struct-typed attributes }} |
| {{#unless (isStrEqual chipCallback.name "Unsupported")}} |
| |
| CHIP{{chipCallback.name}}AttributeCallback::CHIP{{chipCallback.name}}AttributeCallback(jobject javaCallback, bool keepAlive) : |
| chip::Callback::Callback<{{chipCallback.name}}AttributeCallback>(CallbackFn, this), keepAlive(keepAlive) |
| { |
| JNIEnv * env = chip::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{{chipCallback.name}}AttributeCallback::~CHIP{{chipCallback.name}}AttributeCallback() { |
| JNIEnv * env = chip::JniReferences::GetInstance().GetEnvForCurrentThread(); |
| if (env == nullptr) |
| { |
| ChipLogError(Zcl, "Could not delete global reference for Java callback"); |
| return; |
| } |
| env->DeleteGlobalRef(javaCallbackRef); |
| } |
| |
| void CHIP{{chipCallback.name}}AttributeCallback::CallbackFn(void * context, {{chipCallback.type}} value) |
| { |
| chip::DeviceLayer::StackUnlock unlock; |
| CHIP_ERROR err = CHIP_NO_ERROR; |
| |
| JNIEnv * env = chip::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 = chip::JniReferences::GetInstance().FindMethod(env, javaCallbackRef, "onSuccess", "({{convertCTypeToJniSignature chipCallback.type false}})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 = chip::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 = chip::JniReferences::GetInstance().FindMethod(env, javaCallbackRef, "onSuccess", "(Ljava/lang/String;)V", &javaMethod); |
| VerifyOrReturn(err == CHIP_NO_ERROR, ChipLogError(Zcl, "Could not find onSuccess method")); |
| |
| chip::UtfString valueStr(env, value); |
| env->CallVoidMethod(javaCallbackRef, javaMethod, valueStr.jniValue()); |
| {{/if}} |
| } |
| {{/unless}} |
| {{/chip_server_global_responses}} |
| |
| {{#chip_client_clusters}} |
| {{#chip_server_cluster_attributes}} |
| {{! TODO: Add support for struct-typed attributes }} |
| {{#unless (isStrEqual chipCallback.name "Unsupported")}} |
| |
| {{! NOTE: Some of our helpers rely on broken ZAP APIs that sniff for "isArray" |
| when we are just trying to work with the type of an array element. Fix |
| that by defining some inline partials that let us force isArray to false as |
| needed. }} |
| {{~#*inline "asUnboxedJniSignature"}}{{asJniSignature type false}}{{/inline}} |
| {{~#*inline "asUnboxedJniSignatureForEntry"}}{{> asUnboxedJniSignature isArray=false}}{{/inline~}} |
| {{~#*inline "asBoxedJavaBasicType"}}{{asJavaBasicTypeForZclType type true}}{{/inline~}} |
| {{~#*inline "asBoxedJavaBasicTypeForEntry"}}{{>asBoxedJavaBasicType isArray=false}}{{/inline~}} |
| {{#if_in_global_responses}} |
| {{else}} |
| CHIP{{asUpperCamelCase parent.name}}{{asUpperCamelCase name}}AttributeCallback::CHIP{{asUpperCamelCase parent.name}}{{asUpperCamelCase name}}AttributeCallback(jobject javaCallback, bool keepAlive) : |
| chip::Callback::Callback<CHIP{{asUpperCamelCase parent.name}}Cluster{{asUpperCamelCase name}}AttributeCallbackType>(CallbackFn, this), keepAlive(keepAlive) |
| { |
| JNIEnv * env = chip::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}}{{asUpperCamelCase name}}AttributeCallback::~CHIP{{asUpperCamelCase parent.name}}{{asUpperCamelCase name}}AttributeCallback() { |
| JNIEnv * env = chip::JniReferences::GetInstance().GetEnvForCurrentThread(); |
| if (env == nullptr) |
| { |
| ChipLogError(Zcl, "Could not delete global reference for Java callback"); |
| return; |
| } |
| env->DeleteGlobalRef(javaCallbackRef); |
| } |
| {{/if_in_global_responses}} |
| |
| {{#if isList}} |
| void CHIP{{asUpperCamelCase parent.name}}{{asUpperCamelCase name}}AttributeCallback::CallbackFn(void * context, {{zapTypeToDecodableClusterObjectType type ns=parent.name isArgument=true}} list) |
| { |
| chip::DeviceLayer::StackUnlock unlock; |
| CHIP_ERROR err = CHIP_NO_ERROR; |
| JNIEnv * env = chip::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, decltype(&maybeDestroy)> cppCallback(reinterpret_cast<CHIP{{asUpperCamelCase parent.name}}{{asUpperCamelCase name}}AttributeCallback *>(context), maybeDestroy); |
| |
| // 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 = chip::JniReferences::GetInstance().GetClassRef(env, "java/util/ArrayList", arrayListClass); |
| VerifyOrReturn(err == CHIP_NO_ERROR, ChipLogError(Zcl, "Error using Java ArrayList")); |
| chip::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 = chip::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 = chip::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")); |
| chip::JniClass attributeJniClass(attributeClass); |
| jmethodID attributeCtor = env->GetMethodID(attributeClass, "<init>" |
| , "({{#chip_attribute_list_entryTypes}}{{#if isArray}}{{! TODO: Add support for lists here }}{{else if isStruct}}{{! TODO: Add support for structs here }}{{else if isOptional}}Ljava/util/Optional;{{else if (isString type)}}{{#if (isOctetString type)}}[B{{else}}Ljava/lang/String;{{/if}}{{else}}{{asJniSignature type true}}{{/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}} |
| {{#unless isArray}} |
| {{#unless isStruct}} |
| bool {{asLowerCamelCase name}}Null = false; |
| bool {{asLowerCamelCase name}}HasValue = true; |
| {{#if isNullable}} |
| {{#unless isOptional}} |
| {{chipType}} {{asLowerCamelCase name}}Value; |
| {{asLowerCamelCase name}}Null = entry.{{asLowerCamelCase name}}.IsNull(); |
| if (!{{asLowerCamelCase name}}Null) { |
| {{asLowerCamelCase name}}Value = entry.{{asLowerCamelCase name}}.Value(); |
| } |
| {{/unless}} |
| {{/if}} |
| |
| {{#if isOptional}} |
| {{zapTypeToDecodableClusterObjectType type forceNotOptional=true forceNotNullable=true ns=parent.parent.name}} {{asLowerCamelCase name}}Value; |
| {{#if isNullable}} |
| {{asLowerCamelCase name}}HasValue = entry.{{asLowerCamelCase name}}.HasValue(); |
| if ({{asLowerCamelCase name}}HasValue) { |
| auto {{asLowerCamelCase name}}ValueFromOptional = entry.{{asLowerCamelCase name}}.Value(); |
| {{asLowerCamelCase name}}Null = {{asLowerCamelCase name}}ValueFromOptional.IsNull(); |
| if (!{{asLowerCamelCase name}}Null) { |
| {{asLowerCamelCase name}}Value = {{asLowerCamelCase name}}ValueFromOptional.Value(); |
| } |
| } |
| {{else}} |
| {{asLowerCamelCase name}}HasValue = entry.{{asLowerCamelCase name}}.HasValue(); |
| if ({{asLowerCamelCase name}}HasValue) { |
| {{asLowerCamelCase name}}Value = entry.{{asLowerCamelCase name}}.Value(); |
| } |
| {{/if}} |
| {{/if}} |
| |
| {{#unless isOptional}} |
| {{#unless isNullable}} |
| {{zapTypeToDecodableClusterObjectType type ns=parent.parent.name}} {{asLowerCamelCase name}}Value = entry.{{asLowerCamelCase name}}; |
| {{/unless}} |
| {{/unless}} |
| |
| {{/unless}} |
| {{/unless}} |
| |
| {{#if isArray}} |
| {{! TODO: Add support for lists here }} |
| {{else if isStruct}} |
| {{! TODO: Add support for structs here }} |
| {{else if (isOctetString type)}} |
| jbyteArray {{asLowerCamelCase name}} = nullptr; |
| if (!{{asLowerCamelCase name}}Null && {{asLowerCamelCase name}}HasValue) { |
| {{asLowerCamelCase name}} = env->NewByteArray({{asLowerCamelCase name}}Value.size()); |
| env->SetByteArrayRegion({{asLowerCamelCase name}}, 0, {{asLowerCamelCase name}}Value.size(), reinterpret_cast<const jbyte *>({{asLowerCamelCase name}}Value.data())); |
| } |
| {{else if (isCharString type)}} |
| jstring {{asLowerCamelCase name}} = nullptr; |
| chip::UtfString {{asLowerCamelCase name}}Str(env, {{asLowerCamelCase name}}Value); |
| if (!{{asLowerCamelCase name}}Null && {{asLowerCamelCase name}}HasValue) { |
| {{asLowerCamelCase name}} = jstring({{asLowerCamelCase name}}Str.jniValue()); |
| } |
| {{else}} |
| jobject {{asLowerCamelCase name}} = nullptr; |
| if (!{{asLowerCamelCase name}}Null && {{asLowerCamelCase name}}HasValue) { |
| jclass {{asLowerCamelCase name}}EntryCls; |
| chip::JniReferences::GetInstance().GetClassRef(env, "java/lang/{{>asBoxedJavaBasicTypeForEntry}}", {{asLowerCamelCase name}}EntryCls); |
| chip::JniClass {{asLowerCamelCase name}}JniClass({{asLowerCamelCase name}}EntryCls); |
| jmethodID {{asLowerCamelCase name}}EntryTypeCtor = env->GetMethodID({{asLowerCamelCase name}}EntryCls, "<init>", "({{>asUnboxedJniSignatureForEntry}})V"); |
| {{asLowerCamelCase name}} = env->NewObject({{asLowerCamelCase name}}EntryCls, {{asLowerCamelCase name}}EntryTypeCtor, {{asLowerCamelCase name}}Value); |
| } |
| {{/if}} |
| |
| {{#if isOptional}} |
| {{#unless isArray}} |
| {{#unless isStruct}} |
| jobject {{asLowerCamelCase name}}Optional = nullptr; |
| chip::JniReferences::GetInstance().CreateOptional({{asLowerCamelCase name}}, {{asLowerCamelCase name}}Optional); |
| {{/unless}} |
| {{/unless}} |
| {{/if}} |
| {{/chip_attribute_list_entryTypes}} |
| |
| jobject attributeObj = env->NewObject(attributeClass, attributeCtor |
| {{#chip_attribute_list_entryTypes}} |
| {{#if isArray}} |
| {{! TODO: Add support for lists here }} |
| {{else if isStruct}} |
| {{! TODO: Add support for structs here }} |
| {{else isOptional}} |
| , {{asLowerCamelCase name}}Optional |
| {{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}} |
| bool entryNull = false; |
| {{#unless isStruct}} |
| {{#if isNullable}} |
| {{zapTypeToDecodableClusterObjectType type ns=parent.name forceNotList=true}} entryValue; |
| entryNull = entry.IsNull(); |
| if (!entryNull) { |
| entryValue = entry.Value(); |
| } |
| {{else}} |
| {{zapTypeToDecodableClusterObjectType type ns=parent.name forceNotList=true}} entryValue = entry; |
| {{/if}} |
| {{/unless}} |
| |
| {{#if isStruct}} |
| {{! TODO: Add support for structs here }} |
| {{else if (isOctetString type)}} |
| jbyteArray entryObject = nullptr; |
| if (!entryNull) { |
| entryObject = env->NewByteArray(entryValue.size()); |
| env->SetByteArrayRegion(entryObject, 0, entryValue.size(), reinterpret_cast<const jbyte *>(entryValue.data())); |
| } |
| {{else if (isCharString type)}} |
| jstring entryObject = nullptr; |
| chip::UtfString entryStr(env, entryValue); |
| if (!entryNull) { |
| entryObject = jstring(entryStr.jniValue()); |
| } |
| {{else}} |
| jobject entryObject = nullptr; |
| if (!entryNull) { |
| jclass entryTypeCls; |
| chip::JniReferences::GetInstance().GetClassRef(env, "java/lang/{{>asBoxedJavaBasicTypeForEntry}}", entryTypeCls); |
| chip::JniClass jniClass(entryTypeCls); |
| jmethodID entryTypeCtor = env->GetMethodID(entryTypeCls, "<init>", "({{>asUnboxedJniSignatureForEntry}})V"); |
| entryObject = env->NewObject(entryTypeCls, entryTypeCtor, entryValue); |
| } |
| {{/if}} |
| |
| {{#if isOptional}} |
| jobject entryOptional = nullptr; |
| chip::JniReferences::GetInstance().CreateOptional(entryObject, entryOptional); |
| env->CallBooleanMethod(arrayListObj, arrayListAddMethod, entryOptional); |
| {{else}} |
| env->CallBooleanMethod(arrayListObj, arrayListAddMethod, entryObject); |
| {{/if}} |
| {{/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); |
| } |
| {{else}} |
| {{#if_in_global_responses}} |
| {{else}} |
| void CHIP{{asUpperCamelCase parent.name}}{{asUpperCamelCase name}}AttributeCallback::CallbackFn(void * context, {{zapTypeToDecodableClusterObjectType type ns=parent.name isArgument=true}} value) |
| { |
| chip::DeviceLayer::StackUnlock unlock; |
| CHIP_ERROR err = CHIP_NO_ERROR; |
| JNIEnv * env = chip::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, decltype(&maybeDestroy)> cppCallback(reinterpret_cast<CHIP{{asUpperCamelCase parent.name}}{{asUpperCamelCase name}}AttributeCallback *>(context), maybeDestroy); |
| |
| // 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")); |
| |
| jmethodID javaMethod; |
| err = chip::JniReferences::GetInstance().FindMethod(env, javaCallbackRef, "onSuccess", "({{#if isArray}}{{else if isStruct}}{{else if isOptional}}Ljava/util/Optional;{{else if (isOctetString type)}}[B{{else if (isCharString type)}}Ljava/lang/String;{{else}}{{asJniSignature type true}}{{/if}})V", &javaMethod); |
| VerifyOrReturn(err == CHIP_NO_ERROR, ChipLogError(Zcl, "Could not find onSuccess() method")); |
| |
| {{>decode_value source="value" target="javaValue" cluster=parent.name}} |
| |
| env->CallVoidMethod(javaCallbackRef, javaMethod, javaValue); |
| } |
| {{/if_in_global_responses}} |
| {{/if}} |
| {{/unless}} |
| {{/chip_server_cluster_attributes}} |
| {{/chip_client_clusters}} |
| |
| {{/if}} |