Skip to content

Commit

Permalink
Return node hierarchy from wildcard, and include TLV (project-chip#15495
Browse files Browse the repository at this point in the history
)
  • Loading branch information
austinh0 authored Feb 25, 2022
1 parent f68dd29 commit 448578d
Show file tree
Hide file tree
Showing 10 changed files with 305 additions and 27 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,14 @@ import android.view.View
import android.view.ViewGroup
import android.widget.Button
import android.widget.EditText
import androidx.appcompat.widget.MenuItemHoverListener
import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope
import chip.devicecontroller.ChipDeviceController
import chip.devicecontroller.ReportCallback
import chip.devicecontroller.SubscriptionEstablishedCallback
import chip.devicecontroller.model.ChipAttributePath
import chip.devicecontroller.model.ChipPathId
import chip.devicecontroller.model.NodeState
import com.google.chip.chiptool.ChipClient
import com.google.chip.chiptool.R
import kotlinx.android.synthetic.main.wildcard_fragment.attributeIdEd
Expand All @@ -37,17 +37,13 @@ class WildcardFragment : Fragment() {

private val reportCallback = object : ReportCallback {
override fun onError(attributePath: ChipAttributePath, ex: Exception) {
Log.i(TAG, "Report error for $attributePath: $ex")
Log.e(TAG, "Report error for $attributePath: $ex")
}

override fun onReport(values: Map<ChipAttributePath, Any?>) {
Log.i(TAG, "Received report with ${values.size} values")
val builder = StringBuilder()
values.forEach { builder.append("${it.key}: ${it.value}\n") }
val output = builder.toString()

Log.i(TAG, output)
requireActivity().runOnUiThread { outputTv.text = output }
override fun onReport(nodeData: NodeState) {
Log.i(TAG, "Received wildcard report")
Log.i(TAG, nodeData.toString())
requireActivity().runOnUiThread { outputTv.text = nodeData.toString() }
}
}

Expand Down
66 changes: 54 additions & 12 deletions src/controller/java/AndroidCallbacks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
#include <lib/support/JniReferences.h>
#include <lib/support/JniTypeWrappers.h>
#include <lib/support/logging/CHIPLogging.h>
#include <platform/PlatformManager.h>
#include <type_traits>

namespace chip {
Expand Down Expand Up @@ -155,7 +156,18 @@ ReportCallback::~ReportCallback()

void ReportCallback::OnReportBegin()
{
mReports.clear();
JNIEnv * env = JniReferences::GetInstance().GetEnvForCurrentThread();
CHIP_ERROR err = CHIP_NO_ERROR;

err = JniReferences::GetInstance().GetClassRef(env, "chip/devicecontroller/model/NodeState", mNodeStateCls);
VerifyOrReturn(err == CHIP_NO_ERROR, ChipLogError(Controller, "Could not get NodeState class"));
jmethodID nodeStateCtor = env->GetMethodID(mNodeStateCls, "<init>", "(Ljava/util/Map;)V");
VerifyOrReturn(nodeStateCtor != nullptr, ChipLogError(Controller, "Could not find NodeState constructor"));

jobject map = nullptr;
err = JniReferences::GetInstance().CreateHashMap(map);
VerifyOrReturn(err == CHIP_NO_ERROR, ChipLogError(Controller, "Could not create HashMap"));
mNodeStateObj = env->NewObject(mNodeStateCls, nodeStateCtor, map);
}

void ReportCallback::OnReportEnd()
Expand All @@ -164,19 +176,13 @@ void ReportCallback::OnReportEnd()
CHIP_ERROR err = CHIP_NO_ERROR;
JNIEnv * env = JniReferences::GetInstance().GetEnvForCurrentThread();

jobject map;
JniReferences::GetInstance().CreateHashMap(map);
for (std::pair<jobject, jobject> reportPair : mReports)
{
JniReferences::GetInstance().PutInMap(map, reportPair.first, reportPair.second);
}

jmethodID onReportMethod;
err = JniReferences::GetInstance().FindMethod(env, mReportCallbackRef, "onReport", "(Ljava/util/Map;)V", &onReportMethod);
err = JniReferences::GetInstance().FindMethod(env, mReportCallbackRef, "onReport", "(Lchip/devicecontroller/model/NodeState;)V",
&onReportMethod);
VerifyOrReturn(err == CHIP_NO_ERROR, ChipLogError(Controller, "Could not find onReport method"));

DeviceLayer::StackUnlock unlock;
env->CallVoidMethod(mReportCallbackRef, onReportMethod, map);
env->CallVoidMethod(mReportCallbackRef, onReportMethod, mNodeStateObj);
}

void ReportCallback::OnAttributeData(const app::ConcreteDataAttributePath & aPath, TLV::TLVReader * apData,
Expand Down Expand Up @@ -206,14 +212,50 @@ void ReportCallback::OnAttributeData(const app::ConcreteDataAttributePath & aPat
return;
}

jobject value = DecodeAttributeValue(aPath, *apData, &err);
TLV::TLVReader readerForJavaObject;
TLV::TLVReader readerForJavaTLV;
readerForJavaObject.Init(*apData);
readerForJavaTLV.Init(*apData);

jobject value = DecodeAttributeValue(aPath, readerForJavaObject, &err);
// If we don't know this attribute, just skip it.
VerifyOrReturn(err != CHIP_ERROR_IM_MALFORMED_ATTRIBUTE_PATH);
VerifyOrReturn(err == CHIP_NO_ERROR, ReportError(attributePathObj, err));
VerifyOrReturn(!env->ExceptionCheck(), env->ExceptionDescribe(),
ReportError(attributePathObj, CHIP_JNI_ERROR_EXCEPTION_THROWN));

mReports.push_back(std::make_pair(attributePathObj, value));
// Create TLV byte array to pass to Java layer
size_t bufferLen = readerForJavaTLV.GetRemainingLength() + readerForJavaTLV.GetLengthRead();
std::unique_ptr<uint8_t[]> buffer = std::unique_ptr<uint8_t[]>(new uint8_t[bufferLen]);
uint32_t size = 0;
// The TLVReader's read head is not pointing to the first element in the container, instead of the container itself, use
// a TLVWriter to get a TLV with a normalized TLV buffer (Wrapped with an anonymous tag, no extra "end of container" tag
// at the end.)
TLV::TLVWriter writer;
writer.Init(buffer.get(), bufferLen);
err = writer.CopyElement(TLV::AnonymousTag(), readerForJavaTLV);
VerifyOrReturn(err == CHIP_NO_ERROR, ReportError(attributePathObj, err));
size = writer.GetLengthWritten();
chip::ByteArray jniByteArray(env, reinterpret_cast<jbyte *>(buffer.get()), size);

// Create AttributeState object
jclass attributeStateCls;
err = JniReferences::GetInstance().GetClassRef(env, "chip/devicecontroller/model/AttributeState", attributeStateCls);
VerifyOrReturn(attributeStateCls != nullptr, ChipLogError(Controller, "Could not find AttributeState class"));
chip::JniClass attributeStateJniCls(attributeStateCls);
jmethodID attributeStateCtor = env->GetMethodID(attributeStateCls, "<init>", "(Ljava/lang/Object;[B)V");
VerifyOrReturn(attributeStateCtor != nullptr, ChipLogError(Controller, "Could not find AttributeState constructor"));
jobject attributeStateObj = env->NewObject(attributeStateCls, attributeStateCtor, value, jniByteArray.jniValue());
VerifyOrReturn(attributeStateObj != nullptr, ChipLogError(Controller, "Could not create AttributeState object"));

// Add AttributeState to NodeState
jmethodID addAttributeMethod;
err = JniReferences::GetInstance().FindMethod(env, mNodeStateObj, "addAttribute",
"(IJJLchip/devicecontroller/model/AttributeState;)V", &addAttributeMethod);
VerifyOrReturnError(err == CHIP_NO_ERROR, ChipLogError(Controller, "Could not find addAttribute method"));
env->CallVoidMethod(mNodeStateObj, addAttributeMethod, static_cast<jint>(aPath.mEndpointId),
static_cast<jlong>(aPath.mClusterId), static_cast<jlong>(aPath.mAttributeId), attributeStateObj);
VerifyOrReturnError(!env->ExceptionCheck(), env->ExceptionDescribe());
}

CHIP_ERROR ReportCallback::CreateChipAttributePath(const app::ConcreteDataAttributePath & aPath, jobject & outObj)
Expand Down
5 changes: 3 additions & 2 deletions src/controller/java/AndroidCallbacks.h
Original file line number Diff line number Diff line change
Expand Up @@ -74,8 +74,9 @@ struct ReportCallback : public app::ReadClient::Callback
jobject mWrapperCallbackRef = nullptr;
jobject mSubscriptionEstablishedCallbackRef = nullptr;
jobject mReportCallbackRef = nullptr;
// List of pairs of Java ChipAttributePath and report value. Not using map because jobjects should not be keys.
std::list<std::pair<jobject, jobject>> mReports;
// NodeState Java object that will be returned to the application.
jobject mNodeStateObj = nullptr;
jclass mNodeStateCls = nullptr;
};

} // namespace Controller
Expand Down
4 changes: 4 additions & 0 deletions src/controller/java/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -91,8 +91,12 @@ android_library("java") {
"src/chip/devicecontroller/ReportCallback.java",
"src/chip/devicecontroller/ReportCallbackJni.java",
"src/chip/devicecontroller/SubscriptionEstablishedCallback.java",
"src/chip/devicecontroller/model/AttributeState.java",
"src/chip/devicecontroller/model/ChipAttributePath.java",
"src/chip/devicecontroller/model/ChipPathId.java",
"src/chip/devicecontroller/model/ClusterState.java",
"src/chip/devicecontroller/model/EndpointState.java",
"src/chip/devicecontroller/model/NodeState.java",
"zap-generated/chip/devicecontroller/ChipClusters.java",
"zap-generated/chip/devicecontroller/ChipStructs.java",
"zap-generated/chip/devicecontroller/ClusterInfoMapping.java",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,11 @@
package chip.devicecontroller;

import chip.devicecontroller.model.ChipAttributePath;
import java.util.Map;
import chip.devicecontroller.model.NodeState;

/** An interface for receiving read/subscribe CHIP reports. */
public interface ReportCallback {
void onError(ChipAttributePath attributePath, Exception e);

void onReport(Map<ChipAttributePath, Object> values);
void onReport(NodeState nodeState);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
* Copyright (c) 2022 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.
*
*/
package chip.devicecontroller.model;

/** Represents the reported value of an attribute in object form AND TLV. */
public final class AttributeState {
private Object valueObject;
private byte[] tlv;

public AttributeState(Object valueObject, byte[] tlv) {
this.valueObject = valueObject;
this.tlv = tlv;
}

public Object getValue() {
return valueObject;
}

/**
* Return a byte array containing the TLV for an attribute, wrapped within an anonymous TLV tag.
*/
public byte[] getTlv() {
return tlv;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
import java.util.Locale;
import java.util.Objects;

/** An attribute path that could be used in a request, or received in a report. */
/** An attribute path that should be used for requests. */
public class ChipAttributePath {
private ChipPathId endpointId, clusterId, attributeId;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/*
* Copyright (c) 2022 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.
*
*/
package chip.devicecontroller.model;

import androidx.annotation.Nullable;
import java.util.Map;

/** Class for tracking CHIP cluster state in a hierarchical manner. */
public final class ClusterState {
private Map<Long, AttributeState> attributes;

public ClusterState(Map<Long, AttributeState> attributes) {
this.attributes = attributes;
}

public Map<Long, AttributeState> getAttributeStates() {
return attributes;
}

/**
* Convenience utility for getting an {@code ClusterState}.
*
* @return the {@code ClusterState} for the specified id, or null if not found.
*/
@Nullable
public AttributeState getAttributeState(long attributeId) {
return attributes.get(attributeId);
}

@Override
public String toString() {
StringBuilder builder = new StringBuilder();
attributes.forEach(
(attributeId, attributeState) -> {
builder.append("Attribute ");
builder.append(attributeId);
builder.append(": ");
builder.append(
attributeState.getValue() == null ? "null" : attributeState.getValue().toString());
builder.append("\n");
});
return builder.toString();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/*
* Copyright (c) 2022 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.
*
*/
package chip.devicecontroller.model;

import androidx.annotation.Nullable;
import java.util.Map;

/** Class for tracking CHIP endpoint state in a hierarchical manner. */
public final class EndpointState {
private Map<Long, ClusterState> clusters;

public EndpointState(Map<Long, ClusterState> clusters) {
this.clusters = clusters;
}

public Map<Long, ClusterState> getClusterStates() {
return clusters;
}

/**
* Convenience utility for getting an {@code ClusterState}.
*
* @return the {@code ClusterState} for the specified id, or null if not found.
*/
@Nullable
public ClusterState getClusterState(long clusterId) {
return clusters.get(clusterId);
}

@Override
public String toString() {
StringBuilder builder = new StringBuilder();
clusters.forEach(
(clusterId, clusterState) -> {
builder.append("Cluster ");
builder.append(clusterId);
builder.append(": {\n");
builder.append(clusterState.toString());
builder.append("}\n");
});
return builder.toString();
}
}
Loading

0 comments on commit 448578d

Please sign in to comment.