* Copyright (C) 2026 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 "timer_lock_optimizer.h"
#ifdef RUNNING_LOCK_OPTIMIZE
#include <cinttypes>
#include <unordered_set>
#include "ability_state_data.h"
#include "app_mgr_client.h"
#include "running_process_info.h"
#include "singleton.h"
#include "time_hilog.h"
#include "timer_app_state_observer.h"
#include "timer_info.h"
#include "timer_manager.h"
#include "want_agent_helper.h"
namespace OHOS {
namespace MiscServices {
constexpr int64_t APP_START_RUNNING_LOCK_DURATION_NS = 10LL * 1000000000LL;
using namespace OHOS::AppExecFwk;
using namespace OHOS::AbilityRuntime::WantAgent;
TimerLockOptimizer::TimerLockOptimizer(TimerManager* manager) : manager_(manager) {}
void TimerLockOptimizer::Init()
{
TIME_HILOGI(TIME_MODULE_SERVICE, "Running lock optimize enable, start init TimerLockOptimizer");
if (isInitialized_.load()) {
TIME_HILOGI(TIME_MODULE_SERVICE, "TimerLockOptimizer already initialized");
return;
}
std::weak_ptr<TimerLockOptimizer> weakThis = weak_from_this();
bool registerResult = TimerAppStateObserver::GetInstance()->Register({},
[weakThis = std::move(weakThis)](const std::string &name, bool isRunning) {
if (auto sharedThis = weakThis.lock()) {
sharedThis->UpdateRunningApps(name, isRunning);
}
});
if (!registerResult) {
TIME_HILOGE(TIME_MODULE_SERVICE, "Register app state observer failed");
return;
}
QueryAllRunningApps();
isInitialized_.store(true);
TIME_HILOGI(TIME_MODULE_SERVICE, "Init TimerLockOptimizer end");
}
void TimerLockOptimizer::QueryAllRunningApps()
{
auto appMgrClient = DelayedSingleton<AppMgrClient>::GetInstance();
if (appMgrClient == nullptr) {
return;
}
std::vector<RunningProcessInfo> runningProcesses;
if (appMgrClient->GetAllRunningProcesses(runningProcesses) !=
AppMgrResultCode::RESULT_OK) {
return;
}
std::lock_guard<std::mutex> lock(appListMutex_);
runningApps_.clear();
for (const auto &process : runningProcesses) {
for (const auto &bundleName : process.bundleNames) {
if (!bundleName.empty()) {
runningApps_.insert(bundleName);
}
}
}
TIME_HILOGI(TIME_MODULE_SERVICE,
"Queried %{public}zu running apps",
runningApps_.size());
}
std::string TimerLockOptimizer::ExtractBundleNameFromWantAgent(const std::shared_ptr<WantAgent>& wantAgent)
{
auto want = WantAgentHelper::GetWant(wantAgent);
if (want == nullptr) {
return "";
}
if (!IsAbilityStartingOperation(WantAgentHelper::GetType(wantAgent))) {
return "";
}
return want->GetElement().GetBundleName();
}
std::string TimerLockOptimizer::GetTargetBundleName(const std::shared_ptr<TimerInfo>& timer)
{
if (timer == nullptr) {
return "";
}
auto want = WantAgentHelper::GetWant(timer->wantAgent);
if (want != nullptr) {
return ExtractBundleNameFromWantAgent(timer->wantAgent);
}
return GetBundleNameMultiUser(timer);
}
std::string TimerLockOptimizer::GetBundleNameMultiUser(const std::shared_ptr<TimerInfo>& timer)
{
if (manager_ == nullptr) {
TIME_HILOGE(TIME_MODULE_SERVICE, "manager_ is nullptr");
return "";
}
#ifdef MULTI_ACCOUNT_ENABLE
if (manager_->CheckUserIdForNotify(timer) != E_TIME_OK) {
return "";
}
#endif
auto wantStr = manager_->GetWantString(timer->id);
if (wantStr.empty()) {
return "";
}
auto newWantAgent = WantAgentHelper::FromString(wantStr);
return ExtractBundleNameFromWantAgent(newWantAgent);
}
void TimerLockOptimizer::BatchAcquireRunningLock(
const std::vector<std::shared_ptr<TimerInfo>> &triggerList)
{
int64_t bootTime = TimeUtils::GetBootTimeNs().time_since_epoch().count();
MergeNewTimers(triggerList, bootTime);
SortAndDeduplicate(bootTime);
int64_t maxExpireTime = 0;
std::string bundleName;
{
std::lock_guard<std::mutex> lock(lockInfosMutex_);
if (!lockInfos_.empty()) {
maxExpireTime = lockInfos_.front().lockExpireTime;
bundleName = lockInfos_.front().wantBundleName;
}
}
if (maxExpireTime > 0) {
TIME_HILOGD(TIME_MODULE_SERVICE,
"BatchAcquireRunningLock: maxExpireTime=%{public}" PRId64 ",bundleName=%{public}s",
maxExpireTime, bundleName.c_str());
AcquireRunningLockInternal(maxExpireTime, bootTime);
}
}
bool TimerLockOptimizer::IsAppRunning(const std::string &bundleName)
{
std::lock_guard<std::mutex> lock(appListMutex_);
return runningApps_.find(bundleName) != runningApps_.end();
}
void TimerLockOptimizer::UpdateRunningApps(const std::string &bundleName, bool isRunning)
{
TIME_HILOGD(
TIME_MODULE_SERVICE,
"UpdateRunningApps: bundleName=%{public}s, isRunning=%{public}d",
bundleName.c_str(), isRunning);
if (bundleName.empty()) {
return;
}
if (isRunning) {
{
std::lock_guard<std::mutex> lock(appListMutex_);
runningApps_.insert(bundleName);
}
RecalcLockForBundle(bundleName);
} else {
std::lock_guard<std::mutex> lock(appListMutex_);
runningApps_.erase(bundleName);
}
TIME_HILOGD(TIME_MODULE_SERVICE, "runningApps_ size=%{public}zu", runningApps_.size());
}
void TimerLockOptimizer::RecalcLockForBundle(const std::string &bundleName)
{
if (bundleName.empty()) {
return;
}
int64_t currentBootTime = TimeUtils::GetBootTimeNs().time_since_epoch().count();
int64_t newMaxExpireTime = 0;
bool hasNonEmptyBundleName = false;
{
std::lock_guard<std::mutex> lock(lockInfosMutex_);
if (lockInfos_.empty()) {
return;
}
auto it = std::remove_if(lockInfos_.begin(), lockInfos_.end(),
[&bundleName, currentBootTime, &hasNonEmptyBundleName](const TimerLockInfo &info) {
if (info.wantBundleName == bundleName || info.lockExpireTime < currentBootTime) {
if (!info.wantBundleName.empty()) {
hasNonEmptyBundleName = true;
}
return true;
}
return false;
});
if (it == lockInfos_.end()) {
return;
}
lockInfos_.erase(it, lockInfos_.end());
if (!lockInfos_.empty()) {
newMaxExpireTime = lockInfos_.front().lockExpireTime;
}
if (hasNonEmptyBundleName) {
int64_t additionalExpireTime = currentBootTime + TimerManager::GetDefaultRunningLockDuration();
newMaxExpireTime = std::max(newMaxExpireTime, additionalExpireTime);
}
}
AcquireRunningLockInternal(newMaxExpireTime, currentBootTime);
}
void TimerLockOptimizer::AcquireRunningLockInternal(int64_t expireTime, int64_t bootTime)
{
if (manager_ == nullptr) {
TIME_HILOGD(TIME_MODULE_SERVICE, "AcquireRunningLockInternal: manager_ is nullptr");
return;
}
int64_t lockTime = 0;
if (expireTime <= manager_->lockExpiredTime_.load()) {
TIME_HILOGD(TIME_MODULE_SERVICE,
"AcquireRunningLockInternal: expireTime=%{public}" PRId64 " <= lockExpiredTime=%{public}" PRId64,
expireTime, manager_->lockExpiredTime_.load());
lockTime = manager_->lockExpiredTime_.load() - bootTime;
} else {
timerLockExpireTime_.store(expireTime);
lockTime = expireTime - bootTime;
}
if (lockTime > 0) {
manager_->AddRunningLock(lockTime);
}
}
bool TimerLockOptimizer::IsAbilityStartingOperation(WantAgentConstant::OperationType operType)
{
return operType != WantAgentConstant::OperationType::SEND_COMMON_EVENT &&
operType != WantAgentConstant::OperationType::UNKNOWN_TYPE;
}
void TimerLockOptimizer::MergeNewTimers(
const std::vector<std::shared_ptr<TimerInfo>> &triggerList,
int64_t bootTime)
{
std::vector<TimerLockInfo> newTimers;
newTimers.reserve(triggerList.size());
for (const auto &timer : triggerList) {
if (!timer->wakeup) {
continue;
}
TimerLockInfo newInfo;
newInfo.timerId = timer->id;
int64_t lockDuration = TimerManager::GetDefaultRunningLockDuration();
if (timer->wantAgent) {
auto wantBundleName = GetTargetBundleName(timer);
if (!wantBundleName.empty()) {
newInfo.wantBundleName = wantBundleName;
lockDuration = APP_START_RUNNING_LOCK_DURATION_NS;
targetBundleNameCache_[timer->id] = wantBundleName;
}
}
newInfo.lockExpireTime = bootTime + lockDuration;
newTimers.push_back(newInfo);
}
{
std::lock_guard<std::mutex> lock(lockInfosMutex_);
lockInfos_.insert(lockInfos_.end(), newTimers.begin(), newTimers.end());
}
}
void TimerLockOptimizer::SortAndDeduplicate(int64_t bootTime)
{
std::lock_guard<std::mutex> lock(lockInfosMutex_);
std::sort(lockInfos_.begin(), lockInfos_.end(),
[](const TimerLockInfo &a, const TimerLockInfo &b) {
if (a.lockExpireTime != b.lockExpireTime) {
return a.lockExpireTime > b.lockExpireTime;
}
return a.wantBundleName < b.wantBundleName;
});
std::unordered_set<std::string> seenBundles;
size_t writeIdx = 0;
for (size_t readIdx = 0; readIdx < lockInfos_.size(); ++readIdx) {
const auto &info = lockInfos_[readIdx];
if (info.lockExpireTime < bootTime) {
continue;
}
if (!info.wantBundleName.empty()) {
if (seenBundles.find(info.wantBundleName) != seenBundles.end()) {
continue;
}
seenBundles.insert(info.wantBundleName);
}
if (writeIdx != readIdx) {
lockInfos_[writeIdx] = std::move(lockInfos_[readIdx]);
}
++writeIdx;
}
lockInfos_.resize(writeIdx);
}
}
}
#endif