Skip to content

Commit

Permalink
Add JAVA/JNI IM Write API (#25445)
Browse files Browse the repository at this point in the history
* Add JAVA/JNI IM Write API

-- Add generic IM write API  which takes a list of AttributeWrites, in
each write it would have concrete attribue path and tlv blob.
-- Add initial IM write test in java matter controller test example,
next step is to integrate kotlin tlvwriter/tlvreader, and refactor this
test command to accept the customized paratmeters for attribute path and
data.
  • Loading branch information
yunhanw-google authored and pull[bot] committed Aug 21, 2023
1 parent b6ce414 commit a8e12dc
Show file tree
Hide file tree
Showing 14 changed files with 669 additions and 10 deletions.
1 change: 1 addition & 0 deletions examples/java-matter-controller/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ java_library("java") {
"java/src/com/matter/controller/commands/pairing/PairOnNetworkFabricCommand.java",
"java/src/com/matter/controller/commands/pairing/PairOnNetworkInstanceNameCommand.java",
"java/src/com/matter/controller/commands/pairing/PairOnNetworkLongCommand.java",
"java/src/com/matter/controller/commands/pairing/PairOnNetworkLongImWriteCommand.java",
"java/src/com/matter/controller/commands/pairing/PairOnNetworkShortCommand.java",
"java/src/com/matter/controller/commands/pairing/PairOnNetworkVendorCommand.java",
"java/src/com/matter/controller/commands/pairing/PairingCommand.java",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,15 @@ private fun getPairingCommands(
)
}

private fun getImCommands(
controller: ChipDeviceController,
credentialsIssuer: CredentialsIssuer
): List<Command> {
return listOf(
PairOnNetworkLongImWriteCommand(controller, credentialsIssuer),
)
}

fun main(args: Array<String>) {
val controller = ChipDeviceController(
ControllerParams.newBuilder()
Expand All @@ -70,7 +79,7 @@ fun main(args: Array<String>) {

commandManager.register("discover", getDiscoveryCommands(controller, credentialsIssuer))
commandManager.register("pairing", getPairingCommands(controller, credentialsIssuer))

commandManager.register("im", getImCommands(controller, credentialsIssuer))
try {
commandManager.run(args)
} catch (e: Exception) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,13 @@ public synchronized void waitResult() {
} catch (InterruptedException e) {
}
}

if (!realResult.get().getResult()) {
logger.log(Level.INFO, "error: %s", realResult.get().getError());
throw new RuntimeException("received failure test result");
}
}

public synchronized void clear() {
realResult = Optional.empty();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -102,16 +102,20 @@ public void run() throws Exception {

protected abstract void runCommand();

public void setSuccess() {
public final void setSuccess() {
mFutureResult.setRealResult(RealResult.Success());
}

public void setFailure(String error) {
public final void setFailure(String error) {
mFutureResult.setRealResult(RealResult.Error(error));
}

public void waitCompleteMs(long timeoutMs) {
public final void waitCompleteMs(long timeoutMs) {
mFutureResult.setTimeoutMs(timeoutMs);
mFutureResult.waitResult();
}

public final void clear() {
mFutureResult.clear();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
package com.matter.controller.commands.pairing;

import chip.devicecontroller.ChipDeviceController;
import chip.devicecontroller.GetConnectedDeviceCallbackJni.GetConnectedDeviceCallback;
import chip.devicecontroller.WriteAttributesCallback;
import chip.devicecontroller.model.AttributeWriteRequest;
import chip.devicecontroller.model.ChipAttributePath;
import com.matter.controller.commands.common.CredentialsIssuer;
import java.util.ArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.annotation.Nullable;

public final class PairOnNetworkLongImWriteCommand extends PairingCommand {
private static final int MATTER_PORT = 5540;
private long devicePointer;
private static final int CLUSTER_ID_BASIC = 0x0028;
private static final int ATTR_ID_LOCAL_CONFIG_DISABLED = 16;
private static Logger logger = Logger.getLogger(PairOnNetworkLongImWriteCommand.class.getName());

private void setDevicePointer(long devicePointer) {
this.devicePointer = devicePointer;
}

private class InternalWriteAttributesCallback implements WriteAttributesCallback {
@Override
public void onError(@Nullable ChipAttributePath attributePath, Exception e) {
logger.log(Level.INFO, "Write receive onError on ");
if (attributePath != null) {
logger.log(Level.INFO, attributePath.toString());
}

setFailure("write failure");
}

@Override
public void onResponse(ChipAttributePath attributePath) {
logger.log(Level.INFO, "Write receve OnResponse on ");
if (attributePath != null) {
logger.log(Level.INFO, attributePath.toString());
}
setSuccess();
}
}

private class InternalGetConnectedDeviceCallback implements GetConnectedDeviceCallback {
@Override
public void onDeviceConnected(long devicePointer) {
setDevicePointer(devicePointer);
logger.log(Level.INFO, "onDeviceConnected");
}

@Override
public void onConnectionFailure(long nodeId, Exception error) {
logger.log(Level.INFO, "onConnectionFailure");
}
}

public PairOnNetworkLongImWriteCommand(
ChipDeviceController controller, CredentialsIssuer credsIssue) {
super(
controller,
"onnetwork-long-im-write",
PairingModeType.ON_NETWORK,
PairingNetworkType.NONE,
credsIssue,
DiscoveryFilterType.LONG_DISCRIMINATOR);
}

@Override
protected void runCommand() {
// boolean true for tlv
byte[] booleanTLV = {0x09};
AttributeWriteRequest attribute =
AttributeWriteRequest.newInstance(
/* endpointId= */ 0, CLUSTER_ID_BASIC, ATTR_ID_LOCAL_CONFIG_DISABLED, booleanTLV);
ArrayList<AttributeWriteRequest> attributeList = new ArrayList<>();
attributeList.add(attribute);

currentCommissioner()
.pairDeviceWithAddress(
getNodeId(),
getRemoteAddr().getHostAddress(),
MATTER_PORT,
getDiscriminator(),
getSetupPINCode(),
null);
currentCommissioner().setCompletionListener(this);
waitCompleteMs(getTimeoutMillis());
currentCommissioner()
.getConnectedDevicePointer(getNodeId(), new InternalGetConnectedDeviceCallback());
clear();

currentCommissioner()
.write(new InternalWriteAttributesCallback(), devicePointer, attributeList, 0, 0);

waitCompleteMs(getTimeoutMillis());
}
}
15 changes: 15 additions & 0 deletions src/controller/java/AndroidCallbacks-JNI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -70,3 +70,18 @@ JNI_METHOD(void, ReportEventCallbackJni, deleteCallback)(JNIEnv * env, jobject s
VerifyOrReturn(reportCallback != nullptr, ChipLogError(Controller, "ReportCallback handle is nullptr"));
delete reportCallback;
}

JNI_METHOD(jlong, WriteAttributesCallbackJni, newCallback)
(JNIEnv * env, jobject self, jobject writeAttributesCallbackJava)
{
WriteAttributesCallback * writeAttributesCallback =
chip::Platform::New<WriteAttributesCallback>(self, writeAttributesCallbackJava);
return reinterpret_cast<jlong>(writeAttributesCallback);
}

JNI_METHOD(void, WriteAttributesCallbackJni, deleteCallback)(JNIEnv * env, jobject self, jlong callbackHandle)
{
WriteAttributesCallback * writeAttributesCallback = reinterpret_cast<WriteAttributesCallback *>(callbackHandle);
VerifyOrReturn(writeAttributesCallback != nullptr, ChipLogError(Controller, "WriteAttributesCallback handle is nullptr"));
delete writeAttributesCallback;
}
116 changes: 113 additions & 3 deletions src/controller/java/AndroidCallbacks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ void GetConnectedDeviceCallback::OnDeviceConnectedFn(void * context, Messaging::
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 ScopedNodeId & peerId, CHIP_ERROR error)
Expand Down Expand Up @@ -118,6 +119,7 @@ void GetConnectedDeviceCallback::OnDeviceConnectionFailureFn(void * context, con

DeviceLayer::StackUnlock unlock;
env->CallVoidMethod(javaCallback, failureMethod, peerId.GetNodeId(), exception);
VerifyOrReturn(!env->ExceptionCheck(), env->ExceptionDescribe());
}

ReportCallback::ReportCallback(jobject wrapperCallback, jobject subscriptionEstablishedCallback, jobject reportCallback,
Expand Down Expand Up @@ -200,6 +202,7 @@ void ReportCallback::OnReportEnd()

DeviceLayer::StackUnlock unlock;
env->CallVoidMethod(mReportCallbackRef, onReportMethod, mNodeStateObj);
VerifyOrReturn(!env->ExceptionCheck(), env->ExceptionDescribe());
}

void ReportCallback::OnAttributeData(const app::ConcreteDataAttributePath & aPath, TLV::TLVReader * apData,
Expand Down Expand Up @@ -398,7 +401,7 @@ void ReportCallback::OnEventData(const app::EventHeader & aEventHeader, TLV::TLV
VerifyOrReturn(!env->ExceptionCheck(), env->ExceptionDescribe());
}

CHIP_ERROR ReportCallback::CreateChipAttributePath(const app::ConcreteDataAttributePath & aPath, jobject & outObj)
CHIP_ERROR CreateChipAttributePath(const app::ConcreteDataAttributePath & aPath, jobject & outObj)
{
JNIEnv * env = JniReferences::GetInstance().GetEnvForCurrentThread();
CHIP_ERROR err = CHIP_NO_ERROR;
Expand Down Expand Up @@ -455,7 +458,7 @@ void ReportCallback::OnDone(app::ReadClient *)

DeviceLayer::StackUnlock unlock;
env->CallVoidMethod(mReportCallbackRef, onDoneMethod);

VerifyOrReturn(!env->ExceptionCheck(), env->ExceptionDescribe());
JniReferences::GetInstance().GetEnvForCurrentThread()->DeleteGlobalRef(mWrapperCallbackRef);
}

Expand All @@ -479,7 +482,7 @@ CHIP_ERROR ReportCallback::OnResubscriptionNeeded(app::ReadClient * apReadClient
DeviceLayer::StackUnlock unlock;
env->CallVoidMethod(mResubscriptionAttemptCallbackRef, onResubscriptionAttemptMethod, aTerminationCause.AsInteger(),
apReadClient->ComputeTimeTillNextSubscription());

VerifyOrReturnError(!env->ExceptionCheck(), CHIP_JNI_ERROR_EXCEPTION_THROWN);
return CHIP_NO_ERROR;
}

Expand Down Expand Up @@ -512,6 +515,7 @@ void ReportCallback::ReportError(jobject attributePath, jobject eventPath, const

DeviceLayer::StackUnlock unlock;
env->CallVoidMethod(mReportCallbackRef, onErrorMethod, attributePath, eventPath, exception);
VerifyOrReturn(!env->ExceptionCheck(), env->ExceptionDescribe());
}

ReportEventCallback::ReportEventCallback(jobject wrapperCallback, jobject subscriptionEstablishedCallback, jobject reportCallback,
Expand Down Expand Up @@ -762,5 +766,111 @@ void ReportEventCallback::ReportError(jobject eventPath, const char * message, C
env->CallVoidMethod(mReportCallbackRef, onErrorMethod, eventPath, exception);
}

WriteAttributesCallback::WriteAttributesCallback(jobject wrapperCallback, jobject javaCallback) : mChunkedWriteCallback(this)
{
JNIEnv * env = JniReferences::GetInstance().GetEnvForCurrentThread();
VerifyOrReturn(env != nullptr, ChipLogError(Controller, "Could not get JNIEnv for current thread"));

mWrapperCallbackRef = env->NewGlobalRef(wrapperCallback);
VerifyOrReturn(!env->ExceptionCheck(), env->ExceptionDescribe());
if (mWrapperCallbackRef == nullptr)
{
ChipLogError(Controller, "Could not create global reference for Wrapper WriteAttributesCallback");
}
mJavaCallbackRef = env->NewGlobalRef(javaCallback);
VerifyOrReturn(!env->ExceptionCheck(), env->ExceptionDescribe());
if (mJavaCallbackRef == nullptr)
{
ChipLogError(Controller, "Could not create global reference for Java WriteAttributesCallback");
}
}

WriteAttributesCallback::~WriteAttributesCallback()
{
JNIEnv * env = JniReferences::GetInstance().GetEnvForCurrentThread();
VerifyOrReturn(env != nullptr, ChipLogError(Controller, "Could not get JNIEnv for current thread"));
env->DeleteGlobalRef(mJavaCallbackRef);
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();
jobject attributePathObj = nullptr;
err = CreateChipAttributePath(aPath, attributePathObj);
VerifyOrReturn(err == CHIP_NO_ERROR, ChipLogError(Controller, "Unable to create Java ChipAttributePath: %s", ErrorStr(err)));

if (aStatus.mStatus != Protocols::InteractionModel::Status::Success)
{
ReportError(attributePathObj, aStatus.mStatus);
return;
}

jmethodID onResponseMethod;
err = JniReferences::GetInstance().FindMethod(env, mJavaCallbackRef, "onResponse",
"(Lchip/devicecontroller/model/ChipAttributePath;)V", &onResponseMethod);
VerifyOrReturn(err == CHIP_NO_ERROR, ChipLogError(Controller, "Unable to find onError method: %s", ErrorStr(err)));

DeviceLayer::StackUnlock unlock;
env->CallVoidMethod(mJavaCallbackRef, onResponseMethod, attributePathObj);
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();

jmethodID onDoneMethod;
err = JniReferences::GetInstance().FindMethod(env, mJavaCallbackRef, "onDone", "()V", &onDoneMethod);
VerifyOrReturn(err == CHIP_NO_ERROR, ChipLogError(Controller, "Could not find onDone method"));

DeviceLayer::StackUnlock unlock;
env->CallVoidMethod(mJavaCallbackRef, onDoneMethod);
VerifyOrReturn(!env->ExceptionCheck(), env->ExceptionDescribe());
JniReferences::GetInstance().GetEnvForCurrentThread()->DeleteGlobalRef(mWrapperCallbackRef);
}

void WriteAttributesCallback::ReportError(jobject attributePath, CHIP_ERROR err)
{
ReportError(attributePath, ErrorStr(err), err.AsInteger());
}

void WriteAttributesCallback::ReportError(jobject attributePath, Protocols::InteractionModel::Status status)
{
ReportError(attributePath, "IM Status", static_cast<std::underlying_type_t<Protocols::InteractionModel::Status>>(status));
}

void WriteAttributesCallback::ReportError(jobject attributePath, const char * message, ChipError::StorageType errorCode)
{
CHIP_ERROR err = CHIP_NO_ERROR;
JNIEnv * env = JniReferences::GetInstance().GetEnvForCurrentThread();

ChipLogError(Controller, "ReportError is called");
jthrowable exception;
err = AndroidClusterExceptions::GetInstance().CreateIllegalStateException(env, message, errorCode, exception);
VerifyOrReturn(err == CHIP_NO_ERROR, ChipLogError(Controller, "Unable to create IllegalStateException: %s", ErrorStr(err)));

jmethodID onErrorMethod;
err = JniReferences::GetInstance().FindMethod(env, mJavaCallbackRef, "onError",
"(Lchip/devicecontroller/model/ChipAttributePath;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(mJavaCallbackRef, onErrorMethod, attributePath, exception);
VerifyOrReturn(!env->ExceptionCheck(), env->ExceptionDescribe());
}

} // namespace Controller
} // namespace chip
Loading

0 comments on commit a8e12dc

Please sign in to comment.