Skip to content

Commit a09f5b3

Browse files
silabs-borislrzr
authored andcommitted
UIC-3222: Fix User interview
Now correctly interview ALL users on the first discovery (previously was only checking the first one due to a resolution error) All user checksum happens after all users & all credentials are reported
1 parent 9fd64a2 commit a09f5b3

File tree

2 files changed

+145
-19
lines changed

2 files changed

+145
-19
lines changed

applications/zpc/components/zwave_command_classes/src/zwave_command_class_user_credential.cpp

Lines changed: 106 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,101 @@ std::set<user_credential_slot_message_callback_t>
8383
user_credential_slot_message_callback; //NOSONAR - false positive since it is warped in a namespace
8484
} // namespace
8585

86+
/**
87+
* @brief Keep track of the interview state
88+
*
89+
* User interview start with a get on User 0 to get the first user.
90+
* We keep this node to store all the credentials that are not assigned to a user.
91+
*
92+
* So we need to set its reported in the first User Report otherwise the first GET will
93+
* never be marked as resolved.
94+
*
95+
* We also need to keep track of the next user to interview, so we can first get all the
96+
* credential for the current user before moving to the next one.
97+
*/
98+
namespace users_interview_state
99+
{
100+
// WARNING : Use functions instead of those variables
101+
// Next user ID to be interviewed
102+
user_credential_user_unique_id_t next_user_id = 0;
103+
// Special node for user 0
104+
// Used to resolve it in the report instead of in the get to prevent resolver issues
105+
attribute_store::attribute user_0_node = ATTRIBUTE_STORE_INVALID_NODE;
106+
107+
/**
108+
* @brief Mark an node as the user 0 node
109+
*
110+
* @param node Node to mark as user 0
111+
*/
112+
void set_user_0_node(attribute_store::attribute node)
113+
{
114+
sl_log_debug(LOG_TAG, "Starting interview for all users on the device.");
115+
user_0_node = node;
116+
}
117+
118+
/**
119+
* @brief Resolve user 0 if needed
120+
*/
121+
void resolve_user_0_node()
122+
{
123+
if (user_0_node == ATTRIBUTE_STORE_INVALID_NODE) {
124+
return;
125+
}
126+
127+
try {
128+
user_0_node.set_reported<user_credential_user_unique_id_t>(0);
129+
user_0_node = ATTRIBUTE_STORE_INVALID_NODE;
130+
} catch (const std::exception &e) {
131+
sl_log_error(LOG_TAG, "Error while setting user 0 node : %s", e.what());
132+
}
133+
}
134+
135+
/**
136+
* @brief Schedule the next user interview
137+
*
138+
* @param user_id User ID to interview next
139+
*/
140+
void schedule_next_user_interview(user_credential_user_unique_id_t user_id)
141+
{
142+
sl_log_debug(LOG_TAG, "Schedule a get for next User ID :%d", next_user_id);
143+
next_user_id = user_id;
144+
}
145+
146+
/**
147+
* @brief Trigger a get for the next user
148+
*
149+
* If there is not next user, we will trigger the all users checksum get
150+
* (if supported)
151+
*
152+
* @param endpoint_node Current endpoint node
153+
*/
154+
void trigger_get_for_next_user(attribute_store::attribute endpoint_node)
155+
{
156+
// If we get here it means that we have finished the interview for users and
157+
// their credentials
158+
if (next_user_id == 0) {
159+
sl_log_debug(LOG_TAG, "User interview finished");
160+
// Check if we supports all users checksum to compute it
161+
user_credential::user_capabilities capabilities(endpoint_node);
162+
if (capabilities.is_all_users_checksum_supported()) {
163+
// This will do nothing if the node already exists
164+
// Otherwise it will trigger all_users_checksum_get
165+
endpoint_node.emplace_node(ATTRIBUTE(ALL_USERS_CHECKSUM));
166+
}
167+
return;
168+
}
169+
170+
sl_log_debug(LOG_TAG, "Trigger a get for next User ID :%d", next_user_id);
171+
172+
endpoint_node.emplace_node(ATTRIBUTE(USER_UNIQUE_ID),
173+
next_user_id,
174+
DESIRED_ATTRIBUTE);
175+
176+
// Reset state
177+
next_user_id = 0;
178+
}
179+
} // namespace users_interview_state
180+
86181
/////////////////////////////////////////////////////////////////////////////
87182
// Callbacks
88183
/////////////////////////////////////////////////////////////////////////////
@@ -1036,6 +1131,8 @@ sl_status_t zwave_command_class_user_credential_credential_handle_report(
10361131
next_credential_slot);
10371132
} else {
10381133
sl_log_debug(LOG_TAG, "No more credential to get.");
1134+
// Trigger the next user interview if any
1135+
users_interview_state::trigger_get_for_next_user(endpoint_node);
10391136
}
10401137

10411138
} catch (const std::exception &e) {
@@ -1523,9 +1620,7 @@ static sl_status_t zwave_command_class_user_credential_user_get(
15231620

15241621
// This special user ID will contains the unaffected credentials.
15251622
if (user_id == 0) {
1526-
sl_log_debug(LOG_TAG, "Starting interview for all users on the device.");
1527-
user_unique_id_node.clear_desired();
1528-
user_unique_id_node.set_reported(user_id);
1623+
users_interview_state::set_user_0_node(user_unique_id_node);
15291624
}
15301625

15311626
return SL_STATUS_OK;
@@ -1553,6 +1648,10 @@ sl_status_t zwave_command_class_user_credential_user_handle_report(
15531648
attribute_store::attribute endpoint_node(
15541649
zwave_command_class_get_endpoint_node(connection_info));
15551650

1651+
// Resolve user 0 node if needed
1652+
// We need to do that here so that the attribute store mark this node as resolved
1653+
users_interview_state::resolve_user_0_node();
1654+
15561655
const uint8_t expected_min_size = 16;
15571656

15581657
try {
@@ -1699,23 +1798,16 @@ sl_status_t zwave_command_class_user_credential_user_handle_report(
16991798
if (next_user_id != 0
17001799
&& user_report_type == user_report_type_t::RESPONSE_TO_GET) {
17011800
if (!user_exists(endpoint_node, next_user_id)) {
1702-
sl_log_debug(LOG_TAG, "Trigger a get for next user (%d)", next_user_id);
1703-
endpoint_node.add_node(ATTRIBUTE(USER_UNIQUE_ID))
1704-
.set_desired(next_user_id);
1801+
users_interview_state::schedule_next_user_interview(next_user_id);
17051802
} else {
17061803
sl_log_error(LOG_TAG,
17071804
"User %d already exists. Not discovering more users.",
17081805
next_user_id);
17091806
}
17101807
} else {
1711-
sl_log_debug(LOG_TAG, "No more users to discover");
1712-
// Check if we supports all users checksum to compute it
1713-
user_credential::user_capabilities capabilities(endpoint_node);
1714-
if (capabilities.is_all_users_checksum_supported()) {
1715-
// This will do nothing if the node already exists
1716-
// Otherwise it will trigger all_users_checksum_get
1717-
endpoint_node.emplace_node(ATTRIBUTE(ALL_USERS_CHECKSUM));
1718-
}
1808+
sl_log_debug(LOG_TAG,
1809+
"User %d is the last user to be discovered.",
1810+
current_user_id);
17191811
}
17201812
} catch (const std::exception &e) {
17211813
sl_log_error(LOG_TAG,

applications/zpc/components/zwave_command_classes/test/zwave_command_class_user_credential_test.cpp

Lines changed: 39 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1001,20 +1001,43 @@ void test_user_credential_user_report_happy_case()
10011001
// Test structure
10021002
auto user_id_count
10031003
= cpp_endpoint_id_node.children(ATTRIBUTE(USER_UNIQUE_ID)).size();
1004-
TEST_ASSERT_EQUAL_MESSAGE(
1005-
3,
1006-
user_id_count,
1007-
"User node count mismatch. Should be 3 by now. One with special id 0, the "
1008-
"reported one and the new desired one");
1004+
TEST_ASSERT_EQUAL_MESSAGE(2,
1005+
user_id_count,
1006+
"User node count mismatch. Should be 2 by now. One "
1007+
"with special id 0 and the "
1008+
"reported one");
10091009

1010+
// Simulate no credential for this User
1011+
helper_simulate_credential_report_frame(0x00,
1012+
user_id,
1013+
0,
1014+
0,
1015+
0,
1016+
std::vector<uint8_t>(),
1017+
0,
1018+
0,
1019+
0,
1020+
0);
10101021
auto second_user_id_node
10111022
= cpp_endpoint_id_node.child_by_type(ATTRIBUTE(USER_UNIQUE_ID), 2);
1023+
1024+
TEST_ASSERT_TRUE_MESSAGE(
1025+
second_user_id_node.is_valid(),
1026+
"Second user node should be created & valid");
10121027

10131028
TEST_ASSERT_FALSE_MESSAGE(
10141029
cpp_endpoint_id_node.child_by_type(ATTRIBUTE(ALL_USERS_CHECKSUM))
10151030
.is_valid(),
10161031
"ALL_USERS_CHECKSUM node should not be created yet");
10171032

1033+
user_id_count
1034+
= cpp_endpoint_id_node.children(ATTRIBUTE(USER_UNIQUE_ID)).size();
1035+
TEST_ASSERT_EQUAL_MESSAGE(
1036+
3,
1037+
user_id_count,
1038+
"User node count mismatch. Should be 3 by now. One "
1039+
"with special id 0, the reported one & the next in queue");
1040+
10181041
printf("Second and last user creation\n");
10191042

10201043
TEST_ASSERT_EQUAL_MESSAGE(
@@ -1074,6 +1097,17 @@ void test_user_credential_user_report_happy_case()
10741097
second_user_id_node.reported<user_credential_user_unique_id_t>(),
10751098
"Second user id mismatch");
10761099

1100+
// Still no credential, so we can create the all user checksum
1101+
helper_simulate_credential_report_frame(0x00,
1102+
user_id,
1103+
0,
1104+
0,
1105+
0,
1106+
std::vector<uint8_t>(),
1107+
0,
1108+
0,
1109+
0,
1110+
0);
10771111
TEST_ASSERT_TRUE_MESSAGE(
10781112
cpp_endpoint_id_node.child_by_type(ATTRIBUTE(ALL_USERS_CHECKSUM))
10791113
.is_valid(),

0 commit comments

Comments
 (0)