From 5f280dd0b03821a759fefb23d7f399c6c66ed0e8 Mon Sep 17 00:00:00 2001 From: Anand Kandasamy Date: Fri, 29 May 2020 12:42:02 -0400 Subject: [PATCH 01/56] DELIA-41812: Network Plugin fixes --- Network/NetUtils.cpp | 973 +++++++++++++++++++++++++++++++++- Network/NetUtils.h | 126 ++++- Network/NetworkTraceroute.cpp | 29 +- Network/PingNotifier.cpp | 39 +- 4 files changed, 1119 insertions(+), 48 deletions(-) diff --git a/Network/NetUtils.cpp b/Network/NetUtils.cpp index 0366027b25..cbcca76753 100644 --- a/Network/NetUtils.cpp +++ b/Network/NetUtils.cpp @@ -18,14 +18,29 @@ **/ #include "NetUtils.h" +#include +#include +#include +//#include +#include #include +#include +#include #include "Network.h" //Defines +#define DELETE_ROUTE_CMD "route delete default gw %s 2>&1" //192.168.1.1 +#define RESTART_DHCPC_CMD "/sbin/udhcpc -i %s -p /tmp/udhcpc.%s.pid 2>&1 &" //eth0:0, eth0:0 #define NETUTIL_DEVICE_PROPERTIES_FILE "/etc/device.properties" +#define NETUTIL_PERSIST_DEFAULT_INTERFACE_FILE "/opt/persistent/defaultInterface" +#define ESTB_IPV6_FILE_NAME "/tmp/estb_ipv6" +#define ESTB_IPV4_FILE_NAME "/tmp/estb_ipv4" #define COMMAND_RESULT_FILE "cmdresult" +#define CONNECTED_FLAGS (IFF_UP|IFF_RUNNING) +#define INTERFACE_CONNECTED(flags) (((flags & CONNECTED_FLAGS) == CONNECTED_FLAGS) ? 1 : 0) + namespace WPEFramework { namespace Plugin { @@ -36,43 +51,360 @@ namespace WPEFramework { /* * */ - NetUtils::NetUtils() + NetUtils::NetUtils() : + m_dataProtect(), + m_netlinkProtect() { } NetUtils::~NetUtils() { + _stopMonitor(); } /* * Initialise netutils: - * - load interface descriptions + * - initialise the interface list + * - check (and set) the persistent default interface + * - start the network monitor task */ void NetUtils::InitialiseNetUtils() { - _loadInterfaceDescriptions(); + std::string defaultInterface; + + // Initialise interface list (this is required for setting the default interface) + _initialiseInterfaceList(); + //_displayInterfaceStatus(); + + // If we are persisting the default interface, try to set it now (on boot this is unlikely + // to work as the interfaces will probably not be up yet) + if (NetUtils::getDefaultGatewayPersistent(defaultInterface)) + { + if (!SetDefaultInterface(defaultInterface, true)) + { + LOGINFO("Setting %s as the default interface is pending...", defaultInterface.c_str()); + + // Store the setting to be applied when the interfaces are ready + _setPendingDefaultInterface(defaultInterface); + } + } + + // Start the network monitor task + _startMonitor(); + } + + /* + * Get the MAC address of the interface + */ + bool NetUtils::GetInterfaceMACAddress(const std::string &interfaceDescription, std::string &macAddr) + { + std::lock_guard lock(m_dataProtect); + + for ( auto &info : interfaceList) + { + if (info.m_if_descr == interfaceDescription) + { + macAddr = info.m_if_macaddr; + return true; + } + } + + return false; + } + + /* + * Get the connection state of the interface + */ + bool NetUtils::GetInterfaceConnected(const std::string &interfaceDescription, bool &connected) + { + std::lock_guard lock(m_dataProtect); + + for ( auto &info : interfaceList) + { + if (info.m_if_descr == interfaceDescription) + { + connected = INTERFACE_CONNECTED(info.m_if_flags); + return true; + } + } + + return false; + } + + /* + * Get the name of the interface (e.g. wlan0, wlan0:0, eth0, eth0:0) + * If physical==false then a virtual interface name may be returned if one exists + */ + bool NetUtils::GetInterfaceName(const std::string &interfaceDescription, std::string &name, bool physical) + { + std::lock_guard lock(m_dataProtect); + + for ( auto &info : interfaceList) + { + if (info.m_if_descr == interfaceDescription) + { + name = info.interfaceName(physical); + return true; + } + } + + return false; + } + + /* + * Get the descriptive name for the default interface (e.g. WIFI, ETHERNET etc.) + */ + bool NetUtils::GetDefaultInterfaceDescription(std::string &description) + { + stringList interfaces; + stringList gateways; + stringList descriptions; + + if (!_getDefaultInterfaces(interfaces, gateways, descriptions)) + { + LOGERR("Failed to get default interface information."); + } + else + { + description = descriptions[0]; // assume single default interface + return true; + } + + return false; + } + + /* + * Internal method to request the default route(s) information (from netlink) and match + * it to our return list to get the interface name, description etc. + * If physical=true return the name of the physical interface (eg. eth0) + * else it may be a virtual interface if one exists (eg. eth0:0) + * If async=true then the netlink request will be run in a separate thread + */ + bool NetUtils::_getDefaultInterfaces(stringList &interfaces, stringList &gateways, stringList &descriptions, + bool physical, bool async) + { + // Serialise requests so we only open one netlink request socket at a time + std::lock_guard lock(m_netlinkProtect); + + indexList interfaceIndexList; + bool success = false; + + if (async) + { + // Async indicates to make the netlink call in a thread (this is to do with how the socket + // pid is defined - we can only have one netlink socket per thread/process) + _asyncQueryDefaultInterfaces(interfaceIndexList, gateways, success); + } + else + { + _queryDefaultInterfaces(interfaceIndexList, gateways, success); + } + + if (!success) + { + LOGERR("Failed to send route information request."); + } + else + { + std::lock_guard lock(m_dataProtect); + + for ( auto &index : interfaceIndexList) + { + for ( auto &info : interfaceList) + { + if (info.m_if_index == (int)index) + { + interfaces.push_back(info.interfaceName(physical)); + descriptions.push_back(info.m_if_descr); + LOGINFO("Default route: %s, %s", + info.m_if_descr.c_str(), info.interfaceName().c_str()); + return true; + } + } + } + } + + return false; + } + + /* + * Make a netlink request to get the default interfaces (in a separate thread) + */ + void NetUtils::_asyncQueryDefaultInterfaces(indexList &interfaces, stringList &gateways, bool &success) + { + std::thread asyncTask = std::thread(_queryDefaultInterfaces, + std::ref(interfaces), std::ref(gateways), std::ref(success)); + + if (asyncTask.joinable()) + asyncTask.join(); + } + + /* + * Make a netlink request to get the default interfaces + */ + void NetUtils::_queryDefaultInterfaces(indexList &interfaces, stringList &gateways, bool &success) + { + Netlink netlinkRequest; + + success = false; + + if (!netlinkRequest.connect()) + { + LOGERR("Failed to connect netlink request socket"); + } + else if (NetUtils::isConfiguredIPV6()) + { + if (!netlinkRequest.getDefaultInterfaces(interfaces, gateways, true)) + { + LOGWARN("Failed to get default IPv6 interfaces"); + } + else + { + success = true; + } + } + else if (!netlinkRequest.getDefaultInterfaces(interfaces, gateways, false)) + { + LOGWARN("Failed to get default interfaces"); + } + else + { + success = true; + } + } + + + /* + * Set the default interface + * + * NOTE - The flag pendingRequest is used to indicate that we are trying to set the default + * interface at startup, probably at boot time. In this case we will only proceed if the + * interface is connected and we have a default interface. (As this may be called from the + * monitor task we will perform the netlink request asynchronously in a separate thread) + */ + bool NetUtils::SetDefaultInterface(std::string &interfaceDescription, bool onInit) + { + std::string interfaceName = ""; + bool connected = false; + bool success = false; + + // Check the interface exists and is connected (UP and RUNNING) + if (!GetInterfaceName(interfaceDescription, interfaceName) || + !GetInterfaceConnected(interfaceDescription, connected)) + { + LOGERR("Interface not recognised"); + } + else if (!connected) + { + LOGERR("Interface is not connected"); + } + else + { + stringList names; + stringList gateways; + stringList descriptions; + + // Get a list of current default interfaces (routes) + // We want the name of the virtual interface if it exists + if (!_getDefaultInterfaces(names, gateways, descriptions, false, onInit)) + { + LOGWARN("Could not get current default interface"); + + // If this is on initialisation (we may be waiting for the network connections to + // be set up) then exit if we do not have a default interfaces (yet) + if (onInit) + { + return false; + } + } + + // Kill the current udhcpc processes and delete the default interfaces + for (unsigned i = 0; i < names.size(); i++) + { + if (interfaceDescription == descriptions[i]) + { + LOGINFO("%s is already a default interface", interfaceDescription.c_str()); + success = true; + } + else + { + LOGINFO("Deleting default interface on %s", names[i].c_str()); + _deleteDefaultInterface(names[i], gateways[i]); + } + } + + if (!success) + { + // Start udhcpc on the requested interface + success = _createDefaultInterface(interfaceName); + } + + if (success) + { + // Make sure any pending setting is cleared + _clearPendingDefaultInterface(); + } + } + + return success; } - void NetUtils::_loadInterfaceDescriptions() + /* + * Delete the current default interface (route) + */ + bool NetUtils::_deleteDefaultInterface(std::string &name, std::string &gateway) { - string value; + std::string output = ""; + char command[MAX_COMMAND_LENGTH]; + bool success = true; - if (envGetValue("WIFI_SUPPORT", value) && (value == "true") && envGetValue("WIFI_INTERFACE", value)) - interface_descriptions.insert({value, "WIFI"}); - if (envGetValue("MOCA_SUPPORT", value) && (value == "true") && envGetValue("MOCA_INTERFACE", value)) - interface_descriptions.insert({value, "MOCA"}); - if (envGetValue("ETHERNET_INTERFACE", value)) - interface_descriptions.insert({value, "ETHERNET"}); + // Terminate udhcpc on the interface + snprintf(command, MAX_COMMAND_LENGTH, "cat /tmp/udhcpc.%s.pid 2>&1", name.c_str()); + if (NetUtils::execCmd(command, output) < 0) + { + LOGERR("Failed to get udhcpc pid"); + } + else if (output.length() > 0) //pid of udhcpc + { + snprintf(command, MAX_COMMAND_LENGTH, "kill -9 %s 2>&1", output.c_str()); + NetUtils::execCmd(command, output); + } - for (const auto& e : interface_descriptions) - LOGINFO ("%s %s", e.first.c_str(), e.second.c_str()); + // Delete the default route + if (gateway.length() > 0) + { + snprintf(command, MAX_COMMAND_LENGTH, DELETE_ROUTE_CMD, gateway.c_str()); + if (NetUtils::execCmd(command, output) < 0) + { + LOGERR("Failed to delete default route"); + success = false; + } + } + + return success; } - const std::string& NetUtils::getInterfaceDescription(const std::string interface) + /* + * Set the default interface (route) + */ + bool NetUtils::_createDefaultInterface(std::string &name) { - static std::string empty(""); - auto it = interface_descriptions.find(interface.substr(0, interface.find(':'))); // look up "eth0" (real interface) for "eth0:0" (virtual interface) input also - return (it != interface_descriptions.end()) ? it->second : empty; + std::string output = ""; + char command[MAX_COMMAND_LENGTH]; + + // Request a dhcp lease for the new interface + snprintf(command, MAX_COMMAND_LENGTH, RESTART_DHCPC_CMD, + name.c_str(), name.c_str()); + + if (NetUtils::execCmd(command, output) < 0) + { + LOGERR("Failed to create default route"); + return false; + } + else + { + return true; + } } /* @@ -178,6 +510,186 @@ namespace WPEFramework { return (inet_pton(AF_INET6, address.c_str(), &ipv6address) > 0); } + // Not every character can be used for endpoint + bool NetUtils::_isCharacterIllegal(const int& c) + { + //character must be "-./0-9a-zA-Z" + return (c < 45) || ((c > 57) && (c < 65)) || ((c > 90) && (c < 97)) || (c > 122); + } + + // Check if valid - consist of only allowed characters + bool NetUtils::isValidEndpointURL(const std::string& endpoint) + { + return std::find_if(endpoint.begin(), endpoint.end(), _isCharacterIllegal) == endpoint.end(); + } + + /* + * See if the device is configured to use ipv6 + */ + bool NetUtils::isConfiguredIPV6() + { + struct stat buffer; + return (stat (ESTB_IPV6_FILE_NAME, &buffer) == 0); + } + + /* + * See if the device is configured to use ipv4 + */ + bool NetUtils::isConfiguredIPV4() + { + struct stat buffer; + return (stat (ESTB_IPV4_FILE_NAME, &buffer) == 0); + } + + /* + * Get the CMTS (default) interface name + */ +#ifdef USE_NETLINK + bool NetUtils::getCMTSInterface(std::string &interface) + { + stringList interfaces; + stringList gateways; + stringList descriptions; + + // Get the name and ip address for the default interface + if (!_getDefaultInterfaces(interfaces, gateways, descriptions)) + { + LOGERR("Failed to get default interface information."); + return false; + } + else + { + interface = interfaces[0]; + return true; + } + } +#else + bool NetUtils::getCMTSInterface(std::string &interface) + { + //Try to use DEFAULT_ESTB_INTERFACE but this may not exist on all devices + if (!NetUtils::envGetValue("DEFAULT_ESTB_INTERFACE", interface)) + { + //Query netsrvmgr for the active interface + std::string interfaceDesription = ""; + if (!Network::getInstance()->_getActiveInterface(interfaceDesription)) + { + LOGERR("%s: failed to get active interface", __FUNCTION__); + return false; + } + //... and convert to interface name + else if (!GetInterfaceName(interfaceDesription, interface, true)) //ignore virtual interfaces + { + LOGERR("%s: failed to get active interface name", __FUNCTION__); + return false; + } + } + return true; + } +#endif + + /* + * Get the CMTS gateway from configured information + */ +#ifdef USE_NETLINK + bool NetUtils::getCMTSGateway(std::string &gateway) + { + stringList interfaces; + stringList gateways; + stringList descriptions; + + gateway = ""; + + // Get the name and ip address for the default interface + if (!_getDefaultInterfaces(interfaces, gateways, descriptions)) + { + LOGERR("Failed to get default interface information."); + return false; + } + else + { + gateway = gateways[0]; // assume single default interface + return true; + } + } +#else + bool NetUtils::getCMTSGateway(std::string &gateway) + { + std::string interface = ""; + char cmd [1000] = {0x0}; + + gateway = ""; + + if (getCMTSInterface(interface)) + { + if (isConfiguredIPV6()) + { + snprintf(cmd, sizeof(cmd), "route -A inet6 | grep %s | grep 'UG' | awk '{print $2}'", interface.c_str()); + if (execCmd(cmd, gateway) < 0) + { + LOGERR("%s: failed to get IPv6 gateway address", __FUNCTION__); + } + } + + // If we are not configured for ipv6 or didn't get a gateway address, default to ipv4 and try that + if (gateway.length() == 0) + { + snprintf(cmd, sizeof(cmd), "route -n | grep %s | grep 'UG' | awk '{print $2}'", interface.c_str()); + if (execCmd(cmd, gateway) < 0) + { + LOGERR("%s: failed to get IPv4 gateway address", __FUNCTION__); + } + } + + if (gateway.length() > 0) + { + // We can get multiple lines matching (especially ipv6) so delete all after the first + size_t lfpos = gateway.find('\n'); + if (lfpos != std::string::npos) + { + gateway.erase(lfpos); + } + + LOGINFO("gateway = %s", gateway.c_str()); + } + } + + return (gateway.length() > 0); + } +#endif + + /* + * Set the persistence state of the default interface + */ + bool NetUtils::setDefaultGatewayPersistent(const char *interface) + { + bool result = false; + if (interface) + { + std::ofstream ofs(NETUTIL_PERSIST_DEFAULT_INTERFACE_FILE); + if (ofs.is_open()) + { + ofs << interface; + result = ofs.fail(); + ofs.close(); + } + } + else + { + std::remove(NETUTIL_PERSIST_DEFAULT_INTERFACE_FILE); + result = true; + } + return result; + } + + /* + * Get the persistence state of the default interface + */ + bool NetUtils::getDefaultGatewayPersistent(std::string &interface) + { + return getFile(NETUTIL_PERSIST_DEFAULT_INTERFACE_FILE, interface); + } + + /* * Get the contents of a file * if deleteFile is true, remove the file after it is read @@ -204,6 +716,39 @@ namespace WPEFramework { return result; } + /* + * Given the interface name, check the device file so we try to match what the lower layers + * expect/deliver (i.e stay in sync with existing methods) + */ + bool NetUtils::_envGetInterfaceDescription(const char *name, std::string &description) + { + description.clear(); + + if (envCheckBool("WIFI_SUPPORT")) + { + if (envCheckValue("WIFI_INTERFACE", name)) + { + description = "WIFI"; + return true; + } + } + if (envCheckBool("MOCA_SUPPORT")) + { + if (envCheckValue("MOCA_INTERFACE", name)) + { + description = "MOCA"; + return true; + } + } + if (envCheckValue("ETHERNET_INTERFACE", name)) + { + description = "ETHERNET"; + return true; + } + + return false; + } + /* * Get the value of the given key from the environment (device properties file) */ @@ -233,6 +778,377 @@ namespace WPEFramework { return false; } + bool NetUtils::envCheckValue(const char *key, const char *value) + { + std::string envValue = ""; + if (!envGetValue(key, envValue)) + { + LOGWARN("Could not find property: %s", key); + } + return (envValue == value); + } + + bool NetUtils::envCheckBool(const char *key) + { + std::string envValue = ""; + if (!envGetValue(key, envValue)) + { + LOGWARN("Could not find property: %s", key); + } + return (envValue == "true"); + } + + bool NetUtils::_parseMACAddress(unsigned char *addr, int addlen, std::string &macAddr) + { + for (int i = 0; i < addlen; i++) + { + char buffer[4]; + sprintf(buffer, "%s%02x", i ? ":" : "", addr[i]); + buffer[3] = '\0'; + + macAddr += buffer; + } + + return macAddr.length() > 0; + } + + /* + * Initialise the data that we use to monitor for network changes and provide + * information on interfaces + */ + void NetUtils::_initialiseInterfaceList() + { + std::lock_guard lock(m_netlinkProtect); + struct ifaddrs *pIntfList = NULL; + struct ifaddrs *pIntf = NULL; + std::string description; + + getifaddrs(&pIntfList); + if (pIntfList) + { + // Use AF_PACKET items to determine the physical interfaces + for (pIntf = pIntfList; pIntf != NULL; pIntf = pIntf->ifa_next) + { + if (pIntf->ifa_addr->sa_family == AF_PACKET) + { + struct sockaddr_ll *hw = (struct sockaddr_ll *)pIntf->ifa_addr; + struct iface_info info; + info.m_if_name = pIntf->ifa_name; + info.m_if_index = hw->sll_ifindex; + info.m_if_flags = pIntf->ifa_flags; + + // We do not need set the address and flags details here + info.m_if_addr = ""; + info.m_if_addrv6 = ""; + + info.m_if_vname = ""; + info.m_if_vaddr = ""; + info.m_if_vaddrv6 = ""; + + if ( hw->sll_hatype != ARPHRD_ETHER) + { + // Ignore non-ethernet ineterfaces (lo, sit0 etc.) + LOGINFO("Not ethernet interface: %s", pIntf->ifa_name); + } + // Look for the interface name in the environment file + else if (!_envGetInterfaceDescription(pIntf->ifa_name, info.m_if_descr)) + { + // We expect the interfcaes to be defined in the device.properties file + LOGERR("No description for interface %s", pIntf->ifa_name); + } + else if (!_parseMACAddress(hw->sll_addr, hw->sll_halen, info.m_if_macaddr)) + { + LOGERR("Could not parse mac address for interface %s", pIntf->ifa_name); + } + else + { + LOGINFO("Storing interface %s, %s, %s", info.m_if_name.c_str(), info.m_if_descr.c_str(), info.m_if_macaddr.c_str()); + interfaceList.push_back(info); + } + } + } + + // Use Update the interface list with addresses (and virtual interfaces) + for ( auto &info : interfaceList) + { + for (pIntf = pIntfList; pIntf != NULL; pIntf = pIntf->ifa_next) + { + if ((pIntf->ifa_addr->sa_family == AF_INET) || + (pIntf->ifa_addr->sa_family == AF_INET6)) + { + if ((0 == strncmp(info.m_if_name.c_str(), pIntf->ifa_name, info.m_if_name.length())) && + (0 != strcmp(info.m_if_name.c_str(), pIntf->ifa_name))) + { + info.m_if_vname = pIntf->ifa_name; + } + } + } + } + + freeifaddrs(pIntfList); + } + } + + /* + * Background monitor methods + */ + + /* + * Start a background network monitor task + */ + void NetUtils::_startMonitor() + { + m_fdNlMonitorMsgPipe[0] = -1; + m_fdNlMonitorMsgPipe[1] = -1; + + /* Create a pipe for posting a shutdown request */ + if (0 == pipe(m_fdNlMonitorMsgPipe)) + { + m_netlinkMonitorThread = std::thread(_runMonitor, this); + } + else + { + LOGERR("ERROR: Failed to create netlink monitor abort pipe."); + } + } + + /* + * Stop the background network monitor task + */ + void NetUtils::_stopMonitor() + { + if (m_fdNlMonitorMsgPipe[1] >= 0) + { + if (m_netlinkMonitorThread.joinable()) + { + /* Post a shutdown request then wait for thread to terminate */ + if (write(m_fdNlMonitorMsgPipe[1], &m_fdNlMonitorMsgPipe[1], sizeof(int)) != sizeof(int)) + { + LOGERR("ERROR: Failed to write shutdown request. Netlink monitor task will not terminate."); + } + LOGINFO("Joining netlink connection monitor thread."); + m_netlinkMonitorThread.join(); + LOGINFO("Netlink connection monitor thread terminated."); + } + + close(m_fdNlMonitorMsgPipe[0]); + close(m_fdNlMonitorMsgPipe[1]); + m_fdNlMonitorMsgPipe[0] = -1; + m_fdNlMonitorMsgPipe[1] = -1; + } + } + + /* + * Netlink monitor task + * This is currently used just to detect that a change has occurred, the details will + * be identified in _updateInterfaceStatus() using getifaddrs + */ + void NetUtils::_runMonitor(NetUtils *utils) + { + char msgBuffer[NETLINK_MESSAGE_BUFFER_SIZE]; + int msgLength; + struct pollfd pfds[2]; + Netlink netlink; + + LOGWARN("%s : Netlink monitor RUNNING...", __FUNCTION__); + + // Connect a netlink socket to the groups we want to monitor + if (!netlink.connect(RTMGRP_LINK|RTMGRP_IPV4_IFADDR|RTMGRP_IPV6_IFADDR)) + { + LOGERR("Could not connect to netlionk socket."); + return; + } + + // first fd is the message pipe + pfds[0].fd = utils->m_fdNlMonitorMsgPipe[0]; + pfds[0].events = POLLIN; + pfds[0].revents = 0; + + pfds[1].fd = netlink.sockfd(); + pfds[1].events = POLLIN; + pfds[1].revents = 0; + + utils->_lock(); + utils->_resetInterfaceList(); + + while (1) + { + utils->_updateInterfaceStatus(); + //utils->_displayInterfaceStatus(); + + // Check to see if we are waiting to set the default interface (on startup) + utils->_checkPendingDefaultInterface(); + utils->_unlock(); + + // wait for an event + if (poll(pfds, 2, -1) <= 0) + { + LOGWARN("Netlink socket poll returned no events"); + continue; + } + + utils->_lock(); + + if (pfds[0].revents) // Check for an event on our shutdown pipe + { + LOGINFO("Shutting down netlink monitor"); + break; + } + else if ((msgLength = netlink.read(msgBuffer, NETLINK_MESSAGE_BUFFER_SIZE)) < static_cast(sizeof(struct nlmsghdr)) ) + { + // Checking netlink messages. 0=no msg & peer shutdown, -1=error, see errno + LOGERR("Invalid netlink message (retval %d)", msgLength); + std::this_thread::sleep_for (std::chrono::milliseconds(100)); + } + else + { + // At least one event relating to ip address or connection status was received. Log what types. + netlink.displayMessages(msgBuffer, msgLength); + } + } + + utils->_unlock(); + + LOGWARN("%s : ENDED.", __FUNCTION__); + } + + /* + * Notifications from monitor thread + */ + + void NetUtils::_interfaceFlagsUpdated(struct iface_info &info) + { + JsonObject params; + + params["interface"] = info.m_if_descr; + params["status"] = INTERFACE_CONNECTED(info.m_if_flags) ? "CONNECTED" : "DISCONNECTED"; + Network::_instance->_asyncNotifyConnection(params); + } + + void NetUtils::_interfaceAddressUpdated(struct iface_info &info, std::string &addr) + { + JsonObject params; + + params["interface"] = info.m_if_descr; + params["ip6Address"] = info.ipv6Addr(); + params["ip4Address"] = info.ipv4Addr(); + params["status"] = (addr.length() == 0) ? "LOST" : "ACQUIRED"; + Network::_instance->_asyncNotifyIPAddr(params); + } + + /* + * Reset any information needed to trigger a notification message on update + */ + void NetUtils::_resetInterfaceList() + { + for ( auto &info : interfaceList) + { + info.m_if_flags = IFF_RUNNING; + info.m_if_addr = ""; + info.m_if_addrv6 = ""; + } + } + + /* + * Update the interfaces and notify of changes to connection state or ip address + */ + void NetUtils::_updateInterfaceStatus() + { + struct ifaddrs * pIntfList=NULL; + struct ifaddrs * pIntf=NULL; + + getifaddrs(&pIntfList); + + /* For each interface in our list, see if the address or connection status has changed */ + for ( auto &info : interfaceList) + { + int if_flags = 0; + char ipAddr[INET6_ADDRSTRLEN]; + std::string ipv4Addr = info.ipv4Addr(); + std::string ipv6Addr = info.ipv6Addr(); + if_flags = info.m_if_flags; + + info.m_if_addr = ""; + info.m_if_addrv6 = ""; + info.m_if_vname = ""; + info.m_if_vaddr = ""; + info.m_if_vaddrv6 = ""; + + for (pIntf = pIntfList; pIntf != NULL; pIntf = pIntf->ifa_next) + { + // Match the address to the interface via the interface name + // Note - we will assume the format of a virtual interface name is just 'name:0' (e.g. 'eth0:0') + // so we will match the interface name to the first part of the address interface name + if (0 == strncmp(info.m_if_name.c_str(), pIntf->ifa_name, info.m_if_name.length())) + { + info.m_if_flags = pIntf->ifa_flags; + + if ((pIntf->ifa_addr->sa_family == AF_INET) || + (pIntf->ifa_addr->sa_family == AF_INET6)) + { + inet_ntop(pIntf->ifa_addr->sa_family, &((struct sockaddr_in *)pIntf->ifa_addr)->sin_addr, ipAddr, INET6_ADDRSTRLEN); + + // If this is a virtual interface then the names will not match over the full length + if (0 != strcmp(info.m_if_name.c_str(), pIntf->ifa_name)) + { + info.m_if_vname = pIntf->ifa_name; + + if (pIntf->ifa_addr->sa_family == AF_INET) + { + info.m_if_vaddr = ipAddr; + } + else if (pIntf->ifa_addr->sa_family == AF_INET6) + { + info.m_if_vaddrv6 = ipAddr; + } + } + else + { + if (pIntf->ifa_addr->sa_family == AF_INET) + { + info.m_if_addr = ipAddr; + } + else if (pIntf->ifa_addr->sa_family == AF_INET6) + { + info.m_if_addrv6 = ipAddr; + } + } + } + } + } + + /* + * See if anything has changed + */ + if (INTERFACE_CONNECTED(info.m_if_flags) != INTERFACE_CONNECTED(if_flags)) + { + _interfaceFlagsUpdated(info); + } + + if (ipv4Addr != info.ipv4Addr()) + { + _interfaceAddressUpdated(info, info.ipv4Addr()); + } + else if (ipv6Addr != info.ipv6Addr()) + { + _interfaceAddressUpdated(info, info.ipv6Addr()); + } + } + } + + /* + * Check to see if we have stored a default interface to set when we are able + */ + void NetUtils::_checkPendingDefaultInterface() + { + if (!NetUtils::isConfiguredIPV6() && //TBD support for default interface on IPv6 + !m_pendingDefaultInterface.empty()) + { + LOGINFO("Try setting %s as the default interface...", m_pendingDefaultInterface.c_str()); + SetDefaultInterface(m_pendingDefaultInterface, true); + } + } + void NetUtils::getTmpFilename(const char *in, std::string &out) { std::lock_guard lock(m_counterProtect); @@ -241,5 +1157,28 @@ namespace WPEFramework { out += std::to_string(m_counter++); } + /* + * Debug info + */ + void NetUtils::_displayInterfaceStatus() + { + for ( auto &info : interfaceList) + { + LOGINFO ("<%.40s>, %.40s, UP = %s, RUNNING = %s\n", + info.m_if_descr.c_str(), + info.m_if_macaddr.c_str(), + info.m_if_flags & IFF_UP ? "YES" : "NO", + info.m_if_flags & IFF_RUNNING ? "YES" : "NO"); + LOGINFO ("> <%.40s>, IP v4 Address = %.40s, IP v6 Address = %.40s.\n", + info.m_if_name.c_str(), + info.m_if_addr.c_str(), + info.m_if_addrv6.c_str()); + if (!info.m_if_name.empty()) + LOGINFO ("> <%.40s>, IP v4 Address = %.40s, IP v6 Address = %.40s.\n", + info.m_if_vname.c_str(), + info.m_if_vaddr.c_str(), + info.m_if_vaddrv6.c_str()); + } + } } // namespace Plugin } // namespace WPEFramework diff --git a/Network/NetUtils.h b/Network/NetUtils.h index f683012b89..e6684ef406 100644 --- a/Network/NetUtils.h +++ b/Network/NetUtils.h @@ -21,7 +21,8 @@ #include #include -#include +#include +#include #include #include "utils.h" #include "NetUtilsNetlink.h" @@ -33,30 +34,147 @@ namespace WPEFramework { namespace Plugin { class Network; + class iface_info + { + public: + iface_info() {;} + virtual ~iface_info() {;} + + int m_if_index; // interface index + std::string m_if_descr; // interface descriptive name + std::string m_if_macaddr; // mac address + int m_if_flags; // SIOCGIFFLAGS + + std::string m_if_name; // interface name + std::string m_if_addr; // ip address v4 + std::string m_if_addrv6; // ip address v6 + + std::string m_if_vname; // virtual interface name + std::string m_if_vaddr; // virtual ip address v4 + std::string m_if_vaddrv6; // virtual ip address v6 + + std::string &ipv6Addr() + { + if (m_if_vname.length() && m_if_vaddrv6.length()) + { + return m_if_vaddrv6; + } + return m_if_addrv6; + } + std::string &ipv4Addr() + { + if (m_if_vname.length() && m_if_vaddr.length()) + { + return m_if_vaddr; + } + return m_if_addr; + } + std::string &interfaceName(bool physical = false) + { + if (physical || (m_if_vname.length() == 0)) + { + return m_if_name; // return the name of the physical interface + } + else + { + return m_if_vname; // return the name of the virtual interface + } + } + }; + + class NetUtils { private: - void _loadInterfaceDescriptions(); + std::vector interfaceList; + + void _startMonitor(); + void _stopMonitor(); + static void _runMonitor(NetUtils *utils); + + void _resetInterfaceList(); + void _updateInterfaceStatus(); + void _interfaceFlagsUpdated(struct iface_info &info); + void _interfaceAddressUpdated(struct iface_info &info, std::string &addr); + void _checkPendingDefaultInterface(); + + void _displayInterfaceStatus(); + + bool _envGetInterfaceDescription(const char *name, std::string &description); + bool _getDefaultInterfaces(stringList &interfaces, stringList &gateways, stringList &descriptions, + bool physical = true, bool async = false); + + void _asyncQueryDefaultInterfaces(indexList &interfaces, stringList &gateways, bool &success); + static void _queryDefaultInterfaces(indexList &interfaces, stringList &gateways, bool &success); + + void _initialiseInterfaceList(); + bool _parseMACAddress(unsigned char *addr, int addlen, std::string &macAddr); + + void _lock() + { + m_dataProtect.lock(); + } + + void _unlock() + { + m_dataProtect.unlock(); + } + + void _setPendingDefaultInterface(std::string &interface) + { + std::lock_guard lock(m_dataProtect); + m_pendingDefaultInterface = interface; + } + void _clearPendingDefaultInterface() + { + std::lock_guard lock(m_dataProtect); + m_pendingDefaultInterface.empty(); + } public: NetUtils(); virtual ~NetUtils(); void InitialiseNetUtils(); - const std::string& getInterfaceDescription(const std::string name); + bool GetInterfaceMACAddress(const std::string &interfaceDescription, std::string &macAddr); + bool GetInterfaceConnected(const std::string &interfaceDescription, bool &connected); + bool GetInterfaceName(const std::string &interfaceDescription, std::string &name, bool physical = false); + + bool GetDefaultInterfaceDescription(std::string &description); + bool SetDefaultInterface(std::string &interfaceDescription, bool onInit = false); + + bool getCMTSInterface(std::string &interface); + bool getCMTSGateway(std::string &gateway); static bool isIPV4(const std::string &address); static bool isIPV6(const std::string &address); + static bool isConfiguredIPV6(); + static bool isConfiguredIPV4(); static int execCmd(const char *command, std::string &output, bool *result = NULL, const char *outputfile = NULL); + static bool setDefaultGatewayPersistent(const char *interface); + static bool getDefaultGatewayPersistent(std::string &interface); static bool getFile(const char *filepath, std::string &contents, bool deleteFile = false); static bool envGetValue(const char *key, std::string &value); + static bool envCheckValue(const char *key, const char *value); + static bool envCheckBool(const char *key); static void getTmpFilename(const char *in, std::string &out); + static bool isValidEndpointURL(const std::string& endpoint); private: + std::thread m_netlinkMonitorThread; + std::recursive_mutex m_dataProtect; + std::mutex m_netlinkProtect; + int m_fdNlMonitorMsgPipe[2]; + bool m_monitorRunning; + std::string m_pendingDefaultInterface; + static unsigned int m_counter; static std::mutex m_counterProtect; - std::map interface_descriptions; + + static bool _createDefaultInterface(std::string &name); + static bool _deleteDefaultInterface(std::string &name, std::string &gateway); + static bool _isCharacterIllegal(const int& c); }; } // namespace Plugin } // namespace WPEFramework diff --git a/Network/NetworkTraceroute.cpp b/Network/NetworkTraceroute.cpp index 41de148cba..064e106a7e 100644 --- a/Network/NetworkTraceroute.cpp +++ b/Network/NetworkTraceroute.cpp @@ -38,13 +38,12 @@ namespace WPEFramework { bool Network::_doTraceNamedEndpoint(std::string &endpointName, int packets, JsonObject &response) { std::string endpoint = ""; - string interface; if (endpointName != "CMTS") // currently we only support CMTS { response["error"] = "Unsupported named endpoint"; } - else if (_getDefaultInterface(interface, endpoint)) + else if (m_netUtils.getCMTSGateway(endpoint)) { return _doTrace(endpoint, packets, response); } @@ -61,7 +60,6 @@ namespace WPEFramework { std::string output = ""; std::string error = ""; std::string interface = ""; - std::string gateway; int wait = DEFAULT_WAIT; int maxHops = DEFAULT_MAX_HOPS; int packetLen = DEFAULT_PACKET_LENGTH; @@ -77,11 +75,13 @@ namespace WPEFramework { { error = "Invalid endpoint"; } - else if (!NetUtils::isIPV4(endpoint) && !NetUtils::isIPV6(endpoint)) + else if (!NetUtils::isIPV4(endpoint) && + !NetUtils::isIPV6(endpoint) && + !NetUtils::isValidEndpointURL(endpoint)) { error = "Invalid endpoint"; } - else if (!_getDefaultInterface(interface, gateway)) + else if (!m_netUtils.getCMTSInterface(interface)) { error = "Could not get default interface"; } @@ -119,8 +119,24 @@ namespace WPEFramework { if (error.empty()) { + // We return the entire output of the trace command but since this contains newlines it is not valid as + // a json value so we will parse the output into an array of strings, one element for each line. + JsonArray list; + if (!output.empty()) + { + std::string::size_type last = 0; + std::string::size_type next = output.find('\n'); + while (next != std::string::npos) + { + list.Add(output.substr(last, next - last)); + last = next + 1; + next = output.find('\n', last); + } + list.Add(output.substr(last)); + } + response["target"] = endpoint; - response["results"] = output; + response["results"] = list; response["error"] = ""; return true; } @@ -134,3 +150,4 @@ namespace WPEFramework { } } // namespace Plugin } // namespace WPEFramework + diff --git a/Network/PingNotifier.cpp b/Network/PingNotifier.cpp index bbe75c8b63..432b083564 100644 --- a/Network/PingNotifier.cpp +++ b/Network/PingNotifier.cpp @@ -19,24 +19,11 @@ #include "Network.h" + namespace WPEFramework { namespace Plugin { - // Not every character can be used for endpoint - bool is_character_illegal(const int& c) - { - //character must be "-./0-9a-zA-Z" - return (c < 45) || ((c > 58) && (c < 97)) || (c >= 122); - } - - // Check if valid - consist of only allowed characters - bool is_endpoint_valid(const std::string& endpoint) - { - //return std::find_if(endpoint.begin(), endpoint.end(), is_character_illegal) == endpoint.end(); - return (NetUtils::isIPV4(endpoint) || NetUtils::isIPV6(endpoint)); - } - /** * @ingroup SERVMGR_PING_API */ @@ -45,22 +32,33 @@ namespace WPEFramework LOGINFO("PingService calling ping"); JsonObject pingResult; std::string interface = ""; - std::string gateway; bool result = false; std::string outputFile; FILE *fp = NULL; pingResult["target"] = endPoint; - if(!is_endpoint_valid(endPoint)) + if(NetUtils::isIPV6(endPoint)) + { + LOGINFO("%s: Endpoint '%s' is ipv6", __FUNCTION__,endPoint.c_str()); + } + else if(NetUtils::isIPV4(endPoint)) + { + LOGINFO("%s: Endpoint '%s' is ipv4", __FUNCTION__,endPoint.c_str()); + } + else if(NetUtils::isValidEndpointURL(endPoint)) + { + LOGINFO("%s: Endpoint '%s' is url", __FUNCTION__,endPoint.c_str()); + } + else { - LOGERR("%s: Endpoint is not valid string", __FUNCTION__); + LOGERR("%s: Endpoint '%s' is not valid", __FUNCTION__,endPoint.c_str()); pingResult["success"] = false; pingResult["error"] = "invalid input for endpoint: " + endPoint; return pingResult; } - if (!_getDefaultInterface(interface, gateway)) + if (!m_netUtils.getCMTSInterface(interface)) { LOGERR("%s: Could not get default interface", __FUNCTION__); pingResult["success"] = false; @@ -197,9 +195,8 @@ namespace WPEFramework if (endpointName == "CMTS") { - std::string gateway = ""; - string interface; - if (_getDefaultInterface(interface, gateway)) + std::string gateway; + if (m_netUtils.getCMTSGateway(gateway)) { returnResult = _doPing(gateway, packets); } From 1d549121db48704b8728640ef4e36bf43179218d Mon Sep 17 00:00:00 2001 From: Deekshit Devadas Date: Fri, 5 Jun 2020 17:39:08 +0100 Subject: [PATCH 02/56] DELIA-43653 : Add Dolby MS12 APIs to displaysettings plugin Reason for change: 1) Added dolby APIs mentioned in the ticket 2) Added Volume gain and level adjustment APIs 3) Modified existing dolby APIs to take audioPort arg, default is HDMI0 Test Procedure: Verify using curl commands Risks: None Signed-off-by: Deekshit Devadas --- DisplaySettings/DisplaySettings.cpp | 533 ++++++++++++++++++++++++++-- DisplaySettings/DisplaySettings.h | 16 + 2 files changed, 512 insertions(+), 37 deletions(-) diff --git a/DisplaySettings/DisplaySettings.cpp b/DisplaySettings/DisplaySettings.cpp index 4c7d33e0db..7c030e3edd 100644 --- a/DisplaySettings/DisplaySettings.cpp +++ b/DisplaySettings/DisplaySettings.cpp @@ -150,6 +150,22 @@ namespace WPEFramework { registerMethod("getDialogEnhancement", &DisplaySettings::getDialogEnhancement, this); registerMethod("setIntelligentEqualizerMode", &DisplaySettings::setIntelligentEqualizerMode, this); registerMethod("getIntelligentEqualizerMode", &DisplaySettings::getIntelligentEqualizerMode, this); + registerMethod("getVolumeLeveller", &DisplaySettings::getVolumeLeveller, this); + registerMethod("getBassEnhancer", &DisplaySettings::getBassEnhancer, this); + registerMethod("isSurroundDecoderEnabled", &DisplaySettings::isSurroundDecoderEnabled, this); + registerMethod("getDRCMode", &DisplaySettings::getDRCMode, this); + registerMethod("getSurroundVirtualizer", &DisplaySettings::getSurroundVirtualizer, this); + registerMethod("setVolumeLeveller", &DisplaySettings::setVolumeLeveller, this); + registerMethod("setBassEnhancer", &DisplaySettings::setBassEnhancer, this); + registerMethod("enableSurroundDecoder", &DisplaySettings::enableSurroundDecoder, this); + registerMethod("setSurroundVirtualizer", &DisplaySettings::setSurroundVirtualizer, this); + registerMethod("setMISteering", &DisplaySettings::setMISteering, this); + registerMethod("setGain", &DisplaySettings::setGain, this); + registerMethod("getGain", &DisplaySettings::getGain, this); + registerMethod("setLevel", &DisplaySettings::setLevel, this); + registerMethod("getLevel", &DisplaySettings::getLevel, this); + registerMethod("setDRCMode", &DisplaySettings::setDRCMode, this); + registerMethod("getMISteering", &DisplaySettings::getMISteering, this); registerMethod("getAudioDelay", &DisplaySettings::getAudioDelay, this); registerMethod("setAudioDelay", &DisplaySettings::setAudioDelay, this); @@ -1171,23 +1187,24 @@ namespace WPEFramework { returnIfParamNotFound(parameters, "compresionLevel"); string sCompresionLevel = parameters["compresionLevel"].String(); - int compresionLevel = 0; + int compresionLevel = 0; try { compresionLevel = stoi(sCompresionLevel); }catch (const device::Exception& err) { - LOG_DEVICE_EXCEPTION1(sCompresionLevel); - returnResponse(false); + LOG_DEVICE_EXCEPTION1(sCompresionLevel); + returnResponse(false); } bool success = true; + string audioPort = parameters.HasLabel("audioPort") ? parameters["audioPort"].String() : "HDMI0"; try { - device::AudioOutputPort aPort = device::Host::getInstance().getAudioOutputPort("HDMI0"); + device::AudioOutputPort aPort = device::Host::getInstance().getAudioOutputPort(audioPort); aPort.setCompression (compresionLevel); } catch (const device::Exception& err) { - LOG_DEVICE_EXCEPTION2(string("HDMI0"), sCompresionLevel); + LOG_DEVICE_EXCEPTION2(audioPort, sCompresionLevel); success = false; } returnResponse(success); @@ -1196,20 +1213,22 @@ namespace WPEFramework { uint32_t DisplaySettings::getMS12AudioCompression (const JsonObject& parameters, JsonObject& response) { //sample servicemanager response: LOGINFOMETHOD(); - bool success = true; - int compressionlevel = 0; + bool success = true; + int compressionlevel = 0; + + string audioPort = parameters.HasLabel("audioPort") ? parameters["audioPort"].String() : "HDMI0"; try { - device::AudioOutputPort aPort = device::Host::getInstance().getAudioOutputPort("HDMI0"); + device::AudioOutputPort aPort = device::Host::getInstance().getAudioOutputPort(audioPort); compressionlevel = aPort.getCompression(); response["compressionlevel"] = compressionlevel; - response["enable"] = (compressionlevel ? true : false); + response["enable"] = (compressionlevel ? true : false); } catch(const device::Exception& err) { - LOG_DEVICE_EXCEPTION1(string("HDMI0")); + LOG_DEVICE_EXCEPTION1(audioPort); response["compressionlevel"] = 0; - response["enable"] = false; + response["enable"] = false; success = false; } returnResponse(success); @@ -1220,7 +1239,7 @@ namespace WPEFramework { LOGINFOMETHOD(); returnIfParamNotFound(parameters, "dolbyVolumeMode"); - string sDolbyVolumeMode = parameters["dolbyVolumeMode"].String(); + string sDolbyVolumeMode = parameters["dolbyVolumeMode"].String(); bool dolbyVolumeMode = false; int iDolbyVolumeMode = 0; @@ -1230,7 +1249,7 @@ namespace WPEFramework { } catch (const device::Exception& err) { - LOG_DEVICE_EXCEPTION1(sDolbyVolumeMode); + LOG_DEVICE_EXCEPTION1(sDolbyVolumeMode); returnResponse(false); } if (0 == iDolbyVolumeMode) { @@ -1239,16 +1258,16 @@ namespace WPEFramework { dolbyVolumeMode = true; } - bool success = true; + string audioPort = parameters.HasLabel("audioPort") ? parameters["audioPort"].String() : "HDMI0"; try { - device::AudioOutputPort aPort = device::Host::getInstance().getAudioOutputPort("HDMI0"); + device::AudioOutputPort aPort = device::Host::getInstance().getAudioOutputPort(audioPort); aPort.setDolbyVolumeMode (dolbyVolumeMode); } catch (const device::Exception& err) { - LOG_DEVICE_EXCEPTION2(string("HDMI0"), sDolbyVolumeMode); + LOG_DEVICE_EXCEPTION2(audioPort, sDolbyVolumeMode); success = false; } returnResponse(success); @@ -1257,15 +1276,17 @@ namespace WPEFramework { uint32_t DisplaySettings::getDolbyVolumeMode (const JsonObject& parameters, JsonObject& response) { //sample servicemanager response: LOGINFOMETHOD(); - bool success = true; + bool success = true; + + string audioPort = parameters.HasLabel("audioPort") ? parameters["audioPort"].String() : "HDMI0"; try { - device::AudioOutputPort aPort = device::Host::getInstance().getAudioOutputPort("HDMI0"); + device::AudioOutputPort aPort = device::Host::getInstance().getAudioOutputPort(audioPort); response["dolbyVolumeMode"] = aPort.getDolbyVolumeMode(); } catch(const device::Exception& err) { - LOG_DEVICE_EXCEPTION1(string("HDMI0")); + LOG_DEVICE_EXCEPTION1(audioPort); success = false; } returnResponse(success); @@ -1277,23 +1298,24 @@ namespace WPEFramework { returnIfParamNotFound(parameters, "enhancerlevel"); string sEnhancerlevel = parameters["enhancerlevel"].String(); - int enhancerlevel = 0; + int enhancerlevel = 0; try { enhancerlevel = stoi(sEnhancerlevel); }catch (const device::Exception& err) { - LOG_DEVICE_EXCEPTION1(sEnhancerlevel); - returnResponse(false); + LOG_DEVICE_EXCEPTION1(sEnhancerlevel); + returnResponse(false); } bool success = true; + string audioPort = parameters.HasLabel("audioPort") ? parameters["audioPort"].String() : "HDMI0"; try { - device::AudioOutputPort aPort = device::Host::getInstance().getAudioOutputPort("HDMI0"); + device::AudioOutputPort aPort = device::Host::getInstance().getAudioOutputPort(audioPort); aPort.setDialogEnhancement (enhancerlevel); } catch (const device::Exception& err) { - LOG_DEVICE_EXCEPTION2(string("HDMI0"), sEnhancerlevel); + LOG_DEVICE_EXCEPTION2(audioPort, sEnhancerlevel); success = false; } returnResponse(success); @@ -1302,18 +1324,20 @@ namespace WPEFramework { uint32_t DisplaySettings::getDialogEnhancement (const JsonObject& parameters, JsonObject& response) { //sample servicemanager response: LOGINFOMETHOD(); - bool success = true; - int enhancerlevel = 0; + bool success = true; + int enhancerlevel = 0; + + string audioPort = parameters.HasLabel("audioPort") ? parameters["audioPort"].String() : "HDMI0"; try { - device::AudioOutputPort aPort = device::Host::getInstance().getAudioOutputPort("HDMI0"); + device::AudioOutputPort aPort = device::Host::getInstance().getAudioOutputPort(audioPort); enhancerlevel = aPort.getDialogEnhancement(); response["enable"] = (enhancerlevel ? true : false); response["enhancerlevel"] = enhancerlevel; } catch(const device::Exception& err) { - LOG_DEVICE_EXCEPTION1(string("HDMI0")); + LOG_DEVICE_EXCEPTION1(audioPort); response["enable"] = false; response["enhancerlevel"] = 0; success = false; @@ -1327,23 +1351,24 @@ namespace WPEFramework { returnIfParamNotFound(parameters, "intelligentEqualizerMode"); string sIntelligentEqualizerMode = parameters["intelligentEqualizerMode"].String(); - int intelligentEqualizerMode = 0; + int intelligentEqualizerMode = 0; try { intelligentEqualizerMode = stoi(sIntelligentEqualizerMode); }catch (const device::Exception& err) { - LOG_DEVICE_EXCEPTION1(sIntelligentEqualizerMode); - returnResponse(false); + LOG_DEVICE_EXCEPTION1(sIntelligentEqualizerMode); + returnResponse(false); } bool success = true; + string audioPort = parameters.HasLabel("audioPort") ? parameters["audioPort"].String() : "HDMI0"; try { - device::AudioOutputPort aPort = device::Host::getInstance().getAudioOutputPort("HDMI0"); + device::AudioOutputPort aPort = device::Host::getInstance().getAudioOutputPort(audioPort); aPort.setIntelligentEqualizerMode (intelligentEqualizerMode); } catch (const device::Exception& err) { - LOG_DEVICE_EXCEPTION2(string("HDMI0"), sIntelligentEqualizerMode); + LOG_DEVICE_EXCEPTION2(audioPort, sIntelligentEqualizerMode); success = false; } returnResponse(success); @@ -1352,25 +1377,459 @@ namespace WPEFramework { uint32_t DisplaySettings::getIntelligentEqualizerMode (const JsonObject& parameters, JsonObject& response) { //sample servicemanager response: LOGINFOMETHOD(); - bool success = true; - int intelligentEqualizerMode = 0; + bool success = true; + int intelligentEqualizerMode = 0; + + string audioPort = parameters.HasLabel("audioPort") ? parameters["audioPort"].String() : "HDMI0"; try { - device::AudioOutputPort aPort = device::Host::getInstance().getAudioOutputPort("HDMI0"); + device::AudioOutputPort aPort = device::Host::getInstance().getAudioOutputPort(audioPort); intelligentEqualizerMode = aPort.getIntelligentEqualizerMode (); response["enable"] = (intelligentEqualizerMode ? true : false); response["mode"] = intelligentEqualizerMode; } catch(const device::Exception& err) { - LOG_DEVICE_EXCEPTION1(string("HDMI0")); + LOG_DEVICE_EXCEPTION1(audioPort); response["enable"] = false; response["mode"] = 0; + } + returnResponse(success); + } + + uint32_t DisplaySettings::getVolumeLeveller(const JsonObject& parameters, JsonObject& response) + { + LOGINFOMETHOD(); + + bool success = true; + string audioPort = parameters.HasLabel("audioPort") ? parameters["audioPort"].String() : "HDMI0"; + int level = 0; + + try + { + device::AudioOutputPort aPort = device::Host::getInstance().getAudioOutputPort(audioPort); + if (aPort.isConnected()) + { + level= aPort.getVolumeLeveller(); + response["enable"] = (level ? true : false); + response["level"] = level; + } + } + catch (const device::Exception& err) + { + LOG_DEVICE_EXCEPTION1(audioPort); + success = false; + response["enable"] = false; + response["mode"] = 0; + } + returnResponse(success); + } + + + uint32_t DisplaySettings::getBassEnhancer(const JsonObject& parameters, JsonObject& response) + { + LOGINFOMETHOD(); + bool success = true; + string audioPort = parameters.HasLabel("audioPort") ? parameters["audioPort"].String() : "HDMI0"; + bool enable = false; + try + { + device::AudioOutputPort aPort = device::Host::getInstance().getAudioOutputPort(audioPort); + if (aPort.isConnected()) + { + enable = aPort.getBassEnhancer(); + response["bassEnhancerEnable"] = enable; + } + else + { + LOGERR("aport is not connected!"); + success = false; + } + } + catch (const device::Exception& err) + { + LOG_DEVICE_EXCEPTION1(audioPort); + success = false; + } + returnResponse(success); + } + + uint32_t DisplaySettings::isSurroundDecoderEnabled(const JsonObject& parameters, JsonObject& response) + { + LOGINFOMETHOD(); + bool success = true; + bool surroundDecoderEnable = false; + string audioPort = parameters.HasLabel("audioPort") ? parameters["audioPort"].String() : "HDMI0"; + + try + { + device::AudioOutputPort aPort = device::Host::getInstance().getAudioOutputPort(audioPort); + if (aPort.isConnected()) + { + surroundDecoderEnable = aPort.isSurroundDecoderEnabled(); + response["surroundDecoderEnable"] = surroundDecoderEnable; + } + else + { + LOGERR("aport is not connected!"); + success = false; + } + } + catch (const device::Exception& err) + { + LOG_DEVICE_EXCEPTION1(audioPort); + success = false; + } + returnResponse(success); + } + + uint32_t DisplaySettings::getGain (const JsonObject& parameters, JsonObject& response) + { + LOGINFOMETHOD(); + bool success = true; + float gain = 0; + + string audioPort = parameters.HasLabel("audioPort") ? parameters["audioPort"].String() : "HDMI0"; + try + { + device::AudioOutputPort aPort = device::Host::getInstance().getAudioOutputPort(audioPort); + gain = aPort.getGain(); + response["gain"] = to_string(gain); + } + catch(const device::Exception& err) + { + LOG_DEVICE_EXCEPTION1(audioPort); success = false; } returnResponse(success); } + uint32_t DisplaySettings::getLevel (const JsonObject& parameters, JsonObject& response) + { + LOGINFOMETHOD(); + bool success = true; + float level = 0; + + string audioPort = parameters.HasLabel("audioPort") ? parameters["audioPort"].String() : "HDMI0"; + try + { + device::AudioOutputPort aPort = device::Host::getInstance().getAudioOutputPort(audioPort); + level = aPort.getLevel(); + response["level"] = to_string(level); + } + catch(const device::Exception& err) + { + LOG_DEVICE_EXCEPTION1(audioPort); + success = false; + } + returnResponse(success); + } + + uint32_t DisplaySettings::getDRCMode(const JsonObject& parameters, JsonObject& response) + { + LOGINFOMETHOD(); + bool success = true; + int mode = 0; + string audioPort = parameters.HasLabel("audioPort") ? parameters["audioPort"].String() : "HDMI0"; + try + { + device::AudioOutputPort aPort = device::Host::getInstance().getAudioOutputPort(audioPort); + if (aPort.isConnected()) + { + mode = aPort.getDRCMode(); + response["DRCMode"] = mode ? "RF" : "line" ; + } + else + { + LOGERR("aport is not connected!"); + success = false; + } + } + catch (const device::Exception& err) + { + LOG_DEVICE_EXCEPTION1(audioPort); + success = false; + } + returnResponse(success); + } + + uint32_t DisplaySettings::getSurroundVirtualizer(const JsonObject& parameters, JsonObject& response) + { + LOGINFOMETHOD(); + bool success = true; + string audioPort = parameters.HasLabel("audioPort") ? parameters["audioPort"].String() : "HDMI0"; + int boost = 0; + + try + { + device::AudioOutputPort aPort = device::Host::getInstance().getAudioOutputPort(audioPort); + if (aPort.isConnected()) + { + boost = aPort.getSurroundVirtualizer(); + response["enable"] = boost ? true : false ; + response["boost"] = boost; + } + else + { + LOGERR("aport is not connected!"); + success = false; + } + } + catch (const device::Exception& err) + { + LOG_DEVICE_EXCEPTION1(audioPort); + success = false; + } + returnResponse(success); + } + + uint32_t DisplaySettings::getMISteering(const JsonObject& parameters, JsonObject& response) + { + LOGINFOMETHOD(); + bool success = true; + bool enable = false; + string audioPort = parameters.HasLabel("audioPort") ? parameters["audioPort"].String() : "HDMI0"; + try + { + device::AudioOutputPort aPort = device::Host::getInstance().getAudioOutputPort(audioPort); + if (aPort.isConnected()) + { + enable = aPort.getMISteering(); + response["MISteeringEnable"] = enable; + } + else + { + LOGERR("aport is not connected!"); + success = false; + } + } + catch (const device::Exception& err) + { + LOG_DEVICE_EXCEPTION1(audioPort); + success = false; + } + returnResponse(success); + } + + uint32_t DisplaySettings::setVolumeLeveller(const JsonObject& parameters, JsonObject& response) + { + LOGINFOMETHOD(); + returnIfParamNotFound(parameters, "level"); + string sVolumeLeveller = parameters["level"].String(); + int VolumeLeveller = 0; + try { + VolumeLeveller = stoi(sVolumeLeveller); + }catch (const device::Exception& err) { + LOG_DEVICE_EXCEPTION1(sVolumeLeveller); + returnResponse(false); + } + bool success = true; + string audioPort = parameters.HasLabel("audioPort") ? parameters["audioPort"].String() : "HDMI0"; + try + { + device::AudioOutputPort aPort = device::Host::getInstance().getAudioOutputPort(audioPort); + aPort.setVolumeLeveller(VolumeLeveller); + } + catch (const device::Exception& err) + { + LOG_DEVICE_EXCEPTION2(audioPort, sVolumeLeveller); + success = false; + } + returnResponse(success); + } + + uint32_t DisplaySettings::enableSurroundDecoder(const JsonObject& parameters, JsonObject& response) + { + LOGINFOMETHOD(); + returnIfParamNotFound(parameters, "surroundDecoderEnable"); + string sEnableSurroundDecoder = parameters["surroundDecoderEnable"].String(); + bool enableSurroundDecoder = false; + try { + enableSurroundDecoder= parameters["surroundDecoderEnable"].Boolean(); + }catch (const device::Exception& err) { + LOG_DEVICE_EXCEPTION1(sEnableSurroundDecoder); + returnResponse(false); + } + bool success = true; + string audioPort = parameters.HasLabel("audioPort") ? parameters["audioPort"].String() : "HDMI0"; + try + { + device::AudioOutputPort aPort = device::Host::getInstance().getAudioOutputPort(audioPort); + aPort.enableSurroundDecoder(enableSurroundDecoder); + } + catch (const device::Exception& err) + { + LOG_DEVICE_EXCEPTION2(audioPort, sEnableSurroundDecoder); + success = false; + } + returnResponse(success); + } + + uint32_t DisplaySettings::setBassEnhancer(const JsonObject& parameters, JsonObject& response) + { + LOGINFOMETHOD(); + returnIfParamNotFound(parameters, "bassEnhancerEnable"); + string sBassEnhancer = parameters["bassEnhancerEnable"].String(); + bool bassEnhancer = false; + try { + bassEnhancer = parameters["bassEnhancerEnable"].Boolean(); + }catch (const device::Exception& err) { + LOG_DEVICE_EXCEPTION1(sBassEnhancer); + returnResponse(false); + } + bool success = true; + string audioPort = parameters.HasLabel("audioPort") ? parameters["audioPort"].String() : "HDMI0"; + try + { + device::AudioOutputPort aPort = device::Host::getInstance().getAudioOutputPort(audioPort); + aPort.setBassEnhancer(bassEnhancer); + } + catch (const device::Exception& err) + { + LOG_DEVICE_EXCEPTION2(audioPort, sBassEnhancer); + success = false; + } + returnResponse(success); + } + + uint32_t DisplaySettings::setSurroundVirtualizer(const JsonObject& parameters, JsonObject& response) + { + LOGINFOMETHOD(); + returnIfParamNotFound(parameters, "boost"); + string sSurroundVirtualizer = parameters["boost"].String(); + int surroundVirtualizer = 0; + + try { + surroundVirtualizer = stoi(sSurroundVirtualizer); + }catch (const device::Exception& err) { + LOG_DEVICE_EXCEPTION1(sSurroundVirtualizer); + returnResponse(false); + } + bool success = true; + string audioPort = parameters.HasLabel("audioPort") ? parameters["audioPort"].String() : "HDMI0"; + try + { + device::AudioOutputPort aPort = device::Host::getInstance().getAudioOutputPort(audioPort); + aPort.setSurroundVirtualizer(surroundVirtualizer); + } + catch (const device::Exception& err) + { + LOG_DEVICE_EXCEPTION2(audioPort, sSurroundVirtualizer); + success = false; + } + returnResponse(success); + } + + uint32_t DisplaySettings::setMISteering(const JsonObject& parameters, JsonObject& response) + { + LOGINFOMETHOD(); + returnIfParamNotFound(parameters, "MISteeringEnable"); + string sMISteering = parameters["MISteeringEbnable"].String(); + bool MISteering = false; + try { + MISteering = parameters["MISteeringEnable"].Boolean(); + }catch (const device::Exception& err) { + LOG_DEVICE_EXCEPTION1(sMISteering); + returnResponse(false); + } + bool success = true; + string audioPort = parameters.HasLabel("audioPort") ? parameters["audioPort"].String() : "HDMI0"; + try + { + device::AudioOutputPort aPort = device::Host::getInstance().getAudioOutputPort(audioPort); + aPort.setMISteering(MISteering); + } + catch (const device::Exception& err) + { + LOG_DEVICE_EXCEPTION2(audioPort, sMISteering); + success = false; + } + returnResponse(success) + } + + uint32_t DisplaySettings::setGain(const JsonObject& parameters, JsonObject& response) + { + LOGINFOMETHOD(); + returnIfParamNotFound(parameters, "gain"); + string sGain = parameters["gain"].String(); + float newGain = 0; + try { + newGain = stof(sGain); + }catch (const device::Exception& err) { + LOG_DEVICE_EXCEPTION1(sGain); + returnResponse(false); + } + bool success = true; + string audioPort = parameters.HasLabel("audioPort") ? parameters["audioPort"].String() : "HDMI0"; + try + { + device::AudioOutputPort aPort = device::Host::getInstance().getAudioOutputPort(audioPort); + aPort.setGain(newGain); + success= true; + } + catch (const device::Exception& err) + { + LOG_DEVICE_EXCEPTION2(audioPort, sGain); + success = false; + } + returnResponse(success); + } + + uint32_t DisplaySettings::setLevel(const JsonObject& parameters, JsonObject& response) + { + LOGINFOMETHOD(); + returnIfParamNotFound(parameters, "level"); + string sLevel = parameters["level"].String(); + float level = 0; + try { + level = stof(sLevel); + }catch (const device::Exception& err) { + LOG_DEVICE_EXCEPTION1(sLevel); + returnResponse(false); + } + bool success = true; + string audioPort = parameters.HasLabel("audioPort") ? parameters["audioPort"].String() : "HDMI0"; + try + { + device::AudioOutputPort aPort = device::Host::getInstance().getAudioOutputPort(audioPort); + aPort.setLevel(level); + success= true; + } + catch (const device::Exception& err) + { + LOG_DEVICE_EXCEPTION2(audioPort, sLevel); + success = false; + } + returnResponse(success); + } + + uint32_t DisplaySettings::setDRCMode(const JsonObject& parameters, JsonObject& response) + { + LOGINFOMETHOD(); + returnIfParamNotFound(parameters, "DRCMode"); + string sDRCMode = parameters["DRCMode"].String(); + int DRCMode = 0; + try { + DRCMode = stoi(sDRCMode); + }catch (const device::Exception& err) { + LOG_DEVICE_EXCEPTION1(sDRCMode); + returnResponse(false); + } + bool success = true; + string audioPort = parameters.HasLabel("audioPort") ? parameters["audioPort"].String() : "HDMI0"; + try + { + device::AudioOutputPort aPort = device::Host::getInstance().getAudioOutputPort(audioPort); + aPort.setDRCMode(DRCMode); + } + catch (const device::Exception& err) + { + LOG_DEVICE_EXCEPTION2(audioPort, sDRCMode); + success = false; + } + returnResponse(success); + } + uint32_t DisplaySettings::getAudioDelay (const JsonObject& parameters, JsonObject& response) { //sample servicemanager response: LOGINFOMETHOD(); diff --git a/DisplaySettings/DisplaySettings.h b/DisplaySettings/DisplaySettings.h index 7b6fa679b8..1debb56904 100644 --- a/DisplaySettings/DisplaySettings.h +++ b/DisplaySettings/DisplaySettings.h @@ -92,6 +92,22 @@ namespace WPEFramework { uint32_t getTVHDRCapabilities(const JsonObject& parameters, JsonObject& response); uint32_t getDefaultResolution(const JsonObject& parameters, JsonObject& response); uint32_t setScartParameter(const JsonObject& parameters, JsonObject& response); + uint32_t getVolumeLeveller(const JsonObject& parameters, JsonObject& response); + uint32_t getBassEnhancer(const JsonObject& parameters, JsonObject& response); + uint32_t isSurroundDecoderEnabled(const JsonObject& parameters, JsonObject& response); + uint32_t getDRCMode(const JsonObject& parameters, JsonObject& response); + uint32_t getSurroundVirtualizer(const JsonObject& parameters, JsonObject& response); + uint32_t getMISteering(const JsonObject& parameters, JsonObject& response); + uint32_t setVolumeLeveller(const JsonObject& parameters, JsonObject& response); + uint32_t setBassEnhancer(const JsonObject& parameters, JsonObject& response); + uint32_t enableSurroundDecoder(const JsonObject& parameters, JsonObject& response); + uint32_t setSurroundVirtualizer(const JsonObject& parameters, JsonObject& response); + uint32_t setMISteering(const JsonObject& parameters, JsonObject& response); + uint32_t setGain(const JsonObject& parameters, JsonObject& response); + uint32_t getGain(const JsonObject& parameters, JsonObject& response); + uint32_t setLevel(const JsonObject& parameters, JsonObject& response); + uint32_t getLevel(const JsonObject& parameters, JsonObject& response); + uint32_t setDRCMode(const JsonObject& parameters, JsonObject& response); //End methods //Begin events From 6e851d8f4849210d5a9b05b5e285c779a167cfb1 Mon Sep 17 00:00:00 2001 From: ddevad <66484031+ddevad@users.noreply.github.com> Date: Fri, 5 Jun 2020 17:46:25 +0100 Subject: [PATCH 03/56] Update DisplaySettings.cpp --- DisplaySettings/DisplaySettings.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/DisplaySettings/DisplaySettings.cpp b/DisplaySettings/DisplaySettings.cpp index 7c030e3edd..d70a37af25 100644 --- a/DisplaySettings/DisplaySettings.cpp +++ b/DisplaySettings/DisplaySettings.cpp @@ -1420,7 +1420,6 @@ namespace WPEFramework { LOG_DEVICE_EXCEPTION1(audioPort); success = false; response["enable"] = false; - response["mode"] = 0; } returnResponse(success); } From d109fd48a8287f2426aef81e00500c6a5a6fc90d Mon Sep 17 00:00:00 2001 From: apatel859 Date: Mon, 8 Jun 2020 16:19:45 -0400 Subject: [PATCH 04/56] RDK-28793: HDMI-CEC OTP for xione Reason for change:Implement HdmiCec_2 plugin Test Procedure: Refer JIRA. Risks: medium Signed-off-by: apatel859 --- CMakeLists.txt | 4 + HdmiCec_2/CMakeLists.txt | 45 ++ HdmiCec_2/HdmiCec_2.config | 3 + HdmiCec_2/HdmiCec_2.cpp | 1084 ++++++++++++++++++++++++++++++++++++ HdmiCec_2/HdmiCec_2.h | 158 ++++++ HdmiCec_2/Module.cpp | 22 + HdmiCec_2/Module.h | 29 + HdmiCec_2/README.md | 9 + 8 files changed, 1354 insertions(+) create mode 100644 HdmiCec_2/CMakeLists.txt create mode 100644 HdmiCec_2/HdmiCec_2.config create mode 100644 HdmiCec_2/HdmiCec_2.cpp create mode 100644 HdmiCec_2/HdmiCec_2.h create mode 100644 HdmiCec_2/Module.cpp create mode 100644 HdmiCec_2/Module.h create mode 100644 HdmiCec_2/README.md diff --git a/CMakeLists.txt b/CMakeLists.txt index 063c5e4954..42f37d3105 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -151,6 +151,10 @@ endif() add_subdirectory(HdmiCec) endif() + if(HAS_API_HDMI_CEC_2) + add_subdirectory(HdmiCec_2) + endif() + if(ENABLE_LOCATION_SYNC) add_subdirectory(LocationSync) endif() diff --git a/HdmiCec_2/CMakeLists.txt b/HdmiCec_2/CMakeLists.txt new file mode 100644 index 0000000000..577f3dc1ae --- /dev/null +++ b/HdmiCec_2/CMakeLists.txt @@ -0,0 +1,45 @@ +# If not stated otherwise in this file or this component's license file the +# following copyright and licenses apply: +# +# Copyright 2020 RDK Management +# +# 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. +set(PLUGIN_NAME HdmiCec_2) +set(MODULE_NAME ${NAMESPACE}${PLUGIN_NAME}) + +find_package(${NAMESPACE}Plugins REQUIRED) + +add_library(${MODULE_NAME} SHARED + HdmiCec_2.cpp + Module.cpp + ../helpers/utils.cpp) + +set_target_properties(${MODULE_NAME} PROPERTIES + CXX_STANDARD 11 + CXX_STANDARD_REQUIRED YES) + +find_package(DS) +find_package(IARMBus) +find_package(CEC) + +target_include_directories(${MODULE_NAME} PRIVATE ${IARMBUS_INCLUDE_DIRS} ../helpers) +target_include_directories(${MODULE_NAME} PRIVATE ${CEC_INCLUDE_DIRS}) +target_include_directories(${MODULE_NAME} PRIVATE ${DS_INCLUDE_DIRS}) + +target_link_libraries(${MODULE_NAME} PUBLIC ${NAMESPACE}Plugins::${NAMESPACE}Plugins ${IARMBUS_LIBRARIES} ${CEC_LIBRARIES} ${DS_LIBRARIES} ) + + +install(TARGETS ${MODULE_NAME} + DESTINATION lib/${STORAGE_DIRECTORY}/plugins) + +write_config(${PLUGIN_NAME}) diff --git a/HdmiCec_2/HdmiCec_2.config b/HdmiCec_2/HdmiCec_2.config new file mode 100644 index 0000000000..35c0d9fd27 --- /dev/null +++ b/HdmiCec_2/HdmiCec_2.config @@ -0,0 +1,3 @@ +set (autostart false) +set (preconditions Platform) +set (callsign "org.rdk.HdmiCec_2") diff --git a/HdmiCec_2/HdmiCec_2.cpp b/HdmiCec_2/HdmiCec_2.cpp new file mode 100644 index 0000000000..002954e7cd --- /dev/null +++ b/HdmiCec_2/HdmiCec_2.cpp @@ -0,0 +1,1084 @@ +/** +* If not stated otherwise in this file or this component's LICENSE +* file the following copyright and licenses apply: +* +* Copyright 2019 RDK Management +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +**/ + +#include "HdmiCec_2.h" + + +#include "ccec/Connection.hpp" +#include "ccec/CECFrame.hpp" +#include "ccec/MessageEncoder.hpp" +#include "host.hpp" +#include "ccec/host/RDK.hpp" + + +#include "ccec/drivers/iarmbus/CecIARMBusMgr.h" + +#include "pwrMgr.h" +#include "dsMgr.h" +#include "dsDisplay.h" +#include "videoOutputPort.hpp" +#include "manager.hpp" +#include "websocket/URL.h" + +#include "utils.h" + +#define HDMICEC2_METHOD_SET_ENABLED "setEnabled" +#define HDMICEC2_METHOD_GET_ENABLED "getEnabled" +#define HDMICEC2_METHOD_OTP_SET_ENABLED "setOTPEnabled" +#define HDMICEC2_METHOD_OTP_GET_ENABLED "getOTPEnabled" +#define HDMICEC2_METHOD_SET_OSD_NAME "setOSDName" +#define HDMICEC2_METHOD_GET_OSD_NAME "getOSDName" +#define HDMICEC2_METHOD_SET_VENDOR_ID "setVendorId" +#define HDMICEC2_METHOD_GET_VENDOR_ID "getVendorId" +#define HDMICEC2_METHOD_PERFORM_OTP_ACTION "performOTPAction" + +#define HDMICEC_EVENT_ON_DEVICES_CHANGED "onDevicesChanged" +#define HDMICEC_EVENT_ON_HDMI_HOT_PLUG "onHdmiHotPlug" +#define DEV_TYPE_TUNER 1 +#define HDMI_HOT_PLUG_EVENT_CONNECTED 0 + +#define CEC_SETTING_ENABLED_FILE "/opt/persistent/ds/cecData_2.json" +#define CEC_SETTING_ENABLED "cecEnabled" +#define CEC_SETTING_OTP_ENABLED "cecOTPEnabled" +#define CEC_SETTING_OSD_NAME "cecOSDName" +#define CEC_SETTING_VENDOR_ID "cecVendorId" + +static vector defaultVendorId = {0x00,0x19,0xFB}; +static VendorID appVendorId = {defaultVendorId.at(0),defaultVendorId.at(1),defaultVendorId.at(2)}; +static VendorID lgVendorId = {0x00,0xE0,0x91}; +static PhysicalAddress physical_addr = {0x0F,0x0F,0x0F,0x0F}; +static LogicalAddress logicalAddress = 0xF; +static OSDName osdName = "TV Box"; +static int32_t powerState = 1; +static PowerStatus tvPowerState = 1; +static bool isDeviceActiveSource = true; +static bool isLGTvConnected = false; + +namespace WPEFramework +{ + namespace Plugin + { + SERVICE_REGISTRATION(HdmiCec_2, 1, 0); + + HdmiCec_2* HdmiCec_2::_instance = nullptr; + static int libcecInitStatus = 0; + +//=========================================== HdmiCec_2FrameListener ========================================= + void HdmiCec_2FrameListener::notify(const CECFrame &in) const { + const uint8_t *buf = NULL; + char strBuffer[512] = {0}; + size_t len = 0; + + in.getBuffer(&buf, &len); + for (int i = 0; i < len; i++) { + sprintf(strBuffer + (i*3) , "%02X ",(uint8_t) *(buf + i)); + } + LOGINFO(" >>>>> Received CEC Frame: :%s \n",strBuffer); + + MessageDecoder(processor).decode(in); + } + +//=========================================== HdmiCec_2Processor ========================================= + void HdmiCec_2Processor::process (const ActiveSource &msg, const Header &header) + { + printHeader(header); + LOGINFO("Command: ActiveSource %s : %s : %s \n",GetOpName(msg.opCode()),msg.physicalAddress.name().c_str(),msg.physicalAddress.toString().c_str()); + if(msg.physicalAddress.toString() == physical_addr.toString()) + isDeviceActiveSource = true; + else + isDeviceActiveSource = false; + LOGINFO("ActiveSource isDeviceActiveSource status :%d \n", isDeviceActiveSource); + } + void HdmiCec_2Processor::process (const InActiveSource &msg, const Header &header) + { + printHeader(header); + LOGINFO("Command: InActiveSource %s : %s : %s \n",GetOpName(msg.opCode()),msg.physicalAddress.name().c_str(),msg.physicalAddress.toString().c_str()); + } + void HdmiCec_2Processor::process (const ImageViewOn &msg, const Header &header) + { + printHeader(header); + LOGINFO("Command: ImageViewOn \n"); + } + void HdmiCec_2Processor::process (const TextViewOn &msg, const Header &header) + { + printHeader(header); + LOGINFO("Command: TextViewOn\n"); + } + void HdmiCec_2Processor::process (const RequestActiveSource &msg, const Header &header) + { + printHeader(header); + LOGINFO("Command: RequestActiveSource\n"); + if(isDeviceActiveSource) + { + LOGINFO("sending ActiveSource\n"); + try + { + conn.sendTo(LogicalAddress::BROADCAST, MessageEncoder().encode(ActiveSource(physical_addr))); + } + catch(...) + { + LOGWARN("Exception while sending ActiveSource"); + } + } + } + void HdmiCec_2Processor::process (const Standby &msg, const Header &header) + { + printHeader(header); + if(header.from.toInt() == LogicalAddress::TV) + { + tvPowerState = 1; + LOGINFO("Command: Standby tvPowerState :%s \n",(tvPowerState.toInt())?"OFF":"ON"); + } + } + void HdmiCec_2Processor::process (const GetCECVersion &msg, const Header &header) + { + printHeader(header); + LOGINFO("Command: GetCECVersion sending CECVersion response \n"); + try + { + conn.sendTo(header.from, MessageEncoder().encode(CECVersion(Version::V_1_4))); + } + catch(...) + { + LOGWARN("Exception while sending CECVersion "); + } + } + void HdmiCec_2Processor::process (const CECVersion &msg, const Header &header) + { + printHeader(header); + LOGINFO("Command: CECVersion Version : %s \n",msg.version.toString().c_str()); + } + void HdmiCec_2Processor::process (const SetMenuLanguage &msg, const Header &header) + { + printHeader(header); + LOGINFO("Command: SetMenuLanguage Language : %s \n",msg.language.toString().c_str()); + } + void HdmiCec_2Processor::process (const GiveOSDName &msg, const Header &header) + { + printHeader(header); + LOGINFO("Command: GiveOSDName sending SetOSDName : %s\n",osdName.toString().c_str()); + try + { + conn.sendTo(header.from, MessageEncoder().encode(SetOSDName(osdName))); + } + catch(...) + { + LOGWARN("Exception while sending SetOSDName"); + } + } + void HdmiCec_2Processor::process (const GivePhysicalAddress &msg, const Header &header) + { + LOGINFO("Command: GivePhysicalAddress\n"); + if (!(header.from == LogicalAddress(LogicalAddress::BROADCAST))) + { + try + { + LOGINFO(" sending ReportPhysicalAddress response physical_addr :%s logicalAddress :%x \n",physical_addr.toString().c_str(), logicalAddress.toInt()); + conn.sendTo(LogicalAddress(LogicalAddress::BROADCAST), MessageEncoder().encode(ReportPhysicalAddress(physical_addr,logicalAddress.toInt()))); + } + catch(...) + { + LOGWARN("Exception while sending ReportPhysicalAddress "); + } + } + } + void HdmiCec_2Processor::process (const GiveDeviceVendorID &msg, const Header &header) + { + printHeader(header); + try + { + LOGINFO("Command: GiveDeviceVendorID sending VendorID response :%s\n",(isLGTvConnected)?lgVendorId.toString().c_str():appVendorId.toString().c_str()); + if(isLGTvConnected) + conn.sendTo(LogicalAddress(LogicalAddress::BROADCAST), MessageEncoder().encode(DeviceVendorID(lgVendorId))); + else + conn.sendTo(LogicalAddress(LogicalAddress::BROADCAST), MessageEncoder().encode(DeviceVendorID(appVendorId))); + } + catch(...) + { + LOGWARN("Exception while sending DeviceVendorID"); + } + + } + void HdmiCec_2Processor::process (const SetOSDString &msg, const Header &header) + { + printHeader(header); + LOGINFO("Command: SetOSDString OSDString : %s\n",msg.osdString.toString().c_str()); + } + void HdmiCec_2Processor::process (const SetOSDName &msg, const Header &header) + { + printHeader(header); + LOGINFO("Command: SetOSDName OSDName : %s\n",msg.osdName.toString().c_str()); + } + void HdmiCec_2Processor::process (const RoutingChange &msg, const Header &header) + { + printHeader(header); + LOGINFO("Command: RoutingChange From : %s To: %s \n",msg.from.toString().c_str(),msg.to.toString().c_str()); + if(msg.to.toString() == physical_addr.toString()) + isDeviceActiveSource = true; + else + isDeviceActiveSource = false; + LOGINFO("physical_addr : %s isDeviceActiveSource :%d \n",physical_addr.toString().c_str(),isDeviceActiveSource); + } + void HdmiCec_2Processor::process (const RoutingInformation &msg, const Header &header) + { + printHeader(header); + LOGINFO("Command: RoutingInformation Routing Information to Sink : %s\n",msg.toSink.toString().c_str()); + if(msg.toSink.toString() == physical_addr.toString()) + isDeviceActiveSource = true; + else + isDeviceActiveSource = false; + LOGINFO("physical_addr : %s isDeviceActiveSource :%d \n",physical_addr.toString().c_str(),isDeviceActiveSource); + } + void HdmiCec_2Processor::process (const SetStreamPath &msg, const Header &header) + { + printHeader(header); + LOGINFO("Command: SetStreamPath Set Stream Path to Sink : %s\n",msg.toSink.toString().c_str()); + if(msg.toSink.toString() == physical_addr.toString()) + isDeviceActiveSource = true; + else + isDeviceActiveSource = false; + LOGINFO("physical_addr : %s isDeviceActiveSource :%d \n",physical_addr.toString().c_str(),isDeviceActiveSource); + + } + void HdmiCec_2Processor::process (const GetMenuLanguage &msg, const Header &header) + { + printHeader(header); + LOGINFO("Command: GetMenuLanguage\n"); + } + void HdmiCec_2Processor::process (const ReportPhysicalAddress &msg, const Header &header) + { + printHeader(header); + LOGINFO("Command: ReportPhysicalAddress\n"); + } + void HdmiCec_2Processor::process (const DeviceVendorID &msg, const Header &header) + { + printHeader(header); + LOGINFO("Command: DeviceVendorID VendorID : %s\n",msg.vendorId.toString().c_str()); + } + void HdmiCec_2Processor::process (const GiveDevicePowerStatus &msg, const Header &header) + { + printHeader(header); + LOGINFO("Command: GiveDevicePowerStatus sending powerState :%d \n",powerState); + try + { + conn.sendTo(header.from, MessageEncoder().encode(ReportPowerStatus(PowerStatus(powerState)))); + } + catch(...) + { + LOGWARN("Exception while sending ReportPowerStatus"); + } + } + void HdmiCec_2Processor::process (const ReportPowerStatus &msg, const Header &header) + { + printHeader(header); + if ((header.from == LogicalAddress(LogicalAddress::TV))) + tvPowerState = msg.status; + LOGINFO("Command: ReportPowerStatus TV Power Status from:%s status : %s \n",header.from.toString().c_str(),msg.status.toString().c_str()); + } + void HdmiCec_2Processor::process (const FeatureAbort &msg, const Header &header) + { + printHeader(header); + LOGINFO("Command: FeatureAbort\n"); + } + void HdmiCec_2Processor::process (const Abort &msg, const Header &header) + { + printHeader(header); + LOGINFO("Command: Abort\n"); + } + void HdmiCec_2Processor::process (const Polling &msg, const Header &header) { + printHeader(header); + LOGINFO("Command: Polling\n"); + } + + +//=========================================== HdmiCec_2 ========================================= + + HdmiCec_2::HdmiCec_2() + : AbstractPlugin() + { + LOGWARN("Initlaizing CEC_2"); + HdmiCec_2::_instance = this; + + InitializeIARM(); + + registerMethod(HDMICEC2_METHOD_SET_ENABLED, &HdmiCec_2::setEnabledWrapper, this); + registerMethod(HDMICEC2_METHOD_GET_ENABLED, &HdmiCec_2::getEnabledWrapper, this); + registerMethod(HDMICEC2_METHOD_OTP_SET_ENABLED, &HdmiCec_2::setOTPEnabledWrapper, this); + registerMethod(HDMICEC2_METHOD_OTP_GET_ENABLED, &HdmiCec_2::getOTPEnabledWrapper, this); + registerMethod(HDMICEC2_METHOD_SET_OSD_NAME, &HdmiCec_2::setOSDNameWrapper, this); + registerMethod(HDMICEC2_METHOD_GET_OSD_NAME, &HdmiCec_2::getOSDNameWrapper, this); + registerMethod(HDMICEC2_METHOD_SET_VENDOR_ID, &HdmiCec_2::setVendorIdWrapper, this); + registerMethod(HDMICEC2_METHOD_GET_VENDOR_ID, &HdmiCec_2::getVendorIdWrapper, this); + registerMethod(HDMICEC2_METHOD_PERFORM_OTP_ACTION, &HdmiCec_2::performOTPActionWrapper, this); + + logicalAddressDeviceType = "None"; + logicalAddress = 0xFF; + + // load persistence setting + loadSettings(); + + try + { + //TODO(MROLLINS) this is probably per process so we either need to be running in our own process or be carefull no other plugin is calling it + device::Manager::Initialize(); + device::VideoOutputPort vPort = device::Host::getInstance().getVideoOutputPort("HDMI0"); + if (vPort.isDisplayConnected()) + { + vector edidVec; + vPort.getDisplay().getEDIDBytes(edidVec); + //Set LG vendor id if connected with LG TV + if(edidVec.at(8) == 0x1E && edidVec.at(9) == 0x6D) + { + isLGTvConnected = true; + } + LOGINFO("manufacturer byte from edid :%x: %x isLGTvConnected :%d",edidVec.at(8),edidVec.at(9),isLGTvConnected); + } + } + catch(...) + { + LOGWARN("Exception in getting edid info .\r\n"); + } + + // get power state: + IARM_Bus_PWRMgr_GetPowerState_Param_t param; + int err = IARM_Bus_Call(IARM_BUS_PWRMGR_NAME, + IARM_BUS_PWRMGR_API_GetPowerState, + (void *)¶m, + sizeof(param)); + if(err == IARM_RESULT_SUCCESS) + { + powerState = (param.curState == IARM_BUS_PWRMGR_POWERSTATE_ON)?0:1 ; + LOGINFO("Current state is IARM: (%d) powerState :%d \n",param.curState,powerState); + } + + if (cecSettingEnabled) + { + try + { + CECEnable(); + } + catch(...) + { + LOGWARN("Exception while enabling CEC settings .\r\n"); + } + } + } + + HdmiCec_2::~HdmiCec_2() + { + LOGINFO(); + HdmiCec_2::_instance = nullptr; + DeinitializeIARM(); + } + + const void HdmiCec_2::InitializeIARM() + { + LOGINFO(); + + if (Utils::IARM::init()) + { + IARM_Result_t res; + IARM_CHECK( IARM_Bus_RegisterEventHandler(IARM_BUS_CECMGR_NAME, IARM_BUS_CECMGR_EVENT_DAEMON_INITIALIZED,cecMgrEventHandler) ); + IARM_CHECK( IARM_Bus_RegisterEventHandler(IARM_BUS_CECMGR_NAME, IARM_BUS_CECMGR_EVENT_STATUS_UPDATED,cecMgrEventHandler) ); + IARM_CHECK( IARM_Bus_RegisterEventHandler(IARM_BUS_DSMGR_NAME,IARM_BUS_DSMGR_EVENT_HDMI_HOTPLUG, dsHdmiEventHandler) ); + IARM_CHECK( IARM_Bus_RegisterEventHandler(IARM_BUS_PWRMGR_NAME,IARM_BUS_PWRMGR_EVENT_MODECHANGED, pwrMgrModeChangeEventHandler) ); + } + } + + void HdmiCec_2::DeinitializeIARM() + { + LOGINFO(); + + if (Utils::IARM::isConnected()) + { + IARM_Result_t res; + IARM_CHECK( IARM_Bus_UnRegisterEventHandler(IARM_BUS_CECMGR_NAME, IARM_BUS_CECMGR_EVENT_DAEMON_INITIALIZED) ); + IARM_CHECK( IARM_Bus_UnRegisterEventHandler(IARM_BUS_CECMGR_NAME, IARM_BUS_CECMGR_EVENT_STATUS_UPDATED) ); + IARM_CHECK( IARM_Bus_UnRegisterEventHandler(IARM_BUS_DSMGR_NAME,IARM_BUS_DSMGR_EVENT_HDMI_HOTPLUG) ); + IARM_CHECK( IARM_Bus_UnRegisterEventHandler(IARM_BUS_PWRMGR_NAME,IARM_BUS_PWRMGR_EVENT_MODECHANGED) ); + } + } + + void HdmiCec_2::cecMgrEventHandler(const char *owner, IARM_EventId_t eventId, void *data, size_t len) + { + LOGINFO(); + + if(!HdmiCec_2::_instance) + return; + + if( !strcmp(owner, IARM_BUS_CECMGR_NAME)) + { + switch (eventId) + { + case IARM_BUS_CECMGR_EVENT_DAEMON_INITIALIZED: + { + HdmiCec_2::_instance->onCECDaemonInit(); + } + break; + case IARM_BUS_CECMGR_EVENT_STATUS_UPDATED: + { + IARM_Bus_CECMgr_Status_Updated_Param_t *evtData = new IARM_Bus_CECMgr_Status_Updated_Param_t; + if(evtData) + { + memcpy(evtData,data,sizeof(IARM_Bus_CECMgr_Status_Updated_Param_t)); + HdmiCec_2::_instance->cecStatusUpdated(evtData); + } + } + break; + default: + /*Do nothing*/ + break; + } + } + } + + void HdmiCec_2::dsHdmiEventHandler(const char *owner, IARM_EventId_t eventId, void *data, size_t len) + { + LOGINFO(); + + if(!HdmiCec_2::_instance) + return; + + if (IARM_BUS_DSMGR_EVENT_HDMI_HOTPLUG == eventId) + { + IARM_Bus_DSMgr_EventData_t *eventData = (IARM_Bus_DSMgr_EventData_t *)data; + int hdmi_hotplug_event = eventData->data.hdmi_hpd.event; + LOGINFO("Received IARM_BUS_DSMGR_EVENT_HDMI_HOTPLUG event data:%d \r\n", hdmi_hotplug_event); + HdmiCec_2::_instance->onHdmiHotPlug(hdmi_hotplug_event); + } + } + + void HdmiCec_2::pwrMgrModeChangeEventHandler(const char *owner, IARM_EventId_t eventId, void *data, size_t len) + { + LOGINFO(); + + if(!HdmiCec_2::_instance) + return; + + if (strcmp(owner, IARM_BUS_PWRMGR_NAME) == 0) { + if (eventId == IARM_BUS_PWRMGR_EVENT_MODECHANGED ) { + IARM_Bus_PWRMgr_EventData_t *param = (IARM_Bus_PWRMgr_EventData_t *)data; + LOGINFO("Event IARM_BUS_PWRMGR_EVENT_MODECHANGED: State Changed %d -- > %d\r", + param->data.state.curState, param->data.state.newState); + if(param->data.state.newState == IARM_BUS_PWRMGR_POWERSTATE_ON) + { + powerState = 0; + _instance->performOTPAction(); + } + else + powerState = 1; + + } + } + } + + void HdmiCec_2::onCECDaemonInit() + { + LOGINFO(); + + if(true == getEnabled()) + { + setEnabled(false); + setEnabled(true); + } + else + { + /*Do nothing as CEC is not already enabled*/ + } + } + + void HdmiCec_2::cecStatusUpdated(void *evtStatus) + { + LOGINFO(); + + IARM_Bus_CECMgr_Status_Updated_Param_t *evtData = (IARM_Bus_CECMgr_Status_Updated_Param_t *)evtStatus; + if(evtData) + { + try{ + getPhysicalAddress(); + + unsigned int logicalAddr = evtData->logicalAddress; + std::string logicalAddrDeviceType = DeviceType(LogicalAddress(evtData->logicalAddress).getType()).toString().c_str(); + + LOGINFO("cecLogicalAddressUpdated: logical address updated: %d , saved : %d ", logicalAddr, logicalAddress.toInt()); + if (logicalAddr != logicalAddress.toInt() || logicalAddrDeviceType != logicalAddressDeviceType) + { + logicalAddress = logicalAddr; + logicalAddressDeviceType = logicalAddrDeviceType; + } + } + catch (const std::exception e) + { + LOGWARN("CEC exception caught from cecStatusUpdated"); + } + + delete evtData; + } + return; + } + + void HdmiCec_2::onHdmiHotPlug(int connectStatus) + { + LOGINFO(); + + if (HDMI_HOT_PLUG_EVENT_CONNECTED == connectStatus) + { + LOGINFO ("onHdmiHotPlug Status : %d ", connectStatus); + getPhysicalAddress(); + getLogicalAddress(); + try + { + device::VideoOutputPort vPort = device::Host::getInstance().getVideoOutputPort("HDMI0"); + if (vPort.isDisplayConnected()) + { + vector edidVec; + vPort.getDisplay().getEDIDBytes(edidVec); + //Set LG vendor id if connected with LG TV + if(edidVec.at(8) == 0x1E && edidVec.at(9) == 0x6D) + { + isLGTvConnected = true; + } + LOGINFO("manufacturer byte from edid :%x: %x isLGTvConnected :%d",edidVec.at(8),edidVec.at(9),isLGTvConnected); + } + } + catch(...) + { + LOGWARN("Exception in getting edid info .\r\n"); + } + } + return; + } + + uint32_t HdmiCec_2::setEnabledWrapper(const JsonObject& parameters, JsonObject& response) + { + LOGINFO(); + + bool enabled = false; + + if (parameters.HasLabel("enabled")) + { + getBoolParameter("enabled", enabled); + } + else + { + returnResponse(false); + } + + setEnabled(enabled); + returnResponse(true); + } + + uint32_t HdmiCec_2::getEnabledWrapper(const JsonObject& parameters, JsonObject& response) + { + response["enabled"] = getEnabled(); + returnResponse(true); + } + uint32_t HdmiCec_2::setOTPEnabledWrapper(const JsonObject& parameters, JsonObject& response) + { + LOGINFO(); + + bool enabled = false; + + if (parameters.HasLabel("enabled")) + { + getBoolParameter("enabled", enabled); + } + else + { + returnResponse(false); + } + + setOTPEnabled(enabled); + returnResponse(true); + } + + uint32_t HdmiCec_2::getOTPEnabledWrapper(const JsonObject& parameters, JsonObject& response) + { + response["enabled"] = getOTPEnabled(); + returnResponse(true); + } + + uint32_t HdmiCec_2::setOSDNameWrapper(const JsonObject& parameters, JsonObject& response) + { + LOGINFO(); + bool enabled = false; + + if (parameters.HasLabel("name")) + { + std::string osd = parameters["name"].String(); + LOGINFO("setOSDNameWrapper osdName: %s",osd.c_str()); + osdName = osd.c_str(); + persistOSDName(osd.c_str()); + } + else + { + returnResponse(false); + } + returnResponse(true); + } + + uint32_t HdmiCec_2::getOSDNameWrapper(const JsonObject& parameters, JsonObject& response) + { + response["name"] = osdName.toString(); + LOGINFO("getOSDNameWrapper osdName : %s \n",osdName.toString().c_str()); + returnResponse(true); + } + + uint32_t HdmiCec_2::setVendorIdWrapper(const JsonObject& parameters, JsonObject& response) + { + LOGINFO(); + + bool enabled = false; + + if (parameters.HasLabel("vendorid")) + { + std::string id = parameters["vendorid"].String(); + unsigned int vendorID = 0x00; + try + { + vendorID = stoi(id,NULL,16); + } + catch (...) + { + LOGWARN("Exception in setVendorIdWrapper set default value\n"); + vendorID = 0x0019FB; + } + appVendorId = {(uint8_t)(vendorID >> 16 & 0xff),(uint8_t)(vendorID>> 8 & 0xff),(uint8_t) (vendorID & 0xff)}; + LOGINFO("appVendorId : %s vendorID :%x \n",appVendorId.toString().c_str(), vendorID ); + + persistVendorId(vendorID); + } + else + { + returnResponse(false); + } + returnResponse(true); + } + + uint32_t HdmiCec_2::getVendorIdWrapper(const JsonObject& parameters, JsonObject& response) + { + LOGINFO("getVendorIdWrapper appVendorId : %s \n",appVendorId.toString().c_str()); + response["vendorid"] = appVendorId.toString() ; + returnResponse(true); + } + + + uint32_t HdmiCec_2::performOTPActionWrapper(const JsonObject& parameters, JsonObject& response) + { + if(performOTPAction()) + { + returnResponse(true); + } + else + { + returnResponse(false); + } + } + + bool HdmiCec_2::loadSettings() + { + Core::File file; + file = CEC_SETTING_ENABLED_FILE; + + if( file.Open()) + { + JsonObject parameters; + parameters.IElement::FromFile(file); + bool isConfigAdded = false; + + if( parameters.HasLabel(CEC_SETTING_ENABLED)) + { + getBoolParameter(CEC_SETTING_ENABLED, cecSettingEnabled); + LOGINFO("CEC_SETTING_ENABLED present value:%d",cecSettingEnabled); + } + else + { + parameters[CEC_SETTING_ENABLED] = true; + cecSettingEnabled = true; + isConfigAdded = true; + LOGINFO("CEC_SETTING_ENABLED not present set dafult true:\n "); + } + + if( parameters.HasLabel(CEC_SETTING_OTP_ENABLED)) + { + getBoolParameter(CEC_SETTING_OTP_ENABLED, cecOTPSettingEnabled); + LOGINFO("CEC_SETTING_OTP_ENABLED present value :%d",cecOTPSettingEnabled); + } + else + { + parameters[CEC_SETTING_OTP_ENABLED] = true; + cecOTPSettingEnabled = true; + isConfigAdded = true; + LOGINFO("CEC_SETTING_OTP_ENABLED not present set dafult true:\n "); + } + if( parameters.HasLabel(CEC_SETTING_OSD_NAME)) + { + std::string osd_name; + getStringParameter(CEC_SETTING_OSD_NAME, osd_name); + osdName = osd_name.c_str(); + LOGINFO("CEC_SETTING_OTP_ENABLED present osd_name :%s",osdName.toString().c_str()); + } + else + { + parameters[CEC_SETTING_OSD_NAME] = osdName.toString(); + LOGINFO("CEC_SETTING_OSD_NMAE not present set dafult value :%s\n ",osdName.toString().c_str()); + isConfigAdded = true; + } + unsigned int vendorId = 0x0019FB; + if( parameters.HasLabel(CEC_SETTING_VENDOR_ID)) + { + getNumberParameter(CEC_SETTING_VENDOR_ID, vendorId); + LOGINFO("CEC_SETTING_VENDOR_ID present :%x ",vendorId); + } + else + { + LOGINFO("CEC_SETTING_OSD_NMAE not present set dafult value :\n "); + parameters[CEC_SETTING_VENDOR_ID] = vendorId; + isConfigAdded = true; + } + + appVendorId = {(uint8_t)(vendorId >> 16 & 0xff),(uint8_t)(vendorId >> 8 & 0xff),(uint8_t) (vendorId & 0xff)}; + LOGINFO("appVendorId : %s vendorId :%x \n",appVendorId.toString().c_str(), vendorId ); + + if(isConfigAdded) + { + LOGINFO("isConfigAdded true so update file:\n "); + file.Destroy(); + file.Create(); + parameters.IElement::ToFile(file); + + } + + file.Close(); + } + else + { + LOGINFO("CEC_SETTING_ENABLED_FILE file not present create with default settings "); + file.Open(false); + if (!file.IsOpen()) + file.Create(); + + JsonObject parameters; + parameters[CEC_SETTING_ENABLED] = true; + parameters[CEC_SETTING_OTP_ENABLED] = true; + parameters[CEC_SETTING_OSD_NAME] = osdName.toString(); + cecSettingEnabled = true; + cecOTPSettingEnabled = true; + parameters.IElement::ToFile(file); + + file.Close(); + + } + + return cecSettingEnabled; + } + + void HdmiCec_2::persistSettings(bool enableStatus) + { + Core::File file; + file = CEC_SETTING_ENABLED_FILE; + + file.Open(false); + if (!file.IsOpen()) + file.Create(); + + JsonObject cecSetting; + cecSetting.IElement::FromFile(file); + file.Destroy(); + file.Create(); + cecSetting[CEC_SETTING_ENABLED] = enableStatus; + cecSetting.IElement::ToFile(file); + + file.Close(); + + return; + } + + void HdmiCec_2::persistOTPSettings(bool enableStatus) + { + Core::File file; + file = CEC_SETTING_ENABLED_FILE; + + file.Open(false); + if (!file.IsOpen()) + file.Create(); + + JsonObject cecSetting; + cecSetting.IElement::FromFile(file); + file.Destroy(); + file.Create(); + cecSetting[CEC_SETTING_OTP_ENABLED] = enableStatus; + cecSetting.IElement::ToFile(file); + + file.Close(); + + return; + } + + void HdmiCec_2::persistOSDName(const char *name) + { + Core::File file; + file = CEC_SETTING_ENABLED_FILE; + + file.Open(false); + if (!file.IsOpen()) + file.Create(); + + JsonObject cecSetting; + cecSetting.IElement::FromFile(file); + file.Destroy(); + file.Create(); + cecSetting[CEC_SETTING_OSD_NAME] = name; + cecSetting.IElement::ToFile(file); + + file.Close(); + + return; + } + + void HdmiCec_2::persistVendorId(unsigned int vendorId) + { + Core::File file; + file = CEC_SETTING_ENABLED_FILE; + + file.Open(false); + if (!file.IsOpen()) + file.Create(); + + JsonObject cecSetting; + cecSetting.IElement::FromFile(file); + file.Destroy(); + file.Create(); + cecSetting[CEC_SETTING_VENDOR_ID] = vendorId; + cecSetting.IElement::ToFile(file); + + file.Close(); + + return; + } + + void HdmiCec_2::setEnabled(bool enabled) + { + LOGINFO("Entered setEnabled "); + + if (cecSettingEnabled != enabled) + { + persistSettings(enabled); + cecSettingEnabled = enabled; + } + if(true == enabled) + { + CECEnable(); + } + else + { + CECDisable(); + } + return; + } + + void HdmiCec_2::setOTPEnabled(bool enabled) + { + if (cecOTPSettingEnabled != enabled) + { + LOGINFO("persist setOTPEnabled "); + persistOTPSettings(enabled); + cecOTPSettingEnabled = enabled; + } + return; + } + + void HdmiCec_2::CECEnable(void) + { + LOGINFO("Entered CECEnable"); + if (cecEnableStatus) + { + LOGWARN("CEC Already Enabled"); + return; + } + + if(0 == libcecInitStatus) + { + try + { + LibCCEC::getInstance().init(); + } + catch (const std::exception e) + { + LOGWARN("CEC exception caught from LibCCEC::getInstance().init()"); + } + } + libcecInitStatus++; + + + //Acquire CEC Addresses + getPhysicalAddress(); + getLogicalAddress(); + + smConnection = new Connection(logicalAddress.toInt(),false,"ServiceManager::Connection::"); + smConnection->open(); + msgProcessor = new HdmiCec_2Processor(*smConnection); + msgFrameListener = new HdmiCec_2FrameListener(*msgProcessor); + smConnection->addFrameListener(msgFrameListener); + + cecEnableStatus = true; + + if(smConnection) + { + LOGINFO("Command: sending GiveDevicePowerStatus \r\n"); + smConnection->sendTo(LogicalAddress(LogicalAddress::TV), MessageEncoder().encode(GiveDevicePowerStatus()), 5000); + LOGINFO("Command: sending request active Source\r\n"); + smConnection->sendTo(LogicalAddress(LogicalAddress::BROADCAST), MessageEncoder().encode(RequestActiveSource()), 5000); + isDeviceActiveSource = true; + } + return; + } + + void HdmiCec_2::CECDisable(void) + { + LOGINFO("Entered CECDisable "); + + if(!cecEnableStatus) + { + LOGWARN("CEC Already Disabled "); + return; + } + + if (smConnection != NULL) + { + smConnection->close(); + delete smConnection; + smConnection = NULL; + } + cecEnableStatus = false; + + if(1 == libcecInitStatus) + { + try + { + LibCCEC::getInstance().term(); + } + catch (const std::exception e) + { + LOGWARN("CEC exception caught from LibCCEC::getInstance().term() "); + } + } + + libcecInitStatus--; + + return; + } + + + void HdmiCec_2::getPhysicalAddress() + { + LOGINFO("Entered getPhysicalAddress "); + + uint32_t physAddress = 0x0F0F0F0F; + + try { + LibCCEC::getInstance().getPhysicalAddress(&physAddress); + physical_addr = {(uint8_t)((physAddress >> 24) & 0xFF),(uint8_t)((physAddress >> 16) & 0xFF),(uint8_t) ((physAddress >> 8) & 0xFF),(uint8_t)((physAddress) & 0xFF)}; + LOGINFO("getPhysicalAddress: physicalAddress: %s ", physical_addr.toString().c_str()); + } + catch (const std::exception e) + { + LOGWARN("exception caught from getPhysicalAddress"); + } + return; + } + + void HdmiCec_2::getLogicalAddress() + { + LOGINFO("Entered getLogicalAddress "); + + try{ + LogicalAddress addr = LibCCEC::getInstance().getLogicalAddress(DEV_TYPE_TUNER); + + std::string logicalAddrDeviceType = DeviceType(LogicalAddress(addr).getType()).toString().c_str(); + + LOGINFO("logical address obtained is %d , saved logical address is %d ", addr.toInt(), logicalAddress.toInt()); + + if (logicalAddress.toInt() != addr.toInt() || logicalAddressDeviceType != logicalAddrDeviceType) + + { + logicalAddress = addr; + logicalAddressDeviceType = logicalAddrDeviceType; + } + } + catch (const std::exception e) + { + LOGWARN("CEC exception caught from getLogicalAddress "); + } + return; + } + + bool HdmiCec_2::getEnabled() + { + if(true == cecEnableStatus) + return true; + else + return false; + LOGINFO("getEnabled :%d ",cecEnableStatus); + } + + bool HdmiCec_2::getOTPEnabled() + { + if(true == cecOTPSettingEnabled) + return true; + else + return false; + LOGINFO("getOTPEnabled :%d ",cecOTPSettingEnabled); + } + + bool HdmiCec_2::performOTPAction() + { + LOGINFO("performOTPAction "); + bool ret = false; + if((true == cecEnableStatus) && (cecOTPSettingEnabled == true)) + { + try + { + if(tvPowerState.toInt()) + { + LOGINFO("Command: sending ImageViewOn TV \r\n"); + smConnection->sendTo(LogicalAddress(LogicalAddress::TV), MessageEncoder().encode(ImageViewOn()), 5000); + usleep(10000); + } + if(!isDeviceActiveSource) + { + LOGINFO("Command: sending ActiveSource physical_addr :%s \r\n",physical_addr.toString().c_str()); + smConnection->sendTo(LogicalAddress(LogicalAddress::BROADCAST), MessageEncoder().encode(ActiveSource(physical_addr)), 5000); + usleep(10000); + } + LOGINFO("Command: sending GiveDevicePowerStatus \r\n"); + smConnection->sendTo(LogicalAddress(LogicalAddress::TV), MessageEncoder().encode(GiveDevicePowerStatus()), 5000); + ret = true; + } + catch(...) + { + LOGWARN("Exception while processing performOTPAction"); + } + } + else + LOGWARN("cecEnableStatus=false"); + return ret; + } + } // namespace Plugin +} // namespace WPEFramework diff --git a/HdmiCec_2/HdmiCec_2.h b/HdmiCec_2/HdmiCec_2.h new file mode 100644 index 0000000000..5420ce2d0c --- /dev/null +++ b/HdmiCec_2/HdmiCec_2.h @@ -0,0 +1,158 @@ +/** +* If not stated otherwise in this file or this component's LICENSE +* file the following copyright and licenses apply: +* +* Copyright 2019 RDK Management +* +* 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. +**/ + +#pragma once + +#include +#include "ccec/FrameListener.hpp" +#include "ccec/Connection.hpp" + +#include "libIBus.h" +#include "ccec/Assert.hpp" +#include "ccec/Messages.hpp" +#include "ccec/MessageDecoder.hpp" +#include "ccec/MessageProcessor.hpp" + +#undef Assert // this define from Connection.hpp conflicts with WPEFramework + +#include "Module.h" +#include "utils.h" +#include "AbstractPlugin.h" + +namespace WPEFramework { + + namespace Plugin { + class HdmiCec_2FrameListener : public FrameListener + { + public: + HdmiCec_2FrameListener(MessageProcessor &processor) : processor(processor) {} + void notify(const CECFrame &in) const; + ~HdmiCec_2FrameListener() {} + private: + MessageProcessor &processor; + }; + + class HdmiCec_2Processor : public MessageProcessor + { + public: + HdmiCec_2Processor(Connection &conn) : conn(conn) {} + void process (const ActiveSource &msg, const Header &header); + void process (const InActiveSource &msg, const Header &header); + void process (const ImageViewOn &msg, const Header &header); + void process (const TextViewOn &msg, const Header &header); + void process (const RequestActiveSource &msg, const Header &header); + void process (const Standby &msg, const Header &header); + void process (const GetCECVersion &msg, const Header &header); + void process (const CECVersion &msg, const Header &header); + void process (const SetMenuLanguage &msg, const Header &header); + void process (const GiveOSDName &msg, const Header &header); + void process (const GivePhysicalAddress &msg, const Header &header); + void process (const GiveDeviceVendorID &msg, const Header &header); + void process (const SetOSDString &msg, const Header &header); + void process (const SetOSDName &msg, const Header &header); + void process (const RoutingChange &msg, const Header &header); + void process (const RoutingInformation &msg, const Header &header); + void process (const SetStreamPath &msg, const Header &header); + void process (const GetMenuLanguage &msg, const Header &header); + void process (const ReportPhysicalAddress &msg, const Header &header); + void process (const DeviceVendorID &msg, const Header &header); + void process (const GiveDevicePowerStatus &msg, const Header &header); + void process (const ReportPowerStatus &msg, const Header &header); + void process (const FeatureAbort &msg, const Header &header); + void process (const Abort &msg, const Header &header); + void process (const Polling &msg, const Header &header); + private: + Connection conn; + void printHeader(const Header &header) + { + printf("Header : From : %s \n", header.from.toString().c_str()); + printf("Header : to : %s \n", header.to.toString().c_str()); + } + + }; + // This is a server for a JSONRPC communication channel. + // For a plugin to be capable to handle JSONRPC, inherit from PluginHost::JSONRPC. + // By inheriting from this class, the plugin realizes the interface PluginHost::IDispatcher. + // This realization of this interface implements, by default, the following methods on this plugin + // - exists + // - register + // - unregister + // Any other methood to be handled by this plugin can be added can be added by using the + // templated methods Register on the PluginHost::JSONRPC class. + // As the registration/unregistration of notifications is realized by the class PluginHost::JSONRPC, + // this class exposes a public method called, Notify(), using this methods, all subscribed clients + // will receive a JSONRPC message as a notification, in case this method is called. + class HdmiCec_2 : public AbstractPlugin { + public: + HdmiCec_2(); + virtual ~HdmiCec_2(); + static HdmiCec_2* _instance; + private: + // We do not allow this plugin to be copied !! + HdmiCec_2(const HdmiCec_2&) = delete; + HdmiCec_2& operator=(const HdmiCec_2&) = delete; + + //Begin methods + uint32_t setEnabledWrapper(const JsonObject& parameters, JsonObject& response); + uint32_t getEnabledWrapper(const JsonObject& parameters, JsonObject& response); + uint32_t setOTPEnabledWrapper(const JsonObject& parameters, JsonObject& response); + uint32_t getOTPEnabledWrapper(const JsonObject& parameters, JsonObject& response); + uint32_t setOSDNameWrapper(const JsonObject& parameters, JsonObject& response); + uint32_t getOSDNameWrapper(const JsonObject& parameters, JsonObject& response); + uint32_t setVendorIdWrapper(const JsonObject& parameters, JsonObject& response); + uint32_t getVendorIdWrapper(const JsonObject& parameters, JsonObject& response); + uint32_t performOTPActionWrapper(const JsonObject& parameters, JsonObject& response); + //End methods + std::string logicalAddressDeviceType; + bool cecSettingEnabled; + bool cecOTPSettingEnabled; + bool cecEnableStatus; + Connection *smConnection; + HdmiCec_2Processor *msgProcessor; + HdmiCec_2FrameListener *msgFrameListener; + const void InitializeIARM(); + void DeinitializeIARM(); + static void cecMgrEventHandler(const char *owner, IARM_EventId_t eventId, void *data, size_t len); + static void dsHdmiEventHandler(const char *owner, IARM_EventId_t eventId, void *data, size_t len); + static void pwrMgrModeChangeEventHandler(const char *owner, IARM_EventId_t eventId, void *data, size_t len); + void onCECDaemonInit(); + void cecStatusUpdated(void *evtStatus); + void onHdmiHotPlug(int connectStatus); + bool loadSettings(); + void persistSettings(bool enableStatus); + void persistOTPSettings(bool enableStatus); + void persistOSDName(const char *name); + void persistVendorId(unsigned int vendorID); + void setEnabled(bool enabled); + bool getEnabled(); + void setOTPEnabled(bool enabled); + bool getOTPEnabled(); + bool performOTPAction(); + void CECEnable(void); + void CECDisable(void); + void getPhysicalAddress(); + void getLogicalAddress(); + void cecAddressesChanged(int changeStatus); + }; + } // namespace Plugin +} // namespace WPEFramework + + + + diff --git a/HdmiCec_2/Module.cpp b/HdmiCec_2/Module.cpp new file mode 100644 index 0000000000..ce759b615f --- /dev/null +++ b/HdmiCec_2/Module.cpp @@ -0,0 +1,22 @@ +/** +* If not stated otherwise in this file or this component's LICENSE +* file the following copyright and licenses apply: +* +* Copyright 2019 RDK Management +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +**/ + +#include "Module.h" + +MODULE_NAME_DECLARATION(BUILD_REFERENCE) diff --git a/HdmiCec_2/Module.h b/HdmiCec_2/Module.h new file mode 100644 index 0000000000..d2d75627fe --- /dev/null +++ b/HdmiCec_2/Module.h @@ -0,0 +1,29 @@ +/** +* If not stated otherwise in this file or this component's LICENSE +* file the following copyright and licenses apply: +* +* Copyright 2019 RDK Management +* +* 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. +**/ + +#pragma once +#ifndef MODULE_NAME +#define MODULE_NAME HdmiCec_2 +#endif + +#include +#include + +#undef EXTERNAL +#define EXTERNAL diff --git a/HdmiCec_2/README.md b/HdmiCec_2/README.md new file mode 100644 index 0000000000..fe054a42fc --- /dev/null +++ b/HdmiCec_2/README.md @@ -0,0 +1,9 @@ +----------------- +Build: + +bitbake wpeframework-service-plugins + +----------------- +Test: + +curl --header "Content-Type: application/json" --request POST --data '{"jsonrpc":"2.0","id":"3","method": "rdk.org.HdmiCec_2.1."}' http://127.0.0.1:9998/jsonrpc From 77b47d0f98c107cdc0c363bd89e66529ee95f2c8 Mon Sep 17 00:00:00 2001 From: Arun P Madhavan Date: Tue, 9 Jun 2020 15:45:35 +0000 Subject: [PATCH 05/56] DELIA-43557: Thunder Process is crashing when getDownloadedFirmwareInfo method is invoked Reason for change: Fixed the stack smashing caused by unallocated pointers. Test Procedure: Please see ticket for details Risks: Low Signed-off-by: Arun P Madhavan --- SystemServices/SystemServices.cpp | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/SystemServices/SystemServices.cpp b/SystemServices/SystemServices.cpp index d6e413cc8b..b79899515b 100644 --- a/SystemServices/SystemServices.cpp +++ b/SystemServices/SystemServices.cpp @@ -1277,11 +1277,16 @@ namespace WPEFramework { JsonObject& response) { bool retStat = false; - char downloadedFWVersion[] = ""; - char downloadedFWLocation[] = ""; + string downloadedFWVersion = ""; + string downloadedFWLocation = ""; bool isRebootDeferred = false; std::vector lines; + if (!Utils::fileExists(FWDNLDSTATUS_FILE_NAME)) { + populateResponseWithError(SysSrv_FileNotPresent, response); + returnResponse(retStatus); + } + if (getFileContent(FWDNLDSTATUS_FILE_NAME, lines)) { for (std::vector::const_iterator i = lines.begin(); i != lines.end(); ++i) { @@ -1298,9 +1303,9 @@ namespace WPEFramework { } line = std::regex_replace(line, std::regex("^ +| +$"), "$1"); if (line.length() > 1) { - if (!((strcicmp(line.c_str(), "1")) - && (strcicmp(line.c_str(), "yes")) - && (strcicmp(line.c_str(), "true")))) { + if (!((strncasecmp(line.c_str(), "1", strlen("1"))) + && (strncasecmp(line.c_str(), "yes", strlen("yes"))) + && (strncasecmp(line.c_str(), "true", strlen("true"))))) { isRebootDeferred = true; } } @@ -1313,7 +1318,7 @@ namespace WPEFramework { } line = std::regex_replace(line, std::regex("^ +| +$"), "$1"); if (line.length() > 1) { - strcpy(downloadedFWVersion, line.c_str()); + downloadedFWVersion = line.c_str(); } } found = line.find("DnldURL|"); @@ -1324,13 +1329,13 @@ namespace WPEFramework { } line = std::regex_replace(line, std::regex("^ +| +$"), "$1"); if (line.length() > 1) { - strcpy(downloadedFWLocation, line.c_str()); + downloadedFWLocation = line.c_str(); } } } response["currentFWVersion"] = getStbVersionString(); - response["downloadedFWVersion"] = string(downloadedFWVersion); - response["downloadedFWLocation"] = string(downloadedFWLocation); + response["downloadedFWVersion"] = downloadedFWVersion; + response["downloadedFWLocation"] = downloadedFWLocation; response["isRebootDeferred"] = isRebootDeferred; retStat = true; } else { From 9da90530b839e2a54c00681539ccf6192640a6c2 Mon Sep 17 00:00:00 2001 From: mfiess200 Date: Tue, 9 Jun 2020 14:10:45 -0400 Subject: [PATCH 06/56] scale and initial animation support --- RDKShell/RDKShell.cpp | 236 +++++++++++++++++++++++++++++++++++++----- RDKShell/RDKShell.h | 12 +++ 2 files changed, 224 insertions(+), 24 deletions(-) mode change 100644 => 100755 RDKShell/RDKShell.cpp diff --git a/RDKShell/RDKShell.cpp b/RDKShell/RDKShell.cpp old mode 100644 new mode 100755 index b9a1aa80c5..c67b5c54db --- a/RDKShell/RDKShell.cpp +++ b/RDKShell/RDKShell.cpp @@ -48,6 +48,10 @@ const string WPEFramework::Plugin::RDKShell::RDKSHELL_METHOD_GET_VISIBILITY = "g const string WPEFramework::Plugin::RDKShell::RDKSHELL_METHOD_SET_VISIBILITY = "setVisibility"; const string WPEFramework::Plugin::RDKShell::RDKSHELL_METHOD_GET_OPACITY = "getOpacity"; const string WPEFramework::Plugin::RDKShell::RDKSHELL_METHOD_SET_OPACITY = "setOpacity"; +const string WPEFramework::Plugin::RDKShell::RDKSHELL_METHOD_GET_SCALE = "getScale"; +const string WPEFramework::Plugin::RDKShell::RDKSHELL_METHOD_SET_SCALE = "setScale"; +const string WPEFramework::Plugin::RDKShell::RDKSHELL_METHOD_REMOVE_ANIMATION = "removeAnimation"; +const string WPEFramework::Plugin::RDKShell::RDKSHELL_METHOD_ADD_ANIMATION = "addAnimation"; using namespace std; using namespace RdkShell; @@ -155,6 +159,10 @@ namespace WPEFramework { registerMethod(RDKSHELL_METHOD_SET_VISIBILITY, &RDKShell::setVisibilityWrapper, this); registerMethod(RDKSHELL_METHOD_GET_OPACITY, &RDKShell::getOpacityWrapper, this); registerMethod(RDKSHELL_METHOD_SET_OPACITY, &RDKShell::setOpacityWrapper, this); + registerMethod(RDKSHELL_METHOD_GET_SCALE, &RDKShell::getScaleWrapper, this); + registerMethod(RDKSHELL_METHOD_SET_SCALE, &RDKShell::setScaleWrapper, this); + registerMethod(RDKSHELL_METHOD_REMOVE_ANIMATION, &RDKShell::removeAnimationWrapper, this); + registerMethod(RDKSHELL_METHOD_ADD_ANIMATION, &RDKShell::addAnimationWrapper, this); } RDKShell::~RDKShell() @@ -178,6 +186,7 @@ namespace WPEFramework { double startFrameTime = RdkShell::microseconds(); gRdkShellMutex.lock(); RdkShell::draw(); + RdkShell::update(); gRdkShellMutex.unlock(); double frameTime = (int)RdkShell::microseconds() - (int)startFrameTime; int32_t sleepTimeInMs = gCurrentFramerate - frameTime; @@ -507,33 +516,30 @@ namespace WPEFramework { result = false; response["message"] = "please specify client"; } - if (!parameters.HasLabel("x")) - { - result = false; - response["message"] = "please specify x"; - } - if (!parameters.HasLabel("y")) - { - result = false; - response["message"] = "please specify y"; - } - if (!parameters.HasLabel("w")) - { - result = false; - response["message"] = "please specify w"; - } - if (!parameters.HasLabel("h")) - { - result = false; - response["message"] = "please specify h"; - } if (result) { const string client = parameters["client"].String(); - const unsigned int x = parameters["x"].Number(); - const unsigned int y = parameters["y"].Number(); - const unsigned int w = parameters["w"].Number(); - const unsigned int h = parameters["h"].Number(); + + unsigned int x=0,y=0,w=0,h=0; + gRdkShellMutex.lock(); + CompositorController::getBounds(client, x, y, w, h); + gRdkShellMutex.unlock(); + if (parameters.HasLabel("x")) + { + x = parameters["x"].Number(); + } + if (parameters.HasLabel("y")) + { + y = parameters["y"].Number(); + } + if (parameters.HasLabel("w")) + { + w = parameters["w"].Number(); + } + if (parameters.HasLabel("h")) + { + h = parameters["h"].Number(); + } result = setBounds(client, x, y, w, h); if (false == result) { @@ -653,6 +659,113 @@ namespace WPEFramework { returnResponse(result); } + uint32_t RDKShell::getScaleWrapper(const JsonObject& parameters, JsonObject& response) + { + LOGINFOMETHOD(); + UNUSED(parameters); + bool result = true; + if (!parameters.HasLabel("client")) + { + result = false; + response["message"] = "please specify client"; + } + + if (result) + { + const string client = parameters["client"].String(); + double scaleX = 1.0; + double scaleY = 1.0; + if (!getScale(client, scaleX, scaleY)) + { + response["message"] = "failed to get scale"; + result = false; + } else { + response["sx"] = std::to_string(scaleX); + response["sy"] = std::to_string(scaleY); + result = true; + } + } + + returnResponse(result); + } + + uint32_t RDKShell::setScaleWrapper(const JsonObject& parameters, JsonObject& response) + { + LOGINFOMETHOD(); + bool result = true; + if (!parameters.HasLabel("client")) + { + result = false; + response["message"] = "please specify client"; + } + if (!parameters.HasLabel("sx") && !parameters.HasLabel("sy")) + { + result = false; + response["message"] = "please specify sx and/or sy"; + } + if (result) + { + const string client = parameters["client"].String(); + double scaleX = 1.0; + double scaleY = 1.0; + getScale(client, scaleX, scaleY); + if (parameters.HasLabel("sx")) + { + scaleX = std::stod(parameters["sx"].String()); + } + if (parameters.HasLabel("sy")) + { + scaleY = std::stod(parameters["sy"].String()); + } + + result = setScale(client, scaleX, scaleY); + if (false == result) { + response["message"] = "failed to set scale"; + } + } + returnResponse(result); + } + + uint32_t RDKShell::removeAnimationWrapper(const JsonObject& parameters, JsonObject& response) + { + LOGINFOMETHOD(); + UNUSED(parameters); + bool result = true; + if (!parameters.HasLabel("client")) + { + result = false; + response["message"] = "please specify client"; + } + + if (result) + { + const string client = parameters["client"].String(); + if (!removeAnimation(client)) + { + response["message"] = "failed to remove animation"; + result = false; + } + } + returnResponse(result); + } + + uint32_t RDKShell::addAnimationWrapper(const JsonObject& parameters, JsonObject& response) + { + LOGINFOMETHOD(); + bool result = true; + + bool arraybased = false; + if (parameters.HasLabel("animations")) + { + const JsonArray animations = parameters["animations"].Array(); + result = addAnimationList(animations); + if (false == result) { + response["message"] = "failed to add animation list"; + } + } + returnResponse(result); + } + // Registered methods begin // Events begin @@ -853,6 +966,81 @@ namespace WPEFramework { gRdkShellMutex.unlock(); return ret; } + + bool RDKShell::getScale(const string& client, double& scaleX, double& scaleY) + { + bool ret = false; + gRdkShellMutex.lock(); + ret = CompositorController::getScale(client, scaleX, scaleY); + gRdkShellMutex.unlock(); + return ret; + } + + bool RDKShell::setScale(const string& client, const double scaleX, const double scaleY) + { + bool ret = false; + gRdkShellMutex.lock(); + ret = CompositorController::setScale(client, scaleX, scaleY); + gRdkShellMutex.unlock(); + return ret; + } + + bool RDKShell::removeAnimation(const string& client) + { + bool ret = false; + gRdkShellMutex.lock(); + ret = CompositorController::removeAnimation(client); + gRdkShellMutex.unlock(); + return ret; + } + + bool RDKShell::addAnimationList(const JsonArray& animations) + { + gRdkShellMutex.lock(); + for (int i=0; i animationProperties; + if (animationInfo.HasLabel("x")) + { + int32_t x = animationInfo["x"].Number(); + animationProperties["x"] = x; + } + if (animationInfo.HasLabel("y")) + { + int32_t y = animationInfo["y"].Number(); + animationProperties["y"] = y; + } + if (animationInfo.HasLabel("w")) + { + uint32_t width = animationInfo["w"].Number(); + animationProperties["w"] = width; + } + if (animationInfo.HasLabel("h")) + { + uint32_t height = animationInfo["h"].Number(); + animationProperties["h"] = height; + } + if (animationInfo.HasLabel("sx")) + { + double scaleX = std::stod(animationInfo["sx"].String()); + animationProperties["sx"] = scaleX; + } + if (animationInfo.HasLabel("sy")) + { + double scaleY = std::stod(animationInfo["sy"].String()); + animationProperties["sy"] = scaleY; + } + CompositorController::addAnimation(client, duration, animationProperties); + } + } + gRdkShellMutex.unlock(); + return true; + } + // Internal methods end } // namespace Plugin } // namespace WPEFramework diff --git a/RDKShell/RDKShell.h b/RDKShell/RDKShell.h index 8d9f18bf8c..d5ae2d1b49 100755 --- a/RDKShell/RDKShell.h +++ b/RDKShell/RDKShell.h @@ -62,6 +62,10 @@ namespace WPEFramework { static const string RDKSHELL_METHOD_SET_VISIBILITY; static const string RDKSHELL_METHOD_GET_OPACITY; static const string RDKSHELL_METHOD_SET_OPACITY; + static const string RDKSHELL_METHOD_GET_SCALE; + static const string RDKSHELL_METHOD_SET_SCALE; + static const string RDKSHELL_METHOD_ADD_ANIMATION; + static const string RDKSHELL_METHOD_REMOVE_ANIMATION; private/*registered methods (wrappers)*/: @@ -84,6 +88,10 @@ namespace WPEFramework { uint32_t setVisibilityWrapper(const JsonObject& parameters, JsonObject& response); uint32_t getOpacityWrapper(const JsonObject& parameters, JsonObject& response); uint32_t setOpacityWrapper(const JsonObject& parameters, JsonObject& response); + uint32_t getScaleWrapper(const JsonObject& parameters, JsonObject& response); + uint32_t setScaleWrapper(const JsonObject& parameters, JsonObject& response); + uint32_t addAnimationWrapper(const JsonObject& parameters, JsonObject& response); + uint32_t removeAnimationWrapper(const JsonObject& parameters, JsonObject& response); private/*internal methods*/: RDKShell(const RDKShell&) = delete; @@ -107,6 +115,10 @@ namespace WPEFramework { bool setVisibility(const string& client, const bool visible); bool getOpacity(const string& client, unsigned int& opacity); bool setOpacity(const string& client, const unsigned int opacity); + bool getScale(const string& client, double& scaleX, double& scaleY); + bool setScale(const string& client, const double scaleX, const double scaleY); + bool removeAnimation(const string& client); + bool addAnimationList(const JsonArray& animations); private/*classes */: From f8824668d8f75bf4a30543f0bf427acbde137fca Mon Sep 17 00:00:00 2001 From: Anand Kandasamy Date: Tue, 9 Jun 2020 16:19:53 -0400 Subject: [PATCH 07/56] Revert "DELIA-41812: Network Plugin fixes" This reverts commit 5f280dd0b03821a759fefb23d7f399c6c66ed0e8. --- Network/NetUtils.cpp | 973 +--------------------------------- Network/NetUtils.h | 126 +---- Network/NetworkTraceroute.cpp | 29 +- Network/PingNotifier.cpp | 39 +- 4 files changed, 48 insertions(+), 1119 deletions(-) diff --git a/Network/NetUtils.cpp b/Network/NetUtils.cpp index cbcca76753..0366027b25 100644 --- a/Network/NetUtils.cpp +++ b/Network/NetUtils.cpp @@ -18,29 +18,14 @@ **/ #include "NetUtils.h" -#include -#include -#include -//#include -#include #include -#include -#include #include "Network.h" //Defines -#define DELETE_ROUTE_CMD "route delete default gw %s 2>&1" //192.168.1.1 -#define RESTART_DHCPC_CMD "/sbin/udhcpc -i %s -p /tmp/udhcpc.%s.pid 2>&1 &" //eth0:0, eth0:0 #define NETUTIL_DEVICE_PROPERTIES_FILE "/etc/device.properties" -#define NETUTIL_PERSIST_DEFAULT_INTERFACE_FILE "/opt/persistent/defaultInterface" -#define ESTB_IPV6_FILE_NAME "/tmp/estb_ipv6" -#define ESTB_IPV4_FILE_NAME "/tmp/estb_ipv4" #define COMMAND_RESULT_FILE "cmdresult" -#define CONNECTED_FLAGS (IFF_UP|IFF_RUNNING) -#define INTERFACE_CONNECTED(flags) (((flags & CONNECTED_FLAGS) == CONNECTED_FLAGS) ? 1 : 0) - namespace WPEFramework { namespace Plugin { @@ -51,360 +36,43 @@ namespace WPEFramework { /* * */ - NetUtils::NetUtils() : - m_dataProtect(), - m_netlinkProtect() + NetUtils::NetUtils() { } NetUtils::~NetUtils() { - _stopMonitor(); } /* * Initialise netutils: - * - initialise the interface list - * - check (and set) the persistent default interface - * - start the network monitor task + * - load interface descriptions */ void NetUtils::InitialiseNetUtils() { - std::string defaultInterface; - - // Initialise interface list (this is required for setting the default interface) - _initialiseInterfaceList(); - //_displayInterfaceStatus(); - - // If we are persisting the default interface, try to set it now (on boot this is unlikely - // to work as the interfaces will probably not be up yet) - if (NetUtils::getDefaultGatewayPersistent(defaultInterface)) - { - if (!SetDefaultInterface(defaultInterface, true)) - { - LOGINFO("Setting %s as the default interface is pending...", defaultInterface.c_str()); - - // Store the setting to be applied when the interfaces are ready - _setPendingDefaultInterface(defaultInterface); - } - } - - // Start the network monitor task - _startMonitor(); - } - - /* - * Get the MAC address of the interface - */ - bool NetUtils::GetInterfaceMACAddress(const std::string &interfaceDescription, std::string &macAddr) - { - std::lock_guard lock(m_dataProtect); - - for ( auto &info : interfaceList) - { - if (info.m_if_descr == interfaceDescription) - { - macAddr = info.m_if_macaddr; - return true; - } - } - - return false; - } - - /* - * Get the connection state of the interface - */ - bool NetUtils::GetInterfaceConnected(const std::string &interfaceDescription, bool &connected) - { - std::lock_guard lock(m_dataProtect); - - for ( auto &info : interfaceList) - { - if (info.m_if_descr == interfaceDescription) - { - connected = INTERFACE_CONNECTED(info.m_if_flags); - return true; - } - } - - return false; - } - - /* - * Get the name of the interface (e.g. wlan0, wlan0:0, eth0, eth0:0) - * If physical==false then a virtual interface name may be returned if one exists - */ - bool NetUtils::GetInterfaceName(const std::string &interfaceDescription, std::string &name, bool physical) - { - std::lock_guard lock(m_dataProtect); - - for ( auto &info : interfaceList) - { - if (info.m_if_descr == interfaceDescription) - { - name = info.interfaceName(physical); - return true; - } - } - - return false; - } - - /* - * Get the descriptive name for the default interface (e.g. WIFI, ETHERNET etc.) - */ - bool NetUtils::GetDefaultInterfaceDescription(std::string &description) - { - stringList interfaces; - stringList gateways; - stringList descriptions; - - if (!_getDefaultInterfaces(interfaces, gateways, descriptions)) - { - LOGERR("Failed to get default interface information."); - } - else - { - description = descriptions[0]; // assume single default interface - return true; - } - - return false; - } - - /* - * Internal method to request the default route(s) information (from netlink) and match - * it to our return list to get the interface name, description etc. - * If physical=true return the name of the physical interface (eg. eth0) - * else it may be a virtual interface if one exists (eg. eth0:0) - * If async=true then the netlink request will be run in a separate thread - */ - bool NetUtils::_getDefaultInterfaces(stringList &interfaces, stringList &gateways, stringList &descriptions, - bool physical, bool async) - { - // Serialise requests so we only open one netlink request socket at a time - std::lock_guard lock(m_netlinkProtect); - - indexList interfaceIndexList; - bool success = false; - - if (async) - { - // Async indicates to make the netlink call in a thread (this is to do with how the socket - // pid is defined - we can only have one netlink socket per thread/process) - _asyncQueryDefaultInterfaces(interfaceIndexList, gateways, success); - } - else - { - _queryDefaultInterfaces(interfaceIndexList, gateways, success); - } - - if (!success) - { - LOGERR("Failed to send route information request."); - } - else - { - std::lock_guard lock(m_dataProtect); - - for ( auto &index : interfaceIndexList) - { - for ( auto &info : interfaceList) - { - if (info.m_if_index == (int)index) - { - interfaces.push_back(info.interfaceName(physical)); - descriptions.push_back(info.m_if_descr); - LOGINFO("Default route: %s, %s", - info.m_if_descr.c_str(), info.interfaceName().c_str()); - return true; - } - } - } - } - - return false; - } - - /* - * Make a netlink request to get the default interfaces (in a separate thread) - */ - void NetUtils::_asyncQueryDefaultInterfaces(indexList &interfaces, stringList &gateways, bool &success) - { - std::thread asyncTask = std::thread(_queryDefaultInterfaces, - std::ref(interfaces), std::ref(gateways), std::ref(success)); - - if (asyncTask.joinable()) - asyncTask.join(); - } - - /* - * Make a netlink request to get the default interfaces - */ - void NetUtils::_queryDefaultInterfaces(indexList &interfaces, stringList &gateways, bool &success) - { - Netlink netlinkRequest; - - success = false; - - if (!netlinkRequest.connect()) - { - LOGERR("Failed to connect netlink request socket"); - } - else if (NetUtils::isConfiguredIPV6()) - { - if (!netlinkRequest.getDefaultInterfaces(interfaces, gateways, true)) - { - LOGWARN("Failed to get default IPv6 interfaces"); - } - else - { - success = true; - } - } - else if (!netlinkRequest.getDefaultInterfaces(interfaces, gateways, false)) - { - LOGWARN("Failed to get default interfaces"); - } - else - { - success = true; - } - } - - - /* - * Set the default interface - * - * NOTE - The flag pendingRequest is used to indicate that we are trying to set the default - * interface at startup, probably at boot time. In this case we will only proceed if the - * interface is connected and we have a default interface. (As this may be called from the - * monitor task we will perform the netlink request asynchronously in a separate thread) - */ - bool NetUtils::SetDefaultInterface(std::string &interfaceDescription, bool onInit) - { - std::string interfaceName = ""; - bool connected = false; - bool success = false; - - // Check the interface exists and is connected (UP and RUNNING) - if (!GetInterfaceName(interfaceDescription, interfaceName) || - !GetInterfaceConnected(interfaceDescription, connected)) - { - LOGERR("Interface not recognised"); - } - else if (!connected) - { - LOGERR("Interface is not connected"); - } - else - { - stringList names; - stringList gateways; - stringList descriptions; - - // Get a list of current default interfaces (routes) - // We want the name of the virtual interface if it exists - if (!_getDefaultInterfaces(names, gateways, descriptions, false, onInit)) - { - LOGWARN("Could not get current default interface"); - - // If this is on initialisation (we may be waiting for the network connections to - // be set up) then exit if we do not have a default interfaces (yet) - if (onInit) - { - return false; - } - } - - // Kill the current udhcpc processes and delete the default interfaces - for (unsigned i = 0; i < names.size(); i++) - { - if (interfaceDescription == descriptions[i]) - { - LOGINFO("%s is already a default interface", interfaceDescription.c_str()); - success = true; - } - else - { - LOGINFO("Deleting default interface on %s", names[i].c_str()); - _deleteDefaultInterface(names[i], gateways[i]); - } - } - - if (!success) - { - // Start udhcpc on the requested interface - success = _createDefaultInterface(interfaceName); - } - - if (success) - { - // Make sure any pending setting is cleared - _clearPendingDefaultInterface(); - } - } - - return success; + _loadInterfaceDescriptions(); } - /* - * Delete the current default interface (route) - */ - bool NetUtils::_deleteDefaultInterface(std::string &name, std::string &gateway) + void NetUtils::_loadInterfaceDescriptions() { - std::string output = ""; - char command[MAX_COMMAND_LENGTH]; - bool success = true; + string value; - // Terminate udhcpc on the interface - snprintf(command, MAX_COMMAND_LENGTH, "cat /tmp/udhcpc.%s.pid 2>&1", name.c_str()); - if (NetUtils::execCmd(command, output) < 0) - { - LOGERR("Failed to get udhcpc pid"); - } - else if (output.length() > 0) //pid of udhcpc - { - snprintf(command, MAX_COMMAND_LENGTH, "kill -9 %s 2>&1", output.c_str()); - NetUtils::execCmd(command, output); - } + if (envGetValue("WIFI_SUPPORT", value) && (value == "true") && envGetValue("WIFI_INTERFACE", value)) + interface_descriptions.insert({value, "WIFI"}); + if (envGetValue("MOCA_SUPPORT", value) && (value == "true") && envGetValue("MOCA_INTERFACE", value)) + interface_descriptions.insert({value, "MOCA"}); + if (envGetValue("ETHERNET_INTERFACE", value)) + interface_descriptions.insert({value, "ETHERNET"}); - // Delete the default route - if (gateway.length() > 0) - { - snprintf(command, MAX_COMMAND_LENGTH, DELETE_ROUTE_CMD, gateway.c_str()); - if (NetUtils::execCmd(command, output) < 0) - { - LOGERR("Failed to delete default route"); - success = false; - } - } - - return success; + for (const auto& e : interface_descriptions) + LOGINFO ("%s %s", e.first.c_str(), e.second.c_str()); } - /* - * Set the default interface (route) - */ - bool NetUtils::_createDefaultInterface(std::string &name) + const std::string& NetUtils::getInterfaceDescription(const std::string interface) { - std::string output = ""; - char command[MAX_COMMAND_LENGTH]; - - // Request a dhcp lease for the new interface - snprintf(command, MAX_COMMAND_LENGTH, RESTART_DHCPC_CMD, - name.c_str(), name.c_str()); - - if (NetUtils::execCmd(command, output) < 0) - { - LOGERR("Failed to create default route"); - return false; - } - else - { - return true; - } + static std::string empty(""); + auto it = interface_descriptions.find(interface.substr(0, interface.find(':'))); // look up "eth0" (real interface) for "eth0:0" (virtual interface) input also + return (it != interface_descriptions.end()) ? it->second : empty; } /* @@ -510,186 +178,6 @@ namespace WPEFramework { return (inet_pton(AF_INET6, address.c_str(), &ipv6address) > 0); } - // Not every character can be used for endpoint - bool NetUtils::_isCharacterIllegal(const int& c) - { - //character must be "-./0-9a-zA-Z" - return (c < 45) || ((c > 57) && (c < 65)) || ((c > 90) && (c < 97)) || (c > 122); - } - - // Check if valid - consist of only allowed characters - bool NetUtils::isValidEndpointURL(const std::string& endpoint) - { - return std::find_if(endpoint.begin(), endpoint.end(), _isCharacterIllegal) == endpoint.end(); - } - - /* - * See if the device is configured to use ipv6 - */ - bool NetUtils::isConfiguredIPV6() - { - struct stat buffer; - return (stat (ESTB_IPV6_FILE_NAME, &buffer) == 0); - } - - /* - * See if the device is configured to use ipv4 - */ - bool NetUtils::isConfiguredIPV4() - { - struct stat buffer; - return (stat (ESTB_IPV4_FILE_NAME, &buffer) == 0); - } - - /* - * Get the CMTS (default) interface name - */ -#ifdef USE_NETLINK - bool NetUtils::getCMTSInterface(std::string &interface) - { - stringList interfaces; - stringList gateways; - stringList descriptions; - - // Get the name and ip address for the default interface - if (!_getDefaultInterfaces(interfaces, gateways, descriptions)) - { - LOGERR("Failed to get default interface information."); - return false; - } - else - { - interface = interfaces[0]; - return true; - } - } -#else - bool NetUtils::getCMTSInterface(std::string &interface) - { - //Try to use DEFAULT_ESTB_INTERFACE but this may not exist on all devices - if (!NetUtils::envGetValue("DEFAULT_ESTB_INTERFACE", interface)) - { - //Query netsrvmgr for the active interface - std::string interfaceDesription = ""; - if (!Network::getInstance()->_getActiveInterface(interfaceDesription)) - { - LOGERR("%s: failed to get active interface", __FUNCTION__); - return false; - } - //... and convert to interface name - else if (!GetInterfaceName(interfaceDesription, interface, true)) //ignore virtual interfaces - { - LOGERR("%s: failed to get active interface name", __FUNCTION__); - return false; - } - } - return true; - } -#endif - - /* - * Get the CMTS gateway from configured information - */ -#ifdef USE_NETLINK - bool NetUtils::getCMTSGateway(std::string &gateway) - { - stringList interfaces; - stringList gateways; - stringList descriptions; - - gateway = ""; - - // Get the name and ip address for the default interface - if (!_getDefaultInterfaces(interfaces, gateways, descriptions)) - { - LOGERR("Failed to get default interface information."); - return false; - } - else - { - gateway = gateways[0]; // assume single default interface - return true; - } - } -#else - bool NetUtils::getCMTSGateway(std::string &gateway) - { - std::string interface = ""; - char cmd [1000] = {0x0}; - - gateway = ""; - - if (getCMTSInterface(interface)) - { - if (isConfiguredIPV6()) - { - snprintf(cmd, sizeof(cmd), "route -A inet6 | grep %s | grep 'UG' | awk '{print $2}'", interface.c_str()); - if (execCmd(cmd, gateway) < 0) - { - LOGERR("%s: failed to get IPv6 gateway address", __FUNCTION__); - } - } - - // If we are not configured for ipv6 or didn't get a gateway address, default to ipv4 and try that - if (gateway.length() == 0) - { - snprintf(cmd, sizeof(cmd), "route -n | grep %s | grep 'UG' | awk '{print $2}'", interface.c_str()); - if (execCmd(cmd, gateway) < 0) - { - LOGERR("%s: failed to get IPv4 gateway address", __FUNCTION__); - } - } - - if (gateway.length() > 0) - { - // We can get multiple lines matching (especially ipv6) so delete all after the first - size_t lfpos = gateway.find('\n'); - if (lfpos != std::string::npos) - { - gateway.erase(lfpos); - } - - LOGINFO("gateway = %s", gateway.c_str()); - } - } - - return (gateway.length() > 0); - } -#endif - - /* - * Set the persistence state of the default interface - */ - bool NetUtils::setDefaultGatewayPersistent(const char *interface) - { - bool result = false; - if (interface) - { - std::ofstream ofs(NETUTIL_PERSIST_DEFAULT_INTERFACE_FILE); - if (ofs.is_open()) - { - ofs << interface; - result = ofs.fail(); - ofs.close(); - } - } - else - { - std::remove(NETUTIL_PERSIST_DEFAULT_INTERFACE_FILE); - result = true; - } - return result; - } - - /* - * Get the persistence state of the default interface - */ - bool NetUtils::getDefaultGatewayPersistent(std::string &interface) - { - return getFile(NETUTIL_PERSIST_DEFAULT_INTERFACE_FILE, interface); - } - - /* * Get the contents of a file * if deleteFile is true, remove the file after it is read @@ -716,39 +204,6 @@ namespace WPEFramework { return result; } - /* - * Given the interface name, check the device file so we try to match what the lower layers - * expect/deliver (i.e stay in sync with existing methods) - */ - bool NetUtils::_envGetInterfaceDescription(const char *name, std::string &description) - { - description.clear(); - - if (envCheckBool("WIFI_SUPPORT")) - { - if (envCheckValue("WIFI_INTERFACE", name)) - { - description = "WIFI"; - return true; - } - } - if (envCheckBool("MOCA_SUPPORT")) - { - if (envCheckValue("MOCA_INTERFACE", name)) - { - description = "MOCA"; - return true; - } - } - if (envCheckValue("ETHERNET_INTERFACE", name)) - { - description = "ETHERNET"; - return true; - } - - return false; - } - /* * Get the value of the given key from the environment (device properties file) */ @@ -778,377 +233,6 @@ namespace WPEFramework { return false; } - bool NetUtils::envCheckValue(const char *key, const char *value) - { - std::string envValue = ""; - if (!envGetValue(key, envValue)) - { - LOGWARN("Could not find property: %s", key); - } - return (envValue == value); - } - - bool NetUtils::envCheckBool(const char *key) - { - std::string envValue = ""; - if (!envGetValue(key, envValue)) - { - LOGWARN("Could not find property: %s", key); - } - return (envValue == "true"); - } - - bool NetUtils::_parseMACAddress(unsigned char *addr, int addlen, std::string &macAddr) - { - for (int i = 0; i < addlen; i++) - { - char buffer[4]; - sprintf(buffer, "%s%02x", i ? ":" : "", addr[i]); - buffer[3] = '\0'; - - macAddr += buffer; - } - - return macAddr.length() > 0; - } - - /* - * Initialise the data that we use to monitor for network changes and provide - * information on interfaces - */ - void NetUtils::_initialiseInterfaceList() - { - std::lock_guard lock(m_netlinkProtect); - struct ifaddrs *pIntfList = NULL; - struct ifaddrs *pIntf = NULL; - std::string description; - - getifaddrs(&pIntfList); - if (pIntfList) - { - // Use AF_PACKET items to determine the physical interfaces - for (pIntf = pIntfList; pIntf != NULL; pIntf = pIntf->ifa_next) - { - if (pIntf->ifa_addr->sa_family == AF_PACKET) - { - struct sockaddr_ll *hw = (struct sockaddr_ll *)pIntf->ifa_addr; - struct iface_info info; - info.m_if_name = pIntf->ifa_name; - info.m_if_index = hw->sll_ifindex; - info.m_if_flags = pIntf->ifa_flags; - - // We do not need set the address and flags details here - info.m_if_addr = ""; - info.m_if_addrv6 = ""; - - info.m_if_vname = ""; - info.m_if_vaddr = ""; - info.m_if_vaddrv6 = ""; - - if ( hw->sll_hatype != ARPHRD_ETHER) - { - // Ignore non-ethernet ineterfaces (lo, sit0 etc.) - LOGINFO("Not ethernet interface: %s", pIntf->ifa_name); - } - // Look for the interface name in the environment file - else if (!_envGetInterfaceDescription(pIntf->ifa_name, info.m_if_descr)) - { - // We expect the interfcaes to be defined in the device.properties file - LOGERR("No description for interface %s", pIntf->ifa_name); - } - else if (!_parseMACAddress(hw->sll_addr, hw->sll_halen, info.m_if_macaddr)) - { - LOGERR("Could not parse mac address for interface %s", pIntf->ifa_name); - } - else - { - LOGINFO("Storing interface %s, %s, %s", info.m_if_name.c_str(), info.m_if_descr.c_str(), info.m_if_macaddr.c_str()); - interfaceList.push_back(info); - } - } - } - - // Use Update the interface list with addresses (and virtual interfaces) - for ( auto &info : interfaceList) - { - for (pIntf = pIntfList; pIntf != NULL; pIntf = pIntf->ifa_next) - { - if ((pIntf->ifa_addr->sa_family == AF_INET) || - (pIntf->ifa_addr->sa_family == AF_INET6)) - { - if ((0 == strncmp(info.m_if_name.c_str(), pIntf->ifa_name, info.m_if_name.length())) && - (0 != strcmp(info.m_if_name.c_str(), pIntf->ifa_name))) - { - info.m_if_vname = pIntf->ifa_name; - } - } - } - } - - freeifaddrs(pIntfList); - } - } - - /* - * Background monitor methods - */ - - /* - * Start a background network monitor task - */ - void NetUtils::_startMonitor() - { - m_fdNlMonitorMsgPipe[0] = -1; - m_fdNlMonitorMsgPipe[1] = -1; - - /* Create a pipe for posting a shutdown request */ - if (0 == pipe(m_fdNlMonitorMsgPipe)) - { - m_netlinkMonitorThread = std::thread(_runMonitor, this); - } - else - { - LOGERR("ERROR: Failed to create netlink monitor abort pipe."); - } - } - - /* - * Stop the background network monitor task - */ - void NetUtils::_stopMonitor() - { - if (m_fdNlMonitorMsgPipe[1] >= 0) - { - if (m_netlinkMonitorThread.joinable()) - { - /* Post a shutdown request then wait for thread to terminate */ - if (write(m_fdNlMonitorMsgPipe[1], &m_fdNlMonitorMsgPipe[1], sizeof(int)) != sizeof(int)) - { - LOGERR("ERROR: Failed to write shutdown request. Netlink monitor task will not terminate."); - } - LOGINFO("Joining netlink connection monitor thread."); - m_netlinkMonitorThread.join(); - LOGINFO("Netlink connection monitor thread terminated."); - } - - close(m_fdNlMonitorMsgPipe[0]); - close(m_fdNlMonitorMsgPipe[1]); - m_fdNlMonitorMsgPipe[0] = -1; - m_fdNlMonitorMsgPipe[1] = -1; - } - } - - /* - * Netlink monitor task - * This is currently used just to detect that a change has occurred, the details will - * be identified in _updateInterfaceStatus() using getifaddrs - */ - void NetUtils::_runMonitor(NetUtils *utils) - { - char msgBuffer[NETLINK_MESSAGE_BUFFER_SIZE]; - int msgLength; - struct pollfd pfds[2]; - Netlink netlink; - - LOGWARN("%s : Netlink monitor RUNNING...", __FUNCTION__); - - // Connect a netlink socket to the groups we want to monitor - if (!netlink.connect(RTMGRP_LINK|RTMGRP_IPV4_IFADDR|RTMGRP_IPV6_IFADDR)) - { - LOGERR("Could not connect to netlionk socket."); - return; - } - - // first fd is the message pipe - pfds[0].fd = utils->m_fdNlMonitorMsgPipe[0]; - pfds[0].events = POLLIN; - pfds[0].revents = 0; - - pfds[1].fd = netlink.sockfd(); - pfds[1].events = POLLIN; - pfds[1].revents = 0; - - utils->_lock(); - utils->_resetInterfaceList(); - - while (1) - { - utils->_updateInterfaceStatus(); - //utils->_displayInterfaceStatus(); - - // Check to see if we are waiting to set the default interface (on startup) - utils->_checkPendingDefaultInterface(); - utils->_unlock(); - - // wait for an event - if (poll(pfds, 2, -1) <= 0) - { - LOGWARN("Netlink socket poll returned no events"); - continue; - } - - utils->_lock(); - - if (pfds[0].revents) // Check for an event on our shutdown pipe - { - LOGINFO("Shutting down netlink monitor"); - break; - } - else if ((msgLength = netlink.read(msgBuffer, NETLINK_MESSAGE_BUFFER_SIZE)) < static_cast(sizeof(struct nlmsghdr)) ) - { - // Checking netlink messages. 0=no msg & peer shutdown, -1=error, see errno - LOGERR("Invalid netlink message (retval %d)", msgLength); - std::this_thread::sleep_for (std::chrono::milliseconds(100)); - } - else - { - // At least one event relating to ip address or connection status was received. Log what types. - netlink.displayMessages(msgBuffer, msgLength); - } - } - - utils->_unlock(); - - LOGWARN("%s : ENDED.", __FUNCTION__); - } - - /* - * Notifications from monitor thread - */ - - void NetUtils::_interfaceFlagsUpdated(struct iface_info &info) - { - JsonObject params; - - params["interface"] = info.m_if_descr; - params["status"] = INTERFACE_CONNECTED(info.m_if_flags) ? "CONNECTED" : "DISCONNECTED"; - Network::_instance->_asyncNotifyConnection(params); - } - - void NetUtils::_interfaceAddressUpdated(struct iface_info &info, std::string &addr) - { - JsonObject params; - - params["interface"] = info.m_if_descr; - params["ip6Address"] = info.ipv6Addr(); - params["ip4Address"] = info.ipv4Addr(); - params["status"] = (addr.length() == 0) ? "LOST" : "ACQUIRED"; - Network::_instance->_asyncNotifyIPAddr(params); - } - - /* - * Reset any information needed to trigger a notification message on update - */ - void NetUtils::_resetInterfaceList() - { - for ( auto &info : interfaceList) - { - info.m_if_flags = IFF_RUNNING; - info.m_if_addr = ""; - info.m_if_addrv6 = ""; - } - } - - /* - * Update the interfaces and notify of changes to connection state or ip address - */ - void NetUtils::_updateInterfaceStatus() - { - struct ifaddrs * pIntfList=NULL; - struct ifaddrs * pIntf=NULL; - - getifaddrs(&pIntfList); - - /* For each interface in our list, see if the address or connection status has changed */ - for ( auto &info : interfaceList) - { - int if_flags = 0; - char ipAddr[INET6_ADDRSTRLEN]; - std::string ipv4Addr = info.ipv4Addr(); - std::string ipv6Addr = info.ipv6Addr(); - if_flags = info.m_if_flags; - - info.m_if_addr = ""; - info.m_if_addrv6 = ""; - info.m_if_vname = ""; - info.m_if_vaddr = ""; - info.m_if_vaddrv6 = ""; - - for (pIntf = pIntfList; pIntf != NULL; pIntf = pIntf->ifa_next) - { - // Match the address to the interface via the interface name - // Note - we will assume the format of a virtual interface name is just 'name:0' (e.g. 'eth0:0') - // so we will match the interface name to the first part of the address interface name - if (0 == strncmp(info.m_if_name.c_str(), pIntf->ifa_name, info.m_if_name.length())) - { - info.m_if_flags = pIntf->ifa_flags; - - if ((pIntf->ifa_addr->sa_family == AF_INET) || - (pIntf->ifa_addr->sa_family == AF_INET6)) - { - inet_ntop(pIntf->ifa_addr->sa_family, &((struct sockaddr_in *)pIntf->ifa_addr)->sin_addr, ipAddr, INET6_ADDRSTRLEN); - - // If this is a virtual interface then the names will not match over the full length - if (0 != strcmp(info.m_if_name.c_str(), pIntf->ifa_name)) - { - info.m_if_vname = pIntf->ifa_name; - - if (pIntf->ifa_addr->sa_family == AF_INET) - { - info.m_if_vaddr = ipAddr; - } - else if (pIntf->ifa_addr->sa_family == AF_INET6) - { - info.m_if_vaddrv6 = ipAddr; - } - } - else - { - if (pIntf->ifa_addr->sa_family == AF_INET) - { - info.m_if_addr = ipAddr; - } - else if (pIntf->ifa_addr->sa_family == AF_INET6) - { - info.m_if_addrv6 = ipAddr; - } - } - } - } - } - - /* - * See if anything has changed - */ - if (INTERFACE_CONNECTED(info.m_if_flags) != INTERFACE_CONNECTED(if_flags)) - { - _interfaceFlagsUpdated(info); - } - - if (ipv4Addr != info.ipv4Addr()) - { - _interfaceAddressUpdated(info, info.ipv4Addr()); - } - else if (ipv6Addr != info.ipv6Addr()) - { - _interfaceAddressUpdated(info, info.ipv6Addr()); - } - } - } - - /* - * Check to see if we have stored a default interface to set when we are able - */ - void NetUtils::_checkPendingDefaultInterface() - { - if (!NetUtils::isConfiguredIPV6() && //TBD support for default interface on IPv6 - !m_pendingDefaultInterface.empty()) - { - LOGINFO("Try setting %s as the default interface...", m_pendingDefaultInterface.c_str()); - SetDefaultInterface(m_pendingDefaultInterface, true); - } - } - void NetUtils::getTmpFilename(const char *in, std::string &out) { std::lock_guard lock(m_counterProtect); @@ -1157,28 +241,5 @@ namespace WPEFramework { out += std::to_string(m_counter++); } - /* - * Debug info - */ - void NetUtils::_displayInterfaceStatus() - { - for ( auto &info : interfaceList) - { - LOGINFO ("<%.40s>, %.40s, UP = %s, RUNNING = %s\n", - info.m_if_descr.c_str(), - info.m_if_macaddr.c_str(), - info.m_if_flags & IFF_UP ? "YES" : "NO", - info.m_if_flags & IFF_RUNNING ? "YES" : "NO"); - LOGINFO ("> <%.40s>, IP v4 Address = %.40s, IP v6 Address = %.40s.\n", - info.m_if_name.c_str(), - info.m_if_addr.c_str(), - info.m_if_addrv6.c_str()); - if (!info.m_if_name.empty()) - LOGINFO ("> <%.40s>, IP v4 Address = %.40s, IP v6 Address = %.40s.\n", - info.m_if_vname.c_str(), - info.m_if_vaddr.c_str(), - info.m_if_vaddrv6.c_str()); - } - } } // namespace Plugin } // namespace WPEFramework diff --git a/Network/NetUtils.h b/Network/NetUtils.h index e6684ef406..f683012b89 100644 --- a/Network/NetUtils.h +++ b/Network/NetUtils.h @@ -21,8 +21,7 @@ #include #include -#include -#include +#include #include #include "utils.h" #include "NetUtilsNetlink.h" @@ -34,147 +33,30 @@ namespace WPEFramework { namespace Plugin { class Network; - class iface_info - { - public: - iface_info() {;} - virtual ~iface_info() {;} - - int m_if_index; // interface index - std::string m_if_descr; // interface descriptive name - std::string m_if_macaddr; // mac address - int m_if_flags; // SIOCGIFFLAGS - - std::string m_if_name; // interface name - std::string m_if_addr; // ip address v4 - std::string m_if_addrv6; // ip address v6 - - std::string m_if_vname; // virtual interface name - std::string m_if_vaddr; // virtual ip address v4 - std::string m_if_vaddrv6; // virtual ip address v6 - - std::string &ipv6Addr() - { - if (m_if_vname.length() && m_if_vaddrv6.length()) - { - return m_if_vaddrv6; - } - return m_if_addrv6; - } - std::string &ipv4Addr() - { - if (m_if_vname.length() && m_if_vaddr.length()) - { - return m_if_vaddr; - } - return m_if_addr; - } - std::string &interfaceName(bool physical = false) - { - if (physical || (m_if_vname.length() == 0)) - { - return m_if_name; // return the name of the physical interface - } - else - { - return m_if_vname; // return the name of the virtual interface - } - } - }; - - class NetUtils { private: - std::vector interfaceList; - - void _startMonitor(); - void _stopMonitor(); - static void _runMonitor(NetUtils *utils); - - void _resetInterfaceList(); - void _updateInterfaceStatus(); - void _interfaceFlagsUpdated(struct iface_info &info); - void _interfaceAddressUpdated(struct iface_info &info, std::string &addr); - void _checkPendingDefaultInterface(); - - void _displayInterfaceStatus(); - - bool _envGetInterfaceDescription(const char *name, std::string &description); - bool _getDefaultInterfaces(stringList &interfaces, stringList &gateways, stringList &descriptions, - bool physical = true, bool async = false); - - void _asyncQueryDefaultInterfaces(indexList &interfaces, stringList &gateways, bool &success); - static void _queryDefaultInterfaces(indexList &interfaces, stringList &gateways, bool &success); - - void _initialiseInterfaceList(); - bool _parseMACAddress(unsigned char *addr, int addlen, std::string &macAddr); - - void _lock() - { - m_dataProtect.lock(); - } - - void _unlock() - { - m_dataProtect.unlock(); - } - - void _setPendingDefaultInterface(std::string &interface) - { - std::lock_guard lock(m_dataProtect); - m_pendingDefaultInterface = interface; - } - void _clearPendingDefaultInterface() - { - std::lock_guard lock(m_dataProtect); - m_pendingDefaultInterface.empty(); - } + void _loadInterfaceDescriptions(); public: NetUtils(); virtual ~NetUtils(); void InitialiseNetUtils(); - bool GetInterfaceMACAddress(const std::string &interfaceDescription, std::string &macAddr); - bool GetInterfaceConnected(const std::string &interfaceDescription, bool &connected); - bool GetInterfaceName(const std::string &interfaceDescription, std::string &name, bool physical = false); - - bool GetDefaultInterfaceDescription(std::string &description); - bool SetDefaultInterface(std::string &interfaceDescription, bool onInit = false); - - bool getCMTSInterface(std::string &interface); - bool getCMTSGateway(std::string &gateway); + const std::string& getInterfaceDescription(const std::string name); static bool isIPV4(const std::string &address); static bool isIPV6(const std::string &address); - static bool isConfiguredIPV6(); - static bool isConfiguredIPV4(); static int execCmd(const char *command, std::string &output, bool *result = NULL, const char *outputfile = NULL); - static bool setDefaultGatewayPersistent(const char *interface); - static bool getDefaultGatewayPersistent(std::string &interface); static bool getFile(const char *filepath, std::string &contents, bool deleteFile = false); static bool envGetValue(const char *key, std::string &value); - static bool envCheckValue(const char *key, const char *value); - static bool envCheckBool(const char *key); static void getTmpFilename(const char *in, std::string &out); - static bool isValidEndpointURL(const std::string& endpoint); private: - std::thread m_netlinkMonitorThread; - std::recursive_mutex m_dataProtect; - std::mutex m_netlinkProtect; - int m_fdNlMonitorMsgPipe[2]; - bool m_monitorRunning; - std::string m_pendingDefaultInterface; - static unsigned int m_counter; static std::mutex m_counterProtect; - - static bool _createDefaultInterface(std::string &name); - static bool _deleteDefaultInterface(std::string &name, std::string &gateway); - static bool _isCharacterIllegal(const int& c); + std::map interface_descriptions; }; } // namespace Plugin } // namespace WPEFramework diff --git a/Network/NetworkTraceroute.cpp b/Network/NetworkTraceroute.cpp index 064e106a7e..41de148cba 100644 --- a/Network/NetworkTraceroute.cpp +++ b/Network/NetworkTraceroute.cpp @@ -38,12 +38,13 @@ namespace WPEFramework { bool Network::_doTraceNamedEndpoint(std::string &endpointName, int packets, JsonObject &response) { std::string endpoint = ""; + string interface; if (endpointName != "CMTS") // currently we only support CMTS { response["error"] = "Unsupported named endpoint"; } - else if (m_netUtils.getCMTSGateway(endpoint)) + else if (_getDefaultInterface(interface, endpoint)) { return _doTrace(endpoint, packets, response); } @@ -60,6 +61,7 @@ namespace WPEFramework { std::string output = ""; std::string error = ""; std::string interface = ""; + std::string gateway; int wait = DEFAULT_WAIT; int maxHops = DEFAULT_MAX_HOPS; int packetLen = DEFAULT_PACKET_LENGTH; @@ -75,13 +77,11 @@ namespace WPEFramework { { error = "Invalid endpoint"; } - else if (!NetUtils::isIPV4(endpoint) && - !NetUtils::isIPV6(endpoint) && - !NetUtils::isValidEndpointURL(endpoint)) + else if (!NetUtils::isIPV4(endpoint) && !NetUtils::isIPV6(endpoint)) { error = "Invalid endpoint"; } - else if (!m_netUtils.getCMTSInterface(interface)) + else if (!_getDefaultInterface(interface, gateway)) { error = "Could not get default interface"; } @@ -119,24 +119,8 @@ namespace WPEFramework { if (error.empty()) { - // We return the entire output of the trace command but since this contains newlines it is not valid as - // a json value so we will parse the output into an array of strings, one element for each line. - JsonArray list; - if (!output.empty()) - { - std::string::size_type last = 0; - std::string::size_type next = output.find('\n'); - while (next != std::string::npos) - { - list.Add(output.substr(last, next - last)); - last = next + 1; - next = output.find('\n', last); - } - list.Add(output.substr(last)); - } - response["target"] = endpoint; - response["results"] = list; + response["results"] = output; response["error"] = ""; return true; } @@ -150,4 +134,3 @@ namespace WPEFramework { } } // namespace Plugin } // namespace WPEFramework - diff --git a/Network/PingNotifier.cpp b/Network/PingNotifier.cpp index 432b083564..bbe75c8b63 100644 --- a/Network/PingNotifier.cpp +++ b/Network/PingNotifier.cpp @@ -19,11 +19,24 @@ #include "Network.h" - namespace WPEFramework { namespace Plugin { + // Not every character can be used for endpoint + bool is_character_illegal(const int& c) + { + //character must be "-./0-9a-zA-Z" + return (c < 45) || ((c > 58) && (c < 97)) || (c >= 122); + } + + // Check if valid - consist of only allowed characters + bool is_endpoint_valid(const std::string& endpoint) + { + //return std::find_if(endpoint.begin(), endpoint.end(), is_character_illegal) == endpoint.end(); + return (NetUtils::isIPV4(endpoint) || NetUtils::isIPV6(endpoint)); + } + /** * @ingroup SERVMGR_PING_API */ @@ -32,33 +45,22 @@ namespace WPEFramework LOGINFO("PingService calling ping"); JsonObject pingResult; std::string interface = ""; + std::string gateway; bool result = false; std::string outputFile; FILE *fp = NULL; pingResult["target"] = endPoint; - if(NetUtils::isIPV6(endPoint)) - { - LOGINFO("%s: Endpoint '%s' is ipv6", __FUNCTION__,endPoint.c_str()); - } - else if(NetUtils::isIPV4(endPoint)) - { - LOGINFO("%s: Endpoint '%s' is ipv4", __FUNCTION__,endPoint.c_str()); - } - else if(NetUtils::isValidEndpointURL(endPoint)) - { - LOGINFO("%s: Endpoint '%s' is url", __FUNCTION__,endPoint.c_str()); - } - else + if(!is_endpoint_valid(endPoint)) { - LOGERR("%s: Endpoint '%s' is not valid", __FUNCTION__,endPoint.c_str()); + LOGERR("%s: Endpoint is not valid string", __FUNCTION__); pingResult["success"] = false; pingResult["error"] = "invalid input for endpoint: " + endPoint; return pingResult; } - if (!m_netUtils.getCMTSInterface(interface)) + if (!_getDefaultInterface(interface, gateway)) { LOGERR("%s: Could not get default interface", __FUNCTION__); pingResult["success"] = false; @@ -195,8 +197,9 @@ namespace WPEFramework if (endpointName == "CMTS") { - std::string gateway; - if (m_netUtils.getCMTSGateway(gateway)) + std::string gateway = ""; + string interface; + if (_getDefaultInterface(interface, gateway)) { returnResult = _doPing(gateway, packets); } From 94484d5e3271d924239e1e67f9c7274f09a6a40d Mon Sep 17 00:00:00 2001 From: Ievgen Mutavchi Date: Tue, 9 Jun 2020 16:32:16 -0400 Subject: [PATCH 08/56] Add an option to configure resident app startup url --- WebKitBrowser/CMakeLists.txt | 1 + WebKitBrowser/ResidentApp.config | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/WebKitBrowser/CMakeLists.txt b/WebKitBrowser/CMakeLists.txt index 38d3b75bac..4da02aec6b 100644 --- a/WebKitBrowser/CMakeLists.txt +++ b/WebKitBrowser/CMakeLists.txt @@ -74,6 +74,7 @@ set(PLUGIN_APPS_USERAGENT ${PLUGIN_WEBKITBROWSER_USERAGENT} CACHE STRING "User a set(PLUGIN_RESIDENT_APP_AUTOSTART false CACHE STRING "Automatically start Resident App plugin") set(PLUGIN_RESIDENT_APP_USERAGENT ${PLUGIN_WEBKITBROWSER_USERAGENT} CACHE STRING "User agent string for Resident App") +set(PLUGIN_RESIDENT_APP_STARTURL "about:blank" CACHE STRING "Initial URL for Resident App plugin") set(PLUGIN_SEARCH_AND_DISCOVERY_APP_USERAGENT ${PLUGIN_WEBKITBROWSER_USERAGENT} CACHE STRING "User agent string for Search&Discovery App") set(PLUGIN_HTML_APP_USERAGENT ${PLUGIN_WEBKITBROWSER_USERAGENT} CACHE STRING "User agent string for Html App") set(PLUGIN_LIGHTNING_APP_USERAGENT ${PLUGIN_WEBKITBROWSER_USERAGENT} CACHE STRING "User agent string for Lightning App") diff --git a/WebKitBrowser/ResidentApp.config b/WebKitBrowser/ResidentApp.config index a23a491f30..f1eabf3075 100644 --- a/WebKitBrowser/ResidentApp.config +++ b/WebKitBrowser/ResidentApp.config @@ -14,7 +14,7 @@ end() ans(rootobject) map() - kv(url "about:blank") + kv(url "${PLUGIN_RESIDENT_APP_STARTURL}") kv(useragent ${PLUGIN_RESIDENT_APP_USERAGENT}) kv(injectedbundle "libWPEInjectedBundle${CMAKE_SHARED_LIBRARY_SUFFIX}") kv(transparent ${PLUGIN_WEBKITBROWSER_TRANSPARENT}) From 34fe38eacdfadf2becec08db6236dd6caa69021d Mon Sep 17 00:00:00 2001 From: Arun P Madhavan Date: Wed, 10 Jun 2020 05:14:50 +0000 Subject: [PATCH 09/56] DELIA-43557: Thunder Process is crashing when getDownloadedFirmwareInfo method is invoked Reason for change: Fixed build error due to copycat. Test Procedure: Please see ticket for details. Risks: Low Signed-off-by: Arun P Madhavan --- SystemServices/SystemServices.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SystemServices/SystemServices.cpp b/SystemServices/SystemServices.cpp index b79899515b..a7f6b38100 100644 --- a/SystemServices/SystemServices.cpp +++ b/SystemServices/SystemServices.cpp @@ -1284,7 +1284,7 @@ namespace WPEFramework { if (!Utils::fileExists(FWDNLDSTATUS_FILE_NAME)) { populateResponseWithError(SysSrv_FileNotPresent, response); - returnResponse(retStatus); + returnResponse(retStat); } if (getFileContent(FWDNLDSTATUS_FILE_NAME, lines)) { From 901fd8d5115c2d615b787f4d659340ecef0ff011 Mon Sep 17 00:00:00 2001 From: Ievgen Mutavchi Date: Wed, 10 Jun 2020 15:38:46 -0400 Subject: [PATCH 10/56] Don't inject js bindings into shadow contexts --- WebKitBrowser/InjectedBundle/main.cpp | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/WebKitBrowser/InjectedBundle/main.cpp b/WebKitBrowser/InjectedBundle/main.cpp index 2a89fd76eb..c23328a0cf 100644 --- a/WebKitBrowser/InjectedBundle/main.cpp +++ b/WebKitBrowser/InjectedBundle/main.cpp @@ -240,11 +240,13 @@ static WKBundlePageLoaderClientV6 s_pageLoaderClient = { nullptr, // didRunInsecureContentForFrame // didClearWindowObjectForFrame [](WKBundlePageRef page, WKBundleFrameRef frame, WKBundleScriptWorldRef scriptWorld, const void*) { - #if defined(ENABLE_AAMP_JSBINDINGS) - JavaScript::AAMP::LoadJSBindings(frame); - #endif - - JavaScript::BridgeObject::InjectJS(frame); + bool isMainCtx = (WKBundleFrameGetJavaScriptContext(frame) == WKBundleFrameGetJavaScriptContextForWorld(frame, scriptWorld)); + if (isMainCtx) { + #if defined(ENABLE_AAMP_JSBINDINGS) + JavaScript::AAMP::LoadJSBindings(frame); + #endif + JavaScript::BridgeObject::InjectJS(frame); + } // Add JS classes to JS world. ClassDefinition::Iterator ite = ClassDefinition::GetClassDefinitions(); From 3fb4a24f62c6a1e473f241cf2d0c1e9f03ec05d3 Mon Sep 17 00:00:00 2001 From: Anand Kandasamy Date: Thu, 11 Jun 2020 14:34:14 -0400 Subject: [PATCH 11/56] Revert "RDK-28421 : Use Logmilestone in rdkservices" This reverts commit b9abc8f328d17609250ba4b8f10e987774489b13. --- SystemServices/CMakeLists.txt | 3 --- SystemServices/SystemServices.cpp | 25 +++++++++++++++++-------- cmake/FindRdkLoggers.cmake | 14 -------------- 3 files changed, 17 insertions(+), 25 deletions(-) delete mode 100644 cmake/FindRdkLoggers.cmake diff --git a/SystemServices/CMakeLists.txt b/SystemServices/CMakeLists.txt index 0f626f66f9..d246cd9b47 100644 --- a/SystemServices/CMakeLists.txt +++ b/SystemServices/CMakeLists.txt @@ -70,9 +70,6 @@ else (CURL_FOUND) message ("Curl/libcurl required.") endif (CURL_FOUND) -find_package(RdkLoggers) -target_link_libraries(${MODULE_NAME} PRIVATE ${RDKL_LIBRARY}) - target_include_directories(${MODULE_NAME} PRIVATE ../helpers) target_include_directories(${MODULE_NAME} PRIVATE ./) diff --git a/SystemServices/SystemServices.cpp b/SystemServices/SystemServices.cpp index d6e413cc8b..7087272142 100644 --- a/SystemServices/SystemServices.cpp +++ b/SystemServices/SystemServices.cpp @@ -40,7 +40,6 @@ #include "SystemServices.h" #include "StateObserverHelper.h" #include "utils.h" -#include "rdk_logger_milestone.h" #if defined(USE_IARMBUS) || defined(USE_IARM_BUS) #include "libIARM.h" @@ -545,16 +544,26 @@ namespace WPEFramework { uint32_t SystemServices::requestSystemUptime(const JsonObject& parameters, JsonObject& response) { - std::string value = std::to_string(getUptimeMS() / 1e3); - value = value.erase(value.find_last_not_of("0") + 1); + struct timespec time; + bool result = false; - if (value.back() == '.') - value += '0'; + if (clock_gettime(CLOCK_MONOTONIC_RAW, &time) == 0) + { + float uptime = (float)time.tv_sec + (float)time.tv_nsec / 1e9; + std::string value = std::to_string(uptime); + value = value.erase(value.find_last_not_of("0") + 1); - response["systemUptime"] = value; - LOGINFO("uptime is %s seconds", value.c_str()); + if (value.back() == '.') + value += '0'; - returnResponse(true); + response["systemUptime"] = value; + LOGINFO("uptime is %s seconds", value.c_str()); + result = true; + } + else + LOGERR("unable to evaluate uptime by clock_gettime"); + + returnResponse(result); } /** diff --git a/cmake/FindRdkLoggers.cmake b/cmake/FindRdkLoggers.cmake deleted file mode 100644 index 4c73a7061e..0000000000 --- a/cmake/FindRdkLoggers.cmake +++ /dev/null @@ -1,14 +0,0 @@ -# - Try to find Log Milestome library -# Once done this will define -# LM_LIBRARY - The library needed to use LogMilestone library -# - -find_package(PkgConfig) - -find_library(RDKL_LIBRARY NAMES rdkloggers) - -set(RDKL_LIBRARY ${RDKL_LIBRARY} CACHE PATH "Path to rdkloggers library") - -mark_as_advanced(RDKL_LIBRARY) - - From d6d6a006481c5f63de751b6246e5c5dade3236a1 Mon Sep 17 00:00:00 2001 From: apatel859 Date: Thu, 11 Jun 2020 14:45:02 -0400 Subject: [PATCH 12/56] RDK-28793: HDMI-CEC OTP for xione Reason for change:update default vendorId Test Procedure: Refer JIRA. Risks: medium Signed-off-by: apatel859 --- HdmiCec_2/HdmiCec_2.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/HdmiCec_2/HdmiCec_2.cpp b/HdmiCec_2/HdmiCec_2.cpp index 002954e7cd..74fc1ff4f2 100644 --- a/HdmiCec_2/HdmiCec_2.cpp +++ b/HdmiCec_2/HdmiCec_2.cpp @@ -740,7 +740,7 @@ namespace WPEFramework LOGINFO("CEC_SETTING_OSD_NMAE not present set dafult value :%s\n ",osdName.toString().c_str()); isConfigAdded = true; } - unsigned int vendorId = 0x0019FB; + unsigned int vendorId = (defaultVendorId.at(0) <<16) | ( defaultVendorId.at(1) << 8 ) | defaultVendorId.at(2); if( parameters.HasLabel(CEC_SETTING_VENDOR_ID)) { getNumberParameter(CEC_SETTING_VENDOR_ID, vendorId); @@ -748,7 +748,7 @@ namespace WPEFramework } else { - LOGINFO("CEC_SETTING_OSD_NMAE not present set dafult value :\n "); + LOGINFO("CEC_SETTING_VENDOR_ID not present set dafult value :%x \n ",vendorId); parameters[CEC_SETTING_VENDOR_ID] = vendorId; isConfigAdded = true; } @@ -775,9 +775,12 @@ namespace WPEFramework file.Create(); JsonObject parameters; + unsigned int vendorId = (defaultVendorId.at(0) <<16) | ( defaultVendorId.at(1) << 8 ) | defaultVendorId.at(2); parameters[CEC_SETTING_ENABLED] = true; parameters[CEC_SETTING_OTP_ENABLED] = true; parameters[CEC_SETTING_OSD_NAME] = osdName.toString(); + parameters[CEC_SETTING_VENDOR_ID] = vendorId; + cecSettingEnabled = true; cecOTPSettingEnabled = true; parameters.IElement::ToFile(file); From c887d33bced09e2e7607e2919379600cd1dcf8cb Mon Sep 17 00:00:00 2001 From: Maksym Savchenko Date: Sat, 13 Jun 2020 01:07:38 +0000 Subject: [PATCH 13/56] RDK-28250 : RDK Services Warehouse.isClean: support new customer data file --- Warehouse/Warehouse.cpp | 57 ++++++++++++++++++++++++++++++++--------- 1 file changed, 45 insertions(+), 12 deletions(-) diff --git a/Warehouse/Warehouse.cpp b/Warehouse/Warehouse.cpp index e35b62497d..dd20925a7b 100644 --- a/Warehouse/Warehouse.cpp +++ b/Warehouse/Warehouse.cpp @@ -59,7 +59,7 @@ #define DEVICE_INFO_SCRIPT "sh /lib/rdk/getDeviceDetails.sh read" #define VERSION_FILE_NAME "/version.txt" -#define CUSTOM_DATA_FILE "/lib/rdk/cust-data.conf" +#define CUSTOM_DATA_FILE "/lib/rdk/wh_api_5.conf" #define LIGHT_RESET_SCRIPT "rm -rf /opt/netflix/* SD_CARD_MOUNT_PATH/netflix/* XDG_DATA_HOME/* XDG_CACHE_HOME/* XDG_CACHE_HOME/../.sparkStorage/ /opt/QT/home/data/* /opt/hn_service_settings.conf /opt/apps/common/proxies.conf /opt/lib/bluetooth" #define INTERNAL_RESET_SCRIPT "rm -rf /opt/drm /opt/www/whitebox /opt/www/authService && /rebootNow.sh -s WarehouseService &" @@ -530,7 +530,7 @@ namespace WPEFramework if (line.length() > 0) { char firstChar = line[0]; - if (firstChar != '#' && firstChar != '[') + if (firstChar != '#' && firstChar != '[' && line.substr(0, 2) != ". ") listPathsToRemove.emplace_back(line); } } @@ -576,20 +576,52 @@ namespace WPEFramework if (std::find_if(path.begin(), path.end(), [](char c) { return c == '$' || c == '*' || c == '?' || c == '+'; } ) != path.end()) { - // allow search recursively if path ends by '/*', otherwise, for cases like /*.ini, searching process will be done only for given path + std::list exclusions; + + if (path.find('|') != string::npos) + { + size_t last = 0, next = 0; + while ((next = path.find('|', last)) != string::npos) + { + std::string s = path.substr(last, next - last); + Utils::String::trim(s); + if (s.length() > 0) + exclusions.push_back(s); + last = next + 1; + } + std::string s = path.substr(last, next - last); + Utils::String::trim(s); + if (s.length() > 0) + exclusions.push_back(s); + path = exclusions.front(); + } + + // allow search recursively if path ends by '/*[|...]', otherwise, for cases like /*.ini, searching process will be done only by given path std::string maxDepth = "-maxdepth 1 "; - int pathLength = path.length(); - if (pathLength > 1 && path.find("/*", path.length() - 2) != std::string::npos) + if (path.find("/*", path.length() - 2) != std::string::npos) maxDepth = ""; std::string script = ". /etc/device.properties; fp=\""; script += path; script += "\"; p=${fp%/*}; f=${fp##*/}; find $p -mindepth 1 "; script += maxDepth; - script += "! -path \"*/\\.*\" -name \"$f\" 2>&1 | head -n 10"; + script += "! -path \"*/\\.*\" -name \"$f\""; + + for (auto i = exclusions.begin(); ++i != exclusions.end(); ) + { + auto exclusion = *i; + script += " ! -path \""; + Utils::String::trim(exclusion); + script += "$p/"; + script += exclusion; + script += "\""; + } + + script += " 2>/dev/null | head -n 10"; std::string result = Utils::cRunScript(script.c_str()); Utils::String::trim(result); + totalPathsCounter++; if (result.length() > 1) { std::stringstream ss(result); @@ -603,7 +635,7 @@ namespace WPEFramework existedObjects.Add(line); std::string strAge = std::to_string(age) + " seconds"; - LOGINFO("object %d by path '%s' : '%s' %s", ++totalPathsCounter, path.c_str(), line.c_str() + LOGINFO("object by path %d: '%s' : '%s' %s", totalPathsCounter, path.c_str(), line.c_str() , objectExists ? (std::string("exists and was modified more than ") + strAge + " ago").c_str() : (std::string("doesn't exist or was modified in ") + strAge).c_str()); @@ -611,12 +643,12 @@ namespace WPEFramework else { existedObjects.Add(line); - LOGINFO("object %d by path '%s' : '%s' exists", ++totalPathsCounter, path.c_str(), line.c_str()); + LOGINFO("object by path %d: '%s' : '%s' exists", totalPathsCounter, path.c_str(), line.c_str()); } } } else - LOGINFO("objects %d by path '%s' don't exist", ++totalPathsCounter, path.c_str()); + LOGINFO("objects by path %d: '%s' don't exist", totalPathsCounter, path.c_str()); } else { @@ -624,20 +656,21 @@ namespace WPEFramework if (objectExists) existedObjects.Add(path); + totalPathsCounter++; if (age > -1) { std::string strAge = std::to_string(age) + " seconds"; - LOGINFO("object %d by path '%s' : '%s' %s", ++totalPathsCounter, path.c_str(), path.c_str() + LOGINFO("object by path %d: '%s' %s", totalPathsCounter, path.c_str() , objectExists ? (std::string("exists and was modified more than ") + strAge + " ago").c_str() : (std::string("doesn't exist or was modified in ") + strAge).c_str()); } else - LOGINFO("object %d by path '%s' %s", ++totalPathsCounter, path.c_str(), objectExists ? "exists" : "doesn't exist"); + LOGINFO("object by path %d: '%s' %s", totalPathsCounter, path.c_str(), objectExists ? "exists" : "doesn't exist"); } } - LOGINFO("checked %d paths, found objects %d", totalPathsCounter, (int)existedObjects.Length()); + LOGINFO("checked %d paths, found %d objects", totalPathsCounter, (int)existedObjects.Length()); response[PARAM_SUCCESS] = true; response["files"] = existedObjects; response["clean"] = existedObjects.Length() == 0; From 2ba5d504f15fd83f37cda2931c5774d0ff976f40 Mon Sep 17 00:00:00 2001 From: Tony Ukken Date: Sun, 14 Jun 2020 16:42:31 -0400 Subject: [PATCH 14/56] DELIA-43804: Correct getDefaultInterface reporting with WIFI disabled --- Network/Network.cpp | 17 ++++++----------- Network/NetworkTraceroute.cpp | 6 +++--- Network/PingNotifier.cpp | 6 +++--- 3 files changed, 12 insertions(+), 17 deletions(-) diff --git a/Network/Network.cpp b/Network/Network.cpp index 18b928fbd2..46f98045ca 100644 --- a/Network/Network.cpp +++ b/Network/Network.cpp @@ -267,18 +267,13 @@ namespace WPEFramework LOGWARN ("Entering %s \n", __FUNCTION__); if (m_apiVersionNumber >= 1) { - IARM_BUS_NetSrvMgr_Iface_EventData_t param; - if (IARM_RESULT_SUCCESS == IARM_Bus_Call(IARM_BUS_NM_SRV_MGR_NAME, IARM_BUS_NETSRVMGR_API_getActiveInterface, (void*)¶m, sizeof(param))) + std::string interface; + std::string gateway; + if (_getDefaultInterface(interface, gateway)) { - LOGINFO("%s :: Interface = %s \n",__FUNCTION__,param.activeIface); - std::string interface = param.activeIface; - response["interface"] = interface; + response["interface"] = m_netUtils.getInterfaceDescription(interface); returnResponse(true); } - else - { - LOGWARN ("Call to %s for %s failed\n", IARM_BUS_NM_SRV_MGR_NAME, IARM_BUS_NETSRVMGR_API_getActiveInterface); - } } else { @@ -675,10 +670,10 @@ namespace WPEFramework IARM_BUS_NetSrvMgr_DefaultRoute_t defaultRoute = {0}; if (IARM_RESULT_SUCCESS == IARM_Bus_Call(IARM_BUS_NM_SRV_MGR_NAME, IARM_BUS_NETSRVMGR_API_getDefaultInterface, (void*)&defaultRoute, sizeof(defaultRoute))) { - LOGWARN ("Call to %s for %s returned interface = %s, gateway = %s\n", IARM_BUS_NM_SRV_MGR_NAME, IARM_BUS_NETSRVMGR_API_getDefaultInterface, defaultRoute.interface, defaultRoute.gateway); + LOGINFO ("Call to %s for %s returned interface = %s, gateway = %s\n", IARM_BUS_NM_SRV_MGR_NAME, IARM_BUS_NETSRVMGR_API_getDefaultInterface, defaultRoute.interface, defaultRoute.gateway); interface = defaultRoute.interface; gateway = defaultRoute.gateway; - return !interface.empty(); + return true; } else { diff --git a/Network/NetworkTraceroute.cpp b/Network/NetworkTraceroute.cpp index 7e1de215e7..312624d1f3 100644 --- a/Network/NetworkTraceroute.cpp +++ b/Network/NetworkTraceroute.cpp @@ -37,14 +37,14 @@ namespace WPEFramework { bool Network::_doTraceNamedEndpoint(std::string &endpointName, int packets, JsonObject &response) { + std::string interface; std::string endpoint = ""; - string interface; if (endpointName != "CMTS") // currently we only support CMTS { response["error"] = "Unsupported named endpoint"; } - else if (_getDefaultInterface(interface, endpoint)) + else if (_getDefaultInterface(interface, endpoint) && !endpoint.empty()) { return _doTrace(endpoint, packets, response); } @@ -83,7 +83,7 @@ namespace WPEFramework { { error = "Invalid endpoint"; } - else if (!_getDefaultInterface(interface, gateway)) + else if (!_getDefaultInterface(interface, gateway) || interface.empty()) { error = "Could not get default interface"; } diff --git a/Network/PingNotifier.cpp b/Network/PingNotifier.cpp index c55ab591cc..5eff653c24 100644 --- a/Network/PingNotifier.cpp +++ b/Network/PingNotifier.cpp @@ -58,7 +58,7 @@ namespace WPEFramework return pingResult; } - if (!_getDefaultInterface(interface, gateway)) + if (!_getDefaultInterface(interface, gateway) || interface.empty()) { LOGERR("%s: Could not get default interface", __FUNCTION__); pingResult["success"] = false; @@ -195,9 +195,9 @@ namespace WPEFramework if (endpointName == "CMTS") { + std::string interface; std::string gateway = ""; - string interface; - if (_getDefaultInterface(interface, gateway)) + if (_getDefaultInterface(interface, gateway) && !gateway.empty()) { returnResult = _doPing(gateway, packets); } From 17ea672a0ce46cadc5b531a971c4faac591f38cc Mon Sep 17 00:00:00 2001 From: Sergey Borushevsky Date: Mon, 15 Jun 2020 17:44:24 +0000 Subject: [PATCH 15/56] XRE-15671 : warehouse.getDeviceInfo in thunder differs from SM Got rid of "deviceInfo" container for values in getDeviceInfo call. --- Warehouse/Warehouse.cpp | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/Warehouse/Warehouse.cpp b/Warehouse/Warehouse.cpp index e35b62497d..d06e60b6b4 100644 --- a/Warehouse/Warehouse.cpp +++ b/Warehouse/Warehouse.cpp @@ -655,7 +655,7 @@ namespace WPEFramework resetDevice(suppressReboot); - response["PARAM_SUCCESS"] = true; + response[PARAM_SUCCESS] = true; returnResponse(true); } @@ -663,11 +663,9 @@ namespace WPEFramework { LOGINFO(); - JsonObject deviceInfo; - getDeviceInfo(deviceInfo); + getDeviceInfo(response); - response["deviceInfo"] = deviceInfo; - response["PARAM_SUCCESS"] = true; + response[PARAM_SUCCESS] = true; returnResponse(true); } From eb013b38ad8b24345b34f5048f640ee4d94902a8 Mon Sep 17 00:00:00 2001 From: Sergey Borushevsky Date: Mon, 15 Jun 2020 17:48:37 +0000 Subject: [PATCH 16/56] DELIA-43636 : Issue in dsHdmiEventHandler (HDCPprofile plugin) Registered for IARM_BUS_DSMGR_EVENT_HDCP_STATUS event, since otherwise notification with HDCP status will be sent in the case of hot-plug only. --- HdcpProfile/HdcpProfile.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/HdcpProfile/HdcpProfile.cpp b/HdcpProfile/HdcpProfile.cpp index 2210ebd22a..6ff442f2b4 100644 --- a/HdcpProfile/HdcpProfile.cpp +++ b/HdcpProfile/HdcpProfile.cpp @@ -71,6 +71,7 @@ namespace WPEFramework IARM_Result_t res; IARM_CHECK( IARM_Bus_RegisterEventHandler(IARM_BUS_DSMGR_NAME,IARM_BUS_DSMGR_EVENT_HDMI_HOTPLUG, dsHdmiEventHandler) ); + IARM_CHECK( IARM_Bus_RegisterEventHandler(IARM_BUS_DSMGR_NAME,IARM_BUS_DSMGR_EVENT_HDCP_STATUS, dsHdmiEventHandler) ); } void HdcpProfile::DeinitializeIARM() @@ -81,6 +82,7 @@ namespace WPEFramework { IARM_Result_t res; IARM_CHECK( IARM_Bus_UnRegisterEventHandler(IARM_BUS_DSMGR_NAME,IARM_BUS_DSMGR_EVENT_HDMI_HOTPLUG) ); + IARM_CHECK( IARM_Bus_UnRegisterEventHandler(IARM_BUS_DSMGR_NAME,IARM_BUS_DSMGR_EVENT_HDCP_STATUS) ); } } From 018b22989051861e8dc113fb92eb3f23234e5eb4 Mon Sep 17 00:00:00 2001 From: ravish-cts <54617660+ravish-cts@users.noreply.github.com> Date: Mon, 15 Jun 2020 23:29:02 +0530 Subject: [PATCH 17/56] BLDK-734:RDK License header changes-for Notice header update BLDK-734:RDK License header changes-for Notice header update Reason for change: RDK License header changes-for Notice header update Test Procedure: Jenkin build should pass after License header changes for RDK for listed repos Risks: None --- NOTICE | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/NOTICE b/NOTICE index 50eaef213a..5c1bd434c8 100644 --- a/NOTICE +++ b/NOTICE @@ -1,5 +1,11 @@ -Copyright 2018 RDK Management -Licensed under the Apache License, Version 2.0 +This component contains software that is Copyright (c) 2018 RDK Management. +The component is licensed to you under the Apache License, Version 2.0 (the "License"). +You may not use the component except in compliance with the License. + +The component may include material which is licensed under other licenses / copyrights as +listed below. Your use of this material within the component is also subject to the terms and +conditions of these licenses. The LICENSE file contains the text of all the licenses which apply +within this component. Copyright (C) 2012 Raphael Kubo da Costa Licensed under the BSD-2 license From 8e6b85e7d285fa4fe09682eb9880dce261677330 Mon Sep 17 00:00:00 2001 From: apatel859 Date: Tue, 16 Jun 2020 10:07:34 -0400 Subject: [PATCH 18/56] DELIA-43817: CEC is not working after power cycle Reason for change: Init isDeviceActiveSource to false. Test Procedure: Refer JIRA. Risks: Low Signed-off-by: apatel859 --- HdmiCec_2/HdmiCec_2.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/HdmiCec_2/HdmiCec_2.cpp b/HdmiCec_2/HdmiCec_2.cpp index 74fc1ff4f2..0fffab4854 100644 --- a/HdmiCec_2/HdmiCec_2.cpp +++ b/HdmiCec_2/HdmiCec_2.cpp @@ -67,7 +67,7 @@ static LogicalAddress logicalAddress = 0xF; static OSDName osdName = "TV Box"; static int32_t powerState = 1; static PowerStatus tvPowerState = 1; -static bool isDeviceActiveSource = true; +static bool isDeviceActiveSource = false; static bool isLGTvConnected = false; namespace WPEFramework @@ -946,9 +946,9 @@ namespace WPEFramework { LOGINFO("Command: sending GiveDevicePowerStatus \r\n"); smConnection->sendTo(LogicalAddress(LogicalAddress::TV), MessageEncoder().encode(GiveDevicePowerStatus()), 5000); - LOGINFO("Command: sending request active Source\r\n"); + LOGINFO("Command: sending request active Source isDeviceActiveSource is set to false\r\n"); smConnection->sendTo(LogicalAddress(LogicalAddress::BROADCAST), MessageEncoder().encode(RequestActiveSource()), 5000); - isDeviceActiveSource = true; + isDeviceActiveSource = false; } return; } @@ -1069,6 +1069,7 @@ namespace WPEFramework LOGINFO("Command: sending ActiveSource physical_addr :%s \r\n",physical_addr.toString().c_str()); smConnection->sendTo(LogicalAddress(LogicalAddress::BROADCAST), MessageEncoder().encode(ActiveSource(physical_addr)), 5000); usleep(10000); + isDeviceActiveSource = true; } LOGINFO("Command: sending GiveDevicePowerStatus \r\n"); smConnection->sendTo(LogicalAddress(LogicalAddress::TV), MessageEncoder().encode(GiveDevicePowerStatus()), 5000); From 38b92a96b9ccb2663776c1056f3faf3de85e27df Mon Sep 17 00:00:00 2001 From: Maksym Savchenko Date: Tue, 16 Jun 2020 22:09:51 +0300 Subject: [PATCH 19/56] DELIA-40884 : Prevent command injection in RDK Ping --- Network/PingNotifier.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Network/PingNotifier.cpp b/Network/PingNotifier.cpp index 5eff653c24..e8fb77c8aa 100644 --- a/Network/PingNotifier.cpp +++ b/Network/PingNotifier.cpp @@ -69,12 +69,12 @@ namespace WPEFramework char cmd [1000] = {0x0}; if (NetUtils::isIPV6(endPoint)) { - snprintf(cmd, sizeof(cmd), "ping6 -I %s -c %d -W 5 %s 2>&1", + snprintf(cmd, sizeof(cmd), "ping6 -I %s -c %d -W 5 '%s' 2>&1", interface.c_str(), packets, endPoint.c_str()); } else { - snprintf(cmd, sizeof(cmd), "ping -c %d -W 5 %s 2>&1", + snprintf(cmd, sizeof(cmd), "ping -c %d -W 5 '%s' 2>&1", packets, endPoint.c_str()); } From a8e55a7c2a3733cd5ad2945eaedbb4b7870e2d04 Mon Sep 17 00:00:00 2001 From: mfiess200 Date: Tue, 16 Jun 2020 16:49:49 -0400 Subject: [PATCH 20/56] initial event support --- RDKShell/RDKShell.cpp | 212 +++++++++++++++++++++++++++++++++++++++++- RDKShell/RDKShell.h | 40 ++++++++ 2 files changed, 249 insertions(+), 3 deletions(-) diff --git a/RDKShell/RDKShell.cpp b/RDKShell/RDKShell.cpp index c67b5c54db..5648f5941a 100755 --- a/RDKShell/RDKShell.cpp +++ b/RDKShell/RDKShell.cpp @@ -21,10 +21,8 @@ #include #include #include -#include -#include -#include #include +#include const short WPEFramework::Plugin::RDKShell::API_VERSION_NUMBER_MAJOR = 1; const short WPEFramework::Plugin::RDKShell::API_VERSION_NUMBER_MINOR = 0; @@ -37,6 +35,9 @@ const string WPEFramework::Plugin::RDKShell::RDKSHELL_METHOD_SET_FOCUS = "setFoc const string WPEFramework::Plugin::RDKShell::RDKSHELL_METHOD_KILL = "kill"; const string WPEFramework::Plugin::RDKShell::RDKSHELL_METHOD_ADD_KEY_INTERCEPT = "addKeyIntercept"; const string WPEFramework::Plugin::RDKShell::RDKSHELL_METHOD_REMOVE_KEY_INTERCEPT = "removeKeyIntercept"; +const string WPEFramework::Plugin::RDKShell::RDKSHELL_METHOD_ADD_KEY_LISTENER = "addKeyListener"; +const string WPEFramework::Plugin::RDKShell::RDKSHELL_METHOD_REMOVE_KEY_LISTENER = "removeKeyListener"; +const string WPEFramework::Plugin::RDKShell::RDKSHELL_METHOD_INJECT_KEY = "injectKey"; const string WPEFramework::Plugin::RDKShell::RDKSHELL_METHOD_GET_SCREEN_RESOLUTION = "getScreenResolution"; const string WPEFramework::Plugin::RDKShell::RDKSHELL_METHOD_SET_SCREEN_RESOLUTION = "setScreenResolution"; const string WPEFramework::Plugin::RDKShell::RDKSHELL_METHOD_CREATE_DISPLAY = "createDisplay"; @@ -57,6 +58,8 @@ using namespace std; using namespace RdkShell; extern int gCurrentFramerate; +#define ANY_KEY 65536 + namespace WPEFramework { namespace Plugin { @@ -103,6 +106,7 @@ namespace WPEFramework { std::string clientidentifier = serviceConfig["clientidentifier"].String(); gRdkShellMutex.lock(); RdkShell::CompositorController::createDisplay(service->Callsign(), clientidentifier); + RdkShell::CompositorController::addListener(clientidentifier, mShell.mEventListener); gRdkShellMutex.unlock(); } } @@ -129,6 +133,7 @@ namespace WPEFramework { std::string clientidentifier = serviceConfig["clientidentifier"].String(); gRdkShellMutex.lock(); RdkShell::CompositorController::kill(clientidentifier); + RdkShell::CompositorController::removeListener(clientidentifier, mShell.mEventListener); gRdkShellMutex.unlock(); } } @@ -140,6 +145,8 @@ namespace WPEFramework { { LOGINFO("ctor"); RDKShell::_instance = this; + mEventListener = std::make_shared(this); + mRemoteShell = false; registerMethod(RDKSHELL_METHOD_MOVE_TO_FRONT, &RDKShell::moveToFrontWrapper, this); registerMethod(RDKSHELL_METHOD_MOVE_TO_BACK, &RDKShell::moveToBackWrapper, this); @@ -148,6 +155,9 @@ namespace WPEFramework { registerMethod(RDKSHELL_METHOD_KILL, &RDKShell::killWrapper, this); registerMethod(RDKSHELL_METHOD_ADD_KEY_INTERCEPT, &RDKShell::addKeyInterceptWrapper, this); registerMethod(RDKSHELL_METHOD_REMOVE_KEY_INTERCEPT, &RDKShell::removeKeyInterceptWrapper, this); + registerMethod(RDKSHELL_METHOD_ADD_KEY_LISTENER, &RDKShell::addKeyListenersWrapper, this); + registerMethod(RDKSHELL_METHOD_REMOVE_KEY_LISTENER, &RDKShell::removeKeyListenersWrapper, this); + registerMethod(RDKSHELL_METHOD_INJECT_KEY, &RDKShell::injectKeyWrapper, this); registerMethod(RDKSHELL_METHOD_GET_SCREEN_RESOLUTION, &RDKShell::getScreenResolutionWrapper, this); registerMethod(RDKSHELL_METHOD_SET_SCREEN_RESOLUTION, &RDKShell::setScreenResolutionWrapper, this); registerMethod(RDKSHELL_METHOD_CREATE_DISPLAY, &RDKShell::createDisplayWrapper, this); @@ -171,6 +181,7 @@ namespace WPEFramework { mClientsMonitor->Release(); RDKShell::_instance = nullptr; mRemoteShell = false; + mEventListener = nullptr; } const string RDKShell::Initialize(PluginHost::IShell* service ) @@ -213,6 +224,31 @@ namespace WPEFramework { return(string("{\"service\": \"") + SERVICE_NAME + string("\"}")); } + void RDKShell::RdkShellListener::onApplicationLaunched(const std::string& client) + { + std::cout << "RDKShell onApplicationLaunched event received ..." << client << std::endl; + } + + void RDKShell::RdkShellListener::onApplicationConnected(const std::string& client) + { + std::cout << "RDKShell onApplicationConnected event received ..." << client << std::endl; + } + + void RDKShell::RdkShellListener::onApplicationDisconnected(const std::string& client) + { + std::cout << "RDKShell onApplicationDisconnected event received ..." << client << std::endl; + } + + void RDKShell::RdkShellListener::onApplicationTerminated(const std::string& client) + { + std::cout << "RDKShell onApplicationTerminated event received ..." << client << std::endl; + } + + void RDKShell::RdkShellListener::onApplicationFirstFrame(const std::string& client) + { + std::cout << "RDKShell onApplicationFirstFrame event received ..." << client << std::endl; + } + // Registered methods (wrappers) begin uint32_t RDKShell::moveToFrontWrapper(const JsonObject& parameters, JsonObject& response) { @@ -380,6 +416,87 @@ namespace WPEFramework { returnResponse(result); } + uint32_t RDKShell::addKeyListenersWrapper(const JsonObject& parameters, JsonObject& response) + { + LOGINFOMETHOD(); + bool result = true; + + if (!parameters.HasLabel("keys")) + { + result = false; + response["message"] = "please specify keys"; + } + if (!parameters.HasLabel("client")) + { + result = false; + response["message"] = "please specify client"; + } + + if (result) + { + const JsonArray keys = parameters["keys"].Array(); + const string client = parameters["client"].String(); + result = addKeyListeners(client, keys); + if (false == result) { + response["message"] = "failed to add key listeners"; + } + } + returnResponse(result); + } + + uint32_t RDKShell::removeKeyListenersWrapper(const JsonObject& parameters, JsonObject& response) + { + LOGINFOMETHOD(); + bool result = true; + + if (!parameters.HasLabel("keys")) + { + result = false; + response["message"] = "please specify keys"; + } + if (!parameters.HasLabel("client")) + { + result = false; + response["message"] = "please specify client"; + } + + if (result) + { + const JsonArray keys = parameters["keys"].Array(); + const string client = parameters["client"].String(); + result = removeKeyListeners(client, keys); + if (false == result) { + response["message"] = "failed to remove key listeners"; + } + } + returnResponse(result); + } + + uint32_t RDKShell::injectKeyWrapper(const JsonObject& parameters, JsonObject& response) + { + LOGINFOMETHOD(); + bool result = true; + + if (!parameters.HasLabel("keyCode")) + { + result = false; + response["message"] = "please specify keyCode"; + } + + if (result) + { + //optional param? + const JsonArray modifiers = parameters.HasLabel("modifiers") ? parameters["modifiers"].Array() : JsonArray(); + + const uint32_t keyCode = parameters["keyCode"].Number(); + result = injectKey(keyCode, modifiers); + if (false == result) { + response["message"] = "failed to inject key"; + } + } + returnResponse(result); + } + uint32_t RDKShell::getScreenResolutionWrapper(const JsonObject& parameters, JsonObject& response) { LOGINFOMETHOD(); @@ -817,6 +934,7 @@ namespace WPEFramework { { bool ret = false; gRdkShellMutex.lock(); + RdkShell::CompositorController::removeListener(client, mEventListener); ret = CompositorController::kill(client); gRdkShellMutex.unlock(); return ret; @@ -848,6 +966,88 @@ namespace WPEFramework { return ret; } + bool RDKShell::addKeyListeners(const string& client, const JsonArray& keys) + { + gRdkShellMutex.lock(); + for (int i=0; i properties; + if (keyInfo.HasLabel("activate")) + { + bool activate = keyInfo["activate"].Boolean(); + properties["activate"] = activate; + } + if (keyInfo.HasLabel("propagate")) + { + bool propagate = keyInfo["propagate"].Boolean(); + properties["propagate"] = propagate; + } + CompositorController::addKeyListener(client, keyCode, flags, properties); + } + } + gRdkShellMutex.unlock(); + return true; + } + + bool RDKShell::removeKeyListeners(const string& client, const JsonArray& keys) + { + gRdkShellMutex.lock(); + for (int i=0; i #include "Module.h" #include "utils.h" +#include +#include +#include #include "AbstractPlugin.h" namespace WPEFramework { @@ -51,6 +54,10 @@ namespace WPEFramework { static const string RDKSHELL_METHOD_KILL; static const string RDKSHELL_METHOD_ADD_KEY_INTERCEPT; static const string RDKSHELL_METHOD_REMOVE_KEY_INTERCEPT; + static const string RDKSHELL_METHOD_ADD_KEY_LISTENER; + static const string RDKSHELL_METHOD_REMOVE_KEY_LISTENER; + static const string RDKSHELL_METHOD_INJECT_KEY; + static const string RDKSHELL_METHOD_INJECT_KEYS; static const string RDKSHELL_METHOD_GET_SCREEN_RESOLUTION; static const string RDKSHELL_METHOD_SET_SCREEN_RESOLUTION; static const string RDKSHELL_METHOD_CREATE_DISPLAY; @@ -77,6 +84,10 @@ namespace WPEFramework { uint32_t killWrapper(const JsonObject& parameters, JsonObject& response); uint32_t addKeyInterceptWrapper(const JsonObject& parameters, JsonObject& response); uint32_t removeKeyInterceptWrapper(const JsonObject& parameters, JsonObject& response); + uint32_t addKeyListenersWrapper(const JsonObject& parameters, JsonObject& response); + uint32_t removeKeyListenersWrapper(const JsonObject& parameters, JsonObject& response); + uint32_t injectKeyWrapper(const JsonObject& parameters, JsonObject& response); + uint32_t injectKeysWrapper(const JsonObject& parameters, JsonObject& response); uint32_t getScreenResolutionWrapper(const JsonObject& parameters, JsonObject& response); uint32_t setScreenResolutionWrapper(const JsonObject& parameters, JsonObject& response); uint32_t createDisplayWrapper(const JsonObject& parameters, JsonObject& response); @@ -104,6 +115,11 @@ namespace WPEFramework { bool kill(const string& client); bool addKeyIntercept(const uint32_t& keyCode, const JsonArray& modifiers, const string& client); bool removeKeyIntercept(const uint32_t& keyCode, const JsonArray& modifiers, const string& client); + bool addKeyListeners(const string& client, const JsonArray& listeners); + bool removeKeyListeners(const string& client, const JsonArray& listeners); + bool addAnyKeyListener(const string& client, const JsonArray& listeners); + bool injectKey(const uint32_t& keyCode, const JsonArray& modifiers); + bool injectKeys(const JsonArray& keyInputs); bool getScreenResolution(JsonObject& out); bool setScreenResolution(const unsigned int w, const unsigned int h); bool createDisplay(const string& client, const string& displayName); @@ -122,6 +138,29 @@ namespace WPEFramework { private/*classes */: + class RdkShellListener : public RdkShell::RdkShellEventListener { + + public: + RdkShellListener(RDKShell* shell) + : mShell(*shell) + { + } + + ~RdkShellListener() + { + } + + // rdkshell events listeners + virtual void onApplicationLaunched(const std::string& client); + virtual void onApplicationConnected(const std::string& client); + virtual void onApplicationDisconnected(const std::string& client); + virtual void onApplicationTerminated(const std::string& client); + virtual void onApplicationFirstFrame(const std::string& client); + + private: + RDKShell& mShell; + }; + class MonitorClients : public PluginHost::IPlugin::INotification { private: MonitorClients() = delete; @@ -153,6 +192,7 @@ namespace WPEFramework { private/*members*/: bool mRemoteShell; MonitorClients* mClientsMonitor; + std::shared_ptr mEventListener; //std::mutex m_callMutex; }; } // namespace Plugin From 5f0bea5822f3322e9b407f1a5e07d1109318ddd5 Mon Sep 17 00:00:00 2001 From: Sergey Borushevsky Date: Wed, 17 Jun 2020 20:40:20 +0000 Subject: [PATCH 21/56] DELIA-43141 : ClassCastException on getCecAddresses call during HdmiCecManager initialization Changed output of getCECAddresses to match legacy service. --- HdmiCec/HdmiCec.cpp | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/HdmiCec/HdmiCec.cpp b/HdmiCec/HdmiCec.cpp index 8cecae617a..de0892a775 100644 --- a/HdmiCec/HdmiCec.cpp +++ b/HdmiCec/HdmiCec.cpp @@ -503,22 +503,16 @@ namespace WPEFramework JsonObject CECAddress; LOGINFO("Entered getCECAddresses "); - JsonArray pa; - pa.Add((physicalAddress >> 24) & 0xff); - pa.Add((physicalAddress >> 16) & 0xff); - pa.Add((physicalAddress >> 8) & 0xff); - pa.Add( physicalAddress & 0xff); + char pa[32] = {0}; + snprintf(pa, sizeof(pa), "\\u00%02X\\u00%02X\\u00%02X\\u00%02X", (physicalAddress >> 24) & 0xff, (physicalAddress >> 16) & 0xff, (physicalAddress >> 8) & 0xff, physicalAddress & 0xff); - CECAddress["physicalAddress"] = pa; + CECAddress["physicalAddress"] = (const char *)pa; JsonObject logical; logical["deviceType"] = logicalAddressDeviceType; logical["logicalAddress"] = logicalAddress; - JsonArray logicalArray; - logicalArray.Add(logical); - - CECAddress["logicalAddresses"] = logicalArray; + CECAddress["logicalAddresses"] = logical; LOGWARN("getCECAddresses: physicalAddress from QByteArray : %x %x %x %x ", (physicalAddress >> 24) & 0xFF, (physicalAddress >> 16) & 0xFF, (physicalAddress >> 8) & 0xFF, (physicalAddress) & 0xFF); LOGWARN("getCECAddresses: logical address: %x ", logicalAddress); From 883064823e8fe815443c41d05954bc2ce052b9e8 Mon Sep 17 00:00:00 2001 From: bobseamon Date: Thu, 18 Jun 2020 11:33:38 -0400 Subject: [PATCH 22/56] DELIA-43963 Added links to RDKCentral documentation until docs are migrated to code for auto generation of docs --- AVInput/doc/AVInput.md | 1 + ActivityMonitor/doc/ActivityMonitor.md | 1 + Bluetooth/doc/Bluetooth.md | 2 ++ ContinueWatching/doc/ContinueWatching.md | 2 ++ ControlService/doc/ControlService.md | 2 ++ DataCapture/doc/DataCapture.md | 2 ++ DeviceDiagnostics/doc/DeviceDiagnostics.md | 2 ++ DisplaySettings/doc/DisplaySettings.md | 2 ++ FrameRate/doc/FrameRate.md | 2 ++ FrontPanel/doc/FrontPanel.md | 2 ++ HdcpProfile/doc/HDCPProfile.md | 2 ++ HdmiCec/doc/HDMICEC.md | 2 ++ HdmiInput/doc/HDMIInput.md | 2 ++ LoggingPreferences/doc/LoggingPreferences.md | 2 ++ Network/doc/Network.md | 2 ++ RDKShell/doc/RDKShell.md | 2 ++ RemoteActionMapping/doc/RemoteActionMapping.md | 2 ++ ScreenCapture/doc/ScreenCapture.md | 2 ++ StateObserver/doc/StateObserver.md | 2 ++ SystemServices/doc/System.md | 2 ++ Timer/doc/Timer.md | 2 ++ UserPreferences/doc/UserPreferences.md | 2 ++ Warehouse/doc/Warehouse.md | 2 ++ WifiManager/doc/Wifi.md | 2 ++ XCast/doc/XCast.md | 2 ++ 25 files changed, 48 insertions(+) create mode 100644 AVInput/doc/AVInput.md create mode 100644 ActivityMonitor/doc/ActivityMonitor.md create mode 100644 Bluetooth/doc/Bluetooth.md create mode 100644 ContinueWatching/doc/ContinueWatching.md create mode 100644 ControlService/doc/ControlService.md create mode 100644 DataCapture/doc/DataCapture.md create mode 100644 DeviceDiagnostics/doc/DeviceDiagnostics.md create mode 100644 DisplaySettings/doc/DisplaySettings.md create mode 100644 FrameRate/doc/FrameRate.md create mode 100644 FrontPanel/doc/FrontPanel.md create mode 100644 HdcpProfile/doc/HDCPProfile.md create mode 100644 HdmiCec/doc/HDMICEC.md create mode 100644 HdmiInput/doc/HDMIInput.md create mode 100644 LoggingPreferences/doc/LoggingPreferences.md create mode 100644 Network/doc/Network.md create mode 100644 RDKShell/doc/RDKShell.md create mode 100644 RemoteActionMapping/doc/RemoteActionMapping.md create mode 100644 ScreenCapture/doc/ScreenCapture.md create mode 100644 StateObserver/doc/StateObserver.md create mode 100644 SystemServices/doc/System.md create mode 100644 Timer/doc/Timer.md create mode 100644 UserPreferences/doc/UserPreferences.md create mode 100644 Warehouse/doc/Warehouse.md create mode 100644 WifiManager/doc/Wifi.md create mode 100644 XCast/doc/XCast.md diff --git a/AVInput/doc/AVInput.md b/AVInput/doc/AVInput.md new file mode 100644 index 0000000000..59fa202c85 --- /dev/null +++ b/AVInput/doc/AVInput.md @@ -0,0 +1 @@ +[AVInput Docs](https://wiki.rdkcentral.com/display/RDK/AV+Input) diff --git a/ActivityMonitor/doc/ActivityMonitor.md b/ActivityMonitor/doc/ActivityMonitor.md new file mode 100644 index 0000000000..ea3a3da1eb --- /dev/null +++ b/ActivityMonitor/doc/ActivityMonitor.md @@ -0,0 +1 @@ +[Activity Monitor docs](https://wiki.rdkcentral.com/display/RDK/ActivityMonitor) diff --git a/Bluetooth/doc/Bluetooth.md b/Bluetooth/doc/Bluetooth.md new file mode 100644 index 0000000000..8b62ca2f7b --- /dev/null +++ b/Bluetooth/doc/Bluetooth.md @@ -0,0 +1,2 @@ +[Bluetooth](https://wiki.rdkcentral.com/display/RDK/Bluetooth) + diff --git a/ContinueWatching/doc/ContinueWatching.md b/ContinueWatching/doc/ContinueWatching.md new file mode 100644 index 0000000000..83e27c2a13 --- /dev/null +++ b/ContinueWatching/doc/ContinueWatching.md @@ -0,0 +1,2 @@ +[Continue Watching](https://wiki.rdkcentral.com/display/RDK/Continue+Watching) + diff --git a/ControlService/doc/ControlService.md b/ControlService/doc/ControlService.md new file mode 100644 index 0000000000..34dee4d27b --- /dev/null +++ b/ControlService/doc/ControlService.md @@ -0,0 +1,2 @@ +[Control Service](https://wiki.rdkcentral.com/display/RDK/Control+Service) + diff --git a/DataCapture/doc/DataCapture.md b/DataCapture/doc/DataCapture.md new file mode 100644 index 0000000000..9554f2f8c8 --- /dev/null +++ b/DataCapture/doc/DataCapture.md @@ -0,0 +1,2 @@ +[DataCapture](https://wiki.rdkcentral.com/display/RDK/DataCapture) + diff --git a/DeviceDiagnostics/doc/DeviceDiagnostics.md b/DeviceDiagnostics/doc/DeviceDiagnostics.md new file mode 100644 index 0000000000..63202605bb --- /dev/null +++ b/DeviceDiagnostics/doc/DeviceDiagnostics.md @@ -0,0 +1,2 @@ +[DeviceDiagnostics](https://wiki.rdkcentral.com/display/RDK/Device+Diagnostics) + diff --git a/DisplaySettings/doc/DisplaySettings.md b/DisplaySettings/doc/DisplaySettings.md new file mode 100644 index 0000000000..a050970a91 --- /dev/null +++ b/DisplaySettings/doc/DisplaySettings.md @@ -0,0 +1,2 @@ +[DisplaySettings](https://wiki.rdkcentral.com/display/RDK/Display+Settings) + diff --git a/FrameRate/doc/FrameRate.md b/FrameRate/doc/FrameRate.md new file mode 100644 index 0000000000..2cbb39c4da --- /dev/null +++ b/FrameRate/doc/FrameRate.md @@ -0,0 +1,2 @@ +[FrameRate](https://wiki.rdkcentral.com/display/RDK/Frame+Rate) + diff --git a/FrontPanel/doc/FrontPanel.md b/FrontPanel/doc/FrontPanel.md new file mode 100644 index 0000000000..8a20da363b --- /dev/null +++ b/FrontPanel/doc/FrontPanel.md @@ -0,0 +1,2 @@ +[FrontPane](https://wiki.rdkcentral.com/display/RDK/FrontPanel) + diff --git a/HdcpProfile/doc/HDCPProfile.md b/HdcpProfile/doc/HDCPProfile.md new file mode 100644 index 0000000000..66878d5647 --- /dev/null +++ b/HdcpProfile/doc/HDCPProfile.md @@ -0,0 +1,2 @@ +[HDCPProfile](https://wiki.rdkcentral.com/display/RDK/HDCPProfile) + diff --git a/HdmiCec/doc/HDMICEC.md b/HdmiCec/doc/HDMICEC.md new file mode 100644 index 0000000000..abb3ed17bb --- /dev/null +++ b/HdmiCec/doc/HDMICEC.md @@ -0,0 +1,2 @@ +[HDMI CEC](https://wiki.rdkcentral.com/display/RDK/HDMI++CEC) + diff --git a/HdmiInput/doc/HDMIInput.md b/HdmiInput/doc/HDMIInput.md new file mode 100644 index 0000000000..b8d1afe100 --- /dev/null +++ b/HdmiInput/doc/HDMIInput.md @@ -0,0 +1,2 @@ +[HDMI Input](https://wiki.rdkcentral.com/display/RDK/HDMI++Input) + diff --git a/LoggingPreferences/doc/LoggingPreferences.md b/LoggingPreferences/doc/LoggingPreferences.md new file mode 100644 index 0000000000..59c154bbc6 --- /dev/null +++ b/LoggingPreferences/doc/LoggingPreferences.md @@ -0,0 +1,2 @@ +[Logging Preferences](https://wiki.rdkcentral.com/display/RDK/Logging++Preferences) + diff --git a/Network/doc/Network.md b/Network/doc/Network.md new file mode 100644 index 0000000000..994a8558ba --- /dev/null +++ b/Network/doc/Network.md @@ -0,0 +1,2 @@ +[Network](https://wiki.rdkcentral.com/display/RDK/Network) + diff --git a/RDKShell/doc/RDKShell.md b/RDKShell/doc/RDKShell.md new file mode 100644 index 0000000000..0a129af280 --- /dev/null +++ b/RDKShell/doc/RDKShell.md @@ -0,0 +1,2 @@ +[RDKShell](https://wiki.rdkcentral.com/display/RDK/RDK+Shell) + diff --git a/RemoteActionMapping/doc/RemoteActionMapping.md b/RemoteActionMapping/doc/RemoteActionMapping.md new file mode 100644 index 0000000000..006c6bff13 --- /dev/null +++ b/RemoteActionMapping/doc/RemoteActionMapping.md @@ -0,0 +1,2 @@ +[RemoteActionMapping](https://wiki.rdkcentral.com/display/RDK/Remote+Action+Mapping) + diff --git a/ScreenCapture/doc/ScreenCapture.md b/ScreenCapture/doc/ScreenCapture.md new file mode 100644 index 0000000000..01d289ace7 --- /dev/null +++ b/ScreenCapture/doc/ScreenCapture.md @@ -0,0 +1,2 @@ +[Screen Capture](https://wiki.rdkcentral.com/display/RDK/Screen+Capture) + diff --git a/StateObserver/doc/StateObserver.md b/StateObserver/doc/StateObserver.md new file mode 100644 index 0000000000..dc12abf01d --- /dev/null +++ b/StateObserver/doc/StateObserver.md @@ -0,0 +1,2 @@ +[State Observer](https://wiki.rdkcentral.com/display/RDK/State+Observer) + diff --git a/SystemServices/doc/System.md b/SystemServices/doc/System.md new file mode 100644 index 0000000000..235742a73a --- /dev/null +++ b/SystemServices/doc/System.md @@ -0,0 +1,2 @@ +[System](https://wiki.rdkcentral.com/pages/viewpage.action?pageId=105516014) + diff --git a/Timer/doc/Timer.md b/Timer/doc/Timer.md new file mode 100644 index 0000000000..441ac49b18 --- /dev/null +++ b/Timer/doc/Timer.md @@ -0,0 +1,2 @@ +[Sleep/Wake Timer](https://wiki.rdkcentral.com/pages/viewpage.action?pageId=105516016) + diff --git a/UserPreferences/doc/UserPreferences.md b/UserPreferences/doc/UserPreferences.md new file mode 100644 index 0000000000..5a49c77b9d --- /dev/null +++ b/UserPreferences/doc/UserPreferences.md @@ -0,0 +1,2 @@ +[User Preferences](https://wiki.rdkcentral.com/display/RDK/User+Preferences) + diff --git a/Warehouse/doc/Warehouse.md b/Warehouse/doc/Warehouse.md new file mode 100644 index 0000000000..94cb96b837 --- /dev/null +++ b/Warehouse/doc/Warehouse.md @@ -0,0 +1,2 @@ +[Warehouse](https://wiki.rdkcentral.com/display/RDK/Warehouse) + diff --git a/WifiManager/doc/Wifi.md b/WifiManager/doc/Wifi.md new file mode 100644 index 0000000000..477c922b85 --- /dev/null +++ b/WifiManager/doc/Wifi.md @@ -0,0 +1,2 @@ +[Wifi](https://wiki.rdkcentral.com/display/RDK/Wifi) + diff --git a/XCast/doc/XCast.md b/XCast/doc/XCast.md new file mode 100644 index 0000000000..8dacb1e47e --- /dev/null +++ b/XCast/doc/XCast.md @@ -0,0 +1,2 @@ +[Xcast](https://wiki.rdkcentral.com/display/RDK/XCast) + From 9530558a52a291785610603207eb13ca87cb61e8 Mon Sep 17 00:00:00 2001 From: Anand Kandasamy Date: Thu, 18 Jun 2020 11:47:53 -0400 Subject: [PATCH 23/56] CPC-2681: Enable screen capture plugin for thunder proxy --- services.cmake | 1 - 1 file changed, 1 deletion(-) diff --git a/services.cmake b/services.cmake index c316002646..b20842e638 100644 --- a/services.cmake +++ b/services.cmake @@ -63,7 +63,6 @@ add_definitions (-DHAS_API_WEBSOCKET_PROXY) option(HAS_API_WEBSOCKET_PROXY "HAS_API_WEBSOCKET_PROXY" OFF) add_definitions (-DHAS_API_SCREEN_CAPTURE) -option(HAS_API_SCREEN_CAPTURE "HAS_API_SCREEN_CAPTURE" OFF) add_definitions (-DHAS_API_STORAGE_MANAGER) option(HAS_API_STORAGE_MANAGER "HAS_API_STORAGE_MANAGER" OFF) From 7523f280c5d33e640b5e37cfa6a390715d3304d4 Mon Sep 17 00:00:00 2001 From: bobseamon Date: Thu, 18 Jun 2020 11:59:12 -0400 Subject: [PATCH 24/56] DELIA-43963 Fixed typo --- FrontPanel/doc/FrontPanel.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/FrontPanel/doc/FrontPanel.md b/FrontPanel/doc/FrontPanel.md index 8a20da363b..98a6f0ebcd 100644 --- a/FrontPanel/doc/FrontPanel.md +++ b/FrontPanel/doc/FrontPanel.md @@ -1,2 +1,2 @@ -[FrontPane](https://wiki.rdkcentral.com/display/RDK/FrontPanel) +[FrontPanel](https://wiki.rdkcentral.com/display/RDK/FrontPanel) From bc63a0c63068b4adcbb7967980467d5d4c7fc084 Mon Sep 17 00:00:00 2001 From: mstrozyn Date: Fri, 19 Jun 2020 12:00:09 +0200 Subject: [PATCH 25/56] RDK-28967: Extend getDeviceInfo method in system thunder plugin Reason for change: Add manufacturer data fields Test Procedure: Build and test based on README.md Risks: Low Signed-off-by: Mariusz Strozynski --- SystemServices/SystemServices.cpp | 74 ++++++++++++++++++++++++------- SystemServices/SystemServices.h | 5 +++ helpers/SystemServicesHelper.cpp | 3 +- helpers/SystemServicesHelper.h | 3 +- realtek.cmake | 3 ++ 5 files changed, 69 insertions(+), 19 deletions(-) diff --git a/SystemServices/SystemServices.cpp b/SystemServices/SystemServices.cpp index 128d43bcf3..a137e9af92 100644 --- a/SystemServices/SystemServices.cpp +++ b/SystemServices/SystemServices.cpp @@ -53,6 +53,10 @@ #include "powerstate.h" #endif /* HAS_API_SYSTEM && HAS_API_POWERSTATE */ +#ifdef ENABLE_DEVICE_MANUFACTURER_INFO +#include "mfrMgr.h" +#endif + using namespace std; #define SYSSRV_MAJOR_VERSION 1 @@ -207,6 +211,8 @@ namespace WPEFramework { cTimer SystemServices::m_operatingModeTimer; int SystemServices::m_remainingDuration = 0; JsonObject SystemServices::_systemParams; + const string SystemServices::MODEL_NAME = "modelName"; + const string SystemServices::HARDWARE_ID = "hardwareID"; IARM_Bus_SYSMgr_GetSystemStates_Param_t SystemServices::paramGetSysState = {}; static void _powerEventHandler(const char *owner, IARM_EventId_t eventId, @@ -577,29 +583,63 @@ namespace WPEFramework { JsonObject& response) { bool retAPIStatus = false; - string queryParams = parameters["params"].String(); - removeCharsFromString(queryParams, "[\"]"); - string methodType = queryParams; - string respBuffer; - string fileName = "/tmp/." + methodType; - LOGERR("accessing fileName : %s\n", fileName.c_str()); + string queryParams = parameters["params"].String(); + removeCharsFromString(queryParams, "[\"]"); +#ifdef ENABLE_DEVICE_MANUFACTURER_INFO + if (!queryParams.compare(MODEL_NAME) || !queryParams.compare(HARDWARE_ID)) { + returnResponse(getManufacturerData(queryParams, response)); + } +#endif + string methodType = queryParams; + string respBuffer; + string fileName = "/tmp/." + methodType; + LOGERR("accessing fileName : %s\n", fileName.c_str()); if (Utils::fileExists(fileName.c_str())) { - respBuffer = collectDeviceInfo(methodType); - removeCharsFromString(respBuffer, "\n\r"); - LOGERR("respBuffer : %s\n", respBuffer.c_str()); - if (respBuffer.length() <= 0) { - populateResponseWithError(SysSrv_FileAccessFailed, - response); - } else { - response[methodType.c_str()] = respBuffer; - retAPIStatus = true; - } + respBuffer = collectDeviceInfo(methodType); + removeCharsFromString(respBuffer, "\n\r"); + LOGERR("respBuffer : %s\n", respBuffer.c_str()); + if (respBuffer.length() <= 0) { + populateResponseWithError(SysSrv_FileAccessFailed, response); } else { - populateResponseWithError(SysSrv_FileNotPresent, response); + response[methodType.c_str()] = respBuffer; + retAPIStatus = true; + } + } else { + populateResponseWithError(SysSrv_FileNotPresent, response); } returnResponse(retAPIStatus); } +#ifdef ENABLE_DEVICE_MANUFACTURER_INFO + bool SystemServices::getManufacturerData(const string& parameter, JsonObject& response) + { + LOGWARN("SystemService getDeviceInfo query %s", parameter.c_str()); + + IARM_Bus_MFRLib_GetSerializedData_Param_t param; + param.bufLen = 0; + param.type = mfrSERIALIZED_TYPE_MANUFACTURER; + if (!parameter.compare(MODEL_NAME)) { + param.type = mfrSERIALIZED_TYPE_MODELNAME; + } else if (!parameter.compare(HARDWARE_ID)) { + param.type = mfrSERIALIZED_TYPE_SERIALNUMBER; + } + IARM_Result_t result = IARM_Bus_Call(IARM_BUS_MFRLIB_NAME, IARM_BUS_MFRLIB_API_GetSerializedData, ¶m, sizeof(param)); + param.buffer[param.bufLen] = '\0'; + + LOGWARN("SystemService getDeviceInfo result %s", param.buffer); + + bool status = false; + if (result == IARM_RESULT_SUCCESS) { + response[parameter.c_str()] = string(param.buffer); + status = true; + } else { + populateResponseWithError(SysSrv_ManufacturerDataReadFailed, response); + } + + return status; + } +#endif + /*** * @brief : Checks if Moca is Enabled or Not. * diff --git a/SystemServices/SystemServices.h b/SystemServices/SystemServices.h index cfc35f291b..d1fce3e508 100644 --- a/SystemServices/SystemServices.h +++ b/SystemServices/SystemServices.h @@ -94,6 +94,8 @@ namespace WPEFramework { /* TODO: Need to decide whether needed or not since setProperty and getProperty functionalities are XRE/RTRemote dependent. */ static JsonObject _systemParams; + static const string MODEL_NAME; + static const string HARDWARE_ID; // We do not allow this plugin to be copied !! SystemServices(const SystemServices&) = delete; @@ -107,6 +109,9 @@ namespace WPEFramework { static void startModeTimer(int duration); static void stopModeTimer(); static void updateDuration(); +#ifdef ENABLE_DEVICE_MANUFACTURER_INFO + bool getManufacturerData(const string& parameter, JsonObject& response); +#endif public: SystemServices(); virtual ~SystemServices(); diff --git a/helpers/SystemServicesHelper.cpp b/helpers/SystemServicesHelper.cpp index b7d81a8c44..b50f325cc9 100644 --- a/helpers/SystemServicesHelper.cpp +++ b/helpers/SystemServicesHelper.cpp @@ -40,7 +40,8 @@ std::map ErrCodeMap = { {SysSrv_Unexpected, "Unexpected error"}, {SysSrv_SupportNotAvailable, "Support not available/enabled"}, {SysSrv_LibcurlError, "LIbCurl service error"}, - {SysSrv_DynamicMemoryAllocationFailed, "Dynamic Memory Allocation Failed"} + {SysSrv_DynamicMemoryAllocationFailed, "Dynamic Memory Allocation Failed"}, + {SysSrv_ManufacturerDataReadFailed, "Manufacturer Data Read Failed"} }; std::string getErrorDescription(int errCode) diff --git a/helpers/SystemServicesHelper.h b/helpers/SystemServicesHelper.h index 62f85ccf21..7f4d854fda 100644 --- a/helpers/SystemServicesHelper.h +++ b/helpers/SystemServicesHelper.h @@ -89,7 +89,8 @@ enum SysSrv_ErrorCode { SysSrv_Unexpected, SysSrv_SupportNotAvailable, SysSrv_LibcurlError, - SysSrv_DynamicMemoryAllocationFailed + SysSrv_DynamicMemoryAllocationFailed, + SysSrv_ManufacturerDataReadFailed }; enum FirmwareUpdateState { diff --git a/realtek.cmake b/realtek.cmake index 5d72084da2..c1c28949cf 100644 --- a/realtek.cmake +++ b/realtek.cmake @@ -122,6 +122,9 @@ if (BUILD_XI1) add_definitions (-DENABLE_VREX_SERVICE) add_definitions (-DRF4CE_API) add_definitions (-DHAS_STATE_OBSERVER) + + message("Building with device manufacturer info") + add_definitions (-DENABLE_DEVICE_MANUFACTURER_INFO) endif() if(SCREEN_CAPTURE) From 1abe150cf25587d78547cdce65f988e0f471a775 Mon Sep 17 00:00:00 2001 From: nrajan002c Date: Fri, 19 Jun 2020 14:49:27 +0000 Subject: [PATCH 26/56] TextToSpeech Service Initial implementation submit Signed-off-by: nrajan002c --- CMakeLists.txt | 5 + TextToSpeech/CMakeLists.txt | 45 ++ TextToSpeech/Module.cpp | 22 + TextToSpeech/Module.h | 29 + TextToSpeech/README.md | 20 + TextToSpeech/TextToSpeech.config | 3 + TextToSpeech/TextToSpeech.h | 333 +++++++++ TextToSpeech/TextToSpeech.json | 9 + TextToSpeech/impl/TTSCommon.h | 68 ++ TextToSpeech/impl/TTSManager.cpp | 747 +++++++++++++++++++ TextToSpeech/impl/TTSManager.h | 116 +++ TextToSpeech/impl/TTSSession.cpp | 324 +++++++++ TextToSpeech/impl/TTSSession.h | 105 +++ TextToSpeech/impl/TTSSpeaker.cpp | 918 ++++++++++++++++++++++++ TextToSpeech/impl/TTSSpeaker.h | 189 +++++ TextToSpeech/impl/TextToSpeechCommon.h | 85 +++ TextToSpeech/impl/logger.cpp | 169 +++++ TextToSpeech/impl/logger.h | 92 +++ TextToSpeech/test/CMakeLists.txt | 34 + TextToSpeech/test/Module.h | 27 + TextToSpeech/test/TTSServiceAPITest.cpp | 785 ++++++++++++++++++++ 21 files changed, 4125 insertions(+) create mode 100644 TextToSpeech/CMakeLists.txt create mode 100644 TextToSpeech/Module.cpp create mode 100644 TextToSpeech/Module.h create mode 100644 TextToSpeech/README.md create mode 100644 TextToSpeech/TextToSpeech.config create mode 100644 TextToSpeech/TextToSpeech.h create mode 100644 TextToSpeech/TextToSpeech.json create mode 100644 TextToSpeech/impl/TTSCommon.h create mode 100644 TextToSpeech/impl/TTSManager.cpp create mode 100644 TextToSpeech/impl/TTSManager.h create mode 100644 TextToSpeech/impl/TTSSession.cpp create mode 100644 TextToSpeech/impl/TTSSession.h create mode 100644 TextToSpeech/impl/TTSSpeaker.cpp create mode 100644 TextToSpeech/impl/TTSSpeaker.h create mode 100644 TextToSpeech/impl/TextToSpeechCommon.h create mode 100644 TextToSpeech/impl/logger.cpp create mode 100644 TextToSpeech/impl/logger.h create mode 100644 TextToSpeech/test/CMakeLists.txt create mode 100644 TextToSpeech/test/Module.h create mode 100644 TextToSpeech/test/TTSServiceAPITest.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 42f37d3105..1f99333062 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -46,6 +46,7 @@ option(PLUGIN_CONTROLSERVICE "Include ControlService plugin" ON) option(PLUGIN_VIDEOAPPLICATIONEVENTS "Include video application events plugin" OFF) option(PLUGIN_INACTIVITYNOTIFIER "Include InactivityNotifier plugin" OFF) option(PLUGIN_RDKSHELL "Include RDKShell plugin" OFF) +option(PLUGIN_TEXTTOSPEECH "Include TextToSpeech plugin" OFF) # Library installation section string(TOLOWER ${NAMESPACE} STORAGE_DIRECTORY) @@ -287,6 +288,10 @@ endif() add_subdirectory(WebKitBrowser) endif() + if(PLUGIN_TEXTTOSPEECH) + add_subdirectory(TextToSpeech) + endif() + if(WPEFRAMEWORK_CREATE_IPKG_TARGETS) set(CPACK_GENERATOR "DEB") set(CPACK_DEB_COMPONENT_INSTALL ON) diff --git a/TextToSpeech/CMakeLists.txt b/TextToSpeech/CMakeLists.txt new file mode 100644 index 0000000000..2cdf82edda --- /dev/null +++ b/TextToSpeech/CMakeLists.txt @@ -0,0 +1,45 @@ +set(PLUGIN_NAME TextToSpeech) +set(MODULE_NAME ${NAMESPACE}${PLUGIN_NAME}) + +find_package(${NAMESPACE}Plugins REQUIRED) + +#if(BUILD_TESTS) + add_subdirectory(test) +#endif() + +add_library(${MODULE_NAME} SHARED + TextToSpeech.cpp + Module.cpp + impl/TTSManager.cpp + impl/TTSSession.cpp + impl/TTSSpeaker.cpp + impl/logger.cpp + ) +set_target_properties(${MODULE_NAME} PROPERTIES + CXX_STANDARD 11 + CXX_STANDARD_REQUIRED YES) + +list(APPEND CMAKE_MODULE_PATH + "${CMAKE_CURRENT_SOURCE_DIR}/cmake/") + +find_program(PKG_CONFIG_EXECUTABLE NAMES pkg-config DOC "pkg-config executable") +execute_process(COMMAND ${PKG_CONFIG_EXECUTABLE} gstreamer-1.0 --cflags OUTPUT_VARIABLE GSTREAMER_CFLAGS OUTPUT_STRIP_TRAILING_WHITESPACE) +execute_process(COMMAND ${PKG_CONFIG_EXECUTABLE} gstreamer-1.0 --libs OUTPUT_VARIABLE GSTREAMER_LDLAGS OUTPUT_STRIP_TRAILING_WHITESPACE) + +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${GSTREAMER_CFLAGS}") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${GSTREAMER_LDLAGS}") + +add_definitions (-DUSE_GST1) +add_definitions (-DBCM_NEXUS) + +find_package(Curl) +find_package(GLIB REQUIRED) +find_package(IARMBus) + +target_include_directories(${MODULE_NAME} PRIVATE ${GLIB_INCLUDE_DIRS} ${IARMBUS_INCLUDE_DIRS} ../helpers) +target_link_libraries(${MODULE_NAME} PRIVATE ${NAMESPACE}Plugins::${NAMESPACE}Plugins ${CURL_LIBRARY} ${GLIB_LIBRARIES} ${IARMBUS_LIBRARIES}) + +install(TARGETS ${MODULE_NAME} + DESTINATION lib/${STORAGE_DIRECTORY}/plugins) + +write_config(${PLUGIN_NAME}) diff --git a/TextToSpeech/Module.cpp b/TextToSpeech/Module.cpp new file mode 100644 index 0000000000..ce759b615f --- /dev/null +++ b/TextToSpeech/Module.cpp @@ -0,0 +1,22 @@ +/** +* If not stated otherwise in this file or this component's LICENSE +* file the following copyright and licenses apply: +* +* Copyright 2019 RDK Management +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +**/ + +#include "Module.h" + +MODULE_NAME_DECLARATION(BUILD_REFERENCE) diff --git a/TextToSpeech/Module.h b/TextToSpeech/Module.h new file mode 100644 index 0000000000..3fa00a9e56 --- /dev/null +++ b/TextToSpeech/Module.h @@ -0,0 +1,29 @@ +/** +* If not stated otherwise in this file or this component's LICENSE +* file the following copyright and licenses apply: +* +* Copyright 2019 RDK Management +* +* 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. +**/ + +#pragma once +#ifndef MODULE_NAME +#define MODULE_NAME TextToSpeech +#endif + +#include +#include + +#undef EXTERNAL +#define EXTERNAL diff --git a/TextToSpeech/README.md b/TextToSpeech/README.md new file mode 100644 index 0000000000..91342a86b0 --- /dev/null +++ b/TextToSpeech/README.md @@ -0,0 +1,20 @@ +----------------- +Build: + +bitbake thunder-plugins + +----------------- +Test: + +---acquire Resource-- +curl --header "Content-Type: application/json" -X POST http://localhost:9998/jsonrpc -d '{"jsonrpc":"2.0","id":"3","method": "org.rdk.TTSResource.1.acquireResource", "params":{"appId":32}}'/jsonrpc + +---releaseResource-- +curl --header "Content-Type: application/json" -X POST http://localhost:9998/jsonrpc -d '{"jsonrpc":"2.0","id":"3","method": "org.rdk.TTSResource.1.releaseResource", "params":{"appId":32}}'/jsonrpc + +---claimResource-- +curl --header "Content-Type: application/json" -X POST http://localhost:9998/jsonrpc -d '{"jsonrpc":"2.0","id":"3","method": "org.rdk.TTSResource.1.claimResource", "params":{"appId":32}}'/jsonrpc + +---claimResource-- +curl --header "Content-Type: application/json" -X POST http://localhost:9998/jsonrpc -d '{"jsonrpc":"2.0","id":"3","method": "org.rdk.TTSResource.1.isSessionActiveForApp", "params":{"appId":32}}'/jsonrpc + diff --git a/TextToSpeech/TextToSpeech.config b/TextToSpeech/TextToSpeech.config new file mode 100644 index 0000000000..6e59029f31 --- /dev/null +++ b/TextToSpeech/TextToSpeech.config @@ -0,0 +1,3 @@ +set (autostart true) +set (preconditions Platform) +set (callsign "org.rdk.TextToSpeech") diff --git a/TextToSpeech/TextToSpeech.h b/TextToSpeech/TextToSpeech.h new file mode 100644 index 0000000000..d1737d5f9b --- /dev/null +++ b/TextToSpeech/TextToSpeech.h @@ -0,0 +1,333 @@ +/** +* If not stated otherwise in this file or this component's LICENSE +* file the following copyright and licenses apply: +* +* Copyright 2019 RDK Management +* +* 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. +**/ + +/* + * If not stated otherwise in this file or this component's license file the + * following copyright and licenses apply: + * + * Copyright 2018 RDK Management + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file TTSResource.cpp + * @brief Thunder Plugin based Implementation for TTS service API's (RDK-RDK-25832). + */ + +/** + @mainpage Text To Speech (TTS) + + TTSResource TTS Resource Service provides APIs for the arbitrators + * (ex: Receiver / Optimus App Manager) to reserve the TTS resource for a particular + * Application. Only when a TTS resource is reserved for an app, the service can be + * used by the apps. (i.e., if the app has a session, its session will + * be made "active". If the app does not have a session, whenever the + * session is created, it will be made active. + */ + +#pragma once + +#include +#include +#include "Module.h" +#include "impl/TTSManager.h" +#include "impl/TTSSession.h" +#include "tracing/Logging.h" +#include "utils.h" +#include "AbstractPlugin.h" +#include "TTSServicesCommon.h" +#include "impl/TextToSpeechCommon.h" +#include + +namespace WPEFramework { + namespace Plugin { + + class TTSConnectionCallback; + class TTSSessionServiceCallback; + /** + * @brief WPEFramework class declarioin for TextToSpeech + **/ + // This is a server for a JSONRPC communication channel. + // For a plugin to be capable to handle JSONRPC, inherit from PluginHost::JSONRPC. + // By inheriting from this class, the plugin realizes the interface PluginHost::IDispatcher. + // This realization of this interface implements, by default, the following methods on this plugin + // - exists + // - register + // - unregister + // Any other methood to be handled by this plugin can be added can be added by using the + // templated methods Register on the PluginHost::JSONRPC class. + // As the registration/unregistration of notifications is realized by the class PluginHost::JSONRPC, + // this class exposes a public method called, Notify(), using this methods, all subscribed clients + // will receive a JSONRPC message as a notification, in case this method is called. + class TextToSpeech: public AbstractPlugin { + private: + typedef Core::JSON::String JString; + typedef Core::JSON::ArrayType JStringArray; + typedef Core::JSON::Boolean JBool; + + // We do not allow this plugin to be copied !! + TextToSpeech(const TextToSpeech&) = delete; + TextToSpeech& operator=(const TextToSpeech&) = delete; + + // TTS Global APIs + uint32_t enableTTS(const JsonObject& parameters, JsonObject& response); + uint32_t listVoices(const JsonObject& parameters, JsonObject& response); + uint32_t setTTSConfiguration(const JsonObject& parameters, JsonObject& response); + uint32_t getTTSConfiguration(const JsonObject& parameters, JsonObject& response); + bool isTTSEnabled(const JsonObject& parameters ,JsonObject& response); + uint32_t isSessionActiveForApp(const JsonObject& parameters, JsonObject& response); + + + // Resource management APIs + uint32_t acquireResource(const JsonObject& parameters, JsonObject& response); + uint32_t claimResource(const JsonObject& parameters, JsonObject& response); + uint32_t releaseResource(const JsonObject& parameters, JsonObject& response); + + //Session management APIs + uint32_t createSession(const JsonObject& parameters, JsonObject& response); + uint32_t destroySession(const JsonObject& parameters, JsonObject& response); + uint32_t isActiveSession(const JsonObject& parameters, JsonObject& response); + uint32_t setPreemptiveSpeak(const JsonObject& parameters, JsonObject& response); + uint32_t requestExtendedEvents(const JsonObject& parameters, JsonObject& response); + + // Speak APIs + uint32_t speak(const JsonObject& parameters, JsonObject& response); + uint32_t pause(const JsonObject& parameters, JsonObject& response); + uint32_t resume(const JsonObject& parameters, JsonObject& response); + uint32_t abort(const JsonObject& parameters, JsonObject& response); + uint32_t isSpeaking(const JsonObject& parameters, JsonObject& response); + uint32_t getSpeechState(const JsonObject& parameters, JsonObject& response); + + //version number API's + uint32_t getApiVersionNumber(); + void setApiVersionNumber(uint32_t apiVersionNumber); + + TTS::ResourceAllocationPolicy getResourceAllocationPolicy(); + void setResponseArray(JsonObject& response, const char* key, const std::vector& items); + void notify(std::string eventname, JsonObject& param); + //End methods + private: + static uint32_t m_serviceObjCount; + static TTS::TTSManager* m_ttsManager; + static TTSConnectionCallback* m_connectionCallback; + static TTSSessionServiceCallback* m_sessionCallback; + TTS::Configuration m_config; + static TTS::ResourceAllocationPolicy m_policy; + static bool m_ttsEnabled; + uint32_t m_apiVersionNumber; + std::map m_sessionMap; + + public: + TextToSpeech(); + virtual ~TextToSpeech(); + void restoreTextToSpeech(); + }; + + /** + * @brief WPEFramework class declaration for TTSResourceCallback + **/ + class TTSConnectionCallback : public TTS::TTSConnectionCallback + { + public: + TTSConnectionCallback(TextToSpeech* callback) + { + m_eventHandler = callback; + } + + void onTTSServerConnected() + { + LOGINFO("TTS Server Connected"); + m_eventHandler->restoreTextToSpeech(); + } + + void onTTSServerClosed() + { + LOGINFO("TTS Server Closed"); + } + + void onTTSStateChanged(bool state) + { + JsonObject params; + params["state"] = JsonValue((bool)state); + LOGINFO("TTS state changed to '%d'\n", state); + m_eventHandler->Notify("onTTSStateChanged", params); + } + + void onVoiceChanged(std::string voice) + { + LOGINFO("TTS voice changed (%s)", voice.c_str()); + } + private: + TextToSpeech *m_eventHandler; + }; + + class TTSSessionServiceCallback : public TTS::TTSSessionCallback + { + public: + TTSSessionServiceCallback(TextToSpeech* callback ) + { + m_eventHandler = callback; + } + + ~TTSSessionServiceCallback() {} + + void onResourceAcquired(uint32_t appId, uint32_t sessionId) + { + LOGINFO("onResourceAcquired appId(%d) sessionId(%d)", appId, sessionId); + JsonObject params; + params["appId"] = JsonValue((int)appId); + params["sessionId"] = JsonValue((int)sessionId); + m_eventHandler->Notify("onResourceAcquired", params); + } + + void onResourceReleased(uint32_t appId, uint32_t sessionId) + { + LOGINFO("onResourceReleased appId(%d) sessionId(%d)", appId, sessionId); + JsonObject params; + params["appId"] = JsonValue((int)appId); + params["sessionId"] = JsonValue((int)sessionId); + m_eventHandler->Notify("onResourceReleased", params); + + } + + void onTTSSessionCreated(uint32_t appId, uint32_t sessionId) + { + LOGINFO("onTTSSessionCreated appId(%d) sessionId(%d)", appId, sessionId); + JsonObject params; + params["appId"] = JsonValue((int)appId); + params["sessionId"] = JsonValue((int)sessionId); + m_eventHandler->Notify("onTTSSessionCreated", params); + } + + + void onWillSpeak(uint32_t appId, uint32_t sessionId, TTS::SpeechData &data) + { + LOGINFO("onWillSpeak appId(%d) sessionId(%d)", appId, sessionId); + JsonObject params; + params["appId"] = JsonValue((int)appId); + params["sessionId"] = JsonValue((int)sessionId); + params["speechId"] = JsonValue((int)data.id); + params["text"] = data.text; + m_eventHandler->Notify("onWillSpeak", params); + } + + void onSpeechStart(uint32_t appId, uint32_t sessionId, TTS::SpeechData &data) + { + LOGINFO("onSpeechStart(%d) sessionId(%d)", appId, sessionId); + JsonObject params; + params["appId"] = JsonValue((int)appId); + params["sessionId"] = JsonValue((int)sessionId); + params["speechId"] = JsonValue((int)data.id); + params["text"] = data.text; + m_eventHandler->Notify("onSpeechStart", params); + } + + void onSpeechPause(uint32_t appId, uint32_t sessionId, uint32_t speechId) + { + LOGINFO("onSpeechPause appId(%d) sessionId(%d) speechId(%d)", appId, sessionId, speechId); + JsonObject params; + params["appId"] = JsonValue((int)appId); + params["sessionId"] = JsonValue((int)sessionId); + params["speechId"] = JsonValue((int)speechId); + m_eventHandler->Notify("onSpeechPause", params); + } + + void onSpeechResume(uint32_t appId, uint32_t sessionId, uint32_t speechId) + { + LOGINFO("onSpeechResume appId(%d) sessionId(%d) speechId(%d)", appId, sessionId, speechId); + JsonObject params; + params["appId"] = JsonValue((int)appId); + params["sessionId"] = JsonValue((int)sessionId); + params["speechId"] = JsonValue((int)speechId); + m_eventHandler->Notify("onSpeechResume", params); } + + void onSpeechCancelled(uint32_t appId, uint32_t sessionId, std::string id) + { + char *token = strtok((char*)id.c_str(), ","); + uint32_t speechId = 0; + while(token) { + speechId = atol(token); + token = strtok(NULL, ","); + } + LOGINFO("onSpeechCancelled appId(%d) sessionId(%d) speechId(%d)", appId, sessionId, speechId); + JsonObject params; + params["appId"] = JsonValue((int)appId); + params["sessionId"] = JsonValue((int)sessionId); + params["speechId"] = JsonValue((int)speechId); + m_eventHandler->Notify("onSpeechCancelled", params); + } + + void onSpeechInterrupted(uint32_t appId, uint32_t sessionId, uint32_t speechId) + { + LOGINFO("onSpeechInterrupted appId(%d) sessionId(%d) speechId(%d)", appId, sessionId, speechId); + JsonObject params; + params["appId"] = JsonValue((int)appId); + params["sessionId"] = JsonValue((int)sessionId); + params["speechId"] = JsonValue((int)speechId); + m_eventHandler->Notify("onSpeechInterrupted", params); + } + + void onNetworkError(uint32_t appId, uint32_t sessionId, uint32_t speechId) + { + LOGINFO("onNetworkError appId(%d) sessionId(%d) speechId(%d)", appId, sessionId, speechId); + JsonObject params; + params["appId"] = JsonValue((int)appId); + params["sessionId"] = JsonValue((int)sessionId); + params["speechId"] = JsonValue((int)speechId); + m_eventHandler->Notify("onNetworkError", params); + } + + void onPlaybackError(uint32_t appId, uint32_t sessionId, uint32_t speechId) + { + LOGINFO("onPlaybackError appId(%d) sessionId(%d) speechId(%d)", appId, sessionId, speechId); + JsonObject params; + params["appId"] = JsonValue((int)appId); + params["sessionId"] = JsonValue((int)sessionId); + params["speechId"] = JsonValue((int)speechId); + m_eventHandler->Notify("onPlaybackError", params); + } + + void onSpeechComplete(uint32_t appId, uint32_t sessionId, TTS::SpeechData &data) + { + LOGINFO("onSpeechComplete appId(%d) sessionId(%d)", appId, sessionId); + JsonObject params; + params["appId"] = JsonValue((int)appId); + params["sessionId"] = JsonValue((int)sessionId); + params["speechId"] = JsonValue((int)data.id); + params["text"] = data.text; + m_eventHandler->Notify("onSpeechComplete", params); + } + + private: + TextToSpeech *m_eventHandler; + }; + + } // namespace Plugin +} // namespace WPEFramework diff --git a/TextToSpeech/TextToSpeech.json b/TextToSpeech/TextToSpeech.json new file mode 100644 index 0000000000..9107ca2a24 --- /dev/null +++ b/TextToSpeech/TextToSpeech.json @@ -0,0 +1,9 @@ +{ + "locator":"libWPEFrameworkTextToSpeech.so", + "classname":"TextToSpeech", + "precondition":[ + "Platform" + ], + "callsign":"org.rdk.TextToSpeech", + "autostart":true +} \ No newline at end of file diff --git a/TextToSpeech/impl/TTSCommon.h b/TextToSpeech/impl/TTSCommon.h new file mode 100644 index 0000000000..12e255bc28 --- /dev/null +++ b/TextToSpeech/impl/TTSCommon.h @@ -0,0 +1,68 @@ +/* + * If not stated otherwise in this file or this component's Licenses.txt file the + * following copyright and licenses apply: + * + * Copyright 2019 RDK Management + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +*/ +#ifndef _TTS_ERRORS_H_ +#define _TTS_ERRORS_H_ + +namespace TTS { + +enum ResourceAllocationPolicy { + INVALID_POLICY = -1, + RESERVATION, // Resource must be reserved before usage + PRIORITY, // NOT IMPLEMENTED + OPEN // Any client can use the resource without any prior reservation +}; + +enum SpeechState { + SPEECH_PENDING = 0, + SPEECH_IN_PROGRESS, + SPEECH_PAUSED, + SPEECH_NOT_FOUND +}; + +enum ExtendedEvents { + EXT_EVENT_WILL_SPEAK = 1 << 0, + EXT_EVENT_PAUSED = 1 << 1, + EXT_EVENT_RESUMED = 1 << 2, + EXT_EVENT_CANCELLED = 1 << 3, + EXT_EVENT_INTERRUPTED = 1 << 4, + EXT_EVENT_NETWORK_ERROR = 1 << 5, + EXT_EVENT_PLAYBACK_ERROR = 1 << 6, + EXT_EVENT_ALL = 0xFFFF +}; + +enum TTS_Error { + TTS_OK = 0, + TTS_FAIL, + TTS_NOT_ENABLED, + TTS_CREATE_SESSION_DUPLICATE, + TTS_EMPTY_APPID_INPUT, + TTS_RESOURCE_BUSY, + TTS_NO_SESSION_FOUND, + TTS_NESTED_CLAIM_REQUEST, + TTS_INVALID_CONFIGURATION, + TTS_SESSION_NOT_ACTIVE, + TTS_APP_NOT_FOUND, + TTS_POLICY_VIOLATION, + TTS_OBJECT_DESTROYED = 1010, + TTS_SPEECH_NOT_FOUND, +}; + +} + +#endif diff --git a/TextToSpeech/impl/TTSManager.cpp b/TextToSpeech/impl/TTSManager.cpp new file mode 100644 index 0000000000..9bfd906fc2 --- /dev/null +++ b/TextToSpeech/impl/TTSManager.cpp @@ -0,0 +1,747 @@ +/* + * If not stated otherwise in this file or this component's Licenses.txt file the + * following copyright and licenses apply: + * + * Copyright 2019 RDK Management + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +*/ + +#include "TTSManager.h" +#include "logger.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +namespace TTS { + +extern GMainLoop* gLoop; + +std::string TTS_CONFIGURATION_FILE = "/opt/tts/tts.ini"; +#define RESERVATION_POLICY_STRING "Reservation" + +#define CHECK_RETURN_IF_FAIL(condition, errString) do {\ + if(!(condition)) { \ + TTSLOG_ERROR("%s", (errString)); \ + return; \ + } } while(0) + +// Find the session information +#define FIND_SESSION_OR_RETURN(sessionId) \ + ID_Session_Map::iterator it; \ + TTSSession* session = NULL; \ + if((it = m_sessionMap.find(sessionId)) == m_sessionMap.end()) { \ + TTSLOG_ERROR("Session \"%u\" not found in session map", sessionId); \ + return TTS_NO_SESSION_FOUND; \ + } \ + session = (TTSSession*)it->second; (void)session; +// ------------------------- + + +uint32_t nextSessionId() { + static uint32_t counter = 0; + return ++counter; +} + +#if 0 //TTS-ThunderPlugin TODO *** +void TTSManager::MonitorClientsSourceIOCB(void *source, void *ctx) { + TTSLOG_TRACE("TTSManager::MonitorClientsSourceIOCB"); + + char buf[256]; + EventSource *s = (EventSource*)source; + TTSManager *manager = (TTSManager*)ctx; + + if(!s || !manager) { + TTSLOG_WARNING("Null Source | Null Manager in %s", __FUNCTION__); + return; + } + + int rc = read(s->pfd.fd,buf,sizeof(buf)); + if(rc > 0) { + buf[rc] = '\0'; + TTSLOG_VERBOSE("Read %d bytes from fd=%d, data=%s", rc, s->pfd.fd, buf); + + // Update TTSManager's connection map + s->sessionId = std::atol(buf); + manager->m_connectionMap[s->pfd.fd] = s; + } +} + +void TTSManager::MonitorClientsSourceDestroyedCB(void *source, void *ctx) { + TTSLOG_TRACE("TTSManager::MonitorClientsSourceDestroyedCB"); + + EventSource *s = (EventSource*)source; + TTSManager *manager = (TTSManager*)ctx; + + if(!s || !manager) { + TTSLOG_WARNING("Null Source | Null Manager in %s", __FUNCTION__); + return; + } + + TTSLOG_WARNING("Source with fd=%d is closed, its session \"%u\" will be destroyed", + s->pfd.fd, s->sessionId); + + // Remove the session from TTSManager + rtValue result; + manager->destroySession(s->sessionId, result); +} + +void TTSManager::MonitorClients(void *ctx) { + TTSLOG_TRACE("TTSManager::MonitorClients"); + + TTSManager* manager = (TTSManager*)ctx; + struct sockaddr_un addr; + int serverFd, connectedFd; + + TTSLOG_INFO("Starting thread..."); + if ( (serverFd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) { + TTSLOG_ERROR("socket error"); + exit(-1); + } + + memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + strncpy(addr.sun_path, CLIENT_MONITOR_SOCKET_PATH, sizeof(addr.sun_path)-1); + unlink(CLIENT_MONITOR_SOCKET_PATH); + + if (bind(serverFd, (struct sockaddr*)&addr, sizeof(addr)) == -1) { + TTSLOG_ERROR("bind error"); + exit(-1); + } + + if (listen(serverFd, 10) == -1) { + TTSLOG_ERROR("listen error"); + exit(-1); + } + + while (manager && manager->m_monitorClients) { + if ( (connectedFd = accept(serverFd, NULL, NULL)) == -1) { + TTSLOG_ERROR("accept error : %s", strerror(errno)); + continue; + } + + TTSLOG_INFO("New session connected with fd=%d", connectedFd); + GSource *gs = create_and_setup_source(connectedFd, MonitorClientsSourceIOCB, MonitorClientsSourceDestroyedCB, manager); + g_source_attach(gs, g_main_loop_get_context(gLoop)); + } + + TTSLOG_INFO("Exiting thread..."); + close(serverFd); + unlink(CLIENT_MONITOR_SOCKET_PATH); +} +#endif //TTS-ThunderPlugin TODO *** + +// ------------------------- + +void TTSManager::loadConfigurationsFromFile(std::string configFileName) { + TTSLOG_TRACE("Reading configuration file"); + + // Read configuration file and update m_defaultConfiguration + std::ifstream configFile(configFileName, std::ios::in); + + if(configFile.is_open()) { + std::cmatch m; + std::string line; + std::map configSet; + std::regex re("\\s*([a-zA-Z0-9_-]+)\\s*=\\s*([^ ]+).*$"); + + while(1) { + if(std::getline(configFile, line)) { + if(!line.empty() && std::regex_match(line.c_str(), m, re) && m.size() >= 3) + configSet[m[1].str()] = m[2].str(); + } else + break; + } + + std::map::iterator it; + if((it = configSet.find("TTSEndPoint")) != configSet.end()) { + m_defaultConfiguration.setEndPoint(it->second.c_str()); + configSet.erase(it); + } + + if((it = configSet.find("SecureTTSEndPoint")) != configSet.end()) { + m_defaultConfiguration.setSecureEndPoint(it->second.c_str()); + configSet.erase(it); + } + + if((it = configSet.find("Language")) != configSet.end()) { + m_defaultConfiguration.setLanguage(it->second.c_str()); + configSet.erase(it); + } + + if((it = configSet.find("Voice")) != configSet.end()) { + m_defaultConfiguration.setVoice(it->second.c_str()); + configSet.erase(it); + } + + if((it = configSet.find("Volume")) != configSet.end()) { + m_defaultConfiguration.setVolume(std::stod(it->second)); + configSet.erase(it); + } + + if((it = configSet.find("Rate")) != configSet.end()) { + m_defaultConfiguration.setRate(std::stoi(it->second)); + configSet.erase(it); + } + + ResourceAllocationPolicy policy = OPEN; + if((it = configSet.find("ResourceAccessPolicy")) != configSet.end()) { + std::string &policyStr = it->second; + if(!policyStr.empty() && policyStr == RESERVATION_POLICY_STRING) + policy = RESERVATION; + configSet.erase(it); + } + setResourceAllocationPolicy(policy); + + m_defaultConfiguration.m_others = std::move(configSet); + + configFile.close(); + } else { + TTSLOG_ERROR("Configuration file \"%s\" is not found, using defaults", configFileName.c_str()); + } + + TTSLOG_WARNING("TTSEndPoint : %s", m_defaultConfiguration.endPoint().c_str()); + TTSLOG_WARNING("SecureTTSEndPoint : %s", m_defaultConfiguration.secureEndPoint().c_str()); + TTSLOG_WARNING("Language : %s", m_defaultConfiguration.language().c_str()); + TTSLOG_WARNING("Voice : %s", m_defaultConfiguration.voice().c_str()); + TTSLOG_WARNING("Volume : %lf", m_defaultConfiguration.volume()); + TTSLOG_WARNING("Rate : %u", m_defaultConfiguration.rate()); + + auto it = m_defaultConfiguration.m_others.begin(); + while( it != m_defaultConfiguration.m_others.end()) { + TTSLOG_WARNING("%s : %s", it->first.c_str(), it->second.c_str()); + ++it; + } +} + +TTSManager* TTSManager::create(TTSConnectionCallback *connCallback) +{ + TTSLOG_TRACE("TTSManager::create"); + static std::mutex mutex; + std::lock_guard lock(mutex); + + return new TTSManager(connCallback); +} + +TTSManager::TTSManager(TTSConnectionCallback *callback) : + m_policy(INVALID_POLICY), + m_reservationForApp(0), + m_reservedApp(0), + m_claimedApp(0), + m_activeSession(NULL), + m_speaker(NULL), + m_thread(NULL), + m_monitorClients(true), + m_claimedSession(false), + m_ttsEnabled(false) , + m_callback(callback) { + + TTSLOG_TRACE("TTSManager::TTSManager"); + + // Load configuration from file + loadConfigurationsFromFile(TTS_CONFIGURATION_FILE); + + // Setup Speaker passing the read configuration + m_speaker = new TTSSpeaker(m_defaultConfiguration); + + // Start client monitor thread + //m_thread = new std::thread(MonitorClients, this); //TTS-ThunderPlugin TODO *** +} + +TTSManager::~TTSManager() { + printf("TTSManager::~TTSManager\n"); + fflush(stdout); + + // Clear active session + if(m_activeSession) { + m_claimedSession = false; + m_activeSession->setInactive(); + m_activeSession = NULL; + } + + // Clear All Sessions + m_sessionMap.clear(); + m_appMap.clear(); + + // Clear Speaker Instance + if(m_speaker) { + delete m_speaker; + m_speaker = NULL; + } + +#if 0 //TTS-ThunderPlugin TODO *** + // Attempt to stop the MonitorClients thread + if(m_monitorClients && m_thread) { + m_monitorClients = false; + int fd = socket(AF_UNIX, SOCK_STREAM, 0); + if(fd>0) { + struct sockaddr_un addr; + memset(&addr, 0, sizeof(addr)); + + addr.sun_family = AF_UNIX; + strncpy(addr.sun_path, CLIENT_MONITOR_SOCKET_PATH, sizeof(addr.sun_path)-1); + connect(fd, (struct sockaddr*)&addr, sizeof(addr)); + m_thread->join(); + close(fd); + + delete m_thread; + m_thread = NULL; + } + } +#endif +} + +TTS_Error TTSManager::enableTTS(bool enable) { + std::lock_guard lock(m_mutex); + + if(m_ttsEnabled != enable) { + m_ttsEnabled = enable; + TTSLOG_INFO("TTS is %s", enable ? "Enabled" : "Disabled"); + + m_callback->onTTSStateChanged(m_ttsEnabled); + + if(m_ttsEnabled) { + if(m_policy == RESERVATION) { + makeReservedOrClaimedSessionActive(); + } else { + if(!m_sessionMap.empty()) + m_speaker->ensurePipeline(true); + + TTSLOG_INFO("Making all the sessions active"); + for(ID_Session_Map::iterator it = m_sessionMap.begin(); it != m_sessionMap.end(); ++it) + it->second->setActive(m_speaker, false); + } + } else { + if(m_policy == RESERVATION) { + makeSessionInActive(m_activeSession); + } else { + m_speaker->ensurePipeline(false); + TTSLOG_INFO("Making all the sessions inactive"); + for(ID_Session_Map::iterator it = m_sessionMap.begin(); it != m_sessionMap.end(); ++it) + it->second->setInactive(false); + } + } + } + + return TTS_OK; +} + +bool TTSManager::isTTSEnabled() { + TTSLOG_INFO("TTSManager isTTSEnabled(%d)",m_ttsEnabled); + return m_ttsEnabled; +} + +TTS_Error TTSManager::listVoices(std::string language, std::vector &voices) { + bool returnCurrentConfiguration = false; + std::string key = std::string("voice_for_"); // return all voices + + if(language.empty()) { + returnCurrentConfiguration = true; // return voice for the configured language + key = m_defaultConfiguration.language(); + } else if(language != "*") { + key += language.c_str(); // return voices for only the passed language + } + + if(returnCurrentConfiguration) { + TTSLOG_INFO("Retrieving voice configured for language=%s", key.c_str()); + voices.push_back(m_defaultConfiguration.voice().c_str()); + } else { + TTSLOG_INFO("Retrieving voice list for language key=%s", key.c_str()); + auto it = m_defaultConfiguration.m_others.begin(); + while(it != m_defaultConfiguration.m_others.end()) { + if(it->first.find(key.c_str()) == 0) + voices.push_back(it->second.c_str()); + ++it; + } + } + + return TTS_OK; +} + +TTS_Error TTSManager::setConfiguration(Configuration &configuration) { + TTSLOG_VERBOSE("Setting Default Configuration"); + + std::string v = m_defaultConfiguration.voice(); + + m_mutex.lock(); + + if(!configuration.ttsEndPoint.empty()) + m_defaultConfiguration.setEndPoint(configuration.ttsEndPoint.c_str()); + if(!configuration.ttsEndPointSecured.empty()) + m_defaultConfiguration.setSecureEndPoint(configuration.ttsEndPointSecured.c_str()); + if(!configuration.language.empty()) + m_defaultConfiguration.setLanguage(configuration.language.c_str()); + if(!configuration.voice.empty()) + m_defaultConfiguration.setVoice(configuration.voice.c_str()); + if(configuration.volume) + m_defaultConfiguration.setVolume(configuration.volume); + if(configuration.rate) + m_defaultConfiguration.setRate(configuration.rate); + + + if(m_defaultConfiguration.endPoint().empty() && !m_defaultConfiguration.secureEndPoint().empty()) + m_defaultConfiguration.setEndPoint(m_defaultConfiguration.secureEndPoint()); + else if(m_defaultConfiguration.secureEndPoint().empty() && !m_defaultConfiguration.endPoint().empty()) + m_defaultConfiguration.setSecureEndPoint(m_defaultConfiguration.endPoint()); + else if(m_defaultConfiguration.endPoint().empty() && m_defaultConfiguration.secureEndPoint().empty()) + TTSLOG_WARNING("TTSEndPoint & SecureTTSEndPoints are empty!!!"); + + // Pass newly set configuration to all the sessions + // WARN : This might over write the session specific configurations (TBD) + ID_Session_Map::iterator it = m_sessionMap.begin(); + while(it != m_sessionMap.end()) { + it->second->setConfiguration(m_defaultConfiguration);; + ++it; + } + m_mutex.unlock(); + + TTSLOG_INFO("Default config updated, endPoint=%s, secureEndPoint=%s, lang=%s, voice=%s, vol=%lf, rate=%u", + m_defaultConfiguration.endPoint().c_str(), + m_defaultConfiguration.secureEndPoint().c_str(), + m_defaultConfiguration.language().c_str(), + m_defaultConfiguration.voice().c_str(), + m_defaultConfiguration.volume(), + m_defaultConfiguration.rate()); + + TTSLOG_INFO("Config values, endPoint=%s, secureEndPoint=%s, lang=%s, voice=%s, vol=%lf, rate=%u", + configuration.ttsEndPoint.c_str(), + configuration.ttsEndPointSecured.c_str(), + configuration.language.c_str(), + configuration.voice.c_str(), + configuration.volume, + configuration.rate); + + if(strcmp (v.c_str(), m_defaultConfiguration.voice().c_str()) ) { + m_callback->onVoiceChanged(m_defaultConfiguration.voice()); + } + return TTS_OK; +} + +TTS_Error TTSManager::getConfiguration(Configuration &configuration) { + TTSLOG_VERBOSE("Getting Default Configuration"); + + configuration.ttsEndPoint = m_defaultConfiguration.endPoint().c_str(); + configuration.ttsEndPointSecured = m_defaultConfiguration.secureEndPoint().c_str(); + configuration.language = m_defaultConfiguration.language().c_str(); + configuration.voice = m_defaultConfiguration.voice().c_str(); + configuration.volume = m_defaultConfiguration.volume(); + configuration.rate = m_defaultConfiguration.rate(); + + return TTS_OK; +} + +bool TTSManager::isSessionActiveForApp(uint32_t appid) { + bool active = false; + + if(m_policy == RESERVATION) { + active = (m_activeSession && m_activeSession->appId() == appid); + } else { + active = (m_appMap.find(appid) != m_appMap.end()); + } + return active; +} + +TTSSession* TTSManager::createSession(uint32_t appId, std::string appName, uint32_t &sessionID, bool &ttsEnabled, TTS_Error &status, TTSSessionCallback *eventCallbacks) { + TTSSession *session = NULL; + { + std::lock_guard lock(m_mutex); + + // Check for duplicate App IDs / Sessions + ID_Session_Map::iterator it = m_sessionMap.begin(); + while(it != m_sessionMap.end()) { + session = it->second; + if(session->appId() == appId) + break; + ++it; + } + + if(it != m_sessionMap.end() && session) { + TTSLOG_ERROR("Application \"%s\" already has a session \"%u\"", appName.c_str(), session->sessionId()); + status = TTS_CREATE_SESSION_DUPLICATE; + return session; + } + + // Create a session + uint32_t sessionId = nextSessionId(); + session = new TTSSession(appId, appName, sessionId, m_defaultConfiguration, eventCallbacks); + + // Update return values + sessionID = sessionId; + ttsEnabled = m_ttsEnabled; + // Update session map + m_appMap[appId] = session; + m_sessionMap[sessionId] = session; + TTSLOG_INFO("New session \"%u\" created for app (%u, %s, %p)...", + sessionId, appId, appName.c_str(), session); + + if(m_policy == RESERVATION) { + // Allocate resource when it is reserved for this session + makeReservedOrClaimedSessionActive(); + } else { + // Make all the new sessions active to enable them speak any time + if(m_ttsEnabled) { + m_speaker->ensurePipeline(true); + session->setActive(m_speaker, false); + } + } + + status = TTS_OK; + } + + return session; +} + +TTS_Error TTSManager::destroySession(uint32_t sessionId) { + std::lock_guard lock(m_mutex); + + // Find the session information + FIND_SESSION_OR_RETURN(sessionId); + + // Deactivate session + if(m_policy == RESERVATION) + releasePlayerResource(session->appId(), true); + else + session->setInactive(false); + + // Remove from the map + m_sessionMap.erase(it); + + ID_Session_Map::iterator ait = m_appMap.find(session->appId()); + if(ait != m_appMap.end()) + m_appMap.erase(ait); + + TTSLOG_WARNING("Session \"%u\" with AppID \"%u\" is destroyed, map_size=%d", sessionId, session->appId(), m_sessionMap.size()); + + // Remove ConnectionMap Entry //TTS-ThunderPlugin TODO *** + /*EventSource *es = NULL; + ConnectionMap::iterator citr = m_connectionMap.begin(); + while(citr != m_connectionMap.end()) { + es = (EventSource*)citr->second; + if(es->sessionId == sessionId) { + int fd = es->pfd.fd; + g_source_remove_poll((GSource*)es, &es->pfd); + g_source_destroy((GSource*)es); + g_source_unref((GSource*)es); + m_connectionMap.erase(citr); + close(fd); + break; + } + ++citr; + }*/ + + if(m_sessionMap.size() == 0) { + TTSLOG_WARNING("All sessions were destroyed, destroy pipeline"); + m_speaker->ensurePipeline(false); + } + + return TTS_OK; +} + +void TTSManager::setResourceAllocationPolicy(ResourceAllocationPolicy policy) { + if(m_policy != policy) { + if(policy == PRIORITY) { + TTSLOG_WARNING("Priority based resource allocation is not implemented now, falling back to Open policy"); + policy = OPEN; + } + + m_policy = policy; + TTSLOG_INFO("%s Policy is in effect", (policy == RESERVATION) ? "Reservation" : ((policy == OPEN) ? "Open" : "Priority")); + } +} + +TTS_Error TTSManager::getResourceAllocationPolicy(ResourceAllocationPolicy &policy) { + policy = m_policy; + return TTS_OK; +} + +void TTSManager::makeSessionActive(TTSSession *session) { + if(session && m_activeSession != session) { + m_speaker->ensurePipeline(true); + session->setActive(m_speaker); + m_activeSession = session; + TTSLOG_INFO("Reserved Resource, RequestingAppName = \"%s\", AppId = \"%u\" is made active", + session->appName().c_str() ? session->appName().c_str() : "Null", session->appId()); + } +} + +void TTSManager::makeSessionInActive(TTSSession *session) { + if(session && m_activeSession == session) { + session->setInactive(); + m_speaker->ensurePipeline(false); + m_activeSession = NULL; + TTSLOG_INFO("Released Resource, RequestingAppName = \"%s\", AppId = \"%u\" is made in-active", + session->appName().c_str() ?session->appName().c_str() : "Null", session->appId()); + } +} + +void TTSManager::makeReservedOrClaimedSessionActive() { + if(m_ttsEnabled) { + uint32_t appid = m_claimedApp ? m_claimedApp : m_reservedApp; + if(appid) { + // Find the App information + ID_Session_Map::iterator ait; + if((ait = m_appMap.find(appid)) == m_appMap.end()) { + // Session is not found, resource will be alloted to the App when it is created + TTSLOG_WARNING("No App is live with ID \"%u\", will be granted resource on creation", appid); + } else { + // Make the requested App active + makeSessionActive((TTSSession*)ait->second); + } + } + } +} + +TTS_Error TTSManager::reservePlayerResource(uint32_t appId, bool internalReq) { + if(m_policy != RESERVATION) { + TTSLOG_WARNING("%s policy is in effect, skipping reservation request", (m_policy == PRIORITY) ? "Priority based" : "Open"); + return TTS_OK; + } + + // Lock would be already held by claimPlayerResource() + if(!internalReq) + std::lock_guard lock(m_mutex); + + TTSLOG_INFO("Request to reserve the Player for %u, reservedApp=%u, claimedApp=%u, activeApp=%u", + appId, m_reservedApp, m_claimedApp, m_activeSession ? m_activeSession->appId() : 0); + + if (!appId) { + TTSLOG_ERROR("Empty input"); + return TTS_EMPTY_APPID_INPUT; + } + + if(m_reservedApp != 0) { + if(m_reservedApp == appId) { + TTSLOG_WARNING("App \"%u\" already holds the Player", appId); + return TTS_OK; + } else { + TTSLOG_ERROR("Resource is already reserved for app \"%u\", should be released first", m_reservedApp); + return TTS_RESOURCE_BUSY; + } + } + + m_reservedApp = appId; + + makeReservedOrClaimedSessionActive(); + + return TTS_OK; +} + +TTS_Error TTSManager::releasePlayerResource(uint32_t appId, bool internalReq) { + if(m_policy != RESERVATION) { + TTSLOG_WARNING("%s policy is in effect, skipping release request", (m_policy == PRIORITY) ? "Priority based" : "Open"); + return TTS_OK; + } + + if(!internalReq) + std::lock_guard lock(m_mutex); + + TTSLOG_INFO("Request to release the Player from %u, reservedApp=%u, claimedApp=%u, activeApp=%u", + appId, m_reservedApp, m_claimedApp, m_activeSession ? m_activeSession->appId() : 0); + + if (!appId) { + TTSLOG_ERROR("Empty input"); + return TTS_EMPTY_APPID_INPUT; + } + + // Handle releasing of claimed resource first + if(m_claimedApp != 0 && m_claimedApp == appId) { + m_claimedApp = 0; + // Releasing of resource should happen irrespective of TTS Enabled / not + if(m_activeSession && m_activeSession->appId() == appId && appId != m_reservedApp) { + makeSessionInActive(m_activeSession); + makeReservedOrClaimedSessionActive(); + } + return TTS_OK; + } + + // Handle releasing of normal reserved app + if(m_reservedApp != 0) { + if(m_reservedApp != appId) { + TTSLOG_ERROR("App \"%u\" doesn't own the resource", appId); + return TTS_FAIL; + } else { + // Releasing of resource should happen irrespective of TTS Enabled / not + if(m_activeSession && m_activeSession->appId() == appId) + makeSessionInActive(m_activeSession); + m_reservedApp = 0; + } + } + + return TTS_OK; +} + +TTS_Error TTSManager::claimPlayerResource(uint32_t appId) { + if(m_policy != RESERVATION) { + TTSLOG_WARNING("%s policy is in effect, skipping claim request", (m_policy == PRIORITY) ? "Priority based" : "Open"); + return TTS_OK; + } + + std::lock_guard lock(m_mutex); + + TTSLOG_INFO("Request to claim the Player for %u, reservedApp=%u, claimedApp=%u, activeApp=%u", + appId, m_reservedApp, m_claimedApp, m_activeSession ? m_activeSession->appId() : 0); + + if (!appId) { + TTSLOG_ERROR("Empty input"); + return TTS_EMPTY_APPID_INPUT; + } else if (m_appMap.find(appId) == m_appMap.end()) { + TTSLOG_ERROR("App \"%u\" not found in the map", appId); + return TTS_APP_NOT_FOUND; + } + + if(m_claimedApp) { + if(m_claimedApp == appId) { + // Reject recursive claim requests + TTSLOG_WARNING("App is still holding claimed the resource"); + return TTS_OK; + } else { + // Reject nested claim requests + TTSLOG_ERROR("Resource had already been claimed by another App, couldn't satisfy request"); + return TTS_RESOURCE_BUSY; + } + } + + m_claimedApp = appId; + + if(m_reservedApp) { + if(m_reservedApp == m_claimedApp) { + // Active App request "claim" + m_claimedApp = 0; + TTSLOG_WARNING("App already holds the resource"); + return TTS_OK; + } else { + uint32_t tmp = m_reservedApp; + releasePlayerResource(m_reservedApp, true); + + // Keep previously reserved app, so that once claimed app is done with the resource + // it can be assigned back to the reserved app + m_reservedApp = tmp; + } + } + + makeReservedOrClaimedSessionActive(); + + return (!m_ttsEnabled ? TTS_OK : ((m_activeSession && m_activeSession->appId() == m_claimedApp) ? TTS_OK : TTS_FAIL)); +} + +} // namespace TTS diff --git a/TextToSpeech/impl/TTSManager.h b/TextToSpeech/impl/TTSManager.h new file mode 100644 index 0000000000..92b0e50994 --- /dev/null +++ b/TextToSpeech/impl/TTSManager.h @@ -0,0 +1,116 @@ +/* + * If not stated otherwise in this file or this component's Licenses.txt file the + * following copyright and licenses apply: + * + * Copyright 2019 RDK Management + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +*/ +#ifndef _TTS_ENGINE_H_ +#define _TTS_ENGINE_H_ + +#include "TTSCommon.h" +#include +#include "TTSSession.h" + +#include +#include +#include +#include +#include + +namespace TTS { + +struct Configuration { + Configuration() : volume(0), rate(0) {}; + ~Configuration() {} + + std::string ttsEndPoint; + std::string ttsEndPointSecured; + std::string language; + std::string voice; + double volume; + uint8_t rate; +}; + +class TTSConnectionCallback { +public: + TTSConnectionCallback() {} + virtual ~TTSConnectionCallback() {} + + virtual void onTTSServerConnected() = 0; + virtual void onTTSServerClosed() = 0; + virtual void onTTSStateChanged(bool enabled) = 0; + virtual void onVoiceChanged(std::string voice) { (void)voice; } +}; + +class TTSManager { +public: + static TTSManager *create(TTSConnectionCallback *connCallback); + TTSManager(TTSConnectionCallback *connCallback); + virtual ~TTSManager(); + + // TTS Global APIs + TTS_Error enableTTS(bool enable); + bool isTTSEnabled(); + TTS_Error listVoices(std::string language, std::vector &voices); + TTS_Error setConfiguration(Configuration &configuration); + TTS_Error getConfiguration(Configuration &configuration); + bool isSessionActiveForApp(uint32_t appid); + + // Resource management APIs + TTS_Error getResourceAllocationPolicy(ResourceAllocationPolicy &policy); + TTS_Error reservePlayerResource(uint32_t appId, bool internalReq=false); + TTS_Error releasePlayerResource(uint32_t appId, bool internalReq=false); + + /***************** For Override Control *****************/ + TTS_Error claimPlayerResource(uint32_t appId); + /***************** For Override Control *****************/ + + // Session control functions + TTSSession* createSession(uint32_t appId, std::string appName, uint32_t &sessionID, bool &ttsEnabled, TTS_Error &status, TTSSessionCallback *eventCallbacks); + TTS_Error destroySession(uint32_t sessionId); + +private: + using ID_Session_Map=std::map; + ID_Session_Map m_appMap; + ID_Session_Map m_sessionMap; + + TTSConfiguration m_defaultConfiguration; + ResourceAllocationPolicy m_policy; + uint32_t m_reservationForApp; + uint32_t m_reservedApp; + uint32_t m_claimedApp; + TTSSession *m_activeSession; + TTSSpeaker *m_speaker; + std::thread *m_thread; + bool m_monitorClients; + bool m_claimedSession; + bool m_ttsEnabled; + std::mutex m_mutex; + TTSConnectionCallback *m_callback; + + void loadConfigurationsFromFile(std::string configFile); + void setResourceAllocationPolicy(ResourceAllocationPolicy policy); + void makeSessionActive(TTSSession *session); + void makeSessionInActive(TTSSession *session); + void makeReservedOrClaimedSessionActive(); + + static void MonitorClients(void *ctx); + static void MonitorClientsSourceIOCB(void *source, void *ctx); + static void MonitorClientsSourceDestroyedCB(void *source, void *ctx); +}; + +} // namespace TTS + +#endif diff --git a/TextToSpeech/impl/TTSSession.cpp b/TextToSpeech/impl/TTSSession.cpp new file mode 100644 index 0000000000..70409b4f5c --- /dev/null +++ b/TextToSpeech/impl/TTSSession.cpp @@ -0,0 +1,324 @@ +/* + * If not stated otherwise in this file or this component's Licenses.txt file the + * following copyright and licenses apply: + * + * Copyright 2019 RDK Management + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +*/ + +#include "TTSSession.h" +#include "TTSCommon.h" +#include "logger.h" + +#include + +namespace TTS { + +// --- // + +#define CHECK_ACTIVENESS() do {\ + if(!m_speaker) { \ + TTSLOG_ERROR("Session \"%u\" is not active to start a speech", m_sessionId); \ + return TTS_SESSION_NOT_ACTIVE; \ + } } while(0) + +// --- // + + +TTSSession::TTSSession(uint32_t appId, std::string appName, uint32_t sessionId, TTSConfiguration configuration, TTSSessionCallback *eventCallbacks) : + m_speaker(NULL), m_havingConfigToUpdate(false), m_extendedEvents(0) { + m_appId = appId; + m_name = appName; + m_sessionId = sessionId; + m_configuration = configuration; + m_callback = eventCallbacks; +} + +TTSSession::~TTSSession() { +} + +TTS_Error TTSSession::setPreemptiveSpeak(bool preemptive) { + std::lock_guard lock(m_mutex); + m_configuration.setPreemptiveSpeak(preemptive); + TTSLOG_INFO("Preemptive Speech has been %s", preemptive ? "enabled" : "disabled"); + return TTS_OK; +} + +TTS_Error TTSSession::getSpeechState(uint32_t id, SpeechState &state) { + TTSLOG_TRACE("Speak"); + + // Check if it is active session + CHECK_ACTIVENESS(); + + state = m_speaker->getSpeechState(this, id); + return TTS_OK; +} + +TTS_Error TTSSession::speak(uint32_t id, std::string text, bool secure) { + TTSLOG_TRACE("Speak"); + + // Check if it is active session + CHECK_ACTIVENESS(); + + if(!m_configuration.isValid()) { + TTSLOG_ERROR("Configuration is not set, can't speak"); + return TTS_INVALID_CONFIGURATION; + } + + m_speaker->speak(this, id, text, secure); + + return TTS_OK; +} + +TTS_Error TTSSession::pause(uint32_t id) { + TTSLOG_TRACE("Pause"); + + // Check if it is active session + CHECK_ACTIVENESS(); + + if(m_speaker->isSpeaking(this)) { + m_speaker->pause(id); + } + + return TTS_OK; +} + +TTS_Error TTSSession::resume(uint32_t id) { + TTSLOG_TRACE("Resume"); + + // Check if it is active session + CHECK_ACTIVENESS(); + + if(m_speaker->isSpeaking(this)) { + m_speaker->resume(id); + } + + return TTS_OK; +} + +TTS_Error TTSSession::shut() { + TTSLOG_TRACE("Shut"); + + // Check if it is active session + CHECK_ACTIVENESS(); + + if(m_speaker->isSpeaking(this)) { + m_speaker->cancelCurrentSpeech(); + } + + return TTS_OK; +} + +TTS_Error TTSSession::abortAndClearPending() { + TTSLOG_INFO("Clearing all speeches from session"); + if(m_speaker) { + std::vector speechesCancelled; + m_speaker->clearAllSpeechesFrom(this, speechesCancelled); + cancelled(speechesCancelled); + } + + return TTS_OK; +} + +TTS_Error TTSSession::requestExtendedEvents(uint32_t eventflags) { + m_extendedEvents = eventflags; + return TTS_OK; +} + +void TTSSession::isActive(bool &active) const { + active = (m_speaker != NULL); +} + +void TTSSession::isSpeaking(bool &speaking) const { + speaking = (m_speaker && m_speaker->isSpeaking(this)); +} + +void TTSSession::sessionID(uint32_t &sessionid) const { + sessionid = sessionId(); +} + +//TTS-ThunderPlugin TODO *** +/*TTS_ERROR TTSSession::getConfiguration(rtObjectRef &configuration) { + TTSLOG_TRACE("Getting configuration"); + + configuration.set("ttsEndPoint", m_configuration.endPoint()); + configuration.set("ttsEndPointSecured", m_configuration.secureEndPoint()); + configuration.set("language", m_configuration.language()); + configuration.set("volume", m_configuration.volume()); + configuration.set("voice", m_configuration.voice()); + configuration.set("rate", m_configuration.rate()); + + return TTS_OK; +}*/ + +void TTSSession::setConfiguration(TTSConfiguration &config) { + std::lock_guard lock(m_mutex); + + if(m_speaker && m_speaker->isSpeaking(this)) { + TTSLOG_WARNING("Session \"%u\" is speaking now, will update configuration once speaking is done", m_sessionId); + m_tmpConfiguration = config; + TTSLOG_INFO("tmpConfiguration, endPoint=%s, secureEndPoint=%s, lang=%s, voice=%s, vol=%lf, rate=%u", + m_tmpConfiguration.endPoint().c_str(), + m_tmpConfiguration.secureEndPoint().c_str(), + m_tmpConfiguration.language().c_str(), + m_tmpConfiguration.voice().c_str(), + m_tmpConfiguration.volume(), + m_tmpConfiguration.rate()); + m_havingConfigToUpdate = true; + } else { + m_configuration.updateWith(config); + TTSLOG_INFO("configuration, endPoint=%s, secureEndPoint=%s, lang=%s, voice=%s, vol=%lf, rate=%u", + m_configuration.endPoint().c_str(), + m_configuration.secureEndPoint().c_str(), + m_configuration.language().c_str(), + m_configuration.voice().c_str(), + m_configuration.volume(), + m_configuration.rate()); + } +} + +void TTSSession::setActive(TTSSpeaker *speaker, bool notifyClient) { + TTSLOG_TRACE("Activating Session"); + + if(m_speaker) { + TTSLOG_ERROR("Session \"%u\" is already active", sessionId()); + return; + } + + m_speaker = speaker; + + if(notifyClient) { + m_callback->onResourceAcquired(appId(), sessionId()); + } +} + +void TTSSession::setInactive(bool notifyClient) { + TTSLOG_TRACE("Deactivating Session"); + + // If active session, reset speaker + if(m_speaker) { + abortAndClearPending(); + m_speaker = NULL; + + if(notifyClient) { + m_callback->onResourceReleased(appId(), sessionId()); + } + } +} + +TTSConfiguration *TTSSession::configuration() { + std::lock_guard lock(m_mutex); + return &m_configuration; +} + +void TTSSession::willSpeak(uint32_t speech_id, std::string text) { + if(!(m_extendedEvents & EXT_EVENT_WILL_SPEAK)) + return; + + TTSLOG_VERBOSE(" [%d, %s]", speech_id, text.c_str()); + + SpeechData d; + d.id = speech_id; + d.text = text.c_str(); + m_callback->onWillSpeak(appId(), sessionId(), d); +} + +void TTSSession::started(uint32_t speech_id, std::string text) { + TTSLOG_WARNING(" [%d, %s]", speech_id, text.c_str()); + + SpeechData d; + d.id = speech_id; + d.text = text.c_str(); + m_callback->onSpeechStart(appId(), sessionId(), d); +} + +void TTSSession::spoke(uint32_t speech_id, std::string text) { + TTSLOG_VERBOSE(" [%d, %s]", speech_id, text.c_str()); + + if(m_havingConfigToUpdate) { + m_configuration.updateWith(m_tmpConfiguration); + m_havingConfigToUpdate = false; + } + + SpeechData d; + d.id = speech_id; + d.text = text.c_str(); + m_callback->onSpeechComplete(appId(), sessionId(), d); +} + +void TTSSession::paused(uint32_t speech_id) { + if(!(m_extendedEvents & EXT_EVENT_PAUSED)) + return; + + TTSLOG_WARNING(" [id=%d]", speech_id); + + m_callback->onSpeechPause(appId(), sessionId(), speech_id); +} + +void TTSSession::resumed(uint32_t speech_id) { + if(!(m_extendedEvents & EXT_EVENT_RESUMED)) + return; + + TTSLOG_WARNING(" [id=%d]", speech_id); + + m_callback->onSpeechResume(appId(), sessionId(), speech_id); +} + +void TTSSession::cancelled(std::vector &speeches) { + if(!(m_extendedEvents & EXT_EVENT_CANCELLED)) + return; + + if(speeches.size() <= 0) + return; + + std::stringstream ss; + for(auto it = speeches.begin(); it != speeches.end(); ++it) { + if(it != speeches.begin()) + ss << ","; + ss << *it; + } + TTSLOG_WARNING(" [ids=%s]", ss.str().c_str()); + + m_callback->onSpeechCancelled(appId(), sessionId(), ss.str().c_str()); +} + +void TTSSession::interrupted(uint32_t speech_id) { + if(!(m_extendedEvents & EXT_EVENT_INTERRUPTED)) + return; + + TTSLOG_WARNING(" [id=%d]", speech_id); + + m_callback->onSpeechInterrupted(appId(), sessionId(), speech_id); +} + +void TTSSession::networkerror(uint32_t speech_id){ + if(!(m_extendedEvents & EXT_EVENT_NETWORK_ERROR)) + return; + + TTSLOG_WARNING(" [id=%d]", speech_id); + + m_callback->onNetworkError(appId(), sessionId(), speech_id); +} + +void TTSSession::playbackerror(uint32_t speech_id){ + if(!(m_extendedEvents & EXT_EVENT_PLAYBACK_ERROR)) + return; + + TTSLOG_WARNING(" [id=%d]", speech_id); + + m_callback->onPlaybackError(appId(), sessionId(), speech_id); +} + +} // namespace TTS + diff --git a/TextToSpeech/impl/TTSSession.h b/TextToSpeech/impl/TTSSession.h new file mode 100644 index 0000000000..39e4512fc0 --- /dev/null +++ b/TextToSpeech/impl/TTSSession.h @@ -0,0 +1,105 @@ +/* + * If not stated otherwise in this file or this component's Licenses.txt file the + * following copyright and licenses apply: + * + * Copyright 2019 RDK Management + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +*/ +#ifndef _TTS_SESSION_H_ +#define _TTS_SESSION_H_ + +#include "TTSSpeaker.h" +#include "TTSCommon.h" + +#include + +namespace TTS { + +class TTSSessionCallback { +public: + TTSSessionCallback() {} + virtual ~TTSSessionCallback() {} + + virtual void onTTSSessionCreated(uint32_t appId, uint32_t sessionId) = 0; + virtual void onResourceAcquired(uint32_t appId, uint32_t sessionId) = 0; + virtual void onResourceReleased(uint32_t appId, uint32_t sessionId) = 0; + virtual void onWillSpeak(uint32_t appId, uint32_t sessionId, SpeechData &data) { (void)appId; (void)sessionId; (void)data; } + virtual void onSpeechStart(uint32_t appId, uint32_t sessionId, SpeechData &data) { (void)appId; (void)sessionId; (void)data; } + virtual void onSpeechPause(uint32_t appId, uint32_t sessionId, uint32_t speechId) { (void)appId; (void)sessionId; (void)speechId; } + virtual void onSpeechResume(uint32_t appId, uint32_t sessionId, uint32_t speechId) { (void)appId; (void)sessionId; (void)speechId; } + virtual void onSpeechCancelled(uint32_t appId, uint32_t sessionId, std::string id) { (void)appId; (void)sessionId; (void)id; } + virtual void onSpeechInterrupted(uint32_t appId, uint32_t sessionId, uint32_t speechId) { (void)appId; (void)sessionId; (void)speechId; } + virtual void onNetworkError(uint32_t appId, uint32_t sessionId, uint32_t speechId) { (void)appId; (void)sessionId; (void)speechId; } + virtual void onPlaybackError(uint32_t appId, uint32_t sessionId, uint32_t speechId) { (void)appId; (void)sessionId; (void)speechId; } + virtual void onSpeechComplete(uint32_t appId, uint32_t sessionId, SpeechData &data) { (void)appId; (void)sessionId; (void)data; } +}; + +class TTSSession : public TTSSpeakerClient { +public: + TTSSession(uint32_t appId, std::string appName, uint32_t sessionId, TTSConfiguration configuration, TTSSessionCallback *eventCallbacks); + virtual ~TTSSession(); + + //TTS_Error getConfiguration(rtObjectRef &configuration); //TTS-ThunderPlugin TODO *** + TTS_Error setPreemptiveSpeak(bool preemptive); + TTS_Error getSpeechState(uint32_t id, SpeechState &state); + TTS_Error speak(uint32_t id, std::string text, bool secure); + TTS_Error pause(uint32_t id); + TTS_Error resume(uint32_t id); + TTS_Error shut(); + TTS_Error abortAndClearPending(); + TTS_Error requestExtendedEvents(uint32_t eventflags); + + void isActive(bool &active) const; + void sessionID(uint32_t &sessionid) const; + void isSpeaking(bool &speaking) const; + + void setConfiguration(TTSConfiguration &config); + void setActive(TTSSpeaker *speaker, bool notifyClient=true); + void setInactive(bool notifyClient=true); + + uint32_t appId() const { return m_appId; } + std::string appName() const { return m_name; } + uint32_t sessionId() const { return m_sessionId; } + +protected: + // Speaker Client Callbacks + virtual TTSConfiguration *configuration(); + virtual void willSpeak(uint32_t speech_id, std::string text); + virtual void started(uint32_t speech_id, std::string text); + virtual void spoke(uint32_t speech_id, std::string text); + virtual void paused(uint32_t speech_id); + virtual void resumed(uint32_t speech_id); + virtual void cancelled(std::vector &speeches); + virtual void interrupted(uint32_t speech_id); + virtual void networkerror(uint32_t speech_id); + virtual void playbackerror(uint32_t speech_id); + + TTSSpeaker *m_speaker; + std::mutex m_mutex; + +private: + TTSConfiguration m_tmpConfiguration; + TTSConfiguration m_configuration; + bool m_havingConfigToUpdate; + TTSSessionCallback *m_callback; + + std::string m_name; + uint32_t m_appId; + uint32_t m_sessionId; + uint32_t m_extendedEvents; +}; + +} // namespace TTS + +#endif diff --git a/TextToSpeech/impl/TTSSpeaker.cpp b/TextToSpeech/impl/TTSSpeaker.cpp new file mode 100644 index 0000000000..834b9ef0a5 --- /dev/null +++ b/TextToSpeech/impl/TTSSpeaker.cpp @@ -0,0 +1,918 @@ +/* + * If not stated otherwise in this file or this component's Licenses.txt file the + * following copyright and licenses apply: + * + * Copyright 2019 RDK Management + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +*/ + +#include "TTSSpeaker.h" +#include "logger.h" + +#include +#include +#include + +#define INT_FROM_ENV(env, default_value) ((getenv(env) ? atoi(getenv(env)) : 0) > 0 ? atoi(getenv(env)) : default_value) + +namespace TTS { + +std::map TTSConfiguration::m_others; + +TTSConfiguration::TTSConfiguration() : + m_ttsEndPoint(""), + m_ttsEndPointSecured(""), + m_language("en-US"), + m_voice(""), + m_volume(MAX_VOLUME), + m_rate(DEFAULT_RATE), + m_preemptiveSpeaking(true) { } + +TTSConfiguration::~TTSConfiguration() {} + +void TTSConfiguration::setEndPoint(const std::string endpoint) { + if(!endpoint.empty()) + m_ttsEndPoint = endpoint; + else + TTSLOG_WARNING("Invalid TTSEndPoint input \"%s\"", endpoint.c_str()); +} + +void TTSConfiguration::setSecureEndPoint(const std::string endpoint) { + if(!endpoint.empty()) + m_ttsEndPointSecured = endpoint; + else + TTSLOG_WARNING("Invalid Secured TTSEndPoint input \"%s\"", endpoint.c_str()); +} + +void TTSConfiguration::setLanguage(const std::string language) { + if(!language.empty()) + m_language = language; + else + TTSLOG_WARNING("Empty Language input"); +} + +void TTSConfiguration::setVoice(const std::string voice) { + if(!voice.empty()) + m_voice = voice; + else + TTSLOG_WARNING("Empty Voice input"); +} + +void TTSConfiguration::setVolume(const double volume) { + if(volume >= 1 && volume <= 100) + m_volume = volume; + else + TTSLOG_WARNING("Invalid Volume input \"%lf\"", volume); +} + +void TTSConfiguration::setRate(const uint8_t rate) { + if(rate >= 1 && rate <= 100) + m_rate = rate; + else + TTSLOG_WARNING("Invalid Rate input \"%u\"", rate); +} + +void TTSConfiguration::setPreemptiveSpeak(const bool preemptive) { + m_preemptiveSpeaking = preemptive; +} + +const std::string TTSConfiguration::voice() { + std::string str; + + if(!m_voice.empty()) + return m_voice; + else { + std::string key = std::string("voice_for_") + m_language.c_str(); + auto it = m_others.find(key); + if(it != m_others.end()) + str = it->second.c_str(); + return str; + } +} + +void TTSConfiguration::updateWith(TTSConfiguration &nConfig) { + setEndPoint(nConfig.m_ttsEndPoint); + setSecureEndPoint(nConfig.m_ttsEndPointSecured); + setLanguage(nConfig.m_language); + setVoice(nConfig.m_voice); + setVolume(nConfig.m_volume); + setRate(nConfig.m_rate); +} + +bool TTSConfiguration::isValid() { + if((m_ttsEndPoint.empty() && m_ttsEndPointSecured.empty())) { + TTSLOG_ERROR("TTSEndPointEmpty=%d, TTSSecuredEndPointEmpty=%d", + m_ttsEndPoint.empty(), m_ttsEndPointSecured.empty()); + return false; + } + return true; +} + +// --- // + +TTSSpeaker::TTSSpeaker(TTSConfiguration &config) : + m_defaultConfig(config), + m_clientSpeaking(NULL), + m_currentSpeech(NULL), + m_isSpeaking(false), + m_isPaused(false), + m_pipeline(NULL), + m_source(NULL), + m_audioSink(NULL), + m_pipelineError(false), + m_networkError(false), + m_runThread(true), + m_flushed(false), + m_isEOS(false), + m_ensurePipeline(false), + m_gstThread(new std::thread(GStreamerThreadFunc, this)), + m_busWatch(0), + m_pipelineConstructionFailures(0), + m_maxPipelineConstructionFailures(INT_FROM_ENV("MAX_PIPELINE_FAILURE_THRESHOLD", 1)) { + setenv("GST_DEBUG", "2", 0); +} + +TTSSpeaker::~TTSSpeaker() { + if(m_isSpeaking) + m_flushed = true; + m_runThread = false; + m_condition.notify_one(); + + if(m_gstThread) { + m_gstThread->join(); + m_gstThread = NULL; + } +} + +void TTSSpeaker::ensurePipeline(bool flag) { + std::unique_lock mlock(m_queueMutex); + TTSLOG_WARNING("%s", __FUNCTION__); + m_ensurePipeline = flag; + m_condition.notify_one(); +} + +int TTSSpeaker::speak(TTSSpeakerClient *client, uint32_t id, std::string text, bool secure) { + TTSLOG_TRACE("id=%d, text=\"%s\"", id, text.c_str()); + + // If force speak is set, clear old queued data & stop speaking + if(client->configuration()->isPreemptive()) + reset(); + + SpeechData data(client, id, text, secure); + queueData(data); + + return 0; +} + +SpeechState TTSSpeaker::getSpeechState(const TTSSpeakerClient *client, uint32_t id) { + // See if the speech is in progress i.e Speaking / Paused + { + std::lock_guard lock(m_stateMutex); + if(client == m_clientSpeaking && m_currentSpeech && id == m_currentSpeech->id) { + if(m_isPaused) + return SPEECH_PAUSED; + else + return SPEECH_IN_PROGRESS; + } + } + + // Or in queue + { + std::lock_guard lock(m_queueMutex); + for(auto it = m_queue.begin(); it != m_queue.end(); ++it) { + if(it->id == id && it->client == client) + return SPEECH_PENDING; + } + } + + return SPEECH_NOT_FOUND; +} + +void TTSSpeaker::clearAllSpeechesFrom(const TTSSpeakerClient *client, std::vector &ids) { + TTSLOG_VERBOSE("Cancelling all speeches"); + std::lock_guard lock(m_queueMutex); + for(auto it = m_queue.begin(); it != m_queue.end();) { + if(it->client == client) { + ids.push_back(it->id); + it = m_queue.erase(it); + } else { + ++it; + } + } + + if(isSpeaking(client)) + cancelCurrentSpeech(); +} + +bool TTSSpeaker::isSpeaking(const TTSSpeakerClient *client) { + std::lock_guard lock(m_stateMutex); + + if(client) + return (client == m_clientSpeaking); + + return m_isSpeaking; +} + +void TTSSpeaker::cancelCurrentSpeech() { + TTSLOG_VERBOSE("Cancelling current speech"); + if(m_isSpeaking) { + m_isPaused = false; + m_flushed = true; + m_condition.notify_one(); + } +} + +bool TTSSpeaker::reset() { + TTSLOG_VERBOSE("Resetting Speaker"); + cancelCurrentSpeech(); + flushQueue(); + + return true; +} + +void TTSSpeaker::pause(uint32_t id) { + if(!m_isSpeaking || !m_currentSpeech || (id && id != m_currentSpeech->id)) + return; + + if(m_pipeline) { + if(!m_isPaused) { + m_isPaused = true; + gst_element_set_state(m_pipeline, GST_STATE_PAUSED); + TTSLOG_INFO("Set state to PAUSED"); + } + } +} + +void TTSSpeaker::resume(uint32_t id) { + if(!m_isSpeaking || !m_currentSpeech || (id && id != m_currentSpeech->id)) + return; + + if(m_pipeline) { + if(m_isPaused) { + gst_element_set_state(m_pipeline, GST_STATE_PLAYING); + TTSLOG_INFO("Set state to PLAYING"); + } + } +} + +void TTSSpeaker::setSpeakingState(bool state, TTSSpeakerClient *client) { + std::lock_guard lock(m_stateMutex); + + m_isSpeaking = state; + m_clientSpeaking = client; + + // If thread just completes speaking (called only from GStreamerThreadFunc), + // it will take the next text from queue, no need to keep + // m_flushed (as nothing is being spoken, which needs bail out) + if(state == false) + m_flushed = false; +} + +void TTSSpeaker::queueData(SpeechData data) { + std::lock_guard lock(m_queueMutex); + m_queue.push_back(data); + m_condition.notify_one(); +} + +void TTSSpeaker::flushQueue() { + std::lock_guard lock(m_queueMutex); + m_queue.clear(); +} + +SpeechData TTSSpeaker::dequeueData() { + std::lock_guard lock(m_queueMutex); + SpeechData d; + d = m_queue.front(); + m_queue.pop_front(); + m_flushed = false; + return d; +} + +bool TTSSpeaker::waitForStatus(GstState expected_state, uint32_t timeout_ms) { + // wait for the pipeline to get to pause so we know we have the audio device + if(m_pipeline) { + GstState state; + GstState pending; + + auto timeout = std::chrono::system_clock::now() + std::chrono::milliseconds(timeout_ms); + + do { + std::unique_lock mlock(m_queueMutex); + m_condition.wait_until(mlock, timeout, [this, &state, &pending, expected_state] () { + // Speaker has flushed the data, no need wait for the completion + // must break and reset the pipeline + if(m_flushed) { + TTSLOG_VERBOSE("Bailing out because of forced text queue (m_flushed=true)"); + return true; + } + + gst_element_get_state(m_pipeline, &state, &pending, GST_CLOCK_TIME_NONE); + if(state == expected_state) + return true; + + return false; + }); + } while(!m_flushed && state != expected_state && timeout > std::chrono::system_clock::now()); + + if(state == expected_state) { + TTSLOG_VERBOSE("Got Status : expected_state = %d, new_state = %d", expected_state, state); + return true; + } + + TTSLOG_WARNING("Timed Out waiting for state %s, currentState %s", + gst_element_state_get_name(expected_state), gst_element_state_get_name(state)); + return false; + } + + return true; +} + +#ifdef INTELCE +static GstElement* findElement(GstElement *element, const char* targetName) +{ + GstElement *resultElement = NULL; + if(GST_IS_BIN(element)) { + bool done = false; + GValue nextItem = G_VALUE_INIT; + GstIterator* iterator = gst_bin_iterate_elements(GST_BIN(element)); + + while(!done) { + switch(gst_iterator_next(iterator, &nextItem)) { + case GST_ITERATOR_OK: + { + GstElement *nextElement = GST_ELEMENT(g_value_get_object(&nextItem)); + done = (resultElement = findElement(nextElement, targetName)) != NULL; + g_value_reset(&nextItem); + } + break; + + case GST_ITERATOR_RESYNC: + gst_iterator_resync(iterator); + break; + + case GST_ITERATOR_ERROR: + case GST_ITERATOR_DONE: + done = true; + break; + } + } + + gst_iterator_free(iterator); + g_value_unset(&nextItem); + } else { + if(strstr(gst_element_get_name(element), targetName)) + resultElement = element; + } + + return resultElement; +} + +static void onHaveType(GstElement *typefind, guint /*probability*/, GstCaps *srcPadCaps, gpointer user_data) +{ + GstElement* pipeline = static_cast(user_data); + + if ((srcPadCaps == NULL) || (pipeline == NULL)) { + TTSLOG_ERROR( "Typefind SRC Pad Caps NULL"); + return; + } + + GstStructure *s = gst_caps_get_structure(srcPadCaps, 0); + TTSLOG_WARNING("onHaveType %s", gst_structure_get_name(s)); + + if (strncmp (gst_structure_get_name(s), "audio/", 6) == 0) { + // link typefind directly to mpegaudioparse to complete pipeline + GstElement *sink = findElement(pipeline, "mpegaudioparse"); + GstPad *sinkpad = gst_element_get_static_pad (sink, "sink"); + GstPad *srcpad = gst_element_get_static_pad (typefind, "src"); + + if(!gst_pad_is_linked(sinkpad) && !gst_pad_is_linked(srcpad)) { + bool linked = GST_PAD_LINK_SUCCESSFUL(gst_pad_link (srcpad, sinkpad)); + if(!linked) + TTSLOG_WARNING("Failed to link typefind and audio"); + } + + gst_object_unref (sinkpad); + gst_object_unref (srcpad); + } else if (strncmp (gst_structure_get_name(s), "application/x-id3", 17) == 0) { + // link typefind to id3demux then id3demux to mpegaudioparse to complete pipeline + GstElement *sink = findElement(pipeline, "mpegaudioparse"); + GstElement *id3demux = findElement(pipeline, "id3demux"); + GstPad *sinkpad = gst_element_get_static_pad (sink, "sink"); + GstPad *srcpad = gst_element_get_static_pad (typefind, "src"); + GstPad *id3Sinkpad = gst_element_get_static_pad (id3demux, "sink"); + GstPad *id3Srcpad = gst_element_get_static_pad (id3demux, "src"); + + if(!gst_pad_is_linked(sinkpad) && !gst_pad_is_linked(srcpad) + && !gst_pad_is_linked(id3Srcpad) && !gst_pad_is_linked(id3Sinkpad)) { + bool linkedid3Sink = GST_PAD_LINK_SUCCESSFUL(gst_pad_link (srcpad, id3Sinkpad)); + bool linkedid3Src = GST_PAD_LINK_SUCCESSFUL(gst_pad_link (id3Srcpad, sinkpad)); + if (!linkedid3Sink || !linkedid3Src) + TTSLOG_WARNING("Failed to link typefind and audio"); + } + + gst_object_unref (id3Sinkpad); + gst_object_unref (id3Srcpad); + gst_object_unref (sinkpad); + gst_object_unref (srcpad); + } +} +#endif + +// GStreamer Releated members +void TTSSpeaker::createPipeline() { + m_isEOS = false; + + if(!m_ensurePipeline || m_pipeline) { + TTSLOG_WARNING("Skipping Pipeline creation"); + return; + } + + TTSLOG_WARNING("Creating Pipeline..."); + m_pipeline = gst_pipeline_new(NULL); + if (!m_pipeline) { + m_pipelineConstructionFailures++; + TTSLOG_ERROR("Failed to create gstreamer pipeline"); + return; + } + + m_source = gst_element_factory_make("souphttpsrc", NULL); + + // create soc specific elements +#if defined(BCM_NEXUS) + GstElement *decodebin = NULL; + decodebin = gst_element_factory_make("brcmmp3decoder", NULL); + m_audioSink = gst_element_factory_make("brcmpcmsink", NULL); +#elif defined(INTELCE) + GstElement *typefind = NULL; + GstElement *id3demux = NULL; + GstElement *parse = NULL; + typefind = gst_element_factory_make("typefind", NULL); + id3demux = gst_element_factory_make("id3demux", NULL); + parse = gst_element_factory_make("mpegaudioparse", NULL); + m_audioSink = gst_element_factory_make("ismd_audio_sink", NULL); + // Need these properties so two gstreamer pipelines can play back audio at same time on ismd... + g_object_set(G_OBJECT(m_audioSink), "sync", FALSE, NULL); + g_object_set(G_OBJECT(m_audioSink), "audio-input-set-as-primary", FALSE, NULL); +#endif + + std::string tts_url = + !m_defaultConfig.secureEndPoint().empty() ? m_defaultConfig.secureEndPoint().c_str() : m_defaultConfig.endPoint().c_str(); + if(!tts_url.empty()) { + if(!m_defaultConfig.voice().empty()) { + tts_url.append("voice="); + tts_url.append(m_defaultConfig.voice()); + } + + if(!m_defaultConfig.language().empty()) { + tts_url.append("&language="); + tts_url.append(m_defaultConfig.language()); + } + + tts_url.append("&text=init"); + curlSanitize(tts_url); + + g_object_set(G_OBJECT(m_source), "location", tts_url.c_str(), NULL); + } + + // set the TTS volume to max. + g_object_set(G_OBJECT(m_audioSink), "volume", (double) (m_defaultConfig.volume() / MAX_VOLUME), NULL); + + // Add elements to pipeline and link + bool result = TRUE; +#if defined(BCM_NEXUS) + gst_bin_add_many(GST_BIN(m_pipeline), m_source, decodebin, m_audioSink, NULL); + result &= gst_element_link (m_source, decodebin); + result &= gst_element_link (decodebin, m_audioSink); +#elif defined(INTELCE) + gst_bin_add_many(GST_BIN(m_pipeline), m_source, typefind, id3demux, parse, m_audioSink, NULL); + result &= gst_element_link (m_source, typefind); + result &= gst_element_link (parse, m_audioSink); + // used to link rest of elements based on typefind results + g_signal_connect (typefind, "have-type", G_CALLBACK (onHaveType), m_pipeline); +#endif + + if(!result) { + TTSLOG_ERROR("failed to link elements!"); + gst_object_unref(m_pipeline); + m_pipeline = NULL; + m_pipelineConstructionFailures++; + return; + } + + GstBus *bus = gst_element_get_bus(m_pipeline); + m_busWatch = gst_bus_add_watch(bus, GstBusCallback, (gpointer)(this)); + gst_object_unref(bus); + m_pipelineConstructionFailures = 0; + + // wait until pipeline is set to NULL state + resetPipeline(); +} + +void TTSSpeaker::resetPipeline() { + TTSLOG_WARNING("Resetting Pipeline..."); + + // Detect pipe line error and destroy the pipeline if any + if(m_pipelineError) { + TTSLOG_WARNING("Pipeline error occured, attempting to recover by re-creating pipeline"); + + // Try to recover from errors by destroying the pipeline + destroyPipeline(); + } + m_pipelineError = false; + m_networkError = false; + m_isPaused = false; + m_isEOS = false; + + if(!m_pipeline) { + // If pipe line is NULL, create one + createPipeline(); + } else { + // If pipeline is present, bring it to NULL state + gst_element_set_state(m_pipeline, GST_STATE_NULL); + while(!waitForStatus(GST_STATE_NULL, 60*1000)); + } +} + +void TTSSpeaker::destroyPipeline() { + TTSLOG_WARNING("Destroying Pipeline..."); + + if(m_pipeline) { + gst_element_set_state(m_pipeline, GST_STATE_NULL); + waitForStatus(GST_STATE_NULL, 1*1000); + g_source_remove(m_busWatch); + gst_object_unref(m_pipeline); + } + + m_busWatch = 0; + m_pipeline = NULL; + m_pipelineConstructionFailures = 0; + m_condition.notify_one(); +} + +void TTSSpeaker::waitForAudioToFinishTimeout(float timeout_s) { + TTSLOG_TRACE("timeout_s=%f", timeout_s); + + auto timeout = std::chrono::system_clock::now() + std::chrono::seconds((unsigned long)timeout_s); + + while(m_pipeline && !m_pipelineError && !m_isEOS && !m_flushed && timeout > std::chrono::system_clock::now()) { + std::unique_lock mlock(m_queueMutex); + m_condition.wait_until(mlock, timeout, [this, &timeout, timeout_s] () { + if(!m_pipeline || m_pipelineError) + return true; + + // EOS enquiry + if(m_isEOS) + return true; + + // Speaker has flushed the data, no need wait for the completion + // must break and reset the pipeline + if(m_flushed) { + TTSLOG_VERBOSE("Bailing out because of forced text queue (m_flushed=true)"); + return true; + } + + if(m_isPaused) { + timeout = std::chrono::system_clock::now() + std::chrono::seconds((unsigned long)timeout_s); + } + + return false; + }); + } + TTSLOG_INFO("m_isEOS=%d, m_pipeline=%p, m_pipelineError=%d, m_flushed=%d", + m_isEOS, m_pipeline, m_pipelineError, m_flushed); + + // Irrespective of EOS / Timeout reset pipeline + if(m_pipeline) + gst_element_set_state(m_pipeline, GST_STATE_NULL); + + if(!m_isEOS) + TTSLOG_ERROR("Stopped waiting for audio to finish without hitting EOS!"); + m_isEOS = false; +} + +void TTSSpeaker::replaceIfIsolated(std::string& text, const std::string& search, const std::string& replace) { + size_t pos = 0; + while ((pos = text.find(search, pos)) != std::string::npos) { + bool punctBefore = (pos == 0 || std::ispunct(text[pos-1]) || std::isspace(text[pos-1])); + bool punctAfter = (pos+1 == text.length() || std::ispunct(text[pos+1]) || std::isspace(text[pos+1])); + + if(punctBefore && punctAfter) { + text.replace(pos, search.length(), replace); + pos += replace.length(); + } else { + pos += search.length(); + } + } +} + +bool TTSSpeaker::isSilentPunctuation(const char c) { + static std::string SilentPunctuation = "?!:;-()"; + return (SilentPunctuation.find(c) != std::string::npos); +} + +void TTSSpeaker::replaceSuccesivePunctuation(std::string& text) { + size_t pos = 0; + while(pos < text.length()) { + // Remove unwanted characters + static std::string stray = "\""; + if(stray.find(text[pos]) != std::string::npos) { + text.erase(pos,1); + if(++pos == text.length()) + break; + } + + if(ispunct(text[pos])) { + ++pos; + while(pos < text.length() && (isSilentPunctuation(text[pos]) || isspace(text[pos]))) { + if(isSilentPunctuation(text[pos])) + text.erase(pos,1); + else + ++pos; + } + } else { + ++pos; + } + } +} + +void TTSSpeaker::curlSanitize(std::string &sanitizedString) { + CURL *curl = curl_easy_init(); + if(curl) { + char *output = curl_easy_escape(curl, sanitizedString.c_str(), sanitizedString.size()); + if(output) { + sanitizedString = output; + curl_free(output); + } + } + curl_easy_cleanup(curl); +} + +void TTSSpeaker::sanitizeString(std::string &input, std::string &sanitizedString) { + sanitizedString = input.c_str(); + + replaceIfIsolated(sanitizedString, "$", "dollar"); + replaceIfIsolated(sanitizedString, "#", "pound"); + replaceIfIsolated(sanitizedString, "&", "and"); + replaceIfIsolated(sanitizedString, "|", "bar"); + replaceIfIsolated(sanitizedString, "/", "or"); + + replaceSuccesivePunctuation(sanitizedString); + + curlSanitize(sanitizedString); + + TTSLOG_VERBOSE("In:%s, Out:%s", input.c_str(), sanitizedString.c_str()); +} + +bool TTSSpeaker::needsPipelineUpdate() { + return (m_pipelineConstructionFailures < m_maxPipelineConstructionFailures ? true : !m_queue.empty()) && + ((m_ensurePipeline && !m_pipeline) || (m_pipeline && !m_ensurePipeline)); +} + +std::string TTSSpeaker::constructURL(TTSConfiguration &config, SpeechData &d) { + if(!config.isValid()) { + TTSLOG_ERROR("Invalid configuration"); + return ""; + } + + // EndPoint URL + std::string tts_request; + if(d.secure) + tts_request.append(config.secureEndPoint()); + else + tts_request.append(config.endPoint()); + + // Voice + if(!config.voice().empty()) { + tts_request.append("voice="); + tts_request.append(config.voice().c_str()); + } + + // Language + if(!config.language().empty()) { + tts_request.append("&language="); + tts_request.append(config.language()); + } + + // Rate / speed + tts_request.append("&rate="); + tts_request.append(std::to_string(config.rate() > 100 ? 100 : config.rate())); + + // Sanitize String + std::string sanitizedString; + sanitizeString(d.text, sanitizedString); + + tts_request.append("&text="); + tts_request.append(sanitizedString); + + TTSLOG_WARNING("Constructured final URL is %s", tts_request.c_str()); + return tts_request; +} + +void TTSSpeaker::speakText(TTSConfiguration config, SpeechData &data) { + m_isEOS = false; + + if(m_pipeline && !m_pipelineError && !m_flushed) { + m_currentSpeech = &data; + + g_object_set(G_OBJECT(m_source), "location", constructURL(config, data).c_str(), NULL); + // PCM Sink seems to be accepting volume change before PLAYING state + g_object_set(G_OBJECT(m_audioSink), "volume", (double) (data.client->configuration()->volume() / MAX_VOLUME), NULL); + gst_element_set_state(m_pipeline, GST_STATE_PLAYING); + TTSLOG_VERBOSE("Speaking.... (%d, \"%s\")", data.id, data.text.c_str()); + + //Wait for EOS with a timeout incase EOS never comes + waitForAudioToFinishTimeout(60); + } else { + TTSLOG_WARNING("m_pipeline=%p, m_pipelineError=%d", m_pipeline, m_pipelineError); + } + m_currentSpeech = NULL; +} + +void TTSSpeaker::GStreamerThreadFunc(void *ctx) { + TTSSpeaker *speaker = (TTSSpeaker*) ctx; + + TTSLOG_INFO("Starting GStreamerThread"); + + while(speaker && speaker->m_runThread) { + if(speaker->needsPipelineUpdate()) { + if(speaker->m_ensurePipeline) { + speaker->createPipeline(); + + // If pipeline creation fails, send playbackerror to the client and remove the req from queue + if(!speaker->m_pipeline && !speaker->m_queue.empty()) { + SpeechData data = speaker->dequeueData(); + TTSLOG_ERROR("Pipeline creation failed, sending error for speech=%d from client %p\n", data.id, data.client); + data.client->playbackerror(data.id); + speaker->m_pipelineConstructionFailures = 0; + } + } else { + speaker->destroyPipeline(); + } + } + + // Take an item from the queue + TTSLOG_INFO("Waiting for text input"); + while(speaker->m_runThread && speaker->m_queue.empty() && !speaker->needsPipelineUpdate()) { + std::unique_lock mlock(speaker->m_queueMutex); + speaker->m_condition.wait(mlock, [speaker] () { + return (!speaker->m_queue.empty() || !speaker->m_runThread || speaker->needsPipelineUpdate()); + }); + } + + // Stop thread on Speaker's cue + if(!speaker->m_runThread) { + if(speaker->m_pipeline) { + gst_element_set_state(speaker->m_pipeline, GST_STATE_NULL); + speaker->waitForStatus(GST_STATE_NULL, 1*1000); + } + TTSLOG_INFO("Stopping GStreamerThread"); + return; + } + + if(speaker->needsPipelineUpdate()) { + continue; + } + + TTSLOG_INFO("Got text input, list size=%d", speaker->m_queue.size()); + SpeechData data = speaker->dequeueData(); + + speaker->setSpeakingState(true, data.client); + // Inform the client before speaking + if(!speaker->m_flushed) + data.client->willSpeak(data.id, data.text); + + // Push it to gstreamer for speaking + if(!speaker->m_flushed) { + speaker->speakText(*data.client->configuration(), data); + } + + // Inform the client after speaking + if(speaker->m_flushed) + data.client->interrupted(data.id); + else if(speaker->m_networkError) + data.client->networkerror(data.id); + else if(!speaker->m_pipeline || speaker->m_pipelineError) + data.client->playbackerror(data.id); + else + data.client->spoke(data.id, data.text); + speaker->setSpeakingState(false); + + // stop the pipeline until the next tts string... + speaker->resetPipeline(); + } + + speaker->destroyPipeline(); +} + +int TTSSpeaker::GstBusCallback(GstBus *, GstMessage *message, gpointer data) { + TTSSpeaker *speaker = (TTSSpeaker*)data; + return speaker->handleMessage(message); +} + +bool TTSSpeaker::handleMessage(GstMessage *message) { + GError* error = NULL; + gchar* debug = NULL; + + if(!m_pipeline) { + TTSLOG_WARNING("NULL Pipeline"); + return false; + } + + switch (GST_MESSAGE_TYPE(message)){ + case GST_MESSAGE_ERROR: { + gst_message_parse_error(message, &error, &debug); + TTSLOG_ERROR("error! code: %d, %s, Debug: %s", error->code, error->message, debug); + GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS(GST_BIN(m_pipeline), GST_DEBUG_GRAPH_SHOW_ALL, "error-pipeline"); + std::string source = GST_MESSAGE_SRC_NAME(message); + if(source.find("souphttpsrc") == 0) + m_networkError = true; + m_pipelineError = true; + m_condition.notify_one(); + } + break; + + case GST_MESSAGE_WARNING: { + gst_message_parse_warning(message, &error, &debug); + TTSLOG_WARNING("warning! code: %d, %s, Debug: %s", error->code, error->message, debug); + } + break; + + case GST_MESSAGE_EOS: { + TTSLOG_INFO("Audio EOS message received"); + m_isEOS = true; + m_condition.notify_one(); + } + break; + + + case GST_MESSAGE_STATE_CHANGED: { + gchar* filename; + GstState oldstate, newstate, pending; + gst_message_parse_state_changed (message, &oldstate, &newstate, &pending); + + // Ignore messages not coming directly from the pipeline. + if (GST_ELEMENT(GST_MESSAGE_SRC(message)) != m_pipeline) + break; + + filename = g_strdup_printf("%s-%s", gst_element_state_get_name(oldstate), gst_element_state_get_name(newstate)); + GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS(GST_BIN(m_pipeline), GST_DEBUG_GRAPH_SHOW_ALL, filename); + g_free(filename); + + // get the name and state + TTSLOG_VERBOSE("%s old_state %s, new_state %s, pending %s", + GST_MESSAGE_SRC_NAME(message) ? GST_MESSAGE_SRC_NAME(message) : "", + gst_element_state_get_name (oldstate), gst_element_state_get_name (newstate), gst_element_state_get_name (pending)); + + if (oldstate == GST_STATE_NULL && newstate == GST_STATE_READY) { + } else if (oldstate == GST_STATE_READY && newstate == GST_STATE_PAUSED) { + GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS(GST_BIN(m_pipeline), GST_DEBUG_GRAPH_SHOW_ALL, "paused-pipeline"); + } else if (oldstate == GST_STATE_PAUSED && newstate == GST_STATE_PAUSED) { + } else if (oldstate == GST_STATE_PAUSED && newstate == GST_STATE_PLAYING) { + GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS(GST_BIN(m_pipeline), GST_DEBUG_GRAPH_SHOW_ALL, "playing-pipeline"); + std::lock_guard lock(m_stateMutex); + if(m_clientSpeaking) { + if(m_isPaused) { + m_isPaused = false; + m_clientSpeaking->resumed(m_currentSpeech->id); + m_condition.notify_one(); + } else { + m_clientSpeaking->started(m_currentSpeech->id, m_currentSpeech->text); + } + } + } else if (oldstate == GST_STATE_PLAYING && newstate == GST_STATE_PAUSED) { + std::lock_guard lock(m_stateMutex); + if(m_clientSpeaking && m_isPaused) { + m_clientSpeaking->paused(m_currentSpeech->id); + m_condition.notify_one(); + } + } else if (oldstate == GST_STATE_PAUSED && newstate == GST_STATE_READY) { + } else if (oldstate == GST_STATE_READY && newstate == GST_STATE_NULL) { + } + } + break; + + default: + break; + } + + if(error) + g_error_free(error); + + if(debug) + g_free(debug); + + return true; +} + +} // namespace TTS diff --git a/TextToSpeech/impl/TTSSpeaker.h b/TextToSpeech/impl/TTSSpeaker.h new file mode 100644 index 0000000000..0e5eed9db0 --- /dev/null +++ b/TextToSpeech/impl/TTSSpeaker.h @@ -0,0 +1,189 @@ +/* + * If not stated otherwise in this file or this component's Licenses.txt file the + * following copyright and licenses apply: + * + * Copyright 2019 RDK Management + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +*/ +#ifndef _TTS_SPEAKER_H_ +#define _TTS_SPEAKER_H_ + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "TTSCommon.h" +#include + +// --- // + +namespace TTS { + +#define DEFAULT_RATE 50 +#define DEFAULT_WPM 200 +#define MAX_VOLUME 100 + +// --- // + +class TTSConfiguration { +public: + TTSConfiguration(); + ~TTSConfiguration(); + + void setEndPoint(const std::string endpoint); + void setSecureEndPoint(const std::string endpoint); + void setLanguage(const std::string language); + void setVoice(const std::string voice); + void setVolume(const double volume); + void setRate(const uint8_t rate); + void setPreemptiveSpeak(const bool preemptive); + + const std::string &endPoint() { return m_ttsEndPoint; } + const std::string &secureEndPoint() { return m_ttsEndPointSecured; } + const std::string &language() { return m_language; } + const double &volume() { return m_volume; } + const uint8_t &rate() { return m_rate; } + bool isPreemptive() { return m_preemptiveSpeaking; } + const std::string voice(); + + void updateWith(TTSConfiguration &config); + bool isValid(); + + static std::map m_others; + +private: + std::string m_ttsEndPoint; + std::string m_ttsEndPointSecured; + std::string m_language; + std::string m_voice; + double m_volume; + uint8_t m_rate; + bool m_preemptiveSpeaking; +}; + +class TTSSpeakerClient { +public: + virtual TTSConfiguration* configuration() = 0; + virtual void willSpeak(uint32_t speech_id, std::string text) = 0; + virtual void started(uint32_t speech_id, std::string text) = 0; + virtual void spoke(uint32_t speech_id, std::string text) = 0; + virtual void paused(uint32_t speech_id) = 0; + virtual void resumed(uint32_t speech_id) = 0; + virtual void cancelled(std::vector &speeches) = 0; + virtual void interrupted(uint32_t speech_id) = 0; + virtual void networkerror(uint32_t speech_id) = 0; + virtual void playbackerror(uint32_t speech_id) = 0; +}; + +struct SpeechData { + public: + SpeechData() : client(NULL), secure(false), id(0), text() {} + SpeechData(TTSSpeakerClient *c, uint32_t i, std::string t, bool s=false) : client(c), secure(s), id(i), text(t) {} + SpeechData(const SpeechData &n) { + client = n.client; + id = n.id; + text = n.text; + secure = n.secure; + } + ~SpeechData() {} + + TTSSpeakerClient *client; + bool secure; + uint32_t id; + std::string text; +}; + +class TTSSpeaker { +public: + TTSSpeaker(TTSConfiguration &config); + ~TTSSpeaker(); + + void ensurePipeline(bool flag=true); + + // Speak Functions + int speak(TTSSpeakerClient* client, uint32_t id, std::string text, bool secure); // Formalize data to speak API + bool isSpeaking(const TTSSpeakerClient *client = NULL); + SpeechState getSpeechState(const TTSSpeakerClient *client, uint32_t id); + void clearAllSpeechesFrom(const TTSSpeakerClient *client, std::vector &speechesCancelled); + void cancelCurrentSpeech(); + bool reset(); + + void pause(uint32_t id = 0); + void resume(uint32_t id = 0); + +private: + + // Private Data + TTSConfiguration &m_defaultConfig; + TTSSpeakerClient *m_clientSpeaking; + SpeechData *m_currentSpeech; + bool m_isSpeaking; + bool m_isPaused; + + std::mutex m_stateMutex; + std::condition_variable m_condition; + + std::list m_queue; + std::mutex m_queueMutex; + void queueData(SpeechData); + void flushQueue(); + SpeechData dequeueData(); + + // Private functions + inline void setSpeakingState(bool state, TTSSpeakerClient *client=NULL); + + // GStreamer Releated members + GstElement *m_pipeline; + GstElement *m_source; + GstElement *m_audioSink; + bool m_pipelineError; + bool m_networkError; + bool m_runThread; + bool m_flushed; + bool m_isEOS; + bool m_ensurePipeline; + std::thread *m_gstThread; + guint m_busWatch; + uint8_t m_pipelineConstructionFailures; + const uint8_t m_maxPipelineConstructionFailures; + + static void GStreamerThreadFunc(void *ctx); + void createPipeline(); + void resetPipeline(); + void destroyPipeline(); + + // GStreamer Helper functions + bool needsPipelineUpdate(); + std::string constructURL(TTSConfiguration &config, SpeechData &d); + bool isSilentPunctuation(const char c); + void replaceSuccesivePunctuation(std::string& subject); + void replaceIfIsolated(std::string& subject, const std::string& search, const std::string& replace); + void curlSanitize(std::string &url); + void sanitizeString(std::string &input, std::string &sanitizedString); + void speakText(TTSConfiguration config, SpeechData &data); + bool waitForStatus(GstState expected_state, uint32_t timeout_ms); + void waitForAudioToFinishTimeout(float timeout_s); + bool handleMessage(GstMessage*); + static int GstBusCallback(GstBus *bus, GstMessage *message, gpointer data); +}; + +} // namespace TTS + +#endif diff --git a/TextToSpeech/impl/TextToSpeechCommon.h b/TextToSpeech/impl/TextToSpeechCommon.h new file mode 100644 index 0000000000..a78b9a2014 --- /dev/null +++ b/TextToSpeech/impl/TextToSpeechCommon.h @@ -0,0 +1,85 @@ +/** +* If not stated otherwise in this file or this component's LICENSE +* file the following copyright and licenses apply: +* +* Copyright 2019 RDK Management +* +* 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. +**/ + +#pragma once +#include "TTSCommon.h" +#include "TTSSession.h" +#include "utils.h" + +#define CHECK_CONNECTION_RETURN_ON_FAIL(ret) do {\ + if(!m_ttsManager) { \ + LOGERR("TTS manager is not establised"); \ + response["status"] = TTS::TTS_NOT_ENABLED; \ + returnResponse(ret); \ + } } while(0) + +#define CHECK_SESSION_RETURN_ON_FAIL(id, sessionitr, sessioninfo, ret) do { \ + sessionitr = m_sessionMap.find(id); \ + if(sessionitr == m_sessionMap.end()) { \ + LOGINFO("TTS Session is not created"); \ + response["status"] = TTS::TTS_NO_SESSION_FOUND; \ + returnResponse(ret); \ + } \ + sessioninfo = sessionitr->second; \ + } while(0) + +#define SET_UNSET_EXTENDED_EVENT(sessionInfo, input_event_list, event_flag, event_name) do { \ + uint32_t event = (uint32_t)(event_flag); \ + if((input_event_list & event) && !(sessionInfo->m_extendedEvents & event)) { \ + sessionInfo->m_extendedEvents |= event; \ + } else if(!(input_event_list & event) && (sessionInfo->m_extendedEvents & event)) { \ + sessionInfo->m_extendedEvents &= ~event; \ + } } while(0) + +namespace WPEFramework { + namespace Plugin { + struct SessionInfo { + SessionInfo() : + m_appId(0), + m_sessionId(0), + m_extendedEvents(0), + m_gotResource(false), + m_callback(NULL) {} + + ~SessionInfo() { + m_callback = NULL; + m_gotResource = 0; + m_sessionId = 0; + m_appId = 0; + } + + uint32_t m_appId; + uint32_t m_sessionId; + uint32_t m_extendedEvents; + std::string m_appName; + bool m_gotResource; + TTS::TTSSession *m_session; + TTS::TTSSessionCallback *m_callback; + }; + +static inline const char *policyStr(TTS::ResourceAllocationPolicy policy) { + switch(policy) { + case TTS::RESERVATION: return "Reservation"; + case TTS::PRIORITY: return "Priority"; + case TTS::OPEN: return "Open"; + default: return "*Invalid*"; + } +} + }//namespace Plugin +}//namespace WPEFramework diff --git a/TextToSpeech/impl/logger.cpp b/TextToSpeech/impl/logger.cpp new file mode 100644 index 0000000000..885bc29099 --- /dev/null +++ b/TextToSpeech/impl/logger.cpp @@ -0,0 +1,169 @@ +/* + * If not stated otherwise in this file or this component's Licenses.txt file the + * following copyright and licenses apply: + * + * Copyright 2016 RDK Management + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +*/ +#include "logger.h" +#include +#include +#include +#include +#include + +#ifdef USE_RDK_LOGGER +#include "rdk_debug.h" +#endif + +namespace TTS { + +static inline void sync_stdout() +{ + if (getenv("SYNC_STDOUT")) + setvbuf(stdout, NULL, _IOLBF, 0); +} + +const char* methodName(const std::string& prettyFunction) +{ + size_t colons = prettyFunction.find("::"); + size_t begin = prettyFunction.substr(0,colons).rfind(" ") + 1; + size_t end = prettyFunction.rfind("(") - begin; + + return prettyFunction.substr(begin,end).c_str(); +} + +#ifdef USE_RDK_LOGGER + +void logger_init() +{ + sync_stdout(); + TTS_logger_init("/etc/debug.ini"); +} + +void log(LogLevel level, + const char* func, + const char* file, + int line, + int, // thread id is already handled by TTS_logger + const char* format, ...) +{ + const TTS_LogLevel levelMap[] = + {TTS_LOG_FATAL, TTS_LOG_ERROR, TTS_LOG_WARN, TTS_LOG_INFO, TTS_LOG_DEBUG, TTS_LOG_TRACE1}; + + const short kFormatMessageSize = 4096; + // TTSLogger is backed with log4c which has its own default level + // for filtering messages. Therefore, we don't check level here. + char userFormatted[kFormatMessageSize]; + char finalFormatted[kFormatMessageSize]; + + va_list argptr; + va_start(argptr, format); + vsnprintf(userFormatted, kFormatMessageSize, format, argptr); + snprintf(finalFormatted, kFormatMessageSize, "%s:%s:%d %s", func, + basename(file), + line, + userFormatted); + va_end(argptr); + + // Currently, we use customized layout 'comcast_dated_nocr' in log4c. + // This layout doesn't have trailing carriage return, so we need + // to add it explicitly. + // Once the default layout is used, this addition should be deleted. + RDK_LOG(levelMap[static_cast(level)], + "LOG.RDK.TTS", + "%s\n", + finalFormatted); + + if (FATAL_LEVEL == level) + std::abort(); +} + +#else + +// static int gDefaultLogLevel = INFO_LEVEL; +static int gDefaultLogLevel = VERBOSE_LEVEL; + +void logger_init() +{ + sync_stdout(); + const char* level = getenv("TTS_DEFAULT_LOG_LEVEL"); + if (level) + gDefaultLogLevel = static_cast(atoi(level)); +} + +void log(LogLevel level, + const char* func, + const char* file, + int line, + int threadID, + const char* format, ...) +{ + if (gDefaultLogLevel < level) + return; + + const char* levelMap[] = {"Fatal", "Error", "Warning", "Info", "Verbose", "Trace"}; + const short kFormatMessageSize = 4096; + char formatted[kFormatMessageSize]; + + va_list argptr; + va_start(argptr, format); + vsnprintf(formatted, kFormatMessageSize, format, argptr); + va_end(argptr); + + char timestamp[0xFF] = {0}; + struct timespec spec; + struct tm tm; + + clock_gettime(CLOCK_REALTIME, &spec); + gmtime_r(&spec.tv_sec, &tm); + long ms = spec.tv_nsec / 1.0e6; + + sprintf(timestamp, "%02d%02d%02d-%02d:%02d:%02d.%03ld", + tm.tm_year % 100, + tm.tm_mon + 1, + tm.tm_mday, + tm.tm_hour, + tm.tm_min, + tm.tm_sec, + ms); + + if (threadID) + { + printf("%s [%s] [tid=%d] %s:%s:%d %s\n", + timestamp, + levelMap[static_cast(level)], + threadID, + func, basename(file), line, + formatted); + } + else + { + printf("%s [%s] %s:%s:%d %s\n", + timestamp, + levelMap[static_cast(level)], + func, basename(file), line, + formatted); + } + + fflush(stdout); + + if (FATAL_LEVEL == level) + std::abort(); +} + +#endif // USE_TTS_LOGGER + + +} // namespace TTS diff --git a/TextToSpeech/impl/logger.h b/TextToSpeech/impl/logger.h new file mode 100644 index 0000000000..f1221eff61 --- /dev/null +++ b/TextToSpeech/impl/logger.h @@ -0,0 +1,92 @@ +/* + * If not stated otherwise in this file or this component's Licenses.txt file the + * following copyright and licenses apply: + * + * Copyright 2016 RDK Management + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +*/ +#ifndef TTS_LOGGER_H +#define TTS_LOGGER_H + +#include +#include +#include +#include +#include +#include + +namespace TTS { + +const char* methodName(const std::string& prettyFunction); +#define __METHOD_NAME__ TTS::methodName(__PRETTY_FUNCTION__) + +/** + * Logging level with an increasing order of refinement + * (TRACE_LEVEL = Finest logging) + * It is essental to start with 0 and increase w/o gaps as the value + * can be used for indexing in a mapping table. + */ +enum LogLevel {FATAL_LEVEL = 0, ERROR_LEVEL, WARNING_LEVEL, INFO_LEVEL, VERBOSE_LEVEL, TRACE_LEVEL}; + +/** + * @brief Init logging + * Should be called once per program run before calling log-functions + */ +void logger_init(); + + +#define TTS_assert(expr) do { \ + if ( __builtin_expect(expr, true) ) \ + {} \ + else \ + TTSLOG_ERROR("%s", #expr); \ + } while (0) + +/** + * @brief Log a message + * The function is defined by logging backend. + * Currently 2 variants are supported: TTS_logger (USE_TTS_LOGGER), + * stdout(default) + */ +void log(LogLevel level, + const char* func, + const char* file, + int line, + int threadID, + const char* format, ...); + +#ifdef USE_RDK_LOGGER +#define _LOG(LEVEL, FORMAT, ...) \ + TTS::log(LEVEL, \ + __func__, __FILE__, __LINE__, 0, \ + FORMAT, \ + ##__VA_ARGS__) +#else +#define _LOG(LEVEL, FORMAT, ...) \ + TTS::log(LEVEL, \ + __func__, __FILE__, __LINE__, syscall(__NR_gettid), \ + FORMAT, \ + ##__VA_ARGS__) +#endif + +#define TTSLOG_TRACE(FMT, ...) _LOG(TTS::TRACE_LEVEL, FMT, ##__VA_ARGS__) +#define TTSLOG_VERBOSE(FMT, ...) _LOG(TTS::VERBOSE_LEVEL, FMT, ##__VA_ARGS__) +#define TTSLOG_INFO(FMT, ...) _LOG(TTS::INFO_LEVEL, FMT, ##__VA_ARGS__) +#define TTSLOG_WARNING(FMT, ...) _LOG(TTS::WARNING_LEVEL, FMT, ##__VA_ARGS__) +#define TTSLOG_ERROR(FMT, ...) _LOG(TTS::ERROR_LEVEL, FMT, ##__VA_ARGS__) +#define TTSLOG_FATAL(FMT, ...) _LOG(TTS::FATAL_LEVEL, FMT, ##__VA_ARGS__) + +} // namespace TTS + +#endif // TTS_LOGGER_H diff --git a/TextToSpeech/test/CMakeLists.txt b/TextToSpeech/test/CMakeLists.txt new file mode 100644 index 0000000000..cdc9aba57f --- /dev/null +++ b/TextToSpeech/test/CMakeLists.txt @@ -0,0 +1,34 @@ +# If not stated otherwise in this file or this component's license file the +# following copyright and licenses apply: +# +# Copyright 2020 RDK Management +# +# 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. + +set(PLUGIN_NAME TTSServiceAPITest) +find_package(${NAMESPACE}Protocols REQUIRED) + +add_executable(${PLUGIN_NAME} TTSServiceAPITest.cpp) + +set_target_properties(${PLUGIN_NAME} PROPERTIES + CXX_STANDARD 11 + CXX_STANDARD_REQUIRED YES + ) + +target_link_libraries(${PLUGIN_NAME} + PRIVATE + ${NAMESPACE}Protocols::${NAMESPACE}Protocols + ) + +install(TARGETS ${PLUGIN_NAME} DESTINATION bin) + diff --git a/TextToSpeech/test/Module.h b/TextToSpeech/test/Module.h new file mode 100644 index 0000000000..dfdefed1da --- /dev/null +++ b/TextToSpeech/test/Module.h @@ -0,0 +1,27 @@ +/** +* If not stated otherwise in this file or this component's LICENSE +* file the following copyright and licenses apply: +* +* Copyright 2020 RDK Management +* +* 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. +**/ + +#pragma once + +#ifndef MODULE_NAME +#define MODULE_NAME TTSServiceAPITest +#endif + +#include +#include diff --git a/TextToSpeech/test/TTSServiceAPITest.cpp b/TextToSpeech/test/TTSServiceAPITest.cpp new file mode 100644 index 0000000000..0578386f75 --- /dev/null +++ b/TextToSpeech/test/TTSServiceAPITest.cpp @@ -0,0 +1,785 @@ +/* + * If not stated otherwise in this file or this component's Licenses.txt file the + * following copyright and licenses apply: + * + * Copyright 2019 RDK Management + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +*/ + +#include "../impl/logger.h" +#include "../impl/TTSCommon.h" + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "Module.h" + +#define TTSSRV_CALLSIGN "org.rdk.TextToSpeech" +#define SERVER_DETAILS "127.0.0.1:9998" + +using namespace std; +using namespace WPEFramework; + +using namespace TTS; + +volatile unsigned short g_connectedToTTSEventCount = 0; +volatile bool g_connectedToTTS = false; + +#define OPT_ENABLE_TTS 1 +#define OPT_VOICE_LIST 2 +#define OPT_SET_CONFIG 3 +#define OPT_GET_CONFIG 4 +#define OPT_TTS_ENABLED 5 +#define OPT_SESSION_ACTIVE 6 +#define OPT_ACQUIRE_RESOURCE 7 +#define OPT_CLAIM_RESOURCE 8 +#define OPT_RELEASE_RESOURCE 9 +#define OPT_CREATE_SESSION 10 +#define OPT_IS_ACTIVE_SESSION 11 +#define OPT_SET_PREEMPTIVE 12 +#define OPT_REQ_EXT_EVENTS 13 +#define OPT_SPEAK 14 +#define OPT_PAUSE 15 +#define OPT_RESUME 16 +#define OPT_ABORT 17 +#define OPT_IS_SPEAKING 18 +#define OPT_SPEECH_STATE 19 +#define OPT_DESTROY_SESSION 20 +#define OPT_EXIT 21 +#define OPT_BLOCK_TILL_INPUT 22 +#define OPT_SLEEP 23 + +void showMenu() +{ + cout << "------------------------" << endl; + cout << OPT_ENABLE_TTS << ".enableTTS" << endl; + cout << OPT_VOICE_LIST << ".listVoices" << endl; + cout << OPT_SET_CONFIG << ".setTTSConfiguration" << endl; + cout << OPT_GET_CONFIG << ".getTTSConfiguration" << endl; + cout << OPT_TTS_ENABLED << ".isTTSEnabled" << endl; + cout << OPT_SESSION_ACTIVE << ".isSessionActiveForApp" << endl; + cout << "-" << endl; + cout << OPT_ACQUIRE_RESOURCE << ".acquireResource" << endl; + cout << OPT_CLAIM_RESOURCE << ".claimResource" << endl; + cout << OPT_RELEASE_RESOURCE << ".releaseResource" << endl; + cout << "-" << endl; + cout << OPT_CREATE_SESSION << ".createSession" << endl; + cout << OPT_IS_ACTIVE_SESSION << ".isActiveSession" << endl; + cout << OPT_SET_PREEMPTIVE << ".setPreemptiveSpeech" << endl; + cout << OPT_REQ_EXT_EVENTS << ".requestExtendedEvents" << endl; + cout << OPT_SPEAK << ".speak" << endl; + cout << OPT_PAUSE << ".pause" << endl; + cout << OPT_RESUME << ".resume" << endl; + cout << OPT_ABORT << ".abort" << endl; + cout << OPT_IS_SPEAKING << ".isSpeaking" << endl; + cout << OPT_SPEECH_STATE << ".getSpeechState" << endl; + cout << OPT_DESTROY_SESSION << ".destroySession" << endl; + cout << OPT_EXIT << ".exit" << endl; + cout << OPT_BLOCK_TILL_INPUT << ".dummyInput" << endl; + cout << OPT_SLEEP << ".sleep" << endl; + cout << "------------------------" << endl; +} + +struct AppInfo { + AppInfo(uint32_t sid) : m_sessionId(sid) {} + uint32_t m_sessionId; +}; + +struct MyStream { + MyStream() : myfile(NULL), in(&cin) { + } + + MyStream(string fname) : myfile(new ifstream(fname)) { + if(myfile->is_open()) { + cout << "Reading from file" << endl; + in = myfile; + } else { + cout << "Reading from std::cin" << endl; + in = &cin; + } + } + + template + bool getInput(T &var, const char *prompt = NULL, bool console = false) { + stringstream ss; + static char cstr[5 * 1024]; + string str; + + istream *tin = in; + if(console) + in = &cin; + + do { + if(prompt) + cout << prompt; + try { + if(in == myfile && in->eof()) + in = &cin; + in->getline(cstr, sizeof(cstr)-1); + } catch(...) { + if(in == myfile) + in = &cin; + } + + str = cstr; + if(in == myfile) + cout << cstr << endl; + + if((str.find('#') == string::npos || !str.erase(str.find('#')).empty()) && !str.empty()) { + ss.str(str); + ss >> var; + break; + } + } while(1); + in = tin; + + return true; + } + +private: + ifstream *myfile; + istream *in; +}; + +namespace Handlers { + /* Event Handlers */ + static void onTTSStateChangedHandler(const JsonObject& params) { + bool state = params["state"].Boolean(); + std::cout << "[TTSSrvEvt] " << __FUNCTION__ << ": " << state << std::endl; + } + static void onTTSSessionCreatedHandler(const JsonObject& params) { + int appId = params["appId"].Number(); + int sessionId = params["sessionId"].Number(); + std::cout << "[TTSSrvEvt] " << __FUNCTION__ << ": appId: " << appId << "sessionId: "<< sessionId << std::endl; + } + static void onResourceAcquiredHandler(const JsonObject& params) { + int appId = params["appId"].Number(); + int sessionId = params["sessionId"].Number(); + std::cout << "[TTSSrvEvt] " << __FUNCTION__ << ": appId: " << appId << "sessionId: "<< sessionId << std::endl; + } + static void onResourceReleasedHandler(const JsonObject& params) { + int appId = params["appId"].Number(); + int sessionId = params["sessionId"].Number(); + std::cout << "[TTSSrvEvt] " << __FUNCTION__ << ": appId: " << appId << "sessionId: "<< sessionId << std::endl; + } + static void onWillSpeakHandler(const JsonObject& params) { + int appId = params["appId"].Number(); + int sessionId = params["sessionId"].Number(); + int speechId = params["speechId"].Number(); + std::string text = params["text"].String(); + std::cout << "[TTSSrvEvt] " << __FUNCTION__ << ": appId: " << appId << " sessionId: "<< sessionId << " speechid: " << speechId << " text: " << text << std::endl; + } + static void onSpeechStartHandler(const JsonObject& params) { + int appId = params["appId"].Number(); + int sessionId = params["sessionId"].Number(); + int speechId = params["speechId"].Number(); + std::string text = params["text"].String(); + std::cout << "[TTSSrvEvt] " << __FUNCTION__ << ": appId: " << appId << " sessionId: "<< sessionId << " speechid: " << speechId << " text: " << text << std::endl; + } + static void onSpeechPauseHandler(const JsonObject& params) { + int appId = params["appId"].Number(); + int sessionId = params["sessionId"].Number(); + int speechId = params["speechId"].Number(); + std::cout << "[TTSSrvEvt] " << __FUNCTION__ << ": appId: " << appId << " sessionId: "<< sessionId << " speechid: " << speechId << std::endl; + } + static void onSpeechResumeHandler(const JsonObject& params) { + int appId = params["appId"].Number(); + int sessionId = params["sessionId"].Number(); + int speechId = params["speechId"].Number(); + std::cout << "[TTSSrvEvt] " << __FUNCTION__ << ": appId: " << appId << " sessionId: "<< sessionId << " speechid: " << speechId << std::endl; + } + static void onSpeechCancelledHandler(const JsonObject& params) { + int appId = params["appId"].Number(); + int sessionId = params["sessionId"].Number(); + int speechId = params["speechId"].Number(); + std::cout << "[TTSSrvEvt] " << __FUNCTION__ << ": appId: " << appId << " sessionId: "<< sessionId << " speechid: " << speechId << std::endl; + } + static void onSpeechInterruptedHandler(const JsonObject& params) { + int appId = params["appId"].Number(); + int sessionId = params["sessionId"].Number(); + int speechId = params["speechId"].Number(); + std::cout << "[TTSSrvEvt] " << __FUNCTION__ << ": appId: " << appId << " sessionId: "<< sessionId << " speechid: " << speechId << std::endl; + } + static void onNetworkErrorHandler(const JsonObject& params) { + int appId = params["appId"].Number(); + int sessionId = params["sessionId"].Number(); + int speechId = params["speechId"].Number(); + std::cout << "[TTSSrvEvt] " << __FUNCTION__ << ": appId: " << appId << " sessionId: "<< sessionId << " speechid: " << speechId << std::endl; + } + static void onPlaybackErrorHandler(const JsonObject& params) { + int appId = params["appId"].Number(); + int sessionId = params["sessionId"].Number(); + int speechId = params["speechId"].Number(); + std::cout << "[TTSSrvEvt] " << __FUNCTION__ << ": appId: " << appId << " sessionId: "<< sessionId << " speechid: " << speechId << std::endl; + } + static void onSpeechCompleteHandler(const JsonObject& params) { + int appId = params["appId"].Number(); + int sessionId = params["sessionId"].Number(); + int speechId = params["speechId"].Number(); + std::string text = params["text"].String(); + std::cout << "[TTSSrvEvt] " << __FUNCTION__ << ": appId: " << appId << "sessionId: "<< sessionId << "speechid: " << speechId << "text: " << text << std::endl; + } +} +int main(int argc, char *argv[]) { + std::map appInfoMap; + + JSONRPC::Client* remoteObject = NULL; + std::string reqPayload; + + int choice; + JsonObject result; + uint32_t ret; + int sid = 0; + int appid = 0; + int secure = false; + int sessionid = 0; + char clearall = 'n'; + string stext; + string appname; + + MyStream stream((argc > 1 ? argv[1] : "example.txt")); + + Core::SystemInfo::SetEnvironment(_T("THUNDER_ACCESS"), (_T(SERVER_DETAILS))); + + if (NULL == remoteObject) { + remoteObject = new JSONRPC::Client(_T(TTSSRV_CALLSIGN), _T("")); + if (NULL == remoteObject) { + std::cout << "JSONRPC::Client initialization failed" << std::endl; + } else { + /* Register handlers for Event reception. */ + if (remoteObject->Subscribe(1000, _T("onTTSStateChanged"), + &Handlers::onTTSStateChangedHandler) == Core::ERROR_NONE) { + std::cout << "Subscribed to : onTTSStateChangedHandler" << std::endl; + } else { + std::cout << "Failed to Subscribe notification handler : onTTSStateChangedHandler" << std::endl; + } + if (remoteObject->Subscribe(1000, _T("onTTSSessionCreated"), + &Handlers::onTTSSessionCreatedHandler) == Core::ERROR_NONE) { + std::cout << "Subscribed to : onTTSSessionCreatedHandler" << std::endl; + } else { + std::cout << "Failed to Subscribe notification handler : onTTSSessionCreatedHandler" << std::endl; + } + if (remoteObject->Subscribe(1000, _T("onResourceAcquired"), + &Handlers::onResourceAcquiredHandler) == Core::ERROR_NONE) { + std::cout << "Subscribed to : onResourceAcquiredHandler" << std::endl; + } else { + std::cout << "Failed to Subscribe notification handler : onResourceAcquiredHandler" << std::endl; + } + if (remoteObject->Subscribe(1000, _T("onResourceReleased"), + &Handlers::onResourceReleasedHandler) == Core::ERROR_NONE) { + std::cout << "Subscribed to : onResourceReleasedHandler" << std::endl; + } else { + std::cout << "Failed to Subscribe notification handler : onResourceReleasedHandler" << std::endl; + } + if (remoteObject->Subscribe(1000, _T("onWillSpeak"), + &Handlers::onWillSpeakHandler) == Core::ERROR_NONE) { + std::cout << "Subscribed to : onWillSpeakHandler" << std::endl; + } else { + std::cout << "Failed to Subscribe notification handler : onWillSpeakHandler" << std::endl; + } + if (remoteObject->Subscribe(1000, _T("onSpeechStart"), + &Handlers::onSpeechStartHandler) == Core::ERROR_NONE) { + std::cout << "Subscribed to : onSpeechStartHandler" << std::endl; + } else { + std::cout << "Failed to Subscribe notification handler : onSpeechStartHandler" << std::endl; + } + if (remoteObject->Subscribe(1000, _T("onSpeechPause"), + &Handlers::onSpeechPauseHandler) == Core::ERROR_NONE) { + std::cout << "Subscribed to : onSpeechPauseHandler" << std::endl; + } else { + std::cout << "Failed to Subscribe notification handler : onSpeechPauseHandler" << std::endl; + } + if (remoteObject->Subscribe(1000, _T("onSpeechResume"), + &Handlers::onSpeechResumeHandler) == Core::ERROR_NONE) { + std::cout << "Subscribed to : onSpeechResumeHandler" << std::endl; + } else { + std::cout << "Failed to Subscribe notification handler : onSpeechResumeHandler" << std::endl; + } + if (remoteObject->Subscribe(1000, _T("onSpeechCancelled"), + &Handlers::onSpeechCancelledHandler) == Core::ERROR_NONE) { + std::cout << "Subscribed to : onSpeechCancelledHandler" << std::endl; + } else { + std::cout << "Failed to Subscribe notification handler : onSpeechCancelledHandler" << std::endl; + } + if (remoteObject->Subscribe(1000, _T("onSpeechInterrupted"), + &Handlers::onSpeechInterruptedHandler) == Core::ERROR_NONE) { + std::cout << "Subscribed to : onSpeechInterruptedHandler" << std::endl; + } else { + std::cout << "Failed to Subscribe notification handler : onSpeechInterruptedHandler" << std::endl; + } + if (remoteObject->Subscribe(1000, _T("onNetworkError"), + &Handlers::onNetworkErrorHandler) == Core::ERROR_NONE) { + std::cout << "Subscribed to : onNetworkErrorHandler" << std::endl; + } else { + std::cout << "Failed to Subscribe notification handler : onNetworkErrorHandler" << std::endl; + } + if (remoteObject->Subscribe(1000, _T("onPlaybackError"), + &Handlers::onPlaybackErrorHandler) == Core::ERROR_NONE) { + std::cout << "Subscribed to : onPlaybackErrorHandler" << std::endl; + } else { + std::cout << "Failed to Subscribe notification handler : onPlaybackErrorHandler" << std::endl; + } + if (remoteObject->Subscribe(1000, _T("onSpeechComplete"), + &Handlers::onSpeechCompleteHandler) == Core::ERROR_NONE) { + std::cout << "Subscribed to : onSpeechCompleteHandler" << std::endl; + } else { + std::cout << "Failed to Subscribe notification handler : onSpeechCompleteHandler" << std::endl; + } + + while (true) { + std::cout << std::endl; + showMenu(); + stream.getInput(choice, "Enter your choice : "); + + switch (choice) { + case OPT_ENABLE_TTS: + { + JsonObject params; + bool enable; + stream.getInput(enable, "1.Enable/0.Disable TTS : "); + params["enableTTS"] = JsonValue((bool)enable); + ret = remoteObject->Invoke(1000, + _T("enableTTS"), params, result); + if (result["success"].Boolean()) { + cout << "TextToSpeech: enableTTS call Success" << endl; + } else { + cout << "TextToSpeech: enableTTS call failed. TTS_Status: " << result["TTS_Status"].String() << endl; + } + + } + break; + + case OPT_VOICE_LIST: + { + JsonObject params; + std::string voices; + std::string language; + std::string json; + stream.getInput(language, "Enter the language [\"*\" - all voices, \".\" - current voice]: "); + params["language"] = language; + params.ToString(json); + ret = remoteObject->Invoke(1000, + _T("listVoices"), params, result); + if (result["success"].Boolean()) { + cout << "TextToSpeech: listVoices call Success" << endl; + voices = result["voices"].String(); + cout << "Supported voices for langauge: " << voices << endl; + } else { + cout << "TextToSpeech: listVoices call failed. TTS_Status: " << result["TTS_Status"].String() << endl; + } + } + break; + + case OPT_SET_CONFIG: + { + JsonObject params; + std::string voice; + std::string language; + std::string volume; + int rate = 0; + stream.getInput(language, "Enter language [en-US/es-MX] : "); + params["language"] = language; + stream.getInput(voice, "Enter voice [carol/Angelica] : "); + params["voice"] = voice; + stream.getInput(volume, "Enter volume [0.0-100.0] : "); + params["volume"] = volume; + stream.getInput(rate, "Enter speed [0-100] : "); + params["rate"] = JsonValue(rate); + ret = remoteObject->Invoke(1000, + _T("setTTSConfiguration"), params, result); + if (result["success"].Boolean()) { + cout << "setTTSConfiguration call Success" << endl; + } else { + cout << "setTTSConfiguration call failed. TTS_Status: " << result["TTS_Status"].String() << endl; + } + } + break; + + case OPT_GET_CONFIG: + { + JsonObject params; + ret = remoteObject->Invoke(1000, + _T("getTTSConfiguration"), params, result); + if (result["success"].Boolean()) { + cout << "ttsEndPoint : " << result["ttsEndPoint"].String() << endl; + cout << "ttsEndPointSecured : " << result["ttsEndPointSecured"].String() << endl; + cout << "language : " << result["language"].String() << endl; + cout << "voice : " << result["voice"].String() << endl; + cout << "volume : " << result["volume"].String() << endl; + cout << "rate : " << result["rate"].Number() << endl; + } else { + cout << "getTTSConfiguration call failed. TTS_Status: " << result["TTS_Status"].String() << endl; + } + } + break; + + case OPT_TTS_ENABLED: + { + JsonObject params; + ret = remoteObject->Invoke(1000, + _T("isTTSEnabled"), params, result); + if (result["success"].Boolean()) { + cout << "TTS is " << (result["isEnabled"].Boolean()? "Enabled": "Disabled") << endl; + } else { + cout << "isTTSEnabled call failed. TTS_Status: " << result["TTS_Status"].String() << endl; + } + } + break; + + case OPT_SESSION_ACTIVE: + { + JsonObject params; + stream.getInput(appid, "Enter app id : "); + params["appId"] = JsonValue(appid); + ret = remoteObject->Invoke(1000, + _T("isSessionActiveForApp"), params, result); + if (result["success"].Boolean()) { + cout << "App " << appid << (result["isActive"].Boolean()? " has session & active" : " has no session / inactive") << endl; + } else { + cout << "isSessionActiveForApp call failed. TTS_Status: " << result["TTS_Status"].String() << endl; + } + } + break; + + case OPT_ACQUIRE_RESOURCE: + { + JsonObject params; + stream.getInput(appid, "Enter app id : "); + params["appId"] = appid; + ret = remoteObject->Invoke(1000, + _T("acquireResource"), params, result); + if (result["success"].Boolean()) { + cout << "acquireResource call Success" << endl; + } else { + cout << "acquireResource call failed. TTS_Status: " << result["TTS_Status"].String() << endl; + } + } + break; + + case OPT_CLAIM_RESOURCE: + { + JsonObject params; + stream.getInput(appid, "Enter app id : "); + params["appId"] = appid; + ret = remoteObject->Invoke(1000, + _T("claimResource"), params, result); + if (result["success"].Boolean()) { + cout << "claimResource call Success" << endl; + } else { + cout << "claimResource call failed. TTS_Status: " << result["TTS_Status"].String() << endl; + } + } + break; + + case OPT_RELEASE_RESOURCE: + { + JsonObject params; + stream.getInput(appid, "Enter app id : "); + params["appId"] = appid; + ret = remoteObject->Invoke(1000, + _T("releaseResource"), params, result); + if (result["success"].Boolean()) { + cout << "releaseResource call Success" << endl; + } else { + cout << "releaseResource call failed. TTS_Status: " << result["TTS_Status"].String() << endl; + } + } + break; + + case OPT_CREATE_SESSION: + { + JsonObject params; + stream.getInput(appid, "Enter app id : "); + params["appId"] = JsonValue(appid); + stream.getInput(appname, "Enter app name : "); + params["appName"] = appname; + ret = remoteObject->Invoke(1000, + _T("createSession"), params, result); + if (result["success"].Boolean()) { + JsonValue id = result.Get("TTSSessionId"); + sessionid = id.Number(); + if(sessionid) { + appInfoMap[appid] = new AppInfo(sessionid); + cout << "Session (" << sessionid << ") created for app (" << appid << ")" << endl; + } else { + cout << "Session couldn't be created for app (" << appid << ") TTS_Status: "<< result["TTS_Status"].String() << endl; + } + + } else { + cout << "createSession call failed. TTS_Status: " << result["TTS_Status"].String() << endl; + } + } + break; + + case OPT_IS_ACTIVE_SESSION: + { + JsonObject params; + stream.getInput(appid, "Enter app id : "); + if(appInfoMap.find(appid) != appInfoMap.end()) { + sessionid = appInfoMap.find(appid)->second->m_sessionId; + params["sessionId"] = sessionid; + params["forcefetch"] = true; + ret = remoteObject->Invoke(1000, + _T("isActiveSession"), params, result); + if (result["success"].Boolean()) { + cout << "Session (" << sessionid << ") of app (" << appid << ") is " << (result["isActive"].Boolean() ? "active" : "inactive") << endl; + } else { + cout << "isActiveSession call failed. TTS_Status: " << result["TTS_Status"].String() << endl; + } + } else { + cout << "Session hasn't been created for app(" << appid << ")" << endl; + } + } + break; + + case OPT_SET_PREEMPTIVE: + { + JsonObject params; + stream.getInput(appid, "Enter app id : "); + if(appInfoMap.find(appid) != appInfoMap.end()) { + bool preemptive = true; + stream.getInput(preemptive, "Enter preemptive speech [0/1] : "); + params["preemptive"] = preemptive; + sessionid = appInfoMap.find(appid)->second->m_sessionId; + params["sessionId"] = sessionid; + ret = remoteObject->Invoke(1000, + _T("setPreemptiveSpeak"), params, result); + if (result["success"].Boolean()) { + cout << "setPreemptiveSpeak call success" << endl; + } else { + cout << "setPreemptiveSpeak call failed. TTS_Status: " << result["TTS_Status"].String() << endl; + } + } else { + cout << "Session hasn't been created for app(" << appid << ")" << endl; + } + } + break; + + case OPT_REQ_EXT_EVENTS: + { + JsonObject params; + stream.getInput(appid, "Enter app id : "); + if(appInfoMap.find(appid) != appInfoMap.end()) { + int events = 0; + stream.getInput(events, + "Enter required events flag \n[LSB6-Playback Error, LSB5-Network Error, LSB4-Interrupted, LSB3-Cancelled, LSB2-Resumed, LSB1-Paused]\nEnter events flag [0-63] : "); + sessionid = appInfoMap.find(appid)->second->m_sessionId; + params["sessionId"] = sessionid; + params["extendedEvents"] = events; + ret = remoteObject->Invoke(1000, + _T("requestExtendedEvents"), params, result); + if (result["success"].Boolean()) { + cout << "requestExtendedEvents call success" << endl; + } else { + cout << "requestExtendedEvents call failed. TTS_Status: " << result["TTS_Status"].String() << endl; + } + } else { + cout << "Session hasn't been created for app(" << appid << ")" << endl; + } + } + break; + + case OPT_SPEAK: + { + JsonObject params; + stream.getInput(appid, "Enter app id : "); + if(appInfoMap.find(appid) != appInfoMap.end()) { + sessionid = appInfoMap.find(appid)->second->m_sessionId; + params["sessionId"] = sessionid; + stream.getInput(secure, "Secure/Plain Transfer [0/1] : "); + params["secure"] = secure; + stream.getInput(sid, "Speech Id (int) : "); + params["id"] = sid; + stream.getInput(stext, "Enter text to be spoken : "); + params["text"] = stext; + ret = remoteObject->Invoke(1000, + _T("speak"), params, result); + if (result["success"].Boolean()) { + cout << "speak call success" << endl; + } else { + cout << "speak call failed. TTS_Status: " << result["TTS_Status"].String() << endl; + } + } else { + cout << "Session hasn't been created for app(" << appid << ")" << endl; + } + } + break; + + case OPT_PAUSE: + { + JsonObject params; + stream.getInput(appid, "Enter app id : "); + if(appInfoMap.find(appid) != appInfoMap.end()) { + sessionid = appInfoMap.find(appid)->second->m_sessionId; + params["sessionId"] = sessionid; + stream.getInput(sid, "Speech Id (int) : "); + params["speechId"] = sid; + ret = remoteObject->Invoke(1000, + _T("pause"), params, result); + if (result["success"].Boolean()) { + cout << "pause call success" << endl; + } else { + cout << "pause call failed. TTS_Status: " << result["TTS_Status"].String() << endl; + } + } else { + cout << "Session hasn't been created for app(" << appid << ")" << endl; + } + } + break; + + case OPT_RESUME: + { + JsonObject params; + stream.getInput(appid, "Enter app id : "); + if(appInfoMap.find(appid) != appInfoMap.end()) { + sessionid = appInfoMap.find(appid)->second->m_sessionId; + params["sessionId"] = sessionid; + stream.getInput(sid, "Speech Id (int) : "); + params["speechId"] = sid; + ret = remoteObject->Invoke(1000, + _T("resume"), params, result); + if (result["success"].Boolean()) { + cout << "resume call success" << endl; + } else { + cout << "resume call failed. TTS_Status: " << result["TTS_Status"].String() << endl; + } + } else { + cout << "Session hasn't been created for app(" << appid << ")" << endl; + } + } + break; + + case OPT_ABORT: + { + JsonObject params; + stream.getInput(appid, "Enter app id : "); + if(appInfoMap.find(appid) != appInfoMap.end()) { + sessionid = appInfoMap.find(appid)->second->m_sessionId; + params["sessionId"] = sessionid; + stream.getInput(clearall, "Should clear pending speeches [y/n]: "); + params["clearPending"] = clearall; + ret = remoteObject->Invoke(1000, + _T("abort"), params, result); + if (result["success"].Boolean()) { + cout << "abort call success" << endl; + } else { + cout << "abort call failed. TTS_Status: " << result["TTS_Status"].String() << endl; + } + } else { + cout << "Session hasn't been created for app(" << appid << ")" << endl; + } + } + break; + + case OPT_IS_SPEAKING: + { + JsonObject params; + stream.getInput(appid, "Enter app id : "); + if(appInfoMap.find(appid) != appInfoMap.end()) { + sessionid = appInfoMap.find(appid)->second->m_sessionId; + params["sessionId"] = sessionid; + ret = remoteObject->Invoke(1000, + _T("isSpeaking"), params, result); + if (result["success"].Boolean()) { + cout << "Session (" << sessionid << ") of app (" << appid << ") is " << (result["speaking"].Boolean() ? "speaking" : "not speaking") << endl; + } else { + cout << "isSpeaking call failed. TTS_Status: " << result["TTS_Status"].String() << endl; + } + } else { + cout << "Session hasn't been created for app(" << appid << ")" << endl; + } + } + break; + + case OPT_SPEECH_STATE: + { + JsonObject params; + stream.getInput(appid, "Enter app id : "); + if(appInfoMap.find(appid) != appInfoMap.end()) { + sessionid = appInfoMap.find(appid)->second->m_sessionId; + params["sessionId"] = sessionid; + stream.getInput(sid, "Speech Id (int) : "); + params["speechId"] = sid; + ret = remoteObject->Invoke(1000, + _T("getSpeechState"), params, result); + if (result["success"].Boolean()) { + string state; + SpeechState sstate; + sstate = result["speechState"].Number(); + switch(sstate) { + case SPEECH_PENDING: state = "Pending"; break; + case SPEECH_IN_PROGRESS: state = "In Progress/Speaking"; break; + case SPEECH_PAUSED: state = "Paused"; break; + default: state = "Not found"; + } + cout << "Speech Status : " << state << endl; + } else { + cout << "getSpeechState call failed. TTS_Status: " << result["TTS_Status"].String() << endl; + } + } else { + cout << "Session hasn't been created for app(" << appid << ")" << endl; + } + } + break; + + case OPT_DESTROY_SESSION: + { + JsonObject params; + stream.getInput(appid, "Enter app id : "); + if(appInfoMap.find(appid) != appInfoMap.end()) { + sessionid = appInfoMap.find(appid)->second->m_sessionId; + params["sessionId"] = sessionid; + ret = remoteObject->Invoke(1000, + _T("destroySession"), params, result); + if (result["success"].Boolean()) { + AppInfo *ai = appInfoMap.find(appid)->second; + appInfoMap.erase(appInfoMap.find(appid)); + delete ai; + cout << "destroySession call success" << endl; + } else { + cout << "destroySession call failed. TTS_Status: " << result["TTS_Status"].String() << endl; + } + } else { + cout << "Session hasn't been created for app(" << appid << ")" << endl; + } + } + break; + + case OPT_EXIT: + exit(0); + + case OPT_BLOCK_TILL_INPUT: { + std::string in; + stream.getInput(in, "Enter any value to continue : ", true); + } + break; + + case OPT_SLEEP: + stream.getInput(appid, "Enter delay (in secs) : "); + sleep(appid); + break; + } + } + } + } + return 0; +} + From 64a15575ea463dfab94bf9b2aa410878dccd661e Mon Sep 17 00:00:00 2001 From: mstrozyn Date: Mon, 22 Jun 2020 05:57:25 +0200 Subject: [PATCH 27/56] RDK-28967: Extend getDeviceInfo method in system thunder plugin Reason for change: Extend supported platforms Test Procedure: Build and test based on README.md Risks: Low Signed-off-by: Mariusz Strozynski --- amlogic.cmake | 3 +++ 1 file changed, 3 insertions(+) diff --git a/amlogic.cmake b/amlogic.cmake index baa90ff8e5..5413d0d5fa 100644 --- a/amlogic.cmake +++ b/amlogic.cmake @@ -113,6 +113,9 @@ if (BUILD_LLAMA) add_definitions (-DENABLE_VREX_SERVICE) add_definitions (-DRF4CE_API) add_definitions (-DHAS_STATE_OBSERVER) + + message("Building with device manufacturer info") + add_definitions (-DENABLE_DEVICE_MANUFACTURER_INFO) endif() if(SCREEN_CAPTURE) From db3d322bb1b0e0d15027a382f3bef257219b99fd Mon Sep 17 00:00:00 2001 From: Arun P Madhavan Date: Tue, 23 Jun 2020 05:10:04 +0000 Subject: [PATCH 28/56] DELIA-43983: [Thunder]-System Plugin Issues Reason for change: getSerialNumber - Removed fork logic, updated setTimezoneDST to check boundary conditions. Test Procedure: Refer ticket Risks: None Signed-off-by: Arun Madhavan --- SystemServices/SystemServices.cpp | 119 +++++++++++++++--------------- 1 file changed, 58 insertions(+), 61 deletions(-) diff --git a/SystemServices/SystemServices.cpp b/SystemServices/SystemServices.cpp index 128d43bcf3..78d51bf4ac 100644 --- a/SystemServices/SystemServices.cpp +++ b/SystemServices/SystemServices.cpp @@ -1183,38 +1183,29 @@ namespace WPEFramework { bool SystemServices::getSerialNumberSnmp(JsonObject& response) { bool retAPIStatus = false; - pid_t pid; - - if (-1 == (pid = fork())) { - LOGERR("could not fork\n"); - return retAPIStatus; - } - if (0 == pid) { - if (Utils::fileExists("/lib/rdk/getStateDetails.sh")) { - execl("/bin/sh", "sh", "-c", - "/lib/rdk/getStateDetails.sh STB_SER_NO", (char *)0); - } else { - populateResponseWithError(SysSrv_FileNotPresent, response); - } - //this script is expected to write to a file - } else { - retAPIStatus = false; - wait(NULL); //wait for child process to finish, only then start reading the file - std::vector lines; - if (true == Utils::fileExists(TMP_SERIAL_NUMBER_FILE)) { - if (getFileContent(TMP_SERIAL_NUMBER_FILE, lines)) { - string serialNumber = lines.front(); - response["serialNumber"] = serialNumber; - retAPIStatus = true; - } else { - populateResponseWithError(SysSrv_FileContentUnsupported, response); - } - } else { - populateResponseWithError(SysSrv_FileNotPresent, response); - } - } - return retAPIStatus; - } + if (!Utils::fileExists("/lib/rdk/getStateDetails.sh")) { + LOGERR("/lib/rdk/getStateDetails.sh not found."); + populateResponseWithError(SysSrv_FileNotPresent, response); + } else { + /* TODO: remove system() once alternate available. */ + system("/lib/rdk/getStateDetails.sh STB_SER_NO"); + std::vector lines; + if (true == Utils::fileExists(TMP_SERIAL_NUMBER_FILE)) { + if (getFileContent(TMP_SERIAL_NUMBER_FILE, lines)) { + string serialNumber = lines.front(); + response["serialNumber"] = serialNumber; + retAPIStatus = true; + } else { + LOGERR("Unexpected contents in %s file.", TMP_SERIAL_NUMBER_FILE); + populateResponseWithError(SysSrv_FileContentUnsupported, response); + } + } else { + LOGERR("%s file not found.", TMP_SERIAL_NUMBER_FILE); + populateResponseWithError(SysSrv_FileNotPresent, response); + } + } + return retAPIStatus; + } /*** * @brief : To retrieve Device Serial Number @@ -1515,40 +1506,46 @@ namespace WPEFramework { /*** * @brief : To set the Time to TZ_FILE. - * @param1[in] : {"params":{"param":{"timeZone":""}}} + * @param1[in] : {"params":{"timeZone":""}} * @param2[out] : {"jsonrpc":"2.0","id":3,"result":{"success":}} * @return : Core:: */ uint32_t SystemServices::setTimeZoneDST(const JsonObject& parameters, JsonObject& response) - { - std::string dir = dirnameOf(TZ_FILE); - JsonObject param; - param.FromString(parameters["param"].String()); - std::string timeZone = param["timeZone"].String(); - ofstream outfile; - bool resp = false; - - if (!dirExists(dir)) { - std::string command = "mkdir -p " + dir + " \0"; - Utils::cRunScript(command.c_str()); - } else { - //Do nothing// - } - - outfile.open(TZ_FILE,ios::out); - if (outfile) { - outfile << timeZone; - outfile.close(); - LOGWARN("Set TimeZone: %s\n", timeZone.c_str()); - resp = true; - } else { - LOGERR("Unable to open %s file.\n", TZ_FILE); - populateResponseWithError(SysSrv_FileAccessFailed, response); - resp = false; - } - returnResponse(resp); - } + { + std::string dir = dirnameOf(TZ_FILE); + ofstream outfile; + std::string timeZone = ""; + bool resp = false; + try { + timeZone = parameters["timeZone"].String(); + if (timeZone.empty() || (timeZone == "null")) { + LOGERR("Empty timeZone received."); + } else { + if (!dirExists(dir)) { + std::string command = "mkdir -p " + dir + " \0"; + Utils::cRunScript(command.c_str()); + } else { + //Do nothing// + } + + outfile.open(TZ_FILE,ios::out); + if (outfile) { + outfile << timeZone; + outfile.close(); + LOGWARN("Set TimeZone: %s\n", timeZone.c_str()); + resp = true; + } else { + LOGERR("Unable to open %s file.\n", TZ_FILE); + populateResponseWithError(SysSrv_FileAccessFailed, response); + resp = false; + } + } + } catch (...) { + LOGERR("catch block : parameters[\"timeZone\"]..."); + } + returnResponse(resp); + } /*** * @brief : To fetch timezone from TZ_FILE. From 0e58a3a7cf821d00ae80f3dba3db713d1b455a68 Mon Sep 17 00:00:00 2001 From: Arun P Madhavan <54109113+arun-madhavan-013@users.noreply.github.com> Date: Tue, 23 Jun 2020 10:51:26 +0530 Subject: [PATCH 29/56] Updated README Updated README per code changes and added a note for callsign mismatch. --- SystemServices/README.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/SystemServices/README.md b/SystemServices/README.md index 2048f0a568..9e8271af7c 100644 --- a/SystemServices/README.md +++ b/SystemServices/README.md @@ -1,7 +1,7 @@ # **System Service Thunder Plugin** (_RDK-25849_) This document enlists the supported Methods and Events of System Service Thunder Plugin. The following section details each. * [**Methods**](#System-Service-Thunder-Plugin-Methods) - - The generic thunder JSONRPC API format is `{"jsonrpc":"2.0","id":"","method":"callsign.pluginVersion.methodName",}` where default value of `callsign` is `SystemServices`, `pluginVersion` is `1` and `methodName` is the methods listed in `Methods` section. `Payload` information is implementation specific to each `method` and is described under respective `method` section. + - The generic thunder JSONRPC API format is `{"jsonrpc":"2.0","id":"","method":"callsign.pluginVersion.methodName",}` where default value of `callsign` is `org.rdk.System`, `pluginVersion` is `1` and `methodName` is the methods listed in `Methods` section. `Payload` information is implementation specific to each `method` and is described under respective `method` section. * [**Events**](#System-Service-Thunder-Plugin-Events) - These JSONRPC events shall be broadcasted to all subscribed endpoints over websocket. Each `event` payload details are enlisted under respective section below. * [**Tests**](#System-Service-Thunder-Plugin-Test-Client) @@ -221,7 +221,7 @@ - **setTimeZoneDST** To set the Time to System TZ_FILE. - _**Request payload:**_ `{"params":{"param":{"timezone":""}}}` + _**Request payload:**_ `{"params":{"timezone":""}}` _**Response payload:**_ `{"result":{"success":}}` - **updateFirmware** @@ -262,6 +262,8 @@ **API request format** `curl --header "Content-Type:application/json" --request POST http://localhost:9998/jsonrpc --data-raw ''` +_Note:_ Here callsign used is `org.rdk.SystemServices` instead of actual `org.rdk.System` since testing purpose it was set so. + Method | Request Payload | Response Payload :--- | :--- | :--- | cacheContains | {"jsonrpc":"2.0","id":"1","method":"org.rdk.SystemServices.1.cacheContains","params":{"param":{"cacheKey":"sampleCache"}}} | {"jsonrpc":"2.0","id":1,"result":{"success":true}} | @@ -305,5 +307,5 @@ Method | Request Payload | Response Payload | setPowerState | {"jsonrpc":"2.0","id":"38","method":"org.rdk.SystemServices.1.setPowerState","params":{"param":{"powerState":"ON", "standbyReason":"APIUnitTest"}}} | {"jsonrpc":"2.0","id":38,"result":{"success":true}} | | setPreferredStandbyMode | {"jsonrpc":"2.0","id":"39","method":"org.rdk.SystemServices.1.setPreferredStandbyMode","params":{"param":{"mode":"DEEP_SLEEP"}}} | {"jsonrpc":"2.0","id":39,"result":{"success":true}} | | setTemperatureThresholds | {"jsonrpc":"2.0","id":"40","method":"org.rdk.SystemServices.1.setTemperatureThresholds","params":{"thresholds":{"WARN":"50.000000","MAX":"80.000000"}}} | {"jsonrpc":"2.0","id":40,"result":{"success":true}} | -| setTimeZoneDST | {"jsonrpc":"2.0","id":"41","method":"org.rdk.SystemServices.1.setTimeZoneDST","params":{"param":{"timezone":"UTC-5"}}} | {"jsonrpc":"2.0","id":41,"result":{"success":true}} | +| setTimeZoneDST | {"jsonrpc":"2.0","id":"41","method":"org.rdk.SystemServices.1.setTimeZoneDST","params":{"timezone":"UTC-5"}} | {"jsonrpc":"2.0","id":41,"result":{"success":true}} | | updateFirmware | {"jsonrpc":"2.0","id":"42","method":"org.rdk.SystemServices.1.updateFirmware","params":{}} | {"jsonrpc":"2.0","id":42,"result":{"success":true}} | From 6963ea6997cfb52a287de2ef19ac33cd60094576 Mon Sep 17 00:00:00 2001 From: Vijay Selvaraj Date: Tue, 23 Jun 2020 09:30:43 -0400 Subject: [PATCH 30/56] RDK-25261: Initial version of Packager from rdkcentral/ThunderNanoServices --- Packager/CMakeLists.txt | 51 ++++ Packager/Module.cpp | 22 ++ Packager/Module.h | 33 +++ Packager/Packager.config | 9 + Packager/Packager.cpp | 146 ++++++++++++ Packager/Packager.h | 138 +++++++++++ Packager/PackagerImplementation.cpp | 353 ++++++++++++++++++++++++++++ Packager/PackagerImplementation.h | 306 ++++++++++++++++++++++++ Packager/PackagerPlugin.json | 16 ++ Packager/doc/PackagerPlugin.md | 181 ++++++++++++++ Packager/opkg.conf.in | 6 + 11 files changed, 1261 insertions(+) create mode 100644 Packager/CMakeLists.txt create mode 100644 Packager/Module.cpp create mode 100644 Packager/Module.h create mode 100644 Packager/Packager.config create mode 100644 Packager/Packager.cpp create mode 100644 Packager/Packager.h create mode 100644 Packager/PackagerImplementation.cpp create mode 100644 Packager/PackagerImplementation.h create mode 100644 Packager/PackagerPlugin.json create mode 100644 Packager/doc/PackagerPlugin.md create mode 100644 Packager/opkg.conf.in diff --git a/Packager/CMakeLists.txt b/Packager/CMakeLists.txt new file mode 100644 index 0000000000..0b7363eaef --- /dev/null +++ b/Packager/CMakeLists.txt @@ -0,0 +1,51 @@ +# If not stated otherwise in this file or this component's LICENSE file the +# following copyright and licenses apply: +# +# Copyright 2020 RDK Management +# +# 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. + +set(PLUGIN_NAME Packager) +set(MODULE_NAME WPEFramework${PLUGIN_NAME}) + +find_package(${NAMESPACE}Plugins REQUIRED) +find_package(libprovision REQUIRED) +find_package(LibOPKG REQUIRED) +find_package(CompileSettingsDebug CONFIG REQUIRED) + +add_library(${MODULE_NAME} SHARED + Module.cpp + Packager.cpp + PackagerImplementation.cpp) + +target_link_libraries(${MODULE_NAME} + PRIVATE + CompileSettingsDebug::CompileSettingsDebug + ${NAMESPACE}Plugins::${NAMESPACE}Plugins + libprovision::libprovision + LibOPKG::LibOPKG + ) + +string(TOLOWER ${NAMESPACE} STORAGENAME) +install(TARGETS ${MODULE_NAME} + DESTINATION lib/${STORAGENAME}/plugins) + +configure_file( + "${CMAKE_CURRENT_SOURCE_DIR}/opkg.conf.in" + "${CMAKE_CURRENT_BINARY_DIR}/opkg.conf" + @ONLY) + +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/opkg.conf + DESTINATION "/usr/share/${NAMESPACE}/${PLUGIN_NAME}") + +write_config(${PLUGIN_NAME}) diff --git a/Packager/Module.cpp b/Packager/Module.cpp new file mode 100644 index 0000000000..a85f16252b --- /dev/null +++ b/Packager/Module.cpp @@ -0,0 +1,22 @@ +/* + * If not stated otherwise in this file or this component's LICENSE file the + * following copyright and licenses apply: + * + * Copyright 2020 RDK Management + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Module.h" + +MODULE_NAME_DECLARATION(BUILD_REFERENCE) diff --git a/Packager/Module.h b/Packager/Module.h new file mode 100644 index 0000000000..fa7b3aed27 --- /dev/null +++ b/Packager/Module.h @@ -0,0 +1,33 @@ +/* + * If not stated otherwise in this file or this component's LICENSE file the + * following copyright and licenses apply: + * + * Copyright 2020 RDK Management + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __MODULE_PLUGIN_PACKAGER_H +#define __MODULE_PLUGIN_PACKAGER_H + +#ifndef MODULE_NAME +#define MODULE_NAME Plugin_Packager +#endif + +#include +#include + +#undef EXTERNAL +#define EXTERNAL + +#endif // __MODULE_PLUGIN_PACKAGER_H diff --git a/Packager/Packager.config b/Packager/Packager.config new file mode 100644 index 0000000000..5da847a650 --- /dev/null +++ b/Packager/Packager.config @@ -0,0 +1,9 @@ +set (autostart true) + +map() + key(root) + map() + kv(outofprocess true) + end() +end() +ans(configuration) diff --git a/Packager/Packager.cpp b/Packager/Packager.cpp new file mode 100644 index 0000000000..b596216eed --- /dev/null +++ b/Packager/Packager.cpp @@ -0,0 +1,146 @@ +/* + * If not stated otherwise in this file or this component's LICENSE file the + * following copyright and licenses apply: + * + * Copyright 2020 RDK Management + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Packager.h" + +namespace WPEFramework { +namespace Plugin { +namespace { + + constexpr int kMaxValueLength = 256; + +} + + SERVICE_REGISTRATION(Packager, 1, 0); + + const string Packager::Initialize(PluginHost::IShell* service) { + ASSERT (_service == nullptr); + ASSERT (service != nullptr); + + _service = service; + _skipURL = static_cast(service->WebPrefix().length()); + _service->Register(&_notification); + + string result; + _implementation = _service->Root(_connectionId, 2000, _T("PackagerImplementation")); + if (_implementation == nullptr) { + result = _T("Couldn't create package instance"); + } else { + if (_implementation->Configure(_service) != Core::ERROR_NONE) { + result = _T("Couldn't initialize package instance"); + } + } + + return (result); + } + + void Packager::Deinitialize(PluginHost::IShell* service) + { + ASSERT(_service == service); + + _service->Unregister(&_notification); + + if (_implementation->Release() != Core::ERROR_DESTRUCTION_SUCCEEDED) { + + ASSERT(_connectionId != 0); + + RPC::IRemoteConnection* connection(_service->RemoteConnection(_connectionId)); + + // The process can disappear in the meantime... + if (connection != nullptr) { + + // But if it did not dissapear in the meantime, forcefully terminate it. Shoot to kill :-) + connection->Terminate(); + connection->Release(); + } + } + + _service = nullptr; + _implementation = nullptr; + } + + string Packager::Information() const + { + return (string()); + } + + void Packager::Inbound(Web::Request& request) + { + } + + Core::ProxyType Packager::Process(const Web::Request& request) + { + ASSERT(_skipURL <= request.Path.length()); + + Core::ProxyType result(PluginHost::IFactories::Instance().Response()); + Core::TextSegmentIterator index(Core::TextFragment(request.Path, _skipURL, request.Path.length() - _skipURL), false, '/'); + + // Always skip the first one, it is an empty part because we start with a '/' if there are more parameters. + index.Next(); + + result->ErrorCode = Web::STATUS_BAD_REQUEST; + result->Message = _T("Invalid request to packager plugin."); + + if (index.Next() && (request.Verb == Web::Request::HTTP_POST || request.Verb == Web::Request::HTTP_PUT)) { + uint32_t status = Core::ERROR_UNAVAILABLE; + if (index.Current().Text() == "Install") { + std::array package {0}; + std::array version {0}; + std::array arch {0}; + Core::URL::KeyValue options(request.Query.Value()); + if (options.Exists(_T("Package"), true) == true) { + const string name (options[_T("Package")].Text()); + Core::URL::Decode (name.c_str(), name.length(), package.data(), package.size()); + } + if (options.Exists(_T("Architecture"), true) == true) { + const string name (options[_T("Architecture")].Text()); + Core::URL::Decode (name.c_str(), name.length(), arch.data(), arch.size()); + } + if (options.Exists(_T("Version"), true) == true) { + const string name (options[_T("Version")].Text()); + Core::URL::Decode (name.c_str(), name.length(), version.data(), version.size()); + } + + status = _implementation->Install(package.data(), version.data(), arch.data()); + } else if (index.Current().Text() == "SynchronizeRepository") { + status = _implementation->SynchronizeRepository(); + } + + if (status == Core::ERROR_NONE) { + result->ErrorCode = Web::STATUS_OK; + result->Message = _T("OK"); + } else if (status == Core::ERROR_INPROGRESS) { + result->Message = _T("Some operation already in progress. Only one at a time is allowed"); + } + } + + return(result); + } + + void Packager::Deactivated(RPC::IRemoteConnection* connection) + { + if (connection->Id() == _connectionId) { + ASSERT(_service != nullptr); + Core::IWorkerPool::Instance().Submit(PluginHost::IShell::Job::Create(_service, + PluginHost::IShell::DEACTIVATED, + PluginHost::IShell::FAILURE)); + } + } +} // namespace Plugin +} // namespace WPEFramework diff --git a/Packager/Packager.h b/Packager/Packager.h new file mode 100644 index 0000000000..951b71e258 --- /dev/null +++ b/Packager/Packager.h @@ -0,0 +1,138 @@ +/* + * If not stated otherwise in this file or this component's LICENSE file the + * following copyright and licenses apply: + * + * Copyright 2020 RDK Management + * + * 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. + */ + +#pragma once + +#include "Module.h" +#include + +namespace WPEFramework { +namespace Plugin { +namespace { + constexpr auto* kInstallMethodName = _T("install"); + constexpr auto* kSynchronizeMethodName = _T("synchronize"); +} + + class Packager : public PluginHost::IPlugin, public PluginHost::IWeb, public PluginHost::JSONRPC { + public: + struct Params : public Core::JSON::Container { + Params& operator=(const Params& other) = delete; + Params() { + Add(_T("package"), &Package); + Add(_T("architecture"), &Architecture); + Add(_T("version"), &Version); + } + Params(const Params& other) + : Package(other.Package) + , Architecture(other.Architecture) + , Version(other.Version) { + Add(_T("package"), &Package); + Add(_T("architecture"), &Architecture); + Add(_T("version"), &Version); + } + Core::JSON::String Package; + Core::JSON::String Architecture; + Core::JSON::String Version; + }; + + Packager(const Packager&) = delete; + Packager& operator=(const Packager&) = delete; + Packager() + : _skipURL(0) + , _connectionId(0) + , _service(nullptr) + , _implementation(nullptr) + , _notification(this) + { + Register(kInstallMethodName, [this](const Params& params) -> uint32_t { + return this->_implementation->Install(params.Package.Value(), params.Version.Value(), + params.Architecture.Value()); + }); + Register(kSynchronizeMethodName, [this]() -> uint32_t { + return this->_implementation->SynchronizeRepository(); + }); + } + + ~Packager() override + { + Unregister(kInstallMethodName); + Unregister(kSynchronizeMethodName); + } + + BEGIN_INTERFACE_MAP(Packager) + INTERFACE_ENTRY(PluginHost::IPlugin) + INTERFACE_ENTRY(PluginHost::IWeb) + INTERFACE_ENTRY(PluginHost::IDispatcher) + INTERFACE_AGGREGATE(Exchange::IPackager, _implementation) + END_INTERFACE_MAP + + // IPlugin methods + const string Initialize(PluginHost::IShell* service) override; + void Deinitialize(PluginHost::IShell* service) override; + string Information() const override; + + // IWeb methods + void Inbound(Web::Request& request) override; + Core::ProxyType Process(const Web::Request& request) override; + + private: + class Notification : public RPC::IRemoteConnection::INotification { + public: + explicit Notification(Packager* parent) + : _parent(*parent) + { + ASSERT(parent != nullptr); + } + + ~Notification() override + { + } + + Notification() = delete; + Notification(const Notification&) = delete; + Notification& operator=(const Notification&) = delete; + + void Activated(RPC::IRemoteConnection*) override + { + } + + void Deactivated(RPC::IRemoteConnection* connection) override + { + _parent.Deactivated(connection); + } + + BEGIN_INTERFACE_MAP(Notification) + INTERFACE_ENTRY(RPC::IRemoteConnection::INotification) + END_INTERFACE_MAP + + private: + Packager& _parent; + }; + + void Deactivated(RPC::IRemoteConnection* connection); + + uint8_t _skipURL; + uint32_t _connectionId; + PluginHost::IShell* _service; + Exchange::IPackager* _implementation; + Core::Sink _notification; + }; + +} // namespace Plugin +} // namespace WPEFramework diff --git a/Packager/PackagerImplementation.cpp b/Packager/PackagerImplementation.cpp new file mode 100644 index 0000000000..7db85a4ca5 --- /dev/null +++ b/Packager/PackagerImplementation.cpp @@ -0,0 +1,353 @@ +/* + * If not stated otherwise in this file or this component's LICENSE file the + * following copyright and licenses apply: + * + * Copyright 2020 RDK Management + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "PackagerImplementation.h" + +#if defined (DO_NOT_USE_DEPRECATED_API) +#include +#else +#include +#endif +#include + + +namespace WPEFramework { +namespace Plugin { + + SERVICE_REGISTRATION(PackagerImplementation, 1, 0); + + void PackagerImplementation::UpdateConfig() const { + ASSERT(!_configFile.empty() && !_tempPath.empty() && !_cachePath.empty()); + opkg_config->conf_file = strdup(_configFile.c_str()); + opkg_config->tmp_dir = strdup(_tempPath.c_str()); + opkg_config->host_cache_dir = 1; + opkg_config->cache_dir = strdup(_cachePath.c_str()); + opkg_config->verbosity = _verbosity; + opkg_config->nodeps = _noDeps; + + if (_volatileCache == true) { + opkg_config->volatile_cache = 1; + } + + if (_skipSignatureChecking == true) { + opkg_config->check_pkg_signature = 0; + } else { + opkg_config->check_pkg_signature = 1; + opkg_config->signature_type = strdup("provision"); + } + } + + uint32_t PackagerImplementation::Configure(PluginHost::IShell* service) + { + uint32_t result = Core::ERROR_NONE; + Config config; + + config.FromString(service->ConfigLine()); + + if ((config.ConfigFile.IsSet() == true) && (config.ConfigFile.Value().empty() == false)) { + _configFile = config.ConfigFile.Value(); + } else { + _configFile = service->DataPath() + _T("opkg.conf"); + } + + if ((config.TempDir.IsSet() == true) && (config.TempDir.Value().empty() == false)) { + _tempPath = Core::Directory::Normalize(config.TempDir.Value()); + } else { + _tempPath = service->VolatilePath() + service->Callsign(); + } + + if ((config.CacheDir.IsSet() == true) && (config.CacheDir.Value().empty() == false)) { + _cachePath = Core::Directory::Normalize(config.CacheDir.Value()); + } else { + _cachePath = service->PersistentPath() + service->Callsign(); + } + + if (config.Verbosity.IsSet() == true) { + _verbosity = config.Verbosity.Value(); + } + + if (config.NoDeps.IsSet() == true) { + _noDeps = config.NoDeps.Value(); + } + + if (config.NoSignatureCheck.IsSet() == true) { + _skipSignatureChecking = config.NoSignatureCheck.Value(); + } + + if (config.AlwaysUpdateFirst.IsSet() == true) { + _alwaysUpdateFirst = config.AlwaysUpdateFirst.Value(); + } + + if (config.MakeCacheVolatile.IsSet() == true) { + _volatileCache = config.MakeCacheVolatile.Value(); + } + + if (Core::File(_configFile).Exists() == false) { + result = Core::ERROR_GENERAL; + } else if (Core::Directory(_tempPath.c_str()).CreatePath() == false) { + result = Core::ERROR_GENERAL; + } else if (Core::Directory(_cachePath.c_str()).CreatePath() == false) { + result = Core::ERROR_GENERAL; + } else { + /* See Install() for explanation why it's not done here. + if (InitOPKG() == false) { + result = Core::ERROR_GENERAL; + } + */ + } + + return (result); + } + + PackagerImplementation::~PackagerImplementation() + { + FreeOPKG(); + } + + void PackagerImplementation::Register(Exchange::IPackager::INotification* notification) + { + ASSERT(notification); + _adminLock.Lock(); + notification->AddRef(); + _notifications.push_back(notification); + if (_inProgress.Install != nullptr) { + ASSERT(_inProgress.Package != nullptr); + notification->StateChange(_inProgress.Package, _inProgress.Install); + } + _adminLock.Unlock(); + } + + void PackagerImplementation::Unregister(const Exchange::IPackager::INotification* notification) + { + ASSERT(notification); + _adminLock.Lock(); + auto item = std::find(_notifications.begin(), _notifications.end(), notification); + ASSERT(item != _notifications.end()); + _notifications.erase(item); + (*item)->Release(); + _adminLock.Unlock(); + } + + uint32_t PackagerImplementation::Install(const string& name, const string& version, const string& arch) + { + return DoWork(&name, &version, &arch); + } + + uint32_t PackagerImplementation::SynchronizeRepository() + { + return DoWork(nullptr, nullptr, nullptr); + } + + uint32_t PackagerImplementation::DoWork(const string* name, const string* version, const string* arch) + { + uint32_t result = Core::ERROR_INPROGRESS; + + _adminLock.Lock(); + if (_inProgress.Install == nullptr && _isSyncing == false) { + ASSERT(_inProgress.Package == nullptr); + result = Core::ERROR_NONE; + // OPKG bug: it marks it checked dependency for a package as cyclic dependency handling fix + // but since in our case it's not an process which dies when done, this info survives and makes the + // deps check to be skipped on subsequent calls. This is why hash_deinit() is called below + // and needs to be initialized here agian. + if (_opkgInitialized == true) // it was initialized + FreeOPKG(); + _opkgInitialized = InitOPKG(); + + if (_opkgInitialized) { + if (name && version && arch) { + _inProgress.Package = Core::Service::Create(*name, *version, *arch); + _inProgress.Install = Core::Service::Create(); + } else { + _isSyncing = true; + } + _worker.Run(); + } else { + result = Core::ERROR_GENERAL; + } + } + _adminLock.Unlock(); + + return result; + + } + + void PackagerImplementation::BlockingInstallUntilCompletionNoLock() { + ASSERT(_inProgress.Install != nullptr && _inProgress.Package != nullptr); + +#if defined (DO_NOT_USE_DEPRECATED_API) + opkg_cmd_t* command = opkg_cmd_find("install"); + if (command) { + _inProgress.Install->SetState(Exchange::IPackager::INSTALLING); + NotifyStateChange(); + opkg_config->pfm = command->pfm; + std::unique_ptr targetCopy(new char [_inProgress.Package->Name().length() + 1]); + std::copy_n(_inProgress.Package->Name().begin(), _inProgress.Package->Name().length(), targetCopy.get()); + (targetCopy.get())[_inProgress.Package->Name().length()] = 0; + const char* argv[1]; + argv[0] = targetCopy.get(); + if (opkg_cmd_exec(command, 1, argv) == 0) { + _inProgress.Install->SetProgress(100); + _inProgress.Install->SetState(Exchange::IPackager::INSTALLED); + } else { + _inProgress.Install->SetError(Core::ERROR_GENERAL); + } + NotifyStateChange(); + } else { + _inProgress.Install->SetError(Core::ERROR_GENERAL); + NotifyStateChange(); + } +#else + _isUpgrade = false; + opkg_package_callback_t checkUpgrade = [](pkg* pkg, void* user_data) { + PackagerImplementation* self = static_cast(user_data); + if (self->_isUpgrade == false) { + self->_isUpgrade = self->_inProgress.Package->Name() == pkg->name; + if (self->_isUpgrade && self->_inProgress.Package->Version().empty() == false) { + self->_isUpgrade = opkg_compare_versions(pkg->version, + self->_inProgress.Package->Version().c_str()) < 0; + } + } + }; + opkg_list_upgradable_packages(checkUpgrade, this); + + typedef int (*InstallFunction)(const char *, opkg_progress_callback_t, void *); + InstallFunction installFunction = opkg_install_package; + if (_isUpgrade) { + installFunction = opkg_upgrade_package; + } + _isUpgrade = false; + + if (installFunction(_inProgress.Package->Name().c_str(), PackagerImplementation::InstallationProgessNoLock, + this) != 0) { + _inProgress.Install->SetError(Core::ERROR_GENERAL); + NotifyStateChange(); + } +#endif + } + +#if !defined (DO_NOT_USE_DEPRECATED_API) + /* static */ void PackagerImplementation::InstallationProgessNoLock(const opkg_progress_data_t* progress, + void* data) + { + PackagerImplementation* self = static_cast(data); + self->_inProgress.Install->SetProgress(progress->percentage); + if (progress->action == OPKG_INSTALL && + self->_inProgress.Install->State() == Exchange::IPackager::DOWNLOADING) { + self->_inProgress.Install->SetState(Exchange::IPackager::DOWNLOADED); + self->NotifyStateChange(); + } + bool stateChanged = false; + switch (progress->action) { + case OPKG_DOWNLOAD: + if (self->_inProgress.Install->State() != Exchange::IPackager::DOWNLOADING) { + self->_inProgress.Install->SetState(Exchange::IPackager::DOWNLOADING); + stateChanged = true; + } + break; + case OPKG_INSTALL: + if (self->_inProgress.Install->State() != Exchange::IPackager::INSTALLING) { + self->_inProgress.Install->SetState(Exchange::IPackager::INSTALLING); + stateChanged = true; + } + break; + } + + if (stateChanged == true) + self->NotifyStateChange(); + if (progress->percentage == 100) { + self->_inProgress.Install->SetState(Exchange::IPackager::INSTALLED); + self->NotifyStateChange(); + } + } +#endif + + void PackagerImplementation::NotifyStateChange() + { + _adminLock.Lock(); + TRACE_L1("State for %s changed to %d (%d %%, %d)", _inProgress.Package->Name().c_str(), _inProgress.Install->State(), _inProgress.Install->Progress(), _inProgress.Install->ErrorCode()); + for (auto* notification : _notifications) { + notification->StateChange(_inProgress.Package, _inProgress.Install); + } + _adminLock.Unlock(); + } + + void PackagerImplementation::NotifyRepoSynced(uint32_t status) + { + _adminLock.Lock(); + _isSyncing = false; + for (auto* notification : _notifications) { + notification->RepositorySynchronize(status); + } + _adminLock.Unlock(); + } + + bool PackagerImplementation::InitOPKG() + { + UpdateConfig(); +#if defined DO_NOT_USE_DEPRECATED_API + return opkg_conf_init() == 0 && pkg_hash_load_feeds() == 0 && pkg_hash_load_status_files() == 0; +#else + return opkg_new() == 0; +#endif + } + + void PackagerImplementation::FreeOPKG() + { + if (_opkgInitialized == true) { + opkg_download_cleanup(); + opkg_conf_deinit(); + _opkgInitialized = false; + } + } + + void PackagerImplementation::BlockingSetupLocalRepoNoLock(RepoSyncMode mode) + { + string dirPath = Core::ToString(opkg_config->lists_dir); + Core::Directory dir(dirPath.c_str()); + bool containFiles = false; + if (mode == RepoSyncMode::SETUP && _alwaysUpdateFirst == false) { + while (dir.Next() == true) { + if (dir.Name() != _T(".") && dir.Name() != _T("..") && dir.Name() != dirPath) { + containFiles = true; + break; + } + } + } + ASSERT(mode == RepoSyncMode::SETUP || _isSyncing == true); + if (containFiles == false) { + uint32_t result = Core::ERROR_NONE; +#if defined DO_NOT_USE_DEPRECATED_API + opkg_cmd_t* command = opkg_cmd_find("update"); + if (command) + opkg_config->pfm = command->pfm; + if (command == nullptr || opkg_cmd_exec(command, 0, nullptr) != 0) +#else + if (opkg_update_package_lists(nullptr, nullptr) != 0) +#endif + { + TRACE_L1("Failed to set up local repo. Installing might not work"); + result = Core::ERROR_GENERAL; + } + NotifyRepoSynced(result); + } + } + +} // namespace Plugin +} // namespace WPEFramework diff --git a/Packager/PackagerImplementation.h b/Packager/PackagerImplementation.h new file mode 100644 index 0000000000..53e0588169 --- /dev/null +++ b/Packager/PackagerImplementation.h @@ -0,0 +1,306 @@ +/* + * If not stated otherwise in this file or this component's LICENSE file the + * following copyright and licenses apply: + * + * Copyright 2020 RDK Management + * + * 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. + */ + +#pragma once + +#include "Module.h" +#include + +#include +#include + +// Forward declarations so we do not need to include the OPKG headers here. +struct opkg_conf; +struct _opkg_progress_data_t; + +namespace WPEFramework { +namespace Plugin { + + class PackagerImplementation : public Exchange::IPackager { + public: + PackagerImplementation(const PackagerImplementation&) = delete; + PackagerImplementation& operator=(const PackagerImplementation&) = delete; + + class EXTERNAL Config : public Core::JSON::Container { + public: + Config() + : Core::JSON::Container() + , TempDir() // Specify tmp-dir. + , CacheDir() // Specify cache directory + , MakeCacheVolatile(false) // Use volatile cache. Volatile cache will be cleared on exit + , Verbosity() + , NoDeps() + , NoSignatureCheck() + , AlwaysUpdateFirst() + { + Add(_T("config"), &ConfigFile); + Add(_T("temppath"), &TempDir); + Add(_T("cachepath"), &CacheDir); + Add(_T("volatilecache"), &MakeCacheVolatile); + Add(_T("verbosity"), &Verbosity); + Add(_T("nodeps"), &NoDeps); + Add(_T("nosignaturecheck"), &NoSignatureCheck); + Add(_T("alwaysupdatefirst"), &AlwaysUpdateFirst); + } + + ~Config() override + { + } + + Config(const Config&) = delete; + Config& operator=(const Config&) = delete; + + Core::JSON::String ConfigFile; + Core::JSON::String TempDir; + Core::JSON::String CacheDir; + Core::JSON::Boolean MakeCacheVolatile; + Core::JSON::DecUInt8 Verbosity; + Core::JSON::Boolean NoDeps; + Core::JSON::Boolean NoSignatureCheck; + Core::JSON::Boolean AlwaysUpdateFirst; + }; + + PackagerImplementation() + : _adminLock() + , _configFile() + , _tempPath() + , _cachePath() + , _verbosity(0) + , _noDeps(false) + , _skipSignatureChecking(false) + , _alwaysUpdateFirst(false) + , _volatileCache(false) + , _opkgInitialized(false) + , _worker(this) + , _isUpgrade(false) + , _isSyncing(false) + { + } + + ~PackagerImplementation() override; + + BEGIN_INTERFACE_MAP(PackagerImplementation) + INTERFACE_ENTRY(Exchange::IPackager) + END_INTERFACE_MAP + + // IPackager methods + void Register(Exchange::IPackager::INotification* observer) override; + void Unregister(const Exchange::IPackager::INotification* observer) override; + uint32_t Configure(PluginHost::IShell* service) override; + uint32_t Install(const string& name, const string& version, const string& arch) override; + uint32_t SynchronizeRepository() override; + + private: + class PackageInfo : public Exchange::IPackager::IPackageInfo { + public: + PackageInfo(const PackageInfo&) = delete; + PackageInfo& operator=(const PackageInfo&) = delete; + + ~PackageInfo() override + { + } + + PackageInfo(const std::string& name, + const std::string& version, + const std::string& arch) + : _name(name) + , _version(version) + , _arch(arch) + { + } + + BEGIN_INTERFACE_MAP(PackageInfo) + INTERFACE_ENTRY(Exchange::IPackager::IPackageInfo) + END_INTERFACE_MAP + + // IPackageInfo methods + string Name() const override + { + return _name; + } + + string Version() const override + { + return _version; + } + + string Architecture() const override + { + return _arch; + } + + private: + std::string _name; + std::string _version; + std::string _arch; + }; + + class InstallInfo : public Exchange::IPackager::IInstallationInfo { + public: + InstallInfo(const PackageInfo&) = delete; + InstallInfo& operator=(const PackageInfo&) = delete; + + ~InstallInfo() override + { + } + + InstallInfo() = default; + + BEGIN_INTERFACE_MAP(InstallInfo) + INTERFACE_ENTRY(Exchange::IPackager::IInstallationInfo) + END_INTERFACE_MAP + + // IInstallationInfo methods + Exchange::IPackager::state State() const override + { + return _state; + } + + uint8_t Progress() const override + { + return _progress; + } + + uint32_t ErrorCode() const override + { + return _error; + } + + uint32_t Abort() override + { + return _error != 0 ? Core::ERROR_NONE : Core::ERROR_UNAVAILABLE; + } + + void SetState(Exchange::IPackager::state state) + { + TRACE_L1("Setting state to %d", state); + _state = state; + } + + void SetProgress(uint8_t progress) + { + TRACE_L1("Setting progress to %d", progress); + _progress = progress; + } + + void SetError(uint32_t err) + { + TRACE_L1("Setting error to %d", err); + _error = err; + } + + private: + Exchange::IPackager::state _state = Exchange::IPackager::IDLE; + uint32_t _error = 0u; + uint8_t _progress = 0u; + }; + + struct InstallationData { + InstallationData(const InstallationData& other) = delete; + InstallationData& operator=(const InstallationData& other) = delete; + InstallationData() = default; + + ~InstallationData() + { + if (Package) + Package->Release(); + if (Install) + Install->Release(); + } + PackageInfo* Package = nullptr; + InstallInfo* Install = nullptr; + }; + + class InstallThread : public Core::Thread { + public: + InstallThread(PackagerImplementation* parent) + : _parent(parent) + {} + + InstallThread& operator=(const InstallThread&) = delete; + InstallThread(const InstallThread&) = delete; + + uint32_t Worker() override { + while(IsRunning() == true) { + _parent->_adminLock.Lock(); // The parent may have lock when this starts so wait for it to release. + bool isInstall = _parent->_inProgress.Install != nullptr; + ASSERT(isInstall != true || _parent->_inProgress.Package != nullptr); + _parent->_adminLock.Unlock(); + + // After this point locking is not needed because API running on other threads only read if in + // progress is filled in. + _parent->BlockingSetupLocalRepoNoLock(isInstall == true ? RepoSyncMode::SETUP : RepoSyncMode::FORCED); + if (isInstall) + _parent->BlockingInstallUntilCompletionNoLock(); + + if (isInstall) { + _parent->_adminLock.Lock(); + _parent->_inProgress.Install->Release(); + _parent->_inProgress.Package->Release(); + _parent->_inProgress.Install = nullptr; + _parent->_inProgress.Package = nullptr; + _parent->_adminLock.Unlock(); + } + + Block(); + } + + return Core::infinite; + } + + private: + PackagerImplementation* _parent; + }; + + enum class RepoSyncMode { + FORCED, + SETUP + }; + + uint32_t DoWork(const string* name, const string* version, const string* arch); + void UpdateConfig() const; +#if !defined (DO_NOT_USE_DEPRECATED_API) + static void InstallationProgessNoLock(const _opkg_progress_data_t* progress, void* data); +#endif + void NotifyStateChange(); + void NotifyRepoSynced(uint32_t status); + void BlockingInstallUntilCompletionNoLock(); + void BlockingSetupLocalRepoNoLock(RepoSyncMode mode); + bool InitOPKG(); + void FreeOPKG(); + + Core::CriticalSection _adminLock; + string _configFile; + string _tempPath; + string _cachePath; + int _verbosity; + bool _noDeps; + bool _skipSignatureChecking; + bool _alwaysUpdateFirst; + bool _volatileCache; + bool _opkgInitialized; + std::vector _notifications; + InstallationData _inProgress; + InstallThread _worker; + bool _isUpgrade; + bool _isSyncing; + }; + +} // namespace Plugin +} // namespace WPEFramework diff --git a/Packager/PackagerPlugin.json b/Packager/PackagerPlugin.json new file mode 100644 index 0000000000..e175718f3d --- /dev/null +++ b/Packager/PackagerPlugin.json @@ -0,0 +1,16 @@ +{ + "$schema": "plugin.schema.json", + "info": { + "title": "Packager Plugin", + "callsign": "Packager", + "locator": "libWPEFrameworkPackager.so", + "status": "alpha", + "description": [ + "The Packager plugin allows installation of OPKG, IPKG and DEB packages to the system from a remote repository." + ], + "version": "1.0" + }, + "interface": { + "$ref": "{interfacedir}/Packager.json" + } +} diff --git a/Packager/doc/PackagerPlugin.md b/Packager/doc/PackagerPlugin.md new file mode 100644 index 0000000000..df7628ad29 --- /dev/null +++ b/Packager/doc/PackagerPlugin.md @@ -0,0 +1,181 @@ + + +# Packager Plugin + +**Version: 1.0** + +**Status: :black_circle::white_circle::white_circle:** + +Packager plugin for Thunder framework. + +### Table of Contents + +- [Introduction](#head.Introduction) +- [Description](#head.Description) +- [Configuration](#head.Configuration) +- [Methods](#head.Methods) + + +# Introduction + + +## Scope + +This document describes purpose and functionality of the Packager plugin. It includes detailed specification of its configuration and methods provided. + + +## Case Sensitivity + +All identifiers on the interface described in this document are case-sensitive. Thus, unless stated otherwise, all keywords, entities, properties, relations and actions should be treated as such. + + +## Acronyms, Abbreviations and Terms + +The table below provides and overview of acronyms used in this document and their definitions. + +| Acronym | Description | +| :-------- | :-------- | +| API | Application Programming Interface | +| HTTP | Hypertext Transfer Protocol | +| JSON | JavaScript Object Notation; a data interchange format | +| JSON-RPC | A remote procedure call protocol encoded in JSON | + +The table below provides and overview of terms and abbreviations used in this document and their definitions. + +| Term | Description | +| :-------- | :-------- | +| callsign | The name given to an instance of a plugin. One plugin can be instantiated multiple times, but each instance the instance name, callsign, must be unique. | + + +## References + +| Ref ID | Description | +| :-------- | :-------- | +| [HTTP](http://www.w3.org/Protocols) | HTTP specification | +| [JSON-RPC](https://www.jsonrpc.org/specification) | JSON-RPC 2.0 specification | +| [JSON](http://www.json.org/) | JSON specification | +| [Thunder](https://github.com/WebPlatformForEmbedded/Thunder/blob/master/doc/WPE%20-%20API%20-%20WPEFramework.docx) | Thunder API Reference | + + +# Description + +The Packager plugin allows installation of OPKG, IPKG and DEB packages to the system from a remote repository. + +The plugin is designed to be loaded and executed within the Thunder framework. For more information about the framework refer to [[Thunder](#ref.Thunder)]. + + +# Configuration + +The table below lists configuration options of the plugin. + +| Name | Type | Description | +| :-------- | :-------- | :-------- | +| callsign | string | Plugin instance name (default: *Packager*) | +| classname | string | Class name: *Packager* | +| locator | string | Library name: *libWPEFrameworkPackager.so* | +| autostart | boolean | Determines if the plugin is to be started automatically along with the framework | + + +# Methods + +The following methods are provided by the Packager plugin: + +Packager interface methods: + +| Method | Description | +| :-------- | :-------- | +| [install](#method.install) | Installs a package given by a name, an URL or a file path | +| [synchronize](#method.synchronize) | Synchronizes repository manifest with a repository | + + +## *install method* + +Installs a package given by a name, an URL or a file path. + +### Parameters + +| Name | Type | Description | +| :-------- | :-------- | :-------- | +| params | object | | +| params.package | string | A name, an URL or a file path of the package to install | +| params?.version | string | *(optional)* Version of the package to install | +| params?.architecture | string | *(optional)* Architecture of the package to install | + +### Result + +| Name | Type | Description | +| :-------- | :-------- | :-------- | +| result | null | Always null | + +### Errors + +| Code | Message | Description | +| :-------- | :-------- | :-------- | +| 12 | ```ERROR_INPROGRESS``` | Returned when the function is called while other installation/synchronization is already in progress. | + +### Example + +#### Request + +```json +{ + "jsonrpc": "2.0", + "id": 1234567890, + "method": "Packager.1.install", + "params": { + "package": "wpeframework-plugin-netflix", + "version": "1.0", + "architecture": "arm" + } +} +``` +#### Response + +```json +{ + "jsonrpc": "2.0", + "id": 1234567890, + "result": null +} +``` + +## *synchronize method* + +Synchronizes repository manifest with a repository. + +### Parameters + +This method takes no parameters. + +### Result + +| Name | Type | Description | +| :-------- | :-------- | :-------- | +| result | null | Always null | + +### Errors + +| Code | Message | Description | +| :-------- | :-------- | :-------- | +| 12 | ```ERROR_INPROGRESS``` | Returned when the function is called while other installation/synchronization is already in progress. | + +### Example + +#### Request + +```json +{ + "jsonrpc": "2.0", + "id": 1234567890, + "method": "Packager.1.synchronize" +} +``` +#### Response + +```json +{ + "jsonrpc": "2.0", + "id": 1234567890, + "result": null +} +``` diff --git a/Packager/opkg.conf.in b/Packager/opkg.conf.in new file mode 100644 index 0000000000..37b6281589 --- /dev/null +++ b/Packager/opkg.conf.in @@ -0,0 +1,6 @@ +src/gz snapshots http://yoctobuild.metrological.com/snapshots/vodafone +src/gz local file:///hdd/cache/packages +dest root / +dest ram /tmp +lists_dir ext /var/opkg-lists +option overlay_root /overlay From 5496405269251545a393a725813a5c493bc03145 Mon Sep 17 00:00:00 2001 From: mfiess200 Date: Wed, 24 Jun 2020 15:30:17 -0400 Subject: [PATCH 31/56] key handling, key metadata, inactivity, and animation updates --- RDKShell/CMakeLists.txt | 2 +- RDKShell/RDKShell.cpp | 219 ++++++++++++++++++++++++++++++++++++++-- RDKShell/RDKShell.h | 17 ++++ 3 files changed, 229 insertions(+), 9 deletions(-) diff --git a/RDKShell/CMakeLists.txt b/RDKShell/CMakeLists.txt index b6a3b6d9fd..a922266415 100755 --- a/RDKShell/CMakeLists.txt +++ b/RDKShell/CMakeLists.txt @@ -34,7 +34,7 @@ target_include_directories(${MODULE_NAME} PRIVATE ../helpers) set(RDKSHELL_INCLUDES $ENV{RDKSHELL_INCLUDES}) separate_arguments(RDKSHELL_INCLUDES) include_directories(BEFORE ${RDKSHELL_INCLUDES}) -target_link_libraries(${MODULE_NAME} PRIVATE ${NAMESPACE}Plugins::${NAMESPACE}Plugins -lrdkshell) +target_link_libraries(${MODULE_NAME} PRIVATE ${NAMESPACE}Plugins::${NAMESPACE}Plugins -lrdkshell rfcapi) install(TARGETS ${MODULE_NAME} DESTINATION lib/${STORAGE_DIRECTORY}/plugins) diff --git a/RDKShell/RDKShell.cpp b/RDKShell/RDKShell.cpp index 5648f5941a..b2401bd6e9 100755 --- a/RDKShell/RDKShell.cpp +++ b/RDKShell/RDKShell.cpp @@ -23,6 +23,7 @@ #include #include #include +#include "rfcapi.h" const short WPEFramework::Plugin::RDKShell::API_VERSION_NUMBER_MAJOR = 1; const short WPEFramework::Plugin::RDKShell::API_VERSION_NUMBER_MINOR = 0; @@ -37,6 +38,8 @@ const string WPEFramework::Plugin::RDKShell::RDKSHELL_METHOD_ADD_KEY_INTERCEPT = const string WPEFramework::Plugin::RDKShell::RDKSHELL_METHOD_REMOVE_KEY_INTERCEPT = "removeKeyIntercept"; const string WPEFramework::Plugin::RDKShell::RDKSHELL_METHOD_ADD_KEY_LISTENER = "addKeyListener"; const string WPEFramework::Plugin::RDKShell::RDKSHELL_METHOD_REMOVE_KEY_LISTENER = "removeKeyListener"; +const string WPEFramework::Plugin::RDKShell::RDKSHELL_METHOD_ADD_KEY_METADATA_LISTENER = "addKeyMetadataListener"; +const string WPEFramework::Plugin::RDKShell::RDKSHELL_METHOD_REMOVE_KEY_METADATA_LISTENER = "removeKeyMetadataListener"; const string WPEFramework::Plugin::RDKShell::RDKSHELL_METHOD_INJECT_KEY = "injectKey"; const string WPEFramework::Plugin::RDKShell::RDKSHELL_METHOD_GET_SCREEN_RESOLUTION = "getScreenResolution"; const string WPEFramework::Plugin::RDKShell::RDKSHELL_METHOD_SET_SCREEN_RESOLUTION = "setScreenResolution"; @@ -53,6 +56,11 @@ const string WPEFramework::Plugin::RDKShell::RDKSHELL_METHOD_GET_SCALE = "getSca const string WPEFramework::Plugin::RDKShell::RDKSHELL_METHOD_SET_SCALE = "setScale"; const string WPEFramework::Plugin::RDKShell::RDKSHELL_METHOD_REMOVE_ANIMATION = "removeAnimation"; const string WPEFramework::Plugin::RDKShell::RDKSHELL_METHOD_ADD_ANIMATION = "addAnimation"; +const string WPEFramework::Plugin::RDKShell::RDKSHELL_METHOD_ENABLE_INACTIVITY_REPORTING = "enableInactivityReporting"; +const string WPEFramework::Plugin::RDKShell::RDKSHELL_METHOD_SET_INACTIVITY_INTERVAL = "setInactivityInterval"; +const string WPEFramework::Plugin::RDKShell::RDKSHELL_METHOD_SCALE_TO_FIT = "scaleToFit"; + +const string WPEFramework::Plugin::RDKShell::RDKSHELL_EVENT_ON_USER_INACTIVITY = "onUserInactivity"; using namespace std; using namespace RdkShell; @@ -81,6 +89,16 @@ namespace WPEFramework { return flag; } + bool getRFCConfig(char* paramName, RFC_ParamData_t& paramOutput) + { + WDMP_STATUS wdmpStatus = getRFCParameter("RDKShell", paramName, ¶mOutput); + if (wdmpStatus == WDMP_SUCCESS || wdmpStatus == WDMP_ERR_DEFAULT_VALUE) + { + return true; + } + return false; + } + SERVICE_REGISTRATION(RDKShell, 1, 0); RDKShell* RDKShell::_instance = nullptr; @@ -157,6 +175,8 @@ namespace WPEFramework { registerMethod(RDKSHELL_METHOD_REMOVE_KEY_INTERCEPT, &RDKShell::removeKeyInterceptWrapper, this); registerMethod(RDKSHELL_METHOD_ADD_KEY_LISTENER, &RDKShell::addKeyListenersWrapper, this); registerMethod(RDKSHELL_METHOD_REMOVE_KEY_LISTENER, &RDKShell::removeKeyListenersWrapper, this); + registerMethod(RDKSHELL_METHOD_ADD_KEY_METADATA_LISTENER, &RDKShell::addKeyMetadataListenerWrapper, this); + registerMethod(RDKSHELL_METHOD_REMOVE_KEY_METADATA_LISTENER, &RDKShell::removeKeyMetadataListenerWrapper, this); registerMethod(RDKSHELL_METHOD_INJECT_KEY, &RDKShell::injectKeyWrapper, this); registerMethod(RDKSHELL_METHOD_GET_SCREEN_RESOLUTION, &RDKShell::getScreenResolutionWrapper, this); registerMethod(RDKSHELL_METHOD_SET_SCREEN_RESOLUTION, &RDKShell::setScreenResolutionWrapper, this); @@ -173,6 +193,9 @@ namespace WPEFramework { registerMethod(RDKSHELL_METHOD_SET_SCALE, &RDKShell::setScaleWrapper, this); registerMethod(RDKSHELL_METHOD_REMOVE_ANIMATION, &RDKShell::removeAnimationWrapper, this); registerMethod(RDKSHELL_METHOD_ADD_ANIMATION, &RDKShell::addAnimationWrapper, this); + registerMethod(RDKSHELL_METHOD_ENABLE_INACTIVITY_REPORTING, &RDKShell::enableInactivityReportingWrapper, this); + registerMethod(RDKSHELL_METHOD_SET_INACTIVITY_INTERVAL, &RDKShell::setInactivityIntervalWrapper, this); + registerMethod(RDKSHELL_METHOD_SCALE_TO_FIT, &RDKShell::scaleToFitWrapper, this); } RDKShell::~RDKShell() @@ -181,6 +204,7 @@ namespace WPEFramework { mClientsMonitor->Release(); RDKShell::_instance = nullptr; mRemoteShell = false; + CompositorController::setEventListener(nullptr); mEventListener = nullptr; } @@ -188,6 +212,19 @@ namespace WPEFramework { { LOGINFO(); + CompositorController::setEventListener(mEventListener); + RFC_ParamData_t param; + bool ret = getRFCConfig("Device.DeviceInfo.X_RDKCENTRAL-COM_RFC.Feature.Power.UserInactivityNotification.Enable", param); + if (true == ret && param.type == WDMP_BOOLEAN && (strncasecmp(param.value,"true",4) == 0)) + { + CompositorController::enableInactivityReporting(true); + ret = getRFCConfig("Device.DeviceInfo.X_RDKCENTRAL-COM_RFC.Feature.Power.UserInactivityNotification.TimeMinutes", param); + if (true == ret && param.type == WDMP_UINT) + { + CompositorController::setInactivityInterval(std::stod(param.value)); + } + } + shellThread = std::thread([]() { gRdkShellMutex.lock(); RdkShell::initialize(); @@ -200,7 +237,6 @@ namespace WPEFramework { RdkShell::update(); gRdkShellMutex.unlock(); double frameTime = (int)RdkShell::microseconds() - (int)startFrameTime; - int32_t sleepTimeInMs = gCurrentFramerate - frameTime; if (frameTime < maxSleepTime) { int sleepTime = (int)maxSleepTime-(int)frameTime; @@ -249,6 +285,17 @@ namespace WPEFramework { std::cout << "RDKShell onApplicationFirstFrame event received ..." << client << std::endl; } + void RDKShell::RdkShellListener::onUserInactive(const double minutes) + { + std::cout << "RDKShell onUserInactive event received ..." << minutes << std::endl; + JsonObject response; + response["method"] = RDKSHELL_EVENT_ON_USER_INACTIVITY; + JsonObject params; + params["minutes"] = std::to_string(minutes); + response["params"] = params; + mShell.notify(RDKSHELL_EVENT_ON_USER_INACTIVITY, response); + } + // Registered methods (wrappers) begin uint32_t RDKShell::moveToFrontWrapper(const JsonObject& parameters, JsonObject& response) { @@ -472,6 +519,54 @@ namespace WPEFramework { returnResponse(result); } + uint32_t RDKShell::addKeyMetadataListenerWrapper(const JsonObject& parameters, JsonObject& response) + { + LOGINFOMETHOD(); + bool result = true; + + if (!parameters.HasLabel("client")) + { + result = false; + response["message"] = "please specify client"; + } + + if (result) + { + const string client = parameters["client"].String(); + gRdkShellMutex.lock(); + result = CompositorController::addKeyMetadataListener(client); + gRdkShellMutex.unlock(); + if (false == result) { + response["message"] = "failed to add key metadata listeners"; + } + } + returnResponse(result); + } + + uint32_t RDKShell::removeKeyMetadataListenerWrapper(const JsonObject& parameters, JsonObject& response) + { + LOGINFOMETHOD(); + bool result = true; + + if (!parameters.HasLabel("client")) + { + result = false; + response["message"] = "please specify client"; + } + + if (result) + { + const string client = parameters["client"].String(); + gRdkShellMutex.lock(); + result = CompositorController::removeKeyMetadataListener(client); + gRdkShellMutex.unlock(); + if (false == result) { + response["message"] = "failed to remove key metadata listeners"; + } + } + returnResponse(result); + } + uint32_t RDKShell::injectKeyWrapper(const JsonObject& parameters, JsonObject& response) { LOGINFOMETHOD(); @@ -871,7 +966,6 @@ namespace WPEFramework { LOGINFOMETHOD(); bool result = true; - bool arraybased = false; if (parameters.HasLabel("animations")) { const JsonArray animations = parameters["animations"].Array(); @@ -883,15 +977,103 @@ namespace WPEFramework { returnResponse(result); } + uint32_t RDKShell::enableInactivityReportingWrapper(const JsonObject& parameters, JsonObject& response) + { + LOGINFOMETHOD(); + bool result = true; + if (!parameters.HasLabel("enable")) + { + result = false; + response["message"] = "please specify enable parameter"; + } + if (result) + { + const bool enable = parameters["enable"].Boolean(); + + result = enableInactivityReporting(enable); + + if (false == result) { + response["message"] = "failed to set inactivity notification"; + } + } + returnResponse(result); + } + + uint32_t RDKShell::setInactivityIntervalWrapper(const JsonObject& parameters, JsonObject& response) + { + LOGINFOMETHOD(); + bool result = true; + if (!parameters.HasLabel("interval")) + { + result = false; + response["message"] = "please specify interval parameter"; + } + if (result) + { + const string interval = parameters["interval"].String(); + + result = setInactivityInterval(interval); + // Just realized: we need one more string& param for the the error message in case setScreenResolution() fails internally + // Also, we might not need a "non-wrapper" method at all, nothing prevents us from implementing it right here + + if (false == result) { + response["message"] = "failed to set inactivity interval"; + } + } + returnResponse(result); + } + + uint32_t RDKShell::scaleToFitWrapper(const JsonObject& parameters, JsonObject& response) + { + LOGINFOMETHOD(); + bool result = true; + if (!parameters.HasLabel("client")) + { + result = false; + response["message"] = "please specify client"; + } + if (result) + { + const string client = parameters["client"].String(); + + unsigned int x = 0, y = 0; + unsigned int clientWidth = 0, clientHeight = 0; + gRdkShellMutex.lock(); + CompositorController::getBounds(client, x, y, clientWidth, clientHeight); + if (parameters.HasLabel("x")) + { + x = parameters["x"].Number(); + } + if (parameters.HasLabel("y")) + { + y = parameters["y"].Number(); + } + if (parameters.HasLabel("w")) + { + clientWidth = parameters["w"].Number(); + } + if (parameters.HasLabel("h")) + { + clientHeight = parameters["h"].Number(); + } + result = CompositorController::scaleToFit(client, x, y, clientWidth, clientHeight); + gRdkShellMutex.unlock(); + + if (!result) { + response["message"] = "failed to scale to fit"; + } + } + returnResponse(result); + } + // Registered methods begin // Events begin -// void RDKShell::onSomeEvent(JsonObject& url) -// { -// LOGINFO(); -// sendNotify(C_STR(RDKSHELL_EVENT_SOME_EVENT), url); -// } - // Events end + void RDKShell::notify(const std::string& event, const JsonObject& parameters) + { + sendNotify(event.c_str(), parameters); + } + // Events end // Internal methods begin bool RDKShell::moveToFront(const string& client) @@ -1235,6 +1417,11 @@ namespace WPEFramework { double scaleY = std::stod(animationInfo["sy"].String()); animationProperties["sy"] = scaleY; } + if (animationInfo.HasLabel("a")) + { + uint32_t opacity = animationInfo["a"].Number(); + animationProperties["a"] = opacity; + } if (animationInfo.HasLabel("tween")) { std::string tween = animationInfo["tween"].String(); @@ -1247,6 +1434,22 @@ namespace WPEFramework { return true; } + bool RDKShell::enableInactivityReporting(const bool enable) + { + gRdkShellMutex.lock(); + CompositorController::enableInactivityReporting(enable); + gRdkShellMutex.unlock(); + return true; + } + + bool RDKShell::setInactivityInterval(const string interval) + { + gRdkShellMutex.lock(); + CompositorController::setInactivityInterval(std::stod(interval)); + gRdkShellMutex.unlock(); + return true; + } + // Internal methods end } // namespace Plugin } // namespace WPEFramework diff --git a/RDKShell/RDKShell.h b/RDKShell/RDKShell.h index 0d6b0ff920..ff775ad1df 100755 --- a/RDKShell/RDKShell.h +++ b/RDKShell/RDKShell.h @@ -56,6 +56,8 @@ namespace WPEFramework { static const string RDKSHELL_METHOD_REMOVE_KEY_INTERCEPT; static const string RDKSHELL_METHOD_ADD_KEY_LISTENER; static const string RDKSHELL_METHOD_REMOVE_KEY_LISTENER; + static const string RDKSHELL_METHOD_ADD_KEY_METADATA_LISTENER; + static const string RDKSHELL_METHOD_REMOVE_KEY_METADATA_LISTENER; static const string RDKSHELL_METHOD_INJECT_KEY; static const string RDKSHELL_METHOD_INJECT_KEYS; static const string RDKSHELL_METHOD_GET_SCREEN_RESOLUTION; @@ -73,6 +75,12 @@ namespace WPEFramework { static const string RDKSHELL_METHOD_SET_SCALE; static const string RDKSHELL_METHOD_ADD_ANIMATION; static const string RDKSHELL_METHOD_REMOVE_ANIMATION; + static const string RDKSHELL_METHOD_ENABLE_INACTIVITY_REPORTING; + static const string RDKSHELL_METHOD_SET_INACTIVITY_INTERVAL; + static const string RDKSHELL_METHOD_SCALE_TO_FIT; + + // events + static const string RDKSHELL_EVENT_ON_USER_INACTIVITY; private/*registered methods (wrappers)*/: @@ -86,6 +94,8 @@ namespace WPEFramework { uint32_t removeKeyInterceptWrapper(const JsonObject& parameters, JsonObject& response); uint32_t addKeyListenersWrapper(const JsonObject& parameters, JsonObject& response); uint32_t removeKeyListenersWrapper(const JsonObject& parameters, JsonObject& response); + uint32_t addKeyMetadataListenerWrapper(const JsonObject& parameters, JsonObject& response); + uint32_t removeKeyMetadataListenerWrapper(const JsonObject& parameters, JsonObject& response); uint32_t injectKeyWrapper(const JsonObject& parameters, JsonObject& response); uint32_t injectKeysWrapper(const JsonObject& parameters, JsonObject& response); uint32_t getScreenResolutionWrapper(const JsonObject& parameters, JsonObject& response); @@ -103,6 +113,10 @@ namespace WPEFramework { uint32_t setScaleWrapper(const JsonObject& parameters, JsonObject& response); uint32_t addAnimationWrapper(const JsonObject& parameters, JsonObject& response); uint32_t removeAnimationWrapper(const JsonObject& parameters, JsonObject& response); + uint32_t enableInactivityReportingWrapper(const JsonObject& parameters, JsonObject& response); + uint32_t setInactivityIntervalWrapper(const JsonObject& parameters, JsonObject& response); + uint32_t scaleToFitWrapper(const JsonObject& parameters, JsonObject& response); + void notify(const std::string& event, const JsonObject& parameters); private/*internal methods*/: RDKShell(const RDKShell&) = delete; @@ -135,6 +149,8 @@ namespace WPEFramework { bool setScale(const string& client, const double scaleX, const double scaleY); bool removeAnimation(const string& client); bool addAnimationList(const JsonArray& animations); + bool enableInactivityReporting(const bool enable); + bool setInactivityInterval(const string interval); private/*classes */: @@ -156,6 +172,7 @@ namespace WPEFramework { virtual void onApplicationDisconnected(const std::string& client); virtual void onApplicationTerminated(const std::string& client); virtual void onApplicationFirstFrame(const std::string& client); + virtual void onUserInactive(const double minutes); private: RDKShell& mShell; From c34332e0f6c0010e0bad9bf19995b41f098667e2 Mon Sep 17 00:00:00 2001 From: jschmi201 Date: Thu, 25 Jun 2020 15:26:43 +0000 Subject: [PATCH 32/56] DELIA-43686 : Add quirk to CS Thunder Plugin to add Security type to Remote Data Reason for change: Security Type was missing Test Procedure: Verify Security Type is available through CS Thunder Plugin. Risks: None. Signed-off-by: jschmidt Date: Tue, 23 Jun 2020 03:34:44 +0000 Subject: [PATCH 33/56] RDK-27957: TextToSpeech plugin changes Signed-off-by: nrajan002c --- TextToSpeech/TextToSpeech.cpp | 839 ++++++++++++++++++ TextToSpeech/TextToSpeech.h | 33 +- TextToSpeech/TextToSpeech.json | 9 - TextToSpeech/impl/TTSManager.cpp | 6 +- TextToSpeech/impl/TTSSpeaker.cpp | 88 +- TextToSpeech/impl/TTSSpeaker.h | 4 + TextToSpeech/impl/TextToSpeechCommon.h | 9 +- TextToSpeech/test/CMakeLists.txt | 6 +- TextToSpeech/test/Module.h | 2 +- ...rviceAPITest.cpp => TTSThunderAPITest.cpp} | 151 ++-- 10 files changed, 1021 insertions(+), 126 deletions(-) create mode 100644 TextToSpeech/TextToSpeech.cpp delete mode 100644 TextToSpeech/TextToSpeech.json rename TextToSpeech/test/{TTSServiceAPITest.cpp => TTSThunderAPITest.cpp} (83%) diff --git a/TextToSpeech/TextToSpeech.cpp b/TextToSpeech/TextToSpeech.cpp new file mode 100644 index 0000000000..b08de293fc --- /dev/null +++ b/TextToSpeech/TextToSpeech.cpp @@ -0,0 +1,839 @@ +/** +* If not stated otherwise in this file or this component's LICENSE +* file the following copyright and licenses apply: +* +* Copyright 2019 RDK Management +* +* 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. +**/ + +/* + * If not stated otherwise in this file or this component's license file the + * following copyright and licenses apply: + * + * Copyright 2018 RDK Management + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file TextToSpeech.cpp + * @brief Thunder Plugin based Implementation for TTS service API's (RDK-RDK-25832). + */ + +/** + @mainpage Text To Speech (TTS) + + TextToSpeech TTS Service provides APIs for the arbitrators + * (ex: Receiver / Optimus App Manager) to reserve the TTS resource for a particular + * Application. Only when a TTS resource is reserved for an app, the service can be + * used by the apps. (i.e., if the app has a session, its session will + * be made "active". If the app does not have a session, whenever the + * session is created, it will be made active. + */ +#include "TextToSpeech.h" + +#define TTS_MAJOR_VERSION 1 +#define TTS_MINOR_VERSION 0 + +#define API_VERSION_NUMBER 1 + +namespace WPEFramework +{ + namespace Plugin + { + /* + *Register TextToSpeech module as wpeframework plugin + **/ + SERVICE_REGISTRATION(TextToSpeech, TTS_MAJOR_VERSION, TTS_MINOR_VERSION); + + /** + * @brief class varibales to handle TTS Engine communication. + */ + bool TextToSpeech::m_ttsEnabled= false; + uint32_t TextToSpeech::m_serviceObjCount = 0; + TTS::TTSManager* TextToSpeech::m_ttsManager = NULL; + TTSConnectionCallback* TextToSpeech::m_connectionCallback = NULL; + TTSSessionServiceCallback* TextToSpeech::m_sessionCallback = NULL; + TTS::ResourceAllocationPolicy TextToSpeech::m_policy = TTS::INVALID_POLICY; + TextToSpeech::TextToSpeech() + : AbstractPlugin() + , m_apiVersionNumber(API_VERSION_NUMBER) + { + LOGINFO(); + registerMethod("enableTTS", &TextToSpeech::enableTTS, this); + registerMethod("listVoices", &TextToSpeech::listVoices, this); + registerMethod("setTTSConfiguration", &TextToSpeech::setTTSConfiguration, this); + registerMethod("getTTSConfiguration", &TextToSpeech::getTTSConfiguration, this); + registerMethod("isTTSEnabled", &TextToSpeech::isTTSEnabled, this); + registerMethod("isSessionActiveForApp", &TextToSpeech::isSessionActiveForApp, this); + registerMethod("acquireResource", &TextToSpeech::acquireResource, this); + registerMethod("claimResource", &TextToSpeech::claimResource, this); + registerMethod("releaseResource", &TextToSpeech::releaseResource, this); + registerMethod("createSession", &TextToSpeech::createSession, this); + registerMethod("destroySession", &TextToSpeech::destroySession, this); + registerMethod("isActiveSession", &TextToSpeech::isActiveSession, this); + registerMethod("setPreemptiveSpeak", &TextToSpeech::setPreemptiveSpeak, this); + registerMethod("speak", &TextToSpeech::speak, this); + registerMethod("pause", &TextToSpeech::pause, this); + registerMethod("resume", &TextToSpeech::resume, this); + registerMethod("abort", &TextToSpeech::abort, this); + registerMethod("isSpeaking", &TextToSpeech::isSpeaking, this); + registerMethod("getSpeechState", &TextToSpeech::getSpeechState, this); + registerMethod("requestExtendedEvents", &TextToSpeech::requestExtendedEvents, this); + + if(!m_sessionCallback) + m_sessionCallback = new TTSSessionServiceCallback(this); + + if(!m_connectionCallback) + m_connectionCallback = new TTSConnectionCallback(this); + + if(!m_ttsManager) { + m_ttsManager = TTS::TTSManager::create(m_connectionCallback); + if(m_ttsManager) + { + m_policy = getResourceAllocationPolicy(); + LOGINFO("%d(%s) policy is enforced by TTSEngine", m_policy, policyStr(m_policy)); + if(m_connectionCallback) + m_connectionCallback->onTTSServerConnected(); + } + } + + m_serviceObjCount++; + } + + TextToSpeech::~TextToSpeech() + { + LOGINFO(); + m_serviceObjCount--; + if(m_serviceObjCount == 0) + { + if(m_ttsManager) + { + delete m_ttsManager; + m_ttsManager = NULL; + } + + if(m_connectionCallback) + { + delete m_connectionCallback; + m_connectionCallback = NULL; + } + + if(m_sessionCallback) + { + delete m_sessionCallback; + m_sessionCallback = NULL; + } + } + } + + void TextToSpeech::restoreTextToSpeech() + { + if(m_ttsEnabled && m_ttsManager) + { + m_ttsManager->enableTTS(m_ttsEnabled); + LOGWARN("Restored TTS connection and settings"); + } + } + + uint32_t TextToSpeech::enableTTS(const JsonObject& parameters, JsonObject& response) + { + bool success = false; + TTS::TTS_Error status = TTS::TTS_FAIL; + + CHECK_CONNECTION_RETURN_ON_FAIL(success); + + if (parameters.HasLabel("enableTTS")) + { + bool value = parameters["enableTTS"].Boolean(); + m_ttsEnabled = value; + status = m_ttsManager->enableTTS(m_ttsEnabled); + success = true; + } + logResponse(status, response); + returnResponse(success); + } + + uint32_t TextToSpeech::listVoices(const JsonObject& parameters, JsonObject& response) + { + bool success = false; + TTS::TTS_Error status = TTS::TTS_FAIL; + + CHECK_CONNECTION_RETURN_ON_FAIL(success); + + if(parameters.HasLabel("language")) + { + std::vector voice; + std::string lang = parameters["language"].String(); + status = m_ttsManager->listVoices(lang, voice); + if(status == TTS::TTS_OK) + { + setResponseArray(response, "voices", voice); + success = true; + } + } + + logResponse(status,response); + returnResponse(success); + } + + uint32_t TextToSpeech::setTTSConfiguration(const JsonObject& parameters, JsonObject& response) + { + bool success = false; + TTS::TTS_Error status = TTS::TTS_FAIL; + + CHECK_CONNECTION_RETURN_ON_FAIL(success); + + + if (parameters.HasLabel("ttsEndPoint")) + { + m_config.ttsEndPoint = parameters["ttsEndPoint"].String(); + } + if (parameters.HasLabel("ttsEndPointSecured")) + { + m_config.ttsEndPointSecured = parameters["ttsEndPointSecured"].String(); + } + if (parameters.HasLabel("language")) + { + m_config.language = parameters["language"].String(); + } + if (parameters.HasLabel("voice")) + { + m_config.voice = parameters["voice"].String(); + } + if (parameters.HasLabel("volume")) + { + m_config.volume = stod(parameters["volume"].String()); + } + if (parameters.HasLabel("rate")) + { + int rate=0; + getNumberParameter("rate", rate); + m_config.rate = static_cast(rate); + } + + status = m_ttsManager->setConfiguration(m_config); + if(status == TTS::TTS_OK) + success = true; + logResponse(status, response); + returnResponse(success); + } + + uint32_t TextToSpeech::getTTSConfiguration(const JsonObject& parameters, JsonObject& response) + { + bool success = false; + TTS::TTS_Error status = TTS::TTS_FAIL; + + CHECK_CONNECTION_RETURN_ON_FAIL(success); + + TTS::Configuration ttsConfig; + status = m_ttsManager->getConfiguration(ttsConfig); + if(status == TTS::TTS_OK) + { + std::string volume; + response["ttsEndPoint"] = ttsConfig.ttsEndPoint; + response["ttsEndPointSecured"] = ttsConfig.ttsEndPointSecured; + response["language"] = ttsConfig.language; + response["voice"] = ttsConfig.voice; + response["rate"] = ttsConfig.rate; + volume = std::to_string(ttsConfig.volume); + response["volume"] = volume; + success = true; + } + logResponse(status, response); + returnResponse(success); + } + + bool TextToSpeech::isTTSEnabled(const JsonObject& parameters ,JsonObject& response) + { + bool success = false; + bool force = false; + TTS::TTS_Error status = TTS::TTS_FAIL; + + CHECK_CONNECTION_RETURN_ON_FAIL(success); + + if (parameters.HasLabel("force")) + { + force = parameters["force"].Boolean(); + } + + if(!force){ + response["isEnabled"] = JsonValue((bool)m_ttsEnabled); + } else { + m_ttsEnabled = m_ttsManager->isTTSEnabled(); + response["isEnabled"] = JsonValue((bool)m_ttsEnabled); + } + + status = TTS::TTS_OK; + success = true; + logResponse(status,response); + + returnResponse(success); + } + + uint32_t TextToSpeech::isSessionActiveForApp(const JsonObject& parameters, JsonObject& response) + { + bool success = false; + TTS::TTS_Error status = TTS::TTS_FAIL; + + CHECK_CONNECTION_RETURN_ON_FAIL(success); + + if (parameters.HasLabel("appId")) + { + int app_id = 0; + getNumberParameter("appId", app_id); + response["isActive"] = m_ttsManager->isSessionActiveForApp(static_cast(app_id)); + status = TTS::TTS_OK; + success = true; + } + logResponse(status, response); + returnResponse(success); + } + + uint32_t TextToSpeech::acquireResource(const JsonObject& parameters, JsonObject& response) + { + bool success = false; + TTS::TTS_Error status = TTS::TTS_FAIL; + + CHECK_CONNECTION_RETURN_ON_FAIL(success); + + if(m_policy != TTS::RESERVATION) { + LOGERR("Non-Reservation policy (i.e %s) is in effect, declining request", policyStr(m_policy)); + status = TTS::TTS_POLICY_VIOLATION; + logResponse(status, response); + returnResponse(success); + } + + if(parameters.HasLabel("appId")) + { + uint32_t app_id = parameters["appId"].Number(); + status = m_ttsManager->reservePlayerResource(app_id); + if(status != TTS::TTS_OK) + { + LOGERR("Couldn't request reservation of resource, TTS Code = %d", status); + logResponse(status, response); + returnResponse(success); + } + success = true; + } + logResponse(status, response); + returnResponse(success); + } + + uint32_t TextToSpeech::claimResource(const JsonObject& parameters, JsonObject& response) + { + bool success = false; + TTS::TTS_Error status = TTS::TTS_FAIL; + + CHECK_CONNECTION_RETURN_ON_FAIL(success); + + if(m_policy != TTS::RESERVATION) { + LOGERR("Non-Reservation policy (i.e %s) is in effect, declining request", policyStr(m_policy)); + status = TTS::TTS_POLICY_VIOLATION; + logResponse(status, response); + returnResponse(success); + } + + if(parameters.HasLabel("appId")) + { + uint32_t app_id = parameters["appId"].Number(); + status = m_ttsManager->claimPlayerResource(app_id); + if(status != TTS::TTS_OK) + { + LOGERR("Couldn't claim reservation of resource, TTS Code = %d", status); + logResponse(status, response); + returnResponse(success); + } + success = true; + } + logResponse(status, response); + returnResponse(success); + } + + uint32_t TextToSpeech::releaseResource(const JsonObject& parameters, JsonObject& response) + { + bool success = false; + TTS::TTS_Error status = TTS::TTS_FAIL; + + CHECK_CONNECTION_RETURN_ON_FAIL(success); + + if(m_policy != TTS::RESERVATION) { + LOGERR("Non-Reservation policy (i.e %s) is in effect, declining request", policyStr(m_policy)); + status = TTS::TTS_POLICY_VIOLATION; + logResponse(status, response); + returnResponse(success); + } + + if(parameters.HasLabel("appId")) + { + uint32_t app_id = parameters["appId"].Number(); + status = m_ttsManager->releasePlayerResource(app_id); + if(status != TTS::TTS_OK) + { + LOGERR("Resource release didn't succeed, TTS Code = %d", status); + logResponse(status, response); + returnResponse(success); + } + success = true; + } + logResponse(status, response); + returnResponse(success); + } + + uint32_t TextToSpeech::createSession(const JsonObject& parameters, JsonObject& response) + { + bool success = false; + TTS::TTS_Error status = TTS::TTS_FAIL; + + CHECK_CONNECTION_RETURN_ON_FAIL(success); + + TTS::TTSSession *sessionObject=NULL; + uint32_t sessionId; + bool ttsEnabled = false; + string appName = parameters["appName"].String(); + int appId = 0; + uint32_t app_id = 0; + getNumberParameter("appId", appId); + app_id = static_cast(appId); + + if(app_id && m_sessionCallback) + { + SessionInfo *sessionInfo = new SessionInfo(); + sessionInfo->m_appId = app_id; + sessionInfo->m_appName = appName; + if(m_policy != TTS::RESERVATION) { + sessionInfo->m_gotResource = true; + } + + sessionObject = m_ttsManager->createSession(app_id, appName, sessionId, ttsEnabled, status, m_sessionCallback); + if(!sessionObject){ + LOGWARN("Session couldn't be created for App (\"%u\", \"%s\")", app_id, appName.c_str()); + delete sessionInfo; + } else { + LOGINFO("Created TTS SessionId = %d, for appId = %d, appname = %s, ttsEnabled = %d", sessionId, app_id, appName.c_str(), ttsEnabled); + sessionInfo->m_sessionId = sessionId; + sessionInfo->m_session = sessionObject; + m_sessionMap[sessionInfo->m_sessionId] = sessionInfo; + response["TTSSessionId"] = JsonValue(sessionId); + if(m_connectionCallback) { + m_ttsEnabled = sessionId; + m_connectionCallback->onTTSStateChanged(m_ttsEnabled); + } + m_sessionCallback->onTTSSessionCreated(sessionInfo->m_appId, sessionInfo->m_sessionId); + success = true; + } + } + + logResponse(status, response); + returnResponse(success); + } + + uint32_t TextToSpeech::destroySession(const JsonObject& parameters, JsonObject& response) + { + LOGINFO(); + + bool success = false; + TTS::TTS_Error status = TTS::TTS_FAIL; + + CHECK_CONNECTION_RETURN_ON_FAIL(success); + + SessionInfo *sessionInfo; + std::map::iterator sessionItr; + + uint32_t sessionId = parameters["sessionId"].Number(); + + CHECK_SESSION_RETURN_ON_FAIL(sessionId, sessionItr, sessionInfo, success); + + status = m_ttsManager->destroySession(sessionId); + + if(status != TTS::TTS_OK) { + LOGWARN("Failed to destroy TTS SessionId = %d", sessionId); + logResponse(TTS::TTS_FAIL,response); + returnResponse(success); + } + + LOGINFO("destroy TTS SessionId = %d", sessionId); + delete sessionInfo; + m_sessionMap.erase(sessionItr); + success = true; + + logResponse(status,response); + returnResponse(success); + } + + uint32_t TextToSpeech::isActiveSession(const JsonObject& parameters, JsonObject& response) + { + LOGINFO(); + + bool success = false; + TTS::TTS_Error status = TTS::TTS_FAIL; + + CHECK_CONNECTION_RETURN_ON_FAIL(success); + + SessionInfo *sessionInfo; + std::map::iterator sessionItr; + bool forcefetch = false; + + if (parameters.HasLabel("forcefetch")) + forcefetch = parameters["forcefetch"].Boolean(); + + uint32_t sessionId = parameters["sessionId"].Number(); + + CHECK_SESSION_RETURN_ON_FAIL(sessionId, sessionItr, sessionInfo, success); + + if (forcefetch) + { + bool active = false; + sessionInfo->m_session->isActive(active); + sessionInfo->m_gotResource = active; + } + + response["isActive"] = sessionInfo->m_gotResource; + + status = TTS::TTS_OK; + success = true; + logResponse(status, response); + returnResponse(success); + } + + + uint32_t TextToSpeech::setPreemptiveSpeak(const JsonObject& parameters, JsonObject& response) + { + LOGINFO(); + + bool success = false; + TTS::TTS_Error status = TTS::TTS_FAIL; + + CHECK_CONNECTION_RETURN_ON_FAIL(success); + + SessionInfo *sessionInfo; + std::map::iterator sessionItr; + bool preemptive=true; + + uint32_t sessionId = parameters["sessionId"].Number(); + CHECK_SESSION_RETURN_ON_FAIL(sessionId, sessionItr, sessionInfo, success); + if (parameters.HasLabel("preemptive")) + preemptive = parameters["preemptive"].Boolean(); + + status = sessionInfo->m_session->setPreemptiveSpeak(preemptive); + success = true; + logResponse(status, response); + returnResponse(success); + } + + uint32_t TextToSpeech::requestExtendedEvents(const JsonObject& parameters, JsonObject& response) + { + LOGINFO(); + + bool success = false; + TTS::TTS_Error status = TTS::TTS_FAIL; + + CHECK_CONNECTION_RETURN_ON_FAIL(success); + + SessionInfo *sessionInfo; + std::map::iterator sessionItr; + + uint32_t sessionId = parameters["sessionId"].Number(); + CHECK_SESSION_RETURN_ON_FAIL(sessionId, sessionItr, sessionInfo, success); + + uint32_t extendedEvents = parameters["extendedEvents"].Number(); + + SET_UNSET_EXTENDED_EVENT(sessionInfo, extendedEvents, TTS::EXT_EVENT_WILL_SPEAK, "willSpeak"); + SET_UNSET_EXTENDED_EVENT(sessionInfo, extendedEvents, TTS::EXT_EVENT_PAUSED, "paused"); + SET_UNSET_EXTENDED_EVENT(sessionInfo, extendedEvents, TTS::EXT_EVENT_RESUMED, "resumed"); + SET_UNSET_EXTENDED_EVENT(sessionInfo, extendedEvents, TTS::EXT_EVENT_CANCELLED, "cancelled"); + SET_UNSET_EXTENDED_EVENT(sessionInfo, extendedEvents, TTS::EXT_EVENT_INTERRUPTED, "interrupted"); + SET_UNSET_EXTENDED_EVENT(sessionInfo, extendedEvents, TTS::EXT_EVENT_NETWORK_ERROR, "networkerror"); + SET_UNSET_EXTENDED_EVENT(sessionInfo, extendedEvents, TTS::EXT_EVENT_PLAYBACK_ERROR, "playbackerror"); + + status = sessionInfo->m_session->requestExtendedEvents(sessionInfo->m_extendedEvents); + success = true; + logResponse(status, response); + + returnResponse(success); + } + + uint32_t TextToSpeech::speak(const JsonObject& parameters, JsonObject& response) + { + LOGINFO(); + + bool success = false; + TTS::TTS_Error status = TTS::TTS_FAIL; + + CHECK_CONNECTION_RETURN_ON_FAIL(success); + + SessionInfo *sessionInfo; + std::map::iterator sessionItr; + + uint32_t sessionId = parameters["sessionId"].Number(); + uint32_t id = parameters["id"].Number(); + std::string text = parameters["text"].String(); + bool secure = parameters["secure"].Boolean(); + + CHECK_SESSION_RETURN_ON_FAIL(sessionId, sessionItr, sessionInfo, success); + + if(!m_ttsEnabled) { + LOGERR("TTS is disabled, can't speak"); + status = TTS::TTS_NOT_ENABLED; + logResponse(status, response); + returnResponse(success); + } + + if(!sessionInfo->m_gotResource) { + LOGERR("Session is not active, can't speak"); + status = TTS::TTS_SESSION_NOT_ACTIVE; + logResponse(status, response); + returnResponse(success); + } + + status = sessionInfo->m_session->speak(id, text, secure); + + if(status == TTS::TTS_OK) + success = true; + + logResponse(status, response); + returnResponse(success); + } + + uint32_t TextToSpeech::abort(const JsonObject& parameters, JsonObject& response) + { + LOGINFO(); + + bool success = false; + bool clearPending = false; + TTS::TTS_Error status = TTS::TTS_FAIL; + + CHECK_CONNECTION_RETURN_ON_FAIL(success); + + SessionInfo *sessionInfo; + std::map::iterator sessionItr; + + uint32_t sessionId = parameters["sessionId"].Number(); + clearPending = parameters["clearPending"].Boolean(); + + CHECK_SESSION_RETURN_ON_FAIL(sessionId, sessionItr, sessionInfo, success); + + if(!m_ttsEnabled) { + LOGERR("TTS is disabled, nothing to pause"); + status = TTS::TTS_NOT_ENABLED; + logResponse(status, response); + returnResponse(success); + } + + if(clearPending) + status = sessionInfo->m_session->abortAndClearPending(); + else + status = sessionInfo->m_session->shut(); + + success = true; + logResponse(status, response); + + returnResponse(success); + } + + uint32_t TextToSpeech::pause(const JsonObject& parameters, JsonObject& response) + { + LOGINFO(); + + bool success = false; + TTS::TTS_Error status = TTS::TTS_FAIL; + + CHECK_CONNECTION_RETURN_ON_FAIL(success); + + SessionInfo *sessionInfo; + std::map::iterator sessionItr; + + uint32_t sessionId = parameters["sessionId"].Number(); + CHECK_SESSION_RETURN_ON_FAIL(sessionId, sessionItr, sessionInfo, success); + + uint32_t speechId = parameters["speechId"].Number(); + + if(!m_ttsEnabled) { + LOGERR("TTS is disabled, nothing to pause"); + status = TTS::TTS_NOT_ENABLED; + logResponse(status, response); + returnResponse(success); + } + + status = sessionInfo->m_session->pause(speechId); + success = true; + logResponse(status, response); + + returnResponse(success); + } + + uint32_t TextToSpeech::resume(const JsonObject& parameters, JsonObject& response) + { + LOGINFO(); + + bool success = false; + TTS::TTS_Error status = TTS::TTS_FAIL; + + CHECK_CONNECTION_RETURN_ON_FAIL(success); + + SessionInfo *sessionInfo; + std::map::iterator sessionItr; + + uint32_t sessionId = parameters["sessionId"].Number(); + CHECK_SESSION_RETURN_ON_FAIL(sessionId, sessionItr, sessionInfo, success); + + uint32_t speechId = parameters["speechId"].Number(); + + if(!m_ttsEnabled) { + LOGERR("TTS is disabled, nothing to resume"); + status = TTS::TTS_NOT_ENABLED; + logResponse(status, response); + returnResponse(success); + } + + status = sessionInfo->m_session->resume(speechId); + success = true; + logResponse(status, response); + + returnResponse(success); + } + + uint32_t TextToSpeech::isSpeaking(const JsonObject& parameters, JsonObject& response) + { + LOGINFO(); + + bool success = false; + TTS::TTS_Error status = TTS::TTS_FAIL; + + CHECK_CONNECTION_RETURN_ON_FAIL(success); + + SessionInfo *sessionInfo; + std::map::iterator sessionItr; + bool speaking = false; + + uint32_t sessionId = parameters["sessionId"].Number(); + + CHECK_SESSION_RETURN_ON_FAIL(sessionId, sessionItr, sessionInfo, success); + + if(sessionInfo->m_gotResource) { + sessionInfo->m_session->isSpeaking(speaking); + response["speaking"] = speaking; + status = TTS::TTS_OK; + success = true; + } + + logResponse(status, response); + returnResponse(true); + } + + uint32_t TextToSpeech::getSpeechState(const JsonObject& parameters, JsonObject& response) + { + LOGINFO(); + + bool success = false; + TTS::TTS_Error status = TTS::TTS_FAIL; + + CHECK_CONNECTION_RETURN_ON_FAIL(success); + + SessionInfo *sessionInfo; + std::map::iterator sessionItr; + TTS::SpeechState state; + + uint32_t sessionId = parameters["sessionId"].Number(); + CHECK_SESSION_RETURN_ON_FAIL(sessionId, sessionItr, sessionInfo, success); + + uint32_t speechId = parameters["speechId"].Number(); + + + status = sessionInfo->m_session->getSpeechState(speechId, state); + if(status == TTS::TTS_OK) + { + response["speechState"] = (int) state; + success = true; + } + + logResponse(status, response); + returnResponse(success); + } + + /* @brief : To get API version Number. + * @param[in] : void + * @param[out] : m_apiVersionNumber + */ + uint32_t TextToSpeech::getApiVersionNumber() + { + return m_apiVersionNumber; + } + + /* @brief : To set API version Number. + * @param[in] : apiVersionNumber + * @param[out] : void + */ + void TextToSpeech::setApiVersionNumber(unsigned int apiVersionNumber) + { + LOGINFO("Set API Version Number = %d\n", apiVersionNumber); + m_apiVersionNumber = apiVersionNumber; + } + + TTS::ResourceAllocationPolicy TextToSpeech::getResourceAllocationPolicy() + { + TTS::ResourceAllocationPolicy policy = TTS::INVALID_POLICY; + + if (m_ttsManager) + { + m_ttsManager->getResourceAllocationPolicy(policy); + } + return policy; + } + void TextToSpeech::setResponseArray(JsonObject& response, const char* key, const std::vector& items) + { + JsonArray arr; + for(auto& i : items) arr.Add(JsonValue(i)); + + response[key] = arr; + + string json; + response.ToString(json); + } + + void TextToSpeech::notifyClient(string eventname, JsonObject& params) + { + //property added in registeredProperty list hence call sendNotify + sendNotify(eventname.c_str(), params); + } + + void TextToSpeech::ResourceAcquired(uint32_t sessionId) + { + SessionInfo *sessionInfo; + std::map::iterator sessionItr; + + do { + sessionItr = m_sessionMap.find(sessionId); + if(sessionItr == m_sessionMap.end()) { + LOGERR("TTS Session is not created"); + return; + } + sessionInfo = sessionItr->second; + } while(0); + + sessionInfo->m_gotResource = true; + } + + } // namespace Plugin +} // namespace WPEFramework diff --git a/TextToSpeech/TextToSpeech.h b/TextToSpeech/TextToSpeech.h index d1737d5f9b..3809ebd508 100644 --- a/TextToSpeech/TextToSpeech.h +++ b/TextToSpeech/TextToSpeech.h @@ -131,7 +131,6 @@ namespace WPEFramework { TTS::ResourceAllocationPolicy getResourceAllocationPolicy(); void setResponseArray(JsonObject& response, const char* key, const std::vector& items); - void notify(std::string eventname, JsonObject& param); //End methods private: static uint32_t m_serviceObjCount; @@ -148,6 +147,8 @@ namespace WPEFramework { TextToSpeech(); virtual ~TextToSpeech(); void restoreTextToSpeech(); + void notifyClient(std::string eventname, JsonObject& param); + void ResourceAcquired(uint32_t sessionId); }; /** @@ -177,12 +178,15 @@ namespace WPEFramework { JsonObject params; params["state"] = JsonValue((bool)state); LOGINFO("TTS state changed to '%d'\n", state); - m_eventHandler->Notify("onTTSStateChanged", params); + m_eventHandler->notifyClient("onTTSStateChanged", params); } void onVoiceChanged(std::string voice) { + JsonObject params; LOGINFO("TTS voice changed (%s)", voice.c_str()); + params["voice"] = voice; + m_eventHandler->notifyClient("onVoiceChanged", params); } private: TextToSpeech *m_eventHandler; @@ -201,10 +205,11 @@ namespace WPEFramework { void onResourceAcquired(uint32_t appId, uint32_t sessionId) { LOGINFO("onResourceAcquired appId(%d) sessionId(%d)", appId, sessionId); + m_eventHandler->ResourceAcquired(sessionId); JsonObject params; params["appId"] = JsonValue((int)appId); params["sessionId"] = JsonValue((int)sessionId); - m_eventHandler->Notify("onResourceAcquired", params); + m_eventHandler->notifyClient("onResourceAcquired", params); } void onResourceReleased(uint32_t appId, uint32_t sessionId) @@ -213,7 +218,7 @@ namespace WPEFramework { JsonObject params; params["appId"] = JsonValue((int)appId); params["sessionId"] = JsonValue((int)sessionId); - m_eventHandler->Notify("onResourceReleased", params); + m_eventHandler->notifyClient("onResourceReleased", params); } @@ -223,7 +228,7 @@ namespace WPEFramework { JsonObject params; params["appId"] = JsonValue((int)appId); params["sessionId"] = JsonValue((int)sessionId); - m_eventHandler->Notify("onTTSSessionCreated", params); + m_eventHandler->notifyClient("onTTSSessionCreated", params); } @@ -235,7 +240,7 @@ namespace WPEFramework { params["sessionId"] = JsonValue((int)sessionId); params["speechId"] = JsonValue((int)data.id); params["text"] = data.text; - m_eventHandler->Notify("onWillSpeak", params); + m_eventHandler->notifyClient("onWillSpeak", params); } void onSpeechStart(uint32_t appId, uint32_t sessionId, TTS::SpeechData &data) @@ -246,7 +251,7 @@ namespace WPEFramework { params["sessionId"] = JsonValue((int)sessionId); params["speechId"] = JsonValue((int)data.id); params["text"] = data.text; - m_eventHandler->Notify("onSpeechStart", params); + m_eventHandler->notifyClient("onSpeechStart", params); } void onSpeechPause(uint32_t appId, uint32_t sessionId, uint32_t speechId) @@ -256,7 +261,7 @@ namespace WPEFramework { params["appId"] = JsonValue((int)appId); params["sessionId"] = JsonValue((int)sessionId); params["speechId"] = JsonValue((int)speechId); - m_eventHandler->Notify("onSpeechPause", params); + m_eventHandler->notifyClient("onSpeechPause", params); } void onSpeechResume(uint32_t appId, uint32_t sessionId, uint32_t speechId) @@ -266,7 +271,7 @@ namespace WPEFramework { params["appId"] = JsonValue((int)appId); params["sessionId"] = JsonValue((int)sessionId); params["speechId"] = JsonValue((int)speechId); - m_eventHandler->Notify("onSpeechResume", params); } + m_eventHandler->notifyClient("onSpeechResume", params); } void onSpeechCancelled(uint32_t appId, uint32_t sessionId, std::string id) { @@ -281,7 +286,7 @@ namespace WPEFramework { params["appId"] = JsonValue((int)appId); params["sessionId"] = JsonValue((int)sessionId); params["speechId"] = JsonValue((int)speechId); - m_eventHandler->Notify("onSpeechCancelled", params); + m_eventHandler->notifyClient("onSpeechCancelled", params); } void onSpeechInterrupted(uint32_t appId, uint32_t sessionId, uint32_t speechId) @@ -291,7 +296,7 @@ namespace WPEFramework { params["appId"] = JsonValue((int)appId); params["sessionId"] = JsonValue((int)sessionId); params["speechId"] = JsonValue((int)speechId); - m_eventHandler->Notify("onSpeechInterrupted", params); + m_eventHandler->notifyClient("onSpeechInterrupted", params); } void onNetworkError(uint32_t appId, uint32_t sessionId, uint32_t speechId) @@ -301,7 +306,7 @@ namespace WPEFramework { params["appId"] = JsonValue((int)appId); params["sessionId"] = JsonValue((int)sessionId); params["speechId"] = JsonValue((int)speechId); - m_eventHandler->Notify("onNetworkError", params); + m_eventHandler->notifyClient("onNetworkError", params); } void onPlaybackError(uint32_t appId, uint32_t sessionId, uint32_t speechId) @@ -311,7 +316,7 @@ namespace WPEFramework { params["appId"] = JsonValue((int)appId); params["sessionId"] = JsonValue((int)sessionId); params["speechId"] = JsonValue((int)speechId); - m_eventHandler->Notify("onPlaybackError", params); + m_eventHandler->notifyClient("onPlaybackError", params); } void onSpeechComplete(uint32_t appId, uint32_t sessionId, TTS::SpeechData &data) @@ -322,7 +327,7 @@ namespace WPEFramework { params["sessionId"] = JsonValue((int)sessionId); params["speechId"] = JsonValue((int)data.id); params["text"] = data.text; - m_eventHandler->Notify("onSpeechComplete", params); + m_eventHandler->notifyClient("onSpeechComplete", params); } private: diff --git a/TextToSpeech/TextToSpeech.json b/TextToSpeech/TextToSpeech.json deleted file mode 100644 index 9107ca2a24..0000000000 --- a/TextToSpeech/TextToSpeech.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "locator":"libWPEFrameworkTextToSpeech.so", - "classname":"TextToSpeech", - "precondition":[ - "Platform" - ], - "callsign":"org.rdk.TextToSpeech", - "autostart":true -} \ No newline at end of file diff --git a/TextToSpeech/impl/TTSManager.cpp b/TextToSpeech/impl/TTSManager.cpp index 9bfd906fc2..fb3f023c74 100644 --- a/TextToSpeech/impl/TTSManager.cpp +++ b/TextToSpeech/impl/TTSManager.cpp @@ -58,8 +58,9 @@ std::string TTS_CONFIGURATION_FILE = "/opt/tts/tts.ini"; // ------------------------- +static uint32_t counter = 0; + uint32_t nextSessionId() { - static uint32_t counter = 0; return ++counter; } @@ -478,6 +479,8 @@ TTSSession* TTSManager::createSession(uint32_t appId, std::string appName, uint3 if(it != m_sessionMap.end() && session) { TTSLOG_ERROR("Application \"%s\" already has a session \"%u\"", appName.c_str(), session->sessionId()); status = TTS_CREATE_SESSION_DUPLICATE; + sessionID = session->sessionId(); + ttsEnabled = m_ttsEnabled; return session; } @@ -552,6 +555,7 @@ TTS_Error TTSManager::destroySession(uint32_t sessionId) { if(m_sessionMap.size() == 0) { TTSLOG_WARNING("All sessions were destroyed, destroy pipeline"); m_speaker->ensurePipeline(false); + counter = 0; } return TTS_OK; diff --git a/TextToSpeech/impl/TTSSpeaker.cpp b/TextToSpeech/impl/TTSSpeaker.cpp index 834b9ef0a5..659388e78e 100644 --- a/TextToSpeech/impl/TTSSpeaker.cpp +++ b/TextToSpeech/impl/TTSSpeaker.cpp @@ -133,11 +133,14 @@ TTSSpeaker::TTSSpeaker(TTSConfiguration &config) : m_pipelineError(false), m_networkError(false), m_runThread(true), + m_busThread(true), m_flushed(false), m_isEOS(false), m_ensurePipeline(false), m_gstThread(new std::thread(GStreamerThreadFunc, this)), + m_gstbusThread(new std::thread(GStreamerBusWatchThreadFunc, this)), m_busWatch(0), + m_duration(0), m_pipelineConstructionFailures(0), m_maxPipelineConstructionFailures(INT_FROM_ENV("MAX_PIPELINE_FAILURE_THRESHOLD", 1)) { setenv("GST_DEBUG", "2", 0); @@ -147,12 +150,17 @@ TTSSpeaker::~TTSSpeaker() { if(m_isSpeaking) m_flushed = true; m_runThread = false; + m_busThread = false; m_condition.notify_one(); if(m_gstThread) { m_gstThread->join(); m_gstThread = NULL; } + if(m_gstbusThread) { + m_gstbusThread->join(); + m_gstbusThread = NULL; + } } void TTSSpeaker::ensurePipeline(bool flag) { @@ -510,9 +518,12 @@ void TTSSpeaker::createPipeline() { return; } + TTSLOG_WARNING ("gst_element_get_bus\n"); +#if 0 GstBus *bus = gst_element_get_bus(m_pipeline); m_busWatch = gst_bus_add_watch(bus, GstBusCallback, (gpointer)(this)); gst_object_unref(bus); +#endif m_pipelineConstructionFailures = 0; // wait until pipeline is set to NULL state @@ -564,30 +575,44 @@ void TTSSpeaker::waitForAudioToFinishTimeout(float timeout_s) { TTSLOG_TRACE("timeout_s=%f", timeout_s); auto timeout = std::chrono::system_clock::now() + std::chrono::seconds((unsigned long)timeout_s); + auto startTime = std::chrono::system_clock::now(); + gint64 lastPosition = 0; + + auto playbackInterrupted = [this] () -> bool { return !m_pipeline || m_pipelineError || m_flushed; }; + auto playbackCompleted = [this] () -> bool { return m_isEOS; }; - while(m_pipeline && !m_pipelineError && !m_isEOS && !m_flushed && timeout > std::chrono::system_clock::now()) { + while(timeout > std::chrono::system_clock::now()) { std::unique_lock mlock(m_queueMutex); - m_condition.wait_until(mlock, timeout, [this, &timeout, timeout_s] () { - if(!m_pipeline || m_pipelineError) - return true; - - // EOS enquiry - if(m_isEOS) - return true; - - // Speaker has flushed the data, no need wait for the completion - // must break and reset the pipeline - if(m_flushed) { - TTSLOG_VERBOSE("Bailing out because of forced text queue (m_flushed=true)"); - return true; - } + m_condition.wait_until(mlock, timeout, [this, playbackInterrupted, playbackCompleted] () { + return playbackInterrupted() || playbackCompleted(); + }); - if(m_isPaused) { + if(playbackInterrupted() || playbackCompleted()) { + if(m_flushed) + TTSLOG_VERBOSE("Bailing out because of forced text queue (m_flushed=true)"); + break; + } else { + if(m_isPaused) { + timeout = std::chrono::system_clock::now() + std::chrono::seconds((unsigned long)timeout_s); + } else { + if(m_duration > 0 && m_duration != (gint64)GST_CLOCK_TIME_NONE && + std::chrono::system_clock::now() < startTime + std::chrono::nanoseconds(m_duration)) { timeout = std::chrono::system_clock::now() + std::chrono::seconds((unsigned long)timeout_s); + TTSLOG_VERBOSE("Not reached duration"); + } else { + // This is a workaround for broken BRCM PCM Sink duration query - To be deleted once that is fixed + m_duration = 0; + gint64 position = 0; + gst_element_query_position(m_pipeline, GST_FORMAT_TIME, &position); + if(position > 0 && position != (gint64)GST_CLOCK_TIME_NONE && position > lastPosition) { + TTSLOG_VERBOSE("Reached/Invalid duration, last position=%" GST_TIME_FORMAT ", current position=%" GST_TIME_FORMAT, + GST_TIME_ARGS(lastPosition), GST_TIME_ARGS(position)); + timeout = std::chrono::system_clock::now() + std::chrono::seconds((unsigned long)timeout_s); + lastPosition = position; + } } - - return false; - }); + } + } } TTSLOG_INFO("m_isEOS=%d, m_pipeline=%p, m_pipelineError=%d, m_flushed=%d", m_isEOS, m_pipeline, m_pipelineError, m_flushed); @@ -721,6 +746,7 @@ std::string TTSSpeaker::constructURL(TTSConfiguration &config, SpeechData &d) { void TTSSpeaker::speakText(TTSConfiguration config, SpeechData &data) { m_isEOS = false; + m_duration = 0; if(m_pipeline && !m_pipelineError && !m_flushed) { m_currentSpeech = &data; @@ -732,13 +758,29 @@ void TTSSpeaker::speakText(TTSConfiguration config, SpeechData &data) { TTSLOG_VERBOSE("Speaking.... (%d, \"%s\")", data.id, data.text.c_str()); //Wait for EOS with a timeout incase EOS never comes - waitForAudioToFinishTimeout(60); + waitForAudioToFinishTimeout(10); } else { TTSLOG_WARNING("m_pipeline=%p, m_pipelineError=%d", m_pipeline, m_pipelineError); } m_currentSpeech = NULL; } +void TTSSpeaker::GStreamerBusWatchThreadFunc(void *ctx) { + TTSSpeaker *speaker = (TTSSpeaker*) ctx; + while(speaker && speaker->m_busThread) { + if (speaker->m_pipeline) + { + GstBus *bus = gst_pipeline_get_bus(GST_PIPELINE(speaker->m_pipeline)); + GstMessage *msg = NULL; + if ((msg = gst_bus_pop(bus)) != NULL) + { + speaker->handleMessage(msg); + gst_message_unref(msg); + } + } + } +} + void TTSSpeaker::GStreamerThreadFunc(void *ctx) { TTSSpeaker *speaker = (TTSSpeaker*) ctx; @@ -856,6 +898,12 @@ bool TTSSpeaker::handleMessage(GstMessage *message) { break; + case GST_MESSAGE_DURATION_CHANGED: { + gst_element_query_duration(m_pipeline, GST_FORMAT_TIME, &m_duration); + TTSLOG_INFO("Duration %" GST_TIME_FORMAT, GST_TIME_ARGS(m_duration)); + } + break; + case GST_MESSAGE_STATE_CHANGED: { gchar* filename; GstState oldstate, newstate, pending; diff --git a/TextToSpeech/impl/TTSSpeaker.h b/TextToSpeech/impl/TTSSpeaker.h index 0e5eed9db0..a4e4ea936c 100644 --- a/TextToSpeech/impl/TTSSpeaker.h +++ b/TextToSpeech/impl/TTSSpeaker.h @@ -156,15 +156,19 @@ class TTSSpeaker { bool m_pipelineError; bool m_networkError; bool m_runThread; + bool m_busThread; bool m_flushed; bool m_isEOS; bool m_ensurePipeline; std::thread *m_gstThread; + std::thread *m_gstbusThread; guint m_busWatch; + gint64 m_duration; uint8_t m_pipelineConstructionFailures; const uint8_t m_maxPipelineConstructionFailures; static void GStreamerThreadFunc(void *ctx); + static void GStreamerBusWatchThreadFunc(void *ctx); void createPipeline(); void resetPipeline(); void destroyPipeline(); diff --git a/TextToSpeech/impl/TextToSpeechCommon.h b/TextToSpeech/impl/TextToSpeechCommon.h index a78b9a2014..b14876334c 100644 --- a/TextToSpeech/impl/TextToSpeechCommon.h +++ b/TextToSpeech/impl/TextToSpeechCommon.h @@ -25,15 +25,15 @@ #define CHECK_CONNECTION_RETURN_ON_FAIL(ret) do {\ if(!m_ttsManager) { \ LOGERR("TTS manager is not establised"); \ - response["status"] = TTS::TTS_NOT_ENABLED; \ + logResponse(TTS::TTS_NOT_ENABLED,response); \ returnResponse(ret); \ } } while(0) #define CHECK_SESSION_RETURN_ON_FAIL(id, sessionitr, sessioninfo, ret) do { \ sessionitr = m_sessionMap.find(id); \ if(sessionitr == m_sessionMap.end()) { \ - LOGINFO("TTS Session is not created"); \ - response["status"] = TTS::TTS_NO_SESSION_FOUND; \ + LOGERR("TTS Session is not created"); \ + logResponse(TTS::TTS_NO_SESSION_FOUND,response); \ returnResponse(ret); \ } \ sessioninfo = sessionitr->second; \ @@ -42,8 +42,11 @@ #define SET_UNSET_EXTENDED_EVENT(sessionInfo, input_event_list, event_flag, event_name) do { \ uint32_t event = (uint32_t)(event_flag); \ if((input_event_list & event) && !(sessionInfo->m_extendedEvents & event)) { \ + LOGINFO("Installing the event \"%s\"", event_name); \ + response["ExtendedEvent"] = event_name; \ sessionInfo->m_extendedEvents |= event; \ } else if(!(input_event_list & event) && (sessionInfo->m_extendedEvents & event)) { \ + LOGINFO("UnInstalling the event \"%s\"", event_name); \ sessionInfo->m_extendedEvents &= ~event; \ } } while(0) diff --git a/TextToSpeech/test/CMakeLists.txt b/TextToSpeech/test/CMakeLists.txt index cdc9aba57f..9b568bf47c 100644 --- a/TextToSpeech/test/CMakeLists.txt +++ b/TextToSpeech/test/CMakeLists.txt @@ -15,16 +15,18 @@ # See the License for the specific language governing permissions and # limitations under the License. -set(PLUGIN_NAME TTSServiceAPITest) +set(PLUGIN_NAME TTSThunderAPITest) find_package(${NAMESPACE}Protocols REQUIRED) -add_executable(${PLUGIN_NAME} TTSServiceAPITest.cpp) +add_executable(${PLUGIN_NAME} TTSThunderAPITest.cpp) set_target_properties(${PLUGIN_NAME} PROPERTIES CXX_STANDARD 11 CXX_STANDARD_REQUIRED YES ) +target_include_directories(${PLUGIN_NAME} PRIVATE ../../helpers ../impl) + target_link_libraries(${PLUGIN_NAME} PRIVATE ${NAMESPACE}Protocols::${NAMESPACE}Protocols diff --git a/TextToSpeech/test/Module.h b/TextToSpeech/test/Module.h index dfdefed1da..561089931c 100644 --- a/TextToSpeech/test/Module.h +++ b/TextToSpeech/test/Module.h @@ -20,7 +20,7 @@ #pragma once #ifndef MODULE_NAME -#define MODULE_NAME TTSServiceAPITest +#define MODULE_NAME TTSThunderAPITest #endif #include diff --git a/TextToSpeech/test/TTSServiceAPITest.cpp b/TextToSpeech/test/TTSThunderAPITest.cpp similarity index 83% rename from TextToSpeech/test/TTSServiceAPITest.cpp rename to TextToSpeech/test/TTSThunderAPITest.cpp index 0578386f75..51063d8917 100644 --- a/TextToSpeech/test/TTSServiceAPITest.cpp +++ b/TextToSpeech/test/TTSThunderAPITest.cpp @@ -17,8 +17,9 @@ * limitations under the License. */ -#include "../impl/logger.h" -#include "../impl/TTSCommon.h" +#include "logger.h" +#include "TTSCommon.h" +#include "utils.h" #include #include @@ -43,6 +44,8 @@ using namespace TTS; volatile unsigned short g_connectedToTTSEventCount = 0; volatile bool g_connectedToTTS = false; +bool manualExecution = false; + #define OPT_ENABLE_TTS 1 #define OPT_VOICE_LIST 2 #define OPT_SET_CONFIG 3 @@ -69,6 +72,7 @@ volatile bool g_connectedToTTS = false; void showMenu() { + cout << endl; cout << "------------------------" << endl; cout << OPT_ENABLE_TTS << ".enableTTS" << endl; cout << OPT_VOICE_LIST << ".listVoices" << endl; @@ -103,6 +107,10 @@ struct AppInfo { uint32_t m_sessionId; }; +void Delay(uint32_t delay_us) { + usleep(1000 * delay_us); +} + struct MyStream { MyStream() : myfile(NULL), in(&cin) { } @@ -111,9 +119,12 @@ struct MyStream { if(myfile->is_open()) { cout << "Reading from file" << endl; in = myfile; + cout << endl; + manualExecution = false; } else { cout << "Reading from std::cin" << endl; in = &cin; + manualExecution = true; } } @@ -163,79 +174,84 @@ namespace Handlers { /* Event Handlers */ static void onTTSStateChangedHandler(const JsonObject& params) { bool state = params["state"].Boolean(); - std::cout << "[TTSSrvEvt] " << __FUNCTION__ << ": " << state << std::endl; + cout << endl << "Event: onTTSStateChanged - state (" << state << ")" << endl; } static void onTTSSessionCreatedHandler(const JsonObject& params) { int appId = params["appId"].Number(); int sessionId = params["sessionId"].Number(); - std::cout << "[TTSSrvEvt] " << __FUNCTION__ << ": appId: " << appId << "sessionId: "<< sessionId << std::endl; + cout << endl << "Event: onTTSSessionCreated - appId (" << appId << ") sessionId (" << sessionId << ")" << endl << endl; + } + static void onVoiceChangedHandler(const JsonObject& params) { + std::string voice = params["voice"].String(); + cout << endl << "Event: onVoiceChanged - TTS voice got changed to (" << voice << ")" << endl << endl; } static void onResourceAcquiredHandler(const JsonObject& params) { int appId = params["appId"].Number(); int sessionId = params["sessionId"].Number(); - std::cout << "[TTSSrvEvt] " << __FUNCTION__ << ": appId: " << appId << "sessionId: "<< sessionId << std::endl; + cout << endl << "Event: onResourceAcquired - appId (" << appId << ") sessionId (" << sessionId << ")" << endl << endl; } static void onResourceReleasedHandler(const JsonObject& params) { int appId = params["appId"].Number(); int sessionId = params["sessionId"].Number(); - std::cout << "[TTSSrvEvt] " << __FUNCTION__ << ": appId: " << appId << "sessionId: "<< sessionId << std::endl; + cout << endl << "Event: onResourceReleased - appId (" << appId << ") sessionId (" << sessionId << ")" << endl << endl; } static void onWillSpeakHandler(const JsonObject& params) { int appId = params["appId"].Number(); int sessionId = params["sessionId"].Number(); int speechId = params["speechId"].Number(); std::string text = params["text"].String(); - std::cout << "[TTSSrvEvt] " << __FUNCTION__ << ": appId: " << appId << " sessionId: "<< sessionId << " speechid: " << speechId << " text: " << text << std::endl; + cout << endl << "Event: onWillSpeak - appId (" << appId << ") sessionId (" << sessionId << ") speechid: (" << speechId << ") text (" << text << ")" << endl; } static void onSpeechStartHandler(const JsonObject& params) { int appId = params["appId"].Number(); int sessionId = params["sessionId"].Number(); int speechId = params["speechId"].Number(); std::string text = params["text"].String(); - std::cout << "[TTSSrvEvt] " << __FUNCTION__ << ": appId: " << appId << " sessionId: "<< sessionId << " speechid: " << speechId << " text: " << text << std::endl; + cout << endl << "Event: onSpeechStart - appId (" << appId << ") sessionId (" << sessionId << ") speechid: (" << speechId << ") text (" << text << ")" << endl; } static void onSpeechPauseHandler(const JsonObject& params) { int appId = params["appId"].Number(); int sessionId = params["sessionId"].Number(); int speechId = params["speechId"].Number(); - std::cout << "[TTSSrvEvt] " << __FUNCTION__ << ": appId: " << appId << " sessionId: "<< sessionId << " speechid: " << speechId << std::endl; + cout << endl << "Event: onSpeechPause - appId (" << appId << ") sessionId (" << sessionId << ") speechid: (" << speechId << ")" << endl; } static void onSpeechResumeHandler(const JsonObject& params) { int appId = params["appId"].Number(); int sessionId = params["sessionId"].Number(); int speechId = params["speechId"].Number(); - std::cout << "[TTSSrvEvt] " << __FUNCTION__ << ": appId: " << appId << " sessionId: "<< sessionId << " speechid: " << speechId << std::endl; + cout << endl << "Event: onSpeechResume - appId (" << appId << ") sessionId (" << sessionId << ") speechid: (" << speechId << ")" << endl; } static void onSpeechCancelledHandler(const JsonObject& params) { int appId = params["appId"].Number(); int sessionId = params["sessionId"].Number(); int speechId = params["speechId"].Number(); - std::cout << "[TTSSrvEvt] " << __FUNCTION__ << ": appId: " << appId << " sessionId: "<< sessionId << " speechid: " << speechId << std::endl; + cout << endl << "Event: onSpeechCancel - appId (" << appId << ") sessionId (" << sessionId << ") speechid: (" << speechId << ")" << endl; } static void onSpeechInterruptedHandler(const JsonObject& params) { int appId = params["appId"].Number(); int sessionId = params["sessionId"].Number(); int speechId = params["speechId"].Number(); - std::cout << "[TTSSrvEvt] " << __FUNCTION__ << ": appId: " << appId << " sessionId: "<< sessionId << " speechid: " << speechId << std::endl; + cout << endl << "Event: onSpeechInterrupt - appId (" << appId << ") sessionId (" << sessionId << ") speechid: (" << speechId << ")" << endl; } static void onNetworkErrorHandler(const JsonObject& params) { int appId = params["appId"].Number(); int sessionId = params["sessionId"].Number(); int speechId = params["speechId"].Number(); - std::cout << "[TTSSrvEvt] " << __FUNCTION__ << ": appId: " << appId << " sessionId: "<< sessionId << " speechid: " << speechId << std::endl; + cout << endl << "Event: onNetworkError - appId (" << appId << ") sessionId (" << sessionId << ") speechid: (" << speechId << ")" << endl; } static void onPlaybackErrorHandler(const JsonObject& params) { int appId = params["appId"].Number(); int sessionId = params["sessionId"].Number(); int speechId = params["speechId"].Number(); - std::cout << "[TTSSrvEvt] " << __FUNCTION__ << ": appId: " << appId << " sessionId: "<< sessionId << " speechid: " << speechId << std::endl; + cout << endl << "Event: onPlaybackError - appId (" << appId << ") sessionId (" << sessionId << ") speechid: (" << speechId << ")" << endl; } static void onSpeechCompleteHandler(const JsonObject& params) { int appId = params["appId"].Number(); int sessionId = params["sessionId"].Number(); int speechId = params["speechId"].Number(); std::string text = params["text"].String(); - std::cout << "[TTSSrvEvt] " << __FUNCTION__ << ": appId: " << appId << "sessionId: "<< sessionId << "speechid: " << speechId << "text: " << text << std::endl; + std::cout << std::endl; + cout << endl << "Event: onSpeechComplete - appId (" << appId << ") sessionId (" << sessionId << ") speechid: (" << speechId << ") text (" << text << ")" << endl; } } int main(int argc, char *argv[]) { @@ -251,7 +267,7 @@ int main(int argc, char *argv[]) { int appid = 0; int secure = false; int sessionid = 0; - char clearall = 'n'; + bool clearall = false; string stext; string appname; @@ -260,93 +276,73 @@ int main(int argc, char *argv[]) { Core::SystemInfo::SetEnvironment(_T("THUNDER_ACCESS"), (_T(SERVER_DETAILS))); if (NULL == remoteObject) { + cout << endl << "TTS Thunder Plugin call sign is " << TTSSRV_CALLSIGN << endl; remoteObject = new JSONRPC::Client(_T(TTSSRV_CALLSIGN), _T("")); if (NULL == remoteObject) { std::cout << "JSONRPC::Client initialization failed" << std::endl; } else { /* Register handlers for Event reception. */ - if (remoteObject->Subscribe(1000, _T("onTTSStateChanged"), + if (!remoteObject->Subscribe(1000, _T("onTTSStateChanged"), &Handlers::onTTSStateChangedHandler) == Core::ERROR_NONE) { - std::cout << "Subscribed to : onTTSStateChangedHandler" << std::endl; - } else { - std::cout << "Failed to Subscribe notification handler : onTTSStateChangedHandler" << std::endl; + LOGERR("Failed to Subscribe notification handler : onTTSStateChangedHandler"); } - if (remoteObject->Subscribe(1000, _T("onTTSSessionCreated"), + if (!remoteObject->Subscribe(1000, _T("onTTSSessionCreated"), &Handlers::onTTSSessionCreatedHandler) == Core::ERROR_NONE) { - std::cout << "Subscribed to : onTTSSessionCreatedHandler" << std::endl; - } else { - std::cout << "Failed to Subscribe notification handler : onTTSSessionCreatedHandler" << std::endl; + LOGERR("Failed to Subscribe notification handler : onTTSSessionCreatedHandler"); } - if (remoteObject->Subscribe(1000, _T("onResourceAcquired"), + if (!remoteObject->Subscribe(1000, _T("onResourceAcquired"), &Handlers::onResourceAcquiredHandler) == Core::ERROR_NONE) { - std::cout << "Subscribed to : onResourceAcquiredHandler" << std::endl; - } else { - std::cout << "Failed to Subscribe notification handler : onResourceAcquiredHandler" << std::endl; + LOGERR("Failed to Subscribe notification handler : onResourceAcquiredHandler"); + } + if (!remoteObject->Subscribe(1000, _T("onVoiceChanged"), + &Handlers::onVoiceChangedHandler) == Core::ERROR_NONE) { + LOGERR("Failed to Subscribe notification handler : onVoiceChangedHandler"); } - if (remoteObject->Subscribe(1000, _T("onResourceReleased"), + if (!remoteObject->Subscribe(1000, _T("onResourceReleased"), &Handlers::onResourceReleasedHandler) == Core::ERROR_NONE) { - std::cout << "Subscribed to : onResourceReleasedHandler" << std::endl; - } else { - std::cout << "Failed to Subscribe notification handler : onResourceReleasedHandler" << std::endl; + LOGERR("Failed to Subscribe notification handler : onResourceReleasedHandler"); } - if (remoteObject->Subscribe(1000, _T("onWillSpeak"), + if (!remoteObject->Subscribe(1000, _T("onWillSpeak"), &Handlers::onWillSpeakHandler) == Core::ERROR_NONE) { - std::cout << "Subscribed to : onWillSpeakHandler" << std::endl; - } else { - std::cout << "Failed to Subscribe notification handler : onWillSpeakHandler" << std::endl; + LOGERR("Failed to Subscribe notification handler : onWillSpeakHandler"); } - if (remoteObject->Subscribe(1000, _T("onSpeechStart"), + if (!remoteObject->Subscribe(1000, _T("onSpeechStart"), &Handlers::onSpeechStartHandler) == Core::ERROR_NONE) { - std::cout << "Subscribed to : onSpeechStartHandler" << std::endl; - } else { - std::cout << "Failed to Subscribe notification handler : onSpeechStartHandler" << std::endl; + LOGERR("Failed to Subscribe notification handler : onSpeechStartHandler"); } - if (remoteObject->Subscribe(1000, _T("onSpeechPause"), + if (!remoteObject->Subscribe(1000, _T("onSpeechPause"), &Handlers::onSpeechPauseHandler) == Core::ERROR_NONE) { - std::cout << "Subscribed to : onSpeechPauseHandler" << std::endl; - } else { - std::cout << "Failed to Subscribe notification handler : onSpeechPauseHandler" << std::endl; + LOGERR("Failed to Subscribe notification handler : onSpeechPauseHandler"); } - if (remoteObject->Subscribe(1000, _T("onSpeechResume"), + if (!remoteObject->Subscribe(1000, _T("onSpeechResume"), &Handlers::onSpeechResumeHandler) == Core::ERROR_NONE) { - std::cout << "Subscribed to : onSpeechResumeHandler" << std::endl; - } else { - std::cout << "Failed to Subscribe notification handler : onSpeechResumeHandler" << std::endl; + LOGERR("Failed to Subscribe notification handler : onSpeechResumeHandler"); } - if (remoteObject->Subscribe(1000, _T("onSpeechCancelled"), + if (!remoteObject->Subscribe(1000, _T("onSpeechCancelled"), &Handlers::onSpeechCancelledHandler) == Core::ERROR_NONE) { - std::cout << "Subscribed to : onSpeechCancelledHandler" << std::endl; - } else { - std::cout << "Failed to Subscribe notification handler : onSpeechCancelledHandler" << std::endl; + LOGERR("Failed to Subscribe notification handler : onSpeechCancelledHandler"); } - if (remoteObject->Subscribe(1000, _T("onSpeechInterrupted"), + if (!remoteObject->Subscribe(1000, _T("onSpeechInterrupted"), &Handlers::onSpeechInterruptedHandler) == Core::ERROR_NONE) { - std::cout << "Subscribed to : onSpeechInterruptedHandler" << std::endl; - } else { - std::cout << "Failed to Subscribe notification handler : onSpeechInterruptedHandler" << std::endl; + LOGERR("Failed to Subscribe notification handler : onSpeechInterruptedHandler"); } - if (remoteObject->Subscribe(1000, _T("onNetworkError"), + if (!remoteObject->Subscribe(1000, _T("onNetworkError"), &Handlers::onNetworkErrorHandler) == Core::ERROR_NONE) { - std::cout << "Subscribed to : onNetworkErrorHandler" << std::endl; - } else { - std::cout << "Failed to Subscribe notification handler : onNetworkErrorHandler" << std::endl; + LOGERR("Failed to Subscribe notification handler : onNetworkErrorHandler"); } - if (remoteObject->Subscribe(1000, _T("onPlaybackError"), + if (!remoteObject->Subscribe(1000, _T("onPlaybackError"), &Handlers::onPlaybackErrorHandler) == Core::ERROR_NONE) { - std::cout << "Subscribed to : onPlaybackErrorHandler" << std::endl; - } else { - std::cout << "Failed to Subscribe notification handler : onPlaybackErrorHandler" << std::endl; + LOGERR("Failed to Subscribe notification handler : onPlaybackErrorHandler"); } - if (remoteObject->Subscribe(1000, _T("onSpeechComplete"), + if (!remoteObject->Subscribe(1000, _T("onSpeechComplete"), &Handlers::onSpeechCompleteHandler) == Core::ERROR_NONE) { - std::cout << "Subscribed to : onSpeechCompleteHandler" << std::endl; - } else { - std::cout << "Failed to Subscribe notification handler : onSpeechCompleteHandler" << std::endl; + LOGERR("Failed to Subscribe notification handler : onSpeechCompleteHandler"); } while (true) { - std::cout << std::endl; - showMenu(); + if(manualExecution) + showMenu(); + cout << "------------------------" << endl; stream.getInput(choice, "Enter your choice : "); switch (choice) { @@ -363,7 +359,7 @@ int main(int argc, char *argv[]) { } else { cout << "TextToSpeech: enableTTS call failed. TTS_Status: " << result["TTS_Status"].String() << endl; } - + Delay(100); } break; @@ -379,7 +375,6 @@ int main(int argc, char *argv[]) { ret = remoteObject->Invoke(1000, _T("listVoices"), params, result); if (result["success"].Boolean()) { - cout << "TextToSpeech: listVoices call Success" << endl; voices = result["voices"].String(); cout << "Supported voices for langauge: " << voices << endl; } else { @@ -587,7 +582,7 @@ int main(int argc, char *argv[]) { ret = remoteObject->Invoke(1000, _T("requestExtendedEvents"), params, result); if (result["success"].Boolean()) { - cout << "requestExtendedEvents call success" << endl; + cout << "Installed handler for event: (" << result["ExtendedEvent"].String() << ")" << endl; } else { cout << "requestExtendedEvents call failed. TTS_Status: " << result["TTS_Status"].String() << endl; } @@ -617,6 +612,7 @@ int main(int argc, char *argv[]) { } else { cout << "speak call failed. TTS_Status: " << result["TTS_Status"].String() << endl; } + Delay(100); } else { cout << "Session hasn't been created for app(" << appid << ")" << endl; } @@ -722,7 +718,7 @@ int main(int argc, char *argv[]) { _T("getSpeechState"), params, result); if (result["success"].Boolean()) { string state; - SpeechState sstate; + int sstate; sstate = result["speechState"].Number(); switch(sstate) { case SPEECH_PENDING: state = "Pending"; break; @@ -730,7 +726,7 @@ int main(int argc, char *argv[]) { case SPEECH_PAUSED: state = "Paused"; break; default: state = "Not found"; } - cout << "Speech Status : " << state << endl; + cout << "Speech Status of id " << sid << " is : " << state << endl; } else { cout << "getSpeechState call failed. TTS_Status: " << result["TTS_Status"].String() << endl; } @@ -757,14 +753,17 @@ int main(int argc, char *argv[]) { } else { cout << "destroySession call failed. TTS_Status: " << result["TTS_Status"].String() << endl; } + Delay(100); } else { cout << "Session hasn't been created for app(" << appid << ")" << endl; } } break; - case OPT_EXIT: + case OPT_EXIT: { + cout << "Test app is exiting" < Date: Thu, 25 Jun 2020 13:50:46 +0200 Subject: [PATCH 34/56] DELIA-44142: wpeframework crash from WifiManager plugin getSignalData() Reason for change: wpeframework crash from WifiManager plugin getSignalData() Test Procedure: Build and test based on README.md Risks: Low Signed-off-by: Mariusz Strozynski --- WifiManager/impl/WifiManagerSignalThreshold.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/WifiManager/impl/WifiManagerSignalThreshold.cpp b/WifiManager/impl/WifiManagerSignalThreshold.cpp index 14483c2ee5..19647d15ae 100644 --- a/WifiManager/impl/WifiManagerSignalThreshold.cpp +++ b/WifiManager/impl/WifiManagerSignalThreshold.cpp @@ -34,7 +34,10 @@ namespace { JsonObject response; wifiManager.getConnectedSSID(JsonObject(), response); - signalStrengthOut = std::stof(response["signalStrength"].String()); + signalStrengthOut = 0.0f; + if (response.HasLabel("signalStrength")) { + signalStrengthOut = std::stof(response["signalStrength"].String()); + } if (signalStrengthOut >= signalStrengthThresholdExcellent && signalStrengthOut < 0) { From 0151ff4bec1448c7149822ce6ba2dddb1a7bb7db Mon Sep 17 00:00:00 2001 From: mstrozyn Date: Mon, 29 Jun 2020 08:41:39 +0200 Subject: [PATCH 35/56] Revert "RDK-28967: Extend getDeviceInfo method in system thunder plugin" This reverts commit 64a15575ea463dfab94bf9b2aa410878dccd661e. --- amlogic.cmake | 3 --- 1 file changed, 3 deletions(-) diff --git a/amlogic.cmake b/amlogic.cmake index 5413d0d5fa..baa90ff8e5 100644 --- a/amlogic.cmake +++ b/amlogic.cmake @@ -113,9 +113,6 @@ if (BUILD_LLAMA) add_definitions (-DENABLE_VREX_SERVICE) add_definitions (-DRF4CE_API) add_definitions (-DHAS_STATE_OBSERVER) - - message("Building with device manufacturer info") - add_definitions (-DENABLE_DEVICE_MANUFACTURER_INFO) endif() if(SCREEN_CAPTURE) From 95537d78af2a373daf3adeadd22598243c0869c0 Mon Sep 17 00:00:00 2001 From: Nikita Poltorapavlo Date: Mon, 29 Jun 2020 14:55:01 +0300 Subject: [PATCH 36/56] RDK-28664 : Persistent Storage Thunder API Reason for change: new thunder plugin Test Procedure: test functionality specified in docs Risks: Low Signed-off-by: Nikita Poltorapavlo --- CMakeLists.txt | 4 + PersistentStore/CMakeLists.txt | 49 ++ PersistentStore/Module.cpp | 22 + PersistentStore/Module.h | 29 + PersistentStore/PersistentStore.config | 3 + PersistentStore/PersistentStore.cpp | 709 +++++++++++++++++++++++++ PersistentStore/PersistentStore.h | 75 +++ PersistentStore/README.md | 35 ++ cmake/FindDL.cmake | 5 + cmake/FindPlabels.cmake | 12 + cmake/FindSqlite.cmake | 3 + cmake/FindSqliteSee.cmake | 3 + 12 files changed, 949 insertions(+) create mode 100644 PersistentStore/CMakeLists.txt create mode 100644 PersistentStore/Module.cpp create mode 100644 PersistentStore/Module.h create mode 100644 PersistentStore/PersistentStore.config create mode 100644 PersistentStore/PersistentStore.cpp create mode 100644 PersistentStore/PersistentStore.h create mode 100644 PersistentStore/README.md create mode 100644 cmake/FindDL.cmake create mode 100644 cmake/FindPlabels.cmake create mode 100644 cmake/FindSqlite.cmake create mode 100644 cmake/FindSqliteSee.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index 1f99333062..68621e9077 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -292,6 +292,10 @@ endif() add_subdirectory(TextToSpeech) endif() +if(PLUGIN_PERSISTENT_STORE) + add_subdirectory(PersistentStore) +endif() + if(WPEFRAMEWORK_CREATE_IPKG_TARGETS) set(CPACK_GENERATOR "DEB") set(CPACK_DEB_COMPONENT_INSTALL ON) diff --git a/PersistentStore/CMakeLists.txt b/PersistentStore/CMakeLists.txt new file mode 100644 index 0000000000..18b9951eb7 --- /dev/null +++ b/PersistentStore/CMakeLists.txt @@ -0,0 +1,49 @@ +set(PLUGIN_NAME PersistentStore) +set(MODULE_NAME ${NAMESPACE}${PLUGIN_NAME}) + +find_package(${NAMESPACE}Plugins REQUIRED) + +find_package(PkgConfig) + +# enabling the secure extension requires a license: https://www.hwaci.com/cgi-bin/see-step1 +if (USE_SQLITE_SEE) + find_package(SqliteSee REQUIRED) +else() + find_package(Sqlite REQUIRED) +endif() + +find_package(GLIB REQUIRED) + +# nonce generator +if (BUILD_WITH_PLABELS AND USE_SQLITE_SEE) + find_package(DL REQUIRED) + find_package(Plabels REQUIRED) +endif() + +add_library(${MODULE_NAME} SHARED + PersistentStore.cpp + Module.cpp +) + +set_target_properties(${MODULE_NAME} PROPERTIES + CXX_STANDARD 11 + CXX_STANDARD_REQUIRED YES) + +add_definitions(${SQLITE_CFLAGS_OTHER} ${PLABELS_FLAGS}) +link_directories(${SQLITE_LIBRARY_DIRS}) + +target_include_directories(${MODULE_NAME} PRIVATE ../helpers + ${SQLITE_INCLUDE_DIRS} + ${PLABELS_INCLUDE_DIRS} + ${GLIB_INCLUDE_DIRS}) + +target_link_libraries(${MODULE_NAME} PRIVATE ${NAMESPACE}Plugins::${NAMESPACE}Plugins + ${SQLITE_LIBRARIES} + ${PLABELS_LIBRARIES} + ${GLIB_LIBRARIES} + ${DL_LIBRARIES}) + +install(TARGETS ${MODULE_NAME} + DESTINATION lib/${STORAGE_DIRECTORY}/plugins) + +write_config(${PLUGIN_NAME}) diff --git a/PersistentStore/Module.cpp b/PersistentStore/Module.cpp new file mode 100644 index 0000000000..ce759b615f --- /dev/null +++ b/PersistentStore/Module.cpp @@ -0,0 +1,22 @@ +/** +* If not stated otherwise in this file or this component's LICENSE +* file the following copyright and licenses apply: +* +* Copyright 2019 RDK Management +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +**/ + +#include "Module.h" + +MODULE_NAME_DECLARATION(BUILD_REFERENCE) diff --git a/PersistentStore/Module.h b/PersistentStore/Module.h new file mode 100644 index 0000000000..9538f1df39 --- /dev/null +++ b/PersistentStore/Module.h @@ -0,0 +1,29 @@ +/** +* If not stated otherwise in this file or this component's LICENSE +* file the following copyright and licenses apply: +* +* Copyright 2019 RDK Management +* +* 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. +**/ + +#pragma once +#ifndef MODULE_NAME +#define MODULE_NAME PersistentStore +#endif + +#include +#include + +#undef EXTERNAL +#define EXTERNAL diff --git a/PersistentStore/PersistentStore.config b/PersistentStore/PersistentStore.config new file mode 100644 index 0000000000..5039bdfa1b --- /dev/null +++ b/PersistentStore/PersistentStore.config @@ -0,0 +1,3 @@ +set (autostart false) +set (preconditions Platform) +set (callsign "org.rdk.PersistentStore") diff --git a/PersistentStore/PersistentStore.cpp b/PersistentStore/PersistentStore.cpp new file mode 100644 index 0000000000..ca14d181a0 --- /dev/null +++ b/PersistentStore/PersistentStore.cpp @@ -0,0 +1,709 @@ +#include "PersistentStore.h" + +#include +#include + +#if defined(USE_PLABELS) +#include "pbnj_utils.hpp" +#endif + +#ifndef SQLITE_FILE_HEADER +#define SQLITE_FILE_HEADER "SQLite format 3" +#endif + +#define SQLITE *(sqlite3**)&mData + +const short WPEFramework::Plugin::PersistentStore::API_VERSION_NUMBER_MAJOR = 1; +const short WPEFramework::Plugin::PersistentStore::API_VERSION_NUMBER_MINOR = 0; +const string WPEFramework::Plugin::PersistentStore::SERVICE_NAME = "org.rdk.PersistentStore"; +const string WPEFramework::Plugin::PersistentStore::METHOD_SET_VALUE = "setValue"; +const string WPEFramework::Plugin::PersistentStore::METHOD_GET_VALUE = "getValue"; +const string WPEFramework::Plugin::PersistentStore::METHOD_DELETE_KEY = "deleteKey"; +const string WPEFramework::Plugin::PersistentStore::METHOD_DELETE_NAMESPACE = "deleteNamespace"; +const string WPEFramework::Plugin::PersistentStore::METHOD_GET_KEYS = "getKeys"; +const string WPEFramework::Plugin::PersistentStore::METHOD_GET_NAMESPACES = "getNamespaces"; +const string WPEFramework::Plugin::PersistentStore::METHOD_GET_STORAGE_SIZE = "getStorageSize"; +const string WPEFramework::Plugin::PersistentStore::EVT_ON_STORAGE_EXCEEDED = "onStorageExceeded"; +const char* WPEFramework::Plugin::PersistentStore::STORE_NAME = "rdkservicestore"; +const char* WPEFramework::Plugin::PersistentStore::STORE_KEY = "xyzzy123"; +const int64_t WPEFramework::Plugin::PersistentStore::MAX_SIZE_BYTES = 1000000; +const int64_t WPEFramework::Plugin::PersistentStore::MAX_VALUE_SIZE_BYTES = 1000; + +using namespace std; + +namespace { + bool fileEncrypted(const char* f) + { + FILE* fd = fopen(f, "rb"); + if (!fd) + return false; + + int magicSize = strlen(SQLITE_FILE_HEADER); + char* fileHeader = (char*)malloc(magicSize + 1); + int readSize = (int)fread(fileHeader, 1, magicSize, fd); + fclose(fd); + + bool eq = magicSize == readSize && ::memcmp(fileHeader, SQLITE_FILE_HEADER, magicSize) == 0; + free(fileHeader); + + return !eq; + } + + bool fileRemove(const char* f) + { + return (remove (f) == 0); + } +} + +namespace WPEFramework { + namespace Plugin { + + SERVICE_REGISTRATION(PersistentStore, 1, 0); + + PersistentStore* PersistentStore::_instance = nullptr; + + PersistentStore::PersistentStore() + : AbstractPlugin() + , mData(nullptr) + { + LOGINFO("ctor"); + PersistentStore::_instance = this; + registerMethod(METHOD_SET_VALUE, &PersistentStore::setValueWrapper, this); + registerMethod(METHOD_GET_VALUE, &PersistentStore::getValueWrapper, this); + registerMethod(METHOD_DELETE_KEY, &PersistentStore::deleteKeyWrapper, this); + registerMethod(METHOD_DELETE_NAMESPACE, &PersistentStore::deleteNamespaceWrapper, this); + registerMethod(METHOD_GET_KEYS, &PersistentStore::getKeysWrapper, this); + registerMethod(METHOD_GET_NAMESPACES, &PersistentStore::getNamespacesWrapper, this); + registerMethod(METHOD_GET_STORAGE_SIZE, &PersistentStore::getStorageSizeWrapper, this); + } + + PersistentStore::~PersistentStore() + { + LOGINFO("dtor"); + PersistentStore::_instance = nullptr; + + term(); + } + + const string PersistentStore::Initialize(PluginHost::IShell* /* service */) + { + LOGINFO(); + + gchar* path = g_build_filename(g_get_user_data_dir(), STORE_NAME, nullptr); // XDG_DATA_HOME + bool success = init(path, STORE_KEY); + g_free(path); + + return success ? "" : "init failed"; + } + + void PersistentStore::Deinitialize(PluginHost::IShell* /* service */) + { + LOGINFO(); + + term(); + } + + string PersistentStore::Information() const + { + return(string("{\"service\": \"") + SERVICE_NAME + string("\"}")); + } + + // Registered methods (wrappers) begin + uint32_t PersistentStore::setValueWrapper(const JsonObject& parameters, JsonObject& response) + { + LOGINFOMETHOD(); + + bool success = false; + if (!parameters.HasLabel("namespace") || + !parameters.HasLabel("key") || + !parameters.HasLabel("value")) + { + response["error"] = "params missing"; + } + else + { + string ns = parameters["namespace"].String(); + string key = parameters["key"].String(); + string value = parameters["value"].String(); + if (ns.empty() || key.empty()) + response["error"] = "params empty"; + else if (ns.size() > 1000 || key.size() > 1000 || value.size() > 1000) + response["error"] = "params too long"; + else + success = setValue(ns, key, value); + } + + returnResponse(success); + } + + uint32_t PersistentStore::getValueWrapper(const JsonObject& parameters, JsonObject& response) + { + LOGINFOMETHOD(); + + bool success = false; + if (!parameters.HasLabel("namespace") || + !parameters.HasLabel("key")) + { + response["error"] = "params missing"; + } + else + { + string ns = parameters["namespace"].String(); + string key = parameters["key"].String(); + if (ns.empty() || key.empty()) + { + response["error"] = "params empty"; + } + else + { + string value; + success = getValue(ns, key, value); + if (success) + response["value"] = value; + } + } + + returnResponse(success); + } + + uint32_t PersistentStore::deleteKeyWrapper(const JsonObject& parameters, JsonObject& response) + { + LOGINFOMETHOD(); + + bool success = false; + if (!parameters.HasLabel("namespace") || + !parameters.HasLabel("key")) + { + response["error"] = "params missing"; + } + else + { + string ns = parameters["namespace"].String(); + string key = parameters["key"].String(); + if (ns.empty() || key.empty()) + response["error"] = "params empty"; + else + success = deleteKey(ns, key); + } + + returnResponse(success); + } + + uint32_t PersistentStore::deleteNamespaceWrapper(const JsonObject& parameters, JsonObject& response) + { + LOGINFOMETHOD(); + + bool success = false; + if (!parameters.HasLabel("namespace")) + { + response["error"] = "params missing"; + } + else + { + string ns = parameters["namespace"].String(); + if (ns.empty()) + response["error"] = "params empty"; + else + success = deleteNamespace(ns); + } + + returnResponse(success); + } + + uint32_t PersistentStore::getKeysWrapper(const JsonObject& parameters, JsonObject& response) + { + LOGINFOMETHOD(); + + bool success = false; + if (!parameters.HasLabel("namespace")) + { + response["error"] = "params missing"; + } + else + { + string ns = parameters["namespace"].String(); + if (ns.empty()) + response["error"] = "params empty"; + else + { + vector keys; + success = getKeys(ns, keys); + if (success) { + JsonArray jsonKeys; + for (auto it = keys.begin(); it != keys.end(); ++it) + jsonKeys.Add(*it); + response["keys"] = jsonKeys; + } + } + } + + returnResponse(success); + } + + uint32_t PersistentStore::getNamespacesWrapper(const JsonObject& parameters, JsonObject& response) + { + LOGINFOMETHOD(); + + bool success = false; + vector namespaces; + success = getNamespaces(namespaces); + if (success) + { + JsonArray jsonNamespaces; + for (auto it = namespaces.begin(); it != namespaces.end(); ++it) + jsonNamespaces.Add(*it); + response["namespaces"] = jsonNamespaces; + } + + returnResponse(success); + } + + uint32_t PersistentStore::getStorageSizeWrapper(const JsonObject& parameters, JsonObject& response) + { + LOGINFOMETHOD(); + + bool success = false; + map namespaceSizes; + success = getStorageSize(namespaceSizes); + if (success) + { + JsonObject jsonNamespaceSizes; + for (auto it = namespaceSizes.begin(); it != namespaceSizes.end(); ++it) + jsonNamespaceSizes[it->first.c_str()] = it->second; + response["namespaceSizes"] = jsonNamespaceSizes; + } + + returnResponse(success); + } + + bool PersistentStore::setValue(const string& ns, const string& key, const string& value) + { + LOGINFO("%s %s %s", ns.c_str(), key.c_str(), value.c_str()); + + bool success = false; + + sqlite3* &db = SQLITE; + + if (db) + { + sqlite3_stmt *stmt; + sqlite3_prepare_v2(db, "SELECT page_count * page_size FROM pragma_page_count(), pragma_page_size();", -1, &stmt, nullptr); + + if (sqlite3_step(stmt) == SQLITE_ROW) + { + int64_t size = sqlite3_column_int64(stmt, 0); + if (size > MAX_SIZE_BYTES) + LOGWARN("max size exceeded: %d", size); + else + success = true; + } + else + LOGERR("ERROR getting size: %s", sqlite3_errmsg(db)); + + sqlite3_finalize(stmt); + } + + if (success) + { + success = false; + + sqlite3_stmt *stmt; + sqlite3_prepare_v2(db, "INSERT OR IGNORE INTO namespace (name) values (?);", -1, &stmt, nullptr); + + sqlite3_bind_text(stmt, 1, ns.c_str(), -1, SQLITE_TRANSIENT); + + int rc = sqlite3_step(stmt); + if (rc != SQLITE_DONE) + LOGERR("ERROR inserting data: %s", sqlite3_errmsg(db)); + else + success = true; + + sqlite3_finalize(stmt); + } + + if (success) + { + success = false; + + sqlite3_stmt *stmt; + sqlite3_prepare_v2(db, "INSERT INTO item (ns,key,value)" + " SELECT id, ?, ?" + " FROM namespace" + " WHERE name = ?" + ";", -1, &stmt, nullptr); + + sqlite3_bind_text(stmt, 1, key.c_str(), -1, SQLITE_TRANSIENT); + sqlite3_bind_text(stmt, 2, value.c_str(), -1, SQLITE_TRANSIENT); + sqlite3_bind_text(stmt, 3, ns.c_str(), -1, SQLITE_TRANSIENT); + + int rc = sqlite3_step(stmt); + if (rc != SQLITE_DONE) + LOGERR("ERROR inserting data: %s", sqlite3_errmsg(db)); + else + success = true; + + sqlite3_finalize(stmt); + } + + if (success) + { + success = false; + + sqlite3_stmt *stmt; + sqlite3_prepare_v2(db, "SELECT page_count * page_size FROM pragma_page_count(), pragma_page_size();", -1, &stmt, nullptr); + + if (sqlite3_step(stmt) == SQLITE_ROW) + { + int64_t size = sqlite3_column_int64(stmt, 0); + if (size > MAX_SIZE_BYTES) + { + LOGWARN("max size exceeded: %d", size); + + JsonObject params; + sendNotify(EVT_ON_STORAGE_EXCEEDED, params); + } + else + success = true; + } + else + LOGERR("ERROR getting size: %s", sqlite3_errmsg(db)); + + sqlite3_finalize(stmt); + } + + return success; + } + + bool PersistentStore::getValue(const string& ns, const string& key, string& value) + { + LOGINFO("%s %s", ns.c_str(), key.c_str()); + + bool success = false; + + sqlite3* &db = SQLITE; + + if (db) + { + sqlite3_stmt *stmt; + sqlite3_prepare_v2(db, "SELECT value" + " FROM item" + " INNER JOIN namespace ON namespace.id = item.ns" + " where name = ? and key = ?" + ";", -1, &stmt, nullptr); + + sqlite3_bind_text(stmt, 1, ns.c_str(), -1, SQLITE_TRANSIENT); + sqlite3_bind_text(stmt, 2, key.c_str(), -1, SQLITE_TRANSIENT); + + int rc = sqlite3_step(stmt); + if (rc == SQLITE_ROW) + { + value = (const char*)sqlite3_column_text(stmt, 0); + success = true; + } + else + LOGWARN("not found: %d", rc); + sqlite3_finalize(stmt); + } + + return success; + } + + bool PersistentStore::deleteKey(const string& ns, const string& key) + { + LOGINFO("%s %s", ns.c_str(), key.c_str()); + + bool success = false; + + sqlite3* &db = SQLITE; + + if (db) + { + sqlite3_stmt *stmt; + sqlite3_prepare_v2(db, "DELETE FROM item" + " where ns in (select id from namespace where name = ?)" + " and key = ?" + ";", -1, &stmt, NULL); + + sqlite3_bind_text(stmt, 1, ns.c_str(), -1, SQLITE_TRANSIENT); + sqlite3_bind_text(stmt, 2, key.c_str(), -1, SQLITE_TRANSIENT); + + int rc = sqlite3_step(stmt); + if (rc != SQLITE_DONE) + LOGERR("ERROR removing data: %s", sqlite3_errmsg(db)); + else + success = true; + + sqlite3_finalize(stmt); + } + + return success; + } + + bool PersistentStore::deleteNamespace(const string& ns) + { + LOGINFO("%s", ns.c_str()); + + bool success = false; + + sqlite3* &db = SQLITE; + + if (db) + { + sqlite3_stmt *stmt; + sqlite3_prepare_v2(db, "DELETE FROM namespace where name = ?;", -1, &stmt, NULL); + + sqlite3_bind_text(stmt, 1, ns.c_str(), -1, SQLITE_TRANSIENT); + + int rc = sqlite3_step(stmt); + if (rc != SQLITE_DONE) + LOGERR("ERROR removing data: %s", sqlite3_errmsg(db)); + else + success = true; + + sqlite3_finalize(stmt); + } + + return success; + } + + bool PersistentStore::getKeys(const string& ns, std::vector& keys) + { + LOGINFO("%s", ns.c_str()); + + bool success = false; + + sqlite3* &db = SQLITE; + + keys.clear(); + + if (db) + { + sqlite3_stmt *stmt; + sqlite3_prepare_v2(db, "SELECT key" + " FROM item" + " where ns in (select id from namespace where name = ?)" + ";", -1, &stmt, NULL); + + sqlite3_bind_text(stmt, 1, ns.c_str(), -1, SQLITE_TRANSIENT); + + while (sqlite3_step(stmt) == SQLITE_ROW) + keys.push_back((const char*)sqlite3_column_text(stmt, 0)); + + sqlite3_finalize(stmt); + success = true; + } + + return success; + } + + bool PersistentStore::getNamespaces(std::vector& namespaces) + { + LOGINFO(); + + bool success = false; + + sqlite3* &db = SQLITE; + + namespaces.clear(); + + if (db) + { + sqlite3_stmt *stmt; + sqlite3_prepare_v2(db, "SELECT name FROM namespace;", -1, &stmt, NULL); + + while (sqlite3_step(stmt) == SQLITE_ROW) + namespaces.push_back((const char*)sqlite3_column_text(stmt, 0)); + + sqlite3_finalize(stmt); + success = true; + } + + return success; + } + + bool PersistentStore::getStorageSize(std::map& namespaceSizes) + { + LOGINFO(); + + bool success = false; + + sqlite3* &db = SQLITE; + + namespaceSizes.clear(); + + if (db) + { + sqlite3_stmt *stmt; + sqlite3_prepare_v2(db, "SELECT name, sum(length(key)+length(value))" + " FROM item" + " INNER JOIN namespace ON namespace.id = item.ns" + " GROUP BY name" + ";", -1, &stmt, NULL); + + while (sqlite3_step(stmt) == SQLITE_ROW) + namespaceSizes[(const char*)sqlite3_column_text(stmt, 0)] = sqlite3_column_int(stmt, 1); + + sqlite3_finalize(stmt); + success = true; + } + + return success; + } + + void PersistentStore::term() + { + LOGINFO(); + + sqlite3* &db = SQLITE; + + if (db) + sqlite3_close(db); + + db = NULL; + } + + void PersistentStore::vacuum() + { + LOGINFO(); + + sqlite3* &db = SQLITE; + + if (db) + { + char *errmsg; + int rc = sqlite3_exec(db, "VACUUM", 0, 0, &errmsg); + if (rc != SQLITE_OK || errmsg) + { + if (errmsg) + { + LOGERR("%s", errmsg); + sqlite3_free(errmsg); + } + else + LOGERR("%d", rc); + } + } + } + + bool PersistentStore::init(const char* filename, const char* key) + { + LOGINFO(); + + sqlite3* &db = SQLITE; + + term(); + + bool shouldEncrypt = key && *key; +#if defined(SQLITE_HAS_CODEC) + bool shouldReKey = shouldEncrypt && Utils::fileExists(filename) && !fileEncrypted(filename); +#endif + int rc = sqlite3_open(filename, &db); + if (rc) + { + LOGERR("%d : %s", rc, sqlite3_errmsg(db)); + term(); + return false; + } + + if (shouldEncrypt) + { +#if defined(SQLITE_HAS_CODEC) + std::vector pKey; +#if defined(USE_PLABELS) + bool result = pbnj_utils::prepareBufferForOrigin(key, [&pKey](const std::vector& buffer) { + pKey = buffer; + }); + if (!result) + { + LOGERR("pbnj_utils fail"); + term(); + return false; + } +#else + LOGWARN("SQLite encryption key is not secure, path=%s", filename); + pKey = std::vector(key, key + strlen(key)); +#endif + if (!shouldReKey) + rc = sqlite3_key_v2(db, nullptr, pKey.data(), pKey.size()); + else + { + rc = sqlite3_rekey_v2(db, nullptr, pKey.data(), pKey.size()); + if (rc == SQLITE_OK) + vacuum(); + } + + if (rc != SQLITE_OK) + { + LOGERR("Failed to attach encryption key to SQLite database %s\nCause - %s", filename, sqlite3_errmsg(db)); + term(); + return false; + } + + if (shouldReKey && !fileEncrypted(filename)) + LOGERR("SQLite database file is clear after re-key, path=%s", filename); +#endif + } + + char *errmsg; + rc = sqlite3_exec(db, "CREATE TABLE if not exists namespace (" + "id INTEGER PRIMARY KEY," + "name TEXT UNIQUE" + ");", 0, 0, &errmsg); + if (rc != SQLITE_OK || errmsg) + { + if (errmsg) + { + LOGERR("%d : %s", rc, errmsg); + sqlite3_free(errmsg); + } + else + LOGERR("%d", rc); + } + + if (rc == SQLITE_NOTADB + && shouldEncrypt +#if defined(SQLITE_HAS_CODEC) + && !shouldReKey // re-key should never fail +#endif + ) + { + LOGWARN("SQLite database is encrypted, but the key doesn't work"); + term(); + if (!fileRemove(filename) || Utils::fileExists(filename)) + { + LOGERR("Can't remove file"); + return false; + } + rc = sqlite3_open(filename, &db); + term(); + if (rc || !Utils::fileExists(filename)) + { + LOGERR("Can't create file"); + return false; + } + LOGWARN("SQLite database has been reset, trying re-key"); + return init(filename, key); + } + + rc = sqlite3_exec(db, "CREATE TABLE if not exists item (" + "ns INTEGER," + "key TEXT," + "value TEXT," + "FOREIGN KEY(ns) REFERENCES namespace(id) ON DELETE CASCADE ON UPDATE NO ACTION," + "UNIQUE(ns,key) ON CONFLICT REPLACE" + ");", 0, 0, &errmsg); + if (rc != SQLITE_OK || errmsg) + { + if (errmsg) + { + LOGERR("%d : %s", rc, errmsg); + sqlite3_free(errmsg); + } + else + LOGERR("%d", rc); + } + + return true; + } + } // namespace Plugin +} // namespace WPEFramework diff --git a/PersistentStore/PersistentStore.h b/PersistentStore/PersistentStore.h new file mode 100644 index 0000000000..de94fa3e6c --- /dev/null +++ b/PersistentStore/PersistentStore.h @@ -0,0 +1,75 @@ +#pragma once + +#include "Module.h" +#include "utils.h" +#include "AbstractPlugin.h" + +#include +#include + +namespace WPEFramework { + + namespace Plugin { + + class PersistentStore : public AbstractPlugin { + public: + PersistentStore(); + virtual ~PersistentStore(); + virtual const string Initialize(PluginHost::IShell* service) override; + virtual void Deinitialize(PluginHost::IShell* service) override; + virtual string Information() const override; + + public/*members*/: + static PersistentStore* _instance; + + public /*constants*/: + static const short API_VERSION_NUMBER_MAJOR; + static const short API_VERSION_NUMBER_MINOR; + static const string SERVICE_NAME; + //methods + static const string METHOD_SET_VALUE; + static const string METHOD_GET_VALUE; + static const string METHOD_DELETE_KEY; + static const string METHOD_DELETE_NAMESPACE; + static const string METHOD_GET_KEYS; + static const string METHOD_GET_NAMESPACES; + static const string METHOD_GET_STORAGE_SIZE; + //events + static const string EVT_ON_STORAGE_EXCEEDED; + //other + static const char* STORE_NAME; + static const char* STORE_KEY; + static const int64_t MAX_SIZE_BYTES; + static const int64_t MAX_VALUE_SIZE_BYTES; + + private/*registered methods (wrappers)*/: + + //methods ("parameters" here is "params" from the curl request) + uint32_t setValueWrapper(const JsonObject& parameters, JsonObject& response); + uint32_t getValueWrapper(const JsonObject& parameters, JsonObject& response); + uint32_t deleteKeyWrapper(const JsonObject& parameters, JsonObject& response); + uint32_t deleteNamespaceWrapper(const JsonObject& parameters, JsonObject& response); + uint32_t getKeysWrapper(const JsonObject& parameters, JsonObject& response); + uint32_t getNamespacesWrapper(const JsonObject& parameters, JsonObject& response); + uint32_t getStorageSizeWrapper(const JsonObject& parameters, JsonObject& response); + + private/*internal methods*/: + PersistentStore(const PersistentStore&) = delete; + PersistentStore& operator=(const PersistentStore&) = delete; + + bool setValue(const string& ns, const string& key, const string& value); + bool getValue(const string& ns, const string& key, string& value); + bool deleteKey(const string& ns, const string& key); + bool deleteNamespace(const string& ns); + bool getKeys(const string& ns, std::vector& keys); + bool getNamespaces(std::vector& namespaces); + bool getStorageSize(std::map& namespaceSizes); + + void term(); + void vacuum(); + bool init(const char* filename, const char* key = nullptr); + + void* mData; + }; + } // namespace Plugin +} // namespace WPEFramework diff --git a/PersistentStore/README.md b/PersistentStore/README.md new file mode 100644 index 0000000000..327e5293d4 --- /dev/null +++ b/PersistentStore/README.md @@ -0,0 +1,35 @@ +----------------- +# PersistentStore + +## Versions +`org.rdk.PersistentStore.1` + +## Methods: +``` +curl -d '{"jsonrpc":"2.0","id":"3","method":"org.rdk.PersistentStore.1.setValue","params":{"namespace":"foo","key":"key1","value":"value1"}}' http://127.0.0.1:9998/jsonrpc +curl -d '{"jsonrpc":"2.0","id":"3","method":"org.rdk.PersistentStore.1.getValue","params":{"namespace":"foo","key":"key1"}}' http://127.0.0.1:9998/jsonrpc +curl -d '{"jsonrpc":"2.0","id":"3","method":"org.rdk.PersistentStore.1.deleteKey","params":{"namespace":"foo","key":"key1"}}' http://127.0.0.1:9998/jsonrpc +curl -d '{"jsonrpc":"2.0","id":"3","method":"org.rdk.PersistentStore.1.deleteNamespace","params":{"namespace":"foo"}}' http://127.0.0.1:9998/jsonrpc +curl -d '{"jsonrpc":"2.0","id":"3","method":"org.rdk.PersistentStore.1.getKeys","params":{"namespace":"foo"}}' http://127.0.0.1:9998/jsonrpc +curl -d '{"jsonrpc":"2.0","id":"3","method":"org.rdk.PersistentStore.1.getNamespaces","params":{}}' http://127.0.0.1:9998/jsonrpc +curl -d '{"jsonrpc":"2.0","id":"3","method":"org.rdk.PersistentStore.1.getStorageSize","params":{}}' http://127.0.0.1:9998/jsonrpc +``` + +## Responses +``` +{"jsonrpc":"2.0","id":3,"result":{"success":true}} +{"jsonrpc":"2.0","id":3,"result":{"value":"value1","success":true}} +{"jsonrpc":"2.0","id":3,"result":{"success":true}} +{"jsonrpc":"2.0","id":3,"result":{"success":true}} +{"jsonrpc":"2.0","id":3,"result":{"keys":["key1","key2","keyN"],"success":true}} +{"jsonrpc":"2.0","id":3,"result":{"namespaces":["ns1","ns2","nsN"],"success":true}} +{"jsonrpc":"2.0","id":3,"result":{"namespaceSizes":{"ns1":534,"ns2":234,"nsN":298},"success":true}} +``` + +## Events +``` +none +``` + +## Full Reference +https://etwiki.sys.comcast.net/display/RDK/PersistentStore diff --git a/cmake/FindDL.cmake b/cmake/FindDL.cmake new file mode 100644 index 0000000000..5e8d6c715f --- /dev/null +++ b/cmake/FindDL.cmake @@ -0,0 +1,5 @@ +find_package(PkgConfig) + +find_library(DL_LIBRARIES NAMES dl) + +mark_as_advanced(DL_LIBRARIES) diff --git a/cmake/FindPlabels.cmake b/cmake/FindPlabels.cmake new file mode 100644 index 0000000000..f679671f97 --- /dev/null +++ b/cmake/FindPlabels.cmake @@ -0,0 +1,12 @@ +find_package(PkgConfig) + +find_path(PLABELS_INCLUDE_DIRS NAMES pbnj_utils.hpp PATH_SUFFIXES pbnj_utils) + +find_library(PLABELS_LIBRARIES NAMES plabels) + +set(PLABELS_FLAGS -DUSE_PLABELS=1 -DRDKLOG_ERROR= -DRDKLOG_INFO= CACHE PATH "Flags for pbnj_utils") + +mark_as_advanced( + PLABELS_FLAGS + PLABELS_INCLUDE_DIRS + PLABELS_LIBRARIES) diff --git a/cmake/FindSqlite.cmake b/cmake/FindSqlite.cmake new file mode 100644 index 0000000000..af0e2591f6 --- /dev/null +++ b/cmake/FindSqlite.cmake @@ -0,0 +1,3 @@ +find_package(PkgConfig) + +pkg_search_module(SQLITE REQUIRED sqlite3) diff --git a/cmake/FindSqliteSee.cmake b/cmake/FindSqliteSee.cmake new file mode 100644 index 0000000000..a9baf61a10 --- /dev/null +++ b/cmake/FindSqliteSee.cmake @@ -0,0 +1,3 @@ +find_package(PkgConfig) + +pkg_search_module(SQLITE REQUIRED sqlite3see) From 9d03333e9d3e488668558e58c0f81ec58a77e212 Mon Sep 17 00:00:00 2001 From: Nikita Poltorapavlo Date: Mon, 29 Jun 2020 21:52:27 +0300 Subject: [PATCH 37/56] add credits for the code parts taken from pxCore --- NOTICE | 3 +++ PersistentStore/PersistentStore.cpp | 2 ++ 2 files changed, 5 insertions(+) diff --git a/NOTICE b/NOTICE index 5c1bd434c8..ec67a3c38e 100644 --- a/NOTICE +++ b/NOTICE @@ -9,3 +9,6 @@ within this component. Copyright (C) 2012 Raphael Kubo da Costa Licensed under the BSD-2 license + +Based on pxCore, Copyright 2015-2018 John Robinson +Licensed under the Apache License, Version 2.0 diff --git a/PersistentStore/PersistentStore.cpp b/PersistentStore/PersistentStore.cpp index ca14d181a0..0684fb8d73 100644 --- a/PersistentStore/PersistentStore.cpp +++ b/PersistentStore/PersistentStore.cpp @@ -605,6 +605,8 @@ namespace WPEFramework { return false; } + /* Based on pxCore, Copyright 2015-2018 John Robinson */ + /* Licensed under the Apache License, Version 2.0 */ if (shouldEncrypt) { #if defined(SQLITE_HAS_CODEC) From e9b8c010cce4af599a34cc11568f46707d4a64b7 Mon Sep 17 00:00:00 2001 From: mstrozyn Date: Tue, 30 Jun 2020 07:12:57 +0200 Subject: [PATCH 38/56] RDK-28967: Extend getDeviceInfo method in system thunder plugin Reason for change: Add support for hardwre id value Test Procedure: Build and test based on README.md Risks: Low Signed-off-by: Mariusz Strozynski --- SystemServices/SystemServices.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SystemServices/SystemServices.cpp b/SystemServices/SystemServices.cpp index a137e9af92..b36088516e 100644 --- a/SystemServices/SystemServices.cpp +++ b/SystemServices/SystemServices.cpp @@ -621,7 +621,7 @@ namespace WPEFramework { if (!parameter.compare(MODEL_NAME)) { param.type = mfrSERIALIZED_TYPE_MODELNAME; } else if (!parameter.compare(HARDWARE_ID)) { - param.type = mfrSERIALIZED_TYPE_SERIALNUMBER; + param.type = mfrSERIALIZED_TYPE_HWID; } IARM_Result_t result = IARM_Bus_Call(IARM_BUS_MFRLIB_NAME, IARM_BUS_MFRLIB_API_GetSerializedData, ¶m, sizeof(param)); param.buffer[param.bufLen] = '\0'; From 42dd2fe919563df7669819929ea863d59e72a481 Mon Sep 17 00:00:00 2001 From: Arun P Madhavan Date: Tue, 30 Jun 2020 15:16:44 +0000 Subject: [PATCH 39/56] DELIA-43983 : [Thunder]-System Plugin Issues Reason for change: Updated getCachedValue, setCachedValue, cacheContains, removeCacheKey, getLastDeepSleepReason, enableXREConnectionRetention. Test Procedure: Refer ticket Risks: Low Signed-off-by: Arun Madhavan --- SystemServices/SystemServices.cpp | 167 ++++++++++++++++-------------- 1 file changed, 90 insertions(+), 77 deletions(-) diff --git a/SystemServices/SystemServices.cpp b/SystemServices/SystemServices.cpp index 78d51bf4ac..34adce5e22 100644 --- a/SystemServices/SystemServices.cpp +++ b/SystemServices/SystemServices.cpp @@ -1603,25 +1603,29 @@ namespace WPEFramework { /*** * @brief : To get cashed value . - * @param1[in] : {"params":{"param":{"cacheKey":""}}} + * @param1[in] : {"params":{"key":""}} * @param2[out] : {"result":{"":"","success":}} * @return : Core:: */ uint32_t SystemServices::getCachedValue(const JsonObject& parameters, - JsonObject& response) - { - JsonObject param; - param.FromString(parameters["param"].String()); - std::string key = param["cacheKey"].String(); - LOGWARN("key: %s\n", key.c_str()); - response[(key.c_str())] = m_cacheService.getValue(key).String(); - returnResponse(true); - } + JsonObject& response) + { + bool retStat = false; + std::string key = parameters["key"].String(); + LOGWARN("key: %s\n", key.c_str()); + if (key.length()) { + response[(key.c_str())] = m_cacheService.getValue(key).String(); + retStat = true; + } else { + populateResponseWithError(SysSrv_UnSupportedFormat, response); + } + returnResponse(retStat); + } /*** * @brief : To set cache value. - * @param1[in] : {"params":{"param":{"cacheKey":"", - * "cacheValue":}}} + * @param1[in] : {"params":{"key":"", + * "cacheValue":}} * @param2[out] : {"jsonrpc":"2.0","id":3,"result":{"success":}} * @return : Core:: */ @@ -1629,59 +1633,66 @@ namespace WPEFramework { JsonObject& response) { bool retStat = false; - JsonObject param; - param.FromString(parameters["param"].String()); - std::string key = param["cacheKey"].String(); - std::string value = param["cacheValue"].String(); + std::string key = parameters["key"].String(); + std::string value = parameters["value"].String(); - if (m_cacheService.setValue(key, value)) { - retStat = true; - } else { - LOGERR("Accessing m_cacheService.setValue failed\n."); - } - returnResponse(retStat); + if (key.length() && value.length()) { + if (m_cacheService.setValue(key, value)) { + retStat = true; + } else { + LOGERR("Accessing m_cacheService.setValue failed\n."); + } + } else { + populateResponseWithError(SysSrv_UnSupportedFormat, response); + } + returnResponse(retStat); } /*** * @brief : To check if key value present in cache. - * @param1[in] : {"params":{"param":{"cacheKey":""}}} + * @param1[in] : {"params":{"key":""}} * @param2[out] : {"jsonrpc":"2.0","id":3,"result":{"success":}} * @return : Core:: */ uint32_t SystemServices::cacheContains(const JsonObject& parameters, JsonObject& response) { - bool retStat = false; - JsonObject param; - param.FromString(parameters["param"].String()); - std::string key = param["cacheKey"].String(); - if (m_cacheService.contains(key)) { - retStat = true; - } else { - LOGERR("Accessing m_cacheService.contains failed\n."); - } - returnResponse(retStat); + bool retStat = false; + std::string key = parameters["key"].String(); + if (key.length()) { + if (m_cacheService.contains(key)) { + retStat = true; + } else { + LOGERR("Accessing m_cacheService.contains failed\n."); + } + } else { + populateResponseWithError(SysSrv_UnSupportedFormat, response); + } + returnResponse(retStat); } /*** * @brief : To delete the key value present in cache. - * @param1[in] : {"params":{"param":{"cacheKey":""}}} + * @param1[in] : {"params":{"key":""}} * @param2[out] : {"jsonrpc":"2.0","id":3,"result":{"success":}} * @return : Core:: */ uint32_t SystemServices::removeCacheKey(const JsonObject& parameters, JsonObject& response) { - bool retStat = false; - JsonObject param; - param.FromString(parameters["param"].String()); - std::string key = param["cacheKey"].String(); - if (m_cacheService.remove(key)) { - retStat = true; - } else { - LOGERR("Accessing m_cacheService.remove failed\n."); - } - returnResponse(retStat); + bool retStat = false; + std::string key = parameters["key"].String(); + if (key.length()) { + if (m_cacheService.remove(key)) { + retStat = true; + } else { + LOGERR("Accessing m_cacheService.remove failed\n."); + populateResponseWithError(SysSrv_Unexpected, response); + } + } else { + populateResponseWithError(SysSrv_UnSupportedFormat, response); + } + returnResponse(retStat); } /*** @@ -1750,28 +1761,30 @@ namespace WPEFramework { */ uint32_t SystemServices::getLastDeepSleepReason(const JsonObject& parameters, JsonObject& response) - { - bool retAPIStatus = false; - string reason; - - if (Utils::fileExists(STANDBY_REASON_FILE)) { - std::ifstream inFile(STANDBY_REASON_FILE); - if (inFile) { - std::getline(inFile, reason); - inFile.close(); - retAPIStatus = true; - } else { - populateResponseWithError(SysSrv_FileAccessFailed, response); - } - } else { - populateResponseWithError(SysSrv_FileNotPresent, response); - } + { + bool retAPIStatus = false; + string reason; + + if (Utils::fileExists(STANDBY_REASON_FILE)) { + std::ifstream inFile(STANDBY_REASON_FILE); + if (inFile) { + std::getline(inFile, reason); + inFile.close(); + retAPIStatus = true; + } else { + populateResponseWithError(SysSrv_FileAccessFailed, response); + } + } else { + populateResponseWithError(SysSrv_FileNotPresent, response); + } - if (retAPIStatus && reason.length()) { - response["lastDeepSleepReason"] = reason; - } - returnResponse(retAPIStatus); - } + if (retAPIStatus && reason.length()) { + response["reason"] = reason; + } else { + response["reason"] = ""; + } + returnResponse(retAPIStatus); + } /*** * @brief : Used to clear last deep sleep reason. @@ -2039,24 +2052,24 @@ namespace WPEFramework { /*** * @brief : Enables XRE Connection Retension option. - * @param1[in] : {"params":{"param":}} + * @param1[in] : {"params":{"enable":}} * @param2[out] : "result":{"success":} * @return : Core:: */ uint32_t SystemServices::enableXREConnectionRetention(const JsonObject& parameters, JsonObject& response) - { - bool enable = false, retstatus = false; - int status = SysSrv_Unexpected; + { + bool enable = false, retstatus = false; + int status = SysSrv_Unexpected; - enable = parameters["param"].Boolean(); - if ((status = enableXREConnectionRetentionHelper(enable)) == SysSrv_OK) { - retstatus = true; - } else { - populateResponseWithError(status, response); - } - returnResponse(retstatus); - } + enable = parameters["enable"].Boolean(); + if ((status = enableXREConnectionRetentionHelper(enable)) == SysSrv_OK) { + retstatus = true; + } else { + populateResponseWithError(status, response); + } + returnResponse(retstatus); + } /*** * @brief : collect device state info. From 781d49eedc6f9ed510f86ff350f8493e27191fef Mon Sep 17 00:00:00 2001 From: Arun P Madhavan Date: Tue, 30 Jun 2020 15:30:43 +0000 Subject: [PATCH 40/56] DELIA-43983 : [Thunder]-System Plugin Issues Reason for change: Updated README and examples. Test Procedure: Refer ticket Risks: None Signed-off-by: Arun Madhavan --- SystemServices/README.md | 18 +++++++++--------- SystemServices/SystemServices.cpp | 3 +-- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/SystemServices/README.md b/SystemServices/README.md index 9e8271af7c..c0472d0764 100644 --- a/SystemServices/README.md +++ b/SystemServices/README.md @@ -15,7 +15,7 @@ - **cacheContains** To check if key value present in cache. - _**Request payload:**_ `{"params":{"param":{"cacheKey":""}}}` + _**Request payload:**_ `{"params":{"key":""}}` _**Response payload:**_ `{"result":{"success":}}` - **clearLastDeepSleepReason** @@ -40,7 +40,7 @@ - **getCachedValue** To get cached value. - _**Request payload:**_ `{"params":{"param":{"cacheKey":""}}}` + _**Request payload:**_ `{"params":{"key":""}}` _**Response payload:**_ `{"result":{"":"","success":}}` - **getCoreTemperature** @@ -170,12 +170,12 @@ - **reboot** This method shall be used to request the system to perform `reboot`. - _**Request payload:**_ `{"params":{"param":{"cacheKey":"sampleCache"}}}` + _**Request payload:**_ `{"params":{"key":"sampleCache"}}` _**Response payload:**_ `{"result":{"success":}}` - **removeCacheKey** To delete the key value present in cache. - _**Request payload:**_ `{"params":{"param":{"cacheKey":""}}}` + _**Request payload:**_ `{"params":{"key":""}}` _**Response payload:**_ `{"result":{"success":}}` - **requestSystemUptime** @@ -185,7 +185,7 @@ - **setCachedValue** To set cache value. - _**Request payload:**_ `{"params":{"param":{"cacheKey":"","cacheValue":}}}` + _**Request payload:**_ `{"params":{"key":"","value":}}` _**Response payload:**_ `{"result":{"success":}}` - **setDeepSleepTimer** @@ -266,12 +266,12 @@ _Note:_ Here callsign used is `org.rdk.SystemServices` instead of actual `org.rd Method | Request Payload | Response Payload :--- | :--- | :--- -| cacheContains | {"jsonrpc":"2.0","id":"1","method":"org.rdk.SystemServices.1.cacheContains","params":{"param":{"cacheKey":"sampleCache"}}} | {"jsonrpc":"2.0","id":1,"result":{"success":true}} | +| cacheContains | {"jsonrpc":"2.0","id":"1","method":"org.rdk.SystemServices.1.cacheContains","params":{"key":"sampleCache"}} | {"jsonrpc":"2.0","id":1,"result":{"success":true}} | | clearLastDeepSleepReason | {"jsonrpc":"2.0","id":"29","method":"org.rdk.SystemServices.1.clearLastDeepSleepReason","params":{}} | {"jsonrpc":"2.0","id":29,"result":{"success":true}} | | enableMoca | {"jsonrpc":"2.0","id":"30","method": "org.rdk.SystemServices.1.enableMoca","params":{"value":false}} | {"jsonrpc":"2.0","id":30,"result":{"success":false}} | | enableXREConnectionRetention | {"jsonrpc":"2.0","id":"32","method":"org.rdk.SystemServices.1.enableXREConnectionRetention","params":{"param":true}} | {"jsonrpc":"2.0","id":32,"result":{"success":true}} | | getAvailableStandbyModes | {"jsonrpc":"2.0","id":"2","method":"org.rdk.SystemServices.1.getAvailableStandbyModes","params":{}} | {"jsonrpc":"2.0","id":2,"result":{"supportedStandbyModes":["LIGHT_SLEEP","DEEP_SLEEP"],"success":true}} | -| getCachedValue | {"jsonrpc":"2.0","id":"3","method":"org.rdk.SystemServices.1.getCachedValue","params":{"param":{"cacheKey":"sampleCache"}}} | {"jsonrpc":"2.0","id":3,"result":{"sampleCache":"4343.3434","success":true}} | +| getCachedValue | {"jsonrpc":"2.0","id":"3","method":"org.rdk.SystemServices.1.getCachedValue","params":{"key":"sampleCache"}} | {"jsonrpc":"2.0","id":3,"result":{"sampleCache":"4343.3434","success":true}} | | getCoreTemperature | {"jsonrpc":"2.0","id":"4","method":"org.rdk.SystemServices.1.getCoreTemperature","params":{}} | {"jsonrpc":"2.0","id":4,"result":{"temperature":"48.000000","success":true}} | | getDeviceInfo | {"jsonrpc":"2.0","id":"5","method":"org.rdk.SystemServices.1.getDeviceInfo","params":{"params":["estb_mac"]}} | {"jsonrpc":"2.0","id":5,"result":{"estb_mac":"20:F1:9E:EE:62:08","success":true}} | | getDownloadedFirmwareInfo | {"jsonrpc":"2.0","id":"6","method":"org.rdk.SystemServices.1.getDownloadedFirmwareInfo","params":{}} | {"jsonrpc":"2.0","id":6,"result":{"currentFWVersion":"AX061AEI_VBN_1911_sprint_20200109040424sdy","downloadedFWVersion":"","downloadedFWLocation":"","isRebootDeferred":false,"success":true}} | @@ -298,9 +298,9 @@ Method | Request Payload | Response Payload | isGzEnabled | {"jsonrpc":"2.0","id":"26","method":"org.rdk.SystemServices.1.isGzEnabled","params":{}} | {"jsonrpc":"2.0","id":26,"result":{"enabled":false,"success":true}} | | queryMocaStatus | {"jsonrpc":"2.0","id":"27","method": "org.rdk.SystemServices.1.queryMocaStatus","params":{}} | {"jsonrpc":"2.0","id":27,"result":{"mocaEnabled":false,"success":true}} | | reboot | {"jsonrpc":"2.0","id":"100","method":"org.rdk.SystemServices.1.reboot","params":{"params":{"reason":"API Validation"}}} | {"jsonrpc":"2.0","id":3,"result":{"IARM_Bus_Call_STATUS":1,"success":true}} | -| removeCacheKey | {"jsonrpc":"2.0","id":"34","method":"org.rdk.SystemServices.1.removeCacheKey","params":{"param":{"cacheKey":"sampleCache"}}} | {"jsonrpc":"2.0","id":34,"result":{"success":true}} | +| removeCacheKey | {"jsonrpc":"2.0","id":"34","method":"org.rdk.SystemServices.1.removeCacheKey","params":{"key":"sampleCache"}} | {"jsonrpc":"2.0","id":34,"result":{"success":true}} | | requestSystemUptime | {"jsonrpc":"2.0","id":"28","method":"org.rdk.SystemServices.1.requestSystemUptime","params":{}} | {"jsonrpc":"2.0","id":28,"result":{"systemUptime":"1666.92","success":true}} | -| setCachedValue | {"jsonrpc":"2.0","id":"33","method":"org.rdk.SystemServices.1.setCachedValue","params":{"param":{"cacheKey":"sampleCache","cacheValue":4343.3434}}} | {"jsonrpc":"2.0","id":33,"result":{"success":true}} | +| setCachedValue | {"jsonrpc":"2.0","id":"33","method":"org.rdk.SystemServices.1.setCachedValue","params":{"key":"sampleCache","value":4343.3434}} | {"jsonrpc":"2.0","id":33,"result":{"success":true}} | | setDeepSleepTimer | {"jsonrpc":"2.0","id":"35","method":"org.rdk.SystemServices.1.setDeepSleepTimer","params":{"param":{"seconds":3}}} | {"jsonrpc":"2.0","id":35,"result":{"success":true}} | | setGzEnabled | {"jsonrpc":"2.0","id":"36","method":"org.rdk.SystemServices.1.setGzEnabled","params":{"param":true}} | {"jsonrpc":"2.0","id":36,"result":{"success":true}} | | setMode | {"jsonrpc":"2.0","id":"37","method":"org.rdk.SystemServices.1.setMode","params":{"modeInfo":{"mode":"NORMAL","duration":20}}} | {"jsonrpc":"2.0","id":37,"result":{"success":true}} | diff --git a/SystemServices/SystemServices.cpp b/SystemServices/SystemServices.cpp index 34adce5e22..437a07d64e 100644 --- a/SystemServices/SystemServices.cpp +++ b/SystemServices/SystemServices.cpp @@ -1624,8 +1624,7 @@ namespace WPEFramework { /*** * @brief : To set cache value. - * @param1[in] : {"params":{"key":"", - * "cacheValue":}} + * @param1[in] : {"params":{"key":"","value":}} * @param2[out] : {"jsonrpc":"2.0","id":3,"result":{"success":}} * @return : Core:: */ From 0cc6052cda48a022f752d38d8c1308f2a788e4a2 Mon Sep 17 00:00:00 2001 From: Arun P Madhavan Date: Tue, 30 Jun 2020 15:35:36 +0000 Subject: [PATCH 41/56] DELIA-43983 : [Thunder]-System Plugin Issues Reason for change: Added error status in Cache related APIs. Test Procedure: Refer ticket Risks: None Signed-off-by: Arun Madhavan --- SystemServices/SystemServices.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/SystemServices/SystemServices.cpp b/SystemServices/SystemServices.cpp index 437a07d64e..1ecd320ad5 100644 --- a/SystemServices/SystemServices.cpp +++ b/SystemServices/SystemServices.cpp @@ -1640,6 +1640,7 @@ namespace WPEFramework { retStat = true; } else { LOGERR("Accessing m_cacheService.setValue failed\n."); + populateResponseWithError(SysSrv_Unexpected, response); } } else { populateResponseWithError(SysSrv_UnSupportedFormat, response); @@ -1663,6 +1664,7 @@ namespace WPEFramework { retStat = true; } else { LOGERR("Accessing m_cacheService.contains failed\n."); + populateResponseWithError(SysSrv_Unexpected, response); } } else { populateResponseWithError(SysSrv_UnSupportedFormat, response); From 53b7023683e13fe73695db825405f396d6cc6e22 Mon Sep 17 00:00:00 2001 From: Arun P Madhavan Date: Tue, 30 Jun 2020 17:02:44 +0000 Subject: [PATCH 42/56] setPowerState: Fixed issue with fd's being left open in error state. --- SystemServices/SystemServices.cpp | 92 +++++++++++++------------------ 1 file changed, 39 insertions(+), 53 deletions(-) diff --git a/SystemServices/SystemServices.cpp b/SystemServices/SystemServices.cpp index 1ecd320ad5..76c6dc3111 100644 --- a/SystemServices/SystemServices.cpp +++ b/SystemServices/SystemServices.cpp @@ -2217,59 +2217,45 @@ namespace WPEFramework { */ uint32_t SystemServices::setDevicePowerState(const JsonObject& parameters, JsonObject& response) - { - bool retVal = false; - string sleepMode; - ofstream outfile; - outfile.open(STANDBY_REASON_FILE, ios::out); - JsonObject paramIn, paramOut; - string state = parameters["powerState"].String(); - string reason = parameters["standbyReason"].String(); - - - if(state=="STANDBY") - { - - - if (SystemServices::_instance) { - SystemServices::_instance->getPreferredStandbyMode(paramIn, paramOut); - /* TODO: parse abd get the sleepMode from paramOut */ - sleepMode= paramOut["preferredStandbyMode"].String(); - - - LOGWARN("Output of preferredStandbyMode: '%s'", sleepMode.c_str()); - - } - else { - LOGWARN("SystemServices::_instance is NULL.\n"); - } - if(convert("DEEP_SLEEP",sleepMode)) - { - - retVal = CPowerState::instance()->setPowerState(sleepMode); - } - else{ - - retVal = CPowerState::instance()->setPowerState(state); - } - - - if (outfile) { - outfile << reason; - outfile.close(); - - } - else { - printf(" GZ_FILE_ERROR: Can't open file for write mode\n"); - } - - } - else { - retVal = CPowerState::instance()->setPowerState(state); - LOGERR("this platform has no API System and/or Powerstate\n"); - } - returnResponse(retVal); - }//end of setPower State + { + bool retVal = false; + string sleepMode; + ofstream outfile; + JsonObject paramIn, paramOut; + string state = parameters["powerState"].String(); + string reason = parameters["standbyReason"].String(); + /* Power state defaults standbyReason is "application". */ + reason = ((reason.length()) ? reason : "application"); + + if (state == "STANDBY") { + if (SystemServices::_instance) { + SystemServices::_instance->getPreferredStandbyMode(paramIn, paramOut); + /* TODO: parse abd get the sleepMode from paramOut */ + sleepMode= paramOut["preferredStandbyMode"].String(); + LOGWARN("Output of preferredStandbyMode: '%s'", sleepMode.c_str()); + } else { + LOGWARN("SystemServices::_instance is NULL.\n"); + } + if (convert("DEEP_SLEEP", sleepMode)) { + retVal = CPowerState::instance()->setPowerState(sleepMode); + } else { + retVal = CPowerState::instance()->setPowerState(state); + } + if (outfile) { + outfile.open(STANDBY_REASON_FILE, ios::out); + if (outfile.is_open()) { + outfile << reason; + outfile.close(); + } + } else { + printf(" GZ_FILE_ERROR: Can't open file for write mode\n"); + } + } else { + retVal = CPowerState::instance()->setPowerState(state); + LOGERR("this platform has no API System and/or Powerstate\n"); + } + returnResponse(retVal); + }//end of setPower State #endif /* HAS_API_SYSTEM && HAS_API_POWERSTATE */ /*** From a1b7ea30f6cfcc47d16d088b75ac38f2f1093d87 Mon Sep 17 00:00:00 2001 From: Sergey Borushevsky Date: Tue, 30 Jun 2020 19:17:46 +0000 Subject: [PATCH 43/56] DELIA-44135 : CEC functionality is failed/not working on latest sprint build. Used fixed copy of Core::FromString, which doesn't add exrta zero to the buffer after base64 decoding. --- HdmiCec/HdmiCec.cpp | 61 +++++++++++++++++++++++++++++++++++++++++++-- HdmiCec/HdmiCec.h | 2 ++ 2 files changed, 61 insertions(+), 2 deletions(-) diff --git a/HdmiCec/HdmiCec.cpp b/HdmiCec/HdmiCec.cpp index 8cecae617a..6fbe9f16d7 100644 --- a/HdmiCec/HdmiCec.cpp +++ b/HdmiCec/HdmiCec.cpp @@ -525,6 +525,62 @@ namespace WPEFramework return CECAddress; } + // Copy of Core::FromString, which doesn't add extra zero at the end + uint16_t HdmiCec::FromBase64String(const string& newValue, uint8_t object[], uint16_t& length, const TCHAR* ignoreList) + { + uint8_t state = 0; + uint16_t index = 0; + uint16_t filler = 0; + uint8_t lastStuff = 0; + + while ((index < newValue.size()) && (filler < length)) { + uint8_t converted; + TCHAR current = newValue[index++]; + + if ((current >= 'A') && (current <= 'Z')) { + converted = (current - 'A'); + } else if ((current >= 'a') && (current <= 'z')) { + converted = (current - 'a' + 26); + } else if ((current >= '0') && (current <= '9')) { + converted = (current - '0' + 52); + } else if (current == '+') { + converted = 62; + } else if (current == '/') { + converted = 63; + } else if ((ignoreList != nullptr) && (::strchr(ignoreList, current) != nullptr)) { + continue; + } else { + break; + } + + if (state == 0) { + lastStuff = converted << 2; + state = 1; + } else if (state == 1) { + object[filler++] = (((converted & 0x30) >> 4) | lastStuff); + lastStuff = ((converted & 0x0F) << 4); + state = 2; + } else if (state == 2) { + object[filler++] = (((converted & 0x3C) >> 2) | lastStuff); + lastStuff = ((converted & 0x03) << 6); + state = 3; + } else if (state == 3) { + object[filler++] = ((converted & 0x3F) | lastStuff); + state = 0; + } + } + + // No need to do this + /*if ((state != 0) && (filler < length)) { + object[filler++] = lastStuff; + LOGINFO("-=========== state %d, lastStuff = %d", state, lastStuff); + }*/ + + length = filler; + + return (index); + } + void HdmiCec::sendMessage(std::string message) { LOGINFO("sendMessage "); @@ -534,14 +590,15 @@ namespace WPEFramework std::vector buf; buf.resize(message.size()); - uint16_t decodedLen = Core::URL::Base64Decode(message.c_str(), message.size(), (uint8_t*)buf.data(), buf.size()); + uint16_t decodedLen = message.size(); + FromBase64String(message, (uint8_t*)buf.data(), decodedLen, NULL); + CECFrame frame = CECFrame((const uint8_t *)buf.data(), decodedLen); // SVCLOG_WARN("Frame to be sent from servicemanager in %s \n",__FUNCTION__); // frame.hexDump(); smConnection->sendAsync(frame); } else - LOGWARN("cecEnableStatus=false"); return; } diff --git a/HdmiCec/HdmiCec.h b/HdmiCec/HdmiCec.h index d0ce240e10..53d4eca64c 100644 --- a/HdmiCec/HdmiCec.h +++ b/HdmiCec/HdmiCec.h @@ -96,6 +96,8 @@ namespace WPEFramework { void setName(std::string name); std::string getName(); JsonObject getCECAddresses(); + + uint16_t FromBase64String(const string& newValue, uint8_t object[], uint16_t& length, const TCHAR* ignoreList); void sendMessage(std::string message); void cecAddressesChanged(int changeStatus); From d9d02b5973d8df05d997f1c721938eb21e264c2f Mon Sep 17 00:00:00 2001 From: mfiess200 Date: Tue, 30 Jun 2020 18:00:16 -0400 Subject: [PATCH 44/56] support for launching native applications and suspend and resume (RDK-28720) --- RDKShell/RDKShell.cpp | 121 +++++++++++++++++++++++++++++++++++++++++- RDKShell/RDKShell.h | 7 +++ 2 files changed, 127 insertions(+), 1 deletion(-) diff --git a/RDKShell/RDKShell.cpp b/RDKShell/RDKShell.cpp index b2401bd6e9..00fae09c97 100755 --- a/RDKShell/RDKShell.cpp +++ b/RDKShell/RDKShell.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include "rfcapi.h" const short WPEFramework::Plugin::RDKShell::API_VERSION_NUMBER_MAJOR = 1; @@ -59,12 +60,18 @@ const string WPEFramework::Plugin::RDKShell::RDKSHELL_METHOD_ADD_ANIMATION = "ad const string WPEFramework::Plugin::RDKShell::RDKSHELL_METHOD_ENABLE_INACTIVITY_REPORTING = "enableInactivityReporting"; const string WPEFramework::Plugin::RDKShell::RDKSHELL_METHOD_SET_INACTIVITY_INTERVAL = "setInactivityInterval"; const string WPEFramework::Plugin::RDKShell::RDKSHELL_METHOD_SCALE_TO_FIT = "scaleToFit"; +const string WPEFramework::Plugin::RDKShell::RDKSHELL_METHOD_LAUNCH_APPLICATION = "launchApplication"; +const string WPEFramework::Plugin::RDKShell::RDKSHELL_METHOD_SUSPEND_APPLICATION = "suspendApplication"; +const string WPEFramework::Plugin::RDKShell::RDKSHELL_METHOD_RESUME_APPLICATION = "resumeApplication"; const string WPEFramework::Plugin::RDKShell::RDKSHELL_EVENT_ON_USER_INACTIVITY = "onUserInactivity"; using namespace std; using namespace RdkShell; extern int gCurrentFramerate; +bool receivedResolutionRequest = false; +unsigned int resolutionWidth = 1280; +unsigned int resolutionHeight = 720; #define ANY_KEY 65536 @@ -196,6 +203,9 @@ namespace WPEFramework { registerMethod(RDKSHELL_METHOD_ENABLE_INACTIVITY_REPORTING, &RDKShell::enableInactivityReportingWrapper, this); registerMethod(RDKSHELL_METHOD_SET_INACTIVITY_INTERVAL, &RDKShell::setInactivityIntervalWrapper, this); registerMethod(RDKSHELL_METHOD_SCALE_TO_FIT, &RDKShell::scaleToFitWrapper, this); + registerMethod(RDKSHELL_METHOD_LAUNCH_APPLICATION, &RDKShell::launchApplicationWrapper, this); + registerMethod(RDKSHELL_METHOD_SUSPEND_APPLICATION, &RDKShell::suspendApplicationWrapper, this); + registerMethod(RDKSHELL_METHOD_RESUME_APPLICATION, &RDKShell::resumeApplicationWrapper, this); } RDKShell::~RDKShell() @@ -233,6 +243,11 @@ namespace WPEFramework { const double maxSleepTime = (1000 / gCurrentFramerate) * 1000; double startFrameTime = RdkShell::microseconds(); gRdkShellMutex.lock(); + if (receivedResolutionRequest) + { + CompositorController::setScreenResolution(resolutionWidth, resolutionHeight); + receivedResolutionRequest = false; + } RdkShell::draw(); RdkShell::update(); gRdkShellMutex.unlock(); @@ -1066,6 +1081,108 @@ namespace WPEFramework { returnResponse(result); } + uint32_t RDKShell::launchApplicationWrapper(const JsonObject& parameters, JsonObject& response) + { + LOGINFOMETHOD(); + bool result = true; + if (!parameters.HasLabel("client")) + { + result = false; + response["message"] = "please specify client"; + } + if (!parameters.HasLabel("uri")) + { + result = false; + response["message"] = "please specify uri"; + } + if (!parameters.HasLabel("mimeType")) + { + result = false; + response["message"] = "please specify mimeType"; + } + if (result) + { + const string client = parameters["client"].String(); + const string uri = parameters["uri"].String(); + const string mimeType = parameters["mimeType"].String(); + + gRdkShellMutex.lock(); + result = CompositorController::launchApplication(client, uri, mimeType); + gRdkShellMutex.unlock(); + + if (!result) { + response["message"] = "failed to launch application"; + } + } + returnResponse(result); + } + + uint32_t RDKShell::suspendApplicationWrapper(const JsonObject& parameters, JsonObject& response) + { + LOGINFOMETHOD(); + bool result = true; + if (!parameters.HasLabel("client")) + { + result = false; + response["message"] = "please specify client"; + } + if (result) + { + const string client = parameters["client"].String(); + std::string mimeType; + + gRdkShellMutex.lock(); + result = CompositorController::getMimeType(client, mimeType); + if (result && mimeType == RDKSHELL_APPLICATION_MIME_TYPE_NATIVE) + { + result = CompositorController::suspendApplication(client); + } + gRdkShellMutex.unlock(); + if (mimeType != RDKSHELL_APPLICATION_MIME_TYPE_NATIVE) + { + } + setVisibility(client, false); + + if (!result) { + response["message"] = "failed to suspend application"; + } + } + returnResponse(result); + } + + uint32_t RDKShell::resumeApplicationWrapper(const JsonObject& parameters, JsonObject& response) + { + LOGINFOMETHOD(); + bool result = true; + if (!parameters.HasLabel("client")) + { + result = false; + response["message"] = "please specify client"; + } + if (result) + { + const string client = parameters["client"].String(); + std::string mimeType; + + gRdkShellMutex.lock(); + result = CompositorController::getMimeType(client, mimeType); + if (result && mimeType == RDKSHELL_APPLICATION_MIME_TYPE_NATIVE) + { + result = CompositorController::resumeApplication(client); + } + gRdkShellMutex.unlock(); + if (mimeType != RDKSHELL_APPLICATION_MIME_TYPE_NATIVE) + { + } + setVisibility(client, true); + + if (!result) { + response["message"] = "failed to resume application"; + } + } + returnResponse(result); + } + // Registered methods begin // Events begin @@ -1249,7 +1366,9 @@ namespace WPEFramework { { bool ret = false; gRdkShellMutex.lock(); - ret = CompositorController::setScreenResolution(w, h); + receivedResolutionRequest = true; + resolutionWidth = w; + resolutionHeight = h; gRdkShellMutex.unlock(); return ret; } diff --git a/RDKShell/RDKShell.h b/RDKShell/RDKShell.h index ff775ad1df..235543950c 100755 --- a/RDKShell/RDKShell.h +++ b/RDKShell/RDKShell.h @@ -78,6 +78,10 @@ namespace WPEFramework { static const string RDKSHELL_METHOD_ENABLE_INACTIVITY_REPORTING; static const string RDKSHELL_METHOD_SET_INACTIVITY_INTERVAL; static const string RDKSHELL_METHOD_SCALE_TO_FIT; + static const string RDKSHELL_METHOD_LAUNCH_APPLICATION; + static const string RDKSHELL_METHOD_SUSPEND_APPLICATION; + static const string RDKSHELL_METHOD_RESUME_APPLICATION; + static const string RDKSHELL_METHOD_CLOSE_APPLICATION; // events static const string RDKSHELL_EVENT_ON_USER_INACTIVITY; @@ -116,6 +120,9 @@ namespace WPEFramework { uint32_t enableInactivityReportingWrapper(const JsonObject& parameters, JsonObject& response); uint32_t setInactivityIntervalWrapper(const JsonObject& parameters, JsonObject& response); uint32_t scaleToFitWrapper(const JsonObject& parameters, JsonObject& response); + uint32_t launchApplicationWrapper(const JsonObject& parameters, JsonObject& response); + uint32_t suspendApplicationWrapper(const JsonObject& parameters, JsonObject& response); + uint32_t resumeApplicationWrapper(const JsonObject& parameters, JsonObject& response); void notify(const std::string& event, const JsonObject& parameters); private/*internal methods*/: From 36c8899d625f0beb2fc0e5a3821b84ac1cee42e2 Mon Sep 17 00:00:00 2001 From: mstrozyn Date: Wed, 1 Jul 2020 07:41:50 +0200 Subject: [PATCH 45/56] RDK-28967: Extend getDeviceInfo method in system thunder plugin Reason for change: Add support for platform switch Test Procedure: Build and test based on README.md Risks: Low Signed-off-by: Mariusz Strozynski --- realtek.cmake | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/realtek.cmake b/realtek.cmake index c1c28949cf..fab3d63386 100644 --- a/realtek.cmake +++ b/realtek.cmake @@ -77,6 +77,10 @@ add_definitions (-DBUILD_ENABLE_THERMAL_PROTECTION) add_definitions (-DENABLE_THERMAL_PROTECTION) endif() +if (BUILD_ENABLE_DEVICE_MANUFACTURER_INFO) + message("Building with device manufacturer info") + add_definitions (-DENABLE_DEVICE_MANUFACTURER_INFO) +endif() if (BUILD_XI1) message("Building for XI1") @@ -122,9 +126,6 @@ if (BUILD_XI1) add_definitions (-DENABLE_VREX_SERVICE) add_definitions (-DRF4CE_API) add_definitions (-DHAS_STATE_OBSERVER) - - message("Building with device manufacturer info") - add_definitions (-DENABLE_DEVICE_MANUFACTURER_INFO) endif() if(SCREEN_CAPTURE) From b799e68e9ac689524a048ef669aa3f08911367df Mon Sep 17 00:00:00 2001 From: Arun P Madhavan Date: Wed, 1 Jul 2020 14:30:04 +0000 Subject: [PATCH 46/56] getFirmwareDownloadPercent: updated the percentage to match with SM. --- SystemServices/SystemServices.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SystemServices/SystemServices.cpp b/SystemServices/SystemServices.cpp index 76c6dc3111..350d95f103 100644 --- a/SystemServices/SystemServices.cpp +++ b/SystemServices/SystemServices.cpp @@ -1259,7 +1259,7 @@ namespace WPEFramework { response["downloadPercent"] = m_downloadPercent; retStatus = true; } else { - response["downloadPercent"] = 0; + response["downloadPercent"] = -1; retStatus = true; } returnResponse(retStatus); From d9aed4e85327c143ecad29a56b8134e4ce4470b9 Mon Sep 17 00:00:00 2001 From: mfiess200 Date: Wed, 1 Jul 2020 18:25:05 -0400 Subject: [PATCH 47/56] suspend and resume updates --- RDKShell/RDKShell.cpp | 139 +++++++++++++++++++++++++++++++++++++++++- RDKShell/RDKShell.h | 9 +++ 2 files changed, 145 insertions(+), 3 deletions(-) diff --git a/RDKShell/RDKShell.cpp b/RDKShell/RDKShell.cpp index 00fae09c97..e1949466ec 100755 --- a/RDKShell/RDKShell.cpp +++ b/RDKShell/RDKShell.cpp @@ -65,6 +65,13 @@ const string WPEFramework::Plugin::RDKShell::RDKSHELL_METHOD_SUSPEND_APPLICATION const string WPEFramework::Plugin::RDKShell::RDKSHELL_METHOD_RESUME_APPLICATION = "resumeApplication"; const string WPEFramework::Plugin::RDKShell::RDKSHELL_EVENT_ON_USER_INACTIVITY = "onUserInactivity"; +const string WPEFramework::Plugin::RDKShell::RDKSHELL_EVENT_ON_APP_LAUNCHED = "onApplicationLaunched"; +const string WPEFramework::Plugin::RDKShell::RDKSHELL_EVENT_ON_APP_CONNECTED = "onApplicationConnected"; +const string WPEFramework::Plugin::RDKShell::RDKSHELL_EVENT_ON_APP_DISCONNECTED = "onApplicationDisconnected"; +const string WPEFramework::Plugin::RDKShell::RDKSHELL_EVENT_ON_APP_TERMINATED = "onApplicationTerminated"; +const string WPEFramework::Plugin::RDKShell::RDKSHELL_EVENT_ON_APP_FIRST_FRAME = "onApplicationFirstFrame"; +const string WPEFramework::Plugin::RDKShell::RDKSHELL_EVENT_ON_APP_SUSPENDED = "onApplicationSuspended"; +const string WPEFramework::Plugin::RDKShell::RDKSHELL_EVENT_ON_APP_RESUMED = "onApplicationResumed"; using namespace std; using namespace RdkShell; @@ -229,7 +236,7 @@ namespace WPEFramework { { CompositorController::enableInactivityReporting(true); ret = getRFCConfig("Device.DeviceInfo.X_RDKCENTRAL-COM_RFC.Feature.Power.UserInactivityNotification.TimeMinutes", param); - if (true == ret && param.type == WDMP_UINT) + if (true == ret && param.type == WDMP_STRING) { CompositorController::setInactivityInterval(std::stod(param.value)); } @@ -278,26 +285,78 @@ namespace WPEFramework { void RDKShell::RdkShellListener::onApplicationLaunched(const std::string& client) { std::cout << "RDKShell onApplicationLaunched event received ..." << client << std::endl; + JsonObject response; + response["method"] = RDKSHELL_EVENT_ON_APP_LAUNCHED; + JsonObject params; + params["client"] = client; + response["params"] = params; + mShell.notify(RDKSHELL_EVENT_ON_APP_LAUNCHED, response); } void RDKShell::RdkShellListener::onApplicationConnected(const std::string& client) { std::cout << "RDKShell onApplicationConnected event received ..." << client << std::endl; + JsonObject response; + response["method"] = RDKSHELL_EVENT_ON_APP_CONNECTED; + JsonObject params; + params["client"] = client; + response["params"] = params; + mShell.notify(RDKSHELL_EVENT_ON_APP_CONNECTED, response); } void RDKShell::RdkShellListener::onApplicationDisconnected(const std::string& client) { std::cout << "RDKShell onApplicationDisconnected event received ..." << client << std::endl; + JsonObject response; + response["method"] = RDKSHELL_EVENT_ON_APP_DISCONNECTED; + JsonObject params; + params["client"] = client; + response["params"] = params; + mShell.notify(RDKSHELL_EVENT_ON_APP_DISCONNECTED, response); } void RDKShell::RdkShellListener::onApplicationTerminated(const std::string& client) { std::cout << "RDKShell onApplicationTerminated event received ..." << client << std::endl; + JsonObject response; + response["method"] = RDKSHELL_EVENT_ON_APP_TERMINATED; + JsonObject params; + params["client"] = client; + response["params"] = params; + mShell.notify(RDKSHELL_EVENT_ON_APP_TERMINATED, response); } void RDKShell::RdkShellListener::onApplicationFirstFrame(const std::string& client) { std::cout << "RDKShell onApplicationFirstFrame event received ..." << client << std::endl; + JsonObject response; + response["method"] = RDKSHELL_EVENT_ON_APP_FIRST_FRAME; + JsonObject params; + params["client"] = client; + response["params"] = params; + mShell.notify(RDKSHELL_EVENT_ON_APP_FIRST_FRAME, response); + } + + void RDKShell::RdkShellListener::onApplicationSuspended(const std::string& client) + { + std::cout << "RDKShell onApplicationSuspended event received for " << client << std::endl; + JsonObject response; + response["method"] = RDKSHELL_EVENT_ON_APP_SUSPENDED; + JsonObject params; + params["client"] = client; + response["params"] = params; + mShell.notify(RDKSHELL_EVENT_ON_APP_SUSPENDED, response); + } + + void RDKShell::RdkShellListener::onApplicationResumed(const std::string& client) + { + std::cout << "RDKShell onApplicationResumed event received for " << client << std::endl; + JsonObject response; + response["method"] = RDKSHELL_EVENT_ON_APP_RESUMED; + JsonObject params; + params["client"] = client; + response["params"] = params; + mShell.notify(RDKSHELL_EVENT_ON_APP_RESUMED, response); } void RDKShell::RdkShellListener::onUserInactive(const double minutes) @@ -1138,14 +1197,51 @@ namespace WPEFramework { result = CompositorController::suspendApplication(client); } gRdkShellMutex.unlock(); + setVisibility(client, false); if (mimeType != RDKSHELL_APPLICATION_MIME_TYPE_NATIVE) { + //suspend and send the event here for non-native applications + static bool checkRfcOnce = true; + static bool suspendBrowserApps = true; + if (checkRfcOnce) + { + RFC_ParamData_t param; + bool ret = getRFCConfig("DeviceInfo.X_RDKCENTRAL-COM_RFC.Feature.HTML5Suspend.Enable", param); + if (true == ret && param.type == WDMP_BOOLEAN && (strncasecmp(param.value,"false",4) == 0)) + { + suspendBrowserApps = false; + } + std::cout << "RDKShell will suspend browser apps: " << suspendBrowserApps << std::endl; + } + if (suspendBrowserApps) + { + std::string pluginName = client + ".1"; + std::shared_ptr> webkitBrowserPlugin = std::make_shared>(_T(pluginName.c_str()), _T("")); + if (webkitBrowserPlugin != nullptr) + { + JsonObject visibleResult; + JsonObject visibleParam; + visibleParam["params"] = "hidden"; + webkitBrowserPlugin->Invoke(1000, _T("visibility"), visibleParam, visibleResult); + std::cout << "RDKShell sent suspend event to browser\n"; + } + } + JsonObject eventResponse; + eventResponse["method"] = RDKSHELL_EVENT_ON_APP_SUSPENDED; + JsonObject eventParams; + eventParams["client"] = client; + eventResponse["params"] = eventParams; + notify(RDKSHELL_EVENT_ON_APP_SUSPENDED, eventResponse); + std::cout << "RDKShell onApplicationSuspended event sent for " << client << std::endl; } - setVisibility(client, false); if (!result) { response["message"] = "failed to suspend application"; } + else + { + std::cout << client << " was suspended" << std::endl << std::flush; + } } returnResponse(result); } @@ -1171,14 +1267,51 @@ namespace WPEFramework { result = CompositorController::resumeApplication(client); } gRdkShellMutex.unlock(); + setVisibility(client, true); if (mimeType != RDKSHELL_APPLICATION_MIME_TYPE_NATIVE) { + static bool checkRfcOnce = true; + static bool resumeBrowserApps = true; + if (checkRfcOnce) + { + RFC_ParamData_t param; + bool ret = getRFCConfig("DeviceInfo.X_RDKCENTRAL-COM_RFC.Feature.HTML5Suspend.Enable", param); + if (true == ret && param.type == WDMP_BOOLEAN && (strncasecmp(param.value,"false",4) == 0)) + { + resumeBrowserApps = false; + } + std::cout << "RDKShell will resume browser apps: " << resumeBrowserApps << std::endl; + } + if (resumeBrowserApps) + { + std::string pluginName = client + ".1"; + std::shared_ptr> webkitBrowserPlugin = std::make_shared>(_T(pluginName.c_str()), _T("")); + if (webkitBrowserPlugin != nullptr) + { + JsonObject visibleResult; + JsonObject visibleParam; + visibleParam["params"] = "visible"; + webkitBrowserPlugin->Invoke(1000, _T("visibility"), visibleParam, visibleResult); + std::cout << "RDKShell sent resume event to browser\n"; + } + } + //resume and send the event here for non-native applications + JsonObject eventResponse; + eventResponse["method"] = RDKSHELL_EVENT_ON_APP_RESUMED; + JsonObject eventParams; + eventParams["client"] = client; + eventResponse["params"] = eventParams; + notify(RDKSHELL_EVENT_ON_APP_RESUMED, eventResponse); + std::cout << "RDKShell onApplicationResumed event sent for " << client << std::endl; } - setVisibility(client, true); if (!result) { response["message"] = "failed to resume application"; } + else + { + std::cout << client << " was resumed" << std::endl << std::flush; + } } returnResponse(result); } diff --git a/RDKShell/RDKShell.h b/RDKShell/RDKShell.h index 235543950c..82e28f90a8 100755 --- a/RDKShell/RDKShell.h +++ b/RDKShell/RDKShell.h @@ -85,6 +85,13 @@ namespace WPEFramework { // events static const string RDKSHELL_EVENT_ON_USER_INACTIVITY; + static const string RDKSHELL_EVENT_ON_APP_LAUNCHED; + static const string RDKSHELL_EVENT_ON_APP_CONNECTED; + static const string RDKSHELL_EVENT_ON_APP_DISCONNECTED; + static const string RDKSHELL_EVENT_ON_APP_TERMINATED; + static const string RDKSHELL_EVENT_ON_APP_FIRST_FRAME; + static const string RDKSHELL_EVENT_ON_APP_SUSPENDED; + static const string RDKSHELL_EVENT_ON_APP_RESUMED; private/*registered methods (wrappers)*/: @@ -179,6 +186,8 @@ namespace WPEFramework { virtual void onApplicationDisconnected(const std::string& client); virtual void onApplicationTerminated(const std::string& client); virtual void onApplicationFirstFrame(const std::string& client); + virtual void onApplicationSuspended(const std::string& client); + virtual void onApplicationResumed(const std::string& client); virtual void onUserInactive(const double minutes); private: From 41985ad50d0b83795cde8a49af632a670e20f889 Mon Sep 17 00:00:00 2001 From: nambi Date: Mon, 6 Jul 2020 18:03:34 -0400 Subject: [PATCH 48/56] RDK-27957: TextToSpeech plugin md file submit Signed-off-by: nambi --- TextToSpeech/doc/TextToSpeechPlugin.md | 348 +++++++++++++++++++++++++ 1 file changed, 348 insertions(+) create mode 100644 TextToSpeech/doc/TextToSpeechPlugin.md diff --git a/TextToSpeech/doc/TextToSpeechPlugin.md b/TextToSpeech/doc/TextToSpeechPlugin.md new file mode 100644 index 0000000000..fed7569388 --- /dev/null +++ b/TextToSpeech/doc/TextToSpeechPlugin.md @@ -0,0 +1,348 @@ + + +# TextToSpeech Plugin + +**Version: 1.0** + +**Status: :black_circle::black_circle::black_circle:** + +TextToSpeech plugin for Thunder framework. + +### Table of Contents + +- [Introduction](#head.Introduction) +- [Description](#head.Description) +- [Configuration](#head.Configuration) +- [Methods](#head.Methods) +- [Notifications](#head.Notifications) + + +# Introduction + + +## Scope + +This document describes purpose and functionality of the TextToSpeech plugin. It includes detailed specification of its configuration, methods provided and notifications sent. + + +## Case Sensitivity + +All identifiers on the interface described in this document are case-sensitive. Thus, unless stated otherwise, all keywords, entities, properties, relations and actions should be treated as such. + + +## Acronyms, Abbreviations and Terms + +The table below provides and overview of acronyms used in this document and their definitions. + +| Acronym | Description | +| :-------- | :-------- | +| API | Application Programming Interface | +| HTTP | Hypertext Transfer Protocol | +| JSON | JavaScript Object Notation; a data interchange format | +| JSON-RPC | A remote procedure call protocol encoded in JSON | + +The table below provides and overview of terms and abbreviations used in this document and their definitions. + +| Term | Description | +| :-------- | :-------- | +| callsign | The name given to an instance of a plugin. One plugin can be instantiated multiple times, but each instance the instance name, callsign, must be unique. | + + +## References + +| Ref ID | Description | +| :-------- | :-------- | +| [HTTP](http://www.w3.org/Protocols) | HTTP specification | +| [JSON-RPC](https://www.jsonrpc.org/specification) | JSON-RPC 2.0 specification | +| [JSON](http://www.json.org/) | JSON specification | +| [Thunder](https://github.com/WebPlatformForEmbedded/Thunder/blob/master/doc/WPE%20-%20API%20-%20WPEFramework.docx) | Thunder API Reference | + + +# Description + +The TextToSpeech plugin provides TTS functionality (Voice Guidance & Speech Synthesis) for the client application. + +The plugin is designed to be loaded and executed within the Thunder framework. For more information about the framework refer to [[Thunder](#ref.Thunder)]. + + +# Configuration + +The table below lists configuration options of the plugin. + +| Name | Type | Description | +| :-------- | :-------- | :-------- | +| callsign | string | Plugin instance name (default: *org.rdk.TextToSpeech*) | +| classname | string | Class name: *TextToSpeech* | +| locator | string | Library name: *libWPEFrameworkTextToSpeech.so* | +| autostart | boolean | Determines if the plugin is to be started automatically along with the framework | + + +# Methods + +The following methods are provided by the TextToSpeech plugin: + +TextToSpeech interface methods: + +| Method | Description | +| :-------- | :-------- | +| [isttsenabled](#method.isttsenabled) | Check whether TTS Engine is enabled or not | +| [speak](#method.speak) | Start Speech | +| [cancel](#method.cancel) | cancel the speech | + + +## *isttsenabled method* + +Check whether TTS Engine is enabled or not. + +### Parameters + +| Name | Type | Description | +| :-------- | :-------- | :-------- | +| params | object | | + +### Result + +| Name | Type | Description | +| :-------- | :-------- | :-------- | +| result | object | | +| result?.isenabled | boolean | *(optional)* Indicates whether TTSEngine is enabled or not | +| result?.TTS_Status | number | *(optional)* TTS Return status (must be one of the following: *TTS_OK*, *TTS_FAIL*, *TTS_NOT_ENABLED*, *TTS_CREATE_SESSION_DUPLICATE*, *TTS_EMPTY_APPID_INPUT*, *TTS_RESOURCE_BUSY*, *TTS_NO_SESSION_FOUND*, *TTS_NESTED_CLAIM_REQUEST*, *TTS_INVALID_CONFIGURATION*, *TTS_SESSION_NOT_ACTIVE*, *TTS_APP_NOT_FOUND*, *TTS_POLICY_VIOLATION*, *TTS_OBJECT_DESTROYED*, *TTS_SPEECH_NOT_FOUND*) | +| result?.success | boolean | *(optional)* Call status | + +### Example + +#### Request + +```json +{ + "jsonrpc": "2.0", + "id": 1234567890, + "method": "TextToSpeech.1.isttsenabled", + "params": {} +} +``` +#### Response + +```json +{ + "jsonrpc": "2.0", + "id": 1234567890, + "result": { + "isenabled": true, + "TTS_Status": 0, + "success": true + } +} +``` + +## *speak method* + +Start Speech. + +### Parameters + +| Name | Type | Description | +| :-------- | :-------- | :-------- | +| params | object | | +| params.text | string | Text input | + +### Result + +| Name | Type | Description | +| :-------- | :-------- | :-------- | +| result | object | | +| result?.TTS_Status | number | *(optional)* TTS Return status (must be one of the following: *TTS_OK*, *TTS_FAIL*, *TTS_NOT_ENABLED*, *TTS_CREATE_SESSION_DUPLICATE*, *TTS_EMPTY_APPID_INPUT*, *TTS_RESOURCE_BUSY*, *TTS_NO_SESSION_FOUND*, *TTS_NESTED_CLAIM_REQUEST*, *TTS_INVALID_CONFIGURATION*, *TTS_SESSION_NOT_ACTIVE*, *TTS_APP_NOT_FOUND*, *TTS_POLICY_VIOLATION*, *TTS_OBJECT_DESTROYED*, *TTS_SPEECH_NOT_FOUND*) | +| result?.success | boolean | *(optional)* Call status | + +### Example + +#### Request + +```json +{ + "jsonrpc": "2.0", + "id": 1234567890, + "method": "TextToSpeech.1.speak", + "params": { + "text": "speech_1" + } +} +``` +#### Response + +```json +{ + "jsonrpc": "2.0", + "id": 1234567890, + "result": { + "TTS_Status": 0, + "success": true + } +} +``` + +## *cancel method* + +cancel the speech. + +### Parameters + +| Name | Type | Description | +| :-------- | :-------- | :-------- | +| params | object | | + +### Result + +| Name | Type | Description | +| :-------- | :-------- | :-------- | +| result | object | | +| result?.TTS_Status | number | *(optional)* TTS Return status (must be one of the following: *TTS_OK*, *TTS_FAIL*, *TTS_NOT_ENABLED*, *TTS_CREATE_SESSION_DUPLICATE*, *TTS_EMPTY_APPID_INPUT*, *TTS_RESOURCE_BUSY*, *TTS_NO_SESSION_FOUND*, *TTS_NESTED_CLAIM_REQUEST*, *TTS_INVALID_CONFIGURATION*, *TTS_SESSION_NOT_ACTIVE*, *TTS_APP_NOT_FOUND*, *TTS_POLICY_VIOLATION*, *TTS_OBJECT_DESTROYED*, *TTS_SPEECH_NOT_FOUND*) | +| result?.success | boolean | *(optional)* Call status | + +### Example + +#### Request + +```json +{ + "jsonrpc": "2.0", + "id": 1234567890, + "method": "TextToSpeech.1.cancel", + "params": {} +} +``` +#### Response + +```json +{ + "jsonrpc": "2.0", + "id": 1234567890, + "result": { + "TTS_Status": 0, + "success": true + } +} +``` + +# Notifications + +Notifications are autonomous events, triggered by the internals of the implementation, and broadcasted via JSON-RPC to all registered observers.Refer to [[Thunder](#ref.Thunder)] for information on how to register for a notification. + +The following events are provided by the TextToSpeech plugin: + +TextToSpeech interface events: + +| Event | Description | +| :-------- | :-------- | +| [onspeechstart](#event.onspeechstart) | Notifies when speech starts | +| [onspeechcancel](#event.onspeechcancel) | Notifies when speech is cancelled | +| [onnetworkerror](#event.onnetworkerror) | Notifies when network error is occurred | +| [onplaybackerror](#event.onplaybackerror) | Notifies when playback error | +| [onspeechcomplete](#event.onspeechcomplete) | Notifies when speech is completed | + + +## *onspeechstart event* + +Notifies when speech starts. + +### Parameters + +| Name | Type | Description | +| :-------- | :-------- | :-------- | +| params | object | | +| params.text | string | Text | + +### Example + +```json +{ + "jsonrpc": "2.0", + "method": "client.events.1.onspeechstart", + "params": { + "text": "speech_1" + } +} +``` + +## *onspeechcancel event* + +Notifies when speech is cancelled. + +### Parameters + +| Name | Type | Description | +| :-------- | :-------- | :-------- | +| params | object | | + +### Example + +```json +{ + "jsonrpc": "2.0", + "method": "client.events.1.onspeechcancel", + "params": {} +} +``` + +## *onnetworkerror event* + +Notifies when network error is occurred. + +### Parameters + +| Name | Type | Description | +| :-------- | :-------- | :-------- | +| params | object | | + +### Example + +```json +{ + "jsonrpc": "2.0", + "method": "client.events.1.onnetworkerror", + "params": {} +} +``` + +## *onplaybackerror event* + +Notifies when playback error. + +### Parameters + +| Name | Type | Description | +| :-------- | :-------- | :-------- | +| params | object | | + +### Example + +```json +{ + "jsonrpc": "2.0", + "method": "client.events.1.onplaybackerror", + "params": {} +} +``` + +## *onspeechcomplete event* + +Notifies when speech is completed. + +### Parameters + +| Name | Type | Description | +| :-------- | :-------- | :-------- | +| params | object | | +| params.text | string | Text | + +### Example + +```json +{ + "jsonrpc": "2.0", + "method": "client.events.1.onspeechcomplete", + "params": { + "text": "speech_1" + } +} +``` From e9eb9d2f013a5b1609931d341fdf7fe9689b2cab Mon Sep 17 00:00:00 2001 From: Anand Kandasamy Date: Tue, 7 Jul 2020 09:17:31 -0400 Subject: [PATCH 49/56] AMLOGIC-197: Remove Network precondition for LocationSync Plugin --- LocationSync/LocationSync.config | 2 -- 1 file changed, 2 deletions(-) diff --git a/LocationSync/LocationSync.config b/LocationSync/LocationSync.config index ea7584a414..2f1c22d19b 100644 --- a/LocationSync/LocationSync.config +++ b/LocationSync/LocationSync.config @@ -1,7 +1,5 @@ set (autostart true) -set (preconditions Network) - map() kv(interval 10) kv(retries 20) From 516af44671c44eee25251bfdbf421a2a090a1a4a Mon Sep 17 00:00:00 2001 From: Sergey Borushevsky Date: Tue, 7 Jul 2020 19:46:46 +0000 Subject: [PATCH 50/56] DELIA-44326 : [Thunder][Dis[laySettings] - HDMI Audio Delay Issues Fixed setAudioDelayOffset call, used device::AudioOutputPort::setAudioDelayOffset(). --- DisplaySettings/DisplaySettings.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DisplaySettings/DisplaySettings.cpp b/DisplaySettings/DisplaySettings.cpp index 4c7d33e0db..cbbcec26c9 100644 --- a/DisplaySettings/DisplaySettings.cpp +++ b/DisplaySettings/DisplaySettings.cpp @@ -1593,7 +1593,7 @@ namespace WPEFramework { } device::AudioOutputPort aPort = device::Host::getInstance().getAudioOutputPort(audioPort); - aPort.setAudioDelay (audioDelayOffsetMs); + aPort.setAudioDelayOffset (audioDelayOffsetMs); } catch (const device::Exception& err) { From 78bdbc8ca9dda6572fbfbb9b3468c263249cbc77 Mon Sep 17 00:00:00 2001 From: Nikita Poltorapavlo Date: Thu, 9 Jul 2020 15:16:08 +0300 Subject: [PATCH 51/56] RDK-28664 : change store path Reason for change: use /opt/persistent Test Procedure: database is in this location Risks: Low Signed-off-by: Nikita Poltorapavlo --- PersistentStore/PersistentStore.cpp | 29 ++++++++++++++++++++++------- Warehouse/Warehouse.cpp | 2 +- 2 files changed, 23 insertions(+), 8 deletions(-) diff --git a/PersistentStore/PersistentStore.cpp b/PersistentStore/PersistentStore.cpp index 0684fb8d73..96bca30556 100644 --- a/PersistentStore/PersistentStore.cpp +++ b/PersistentStore/PersistentStore.cpp @@ -53,6 +53,11 @@ namespace { { return (remove (f) == 0); } + + bool fileExists(const char* f) + { + return g_file_test(f, G_FILE_TEST_EXISTS); + } } namespace WPEFramework { @@ -89,7 +94,9 @@ namespace WPEFramework { { LOGINFO(); - gchar* path = g_build_filename(g_get_user_data_dir(), STORE_NAME, nullptr); // XDG_DATA_HOME + auto path = g_build_filename("opt", "persistent", STORE_NAME, nullptr); + if (!fileExists(path)) + g_mkdir_with_parents(path, 0745); bool success = init(path, STORE_KEY); g_free(path); @@ -293,7 +300,7 @@ namespace WPEFramework { { int64_t size = sqlite3_column_int64(stmt, 0); if (size > MAX_SIZE_BYTES) - LOGWARN("max size exceeded: %d", size); + LOGWARN("max size exceeded: %lld", size); else success = true; } @@ -357,10 +364,10 @@ namespace WPEFramework { int64_t size = sqlite3_column_int64(stmt, 0); if (size > MAX_SIZE_BYTES) { - LOGWARN("max size exceeded: %d", size); + LOGWARN("max size exceeded: %lld", size); JsonObject params; - sendNotify(EVT_ON_STORAGE_EXCEEDED, params); + sendNotify(C_STR(EVT_ON_STORAGE_EXCEEDED), params); } else success = true; @@ -595,7 +602,7 @@ namespace WPEFramework { bool shouldEncrypt = key && *key; #if defined(SQLITE_HAS_CODEC) - bool shouldReKey = shouldEncrypt && Utils::fileExists(filename) && !fileEncrypted(filename); + bool shouldReKey = shouldEncrypt && fileExists(filename) && !fileEncrypted(filename); #endif int rc = sqlite3_open(filename, &db); if (rc) @@ -612,6 +619,14 @@ namespace WPEFramework { #if defined(SQLITE_HAS_CODEC) std::vector pKey; #if defined(USE_PLABELS) + + // NOTE: pbnj_utils stores the nonce under XDG_DATA_HOME/data. + // If the dir doesn't exist it will fail + auto path = g_build_filename(g_get_user_data_dir(), "data", nullptr); + if (!fileExists(path)) + g_mkdir_with_parents(path, 0755); + g_free(path); + bool result = pbnj_utils::prepareBufferForOrigin(key, [&pKey](const std::vector& buffer) { pKey = buffer; }); @@ -671,14 +686,14 @@ namespace WPEFramework { { LOGWARN("SQLite database is encrypted, but the key doesn't work"); term(); - if (!fileRemove(filename) || Utils::fileExists(filename)) + if (!fileRemove(filename) || fileExists(filename)) { LOGERR("Can't remove file"); return false; } rc = sqlite3_open(filename, &db); term(); - if (rc || !Utils::fileExists(filename)) + if (rc || !fileExists(filename)) { LOGERR("Can't create file"); return false; diff --git a/Warehouse/Warehouse.cpp b/Warehouse/Warehouse.cpp index 49ab2bc520..70a06d5db6 100644 --- a/Warehouse/Warehouse.cpp +++ b/Warehouse/Warehouse.cpp @@ -61,7 +61,7 @@ #define VERSION_FILE_NAME "/version.txt" #define CUSTOM_DATA_FILE "/lib/rdk/wh_api_5.conf" -#define LIGHT_RESET_SCRIPT "rm -rf /opt/netflix/* SD_CARD_MOUNT_PATH/netflix/* XDG_DATA_HOME/* XDG_CACHE_HOME/* XDG_CACHE_HOME/../.sparkStorage/ /opt/QT/home/data/* /opt/hn_service_settings.conf /opt/apps/common/proxies.conf /opt/lib/bluetooth" +#define LIGHT_RESET_SCRIPT "rm -rf /opt/netflix/* SD_CARD_MOUNT_PATH/netflix/* XDG_DATA_HOME/* XDG_CACHE_HOME/* XDG_CACHE_HOME/../.sparkStorage/ /opt/QT/home/data/* /opt/hn_service_settings.conf /opt/apps/common/proxies.conf /opt/lib/bluetooth /opt/persistent/rdkservicestore" #define INTERNAL_RESET_SCRIPT "rm -rf /opt/drm /opt/www/whitebox /opt/www/authService && /rebootNow.sh -s WarehouseService &" #define FRONT_PANEL_NONE -1 From 56000ef4639d9986eb0f31150d2bbe23fd282d20 Mon Sep 17 00:00:00 2001 From: Vijay Selvaraj Date: Fri, 10 Jul 2020 15:30:29 -0400 Subject: [PATCH 52/56] [DELIA-44038] WPEFramework Crash in xi3. Disabled autostart for RemoteActionMapping. --- RemoteActionMapping/RemoteActionMapping.config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RemoteActionMapping/RemoteActionMapping.config b/RemoteActionMapping/RemoteActionMapping.config index 0c2ce1b7f7..104978b5ee 100644 --- a/RemoteActionMapping/RemoteActionMapping.config +++ b/RemoteActionMapping/RemoteActionMapping.config @@ -1,3 +1,3 @@ -set (autostart true) +set (autostart false) set (preconditions Platform) set (callsign "org.rdk.RemoteActionMapping") From a779d9ff95fd9a0fbc4b9897cf3829c6c74f06fb Mon Sep 17 00:00:00 2001 From: Nikita Poltorapavlo Date: Mon, 13 Jul 2020 15:07:41 +0300 Subject: [PATCH 53/56] RDK-28664 : fix path creation Reason for change: fix Test Procedure: test functionality specified in docs Risks: Low Signed-off-by: Nikita Poltorapavlo --- PersistentStore/PersistentStore.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/PersistentStore/PersistentStore.cpp b/PersistentStore/PersistentStore.cpp index 96bca30556..9c559a0801 100644 --- a/PersistentStore/PersistentStore.cpp +++ b/PersistentStore/PersistentStore.cpp @@ -94,11 +94,13 @@ namespace WPEFramework { { LOGINFO(); - auto path = g_build_filename("opt", "persistent", STORE_NAME, nullptr); + auto path = g_build_filename("opt", "persistent", nullptr); if (!fileExists(path)) g_mkdir_with_parents(path, 0745); - bool success = init(path, STORE_KEY); + auto file = g_build_filename(path, STORE_NAME, nullptr); + bool success = init(file, STORE_KEY); g_free(path); + g_free(file); return success ? "" : "init failed"; } From 999a13d023aaec3f3f9dd2005f0b9310b127314a Mon Sep 17 00:00:00 2001 From: Nikita Poltorapavlo Date: Mon, 13 Jul 2020 18:02:38 +0300 Subject: [PATCH 54/56] do not rely on page count when calculating size --- PersistentStore/PersistentStore.cpp | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/PersistentStore/PersistentStore.cpp b/PersistentStore/PersistentStore.cpp index 9c559a0801..20a2eb66f0 100644 --- a/PersistentStore/PersistentStore.cpp +++ b/PersistentStore/PersistentStore.cpp @@ -296,7 +296,11 @@ namespace WPEFramework { if (db) { sqlite3_stmt *stmt; - sqlite3_prepare_v2(db, "SELECT page_count * page_size FROM pragma_page_count(), pragma_page_size();", -1, &stmt, nullptr); + sqlite3_prepare_v2(db, "SELECT sum(s) FROM (" + " SELECT sum(length(key)+length(value)) s FROM item" + " UNION ALL" + " SELECT sum(length(name)) s FROM namespace" + ");", -1, &stmt, nullptr); if (sqlite3_step(stmt) == SQLITE_ROW) { @@ -359,7 +363,11 @@ namespace WPEFramework { success = false; sqlite3_stmt *stmt; - sqlite3_prepare_v2(db, "SELECT page_count * page_size FROM pragma_page_count(), pragma_page_size();", -1, &stmt, nullptr); + sqlite3_prepare_v2(db, "SELECT sum(s) FROM (" + " SELECT sum(length(key)+length(value)) s FROM item" + " UNION ALL" + " SELECT sum(length(name)) s FROM namespace" + ");", -1, &stmt, nullptr); if (sqlite3_step(stmt) == SQLITE_ROW) { From 9c4825672a6eec0641b3cfdcdcf256d30a627ac9 Mon Sep 17 00:00:00 2001 From: Nikita Poltorapavlo Date: Mon, 13 Jul 2020 18:36:41 +0300 Subject: [PATCH 55/56] enable foreign keys in sqlite --- PersistentStore/PersistentStore.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/PersistentStore/PersistentStore.cpp b/PersistentStore/PersistentStore.cpp index 20a2eb66f0..50295070fd 100644 --- a/PersistentStore/PersistentStore.cpp +++ b/PersistentStore/PersistentStore.cpp @@ -730,6 +730,18 @@ namespace WPEFramework { LOGERR("%d", rc); } + rc = sqlite3_exec(db, "PRAGMA foreign_keys = ON;", 0, 0, &errmsg); + if (rc != SQLITE_OK || errmsg) + { + if (errmsg) + { + LOGERR("%d : %s", rc, errmsg); + sqlite3_free(errmsg); + } + else + LOGERR("%d", rc); + } + return true; } } // namespace Plugin From 90b966b5bedc4a8b3905c053453780a6236f5623 Mon Sep 17 00:00:00 2001 From: jschmi201 Date: Mon, 13 Jul 2020 19:43:30 +0000 Subject: [PATCH 56/56] DELIA-44559 : Change CS thunder quirk to return array, not string. Reason for change: Control Service Thunder Quirk should be an array, not a string Test Procedure: Verify test app gives "result : {"quirks":["DELIA-43686"],"success":true}". Risks: None. Signed-off-by: jschmidt