blob: d7c33c21f61a58b89f5884ac2970445e5210b639 [file] [log] [blame]
{{> 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}}