Skip to content

Commit 37155dc

Browse files
committed
Add SupportedCluster list to ContentAppPlatform
[Problem] The ContentAppPlatform does not know which clusters each ContentApp supports. Currently the ContentApp is queried to get the passcode as long as the AccountLoginDelegate is present whether or not the installed ContentApp has declared support for AccountLogin cluster in its manifest. [Solution] Extend the native ContentApp with a SupportedCluster list. The list is initialized from the Android AppPlatformService whenever an installed ContentApp is discovered and added to the native AppPlatform. This list is used to check if AccountLogin cluster is supported before querying the ContentApp for the passcode. [Test] The feature is tested end-to-end using a native Linux casting-app and and an Android platform-app and content-app. The content-app static_matter_clusters raw asset was manipulated to verify that the clusters are parsed correctly and that the passcode is only retrievable when the AccountLogin cluster is declared.
1 parent d4d9a99 commit 37155dc

File tree

10 files changed

+205
-23
lines changed

10 files changed

+205
-23
lines changed

examples/tv-app/android/App/platform-app/src/main/java/com/matter/tv/server/service/AppPlatformService.java

+19
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,17 @@
2424
import android.content.IntentFilter;
2525
import android.util.Log;
2626
import androidx.annotation.NonNull;
27+
import com.matter.tv.app.api.SupportedCluster;
2728
import com.matter.tv.server.handlers.ContentAppEndpointManagerImpl;
2829
import com.matter.tv.server.model.ContentApp;
2930
import com.matter.tv.server.receivers.ContentAppDiscoveryService;
3031
import com.matter.tv.server.tvapp.AppPlatform;
32+
import com.matter.tv.server.tvapp.ContentAppSupportedCluster;
3133
import com.matter.tv.server.utils.EndpointsDataStore;
34+
import java.util.Collection;
3235
import java.util.HashMap;
3336
import java.util.Map;
37+
import java.util.stream.Collectors;
3438

3539
/**
3640
* This class facilitates the communication with the ContentAppPlatform. It uses the JNI interface
@@ -168,6 +172,8 @@ public void addContentApp(ContentApp app) {
168172
app.getAppName(),
169173
app.getProductId(),
170174
app.getVersion(),
175+
app.getSupportedClusters(),
176+
mapSupportedCluster(app.getSupportedClusters()),
171177
desiredEndpointId,
172178
new ContentAppEndpointManagerImpl(context));
173179
} else {
@@ -178,6 +184,7 @@ public void addContentApp(ContentApp app) {
178184
app.getAppName(),
179185
app.getProductId(),
180186
app.getVersion(),
187+
mapSupportedCluster(app.getSupportedClusters()),
181188
new ContentAppEndpointManagerImpl(context));
182189
}
183190
if (retEndpointId > 0) {
@@ -187,4 +194,16 @@ public void addContentApp(ContentApp app) {
187194
Log.e(TAG, "Could not add content app as endpoint. App Name " + app.getAppName());
188195
}
189196
}
197+
198+
private Collection<ContentAppSupportedCluster> mapSupportedClusters(Collection<SupportedCluster> supportedClusters) {
199+
supportedClusters.stream().map(AppPlatformService::mapSupportedCluster).collect(Collectors.toList());
200+
}
201+
202+
private static ContentAppSupportedCluster mapSupportedCluster(SupportedCluster cluster) {
203+
return new ContentAppSupportedCluster(
204+
cluster.clusterIdentifier,
205+
cluster.features,
206+
cluster.optionalCommandIdentifiers,
207+
cluster.optionalAttributesIdentifiers);
208+
}
190209
}

examples/tv-app/android/BUILD.gn

+1
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,7 @@ android_library("java") {
122122
"java/src/com/matter/tv/server/tvapp/ChannelProgramResponse.java",
123123
"java/src/com/matter/tv/server/tvapp/Clusters.java",
124124
"java/src/com/matter/tv/server/tvapp/ContentAppEndpointManager.java",
125+
"java/src/com/matter/tv/server/tvapp/ContentAppSupportedCluster.java",
125126
"java/src/com/matter/tv/server/tvapp/ContentLaunchBrandingInformation.java",
126127
"java/src/com/matter/tv/server/tvapp/ContentLaunchEntry.java",
127128
"java/src/com/matter/tv/server/tvapp/ContentLaunchManager.java",

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

+10-9
Original file line numberDiff line numberDiff line change
@@ -339,10 +339,11 @@ ContentApp * ContentAppFactoryImpl::LoadContentApp(const CatalogVendorApp & vend
339339
}
340340

341341
EndpointId ContentAppFactoryImpl::AddContentApp(const char * szVendorName, uint16_t vendorId, const char * szApplicationName,
342-
uint16_t productId, const char * szApplicationVersion, jobject manager)
342+
uint16_t productId, const char * szApplicationVersion, std::vector<SupportedCluster> supportedClusters,
343+
jobject manager)
343344
{
344345
DataVersion * dataVersionBuf = new DataVersion[ArraySize(contentAppClusters)];
345-
ContentAppImpl * app = new ContentAppImpl(szVendorName, vendorId, szApplicationName, productId, szApplicationVersion, "",
346+
ContentAppImpl * app = new ContentAppImpl(szVendorName, vendorId, szApplicationName, productId, szApplicationVersion, "", std::move(supportedClusters),
346347
mAttributeDelegate, mCommandDelegate);
347348
EndpointId epId = ContentAppPlatform::GetInstance().AddContentApp(
348349
app, &contentAppEndpoint, Span<DataVersion>(dataVersionBuf, ArraySize(contentAppClusters)),
@@ -355,11 +356,11 @@ EndpointId ContentAppFactoryImpl::AddContentApp(const char * szVendorName, uint1
355356
}
356357

357358
EndpointId ContentAppFactoryImpl::AddContentApp(const char * szVendorName, uint16_t vendorId, const char * szApplicationName,
358-
uint16_t productId, const char * szApplicationVersion, jobject manager,
359-
EndpointId desiredEndpointId)
359+
uint16_t productId, const char * szApplicationVersion, std::vector<SupportedCluster> supportedClusters,
360+
EndpointId desiredEndpointId, jobject manager)
360361
{
361362
DataVersion * dataVersionBuf = new DataVersion[ArraySize(contentAppClusters)];
362-
ContentAppImpl * app = new ContentAppImpl(szVendorName, vendorId, szApplicationName, productId, szApplicationVersion, "",
363+
ContentAppImpl * app = new ContentAppImpl(szVendorName, vendorId, szApplicationName, productId, szApplicationVersion, "", std::move(supportedClusters),
363364
mAttributeDelegate, mCommandDelegate);
364365
EndpointId epId = ContentAppPlatform::GetInstance().AddContentApp(
365366
app, &contentAppEndpoint, Span<DataVersion>(dataVersionBuf, ArraySize(contentAppClusters)),
@@ -480,21 +481,21 @@ CHIP_ERROR InitVideoPlayerPlatform(jobject contentAppEndpointManager)
480481
}
481482

482483
EndpointId AddContentApp(const char * szVendorName, uint16_t vendorId, const char * szApplicationName, uint16_t productId,
483-
const char * szApplicationVersion, jobject manager)
484+
const char * szApplicationVersion, std::vector<SupportedCluster> supportedClusters, jobject manager)
484485
{
485486
#if CHIP_DEVICE_CONFIG_APP_PLATFORM_ENABLED
486487
ChipLogProgress(DeviceLayer, "AppImpl: AddContentApp vendorId=%d applicationName=%s ", vendorId, szApplicationName);
487-
return gFactory.AddContentApp(szVendorName, vendorId, szApplicationName, productId, szApplicationVersion, manager);
488+
return gFactory.AddContentApp(szVendorName, vendorId, szApplicationName, productId, szApplicationVersion, std::move(supportedClusters), manager);
488489
#endif // CHIP_DEVICE_CONFIG_APP_PLATFORM_ENABLED
489490
return kInvalidEndpointId;
490491
}
491492

492493
EndpointId AddContentApp(const char * szVendorName, uint16_t vendorId, const char * szApplicationName, uint16_t productId,
493-
const char * szApplicationVersion, EndpointId endpointId, jobject manager)
494+
const char * szApplicationVersion, std::vector<SupportedCluster> supportedClusters, EndpointId endpointId, jobject manager)
494495
{
495496
#if CHIP_DEVICE_CONFIG_APP_PLATFORM_ENABLED
496497
ChipLogProgress(DeviceLayer, "AppImpl: AddContentApp vendorId=%d applicationName=%s ", vendorId, szApplicationName);
497-
return gFactory.AddContentApp(szVendorName, vendorId, szApplicationName, productId, szApplicationVersion, manager, endpointId);
498+
return gFactory.AddContentApp(szVendorName, vendorId, szApplicationName, productId, szApplicationVersion, std::move(supportedClusters), endpointId, manager);
498499
#endif // CHIP_DEVICE_CONFIG_APP_PLATFORM_ENABLED
499500
return kInvalidEndpointId;
500501
}

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

+17-10
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
#include <lib/support/JniReferences.h>
3232
#include <stdbool.h>
3333
#include <stdint.h>
34+
#include <vector>
3435

3536
#include "../include/account-login/AccountLoginManager.h"
3637
#include "../include/application-basic/ApplicationBasicManager.h"
@@ -56,9 +57,11 @@
5657

5758
CHIP_ERROR InitVideoPlayerPlatform(jobject contentAppEndpointManager);
5859
EndpointId AddContentApp(const char * szVendorName, uint16_t vendorId, const char * szApplicationName, uint16_t productId,
59-
const char * szApplicationVersion, jobject manager);
60+
const char * szApplicationVersion, std::vector<chip::AppPlatform::ContentApp::SupportedCluster> supportedClusters,
61+
jobject manager);
6062
EndpointId AddContentApp(const char * szVendorName, uint16_t vendorId, const char * szApplicationName, uint16_t productId,
61-
const char * szApplicationVersion, EndpointId endpointId, jobject manager);
63+
const char * szApplicationVersion, std::vector<chip::AppPlatform::ContentApp::SupportedCluster> supportedClusters,
64+
EndpointId endpointId, jobject manager);
6265
EndpointId RemoveContentApp(EndpointId epId);
6366
void ReportAttributeChange(EndpointId epId, chip::ClusterId clusterId, chip::AttributeId attributeId);
6467

@@ -81,6 +84,7 @@ using TargetNavigatorDelegate = app::Clusters::TargetNavigator::Delegate;
8184
using SupportedProtocolsBitmap = app::Clusters::ContentLauncher::SupportedProtocolsBitmap;
8285
using ContentAppAttributeDelegate = chip::AppPlatform::ContentAppAttributeDelegate;
8386
using ContentAppCommandDelegate = chip::AppPlatform::ContentAppCommandDelegate;
87+
using SupportedCluster = chip::AppPlatform::ContentApp::SupportedCluster;
8488

8589
static const int kCatalogVendorId = CHIP_DEVICE_CONFIG_DEVICE_VENDOR_ID;
8690

@@ -91,8 +95,9 @@ class DLL_EXPORT ContentAppImpl : public ContentApp
9195
{
9296
public:
9397
ContentAppImpl(const char * szVendorName, uint16_t vendorId, const char * szApplicationName, uint16_t productId,
94-
const char * szApplicationVersion, const char * setupPIN, ContentAppAttributeDelegate * attributeDelegate,
95-
ContentAppCommandDelegate * commandDelegate) :
98+
const char * szApplicationVersion, const char * setupPIN, std::vector<SupportedCluster> supportedClusters,
99+
ContentAppAttributeDelegate * attributeDelegate, ContentAppCommandDelegate * commandDelegate) :
100+
ContentApp{ supportedClusters },
96101
mApplicationBasicDelegate(kCatalogVendorId, BuildAppId(vendorId), szVendorName, vendorId, szApplicationName, productId,
97102
szApplicationVersion),
98103
mAccountLoginDelegate(commandDelegate, setupPIN),
@@ -160,10 +165,12 @@ class DLL_EXPORT ContentAppFactoryImpl : public ContentAppFactory
160165
ContentApp * LoadContentApp(const CatalogVendorApp & vendorApp) override;
161166

162167
EndpointId AddContentApp(const char * szVendorName, uint16_t vendorId, const char * szApplicationName, uint16_t productId,
163-
const char * szApplicationVersion, jobject manager);
168+
const char * szApplicationVersion, std::vector<SupportedCluster> supportedClusters,
169+
jobject manager);
164170

165171
EndpointId AddContentApp(const char * szVendorName, uint16_t vendorId, const char * szApplicationName, uint16_t productId,
166-
const char * szApplicationVersion, jobject manager, EndpointId desiredEndpointId);
172+
const char * szApplicationVersion, std::vector<SupportedCluster> supportedClusters,
173+
EndpointId desiredEndpointId, jobject manager);
167174

168175
EndpointId RemoveContentApp(EndpointId epId);
169176

@@ -193,10 +200,10 @@ class DLL_EXPORT ContentAppFactoryImpl : public ContentAppFactory
193200

194201
protected:
195202
std::vector<ContentAppImpl *> mContentApps{
196-
new ContentAppImpl("Vendor1", 1, "exampleid", 11, "Version1", "20202021", nullptr, nullptr),
197-
new ContentAppImpl("Vendor2", 65521, "exampleString", 32768, "Version2", "20202021", nullptr, nullptr),
198-
new ContentAppImpl("Vendor3", 9050, "App3", 22, "Version3", "20202021", nullptr, nullptr),
199-
new ContentAppImpl("TestSuiteVendor", 1111, "applicationId", 22, "v2", "20202021", nullptr, nullptr)
203+
new ContentAppImpl("Vendor1", 1, "exampleid", 11, "Version1", "20202021", {}, nullptr, nullptr),
204+
new ContentAppImpl("Vendor2", 65521, "exampleString", 32768, "Version2", "20202021", {}, nullptr, nullptr),
205+
new ContentAppImpl("Vendor3", 9050, "App3", 22, "Version3", "20202021", {}, nullptr, nullptr),
206+
new ContentAppImpl("TestSuiteVendor", 1111, "applicationId", 22, "v2", "20202021", {}, nullptr, nullptr)
200207
};
201208
std::vector<DataVersion *> mDataVersions{};
202209

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

+83-4
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include <lib/support/CHIPJNIError.h>
2424
#include <lib/support/JniReferences.h>
2525
#include <lib/support/JniTypeWrappers.h>
26+
#include <app/app-platform/ContentApp.h>
2627

2728
using namespace chip;
2829
using namespace chip::app;
@@ -34,6 +35,9 @@ using namespace chip::Credentials;
3435
* com.matter.tv.server.tvapp.AppPlatform class.
3536
*/
3637

38+
// Forward declaration
39+
std::vector<ContentApp::SupportedCluster> convert_to_cpp(JNIEnv* env, jobject supportedClusters);
40+
3741
#define JNI_METHOD(RETURN, METHOD_NAME) \
3842
extern "C" JNIEXPORT RETURN JNICALL Java_com_matter_tv_server_tvapp_AppPlatform_##METHOD_NAME
3943

@@ -44,7 +48,8 @@ JNI_METHOD(void, nativeInit)(JNIEnv *, jobject app, jobject contentAppEndpointMa
4448
}
4549

4650
JNI_METHOD(jint, addContentApp)
47-
(JNIEnv *, jobject, jstring vendorName, jint vendorId, jstring appName, jint productId, jstring appVersion, jobject manager)
51+
(JNIEnv *, jobject, jstring vendorName, jint vendorId, jstring appName, jint productId, jstring appVersion, jobject supportedClusters,
52+
jobject manager)
4853
{
4954
chip::DeviceLayer::StackLock lock;
5055
JNIEnv * env = JniReferences::GetInstance().GetEnvForCurrentThread();
@@ -53,12 +58,12 @@ JNI_METHOD(jint, addContentApp)
5358
JniUtfString aName(env, appName);
5459
JniUtfString aVersion(env, appVersion);
5560
EndpointId epId = AddContentApp(vName.c_str(), static_cast<uint16_t>(vendorId), aName.c_str(), static_cast<uint16_t>(productId),
56-
aVersion.c_str(), manager);
61+
aVersion.c_str(), convert_to_cpp(env, supportedClusters), manager);
5762
return static_cast<uint16_t>(epId);
5863
}
5964

6065
JNI_METHOD(jint, addContentAppAtEndpoint)
61-
(JNIEnv *, jobject, jstring vendorName, jint vendorId, jstring appName, jint productId, jstring appVersion, jint endpointId,
66+
(JNIEnv *, jobject, jstring vendorName, jint vendorId, jstring appName, jint productId, jstring appVersion, jobject supportedClusters, jint endpointId,
6267
jobject manager)
6368
{
6469
chip::DeviceLayer::StackLock lock;
@@ -68,7 +73,7 @@ JNI_METHOD(jint, addContentAppAtEndpoint)
6873
JniUtfString aName(env, appName);
6974
JniUtfString aVersion(env, appVersion);
7075
EndpointId epId = AddContentApp(vName.c_str(), static_cast<uint16_t>(vendorId), aName.c_str(), static_cast<uint16_t>(productId),
71-
aVersion.c_str(), static_cast<EndpointId>(endpointId), manager);
76+
aVersion.c_str(), convert_to_cpp(env, supportedClusters), static_cast<EndpointId>(endpointId), manager);
7277
return static_cast<uint16_t>(epId);
7378
}
7479

@@ -93,3 +98,77 @@ JNI_METHOD(void, addSelfVendorAsAdmin)
9398
{
9499
AddSelfVendorAsAdmin();
95100
}
101+
102+
std::vector<uint32_t> consume_and_convert_to_cpp(JNIEnv* env, jobject intArrayObject) {
103+
std::vector<uint32_t> uintVector;
104+
if (intArrayObject != nullptr) {
105+
jsize length = env->GetArrayLength(static_cast<jintArray>(intArrayObject));
106+
jint* elements = env->GetIntArrayElements(static_cast<jintArray>(intArrayObject), nullptr);
107+
if (elements != nullptr) {
108+
// OBS: Implicit type ambiguation from int32_t to uint32_t
109+
uintVector.assign(elements, elements + length);
110+
env->ReleaseIntArrayElements(static_cast<jintArray>(intArrayObject), elements, JNI_ABORT);
111+
}
112+
env->DeleteLocalRef(intArrayObject);
113+
}
114+
return uintVector;
115+
}
116+
117+
std::vector<ContentApp::SupportedCluster> convert_to_cpp(JNIEnv* env, jobject supportedClustersObject) {
118+
if (supportedClustersObject == nullptr || env == nullptr) {
119+
return {};
120+
}
121+
122+
// Find Java classes. WARNING: Reflection
123+
jclass collectionClass = env->FindClass("java/util/Collection");
124+
jclass iteratorClass = env->FindClass("java/util/Iterator");
125+
jclass clusterClass = env->FindClass("com/matter/tv/server/tvapp/SupportedCluster");
126+
if (collectionClass == nullptr || iteratorClass == nullptr || clusterClass == nullptr) {
127+
return {};
128+
}
129+
130+
// Find Java methods. WARNING: Reflection
131+
jmethodID iteratorMethod = env->GetMethodID(collectionClass, "iterator", "()Ljava/util/Iterator;");
132+
jmethodID hasNextMethod = env->GetMethodID(iteratorClass, "hasNext", "()Z");
133+
jmethodID nextMethod = env->GetMethodID(iteratorClass, "next", "()Ljava/lang/Object;");
134+
if (iteratorMethod == nullptr || hasNextMethod == nullptr || nextMethod == nullptr) {
135+
return {};
136+
}
137+
138+
// Find Java SupportedCluster fields. WARNING: Reflection
139+
jfieldID clusterIdentifierField = env->GetFieldID(clusterClass, "clusterIdentifier", "I");
140+
jfieldID featuresField = env->GetFieldID(clusterClass, "features", "I");
141+
jfieldID optionalCommandIdentifiersField = env->GetFieldID(clusterClass, "optionalCommandIdentifiers", "[I");
142+
jfieldID optionalAttributesIdentifiersField = env->GetFieldID(clusterClass, "optionalAttributesIdentifiers", "[I");
143+
if (clusterIdentifierField == nullptr || featuresField == nullptr || optionalCommandIdentifiersField == nullptr || optionalAttributesIdentifiersField == nullptr) {
144+
return {};
145+
}
146+
147+
// Find Set Iterator Object
148+
jobject iteratorObject = env->CallObjectMethod(supportedClustersObject, iteratorMethod);
149+
if (iteratorObject == nullptr) {
150+
return {};
151+
}
152+
153+
// Iterate over the Java Collection and convert each SupportedCluster
154+
std::vector<SupportedCluster> supportedClusters;
155+
while (env->CallBooleanMethod(iteratorObject, hasNextMethod)) {
156+
jobject clusterObject = env->CallObjectMethod(iteratorObject, nextMethod);
157+
if (clusterObject != nullptr) {
158+
jint clusterIdentifier = env->GetIntField(clusterObject, clusterIdentifierField);
159+
jint features = env->GetIntField(clusterObject, featuresField);
160+
jobject commandIdsObject = env->GetObjectField(clusterObject, optionalCommandIdentifiersField);
161+
jobject attributeIdsObject = env->GetObjectField(clusterObject, optionalAttributesIdentifiersField);
162+
// OBS: Type ambiguation from int32_t to uint32_t
163+
supportedClusters.emplace_back(
164+
static_cast<ClusterId>(clusterIdentifier),
165+
static_cast<uint32_t>(features),
166+
consume_and_convert_to_cpp(env, commandIdsObject),
167+
consume_and_convert_to_cpp(env, attributeIdsObject));
168+
env->DeleteLocalRef(clusterObject);
169+
}
170+
}
171+
env->DeleteLocalRef(iteratorObject);
172+
173+
return supportedClusters;
174+
}

examples/tv-app/android/java/src/com/matter/tv/server/tvapp/AppPlatform.java

+4
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
*/
1818
package com.matter.tv.server.tvapp;
1919

20+
import java.util.Collection;
21+
2022
/*
2123
* This class is provides the JNI interface to the linux layer of the ContentAppPlatform
2224
*/
@@ -37,6 +39,7 @@ public native int addContentApp(
3739
String appName,
3840
int productId,
3941
String appVersion,
42+
Collection<ContentAppSupportedCluster> supportedClusters,
4043
ContentAppEndpointManager manager);
4144

4245
// Method to add a content app at an existing endpoint after restart of the matter server
@@ -46,6 +49,7 @@ public native int addContentAppAtEndpoint(
4649
String appName,
4750
int productId,
4851
String appVersion,
52+
Collection<ContentAppSupportedCluster> supportedClusters,
4953
int endpointId,
5054
ContentAppEndpointManager manager);
5155

0 commit comments

Comments
 (0)