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);