/* * * 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 classes relating to Content App platform of the Video Player. */ #pragma once #include #include #include #include #include #include #include using chip::app::Clusters::ApplicationBasic::CatalogVendorApp; using chip::Controller::CommandResponseFailureCallback; using chip::Controller::CommandResponseSuccessCallback; using BindingListType = chip::app::Clusters::Binding::Attributes::Binding::TypeInfo::Type; namespace chip { namespace AppPlatform { // The AppPlatform overrides emberAfExternalAttributeReadCallback to handle external attribute reads for ContentApps. // This callback can be used to handle external attribute reads for attributes belonging to static endpoints. Protocols::InteractionModel::Status AppPlatformExternalAttributeReadCallback(EndpointId endpoint, ClusterId clusterId, const EmberAfAttributeMetadata * attributeMetadata, uint8_t * buffer, uint16_t maxReadLength); // The AppPlatform overrides emberAfExternalAttributeWriteCallback to handle external attribute writes for ContentApps. // This callback can be used to handle external attribute writes for attributes belonging to static endpoints. Protocols::InteractionModel::Status AppPlatformExternalAttributeWriteCallback(EndpointId endpoint, ClusterId clusterId, const EmberAfAttributeMetadata * attributeMetadata, uint8_t * buffer); inline constexpr EndpointId kTargetBindingClusterEndpointId = 0; inline constexpr EndpointId kLocalVideoPlayerEndpointId = 1; inline constexpr EndpointId kLocalSpeakerEndpointId = 2; class DLL_EXPORT ContentAppFactory { public: virtual ~ContentAppFactory() = default; // Lookup CatalogVendor App for this client (vendor id/product id client) // and then write it to destinationApp // return error if not found virtual CHIP_ERROR LookupCatalogVendorApp(uint16_t vendorId, uint16_t productId, CatalogVendorApp * destinationApp) = 0; // Lookup ContentApp for this catalog id / app id and load it virtual ContentApp * LoadContentApp(const CatalogVendorApp & vendorApp) = 0; // Gets the catalog vendor ID used by this platform virtual uint16_t GetPlatformCatalogVendorId() = 0; // Converts application (any catalog) into the platform's catalog Vendor // and then writes it to destinationApp virtual CHIP_ERROR ConvertToPlatformCatalogVendorApp(const CatalogVendorApp & sourceApp, CatalogVendorApp * destinationApp) = 0; // Get the privilege this vendorId should have on endpoints 1, 2, and content app endpoints // In the case of casting video clients, this should usually be Access::Privilege::kOperate // and for voice agents, this may be Access::Privilege::kAdminister // When a vendor has admin privileges, it will get access to all clusters on ep1 virtual Access::Privilege GetVendorPrivilege(uint16_t vendorId) = 0; // Get the cluster list this vendorId/productId should have on static endpoints such as ep1 for casting video clients. // When a vendor has admin privileges, it will get access to all clusters on ep1 virtual std::list GetAllowedClusterListForStaticEndpoint(EndpointId endpointId, uint16_t vendorId, uint16_t productId) = 0; }; class DLL_EXPORT ContentAppPlatform { public: static ContentAppPlatform & GetInstance() { static ContentAppPlatform instance; return instance; } void SetupAppPlatform(); inline void SetContentAppFactory(ContentAppFactory * factory) { mContentAppFactory = factory; }; // add apps to the platform. // This will assign the app to an endpoint (if it is not already added) and make it accessible via Matter // returns the global endpoint for this app, or kNoCurrentEndpointId if an error occurred. // dataVersionStorage.size() needs to be at least as big as the number of // server clusters in the EmberAfEndpointType passed in. EndpointId AddContentApp(ContentApp * app, EmberAfEndpointType * ep, const Span & dataVersionStorage, const Span & deviceTypeList); // add apps to the platform. // This will assign the app to the desiredEndpointId (if it is not already used) // and make it accessible via Matter, return the global endpoint for this app(if app is already added) // , or kNoCurrentEndpointId if an error occurred. desiredEndpointId cannot be less that Fixed endpoint count // dataVersionStorage.size() needs to be at least as big as the number of // server clusters in the EmberAfEndpointType passed in. EndpointId AddContentApp(ContentApp * app, EmberAfEndpointType * ep, const Span & dataVersionStorage, const Span & deviceTypeList, EndpointId desiredEndpointId); // remove app from the platform. // returns the endpoint id where the app was, or 0 if app was not loaded EndpointId RemoveContentApp(ContentApp * app); // load and unload by vendor id // void UnloadContentAppByVendorId(uint16_t vendorId, uint16_t productId); // Lookup ContentApp for this client (vendor id/product id client) and load it ContentApp * LoadContentAppByClient(uint16_t vendorId, uint16_t productId); // Lookup ContentApp described by this application and load it ContentApp * LoadContentApp(const CatalogVendorApp & application); // helpful method to get a Content App by endpoint in order to perform attribute or command ops ContentApp * GetContentApp(EndpointId id); // helpful method to get a Content App by application, does not load if not found ContentApp * GetContentApp(const CatalogVendorApp & application); // sets the current app for this platform void SetCurrentApp(ContentApp * app); // returns true if there is a current app for this platform inline bool HasCurrentApp() { return mCurrentAppEndpointId != kNoCurrentEndpointId; } // returns true if the vendor/app arguments are the current app bool IsCurrentApp(ContentApp * app); // returns the current app endpoint inline EndpointId GetCurrentAppEndpointId() { return mCurrentAppEndpointId; }; // unset this as current app, if it is current app void UnsetIfCurrentApp(ContentApp * app); // loads content app identified by vid/pid of client and calls HandleGetSetupPasscode. // Returns 0 if passcode cannot be obtained. uint32_t GetPasscodeFromContentApp(uint16_t vendorId, uint16_t productId, CharSpan rotatingId); // locates app identified by target info and confirms that it grants access to given vid/pid of client, // loads given app and calls HandleGetSetupPasscode. Sets passcode to 0 if it cannot be obtained. // return true if a matching app was found (and it granted this client access), even if a passcode was not obtained bool HasTargetContentApp(uint16_t vendorId, uint16_t productId, CharSpan rotatingId, Protocols::UserDirectedCommissioning::TargetAppInfo & info, uint32_t & passcode); // returns set of connected nodes for a given content app std::set GetNodeIdsForContentApp(uint16_t vendorId, uint16_t productId); // returns set of connected nodes for a given allowed vendor id std::set GetNodeIdsForAllowedVendorId(uint16_t vendorId); // store node id for content app after commissioning // node id can be used later on to update ACL // in case app is not installed // Note: This is in memory storing, the values are deleted after reboot void StoreNodeIdForContentApp(uint16_t vendorId, uint16_t productId, NodeId nodeId); /** * @brief * Add ACLs on this device for the given client, * and create bindings on the given client so that it knows what it has access to. * * The default implementation follows the device library Video Player Architecture spec * for a typical video player given assumptions like video player endpoint id is 1 and * speaker endpoint id is 2. Some devices may need to override this implementation when * these assumptions are not correct. * * @param[in] exchangeMgr Exchange manager to be used to get an exchange context. * @param[in] sessionHandle Reference to an established session. * @param[in] targetVendorId Vendor ID for the target device. * @param[in] targetProductId Product ID for the target device. * @param[in] localNodeId The NodeId for the local device. * @param[in] rotatingId The rotating account ID to handle account login. * @param[in] passcode The passcode to handle account login. * @param[in] bindings Any additional bindings to include. This may include current bindings. * @param[in] successCb The function to be called on success of adding the binding. * @param[in] failureCb The function to be called on failure of adding the binding. * * @return CHIP_ERROR CHIP_NO_ERROR on success, or corresponding error */ CHIP_ERROR ManageClientAccess(Messaging::ExchangeManager & exchangeMgr, SessionHandle & sessionHandle, uint16_t targetVendorId, uint16_t targetProductId, NodeId localNodeId, chip::CharSpan rotatingId, uint32_t passcode, std::vector bindings, Controller::WriteResponseSuccessCallback successCb, Controller::WriteResponseFailureCallback failureCb); protected: // requires vendorApp to be in the catalog of the platform ContentApp * LoadContentAppInternal(const CatalogVendorApp & vendorApp); ContentApp * GetContentAppInternal(const CatalogVendorApp & vendorApp); CHIP_ERROR GetACLEntryIndex(size_t * foundIndex, FabricIndex fabricIndex, NodeId subjectNodeId); static const int kNoCurrentEndpointId = 0; EndpointId mCurrentAppEndpointId = kNoCurrentEndpointId; ContentAppFactory * mContentAppFactory = nullptr; EndpointId mCurrentEndpointId; EndpointId mFirstDynamicEndpointId; ContentApp * mContentApps[CHIP_DEVICE_CONFIG_DYNAMIC_ENDPOINT_COUNT]; // key is string -> vendorId:producTid std::map> mConnectedContentAppNodeIds; private: void IncrementCurrentEndpointID(); }; } // namespace AppPlatform } // namespace chip