From 81c658b5ea496960a37cbb3651f67a98db029808 Mon Sep 17 00:00:00 2001 From: "szymon.mulczynski" Date: Mon, 20 Mar 2023 16:01:54 +0100 Subject: [PATCH 1/9] RDK-38400: RDKShell Checkpoint and Restore Support The code is guarded by HIBERNATE_SUPPORT_ENABLED flag. --- RDKShell/CMakeLists.txt | 5 ++ RDKShell/RDKShell.cpp | 132 +++++++++++++++++++++++++++++++++++++++- RDKShell/RDKShell.h | 12 ++++ RDKShell/RDKShell.json | 89 +++++++++++++++++++++++++++ 4 files changed, 236 insertions(+), 2 deletions(-) diff --git a/RDKShell/CMakeLists.txt b/RDKShell/CMakeLists.txt index 75a48956a0..b305289c87 100755 --- a/RDKShell/CMakeLists.txt +++ b/RDKShell/CMakeLists.txt @@ -23,6 +23,7 @@ set(PLUGIN_RDKSHELL_STARTUPORDER "" CACHE STRING "Automatically start RDKShell p option(PLUGIN_RDKSHELL_READ_MAC_ON_STARTUP "PLUGIN_RDKSHELL_READ_MAC_ON_STARTUP" OFF) option(PLUGIN_RDKSHELL_DUAL_FTA_SUPPORT "PLUGIN_RDKSHELL_DUAL_FTA_SUPPORT" OFF) +option(PLUGIN_HIBERNATESUPPORT "Include hibernate support in the build." OFF) find_package(${NAMESPACE}Plugins REQUIRED) find_package(IARMBus) @@ -59,6 +60,10 @@ endif (PLUGIN_RDKSHELL_IGNORE_PS_FLAG_ON_MBFTA) target_compile_definitions(${MODULE_NAME} PRIVATE MODULE_NAME=Plugin_${PLUGIN_NAME}) +if(PLUGIN_HIBERNATESUPPORT) + target_compile_definitions(${MODULE_NAME} PRIVATE HIBERNATE_SUPPORT_ENABLED=1) +endif() + target_include_directories(${MODULE_NAME} PRIVATE ../helpers ${IARMBUS_INCLUDE_DIRS} ) set_source_files_properties(RDKShell.cpp PROPERTIES COMPILE_FLAGS "-fexceptions") diff --git a/RDKShell/RDKShell.cpp b/RDKShell/RDKShell.cpp index 9bb242c686..b21b146f5e 100755 --- a/RDKShell/RDKShell.cpp +++ b/RDKShell/RDKShell.cpp @@ -141,6 +141,10 @@ const string WPEFramework::Plugin::RDKShell::RDKSHELL_METHOD_GET_AV_BLOCKED_APPS const string WPEFramework::Plugin::RDKShell::RDKSHELL_METHOD_KEY_REPEAT_CONFIG = "keyRepeatConfig"; const string WPEFramework::Plugin::RDKShell::RDKSHELL_METHOD_GET_GRAPHICS_FRAME_RATE = "getGraphicsFrameRate"; const string WPEFramework::Plugin::RDKShell::RDKSHELL_METHOD_SET_GRAPHICS_FRAME_RATE = "setGraphicsFrameRate"; +#ifdef HIBERNATE_SUPPORT_ENABLED +const string WPEFramework::Plugin::RDKShell::RDKSHELL_METHOD_CHECKPOINT = "checkpoint"; +const string WPEFramework::Plugin::RDKShell::RDKSHELL_METHOD_RESTORE = "restore"; +#endif const string WPEFramework::Plugin::RDKShell::RDKSHELL_EVENT_ON_USER_INACTIVITY = "onUserInactivity"; const string WPEFramework::Plugin::RDKShell::RDKSHELL_EVENT_ON_APP_LAUNCHED = "onApplicationLaunched"; @@ -162,6 +166,10 @@ const string WPEFramework::Plugin::RDKShell::RDKSHELL_EVENT_DEVICE_CRITICALLY_LO const string WPEFramework::Plugin::RDKShell::RDKSHELL_EVENT_ON_EASTER_EGG = "onEasterEgg"; const string WPEFramework::Plugin::RDKShell::RDKSHELL_EVENT_ON_WILL_DESTROY = "onWillDestroy"; const string WPEFramework::Plugin::RDKShell::RDKSHELL_EVENT_ON_SCREENSHOT_COMPLETE = "onScreenshotComplete"; +#ifdef HIBERNATE_SUPPORT_ENABLED +const string WPEFramework::Plugin::RDKShell::RDKSHELL_EVENT_ON_CHECKPOINTED = "onCheckpointed"; +const string WPEFramework::Plugin::RDKShell::RDKSHELL_EVENT_ON_RESTORED = "onRestored"; +#endif using namespace std; using namespace RdkShell; @@ -1271,6 +1279,10 @@ namespace WPEFramework { Register(RDKSHELL_METHOD_SET_GRAPHICS_FRAME_RATE, &RDKShell::setGraphicsFrameRateWrapper, this); Register(RDKSHELL_METHOD_SET_AV_BLOCKED, &RDKShell::setAVBlockedWrapper, this); Register(RDKSHELL_METHOD_GET_AV_BLOCKED_APPS, &RDKShell::getBlockedAVApplicationsWrapper, this); +#ifdef HIBERNATE_SUPPORT_ENABLED + Register(RDKSHELL_METHOD_CHECKPOINT, &RDKShell::checkpointWrapper, this); + Register(RDKSHELL_METHOD_RESTORE, &RDKShell::restoreWrapper, this); +#endif m_timer.connect(std::bind(&RDKShell::onTimer, this)); } @@ -4907,12 +4919,31 @@ namespace WPEFramework { WPEFramework::Core::JSON::String stateString; const string callsignWithVersion = callsign + ".1"; auto thunderPlugin = getThunderControllerClient(callsignWithVersion); - uint32_t stateStatus = thunderPlugin->Get(RDKSHELL_THUNDER_TIMEOUT, "state", stateString); + uint32_t stateStatus = 0; + +#ifdef HIBERNATE_SUPPORT_ENABLED + if(service.JSONState != PluginHost::MetaData::Service::state::HIBERNATED) + { + stateStatus = thunderPlugin->Get(RDKSHELL_THUNDER_TIMEOUT, "state", stateString); + } + else + { + stateString = "checkpointed"; + } +#endif if (stateStatus == 0) { WPEFramework::Core::JSON::String urlString; - uint32_t urlStatus = thunderPlugin->Get(RDKSHELL_THUNDER_TIMEOUT, "url",urlString); + uint32_t urlStatus = 1; +#ifdef HIBERNATE_SUPPORT_ENABLED + if(service.JSONState != PluginHost::MetaData::Service::state::HIBERNATED) + { +#endif + urlStatus = thunderPlugin->Get(RDKSHELL_THUNDER_TIMEOUT, "url",urlString); +#ifdef HIBERNATE_SUPPORT_ENABLED + } +#endif JsonObject typeObject; typeObject["callsign"] = callsign; @@ -6033,6 +6064,103 @@ namespace WPEFramework { returnResponse(status); } +#ifdef HIBERNATE_SUPPORT_ENABLED + uint32_t RDKShell::checkpointWrapper(const JsonObject& parameters, JsonObject& response) + { + LOGINFOMETHOD(); + bool status = false; + if (parameters.HasLabel("callsign")) + { + std::string callsign = parameters["callsign"].String(); + bool isApplicationBeingDestroyed = false; + + gLaunchDestroyMutex.lock(); + if (gDestroyApplications.find(callsign) != gDestroyApplications.end()) + { + isApplicationBeingDestroyed = true; + } + if (gExternalDestroyApplications.find(callsign) != gExternalDestroyApplications.end()) + { + isApplicationBeingDestroyed = true; + } + gLaunchDestroyMutex.unlock(); + + if (isApplicationBeingDestroyed) + { + std::cout << "ignoring checkpoint for " << callsign << " as it is being destroyed " << std::endl; + status = false; + response["message"] = "failed to checkpoint application, is being destroyed"; + returnResponse(status); + } + + std::thread requestsThread = + std::thread([=]() + { + auto thunderController = RDKShell::getThunderControllerClient(); + JsonObject request, result, eventMsg; + request["callsign"] = callsign; + request["timeout"] = RDKSHELL_THUNDER_TIMEOUT; + if(parameters.HasLabel("timeout")) + { + request["timeout"] = parameters["timeout"]; + } + if(parameters.HasLabel("procsequence")) + { + request["procsequence"] = parameters["procsequence"]; + } + uint32_t errCode = thunderController->Invoke(RDKSHELL_THUNDER_TIMEOUT, "hibernate", request, result); + if(errCode > 0) + { + eventMsg["success"] = false; + eventMsg["message"] = result; + } + else + { + eventMsg["success"] = true; + } + notify(RDKShell::RDKSHELL_EVENT_ON_CHECKPOINTED, eventMsg); + }); + requestsThread.detach(); + status = true; + } + + returnResponse(status); + } + + uint32_t RDKShell::restoreWrapper(const JsonObject& parameters, JsonObject& response) + { + LOGINFOMETHOD(); + bool status = false; + if (parameters.HasLabel("callsign")) + { + std::string callsign = parameters["callsign"].String(); + std::thread requestsThread = + std::thread([=]() + { + auto thunderController = RDKShell::getThunderControllerClient(); + JsonObject request, result, eventMsg; + request["callsign"] = callsign; + + uint32_t errCode = thunderController->Invoke(RDKSHELL_THUNDER_TIMEOUT, "activate", request, result); + if(errCode > 0) + { + eventMsg["success"] = false; + eventMsg["message"] = result; + } + else + { + eventMsg["success"] = true; + } + notify(RDKShell::RDKSHELL_EVENT_ON_RESTORED, eventMsg); + }); + requestsThread.detach(); + status = true; + } + + returnResponse(status); + } +#endif + // Registered methods end // Events begin diff --git a/RDKShell/RDKShell.h b/RDKShell/RDKShell.h index 1af2441a8e..e837805e60 100755 --- a/RDKShell/RDKShell.h +++ b/RDKShell/RDKShell.h @@ -145,6 +145,10 @@ namespace WPEFramework { static const string RDKSHELL_METHOD_KEY_REPEAT_CONFIG; static const string RDKSHELL_METHOD_GET_GRAPHICS_FRAME_RATE; static const string RDKSHELL_METHOD_SET_GRAPHICS_FRAME_RATE; +#ifdef HIBERNATE_SUPPORT_ENABLED + static const string RDKSHELL_METHOD_CHECKPOINT; + static const string RDKSHELL_METHOD_RESTORE; +#endif // events static const string RDKSHELL_EVENT_ON_USER_INACTIVITY; @@ -167,6 +171,10 @@ namespace WPEFramework { static const string RDKSHELL_EVENT_ON_EASTER_EGG; static const string RDKSHELL_EVENT_ON_WILL_DESTROY; static const string RDKSHELL_EVENT_ON_SCREENSHOT_COMPLETE; +#ifdef HIBERNATE_SUPPORT_ENABLED + static const string RDKSHELL_EVENT_ON_CHECKPOINTED; + static const string RDKSHELL_EVENT_ON_RESTORED; +#endif void notify(const std::string& event, const JsonObject& parameters); void pluginEventHandler(const JsonObject& parameters); @@ -261,6 +269,10 @@ namespace WPEFramework { uint32_t keyRepeatConfigWrapper(const JsonObject& parameters, JsonObject& response); uint32_t getGraphicsFrameRateWrapper(const JsonObject& parameters, JsonObject& response); uint32_t setGraphicsFrameRateWrapper(const JsonObject& parameters, JsonObject& response); +#ifdef HIBERNATE_SUPPORT_ENABLED + uint32_t checkpointWrapper(const JsonObject& parameters, JsonObject& response); + uint32_t restoreWrapper(const JsonObject& parameters, JsonObject& response); +#endif private/*internal methods*/: RDKShell(const RDKShell&) = delete; diff --git a/RDKShell/RDKShell.json b/RDKShell/RDKShell.json index 65dc830cc7..53f19fc5a2 100755 --- a/RDKShell/RDKShell.json +++ b/RDKShell/RDKShell.json @@ -2232,6 +2232,59 @@ "success" ] } + }, + "checkpoint": { + "summary": "Checkpoint an application.", + "events": { + "onCheckpointed" : "Triggers when an application is checkpointed" + }, + "params": { + "type": "object", + "properties":{ + "callsign": { + "$ref": "#/definitions/callsign" + }, + "timeout": { + "summary": "Timeout in ms for checkpoint procedure", + "type": "number", + "example": 10000 + }, + "procsequence": { + "summary": "Checkpoint sequence of application processes", + "type": "array", + "items": { + "type": "string", + "example": "LightningApp-0" + } + } + }, + "required": [ + "callsign" + ] + }, + "result": { + "$ref": "#/common/result" + } + }, + "restore": { + "summary": "Restore an application.", + "events": { + "onRestored" : "Triggers when an application is restored" + }, + "params": { + "type": "object", + "properties":{ + "callsign": { + "$ref": "#/definitions/callsign" + } + }, + "required": [ + "callsign" + ] + }, + "result": { + "$ref": "#/common/result" + } } }, "events": { @@ -2545,6 +2598,42 @@ "client" ] } + }, + "onCheckpointed":{ + "summary": "Triggers when an application is checkpointed.", + "params": { + "type": "object", + "properties": { + "callsign":{ + "$ref": "#/definitions/callsign" + }, + "success": { + "$ref": "#/common/success" + } + }, + "required": [ + "callsign", + "success" + ] + } + }, + "onRestored":{ + "summary": "Triggers when an application is restored.", + "params": { + "type": "object", + "properties": { + "callsign":{ + "$ref": "#/definitions/callsign" + }, + "success": { + "$ref": "#/common/success" + } + }, + "required": [ + "callsign", + "success" + ] + } } } } From 64c1d992834f83d78ed5a01239f464df64f76474 Mon Sep 17 00:00:00 2001 From: Adrian Muzyka Date: Thu, 24 Aug 2023 08:15:13 +0200 Subject: [PATCH 2/9] RDK-43052 Autocheckpoint native apps and getState fix --- RDKShell/CMakeLists.txt | 5 +++ RDKShell/RDKShell.cpp | 70 ++++++++++++++++++++++++++++++++++++++--- 2 files changed, 70 insertions(+), 5 deletions(-) diff --git a/RDKShell/CMakeLists.txt b/RDKShell/CMakeLists.txt index b305289c87..c2a2451d82 100755 --- a/RDKShell/CMakeLists.txt +++ b/RDKShell/CMakeLists.txt @@ -24,6 +24,7 @@ set(PLUGIN_RDKSHELL_STARTUPORDER "" CACHE STRING "Automatically start RDKShell p option(PLUGIN_RDKSHELL_READ_MAC_ON_STARTUP "PLUGIN_RDKSHELL_READ_MAC_ON_STARTUP" OFF) option(PLUGIN_RDKSHELL_DUAL_FTA_SUPPORT "PLUGIN_RDKSHELL_DUAL_FTA_SUPPORT" OFF) option(PLUGIN_HIBERNATESUPPORT "Include hibernate support in the build." OFF) +option(PLUGIN_HIBERNATE_NATIVE_APPS_ON_SUSPENDED "Try to hibernate native apps on suspended." OFF) find_package(${NAMESPACE}Plugins REQUIRED) find_package(IARMBus) @@ -64,6 +65,10 @@ if(PLUGIN_HIBERNATESUPPORT) target_compile_definitions(${MODULE_NAME} PRIVATE HIBERNATE_SUPPORT_ENABLED=1) endif() +if(PLUGIN_HIBERNATE_NATIVE_APPS_ON_SUSPENDED) + target_compile_definitions(${MODULE_NAME} PRIVATE HIBERNATE_NATIVE_APPS_ON_SUSPENDED=1) +endif() + target_include_directories(${MODULE_NAME} PRIVATE ../helpers ${IARMBUS_INCLUDE_DIRS} ) set_source_files_properties(RDKShell.cpp PROPERTIES COMPILE_FLAGS "-fexceptions") diff --git a/RDKShell/RDKShell.cpp b/RDKShell/RDKShell.cpp index b21b146f5e..15373ad0fd 100755 --- a/RDKShell/RDKShell.cpp +++ b/RDKShell/RDKShell.cpp @@ -197,6 +197,11 @@ bool sForceResidentAppLaunch = false; static bool sRunning = true; bool needsScreenshot = false; +#ifdef HIBERNATE_NATIVE_APPS_ON_SUSPENDED +std::mutex nativeAppWasResumedMutex; +map nativeAppWasResumed; +#endif + #define ANY_KEY 65536 #define RDKSHELL_THUNDER_TIMEOUT 20000 #define RDKSHELL_POWER_TIME_WAIT 2.5 @@ -611,7 +616,38 @@ namespace WPEFramework { JsonObject params; params["client"] = mCallSign; mRDKShell.notify(RDKShell::RDKSHELL_EVENT_ON_PLUGIN_SUSPENDED, params); - } + +#ifdef HIBERNATE_NATIVE_APPS_ON_SUSPENDED + RFC_ParamData_t param; + if (Utils::getRFCConfig("Device.DeviceInfo.X_RDKCENTRAL-COM_RFC.Feature.AppHibernate.Enable", param) + && strncasecmp(param.value, "true", 4) == 0) + { + nativeAppWasResumedMutex.lock(); + if ((mCallSign.find("Netflix") != std::string::npos || mCallSign.find("Cobalt") != std::string::npos) + && nativeAppWasResumed.find(mCallSign) != nativeAppWasResumed.end() + && nativeAppWasResumed[mCallSign]) + { + // call RDKShell.checkpoint + std::thread requestsThread = + std::thread([=]() + { + JsonObject checkpointParams; + JsonObject checkpointResponse; + checkpointParams["callsign"] = mCallSign; + mRDKShell.getThunderControllerClient("org.rdk.RDKShell.1")->Invoke(0, "checkpoint", checkpointParams, checkpointResponse); }); + + requestsThread.detach(); + } + nativeAppWasResumedMutex.unlock(); + } +#endif + } +#ifdef HIBERNATE_NATIVE_APPS_ON_SUSPENDED + nativeAppWasResumedMutex.lock(); + nativeAppWasResumed[mCallSign] = (state == PluginHost::IStateControl::RESUMED); + nativeAppWasResumedMutex.unlock(); +#endif + } BEGIN_INTERFACE_MAP(Notification) @@ -972,6 +1008,11 @@ namespace WPEFramework { if ((currentState == PluginHost::IShell::DEACTIVATED) || (currentState == PluginHost::IShell::DESTROYED)) { gApplicationsExitReason[service->Callsign()] = AppLastExitReason::DEACTIVATED; +#ifdef HIBERNATE_NATIVE_APPS_ON_SUSPENDED + nativeAppWasResumedMutex.lock(); + nativeAppWasResumed[service->Callsign()] = false; + nativeAppWasResumedMutex.unlock(); +#endif } if(service->Reason() == PluginHost::IShell::FAILURE) { @@ -4914,17 +4955,18 @@ namespace WPEFramework { { std::string callsign; service.Callsign.ToString(callsign); - callsign.erase(std::remove(callsign.begin(),callsign.end(),'\"'),callsign.end()); + callsign.erase(std::remove(callsign.begin(),callsign.end(),'\"'),callsign.end()); WPEFramework::Core::JSON::String stateString; const string callsignWithVersion = callsign + ".1"; - auto thunderPlugin = getThunderControllerClient(callsignWithVersion); uint32_t stateStatus = 0; #ifdef HIBERNATE_SUPPORT_ENABLED if(service.JSONState != PluginHost::MetaData::Service::state::HIBERNATED) { - stateStatus = thunderPlugin->Get(RDKSHELL_THUNDER_TIMEOUT, "state", stateString); +#endif + stateStatus = getThunderControllerClient(callsignWithVersion)->Get(RDKSHELL_THUNDER_TIMEOUT, "state", stateString); +#ifdef HIBERNATE_SUPPORT_ENABLED } else { @@ -4932,6 +4974,7 @@ namespace WPEFramework { } #endif + if (stateStatus == 0) { WPEFramework::Core::JSON::String urlString; @@ -4940,7 +4983,7 @@ namespace WPEFramework { if(service.JSONState != PluginHost::MetaData::Service::state::HIBERNATED) { #endif - urlStatus = thunderPlugin->Get(RDKSHELL_THUNDER_TIMEOUT, "url",urlString); + urlStatus = getThunderControllerClient(callsignWithVersion)->Get(RDKSHELL_THUNDER_TIMEOUT, "url",urlString); #ifdef HIBERNATE_SUPPORT_ENABLED } #endif @@ -6093,6 +6136,23 @@ namespace WPEFramework { returnResponse(status); } + if( callsign.find("Netflix") != string::npos || callsign.find("Cobalt") != string::npos ) + { + //Check if native app is suspended + WPEFramework::Core::JSON::String stateString; + const string callsignWithVersion = callsign + ".1"; + auto thunderPlugin = getThunderControllerClient(callsignWithVersion); + uint32_t stateStatus = 0; + stateStatus = thunderPlugin->Get(RDKSHELL_THUNDER_TIMEOUT, "state", stateString); + if(stateStatus || stateString != "suspended") + { + std::cout << "ignoring checkpoint for " << callsign << " as it is not suspended " << std::endl; + status = false; + response["message"] = "failed to checkpoint native application, not suspended"; + returnResponse(status); + } + } + std::thread requestsThread = std::thread([=]() { From 8c69b1c6bd87a848b65a0541cb8d425216d51293 Mon Sep 17 00:00:00 2001 From: Adrian Muzyka Date: Tue, 29 Aug 2023 12:21:26 +0200 Subject: [PATCH 3/9] RDK-43052: Try to restore app in RDKShell::killWrapper --- RDKShell/RDKShell.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/RDKShell/RDKShell.cpp b/RDKShell/RDKShell.cpp index 15373ad0fd..bf9a5c2a57 100755 --- a/RDKShell/RDKShell.cpp +++ b/RDKShell/RDKShell.cpp @@ -2381,6 +2381,18 @@ namespace WPEFramework { std::string mimeType; getMimeType(client, mimeType); +#ifdef HIBERNATE_SUPPORT_ENABLED + // RDKShell::kill only destroys wayland display + // and hibernated app will not detect missing display. + // Wakeup app by getting its state + WPEFramework::Core::JSON::String stateString; + auto thunderPlugin = getThunderControllerClient(client); + if(thunderPlugin) + { + thunderPlugin->Get(RDKSHELL_THUNDER_TIMEOUT, "state", stateString); + } +#endif + // Kill the display result = kill(client); if (!result) From 601d5614aaa814eaea6442c8611a63c97b95bf33 Mon Sep 17 00:00:00 2001 From: Adrian Muzyka Date: Tue, 3 Oct 2023 10:52:46 +0200 Subject: [PATCH 4/9] RDK-43052: Clear nativeAppWasResumed in handleDeactivated --- RDKShell/RDKShell.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/RDKShell/RDKShell.cpp b/RDKShell/RDKShell.cpp index bf9a5c2a57..2c8eddb172 100755 --- a/RDKShell/RDKShell.cpp +++ b/RDKShell/RDKShell.cpp @@ -945,6 +945,11 @@ namespace WPEFramework { { gExitReasonMutex.lock(); gApplicationsExitReason[service->Callsign()] = AppLastExitReason::DEACTIVATED; +#ifdef HIBERNATE_NATIVE_APPS_ON_SUSPENDED + nativeAppWasResumedMutex.lock(); + nativeAppWasResumed[service->Callsign()] = false; + nativeAppWasResumedMutex.unlock(); +#endif if(service->Reason() == PluginHost::IShell::FAILURE) { gApplicationsExitReason[service->Callsign()] = AppLastExitReason::CRASH; From 630b185c326d40ce5651abd544e9b01c55c34aa4 Mon Sep 17 00:00:00 2001 From: Adrian Muzyka Date: Fri, 27 Oct 2023 09:36:56 +0200 Subject: [PATCH 5/9] RDKShell: hibernated state instead of checkpointed --- RDKShell/RDKShell.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RDKShell/RDKShell.cpp b/RDKShell/RDKShell.cpp index 2c8eddb172..b8493158f7 100755 --- a/RDKShell/RDKShell.cpp +++ b/RDKShell/RDKShell.cpp @@ -4987,7 +4987,7 @@ namespace WPEFramework { } else { - stateString = "checkpointed"; + stateString = "hibernated"; } #endif From 3d60f26d18fd30bd6ef6cfb123bc2c9bd3b14027 Mon Sep 17 00:00:00 2001 From: Adrian Muzyka Date: Tue, 31 Oct 2023 09:31:25 +0100 Subject: [PATCH 6/9] RDK-43052: RDKShell: change namings to 'hibernate' from 'checkpoint' --- RDKShell/RDKShell.cpp | 28 ++++++++++++++-------------- RDKShell/RDKShell.h | 6 +++--- RDKShell/RDKShell.json | 14 +++++++------- 3 files changed, 24 insertions(+), 24 deletions(-) diff --git a/RDKShell/RDKShell.cpp b/RDKShell/RDKShell.cpp index b8493158f7..8f712c827b 100755 --- a/RDKShell/RDKShell.cpp +++ b/RDKShell/RDKShell.cpp @@ -142,7 +142,7 @@ const string WPEFramework::Plugin::RDKShell::RDKSHELL_METHOD_KEY_REPEAT_CONFIG = const string WPEFramework::Plugin::RDKShell::RDKSHELL_METHOD_GET_GRAPHICS_FRAME_RATE = "getGraphicsFrameRate"; const string WPEFramework::Plugin::RDKShell::RDKSHELL_METHOD_SET_GRAPHICS_FRAME_RATE = "setGraphicsFrameRate"; #ifdef HIBERNATE_SUPPORT_ENABLED -const string WPEFramework::Plugin::RDKShell::RDKSHELL_METHOD_CHECKPOINT = "checkpoint"; +const string WPEFramework::Plugin::RDKShell::RDKSHELL_METHOD_HIBERNATE = "hibernate"; const string WPEFramework::Plugin::RDKShell::RDKSHELL_METHOD_RESTORE = "restore"; #endif @@ -167,7 +167,7 @@ const string WPEFramework::Plugin::RDKShell::RDKSHELL_EVENT_ON_EASTER_EGG = "onE const string WPEFramework::Plugin::RDKShell::RDKSHELL_EVENT_ON_WILL_DESTROY = "onWillDestroy"; const string WPEFramework::Plugin::RDKShell::RDKSHELL_EVENT_ON_SCREENSHOT_COMPLETE = "onScreenshotComplete"; #ifdef HIBERNATE_SUPPORT_ENABLED -const string WPEFramework::Plugin::RDKShell::RDKSHELL_EVENT_ON_CHECKPOINTED = "onCheckpointed"; +const string WPEFramework::Plugin::RDKShell::RDKSHELL_EVENT_ON_HIBERNATED = "onHibernated"; const string WPEFramework::Plugin::RDKShell::RDKSHELL_EVENT_ON_RESTORED = "onRestored"; #endif @@ -627,14 +627,14 @@ namespace WPEFramework { && nativeAppWasResumed.find(mCallSign) != nativeAppWasResumed.end() && nativeAppWasResumed[mCallSign]) { - // call RDKShell.checkpoint + // call RDKShell.hibernate std::thread requestsThread = std::thread([=]() { - JsonObject checkpointParams; - JsonObject checkpointResponse; - checkpointParams["callsign"] = mCallSign; - mRDKShell.getThunderControllerClient("org.rdk.RDKShell.1")->Invoke(0, "checkpoint", checkpointParams, checkpointResponse); }); + JsonObject hibernateParams; + JsonObject hibernatetResponse; + hibernateParams["callsign"] = mCallSign; + mRDKShell.getThunderControllerClient("org.rdk.RDKShell.1")->Invoke(0, "hibernate", hibernateParams, hibernatetResponse); }); requestsThread.detach(); } @@ -1326,7 +1326,7 @@ namespace WPEFramework { Register(RDKSHELL_METHOD_SET_AV_BLOCKED, &RDKShell::setAVBlockedWrapper, this); Register(RDKSHELL_METHOD_GET_AV_BLOCKED_APPS, &RDKShell::getBlockedAVApplicationsWrapper, this); #ifdef HIBERNATE_SUPPORT_ENABLED - Register(RDKSHELL_METHOD_CHECKPOINT, &RDKShell::checkpointWrapper, this); + Register(RDKSHELL_METHOD_HIBERNATE, &RDKShell::hibernateWrapper, this); Register(RDKSHELL_METHOD_RESTORE, &RDKShell::restoreWrapper, this); #endif m_timer.connect(std::bind(&RDKShell::onTimer, this)); @@ -6125,7 +6125,7 @@ namespace WPEFramework { } #ifdef HIBERNATE_SUPPORT_ENABLED - uint32_t RDKShell::checkpointWrapper(const JsonObject& parameters, JsonObject& response) + uint32_t RDKShell::hibernateWrapper(const JsonObject& parameters, JsonObject& response) { LOGINFOMETHOD(); bool status = false; @@ -6147,9 +6147,9 @@ namespace WPEFramework { if (isApplicationBeingDestroyed) { - std::cout << "ignoring checkpoint for " << callsign << " as it is being destroyed " << std::endl; + std::cout << "ignoring hibernate for " << callsign << " as it is being destroyed " << std::endl; status = false; - response["message"] = "failed to checkpoint application, is being destroyed"; + response["message"] = "failed to hibernate application, is being destroyed"; returnResponse(status); } @@ -6163,9 +6163,9 @@ namespace WPEFramework { stateStatus = thunderPlugin->Get(RDKSHELL_THUNDER_TIMEOUT, "state", stateString); if(stateStatus || stateString != "suspended") { - std::cout << "ignoring checkpoint for " << callsign << " as it is not suspended " << std::endl; + std::cout << "ignoring hibenrate for " << callsign << " as it is not suspended " << std::endl; status = false; - response["message"] = "failed to checkpoint native application, not suspended"; + response["message"] = "failed to hibernate native application, not suspended"; returnResponse(status); } } @@ -6195,7 +6195,7 @@ namespace WPEFramework { { eventMsg["success"] = true; } - notify(RDKShell::RDKSHELL_EVENT_ON_CHECKPOINTED, eventMsg); + notify(RDKShell::RDKSHELL_EVENT_ON_HIBERNATED, eventMsg); }); requestsThread.detach(); status = true; diff --git a/RDKShell/RDKShell.h b/RDKShell/RDKShell.h index e837805e60..a62aa5c39a 100755 --- a/RDKShell/RDKShell.h +++ b/RDKShell/RDKShell.h @@ -146,7 +146,7 @@ namespace WPEFramework { static const string RDKSHELL_METHOD_GET_GRAPHICS_FRAME_RATE; static const string RDKSHELL_METHOD_SET_GRAPHICS_FRAME_RATE; #ifdef HIBERNATE_SUPPORT_ENABLED - static const string RDKSHELL_METHOD_CHECKPOINT; + static const string RDKSHELL_METHOD_HIBERNATE; static const string RDKSHELL_METHOD_RESTORE; #endif @@ -172,7 +172,7 @@ namespace WPEFramework { static const string RDKSHELL_EVENT_ON_WILL_DESTROY; static const string RDKSHELL_EVENT_ON_SCREENSHOT_COMPLETE; #ifdef HIBERNATE_SUPPORT_ENABLED - static const string RDKSHELL_EVENT_ON_CHECKPOINTED; + static const string RDKSHELL_EVENT_ON_HIBERNATED; static const string RDKSHELL_EVENT_ON_RESTORED; #endif @@ -270,7 +270,7 @@ namespace WPEFramework { uint32_t getGraphicsFrameRateWrapper(const JsonObject& parameters, JsonObject& response); uint32_t setGraphicsFrameRateWrapper(const JsonObject& parameters, JsonObject& response); #ifdef HIBERNATE_SUPPORT_ENABLED - uint32_t checkpointWrapper(const JsonObject& parameters, JsonObject& response); + uint32_t hibernateWrapper(const JsonObject& parameters, JsonObject& response); uint32_t restoreWrapper(const JsonObject& parameters, JsonObject& response); #endif diff --git a/RDKShell/RDKShell.json b/RDKShell/RDKShell.json index 53f19fc5a2..dc17d61ed3 100755 --- a/RDKShell/RDKShell.json +++ b/RDKShell/RDKShell.json @@ -2233,10 +2233,10 @@ ] } }, - "checkpoint": { - "summary": "Checkpoint an application.", + "hibernate": { + "summary": "Hibernate an application.", "events": { - "onCheckpointed" : "Triggers when an application is checkpointed" + "onHibernated" : "Triggers when an application is hibernated" }, "params": { "type": "object", @@ -2245,12 +2245,12 @@ "$ref": "#/definitions/callsign" }, "timeout": { - "summary": "Timeout in ms for checkpoint procedure", + "summary": "Timeout in ms for hibernate procedure", "type": "number", "example": 10000 }, "procsequence": { - "summary": "Checkpoint sequence of application processes", + "summary": "Hibernate sequence of application processes", "type": "array", "items": { "type": "string", @@ -2599,8 +2599,8 @@ ] } }, - "onCheckpointed":{ - "summary": "Triggers when an application is checkpointed.", + "onHibernated":{ + "summary": "Triggers when an application is hibernated.", "params": { "type": "object", "properties": { From 4f40853c5d7decbe8a708ed746f9bc6f810e428d Mon Sep 17 00:00:00 2001 From: Adrian Muzyka Date: Fri, 27 Oct 2023 09:43:15 +0200 Subject: [PATCH 7/9] RDKShell: checkpoint native apps on each suspend state change --- RDKShell/RDKShell.cpp | 26 +------------------------- 1 file changed, 1 insertion(+), 25 deletions(-) diff --git a/RDKShell/RDKShell.cpp b/RDKShell/RDKShell.cpp index 8f712c827b..499b840c3b 100755 --- a/RDKShell/RDKShell.cpp +++ b/RDKShell/RDKShell.cpp @@ -197,11 +197,6 @@ bool sForceResidentAppLaunch = false; static bool sRunning = true; bool needsScreenshot = false; -#ifdef HIBERNATE_NATIVE_APPS_ON_SUSPENDED -std::mutex nativeAppWasResumedMutex; -map nativeAppWasResumed; -#endif - #define ANY_KEY 65536 #define RDKSHELL_THUNDER_TIMEOUT 20000 #define RDKSHELL_POWER_TIME_WAIT 2.5 @@ -622,10 +617,7 @@ namespace WPEFramework { if (Utils::getRFCConfig("Device.DeviceInfo.X_RDKCENTRAL-COM_RFC.Feature.AppHibernate.Enable", param) && strncasecmp(param.value, "true", 4) == 0) { - nativeAppWasResumedMutex.lock(); - if ((mCallSign.find("Netflix") != std::string::npos || mCallSign.find("Cobalt") != std::string::npos) - && nativeAppWasResumed.find(mCallSign) != nativeAppWasResumed.end() - && nativeAppWasResumed[mCallSign]) + if ((mCallSign.find("Netflix") != std::string::npos || mCallSign.find("Cobalt") != std::string::npos)) { // call RDKShell.hibernate std::thread requestsThread = @@ -638,15 +630,9 @@ namespace WPEFramework { requestsThread.detach(); } - nativeAppWasResumedMutex.unlock(); } #endif } -#ifdef HIBERNATE_NATIVE_APPS_ON_SUSPENDED - nativeAppWasResumedMutex.lock(); - nativeAppWasResumed[mCallSign] = (state == PluginHost::IStateControl::RESUMED); - nativeAppWasResumedMutex.unlock(); -#endif } @@ -945,11 +931,6 @@ namespace WPEFramework { { gExitReasonMutex.lock(); gApplicationsExitReason[service->Callsign()] = AppLastExitReason::DEACTIVATED; -#ifdef HIBERNATE_NATIVE_APPS_ON_SUSPENDED - nativeAppWasResumedMutex.lock(); - nativeAppWasResumed[service->Callsign()] = false; - nativeAppWasResumedMutex.unlock(); -#endif if(service->Reason() == PluginHost::IShell::FAILURE) { gApplicationsExitReason[service->Callsign()] = AppLastExitReason::CRASH; @@ -1013,11 +994,6 @@ namespace WPEFramework { if ((currentState == PluginHost::IShell::DEACTIVATED) || (currentState == PluginHost::IShell::DESTROYED)) { gApplicationsExitReason[service->Callsign()] = AppLastExitReason::DEACTIVATED; -#ifdef HIBERNATE_NATIVE_APPS_ON_SUSPENDED - nativeAppWasResumedMutex.lock(); - nativeAppWasResumed[service->Callsign()] = false; - nativeAppWasResumedMutex.unlock(); -#endif } if(service->Reason() == PluginHost::IShell::FAILURE) { From 99fdee2741daf54afc202611bcfcffdc3154cbc1 Mon Sep 17 00:00:00 2001 From: Adrian Muzyka Date: Thu, 16 Nov 2023 11:03:10 +0100 Subject: [PATCH 8/9] RDKShell: Skip SetFocus to false for Suspended or Hibernated plugins --- RDKShell/RDKShell.cpp | 76 +++++++++++++++++++++++++++++++++++++++---- 1 file changed, 70 insertions(+), 6 deletions(-) diff --git a/RDKShell/RDKShell.cpp b/RDKShell/RDKShell.cpp index 499b840c3b..ba38e8ac44 100755 --- a/RDKShell/RDKShell.cpp +++ b/RDKShell/RDKShell.cpp @@ -197,6 +197,11 @@ bool sForceResidentAppLaunch = false; static bool sRunning = true; bool needsScreenshot = false; +#ifdef HIBERNATE_SUPPORT_ENABLED +std::mutex gSuspendedOrHibernatedApplicationsMutex; +map gSuspendedOrHibernatedApplications; +#endif + #define ANY_KEY 65536 #define RDKSHELL_THUNDER_TIMEOUT 20000 #define RDKSHELL_POWER_TIME_WAIT 2.5 @@ -605,6 +610,11 @@ namespace WPEFramework { mRDKShell.notify(RDKShell::RDKSHELL_EVENT_ON_LAUNCHED, params); mLaunchEnabled = false; } +#ifdef HIBERNATE_SUPPORT_ENABLED + gSuspendedOrHibernatedApplicationsMutex.lock(); + gSuspendedOrHibernatedApplications[mCallSign] = isSuspended; + gSuspendedOrHibernatedApplicationsMutex.unlock(); +#endif if (isSuspended) { @@ -931,6 +941,17 @@ namespace WPEFramework { { gExitReasonMutex.lock(); gApplicationsExitReason[service->Callsign()] = AppLastExitReason::DEACTIVATED; + +#ifdef HIBERNATE_SUPPORT_ENABLED + //Reset app suspended/hibernated + gSuspendedOrHibernatedApplicationsMutex.lock(); + auto suspendedOrHibernatedIt = gSuspendedOrHibernatedApplications.find(service->Callsign()); + if (suspendedOrHibernatedIt != gSuspendedOrHibernatedApplications.end()) + { + gSuspendedOrHibernatedApplications.erase(suspendedOrHibernatedIt); + } + gSuspendedOrHibernatedApplicationsMutex.unlock(); +#endif if(service->Reason() == PluginHost::IShell::FAILURE) { gApplicationsExitReason[service->Callsign()] = AppLastExitReason::CRASH; @@ -994,6 +1015,16 @@ namespace WPEFramework { if ((currentState == PluginHost::IShell::DEACTIVATED) || (currentState == PluginHost::IShell::DESTROYED)) { gApplicationsExitReason[service->Callsign()] = AppLastExitReason::DEACTIVATED; +#ifdef HIBERNATE_SUPPORT_ENABLED + //Reset app suspended/hibernated on Deactivation/Destroy + gSuspendedOrHibernatedApplicationsMutex.lock(); + auto suspendedOrHibernatedIt = gSuspendedOrHibernatedApplications.find(service->Callsign()); + if (suspendedOrHibernatedIt != gSuspendedOrHibernatedApplications.end()) + { + gSuspendedOrHibernatedApplications.erase(suspendedOrHibernatedIt); + } + gSuspendedOrHibernatedApplicationsMutex.unlock(); +#endif } if(service->Reason() == PluginHost::IShell::FAILURE) { @@ -3719,6 +3750,16 @@ namespace WPEFramework { setAVBlocked(callsign, blockAV); } } +#ifdef HIBERNATE_SUPPORT_ENABLED + //Reset app suspended/hibernated for launch + gSuspendedOrHibernatedApplicationsMutex.lock(); + auto suspendedOrHibernatedIt = gSuspendedOrHibernatedApplications.find(appCallsign); + if (suspendedOrHibernatedIt != gSuspendedOrHibernatedApplications.end()) + { + gSuspendedOrHibernatedApplications.erase(suspendedOrHibernatedIt); + } + gSuspendedOrHibernatedApplicationsMutex.unlock(); +#endif //check to see if plugin already exists bool newPluginFound = false; @@ -6170,6 +6211,9 @@ namespace WPEFramework { else { eventMsg["success"] = true; + gSuspendedOrHibernatedApplicationsMutex.lock(); + gSuspendedOrHibernatedApplications[callsign] = true; + gSuspendedOrHibernatedApplicationsMutex.unlock(); } notify(RDKShell::RDKSHELL_EVENT_ON_HIBERNATED, eventMsg); }); @@ -6472,14 +6516,34 @@ namespace WPEFramework { std::string compositorName = toLower(previousFocusedIterator->first); if (compositorName == previousFocusedClient) { - std::cout << "setting the focus of " << compositorName << " to false " << std::endl; - Exchange::IFocus *focusedCallsign = mCurrentService->QueryInterfaceByCallsign(previousFocusedIterator->first); - if (focusedCallsign != NULL) +#ifdef HIBERNATE_SUPPORT_ENABLED + // Skip for suspended and hibernated apps, since not needed and may cause unwanted restore from hibernation + bool skipFocus = false; + gSuspendedOrHibernatedApplicationsMutex.lock(); + if (gSuspendedOrHibernatedApplications.find(previousFocusedIterator->first) != gSuspendedOrHibernatedApplications.end()) + { + skipFocus = gSuspendedOrHibernatedApplications[previousFocusedIterator->first]; + } + + if (skipFocus == false) { - uint32_t status = focusedCallsign->Focused(false); - std::cout << "result of set focus to false: " << status << std::endl; - focusedCallsign->Release(); +#endif + std::cout << "setting the focus of " << compositorName << " to false " << std::endl; + Exchange::IFocus *focusedCallsign = mCurrentService->QueryInterfaceByCallsign(previousFocusedIterator->first); + if (focusedCallsign != NULL) + { + uint32_t status = focusedCallsign->Focused(false); + std::cout << "result of set focus to false: " << status << std::endl; + focusedCallsign->Release(); + } +#ifdef HIBERNATE_SUPPORT_ENABLED + } + else + { + std::cout << "setting the focus for " << compositorName << " to false skipped, plugin suspended or hibernated " << std::endl; } + gSuspendedOrHibernatedApplicationsMutex.unlock(); +#endif break; } } From 01269162d60ee28b5c0a7b0124cf78c75ccef8f9 Mon Sep 17 00:00:00 2001 From: Adrian Muzyka Date: Tue, 14 Nov 2023 11:22:46 +0100 Subject: [PATCH 9/9] RDK-43052: Update RDKShell API version and CHANGELOG --- RDKShell/CHANGELOG.md | 3 +++ RDKShell/RDKShell.cpp | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/RDKShell/CHANGELOG.md b/RDKShell/CHANGELOG.md index a014b546b4..80693f86e9 100644 --- a/RDKShell/CHANGELOG.md +++ b/RDKShell/CHANGELOG.md @@ -15,6 +15,9 @@ All notable changes to this RDK Service will be documented in this file. * Changes in CHANGELOG should be updated when commits are added to the main or release branches. There should be one CHANGELOG entry per JIRA Ticket. This is not enforced on sprint branches since there could be multiple changes for the same JIRA ticket during development. * For more details, refer to [versioning](https://github.com/rdkcentral/rdkservices#versioning) section under Main README. +## [1.4.11] - 2023-12-04 +### Added +- Added Hibernate and Restore api and Auto Hibernation for suspended native apps ## [1.4.10] - 2023-10-23 ### Added diff --git a/RDKShell/RDKShell.cpp b/RDKShell/RDKShell.cpp index ba38e8ac44..595531e9e7 100755 --- a/RDKShell/RDKShell.cpp +++ b/RDKShell/RDKShell.cpp @@ -52,7 +52,7 @@ #define API_VERSION_NUMBER_MAJOR 1 #define API_VERSION_NUMBER_MINOR 4 -#define API_VERSION_NUMBER_PATCH 6 +#define API_VERSION_NUMBER_PATCH 11 const string WPEFramework::Plugin::RDKShell::SERVICE_NAME = "org.rdk.RDKShell"; //methods