From 3e25e6d02e0443403e1216e01226b336a4ae6863 Mon Sep 17 00:00:00 2001 From: "zhangchen (M)" Date: Tue, 26 Aug 2025 21:32:41 +0800 Subject: [PATCH 1/3] feat: added interface ForceRebootDevice Signed-off-by: zhangchen Change-Id: I32b6d357d9ef7032998d57631b48dccdca86da96 --- frameworks/native/power_mgr_client.cpp | 53 +++ interfaces/inner_api/IPowerMgr.idl | 1 + .../native/include/power_mgr_client.h | 10 + services/native/include/power_mgr_service.h | 3 +- .../include/power_mgr_service_ipc_adapter.h | 4 +- services/native/src/power_mgr_service.cpp | 12 +- .../src/power_mgr_service_ipc_adapter.cpp | 7 + .../src/shutdown/shutdown_controller.cpp | 58 ++- .../native/src/shutdown/shutdown_controller.h | 4 +- test/apitest/inner_api/shutdown/BUILD.gn | 16 + .../shutdown/shutdown_controller_test.cpp | 59 +++ test/fuzztest/power_utils/power_fuzzer.cpp | 2 +- .../rebootdevice_fuzzer_test.cpp | 2 + test/unittest/BUILD.gn | 22 ++ .../general_interfaces_test.cpp | 338 ++++++++++++++++++ 15 files changed, 566 insertions(+), 25 deletions(-) create mode 100644 test/apitest/inner_api/shutdown/shutdown_controller_test.cpp create mode 100644 test/unittest/src/interface_test/general_interfaces_test.cpp diff --git a/frameworks/native/power_mgr_client.cpp b/frameworks/native/power_mgr_client.cpp index a1a34df6..6d3a9d07 100644 --- a/frameworks/native/power_mgr_client.cpp +++ b/frameworks/native/power_mgr_client.cpp @@ -15,6 +15,8 @@ #include "power_mgr_client.h" +#include +#include #include #include #include @@ -40,6 +42,8 @@ #include "running_lock_info.h" #include "power_mgr_async_reply_stub.h" +#define SET_REBOOT _IOW(BOOT_DETECTOR_IOCTL_BASE, 109, int) + namespace OHOS { namespace PowerMgr { std::vector> PowerMgrClient::runningLocks_; @@ -192,6 +196,55 @@ PowerErrors PowerMgrClient::RebootDeviceForDeprecated(const std::string& reason) return static_cast(powerError); } +namespace { +// mostly copied from power_napi.cpp +// no early return if open fd succeeded +void StartBootTimer(bool isReboot) +{ + int fd = open("/dev/bbox", O_WRONLY); + if (fd < 0) { + POWER_HILOGE(FEATURE_SHUTDOWN, "open /dev/bbox failed!"); + return; + } + + fdsan_exchange_owner_tag(fd, 0, DOMAIN_FEATURE_SHUTDOWN); + POWER_HILOGI(FEATURE_SHUTDOWN, "Set shutdown fw start timeout."); + + int rebootFlag = isReboot ? 1 : 0; + int ret = ioctl(fd, SET_REBOOT, &rebootFlag); + if (ret < 0) { + POWER_HILOGE(FEATURE_SHUTDOWN, "set reboot flag failed!"); + } + + int stage = SHUT_STAGE_FRAMEWORK_START; + ret = ioctl(fd, SET_SHUT_STAGE, &stage); + if (ret < 0) { + POWER_HILOGE(FEATURE_SHUTDOWN, "set SHUT_STAGE_FRAMEWORK_START failed!"); + } + + stage = SHUT_STAGE_FRAMEWORK_FINISH; + ret = ioctl(fd, SET_SHUT_STAGE, &stage); + if (ret < 0) { + POWER_HILOGE(FEATURE_SHUTDOWN, "set SHUT_STAGE_FRAMEWORK_FINISH failed!"); + } + + POWER_HILOGI(FEATURE_SHUTDOWN, "Set shutdown timeout mechanism started."); + fdsan_close_with_tag(fd, DOMAIN_FEATURE_SHUTDOWN); + + return; +} +} + +PowerErrors PowerMgrClient::ForceRebootDevice(const std::string& reason) +{ + StartBootTimer(true); + sptr proxy = GetPowerMgrProxy(); + RETURN_IF_WITH_RET(proxy == nullptr, PowerErrors::ERR_CONNECTION_FAIL); + int32_t powerError = static_cast(PowerErrors::ERR_CONNECTION_FAIL); + proxy->ForceRebootDeviceIpc(reason, powerError); + return static_cast(powerError); +} + PowerErrors PowerMgrClient::ShutDownDevice(const std::string& reason) { sptr proxy = GetPowerMgrProxy(); diff --git a/interfaces/inner_api/IPowerMgr.idl b/interfaces/inner_api/IPowerMgr.idl index 9f95343f..3db3f588 100644 --- a/interfaces/inner_api/IPowerMgr.idl +++ b/interfaces/inner_api/IPowerMgr.idl @@ -47,6 +47,7 @@ interface OHOS.PowerMgr.IPowerMgr { // Used for power state machine. void RebootDeviceIpc([in] String reason, [out] int powerError); void RebootDeviceForDeprecatedIpc([in] String reason, [out] int powerError); + void ForceRebootDeviceIpc([in] String reason, [out] int powerError); void ShutDownDeviceIpc([in] String reason, [out] int powerError); void SetSuspendTagIpc([in] String tag, [out] int powerError); void SuspendDeviceIpc([in] long callTimeMs, [in] int reasonValue, [in] boolean suspendImmed, [in] String apiVersion, diff --git a/interfaces/inner_api/native/include/power_mgr_client.h b/interfaces/inner_api/native/include/power_mgr_client.h index 4b77ffc3..d80ee2fe 100644 --- a/interfaces/inner_api/native/include/power_mgr_client.h +++ b/interfaces/inner_api/native/include/power_mgr_client.h @@ -40,6 +40,16 @@ public: PowerErrors RebootDevice(const std::string& reason); PowerErrors RebootDeviceForDeprecated(const std::string& reason); + /** + * @brief Forces the device to reboot. + * Ignores registered takeOverShutdownCallback, set time limits for executing other callbacks. + * Guarantees the invocation of function "DoRebootExt" provided by startup_init within 60 seconds. + * A Timeout will trigger reboot in kernel if the caller has the permission to set reboot stage. + * + * @param reason The reason for rebooting the device. e.g.updater + */ + PowerErrors ForceRebootDevice(const std::string& reason); + /** * Shut down the device. * diff --git a/services/native/include/power_mgr_service.h b/services/native/include/power_mgr_service.h index a0e13df7..cd390315 100644 --- a/services/native/include/power_mgr_service.h +++ b/services/native/include/power_mgr_service.h @@ -80,7 +80,8 @@ public: virtual void OnAddSystemAbility(int32_t systemAbilityId, const std::string& deviceId) override; int32_t Dump(int32_t fd, const std::vector& args) override; virtual PowerErrors RebootDevice(const std::string& reason) override; - virtual PowerErrors RebootDeviceForDeprecated(const std::string& reason) override; + virtual PowerErrors RebootDeviceForDeprecated(const std::string& reason, bool force = false) override; + virtual PowerErrors ForceRebootDevice(const std::string& reason) override; virtual PowerErrors ShutDownDevice(const std::string& reason) override; virtual PowerErrors SetSuspendTag(const std::string& tag) override; virtual PowerErrors SuspendDevice(int64_t callTimeMs, SuspendDeviceType reason, diff --git a/services/native/include/power_mgr_service_ipc_adapter.h b/services/native/include/power_mgr_service_ipc_adapter.h index a6d25e56..caba397d 100644 --- a/services/native/include/power_mgr_service_ipc_adapter.h +++ b/services/native/include/power_mgr_service_ipc_adapter.h @@ -27,6 +27,7 @@ class PowerMgrServiceAdapter : public PowerMgrStub { public: virtual int32_t RebootDeviceIpc(const std::string& reason, int32_t& powerError) override; virtual int32_t RebootDeviceForDeprecatedIpc(const std::string& reason, int32_t& powerError) override; + virtual int32_t ForceRebootDeviceIpc(const std::string& reason, int32_t& powerError) override; virtual int32_t ShutDownDeviceIpc(const std::string& reason, int32_t& powerError) override; virtual int32_t SetSuspendTagIpc(const std::string& tag, int32_t& powerError) override; virtual int32_t SuspendDeviceIpc(int64_t callTimeMs, int32_t reasonValue, bool suspendImmed, @@ -107,7 +108,8 @@ public: virtual int32_t SetPowerKeyFilteringStrategyIpc(int32_t strategy, int32_t& powerError) override; virtual PowerErrors RebootDevice(const std::string& reason) = 0; - virtual PowerErrors RebootDeviceForDeprecated(const std::string& reason) = 0; + virtual PowerErrors RebootDeviceForDeprecated(const std::string& reason, bool force = false) = 0; + virtual PowerErrors ForceRebootDevice(const std::string& reason) = 0; virtual PowerErrors ShutDownDevice(const std::string& reason) = 0; virtual PowerErrors SetSuspendTag(const std::string& tag) = 0; virtual PowerErrors SuspendDevice(int64_t callTimeMs, SuspendDeviceType reason, diff --git a/services/native/src/power_mgr_service.cpp b/services/native/src/power_mgr_service.cpp index dcd4135e..230a4d11 100644 --- a/services/native/src/power_mgr_service.cpp +++ b/services/native/src/power_mgr_service.cpp @@ -1046,7 +1046,7 @@ PowerErrors PowerMgrService::RebootDevice(const std::string& reason) return RebootDeviceForDeprecated(reason); } -PowerErrors PowerMgrService::RebootDeviceForDeprecated(const std::string& reason) +PowerErrors PowerMgrService::RebootDeviceForDeprecated(const std::string& reason, bool force) { std::lock_guard lock(shutdownMutex_); pid_t pid = IPCSkeleton::GetCallingPid(); @@ -1061,10 +1061,18 @@ PowerErrors PowerMgrService::RebootDeviceForDeprecated(const std::string& reason suspendController_->StopSleep(); } POWER_KHILOGI(FEATURE_SHUTDOWN, "Do reboot, called pid: %{public}d, uid: %{public}d", pid, uid); - shutdownController_->Reboot(reason); + shutdownController_->Reboot(reason, force); return PowerErrors::ERR_OK; } +PowerErrors PowerMgrService::ForceRebootDevice(const std::string& reason) +{ + if (!Permission::IsSystem()) { + return PowerErrors::ERR_SYSTEM_API_DENIED; + } + return RebootDeviceForDeprecated(reason, true); +} + PowerErrors PowerMgrService::ShutDownDevice(const std::string& reason) { auto now = static_cast(time(nullptr)); diff --git a/services/native/src/power_mgr_service_ipc_adapter.cpp b/services/native/src/power_mgr_service_ipc_adapter.cpp index 54aec0ff..ba8f4159 100644 --- a/services/native/src/power_mgr_service_ipc_adapter.cpp +++ b/services/native/src/power_mgr_service_ipc_adapter.cpp @@ -40,6 +40,13 @@ int32_t PowerMgrServiceAdapter::RebootDeviceForDeprecatedIpc(const std::string& return ERR_OK; } +int32_t PowerMgrServiceAdapter::ForceRebootDeviceIpc(const std::string& reason, int32_t& powerError) +{ + PowerXCollie powerXCollie("PowerMgrServiceAdapter::RebootDevice", false); + powerError = static_cast(ForceRebootDevice(reason)); + return ERR_OK; +} + int32_t PowerMgrServiceAdapter::ShutDownDeviceIpc(const std::string& reason, int32_t& powerError) { PowerXCollie powerXCollie("PowerMgrServiceAdapter::ShutDownDevice", false); diff --git a/services/native/src/shutdown/shutdown_controller.cpp b/services/native/src/shutdown/shutdown_controller.cpp index a6b1c026..fc538ccc 100644 --- a/services/native/src/shutdown/shutdown_controller.cpp +++ b/services/native/src/shutdown/shutdown_controller.cpp @@ -47,6 +47,8 @@ using namespace std; namespace OHOS { namespace PowerMgr { namespace { +constexpr std::chrono::seconds CALLBACK_TIMEOUT = 10s; +constexpr std::chrono::seconds OFF_TIMEOUT = 5s; const time_t MAX_TIMEOUT_SEC = 30; #ifdef POWER_MANAGER_ENABLE_JUDGING_TAKEOVER_SHUTDOWN const vector REASONS_DISABLE_TAKE_OVER = {"LowCapacity", "HibernateFail"}; @@ -71,9 +73,9 @@ ShutdownController::ShutdownController() : started_(false) syncShutdownCallbackHolder_ = new ShutdownCallbackHolder(); } -void ShutdownController::Reboot(const std::string& reason) +void ShutdownController::Reboot(const std::string& reason, bool force) { - RebootOrShutdown(reason, true); + RebootOrShutdown(reason, true, force); } void ShutdownController::Shutdown(const std::string& reason) @@ -109,22 +111,32 @@ static void SetFrameworkFinishBootStage(void) return; } -void ShutdownController::RebootOrShutdown(const std::string& reason, bool isReboot) +namespace { +std::future g_futForSyncCb; +std::future g_futForOff; +} + +void ShutdownController::RebootOrShutdown(const std::string& reason, bool isReboot, bool force) { - if (started_) { - POWER_HILOGW(FEATURE_SHUTDOWN, "Shutdown is already running"); - return; + // skip takeover callback + // SetFrameworkFinishBootStage done in client + if (!force) { + if (started_) { + POWER_HILOGW(FEATURE_SHUTDOWN, "Shutdown is already running"); + return; + } + started_ = true; + bool isTakeOver = TakeOverShutdownAction(reason, isReboot); + if (isTakeOver) { + started_ = false; + return; + } + if (reason != "test_case") { + SetFrameworkFinishBootStage(); + } } started_ = true; - bool isTakeOver = TakeOverShutdownAction(reason, isReboot); - if (isTakeOver) { - started_ = false; - return; - } POWER_KHILOGI(FEATURE_SHUTDOWN, "Start to detach shutdown thread"); - if (reason != "test_case") { - SetFrameworkFinishBootStage(); - } #ifdef HAS_HIVIEWDFX_HISYSEVENT_PART HiSysEventWrite(HiviewDFX::HiSysEvent::Domain::POWER, "STATE", HiviewDFX::HiSysEvent::EventType::STATISTIC, "STATE", static_cast(PowerState::SHUTDOWN)); @@ -133,9 +145,19 @@ void ShutdownController::RebootOrShutdown(const std::string& reason, bool isRebo std::string actionTimeStr = std::to_string(GetCurrentRealTimeMs()); PowerEventType eventType = isReboot ? PowerEventType::REBOOT : PowerEventType::SHUTDOWN; system::SetParameter("persist.dfx.eventtype", to_string(eventType)); - TriggerSyncShutdownCallback(isReboot); - actionTimeStr = actionTimeStr + "," + std::to_string(GetCurrentRealTimeMs()); - TurnOffScreen(); + if (force) { + // only two threads are allowed, next time it blocks + // either block or thread leak. since the previous action is shutdown, block is safer than thread leak + g_futForSyncCb = async(launch::async, &ShutdownController::TriggerSyncShutdownCallback, this, isReboot); + g_futForOff = async(launch::async, &ShutdownController::TurnOffScreen, this); + g_futForSyncCb.wait_for(CALLBACK_TIMEOUT); + actionTimeStr = actionTimeStr + "," + std::to_string(GetCurrentRealTimeMs()); + g_futForOff.wait_for(OFF_TIMEOUT); + } else { + TriggerSyncShutdownCallback(isReboot); + actionTimeStr = actionTimeStr + "," + std::to_string(GetCurrentRealTimeMs()); + TurnOffScreen(); + } actionTimeStr = actionTimeStr + "," + std::to_string(GetCurrentRealTimeMs()); system::SetParameter("persist.dfx.shutdownactiontime", actionTimeStr); make_unique([=] { @@ -310,7 +332,7 @@ void ShutdownController::TriggerAsyncShutdownCallback(bool isReboot) void ShutdownController::TriggerAsyncShutdownCallbackInner(std::set>& callbacks, bool isReboot) { - for (auto &obj : callbacks) { + for (const auto &obj : callbacks) { auto pidUid = asyncShutdownCallbackHolder_->FindCallbackPidUid(obj); sptr callback = iface_cast(obj); if (callback != nullptr) { diff --git a/services/native/src/shutdown/shutdown_controller.h b/services/native/src/shutdown/shutdown_controller.h index 70a7f3d8..8da88bd4 100644 --- a/services/native/src/shutdown/shutdown_controller.h +++ b/services/native/src/shutdown/shutdown_controller.h @@ -35,7 +35,7 @@ public: ShutdownController(); virtual ~ShutdownController() = default; - virtual void Reboot(const std::string& reason); + virtual void Reboot(const std::string& reason, bool force = false); virtual void Shutdown(const std::string& reason); bool IsShuttingDown(); void EnableMock(IDevicePowerAction* mockPowerAction, IDeviceStateAction* mockStateAction) @@ -61,7 +61,7 @@ public: private: using IntentWant = OHOS::AAFwk::Want; - void RebootOrShutdown(const std::string& reason, bool isReboot); + void RebootOrShutdown(const std::string& reason, bool isReboot, bool force = false); void Prepare(bool isReboot); void TurnOffScreen(); void PublishShutdownEvent() const; diff --git a/test/apitest/inner_api/shutdown/BUILD.gn b/test/apitest/inner_api/shutdown/BUILD.gn index ff0a01cf..fcf0e007 100644 --- a/test/apitest/inner_api/shutdown/BUILD.gn +++ b/test/apitest/inner_api/shutdown/BUILD.gn @@ -110,10 +110,26 @@ ohos_unittest("sync_shutdown_callback_test") { module_out_path = module_output_path } +ohos_unittest("shutdown_controller_test") { + sources = [ "shutdown_controller_test.cpp" ] + configs = [ + "${powermgr_utils_path}:utils_config", + ":module_private_config", + "${powermgr_utils_path}:coverage_flags", + ] + deps = [ + "${powermgr_inner_api}:powermgr_client", + "${powermgr_service_path}:powermgrservice", + ] + external_deps = deps_ex + module_out_path = module_output_path +} + group("unittest") { testonly = true deps = [ ":async_shutdown_callback_test", + ":shutdown_controller_test", ":sync_shutdown_callback_test", ":takeover_shutdown_callback_test", ] diff --git a/test/apitest/inner_api/shutdown/shutdown_controller_test.cpp b/test/apitest/inner_api/shutdown/shutdown_controller_test.cpp new file mode 100644 index 00000000..07756fa7 --- /dev/null +++ b/test/apitest/inner_api/shutdown/shutdown_controller_test.cpp @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * 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 +#include +#include +#include +#include +#include + +namespace OHOS::PowerMgr { +class ShutDownControllerTest : public testing::Test { +public: + bool called = false; + class TakeOverShutdownCallback : public TakeOverShutdownCallbackStub { + public: + ~TakeOverShutdownCallback() override = default; + bool OnTakeOverShutdown(const TakeOverInfo& info) override + { + return true; + } + }; +}; + +namespace { +using namespace OHOS; +using namespace PowerMgr; +using namespace testing; +using namespace ext; +HWTEST_F(ShutDownControllerTest, ShutDownControllerTest001, TestSize.Level0) +{ + POWER_HILOGI(LABEL_TEST, "ShutdownControllerTest001 function start!"); + sptr callback = sptr::MakeSptr(); + NiceMock* stateActionMock = new NiceMock; + NiceMock* powerActionMock = new NiceMock; + ShutdownController controller; + controller.EnableMock(powerActionMock, stateActionMock); + controller.AddCallback(callback, ShutdownPriority::DEFAULT); + EXPECT_CALL(*powerActionMock, Reboot("::testing::_")).Times(0); + controller.Reboot("test_case"); + EXPECT_CALL(*powerActionMock, Reboot("test_case")); + controller.Reboot("test_case", true); + sleep(1); + POWER_HILOGI(LABEL_TEST, "ShutdownControllerTest001 function end!"); +} +} +} \ No newline at end of file diff --git a/test/fuzztest/power_utils/power_fuzzer.cpp b/test/fuzztest/power_utils/power_fuzzer.cpp index bdbf0b98..cfa149a9 100644 --- a/test/fuzztest/power_utils/power_fuzzer.cpp +++ b/test/fuzztest/power_utils/power_fuzzer.cpp @@ -45,7 +45,7 @@ class FuzzShutdownAction : public ShutdownController { public: FuzzShutdownAction() = default; virtual ~FuzzShutdownAction() = default; - virtual void Reboot([[maybe_unused]] const std::string& reason) {}; + virtual void Reboot([[maybe_unused]] const std::string& reason, bool force = false) {}; virtual void Shutdown([[maybe_unused]] const std::string& reason) {}; }; diff --git a/test/fuzztest/rebootdevice_fuzzer/rebootdevice_fuzzer_test.cpp b/test/fuzztest/rebootdevice_fuzzer/rebootdevice_fuzzer_test.cpp index 084ffe35..edb0704a 100644 --- a/test/fuzztest/rebootdevice_fuzzer/rebootdevice_fuzzer_test.cpp +++ b/test/fuzztest/rebootdevice_fuzzer/rebootdevice_fuzzer_test.cpp @@ -33,5 +33,7 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) g_serviceTest.TestPowerServiceStub(static_cast(IPowerMgrIpcCode::COMMAND_REBOOT_DEVICE_IPC), data, size); g_serviceTest.TestPowerServiceStub( static_cast(IPowerMgrIpcCode::COMMAND_REBOOT_DEVICE_FOR_DEPRECATED_IPC), data, size); + g_serviceTest.TestPowerServiceStub( + static_cast(IPowerMgrIpcCode::COMMAND_FORCE_REBOOT_DEVICE_IPC), data, size); return 0; } diff --git a/test/unittest/BUILD.gn b/test/unittest/BUILD.gn index 6835480c..5e45bac3 100644 --- a/test/unittest/BUILD.gn +++ b/test/unittest/BUILD.gn @@ -28,6 +28,7 @@ config("module_private_config") { "include/scenario_test/wakeup_suspend", "mock", "include/utils", + "${powermgr_framework_native}", "${powermgr_service_zidl}/include", "${powermgr_service_zidl}/src/shutdown", "${powermgr_service_path}/native/include", @@ -1420,6 +1421,26 @@ ohos_unittest("test_power_suspend_takeover") { external_deps = deps_ex } +ohos_unittest("test_general_interfaces") { + module_out_path = module_output_path + + sources = [ "src/interface_test/general_interfaces_test.cpp" ] + + configs = [ + "${powermgr_utils_path}:utils_config", + ":module_private_config", + "${powermgr_utils_path}:coverage_flags", + ] + + deps = [ + "${powermgr_inner_api}:powermgr_client", + "${powermgr_service_path}:powermgrservice", + "${powermgr_utils_path}/permission:power_permission", + ] + + external_deps = deps_ex +} + ohos_unittest("test_power_getcontroller_mock") { module_out_path = module_output_path @@ -1794,6 +1815,7 @@ group("unittest") { ":power_mode_module_native_test", ":power_state_machine_native_test", ":test_device_power_action", + ":test_general_interfaces", ":test_mock_parcel", ":test_mock_peer", ":test_mock_proxy", diff --git a/test/unittest/src/interface_test/general_interfaces_test.cpp b/test/unittest/src/interface_test/general_interfaces_test.cpp new file mode 100644 index 00000000..d3a32a07 --- /dev/null +++ b/test/unittest/src/interface_test/general_interfaces_test.cpp @@ -0,0 +1,338 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define SET_REBOOT _IOW(BOOT_DETECTOR_IOCTL_BASE, 109, int) +#define SET_SHUT_STAGE _IOW(BOOT_DETECTOR_IOCTL_BASE, 106, int) + +using namespace testing; +using namespace ext; +using namespace OHOS; +using namespace PowerMgr; + +namespace { +unsigned int g_retCount = 0; +std::vector g_retVec {}; +bool g_boolRet = true; +constexpr const char* PATH = "/dev/bbox"; +pid_t g_testingThreadId = 0; +bool g_isTesting = false; + +bool IsCallFromTesting() +{ + return g_isTesting && gettid() == g_testingThreadId; +} +} // namespace + +// redefinitions of C outer interfaces +extern "C" { +void fdsan_exchange_owner_tag(int fd, uint64_t expected_tag, uint64_t new_tag) +{ + static auto realFdsanExchange = + reinterpret_cast(dlsym(RTLD_NEXT, "fdsan_exchange_owner_tag")); + // In this test (for now), the fake "open" will never return an fd > 0. + // Remove the last condition if it is not the case. + if (realFdsanExchange && (new_tag != DOMAIN_FEATURE_SHUTDOWN || !IsCallFromTesting() || fd > 0)) { + realFdsanExchange(fd, expected_tag, new_tag); + } +} + +int fdsan_close_with_tag(int fd, uint64_t tag) +{ + static auto realFdsanClose = reinterpret_cast(dlsym(RTLD_NEXT, "fdsan_close_with_tag")); + if (tag != DOMAIN_FEATURE_SHUTDOWN || !IsCallFromTesting() || fd > 0) { + return realFdsanClose ? realFdsanClose(fd, tag) : close(fd); + } + return 0; +} + +// dlsym does not depend on open so it should be safe to call it inside my interceptor +int open(const char* path, int flag, ...) +{ + static auto realOpen = reinterpret_cast(dlsym(RTLD_NEXT, "open")); + if (!IsCallFromTesting() || std::string {path} != PATH) { + mode_t mode = 0; + if ((flag & O_CREAT) || (flag & O_TMPFILE) == O_TMPFILE) { + va_list args; + va_start(args, flag); + mode = va_arg(args, mode_t); + va_end(args); + return realOpen ? realOpen(path, flag, mode) : openat(AT_FDCWD, path, flag, mode); + } + return realOpen ? realOpen(path, flag) : openat(AT_FDCWD, path, flag); + } + + POWER_HILOGI(LABEL_TEST, "mock open called"); + if (g_retCount < g_retVec.size()) { + return g_retVec[g_retCount++]; + } + return 0; +} + +int ioctl(int fd, int cmd, ...) +{ + static auto realIoctl = reinterpret_cast(dlsym(RTLD_NEXT, "ioctl")); + if (realIoctl && (!IsCallFromTesting() || (cmd != SET_REBOOT && cmd != SET_SHUT_STAGE))) { + void* param; + va_list args; + va_start(args, cmd); + param = va_arg(args, void*); + va_end(args); + return realIoctl(fd, cmd, param); + } + + POWER_HILOGI(LABEL_TEST, "mock ioctl called"); + if (g_retCount < g_retVec.size()) { + return g_retVec[g_retCount++]; + } + return 0; +} +} // extern "C" + +namespace OHOS::PowerMgr { +class GeneralInterfacesTest : public Test { +public: + class MockProxy : public PowerMgrProxy { + public: + using PowerMgrProxy::PowerMgrProxy; + MOCK_METHOD2(ForceRebootDeviceIpc, int32_t(const std::string&, int32_t&)); + }; + + void SetProxyMockState(bool enable) + { + proxyMockEnabled = enable; + } + + void SetUp(void) + { + // will be passed to smart pointers in EnableMock + if (!stateActionMock || !shutdownStateActionMock || !powerActionMock || !lockActionMock) { + stateActionMock = new NiceMock; + shutdownStateActionMock = new NiceMock; + powerActionMock = new NiceMock; + lockActionMock = new NiceMock; + stub_->EnableMock(stateActionMock, shutdownStateActionMock, powerActionMock, lockActionMock); + } + g_boolRet = true; // controls Permission, refactor it later if I had time + } + + void TearDown(void) + { + constexpr int maxTick = 100; + constexpr int tickTimeUs = 1000 * 50; + // try to wait for the detached thread to complete + for (int tick = 0; tick < maxTick; tick++) { + if (!stub_->GetShutdownController()->IsShuttingDown()) { + break; + } + usleep(tickTimeUs); + } + + // try to release the mock objects just before end of the current testcase + if (!stub_->GetShutdownController()->IsShuttingDown()) { + stub_->EnableMock(nullptr, nullptr, nullptr, nullptr); + // EnableMock for PowerStateMachine ignores nullptr, reset it manually + auto& stateAction = + const_cast&>(stub_->GetPowerStateMachine()->GetStateAction()); + stateAction.reset(); + stateActionMock = nullptr; + shutdownStateActionMock = nullptr; + powerActionMock = nullptr; + lockActionMock = nullptr; + } + } + + static void SetUpTestCase(void) + { + stub_ = DelayedSpSingleton::GetInstance(); + stub_->OnStart(); + mockProxy_ = sptr>::MakeSptr(stub_); + g_testingThreadId = gettid(); + } + + static void TearDownTestCase(void) {} + + // data members + NiceMock* stateActionMock {nullptr}; + NiceMock* shutdownStateActionMock {nullptr}; + NiceMock* powerActionMock {nullptr}; + NiceMock* lockActionMock {nullptr}; + + static inline bool proxyMockEnabled = false; + static inline sptr stub_ {nullptr}; + static inline sptr> mockProxy_ {nullptr}; +}; + +bool PowerMgr::Permission::IsSystem() +{ + return g_boolRet; +} + +bool PowerMgr::Permission::IsPermissionGranted(const std::string&) +{ + return true; +} + +sptr PowerMgrClient::GetPowerMgrProxy() +{ + if (GeneralInterfacesTest::proxyMockEnabled) { + return GeneralInterfacesTest::mockProxy_; + } else { + return GeneralInterfacesTest::stub_; + } +} + +sptr ClientLifeCycle::GetProxy() +{ + if (GeneralInterfacesTest::proxyMockEnabled) { + return GeneralInterfacesTest::mockProxy_; + } else { + return GeneralInterfacesTest::stub_; + } +} + +// test cases below +namespace { +// cover branches of client side call +HWTEST_F(GeneralInterfacesTest, ForceRebootDeviceTest001, TestSize.Level0) +{ + POWER_HILOGI(LABEL_TEST, "ForceRebootDeviceTest001 function start!"); + + SetProxyMockState(true); + + auto& powerMgrClient = PowerMgrClient::GetInstance(); + ASSERT_TRUE(powerActionMock != nullptr); + constexpr int retVecSize = 4; + for (unsigned int cases = 0; cases < (1 << retVecSize); cases++) { + g_retCount = 0; + g_retVec.clear(); + for (int index = 0; index < retVecSize; index++) { + g_retVec.emplace_back((cases & (1 << index)) == 0 ? -1 : 0); + } + EXPECT_CALL(*mockProxy_, ForceRebootDeviceIpc("Some Reason", _)); + g_isTesting = true; + powerMgrClient.ForceRebootDevice("Some Reason"); + g_isTesting = false; + } + + SetProxyMockState(false); + // mock object released, EXPECT_CALL is checked here. Consider moving construction/destruction to setup/teardown + mockProxy_ = nullptr; + + POWER_HILOGI(LABEL_TEST, "ForceRebootDeviceTest001 function end!"); +} + +std::mutex g_cvMutex; +std::condition_variable g_cv; +bool g_notified = false; + +HWTEST_F(GeneralInterfacesTest, ForceRebootDeviceTest002, TestSize.Level0) +{ + // cover some branches which are unlikely to be accessed + POWER_HILOGI(LABEL_TEST, "ForceRebootDeviceTest002 function start!"); + auto& powerMgrClient = PowerMgrClient::GetInstance(); + + g_isTesting = true; + + // Make permission check to fail + g_boolRet = false; + ASSERT_TRUE(powerActionMock != nullptr); + EXPECT_CALL(*powerActionMock, Reboot(_)).Times(0); + powerMgrClient.ForceRebootDevice("Some Reason"); + g_boolRet = true; + + // Non-force and reason != "test_case" + EXPECT_CALL(*powerActionMock, Reboot("Some Reason")); + powerMgrClient.RebootDevice("Some Reason"); + + // Non-force and reason != "test_case" + EXPECT_CALL(*powerActionMock, Reboot("test_case")); + powerMgrClient.RebootDevice("test_case"); + + // Reboot blocks so that the next call will be skipped(since started_ == true), for next non-force reboot + EXPECT_CALL(*powerActionMock, Reboot("Some Reason")).WillOnce(Invoke([]() { + POWER_HILOGI(LABEL_TEST, "blocking reboot action called"); + std::unique_lock lock(g_cvMutex); + g_cv.wait(lock, []() { return g_notified; }); + g_notified = false; + POWER_HILOGI(LABEL_TEST, " reboot action unblocked"); + })); + // first call, reboot blocks + powerMgrClient.RebootDevice("Some Reason"); + // second call, return early + powerMgrClient.RebootDevice("Some Reason"); + g_isTesting = false; + // unblocks the blocking reboot + { + std::lock_guard lock(g_cvMutex); + g_notified = true; + g_cv.notify_all(); + } + POWER_HILOGI(LABEL_TEST, "ForceRebootDeviceTest002 function end!"); +} + +class SyncShutdownCallback : public SyncShutdownCallbackStub { +public: + ~SyncShutdownCallback() override = default; + void OnSyncShutdownOrReboot(bool) override + { + std::unique_lock lock(g_cvMutex); + g_cv.wait(lock, []() { return g_notified; }); + } +}; + +// test async process when calling ForceReboot +// callback blocks, reboot unblocks +HWTEST_F(GeneralInterfacesTest, ForceRebootDeviceTest003, TestSize.Level0) +{ + POWER_HILOGI(LABEL_TEST, "ForceRebootDeviceTest003 function start!"); + sptr callback = sptr::MakeSptr(); + ShutdownClient::GetInstance().RegisterShutdownCallback(callback); + ASSERT_TRUE(powerActionMock != nullptr); + EXPECT_CALL(*powerActionMock, Reboot("Some Reason")).WillOnce(Invoke([]() { + POWER_HILOGI(LABEL_TEST, "unblocking reboot action called"); + std::unique_lock lock(g_cvMutex); + g_notified = true; + g_cv.notify_all(); + })); + + auto& powerMgrClient = PowerMgrClient::GetInstance(); + g_isTesting = true; + powerMgrClient.ForceRebootDevice("Some Reason"); + g_isTesting = false; + { + std::unique_lock lock(g_cvMutex); + EXPECT_FALSE(g_notified); + // only be unblocked after Reboot is called + g_cv.wait(lock, []() { return g_notified; }); + } + POWER_HILOGI(LABEL_TEST, "ForceRebootDeviceTest003 function end!"); +} +} // namespace +} // namespace OHOS::PowerMgr -- Gitee From 85af3675ddc8cf8c2ff068ef0e5146702128a74e Mon Sep 17 00:00:00 2001 From: z30053694 Date: Sat, 30 Aug 2025 14:31:51 +0800 Subject: [PATCH 2/3] fix: fix ut Change-Id: I0972042b98d5821ec511a7ab805a87eba67413fe Signed-off-by: z30053694 --- .../general_interfaces_test.cpp | 44 ++++++++++++------- 1 file changed, 27 insertions(+), 17 deletions(-) diff --git a/test/unittest/src/interface_test/general_interfaces_test.cpp b/test/unittest/src/interface_test/general_interfaces_test.cpp index d3a32a07..3e19a307 100644 --- a/test/unittest/src/interface_test/general_interfaces_test.cpp +++ b/test/unittest/src/interface_test/general_interfaces_test.cpp @@ -129,6 +129,20 @@ public: proxyMockEnabled = enable; } + void WaitForDetachedThread(int maxTick = 100) + { + constexpr int tickTimeUs = 1000 * 50; + if (!stub_) { + return; + } + for (int tick = 0; tick < maxTick; tick++) { + if (!stub_->GetShutdownController()->IsShuttingDown()) { + break; + } + usleep(tickTimeUs); + } + } + void SetUp(void) { // will be passed to smart pointers in EnableMock @@ -144,16 +158,7 @@ public: void TearDown(void) { - constexpr int maxTick = 100; - constexpr int tickTimeUs = 1000 * 50; - // try to wait for the detached thread to complete - for (int tick = 0; tick < maxTick; tick++) { - if (!stub_->GetShutdownController()->IsShuttingDown()) { - break; - } - usleep(tickTimeUs); - } - + WaitForDetachedThread(); // try to release the mock objects just before end of the current testcase if (!stub_->GetShutdownController()->IsShuttingDown()) { stub_->EnableMock(nullptr, nullptr, nullptr, nullptr); @@ -238,6 +243,7 @@ HWTEST_F(GeneralInterfacesTest, ForceRebootDeviceTest001, TestSize.Level0) EXPECT_CALL(*mockProxy_, ForceRebootDeviceIpc("Some Reason", _)); g_isTesting = true; powerMgrClient.ForceRebootDevice("Some Reason"); + WaitForDetachedThread(); g_isTesting = false; } @@ -265,28 +271,31 @@ HWTEST_F(GeneralInterfacesTest, ForceRebootDeviceTest002, TestSize.Level0) ASSERT_TRUE(powerActionMock != nullptr); EXPECT_CALL(*powerActionMock, Reboot(_)).Times(0); powerMgrClient.ForceRebootDevice("Some Reason"); + WaitForDetachedThread(); g_boolRet = true; // Non-force and reason != "test_case" - EXPECT_CALL(*powerActionMock, Reboot("Some Reason")); - powerMgrClient.RebootDevice("Some Reason"); + EXPECT_CALL(*powerActionMock, Reboot("Some Reason a")); + powerMgrClient.RebootDevice("Some Reason a"); + WaitForDetachedThread(); - // Non-force and reason != "test_case" + // Non-force and reason == "test_case" EXPECT_CALL(*powerActionMock, Reboot("test_case")); powerMgrClient.RebootDevice("test_case"); + WaitForDetachedThread(); // Reboot blocks so that the next call will be skipped(since started_ == true), for next non-force reboot - EXPECT_CALL(*powerActionMock, Reboot("Some Reason")).WillOnce(Invoke([]() { + EXPECT_CALL(*powerActionMock, Reboot("Some Reason b")).WillOnce(Invoke([]() { POWER_HILOGI(LABEL_TEST, "blocking reboot action called"); std::unique_lock lock(g_cvMutex); g_cv.wait(lock, []() { return g_notified; }); g_notified = false; POWER_HILOGI(LABEL_TEST, " reboot action unblocked"); })); - // first call, reboot blocks - powerMgrClient.RebootDevice("Some Reason"); + // first call, reboot blocks. Do not add EXPECT_CALL before this call to mock object ends + powerMgrClient.RebootDevice("Some Reason b"); // second call, return early - powerMgrClient.RebootDevice("Some Reason"); + powerMgrClient.RebootDevice("Some Reason c"); g_isTesting = false; // unblocks the blocking reboot { @@ -294,6 +303,7 @@ HWTEST_F(GeneralInterfacesTest, ForceRebootDeviceTest002, TestSize.Level0) g_notified = true; g_cv.notify_all(); } + WaitForDetachedThread(); POWER_HILOGI(LABEL_TEST, "ForceRebootDeviceTest002 function end!"); } -- Gitee From 83837f9e6b10b455ca3ce90ccee53206b3a1028d Mon Sep 17 00:00:00 2001 From: z30053694 Date: Fri, 5 Sep 2025 15:29:07 +0800 Subject: [PATCH 3/3] fix: modified UT Signed-off-by: z30053694 Change-Id: I9f2f4d6ab1bd99e5f8f47bed1c42919ed5b131be --- frameworks/native/power_mgr_client.cpp | 1 + power_dialog/hvigorw | 0 .../src/shutdown/shutdown_controller.cpp | 4 +- .../shutdown/shutdown_controller_test.cpp | 20 +++++++-- .../general_interfaces_test.cpp | 44 +++---------------- 5 files changed, 24 insertions(+), 45 deletions(-) mode change 100644 => 100755 power_dialog/hvigorw diff --git a/frameworks/native/power_mgr_client.cpp b/frameworks/native/power_mgr_client.cpp index 6d3a9d07..c330b0b5 100644 --- a/frameworks/native/power_mgr_client.cpp +++ b/frameworks/native/power_mgr_client.cpp @@ -237,6 +237,7 @@ void StartBootTimer(bool isReboot) PowerErrors PowerMgrClient::ForceRebootDevice(const std::string& reason) { + POWER_HILOGI(FEATURE_SHUTDOWN, "ForceRebootDevice client side"); StartBootTimer(true); sptr proxy = GetPowerMgrProxy(); RETURN_IF_WITH_RET(proxy == nullptr, PowerErrors::ERR_CONNECTION_FAIL); diff --git a/power_dialog/hvigorw b/power_dialog/hvigorw old mode 100644 new mode 100755 diff --git a/services/native/src/shutdown/shutdown_controller.cpp b/services/native/src/shutdown/shutdown_controller.cpp index fc538ccc..4b499a66 100644 --- a/services/native/src/shutdown/shutdown_controller.cpp +++ b/services/native/src/shutdown/shutdown_controller.cpp @@ -122,7 +122,7 @@ void ShutdownController::RebootOrShutdown(const std::string& reason, bool isRebo // SetFrameworkFinishBootStage done in client if (!force) { if (started_) { - POWER_HILOGW(FEATURE_SHUTDOWN, "Shutdown is already running"); + POWER_KHILOGI(FEATURE_SHUTDOWN, "Shutdown is already running"); return; } started_ = true; @@ -149,9 +149,9 @@ void ShutdownController::RebootOrShutdown(const std::string& reason, bool isRebo // only two threads are allowed, next time it blocks // either block or thread leak. since the previous action is shutdown, block is safer than thread leak g_futForSyncCb = async(launch::async, &ShutdownController::TriggerSyncShutdownCallback, this, isReboot); - g_futForOff = async(launch::async, &ShutdownController::TurnOffScreen, this); g_futForSyncCb.wait_for(CALLBACK_TIMEOUT); actionTimeStr = actionTimeStr + "," + std::to_string(GetCurrentRealTimeMs()); + g_futForOff = async(launch::async, &ShutdownController::TurnOffScreen, this); g_futForOff.wait_for(OFF_TIMEOUT); } else { TriggerSyncShutdownCallback(isReboot); diff --git a/test/apitest/inner_api/shutdown/shutdown_controller_test.cpp b/test/apitest/inner_api/shutdown/shutdown_controller_test.cpp index 07756fa7..98e7a6ac 100644 --- a/test/apitest/inner_api/shutdown/shutdown_controller_test.cpp +++ b/test/apitest/inner_api/shutdown/shutdown_controller_test.cpp @@ -13,6 +13,9 @@ * limitations under the License. */ +#include +#include + #include #include #include @@ -50,10 +53,19 @@ HWTEST_F(ShutDownControllerTest, ShutDownControllerTest001, TestSize.Level0) controller.AddCallback(callback, ShutdownPriority::DEFAULT); EXPECT_CALL(*powerActionMock, Reboot("::testing::_")).Times(0); controller.Reboot("test_case"); - EXPECT_CALL(*powerActionMock, Reboot("test_case")); + + std::mutex localMutex; + std::condition_variable cv; + bool notified = false; + EXPECT_CALL(*powerActionMock, Reboot("test_case")).WillOnce(Invoke([&localMutex, &cv, ¬ified]() { + std::unique_lock lock(localMutex); + notified = true; + cv.notify_all(); + })); controller.Reboot("test_case", true); - sleep(1); + std::unique_lock lock(localMutex); + cv.wait(lock, [¬ified]() { return notified; }); POWER_HILOGI(LABEL_TEST, "ShutdownControllerTest001 function end!"); } -} -} \ No newline at end of file +} // namespace +} // namespace OHOS::PowerMgr \ No newline at end of file diff --git a/test/unittest/src/interface_test/general_interfaces_test.cpp b/test/unittest/src/interface_test/general_interfaces_test.cpp index 3e19a307..7de02187 100644 --- a/test/unittest/src/interface_test/general_interfaces_test.cpp +++ b/test/unittest/src/interface_test/general_interfaces_test.cpp @@ -263,12 +263,16 @@ HWTEST_F(GeneralInterfacesTest, ForceRebootDeviceTest002, TestSize.Level0) // cover some branches which are unlikely to be accessed POWER_HILOGI(LABEL_TEST, "ForceRebootDeviceTest002 function start!"); auto& powerMgrClient = PowerMgrClient::GetInstance(); + ASSERT_TRUE(powerActionMock != nullptr); g_isTesting = true; + // normal case + EXPECT_CALL(*powerActionMock, Reboot("First Test")); + powerMgrClient.ForceRebootDevice("First Test"); + WaitForDetachedThread(); // Make permission check to fail g_boolRet = false; - ASSERT_TRUE(powerActionMock != nullptr); EXPECT_CALL(*powerActionMock, Reboot(_)).Times(0); powerMgrClient.ForceRebootDevice("Some Reason"); WaitForDetachedThread(); @@ -306,43 +310,5 @@ HWTEST_F(GeneralInterfacesTest, ForceRebootDeviceTest002, TestSize.Level0) WaitForDetachedThread(); POWER_HILOGI(LABEL_TEST, "ForceRebootDeviceTest002 function end!"); } - -class SyncShutdownCallback : public SyncShutdownCallbackStub { -public: - ~SyncShutdownCallback() override = default; - void OnSyncShutdownOrReboot(bool) override - { - std::unique_lock lock(g_cvMutex); - g_cv.wait(lock, []() { return g_notified; }); - } -}; - -// test async process when calling ForceReboot -// callback blocks, reboot unblocks -HWTEST_F(GeneralInterfacesTest, ForceRebootDeviceTest003, TestSize.Level0) -{ - POWER_HILOGI(LABEL_TEST, "ForceRebootDeviceTest003 function start!"); - sptr callback = sptr::MakeSptr(); - ShutdownClient::GetInstance().RegisterShutdownCallback(callback); - ASSERT_TRUE(powerActionMock != nullptr); - EXPECT_CALL(*powerActionMock, Reboot("Some Reason")).WillOnce(Invoke([]() { - POWER_HILOGI(LABEL_TEST, "unblocking reboot action called"); - std::unique_lock lock(g_cvMutex); - g_notified = true; - g_cv.notify_all(); - })); - - auto& powerMgrClient = PowerMgrClient::GetInstance(); - g_isTesting = true; - powerMgrClient.ForceRebootDevice("Some Reason"); - g_isTesting = false; - { - std::unique_lock lock(g_cvMutex); - EXPECT_FALSE(g_notified); - // only be unblocked after Reboot is called - g_cv.wait(lock, []() { return g_notified; }); - } - POWER_HILOGI(LABEL_TEST, "ForceRebootDeviceTest003 function end!"); -} } // namespace } // namespace OHOS::PowerMgr -- Gitee