diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-10 18:07:22 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-10 18:07:22 +0000 |
commit | c04dcc2e7d834218ef2d4194331e383402495ae1 (patch) | |
tree | 7333e38d10d75386e60f336b80c2443c1166031d /xbmc/addons/binary-addons | |
parent | Initial commit. (diff) | |
download | kodi-c04dcc2e7d834218ef2d4194331e383402495ae1.tar.xz kodi-c04dcc2e7d834218ef2d4194331e383402495ae1.zip |
Adding upstream version 2:20.4+dfsg.upstream/2%20.4+dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'xbmc/addons/binary-addons')
-rw-r--r-- | xbmc/addons/binary-addons/AddonDll.cpp | 553 | ||||
-rw-r--r-- | xbmc/addons/binary-addons/AddonDll.h | 163 | ||||
-rw-r--r-- | xbmc/addons/binary-addons/AddonInstanceHandler.cpp | 386 | ||||
-rw-r--r-- | xbmc/addons/binary-addons/AddonInstanceHandler.h | 135 | ||||
-rw-r--r-- | xbmc/addons/binary-addons/BinaryAddonBase.cpp | 108 | ||||
-rw-r--r-- | xbmc/addons/binary-addons/BinaryAddonBase.h | 54 | ||||
-rw-r--r-- | xbmc/addons/binary-addons/BinaryAddonManager.cpp | 88 | ||||
-rw-r--r-- | xbmc/addons/binary-addons/BinaryAddonManager.h | 95 | ||||
-rw-r--r-- | xbmc/addons/binary-addons/CMakeLists.txt | 12 | ||||
-rw-r--r-- | xbmc/addons/binary-addons/DllAddon.h | 35 |
10 files changed, 1629 insertions, 0 deletions
diff --git a/xbmc/addons/binary-addons/AddonDll.cpp b/xbmc/addons/binary-addons/AddonDll.cpp new file mode 100644 index 0000000..4ff5254 --- /dev/null +++ b/xbmc/addons/binary-addons/AddonDll.cpp @@ -0,0 +1,553 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#include "AddonDll.h" + +#include "ServiceBroker.h" +#include "addons/AddonStatusHandler.h" +#include "addons/addoninfo/AddonInfo.h" +#include "addons/binary-addons/BinaryAddonBase.h" +#include "addons/binary-addons/BinaryAddonManager.h" +#include "addons/binary-addons/DllAddon.h" +#include "addons/interfaces/AddonBase.h" +#include "addons/kodi-dev-kit/include/kodi/versions.h" +#include "addons/settings/AddonSettings.h" +#include "events/EventLog.h" +#include "events/NotificationEvent.h" +#include "filesystem/File.h" +#include "filesystem/SpecialProtocol.h" +#include "messaging/helpers/DialogOKHelper.h" +#include "settings/lib/SettingSection.h" +#include "utils/URIUtils.h" +#include "utils/Variant.h" +#include "utils/log.h" + +#include <utility> + +using namespace KODI::MESSAGING; + +namespace ADDON +{ + +CAddonDll::CAddonDll(const AddonInfoPtr& addonInfo, BinaryAddonBasePtr addonBase) + : CAddon(addonInfo, addonInfo->MainType()), m_binaryAddonBase(std::move(addonBase)) +{ +} + +CAddonDll::CAddonDll(const AddonInfoPtr& addonInfo, AddonType addonType) + : CAddon(addonInfo, addonType), + m_binaryAddonBase(CServiceBroker::GetBinaryAddonManager().GetRunningAddonBase(addonInfo->ID())) +{ +} + +CAddonDll::~CAddonDll() +{ + if (m_initialized) + Destroy(); +} + +std::string CAddonDll::GetDllPath(const std::string &libPath) +{ + std::string strFileName = libPath; + std::string strLibName = URIUtils::GetFileName(strFileName); + + if (strLibName.empty()) + return ""; + + /* Check if lib being loaded exists, else check in XBMC binary location */ +#if defined(TARGET_ANDROID) + if (XFILE::CFile::Exists(strFileName)) + { + bool doCopy = true; + std::string dstfile = URIUtils::AddFileToFolder(CSpecialProtocol::TranslatePath("special://xbmcaltbinaddons/"), strLibName); + + struct __stat64 dstFileStat; + if (XFILE::CFile::Stat(dstfile, &dstFileStat) == 0) + { + struct __stat64 srcFileStat; + if (XFILE::CFile::Stat(strFileName, &srcFileStat) == 0) + { + if (dstFileStat.st_size == srcFileStat.st_size && dstFileStat.st_mtime > srcFileStat.st_mtime) + doCopy = false; + } + } + + if (doCopy) + { + CLog::Log(LOGDEBUG, "ADDON: caching {} to {}", strFileName, dstfile); + XFILE::CFile::Copy(strFileName, dstfile); + } + + strFileName = dstfile; + } + if (!XFILE::CFile::Exists(strFileName)) + { + std::string tempbin = getenv("KODI_ANDROID_LIBS"); + strFileName = tempbin + "/" + strLibName; + } +#endif + + if (!XFILE::CFile::Exists(strFileName)) + { + std::string strAltFileName; + + std::string altbin = CSpecialProtocol::TranslatePath("special://xbmcaltbinaddons/"); + if (!altbin.empty()) + { + strAltFileName = altbin + strLibName; + if (!XFILE::CFile::Exists(strAltFileName)) + { + std::string temp = CSpecialProtocol::TranslatePath("special://xbmc/addons/"); + strAltFileName = strFileName; + strAltFileName.erase(0, temp.size()); + strAltFileName = altbin + strAltFileName; + } + CLog::Log(LOGDEBUG, "ADDON: Trying to load {}", strAltFileName); + } + + if (XFILE::CFile::Exists(strAltFileName)) + strFileName = strAltFileName; + else + { + std::string temp = CSpecialProtocol::TranslatePath("special://xbmc/"); + std::string tempbin = CSpecialProtocol::TranslatePath("special://xbmcbin/"); + strFileName.erase(0, temp.size()); + strFileName = tempbin + strFileName; + if (!XFILE::CFile::Exists(strFileName)) + { + CLog::Log(LOGERROR, "ADDON: Could not locate {}", strLibName); + strFileName.clear(); + } + } + } + + return strFileName; +} + +std::string CAddonDll::LibPath() const +{ + return GetDllPath(CAddon::LibPath()); +} + +bool CAddonDll::LoadDll() +{ + if (m_pDll) + return true; + + std::string strFileName = LibPath(); + if (strFileName.empty()) + return false; + + /* Load the Dll */ + m_pDll = new DllAddon; + m_pDll->SetFile(strFileName); + m_pDll->EnableDelayedUnload(false); + if (!m_pDll->Load()) + { + delete m_pDll; + m_pDll = nullptr; + + std::string heading = + StringUtils::Format("{}: {}", CAddonInfo::TranslateType(Type(), true), Name()); + HELPERS::ShowOKDialogLines(CVariant{heading}, CVariant{24070}, CVariant{24071}); + + return false; + } + + return true; +} + +ADDON_STATUS CAddonDll::Create(KODI_ADDON_INSTANCE_STRUCT* firstKodiInstance) +{ + CLog::Log(LOGDEBUG, "ADDON: Dll Initializing - {}", Name()); + m_initialized = false; + + if (!LoadDll()) + { + return ADDON_STATUS_PERMANENT_FAILURE; + } + + /* Check versions about global parts on add-on (parts used on all types) */ + for (unsigned int id = ADDON_GLOBAL_MAIN; id <= ADDON_GLOBAL_MAX; ++id) + { + if (!CheckAPIVersion(id)) + return ADDON_STATUS_PERMANENT_FAILURE; + } + + /* Allocate the helper function class to allow crosstalk over + helper add-on headers */ + if (!Interface_Base::InitInterface(this, m_interface, firstKodiInstance)) + return ADDON_STATUS_PERMANENT_FAILURE; + + /* Call Create to make connections, initializing data or whatever is + needed to become the AddOn running */ + ADDON_STATUS status = m_pDll->Create(&m_interface); + + // "C" ABI related call, if on add-on used. + if (status == ADDON_STATUS_OK && m_interface.toAddon->create) + status = m_interface.toAddon->create(m_interface.firstKodiInstance, &m_interface.addonBase); + + if (status == ADDON_STATUS_OK) + { + m_initialized = true; + } + else if (status == ADDON_STATUS_NEED_SETTINGS) + { + if ((status = TransferSettings(ADDON_SETTINGS_ID)) == ADDON_STATUS_OK) + m_initialized = true; + else + new CAddonStatusHandler(ID(), ADDON_SETTINGS_ID, status, false); + } + else + { // Addon failed initialization + CLog::Log(LOGERROR, + "ADDON: Dll {} - Client returned bad status ({}) from Create and is not usable", + Name(), status); + + // @todo currently a copy and paste from other function and becomes improved. + std::string heading = + StringUtils::Format("{}: {}", CAddonInfo::TranslateType(Type(), true), Name()); + HELPERS::ShowOKDialogLines(CVariant{ heading }, CVariant{ 24070 }, CVariant{ 24071 }); + } + + return status; +} + +void CAddonDll::Destroy() +{ + /* Unload library file */ + if (m_pDll) + { + if (m_interface.toAddon->destroy) + m_interface.toAddon->destroy(m_interface.addonBase); + m_pDll->Unload(); + } + + Interface_Base::DeInitInterface(m_interface); + + if (m_pDll) + { + delete m_pDll; + m_pDll = nullptr; + CLog::Log(LOGINFO, "ADDON: Dll Destroyed - {}", Name()); + } + + ResetSettings(ADDON_SETTINGS_ID); + + m_initialized = false; +} + +ADDON_STATUS CAddonDll::CreateInstance(KODI_ADDON_INSTANCE_STRUCT* instance) +{ + assert(instance != nullptr); + assert(instance->functions != nullptr); + assert(instance->info != nullptr); + assert(instance->info->functions != nullptr); + + ADDON_STATUS status = ADDON_STATUS_OK; + + if (!m_initialized) + status = Create(instance); + if (status != ADDON_STATUS_OK) + return status; + + /* Check version of requested instance type */ + if (!CheckAPIVersion(instance->info->type)) + return ADDON_STATUS_PERMANENT_FAILURE; + + status = m_interface.toAddon->create_instance(m_interface.addonBase, instance); + + if (instance->info) + { + m_usedInstances[instance->info->kodi] = instance; + } + + return status; +} + +void CAddonDll::DestroyInstance(KODI_ADDON_INSTANCE_STRUCT* instance) +{ + if (m_usedInstances.empty()) + return; + + auto it = m_usedInstances.find(instance->info->kodi); + if (it != m_usedInstances.end()) + { + m_interface.toAddon->destroy_instance(m_interface.addonBase, it->second); + m_usedInstances.erase(it); + } + + if (m_usedInstances.empty()) + Destroy(); +} + +bool CAddonDll::IsInUse() const +{ + if (m_informer) + return m_informer->IsInUse(ID()); + return false; +} + +void CAddonDll::RegisterInformer(CAddonDllInformer* informer) +{ + m_informer = informer; +} + +AddonPtr CAddonDll::GetRunningInstance() const +{ + if (CServiceBroker::IsAddonInterfaceUp()) + return CServiceBroker::GetBinaryAddonManager().GetRunningAddon(ID()); + + return AddonPtr(); +} + +void CAddonDll::OnPreInstall() +{ + if (m_binaryAddonBase) + m_binaryAddonBase->OnPreInstall(); +} + +void CAddonDll::OnPostInstall(bool update, bool modal) +{ + if (m_binaryAddonBase) + m_binaryAddonBase->OnPostInstall(update, modal); +} + +void CAddonDll::OnPreUnInstall() +{ + if (m_binaryAddonBase) + m_binaryAddonBase->OnPreUnInstall(); +} + +void CAddonDll::OnPostUnInstall() +{ + if (m_binaryAddonBase) + m_binaryAddonBase->OnPostUnInstall(); +} + +bool CAddonDll::DllLoaded(void) const +{ + return m_pDll != nullptr; +} + +CAddonVersion CAddonDll::GetTypeVersionDll(int type) const +{ + return CAddonVersion(m_pDll ? m_pDll->GetAddonTypeVersion(type) : nullptr); +} + +CAddonVersion CAddonDll::GetTypeMinVersionDll(int type) const +{ + return CAddonVersion(m_pDll ? m_pDll->GetAddonTypeMinVersion(type) : nullptr); +} + +void CAddonDll::SaveSettings(AddonInstanceId id /* = ADDON_SETTINGS_ID */) +{ + // must save first, as TransferSettings() reloads saved settings! + CAddon::SaveSettings(id); + if (m_initialized) + TransferSettings(id); +} + +ADDON_STATUS CAddonDll::TransferSettings(AddonInstanceId instanceId) +{ + bool restart = false; + ADDON_STATUS reportStatus = ADDON_STATUS_OK; + + CLog::Log(LOGDEBUG, "Calling TransferSettings for: {}", Name()); + + LoadSettings(false, true, instanceId); + + auto settings = GetSettings(instanceId); + if (settings != nullptr) + { + KODI_ADDON_INSTANCE_FUNC* instanceTarget{nullptr}; + KODI_ADDON_INSTANCE_HDL instanceHandle{nullptr}; + if (instanceId != ADDON_SETTINGS_ID) + { + const auto it = std::find_if( + m_usedInstances.begin(), m_usedInstances.end(), + [instanceId](const auto& data) { return data.second->info->number == instanceId; }); + if (it == m_usedInstances.end()) + return ADDON_STATUS_UNKNOWN; + + instanceTarget = it->second->functions; + instanceHandle = it->second->hdl; + } + + for (const auto& section : settings->GetSections()) + { + for (const auto& category : section->GetCategories()) + { + for (const auto& group : category->GetGroups()) + { + for (const auto& setting : group->GetSettings()) + { + if (StringUtils::StartsWith(setting->GetId(), ADDON_SETTING_INSTANCE_GROUP)) + continue; // skip internal settings + + ADDON_STATUS status = ADDON_STATUS_OK; + const char* id = setting->GetId().c_str(); + + switch (setting->GetType()) + { + case SettingType::Boolean: + { + bool tmp = std::static_pointer_cast<CSettingBool>(setting)->GetValue(); + if (instanceId == ADDON_SETTINGS_ID) + { + if (m_interface.toAddon->setting_change_boolean) + status = + m_interface.toAddon->setting_change_boolean(m_interface.addonBase, id, tmp); + } + else if (instanceTarget && instanceHandle) + { + if (instanceTarget->instance_setting_change_boolean) + status = + instanceTarget->instance_setting_change_boolean(instanceHandle, id, tmp); + } + break; + } + + case SettingType::Integer: + { + int tmp = std::static_pointer_cast<CSettingInt>(setting)->GetValue(); + if (instanceId == ADDON_SETTINGS_ID) + { + if (m_interface.toAddon->setting_change_integer) + status = + m_interface.toAddon->setting_change_integer(m_interface.addonBase, id, tmp); + } + else if (instanceTarget && instanceHandle) + { + if (instanceTarget->instance_setting_change_integer) + status = + instanceTarget->instance_setting_change_integer(instanceHandle, id, tmp); + } + break; + } + + case SettingType::Number: + { + float tmpf = static_cast<float>(std::static_pointer_cast<CSettingNumber>(setting)->GetValue()); + if (instanceId == ADDON_SETTINGS_ID) + { + if (m_interface.toAddon->setting_change_float) + status = + m_interface.toAddon->setting_change_float(m_interface.addonBase, id, tmpf); + } + else if (instanceTarget && instanceHandle) + { + if (instanceTarget->instance_setting_change_float) + status = + instanceTarget->instance_setting_change_float(instanceHandle, id, tmpf); + } + break; + } + + case SettingType::String: + { + if (instanceId == ADDON_SETTINGS_ID) + { + if (m_interface.toAddon->setting_change_string) + status = m_interface.toAddon->setting_change_string( + m_interface.addonBase, id, + std::static_pointer_cast<CSettingString>(setting)->GetValue().c_str()); + } + else if (instanceTarget && instanceHandle) + { + if (instanceTarget->instance_setting_change_string) + status = instanceTarget->instance_setting_change_string( + instanceHandle, id, + std::static_pointer_cast<CSettingString>(setting)->GetValue().c_str()); + } + break; + } + + default: + { + // log unknowns as an error, but go ahead and transfer the string + CLog::Log(LOGERROR, "Unknown setting type of '{}' for {}", id, Name()); + if (instanceId == ADDON_SETTINGS_ID) + { + if (m_interface.toAddon->setting_change_string) + status = m_interface.toAddon->setting_change_string( + m_interface.addonBase, id, setting->ToString().c_str()); + } + else if (instanceTarget && instanceHandle) + { + if (instanceTarget->instance_setting_change_string) + status = instanceTarget->instance_setting_change_string( + instanceHandle, id, setting->ToString().c_str()); + } + break; + } + } + + if (status == ADDON_STATUS_NEED_RESTART) + restart = true; + else if (status != ADDON_STATUS_OK) + reportStatus = status; + } + } + } + } + } + + if (restart || reportStatus != ADDON_STATUS_OK) + { + new CAddonStatusHandler(ID(), instanceId, restart ? ADDON_STATUS_NEED_RESTART : reportStatus, + true); + } + + return ADDON_STATUS_OK; +} + +bool CAddonDll::CheckAPIVersion(int type) +{ + /* check the API version */ + CAddonVersion kodiMinVersion(kodi::addon::GetTypeMinVersion(type)); + CAddonVersion addonVersion(m_pDll->GetAddonTypeVersion(type)); + CAddonVersion addonMinVersion = m_pDll->GetAddonTypeMinVersion_available() + ? CAddonVersion(m_pDll->GetAddonTypeMinVersion(type)) + : addonVersion; + + /* Check the global usage from addon + * if not used from addon, empty version is returned + */ + if (type <= ADDON_GLOBAL_MAX && addonVersion.empty()) + return true; + + /* If a instance (not global) version becomes checked must be the version + * present. + */ + if (kodiMinVersion > addonVersion || + addonMinVersion > CAddonVersion(kodi::addon::GetTypeVersion(type))) + { + CLog::Log(LOGERROR, "Add-on '{}' is using an incompatible API version for type '{}'. Kodi API min version = '{}/{}', add-on API version '{}/{}'", + Name(), + kodi::addon::GetTypeName(type), + kodi::addon::GetTypeVersion(type), + kodiMinVersion.asString(), + addonMinVersion.asString(), + addonVersion.asString()); + + if (CServiceBroker::GetGUI()) + { + CEventLog* eventLog = CServiceBroker::GetEventLog(); + if (eventLog) + eventLog->AddWithNotification( + EventPtr(new CNotificationEvent(Name(), 24152, EventLevel::Error))); + } + + return false; + } + + return true; +} + +} /* namespace ADDON */ diff --git a/xbmc/addons/binary-addons/AddonDll.h b/xbmc/addons/binary-addons/AddonDll.h new file mode 100644 index 0000000..5f17a04 --- /dev/null +++ b/xbmc/addons/binary-addons/AddonDll.h @@ -0,0 +1,163 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#pragma once + +#include "addons/Addon.h" +#include "addons/kodi-dev-kit/include/kodi/c-api/addon_base.h" + +#include <map> +#include <memory> +#include <string> + +class DllAddon; + +namespace ADDON +{ + +class CBinaryAddonBase; +using BinaryAddonBasePtr = std::shared_ptr<CBinaryAddonBase>; + +/*! + * Addon instance handler, used as identify for std::map to find related + * addon instance. This class itself not accessed here. + * + * @todo As long game addon system use CAddonDll itself and not + * IAddonInstanceHandler as parent, is the set of this as "void*" needed. + * After game system is changed should by this also changed to + * "const IAddonInstanceHandler*" or direct in map below. + */ +using ADDON_INSTANCE_HANDLER = void*; + +/*! + * @brief Information class for use on addon type managers. + * + * This to query via @ref CAddonDll the manager so that work can be performed. + * If there are multiple instances it be harder to be informed about any changes. + */ +class CAddonDllInformer +{ +public: + virtual ~CAddonDllInformer() = default; + + virtual bool IsInUse(const std::string& id) = 0; +}; + +class CAddonDll : public CAddon +{ +public: + CAddonDll(const AddonInfoPtr& addonInfo, BinaryAddonBasePtr addonBase); + CAddonDll(const AddonInfoPtr& addonInfo, AddonType addonType); + ~CAddonDll() override; + + // Implementation of IAddon via CAddon + std::string LibPath() const override; + + // addon settings + void SaveSettings(AddonInstanceId id = ADDON_SETTINGS_ID) override; + + bool DllLoaded(void) const; + + /*! + * @brief Get api version of moduleType type + * + * @return The version of requested type, if dll is loaded and supported by addon. + * If one of both do not match, an empty version is returned. + * + * @note This should only be called if the associated dll is loaded. + * Otherwise use @ref CAddonInfo::DependencyVersion(...) + */ + CAddonVersion GetTypeVersionDll(int type) const; + + /*! + * @brief Get api min version of moduleType type + * + * @return The version of requested type, if dll is loaded and supported by addon. + * If one of both do not match, an empty version is returned. + * + * @note This should only be called if the associated dll is loaded. + * Otherwise use @ref CAddonInfo::DependencyMinVersion(...) + */ + CAddonVersion GetTypeMinVersionDll(int type) const; + + /*! + * @brief Function to create a addon instance class + * + * @param[in,out] instance The for addon used data structure for active instance + * @return The status of addon after the creation. + */ + ADDON_STATUS CreateInstance(KODI_ADDON_INSTANCE_STRUCT* instance); + + /*! + * @brief Function to destroy a on addon created instance class + * + * @param[in] instance The for addon used data structure for active instance + */ + void DestroyInstance(KODI_ADDON_INSTANCE_STRUCT* instance); + + bool IsInUse() const override; + void RegisterInformer(CAddonDllInformer* informer); + AddonPtr GetRunningInstance() const override; + + void OnPreInstall() override; + void OnPostInstall(bool update, bool modal) override; + void OnPreUnInstall() override; + void OnPostUnInstall() override; + + bool Initialized() const { return m_initialized; } + +protected: + static std::string GetDllPath(const std::string& strFileName); + + std::string m_parentLib; + +private: + /*! + * @brief Main addon creation call function + * + * This becomes called only one time before a addon instance becomes created. + * If another instance becomes requested is this Create no more used. To see + * like a "int main()" on exe. + * + * @param[in] firstKodiInstance The first instance who becomes used. + * In case addon supports only one instance + * and not multiple together can addon use + * only one complete class for everything. + * This is used then to interact on interface. + * @return The status of addon after the creation. + */ + ADDON_STATUS Create(KODI_ADDON_INSTANCE_STRUCT* firstKodiInstance); + + /*! + * @brief Main addon destroying call function + * + * This becomes called only one time after the last addon instance becomes destroyed. + */ + void Destroy(); + + bool CheckAPIVersion(int type); + + BinaryAddonBasePtr m_binaryAddonBase = nullptr; + DllAddon* m_pDll = nullptr; + bool m_initialized = false; + bool LoadDll(); + std::map<ADDON_INSTANCE_HANDLER, KODI_ADDON_INSTANCE_STRUCT*> m_usedInstances; + CAddonDllInformer* m_informer = nullptr; + + virtual ADDON_STATUS TransferSettings(AddonInstanceId instanceId); + + /*! + * This structure, which is fixed to the addon headers, makes use of the at + * least supposed parts for the interface. + * This structure is defined in: + * /xbmc/addons/kodi-dev-kit/include/kodi/AddonBase.h + */ + AddonGlobalInterface m_interface = {}; +}; + +} /* namespace ADDON */ diff --git a/xbmc/addons/binary-addons/AddonInstanceHandler.cpp b/xbmc/addons/binary-addons/AddonInstanceHandler.cpp new file mode 100644 index 0000000..34c303b --- /dev/null +++ b/xbmc/addons/binary-addons/AddonInstanceHandler.cpp @@ -0,0 +1,386 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#include "AddonInstanceHandler.h" + +#include "ServiceBroker.h" +#include "addons/AddonVersion.h" +#include "addons/binary-addons/AddonDll.h" +#include "addons/binary-addons/BinaryAddonManager.h" +#include "addons/interfaces/AddonBase.h" +#include "addons/kodi-dev-kit/include/kodi/AddonBase.h" +#include "addons/settings/AddonSettings.h" +#include "filesystem/Directory.h" +#include "filesystem/SpecialProtocol.h" +#include "settings/lib/Setting.h" +#include "utils/StringUtils.h" +#include "utils/log.h" + +#include <mutex> + +namespace ADDON +{ + +CCriticalSection IAddonInstanceHandler::m_cdSec; + +IAddonInstanceHandler::IAddonInstanceHandler( + ADDON_TYPE type, + const AddonInfoPtr& addonInfo, + AddonInstanceId instanceId /* = ADDON_INSTANCE_ID_UNUSED */, + KODI_HANDLE parentInstance /* = nullptr*/, + const std::string& uniqueWorkID /* = ""*/) + : m_type(type), m_instanceId(instanceId), m_parentInstance(parentInstance), m_addonInfo(addonInfo) +{ + // if no special instance ID is given generate one from class pointer (is + // faster as unique id and also safe enough for them). + m_uniqueWorkID = !uniqueWorkID.empty() ? uniqueWorkID : StringUtils::Format("{}", fmt::ptr(this)); + m_addonBase = CServiceBroker::GetBinaryAddonManager().GetAddonBase(addonInfo, this, m_addon); + + KODI_ADDON_INSTANCE_INFO* info = new KODI_ADDON_INSTANCE_INFO(); + info->number = m_instanceId; // @todo change within next big API change to "instance_id" + info->id = m_uniqueWorkID.c_str(); // @todo change within next big API change to "unique_work_id" + info->version = kodi::addon::GetTypeVersion(m_type); + info->type = m_type; + info->kodi = this; + info->parent = m_parentInstance; + info->first_instance = m_addon && !m_addon->Initialized(); + info->functions = new KODI_ADDON_INSTANCE_FUNC_CB(); + info->functions->get_instance_user_path = get_instance_user_path; + info->functions->is_instance_setting_using_default = is_instance_setting_using_default; + info->functions->get_instance_setting_bool = get_instance_setting_bool; + info->functions->get_instance_setting_int = get_instance_setting_int; + info->functions->get_instance_setting_float = get_instance_setting_float; + info->functions->get_instance_setting_string = get_instance_setting_string; + info->functions->set_instance_setting_bool = set_instance_setting_bool; + info->functions->set_instance_setting_int = set_instance_setting_int; + info->functions->set_instance_setting_float = set_instance_setting_float; + info->functions->set_instance_setting_string = set_instance_setting_string; + m_ifc.info = info; + m_ifc.functions = new KODI_ADDON_INSTANCE_FUNC(); +} + +IAddonInstanceHandler::~IAddonInstanceHandler() +{ + CServiceBroker::GetBinaryAddonManager().ReleaseAddonBase(m_addonBase, this); + + delete m_ifc.functions; + if (m_ifc.info) + delete m_ifc.info->functions; + delete m_ifc.info; +} + +std::string IAddonInstanceHandler::ID() const +{ + return m_addon ? m_addon->ID() : ""; +} + +std::string IAddonInstanceHandler::Name() const +{ + return m_addon ? m_addon->Name() : ""; +} + +std::string IAddonInstanceHandler::Author() const +{ + return m_addon ? m_addon->Author() : ""; +} + +std::string IAddonInstanceHandler::Icon() const +{ + return m_addon ? m_addon->Icon() : ""; +} + +std::string IAddonInstanceHandler::Path() const +{ + return m_addon ? m_addon->Path() : ""; +} + +std::string IAddonInstanceHandler::Profile() const +{ + return m_addon ? m_addon->Profile() : ""; +} + +CAddonVersion IAddonInstanceHandler::Version() const +{ + return m_addon ? m_addon->Version() : CAddonVersion(); +} + +ADDON_STATUS IAddonInstanceHandler::CreateInstance() +{ + if (!m_addon) + return ADDON_STATUS_UNKNOWN; + + std::unique_lock<CCriticalSection> lock(m_cdSec); + + ADDON_STATUS status = m_addon->CreateInstance(&m_ifc); + if (status != ADDON_STATUS_OK) + { + CLog::Log(LOGERROR, + "IAddonInstanceHandler::{}: {} returned bad status \"{}\" during instance creation", + __func__, m_addon->ID(), kodi::addon::TranslateAddonStatus(status)); + } + return status; +} + +void IAddonInstanceHandler::DestroyInstance() +{ + std::unique_lock<CCriticalSection> lock(m_cdSec); + if (m_addon) + m_addon->DestroyInstance(&m_ifc); +} + +std::shared_ptr<CSetting> IAddonInstanceHandler::GetSetting(const std::string& setting) +{ + if (!m_addon->HasSettings(m_instanceId)) + { + CLog::Log(LOGERROR, "IAddonInstanceHandler::{} - couldn't get settings for add-on '{}'", + __func__, Name()); + return nullptr; + } + + auto value = m_addon->GetSettings(m_instanceId)->GetSetting(setting); + if (value == nullptr) + { + CLog::Log(LOGERROR, "IAddonInstanceHandler::{} - can't find setting '{}' in '{}'", __func__, + setting, Name()); + return nullptr; + } + + return value; +} + +char* IAddonInstanceHandler::get_instance_user_path(const KODI_ADDON_INSTANCE_BACKEND_HDL hdl) +{ + IAddonInstanceHandler* instance = static_cast<IAddonInstanceHandler*>(hdl); + if (!instance) + return nullptr; + + const std::string path = CSpecialProtocol::TranslatePath(instance->m_addon->Profile()); + + XFILE::CDirectory::Create(path); + return strdup(path.c_str()); +} + +bool IAddonInstanceHandler::is_instance_setting_using_default( + const KODI_ADDON_INSTANCE_BACKEND_HDL hdl, const char* id) +{ + IAddonInstanceHandler* instance = static_cast<IAddonInstanceHandler*>(hdl); + if (!instance || !id) + return false; + + auto setting = instance->GetSetting(id); + if (setting == nullptr) + return false; + + return setting->IsDefault(); +} + +bool IAddonInstanceHandler::get_instance_setting_bool(const KODI_ADDON_INSTANCE_BACKEND_HDL hdl, + const char* id, + bool* value) +{ + IAddonInstanceHandler* instance = static_cast<IAddonInstanceHandler*>(hdl); + if (!instance || !id || !value) + return false; + + auto setting = instance->GetSetting(id); + if (setting == nullptr) + return false; + + if (setting->GetType() != SettingType::Boolean) + { + CLog::Log(LOGERROR, "IAddonInstanceHandler::{} - setting '{}' is not a boolean in '{}'", + __func__, id, instance->Name()); + return false; + } + + *value = std::static_pointer_cast<CSettingBool>(setting)->GetValue(); + return true; +} + +bool IAddonInstanceHandler::get_instance_setting_int(const KODI_ADDON_INSTANCE_BACKEND_HDL hdl, + const char* id, + int* value) +{ + IAddonInstanceHandler* instance = static_cast<IAddonInstanceHandler*>(hdl); + if (!instance || !id || !value) + return false; + + auto setting = instance->GetSetting(id); + if (setting == nullptr) + return false; + + if (setting->GetType() != SettingType::Integer && setting->GetType() != SettingType::Number) + { + CLog::Log(LOGERROR, "IAddonInstanceHandler::{} - setting '{}' is not a integer in '{}'", + __func__, id, instance->Name()); + return false; + } + + if (setting->GetType() == SettingType::Integer) + *value = std::static_pointer_cast<CSettingInt>(setting)->GetValue(); + else + *value = static_cast<int>(std::static_pointer_cast<CSettingNumber>(setting)->GetValue()); + return true; +} + +bool IAddonInstanceHandler::get_instance_setting_float(const KODI_ADDON_INSTANCE_BACKEND_HDL hdl, + const char* id, + float* value) +{ + IAddonInstanceHandler* instance = static_cast<IAddonInstanceHandler*>(hdl); + if (!instance || !id || !value) + return false; + + auto setting = instance->GetSetting(id); + if (setting == nullptr) + return false; + + if (setting->GetType() != SettingType::Number) + { + CLog::Log(LOGERROR, "IAddonInstanceHandler::{} - setting '{}' is not a number in '{}'", + __func__, id, instance->Name()); + return false; + } + + *value = static_cast<float>(std::static_pointer_cast<CSettingNumber>(setting)->GetValue()); + return true; +} + +bool IAddonInstanceHandler::get_instance_setting_string(const KODI_ADDON_INSTANCE_BACKEND_HDL hdl, + const char* id, + char** value) +{ + IAddonInstanceHandler* instance = static_cast<IAddonInstanceHandler*>(hdl); + if (!instance || !id || !value) + return false; + + auto setting = instance->GetSetting(id); + if (setting == nullptr) + return false; + + if (setting->GetType() != SettingType::String) + { + CLog::Log(LOGERROR, "IAddonInstanceHandler::{} - setting '{}' is not a string in '{}'", + __func__, id, instance->Name()); + return false; + } + + *value = strdup(std::static_pointer_cast<CSettingString>(setting)->GetValue().c_str()); + return true; +} + +bool IAddonInstanceHandler::set_instance_setting_bool(const KODI_ADDON_INSTANCE_BACKEND_HDL hdl, + const char* id, + bool value) +{ + IAddonInstanceHandler* instance = static_cast<IAddonInstanceHandler*>(hdl); + if (!instance || !id) + return false; + + if (Interface_Base::UpdateSettingInActiveDialog(instance->m_addon.get(), instance->m_instanceId, + id, value ? "true" : "false")) + return true; + + if (!instance->m_addon->UpdateSettingBool(id, value, instance->m_instanceId)) + { + CLog::Log(LOGERROR, "IAddonInstanceHandler::{} - invalid setting type", __func__); + return false; + } + + instance->m_addon->SaveSettings(instance->m_instanceId); + + return true; +} + +bool IAddonInstanceHandler::set_instance_setting_int(const KODI_ADDON_INSTANCE_BACKEND_HDL hdl, + const char* id, + int value) +{ + IAddonInstanceHandler* instance = static_cast<IAddonInstanceHandler*>(hdl); + if (!instance || !id) + { + CLog::Log(LOGERROR, "IAddonInstanceHandler::{} - invalid data (instance='{}', id='{}')", + __func__, hdl, static_cast<const void*>(id)); + + return false; + } + + if (Interface_Base::UpdateSettingInActiveDialog(instance->m_addon.get(), instance->m_instanceId, + id, std::to_string(value))) + return true; + + if (!instance->m_addon->UpdateSettingInt(id, value, instance->m_instanceId)) + { + CLog::Log(LOGERROR, "IAddonInstanceHandler::{} - invalid setting type", __func__); + return false; + } + + instance->m_addon->SaveSettings(instance->m_instanceId); + + return true; +} + +bool IAddonInstanceHandler::set_instance_setting_float(const KODI_ADDON_INSTANCE_BACKEND_HDL hdl, + const char* id, + float value) +{ + IAddonInstanceHandler* instance = static_cast<IAddonInstanceHandler*>(hdl); + if (!instance || !id) + { + CLog::Log(LOGERROR, "IAddonInstanceHandler::{} - invalid data (instance='{}', id='{}')", + __func__, hdl, static_cast<const void*>(id)); + + return false; + } + + if (Interface_Base::UpdateSettingInActiveDialog(instance->m_addon.get(), instance->m_instanceId, + id, StringUtils::Format("{:f}", value))) + return true; + + if (!instance->m_addon->UpdateSettingNumber(id, static_cast<double>(value), + instance->m_instanceId)) + { + CLog::Log(LOGERROR, "IAddonInstanceHandler::{} - invalid setting type", __func__); + return false; + } + + instance->m_addon->SaveSettings(instance->m_instanceId); + + return true; +} + +bool IAddonInstanceHandler::set_instance_setting_string(const KODI_ADDON_INSTANCE_BACKEND_HDL hdl, + const char* id, + const char* value) +{ + IAddonInstanceHandler* instance = static_cast<IAddonInstanceHandler*>(hdl); + if (!instance || !id || !value) + { + CLog::Log(LOGERROR, + "IAddonInstanceHandler::{} - invalid data (instance='{}', id='{}', value='{}')", + __func__, hdl, static_cast<const void*>(id), static_cast<const void*>(value)); + + return false; + } + + if (Interface_Base::UpdateSettingInActiveDialog(instance->m_addon.get(), instance->m_instanceId, + id, value)) + return true; + + if (!instance->m_addon->UpdateSettingString(id, value, instance->m_instanceId)) + { + CLog::Log(LOGERROR, "IAddonInstanceHandler::{} - invalid setting type", __func__); + return false; + } + + instance->m_addon->SaveSettings(instance->m_instanceId); + + return true; +} + +} /* namespace ADDON */ diff --git a/xbmc/addons/binary-addons/AddonInstanceHandler.h b/xbmc/addons/binary-addons/AddonInstanceHandler.h new file mode 100644 index 0000000..27ba95b --- /dev/null +++ b/xbmc/addons/binary-addons/AddonInstanceHandler.h @@ -0,0 +1,135 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#pragma once + +#include "addons/IAddon.h" +#include "addons/kodi-dev-kit/include/kodi/c-api/addon_base.h" +#include "addons/kodi-dev-kit/include/kodi/versions.h" +#include "threads/CriticalSection.h" + +#include <memory> +#include <string> + +class CSetting; + +namespace ADDON +{ + +class CAddonDll; +using AddonDllPtr = std::shared_ptr<CAddonDll>; + +class CAddonInfo; +using AddonInfoPtr = std::shared_ptr<CAddonInfo>; + +class CBinaryAddonBase; +using BinaryAddonBasePtr = std::shared_ptr<CBinaryAddonBase>; + +class IAddonInstanceHandler +{ +public: + /** + * @brief Class constructor for handling add-on instance processes, allowing + * an add-on to handle multiple work simultaneously and independently. + * + * @param[in] type The associated add-on type which is processed in the running + * instance. + * @param[in] addonInfo Class for querying available add-on information (e.g. + * content declared in addon.xml). + * @param[in] parentInstance *[opt]* Above running add-on instance which starts this + * instance. Used to have the associated class for + * work to open in the add-on.\n\n + * **Currently used values:** + * | Parent | Target | Description + * |--------|--------|------------- + * | @ref kodi::addon::CInstanceInputStream | @ref kodi::addon::CInstanceVideoCodec | In order to be able to access the overlying input stream instance in the video codec created by Kodi on the add-on. + * @param[in] uniqueWorkID *[opt]* Identification value intended to pass any special + * values to the instance to be opened. + * If not used, the IAddonInstanceHandler class pointer + * is used as a string.\n\n + * **Currently used values:** + * | Add-on instance type | Description + * |----------------------|--------------------- + * | @ref kodi::addon::CInstanceInputStream "Inputstream" | To transfer special values to inputstream using the property @ref STREAM_PROPERTY_INPUTSTREAM_INSTANCE_ID from external space, for example PVR add-on which also supports inputstream can exchange special values with it, e.g. select the necessary add-on processing class, since it is not known at the start what is being executed ( live TV, radio, recordings...) and add-on may use different classes. + * | All other | The used class pointer of Kodi's @ref IAddonInstanceHandler is used as a value to have an individually different value. + */ + IAddonInstanceHandler(ADDON_TYPE type, + const AddonInfoPtr& addonInfo, + AddonInstanceId instanceId = ADDON_INSTANCE_ID_UNUSED, + KODI_HANDLE parentInstance = nullptr, + const std::string& uniqueWorkID = ""); + virtual ~IAddonInstanceHandler(); + + ADDON_TYPE UsedType() const { return m_type; } + AddonInstanceId InstanceId() const { return m_instanceId; } + const std::string& UniqueWorkID() { return m_uniqueWorkID; } + + std::string ID() const; + std::string Name() const; + std::string Author() const; + std::string Icon() const; + std::string Path() const; + std::string Profile() const; + CAddonVersion Version() const; + + ADDON_STATUS CreateInstance(); + void DestroyInstance(); + const AddonDllPtr& Addon() const { return m_addon; } + AddonInfoPtr GetAddonInfo() const { return m_addonInfo; } + + virtual void OnPreInstall() {} + virtual void OnPostInstall(bool update, bool modal) {} + virtual void OnPreUnInstall() {} + virtual void OnPostUnInstall() {} + +protected: + KODI_ADDON_INSTANCE_INFO m_info{}; + KODI_ADDON_INSTANCE_STRUCT m_ifc{}; + +private: + std::shared_ptr<CSetting> GetSetting(const std::string& setting); + + static char* get_instance_user_path(const KODI_ADDON_INSTANCE_BACKEND_HDL hdl); + static bool is_instance_setting_using_default(const KODI_ADDON_INSTANCE_BACKEND_HDL hdl, + const char* id); + static bool get_instance_setting_bool(const KODI_ADDON_INSTANCE_BACKEND_HDL hdl, + const char* id, + bool* value); + static bool get_instance_setting_int(const KODI_ADDON_INSTANCE_BACKEND_HDL hdl, + const char* id, + int* value); + static bool get_instance_setting_float(const KODI_ADDON_INSTANCE_BACKEND_HDL hdl, + const char* id, + float* value); + static bool get_instance_setting_string(const KODI_ADDON_INSTANCE_BACKEND_HDL hdl, + const char* id, + char** value); + static bool set_instance_setting_bool(const KODI_ADDON_INSTANCE_BACKEND_HDL hdl, + const char* id, + bool value); + static bool set_instance_setting_int(const KODI_ADDON_INSTANCE_BACKEND_HDL hdl, + const char* id, + int value); + static bool set_instance_setting_float(const KODI_ADDON_INSTANCE_BACKEND_HDL hdl, + const char* id, + float value); + static bool set_instance_setting_string(const KODI_ADDON_INSTANCE_BACKEND_HDL hdl, + const char* id, + const char* value); + + const ADDON_TYPE m_type; + const AddonInstanceId m_instanceId; + std::string m_uniqueWorkID; + KODI_HANDLE m_parentInstance; + AddonInfoPtr m_addonInfo; + BinaryAddonBasePtr m_addonBase; + AddonDllPtr m_addon; + static CCriticalSection m_cdSec; +}; + +} /* namespace ADDON */ diff --git a/xbmc/addons/binary-addons/BinaryAddonBase.cpp b/xbmc/addons/binary-addons/BinaryAddonBase.cpp new file mode 100644 index 0000000..cea1586 --- /dev/null +++ b/xbmc/addons/binary-addons/BinaryAddonBase.cpp @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#include "BinaryAddonBase.h" + +#include "addons/addoninfo/AddonInfo.h" +#include "addons/binary-addons/AddonDll.h" +#include "addons/binary-addons/AddonInstanceHandler.h" +#include "utils/log.h" + +#include <mutex> + +using namespace ADDON; + +const std::string& CBinaryAddonBase::ID() const +{ + return m_addonInfo->ID(); +} + +AddonDllPtr CBinaryAddonBase::GetAddon(IAddonInstanceHandler* handler) +{ + if (handler == nullptr) + { + CLog::Log(LOGERROR, "CBinaryAddonBase::{}: for Id '{}' called with empty instance handler", + __FUNCTION__, ID()); + return nullptr; + } + + std::unique_lock<CCriticalSection> lock(m_critSection); + + // If no 'm_activeAddon' is defined create it new. + if (m_activeAddon == nullptr) + m_activeAddon = std::make_shared<CAddonDll>(m_addonInfo, shared_from_this()); + + // add the instance handler to the info to know used amount on addon + m_activeAddonHandlers.insert(handler); + + return m_activeAddon; +} + +void CBinaryAddonBase::ReleaseAddon(IAddonInstanceHandler* handler) +{ + if (handler == nullptr) + { + CLog::Log(LOGERROR, "CBinaryAddonBase::{}: for Id '{}' called with empty instance handler", + __FUNCTION__, ID()); + return; + } + + std::unique_lock<CCriticalSection> lock(m_critSection); + + auto presentHandler = m_activeAddonHandlers.find(handler); + if (presentHandler == m_activeAddonHandlers.end()) + return; + + m_activeAddonHandlers.erase(presentHandler); + + // if no handler is present anymore reset and delete the add-on class on information + if (m_activeAddonHandlers.empty()) + { + m_activeAddon.reset(); + } +} + +size_t CBinaryAddonBase::UsedInstanceCount() const +{ + std::unique_lock<CCriticalSection> lock(m_critSection); + return m_activeAddonHandlers.size(); +} + +AddonDllPtr CBinaryAddonBase::GetActiveAddon() +{ + std::unique_lock<CCriticalSection> lock(m_critSection); + return m_activeAddon; +} + +void CBinaryAddonBase::OnPreInstall() +{ + const std::unordered_set<IAddonInstanceHandler*> activeAddonHandlers = m_activeAddonHandlers; + for (const auto& instance : activeAddonHandlers) + instance->OnPreInstall(); +} + +void CBinaryAddonBase::OnPostInstall(bool update, bool modal) +{ + const std::unordered_set<IAddonInstanceHandler*> activeAddonHandlers = m_activeAddonHandlers; + for (const auto& instance : activeAddonHandlers) + instance->OnPostInstall(update, modal); +} + +void CBinaryAddonBase::OnPreUnInstall() +{ + const std::unordered_set<IAddonInstanceHandler*> activeAddonHandlers = m_activeAddonHandlers; + for (const auto& instance : activeAddonHandlers) + instance->OnPreUnInstall(); +} + +void CBinaryAddonBase::OnPostUnInstall() +{ + const std::unordered_set<IAddonInstanceHandler*> activeAddonHandlers = m_activeAddonHandlers; + for (const auto& instance : activeAddonHandlers) + instance->OnPostUnInstall(); +} diff --git a/xbmc/addons/binary-addons/BinaryAddonBase.h b/xbmc/addons/binary-addons/BinaryAddonBase.h new file mode 100644 index 0000000..789b35a --- /dev/null +++ b/xbmc/addons/binary-addons/BinaryAddonBase.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#pragma once + +#include "threads/CriticalSection.h" + +#include <memory> +#include <string> +#include <unordered_set> + +namespace ADDON +{ + + class IAddonInstanceHandler; + + class CAddonInfo; + using AddonInfoPtr = std::shared_ptr<CAddonInfo>; + + class CAddonDll; + typedef std::shared_ptr<CAddonDll> AddonDllPtr; + + class CBinaryAddonBase : public std::enable_shared_from_this<CBinaryAddonBase> + { + public: + explicit CBinaryAddonBase(const AddonInfoPtr& addonInfo) : m_addonInfo(addonInfo) { } + + const std::string& ID() const; + + AddonDllPtr GetAddon(IAddonInstanceHandler* handler); + void ReleaseAddon(IAddonInstanceHandler* handler); + size_t UsedInstanceCount() const; + + AddonDllPtr GetActiveAddon(); + + void OnPreInstall(); + void OnPostInstall(bool update, bool modal); + void OnPreUnInstall(); + void OnPostUnInstall(); + + private: + AddonInfoPtr m_addonInfo; + + mutable CCriticalSection m_critSection; + AddonDllPtr m_activeAddon; + std::unordered_set<IAddonInstanceHandler*> m_activeAddonHandlers; + }; + +} /* namespace ADDON */ diff --git a/xbmc/addons/binary-addons/BinaryAddonManager.cpp b/xbmc/addons/binary-addons/BinaryAddonManager.cpp new file mode 100644 index 0000000..699fc09 --- /dev/null +++ b/xbmc/addons/binary-addons/BinaryAddonManager.cpp @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#include "BinaryAddonManager.h" + +#include "addons/addoninfo/AddonInfo.h" +#include "addons/binary-addons/AddonDll.h" +#include "addons/binary-addons/BinaryAddonBase.h" +#include "utils/log.h" + +#include <mutex> + +using namespace ADDON; + +BinaryAddonBasePtr CBinaryAddonManager::GetAddonBase(const AddonInfoPtr& addonInfo, + IAddonInstanceHandler* handler, + AddonDllPtr& addon) +{ + std::unique_lock<CCriticalSection> lock(m_critSection); + + BinaryAddonBasePtr addonBase; + + const auto& addonInstances = m_runningAddons.find(addonInfo->ID()); + if (addonInstances != m_runningAddons.end()) + { + addonBase = addonInstances->second; + } + else + { + addonBase = std::make_shared<CBinaryAddonBase>(addonInfo); + + m_runningAddons.emplace(addonInfo->ID(), addonBase); + } + + if (addonBase) + { + addon = addonBase->GetAddon(handler); + } + if (!addon) + { + CLog::Log(LOGFATAL, "CBinaryAddonManager::{}: Tried to get add-on '{}' who not available!", + __func__, addonInfo->ID()); + } + + return addonBase; +} + +void CBinaryAddonManager::ReleaseAddonBase(const BinaryAddonBasePtr& addonBase, + IAddonInstanceHandler* handler) +{ + const auto& addon = m_runningAddons.find(addonBase->ID()); + if (addon == m_runningAddons.end()) + return; + + addonBase->ReleaseAddon(handler); + + if (addonBase->UsedInstanceCount() > 0) + return; + + m_runningAddons.erase(addon); +} + +BinaryAddonBasePtr CBinaryAddonManager::GetRunningAddonBase(const std::string& addonId) const +{ + std::unique_lock<CCriticalSection> lock(m_critSection); + + const auto& addonInstances = m_runningAddons.find(addonId); + if (addonInstances != m_runningAddons.end()) + return addonInstances->second; + + return nullptr; +} + +AddonPtr CBinaryAddonManager::GetRunningAddon(const std::string& addonId) const +{ + std::unique_lock<CCriticalSection> lock(m_critSection); + + const BinaryAddonBasePtr base = GetRunningAddonBase(addonId); + if (base) + return base->GetActiveAddon(); + + return nullptr; +} diff --git a/xbmc/addons/binary-addons/BinaryAddonManager.h b/xbmc/addons/binary-addons/BinaryAddonManager.h new file mode 100644 index 0000000..d7f4a9f --- /dev/null +++ b/xbmc/addons/binary-addons/BinaryAddonManager.h @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#pragma once + +#include "threads/CriticalSection.h" + +#include <map> +#include <memory> + +namespace ADDON +{ + + class IAddonInstanceHandler; + + class IAddon; + using AddonPtr = std::shared_ptr<IAddon>; + + class CAddonInfo; + using AddonInfoPtr = std::shared_ptr<CAddonInfo>; + + class CAddonDll; + typedef std::shared_ptr<CAddonDll> AddonDllPtr; + + class CBinaryAddonBase; + typedef std::shared_ptr<CBinaryAddonBase> BinaryAddonBasePtr; + + class CBinaryAddonManager + { + public: + CBinaryAddonManager() = default; + CBinaryAddonManager(const CBinaryAddonManager&) = delete; + ~CBinaryAddonManager() = default; + + /*! + * @brief Create or get available addon instance handle base. + * + * On first call the binary addon base class becomes created, on every next + * call of addon id, this becomes given again and a counter about in + * @ref CBinaryAddonBase increased. + * + * @param[in] addonBase related addon base to release + * @param[in] handler related instance handle class + * + * @warning This and @ref ReleaseAddonBase are only be called from + * @ref IAddonInstanceHandler, use nowhere else allowed! + * + */ + BinaryAddonBasePtr GetAddonBase(const AddonInfoPtr& addonInfo, + IAddonInstanceHandler* handler, + AddonDllPtr& addon); + + /*! + * @brief Release a running addon instance handle base. + * + * On last release call the here on map stored entry becomes + * removed and the dll unloaded. + * + * @param[in] addonBase related addon base to release + * @param[in] handler related instance handle class + * + */ + void ReleaseAddonBase(const BinaryAddonBasePtr& addonBase, IAddonInstanceHandler* handler); + + /*! + * @brief Get running addon base class for a given addon id. + * + * @param[in] addonId the addon id + * @return running addon base class if found, nullptr otherwise. + * + */ + BinaryAddonBasePtr GetRunningAddonBase(const std::string& addonId) const; + + /*! + * @brief Used from other addon manager to get active addon over a from him + * created CAddonDll. + * + * @param[in] addonId related addon id string + * @return if present the pointer to active one or nullptr if not present + * + */ + AddonPtr GetRunningAddon(const std::string& addonId) const; + + private: + mutable CCriticalSection m_critSection; + + std::map<std::string, BinaryAddonBasePtr> m_runningAddons; + }; + +} /* namespace ADDON */ diff --git a/xbmc/addons/binary-addons/CMakeLists.txt b/xbmc/addons/binary-addons/CMakeLists.txt new file mode 100644 index 0000000..33a72ce --- /dev/null +++ b/xbmc/addons/binary-addons/CMakeLists.txt @@ -0,0 +1,12 @@ +set(SOURCES BinaryAddonManager.cpp + AddonDll.cpp + AddonInstanceHandler.cpp + BinaryAddonBase.cpp) + +set(HEADERS BinaryAddonManager.h + AddonDll.h + AddonInstanceHandler.h + BinaryAddonBase.h + DllAddon.h) + +core_add_library(addons_binary-addons) diff --git a/xbmc/addons/binary-addons/DllAddon.h b/xbmc/addons/binary-addons/DllAddon.h new file mode 100644 index 0000000..c0399f7 --- /dev/null +++ b/xbmc/addons/binary-addons/DllAddon.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#pragma once + +#include "DynamicDll.h" + +class DllAddonInterface +{ +public: + virtual ~DllAddonInterface() = default; + virtual ADDON_STATUS Create(void* cb) = 0; + virtual const char* GetAddonTypeVersion(int type)=0; + virtual const char* GetAddonTypeMinVersion(int type) = 0; +}; + +class DllAddon : public DllDynamic, public DllAddonInterface +{ +public: + DECLARE_DLL_WRAPPER_TEMPLATE(DllAddon) + DEFINE_METHOD1(ADDON_STATUS, Create, (void* p1)) + DEFINE_METHOD1(const char*, GetAddonTypeVersion, (int p1)) + DEFINE_METHOD1(const char*, GetAddonTypeMinVersion, (int p1)) + bool GetAddonTypeMinVersion_available() { return m_GetAddonTypeMinVersion != nullptr; } + BEGIN_METHOD_RESOLVE() + RESOLVE_METHOD_RENAME(ADDON_Create, Create) + RESOLVE_METHOD_RENAME(ADDON_GetTypeVersion, GetAddonTypeVersion) + RESOLVE_METHOD_RENAME_OPTIONAL(ADDON_GetTypeMinVersion, GetAddonTypeMinVersion) + END_METHOD_RESOLVE() +}; |