/* * Copyright (c) 2022-2023 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 "power_interface_impl.h" #include "errors.h" #include "hdf_device_desc.h" #include "hdf_remote_service.h" #include "hdf_sbuf.h" #include "pubdef.h" #include "running_lock_impl.h" #include "securec.h" #include "unique_fd.h" #include "power_hdf_log.h" #include "v1_2/power_types.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef DRIVER_PERIPHERAL_POWER_WAKEUP_CAUSE_PATH #include "power_config.h" #endif #ifdef DRIVERS_PERIPHERAL_POWER_ENABLE_S4 #include "hibernate.h" #endif namespace OHOS { namespace HDI { namespace Power { namespace V1_2 { static constexpr const int32_t MAX_FILE_LENGTH = 32 * 1024 * 1024; static constexpr const char * const SUSPEND_STATE = "mem"; static constexpr const char * const SUSPEND_STATE_PATH = "/sys/power/state"; static constexpr const char * const LOCK_PATH = "/sys/power/wake_lock"; static constexpr const char * const UNLOCK_PATH = "/sys/power/wake_unlock"; static constexpr const char * const WAKEUP_COUNT_PATH = "/sys/power/wakeup_count"; #ifdef FASTER_RETRY_OF_SLEEP static constexpr std::chrono::milliseconds DEFAULT_WAIT_TIME(100); // 100ms for phone and tablet #elif defined(SLOWER_RETRY_OF_SLEEP) static constexpr std::chrono::milliseconds DEFAULT_WAIT_TIME(2000); // 2000ms for PC #else static constexpr std::chrono::milliseconds DEFAULT_WAIT_TIME(1000); // 1000ms #endif static constexpr std::chrono::milliseconds MAX_WAIT_TIME(1000 * 60); // 1min static constexpr int32_t WAIT_TIME_FACTOR = 2; static std::chrono::milliseconds waitTime_(DEFAULT_WAIT_TIME); static std::mutex g_mutex; static std::mutex g_suspendMutex; static std::condition_variable g_suspendCv; static std::unique_ptr g_daemon; static std::atomic_bool g_suspending; static std::atomic_bool g_suspendRetry; static sptr g_callback; static UniqueFd wakeupCountFd; static PowerHdfState g_powerState {PowerHdfState::AWAKE}; static void AutoSuspendLoop(); static int32_t DoSuspend(); static void LoadStringFd(int32_t fd, std::string &content); static std::string ReadWakeCount(); static bool WriteWakeCount(const std::string &count); static void NotifyCallback(int code); namespace { sptr g_deathRecipient = nullptr; bool g_isHdiStart = false; } // namespace extern "C" IPowerInterface *PowerInterfaceImplGetInstance(void) { using OHOS::HDI::Power::V1_2::PowerInterfaceImpl; PowerInterfaceImpl *service = new (std::nothrow) PowerInterfaceImpl(); if (service == nullptr) { return nullptr; } if (service->Init() != HDF_SUCCESS) { delete service; return nullptr; } return service; } int32_t PowerInterfaceImpl::Init() { #ifdef DRIVER_PERIPHERAL_POWER_WAKEUP_CAUSE_PATH auto& powerConfig = PowerConfig::GetInstance(); powerConfig.ParseConfig(); #endif #ifdef DRIVERS_PERIPHERAL_POWER_ENABLE_S4 Hibernate::GetInstance().Init(); #endif return HDF_SUCCESS; } int32_t PowerInterfaceImpl::RegisterCallback(const sptr &ipowerHdiCallback) { std::lock_guard lock(g_mutex); if (!g_isHdiStart) { g_callback = ipowerHdiCallback; if (g_callback == nullptr) { UnRegister(); return HDF_SUCCESS; } g_deathRecipient = new PowerDeathRecipient(this); if (g_deathRecipient == nullptr) { return HDF_FAILURE; } AddPowerDeathRecipient(g_callback); g_isHdiStart = true; } return HDF_SUCCESS; } int32_t PowerInterfaceImpl::UnRegister() { HDF_LOGI("UnRegister"); RemovePowerDeathRecipient(g_callback); g_callback = nullptr; g_isHdiStart = false; return HDF_SUCCESS; } int32_t PowerInterfaceImpl::RegisterRunningLockCallback(const sptr &iPowerRunningLockCallback) { if (iPowerRunningLockCallback != nullptr) { UnRegisterRunningLockCallback(); } RunningLockImpl::RegisterRunningLockCallback(iPowerRunningLockCallback); return HDF_SUCCESS; } int32_t PowerInterfaceImpl::UnRegisterRunningLockCallback() { RunningLockImpl::UnRegisterRunningLockCallback(); return HDF_SUCCESS; } int32_t PowerInterfaceImpl::StartSuspend() { HDF_LOGI("start suspend"); std::lock_guard lock(g_mutex); g_suspendRetry = true; if (g_suspending) { g_powerState = PowerHdfState::INACTIVE; g_suspendCv.notify_one(); return HDF_SUCCESS; } g_suspending = true; g_daemon = std::make_unique(&AutoSuspendLoop); g_daemon->detach(); return HDF_SUCCESS; } void AutoSuspendLoop() { auto suspendLock = std::unique_lock(g_suspendMutex); while (true) { std::this_thread::sleep_for(waitTime_); const std::string wakeupCount = ReadWakeCount(); if (wakeupCount.empty()) { continue; } if (!g_suspendRetry) { g_suspendCv.wait(suspendLock); } if (!WriteWakeCount(wakeupCount)) { continue; } NotifyCallback(CMD_ON_SUSPEND); g_powerState = PowerHdfState::SLEEP; DoSuspend(); g_powerState = PowerHdfState::AWAKE; NotifyCallback(CMD_ON_WAKEUP); } g_suspending = false; g_suspendRetry = false; } static std::string g_suspendTag; int32_t PowerInterfaceImpl::SetSuspendTag(const std::string &tag) { HDF_LOGI("Set suspend tag: %{public}s", tag.c_str()); g_suspendTag = tag; return HDF_SUCCESS; } #ifdef DRIVER_PERIPHERAL_POWER_SUSPEND_WITH_TAG static constexpr const int32_t MAX_RETRY_COUNT = 5; int32_t DoSuspendWithTag() { UniqueFd suspendStateFd(TEMP_FAILURE_RETRY(open(SUSPEND_STATE_PATH, O_RDWR | O_CLOEXEC))); if (suspendStateFd < 0) { return HDF_FAILURE; } int loop; for (loop = 0; loop < MAX_RETRY_COUNT; loop++) { bool ret = SaveStringToFd(suspendStateFd, g_suspendTag); if (ret) { break; } HDF_LOGE("SaveStringToFd fail, tag:%{public}s loop:%{public}d", g_suspendTag.c_str(), loop); } HDF_LOGI("Do suspend %{public}d: echo %{public}s > /sys/power/state", loop, g_suspendTag.c_str()); if (loop == MAX_RETRY_COUNT) { HDF_LOGE("DoSuspendWithTag fail: %s", g_suspendTag.c_str()); g_suspendTag.clear(); return HDF_FAILURE; } g_suspendTag.clear(); return HDF_SUCCESS; } #endif int32_t DoSuspend() { std::lock_guard lock(g_mutex); #ifdef DRIVER_PERIPHERAL_POWER_SUSPEND_WITH_TAG if (!g_suspendTag.empty()) { return DoSuspendWithTag(); } #endif UniqueFd suspendStateFd(TEMP_FAILURE_RETRY(open(SUSPEND_STATE_PATH, O_RDWR | O_CLOEXEC))); if (suspendStateFd < 0) { return HDF_FAILURE; } bool ret = SaveStringToFd(suspendStateFd, SUSPEND_STATE); if (!ret) { HDF_LOGE("DoSuspend fail"); waitTime_ = std::min(waitTime_ * WAIT_TIME_FACTOR, MAX_WAIT_TIME); return HDF_FAILURE; } waitTime_ = DEFAULT_WAIT_TIME; return HDF_SUCCESS; } void NotifyCallback(int code) { if (g_callback == nullptr) { return; } switch (code) { case CMD_ON_SUSPEND: g_callback->OnSuspend(); break; case CMD_ON_WAKEUP: g_callback->OnWakeup(); break; default: break; } } int32_t PowerInterfaceImpl::StopSuspend() { HDF_LOGI("stop suspend"); g_suspendRetry = false; g_powerState = PowerHdfState::AWAKE; return HDF_SUCCESS; } int32_t PowerInterfaceImpl::ForceSuspend() { //force suspend changed into active suspend HDF_LOGI("active suspend"); StartSuspend(); return HDF_SUCCESS; } int32_t PowerInterfaceImpl::Hibernate() { #ifdef DRIVERS_PERIPHERAL_POWER_ENABLE_S4 HDF_LOGI("hibernate begin."); return Hibernate::GetInstance().DoHibernate(); #else HDF_LOGI("hdf hibernate interface not supported."); return HDF_FAILURE; #endif } int32_t PowerInterfaceImpl::SuspendBlock(const std::string &name) { std::lock_guard lock(g_mutex); if (name.empty()) { return HDF_ERR_INVALID_PARAM; } UniqueFd fd(TEMP_FAILURE_RETRY(open(LOCK_PATH, O_RDWR | O_CLOEXEC))); bool ret = SaveStringToFd(fd, name); if (!ret) { return HDF_FAILURE; } return HDF_SUCCESS; } int32_t PowerInterfaceImpl::SuspendUnblock(const std::string &name) { std::lock_guard lock(g_mutex); if (name.empty()) { return HDF_ERR_INVALID_PARAM; } UniqueFd fd(TEMP_FAILURE_RETRY(open(UNLOCK_PATH, O_RDWR | O_CLOEXEC))); bool ret = SaveStringToFd(fd, name); if (!ret) { return HDF_FAILURE; } return HDF_SUCCESS; } int32_t PowerInterfaceImpl::AddPowerDeathRecipient(const sptr &callback) { HDF_LOGI("AddPowerDeathRecipient"); const sptr &remote = OHOS::HDI::hdi_objcast(callback); bool result = remote->AddDeathRecipient(g_deathRecipient); if (!result) { HDF_LOGI("AddPowerDeathRecipient fail"); return HDF_FAILURE; } return HDF_SUCCESS; } int32_t PowerInterfaceImpl::RemovePowerDeathRecipient(const sptr &callback) { HDF_LOGI("RemovePowerDeathRecipient"); const sptr &remote = OHOS::HDI::hdi_objcast(callback); bool result = remote->RemoveDeathRecipient(g_deathRecipient); if (!result) { HDF_LOGI("RemovePowerDeathRecipient fail"); return HDF_FAILURE; } return HDF_SUCCESS; } void PowerInterfaceImpl::PowerDeathRecipient::OnRemoteDied(const wptr &object) { HDF_LOGI("PowerDeathRecipient OnRemoteDied"); powerInterfaceImpl_->UnRegister(); RunningLockImpl::Clean(); } void LoadStringFd(int32_t fd, std::string &content) { if (fd <= 0) { HDF_LOGW("invalid fd: %{public}d", fd); return; } const int32_t fileLength = lseek(fd, 0, SEEK_END); if (fileLength > MAX_FILE_LENGTH || fileLength <= 0) { HDF_LOGW("invalid file length(%{public}d)!", fileLength); return; } int32_t loc = lseek(fd, 0, SEEK_SET); if (loc == -1) { HDF_LOGE("lseek file to begin failed!"); return; } content.resize(fileLength); const int32_t len = static_cast(read(fd, content.data(), fileLength)); if (len <= 0) { HDF_LOGW("the length read from file is failed, len: %{public}d, fileLen: %{public}d", len, fileLength); content.clear(); } } std::string ReadWakeCount() { if (wakeupCountFd < 0) { wakeupCountFd = UniqueFd(TEMP_FAILURE_RETRY(open(WAKEUP_COUNT_PATH, O_RDWR | O_CLOEXEC))); } std::string wakeupCount; LoadStringFd(wakeupCountFd, wakeupCount); return wakeupCount; } bool WriteWakeCount(const std::string &count) { if (wakeupCountFd < 0) { wakeupCountFd = UniqueFd(TEMP_FAILURE_RETRY(open(WAKEUP_COUNT_PATH, O_RDWR | O_CLOEXEC))); } bool ret = SaveStringToFd(wakeupCountFd, count.c_str()); return ret; } static void LoadSystemInfo(const std::string &path, std::string &info) { UniqueFd fd(TEMP_FAILURE_RETRY(open(path.c_str(), O_RDWR | O_CLOEXEC))); std::string str; if (fd >= 0) { bool ret = LoadStringFromFd(fd, str); if (!ret) { str = "# Failed to read"; } } else { str = "# Failed to open"; } info.append(path); info.append(": " + str + "\n"); } int32_t PowerInterfaceImpl::PowerDump(std::string &info) { std::string dumpInfo(""); LoadSystemInfo(SUSPEND_STATE_PATH, dumpInfo); LoadSystemInfo(LOCK_PATH, dumpInfo); LoadSystemInfo(UNLOCK_PATH, dumpInfo); info = dumpInfo; return HDF_SUCCESS; } int32_t PowerInterfaceImpl::HoldRunningLock(const RunningLockInfo &info) { return RunningLockImpl::Hold(info, g_powerState); } int32_t PowerInterfaceImpl::UnholdRunningLock(const RunningLockInfo &info) { return RunningLockImpl::Unhold(info); } int32_t PowerInterfaceImpl::HoldRunningLockExt(const RunningLockInfo &info, uint64_t lockid, const std::string &bundleName) { HDF_LOGI("Background runningLock active, type=%{public}d name=%{public}s", info.type, info.name.c_str()); return RunningLockImpl::HoldLock(info, g_powerState, lockid, bundleName); } int32_t PowerInterfaceImpl::UnholdRunningLockExt(const RunningLockInfo &info, uint64_t lockid, const std::string &bundleName) { HDF_LOGI("Background runningLock inactive, type=%{public}d name=%{public}s", info.type, info.name.c_str()); return RunningLockImpl::UnholdLock(info, lockid, bundleName); } int32_t PowerInterfaceImpl::GetWakeupReason(std::string &reason) { #ifdef DRIVER_PERIPHERAL_POWER_WAKEUP_CAUSE_PATH auto& powerConfig = PowerConfig::GetInstance(); std::map sceneConfigMap= powerConfig.GetPowerSceneConfigMap(); std::map::iterator it = sceneConfigMap.find("wakeuo_cause"); if (it == sceneConfigMap.end()) { HDF_LOGW("wakeuo_cause getPath does not exist"); return HDF_FAILURE; } std::string getPath = (it->second).getPath; HDF_LOGI("getPath = %{public}s", getPath.c_str()); UniqueFd wakeupCauseFd(TEMP_FAILURE_RETRY(open(getPath.c_str(), O_RDONLY | O_CLOEXEC))); if (wakeupCauseFd < 0) { return HDF_FAILURE; } LoadStringFd(wakeupCauseFd, reason); return HDF_SUCCESS; #else HDF_LOGW("wakrup cause path not config"); return HDF_FAILURE; #endif } } // namespace V1_2 } // namespace Power } // namespace HDI } // namespace OHOS