Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Android] Add Status handling for onReport code path #32082

Merged
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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."
yunhanw-google marked this conversation as resolved.
Show resolved Hide resolved
}

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();
}
}
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
Loading