Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Android] Use InvokeCommand and support nullable/optional for commands #11873

Merged
merged 6 commits into from
Nov 17, 2021
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions src/android/CHIPTool/app/bin/src/main/cpp/native-lib.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
* Copyright (c) 2020 Project CHIP Authors
* All rights reserved.
*
* 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.
*
*/

// TODO: Build this out with native code to perform tasks with CHIP stack
// QRCode parsing
// Echo request/response processing
// On/Off request/response processing

#include <Base38.h>
#include <SetupPayload.h>
#include <jni.h>

// This is just a sample function to demonstrate end-to-end calling in/out of CHIP stack
// is working.
std::string base38Encode(void)
{
const uint8_t buf[6] = { 0, 1, 2, 3, 4, 5 };
size_t size = 6;
return chip::base38Encode(&buf[0], size);
}

extern "C" JNIEXPORT jstring JNICALL Java_com_google_chip_chiptool_CHIPNativeBridge_base38Encode(JNIEnv * env, jobject thiz)
{
std::string s = base38Encode();
return env->NewStringUTF(s.c_str());
}
2 changes: 2 additions & 0 deletions src/controller/java/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ shared_library("jni") {
"CHIPDeviceController-JNI.cpp",
"zap-generated/CHIPClusters-JNI.cpp",
"zap-generated/CHIPClustersRead-JNI.cpp",
"zap-generated/CHIPInvokeCallbacks.cpp",
"zap-generated/CHIPInvokeCallbacks.h",
"zap-generated/CHIPReadCallbacks.cpp",
"zap-generated/CHIPReadCallbacks.h",
]
Expand Down
14 changes: 14 additions & 0 deletions src/controller/java/templates/CHIPCallbackTypes.zapt
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{{> header}}
{{#if (chip_has_client_clusters)}}
#include <app/util/af-enums.h>
#include <app-common/zap-generated/cluster-objects.h>

typedef void (*CHIPDefaultSuccessCallbackType)(void *, const chip::app::DataModel::NullObjectType &);
typedef void (*CHIPDefaultFailureCallbackType)(void *, EmberAfStatus);

{{#chip_client_clusters}}
{{#chip_cluster_responses}}
typedef void (*CHIP{{asUpperCamelCase parent.name}}Cluster{{asUpperCamelCase name}}CallbackType)(void *, const chip::app::Clusters::{{asUpperCamelCase parent.name}}::Commands::{{asUpperCamelCase name}}::DecodableType &);
{{/chip_cluster_responses}}
{{/chip_client_clusters}}
{{/if}}
166 changes: 21 additions & 145 deletions src/controller/java/templates/CHIPClusters-JNI.zapt
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
{{> header}}
{{#if (chip_has_client_clusters)}}
#include "CHIPCallbackTypes.h"
#include "CHIPInvokeCallbacks.h"
#include "CHIPReadCallbacks.h"

#include <app-common/zap-generated/cluster-objects.h>
Expand All @@ -8,7 +10,6 @@

#include <controller/java/AndroidClusterExceptions.h>
#include <controller/java/CHIPDefaultCallbacks.h>
#include <lib/support/CHIPJNIError.h>
#include <lib/support/JniReferences.h>
#include <lib/support/JniTypeWrappers.h>
#include <jni.h>
Expand All @@ -22,110 +23,6 @@
using namespace chip;
using namespace chip::Controller;

{{! TODO(#8773): Clean up callbacks. }}

{{#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 false}}{{/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}}
JNI_METHOD(void, BaseChipCluster, deleteCluster)(JNIEnv * env, jobject self, jlong clusterPtr)
{
chip::DeviceLayer::StackLock lock;
Expand All @@ -146,58 +43,37 @@ JNI_METHOD(jlong, {{asUpperCamelCase name}}Cluster, initWithDevice)(JNIEnv * env
}

{{#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 false}} {{asLowerCamelCase label}}{{/chip_cluster_command_arguments_with_structs_expanded}})
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 true}} {{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}}
chip::app::Clusters::{{asUpperCamelCase parent.name}}::Commands::{{asUpperCamelCase name}}::Type request;

{{#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}}
{{#chip_cluster_command_arguments}}
{{>encode_value label=(asLowerCamelCase label)}}
{{/chip_cluster_command_arguments}}

{{#*inline "callbackName"}}{{#if hasSpecificResponse}}{{asUpperCamelCase parent.name false}}Cluster{{asUpperCamelCase responseName false}}{{else}}DefaultSuccess{{/if}}{{/inline}}

std::unique_ptr<CHIP{{>callbackName}}Callback, void (*)(CHIP{{>callbackName}}Callback *)> onSuccess(
Platform::New<CHIP{{>callbackName}}Callback>(callback), Platform::Delete<CHIP{{>callbackName}}Callback>);
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);
VerifyOrReturn(onSuccess.get() != nullptr, AndroidClusterExceptions::GetInstance().ReturnIllegalStateException(env, callback, "Error creating native callback", CHIP_ERROR_NO_MEMORY));
VerifyOrReturn(onFailure.get() != nullptr, AndroidClusterExceptions::GetInstance().ReturnIllegalStateException(env, callback, "Error creating native callback", CHIP_ERROR_NO_MEMORY));

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);
VerifyOrReturn(cppCluster != nullptr, AndroidClusterExceptions::GetInstance().ReturnIllegalStateException(env, callback, "Error getting native cluster", CHIP_ERROR_INCORRECT_STATE));

exit:
if (err != CHIP_NO_ERROR) {
jthrowable exception;
jmethodID method;
auto successFn = chip::Callback::Callback<CHIP{{>callbackName}}CallbackType>::FromCancelable(onSuccess->Cancel());
auto failureFn = chip::Callback::Callback<CHIPDefaultFailureCallbackType>::FromCancelable(onFailure->Cancel());

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 = cppCluster->InvokeCommand(request, onSuccess->mContext, successFn->mCall, failureFn->mCall);
VerifyOrReturn(err == CHIP_NO_ERROR, AndroidClusterExceptions::GetInstance().ReturnIllegalStateException(env, callback, "Error invoking command", CHIP_ERROR_INCORRECT_STATE));

err = chip::AndroidClusterExceptions::GetInstance().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();
}
onSuccess.release();
onFailure.release();
}
{{/chip_cluster_commands}}
{{#chip_server_cluster_attributes}}
Expand Down
76 changes: 76 additions & 0 deletions src/controller/java/templates/CHIPInvokeCallbacks-src.zapt
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
{{> header}}
{{#if (chip_has_client_clusters)}}
#include "CHIPCallbackTypes.h"
#include "CHIPInvokeCallbacks.h"

#include <app-common/zap-generated/cluster-objects.h>
#include <jni.h>
#include <lib/support/CodeUtils.h>
#include <lib/support/CHIPJNIError.h>
#include <lib/support/JniReferences.h>
#include <lib/support/JniTypeWrappers.h>
#include <platform/PlatformManager.h>

{{! TODO(#8773): Clean up callbacks. }}

namespace chip {

{{#chip_client_clusters}}
{{#chip_cluster_responses}}
CHIP{{asUpperCamelCase parent.name}}Cluster{{asUpperCamelCase name}}Callback::CHIP{{asUpperCamelCase parent.name}}Cluster{{asUpperCamelCase name}}Callback(jobject javaCallback): Callback::Callback<CHIP{{asUpperCamelCase parent.name}}Cluster{{asUpperCamelCase name}}CallbackType>(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::~CHIP{{asUpperCamelCase parent.name}}Cluster{{asUpperCamelCase name}}Callback()
{
JNIEnv * env = JniReferences::GetInstance().GetEnvForCurrentThread();
if (env == nullptr) {
ChipLogError(Zcl, "Could not delete global reference for Java callback");
return;
}
env->DeleteGlobalRef(javaCallbackRef);
};

void CHIP{{asUpperCamelCase parent.name}}Cluster{{asUpperCamelCase name}}Callback::CallbackFn(void * context, const chip::app::Clusters::{{asUpperCamelCase parent.name}}::Commands::{{asUpperCamelCase name}}::DecodableType & dataResponse)
{
chip::DeviceLayer::StackUnlock unlock;
CHIP_ERROR err = CHIP_NO_ERROR;
JNIEnv * env = JniReferences::GetInstance().GetEnvForCurrentThread();
jobject javaCallbackRef;
jmethodID javaMethod;

VerifyOrReturn(env != nullptr, ChipLogError(Zcl, "Error invoking Java callback: no JNIEnv"));

std::unique_ptr<CHIP{{asUpperCamelCase parent.name}}Cluster{{asUpperCamelCase name}}Callback, void (*)(CHIP{{asUpperCamelCase parent.name}}Cluster{{asUpperCamelCase name}}Callback *)> cppCallback(
reinterpret_cast<CHIP{{asUpperCamelCase parent.name}}Cluster{{asUpperCamelCase name}}Callback *>(context),
chip::Platform::Delete<CHIP{{asUpperCamelCase parent.name}}Cluster{{asUpperCamelCase name}}Callback>
);
VerifyOrReturn(cppCallback != nullptr, ChipLogError(Zcl, "Error invoking Java callback: failed to cast native callback"));

javaCallbackRef = cppCallback->javaCallbackRef;
// Java callback is allowed to be null, exit early if this is the case.
VerifyOrReturn(javaCallbackRef != nullptr);

err = JniReferences::GetInstance().FindMethod(env, javaCallbackRef, "onSuccess", "({{#chip_cluster_response_arguments}}{{#if isArray}}{{else if isOptional}}Ljava/util/Optional;{{else if (isOctetString type)}}[B{{else if (isShortString type)}}Ljava/lang/String;{{else}}{{asJniSignature type true}}{{/if}}{{/chip_cluster_response_arguments}})V", &javaMethod);
VerifyOrReturn(err == CHIP_NO_ERROR, ChipLogError(Zcl, "Error invoking Java callback: %s", ErrorStr(err)));

{{#chip_cluster_response_arguments}}
{{>decode_value}}
{{/chip_cluster_response_arguments}}

env->CallVoidMethod(javaCallbackRef, javaMethod{{#chip_cluster_response_arguments}}, {{asSymbol label}}{{/chip_cluster_response_arguments}});
}
{{/chip_cluster_responses}}
{{/chip_client_clusters}}
} // namespace chip
{{/if}}
29 changes: 29 additions & 0 deletions src/controller/java/templates/CHIPInvokeCallbacks.zapt
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
{{> header}}
{{#if (chip_has_client_clusters)}}
#include "CHIPCallbackTypes.h"

#include <app-common/zap-generated/cluster-objects.h>
#include <jni.h>
#include <zap-generated/CHIPClientCallbacks.h>

namespace chip {

{{#chip_client_clusters}}
{{#chip_cluster_responses}}
class CHIP{{asUpperCamelCase parent.name}}Cluster{{asUpperCamelCase name}}Callback : public Callback::Callback<CHIP{{asUpperCamelCase parent.name}}Cluster{{asUpperCamelCase name}}CallbackType>
{
public:
CHIP{{asUpperCamelCase parent.name}}Cluster{{asUpperCamelCase name}}Callback(jobject javaCallback);

~CHIP{{asUpperCamelCase parent.name}}Cluster{{asUpperCamelCase name}}Callback();

static void CallbackFn(void * context, const chip::app::Clusters::{{asUpperCamelCase parent.name}}::Commands::{{asUpperCamelCase name}}::DecodableType & data);

private:
jobject javaCallbackRef;
};

{{/chip_cluster_responses}}
{{/chip_client_clusters}}
} // namespace chip
{{/if}}
Loading