Skip to content

Commit ecca441

Browse files
[Android] Add Status handling for onReport code path
1 parent 7cc357e commit ecca441

File tree

6 files changed

+251
-41
lines changed

6 files changed

+251
-41
lines changed

examples/java-matter-controller/java/src/com/matter/controller/commands/pairing/PairOnNetworkLongImReadCommand.kt

+29-9
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import chip.devicecontroller.model.ChipPathId
1010
import chip.devicecontroller.model.DataVersionFilter
1111
import chip.devicecontroller.model.EventState
1212
import chip.devicecontroller.model.NodeState
13+
import chip.devicecontroller.model.Status
1314
import com.matter.controller.commands.common.CredentialsIssuer
1415
import java.util.logging.Level
1516
import java.util.logging.Logger
@@ -34,13 +35,6 @@ class PairOnNetworkLongImReadCommand(
3435
eventPath: ChipEventPath?,
3536
e: Exception
3637
) {
37-
if (attributePath != null && attributePath.clusterId.getId() == UNIT_TEST_CLUSTER) {
38-
logger.log(
39-
Level.INFO,
40-
"TODO: skip the error check for unit test cluster that covers most error result"
41-
)
42-
return
43-
}
4438
logger.log(Level.INFO, "Read receive onError")
4539
setFailure("read failure")
4640
}
@@ -72,11 +66,19 @@ class PairOnNetworkLongImReadCommand(
7266
return cluster.equals(expected)
7367
}
7468

69+
fun checkUnitTestClusterGeneralStatus(status: Status): Boolean =
70+
(status.getStatus() == CLUSTER_ID_TEST_GENERAL_ERROR_STATUS)
71+
&& !status.getClusterStatus().isPresent();
72+
73+
fun checkUnitTestClusterClusterStatus(status: Status): Boolean =
74+
(status.getStatus() == CLUSTER_ID_TEST_CLUSTER_ERROR_STATUS)
75+
&& status.getClusterStatus().isPresent() && status.getClusterStatus().get() == CLUSTER_ID_TEST_CLUSTER_ERROR_CLUSTER_STATUS;
76+
7577
private fun validateResponse(nodeState: NodeState) {
7678
val endpointZero =
7779
requireNotNull(nodeState.getEndpointState(0)) { "Endpoint zero not found." }
7880

79-
val endpointOne = requireNotNull(nodeState.getEndpointState(0)) { "Endpoint one not found." }
81+
val endpointOne = requireNotNull(nodeState.getEndpointState(1)) { "Endpoint one not found." }
8082

8183
val basicCluster =
8284
requireNotNull(endpointZero.getClusterState(CLUSTER_ID_BASIC)) {
@@ -93,6 +95,11 @@ class PairOnNetworkLongImReadCommand(
9395
"No local config disabled attribute found."
9496
}
9597

98+
val unitTestCluster =
99+
requireNotNull(endpointOne.getClusterState(UNIT_TEST_CLUSTER)) {
100+
"unit test cluster not found."
101+
}
102+
96103
val startUpEvents =
97104
requireNotNull(basicCluster.getEventState(EVENT_ID_START_UP)) { "No start up event found." }
98105

@@ -122,6 +129,14 @@ class PairOnNetworkLongImReadCommand(
122129
require(checkAllAttributesJsonForFixedLabel(clusterAttributes)) {
123130
"Invalid fixed label cluster attributes Json ${clusterAttributes}"
124131
}
132+
133+
require(checkUnitTestClusterGeneralStatus(unitTestCluster.getAttributeStatuses()[CLUSTER_ID_TEST_GENERAL_ERROR_BOOLEAN]!!)) {
134+
"Invalid unit test cluster generalStatus check ${unitTestCluster}"
135+
}
136+
137+
require(checkUnitTestClusterClusterStatus(unitTestCluster.getAttributeStatuses()[CLUSTER_ID_TEST_CLUSTER_ERROR_BOOLEAN]!!)) {
138+
"Invalid unit test cluster clusterStatus check ${unitTestCluster}"
139+
}
125140
}
126141

127142
override fun onReport(nodeState: NodeState) {
@@ -212,10 +227,15 @@ class PairOnNetworkLongImReadCommand(
212227
private const val MATTER_PORT = 5540
213228
private const val CLUSTER_ID_BASIC = 0x0028L
214229
private const val FIXED_LABEL_CLUSTER = 0x0040L
215-
private const val UNIT_TEST_CLUSTER = 0xfff1fc05
230+
private const val UNIT_TEST_CLUSTER = 0xfff1fc05L
216231
private const val ATTR_ID_LOCAL_CONFIG_DISABLED = 16L
217232
private const val EVENT_ID_START_UP = 0L
218233
private const val GLOBAL_ATTRIBUTE_LIST = 65531L
219234
private const val CLUSTER_ID_BASIC_VERSION = 0L
235+
private const val CLUSTER_ID_TEST_GENERAL_ERROR_BOOLEAN = 0x0031L
236+
private const val CLUSTER_ID_TEST_CLUSTER_ERROR_BOOLEAN = 0x0032L
237+
private const val CLUSTER_ID_TEST_GENERAL_ERROR_STATUS = 0x8d
238+
private const val CLUSTER_ID_TEST_CLUSTER_ERROR_STATUS = 1
239+
private const val CLUSTER_ID_TEST_CLUSTER_ERROR_CLUSTER_STATUS = 17
220240
}
221241
}

src/controller/java/AndroidCallbacks.cpp

+67-28
Original file line numberDiff line numberDiff line change
@@ -55,22 +55,6 @@ CHIP_ERROR CreateChipAttributePath(JNIEnv * env, const app::ConcreteDataAttribut
5555
return CHIP_NO_ERROR;
5656
}
5757

58-
CHIP_ERROR ReportCallback::CreateChipEventPath(JNIEnv * env, const app::ConcreteEventPath & aPath, jobject & outObj)
59-
{
60-
jclass eventPathCls = nullptr;
61-
ReturnErrorOnFailure(
62-
JniReferences::GetInstance().GetLocalClassRef(env, "chip/devicecontroller/model/ChipEventPath", eventPathCls));
63-
64-
jmethodID eventPathCtor =
65-
env->GetStaticMethodID(eventPathCls, "newInstance", "(IJJ)Lchip/devicecontroller/model/ChipEventPath;");
66-
VerifyOrReturnError(eventPathCtor != nullptr, CHIP_JNI_ERROR_METHOD_NOT_FOUND);
67-
68-
outObj = env->CallStaticObjectMethod(eventPathCls, eventPathCtor, static_cast<jint>(aPath.mEndpointId),
69-
static_cast<jlong>(aPath.mClusterId), static_cast<jlong>(aPath.mEventId));
70-
VerifyOrReturnError(outObj != nullptr, CHIP_JNI_ERROR_NULL_OBJECT);
71-
return CHIP_NO_ERROR;
72-
}
73-
7458
GetConnectedDeviceCallback::GetConnectedDeviceCallback(jobject wrapperCallback, jobject javaCallback) :
7559
mOnSuccess(OnDeviceConnectedFn, this), mOnFailure(OnDeviceConnectionFailureFn, this)
7660
{
@@ -270,21 +254,59 @@ CHIP_ERROR ConvertReportTlvToJson(const uint32_t id, TLV::TLVReader & data, std:
270254
return TlvToJson(readerForJson, json);
271255
}
272256

257+
static CHIP_ERROR CreateStatus(JNIEnv * env, const app::StatusIB & aStatus, jobject & outObj)
258+
{
259+
jclass statusCls = nullptr;
260+
ReturnErrorOnFailure(JniReferences::GetInstance().GetLocalClassRef(env, "chip/devicecontroller/model/Status", statusCls));
261+
jmethodID statusCtor = nullptr;
262+
if (aStatus.mClusterStatus.HasValue())
263+
{
264+
statusCtor = env->GetStaticMethodID(statusCls, "newInstance", "(II)Lchip/devicecontroller/model/Status;");
265+
VerifyOrReturnError(!env->ExceptionCheck(), CHIP_JNI_ERROR_EXCEPTION_THROWN);
266+
VerifyOrReturnError(statusCtor != nullptr, CHIP_JNI_ERROR_METHOD_NOT_FOUND);
267+
outObj = env->CallStaticObjectMethod(statusCls, statusCtor, static_cast<jint>(aStatus.mStatus),
268+
static_cast<jint>(aStatus.mClusterStatus.Value()));
269+
}
270+
else
271+
{
272+
statusCtor = env->GetStaticMethodID(statusCls, "newInstance", "(I)Lchip/devicecontroller/model/Status;");
273+
VerifyOrReturnError(!env->ExceptionCheck(), CHIP_JNI_ERROR_EXCEPTION_THROWN);
274+
VerifyOrReturnError(statusCtor != nullptr, CHIP_JNI_ERROR_METHOD_NOT_FOUND);
275+
outObj = env->CallStaticObjectMethod(statusCls, statusCtor, static_cast<jint>(aStatus.mStatus));
276+
}
277+
VerifyOrReturnError(outObj != nullptr, CHIP_JNI_ERROR_METHOD_NOT_FOUND);
278+
return CHIP_NO_ERROR;
279+
}
280+
273281
void ReportCallback::OnAttributeData(const app::ConcreteDataAttributePath & aPath, TLV::TLVReader * apData,
274282
const app::StatusIB & aStatus)
275283
{
276284
CHIP_ERROR err = CHIP_NO_ERROR;
277285
JNIEnv * env = JniReferences::GetInstance().GetEnvForCurrentThread();
278286
VerifyOrReturn(env != nullptr, ChipLogError(Controller, "Could not get JNIEnv for current thread"));
279287
JniLocalReferenceScope scope(env);
280-
281-
jobject attributePathObj = nullptr;
282-
err = CreateChipAttributePath(env, aPath, attributePathObj);
283-
VerifyOrReturn(err == CHIP_NO_ERROR, ChipLogError(Controller, "Unable to create Java ChipAttributePath: %s", ErrorStr(err)));
284-
285288
VerifyOrReturn(!aPath.IsListItemOperation(), ChipLogError(Controller, "Expect non-list item operation"); aPath.LogPath());
286-
VerifyOrReturn(aStatus.IsSuccess(), ChipLogError(Controller, "Receive bad status %s", ErrorStr(aStatus.ToChipError()));
287-
aPath.LogPath());
289+
290+
jobject nodeState = mNodeStateObj.ObjectRef();
291+
if (aStatus.IsFailure())
292+
{
293+
ChipLogError(Controller, "Receive bad status %s", ErrorStr(aStatus.ToChipError()));
294+
jobject statusObj = nullptr;
295+
err = CreateStatus(env, aStatus, statusObj);
296+
VerifyOrReturn(err == CHIP_NO_ERROR,
297+
ChipLogError(Controller, "Fail to create status with error %" CHIP_ERROR_FORMAT, err.Format()));
298+
// Add Attribute Status to NodeState
299+
jmethodID addAttributeStatusMethod = nullptr;
300+
err = JniReferences::GetInstance().FindMethod(env, nodeState, "addAttributeStatus",
301+
"(IJJLchip/devicecontroller/model/Status;)V", &addAttributeStatusMethod);
302+
VerifyOrReturn(
303+
err == CHIP_NO_ERROR,
304+
ChipLogError(Controller, "Could not find addAttributeStatus method with error %" CHIP_ERROR_FORMAT, err.Format()));
305+
env->CallVoidMethod(nodeState, addAttributeStatusMethod, static_cast<jint>(aPath.mEndpointId),
306+
static_cast<jlong>(aPath.mClusterId), static_cast<jlong>(aPath.mAttributeId), statusObj);
307+
VerifyOrReturn(!env->ExceptionCheck(), env->ExceptionDescribe());
308+
return;
309+
}
288310
VerifyOrReturn(apData != nullptr, ChipLogError(Controller, "Receive empty apData"); aPath.LogPath());
289311

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

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

426+
jobject nodeState = mNodeStateObj.ObjectRef();
427+
if (apStatus != nullptr && apStatus->IsFailure())
428+
{
429+
ChipLogError(Controller, "Receive bad status %s", ErrorStr(apStatus->ToChipError()));
430+
jobject statusObj = nullptr;
431+
err = CreateStatus(env, *apStatus, statusObj);
432+
VerifyOrReturn(err == CHIP_NO_ERROR,
433+
ChipLogError(Controller, "Fail to create status with error %" CHIP_ERROR_FORMAT, err.Format()));
434+
// Add Event Status to NodeState
435+
jmethodID addEventStatusMethod;
436+
err = JniReferences::GetInstance().FindMethod(env, nodeState, "addEventStatus",
437+
"(IJJLchip/devicecontroller/model/Status;)V", &addEventStatusMethod);
438+
VerifyOrReturn(
439+
err == CHIP_NO_ERROR,
440+
ChipLogError(Controller, "Could not find addEventStatus method with error %" CHIP_ERROR_FORMAT, err.Format()));
441+
env->CallVoidMethod(nodeState, addEventStatusMethod, static_cast<jint>(aEventHeader.mPath.mEndpointId),
442+
static_cast<jlong>(aEventHeader.mPath.mClusterId), static_cast<jlong>(aEventHeader.mPath.mEventId),
443+
statusObj);
444+
VerifyOrReturn(!env->ExceptionCheck(), env->ExceptionDescribe());
445+
return;
446+
}
408447
VerifyOrReturn(apData != nullptr, ChipLogError(Controller, "Receive empty apData"); aEventHeader.LogPath());
409448

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

485524
// Add EventState to NodeState
486525
jmethodID addEventMethod;
487-
jobject nodeState = mNodeStateObj.ObjectRef();
526+
488527
err = JniReferences::GetInstance().FindMethod(env, nodeState, "addEvent", "(IJJLchip/devicecontroller/model/EventState;)V",
489528
&addEventMethod);
490529
VerifyOrReturn(err == CHIP_NO_ERROR, ChipLogError(Controller, "Could not find addEvent method with error %s", ErrorStr(err));

src/controller/java/BUILD.gn

+1
Original file line numberDiff line numberDiff line change
@@ -482,6 +482,7 @@ android_library("java") {
482482
"src/chip/devicecontroller/model/EventState.java",
483483
"src/chip/devicecontroller/model/InvokeElement.java",
484484
"src/chip/devicecontroller/model/NodeState.java",
485+
"src/chip/devicecontroller/model/Status.java",
485486
]
486487

487488
if (matter_enable_tlv_decoder_api) {

src/controller/java/src/chip/devicecontroller/model/ClusterState.java

+36-2
Original file line numberDiff line numberDiff line change
@@ -32,23 +32,38 @@ public final class ClusterState {
3232
private static final String TAG = "ClusterState";
3333
private Map<Long, AttributeState> attributes;
3434
private Map<Long, ArrayList<EventState>> events;
35+
private Map<Long, Status> attributeStatuses;
36+
private Map<Long, ArrayList<Status>> eventStatuses;
3537
private Optional<Long> dataVersion;
3638

37-
public ClusterState(
38-
Map<Long, AttributeState> attributes, Map<Long, ArrayList<EventState>> events) {
39+
protected ClusterState(
40+
Map<Long, AttributeState> attributes,
41+
Map<Long, ArrayList<EventState>> events,
42+
Map<Long, Status> attributeStatuses,
43+
Map<Long, ArrayList<Status>> eventStatuses) {
3944
this.attributes = attributes;
4045
this.events = events;
46+
this.attributeStatuses = attributeStatuses;
47+
this.eventStatuses = eventStatuses;
4148
this.dataVersion = Optional.empty();
4249
}
4350

4451
public Map<Long, AttributeState> getAttributeStates() {
4552
return attributes;
4653
}
4754

55+
public Map<Long, Status> getAttributeStatuses() {
56+
return attributeStatuses;
57+
}
58+
4859
public Map<Long, ArrayList<EventState>> getEventStates() {
4960
return events;
5061
}
5162

63+
public Map<Long, ArrayList<Status>> getEventStatuses() {
64+
return eventStatuses;
65+
}
66+
5267
public void setDataVersion(long version) {
5368
dataVersion = Optional.of(version);
5469
}
@@ -130,6 +145,25 @@ public String toString() {
130145
builder.append("\n");
131146
});
132147
});
148+
attributeStatuses.forEach(
149+
(attributeId, status) -> {
150+
builder.append("Attribute Status ");
151+
builder.append(attributeId);
152+
builder.append(": ");
153+
builder.append(status.toString());
154+
builder.append("\n");
155+
});
156+
eventStatuses.forEach(
157+
(eventId, status) -> {
158+
status.forEach(
159+
(eventState) -> {
160+
builder.append("Event Status");
161+
builder.append(eventId);
162+
builder.append(": ");
163+
builder.append(status.toString());
164+
builder.append("\n");
165+
});
166+
});
133167
return builder.toString();
134168
}
135169
}

src/controller/java/src/chip/devicecontroller/model/NodeState.java

+61-2
Original file line numberDiff line numberDiff line change
@@ -55,10 +55,15 @@ private void addAttribute(
5555

5656
ClusterState clusterState = endpointState.getClusterState(clusterId);
5757
if (clusterState == null) {
58-
clusterState = new ClusterState(new HashMap<>(), new HashMap<>());
58+
clusterState =
59+
new ClusterState(new HashMap<>(), new HashMap<>(), new HashMap<>(), new HashMap<>());
5960
endpointState.getClusterStates().put(clusterId, clusterState);
6061
}
6162

63+
if (clusterState.getAttributeStatuses().containsKey(attributeId)) {
64+
clusterState.getAttributeStatuses().remove(attributeId);
65+
}
66+
6267
// This will overwrite previous attributes.
6368
clusterState.getAttributeStates().put(attributeId, attributeStateToAdd);
6469
}
@@ -72,16 +77,70 @@ private void addEvent(int endpointId, long clusterId, long eventId, EventState e
7277

7378
ClusterState clusterState = endpointState.getClusterState(clusterId);
7479
if (clusterState == null) {
75-
clusterState = new ClusterState(new HashMap<>(), new HashMap<>());
80+
clusterState =
81+
new ClusterState(new HashMap<>(), new HashMap<>(), new HashMap<>(), new HashMap<>());
7682
endpointState.getClusterStates().put(clusterId, clusterState);
7783
}
7884

7985
if (!clusterState.getEventStates().containsKey(eventId)) {
8086
clusterState.getEventStates().put(eventId, new ArrayList<EventState>());
8187
}
88+
89+
if (clusterState.getEventStatuses().containsKey(eventId)) {
90+
clusterState.getEventStatuses().remove(eventId);
91+
}
92+
8293
clusterState.getEventStates().get(eventId).add(eventStateToAdd);
8394
}
8495

96+
// Called from native code only, which ignores access modifiers.
97+
private void addAttributeStatus(
98+
int endpointId, long clusterId, long attributeId, Status statusToAdd) {
99+
EndpointState endpointState = getEndpointState(endpointId);
100+
if (endpointState == null) {
101+
endpointState = new EndpointState(new HashMap<>());
102+
getEndpointStates().put(endpointId, endpointState);
103+
}
104+
105+
ClusterState clusterState = endpointState.getClusterState(clusterId);
106+
if (clusterState == null) {
107+
clusterState =
108+
new ClusterState(new HashMap<>(), new HashMap<>(), new HashMap<>(), new HashMap<>());
109+
endpointState.getClusterStates().put(clusterId, clusterState);
110+
}
111+
112+
if (clusterState.getAttributeStates().containsKey(attributeId)) {
113+
clusterState.getAttributeStates().remove(attributeId);
114+
}
115+
116+
clusterState.getAttributeStatuses().put(attributeId, statusToAdd);
117+
}
118+
119+
private void addEventStatus(int endpointId, long clusterId, long eventId, Status statusToAdd) {
120+
EndpointState endpointState = getEndpointState(endpointId);
121+
if (endpointState == null) {
122+
endpointState = new EndpointState(new HashMap<>());
123+
getEndpointStates().put(endpointId, endpointState);
124+
}
125+
126+
ClusterState clusterState = endpointState.getClusterState(clusterId);
127+
if (clusterState == null) {
128+
clusterState =
129+
new ClusterState(new HashMap<>(), new HashMap<>(), new HashMap<>(), new HashMap<>());
130+
endpointState.getClusterStates().put(clusterId, clusterState);
131+
}
132+
133+
if (!clusterState.getEventStatuses().containsKey(eventId)) {
134+
clusterState.getEventStatuses().put(eventId, new ArrayList<Status>());
135+
}
136+
137+
if (clusterState.getEventStates().containsKey(eventId)) {
138+
clusterState.getEventStates().remove(eventId);
139+
}
140+
141+
clusterState.getEventStatuses().get(eventId).add(statusToAdd);
142+
}
143+
85144
@Override
86145
public String toString() {
87146
StringBuilder builder = new StringBuilder();

0 commit comments

Comments
 (0)