Skip to content

Commit

Permalink
[Android] Add Status handling for onReport code path (#32082)
Browse files Browse the repository at this point in the history
  • Loading branch information
yunhanw-google authored Feb 13, 2024
1 parent 4597a3b commit f216481
Show file tree
Hide file tree
Showing 6 changed files with 260 additions and 41 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import chip.devicecontroller.model.ChipPathId
import chip.devicecontroller.model.DataVersionFilter
import chip.devicecontroller.model.EventState
import chip.devicecontroller.model.NodeState
import chip.devicecontroller.model.Status
import com.matter.controller.commands.common.CredentialsIssuer
import java.util.logging.Level
import java.util.logging.Logger
Expand All @@ -34,13 +35,6 @@ class PairOnNetworkLongImReadCommand(
eventPath: ChipEventPath?,
e: Exception
) {
if (attributePath != null && attributePath.clusterId.getId() == UNIT_TEST_CLUSTER) {
logger.log(
Level.INFO,
"TODO: skip the error check for unit test cluster that covers most error result"
)
return
}
logger.log(Level.INFO, "Read receive onError")
setFailure("read failure")
}
Expand Down Expand Up @@ -72,11 +66,20 @@ class PairOnNetworkLongImReadCommand(
return cluster.equals(expected)
}

fun checkUnitTestClusterGeneralStatus(status: Status): Boolean =
(status.getStatus() == CLUSTER_ID_TEST_GENERAL_ERROR_STATUS) &&
!status.getClusterStatus().isPresent()

fun checkUnitTestClusterClusterStatus(status: Status): Boolean =
(status.getStatus() == CLUSTER_ID_TEST_CLUSTER_ERROR_STATUS) &&
status.getClusterStatus().isPresent() &&
status.getClusterStatus().get() == CLUSTER_ID_TEST_CLUSTER_ERROR_CLUSTER_STATUS

private fun validateResponse(nodeState: NodeState) {
val endpointZero =
requireNotNull(nodeState.getEndpointState(0)) { "Endpoint zero not found." }

val endpointOne = requireNotNull(nodeState.getEndpointState(0)) { "Endpoint one not found." }
val endpointOne = requireNotNull(nodeState.getEndpointState(1)) { "Endpoint one not found." }

val basicCluster =
requireNotNull(endpointZero.getClusterState(CLUSTER_ID_BASIC)) {
Expand All @@ -93,6 +96,11 @@ class PairOnNetworkLongImReadCommand(
"No local config disabled attribute found."
}

val unitTestCluster =
requireNotNull(endpointOne.getClusterState(UNIT_TEST_CLUSTER)) {
"Unit test cluster not found."
}

val startUpEvents =
requireNotNull(basicCluster.getEventState(EVENT_ID_START_UP)) { "No start up event found." }

Expand Down Expand Up @@ -122,6 +130,22 @@ class PairOnNetworkLongImReadCommand(
require(checkAllAttributesJsonForFixedLabel(clusterAttributes)) {
"Invalid fixed label cluster attributes Json ${clusterAttributes}"
}

require(
checkUnitTestClusterGeneralStatus(
unitTestCluster.getAttributeStatuses()[CLUSTER_ID_TEST_GENERAL_ERROR_BOOLEAN]!!
)
) {
"Invalid unit test cluster generalStatus check ${unitTestCluster}"
}

require(
checkUnitTestClusterClusterStatus(
unitTestCluster.getAttributeStatuses()[CLUSTER_ID_TEST_CLUSTER_ERROR_BOOLEAN]!!
)
) {
"Invalid unit test cluster clusterStatus check ${unitTestCluster}"
}
}

override fun onReport(nodeState: NodeState) {
Expand Down Expand Up @@ -212,10 +236,15 @@ class PairOnNetworkLongImReadCommand(
private const val MATTER_PORT = 5540
private const val CLUSTER_ID_BASIC = 0x0028L
private const val FIXED_LABEL_CLUSTER = 0x0040L
private const val UNIT_TEST_CLUSTER = 0xfff1fc05
private const val UNIT_TEST_CLUSTER = 0xfff1fc05L
private const val ATTR_ID_LOCAL_CONFIG_DISABLED = 16L
private const val EVENT_ID_START_UP = 0L
private const val GLOBAL_ATTRIBUTE_LIST = 65531L
private const val CLUSTER_ID_BASIC_VERSION = 0L
private const val CLUSTER_ID_TEST_GENERAL_ERROR_BOOLEAN = 0x0031L
private const val CLUSTER_ID_TEST_CLUSTER_ERROR_BOOLEAN = 0x0032L
private const val CLUSTER_ID_TEST_GENERAL_ERROR_STATUS = 0x8d
private const val CLUSTER_ID_TEST_CLUSTER_ERROR_STATUS = 1
private const val CLUSTER_ID_TEST_CLUSTER_ERROR_CLUSTER_STATUS = 17
}
}
95 changes: 67 additions & 28 deletions src/controller/java/AndroidCallbacks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -55,22 +55,6 @@ CHIP_ERROR CreateChipAttributePath(JNIEnv * env, const app::ConcreteDataAttribut
return CHIP_NO_ERROR;
}

CHIP_ERROR ReportCallback::CreateChipEventPath(JNIEnv * env, const app::ConcreteEventPath & aPath, jobject & outObj)
{
jclass eventPathCls = nullptr;
ReturnErrorOnFailure(
JniReferences::GetInstance().GetLocalClassRef(env, "chip/devicecontroller/model/ChipEventPath", eventPathCls));

jmethodID eventPathCtor =
env->GetStaticMethodID(eventPathCls, "newInstance", "(IJJ)Lchip/devicecontroller/model/ChipEventPath;");
VerifyOrReturnError(eventPathCtor != nullptr, CHIP_JNI_ERROR_METHOD_NOT_FOUND);

outObj = env->CallStaticObjectMethod(eventPathCls, eventPathCtor, static_cast<jint>(aPath.mEndpointId),
static_cast<jlong>(aPath.mClusterId), static_cast<jlong>(aPath.mEventId));
VerifyOrReturnError(outObj != nullptr, CHIP_JNI_ERROR_NULL_OBJECT);
return CHIP_NO_ERROR;
}

GetConnectedDeviceCallback::GetConnectedDeviceCallback(jobject wrapperCallback, jobject javaCallback) :
mOnSuccess(OnDeviceConnectedFn, this), mOnFailure(OnDeviceConnectionFailureFn, this)
{
Expand Down Expand Up @@ -270,21 +254,59 @@ CHIP_ERROR ConvertReportTlvToJson(const uint32_t id, TLV::TLVReader & data, std:
return TlvToJson(readerForJson, json);
}

static CHIP_ERROR CreateStatus(JNIEnv * env, const app::StatusIB & aStatus, jobject & outObj)
{
jclass statusCls = nullptr;
ReturnErrorOnFailure(JniReferences::GetInstance().GetLocalClassRef(env, "chip/devicecontroller/model/Status", statusCls));
jmethodID statusCtor = nullptr;
if (aStatus.mClusterStatus.HasValue())
{
statusCtor = env->GetStaticMethodID(statusCls, "newInstance", "(II)Lchip/devicecontroller/model/Status;");
VerifyOrReturnError(!env->ExceptionCheck(), CHIP_JNI_ERROR_EXCEPTION_THROWN);
VerifyOrReturnError(statusCtor != nullptr, CHIP_JNI_ERROR_METHOD_NOT_FOUND);
outObj = env->CallStaticObjectMethod(statusCls, statusCtor, static_cast<jint>(aStatus.mStatus),
static_cast<jint>(aStatus.mClusterStatus.Value()));
}
else
{
statusCtor = env->GetStaticMethodID(statusCls, "newInstance", "(I)Lchip/devicecontroller/model/Status;");
VerifyOrReturnError(!env->ExceptionCheck(), CHIP_JNI_ERROR_EXCEPTION_THROWN);
VerifyOrReturnError(statusCtor != nullptr, CHIP_JNI_ERROR_METHOD_NOT_FOUND);
outObj = env->CallStaticObjectMethod(statusCls, statusCtor, static_cast<jint>(aStatus.mStatus));
}
VerifyOrReturnError(outObj != nullptr, CHIP_JNI_ERROR_METHOD_NOT_FOUND);
return CHIP_NO_ERROR;
}

void ReportCallback::OnAttributeData(const app::ConcreteDataAttributePath & aPath, TLV::TLVReader * apData,
const app::StatusIB & aStatus)
{
CHIP_ERROR err = CHIP_NO_ERROR;
JNIEnv * env = JniReferences::GetInstance().GetEnvForCurrentThread();
VerifyOrReturn(env != nullptr, ChipLogError(Controller, "Could not get JNIEnv for current thread"));
JniLocalReferenceScope scope(env);

jobject attributePathObj = nullptr;
err = CreateChipAttributePath(env, aPath, attributePathObj);
VerifyOrReturn(err == CHIP_NO_ERROR, ChipLogError(Controller, "Unable to create Java ChipAttributePath: %s", ErrorStr(err)));

VerifyOrReturn(!aPath.IsListItemOperation(), ChipLogError(Controller, "Expect non-list item operation"); aPath.LogPath());
VerifyOrReturn(aStatus.IsSuccess(), ChipLogError(Controller, "Receive bad status %s", ErrorStr(aStatus.ToChipError()));
aPath.LogPath());

jobject nodeState = mNodeStateObj.ObjectRef();
if (aStatus.IsFailure())
{
ChipLogError(Controller, "Receive bad status %s", ErrorStr(aStatus.ToChipError()));
jobject statusObj = nullptr;
err = CreateStatus(env, aStatus, statusObj);
VerifyOrReturn(err == CHIP_NO_ERROR,
ChipLogError(Controller, "Fail to create status with error %" CHIP_ERROR_FORMAT, err.Format()));
// Add Attribute Status to NodeState
jmethodID addAttributeStatusMethod = nullptr;
err = JniReferences::GetInstance().FindMethod(env, nodeState, "addAttributeStatus",
"(IJJLchip/devicecontroller/model/Status;)V", &addAttributeStatusMethod);
VerifyOrReturn(
err == CHIP_NO_ERROR,
ChipLogError(Controller, "Could not find addAttributeStatus method with error %" CHIP_ERROR_FORMAT, err.Format()));
env->CallVoidMethod(nodeState, addAttributeStatusMethod, static_cast<jint>(aPath.mEndpointId),
static_cast<jlong>(aPath.mClusterId), static_cast<jlong>(aPath.mAttributeId), statusObj);
VerifyOrReturn(!env->ExceptionCheck(), env->ExceptionDescribe());
return;
}
VerifyOrReturn(apData != nullptr, ChipLogError(Controller, "Receive empty apData"); aPath.LogPath());

TLV::TLVReader readerForJavaTLV;
Expand Down Expand Up @@ -345,7 +367,6 @@ void ReportCallback::OnAttributeData(const app::ConcreteDataAttributePath & aPat
VerifyOrReturn(attributeStateObj != nullptr, ChipLogError(Controller, "Could not create AttributeState object");
aPath.LogPath());

jobject nodeState = mNodeStateObj.ObjectRef();
// Add AttributeState to NodeState
jmethodID addAttributeMethod;
err = JniReferences::GetInstance().FindMethod(env, nodeState, "addAttribute",
Expand Down Expand Up @@ -401,10 +422,28 @@ void ReportCallback::OnEventData(const app::EventHeader & aEventHeader, TLV::TLV
CHIP_ERROR err = CHIP_NO_ERROR;
JNIEnv * env = JniReferences::GetInstance().GetEnvForCurrentThread();
VerifyOrReturn(env != nullptr, ChipLogError(Controller, "Could not get JNIEnv for current thread"));
jobject eventPathObj = nullptr;
err = CreateChipEventPath(env, aEventHeader.mPath, eventPathObj);
VerifyOrReturn(err == CHIP_NO_ERROR, ChipLogError(Controller, "Unable to create Java ChipEventPath: %s", ErrorStr(err)));

jobject nodeState = mNodeStateObj.ObjectRef();
if (apStatus != nullptr && apStatus->IsFailure())
{
ChipLogError(Controller, "Receive bad status %s", ErrorStr(apStatus->ToChipError()));
jobject statusObj = nullptr;
err = CreateStatus(env, *apStatus, statusObj);
VerifyOrReturn(err == CHIP_NO_ERROR,
ChipLogError(Controller, "Fail to create status with error %" CHIP_ERROR_FORMAT, err.Format()));
// Add Event Status to NodeState
jmethodID addEventStatusMethod;
err = JniReferences::GetInstance().FindMethod(env, nodeState, "addEventStatus",
"(IJJLchip/devicecontroller/model/Status;)V", &addEventStatusMethod);
VerifyOrReturn(
err == CHIP_NO_ERROR,
ChipLogError(Controller, "Could not find addEventStatus method with error %" CHIP_ERROR_FORMAT, err.Format()));
env->CallVoidMethod(nodeState, addEventStatusMethod, static_cast<jint>(aEventHeader.mPath.mEndpointId),
static_cast<jlong>(aEventHeader.mPath.mClusterId), static_cast<jlong>(aEventHeader.mPath.mEventId),
statusObj);
VerifyOrReturn(!env->ExceptionCheck(), env->ExceptionDescribe());
return;
}
VerifyOrReturn(apData != nullptr, ChipLogError(Controller, "Receive empty apData"); aEventHeader.LogPath());

TLV::TLVReader readerForJavaTLV;
Expand Down Expand Up @@ -484,7 +523,7 @@ void ReportCallback::OnEventData(const app::EventHeader & aEventHeader, TLV::TLV

// Add EventState to NodeState
jmethodID addEventMethod;
jobject nodeState = mNodeStateObj.ObjectRef();

err = JniReferences::GetInstance().FindMethod(env, nodeState, "addEvent", "(IJJLchip/devicecontroller/model/EventState;)V",
&addEventMethod);
VerifyOrReturn(err == CHIP_NO_ERROR, ChipLogError(Controller, "Could not find addEvent method with error %s", ErrorStr(err));
Expand Down
1 change: 1 addition & 0 deletions src/controller/java/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -482,6 +482,7 @@ android_library("java") {
"src/chip/devicecontroller/model/EventState.java",
"src/chip/devicecontroller/model/InvokeElement.java",
"src/chip/devicecontroller/model/NodeState.java",
"src/chip/devicecontroller/model/Status.java",
]

if (matter_enable_tlv_decoder_api) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,23 +32,38 @@ public final class ClusterState {
private static final String TAG = "ClusterState";
private Map<Long, AttributeState> attributes;
private Map<Long, ArrayList<EventState>> events;
private Map<Long, Status> attributeStatuses;
private Map<Long, ArrayList<Status>> eventStatuses;
private Optional<Long> dataVersion;

public ClusterState(
Map<Long, AttributeState> attributes, Map<Long, ArrayList<EventState>> events) {
protected ClusterState(
Map<Long, AttributeState> attributes,
Map<Long, ArrayList<EventState>> events,
Map<Long, Status> attributeStatuses,
Map<Long, ArrayList<Status>> eventStatuses) {
this.attributes = attributes;
this.events = events;
this.attributeStatuses = attributeStatuses;
this.eventStatuses = eventStatuses;
this.dataVersion = Optional.empty();
}

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

public Map<Long, Status> getAttributeStatuses() {
return attributeStatuses;
}

public Map<Long, ArrayList<EventState>> getEventStates() {
return events;
}

public Map<Long, ArrayList<Status>> getEventStatuses() {
return eventStatuses;
}

public void setDataVersion(long version) {
dataVersion = Optional.of(version);
}
Expand Down Expand Up @@ -130,6 +145,25 @@ public String toString() {
builder.append("\n");
});
});
attributeStatuses.forEach(
(attributeId, status) -> {
builder.append("Attribute Status ");
builder.append(attributeId);
builder.append(": ");
builder.append(status.toString());
builder.append("\n");
});
eventStatuses.forEach(
(eventId, status) -> {
status.forEach(
(eventState) -> {
builder.append("Event Status");
builder.append(eventId);
builder.append(": ");
builder.append(status.toString());
builder.append("\n");
});
});
return builder.toString();
}
}
63 changes: 61 additions & 2 deletions src/controller/java/src/chip/devicecontroller/model/NodeState.java
Original file line number Diff line number Diff line change
Expand Up @@ -55,10 +55,15 @@ private void addAttribute(

ClusterState clusterState = endpointState.getClusterState(clusterId);
if (clusterState == null) {
clusterState = new ClusterState(new HashMap<>(), new HashMap<>());
clusterState =
new ClusterState(new HashMap<>(), new HashMap<>(), new HashMap<>(), new HashMap<>());
endpointState.getClusterStates().put(clusterId, clusterState);
}

if (clusterState.getAttributeStatuses().containsKey(attributeId)) {
clusterState.getAttributeStatuses().remove(attributeId);
}

// This will overwrite previous attributes.
clusterState.getAttributeStates().put(attributeId, attributeStateToAdd);
}
Expand All @@ -72,16 +77,70 @@ private void addEvent(int endpointId, long clusterId, long eventId, EventState e

ClusterState clusterState = endpointState.getClusterState(clusterId);
if (clusterState == null) {
clusterState = new ClusterState(new HashMap<>(), new HashMap<>());
clusterState =
new ClusterState(new HashMap<>(), new HashMap<>(), new HashMap<>(), new HashMap<>());
endpointState.getClusterStates().put(clusterId, clusterState);
}

if (!clusterState.getEventStates().containsKey(eventId)) {
clusterState.getEventStates().put(eventId, new ArrayList<EventState>());
}

if (clusterState.getEventStatuses().containsKey(eventId)) {
clusterState.getEventStatuses().remove(eventId);
}

clusterState.getEventStates().get(eventId).add(eventStateToAdd);
}

// Called from native code only, which ignores access modifiers.
private void addAttributeStatus(
int endpointId, long clusterId, long attributeId, Status statusToAdd) {
EndpointState endpointState = getEndpointState(endpointId);
if (endpointState == null) {
endpointState = new EndpointState(new HashMap<>());
getEndpointStates().put(endpointId, endpointState);
}

ClusterState clusterState = endpointState.getClusterState(clusterId);
if (clusterState == null) {
clusterState =
new ClusterState(new HashMap<>(), new HashMap<>(), new HashMap<>(), new HashMap<>());
endpointState.getClusterStates().put(clusterId, clusterState);
}

if (clusterState.getAttributeStates().containsKey(attributeId)) {
clusterState.getAttributeStates().remove(attributeId);
}

clusterState.getAttributeStatuses().put(attributeId, statusToAdd);
}

private void addEventStatus(int endpointId, long clusterId, long eventId, Status statusToAdd) {
EndpointState endpointState = getEndpointState(endpointId);
if (endpointState == null) {
endpointState = new EndpointState(new HashMap<>());
getEndpointStates().put(endpointId, endpointState);
}

ClusterState clusterState = endpointState.getClusterState(clusterId);
if (clusterState == null) {
clusterState =
new ClusterState(new HashMap<>(), new HashMap<>(), new HashMap<>(), new HashMap<>());
endpointState.getClusterStates().put(clusterId, clusterState);
}

if (!clusterState.getEventStatuses().containsKey(eventId)) {
clusterState.getEventStatuses().put(eventId, new ArrayList<Status>());
}

if (clusterState.getEventStates().containsKey(eventId)) {
clusterState.getEventStates().remove(eventId);
}

clusterState.getEventStatuses().get(eventId).add(statusToAdd);
}

@Override
public String toString() {
StringBuilder builder = new StringBuilder();
Expand Down
Loading

0 comments on commit f216481

Please sign in to comment.