/* * * Copyright (c) 2021 Project CHIP Authors * * 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. */ /** * @file Contains Implementation of the ContentApp and the ContentAppPlatform. */ #include "AppImpl.h" #include "ContentAppAttributeDelegate.h" #include "ContentAppCommandDelegate.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace chip; using namespace chip::app::Clusters; using namespace chip::AppPlatform; using namespace chip::DeviceLayer; #if CHIP_DEVICE_CONFIG_APP_PLATFORM_ENABLED ContentAppFactoryImpl gFactory; ContentAppFactoryImpl * GetContentAppFactoryImpl() { return &gFactory; } namespace chip { namespace AppPlatform { // BEGIN DYNAMIC ENDPOINTS // ================================================================================= static const int kNameSize = 32; // Current ZCL implementation of Struct uses a max-size array of 254 bytes static const int kDescriptorAttributeArraySize = 254; // Device types for dynamic endpoints: TODO Need a generated file from ZAP to define these! // (taken from chip-devices.xml) #define DEVICE_TYPE_CONTENT_APP 0x0024 // --------------------------------------------------------------------------- // // CONTENT APP ENDPOINT: contains the following clusters: // - Descriptor // - Application Basic // - Keypad Input // - Application Launcher // - Account Login // - Content Launcher // - Target Navigator // - Channel // Declare Descriptor cluster attributes DECLARE_DYNAMIC_ATTRIBUTE_LIST_BEGIN(descriptorAttrs) DECLARE_DYNAMIC_ATTRIBUTE(Descriptor::Attributes::DeviceTypeList::Id, ARRAY, kDescriptorAttributeArraySize, 0), /* device list */ DECLARE_DYNAMIC_ATTRIBUTE(Descriptor::Attributes::ServerList::Id, ARRAY, kDescriptorAttributeArraySize, 0), /* server list */ DECLARE_DYNAMIC_ATTRIBUTE(Descriptor::Attributes::ClientList::Id, ARRAY, kDescriptorAttributeArraySize, 0), /* client list */ DECLARE_DYNAMIC_ATTRIBUTE(Descriptor::Attributes::PartsList::Id, ARRAY, kDescriptorAttributeArraySize, 0), /* parts list */ DECLARE_DYNAMIC_ATTRIBUTE_LIST_END(); // Declare Application Basic information cluster attributes DECLARE_DYNAMIC_ATTRIBUTE_LIST_BEGIN(applicationBasicAttrs) DECLARE_DYNAMIC_ATTRIBUTE(ApplicationBasic::Attributes::VendorName::Id, CHAR_STRING, kNameSize, 0), /* VendorName */ DECLARE_DYNAMIC_ATTRIBUTE(ApplicationBasic::Attributes::VendorID::Id, INT16U, 1, 0), /* VendorID */ DECLARE_DYNAMIC_ATTRIBUTE(ApplicationBasic::Attributes::ApplicationName::Id, CHAR_STRING, kNameSize, 0), /* ApplicationName */ DECLARE_DYNAMIC_ATTRIBUTE(ApplicationBasic::Attributes::ProductID::Id, INT16U, 1, 0), /* ProductID */ DECLARE_DYNAMIC_ATTRIBUTE(ApplicationBasic::Attributes::Status::Id, INT8U, 1, 0), /* ApplicationStatus */ DECLARE_DYNAMIC_ATTRIBUTE(ApplicationBasic::Attributes::ApplicationVersion::Id, CHAR_STRING, kNameSize, 0), /* ApplicationVersion */ DECLARE_DYNAMIC_ATTRIBUTE(ApplicationBasic::Attributes::AllowedVendorList::Id, ARRAY, kDescriptorAttributeArraySize, 0), /* AllowedVendorList */ DECLARE_DYNAMIC_ATTRIBUTE_LIST_END(); // Declare Keypad Input cluster attributes DECLARE_DYNAMIC_ATTRIBUTE_LIST_BEGIN(keypadInputAttrs) DECLARE_DYNAMIC_ATTRIBUTE(KeypadInput::Attributes::FeatureMap::Id, BITMAP32, 4, 0), /* FeatureMap */ DECLARE_DYNAMIC_ATTRIBUTE_LIST_END(); // Declare Application Launcher cluster attributes // NOTE: Does not make sense for content app to be able to set the AP feature flag DECLARE_DYNAMIC_ATTRIBUTE_LIST_BEGIN(applicationLauncherAttrs) DECLARE_DYNAMIC_ATTRIBUTE(ApplicationLauncher::Attributes::CatalogList::Id, ARRAY, kDescriptorAttributeArraySize, 0), /* catalog list */ DECLARE_DYNAMIC_ATTRIBUTE(ApplicationLauncher::Attributes::CurrentApp::Id, STRUCT, 1, 0), /* current app */ DECLARE_DYNAMIC_ATTRIBUTE_LIST_END(); // Declare Account Login cluster attributes DECLARE_DYNAMIC_ATTRIBUTE_LIST_BEGIN(accountLoginAttrs) DECLARE_DYNAMIC_ATTRIBUTE_LIST_END(); // Declare Content Launcher cluster attributes DECLARE_DYNAMIC_ATTRIBUTE_LIST_BEGIN(contentLauncherAttrs) DECLARE_DYNAMIC_ATTRIBUTE(ContentLauncher::Attributes::AcceptHeader::Id, ARRAY, kDescriptorAttributeArraySize, 0), /* accept header list */ DECLARE_DYNAMIC_ATTRIBUTE(ContentLauncher::Attributes::SupportedStreamingProtocols::Id, BITMAP32, 1, 0), /* streaming protocols */ DECLARE_DYNAMIC_ATTRIBUTE(ContentLauncher::Attributes::FeatureMap::Id, BITMAP32, 4, 0), /* FeatureMap */ DECLARE_DYNAMIC_ATTRIBUTE_LIST_END(); // Declare Media Playback cluster attributes DECLARE_DYNAMIC_ATTRIBUTE_LIST_BEGIN(mediaPlaybackAttrs) DECLARE_DYNAMIC_ATTRIBUTE(MediaPlayback::Attributes::CurrentState::Id, ENUM8, 1, 0), /* current state */ DECLARE_DYNAMIC_ATTRIBUTE(MediaPlayback::Attributes::StartTime::Id, EPOCH_US, 1, 0), /* start time */ DECLARE_DYNAMIC_ATTRIBUTE(MediaPlayback::Attributes::Duration::Id, INT64U, 1, 0), /* duration */ DECLARE_DYNAMIC_ATTRIBUTE(MediaPlayback::Attributes::SampledPosition::Id, STRUCT, 1, 0), /* SampledPosition */ DECLARE_DYNAMIC_ATTRIBUTE(MediaPlayback::Attributes::PlaybackSpeed::Id, SINGLE, 1, 0), /* playback speed */ DECLARE_DYNAMIC_ATTRIBUTE(MediaPlayback::Attributes::SeekRangeEnd::Id, INT64U, 1, 0), /* seek range end */ DECLARE_DYNAMIC_ATTRIBUTE(MediaPlayback::Attributes::SeekRangeStart::Id, INT64U, 1, 0), /* seek range start */ DECLARE_DYNAMIC_ATTRIBUTE(MediaPlayback::Attributes::FeatureMap::Id, BITMAP32, 4, 0), /* FeatureMap */ DECLARE_DYNAMIC_ATTRIBUTE_LIST_END(); // Declare Target Navigator cluster attributes DECLARE_DYNAMIC_ATTRIBUTE_LIST_BEGIN(targetNavigatorAttrs) DECLARE_DYNAMIC_ATTRIBUTE(TargetNavigator::Attributes::TargetList::Id, ARRAY, kDescriptorAttributeArraySize, 0), /* target list */ DECLARE_DYNAMIC_ATTRIBUTE(TargetNavigator::Attributes::CurrentTarget::Id, INT8U, 1, 0), /* current target */ DECLARE_DYNAMIC_ATTRIBUTE_LIST_END(); // Declare Channel cluster attributes DECLARE_DYNAMIC_ATTRIBUTE_LIST_BEGIN(channelAttrs) DECLARE_DYNAMIC_ATTRIBUTE(Channel::Attributes::ChannelList::Id, ARRAY, kDescriptorAttributeArraySize, 0), /* channel list */ DECLARE_DYNAMIC_ATTRIBUTE(Channel::Attributes::Lineup::Id, STRUCT, 1, 0), /* lineup */ DECLARE_DYNAMIC_ATTRIBUTE(Channel::Attributes::CurrentChannel::Id, STRUCT, 1, 0), /* current channel */ DECLARE_DYNAMIC_ATTRIBUTE(Channel::Attributes::FeatureMap::Id, BITMAP32, 4, 0), /* FeatureMap */ DECLARE_DYNAMIC_ATTRIBUTE_LIST_END(); constexpr CommandId keypadInputIncomingCommands[] = { app::Clusters::KeypadInput::Commands::SendKey::Id, kInvalidCommandId, }; constexpr CommandId keypadInputOutgoingCommands[] = { app::Clusters::KeypadInput::Commands::SendKeyResponse::Id, kInvalidCommandId, }; constexpr CommandId applicationLauncherIncomingCommands[] = { app::Clusters::ApplicationLauncher::Commands::LaunchApp::Id, app::Clusters::ApplicationLauncher::Commands::StopApp::Id, app::Clusters::ApplicationLauncher::Commands::HideApp::Id, kInvalidCommandId, }; constexpr CommandId applicationLauncherOutgoingCommands[] = { app::Clusters::ApplicationLauncher::Commands::LauncherResponse::Id, kInvalidCommandId, }; constexpr CommandId accountLoginIncomingCommands[] = { app::Clusters::AccountLogin::Commands::GetSetupPIN::Id, app::Clusters::AccountLogin::Commands::Login::Id, app::Clusters::AccountLogin::Commands::Logout::Id, kInvalidCommandId, }; constexpr CommandId accountLoginOutgoingCommands[] = { app::Clusters::AccountLogin::Commands::GetSetupPINResponse::Id, kInvalidCommandId, }; // TODO: Sort out when the optional commands here should be listed. constexpr CommandId contentLauncherIncomingCommands[] = { app::Clusters::ContentLauncher::Commands::LaunchContent::Id, app::Clusters::ContentLauncher::Commands::LaunchURL::Id, kInvalidCommandId, }; constexpr CommandId contentLauncherOutgoingCommands[] = { app::Clusters::ContentLauncher::Commands::LauncherResponse::Id, kInvalidCommandId, }; // TODO: Sort out when the optional commands here should be listed. constexpr CommandId mediaPlaybackIncomingCommands[] = { app::Clusters::MediaPlayback::Commands::Play::Id, app::Clusters::MediaPlayback::Commands::Pause::Id, app::Clusters::MediaPlayback::Commands::Stop::Id, app::Clusters::MediaPlayback::Commands::StartOver::Id, app::Clusters::MediaPlayback::Commands::Previous::Id, app::Clusters::MediaPlayback::Commands::Next::Id, app::Clusters::MediaPlayback::Commands::Rewind::Id, app::Clusters::MediaPlayback::Commands::FastForward::Id, app::Clusters::MediaPlayback::Commands::SkipForward::Id, app::Clusters::MediaPlayback::Commands::SkipBackward::Id, app::Clusters::MediaPlayback::Commands::Seek::Id, kInvalidCommandId, }; constexpr CommandId mediaPlaybackOutgoingCommands[] = { app::Clusters::MediaPlayback::Commands::PlaybackResponse::Id, kInvalidCommandId, }; constexpr CommandId targetNavigatorIncomingCommands[] = { app::Clusters::TargetNavigator::Commands::NavigateTarget::Id, kInvalidCommandId, }; constexpr CommandId targetNavigatorOutgoingCommands[] = { app::Clusters::TargetNavigator::Commands::NavigateTargetResponse::Id, kInvalidCommandId, }; // TODO: Sort out when the optional commands here should be listed. constexpr CommandId channelIncomingCommands[] = { app::Clusters::Channel::Commands::ChangeChannel::Id, app::Clusters::Channel::Commands::ChangeChannelByNumber::Id, app::Clusters::Channel::Commands::SkipChannel::Id, kInvalidCommandId, }; constexpr CommandId channelOutgoingCommands[] = { app::Clusters::Channel::Commands::ChangeChannelResponse::Id, kInvalidCommandId, }; // Declare Cluster List for Content App endpoint DECLARE_DYNAMIC_CLUSTER_LIST_BEGIN(contentAppClusters) DECLARE_DYNAMIC_CLUSTER(app::Clusters::Descriptor::Id, descriptorAttrs, ZAP_CLUSTER_MASK(SERVER), nullptr, nullptr), DECLARE_DYNAMIC_CLUSTER(app::Clusters::ApplicationBasic::Id, applicationBasicAttrs, ZAP_CLUSTER_MASK(SERVER), nullptr, nullptr), DECLARE_DYNAMIC_CLUSTER(app::Clusters::KeypadInput::Id, keypadInputAttrs, ZAP_CLUSTER_MASK(SERVER), keypadInputIncomingCommands, keypadInputOutgoingCommands), DECLARE_DYNAMIC_CLUSTER(app::Clusters::ApplicationLauncher::Id, applicationLauncherAttrs, ZAP_CLUSTER_MASK(SERVER), applicationLauncherIncomingCommands, applicationLauncherOutgoingCommands), DECLARE_DYNAMIC_CLUSTER(app::Clusters::AccountLogin::Id, accountLoginAttrs, ZAP_CLUSTER_MASK(SERVER), accountLoginIncomingCommands, accountLoginOutgoingCommands), DECLARE_DYNAMIC_CLUSTER(app::Clusters::ContentLauncher::Id, contentLauncherAttrs, ZAP_CLUSTER_MASK(SERVER), contentLauncherIncomingCommands, contentLauncherOutgoingCommands), DECLARE_DYNAMIC_CLUSTER(app::Clusters::MediaPlayback::Id, mediaPlaybackAttrs, ZAP_CLUSTER_MASK(SERVER), mediaPlaybackIncomingCommands, mediaPlaybackOutgoingCommands), DECLARE_DYNAMIC_CLUSTER(app::Clusters::TargetNavigator::Id, targetNavigatorAttrs, ZAP_CLUSTER_MASK(SERVER), targetNavigatorIncomingCommands, targetNavigatorOutgoingCommands), DECLARE_DYNAMIC_CLUSTER(app::Clusters::Channel::Id, channelAttrs, ZAP_CLUSTER_MASK(SERVER), channelIncomingCommands, channelOutgoingCommands), DECLARE_DYNAMIC_CLUSTER_LIST_END; // Declare Content App endpoint DECLARE_DYNAMIC_ENDPOINT(contentAppEndpoint, contentAppClusters); namespace { DataVersion gDataVersions[APP_LIBRARY_SIZE][ArraySize(contentAppClusters)]; EmberAfDeviceType gContentAppDeviceType[] = { { DEVICE_TYPE_CONTENT_APP, 1 } }; std::vector make_default_supported_clusters() { return std::vector{ { Descriptor::Id }, { ApplicationBasic::Id }, { KeypadInput::Id }, { ApplicationLauncher::Id }, { AccountLogin::Id }, { ContentLauncher::Id }, { TargetNavigator::Id }, { Channel::Id } }; } } // anonymous namespace ContentAppFactoryImpl::ContentAppFactoryImpl() : mContentApps{ new ContentAppImpl("Vendor1", 1, "exampleid", 11, "Version1", "20202021", make_default_supported_clusters(), nullptr, nullptr), new ContentAppImpl("Vendor2", 65521, "exampleString", 32768, "Version2", "20202021", make_default_supported_clusters(), nullptr, nullptr), new ContentAppImpl("Vendor3", 9050, "App3", 22, "Version3", "20202021", make_default_supported_clusters(), nullptr, nullptr), new ContentAppImpl("TestSuiteVendor", 1111, "applicationId", 22, "v2", "20202021", make_default_supported_clusters(), nullptr, nullptr) } {} uint16_t ContentAppFactoryImpl::GetPlatformCatalogVendorId() { return kCatalogVendorId; } void ContentAppFactoryImpl::setContentAppAttributeDelegate(ContentAppAttributeDelegate * attributeDelegate) { mAttributeDelegate = attributeDelegate; } void ContentAppFactoryImpl::setContentAppCommandDelegate(ContentAppCommandDelegate * commandDelegate) { mCommandDelegate = commandDelegate; } CHIP_ERROR ContentAppFactoryImpl::LookupCatalogVendorApp(uint16_t vendorId, uint16_t productId, CatalogVendorApp * destinationApp) { std::string appId = BuildAppId(vendorId); destinationApp->catalogVendorId = GetPlatformCatalogVendorId(); Platform::CopyString(destinationApp->applicationId, sizeof(destinationApp->applicationId), appId.c_str()); return CHIP_NO_ERROR; } CHIP_ERROR ContentAppFactoryImpl::ConvertToPlatformCatalogVendorApp(const CatalogVendorApp & sourceApp, CatalogVendorApp * destinationApp) { destinationApp->catalogVendorId = GetPlatformCatalogVendorId(); std::string appId(sourceApp.applicationId); if (appId == "applicationId") { // regression test case passes "applicationId", map this to our test suite app Platform::CopyString(destinationApp->applicationId, sizeof(destinationApp->applicationId), "1111"); } else if (appId == "exampleid") { // cert test case passes "exampleid", map this to our test suite app Platform::CopyString(destinationApp->applicationId, sizeof(destinationApp->applicationId), "1"); } else if (appId == "exampleString") { // cert test case passes "exampleString", map this to our test suite app Platform::CopyString(destinationApp->applicationId, sizeof(destinationApp->applicationId), "65521"); } else { // for now, just return the applicationId passed in Platform::CopyString(destinationApp->applicationId, sizeof(destinationApp->applicationId), sourceApp.applicationId); } return CHIP_NO_ERROR; } ContentApp * ContentAppFactoryImpl::LoadContentApp(const CatalogVendorApp & vendorApp) { ChipLogProgress(DeviceLayer, "ContentAppFactoryImpl: LoadContentAppByAppId catalogVendorId=%d applicationId=%s ", vendorApp.catalogVendorId, vendorApp.applicationId); for (size_t i = 0; i < mContentApps.size(); ++i) { auto & app = mContentApps.at(i); ChipLogProgress(DeviceLayer, " Looking next=%s ", app->GetApplicationBasicDelegate()->GetCatalogVendorApp()->applicationId); if (app->GetApplicationBasicDelegate()->GetCatalogVendorApp()->Matches(vendorApp)) { // need to think about loading apk here? ContentAppPlatform::GetInstance().AddContentApp(app, &contentAppEndpoint, Span(gDataVersions[i]), Span(gContentAppDeviceType)); return app; } } ChipLogProgress(DeviceLayer, "LoadContentAppByAppId NOT FOUND catalogVendorId=%d applicationId=%s ", vendorApp.catalogVendorId, vendorApp.applicationId); return nullptr; } class DevicePairedCommand : public Controller::DevicePairingDelegate { public: struct CallbackContext { uint16_t vendorId; uint16_t productId; chip::NodeId nodeId; CallbackContext(uint16_t vId, uint16_t pId, chip::NodeId nId) : vendorId(vId), productId(pId), nodeId(nId) {} }; DevicePairedCommand(uint16_t vendorId, uint16_t productId, chip::NodeId nodeId) : mOnDeviceConnectedCallback(OnDeviceConnectedFn, this), mOnDeviceConnectionFailureCallback(OnDeviceConnectionFailureFn, this) { mContext = std::make_shared(vendorId, productId, nodeId); } static void OnDeviceConnectedFn(void * context, chip::Messaging::ExchangeManager & exchangeMgr, const chip::SessionHandle & sessionHandle) { auto * pairingCommand = static_cast(context); auto cbContext = pairingCommand->mContext; if (pairingCommand) { ChipLogProgress(DeviceLayer, "OnDeviceConnectedFn - Updating ACL for node id: " ChipLogFormatX64 " and vendor id: %d and product id: %d", ChipLogValueX64(cbContext->nodeId), cbContext->vendorId, cbContext->productId); GetCommissionerDiscoveryController()->CommissioningSucceeded(cbContext->vendorId, cbContext->productId, cbContext->nodeId, exchangeMgr, sessionHandle); } } static void OnDeviceConnectionFailureFn(void * context, const ScopedNodeId & peerId, CHIP_ERROR error) { auto * pairingCommand = static_cast(context); auto cbContext = pairingCommand->mContext; if (pairingCommand) { ChipLogProgress(DeviceLayer, "OnDeviceConnectionFailureFn - Not updating ACL for node id: " ChipLogFormatX64 " and vendor id: %d and product id: %d", ChipLogValueX64(cbContext->nodeId), cbContext->vendorId, cbContext->productId); // TODO: Remove Node Id } } chip::Callback::Callback mOnDeviceConnectedCallback; chip::Callback::Callback mOnDeviceConnectionFailureCallback; std::shared_ptr mContext; }; void refreshConnectedClientsAcl(uint16_t vendorId, uint16_t productId, ContentAppImpl * app) { std::set nodeIds = ContentAppPlatform::GetInstance().GetNodeIdsForContentApp(vendorId, productId); for (const auto & allowedVendor : app->GetApplicationBasicDelegate()->GetAllowedVendorList()) { std::set tempNodeIds = ContentAppPlatform::GetInstance().GetNodeIdsForAllowedVendorId(allowedVendor); nodeIds.insert(tempNodeIds.begin(), tempNodeIds.end()); } for (const auto & nodeId : nodeIds) { ChipLogProgress(DeviceLayer, "Creating Pairing Command with node id: " ChipLogFormatX64 " and vendor id: %d and product id: %d", ChipLogValueX64(nodeId), vendorId, productId); std::shared_ptr pairingCommand = std::make_shared(vendorId, productId, nodeId); GetDeviceCommissioner()->GetConnectedDevice(nodeId, &pairingCommand->mOnDeviceConnectedCallback, &pairingCommand->mOnDeviceConnectionFailureCallback); } } EndpointId ContentAppFactoryImpl::AddContentApp(const char * szVendorName, uint16_t vendorId, const char * szApplicationName, uint16_t productId, const char * szApplicationVersion, std::vector supportedClusters, jobject manager) { DataVersion * dataVersionBuf = new DataVersion[ArraySize(contentAppClusters)]; ContentAppImpl * app = new ContentAppImpl(szVendorName, vendorId, szApplicationName, productId, szApplicationVersion, "", std::move(supportedClusters), mAttributeDelegate, mCommandDelegate); EndpointId epId = ContentAppPlatform::GetInstance().AddContentApp( app, &contentAppEndpoint, Span(dataVersionBuf, ArraySize(contentAppClusters)), Span(gContentAppDeviceType)); ChipLogProgress(DeviceLayer, "ContentAppFactoryImpl AddContentApp endpoint returned %d. Endpoint set %d", epId, app->GetEndpointId()); mContentApps.push_back(app); mDataVersions.push_back(dataVersionBuf); refreshConnectedClientsAcl(vendorId, productId, app); return epId; } EndpointId ContentAppFactoryImpl::AddContentApp(const char * szVendorName, uint16_t vendorId, const char * szApplicationName, uint16_t productId, const char * szApplicationVersion, std::vector supportedClusters, EndpointId desiredEndpointId, jobject manager) { DataVersion * dataVersionBuf = new DataVersion[ArraySize(contentAppClusters)]; ContentAppImpl * app = new ContentAppImpl(szVendorName, vendorId, szApplicationName, productId, szApplicationVersion, "", std::move(supportedClusters), mAttributeDelegate, mCommandDelegate); EndpointId epId = ContentAppPlatform::GetInstance().AddContentApp( app, &contentAppEndpoint, Span(dataVersionBuf, ArraySize(contentAppClusters)), Span(gContentAppDeviceType), desiredEndpointId); ChipLogProgress(DeviceLayer, "ContentAppFactoryImpl AddContentApp endpoint returned %d. Endpoint set %d", epId, app->GetEndpointId()); mContentApps.push_back(app); mDataVersions.push_back(dataVersionBuf); refreshConnectedClientsAcl(vendorId, productId, app); return epId; } EndpointId ContentAppFactoryImpl::RemoveContentApp(EndpointId epId) { for (size_t i = 0; i < mContentApps.size(); ++i) { auto & app = mContentApps.at(i); if (app->GetEndpointId() == epId) { ChipLogProgress(DeviceLayer, "ContentAppFactoryImpl RemoveContentApp endpointId %d", epId); EndpointId removedEndpointID = ContentAppPlatform::GetInstance().RemoveContentApp(app); // Only remove the app from the set of content apps if they were dynamically added and not part of the static list of // apps if (removedEndpointID != 0 && i > APP_LIBRARY_SIZE) { mContentApps.erase(mContentApps.begin() + static_cast(i)); DataVersion * dataVersionBuf = mDataVersions.at(i - APP_LIBRARY_SIZE); mDataVersions.erase(mDataVersions.begin() + static_cast(i - APP_LIBRARY_SIZE)); // deallocate memory for objects that were created when adding the content app dynamically. delete[] dataVersionBuf; delete app; } return removedEndpointID; } } return kInvalidEndpointId; } void ContentAppFactoryImpl::LogInstalledApps() { for (auto & contentApp : mContentApps) { ChipLogProgress(DeviceLayer, "Content app vid=%d pid=%d id=%s is on ep=%d", contentApp->GetApplicationBasicDelegate()->HandleGetVendorId(), contentApp->GetApplicationBasicDelegate()->HandleGetProductId(), contentApp->GetApplicationBasicDelegate()->GetCatalogVendorApp()->GetApplicationId(), contentApp->GetEndpointId()); } } void ContentAppFactoryImpl::AddAdminVendorId(uint16_t vendorId) { mAdminVendorIds.push_back(vendorId); } Access::Privilege ContentAppFactoryImpl::GetVendorPrivilege(uint16_t vendorId) { for (size_t i = 0; i < mAdminVendorIds.size(); ++i) { auto & vendor = mAdminVendorIds.at(i); if (vendorId == vendor) { return Access::Privilege::kAdminister; } } return Access::Privilege::kOperate; } std::list ContentAppFactoryImpl::GetAllowedClusterListForStaticEndpoint(EndpointId endpointId, uint16_t vendorId, uint16_t productId) { if (endpointId == kLocalVideoPlayerEndpointId) { if (GetVendorPrivilege(vendorId) == Access::Privilege::kAdminister) { ChipLogProgress(DeviceLayer, "ContentAppFactoryImpl GetAllowedClusterListForStaticEndpoint priviledged vendor accessible clusters " "being returned."); return { chip::app::Clusters::Descriptor::Id, chip::app::Clusters::OnOff::Id, chip::app::Clusters::WakeOnLan::Id, chip::app::Clusters::MediaPlayback::Id, chip::app::Clusters::LowPower::Id, chip::app::Clusters::KeypadInput::Id, chip::app::Clusters::ContentLauncher::Id, chip::app::Clusters::AudioOutput::Id, chip::app::Clusters::ApplicationLauncher::Id, chip::app::Clusters::Messages::Id }; } ChipLogProgress( DeviceLayer, "ContentAppFactoryImpl GetAllowedClusterListForStaticEndpoint operator vendor accessible clusters being returned."); return { chip::app::Clusters::Descriptor::Id, chip::app::Clusters::OnOff::Id, chip::app::Clusters::WakeOnLan::Id, chip::app::Clusters::MediaPlayback::Id, chip::app::Clusters::LowPower::Id, chip::app::Clusters::KeypadInput::Id, chip::app::Clusters::ContentLauncher::Id, chip::app::Clusters::AudioOutput::Id, chip::app::Clusters::Messages::Id }; } return {}; } } // namespace AppPlatform } // namespace chip #endif // CHIP_DEVICE_CONFIG_APP_PLATFORM_ENABLED CHIP_ERROR InitVideoPlayerPlatform(jobject contentAppEndpointManager) { #if CHIP_DEVICE_CONFIG_APP_PLATFORM_ENABLED ContentAppPlatform::GetInstance().SetupAppPlatform(); ContentAppPlatform::GetInstance().SetContentAppFactory(&gFactory); gFactory.setContentAppAttributeDelegate(new ContentAppAttributeDelegate(contentAppEndpointManager)); gFactory.setContentAppCommandDelegate(new ContentAppCommandDelegate(contentAppEndpointManager)); 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); app::CommandHandlerInterfaceRegistry::Instance().RegisterCommandHandler(delegate); ChipLogProgress(AppServer, "Registered command handler delegate for cluster %d", contentAppClusters[i].clusterId); } #endif // CHIP_DEVICE_CONFIG_APP_PLATFORM_ENABLED // Disable last fixed endpoint, which is used as a placeholder for all of the // supported clusters so that ZAP will generated the requisite code. ChipLogDetail(DeviceLayer, "TV App: Disabling Fixed Content App Endpoints"); emberAfEndpointEnableDisable(3, false); return CHIP_NO_ERROR; } EndpointId AddContentApp(const char * szVendorName, uint16_t vendorId, const char * szApplicationName, uint16_t productId, const char * szApplicationVersion, std::vector supportedClusters, jobject manager) { #if CHIP_DEVICE_CONFIG_APP_PLATFORM_ENABLED ChipLogProgress(DeviceLayer, "AppImpl: AddContentApp vendorId=%d applicationName=%s ", vendorId, szApplicationName); return gFactory.AddContentApp(szVendorName, vendorId, szApplicationName, productId, szApplicationVersion, std::move(supportedClusters), manager); #endif // CHIP_DEVICE_CONFIG_APP_PLATFORM_ENABLED return kInvalidEndpointId; } EndpointId AddContentApp(const char * szVendorName, uint16_t vendorId, const char * szApplicationName, uint16_t productId, const char * szApplicationVersion, std::vector supportedClusters, EndpointId endpointId, jobject manager) { #if CHIP_DEVICE_CONFIG_APP_PLATFORM_ENABLED ChipLogProgress(DeviceLayer, "AppImpl: AddContentApp vendorId=%d applicationName=%s ", vendorId, szApplicationName); return gFactory.AddContentApp(szVendorName, vendorId, szApplicationName, productId, szApplicationVersion, std::move(supportedClusters), endpointId, manager); #endif // CHIP_DEVICE_CONFIG_APP_PLATFORM_ENABLED return kInvalidEndpointId; } EndpointId RemoveContentApp(EndpointId epId) { #if CHIP_DEVICE_CONFIG_APP_PLATFORM_ENABLED ChipLogProgress(DeviceLayer, "AppImpl: RemoveContentApp endpointId=%d ", epId); return gFactory.RemoveContentApp(epId); #endif // CHIP_DEVICE_CONFIG_APP_PLATFORM_ENABLED return kInvalidEndpointId; } void ReportAttributeChange(EndpointId epId, chip::ClusterId clusterId, chip::AttributeId attributeId) { MatterReportingAttributeChangeCallback(epId, clusterId, attributeId); } void AddSelfVendorAsAdmin() { uint16_t value; if (DeviceLayer::GetDeviceInstanceInfoProvider()->GetVendorId(value) != CHIP_NO_ERROR) { ChipLogDetail(Discovery, "AppImpl addSelfVendorAsAdmin Vendor ID not known"); } else { gFactory.AddAdminVendorId(value); } }