Skip to content

Commit e00576c

Browse files
mthiescrestyled-commitslazarkovchrisdecenzo
authored andcommitted
AccountLogin Login/Logout command support (project-chip#34162)
* AccountLogin Login/Logout command support [Problem] * ContentAppPlatform should send AccountLogin::Login command after successfull commissioning if user was shown setupPIN prompt. * Handling Login/Logout commands is currently not implemented in the Android. [Solution] * Call AccountLoginManager::HandleLogin if commissioning succeeded successfully with setupPIN prompt flow. * Implement HandleLogin (and HandleLogout) for Android AccountLoginManager using the ContentAppCommandDelegate. [Testing] WIP * HandleLogin in ContentAppPlatform::ManageClientAccess * Cache rotating ID in OnUserDirectedCommissioningRequest * Restyled by google-java-format * Restyled by clang-format * Update content app * Restyled by whitespace * Restyled by clang-format * Update response * Update method name * Restyled by google-java-format * Update src/app/app-platform/ContentApp.h Co-authored-by: chrisdecenzo <[email protected]> * Update content app platform validation * Update validation logic * Update logic * Update responses * Restyled by clang-format * Simplify logic * Restyled by whitespace * clean up * Update code * Update content app with dynamic pin code * Restyled by google-java-format * Remove getRotatingIdSpan class methods * Restyled by clang-format --------- Co-authored-by: Restyled.io <[email protected]> Co-authored-by: Lazar Kovacic <[email protected]> Co-authored-by: chrisdecenzo <[email protected]>
1 parent b82fab3 commit e00576c

File tree

14 files changed

+254
-68
lines changed

14 files changed

+254
-68
lines changed

examples/tv-app/android/App/content-app/src/main/java/com/example/contentapp/CommandResponseHolder.java

+10
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,16 @@ private CommandResponseHolder() {
3131
Clusters.AccountLogin.Id,
3232
Clusters.AccountLogin.Commands.GetSetupPIN.ID,
3333
"{\"0\":\"20202021\"}");
34+
setResponseValue(
35+
Clusters.AccountLogin.Id,
36+
Clusters.AccountLogin.Commands.Login.ID,
37+
// 0 is for success, you can return 1 for failure
38+
"{\"Status\":0}");
39+
setResponseValue(
40+
Clusters.AccountLogin.Id,
41+
Clusters.AccountLogin.Commands.Logout.ID,
42+
// 0 is for success, you can return 1 for failure
43+
"{\"Status\":0}");
3444
};
3545

3646
public static CommandResponseHolder getInstance() {

examples/tv-app/android/App/content-app/src/main/java/com/example/contentapp/receiver/MatterCommandReceiver.java

+17
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,13 @@
44
import android.content.BroadcastReceiver;
55
import android.content.Context;
66
import android.content.Intent;
7+
import android.provider.Settings;
78
import android.util.Log;
89
import com.example.contentapp.AttributeHolder;
910
import com.example.contentapp.CommandResponseHolder;
1011
import com.example.contentapp.MainActivity;
1112
import com.example.contentapp.matter.MatterAgentClient;
13+
import com.matter.tv.app.api.Clusters;
1214
import com.matter.tv.app.api.MatterIntentConstants;
1315

1416
public class MatterCommandReceiver extends BroadcastReceiver {
@@ -44,6 +46,21 @@ public void onReceive(Context context, Intent intent) {
4446
.append(command)
4547
.toString();
4648
Log.d(TAG, message);
49+
50+
int pinCode =
51+
Settings.Secure.getInt(context.getContentResolver(), "matter_pin_code", 20202021);
52+
Log.d(TAG, "Retrieved pin code:" + pinCode);
53+
54+
CommandResponseHolder.getInstance()
55+
.setResponseValue(
56+
Clusters.AccountLogin.Id,
57+
Clusters.AccountLogin.Commands.GetSetupPIN.ID,
58+
"{\""
59+
+ Clusters.AccountLogin.Commands.GetSetupPINResponse.Fields.SetupPIN
60+
+ "\":\""
61+
+ pinCode
62+
+ "\"}");
63+
4764
String response =
4865
CommandResponseHolder.getInstance().getCommandResponse(clusterId, commandId);
4966

examples/tv-app/android/include/account-login/AccountLoginManager.cpp

+77-10
Original file line numberDiff line numberDiff line change
@@ -24,38 +24,105 @@
2424
#include <lib/core/DataModelTypes.h>
2525

2626
using namespace std;
27+
using namespace chip::app::Clusters;
2728
using namespace chip::app::Clusters::AccountLogin;
2829
using Status = chip::Protocols::InteractionModel::Status;
2930

31+
namespace {
32+
33+
const auto loginTempAccountIdentifierFieldId =
34+
to_string(chip::to_underlying(AccountLogin::Commands::Login::Fields::kTempAccountIdentifier));
35+
const auto loginSetupPINFieldId = to_string(chip::to_underlying(AccountLogin::Commands::Login::Fields::kSetupPIN));
36+
const auto loginNodeFieldId = to_string(chip::to_underlying(AccountLogin::Commands::Login::Fields::kNode));
37+
const auto logoutNodeFieldId = to_string(chip::to_underlying(AccountLogin::Commands::Logout::Fields::kNode));
38+
39+
string charSpanToString(const CharSpan & charSpan)
40+
{
41+
return { charSpan.data(), charSpan.size() };
42+
}
43+
44+
std::string serializeLoginCommand(AccountLogin::Commands::Login::Type cmd)
45+
{
46+
return R"({")" + loginTempAccountIdentifierFieldId + R"(":")" + charSpanToString(cmd.tempAccountIdentifier) + R"(",)" + R"(")" +
47+
loginSetupPINFieldId + R"(":")" + charSpanToString(cmd.setupPIN) + R"(",)" + R"(")" + loginNodeFieldId + R"(":")" +
48+
to_string(cmd.node.Value()) + R"("})";
49+
}
50+
51+
std::string serializeLogoutCommand(AccountLogin::Commands::Logout::Type cmd)
52+
{
53+
return R"({")" + logoutNodeFieldId + R"(":")" + to_string(cmd.node.Value()) + R"("})";
54+
}
55+
56+
} // namespace
57+
3058
AccountLoginManager::AccountLoginManager(ContentAppCommandDelegate * commandDelegate, const char * setupPin) :
3159
mCommandDelegate(commandDelegate)
3260
{
3361
CopyString(mSetupPin, sizeof(mSetupPin), setupPin);
3462
}
3563

36-
bool AccountLoginManager::HandleLogin(const CharSpan & tempAccountIdentifier, const CharSpan & setupPin,
64+
bool AccountLoginManager::HandleLogin(const CharSpan & tempAccountIdentifier, const CharSpan & setupPIN,
3765
const chip::Optional<chip::NodeId> & nodeId)
3866
{
3967
ChipLogProgress(DeviceLayer, "AccountLoginManager::HandleLogin called for endpoint %d", mEndpointId);
40-
string tempAccountIdentifierString(tempAccountIdentifier.data(), tempAccountIdentifier.size());
41-
string setupPinString(setupPin.data(), setupPin.size());
4268

43-
if (strcmp(mSetupPin, setupPinString.c_str()) == 0)
69+
if (mCommandDelegate == nullptr)
4470
{
45-
ChipLogProgress(Zcl, "AccountLoginManager::HandleLogin success");
46-
return true;
71+
ChipLogError(Zcl, "CommandDelegate not found");
72+
return false;
4773
}
48-
else
74+
75+
if (tempAccountIdentifier.empty() || setupPIN.empty() || !nodeId.HasValue())
4976
{
50-
ChipLogProgress(Zcl, "AccountLoginManager::HandleLogin failed expected pin %s", mSetupPin);
77+
ChipLogError(Zcl, "Invalid parameters");
5178
return false;
5279
}
80+
81+
Json::Value response;
82+
bool commandHandled = true;
83+
AccountLogin::Commands::Login::Type cmd = { tempAccountIdentifier, setupPIN, nodeId };
84+
85+
auto status = mCommandDelegate->InvokeCommand(mEndpointId, AccountLogin::Id, AccountLogin::Commands::Login::Id,
86+
serializeLoginCommand(cmd), commandHandled, response);
87+
if (status == Status::Success)
88+
{
89+
// Format status response to verify that response is non-failure.
90+
status = mCommandDelegate->FormatStatusResponse(response);
91+
}
92+
ChipLogProgress(Zcl, "AccountLoginManager::HandleLogin command returned with status: %d", chip::to_underlying(status));
93+
return status == chip::Protocols::InteractionModel::Status::Success;
5394
}
5495

5596
bool AccountLoginManager::HandleLogout(const chip::Optional<chip::NodeId> & nodeId)
5697
{
57-
// TODO: Insert your code here to send logout request
58-
return true;
98+
ChipLogProgress(DeviceLayer, "AccountLoginManager::HandleLogout called for endpoint %d", mEndpointId);
99+
100+
if (mCommandDelegate == nullptr)
101+
{
102+
ChipLogError(Zcl, "CommandDelegate not found");
103+
return false;
104+
}
105+
106+
if (!nodeId.HasValue())
107+
{
108+
ChipLogError(Zcl, "Invalid parameters");
109+
return false;
110+
}
111+
112+
Json::Value response;
113+
bool commandHandled = true;
114+
AccountLogin::Commands::Logout::Type cmd = { nodeId };
115+
116+
auto status = mCommandDelegate->InvokeCommand(mEndpointId, AccountLogin::Id, AccountLogin::Commands::Logout::Id,
117+
serializeLogoutCommand(cmd), commandHandled, response);
118+
119+
if (status == Status::Success)
120+
{
121+
// Format status response to verify that response is non-failure.
122+
status = mCommandDelegate->FormatStatusResponse(response);
123+
}
124+
ChipLogProgress(Zcl, "AccountLoginManager::HandleLogout command returned with status: %d", chip::to_underlying(status));
125+
return status == chip::Protocols::InteractionModel::Status::Success;
59126
}
60127

61128
void AccountLoginManager::HandleGetSetupPin(CommandResponseHelper<GetSetupPINResponse> & helper,

examples/tv-app/android/java/AppImpl.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -417,7 +417,7 @@ void refreshConnectedClientsAcl(uint16_t vendorId, uint16_t productId, ContentAp
417417

418418
for (const auto & allowedVendor : app->GetApplicationBasicDelegate()->GetAllowedVendorList())
419419
{
420-
std::set<NodeId> tempNodeIds = ContentAppPlatform::GetInstance().GetNodeIdsForAllowVendorId(allowedVendor);
420+
std::set<NodeId> tempNodeIds = ContentAppPlatform::GetInstance().GetNodeIdsForAllowedVendorId(allowedVendor);
421421

422422
nodeIds.insert(tempNodeIds.begin(), tempNodeIds.end());
423423
}

examples/tv-app/android/java/ContentAppCommandDelegate.cpp

+44-22
Original file line numberDiff line numberDiff line change
@@ -39,15 +39,9 @@
3939
namespace chip {
4040
namespace AppPlatform {
4141

42-
using CommandHandlerInterface = chip::app::CommandHandlerInterface;
43-
using LaunchResponseType = chip::app::Clusters::ContentLauncher::Commands::LauncherResponse::Type;
44-
using PlaybackResponseType = chip::app::Clusters::MediaPlayback::Commands::PlaybackResponse::Type;
45-
using NavigateTargetResponseType = chip::app::Clusters::TargetNavigator::Commands::NavigateTargetResponse::Type;
46-
using GetSetupPINResponseType = chip::app::Clusters::AccountLogin::Commands::GetSetupPINResponse::Type;
47-
using Status = chip::Protocols::InteractionModel::Status;
48-
49-
const std::string FAILURE_KEY = "PlatformError";
50-
const std::string FAILURE_STATUS_KEY = "Status";
42+
const std::string FAILURE_KEY = "PlatformError";
43+
const std::string FAILURE_STATUS_KEY = "Status";
44+
const std::string RESPONSE_STATUS_KEY = "Status";
5145

5246
bool isValidJson(const char * response)
5347
{
@@ -166,6 +160,7 @@ Status ContentAppCommandDelegate::InvokeCommand(EndpointId epId, ClusterId clust
166160
ChipLogError(Zcl, "Java exception in ContentAppCommandDelegate::sendCommand");
167161
env->ExceptionDescribe();
168162
env->ExceptionClear();
163+
return chip::Protocols::InteractionModel::Status::Failure;
169164
}
170165
else
171166
{
@@ -198,7 +193,8 @@ Status ContentAppCommandDelegate::InvokeCommand(EndpointId epId, ClusterId clust
198193
}
199194
env->DeleteLocalRef(resp);
200195

201-
// handle errors from platform-app
196+
// Parse response here in case there is failure response.
197+
// Return non-success error code to indicate to caller it should not parse response.
202198
if (!value[FAILURE_KEY].empty())
203199
{
204200
value = value[FAILURE_KEY];
@@ -209,7 +205,9 @@ Status ContentAppCommandDelegate::InvokeCommand(EndpointId epId, ClusterId clust
209205
return chip::Protocols::InteractionModel::Status::Failure;
210206
}
211207

212-
return chip::Protocols::InteractionModel::Status::UnsupportedEndpoint;
208+
// Return success to indicate command has been sent, response returned and parsed successfully.
209+
// Caller has to manually parse value input/output parameter to get response status/object.
210+
return chip::Protocols::InteractionModel::Status::Success;
213211
}
214212
else
215213
{
@@ -282,20 +280,31 @@ void ContentAppCommandDelegate::FormatResponseData(CommandHandlerInterface::Hand
282280
}
283281

284282
case app::Clusters::AccountLogin::Id: {
285-
if (app::Clusters::AccountLogin::Commands::GetSetupPIN::Id != handlerContext.mRequestPath.mCommandId)
283+
switch (handlerContext.mRequestPath.mCommandId)
286284
{
287-
// No response for other commands in this cluster
285+
case app::Clusters::AccountLogin::Commands::GetSetupPIN::Id: {
286+
Status status;
287+
GetSetupPINResponseType getSetupPINresponse = FormatGetSetupPINResponse(value, status);
288+
if (status != chip::Protocols::InteractionModel::Status::Success)
289+
{
290+
handlerContext.mCommandHandler.AddStatus(handlerContext.mRequestPath, status);
291+
}
292+
else
293+
{
294+
handlerContext.mCommandHandler.AddResponse(handlerContext.mRequestPath, getSetupPINresponse);
295+
}
288296
break;
289297
}
290-
Status status;
291-
GetSetupPINResponseType getSetupPINresponse = FormatGetSetupPINResponse(value, status);
292-
if (status != chip::Protocols::InteractionModel::Status::Success)
293-
{
294-
handlerContext.mCommandHandler.AddStatus(handlerContext.mRequestPath, status);
298+
case app::Clusters::AccountLogin::Commands::Login::Id: {
299+
handlerContext.mCommandHandler.AddStatus(handlerContext.mRequestPath, FormatStatusResponse(value));
300+
break;
295301
}
296-
else
297-
{
298-
handlerContext.mCommandHandler.AddResponse(handlerContext.mRequestPath, getSetupPINresponse);
302+
case app::Clusters::AccountLogin::Commands::Logout::Id: {
303+
handlerContext.mCommandHandler.AddStatus(handlerContext.mRequestPath, FormatStatusResponse(value));
304+
break;
305+
}
306+
default:
307+
break;
299308
}
300309
break;
301310
}
@@ -388,10 +397,23 @@ GetSetupPINResponseType ContentAppCommandDelegate::FormatGetSetupPINResponse(Jso
388397
}
389398
else
390399
{
391-
getSetupPINresponse.setupPIN = "";
400+
status = chip::Protocols::InteractionModel::Status::Failure;
392401
}
393402
return getSetupPINresponse;
394403
}
395404

405+
Status ContentAppCommandDelegate::FormatStatusResponse(Json::Value value)
406+
{
407+
// check if JSON has "Status" key
408+
if (!value[RESPONSE_STATUS_KEY].empty() && !value[RESPONSE_STATUS_KEY].isUInt())
409+
{
410+
return static_cast<Protocols::InteractionModel::Status>(value.asUInt());
411+
}
412+
else
413+
{
414+
return chip::Protocols::InteractionModel::Status::Failure;
415+
}
416+
}
417+
396418
} // namespace AppPlatform
397419
} // namespace chip

examples/tv-app/android/java/ContentAppCommandDelegate.h

+2
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
#include <json/json.h>
3030
#include <lib/core/DataModelTypes.h>
3131
#include <lib/support/JniReferences.h>
32+
#include <protocols/interaction_model/StatusCode.h>
3233

3334
#include <string>
3435

@@ -75,6 +76,7 @@ class ContentAppCommandDelegate : public CommandHandlerInterface
7576
LaunchResponseType FormatContentLauncherResponse(Json::Value value, Status & status);
7677
NavigateTargetResponseType FormatNavigateTargetResponse(Json::Value value, Status & status);
7778
PlaybackResponseType FormatMediaPlaybackResponse(Json::Value value, Status & status);
79+
Status FormatStatusResponse(Json::Value value);
7880

7981
private:
8082
void InitializeJNIObjects(jobject manager)

examples/tv-app/android/java/TVApp-JNI.cpp

+23-12
Original file line numberDiff line numberDiff line change
@@ -258,15 +258,15 @@ SampleTvAppInstallationService gSampleTvAppInstallationService;
258258

259259
class MyPostCommissioningListener : public PostCommissioningListener
260260
{
261-
void CommissioningCompleted(uint16_t vendorId, uint16_t productId, NodeId nodeId, Messaging::ExchangeManager & exchangeMgr,
262-
const SessionHandle & sessionHandle) override
261+
void CommissioningCompleted(uint16_t vendorId, uint16_t productId, NodeId nodeId, CharSpan rotatingId, uint32_t passcode,
262+
Messaging::ExchangeManager & exchangeMgr, const SessionHandle & sessionHandle) override
263263
{
264264
// read current binding list
265265
chip::Controller::ClusterBase cluster(exchangeMgr, sessionHandle, kTargetBindingClusterEndpointId);
266266

267267
ContentAppPlatform::GetInstance().StoreNodeIdForContentApp(vendorId, productId, nodeId);
268268

269-
cacheContext(vendorId, productId, nodeId, exchangeMgr, sessionHandle);
269+
cacheContext(vendorId, productId, nodeId, rotatingId, passcode, exchangeMgr, sessionHandle);
270270

271271
CHIP_ERROR err =
272272
cluster.ReadAttribute<Binding::Attributes::Binding::TypeInfo>(this, OnReadSuccessResponse, OnReadFailureResponse);
@@ -350,17 +350,23 @@ class MyPostCommissioningListener : public PostCommissioningListener
350350

351351
Optional<SessionHandle> opt = mSecureSession.Get();
352352
SessionHandle & sessionHandle = opt.Value();
353+
auto rotatingIdSpan = CharSpan{ mRotatingId.data(), mRotatingId.size() };
353354
ContentAppPlatform::GetInstance().ManageClientAccess(*mExchangeMgr, sessionHandle, mVendorId, mProductId, localNodeId,
354-
bindings, OnSuccessResponse, OnFailureResponse);
355+
rotatingIdSpan, mPasscode, bindings, OnSuccessResponse,
356+
OnFailureResponse);
355357
clearContext();
356358
}
357359

358-
void cacheContext(uint16_t vendorId, uint16_t productId, NodeId nodeId, Messaging::ExchangeManager & exchangeMgr,
359-
const SessionHandle & sessionHandle)
360+
void cacheContext(uint16_t vendorId, uint16_t productId, NodeId nodeId, CharSpan rotatingId, uint32_t passcode,
361+
Messaging::ExchangeManager & exchangeMgr, const SessionHandle & sessionHandle)
360362
{
361-
mVendorId = vendorId;
362-
mProductId = productId;
363-
mNodeId = nodeId;
363+
mVendorId = vendorId;
364+
mProductId = productId;
365+
mNodeId = nodeId;
366+
mRotatingId = std::string{
367+
rotatingId.data(), rotatingId.size()
368+
}; // Allocates and copies to string instead of storing span to make sure lifetime is valid.
369+
mPasscode = passcode;
364370
mExchangeMgr = &exchangeMgr;
365371
mSecureSession.ShiftToSession(sessionHandle);
366372
}
@@ -370,12 +376,17 @@ class MyPostCommissioningListener : public PostCommissioningListener
370376
mVendorId = 0;
371377
mProductId = 0;
372378
mNodeId = 0;
379+
mRotatingId = {};
380+
mPasscode = 0;
373381
mExchangeMgr = nullptr;
374382
mSecureSession.SessionReleased();
375383
}
376-
uint16_t mVendorId = 0;
377-
uint16_t mProductId = 0;
378-
NodeId mNodeId = 0;
384+
385+
uint16_t mVendorId = 0;
386+
uint16_t mProductId = 0;
387+
NodeId mNodeId = 0;
388+
std::string mRotatingId;
389+
uint32_t mPasscode = 0;
379390
Messaging::ExchangeManager * mExchangeMgr = nullptr;
380391
SessionHolder mSecureSession;
381392
};

0 commit comments

Comments
 (0)