Skip to content

Commit 2b8c71b

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

File tree

6 files changed

+255
-41
lines changed

6 files changed

+255
-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

+65-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,60 @@ 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",
265+
"(II)Lchip/devicecontroller/model/Status;");
266+
VerifyOrReturnError(!env->ExceptionCheck(), CHIP_JNI_ERROR_EXCEPTION_THROWN);
267+
VerifyOrReturnError(statusCtor != nullptr, CHIP_JNI_ERROR_METHOD_NOT_FOUND);
268+
outObj = env->CallStaticObjectMethod(statusCls, statusCtor, static_cast<jint>(aStatus.mStatus),
269+
static_cast<jint>(aStatus.mClusterStatus.Value()));
270+
}
271+
else
272+
{
273+
statusCtor = env->GetStaticMethodID(statusCls, "newInstance",
274+
"(I)Lchip/devicecontroller/model/Status;");
275+
VerifyOrReturnError(!env->ExceptionCheck(), CHIP_JNI_ERROR_EXCEPTION_THROWN);
276+
VerifyOrReturnError(statusCtor != nullptr, CHIP_JNI_ERROR_METHOD_NOT_FOUND);
277+
outObj = env->CallStaticObjectMethod(statusCls, statusCtor, static_cast<jint>(aStatus.mStatus));
278+
}
279+
VerifyOrReturnError(outObj != nullptr, CHIP_JNI_ERROR_METHOD_NOT_FOUND);
280+
return CHIP_NO_ERROR;
281+
}
282+
283+
273284
void ReportCallback::OnAttributeData(const app::ConcreteDataAttributePath & aPath, TLV::TLVReader * apData,
274285
const app::StatusIB & aStatus)
275286
{
276287
CHIP_ERROR err = CHIP_NO_ERROR;
277288
JNIEnv * env = JniReferences::GetInstance().GetEnvForCurrentThread();
278289
VerifyOrReturn(env != nullptr, ChipLogError(Controller, "Could not get JNIEnv for current thread"));
279290
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-
285291
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());
292+
293+
jobject nodeState = mNodeStateObj.ObjectRef();
294+
if (aStatus.IsFailure())
295+
{
296+
ChipLogError(Controller, "Receive bad status %s", ErrorStr(aStatus.ToChipError()));
297+
jobject statusObj = nullptr;
298+
err = CreateStatus(env, aStatus, statusObj);
299+
VerifyOrReturn(err == CHIP_NO_ERROR, ChipLogError(Controller, "Fail to create status with error %" CHIP_ERROR_FORMAT, err.Format()));
300+
// Add Attribute Status to NodeState
301+
jmethodID addAttributeStatusMethod = nullptr;
302+
err = JniReferences::GetInstance().FindMethod(env, nodeState, "addAttributeStatus",
303+
"(IJJLchip/devicecontroller/model/Status;)V", &addAttributeStatusMethod);
304+
VerifyOrReturn(err == CHIP_NO_ERROR,
305+
ChipLogError(Controller, "Could not find addAttributeStatus method with error %" CHIP_ERROR_FORMAT, err.Format()));
306+
env->CallVoidMethod(nodeState, addAttributeStatusMethod, static_cast<jint>(aPath.mEndpointId), static_cast<jlong>(aPath.mClusterId),
307+
static_cast<jlong>(aPath.mAttributeId), statusObj);
308+
VerifyOrReturn(!env->ExceptionCheck(), env->ExceptionDescribe());
309+
return;
310+
}
288311
VerifyOrReturn(apData != nullptr, ChipLogError(Controller, "Receive empty apData"); aPath.LogPath());
289312

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

348-
jobject nodeState = mNodeStateObj.ObjectRef();
349371
// Add AttributeState to NodeState
350372
jmethodID addAttributeMethod;
351373
err = JniReferences::GetInstance().FindMethod(env, nodeState, "addAttribute",
@@ -401,10 +423,25 @@ void ReportCallback::OnEventData(const app::EventHeader & aEventHeader, TLV::TLV
401423
CHIP_ERROR err = CHIP_NO_ERROR;
402424
JNIEnv * env = JniReferences::GetInstance().GetEnvForCurrentThread();
403425
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)));
407426

427+
jobject nodeState = mNodeStateObj.ObjectRef();
428+
if (apStatus != nullptr && apStatus->IsFailure())
429+
{
430+
ChipLogError(Controller, "Receive bad status %s", ErrorStr(apStatus->ToChipError()));
431+
jobject statusObj = nullptr;
432+
err = CreateStatus(env, *apStatus, statusObj);
433+
VerifyOrReturn(err == CHIP_NO_ERROR, 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(err == CHIP_NO_ERROR,
439+
ChipLogError(Controller, "Could not find addEventStatus method with error %" CHIP_ERROR_FORMAT, err.Format()));
440+
env->CallVoidMethod(nodeState, addEventStatusMethod, static_cast<jint>(aEventHeader.mPath.mEndpointId),
441+
static_cast<jlong>(aEventHeader.mPath.mClusterId), static_cast<jlong>(aEventHeader.mPath.mEventId), statusObj);
442+
VerifyOrReturn(!env->ExceptionCheck(), env->ExceptionDescribe());
443+
return;
444+
}
408445
VerifyOrReturn(apData != nullptr, ChipLogError(Controller, "Receive empty apData"); aEventHeader.LogPath());
409446

410447
TLV::TLVReader readerForJavaTLV;
@@ -484,7 +521,7 @@ void ReportCallback::OnEventData(const app::EventHeader & aEventHeader, TLV::TLV
484521

485522
// Add EventState to NodeState
486523
jmethodID addEventMethod;
487-
jobject nodeState = mNodeStateObj.ObjectRef();
524+
488525
err = JniReferences::GetInstance().FindMethod(env, nodeState, "addEvent", "(IJJLchip/devicecontroller/model/EventState;)V",
489526
&addEventMethod);
490527
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

+35-2
Original file line numberDiff line numberDiff line change
@@ -32,23 +32,35 @@ 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, Map<Long, ArrayList<EventState>> events, Map<Long, Status> attributeStatuses, Map<Long, ArrayList<Status>> eventStatuses) {
3941
this.attributes = attributes;
4042
this.events = events;
43+
this.attributeStatuses = attributeStatuses;
44+
this.eventStatuses = eventStatuses;
4145
this.dataVersion = Optional.empty();
4246
}
4347

4448
public Map<Long, AttributeState> getAttributeStates() {
4549
return attributes;
4650
}
4751

52+
public Map<Long, Status> getAttributeStatuses() {
53+
return attributeStatuses;
54+
}
55+
4856
public Map<Long, ArrayList<EventState>> getEventStates() {
4957
return events;
5058
}
5159

60+
public Map<Long, ArrayList<Status>> getEventStatuses() {
61+
return eventStatuses;
62+
}
63+
5264
public void setDataVersion(long version) {
5365
dataVersion = Optional.of(version);
5466
}
@@ -130,6 +142,27 @@ public String toString() {
130142
builder.append("\n");
131143
});
132144
});
145+
attributeStatuses.forEach(
146+
(attributeId, status) -> {
147+
builder.append("Attribute Status ");
148+
builder.append(attributeId);
149+
builder.append(": ");
150+
builder.append(
151+
status.toString());
152+
builder.append("\n");
153+
});
154+
eventStatuses.forEach(
155+
(eventId, status) -> {
156+
status.forEach(
157+
(eventState) -> {
158+
builder.append("Event Status");
159+
builder.append(eventId);
160+
builder.append(": ");
161+
builder.append(
162+
status.toString());
163+
builder.append("\n");
164+
});
165+
});
133166
return builder.toString();
134167
}
135168
}

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 = new ClusterState(new HashMap<>(), new HashMap<>(), new HashMap<>(), new HashMap<>());
5959
endpointState.getClusterStates().put(clusterId, clusterState);
6060
}
6161

62+
if (clusterState.getAttributeStatuses().containsKey(attributeId))
63+
{
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 = new ClusterState(new HashMap<>(), new HashMap<>(), new HashMap<>(), new HashMap<>());
7681
endpointState.getClusterStates().put(clusterId, clusterState);
7782
}
7883

7984
if (!clusterState.getEventStates().containsKey(eventId)) {
8085
clusterState.getEventStates().put(eventId, new ArrayList<EventState>());
8186
}
87+
88+
if (clusterState.getEventStatuses().containsKey(eventId))
89+
{
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 = new ClusterState(new HashMap<>(), new HashMap<>(), new HashMap<>(), new HashMap<>());
108+
endpointState.getClusterStates().put(clusterId, clusterState);
109+
}
110+
111+
if (clusterState.getAttributeStates().containsKey(attributeId))
112+
{
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 = new ClusterState(new HashMap<>(), new HashMap<>(), new HashMap<>(), new HashMap<>());
129+
endpointState.getClusterStates().put(clusterId, clusterState);
130+
}
131+
132+
if (!clusterState.getEventStatuses().containsKey(eventId)) {
133+
clusterState.getEventStatuses().put(eventId, new ArrayList<Status>());
134+
}
135+
136+
if (clusterState.getEventStates().containsKey(eventId))
137+
{
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)