From 108993308470e9389632f10b09e891a7824db617 Mon Sep 17 00:00:00 2001 From: amitnj <74272437+amitnj@users.noreply.github.com> Date: Tue, 28 Jun 2022 14:51:20 -0700 Subject: [PATCH] Add functionality to read attributes from content apps (#19924) * Add functionality to read attributes from content apps * Remove unused code * Fixed issues identified during review * restyle * Failure handling and fixed typo * restyle fixes. --- .../tv/app/api/MatterIntentConstants.java | 5 ++ .../receiver/MatterCommandReceiver.java | 83 ++++++++++++------- .../ContentAppEndpointManagerImpl.java | 20 +++++ .../service/ContentAppAgentService.java | 27 ++++++ examples/tv-app/android/BUILD.gn | 2 + .../AppContentLauncherManager.cpp | 66 +++++++++++++-- .../AppContentLauncherManager.h | 17 ++-- examples/tv-app/android/java/AppImpl.cpp | 4 +- examples/tv-app/android/java/AppImpl.h | 8 +- .../java/ContentAppAttributeDelegate.cpp | 64 ++++++++++++++ .../java/ContentAppAttributeDelegate.h | 76 +++++++++++++++++ .../java/ContentAppCommandDelegate.cpp | 14 ++-- .../tvapp/ContentAppEndpointManager.java | 2 + 13 files changed, 332 insertions(+), 56 deletions(-) create mode 100644 examples/tv-app/android/java/ContentAppAttributeDelegate.cpp create mode 100644 examples/tv-app/android/java/ContentAppAttributeDelegate.h 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 d5e95061b6f6c5..60fa854790000f 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 @@ -15,9 +15,14 @@ public class MatterIntentConstants { public static final String EXTRA_RESPONSE_PAYLOAD = "EXTRA_RESPONSE_PAYLOAD"; + public static final String EXTRA_ATTRIBUTE_ACTION = "EXTRA_ATTRIBUTE_ACTION"; + + public static final String ATTRIBUTE_ACTION_READ = "ATTRIBUTE_ACTION_READ"; + 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"; + public static final String EXTRA_ATTRIBUTE_ID = "EXTRA_ATTRIBUTE_ID"; } diff --git a/examples/tv-app/android/App/content-app/src/main/java/com/example/contentapp/receiver/MatterCommandReceiver.java b/examples/tv-app/android/App/content-app/src/main/java/com/example/contentapp/receiver/MatterCommandReceiver.java index 5ac11d4b741dae..fc2e1271e61ad6 100644 --- a/examples/tv-app/android/App/content-app/src/main/java/com/example/contentapp/receiver/MatterCommandReceiver.java +++ b/examples/tv-app/android/App/content-app/src/main/java/com/example/contentapp/receiver/MatterCommandReceiver.java @@ -9,6 +9,8 @@ public class MatterCommandReceiver extends BroadcastReceiver { private static final String TAG = "MatterCommandReceiver"; + private static final int ACCEPT_HEADER = 0; + private static final int SUPPORTED_STREAMING_PROTOCOLS = 1; @Override public void onReceive(Context context, Intent intent) { @@ -21,37 +23,48 @@ public void onReceive(Context context, Intent intent) { switch (intentAction) { case MatterIntentConstants.ACTION_MATTER_COMMAND: - byte[] commandPayload = - intent.getByteArrayExtra(MatterIntentConstants.EXTRA_COMMAND_PAYLOAD); int commandId = intent.getIntExtra(MatterIntentConstants.EXTRA_COMMAND_ID, -1); - int clusterId = intent.getIntExtra(MatterIntentConstants.EXTRA_CLUSTER_ID, -1); - Log.d( - TAG, - new StringBuilder() - .append("Received matter command ") - .append(commandId) - .append(" on cluster ") - .append(clusterId) - .append(" with payload : ") - .append(new String(commandPayload)) - .toString()); - - PendingIntent pendingIntent = - intent.getParcelableExtra( - MatterIntentConstants.EXTRA_DIRECTIVE_RESPONSE_PENDING_INTENT); - if (pendingIntent != null) { - final Intent responseIntent = - new Intent() - .putExtra( - MatterIntentConstants.EXTRA_RESPONSE_PAYLOAD, - "{\"value\":{\"0\":1, \"1\":\"custom response from content app\"}}" - .getBytes()); - try { - pendingIntent.send(context, 0, responseIntent); - } catch (final PendingIntent.CanceledException ex) { - Log.e(TAG, "Error sending pending intent to the Matter agent", ex); + if (commandId != -1) { + int clusterId = intent.getIntExtra(MatterIntentConstants.EXTRA_CLUSTER_ID, -1); + byte[] commandPayload = + intent.getByteArrayExtra(MatterIntentConstants.EXTRA_COMMAND_PAYLOAD); + Log.d( + TAG, + new StringBuilder() + .append("Received matter command ") + .append(commandId) + .append(" on cluster ") + .append(clusterId) + .append(" with payload : ") + .append(new String(commandPayload)) + .toString()); + String response = "{\"0\":1, \"1\":\"custom response from content app\"}"; + sendResponseViaPendingIntent(context, intent, response); + } else { + int attributeId = intent.getIntExtra(MatterIntentConstants.EXTRA_ATTRIBUTE_ID, -1); + String attributeAction = + intent.getStringExtra(MatterIntentConstants.EXTRA_ATTRIBUTE_ACTION); + if (attributeAction.equals(MatterIntentConstants.ATTRIBUTE_ACTION_READ)) { + String response; + if (attributeId == ACCEPT_HEADER) { + response = + "{\"0\": [\"video/mp4\", \"application/x-mpegURL\", \"application/dash+xml\"] }"; + } else if (attributeId == SUPPORTED_STREAMING_PROTOCOLS) { + response = "{\"1\":3}"; + } else { + response = ""; + } + sendResponseViaPendingIntent(context, intent, response); + } else { + Log.e( + TAG, + new StringBuilder() + .append("Received unknown Attribute Action: ") + .append(intent.getAction()) + .toString()); } } + break; default: Log.e( @@ -62,4 +75,18 @@ public void onReceive(Context context, Intent intent) { .toString()); } } + + private void sendResponseViaPendingIntent(Context context, Intent intent, String response) { + PendingIntent pendingIntent = + intent.getParcelableExtra(MatterIntentConstants.EXTRA_DIRECTIVE_RESPONSE_PENDING_INTENT); + if (pendingIntent != null) { + final Intent responseIntent = + new Intent().putExtra(MatterIntentConstants.EXTRA_RESPONSE_PAYLOAD, response.getBytes()); + try { + pendingIntent.send(context, 0, responseIntent); + } catch (final PendingIntent.CanceledException ex) { + Log.e(TAG, "Error sending pending intent to the Matter agent", ex); + } + } + } } 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 789089b0964271..0b9ea344b161ad 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 @@ -29,4 +29,24 @@ public String sendCommand(int endpointId, int clusterId, int commandId, String c } return "Success"; } + + public String readAttribute(int endpointId, int clusterId, int attributeId) { + Log.d( + TAG, + "Received a attribute read request for endpointId " + + endpointId + + "clusterId" + + clusterId + + " attributeId " + + attributeId); + for (ContentApp app : + ContentAppDiscoveryService.getReceiverInstance().getDiscoveredContentApps().values()) { + if (app.getEndpointId() == endpointId) { + Log.d(TAG, "Sending attribute read request for endpointId " + endpointId); + return ContentAppAgentService.sendAttributeReadRequest( + context, app.getAppName(), clusterId, attributeId); + } + } + return ""; + } } 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 ccc72e4a9221b2..d68758b1152816 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 @@ -80,6 +80,33 @@ public static String sendCommand( return response; } + public static String sendAttributeReadRequest( + Context context, String packageName, int clusterId, int attributeId) { + Intent in = new Intent(MatterIntentConstants.ACTION_MATTER_COMMAND); + Bundle extras = new Bundle(); + extras.putString( + MatterIntentConstants.EXTRA_ATTRIBUTE_ACTION, MatterIntentConstants.ATTRIBUTE_ACTION_READ); + extras.putInt(MatterIntentConstants.EXTRA_ATTRIBUTE_ID, attributeId); + 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, 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 int responseId) { Intent ackBackIntent = new Intent(ACTION_MATTER_RESPONSE); diff --git a/examples/tv-app/android/BUILD.gn b/examples/tv-app/android/BUILD.gn index a616671f049cb6..17abcc4948802c 100644 --- a/examples/tv-app/android/BUILD.gn +++ b/examples/tv-app/android/BUILD.gn @@ -44,6 +44,8 @@ shared_library("jni") { "java/ChannelManager.cpp", "java/ChannelManager.h", "java/ClusterChangeAttribute.cpp", + "java/ContentAppAttributeDelegate.cpp", + "java/ContentAppAttributeDelegate.h", "java/ContentAppCommandDelegate.cpp", "java/ContentAppCommandDelegate.h", "java/ContentLauncherManager.cpp", diff --git a/examples/tv-app/android/include/content-launcher/AppContentLauncherManager.cpp b/examples/tv-app/android/include/content-launcher/AppContentLauncherManager.cpp index 6dc80c5b164f5a..d898612977983a 100644 --- a/examples/tv-app/android/include/content-launcher/AppContentLauncherManager.cpp +++ b/examples/tv-app/android/include/content-launcher/AppContentLauncherManager.cpp @@ -17,18 +17,19 @@ */ #include "AppContentLauncherManager.h" -#include "../../java/ContentAppCommandDelegate.h" +#include "../../java/ContentAppAttributeDelegate.h" +#include using namespace std; using namespace chip::app; using namespace chip::app::Clusters; using namespace chip::app::DataModel; using namespace chip::app::Clusters::ContentLauncher; -using ContentAppCommandDelegate = chip::AppPlatform::ContentAppCommandDelegate; +using ContentAppAttributeDelegate = chip::AppPlatform::ContentAppAttributeDelegate; -AppContentLauncherManager::AppContentLauncherManager(ContentAppCommandDelegate commandDelegate, list acceptHeaderList, - uint32_t supportedStreamingProtocols) : - mCommandDelegate(commandDelegate) +AppContentLauncherManager::AppContentLauncherManager(ContentAppAttributeDelegate attributeDelegate, + list acceptHeaderList, uint32_t supportedStreamingProtocols) : + mAttributeDelegate(attributeDelegate) { mAcceptHeaderList = acceptHeaderList; mSupportedStreamingProtocols = supportedStreamingProtocols; @@ -73,11 +74,8 @@ void AppContentLauncherManager::HandleLaunchUrl(CommandResponseHelper CHIP_ERROR { for (std::string & entry : mAcceptHeaderList) { @@ -98,6 +122,30 @@ CHIP_ERROR AppContentLauncherManager::HandleGetAcceptHeaderList(AttributeValueEn uint32_t AppContentLauncherManager::HandleGetSupportedStreamingProtocols() { ChipLogProgress(Zcl, "AppContentLauncherManager::HandleGetSupportedStreamingProtocols"); + chip::app::ConcreteReadAttributePath aPath(mEndpointId, chip::app::Clusters::ContentLauncher::Id, + chip::app::Clusters::ContentLauncher::Attributes::SupportedStreamingProtocols::Id); + const char * resStr = mAttributeDelegate.Read(aPath); + ChipLogProgress(Zcl, "AppContentLauncherManager::HandleGetSupportedStreamingProtocols response %s", resStr); + + if (resStr == nullptr || *resStr == 0) + { + return mSupportedStreamingProtocols; + } + + Json::Reader reader; + Json::Value value; + if (!reader.parse(resStr, value)) + { + return mSupportedStreamingProtocols; + } + std::string attrId = to_string(chip::app::Clusters::ContentLauncher::Attributes::SupportedStreamingProtocols::Id); + ChipLogProgress(Zcl, "AppContentLauncherManager::HandleGetSupportedStreamingProtocols response parsing done. reading attr %s", + attrId.c_str()); + if (!value[attrId].empty()) + { + uint32_t supportedStreamingProtocols = static_cast(value[attrId].asInt()); + mSupportedStreamingProtocols = supportedStreamingProtocols; + } return mSupportedStreamingProtocols; } diff --git a/examples/tv-app/android/include/content-launcher/AppContentLauncherManager.h b/examples/tv-app/android/include/content-launcher/AppContentLauncherManager.h index b5b8f7a533c4e2..bcce3add47f4a7 100644 --- a/examples/tv-app/android/include/content-launcher/AppContentLauncherManager.h +++ b/examples/tv-app/android/include/content-launcher/AppContentLauncherManager.h @@ -18,7 +18,7 @@ #pragma once -#include "../../java/ContentAppCommandDelegate.h" +#include "../../java/ContentAppAttributeDelegate.h" #include #include @@ -26,16 +26,16 @@ using chip::CharSpan; using chip::EndpointId; using chip::app::AttributeValueEncoder; using chip::app::CommandResponseHelper; -using ContentLauncherDelegate = chip::app::Clusters::ContentLauncher::Delegate; -using LaunchResponseType = chip::app::Clusters::ContentLauncher::Commands::LaunchResponse::Type; -using ParameterType = chip::app::Clusters::ContentLauncher::Structs::Parameter::DecodableType; -using BrandingInformationType = chip::app::Clusters::ContentLauncher::Structs::BrandingInformation::Type; -using ContentAppCommandDelegate = chip::AppPlatform::ContentAppCommandDelegate; +using ContentLauncherDelegate = chip::app::Clusters::ContentLauncher::Delegate; +using LaunchResponseType = chip::app::Clusters::ContentLauncher::Commands::LaunchResponse::Type; +using ParameterType = chip::app::Clusters::ContentLauncher::Structs::Parameter::DecodableType; +using BrandingInformationType = chip::app::Clusters::ContentLauncher::Structs::BrandingInformation::Type; +using ContentAppAttributeDelegate = chip::AppPlatform::ContentAppAttributeDelegate; class AppContentLauncherManager : public ContentLauncherDelegate { public: - AppContentLauncherManager(ContentAppCommandDelegate commandDelegate, std::list acceptHeaderList, + AppContentLauncherManager(ContentAppAttributeDelegate attributeDelegate, std::list acceptHeaderList, uint32_t supportedStreamingProtocols); void HandleLaunchContent(CommandResponseHelper & helper, @@ -56,8 +56,9 @@ class AppContentLauncherManager : public ContentLauncherDelegate private: EndpointId mEndpointId; - ContentAppCommandDelegate mCommandDelegate; // TODO: set this based upon meta data from app uint32_t mDynamicEndpointFeatureMap = 3; + + ContentAppAttributeDelegate mAttributeDelegate; }; diff --git a/examples/tv-app/android/java/AppImpl.cpp b/examples/tv-app/android/java/AppImpl.cpp index 063a2be2528354..90e88c86f3cd09 100644 --- a/examples/tv-app/android/java/AppImpl.cpp +++ b/examples/tv-app/android/java/AppImpl.cpp @@ -357,7 +357,7 @@ ContentApp * ContentAppFactoryImpl::LoadContentApp(const CatalogVendorApp & vend return nullptr; } -EndpointId ContentAppFactoryImpl::AddContentApp(ContentAppImpl * app) +EndpointId ContentAppFactoryImpl::AddContentApp(ContentAppImpl * app, jobject contentAppEndpointManager) { DataVersion dataVersionBuf[ArraySize(contentAppClusters)]; EndpointId epId = ContentAppPlatform::GetInstance().AddContentApp(app, &contentAppEndpoint, Span(dataVersionBuf), @@ -471,7 +471,7 @@ EndpointId AddContentApp(const char * szVendorName, uint16_t vendorId, const cha 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); + return gFactory.AddContentApp(app, manager); #endif // CHIP_DEVICE_CONFIG_APP_PLATFORM_ENABLED return 0; } diff --git a/examples/tv-app/android/java/AppImpl.h b/examples/tv-app/android/java/AppImpl.h index 98a6d61798bc07..6a5f919224ef0d 100644 --- a/examples/tv-app/android/java/AppImpl.h +++ b/examples/tv-app/android/java/AppImpl.h @@ -39,7 +39,7 @@ #include "../include/target-navigator/TargetNavigatorManager.h" #include "ChannelManager.h" #include "CommissionerMain.h" -#include "ContentAppCommandDelegate.h" +#include "ContentAppAttributeDelegate.h" #include "KeypadInputManager.h" #include "MediaPlaybackManager.h" #include "MyUserPrompter-JNI.h" @@ -72,7 +72,7 @@ using KeypadInputDelegate = app::Clusters::KeypadInput::Delegate; using MediaPlaybackDelegate = app::Clusters::MediaPlayback::Delegate; using TargetNavigatorDelegate = app::Clusters::TargetNavigator::Delegate; using SupportedStreamingProtocol = app::Clusters::ContentLauncher::SupportedStreamingProtocol; -using ContentAppCommandDelegate = chip::AppPlatform::ContentAppCommandDelegate; +using ContentAppAttributeDelegate = chip::AppPlatform::ContentAppAttributeDelegate; static const int kCatalogVendorId = CHIP_DEVICE_CONFIG_DEVICE_VENDOR_ID; @@ -86,7 +86,7 @@ class DLL_EXPORT ContentAppImpl : public ContentApp const char * szApplicationVersion, const char * setupPIN, jobject manager) : mApplicationBasicDelegate(kCatalogVendorId, BuildAppId(vendorId), szVendorName, vendorId, szApplicationName, productId, szApplicationVersion), - mAccountLoginDelegate(setupPIN), mContentLauncherDelegate(ContentAppCommandDelegate(manager), { "image/*", "video/*" }, + mAccountLoginDelegate(setupPIN), mContentLauncherDelegate(ContentAppAttributeDelegate(manager), { "image/*", "video/*" }, to_underlying(SupportedStreamingProtocol::kDash) | to_underlying(SupportedStreamingProtocol::kHls)), mTargetNavigatorDelegate({ "home", "search", "info", "guide", "menu" }, 0){}; @@ -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, jobject contentAppEndpointManager); void SendTestMessage(EndpointId epID, const char * message); diff --git a/examples/tv-app/android/java/ContentAppAttributeDelegate.cpp b/examples/tv-app/android/java/ContentAppAttributeDelegate.cpp new file mode 100644 index 00000000000000..7fcf0b313a62a5 --- /dev/null +++ b/examples/tv-app/android/java/ContentAppAttributeDelegate.cpp @@ -0,0 +1,64 @@ +/* + * + * Copyright (c) 2021 Project CHIP Authors + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @brief Contains Implementation of the ContentAppAttributeDelegate + */ + +#include "ContentAppAttributeDelegate.h" +#include +#include +#include +#include +#include +#include +#include + +namespace chip { +namespace AppPlatform { + +using LaunchResponseType = chip::app::Clusters::ContentLauncher::Commands::LaunchResponse::Type; + +const char * ContentAppAttributeDelegate::Read(const chip::app::ConcreteReadAttributePath & aPath) +{ + if (aPath.mEndpointId < FIXED_ENDPOINT_COUNT) + { + return ""; + } + JNIEnv * env = JniReferences::GetInstance().GetEnvForCurrentThread(); + + ChipLogProgress(Zcl, "ContentAppAttributeDelegate::Read being called for endpoint %d cluster %d attribute %d", + aPath.mEndpointId, aPath.mClusterId, aPath.mAttributeId); + + jstring resp = + (jstring) env->CallObjectMethod(mContentAppEndpointManager, mReadAttributeMethod, static_cast(aPath.mEndpointId), + static_cast(aPath.mClusterId), static_cast(aPath.mAttributeId)); + if (env->ExceptionCheck()) + { + ChipLogError(Zcl, "Java exception in ContentAppAttributeDelegate::Read"); + env->ExceptionDescribe(); + env->ExceptionClear(); + return ""; + } + const char * respStr = env->GetStringUTFChars(resp, 0); + ChipLogProgress(Zcl, "ContentAppAttributeDelegate::Read got response %s", respStr); + return respStr; +} + +} // namespace AppPlatform +} // namespace chip diff --git a/examples/tv-app/android/java/ContentAppAttributeDelegate.h b/examples/tv-app/android/java/ContentAppAttributeDelegate.h new file mode 100644 index 00000000000000..592971de5ee60a --- /dev/null +++ b/examples/tv-app/android/java/ContentAppAttributeDelegate.h @@ -0,0 +1,76 @@ +/* + * + * Copyright (c) 2021 Project CHIP Authors + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @brief Facilitates communication with the application handlers in Java + */ + +#pragma once + +#include +#include +#include +#include +#include + +namespace chip { +namespace AppPlatform { + +class ContentAppAttributeDelegate +{ +public: + ContentAppAttributeDelegate(jobject manager) + { + if (manager == nullptr) + { + // To support the existing hardcoded sample apps. + return; + } + InitializeJNIObjects(manager); + } + + const char * Read(const chip::app::ConcreteReadAttributePath & aPath); + +private: + void InitializeJNIObjects(jobject manager) + { + JNIEnv * env = JniReferences::GetInstance().GetEnvForCurrentThread(); + VerifyOrReturn(env != nullptr, ChipLogError(Zcl, "Failed to GetEnvForCurrentThread for ContentAppEndpointManager")); + + mContentAppEndpointManager = env->NewGlobalRef(manager); + VerifyOrReturn(mContentAppEndpointManager != nullptr, + ChipLogError(Zcl, "Failed to NewGlobalRef ContentAppEndpointManager")); + + jclass ContentAppEndpointManagerClass = env->GetObjectClass(manager); + VerifyOrReturn(ContentAppEndpointManagerClass != nullptr, + ChipLogError(Zcl, "Failed to get ContentAppEndpointManager Java class")); + + mReadAttributeMethod = env->GetMethodID(ContentAppEndpointManagerClass, "readAttribute", "(III)Ljava/lang/String;"); + if (mReadAttributeMethod == nullptr) + { + ChipLogError(Zcl, "Failed to access ContentAppEndpointManager 'readAttribute' method"); + env->ExceptionClear(); + } + } + + jobject mContentAppEndpointManager = nullptr; + jmethodID mReadAttributeMethod = nullptr; +}; + +} // namespace AppPlatform +} // namespace chip diff --git a/examples/tv-app/android/java/ContentAppCommandDelegate.cpp b/examples/tv-app/android/java/ContentAppCommandDelegate.cpp index 1cc25f57ee6098..966f532ecd18ac 100644 --- a/examples/tv-app/android/java/ContentAppCommandDelegate.cpp +++ b/examples/tv-app/android/java/ContentAppCommandDelegate.cpp @@ -79,8 +79,9 @@ void ContentAppCommandDelegate::InvokeCommand(CommandHandlerInterface::HandlerCo return; } - JNIEnv * env = JniReferences::GetInstance().GetEnvForCurrentThread(); - UtfString jsonString(env, JsonToString(json).c_str()); + JNIEnv * env = JniReferences::GetInstance().GetEnvForCurrentThread(); + Json::Value value = json["value"]; + UtfString jsonString(env, JsonToString(value).c_str()); ChipLogProgress(Zcl, "ContentAppCommandDelegate::InvokeCommand send command being called with payload %s", JsonToString(json).c_str()); @@ -110,9 +111,12 @@ void ContentAppCommandDelegate::InvokeCommand(CommandHandlerInterface::HandlerCo void ContentAppCommandDelegate::FormatResponseData(CommandHandlerInterface::HandlerContext & handlerContext, const char * response) { Json::Reader reader; - Json::Value resJson; - reader.parse(response, resJson); - Json::Value value = resJson["value"]; + Json::Value value; + if (!reader.parse(response, value)) + { + handlerContext.SetCommandNotHandled(); + return; + } switch (handlerContext.mRequestPath.mClusterId) { diff --git a/examples/tv-app/android/java/src/com/matter/tv/server/tvapp/ContentAppEndpointManager.java b/examples/tv-app/android/java/src/com/matter/tv/server/tvapp/ContentAppEndpointManager.java index 69a0eac364daa1..92c5e5243b85ad 100644 --- a/examples/tv-app/android/java/src/com/matter/tv/server/tvapp/ContentAppEndpointManager.java +++ b/examples/tv-app/android/java/src/com/matter/tv/server/tvapp/ContentAppEndpointManager.java @@ -3,4 +3,6 @@ public interface ContentAppEndpointManager { public String sendCommand(int endpointId, int clusterId, int commandId, String commandPayload); + + public String readAttribute(int endpointId, int clusterId, int attributeId); }