RDK-38472: Implement Cobalt platform extension for ContentEntitlement service
Reason for change: Support Content Entiltements for Cobalt.
Test Procedure: refer ticket.
Risks: None
Priority: P1
Signed-off-by: sramul630 <sunil_ramulu@comcast.com>
Change-Id: Id77f173872f07d3751d6f42995b00374ade7a9b5
diff --git a/plugin/CobaltImplementation.cpp b/plugin/CobaltImplementation.cpp
index 042fc5a..371cb99 100644
--- a/plugin/CobaltImplementation.cpp
+++ b/plugin/CobaltImplementation.cpp
@@ -111,6 +111,7 @@
Add(_T("autosuspenddelay"), &AutoSuspendDelay);
Add(_T("systemproperties"), &SystemProperties);
Add(_T("closurepolicy"), &ClosurePolicy);
+ Add(_T("fireboltendpoint"), &FireboltEndpoint);
}
~Config() {
}
@@ -126,6 +127,7 @@
Core::JSON::DecUInt16 AutoSuspendDelay;
Core::JSON::VariantContainer SystemProperties;
Core::JSON::String ClosurePolicy;
+ Core::JSON::String FireboltEndpoint;
};
class NotificationSink: public Core::Thread {
@@ -318,6 +320,9 @@
SbRdkSetSetting("systemproperties", properties.c_str());
}
+ if (config.FireboltEndpoint.IsSet() == true)
+ Core::SystemInfo::SetEnvironment(_T("FIREBOLT_ENDPOINT"), config.FireboltEndpoint.Value());
+
SYSLOG(Logging::Notification, (_T("Preload is set to: %s\n"), _preloadEnabled ? "true" : "false"));
if (config.ClosurePolicy.IsSet() == true) {
diff --git a/plugin/CobaltPlugin.json b/plugin/CobaltPlugin.json
index 739c5aa..56e1398 100644
--- a/plugin/CobaltPlugin.json
+++ b/plugin/CobaltPlugin.json
@@ -87,6 +87,10 @@
},
"systemproperties": {
"$ref": "#/definitions/systemproperties"
+ },
+ "fireboltendpoint": {
+ "type": "string",
+ "description": "A URL that specifies access point to Firebolt Riple. Should include session id in the query."
}
}
}
diff --git a/plugin/doc/CobaltPlugin.md b/plugin/doc/CobaltPlugin.md
index 80e03e5..3690478 100644
--- a/plugin/doc/CobaltPlugin.md
+++ b/plugin/doc/CobaltPlugin.md
@@ -92,6 +92,7 @@
| configuration?.systemproperties?.integratorname | string | <sup>*(optional)*</sup> The original manufacture of the device |
| configuration?.systemproperties?.friendlyname | string | <sup>*(optional)*</sup> A friendly name for this actual device |
| configuration?.systemproperties?.devicetype | string | <sup>*(optional)*</sup> The type of the device. (must be one of the following: *SetTopBox*, *OverTheTopBox*, *TV*) |
+| configuration?.fireboltendpoint | string | <sup>*(optional)*</sup> A URL that specifies access point to Firebolt Riple. Should include session id in the query |
<a name="head.Methods"></a>
# Methods
diff --git a/src/third_party/starboard/rdk/shared/content_entitlement_platform_service.cc b/src/third_party/starboard/rdk/shared/content_entitlement_platform_service.cc
new file mode 100644
index 0000000..57c2ae7
--- /dev/null
+++ b/src/third_party/starboard/rdk/shared/content_entitlement_platform_service.cc
@@ -0,0 +1,201 @@
+//
+// Copyright 2020 Comcast Cable Communications Management, LLC
+//
+// 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.
+//
+// SPDX-License-Identifier: Apache-2.0//
+// Copyright 2016 The Cobalt 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.
+
+#include "third_party/starboard/rdk/shared/content_entitlement_platform_service.h"
+
+#include <memory>
+#include <string>
+
+#include "cobalt/extension/platform_service.h"
+#include "starboard/common/log.h"
+#include "starboard/common/string.h"
+#include "starboard/configuration.h"
+#include "starboard/shared/starboard/application.h"
+
+#include "third_party/starboard/rdk/shared/firebolt/firebolt.h"
+#include "third_party/starboard/rdk/shared/log_override.h"
+
+#include <core/JSON.h>
+
+const char kCobaltExtensionContentEntitlementName[] = "com.google.youtube.tv.ContentEntitlement";
+
+typedef struct CobaltExtensionPlatformServicePrivate {
+ void* context;
+ ReceiveMessageCallback receive_callback;
+ CobaltExtensionPlatformServicePrivate(void* context, ReceiveMessageCallback receive_callback)
+ : context(context), receive_callback(receive_callback) {};
+ virtual ~CobaltExtensionPlatformServicePrivate() = default;
+ virtual void* Send(void* data, uint64_t data_length, uint64_t* output_length, bool* invalid_state) = 0;
+} CobaltExtensionPlatformServicePrivate;
+
+namespace third_party {
+namespace starboard {
+namespace rdk {
+namespace shared {
+
+namespace {
+
+using namespace WPEFramework;
+
+struct ContentEntitlementCobaltExtensionPlatformService : public CobaltExtensionPlatformServicePrivate {
+ struct ContentEntitlementUpdate : Core::JSON::Container {
+ struct Entitlement : Core::JSON::Container {
+ Entitlement()
+ : Core::JSON::Container() {
+ Init();
+ }
+ Entitlement(const Entitlement& other)
+ : Core::JSON::Container()
+ , id(other.id) {
+ Init();
+ }
+ Entitlement& operator=(const Entitlement& rhs) {
+ id = rhs.id;
+ return (*this);
+ }
+ Core::JSON::String id;
+ private:
+ void Init() {
+ Add(_T("id"), &id);
+ }
+ };
+ struct Payload : Core::JSON::Container {
+ Payload()
+ : Core::JSON::Container() {
+ Add(_T("entitlements"), &entitlements);
+ }
+ Core::JSON::ArrayType<Entitlement> entitlements;
+ };
+ ContentEntitlementUpdate()
+ : Core::JSON::Container() {
+ Add(_T("message"), &message);
+ Add(_T("payload"), &payload);
+ }
+ Core::JSON::String message;
+ Payload payload;
+ };
+
+ ContentEntitlementCobaltExtensionPlatformService(void* context, ReceiveMessageCallback callback)
+ : CobaltExtensionPlatformServicePrivate(context, callback) {
+ }
+
+ void* Send(void* data, uint64_t length, uint64_t* output_length, bool* invalid_state) override {
+ bool result = false;
+
+ if (data && length) {
+ ContentEntitlementUpdate update;
+ std::string json_string(static_cast<const char*>(data), length);
+
+ if(!update.FromString(json_string)) {
+ SB_LOG(ERROR) << "Failed to parse ContentEntitlement message";
+ }
+ else if(update.message == "updateEntitlements") {
+ const auto &payload_entitlements = update.payload.entitlements;
+
+ std::vector<firebolt::Entitlement> entitlements;
+ for(int i = 0; i < payload_entitlements.Length(); ++i)
+ entitlements.push_back({payload_entitlements[i].id.Value()});
+
+ firebolt::Discovery discovery;
+ result = discovery.entitlements(entitlements);
+ }
+ }
+
+ bool* ptr = reinterpret_cast<bool*>(SbMemoryAllocate(sizeof(bool)));
+ *ptr = result;
+ return static_cast<void*>(ptr);
+ }
+};
+
+bool Has(const char* name) {
+ // Check if platform has service name.
+ bool result = strcmp(name, kCobaltExtensionContentEntitlementName) == 0 && firebolt::IsAvailable();
+ SB_LOG(INFO) << "Entitlement Has service called " << name << " result = " << result;
+ return result;
+}
+
+CobaltExtensionPlatformService Open(void* context,
+ const char* name,
+ ReceiveMessageCallback receive_callback) {
+ SB_DCHECK(context);
+
+ CobaltExtensionPlatformService service;
+
+ if (strcmp(name, kCobaltExtensionContentEntitlementName) == 0 && firebolt::IsAvailable()) {
+ SB_LOG(INFO) << "Open() service created: " << name;
+ service =
+ new ContentEntitlementCobaltExtensionPlatformService(context, receive_callback);
+ } else {
+ SB_LOG(ERROR) << "Open() service name does not exist: " << name;
+ service = kCobaltExtensionPlatformServiceInvalid;
+ }
+
+ delete[] name;
+
+ return service;
+}
+
+void Close(CobaltExtensionPlatformService service) {
+ SB_LOG(INFO) << "Close() Service.";
+ delete static_cast<CobaltExtensionPlatformServicePrivate*>(service);
+}
+
+void* Send(CobaltExtensionPlatformService service,
+ void* data,
+ uint64_t length,
+ uint64_t* output_length,
+ bool* invalid_state) {
+ SB_DCHECK(data);
+ SB_DCHECK(length);
+ SB_DCHECK(output_length);
+ SB_DCHECK(invalid_state);
+ return static_cast<CobaltExtensionPlatformServicePrivate*>(service)->Send(data, length, output_length, invalid_state);
+}
+
+const CobaltExtensionPlatformServiceApi kPlatformServiceApi = {
+ kCobaltExtensionPlatformServiceName,
+ 1, // API version that's implemented.
+ &Has,
+ &Open,
+ &Close,
+ &Send
+};
+
+} // namespace
+
+const void* GetPlatformServiceApi() {
+ SB_LOG(INFO) << "GetContentEntitlementPlatformServiceApi return ";
+ return &kPlatformServiceApi;
+}
+
+} // namespace thirdpary
+} // namespace starboard
+} // namespace rdk
+} // namespace shared
diff --git a/src/third_party/starboard/rdk/shared/content_entitlement_platform_service.h b/src/third_party/starboard/rdk/shared/content_entitlement_platform_service.h
new file mode 100644
index 0000000..06174a8
--- /dev/null
+++ b/src/third_party/starboard/rdk/shared/content_entitlement_platform_service.h
@@ -0,0 +1,46 @@
+//
+// Copyright 2020 Comcast Cable Communications Management, LLC
+//
+// 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.
+//
+// SPDX-License-Identifier: Apache-2.0//
+// Copyright 2016 The Cobalt 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.
+
+#ifndef THIRD_PARTY_STARBOARD_RDK_SHARED_CONTENT_ENTITLEMENT_H_
+#define THIRD_PARTY_STARBOARD_RDK_SHARED_CONTENT_ENTITLEMENT_H_
+
+namespace third_party {
+namespace starboard {
+namespace rdk {
+namespace shared {
+
+const void* GetPlatformServiceApi();
+
+} // namespace shared
+} // namespace rdk
+} // namespace starboard
+} // namespace third_party
+
+#endif // THIRD_PARTY_STARBOARD_RDK_SHARED_CONTENT_ENTITLEMENT_H_
diff --git a/src/third_party/starboard/rdk/shared/firebolt/firebolt.cc b/src/third_party/starboard/rdk/shared/firebolt/firebolt.cc
new file mode 100644
index 0000000..9c5a362
--- /dev/null
+++ b/src/third_party/starboard/rdk/shared/firebolt/firebolt.cc
@@ -0,0 +1,468 @@
+//
+// Copyright 2020 Comcast Cable Communications Management, LLC
+//
+// 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.
+//
+// SPDX-License-Identifier: Apache-2.0
+#ifndef MODULE_NAME
+#define MODULE_NAME CobaltRDKServices
+#endif
+
+#include "third_party/starboard/rdk/shared/firebolt/firebolt.h"
+
+#include <mutex>
+#include <chrono>
+#include <condition_variable>
+#include <memory>
+#include <websocket/WebSocketLink.h>
+#include <websocket/URL.h>
+
+#include "third_party/starboard/rdk/shared/log_override.h"
+
+using namespace WPEFramework;
+
+namespace third_party {
+namespace starboard {
+namespace rdk {
+namespace shared {
+namespace firebolt {
+
+static const auto kDefaultTimeout = std::chrono::milliseconds(50);
+
+namespace {
+
+template<typename T>
+T valueOr(const Core::OptionalType<T>& opt, T defaultValue = {}) {
+ return opt.IsSet() ? opt.Value() : defaultValue;
+};
+
+// Simplefied version of WPEFramework::JSONRPC::Link
+class Link {
+private:
+ class CommunicationChannel {
+ private:
+ class FactoryImpl {
+ private:
+ friend Core::SingletonType<FactoryImpl>;
+ FactoryImpl(const FactoryImpl&) = delete;
+ FactoryImpl& operator=(const FactoryImpl&) = delete;
+ FactoryImpl() = default;
+
+ Core::ProxyPoolType<Core::JSONRPC::Message> _jsonRPCFactory { 2 };
+ public:
+ ~FactoryImpl() = default;
+
+ static FactoryImpl& Instance()
+ {
+ static FactoryImpl& _singleton = Core::SingletonType<FactoryImpl>::Instance();
+ return (_singleton);
+ }
+
+ Core::ProxyType<Core::JSONRPC::Message> Element(const string&)
+ {
+ return (_jsonRPCFactory.Element());
+ }
+ };
+
+ class ChannelImpl : public Core::StreamJSONType<Web::WebSocketClientType<Core::SocketStream>, FactoryImpl&, Core::JSON::IElement> {
+ private:
+ ChannelImpl(const ChannelImpl&) = delete;
+ ChannelImpl& operator=(const ChannelImpl&) = delete;
+
+ typedef Core::StreamJSONType<Web::WebSocketClientType<Core::SocketStream>, FactoryImpl&, Core::JSON::IElement> BaseClass;
+
+ public:
+ ChannelImpl(CommunicationChannel* parent, const Core::NodeId& remoteNode, const string& path, const string& query)
+ : BaseClass(5, FactoryImpl::Instance(), path, "", query, "", false, true, false, remoteNode.AnyInterface(), remoteNode, 4096, 4096)
+ , _parent(*parent)
+ {
+ }
+ virtual ~ChannelImpl() = default;
+ virtual void Received(Core::ProxyType<Core::JSON::IElement>& jsonObject) override
+ {
+ Core::ProxyType<Core::JSONRPC::Message> inbound(jsonObject);
+ ASSERT(inbound.IsValid() == true);
+ if (inbound.IsValid() == true) {
+ _parent.Inbound(inbound);
+ }
+ }
+ virtual void Send(Core::ProxyType<Core::JSON::IElement>& jsonObject) override
+ {
+#if 0
+ string message;
+ ToMessage(jsonObject, message);
+ SB_LOG(INFO) << "sending message: " << message;
+#endif
+ }
+ virtual void StateChange() override
+ {
+ _parent.StateChange();
+ }
+ virtual bool IsIdle() const
+ {
+ return (true);
+ }
+ private:
+ void ToMessage(const Core::ProxyType<Core::JSON::IElement>& jsonObject, string& message) const
+ {
+ Core::ProxyType<Core::JSONRPC::Message> inbound(jsonObject);
+ ASSERT(inbound.IsValid());
+ if (inbound.IsValid())
+ inbound->ToString(message);
+ }
+ private:
+ CommunicationChannel& _parent;
+ };
+
+ protected:
+ CommunicationChannel(const Core::NodeId& remoteNode, const string& path, const string& query)
+ : _channel(this, remoteNode, path, query)
+ , _sequence(0)
+ {
+ }
+
+ public:
+ virtual ~CommunicationChannel()
+ {
+ Close();
+ }
+ static Core::ProxyType<CommunicationChannel> Instance(const Core::NodeId& remoteNode, const string& path, const string& query)
+ {
+ static Core::ProxyMapType<string, CommunicationChannel> channelMap;
+ string searchLine = remoteNode.HostAddress() + '@' + path + '?' + query;
+ return (channelMap.template Instance<CommunicationChannel>(searchLine, remoteNode, path, query));
+ }
+ static Core::ProxyType<Core::JSONRPC::Message> Message()
+ {
+ return (FactoryImpl::Instance().Element(string()));
+ }
+ void Register(Link& client)
+ {
+ typename std::unique_lock<std::mutex> lock(_adminLock);
+ ASSERT(std::find(_observers.begin(), _observers.end(), &client) == _observers.end());
+ _observers.push_back(&client);
+ }
+ void Unregister(Link& client)
+ {
+ typename std::unique_lock<std::mutex> lock(_adminLock);
+ auto index = std::find(_observers.begin(), _observers.end(), &client);
+ if (index != _observers.end())
+ _observers.erase(index);
+ }
+ void Submit(const Core::ProxyType<Core::JSON::IElement>& message)
+ {
+ _channel.Submit(message);
+ }
+ bool IsSuspended() const
+ {
+ return (_channel.IsSuspended());
+ }
+ bool IsOpen() const
+ {
+ return _channel.IsOpen();
+ }
+ uint32_t Initialize()
+ {
+ return (Open(1000));
+ }
+ uint32_t Sequence() const
+ {
+ return (++_sequence);
+ }
+ protected:
+ void StateChange()
+ {
+ }
+ bool Open(const uint32_t waitTime)
+ {
+ bool result = true;
+ if (_channel.IsClosed() == true) {
+ result = (_channel.Open(waitTime) == Core::ERROR_NONE);
+ }
+ return (result);
+ }
+ void Close()
+ {
+ _channel.Close(Core::infinite);
+ }
+ private:
+ uint32_t Inbound(const Core::ProxyType<Core::JSONRPC::Message>& inbound)
+ {
+ uint32_t result = Core::ERROR_UNAVAILABLE;
+
+ typename std::unique_lock<std::mutex> lock(_adminLock);
+ auto index = _observers.begin();
+ while ((result != Core::ERROR_NONE) && (index != _observers.end())) {
+ result = (*index)->Inbound(inbound);
+ index++;
+ }
+
+ return (result);
+ }
+
+ private:
+ std::mutex _adminLock;
+ ChannelImpl _channel;
+ std::list< Link *> _observers;
+ mutable std::atomic<uint32_t> _sequence;
+ };
+
+ static Core::ProxyType<CommunicationChannel> CommunicationChannelInstance(const Core::URL& url)
+ {
+ const auto& host = valueOr(url.Host(), string("127.0.0.1"));
+ const auto& path = string("/") + valueOr(url.Path());
+ const auto& port = valueOr(url.Port(), Core::URL::Port(url.Type()));
+ const auto& query = valueOr(url.Query());
+ Core::NodeId nodeId = Core::NodeId(host.c_str(), port);
+ return CommunicationChannel::Instance(nodeId, path, query);
+ }
+
+ Link(const Link&) = delete;
+ Link& operator=(Link&) = delete;
+
+public:
+ using ErrorInfo = Core::JSONRPC::Message::Info;
+ using CallbackFunction = std::function<void(const Core::JSONRPC::Message&)>;
+ using CallbackMap = std::map<uint32_t, CallbackFunction>;
+
+ explicit Link(const Core::URL& url)
+ : _channel ( CommunicationChannelInstance(url) )
+ {
+ _channel->Register(*this);
+ }
+
+ virtual ~Link()
+ {
+ _channel->Unregister(*this);
+ }
+
+ uint32_t SendAsync(const char method[], const Core::JSON::IElement& parameters, CallbackFunction&& cb, uint32_t& callbackId)
+ {
+ uint32_t result = Core::ERROR_UNAVAILABLE;
+ if ( method ) {
+ if ( _channel->IsSuspended() == true || _channel->IsOpen() == false ) {
+ result = Core::ERROR_ASYNC_FAILED;
+ } else {
+ uint32_t id;
+ Core::ProxyType<Core::JSONRPC::Message> message(CommunicationChannel::Message());
+
+ id = _channel->Sequence();
+ message->Id = id;
+ message->Designator = method;
+ if ( parameters.IsSet() ) {
+ string params;
+ parameters.ToString(params);
+ message->Parameters = params;
+ }
+
+ callbackId = id;
+ InsertCallback(id, std::move(cb));
+
+ _channel->Submit(Core::ProxyType<Core::JSON::IElement>(message));
+ message.Release();
+
+ result = Core::ERROR_NONE;
+ }
+ }
+ return (result);
+ }
+
+ uint32_t Send(std::chrono::microseconds waitTime, const char method[], const Core::JSON::IElement& parameters, Core::JSON::IElement& result, ErrorInfo& errorInfo)
+ {
+ uint32_t rc;
+ uint32_t callbackId = -1u;
+ std::condition_variable signal;
+ std::shared_ptr<bool> done = std::make_shared<bool>(false);
+
+ CallbackFunction cb = [this, weak_done = std::weak_ptr<bool>(done), &result, &errorInfo, &signal](const Core::JSONRPC::Message& m) {
+ auto done = weak_done.lock();
+ if ( done == nullptr )
+ return;
+ std::unique_lock<std::mutex> lock(_adminLock);
+ if ( *done == false ) // timeout check
+ {
+ if ( m.Result.IsSet() )
+ result.FromString(m.Result.Value());
+ else
+ errorInfo = m.Error;
+ *done = true;
+ lock.unlock();
+ signal.notify_one();
+ }
+ };
+
+ result.Clear();
+ errorInfo.Clear();
+
+ rc = SendAsync(method, parameters, std::move(cb), callbackId);
+
+ std::unique_lock<std::mutex> lock(_adminLock);
+ if (rc == Core::ERROR_NONE) {
+ if ( ! signal.wait_for(lock, waitTime, [&]{ return *done; }) ) {
+ *done = true; // set to indicate timeout
+ rc = Core::ERROR_ASYNC_FAILED;
+ }
+ }
+
+ RemoveCallback(lock, callbackId);
+
+ return rc;
+ }
+
+ uint32_t RemoveCallbackById(int32_t id)
+ {
+ std::unique_lock<std::mutex> lock(_adminLock);
+ return RemoveCallback(lock, id);
+ }
+
+private:
+ friend CommunicationChannel;
+
+ uint32_t RemoveCallback(std::unique_lock<std::mutex>&, int32_t id)
+ {
+ auto it = _callbackMap.find(id);
+ if (it != _callbackMap.end())
+ _callbackMap.erase(it);
+ return Core::ERROR_NONE;
+ }
+
+ void InsertCallback(uint32_t id, CallbackFunction&& cb)
+ {
+ std::unique_lock<std::mutex> lock(_adminLock);
+
+ _callbackMap.emplace(
+ std::piecewise_construct,
+ std::forward_as_tuple(id),
+ std::forward_as_tuple(std::move(cb)));
+ }
+
+ uint32_t Inbound(const Core::ProxyType<Core::JSONRPC::Message>& inbound)
+ {
+ uint32_t result = Core::ERROR_INVALID_SIGNATURE;
+ if ( inbound->Id.IsSet() ) {
+ if ( inbound->Result.IsSet() || inbound->Error.IsSet() ) {
+ uint32_t id = inbound->Id.Value();
+ CallbackFunction cb;
+
+ _adminLock.lock();
+ auto it = _callbackMap.find(id);
+ if (it != _callbackMap.end()) {
+ cb = std::move(it->second);
+ _callbackMap.erase(it);
+ }
+ _adminLock.unlock();
+
+ if (cb)
+ cb(*inbound);
+
+ result = Core::ERROR_NONE;
+ }
+ }
+ return (result);
+ }
+
+private:
+ Core::ProxyType< CommunicationChannel > _channel;
+ std::mutex _adminLock;
+ CallbackMap _callbackMap;
+};
+
+static Core::URL FireboltEndpoint()
+{
+ static std::once_flag flag;
+ static Core::URL url;
+ std::call_once(flag, [&](){
+ string envVar;
+ if ((Core::SystemInfo::GetEnvironment(_T("FIREBOLT_ENDPOINT"), envVar) == true) && (envVar.empty() == false))
+ url = Core::URL(envVar.c_str());
+ });
+ return url;
+}
+
+struct Entitlements : Core::JSON::Container
+{
+ struct Entitlement : Core::JSON::Container
+ {
+ Entitlement()
+ : Core::JSON::Container()
+ {
+ Init();
+ }
+ Entitlement(const Entitlement& other)
+ : Core::JSON::Container()
+ , entitlementId(other.entitlementId)
+ {
+ Init();
+ }
+ Entitlement& operator=(const Entitlement& rhs)
+ {
+ entitlementId = rhs.entitlementId;
+ return (*this);
+ }
+ Core::JSON::String entitlementId;
+ private:
+ void Init() {
+ Add(_T("entitlementId"), &entitlementId);
+ }
+ };
+ Entitlements()
+ : Core::JSON::Container()
+ {
+ Add(_T("entitlements"), &entitlements);
+ }
+
+ Core::JSON::ArrayType<Entitlement> entitlements;
+};
+
+} // namespace
+
+bool IsAvailable()
+{
+ auto url = FireboltEndpoint();
+ return url.IsValid() && (url.Type() == Core::URL::SCHEME_WS || url.Type() == Core::URL::SCHEME_WSS);
+}
+
+bool Discovery::entitlements(const std::vector<Entitlement>& entitlements)
+{
+ if (IsAvailable())
+ {
+ Link link( FireboltEndpoint() );
+
+ uint32_t rc;
+ Entitlements params;
+ Link::ErrorInfo error;
+ Core::JSON::String result;
+
+ for ( const auto& i : entitlements )
+ params.entitlements.Add().entitlementId = i.entitlementId;
+
+ rc = link.Send(kDefaultTimeout, "discovery.entitlements", params, result, error);
+
+ if (rc != Core::ERROR_NONE || result.IsSet() == false)
+ {
+ SB_LOG(ERROR) << "Failed to send 'discovery.entitlements', rc=" << rc
+ << " ( " << Core::ErrorToString(rc) << " )"
+ << ", error code = " << error.Code.Value()
+ << " (" << error.Text.Value() << ")";
+ }
+ else
+ return true;
+ }
+ return false;
+}
+
+} // namespace firebolt
+} // namespace shared
+} // namespace rdk
+} // namespace starboard
+} // namespace third_party
diff --git a/src/third_party/starboard/rdk/shared/firebolt/firebolt.h b/src/third_party/starboard/rdk/shared/firebolt/firebolt.h
new file mode 100644
index 0000000..2773f27
--- /dev/null
+++ b/src/third_party/starboard/rdk/shared/firebolt/firebolt.h
@@ -0,0 +1,42 @@
+//
+// Copyright 2020 Comcast Cable Communications Management, LLC
+//
+// 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.
+//
+// SPDX-License-Identifier: Apache-2.0
+#pragma once
+
+#include <string>
+#include <vector>
+
+namespace third_party {
+namespace starboard {
+namespace rdk {
+namespace shared {
+namespace firebolt {
+
+bool IsAvailable();
+
+struct Entitlement {
+ std::string entitlementId;
+};
+
+struct Discovery {
+ bool entitlements(const std::vector<Entitlement>&);
+};
+
+} // namespace firebolt
+} // namespace shared
+} // namespace rdk
+} // namespace starboard
+} // namespace third_party
diff --git a/src/third_party/starboard/rdk/shared/sources.gypi b/src/third_party/starboard/rdk/shared/sources.gypi
index a3d9201..4dd0bac 100644
--- a/src/third_party/starboard/rdk/shared/sources.gypi
+++ b/src/third_party/starboard/rdk/shared/sources.gypi
@@ -498,6 +498,10 @@
'<(DEPTH)/third_party/starboard/rdk/shared/hang_detector.cc',
'<(DEPTH)/third_party/starboard/rdk/shared/linux_key_mapping.h',
'<(DEPTH)/third_party/starboard/rdk/shared/linux_key_mapping.cc',
+ '<(DEPTH)/third_party/starboard/rdk/shared/content_entitlement_platform_service.h',
+ '<(DEPTH)/third_party/starboard/rdk/shared/content_entitlement_platform_service.cc',
+ '<(DEPTH)/third_party/starboard/rdk/shared/firebolt/firebolt.cc',
+ '<(DEPTH)/third_party/starboard/rdk/shared/firebolt/firebolt.h',
],
'conditions': [
['sb_api_version == 12', {
diff --git a/src/third_party/starboard/rdk/shared/system/system_get_extensions.cc b/src/third_party/starboard/rdk/shared/system/system_get_extensions.cc
index 9eaa625..1f35297 100644
--- a/src/third_party/starboard/rdk/shared/system/system_get_extensions.cc
+++ b/src/third_party/starboard/rdk/shared/system/system_get_extensions.cc
@@ -32,10 +32,13 @@
#include <cstring>
+#include "cobalt/extension/platform_service.h"
#include "cobalt/extension/configuration.h"
#include "cobalt/extension/crash_handler.h"
#include "starboard/common/string.h"
+#include "starboard/common/log.h"
#include "third_party/starboard/rdk/shared/configuration.h"
+#include "third_party/starboard/rdk/shared/content_entitlement_platform_service.h"
#if SB_IS(EVERGREEN_COMPATIBLE)
#include "starboard/elf_loader/evergreen_config.h"
#include "starboard/shared/starboard/crash_handler.h"
@@ -59,5 +62,8 @@
if (strcmp(name, kCobaltExtensionConfigurationName) == 0) {
return third_party::starboard::rdk::shared::GetConfigurationApi();
}
+ else if (strcmp(name, kCobaltExtensionPlatformServiceName) == 0) {
+ return third_party::starboard::rdk::shared::GetPlatformServiceApi();
+ }
return NULL;
}