blob: 215ef9f680546e9914e35ae8c8b53938bf56011b [file]
//
// 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/rdkservices.h"
#include <string>
#include <cstring>
#include <algorithm>
#include <websocket/JSONRPCLink.h>
#include <interfaces/json/JsonData_HDRProperties.h>
#include <interfaces/json/JsonData_PlayerProperties.h>
#include <interfaces/json/JsonData_DeviceIdentification.h>
#include <interfaces/json/JsonData_DeviceInfo.h>
#ifdef HAS_SECURITY_AGENT
#include <securityagent/securityagent.h>
#endif
#include "starboard/atomic.h"
#include "starboard/audio_sink.h"
#include "starboard/event.h"
#include "starboard/media.h"
#include "starboard/once.h"
#include "starboard/common/atomic.h"
#include "starboard/common/condition_variable.h"
#include "starboard/common/mutex.h"
#include "starboard/common/media.h"
#include "starboard/accessibility.h"
#include "starboard/common/file.h"
#include "starboard/shared/starboard/media/mime_supportability_cache.h"
#include "third_party/starboard/rdk/shared/accessibility_data.h"
#include "third_party/starboard/rdk/shared/log_override.h"
#include "third_party/starboard/rdk/shared/application_rdk.h"
#include "third_party/starboard/rdk/shared/player/player_internal.h"
MODULE_NAME_DECLARATION(BUILD_REFERENCE);
using namespace WPEFramework;
namespace third_party {
namespace starboard {
namespace rdk {
namespace shared {
namespace {
const uint32_t kDefaultTimeoutMs = 100;
const char kDisplayInfoCallsign[] = "DisplayInfo.1";
const char kPlayerInfoCallsign[] = "PlayerInfo.1";
const char kDeviceIdentificationCallsign[] = "DeviceIdentification.1";
const char kNetworkCallsign[] = "org.rdk.Network.1";
const char kTTSCallsign[] = "org.rdk.TextToSpeech.1";
const char kAuthServiceCallsign[] = "org.rdk.AuthService.1";
const char kDeviceInfoCallsign[] = "DeviceInfo.1";
const char kBluetoothCallsign[] = "org.rdk.Bluetooth.1";
const char kAuthServiceExperienceFile[] = "/opt/www/authService/experience.dat";
const uint32_t kPriviligedRequestErrorCode = -32604U;
class ServiceLink {
::starboard::scoped_ptr<JSONRPC::LinkType<Core::JSON::IElement>> link_;
std::string callsign_;
#ifdef HAS_SECURITY_AGENT
static Core::OptionalType<std::string> getToken() {
if (getenv("THUNDER_SECURITY_OFF") != nullptr)
return { };
const uint32_t kMaxBufferSize = 2 * 1024;
const std::string payload = "https://www.youtube.com";
Core::OptionalType<std::string> token;
std::vector<uint8_t> buffer;
buffer.resize(kMaxBufferSize);
for(int i = 0; i < 5; ++i) {
uint32_t inputLen = std::min(kMaxBufferSize, payload.length());
::memcpy (buffer.data(), payload.c_str(), inputLen);
int outputLen = GetToken(kMaxBufferSize, inputLen, buffer.data());
SB_DCHECK(outputLen != 0);
if (outputLen > 0) {
token = std::string(reinterpret_cast<const char*>(buffer.data()), outputLen);
break;
}
else if (outputLen < 0) {
uint32_t rc = -outputLen;
if (rc == Core::ERROR_TIMEDOUT && i < 5) {
SB_LOG(ERROR) << "Failed to get token, trying again. rc = " << rc << " ( " << Core::ErrorToString(rc) << " )";
continue;
}
SB_LOG(ERROR) << "Failed to get token, give up. rc = " << rc << " ( " << Core::ErrorToString(rc) << " )";
}
break;
}
return token;
}
#endif
static std::string buildQuery() {
std::string query;
#ifdef HAS_SECURITY_AGENT
static const auto token = getToken();
if (token.IsSet() && !token.Value().empty())
query = "token=" + token.Value();
#endif
return query;
}
public:
static bool enableEnvOverrides() {
static bool enable_env_overrides = ([]() {
std::string envValue;
if ((Core::SystemInfo::GetEnvironment("COBALT_ENABLE_OVERRIDES", envValue) == true) && (envValue.empty() == false)) {
return envValue.compare("1") == 0 || envValue.compare("true") == 0;
}
return false;
})();
return enable_env_overrides;
}
ServiceLink(const std::string callsign) : callsign_(callsign) {
}
template <typename PARAMETERS>
uint32_t Get(const uint32_t waitTime, const string& method, PARAMETERS& sendObject) {
if (enableEnvOverrides()) {
std::string envValue;
std::string envName = Core::JSONRPC::Message::Callsign(callsign_) + "_" + method;
envName.erase(std::remove(envName.begin(), envName.end(), '.'), envName.end());
if (Core::SystemInfo::GetEnvironment(envName, envValue) == true) {
return sendObject.FromString(envValue) ? Core::ERROR_NONE : Core::ERROR_GENERAL;
}
}
if (!EnsureLink())
return Core::ERROR_UNAVAILABLE;
return link_->template Get<PARAMETERS>(waitTime, method, sendObject);
}
template <typename PARAMETERS, typename HANDLER, typename REALOBJECT>
uint32_t Dispatch(const uint32_t waitTime, const string& method, const PARAMETERS& parameters, const HANDLER& callback, REALOBJECT* objectPtr) {
if (!EnsureLink())
return Core::ERROR_UNAVAILABLE;
return link_->template Dispatch<PARAMETERS, HANDLER, REALOBJECT>(waitTime, method, parameters, callback, objectPtr);
}
template <typename HANDLER, typename REALOBJECT>
uint32_t Dispatch(const uint32_t waitTime, const string& method, const HANDLER& callback, REALOBJECT* objectPtr) {
if (!EnsureLink())
return Core::ERROR_UNAVAILABLE;
return link_->template Dispatch<void, HANDLER, REALOBJECT>(waitTime, method, callback, objectPtr);
}
template <typename INBOUND, typename METHOD, typename REALOBJECT>
uint32_t Subscribe(const uint32_t waitTime, const string& eventName, const METHOD& method, REALOBJECT* objectPtr) {
if (!EnsureLink())
return enableEnvOverrides() ? Core::ERROR_NONE : Core::ERROR_UNAVAILABLE;
return link_->template Subscribe<INBOUND, METHOD, REALOBJECT>(waitTime, eventName, method, objectPtr);
}
void Unsubscribe(const uint32_t waitTime, const string& eventName) {
if (!link_)
return;
return link_->Unsubscribe(waitTime, eventName);
}
void Teardown() {
link_.reset();
}
bool EnsureLink() {
if (!link_) {
static const bool kHasThunderAccessEnv = (getenv("THUNDER_ACCESS") != nullptr);
if (kHasThunderAccessEnv)
link_.reset(new JSONRPC::LinkType<Core::JSON::IElement>(callsign_, nullptr, false, buildQuery()));
}
return link_;
}
};
struct VariableTimeout {
const uint32_t min_ms; // milliseconds
const uint64_t deadline; // ticks
VariableTimeout(uint32_t min, uint32_t max)
: min_ms(min), deadline(Core::Time::Now().Ticks() + max * Core::Time::TicksPerMillisecond) {
}
uint32_t value() const {
auto now = Core::Time::Now().Ticks();
return (now < deadline)
? std::max(static_cast<uint32_t>((deadline - now) / Core::Time::TicksPerMillisecond), min_ms)
: min_ms;
}
};
struct DeviceIdImpl {
DeviceIdImpl() {
JsonData::DeviceIdentification::DeviceidentificationData data;
uint32_t rc = ServiceLink(kDeviceIdentificationCallsign)
.Get(2000, "deviceidentification", data);
if (Core::ERROR_NONE == rc) {
chipset = data.Chipset.Value();
firmware_version = data.Firmwareversion.Value();
std::replace(chipset.begin(), chipset.end(), ' ', '-');
}
if (Core::ERROR_NONE != rc) {
#if defined(SB_PLATFORM_CHIPSET_MODEL_NUMBER_STRING)
chipset = SB_PLATFORM_CHIPSET_MODEL_NUMBER_STRING;
#endif
#if defined(SB_PLATFORM_FIRMWARE_VERSION_STRING)
firmware_version = SB_PLATFORM_FIRMWARE_VERSION_STRING;
#endif
}
}
std::string chipset;
std::string firmware_version;
};
SB_ONCE_INITIALIZE_FUNCTION(DeviceIdImpl, GetDeviceIdImpl);
struct TextToSpeechImpl {
private:
::starboard::atomic_bool is_enabled_ { false };
::starboard::atomic_bool needs_refresh_ { true };
::starboard::atomic_bool did_subscribe_ { false };
int64_t speech_id_ { -1 };
int32_t speech_request_num_ { 0 };
ServiceLink tts_link_ { kTTSCallsign };
::starboard::Mutex mutex_;
::starboard::ConditionVariable condition_ { mutex_ };
std::string client_id_;
struct IsTTSEnabledInfo : public Core::JSON::Container {
IsTTSEnabledInfo()
: Core::JSON::Container() {
Add(_T("isenabled"), &IsEnabled);
}
IsTTSEnabledInfo(const IsTTSEnabledInfo&) = delete;
IsTTSEnabledInfo& operator=(const IsTTSEnabledInfo&) = delete;
Core::JSON::Boolean IsEnabled;
};
struct SpeakResult : public Core::JSON::Container {
SpeakResult()
: Core::JSON::Container()
, SpeechId(-1) {
Add(_T("speechid"), &SpeechId);
}
SpeakResult(const SpeakResult&) = delete;
SpeakResult& operator=(const SpeakResult&) = delete;
Core::JSON::DecSInt64 SpeechId;
};
struct StateInfo : public Core::JSON::Container {
StateInfo()
: Core::JSON::Container()
, State(false) {
Add(_T("state"), &State);
}
StateInfo(const StateInfo& other)
: Core::JSON::Container()
, State(other.State) {
Add(_T("state"), &State);
}
StateInfo& operator=(const StateInfo&) = delete;
Core::JSON::Boolean State;
};
void OnCancelResult(const Core::JSON::String&, const Core::JSONRPC::Error*) {
}
void OnStateChanged(const StateInfo& info) {
is_enabled_.store( info.State.Value() );
}
void OnSpeakResult(const SpeakResult& result, const Core::JSONRPC::Error* err) {
::starboard::ScopedLock lock(mutex_);
if (err) {
SB_LOG(ERROR)
<< "TTS speak request failed. Error code: "
<< err->Code.Value()
<< " message: "
<< err->Text.Value();
speech_id_ = -1;
}
else {
speech_id_ = result.SpeechId;
}
--speech_request_num_;
condition_.Broadcast();
}
public:
TextToSpeechImpl() = default;
void Speak(const std::string &text) {
Refresh();
if (!is_enabled_.load())
return;
JsonObject params;
params.Set(_T("text"), text);
params.Set(_T("callsign"), client_id_ );
uint64_t rc = tts_link_.Dispatch(kDefaultTimeoutMs, "speak", params, &TextToSpeechImpl::OnSpeakResult, this);
if (Core::ERROR_NONE == rc) {
::starboard::ScopedLock lock(mutex_);
++speech_request_num_;
}
}
void Cancel() {
Refresh();
if (!is_enabled_.load())
return;
int64_t speechId = -1;
{
::starboard::ScopedLock lock(mutex_);
if (speech_request_num_ != 0) {
if (!condition_.WaitTimed(kSbTimeMillisecond) || speech_request_num_ != 0)
return;
}
speechId = speech_id_;
}
if (speechId < 0)
return;
JsonObject params;
params.Set(_T("speechid"), speechId);
tts_link_.Dispatch(kDefaultTimeoutMs, "cancel", params, &TextToSpeechImpl::OnCancelResult, this);
}
bool IsEnabled() {
Refresh();
return is_enabled_.load();
}
void Refresh() {
if (!needs_refresh_.load())
return;
uint32_t rc;
if (!did_subscribe_.load()) {
bool old_val = did_subscribe_.exchange(true);
if (old_val == false) {
rc = tts_link_.Subscribe<StateInfo>(kDefaultTimeoutMs, "onttsstatechanged", &TextToSpeechImpl::OnStateChanged, this);
if (Core::ERROR_UNAVAILABLE == rc || kPriviligedRequestErrorCode == rc) {
needs_refresh_.store(false);
SB_LOG(ERROR) << "Failed to subscribe to '" << kTTSCallsign
<< ".onstatechanged' event, rc=" << rc
<< " ( " << Core::ErrorToString(rc) << " )";
return;
}
if (Core::ERROR_NONE != rc && Core::ERROR_DUPLICATE_KEY != rc) {
did_subscribe_.store(false);
SB_LOG(ERROR) << "Failed to subscribe to '" << kTTSCallsign
<< ".onstatechanged' event, rc=" << rc
<< " ( " << Core::ErrorToString(rc) << " )."
<< " Going to try again next time.";
return;
}
}
}
IsTTSEnabledInfo info;
rc = tts_link_.Get(kDefaultTimeoutMs, "isttsenabled", info);
if (Core::ERROR_NONE == rc) {
is_enabled_.store( info.IsEnabled.Value() );
}
if (Core::SystemInfo::GetEnvironment(_T("CLIENT_IDENTIFIER"), client_id_) == true) {
std::string::size_type pos = client_id_.find(',');
if (pos != std::string::npos)
client_id_.erase(pos, std::string::npos);
} else {
client_id_ = "Cobalt";
}
needs_refresh_.store(false);
}
void Teardown() {
if (did_subscribe_.load()) {
tts_link_.Unsubscribe(kDefaultTimeoutMs, "onttsstatechanged");
did_subscribe_.store(false);
}
tts_link_.Teardown();
needs_refresh_.store(true);
is_enabled_.store(false);
}
};
SB_ONCE_INITIALIZE_FUNCTION(TextToSpeechImpl, GetTextToSpeech);
struct AccessibilityImpl {
private:
::starboard::Mutex mutex_;
SbAccessibilityDisplaySettings display_settings_ { };
SbAccessibilityCaptionSettings caption_settings_ { };
public:
AccessibilityImpl() {
memset(&display_settings_, 0, sizeof(display_settings_));
memset(&caption_settings_, 0, sizeof(caption_settings_));
if (ServiceLink::enableEnvOverrides()) {
std::string envValue;
if (Core::SystemInfo::GetEnvironment("AccessibilitySettings_json", envValue) == true) {
SetSettings(envValue);
std::string test;
bool r = GetSettings(test);
SB_LOG(INFO) << "Initialized from 'AccessibilitySettings_json',"
<< " env variable json: '" << envValue << "',"
<< " conversion result: " << r << ","
<< " accessibility setting json: '" << test << "'";
}
}
}
void SetSettings(const std::string& json) {
SB_LOG(INFO) << "Updating accessibility settings: " << json;
JsonData::Accessibility::AccessibilityData settings;
Core::OptionalType<Core::JSON::Error> error;
if ( !settings.FromString(json, error) ) {
SB_LOG(ERROR) << "Failed to parse accessibility settings, error: "
<< (error.IsSet() ? Core::JSON::ErrorDisplayMessage(error.Value()): "Unknown");
return;
}
::starboard::ScopedLock lock(mutex_);
memset(&display_settings_, 0, sizeof(display_settings_));
memset(&caption_settings_, 0, sizeof(caption_settings_));
const auto& cc = settings.ClosedCaptions;
caption_settings_.supports_is_enabled = true;
caption_settings_.supports_set_enabled = false;
caption_settings_.is_enabled = cc.IsEnabled.Value();
if (cc.BackgroundColor.IsSet()) {
caption_settings_.background_color = cc.BackgroundColor.Value();
caption_settings_.background_color_state = kSbAccessibilityCaptionStateSet;
}
if (cc.BackgroundOpacity.IsSet()) {
caption_settings_.background_opacity = cc.BackgroundOpacity.Value();
caption_settings_.background_opacity_state = kSbAccessibilityCaptionStateSet;
}
if (cc.CharacterEdgeStyle.IsSet()) {
caption_settings_.character_edge_style = cc.CharacterEdgeStyle.Value();
caption_settings_.character_edge_style_state = kSbAccessibilityCaptionStateSet;
}
if (cc.FontColor.IsSet()) {
caption_settings_.font_color = cc.FontColor.Value();
caption_settings_.font_color_state = kSbAccessibilityCaptionStateSet;
}
if (cc.FontFamily.IsSet()) {
caption_settings_.font_family = cc.FontFamily.Value();
caption_settings_.font_family_state = kSbAccessibilityCaptionStateSet;
}
if (cc.FontOpacity.IsSet()) {
caption_settings_.font_opacity = cc.FontOpacity.Value();
caption_settings_.font_opacity_state = kSbAccessibilityCaptionStateSet;
}
if (cc.FontSize.IsSet()) {
caption_settings_.font_size = cc.FontSize.Value();
caption_settings_.font_size_state = kSbAccessibilityCaptionStateSet;
}
if (cc.WindowColor.IsSet()) {
caption_settings_.window_color = cc.WindowColor.Value();
caption_settings_.window_color_state = kSbAccessibilityCaptionStateSet;
}
if (cc.WindowOpacity.IsSet()) {
caption_settings_.window_opacity = cc.WindowOpacity.Value();
caption_settings_.window_opacity_state = kSbAccessibilityCaptionStateSet;
}
if (settings.TextDisplay.IsHighContrastTextEnabled.IsSet()) {
display_settings_.has_high_contrast_text_setting = true;
display_settings_.is_high_contrast_text_enabled =
settings.TextDisplay.IsHighContrastTextEnabled.Value();
}
}
bool GetSettings(std::string& out_json) {
JsonData::Accessibility::AccessibilityData settings;
{
::starboard::ScopedLock lock(mutex_);
if (caption_settings_.supports_is_enabled) {
auto& cc = settings.ClosedCaptions;
cc.IsEnabled = caption_settings_.is_enabled;
if (caption_settings_.background_color_state)
cc.BackgroundColor = caption_settings_.background_color;
if (caption_settings_.background_opacity_state)
cc.BackgroundOpacity = caption_settings_.background_opacity;
if (caption_settings_.character_edge_style_state)
cc.CharacterEdgeStyle = caption_settings_.character_edge_style;
if (caption_settings_.font_color_state)
cc.FontColor = caption_settings_.font_color;
if (caption_settings_.font_family_state)
cc.FontFamily = caption_settings_.font_family;
if (caption_settings_.font_opacity_state)
cc.FontOpacity = caption_settings_.font_opacity;
if (caption_settings_.font_size_state)
cc.FontSize = caption_settings_.font_size;
if (caption_settings_.window_color_state)
cc.WindowColor = caption_settings_.window_color;
if (caption_settings_.window_opacity_state)
cc.WindowOpacity = caption_settings_.window_opacity;
}
if (display_settings_.has_high_contrast_text_setting)
settings.TextDisplay.IsHighContrastTextEnabled = display_settings_.is_high_contrast_text_enabled;
}
return settings.ToString(out_json);
}
bool GetCaptionSettings(SbAccessibilityCaptionSettings* out) const {
if (out) {
::starboard::ScopedLock lock(mutex_);
memcpy(out, &caption_settings_, sizeof(caption_settings_));
return true;
}
return false;
}
bool GetDisplaySettings(SbAccessibilityDisplaySettings* out) const {
if (out) {
::starboard::ScopedLock lock(mutex_);
memcpy(out, &display_settings_, sizeof(display_settings_));
return true;
}
return false;
}
};
SB_ONCE_INITIALIZE_FUNCTION(AccessibilityImpl, GetAccessibility);
struct SystemPropertiesImpl {
struct SystemPropertiesData : public Core::JSON::Container {
SystemPropertiesData()
: Core::JSON::Container() {
Add(_T("modelname"), &ModelName);
Add(_T("brandname"), &BrandName);
Add(_T("modelyear"), &ModelYear);
Add(_T("chipsetmodelnumber"), &ChipsetModelNumber);
Add(_T("firmwareversion"), &FirmwareVersion);
Add(_T("integratorname"), &IntegratorName);
Add(_T("friendlyname"), &FriendlyName);
Add(_T("devicetype"), &DeviceType);
}
SystemPropertiesData(const SystemPropertiesData&) = delete;
SystemPropertiesData& operator=(const SystemPropertiesData&) = delete;
Core::JSON::String ModelName;
Core::JSON::String BrandName;
Core::JSON::String ModelYear;
Core::JSON::String ChipsetModelNumber;
Core::JSON::String FirmwareVersion;
Core::JSON::String IntegratorName;
Core::JSON::String FriendlyName;
Core::JSON::String DeviceType;
};
void SetSettings(const std::string& json) {
::starboard::ScopedLock lock(mutex_);
Core::OptionalType<Core::JSON::Error> error;
if ( !props_.FromString(json, error) ) {
props_.Clear();
SB_LOG(ERROR) << "Failed to parse systemproperties settings, error: "
<< (error.IsSet() ? Core::JSON::ErrorDisplayMessage(error.Value()): "Unknown");
return;
}
}
bool GetSettings(std::string& out_json) const {
::starboard::ScopedLock lock(mutex_);
return props_.ToString(out_json);
}
bool GetModelName(std::string &out) const {
::starboard::ScopedLock lock(mutex_);
if (props_.ModelName.IsSet() && !props_.ModelName.Value().empty()) {
out = props_.ModelName.Value();
return true;
}
return false;
}
bool GetBrandName(std::string &out) const {
::starboard::ScopedLock lock(mutex_);
if (props_.BrandName.IsSet() && !props_.BrandName.Value().empty()) {
out = props_.BrandName.Value();
return true;
}
return false;
}
bool GetModelYear(std::string &out) const {
::starboard::ScopedLock lock(mutex_);
if (props_.ModelYear.IsSet() && !props_.ModelYear.Value().empty()) {
out = props_.ModelYear.Value();
return true;
}
return false;
}
bool GetChipset(std::string &out) const {
::starboard::ScopedLock lock(mutex_);
if (props_.ChipsetModelNumber.IsSet() && !props_.ChipsetModelNumber.Value().empty()) {
out = props_.ChipsetModelNumber.Value();
return true;
}
return false;
}
bool GetFirmwareVersion(std::string &out) const {
::starboard::ScopedLock lock(mutex_);
if (props_.FirmwareVersion.IsSet() && !props_.FirmwareVersion.Value().empty()) {
out = props_.FirmwareVersion.Value();
return true;
}
return false;
}
bool GetIntegratorName(std::string &out) const {
::starboard::ScopedLock lock(mutex_);
if (props_.IntegratorName.IsSet() && !props_.IntegratorName.Value().empty()) {
out = props_.IntegratorName.Value();
return true;
}
return false;
}
bool GetFriendlyName(std::string &out) const {
::starboard::ScopedLock lock(mutex_);
if (props_.FriendlyName.IsSet() && !props_.FriendlyName.Value().empty()) {
out = props_.FriendlyName.Value();
return true;
}
return false;
}
bool GetDeviceType(std::string &out) const {
::starboard::ScopedLock lock(mutex_);
if (props_.DeviceType.IsSet() && !props_.DeviceType.Value().empty()) {
out = props_.DeviceType.Value();
return true;
}
return false;
}
private:
::starboard::Mutex mutex_;
SystemPropertiesData props_;
};
SB_ONCE_INITIALIZE_FUNCTION(SystemPropertiesImpl, GetSystemProperties);
struct AdvertisingIdImpl {
struct AdvertisingData : public Core::JSON::Container {
AdvertisingData()
: Core::JSON::Container() {
Add(_T("ifa"), &Ifa);
Add(_T("ifa_type"), &IfaType);
Add(_T("lmt"), &Lmt);
}
AdvertisingData(const AdvertisingData&) = delete;
AdvertisingData& operator=(const AdvertisingData&) = delete;
Core::JSON::String Ifa;
Core::JSON::String IfaType;
Core::JSON::String Lmt;
};
void SetSettings(const std::string& json) {
::starboard::ScopedLock lock(mutex_);
Core::OptionalType<Core::JSON::Error> error;
if ( !props_.FromString(json, error) ) {
props_.Clear();
SB_LOG(ERROR) << "Failed to parse advertisingid settings, error: "
<< (error.IsSet() ? Core::JSON::ErrorDisplayMessage(error.Value()): "Unknown");
return;
}
}
bool GetSettings(std::string& out_json) const {
::starboard::ScopedLock lock(mutex_);
return props_.ToString(out_json);
}
bool GetIfa(std::string &out) const {
::starboard::ScopedLock lock(mutex_);
if (props_.Ifa.IsSet() && !props_.Ifa.Value().empty()) {
out = props_.Ifa.Value();
return true;
}
return false;
}
bool GetIfaType(std::string &out) const {
::starboard::ScopedLock lock(mutex_);
if (props_.IfaType.IsSet() && !props_.IfaType.Value().empty()) {
out = props_.IfaType.Value();
return true;
}
return false;
}
bool GetLmtAdTracking(std::string &out) const {
::starboard::ScopedLock lock(mutex_);
if (props_.Lmt.IsSet() && !props_.Lmt.Value().empty()) {
out = props_.Lmt.Value();
return true;
}
return false;
}
private:
::starboard::Mutex mutex_;
AdvertisingData props_;
};
SB_ONCE_INITIALIZE_FUNCTION(AdvertisingIdImpl, GetAdvertisingProperties);
struct AuthServiceImpl {
bool IsAvailable() const {
return is_available_;
}
bool GetExperience(std::string &out) {
if (!IsAvailable())
return false;
::starboard::ScopedLock lock(mutex_);
if (!experience_.empty()) {
out = experience_;
return true;
}
JsonObject data;
uint32_t rc = ServiceLink(kAuthServiceCallsign)
.Get(kDefaultTimeoutMs, "getExperience", data);
if (Core::ERROR_NONE == rc && data.Get("success").Boolean()) {
experience_ = data.Get("experience").Value();
out = experience_;
return true;
}
// Try to read directly from file
::starboard::ScopedFile file(kAuthServiceExperienceFile, kSbFileOpenOnly | kSbFileRead);
if ( file.IsValid() ) {
const int kBufferSize = 128;
char buffer[kBufferSize];
int bytes_read = file.ReadAll(buffer, kBufferSize);
bytes_read = std::min(bytes_read, kBufferSize - 1);
buffer[bytes_read] = '\0';
experience_.assign(buffer);
out = experience_;
return true;
}
is_available_ = false;
return false;
}
private:
::starboard::Mutex mutex_;
bool is_available_ { true };
std::string experience_;
};
SB_ONCE_INITIALIZE_FUNCTION(AuthServiceImpl, GetAuthService);
struct DisplayInfoImpl {
ResolutionInfo GetResolution() {
Refresh();
return resolution_info_;
}
uint32_t GetHDRCaps() {
Refresh();
return hdr_caps_;
}
float GetDiagonalSizeInInches() {
Refresh();
return diagonal_size_in_inches_;
}
void Teardown() {
if (did_subscribe_.load()) {
display_info_.Unsubscribe(kDefaultTimeoutMs, "updated");
did_subscribe_.store(false);
}
display_info_.Teardown();
needs_refresh_.store(true);
}
private:
void Refresh();
void OnUpdated(const Core::JSON::String&);
void ForceNeedsRefresh() { needs_refresh_.store(true); }
ServiceLink display_info_ { kDisplayInfoCallsign };
ResolutionInfo resolution_info_ { };
uint32_t hdr_caps_ { DisplayInfo::kHdrNone };
float diagonal_size_in_inches_ { 0.f };
::starboard::atomic_bool needs_refresh_ { true };
::starboard::atomic_bool did_subscribe_ { false };
};
SB_ONCE_INITIALIZE_FUNCTION(DisplayInfoImpl, GetDisplayInfo);
void DisplayInfoImpl::Refresh() {
if (!needs_refresh_.load())
return;
VariableTimeout timeout(kDefaultTimeoutMs, 1000);
uint32_t rc;
if (!did_subscribe_.load()) {
bool old_val = did_subscribe_.exchange(true);
if (old_val == false) {
rc = display_info_.Subscribe<Core::JSON::String>(timeout.value(), "updated", &DisplayInfoImpl::OnUpdated, this);
if (Core::ERROR_UNAVAILABLE == rc || kPriviligedRequestErrorCode == rc) {
needs_refresh_.store(false);
SB_LOG(ERROR) << "Failed to subscribe to '" << kDisplayInfoCallsign
<< ".updated' event, rc=" << rc
<< " ( " << Core::ErrorToString(rc) << " )";
return;
}
if (Core::ERROR_NONE != rc && Core::ERROR_DUPLICATE_KEY != rc) {
did_subscribe_.store(false);
SB_LOG(ERROR) << "Failed to subscribe to '" << kDisplayInfoCallsign
<< ".updated' event, rc=" << rc
<< " ( " << Core::ErrorToString(rc) << " )."
<< " Going to try again next time.";
return;
}
}
}
bool needs_refresh = false;
Core::JSON::String resolution;
rc = ServiceLink(kPlayerInfoCallsign).Get(timeout.value(), "resolution", resolution);
if (Core::ERROR_NONE == rc && resolution.IsSet()) {
if (resolution.Value().find("Resolution2160") != std::string::npos) {
resolution_info_ = ResolutionInfo { 3840 , 2160 };
} else {
resolution_info_ = ResolutionInfo { 1920 , 1080 };
}
} else {
needs_refresh |= (Core::ERROR_ASYNC_FAILED == rc || Core::ERROR_TIMEDOUT == rc);
resolution_info_ = ResolutionInfo { 1920 , 1080 };
SB_LOG(ERROR) << "Failed to get 'resolution', rc=" << rc << " ( " << Core::ErrorToString(rc) << " )";
}
Core::JSON::DecUInt16 widthincentimeters, heightincentimeters;
rc = display_info_.Get(timeout.value(), "widthincentimeters", widthincentimeters);
if (Core::ERROR_NONE != rc) {
needs_refresh |= (Core::ERROR_ASYNC_FAILED == rc);
widthincentimeters.Clear();
SB_LOG(ERROR) << "Failed to get 'DisplayInfo.widthincentimeters', rc=" << rc << " ( " << Core::ErrorToString(rc) << " )";
}
rc = display_info_.Get(timeout.value(), "heightincentimeters", heightincentimeters);
if (Core::ERROR_NONE != rc) {
needs_refresh |= (Core::ERROR_ASYNC_FAILED == rc);
heightincentimeters.Clear();
SB_LOG(ERROR) << "Failed to get 'DisplayInfo.heightincentimeters', rc=" << rc << " ( " << Core::ErrorToString(rc) << " )";
}
if (widthincentimeters && heightincentimeters) {
diagonal_size_in_inches_ = sqrtf(powf(widthincentimeters, 2) + powf(heightincentimeters, 2)) / 2.54f;
} else {
diagonal_size_in_inches_ = 0.f;
}
auto detectHdrCaps = [&](const char* method)
{
using HdrTypes = Core::JSON::ArrayType<Core::JSON::EnumType<Exchange::IHDRProperties::HDRType>>;
HdrTypes types;
uint32_t rc = display_info_.Get(timeout.value(), method, types);
if (Core::ERROR_NONE != rc) {
needs_refresh |= (Core::ERROR_ASYNC_FAILED == rc || Core::ERROR_TIMEDOUT == rc);
SB_LOG(ERROR) << "Failed to get '" << method << "', rc=" << rc << " ( " << Core::ErrorToString(rc) << " )";
return 0u;
}
uint32_t result = 0u;
auto index(types.Elements());
while (index.Next()) {
switch(index.Current()) {
case Exchange::IHDRProperties::HDR_10:
result |= DisplayInfo::kHdr10;
break;
case Exchange::IHDRProperties::HDR_10PLUS:
result |= DisplayInfo::kHdr10Plus;
break;
case Exchange::IHDRProperties::HDR_HLG:
result |= DisplayInfo::kHdrHlg;
break;
case Exchange::IHDRProperties::HDR_DOLBYVISION:
result |= DisplayInfo::kHdrDolbyVision;
break;
case Exchange::IHDRProperties::HDR_TECHNICOLOR:
result |= DisplayInfo::kHdrTechnicolor;
break;
default:
break;
}
}
return result;
};
uint32_t tv_caps = detectHdrCaps("tvcapabilities");
uint32_t stb_caps = detectHdrCaps("stbcapabilities");
hdr_caps_ = tv_caps & stb_caps;
needs_refresh_.store(false);
if (needs_refresh) {
SbEventSchedule([](void* data) {
using ::starboard::shared::starboard::media::MimeSupportabilityCache;
MimeSupportabilityCache::GetInstance()->ClearCachedMimeSupportabilities();
GetDisplayInfo()->ForceNeedsRefresh();
}, nullptr, kSbTimeSecond);
}
SB_LOG(INFO) << "Display info updated, resolution: "
<< resolution_info_.Width << 'x' << resolution_info_.Height
<< ", hdr caps: 0x" << std::hex << hdr_caps_
<< " (tvcaps: 0x"<< std::hex << tv_caps
<< ", stbcaps: 0x" << std::hex << stb_caps << ")"
<< ", diagonal size in inches: " << std::dec << diagonal_size_in_inches_;
}
void DisplayInfoImpl::OnUpdated(const Core::JSON::String&) {
if (needs_refresh_.load() == false) {
needs_refresh_.store(true);
SbEventSchedule([](void* data) {
using ::starboard::shared::starboard::media::MimeSupportabilityCache;
// Clear mime cache until display info is updated
MimeSupportabilityCache::GetInstance()->ClearCachedMimeSupportabilities();
Application::Get()->DisplayInfoChanged();
}, nullptr, 0);
}
}
struct NetworkInfoImpl {
private:
ServiceLink network_link_ { kNetworkCallsign };
::starboard::atomic_bool needs_refresh_ { true };
::starboard::atomic_bool did_subscribe_ { false };
::starboard::atomic_bool is_connected_ { false };
::starboard::atomic_bool is_connection_type_wireless_ { false };
::starboard::Mutex mutex_;
SbEventId event_id_ { kSbEventIdInvalid };
struct InterfaceInfo : public Core::JSON::Container {
InterfaceInfo()
: Core::JSON::Container() {
Init();
}
InterfaceInfo(const InterfaceInfo& other)
: Core::JSON::Container()
, InterfaceName(other.InterfaceName)
, IsConnected(other.IsConnected) {
Init();
}
InterfaceInfo& operator=(const InterfaceInfo& rhs) {
InterfaceName = rhs.InterfaceName;
IsConnected = rhs.IsConnected;
return *this;
}
Core::JSON::String InterfaceName;
Core::JSON::Boolean IsConnected;
private:
void Init() {
Add(_T("interface"), &InterfaceName);
Add(_T("connected"), &IsConnected);
}
};
struct InterfacesInfo : public Core::JSON::Container {
InterfacesInfo(const InterfacesInfo&) = delete;
InterfacesInfo& operator=(const InterfacesInfo&) = delete;
InterfacesInfo()
: Core::JSON::Container() {
Add(_T("interfaces"), &Interfaces);
}
Core::JSON::ArrayType<InterfaceInfo> Interfaces;
};
void ScheduleRefresh(SbTime timeout) {
::starboard::ScopedLock lock(mutex_);
if (event_id_ == kSbEventIdInvalid) {
needs_refresh_.store(true);
event_id_ = SbEventSchedule([](void* data) {
auto& self = *static_cast<NetworkInfoImpl*>(data);
self.mutex_.Acquire();
self.event_id_ = kSbEventIdInvalid;
self.mutex_.Release();
self.Refresh();
}, this, timeout);
}
}
void Refresh() {
if (!needs_refresh_.load())
return;
uint32_t rc;
if (!did_subscribe_.load()) {
bool old_val = did_subscribe_.exchange(true);
if (old_val == false) {
rc = network_link_.Subscribe<Core::JSON::String>(
kDefaultTimeoutMs, "onConnectionStatusChanged",
&NetworkInfoImpl::OnConnectionStatusChanged, this);
if (Core::ERROR_NONE != rc && Core::ERROR_DUPLICATE_KEY != rc) {
SB_LOG(ERROR) << "Failed to subscribe to '" << kNetworkCallsign
<< ".onConnectionStatusChanged' event, rc = " << rc
<< " ( " << Core::ErrorToString(rc) << " )";
did_subscribe_.store(false);
}
}
}
InterfacesInfo info;
rc = network_link_.Get(kDefaultTimeoutMs, "getInterfaces", info);
if (Core::ERROR_UNAVAILABLE == rc || kPriviligedRequestErrorCode == rc) {
SB_LOG(ERROR) << "'" << kNetworkCallsign << ".getInterfaces' failed, rc = " << rc
<< " ( " << Core::ErrorToString(rc) << " )";
needs_refresh_.store(false);
return;
}
else if (Core::ERROR_NONE != rc) {
SB_LOG(ERROR) << "'" << kNetworkCallsign << ".getInterfaces' failed, rc = " << rc
<< " ( " << Core::ErrorToString(rc) << " ). Trying again in 5 seconds.";
ScheduleRefresh(5 * kSbTimeSecond);
}
else {
needs_refresh_.store(false);
bool has_connected_interface = false;
auto index(info.Interfaces.Elements());
while (index.Next()) {
const auto& it = index.Current();
if (it.IsConnected) {
SB_LOG(INFO) << "Found connected interface: " << it.InterfaceName.Value();
has_connected_interface = true;
break;
}
}
if (!has_connected_interface) {
SB_LOG(INFO) << "All interfaces are disconnected...";
}
if (is_connected_.load() != has_connected_interface) {
is_connected_.store(has_connected_interface);
if (has_connected_interface)
Application::Get()->InjectOsNetworkConnectedEvent();
else
Application::Get()->InjectOsNetworkDisconnectedEvent();
}
}
InterfaceInfo default_interface;
rc = network_link_.Get(kDefaultTimeoutMs, "getDefaultInterface", default_interface);
if (Core::ERROR_NONE == rc) {
std::string connection_type = default_interface.InterfaceName.Value();
SB_LOG(INFO) << "Default connection type: " << connection_type;
is_connection_type_wireless_.store(0 == connection_type.compare("WIFI"));
}
else {
SB_LOG(INFO) << "Failed to get default interface, rc = " << rc
<< " ( " << Core::ErrorToString(rc) << " )";
}
}
void OnConnectionStatusChanged(const Core::JSON::String&) {
ScheduleRefresh(100 * kSbTimeMillisecond);
}
public:
bool IsDisconnected() {
Refresh();
return !is_connected_.load();
}
bool IsConnectionTypeWireless() {
Refresh();
return is_connection_type_wireless_.load();
}
void Teardown() {
if (did_subscribe_.load()) {
network_link_.Unsubscribe(kDefaultTimeoutMs, "onConnectionStatusChanged");
did_subscribe_.store(false);
}
network_link_.Teardown();
needs_refresh_.store(true);
::starboard::ScopedLock lock(mutex_);
if (event_id_ != kSbEventIdInvalid) {
SbEventCancel(event_id_);
event_id_ = kSbEventIdInvalid;
}
}
};
SB_ONCE_INITIALIZE_FUNCTION(NetworkInfoImpl, GetNetworkInfo);
struct DeviceInfoImpl {
bool GetAudioConfiguration(int index, SbMediaAudioConfiguration* out_audio_configuration);
void Teardown() {
if (did_subscribe_.load()) {
bluetooth_.Unsubscribe(kDefaultTimeoutMs, "onStatusChanged");
did_subscribe_.store(false);
}
device_info_.Teardown();
bluetooth_.Teardown();
needs_refresh_.store(true);
}
bool GetBrandName(std::string& out);
private:
struct DeviceDetailsData : public Core::JSON::Container {
DeviceDetailsData()
: Core::JSON::Container() {
Init();
}
DeviceDetailsData(const DeviceDetailsData& other)
: Core::JSON::Container()
, Name(other.Name)
, Devicetype(other.Devicetype) {
Init();
}
DeviceDetailsData& operator=(const DeviceDetailsData& rhs) {
Name = rhs.Name;
Devicetype = rhs.Devicetype;
return *this;
}
Core::JSON::String Name;
Core::JSON::String Devicetype;
private:
void Init() {
Add(_T("name"), &Name);
Add(_T("deviceType"), &Devicetype);
}
};
struct ConnectedDevicesData : public Core::JSON::Container {
ConnectedDevicesData()
: Core::JSON::Container() {
Add(_T("connectedDevices"), &Connecteddevices);
}
ConnectedDevicesData(const ConnectedDevicesData&) = delete;
ConnectedDevicesData& operator=(const ConnectedDevicesData&) = delete;
Core::JSON::ArrayType<DeviceDetailsData> Connecteddevices;
};
struct StatusChangedData : public Core::JSON::Container {
StatusChangedData()
: Core::JSON::Container() {
Init();
}
StatusChangedData(const StatusChangedData& other)
: Core::JSON::Container()
, Name(other.Name)
, Newstatus(other.Newstatus)
, Devicetype(other.Devicetype)
, Connected(other.Connected) {
Init();
}
StatusChangedData& operator=(const StatusChangedData& rhs) {
Name = rhs.Name;
Devicetype = rhs.Devicetype;
Newstatus = rhs.Newstatus;
Connected = rhs.Connected;
return *this;
}
Core::JSON::String Name;
Core::JSON::String Newstatus;
Core::JSON::String Devicetype;
Core::JSON::Boolean Connected;
private:
void Init() {
Add(_T("name"), &Name);
Add(_T("newStatus"), &Newstatus);
Add(_T("deviceType"), &Devicetype);
Add(_T("connected"), &Connected);
}
};
void OnBluetoothStatusChanged(const StatusChangedData&);
void Refresh();
void ForceNeedsRefresh() { needs_refresh_.store(true); }
void InitAudioConfigurationForAudioPort(const std::string& port_name, SbMediaAudioConfiguration* out);
static bool IsAudioOutputDeviceType(const std::string& type) {
return (strcasestr(type.c_str(), "audio") ||
strcasestr(type.c_str(), "headset") ||
strcasestr(type.c_str(), "headphones") ||
strcasestr(type.c_str(), "loudspeaker") ||
strcasestr(type.c_str(), "handsfree"));
};
ServiceLink device_info_ { kDeviceInfoCallsign };
ServiceLink bluetooth_ { kBluetoothCallsign };
::starboard::atomic_bool did_subscribe_ { false };
::starboard::atomic_bool needs_refresh_ { true };
::starboard::atomic_bool has_bluetooth_audio_connected_ { false };
::starboard::Mutex mutex_;
std::vector<SbMediaAudioConfiguration> audio_configurations_;
Core::OptionalType<std::string> brand_name_;
static constexpr SbMediaAudioConnector kAudioConnectorUnknown = static_cast<SbMediaAudioConnector>(0);
};
SB_ONCE_INITIALIZE_FUNCTION(DeviceInfoImpl, GetDeviceInfo);
void DeviceInfoImpl::InitAudioConfigurationForAudioPort(const std::string& port_name, SbMediaAudioConfiguration* audio_configuration)
{
if (!audio_configuration)
return;
const auto& connectorType = [](const std::string& name) {
if (strncasecmp(name.c_str(), "hdmi", 4) == 0)
return kSbMediaAudioConnectorHdmi;
else if (strncasecmp(name.c_str(), "spdif", 5) == 0)
return kSbMediaAudioConnectorSpdif;
else if (strncasecmp(name.c_str(), "bluetooth", 9) == 0)
return kSbMediaAudioConnectorBluetooth;
#if SB_API_VERSION >= 15
else if (strncasecmp(name.c_str(), "speaker", 7) == 0)
return kSbMediaAudioConnectorBuiltIn;
#endif
return kAudioConnectorUnknown;
};
memset(audio_configuration, 0, sizeof(SbMediaAudioConfiguration));
audio_configuration->connector = connectorType(port_name);
audio_configuration->coding_type = kSbMediaAudioCodingTypePcm;
audio_configuration->number_of_channels = SbAudioSinkGetMaxChannels();
return;
}
void DeviceInfoImpl::OnBluetoothStatusChanged(const StatusChangedData& data) {
const char kConnectionChange[] = "CONNECTION_CHANGE";
SB_LOG(INFO) << "Bluetooth status changed, new status: " << data.Newstatus.Value();
if (data.Newstatus.Value().compare(0, sizeof(kConnectionChange), kConnectionChange) != 0)
return;
if (!IsAudioOutputDeviceType(data.Devicetype.Value()))
return;
SB_LOG(INFO) << "Audio device change."
<< " name: '" << data.Name.Value() << "',"
<< " type: '" << data.Devicetype.Value() << "',"
<< " connected: " << (data.Connected.Value() ? "yes" : "no");
const auto& hasBluetoothConnector = [&]() -> bool {
::starboard::ScopedLock lock(mutex_);
return std::find_if(audio_configurations_.begin(), audio_configurations_.end(), [](const SbMediaAudioConfiguration& cfg) {
return cfg.connector == kSbMediaAudioConnectorBluetooth;
}) != audio_configurations_.end();
};
has_bluetooth_audio_connected_.store(data.Connected.Value());
ForceNeedsRefresh();
// Interrupt player only if new wireless device got connected
if (data.Connected.Value() && !hasBluetoothConnector()) {
SbEventSchedule([](void*) {
player::AudioConfigurationChanged();
}, nullptr, 0);
}
}
void DeviceInfoImpl::Refresh() {
if ( !needs_refresh_.load() || !needs_refresh_.exchange( false ) )
return;
std::vector<SbMediaAudioConfiguration> audio_configs;
uint32_t rc;
bool needs_refresh = false;
VariableTimeout timeout { kDefaultTimeoutMs, 1000 };
if (did_subscribe_.load() == false && did_subscribe_.exchange(true) == false) {
rc = bluetooth_.Subscribe<StatusChangedData>(timeout.value(), "onStatusChanged", &DeviceInfoImpl::OnBluetoothStatusChanged, this);
if (Core::ERROR_NONE != rc && Core::ERROR_DUPLICATE_KEY != rc) {
SB_LOG(ERROR) << "Failed to subscribe to '" << kBluetoothCallsign
<< ".onStatusChanged' event, rc=" << rc
<< " ( " << Core::ErrorToString(rc) << " )";
}
}
using namespace WPEFramework::JsonData::DeviceInfo;
SupportedaudioportsData audio_ports;
rc = device_info_.Get(timeout.value(), "supportedaudioports", audio_ports);
if (Core::ERROR_NONE != rc) {
SB_LOG(ERROR) << "'" << kDeviceInfoCallsign << ".supportedaudioports' failed, rc = " << rc
<< " ( " << Core::ErrorToString(rc) << " )";
needs_refresh |= (Core::ERROR_ASYNC_FAILED == rc || Core::ERROR_TIMEDOUT == rc);
} else if (audio_ports.SupportedAudioPorts.Length() == 0) {
SB_LOG(INFO) << "No supported audio ports.";
} else {
auto index(audio_ports.SupportedAudioPorts.Elements());
while (index.Next()) {
const auto& port_name = index.Current().Value();
SB_LOG(INFO) << "Supported audio port name: " << port_name;
SbMediaAudioConfiguration configuration;
InitAudioConfigurationForAudioPort(port_name, &configuration);
if (configuration.connector != kAudioConnectorUnknown)
audio_configs.push_back(std::move(configuration));
}
}
if (has_bluetooth_audio_connected_.load()) {
SbMediaAudioConfiguration configuration;
InitAudioConfigurationForAudioPort("bluetooth", &configuration);
audio_configs.push_back(std::move(configuration));
} else {
ConnectedDevicesData connected_devices;
rc = bluetooth_.Get(timeout.value(), "getConnectedDevices", connected_devices);
if (Core::ERROR_NONE != rc) {
SB_LOG(ERROR) << "'" << kBluetoothCallsign << ".getConnectedDevices' failed, rc = " << rc
<< " ( " << Core::ErrorToString(rc) << " )";
needs_refresh |= (Core::ERROR_ASYNC_FAILED == rc || Core::ERROR_TIMEDOUT == rc);
} else if (connected_devices.Connecteddevices.Length() == 0) {
SB_LOG(INFO) << "No bluetooth connected devices.";
} else {
auto index(connected_devices.Connecteddevices.Elements());
while (index.Next()) {
const auto& device_details = index.Current();
SB_LOG(INFO) << "Bluetooth device name: " << device_details.Name.Value() << ", type: " << device_details.Devicetype.Value();
if (IsAudioOutputDeviceType(device_details.Devicetype.Value())) {
SbMediaAudioConfiguration configuration;
InitAudioConfigurationForAudioPort("bluetooth", &configuration);
audio_configs.push_back(std::move(configuration));
break;
}
}
}
}
if (audio_configs.empty()) {
SbMediaAudioConfiguration configuration;
InitAudioConfigurationForAudioPort("", &configuration);
audio_configs.push_back(std::move(configuration));
}
if (needs_refresh) {
SbEventSchedule([](void* data) {
GetDeviceInfo()->ForceNeedsRefresh();
}, nullptr, kSbTimeSecond);
}
SB_LOG(INFO) << "Updated audio configuration:";
for (const auto& config : audio_configs) {
SB_LOG(INFO) << " connector: " << (uint32_t) config.connector << " (" << ::starboard::GetMediaAudioConnectorName(config.connector) << ")";
}
::starboard::ScopedLock lock(mutex_);
std::swap(audio_configurations_, audio_configs);
}
bool DeviceInfoImpl::GetAudioConfiguration(int output_index, SbMediaAudioConfiguration* out_configuration) {
SB_DCHECK(output_index >= 0);
SB_DCHECK(out_configuration);
if (!out_configuration || output_index < 0)
return false;
Refresh();
::starboard::ScopedLock lock(mutex_);
size_t index = output_index;
if (index < audio_configurations_.size()) {
*out_configuration = audio_configurations_[index];
return true;
}
else if (index == 0) {
InitAudioConfigurationForAudioPort("", out_configuration);
return true;
}
return false;
}
bool DeviceInfoImpl::GetBrandName(std::string& out) {
if (!brand_name_.IsSet()) {
struct BrandNameInfo : public Core::JSON::Container {
BrandNameInfo() : Core::JSON::Container() {
Add(_T("brand"), &BrandName);
}
Core::JSON::String BrandName;
} info;
uint32_t rc = device_info_.Get(kDefaultTimeoutMs, "brandname", info);
if (Core::ERROR_NONE != rc) {
SB_LOG(ERROR) << "Failed to get '" << kDeviceInfoCallsign
<< ".brandname', rc=" << rc
<< " ( " << Core::ErrorToString(rc) << " ).";
if (rc == Core::ERROR_ASYNC_FAILED || rc == Core::ERROR_TIMEDOUT) {
brand_name_.Clear();
} else {
brand_name_ = "";
}
} else {
brand_name_ = info.BrandName.Value();
SB_LOG(INFO) << "Device brandname: " << brand_name_.Value();
}
}
out = brand_name_.Value();
return !out.empty();
}
} // namespace
ResolutionInfo DisplayInfo::GetResolution() {
return GetDisplayInfo()->GetResolution();
}
float DisplayInfo::GetDiagonalSizeInInches() {
return GetDisplayInfo()->GetDiagonalSizeInInches();
}
uint32_t DisplayInfo::GetHDRCaps() {
return GetDisplayInfo()->GetHDRCaps();
}
std::string DeviceIdentification::GetChipset() {
return GetDeviceIdImpl()->chipset;
}
std::string DeviceIdentification::GetFirmwareVersion() {
return GetDeviceIdImpl()->firmware_version;
}
bool NetworkInfo::IsConnectionTypeWireless() {
return GetNetworkInfo()->IsConnectionTypeWireless();
}
bool NetworkInfo::IsDisconnected() {
return GetNetworkInfo()->IsDisconnected();
}
void TextToSpeech::Speak(const std::string& text) {
GetTextToSpeech()->Speak(text);
}
bool TextToSpeech::IsEnabled() {
return GetTextToSpeech()->IsEnabled();
}
void TextToSpeech::Cancel() {
GetTextToSpeech()->Cancel();
}
bool Accessibility::GetCaptionSettings(SbAccessibilityCaptionSettings* out) {
return GetAccessibility()->GetCaptionSettings(out);
}
bool Accessibility::GetDisplaySettings(SbAccessibilityDisplaySettings* out) {
return GetAccessibility()->GetDisplaySettings(out);
}
void Accessibility::SetSettings(const std::string& json) {
GetAccessibility()->SetSettings(json);
}
bool Accessibility::GetSettings(std::string& out_json) {
return GetAccessibility()->GetSettings(out_json);
}
void AdvertisingId::SetSettings(const std::string& json) {
GetAdvertisingProperties()->SetSettings(json);
}
bool AdvertisingId::GetSettings(std::string& out_json) {
return GetAdvertisingProperties()->GetSettings(out_json);
}
bool AdvertisingId::GetIfa(std::string& out_json) {
return GetAdvertisingProperties()->GetIfa(out_json);
}
bool AdvertisingId::GetIfaType(std::string& out_json) {
return GetAdvertisingProperties()->GetIfaType(out_json);
}
bool AdvertisingId::GetLmtAdTracking(std::string& out_json) {
return GetAdvertisingProperties()->GetLmtAdTracking(out_json);
}
void SystemProperties::SetSettings(const std::string& json) {
GetSystemProperties()->SetSettings(json);
}
bool SystemProperties::GetSettings(std::string& out_json) {
return GetSystemProperties()->GetSettings(out_json);
}
bool SystemProperties::GetChipset(std::string &out) {
return GetSystemProperties()->GetChipset(out);
}
bool SystemProperties::GetFirmwareVersion(std::string &out) {
return GetSystemProperties()->GetFirmwareVersion(out);
}
bool SystemProperties::GetIntegratorName(std::string &out) {
return GetSystemProperties()->GetIntegratorName(out);
}
bool SystemProperties::GetBrandName(std::string &out) {
return GetSystemProperties()->GetBrandName(out);
}
bool SystemProperties::GetModelName(std::string &out) {
return GetSystemProperties()->GetModelName(out);
}
bool SystemProperties::GetModelYear(std::string &out) {
return GetSystemProperties()->GetModelYear(out);
}
bool SystemProperties::GetFriendlyName(std::string &out) {
return GetSystemProperties()->GetFriendlyName(out);
}
bool SystemProperties::GetDeviceType(std::string &out) {
return GetSystemProperties()->GetDeviceType(out);
}
bool AuthService::IsAvailable() {
return GetAuthService()->IsAvailable();
}
bool AuthService::GetExperience(std::string &out) {
return GetAuthService()->GetExperience(out);
}
bool DeviceInfo::GetAudioConfiguration(int index, SbMediaAudioConfiguration* out_audio_configuration) {
return GetDeviceInfo()->GetAudioConfiguration(index, out_audio_configuration);
}
bool DeviceInfo::GetBrandName(std::string& out) {
return GetDeviceInfo()->GetBrandName(out);
}
void TeardownJSONRPCLink() {
GetDisplayInfo()->Teardown();
GetTextToSpeech()->Teardown();
GetNetworkInfo()->Teardown();
GetDeviceInfo()->Teardown();
}
} // namespace shared
} // namespace rdk
} // namespace starboard
} // namespace third_party