From 7690b965074854034d19d759223f039b1cfb8d12 Mon Sep 17 00:00:00 2001
From: amitnj <74272437+amitnj@users.noreply.github.com>
Date: Tue, 14 Jun 2022 06:56:11 -0700
Subject: [PATCH] Enable command passing from clients to content apps (#19506)
---
.../tv/app/api/MatterIntentConstants.java | 3 +
.../content-app/src/main/AndroidManifest.xml | 2 +-
.../receiver/MatterCommandReceiver.java | 15 ++-
.../src/main/res/raw/static_matter_clusters | 14 ++-
.../platform-app/src/main/AndroidManifest.xml | 1 -
.../ContentAppEndpointManagerImpl.java | 5 +-
.../service/ContentAppAgentService.java | 33 ++++---
.../tv/server/service/MatterServant.java | 2 +-
.../tv/server/service/ResponseRegistry.java | 58 ++++++++++++
examples/tv-app/android/BUILD.gn | 2 +-
examples/tv-app/android/java/AppImpl.cpp | 42 ++++++---
examples/tv-app/android/java/AppImpl.h | 14 +--
.../java/ContentAppCommandDelegate.cpp | 93 +++++++++++++++++++
.../android/java/ContentAppCommandDelegate.h | 34 +++++--
examples/tv-app/android/java/TVApp-JNI.cpp | 4 +-
.../chip/tvapp/ContentAppEndpointManager.java | 2 +-
.../tvapp/ContentAppEndpointManagerStub.java | 15 ---
.../java/src/com/tcl/chip/tvapp/TvApp.java | 2 +-
18 files changed, 270 insertions(+), 71 deletions(-)
create mode 100644 examples/tv-app/android/App/platform-app/src/main/java/com/matter/tv/server/service/ResponseRegistry.java
delete mode 100644 examples/tv-app/android/java/src/com/tcl/chip/tvapp/ContentAppEndpointManagerStub.java
diff --git a/examples/tv-app/android/App/common-api/src/main/java/com/matter/tv/app/api/MatterIntentConstants.java b/examples/tv-app/android/App/common-api/src/main/java/com/matter/tv/app/api/MatterIntentConstants.java
index 4ee15e4a84c496..d5e95061b6f6c5 100644
--- a/examples/tv-app/android/App/common-api/src/main/java/com/matter/tv/app/api/MatterIntentConstants.java
+++ b/examples/tv-app/android/App/common-api/src/main/java/com/matter/tv/app/api/MatterIntentConstants.java
@@ -17,4 +17,7 @@ public class MatterIntentConstants {
public static final String EXTRA_DIRECTIVE_RESPONSE_PENDING_INTENT =
"EXTRA_DIRECTIVE_RESPONSE_PENDING_INTENT";
+
+ public static final String EXTRA_COMMAND_ID = "EXTRA_COMMAND_ID";
+ public static final String EXTRA_CLUSTER_ID = "EXTRA_CLUSTER_ID";
}
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 9ba74639750d1d..e9fa3b028f6f58 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
@@ -18,7 +18,7 @@
android:theme="@style/Theme.ContentApp">
-
+
-
diff --git a/examples/tv-app/android/App/platform-app/src/main/java/com/matter/tv/server/handlers/ContentAppEndpointManagerImpl.java b/examples/tv-app/android/App/platform-app/src/main/java/com/matter/tv/server/handlers/ContentAppEndpointManagerImpl.java
index 035e7cd84e8c44..56575545883a32 100644
--- a/examples/tv-app/android/App/platform-app/src/main/java/com/matter/tv/server/handlers/ContentAppEndpointManagerImpl.java
+++ b/examples/tv-app/android/App/platform-app/src/main/java/com/matter/tv/server/handlers/ContentAppEndpointManagerImpl.java
@@ -16,14 +16,15 @@ public ContentAppEndpointManagerImpl(Context context) {
this.context = context;
}
- public String sendCommand(int endpointId, String commandPayload) {
+ public String sendCommand(int endpointId, int clusterId, int commandId, String commandPayload) {
Log.d(TAG, "Received a command for endpointId " + endpointId + ". Message " + commandPayload);
for (ContentApp app :
ContentAppDiscoveryService.getReceiverInstance().getDiscoveredContentApps().values()) {
if (app.getEndpointId() == endpointId) {
Log.d(
TAG, "Sending a command for endpointId " + endpointId + ". Message " + commandPayload);
- ContentAppAgentService.sendCommand(context, app.getAppName(), commandPayload);
+ return ContentAppAgentService.sendCommand(
+ context, app.getAppName(), clusterId, commandId, commandPayload);
}
}
return "Success";
diff --git a/examples/tv-app/android/App/platform-app/src/main/java/com/matter/tv/server/service/ContentAppAgentService.java b/examples/tv-app/android/App/platform-app/src/main/java/com/matter/tv/server/service/ContentAppAgentService.java
index b1799990f99575..ccc72e4a9221b2 100644
--- a/examples/tv-app/android/App/platform-app/src/main/java/com/matter/tv/server/service/ContentAppAgentService.java
+++ b/examples/tv-app/android/App/platform-app/src/main/java/com/matter/tv/server/service/ContentAppAgentService.java
@@ -11,6 +11,7 @@
import androidx.annotation.Nullable;
import com.matter.tv.app.api.IMatterAppAgent;
import com.matter.tv.app.api.MatterIntentConstants;
+import java.util.concurrent.TimeUnit;
public class ContentAppAgentService extends Service {
@@ -20,6 +21,8 @@ public class ContentAppAgentService extends Service {
public static final String EXTRA_RESPONSE_RECEIVING_PACKAGE = "EXTRA_RESPONSE_RECEIVING_PACKAGE";
public static final String EXTRA_RESPONSE_ID = "EXTRA_RESPONSE_ID";
+ private static ResponseRegistry responseRegistry = new ResponseRegistry();
+
private final IBinder appAgentBinder =
new IMatterAppAgent.Stub() {
@Override
@@ -51,23 +54,34 @@ public IBinder onBind(final Intent intent) {
return null;
}
- public static void sendCommand(Context context, String packageName, String payload) {
+ public static String sendCommand(
+ Context context, String packageName, int clusterId, int commandId, String payload) {
Intent in = new Intent(MatterIntentConstants.ACTION_MATTER_COMMAND);
Bundle extras = new Bundle();
extras.putByteArray(MatterIntentConstants.EXTRA_COMMAND_PAYLOAD, payload.getBytes());
+ extras.putInt(MatterIntentConstants.EXTRA_COMMAND_ID, commandId);
+ extras.putInt(MatterIntentConstants.EXTRA_CLUSTER_ID, clusterId);
in.putExtras(extras);
in.setPackage(packageName);
int flags = Intent.FLAG_INCLUDE_STOPPED_PACKAGES;
flags |= Intent.FLAG_RECEIVER_FOREGROUND;
in.setFlags(flags);
+ int messageId = responseRegistry.getNextMessageCounter();
in.putExtra(
MatterIntentConstants.EXTRA_DIRECTIVE_RESPONSE_PENDING_INTENT,
- getPendingIntentForResponse(context, packageName, "0"));
+ getPendingIntentForResponse(context, packageName, messageId));
context.sendBroadcast(in);
+ responseRegistry.waitForMessage(messageId, 10, TimeUnit.SECONDS);
+ String response = responseRegistry.readAndRemoveResponse(messageId);
+ if (response == null) {
+ response = "";
+ }
+ Log.d(TAG, "Response " + response + " being returned for message " + messageId);
+ return response;
}
private static PendingIntent getPendingIntentForResponse(
- final Context context, final String targetPackage, final String responseId) {
+ final Context context, final String targetPackage, final int responseId) {
Intent ackBackIntent = new Intent(ACTION_MATTER_RESPONSE);
ackBackIntent.setClass(context, ContentAppAgentService.class);
ackBackIntent.putExtra(EXTRA_RESPONSE_RECEIVING_PACKAGE, targetPackage);
@@ -78,16 +92,13 @@ private static PendingIntent getPendingIntentForResponse(
@Override
public int onStartCommand(final Intent intent, final int flags, final int startId) {
- Log.d(TAG, "onStartCommand");
-
if (intent != null && ACTION_MATTER_RESPONSE.equals(intent.getAction())) {
- Log.d(
- TAG,
- "Command response "
- + new String(intent.getByteArrayExtra(MatterIntentConstants.EXTRA_RESPONSE_PAYLOAD)));
- // Send the response back to the client.
+ String response =
+ new String(intent.getByteArrayExtra(MatterIntentConstants.EXTRA_RESPONSE_PAYLOAD));
+ int messageId = intent.getIntExtra(EXTRA_RESPONSE_ID, Integer.MAX_VALUE);
+ Log.d(TAG, "Response " + response + " received for message " + messageId);
+ responseRegistry.receivedMessageResponse(messageId, response);
}
-
return START_NOT_STICKY;
}
}
diff --git a/examples/tv-app/android/App/platform-app/src/main/java/com/matter/tv/server/service/MatterServant.java b/examples/tv-app/android/App/platform-app/src/main/java/com/matter/tv/server/service/MatterServant.java
index e248a4bcc300f6..bd11cb5c525d8f 100644
--- a/examples/tv-app/android/App/platform-app/src/main/java/com/matter/tv/server/service/MatterServant.java
+++ b/examples/tv-app/android/App/platform-app/src/main/java/com/matter/tv/server/service/MatterServant.java
@@ -145,7 +145,7 @@ public void onCommissioningComplete() {
chipAppServer = new ChipAppServer();
chipAppServer.startApp();
- mTvApp.postServerInit();
+ mTvApp.postServerInit(new ContentAppEndpointManagerImpl(context));
}
public void restart() {
diff --git a/examples/tv-app/android/App/platform-app/src/main/java/com/matter/tv/server/service/ResponseRegistry.java b/examples/tv-app/android/App/platform-app/src/main/java/com/matter/tv/server/service/ResponseRegistry.java
new file mode 100644
index 00000000000000..9c1ebc0c0b6031
--- /dev/null
+++ b/examples/tv-app/android/App/platform-app/src/main/java/com/matter/tv/server/service/ResponseRegistry.java
@@ -0,0 +1,58 @@
+package com.matter.tv.server.service;
+
+import android.util.Log;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+
+public class ResponseRegistry {
+
+ private static final String TAG = "ResponseRegistry";
+
+ private AtomicInteger messageCounter = new AtomicInteger();
+
+ private Map responses = new ConcurrentHashMap<>();
+
+ private Map latches = new ConcurrentHashMap<>();
+
+ public int getNextMessageCounter() {
+ int counter = messageCounter.incrementAndGet();
+ // MAX_VALUE used for error scenarios
+ if (counter == Integer.MAX_VALUE) {
+ counter = messageCounter.incrementAndGet();
+ }
+ latches.put(counter, new CountDownLatch(1));
+ return counter;
+ }
+
+ public void waitForMessage(int counter, long timeout, TimeUnit unit) {
+ CountDownLatch latch = latches.get(counter);
+ if (latch == null) {
+ return;
+ }
+ try {
+ if (!latch.await(timeout, unit)) {
+ Log.i(TAG, "Timed out while waiting for response for message " + counter);
+ }
+ } catch (InterruptedException e) {
+ Log.i(TAG, "Interrupted while waiting for response for message " + counter);
+ }
+ }
+
+ public void receivedMessageResponse(int counter, String response) {
+ CountDownLatch latch = latches.remove(counter);
+ if (latch == null) {
+ // no point adding response to memory if no one is going to read it.
+ return;
+ }
+ responses.put(counter, response);
+ latch.countDown();
+ }
+
+ public String readAndRemoveResponse(int counter) {
+ // caller should manage null values
+ return responses.remove(counter);
+ }
+}
diff --git a/examples/tv-app/android/BUILD.gn b/examples/tv-app/android/BUILD.gn
index b227f97fe47101..e478c946d3b9d7 100644
--- a/examples/tv-app/android/BUILD.gn
+++ b/examples/tv-app/android/BUILD.gn
@@ -78,6 +78,7 @@ shared_library("jni") {
"${chip_root}/examples/tv-app/tv-common",
"${chip_root}/src/app/server/java:jni",
"${chip_root}/src/lib",
+ "${chip_root}/src/lib/support/jsontlv",
"${chip_root}/third_party/inipp",
]
@@ -108,7 +109,6 @@ android_library("java") {
"java/src/com/tcl/chip/tvapp/ChannelManagerStub.java",
"java/src/com/tcl/chip/tvapp/Clusters.java",
"java/src/com/tcl/chip/tvapp/ContentAppEndpointManager.java",
- "java/src/com/tcl/chip/tvapp/ContentAppEndpointManagerStub.java",
"java/src/com/tcl/chip/tvapp/ContentLaunchBrandingInformation.java",
"java/src/com/tcl/chip/tvapp/ContentLaunchManager.java",
"java/src/com/tcl/chip/tvapp/ContentLaunchManagerStub.java",
diff --git a/examples/tv-app/android/java/AppImpl.cpp b/examples/tv-app/android/java/AppImpl.cpp
index 61f770a4c09383..a0564d2efc3832 100644
--- a/examples/tv-app/android/java/AppImpl.cpp
+++ b/examples/tv-app/android/java/AppImpl.cpp
@@ -21,11 +21,13 @@
#include "AppImpl.h"
+#include "ContentAppCommandDelegate.h"
#include
#include
#include
#include
#include
+#include
#include
#include
#include
@@ -328,12 +330,12 @@ ContentApp * ContentAppFactoryImpl::LoadContentApp(const CatalogVendorApp & vend
{
auto & app = mContentApps.at(i);
- ChipLogProgress(DeviceLayer, " Looking next=%s ", app.GetApplicationBasicDelegate()->GetCatalogVendorApp()->applicationId);
- if (app.GetApplicationBasicDelegate()->GetCatalogVendorApp()->Matches(vendorApp))
+ ChipLogProgress(DeviceLayer, " Looking next=%s ", app->GetApplicationBasicDelegate()->GetCatalogVendorApp()->applicationId);
+ if (app->GetApplicationBasicDelegate()->GetCatalogVendorApp()->Matches(vendorApp))
{
- ContentAppPlatform::GetInstance().AddContentApp(&app, &contentAppEndpoint, Span(gDataVersions[i]),
+ ContentAppPlatform::GetInstance().AddContentApp(app, &contentAppEndpoint, Span(gDataVersions[i]),
Span(gContentAppDeviceType));
- return &app;
+ return app;
}
}
ChipLogProgress(DeviceLayer, "LoadContentAppByAppId NOT FOUND catalogVendorId=%d applicationId=%s ", vendorApp.catalogVendorId,
@@ -342,13 +344,13 @@ ContentApp * ContentAppFactoryImpl::LoadContentApp(const CatalogVendorApp & vend
return nullptr;
}
-EndpointId ContentAppFactoryImpl::AddContentApp(ContentAppImpl & app)
+EndpointId ContentAppFactoryImpl::AddContentApp(ContentAppImpl * app)
{
DataVersion dataVersionBuf[ArraySize(contentAppClusters)];
- EndpointId epId = ContentAppPlatform::GetInstance().AddContentApp(&app, &contentAppEndpoint, Span(dataVersionBuf),
+ EndpointId epId = ContentAppPlatform::GetInstance().AddContentApp(app, &contentAppEndpoint, Span(dataVersionBuf),
Span(gContentAppDeviceType));
ChipLogProgress(DeviceLayer, "ContentAppFactoryImpl AddContentApp endpoint returned %d. Endpoint set %d", epId,
- app.GetEndpointId());
+ app->GetEndpointId());
mContentApps.push_back(app);
return epId;
}
@@ -380,9 +382,9 @@ void ContentAppFactoryImpl::SendTestMessage(EndpointId epId, const char * messag
ChipLogProgress(DeviceLayer, "ContentAppFactoryImpl SendTestMessage called with message %s & endpointId %d", message, epId);
for (size_t i = 0; i < mContentApps.size(); ++i)
{
- ContentAppImpl app = mContentApps.at(i);
- ChipLogProgress(DeviceLayer, "ContentAppFactoryImpl checking app with endpointId %d", app.ContentApp::GetEndpointId());
- if (app.GetEndpointId() == epId)
+ ContentAppImpl * app = mContentApps.at(i);
+ ChipLogProgress(DeviceLayer, "ContentAppFactoryImpl checking app with endpointId %d", app->GetEndpointId());
+ if (app->GetEndpointId() == epId)
{
ChipLogProgress(DeviceLayer, "ContentAppFactoryImpl SendTestMessage endpoint found");
app::ConcreteCommandPath commandPath(epId, app::Clusters::ContentLauncher::Id,
@@ -391,8 +393,8 @@ void ContentAppFactoryImpl::SendTestMessage(EndpointId epId, const char * messag
app::CommandHandler commandHandler(&callback);
CommandResponseHelper helper(&commandHandler, commandPath);
chip::app::Clusters::ContentLauncher::Structs::BrandingInformation::Type branding;
- app.GetContentLauncherDelegate()->HandleLaunchUrl(helper, CharSpan::fromCharString(message),
- CharSpan::fromCharString("Temp Display"), branding);
+ app->GetContentLauncherDelegate()->HandleLaunchUrl(helper, CharSpan::fromCharString(message),
+ CharSpan::fromCharString("Temp Display"), branding);
}
}
}
@@ -402,11 +404,21 @@ void ContentAppFactoryImpl::SendTestMessage(EndpointId epId, const char * messag
#endif // CHIP_DEVICE_CONFIG_APP_PLATFORM_ENABLED
-CHIP_ERROR InitVideoPlayerPlatform(JNIMyUserPrompter * userPrompter)
+CHIP_ERROR InitVideoPlayerPlatform(JNIMyUserPrompter * userPrompter, jobject contentAppEndpointManager)
{
#if CHIP_DEVICE_CONFIG_APP_PLATFORM_ENABLED
ContentAppPlatform::GetInstance().SetupAppPlatform();
ContentAppPlatform::GetInstance().SetContentAppFactory(&gFactory);
+
+ ChipLogProgress(AppServer, "Starting registration of command handler delegates");
+ for (size_t i = 0; i < ArraySize(contentAppClusters); i++)
+ {
+ ContentAppCommandDelegate * delegate =
+ new ContentAppCommandDelegate(contentAppEndpointManager, contentAppClusters[i].clusterId);
+ chip::app::InteractionModelEngine::GetInstance()->RegisterCommandHandler(delegate);
+ ChipLogProgress(AppServer, "Registered command handler delegate for cluster %d", contentAppClusters[i].clusterId);
+ }
+
#endif // CHIP_DEVICE_CONFIG_APP_PLATFORM_ENABLED
#if CHIP_DEVICE_CONFIG_ENABLE_BOTH_COMMISSIONER_AND_COMMISSIONEE
@@ -443,8 +455,8 @@ EndpointId AddContentApp(const char * szVendorName, uint16_t vendorId, const cha
const char * szApplicationVersion, jobject manager)
{
#if CHIP_DEVICE_CONFIG_APP_PLATFORM_ENABLED
- ContentAppImpl app =
- ContentAppImpl(szVendorName, vendorId, szApplicationName, productId, szApplicationVersion, "34567890", manager);
+ ContentAppImpl * app =
+ new ContentAppImpl(szVendorName, vendorId, szApplicationName, productId, szApplicationVersion, "20202021", manager);
ChipLogProgress(DeviceLayer, "AppImpl: AddContentApp vendorId=%d applicationName=%s ", vendorId, szApplicationName);
return gFactory.AddContentApp(app);
#endif // CHIP_DEVICE_CONFIG_APP_PLATFORM_ENABLED
diff --git a/examples/tv-app/android/java/AppImpl.h b/examples/tv-app/android/java/AppImpl.h
index bcd31ccd061fb9..98a6d61798bc07 100644
--- a/examples/tv-app/android/java/AppImpl.h
+++ b/examples/tv-app/android/java/AppImpl.h
@@ -52,7 +52,7 @@
#include
#include
-CHIP_ERROR InitVideoPlayerPlatform(JNIMyUserPrompter * userPrompter);
+CHIP_ERROR InitVideoPlayerPlatform(JNIMyUserPrompter * userPrompter, jobject contentAppEndpointManager);
CHIP_ERROR PreServerInit();
EndpointId AddContentApp(const char * szVendorName, uint16_t vendorId, const char * szApplicationName, uint16_t productId,
const char * szApplicationVersion, jobject manager);
@@ -131,7 +131,7 @@ class DLL_EXPORT ContentAppFactoryImpl : public ContentAppFactory
// Lookup ContentApp for this catalog id / app id and load it
ContentApp * LoadContentApp(const CatalogVendorApp & vendorApp) override;
- EndpointId AddContentApp(ContentAppImpl & app);
+ EndpointId AddContentApp(ContentAppImpl * app);
void SendTestMessage(EndpointId epID, const char * message);
@@ -143,11 +143,11 @@ class DLL_EXPORT ContentAppFactoryImpl : public ContentAppFactory
CHIP_ERROR ConvertToPlatformCatalogVendorApp(const CatalogVendorApp & sourceApp, CatalogVendorApp * destinationApp) override;
protected:
- std::vector mContentApps{
- ContentAppImpl("Vendor1", 1, "exampleid", 11, "Version1", "34567890", nullptr),
- ContentAppImpl("Vendor2", 65521, "exampleString", 32768, "Version2", "20202021", nullptr),
- ContentAppImpl("Vendor3", 9050, "App3", 22, "Version3", "20202021", nullptr),
- ContentAppImpl("TestSuiteVendor", 1111, "applicationId", 22, "v2", "20202021", nullptr)
+ std::vector mContentApps{
+ new ContentAppImpl("Vendor1", 1, "exampleid", 11, "Version1", "20202021", nullptr),
+ new ContentAppImpl("Vendor2", 65520, "exampleString", 32768, "Version2", "20202021", nullptr),
+ new ContentAppImpl("Vendor3", 9050, "App3", 22, "Version3", "20202021", nullptr),
+ new ContentAppImpl("TestSuiteVendor", 1111, "applicationId", 22, "v2", "20202021", nullptr)
};
};
diff --git a/examples/tv-app/android/java/ContentAppCommandDelegate.cpp b/examples/tv-app/android/java/ContentAppCommandDelegate.cpp
index a4d23881b6be3f..1cc25f57ee6098 100644
--- a/examples/tv-app/android/java/ContentAppCommandDelegate.cpp
+++ b/examples/tv-app/android/java/ContentAppCommandDelegate.cpp
@@ -22,14 +22,21 @@
#include "ContentAppCommandDelegate.h"
+#include
+#include
#include
#include
#include
#include
+#include
+#include
namespace chip {
namespace AppPlatform {
+using CommandHandlerInterface = chip::app::CommandHandlerInterface;
+using LaunchResponseType = chip::app::Clusters::ContentLauncher::Commands::LaunchResponse::Type;
+
const char * ContentAppCommandDelegate::sendCommand(chip::EndpointId epID, std::string commandPayload)
{
// to support the hardcoded sample apps.
@@ -55,5 +62,91 @@ const char * ContentAppCommandDelegate::sendCommand(chip::EndpointId epID, std::
return ret;
}
+void ContentAppCommandDelegate::InvokeCommand(CommandHandlerInterface::HandlerContext & handlerContext)
+{
+ if (handlerContext.mRequestPath.mEndpointId >= FIXED_ENDPOINT_COUNT)
+ {
+ TLV::TLVReader readerForJson;
+ readerForJson.Init(handlerContext.mPayload);
+
+ CHIP_ERROR err = CHIP_NO_ERROR;
+ Json::Value json;
+ err = TlvToJson(readerForJson, json);
+ if (err != CHIP_NO_ERROR)
+ {
+ // TODO : Add an interface to let the apps know a message came but there was a serialization error.
+ handlerContext.SetCommandNotHandled();
+ return;
+ }
+
+ JNIEnv * env = JniReferences::GetInstance().GetEnvForCurrentThread();
+ UtfString jsonString(env, JsonToString(json).c_str());
+
+ ChipLogProgress(Zcl, "ContentAppCommandDelegate::InvokeCommand send command being called with payload %s",
+ JsonToString(json).c_str());
+
+ jstring resp = (jstring) env->CallObjectMethod(
+ mContentAppEndpointManager, mSendCommandMethod, static_cast(handlerContext.mRequestPath.mEndpointId),
+ static_cast(handlerContext.mRequestPath.mClusterId), static_cast(handlerContext.mRequestPath.mCommandId),
+ jsonString.jniValue());
+ if (env->ExceptionCheck())
+ {
+ ChipLogError(Zcl, "Java exception in ContentAppCommandDelegate::sendCommand");
+ env->ExceptionDescribe();
+ env->ExceptionClear();
+ FormatResponseData(handlerContext, "{\"value\":{}}");
+ return;
+ }
+ const char * respStr = env->GetStringUTFChars(resp, 0);
+ ChipLogProgress(Zcl, "ContentAppCommandDelegate::InvokeCommand got response %s", respStr);
+ FormatResponseData(handlerContext, respStr);
+ }
+ else
+ {
+ handlerContext.SetCommandNotHandled();
+ }
+}
+
+void ContentAppCommandDelegate::FormatResponseData(CommandHandlerInterface::HandlerContext & handlerContext, const char * response)
+{
+ Json::Reader reader;
+ Json::Value resJson;
+ reader.parse(response, resJson);
+ Json::Value value = resJson["value"];
+
+ switch (handlerContext.mRequestPath.mClusterId)
+ {
+ case app::Clusters::ContentLauncher::Id: {
+ LaunchResponseType launchResponse;
+ if (value["0"].empty())
+ {
+ launchResponse.status = chip::app::Clusters::ContentLauncher::ContentLaunchStatusEnum::kAuthFailed;
+ }
+ else
+ {
+ launchResponse.status = static_cast(value["0"].asInt());
+ if (!value["1"].empty())
+ {
+ launchResponse.data = chip::MakeOptional(CharSpan::fromCharString(value["1"].asCString()));
+ }
+ }
+ handlerContext.mCommandHandler.AddResponseData(handlerContext.mRequestPath, launchResponse);
+ handlerContext.SetCommandHandled();
+ break;
+ }
+
+ // case app::Clusters::TargetNavigator::Id:
+ // break;
+
+ // case app::Clusters::MediaPlayback::Id:
+ // break;
+
+ // case app::Clusters::AccountLogin::Id:
+ // break;
+ default:
+ handlerContext.SetCommandNotHandled();
+ }
+}
+
} // namespace AppPlatform
} // namespace chip
diff --git a/examples/tv-app/android/java/ContentAppCommandDelegate.h b/examples/tv-app/android/java/ContentAppCommandDelegate.h
index 2d45906c51049b..908b2e4d1cc0e9 100644
--- a/examples/tv-app/android/java/ContentAppCommandDelegate.h
+++ b/examples/tv-app/android/java/ContentAppCommandDelegate.h
@@ -22,6 +22,8 @@
#pragma once
+#include
+#include
#include
#include
#include
@@ -29,10 +31,22 @@
namespace chip {
namespace AppPlatform {
-class ContentAppCommandDelegate
+using CommandHandlerInterface = chip::app::CommandHandlerInterface;
+
+class ContentAppCommandDelegate : public CommandHandlerInterface
{
public:
- ContentAppCommandDelegate(jobject manager)
+ ContentAppCommandDelegate(jobject manager, ClusterId aClusterId) : CommandHandlerInterface(Optional(), aClusterId)
+ {
+ if (manager == nullptr)
+ {
+ // To support the existing hardcoded sample apps.
+ return;
+ }
+ InitializeJNIObjects(manager);
+ };
+
+ ContentAppCommandDelegate(jobject manager) : CommandHandlerInterface(Optional(), app::Clusters::ContentLauncher::Id)
{
if (manager == nullptr)
@@ -40,7 +54,16 @@ class ContentAppCommandDelegate
// To support the existing hardcoded sample apps.
return;
}
+ InitializeJNIObjects(manager);
+ };
+
+ void InvokeCommand(CommandHandlerInterface::HandlerContext & handlerContext) override;
+ const char * sendCommand(chip::EndpointId epID, std::string commandPayload);
+
+private:
+ void InitializeJNIObjects(jobject manager)
+ {
JNIEnv * env = JniReferences::GetInstance().GetEnvForCurrentThread();
VerifyOrReturn(env != nullptr, ChipLogError(Zcl, "Failed to GetEnvForCurrentThread for ContentAppEndpointManager"));
@@ -53,17 +76,16 @@ class ContentAppCommandDelegate
ChipLogError(Zcl, "Failed to get ContentAppEndpointManager Java class"));
mSendCommandMethod =
- env->GetMethodID(ContentAppEndpointManagerClass, "sendCommand", "(ILjava/lang/String;)Ljava/lang/String;");
+ env->GetMethodID(ContentAppEndpointManagerClass, "sendCommand", "(IIILjava/lang/String;)Ljava/lang/String;");
if (mSendCommandMethod == nullptr)
{
ChipLogError(Zcl, "Failed to access ContentAppEndpointManager 'sendCommand' method");
env->ExceptionClear();
}
- };
+ }
- const char * sendCommand(chip::EndpointId epID, std::string commandPayload);
+ void FormatResponseData(CommandHandlerInterface::HandlerContext & handlerContext, const char * response);
-private:
jobject mContentAppEndpointManager = nullptr;
jmethodID mSendCommandMethod = nullptr;
};
diff --git a/examples/tv-app/android/java/TVApp-JNI.cpp b/examples/tv-app/android/java/TVApp-JNI.cpp
index 9043a66864ff47..455f1d9da19eab 100644
--- a/examples/tv-app/android/java/TVApp-JNI.cpp
+++ b/examples/tv-app/android/java/TVApp-JNI.cpp
@@ -157,12 +157,12 @@ JNI_METHOD(void, preServerInit)(JNIEnv *, jobject app)
PreServerInit();
}
-JNI_METHOD(void, postServerInit)(JNIEnv *, jobject app)
+JNI_METHOD(void, postServerInit)(JNIEnv *, jobject app, jobject contentAppEndpointManager)
{
chip::DeviceLayer::StackLock lock;
ChipLogProgress(Zcl, "TvAppJNI::postServerInit");
- InitVideoPlayerPlatform(userPrompter);
+ InitVideoPlayerPlatform(userPrompter, contentAppEndpointManager);
}
JNI_METHOD(void, setOnOffManager)(JNIEnv *, jobject, jint endpoint, jobject manager)
diff --git a/examples/tv-app/android/java/src/com/tcl/chip/tvapp/ContentAppEndpointManager.java b/examples/tv-app/android/java/src/com/tcl/chip/tvapp/ContentAppEndpointManager.java
index 09bf1f0a0f8b54..119ff8bd231f5f 100644
--- a/examples/tv-app/android/java/src/com/tcl/chip/tvapp/ContentAppEndpointManager.java
+++ b/examples/tv-app/android/java/src/com/tcl/chip/tvapp/ContentAppEndpointManager.java
@@ -2,5 +2,5 @@
public interface ContentAppEndpointManager {
- public String sendCommand(int endpointId, String commandPayload);
+ public String sendCommand(int endpointId, int clusterId, int commandId, String commandPayload);
}
diff --git a/examples/tv-app/android/java/src/com/tcl/chip/tvapp/ContentAppEndpointManagerStub.java b/examples/tv-app/android/java/src/com/tcl/chip/tvapp/ContentAppEndpointManagerStub.java
deleted file mode 100644
index 3de920ef95f938..00000000000000
--- a/examples/tv-app/android/java/src/com/tcl/chip/tvapp/ContentAppEndpointManagerStub.java
+++ /dev/null
@@ -1,15 +0,0 @@
-package com.tcl.chip.tvapp;
-
-public class ContentAppEndpointManagerStub implements ContentAppEndpointManager {
-
- ContentAppEndpointManager delegate;
-
- public ContentAppEndpointManagerStub(ContentAppEndpointManager delegate) {
- this.delegate = delegate;
- }
-
- @Override
- public String sendCommand(int endpointId, String commandPayload) {
- return delegate.sendCommand(endpointId, commandPayload);
- }
-}
diff --git a/examples/tv-app/android/java/src/com/tcl/chip/tvapp/TvApp.java b/examples/tv-app/android/java/src/com/tcl/chip/tvapp/TvApp.java
index d79e760f18c05d..a7cf235ca46265 100644
--- a/examples/tv-app/android/java/src/com/tcl/chip/tvapp/TvApp.java
+++ b/examples/tv-app/android/java/src/com/tcl/chip/tvapp/TvApp.java
@@ -41,7 +41,7 @@ private void postClusterInit(int clusterId, int endpoint) {
public native void preServerInit();
// called after Matter server is inited
- public native void postServerInit();
+ public native void postServerInit(ContentAppEndpointManager manager);
public native void setKeypadInputManager(int endpoint, KeypadInputManager manager);