diff --git a/examples/common/m5stack-tft/repo b/examples/common/m5stack-tft/repo
index d99f5ef8df180a..a6299b6c7c0b2e 160000
--- a/examples/common/m5stack-tft/repo
+++ b/examples/common/m5stack-tft/repo
@@ -1 +1 @@
-Subproject commit d99f5ef8df180ab34b3d9fff6888d5bede7665c5
+Subproject commit a6299b6c7c0b2e3eb62fa08ee4bf7155c39bad1f
diff --git a/examples/tv-app/android/App/content-app/src/main/AndroidManifest.xml b/examples/tv-app/android/App/content-app/src/main/AndroidManifest.xml
index 61d036e0a90ef4..fff52c4b59db7e 100644
--- a/examples/tv-app/android/App/content-app/src/main/AndroidManifest.xml
+++ b/examples/tv-app/android/App/content-app/src/main/AndroidManifest.xml
@@ -19,7 +19,7 @@
-
+
diff --git a/examples/tv-app/android/java/AppImpl.cpp b/examples/tv-app/android/java/AppImpl.cpp
index 4dfabd03f12ec6..c24e7e537dbb23 100644
--- a/examples/tv-app/android/java/AppImpl.cpp
+++ b/examples/tv-app/android/java/AppImpl.cpp
@@ -355,6 +355,87 @@ ContentApp * ContentAppFactoryImpl::LoadContentApp(const CatalogVendorApp & vend
return nullptr;
}
+class DevicePairedCommand : public Controller::DevicePairingDelegate
+{
+public:
+ struct CallbackContext
+ {
+ uint16_t vendorId;
+ uint16_t productId;
+ chip::NodeId nodeId;
+
+ CallbackContext(uint16_t vId, uint16_t pId, chip::NodeId nId) : vendorId(vId), productId(pId), nodeId(nId) {}
+ };
+ DevicePairedCommand(uint16_t vendorId, uint16_t productId, chip::NodeId nodeId) :
+ mOnDeviceConnectedCallback(OnDeviceConnectedFn, this), mOnDeviceConnectionFailureCallback(OnDeviceConnectionFailureFn, this)
+ {
+ mContext = std::make_shared(vendorId, productId, nodeId);
+ }
+
+ static void OnDeviceConnectedFn(void * context, chip::Messaging::ExchangeManager & exchangeMgr,
+ const chip::SessionHandle & sessionHandle)
+ {
+ auto * pairingCommand = static_cast(context);
+ auto cbContext = pairingCommand->mContext;
+
+ if (pairingCommand)
+ {
+ ChipLogProgress(DeviceLayer,
+ "OnDeviceConnectedFn - Updating ACL for node id: " ChipLogFormatX64
+ " and vendor id: %d and product id: %d",
+ ChipLogValueX64(cbContext->nodeId), cbContext->vendorId, cbContext->productId);
+
+ GetCommissionerDiscoveryController()->CommissioningSucceeded(cbContext->vendorId, cbContext->productId,
+ cbContext->nodeId, exchangeMgr, sessionHandle);
+ }
+ }
+
+ static void OnDeviceConnectionFailureFn(void * context, const ScopedNodeId & peerId, CHIP_ERROR error)
+ {
+ auto * pairingCommand = static_cast(context);
+ auto cbContext = pairingCommand->mContext;
+
+ if (pairingCommand)
+ {
+ ChipLogProgress(DeviceLayer,
+ "OnDeviceConnectionFailureFn - Not updating ACL for node id: " ChipLogFormatX64
+ " and vendor id: %d and product id: %d",
+ ChipLogValueX64(cbContext->nodeId), cbContext->vendorId, cbContext->productId);
+ // TODO: Remove Node Id
+ }
+ }
+
+ chip::Callback::Callback mOnDeviceConnectedCallback;
+ chip::Callback::Callback mOnDeviceConnectionFailureCallback;
+ std::shared_ptr mContext;
+};
+
+void refreshConnectedClientsAcl(uint16_t vendorId, uint16_t productId, ContentAppImpl * app)
+{
+
+ std::set nodeIds = ContentAppPlatform::GetInstance().GetNodeIdsForContentApp(vendorId, productId);
+
+ for (const auto & allowedVendor : app->GetApplicationBasicDelegate()->GetAllowedVendorList())
+ {
+ std::set tempNodeIds = ContentAppPlatform::GetInstance().GetNodeIdsForAllowVendorId(allowedVendor);
+
+ nodeIds.insert(tempNodeIds.begin(), tempNodeIds.end());
+ }
+
+ for (const auto & nodeId : nodeIds)
+ {
+
+ ChipLogProgress(DeviceLayer,
+ "Creating Pairing Command with node id: " ChipLogFormatX64 " and vendor id: %d and product id: %d",
+ ChipLogValueX64(nodeId), vendorId, productId);
+
+ std::shared_ptr pairingCommand = std::make_shared(vendorId, productId, nodeId);
+
+ GetDeviceCommissioner()->GetConnectedDevice(nodeId, &pairingCommand->mOnDeviceConnectedCallback,
+ &pairingCommand->mOnDeviceConnectionFailureCallback);
+ }
+}
+
EndpointId ContentAppFactoryImpl::AddContentApp(const char * szVendorName, uint16_t vendorId, const char * szApplicationName,
uint16_t productId, const char * szApplicationVersion,
std::vector supportedClusters, jobject manager)
@@ -369,6 +450,9 @@ EndpointId ContentAppFactoryImpl::AddContentApp(const char * szVendorName, uint1
app->GetEndpointId());
mContentApps.push_back(app);
mDataVersions.push_back(dataVersionBuf);
+
+ refreshConnectedClientsAcl(vendorId, productId, app);
+
return epId;
}
@@ -387,6 +471,9 @@ EndpointId ContentAppFactoryImpl::AddContentApp(const char * szVendorName, uint1
app->GetEndpointId());
mContentApps.push_back(app);
mDataVersions.push_back(dataVersionBuf);
+
+ refreshConnectedClientsAcl(vendorId, productId, app);
+
return epId;
}
diff --git a/examples/tv-app/android/java/TVApp-JNI.cpp b/examples/tv-app/android/java/TVApp-JNI.cpp
index 04b5f4199edcaa..65a0a4409e9b8f 100644
--- a/examples/tv-app/android/java/TVApp-JNI.cpp
+++ b/examples/tv-app/android/java/TVApp-JNI.cpp
@@ -258,6 +258,8 @@ class MyPostCommissioningListener : public PostCommissioningListener
// read current binding list
chip::Controller::ClusterBase cluster(exchangeMgr, sessionHandle, kTargetBindingClusterEndpointId);
+ ContentAppPlatform::GetInstance().StoreNodeIdForContentApp(vendorId, productId, nodeId);
+
cacheContext(vendorId, productId, nodeId, exchangeMgr, sessionHandle);
CHIP_ERROR err =
diff --git a/examples/tv-app/linux/main.cpp b/examples/tv-app/linux/main.cpp
index 29fdd69cb36792..c2055e0abd03f3 100644
--- a/examples/tv-app/linux/main.cpp
+++ b/examples/tv-app/linux/main.cpp
@@ -45,6 +45,31 @@ void ApplicationInit()
ChipLogDetail(DeviceLayer, "TV Linux App: Warning - Fixed Content App Endpoint Not Disabled");
// Can't disable this without breaking CI unit tests that act upon account login cluster (only available on ep3)
// emberAfEndpointEnableDisable(3, false);
+
+#if CHIP_DEVICE_CONFIG_APP_PLATFORM_ENABLED
+ // Install Content Apps
+ ContentAppFactoryImpl * factory = GetContentAppFactoryImpl();
+
+ // Content App 1
+ constexpr uint16_t kApp1VendorId = 65521;
+ constexpr uint16_t kApp1ProductId = 32769;
+ factory->InstallContentApp(kApp1VendorId, kApp1ProductId);
+
+ // Content App 2
+ constexpr uint16_t kApp2VendorId = 1;
+ constexpr uint16_t kApp2ProductId = 11;
+ factory->InstallContentApp(kApp2VendorId, kApp2ProductId);
+
+ // Content App 3
+ constexpr uint16_t kApp3VendorId = 9050;
+ constexpr uint16_t kApp3ProductId = 22;
+ factory->InstallContentApp(kApp3VendorId, kApp3ProductId);
+
+ // Content App 4
+ constexpr uint16_t kApp4VendorId = 1111;
+ constexpr uint16_t kApp4ProductId = 22;
+ factory->InstallContentApp(kApp4VendorId, kApp4ProductId);
+#endif // CHIP_DEVICE_CONFIG_APP_PLATFORM_ENABLED
}
void ApplicationShutdown() {}
diff --git a/examples/tv-app/tv-common/shell/AppTvShellCommands.cpp b/examples/tv-app/tv-common/shell/AppTvShellCommands.cpp
index b39e84ad40f89a..5125ebbbbd7ecd 100644
--- a/examples/tv-app/tv-common/shell/AppTvShellCommands.cpp
+++ b/examples/tv-app/tv-common/shell/AppTvShellCommands.cpp
@@ -209,6 +209,10 @@ static CHIP_ERROR PrintAllCommands()
#if CHIP_DEVICE_CONFIG_ENABLE_BOTH_COMMISSIONER_AND_COMMISSIONEE
streamer_printf(sout, " print-app-access Print all ACLs for app platform fabric. Usage: app print-app-access\r\n");
streamer_printf(sout, " remove-app-access Remove all ACLs for app platform fabric. Usage: app remove-app-access\r\n");
+ streamer_printf(
+ sout,
+ " print-installed-apps Print all installed content apps with their endpoints. Usage: app print-installed-apps\r\n");
+
streamer_printf(sout,
" commission Commission given udc-entry using given pincode from corresponding app. Usage: "
"app commission 0\r\n");
@@ -436,6 +440,13 @@ static CHIP_ERROR AppPlatformHandler(int argc, char ** argv)
Access::GetAccessControl().DeleteAllEntriesForFabric(GetDeviceCommissioner()->GetFabricIndex());
return CHIP_NO_ERROR;
}
+ else if (strcmp(argv[0], "print-installed-apps") == 0)
+ {
+ ContentAppFactoryImpl * factory = GetContentAppFactoryImpl();
+ factory->LogInstalledApps();
+
+ return CHIP_NO_ERROR;
+ }
else if (strcmp(argv[0], "commission") == 0)
{
if (argc < 2)
diff --git a/examples/tv-app/tv-common/src/AppTv.cpp b/examples/tv-app/tv-common/src/AppTv.cpp
index 03422c3fa462e7..8d81d9cf6c2c5c 100644
--- a/examples/tv-app/tv-common/src/AppTv.cpp
+++ b/examples/tv-app/tv-common/src/AppTv.cpp
@@ -164,6 +164,8 @@ class MyPostCommissioningListener : public PostCommissioningListener
// read current binding list
chip::Controller::ClusterBase cluster(exchangeMgr, sessionHandle, kTargetBindingClusterEndpointId);
+ ContentAppPlatform::GetInstance().StoreNodeIdForContentApp(vendorId, productId, nodeId);
+
cacheContext(vendorId, productId, nodeId, exchangeMgr, sessionHandle);
CHIP_ERROR err =
@@ -565,6 +567,63 @@ void ContentAppFactoryImpl::AddAdminVendorId(uint16_t vendorId)
mAdminVendorIds.push_back(vendorId);
}
+#if CHIP_DEVICE_CONFIG_ENABLE_BOTH_COMMISSIONER_AND_COMMISSIONEE
+class DevicePairedCommand : public Controller::DevicePairingDelegate
+{
+public:
+ struct CallbackContext
+ {
+ uint16_t vendorId;
+ uint16_t productId;
+ chip::NodeId nodeId;
+
+ CallbackContext(uint16_t vId, uint16_t pId, chip::NodeId nId) : vendorId(vId), productId(pId), nodeId(nId) {}
+ };
+ DevicePairedCommand(uint16_t vendorId, uint16_t productId, chip::NodeId nodeId) :
+ mOnDeviceConnectedCallback(OnDeviceConnectedFn, this), mOnDeviceConnectionFailureCallback(OnDeviceConnectionFailureFn, this)
+ {
+ mContext = std::make_shared(vendorId, productId, nodeId);
+ }
+
+ static void OnDeviceConnectedFn(void * context, chip::Messaging::ExchangeManager & exchangeMgr,
+ const chip::SessionHandle & sessionHandle)
+ {
+ auto * pairingCommand = static_cast(context);
+ auto cbContext = pairingCommand->mContext;
+
+ if (pairingCommand)
+ {
+ ChipLogProgress(DeviceLayer,
+ "OnDeviceConnectedFn - Updating ACL for node id: " ChipLogFormatX64
+ " and vendor id: %d and product id: %d",
+ ChipLogValueX64(cbContext->nodeId), cbContext->vendorId, cbContext->productId);
+
+ GetCommissionerDiscoveryController()->CommissioningSucceeded(cbContext->vendorId, cbContext->productId,
+ cbContext->nodeId, exchangeMgr, sessionHandle);
+ }
+ }
+
+ static void OnDeviceConnectionFailureFn(void * context, const ScopedNodeId & peerId, CHIP_ERROR error)
+ {
+ auto * pairingCommand = static_cast(context);
+ auto cbContext = pairingCommand->mContext;
+
+ if (pairingCommand)
+ {
+ ChipLogProgress(DeviceLayer,
+ "OnDeviceConnectionFailureFn - Not updating ACL for node id: " ChipLogFormatX64
+ " and vendor id: %d and product id: %d",
+ ChipLogValueX64(cbContext->nodeId), cbContext->vendorId, cbContext->productId);
+ // TODO: Remove Node Id
+ }
+ }
+
+ chip::Callback::Callback mOnDeviceConnectedCallback;
+ chip::Callback::Callback mOnDeviceConnectionFailureCallback;
+ std::shared_ptr mContext;
+};
+#endif // CHIP_DEVICE_CONFIG_ENABLE_BOTH_COMMISSIONER_AND_COMMISSIONEE
+
void ContentAppFactoryImpl::InstallContentApp(uint16_t vendorId, uint16_t productId)
{
auto make_default_supported_clusters = []() {
@@ -605,6 +664,46 @@ void ContentAppFactoryImpl::InstallContentApp(uint16_t vendorId, uint16_t produc
make_default_supported_clusters());
mContentApps.emplace_back(std::move(ptr));
}
+
+#if CHIP_DEVICE_CONFIG_ENABLE_BOTH_COMMISSIONER_AND_COMMISSIONEE
+ // Get the list of node ids
+ std::set nodeIds = ContentAppPlatform::GetInstance().GetNodeIdsForContentApp(vendorId, productId);
+
+ // update ACLs
+ for (auto & contentApp : mContentApps)
+ {
+ auto app = contentApp.get();
+
+ if (app->MatchesPidVid(productId, vendorId))
+ {
+ CatalogVendorApp vendorApp = app->GetApplicationBasicDelegate()->GetCatalogVendorApp();
+
+ GetContentAppFactoryImpl()->LoadContentApp(vendorApp);
+ }
+
+ // update the list of node ids with content apps allowed vendor list
+ for (const auto & allowedVendor : app->GetApplicationBasicDelegate()->GetAllowedVendorList())
+ {
+ std::set tempNodeIds = ContentAppPlatform::GetInstance().GetNodeIdsForAllowVendorId(allowedVendor);
+
+ nodeIds.insert(tempNodeIds.begin(), tempNodeIds.end());
+ }
+ }
+
+ // refresh ACLs
+ for (const auto & nodeId : nodeIds)
+ {
+
+ ChipLogProgress(DeviceLayer,
+ "Creating Pairing Command with node id: " ChipLogFormatX64 " and vendor id: %d and product id: %d",
+ ChipLogValueX64(nodeId), vendorId, productId);
+
+ std::shared_ptr pairingCommand = std::make_shared(vendorId, productId, nodeId);
+
+ GetDeviceCommissioner()->GetConnectedDevice(nodeId, &pairingCommand->mOnDeviceConnectedCallback,
+ &pairingCommand->mOnDeviceConnectionFailureCallback);
+ }
+#endif // CHIP_DEVICE_CONFIG_ENABLE_BOTH_COMMISSIONER_AND_COMMISSIONEE
}
bool ContentAppFactoryImpl::UninstallContentApp(uint16_t vendorId, uint16_t productId)
@@ -625,8 +724,9 @@ bool ContentAppFactoryImpl::UninstallContentApp(uint16_t vendorId, uint16_t prod
ChipLogProgress(DeviceLayer, "Found an app vid=%d pid=%d. Uninstalling it.",
app->GetApplicationBasicDelegate()->HandleGetVendorId(),
app->GetApplicationBasicDelegate()->HandleGetProductId());
+ EndpointId removedEndpointID = ContentAppPlatform::GetInstance().RemoveContentApp(app);
+ ChipLogProgress(DeviceLayer, "Removed content app at endpoint id: %d", removedEndpointID);
mContentApps.erase(mContentApps.begin() + index);
- // TODO: call ContentAppPlatform->RemoveContentApp(ids...)
return true;
}
@@ -701,22 +801,8 @@ std::list ContentAppFactoryImpl::GetAllowedClusterListForStaticEndpoi
CHIP_ERROR AppTvInit()
{
#if CHIP_DEVICE_CONFIG_APP_PLATFORM_ENABLED
- // test data for apps
- constexpr uint16_t kApp1VendorId = 1;
- constexpr uint16_t kApp1ProductId = 11;
- constexpr uint16_t kApp2VendorId = 65521;
- constexpr uint16_t kApp2ProductId = 32769;
- constexpr uint16_t kApp3VendorId = 9050;
- constexpr uint16_t kApp3ProductId = 22;
- constexpr uint16_t kApp4VendorId = 1111;
- constexpr uint16_t kApp4ProductId = 22;
-
ContentAppPlatform::GetInstance().SetupAppPlatform();
ContentAppPlatform::GetInstance().SetContentAppFactory(&gFactory);
- gFactory.InstallContentApp(kApp1VendorId, kApp1ProductId);
- gFactory.InstallContentApp(kApp2VendorId, kApp2ProductId);
- gFactory.InstallContentApp(kApp3VendorId, kApp3ProductId);
- gFactory.InstallContentApp(kApp4VendorId, kApp4ProductId);
uint16_t value;
if (DeviceLayer::GetDeviceInstanceInfoProvider()->GetVendorId(value) != CHIP_NO_ERROR)
{
diff --git a/src/app/app-platform/ContentAppPlatform.cpp b/src/app/app-platform/ContentAppPlatform.cpp
index fb2d6ab39a1b5f..214af261e26e54 100644
--- a/src/app/app-platform/ContentAppPlatform.cpp
+++ b/src/app/app-platform/ContentAppPlatform.cpp
@@ -387,6 +387,57 @@ ContentApp * ContentAppPlatform::GetContentApp(EndpointId id)
return nullptr;
}
+// create a string key from vendorId and productId
+std::string createKey(uint16_t vendorId, uint16_t productId)
+{
+ return std::to_string(vendorId) + ":" + std::to_string(productId);
+}
+
+void ContentAppPlatform::StoreNodeIdForContentApp(uint16_t vendorId, uint16_t productId, NodeId nodeId)
+{
+ std::string key = createKey(vendorId, productId);
+
+ ChipLogProgress(DeviceLayer, "Stored node id: " ChipLogFormatX64 " for key: %s", ChipLogValueX64(nodeId), key.c_str());
+
+ mConnectedContentAppNodeIds[key].insert(nodeId);
+}
+
+std::set ContentAppPlatform::GetNodeIdsForContentApp(uint16_t vendorId, uint16_t productId)
+{
+ std::string key = createKey(vendorId, productId);
+
+ ChipLogProgress(DeviceLayer, "Retrieving node id for key: %s", key.c_str());
+
+ auto it = mConnectedContentAppNodeIds.find(key);
+ if (it != mConnectedContentAppNodeIds.end())
+ {
+ ChipLogProgress(DeviceLayer, "Found node id");
+ return it->second;
+ }
+
+ ChipLogProgress(DeviceLayer, "Didn't find node id");
+ // If key not found, return an empty set
+ return {};
+}
+
+std::set ContentAppPlatform::GetNodeIdsForAllowVendorId(uint16_t vendorId)
+{
+ std::set result;
+ std::string vendorPrefix = std::to_string(vendorId) + ":";
+
+ for (const auto & pair : mConnectedContentAppNodeIds)
+ {
+ const std::string & key = pair.first;
+ if (key.find(vendorPrefix) == 0)
+ { // Check if the key starts with the vendor prefix
+ const std::set & nodeIds = pair.second;
+ result.insert(nodeIds.begin(), nodeIds.end());
+ }
+ }
+
+ return result;
+}
+
void ContentAppPlatform::SetCurrentApp(ContentApp * app)
{
if (!HasCurrentApp())
diff --git a/src/app/app-platform/ContentAppPlatform.h b/src/app/app-platform/ContentAppPlatform.h
index 16a47c26a3b991..45615d09ed8ddf 100644
--- a/src/app/app-platform/ContentAppPlatform.h
+++ b/src/app/app-platform/ContentAppPlatform.h
@@ -161,6 +161,18 @@ class DLL_EXPORT ContentAppPlatform
bool HasTargetContentApp(uint16_t vendorId, uint16_t productId, CharSpan rotatingId,
Protocols::UserDirectedCommissioning::TargetAppInfo & info, uint32_t & passcode);
+ // returns set of connected nodes for a given content app
+ std::set GetNodeIdsForContentApp(uint16_t vendorId, uint16_t productId);
+
+ // returns set of connected nodes for a given allowed vendor id
+ std::set GetNodeIdsForAllowVendorId(uint16_t vendorId);
+
+ // store node id for content app after commissioning
+ // node id can be used later on to update ACL
+ // in case app is not installed
+ // Note: This is in memory storing, the values are deleted after reboot
+ void StoreNodeIdForContentApp(uint16_t vendorId, uint16_t productId, NodeId nodeId);
+
/**
* @brief
* Add ACLs on this device for the given client,
@@ -201,6 +213,8 @@ class DLL_EXPORT ContentAppPlatform
EndpointId mCurrentEndpointId;
EndpointId mFirstDynamicEndpointId;
ContentApp * mContentApps[CHIP_DEVICE_CONFIG_DYNAMIC_ENDPOINT_COUNT];
+ // key is string -> vendorId:producTid
+ std::map> mConnectedContentAppNodeIds;
private:
void IncrementCurrentEndpointID();