!18898 merge feat/extension-timeout-monitor-for-test into master

feat: add dump interface for extension running timeout monitor

Created-by: zhang_hao_zheng
Commit-by: zhang_hao_zheng
Merged-by: openharmony_ci
Description: **IssueNo**:
https://gitcode.com/openharmony/ability_ability_runtime/issues/14920  
**Description**:

**稳定性自检:**
| 自检项                                                       | 自检结果  |
| ------------------------------------------------------------ | -------- |
| 涉及跨进程调用的相关操作需要抛至主线程或加锁防止并发              |       √  |
| 成员变量进行赋值或创建需要排查并发                               |       √   |
| 谨慎在lambda表达式中使用引用捕获                                |        √  |
| 谨慎在未经拷贝的情况下使用外部传入的string、C字符串               |       √  |
| map\vector\list\set等stl模板类使用时需要排查并发                |       √  |
| 谨慎考虑加锁范围                                               |        √ |
| 在IPC通信中谨慎使用同步通信方式                                 |       √  |
| 禁止传递this指针至其他模块或线程(特别是eventhandler任务)        |       √  |
| 禁止将外部传入的裸指针在内部直接构造智能指针                      |       √  |
| 禁止多个独立创建的智能指针管理同一地址                           |       √  |
| 禁止在析构函数中抛异步任务                                      |        √ |
| 禁止js对象在非js线程(例如在IPC线程)创建、使用或销毁             |       √  |
| 禁止在对外接口中未经判空直接使用外部传入的指针                    |       √  |
| 禁止接口返回局部变量引用                                        |       √  |
| 禁止在信号函数中加锁                                            |        √ |
| 禁止在关键流程(SA启动、应用启动等主流程)执行耗时的操作           |        √ |
| 禁止将同一个cpp编译在不同的so中                                 |       √  |

**安全编码自检:**
| 自检项                                                          | 自检结果 |
| -------------------------------------------------------------- | -------- |
| 裸指针避免通过隐式转换构造为sptr                                 |      √   |
| json对象在取值之前必须先判断类型,避免类型不匹配                   |       √  |
| 序列化时必须对传入的数组大小进行校验,避免出现超大数组              |   √      |
| 避免使用未明确位宽的整型,选择使用int8_t、uint8_t等类型            |      √   |
| 外部传入的路径要做规范化校验,对路径中的.、..、../等特殊字符严格校验 |        √ |
| 指针变量、表示资源描述符的变量、bool变量必须赋初值                  |        √ |
| readParcelable获取的对象使用前需要判空                            |        √ |
| 分配和释放内存的函数需要成对出现                                   |        √ |
| 申请内存后异常退出前需要及时进行内存释放                            |  √       |
| 内存申请前必须对内存大小进行合法性校验                              |      √   |
| 内存分配后必须判断是否成功                                         |       √  |
| 禁止使用realloc、alloca函数                                       |        √ |
| 禁止打印文件路径、口令等敏感信息,如有需要,使用private修饰          |        √ |
| 禁止打印内存地址                                                  |        √ |
| 整数之间运算时必须严格检查,确保不会出现溢出、反转、除0               |        √ |
| 禁止对有符号整数进行位操作符运算                                    |        √ |
| 禁止对指针进行逻辑或位运算                                         |        √ |
| 循环次数如果收外部数据控制,需要检验其合法性                         |        √ |
| 禁止使用内存操作类危险函数,需要使用安全函数                         |       √  |
| 谨慎使用不可重入函数                                               |        √ |
| 必须检查安全函数的返回值,并进行正确处理                             |        √ |
| 禁止仅通过TokenType类型判断绕过权限校验                             |       √  |

**TDD Result**:
pass
**XTS Result**:
pass
### 是否已执行L0用例
- [ ] 已验证
- [ ] 不涉及。如不涉及,请写明理由


See merge request: openharmony/ability_ability_runtime!18898
This commit is contained in:
openharmony_ci
2026-04-11 15:14:28 +08:00
26 changed files with 1205 additions and 8 deletions
+10 -1
View File
@@ -511,4 +511,13 @@ USER_DATA_SIZE:
PARTITION_NAME: {type: STRING, desc: name of partition}
REMAIN_PARTITION_SIZE: {type: UINT64, desc: Remaining size of the partition}
FILE_OR_FOLDER_PATH: {type: STRING, arrsize: 100, desc: File or folder path}
FILE_OR_FOLDER_SIZE: {type: UINT64, arrsize: 100, desc: File or folder size}
FILE_OR_FOLDER_SIZE: {type: UINT64, arrsize: 100, desc: File or folder size}
EXTENSION_RUNNING_TIMEOUT:
__BASE: {type: STATISTIC, level: CRITICAL, desc: extension ability running timeout}
EXTENSION_TYPE: {type: STRING, arrsize: 5, desc: extension type name list}
BUNDLE_NAME: {type: STRING, arrsize: 5, desc: bundle name list}
ABILITY_NAME: {type: STRING, arrsize: 5, desc: ability name list}
RUNNING_DURATION: {type: INT64, arrsize: 5, desc: running duration list in seconds}
STILL_ALIVE: {type: BOOL, arrsize: 5, desc: still alive flag list}
CNT: {type: INT32, arrsize: 5, desc: occurrence count list}
+1
View File
@@ -48,6 +48,7 @@ abilityms_files = [
"src/extension_running_info.cpp",
"src/extension_record/base_extension_record.cpp",
"src/extension_record/extension_record_manager.cpp",
"src/extension_running_timeout_monitor.cpp",
"src/extension_record/extension_record.cpp",
"src/extension_record/extension_record_factory.cpp",
"src/foreground_app_connection_manager/foreground_app_connection_manager.cpp",
@@ -29,6 +29,7 @@
namespace OHOS {
namespace AAFwk {
constexpr static int32_t DEFAULT_EXTENSION_AUTO_DISCONNECT_TIME = -1;
constexpr static int32_t DEFAULT_EXTENSION_RUNNING_TIMEOUT_TIME = -1;
constexpr static bool EXTENSION_NETWORK_ENABLE_FLAG_DEFAULT = true;
constexpr static bool EXTENSION_SA_ENABLE_FLAG_DEFAULT = true;
constexpr static bool EXTENSION_THIRD_PARTY_APP_ENABLE_FLAG_DEFAULT = true;
@@ -55,6 +56,7 @@ struct ExtensionConfigItem {
bool thirdPartyAppEnableFlag = EXTENSION_THIRD_PARTY_APP_ENABLE_FLAG_DEFAULT;
bool serviceEnableFlag = EXTENSION_START_SERVICE_ENABLE_FLAG_DEFAULT;
int32_t extensionAutoDisconnectTime = DEFAULT_EXTENSION_AUTO_DISCONNECT_TIME;
int32_t extensionRunningTimeoutTime = DEFAULT_EXTENSION_RUNNING_TIMEOUT_TIME;
std::unordered_set<std::string> serviceBlockedList;
ScreenUnlockAccessItem screenUnlockAccess;
AbilityAccessItem abilityAccess;
@@ -67,6 +69,7 @@ public:
virtual ~ExtensionConfig() = default;
void LoadExtensionConfiguration();
int32_t GetExtensionAutoDisconnectTime(const std::string &extensionTypeName);
int32_t GetExtensionRunningTimeoutTime(const std::string &extensionTypeName);
bool IsExtensionStartThirdPartyAppEnable(const std::string &extensionTypeName);
bool IsExtensionStartServiceEnable(const std::string &extensionTypeName, const std::string &targetUri);
bool HasAbilityAccess(const std::string &extensionTypeName);
@@ -88,6 +91,7 @@ private:
std::string GetExtensionConfigPath() const;
void LoadExtensionAutoDisconnectTime(const nlohmann::json &object, const std::string &extensionTypeName);
void LoadExtensionRunningTimeoutTime(const nlohmann::json &object, const std::string &extensionTypeName);
void LoadExtensionThirdPartyAppBlockedList(const nlohmann::json &object, std::string extensionTypeName);
void LoadExtensionServiceBlockedList(const nlohmann::json &object, std::string extensionTypeNameobject);
void LoadExtensionNetworkEnable(const nlohmann::json &object, const std::string &extensionTypeName);
@@ -25,6 +25,7 @@
#include "base_extension_record.h"
#include "extension_record.h"
#include "extension_running_timeout_monitor.h"
#include "preload_ui_extension_execute_callback_interface.h"
#include "ui_extension/ui_extension_host_info.h"
#include "ui_extension/ui_extension_session_info.h"
@@ -0,0 +1,98 @@
/*
* 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.
*/
#ifndef OHOS_ABILITY_RUNTIME_EXTENSION_RUNNING_TIMEOUT_MONITOR_H
#define OHOS_ABILITY_RUNTIME_EXTENSION_RUNNING_TIMEOUT_MONITOR_H
#include <list>
#include <mutex>
#include <string>
#include <unordered_map>
#include "singleton.h"
namespace OHOS {
namespace AAFwk {
struct ExtensionTimeoutEvent {
std::string extensionTypeName;
std::string bundleName;
std::string abilityName;
int64_t runningDuration = 0; // seconds
bool stillAlive = false;
int32_t cnt = 1;
};
class ExtensionRunningTimeoutMonitor : public DelayedSingleton<ExtensionRunningTimeoutMonitor> {
DECLARE_DELAYED_SINGLETON(ExtensionRunningTimeoutMonitor)
public:
/**
* @brief Called when an extension ability starts. Records start time.
*/
void OnExtensionStarted(int32_t extensionRecordId, const std::string &extensionTypeName,
const std::string &bundleName, const std::string &abilityName);
/**
* @brief Called when an extension ability ends. Checks if it exceeded configured timeout.
*/
void OnExtensionTerminated(int32_t extensionRecordId);
/**
* @brief Start the periodic report and check timer. Called during service init.
*/
void StartMonitor();
/**
* @brief Stop the periodic report and check timer.
*/
void StopMonitor();
/**
* @brief Report cached timeout events via HiSysEvent and clear cache.
*/
void ReportTimeoutEvents();
/**
* @brief Proactively check all currently alive extensions for timeout violations.
* Adds detected violations to the cached events list.
*/
void CheckAliveExtensions();
private:
struct ExtensionStartInfo {
std::string extensionTypeName;
std::string bundleName;
std::string abilityName;
int64_t startTimeMillis = 0;
};
void AddOrUpdateTimeoutEvent(const ExtensionTimeoutEvent &event);
bool IsDuplicateEvent(const ExtensionTimeoutEvent &event,
std::list<ExtensionTimeoutEvent>::iterator &dupIter);
void SubmitPeriodicTask();
std::mutex monitorMutex_;
std::unordered_map<int32_t, ExtensionStartInfo> runningExtensions_;
std::list<ExtensionTimeoutEvent> cachedEvents_;
static constexpr int32_t HISEVENT_PARAM_COUNT = 7;
static constexpr int32_t MAX_CACHED_EVENTS = 5;
static constexpr int64_t REPORT_INTERVAL_MS = 2 * 60 * 60 * 1000; // 2 hours
static constexpr const char *PERIODIC_TASK_NAME = "ExtensionTimeoutPeriodicTask";
};
} // namespace AAFwk
} // namespace OHOS
#endif // OHOS_ABILITY_RUNTIME_EXTENSION_RUNNING_TIMEOUT_MONITOR_H
@@ -1,3 +1,3 @@
{
"ams_extension_config": []
}
}
@@ -29,6 +29,7 @@
#include "cache_extension_utils.h"
#include "datetime_ex.h"
#include "extension_ability_info.h"
#include "extension_running_timeout_monitor.h"
#include "foreground_app_connection_manager.h"
#include "global_constant.h"
#include "hitrace_meter.h"
@@ -283,6 +284,14 @@ int AbilityConnectManager::StopServiceAbilityLocked(const AbilityRequest &abilit
void AbilityConnectManager::RemoveServiceFromMapSafe(const std::string &serviceKey)
{
std::lock_guard lock(serviceMapMutex_);
auto it = serviceMap_.find(serviceKey);
if (it != serviceMap_.end() && it->second != nullptr) {
auto recordId = it->second->GetRecordId();
auto monitor = DelayedSingleton<AAFwk::ExtensionRunningTimeoutMonitor>::GetInstance();
if (monitor != nullptr) {
monitor->OnExtensionTerminated(recordId);
}
}
serviceMap_.erase(serviceKey);
TAG_LOGD(AAFwkTag::EXT, "ServiceMap remove, size:%{public}zu", serviceMap_.size());
}
@@ -3246,6 +3255,17 @@ void AbilityConnectManager::GetOrCreateServiceRecord(const AbilityRequest &abili
SetServiceAfterNewCreate(abilityRequest, *targetService);
AddToServiceMap(serviceKey, targetService);
isLoadedAbility = false;
// Notify running timeout monitor about service extension start
auto &newAbilityInfo = abilityRequest.abilityInfo;
auto recordId = targetService->GetRecordId();
auto monitor = DelayedSingleton<AAFwk::ExtensionRunningTimeoutMonitor>::GetInstance();
if (monitor != nullptr) {
monitor->OnExtensionStarted(recordId,
newAbilityInfo.extensionTypeName,
newAbilityInfo.bundleName,
newAbilityInfo.name);
}
}
TAG_LOGD(AAFwkTag::EXT, "service map add, serviceKey: %{public}s", serviceKey.c_str());
}
@@ -54,6 +54,7 @@
#include "hidden_start_observer_manager.h"
#include "hitrace_meter.h"
#include "hisysevent_report.h"
#include "extension_running_timeout_monitor.h"
#include "insight_intent_execute_manager.h"
#include "insight_intent_db_cache.h"
#include "insight_intent_utils.h"
@@ -469,6 +470,7 @@ bool AbilityManagerService::Init()
modularObjectExtensionEventMgr_ = std::make_shared<AbilityRuntime::ModularObjectExtensionEventMgr>();
modularObjectExtensionEventMgr_->SubscribeSysEventReceiver();
ReportDataPartitionUsageManager::SendReportDataPartitionUsageEvent();
DelayedSingleton<AAFwk::ExtensionRunningTimeoutMonitor>::GetInstance()->StartMonitor();
#ifdef RESOURCE_SCHEDULE_SERVICE_ENABLE
ResourceSchedule::ResSchedClient::GetInstance().InitKillReasonListener();
#endif
@@ -30,6 +30,7 @@ constexpr const char* EXTENSION_CONFIG_FILE_PATH = "/etc/ams_extension_config.js
constexpr const char* EXTENSION_CONFIG_NAME = "ams_extension_config";
constexpr const char* EXTENSION_TYPE_NAME = "extension_type_name";
constexpr const char* EXTENSION_AUTO_DISCONNECT_TIME = "auto_disconnect_time";
constexpr const char* EXTENSION_RUNNING_TIMEOUT_TIME = "running_timeout_time";
// old access flag, deprecated
constexpr const char* EXTENSION_THIRD_PARTY_APP_BLOCKED_FLAG_NAME = "third_party_app_blocked_flag";
@@ -128,6 +129,7 @@ void ExtensionConfig::LoadExtensionConfig(const nlohmann::json &object)
std::lock_guard lock(configMapMutex_);
std::string extensionTypeName = jsonObject.at(EXTENSION_TYPE_NAME).get<std::string>();
LoadExtensionAutoDisconnectTime(jsonObject, extensionTypeName);
LoadExtensionRunningTimeoutTime(jsonObject, extensionTypeName);
bool hasAbilityAccess = LoadExtensionAbilityAccess(jsonObject, extensionTypeName);
if (!hasAbilityAccess) {
LoadExtensionThirdPartyAppBlockedList(jsonObject, extensionTypeName);
@@ -151,6 +153,30 @@ void ExtensionConfig::LoadExtensionAutoDisconnectTime(const nlohmann::json &obje
configMap_[extensionTypeName].extensionAutoDisconnectTime = extensionAutoDisconnectTime;
}
void ExtensionConfig::LoadExtensionRunningTimeoutTime(const nlohmann::json &object,
const std::string &extensionTypeName)
{
if (!object.contains(EXTENSION_RUNNING_TIMEOUT_TIME) ||
!object.at(EXTENSION_RUNNING_TIMEOUT_TIME).is_number()) {
TAG_LOGD(AAFwkTag::ABILITYMGR, "running timeout time config not set for %{public}s",
extensionTypeName.c_str());
return;
}
int32_t extensionRunningTimeoutTime = object.at(EXTENSION_RUNNING_TIMEOUT_TIME).get<int32_t>();
configMap_[extensionTypeName].extensionRunningTimeoutTime = extensionRunningTimeoutTime;
TAG_LOGI(AAFwkTag::ABILITYMGR, "extension %{public}s running timeout time: %{public}d ms",
extensionTypeName.c_str(), extensionRunningTimeoutTime);
}
int32_t ExtensionConfig::GetExtensionRunningTimeoutTime(const std::string &extensionTypeName)
{
std::lock_guard lock(configMapMutex_);
if (configMap_.find(extensionTypeName) != configMap_.end()) {
return configMap_[extensionTypeName].extensionRunningTimeoutTime;
}
return DEFAULT_EXTENSION_RUNNING_TIMEOUT_TIME;
}
void ExtensionConfig::LoadExtensionThirdPartyAppBlockedList(const nlohmann::json &object,
std::string extensionTypeName)
{
@@ -17,6 +17,7 @@
#include "ability_util.h"
#include "app_utils.h"
#include "extension_running_timeout_monitor.h"
#include "preload_ui_extension_execute_callback_proxy.h"
#include "preload_ui_extension_host_client.h"
#include "ui_extension_record.h"
@@ -89,6 +90,18 @@ void ExtensionRecordManager::AddExtensionRecord(const int32_t extensionRecordId,
TAG_LOGD(AAFwkTag::ABILITYMGR, "extensionRecordId %{public}d.", extensionRecordId);
std::lock_guard<std::mutex> lock(mutex_);
extensionRecords_.emplace(extensionRecordId, record);
// Notify running timeout monitor about extension start
if (record != nullptr && record->abilityRecord_ != nullptr) {
auto &abilityInfo = record->abilityRecord_->GetAbilityInfo();
auto monitor = DelayedSingleton<AAFwk::ExtensionRunningTimeoutMonitor>::GetInstance();
if (monitor != nullptr) {
monitor->OnExtensionStarted(extensionRecordId,
abilityInfo.extensionTypeName,
abilityInfo.bundleName,
abilityInfo.name);
}
}
}
void ExtensionRecordManager::RemoveExtensionRecord(const int32_t extensionRecordId)
@@ -97,6 +110,12 @@ void ExtensionRecordManager::RemoveExtensionRecord(const int32_t extensionRecord
std::lock_guard<std::mutex> lock(mutex_);
extensionRecords_.erase(extensionRecordId);
terminateRecords_.erase(extensionRecordId);
// Notify running timeout monitor about extension termination
auto monitor = DelayedSingleton<AAFwk::ExtensionRunningTimeoutMonitor>::GetInstance();
if (monitor != nullptr) {
monitor->OnExtensionTerminated(extensionRecordId);
}
}
void ExtensionRecordManager::AddExtensionRecordToTerminatedList(const int32_t extensionRecordId)
@@ -0,0 +1,311 @@
/*
* 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 "extension_running_timeout_monitor.h"
#include <chrono>
#include <cinttypes>
#include <functional>
#include "extension_config.h"
#include "securec.h"
#include "hisysevent_report.h"
#include "hilog_tag_wrapper.h"
#include "task_handler_wrap.h"
namespace OHOS {
namespace AAFwk {
namespace {
constexpr const char *DOMAIN = "AAFWK";
constexpr const char *EVENT_NAME = "EXTENSION_RUNNING_TIMEOUT";
constexpr const char *EXTENSION_TYPE_KEY = "EXTENSION_TYPE";
constexpr const char *BUNDLE_NAME_KEY = "BUNDLE_NAME";
constexpr const char *ABILITY_NAME_KEY = "ABILITY_NAME";
constexpr const char *RUNNING_DURATION_KEY = "RUNNING_DURATION";
constexpr const char *STILL_ALIVE_KEY = "STILL_ALIVE";
constexpr const char *CNT_KEY = "CNT";
bool CopyStringParam(const std::string &str, std::vector<std::unique_ptr<char[]>> &buffers,
std::vector<char*> &ptrs)
{
size_t len = str.size() + 1;
auto buf = std::make_unique<char[]>(len);
if (strcpy_s(buf.get(), len, str.c_str()) != EOK) {
return false;
}
ptrs.push_back(buf.get());
buffers.push_back(std::move(buf));
return true;
}
}
ExtensionRunningTimeoutMonitor::ExtensionRunningTimeoutMonitor() {}
ExtensionRunningTimeoutMonitor::~ExtensionRunningTimeoutMonitor() {}
void ExtensionRunningTimeoutMonitor::OnExtensionStarted(int32_t extensionRecordId,
const std::string &extensionTypeName,
const std::string &bundleName, const std::string &abilityName)
{
TAG_LOGD(AAFwkTag::ABILITYMGR,
"recordId: %{public}d, type: %{public}s, bundle: %{public}s, ability: %{public}s",
extensionRecordId, extensionTypeName.c_str(), bundleName.c_str(), abilityName.c_str());
auto now = std::chrono::steady_clock::now();
auto startTimeMillis =
std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch()).count();
ExtensionStartInfo info;
info.extensionTypeName = extensionTypeName;
info.bundleName = bundleName;
info.abilityName = abilityName;
info.startTimeMillis = startTimeMillis;
std::lock_guard<std::mutex> lock(monitorMutex_);
runningExtensions_[extensionRecordId] = info;
}
void ExtensionRunningTimeoutMonitor::OnExtensionTerminated(int32_t extensionRecordId)
{
TAG_LOGD(AAFwkTag::ABILITYMGR, "recordId: %{public}d", extensionRecordId);
ExtensionStartInfo startInfo;
{
std::lock_guard<std::mutex> lock(monitorMutex_);
auto it = runningExtensions_.find(extensionRecordId);
if (it == runningExtensions_.end()) {
TAG_LOGD(AAFwkTag::ABILITYMGR, "recordId %{public}d not found in running list",
extensionRecordId);
return;
}
startInfo = it->second;
runningExtensions_.erase(it);
}
// Get configured timeout for this extension type
int32_t configuredTimeout = DelayedSingleton<ExtensionConfig>::GetInstance()->
GetExtensionRunningTimeoutTime(startInfo.extensionTypeName);
if (configuredTimeout <= 0) {
TAG_LOGD(AAFwkTag::ABILITYMGR, "no timeout configured for type %{public}s, skip",
startInfo.extensionTypeName.c_str());
return;
}
auto now = std::chrono::steady_clock::now();
auto nowMillis = std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch()).count();
int64_t runningDurationMs = nowMillis - startInfo.startTimeMillis;
int64_t runningDurationSec = runningDurationMs / 1000;
// runningDuration <= configuredTimeout means within limit, do not track
if (runningDurationSec <= configuredTimeout) {
TAG_LOGD(AAFwkTag::ABILITYMGR,
"extension %{public}s/%{public}s within timeout, duration: %{public}" PRId64 "s, limit: %{public}ds",
startInfo.bundleName.c_str(), startInfo.abilityName.c_str(),
runningDurationSec, configuredTimeout);
return;
}
TAG_LOGI(AAFwkTag::ABILITYMGR,
"extension %{public}s/%{public}s exceeded timeout, duration: %{public}" PRId64 "s, limit: %{public}ds",
startInfo.bundleName.c_str(), startInfo.abilityName.c_str(),
runningDurationSec, configuredTimeout);
ExtensionTimeoutEvent event;
event.extensionTypeName = startInfo.extensionTypeName;
event.bundleName = startInfo.bundleName;
event.abilityName = startInfo.abilityName;
event.runningDuration = runningDurationSec;
event.stillAlive = false;
event.cnt = 1;
std::lock_guard<std::mutex> lock(monitorMutex_);
AddOrUpdateTimeoutEvent(event);
}
void ExtensionRunningTimeoutMonitor::StartMonitor()
{
TAG_LOGI(AAFwkTag::ABILITYMGR, "start extension running timeout monitor");
SubmitPeriodicTask();
}
void ExtensionRunningTimeoutMonitor::StopMonitor()
{
TAG_LOGI(AAFwkTag::ABILITYMGR, "stop extension running timeout monitor");
auto taskHandler = TaskHandlerWrap::GetFfrtHandler();
if (taskHandler != nullptr) {
taskHandler->CancelTask(PERIODIC_TASK_NAME);
}
}
void ExtensionRunningTimeoutMonitor::SubmitPeriodicTask()
{
auto taskHandler = TaskHandlerWrap::GetFfrtHandler();
if (taskHandler == nullptr) {
TAG_LOGE(AAFwkTag::ABILITYMGR, "failed to get ffrt handler");
return;
}
auto task = []() {
TAG_LOGI(AAFwkTag::ABILITYMGR, "extension timeout periodic task triggered");
auto monitor = DelayedSingleton<ExtensionRunningTimeoutMonitor>::GetInstance();
monitor->CheckAliveExtensions();
monitor->ReportTimeoutEvents();
monitor->SubmitPeriodicTask();
};
taskHandler->SubmitTask(task, PERIODIC_TASK_NAME, REPORT_INTERVAL_MS);
}
void ExtensionRunningTimeoutMonitor::ReportTimeoutEvents()
{
std::list<ExtensionTimeoutEvent> eventsToReport;
{
std::lock_guard<std::mutex> lock(monitorMutex_);
if (cachedEvents_.empty()) {
TAG_LOGD(AAFwkTag::ABILITYMGR, "no timeout events to report");
return;
}
eventsToReport = cachedEvents_;
cachedEvents_.clear();
}
std::vector<std::unique_ptr<char[]>> stringBuffers;
std::vector<char*> extensionTypePtrs;
std::vector<char*> bundleNamePtrs;
std::vector<char*> abilityNamePtrs;
std::vector<int64_t> runningDurations;
std::vector<bool> stillAliveFlags;
std::vector<int32_t> cnts;
for (const auto &event : eventsToReport) {
if (!CopyStringParam(event.extensionTypeName, stringBuffers, extensionTypePtrs) ||
!CopyStringParam(event.bundleName, stringBuffers, bundleNamePtrs) ||
!CopyStringParam(event.abilityName, stringBuffers, abilityNamePtrs)) {
TAG_LOGE(AAFwkTag::ABILITYMGR, "strcpy_s failed for event string");
continue;
}
runningDurations.push_back(event.runningDuration);
stillAliveFlags.push_back(event.stillAlive);
cnts.push_back(event.cnt);
}
if (extensionTypePtrs.empty()) {
TAG_LOGW(AAFwkTag::ABILITYMGR, "no valid events to report after buffer preparation");
return;
}
HisyseventReport report(HISEVENT_PARAM_COUNT);
report.InsertParam(EXTENSION_TYPE_KEY, extensionTypePtrs);
report.InsertParam(BUNDLE_NAME_KEY, bundleNamePtrs);
report.InsertParam(ABILITY_NAME_KEY, abilityNamePtrs);
report.InsertParam(RUNNING_DURATION_KEY, runningDurations);
report.InsertParam(STILL_ALIVE_KEY, stillAliveFlags);
report.InsertParam(CNT_KEY, cnts);
int32_t ret = report.Report(DOMAIN, EVENT_NAME, HISYSEVENT_STATISTIC);
TAG_LOGI(AAFwkTag::ABILITYMGR, "reported %{public}zu timeout events, ret: %{public}d",
eventsToReport.size(), ret);
}
void ExtensionRunningTimeoutMonitor::CheckAliveExtensions()
{
TAG_LOGI(AAFwkTag::ABILITYMGR, "check alive extensions for timeout violations");
// Copy the running extensions map to avoid holding mutex during slow checks
std::unordered_map<int32_t, ExtensionStartInfo> aliveCopy;
{
std::lock_guard<std::mutex> lock(monitorMutex_);
aliveCopy = runningExtensions_;
}
auto now = std::chrono::steady_clock::now();
auto nowMillis = std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch()).count();
std::vector<ExtensionTimeoutEvent> pendingEvents;
for (auto &[recordId, startInfo] : aliveCopy) {
int32_t configuredTimeout = DelayedSingleton<ExtensionConfig>::GetInstance()->
GetExtensionRunningTimeoutTime(startInfo.extensionTypeName);
if (configuredTimeout <= 0) {
continue;
}
int64_t runningDurationMs = nowMillis - startInfo.startTimeMillis;
int64_t runningDurationSec = runningDurationMs / 1000;
if (runningDurationSec <= configuredTimeout) {
continue;
}
TAG_LOGI(AAFwkTag::ABILITYMGR,
"alive extension %{public}s/%{public}s exceeded timeout, duration: %{public}" PRId64 "s",
startInfo.bundleName.c_str(), startInfo.abilityName.c_str(), runningDurationSec);
ExtensionTimeoutEvent event;
event.extensionTypeName = startInfo.extensionTypeName;
event.bundleName = startInfo.bundleName;
event.abilityName = startInfo.abilityName;
event.runningDuration = runningDurationSec;
event.stillAlive = true;
event.cnt = 1;
pendingEvents.push_back(event);
}
if (!pendingEvents.empty()) {
std::lock_guard<std::mutex> lock(monitorMutex_);
for (auto &event : pendingEvents) {
AddOrUpdateTimeoutEvent(event);
}
}
}
bool ExtensionRunningTimeoutMonitor::IsDuplicateEvent(const ExtensionTimeoutEvent &event,
std::list<ExtensionTimeoutEvent>::iterator &dupIter)
{
for (auto it = cachedEvents_.begin(); it != cachedEvents_.end(); ++it) {
if (it->extensionTypeName == event.extensionTypeName &&
it->bundleName == event.bundleName &&
it->abilityName == event.abilityName) {
dupIter = it;
return true;
}
}
return false;
}
void ExtensionRunningTimeoutMonitor::AddOrUpdateTimeoutEvent(const ExtensionTimeoutEvent &event)
{
std::list<ExtensionTimeoutEvent>::iterator dupIter;
if (IsDuplicateEvent(event, dupIter)) {
dupIter->cnt++;
dupIter->runningDuration = event.runningDuration;
dupIter->stillAlive = event.stillAlive;
TAG_LOGD(AAFwkTag::ABILITYMGR,
"updated existing timeout event for %{public}s/%{public}s, cnt: %{public}d",
event.bundleName.c_str(), event.abilityName.c_str(), dupIter->cnt);
return;
}
if (static_cast<int32_t>(cachedEvents_.size()) >= MAX_CACHED_EVENTS) {
TAG_LOGW(AAFwkTag::ABILITYMGR,
"cache full (%{public}d), discarding event for %{public}s/%{public}s",
MAX_CACHED_EVENTS, event.bundleName.c_str(), event.abilityName.c_str());
return;
}
cachedEvents_.push_back(event);
TAG_LOGD(AAFwkTag::ABILITYMGR,
"added new timeout event for %{public}s/%{public}s, cache size: %{public}zu",
event.bundleName.c_str(), event.abilityName.c_str(), cachedEvents_.size());
}
} // namespace AAFwk
} // namespace OHOS
+6 -1
View File
@@ -33,6 +33,9 @@ public:
return;
}
this->params_ = new (std::nothrow) HiSysEventParam[SYSTEM_PARAM_MAX_LEN];
if (this->params_ == nullptr) {
length_ = 0;
}
}
~HisyseventReport()
{
@@ -56,13 +59,15 @@ public:
void InsertParam(const char* name, const std::vector<int32_t> &value);
void InsertParam(const char* name, std::vector<char*> &value);
void InsertParam(const char* name, const std::vector<uint64_t> &value);
void InsertParam(const char* name, const std::vector<bool> &value);
void InsertParam(const char* name, const std::vector<int64_t> &value);
int32_t Report(const char* domain, const char* event, HiSysEventEventType type);
private:
void SetParamName(HiSysEventParam& param, const char* name);
std::vector<std::unique_ptr<char[]>> paramBuffers_;
HiSysEventParam* params_;
HiSysEventParam* params_ = nullptr;
int32_t length_ = SYSTEM_PARAM_MAX_LEN;
int32_t pos_ = 0;
};
+73 -4
View File
@@ -197,7 +197,7 @@ void HisyseventReport::InsertParam(const char* name, char* value)
int32_t len = std::strlen(value) + 1;
auto buffer = std::make_unique<char[]>(len);
int32_t ret = strcpy_s(buffer.get(), len, value);
if (ret != 0) {
if (ret != EOK) {
TAG_LOGE(AAFwkTag::DEFAULT, "InsertParam err %{public}d", ret);
return;
}
@@ -273,20 +273,89 @@ void HisyseventReport::InsertParam(const char* name, std::vector<char*> &value)
params_[pos_++] = param;
}
void HisyseventReport::InsertParam(const char* name, const std::vector<bool> &value)
{
if (length_ <= pos_) {
TAG_LOGE(AAFwkTag::DEFAULT, "param is full");
return;
}
HiSysEventParam param = {
.t = HISYSEVENT_BOOL_ARRAY,
.v = { .array = nullptr},
.arraySize = 0,
};
if (!value.empty()) {
size_t bufSize = value.size() * sizeof(bool);
auto *buf = new (std::nothrow) char[bufSize]();
if (buf != nullptr) {
auto *boolBuf = reinterpret_cast<bool*>(buf);
for (size_t i = 0; i < value.size(); ++i) {
boolBuf[i] = value[i];
}
param.v.array = static_cast<void*>(boolBuf);
param.arraySize = value.size();
paramBuffers_.emplace_back(buf);
}
}
SetParamName(param, name);
params_[pos_++] = param;
}
void HisyseventReport::InsertParam(const char* name, const std::vector<int64_t> &value)
{
if (length_ <= pos_) {
TAG_LOGE(AAFwkTag::DEFAULT, "param is full");
return;
}
HiSysEventParam param = {
.t = HISYSEVENT_INT64_ARRAY,
.v = { .array = nullptr},
.arraySize = 0,
};
if (!value.empty()) {
param.v.array = static_cast<void*>(const_cast<int64_t*>(value.data()));
param.arraySize = value.size();
}
SetParamName(param, name);
params_[pos_++] = param;
}
void HisyseventReport::InsertParam(const char* name, std::string value)
{
this->InsertParam(name, const_cast<char *>(value.c_str()));
this->InsertParam(name, value.c_str());
}
void HisyseventReport::InsertParam(const char* name, const char* value)
{
this->InsertParam(name, const_cast<char *>(value));
if (!name || !value || length_ <= pos_) {
TAG_LOGE(AAFwkTag::DEFAULT, "param is full");
return;
}
int32_t len = static_cast<int32_t>(std::strlen(value)) + 1;
auto buffer = std::make_unique<char[]>(len);
int32_t ret = strcpy_s(buffer.get(), len, value);
if (ret != EOK) {
TAG_LOGE(AAFwkTag::DEFAULT, "InsertParam err %{public}d", ret);
return;
}
HiSysEventParam param = {
.t = HISYSEVENT_STRING,
.v = { .s = buffer.get()},
.arraySize = 0,
};
SetParamName(param, name);
params_[pos_++] = param;
paramBuffers_.emplace_back(std::move(buffer));
}
void HisyseventReport::SetParamName(HiSysEventParam& param, const char* name)
{
int32_t ret = strcpy_s(param.name, sizeof(param.name), name);
if (ret != 0) {
if (ret != EOK) {
TAG_LOGE(AAFwkTag::DEFAULT, "SetParamName err %{public}d", ret);
}
}
@@ -50,6 +50,8 @@ ohos_fuzztest("ExtensionRecordManagerFuzzTest") {
"${ability_runtime_services_path}/abilitymgr/src/extension_record/extension_record.cpp",
"${ability_runtime_services_path}/abilitymgr/src/extension_record/extension_record_factory.cpp",
"${ability_runtime_services_path}/abilitymgr/src/extension_record/extension_record_manager.cpp",
"${ability_runtime_services_path}/abilitymgr/src/extension_running_timeout_monitor.cpp",
"${ability_runtime_services_path}/abilitymgr/src/extension_config.cpp",
"${ability_runtime_services_path}/abilitymgr/src/ui_extension/preload_uiext_state_observer.cpp",
"${ability_runtime_services_path}/abilitymgr/src/ui_extension_record/ui_extension_record.cpp",
"${ability_runtime_services_path}/abilitymgr/src/ui_extension_record/ui_extension_record_factory.cpp",
@@ -48,6 +48,8 @@ ohos_fuzztest("ExtensionRecordManageraFuzzTest") {
"${ability_runtime_services_path}/abilitymgr/src/extension_record/extension_record.cpp",
"${ability_runtime_services_path}/abilitymgr/src/extension_record/extension_record_factory.cpp",
"${ability_runtime_services_path}/abilitymgr/src/extension_record/extension_record_manager.cpp",
"${ability_runtime_services_path}/abilitymgr/src/extension_running_timeout_monitor.cpp",
"${ability_runtime_services_path}/abilitymgr/src/extension_config.cpp",
"${ability_runtime_services_path}/abilitymgr/src/ui_extension/preload_uiext_state_observer.cpp",
"${ability_runtime_services_path}/abilitymgr/src/ui_extension_record/ui_extension_record.cpp",
"${ability_runtime_services_path}/abilitymgr/src/ui_extension_record/ui_extension_record_factory.cpp",
@@ -51,6 +51,8 @@ ohos_fuzztest("ExtensionRecordManagerbFuzzTest") {
"${ability_runtime_services_path}/abilitymgr/src/extension_record/extension_record.cpp",
"${ability_runtime_services_path}/abilitymgr/src/extension_record/extension_record_factory.cpp",
"${ability_runtime_services_path}/abilitymgr/src/extension_record/extension_record_manager.cpp",
"${ability_runtime_services_path}/abilitymgr/src/extension_running_timeout_monitor.cpp",
"${ability_runtime_services_path}/abilitymgr/src/extension_config.cpp",
"${ability_runtime_services_path}/abilitymgr/src/ui_extension/preload_uiext_state_observer.cpp",
"${ability_runtime_services_path}/abilitymgr/src/ui_extension_record/ui_extension_record.cpp",
"${ability_runtime_services_path}/abilitymgr/src/ui_extension_record/ui_extension_record_factory.cpp",
@@ -51,6 +51,8 @@ ohos_fuzztest("ExtensionRecordManagercFuzzTest") {
"${ability_runtime_services_path}/abilitymgr/src/extension_record/extension_record.cpp",
"${ability_runtime_services_path}/abilitymgr/src/extension_record/extension_record_factory.cpp",
"${ability_runtime_services_path}/abilitymgr/src/extension_record/extension_record_manager.cpp",
"${ability_runtime_services_path}/abilitymgr/src/extension_running_timeout_monitor.cpp",
"${ability_runtime_services_path}/abilitymgr/src/extension_config.cpp",
"${ability_runtime_services_path}/abilitymgr/src/ui_extension/preload_uiext_state_observer.cpp",
"${ability_runtime_services_path}/abilitymgr/src/ui_extension_record/ui_extension_record.cpp",
"${ability_runtime_services_path}/abilitymgr/src/ui_extension_record/ui_extension_record_factory.cpp",
@@ -43,6 +43,7 @@ abilityms_files = [
"${ability_runtime_services_path}/abilitymgr/src/extension_running_info.cpp",
"${ability_runtime_services_path}/abilitymgr/src/extension_record/base_extension_record.cpp",
"${ability_runtime_services_path}/abilitymgr/src/extension_record/extension_record_manager.cpp",
"${ability_runtime_services_path}/abilitymgr/src/extension_running_timeout_monitor.cpp",
"${ability_runtime_services_path}/abilitymgr/src/extension_record/extension_record.cpp",
"${ability_runtime_services_path}/abilitymgr/src/extension_record/extension_record_factory.cpp",
"${ability_runtime_services_path}/abilitymgr/src/foreground_app_connection_manager/foreground_app_connection_manager.cpp",
@@ -123,6 +123,7 @@ ohos_unittest("ability_manager_service_fourteenth_test") {
"${ability_runtime_services_path}/abilitymgr/src/extension_record/extension_record.cpp",
"${ability_runtime_services_path}/abilitymgr/src/extension_record/extension_record_factory.cpp",
"${ability_runtime_services_path}/abilitymgr/src/extension_record/extension_record_manager.cpp",
"${ability_runtime_services_path}/abilitymgr/src/extension_running_timeout_monitor.cpp",
"${ability_runtime_services_path}/abilitymgr/src/extension_running_info.cpp",
"${ability_runtime_services_path}/abilitymgr/src/foreground_app_connection_manager/foreground_app_connection_manager.cpp",
"${ability_runtime_services_path}/abilitymgr/src/free_install_observer_manager.cpp",
@@ -125,6 +125,7 @@ ohos_unittest("ability_manager_service_second_test") {
"${ability_runtime_services_path}/abilitymgr/src/extension_record/extension_record.cpp",
"${ability_runtime_services_path}/abilitymgr/src/extension_record/extension_record_factory.cpp",
"${ability_runtime_services_path}/abilitymgr/src/extension_record/extension_record_manager.cpp",
"${ability_runtime_services_path}/abilitymgr/src/extension_running_timeout_monitor.cpp",
"${ability_runtime_services_path}/abilitymgr/src/extension_running_info.cpp",
"${ability_runtime_services_path}/abilitymgr/src/foreground_app_connection_manager/foreground_app_connection_manager.cpp",
"${ability_runtime_services_path}/abilitymgr/src/free_install_observer_manager.cpp",
@@ -43,6 +43,7 @@ abilityms_files = [
"${ability_runtime_services_path}/abilitymgr/src/extension_running_info.cpp",
"${ability_runtime_services_path}/abilitymgr/src/extension_record/base_extension_record.cpp",
"${ability_runtime_services_path}/abilitymgr/src/extension_record/extension_record_manager.cpp",
"${ability_runtime_services_path}/abilitymgr/src/extension_running_timeout_monitor.cpp",
"${ability_runtime_services_path}/abilitymgr/src/extension_record/extension_record.cpp",
"${ability_runtime_services_path}/abilitymgr/src/extension_record/extension_record_factory.cpp",
"${ability_runtime_services_path}/abilitymgr/src/foreground_app_connection_manager/foreground_app_connection_manager.cpp",
@@ -43,6 +43,7 @@ abilityms_files = [
"${ability_runtime_services_path}/abilitymgr/src/extension_running_info.cpp",
"${ability_runtime_services_path}/abilitymgr/src/extension_record/base_extension_record.cpp",
"${ability_runtime_services_path}/abilitymgr/src/extension_record/extension_record_manager.cpp",
"${ability_runtime_services_path}/abilitymgr/src/extension_running_timeout_monitor.cpp",
"${ability_runtime_services_path}/abilitymgr/src/extension_record/extension_record.cpp",
"${ability_runtime_services_path}/abilitymgr/src/extension_record/extension_record_factory.cpp",
"${ability_runtime_services_path}/abilitymgr/src/foreground_app_connection_manager/foreground_app_connection_manager.cpp",
@@ -123,6 +123,7 @@ ohos_unittest("ability_manager_service_thirteenth_test") {
"${ability_runtime_services_path}/abilitymgr/src/extension_record/extension_record.cpp",
"${ability_runtime_services_path}/abilitymgr/src/extension_record/extension_record_factory.cpp",
"${ability_runtime_services_path}/abilitymgr/src/extension_record/extension_record_manager.cpp",
"${ability_runtime_services_path}/abilitymgr/src/extension_running_timeout_monitor.cpp",
"${ability_runtime_services_path}/abilitymgr/src/extension_running_info.cpp",
"${ability_runtime_services_path}/abilitymgr/src/foreground_app_connection_manager/foreground_app_connection_manager.cpp",
"${ability_runtime_services_path}/abilitymgr/src/free_install_observer_manager.cpp",
@@ -48,6 +48,12 @@ ohos_unittest("extension_record_manager_second_test") {
"${ability_runtime_services_path}/abilitymgr/src/extension_record/extension_record.cpp",
"${ability_runtime_services_path}/abilitymgr/src/extension_record/extension_record_factory.cpp",
"${ability_runtime_services_path}/abilitymgr/src/extension_record/extension_record_manager.cpp",
"${ability_runtime_services_path}/abilitymgr/src/extension_running_timeout_monitor.cpp",
"${ability_runtime_services_path}/abilitymgr/src/extension_config.cpp",
"${ability_runtime_services_path}/common/src/ffrt_task_handler_wrap.cpp",
"${ability_runtime_services_path}/common/src/hisysevent_report.cpp",
"${ability_runtime_services_path}/common/src/queue_task_handler_wrap.cpp",
"${ability_runtime_services_path}/common/src/task_handler_wrap.cpp",
"${ability_runtime_services_path}/abilitymgr/src/ui_extension/preload_uiext_state_observer.cpp",
"${ability_runtime_services_path}/abilitymgr/src/ui_extension_record/ui_extension_record.cpp",
"${ability_runtime_services_path}/abilitymgr/src/ui_extension_record/ui_extension_record_factory.cpp",
@@ -73,6 +79,7 @@ ohos_unittest("extension_record_manager_second_test") {
"${ability_runtime_path}/utils/global/common:runtime_utils",
"${ability_runtime_path}/utils/server/startup:startup_util",
"${ability_runtime_services_path}/abilitymgr:abilityms",
"${ability_runtime_services_path}/common:task_handler_wrap",
]
external_deps = [
@@ -14,6 +14,8 @@
*/
#include <gtest/gtest.h>
#include <thread>
#include <chrono>
#include "hilog_tag_wrapper.h"
#include "ability_manager_client.h"
@@ -21,8 +23,10 @@
#define private public
#define protected public
#define inline
#include "extension_record.h"
#include "extension_record.h"
#include "extension_record_manager.h"
#include "extension_running_timeout_monitor.h"
#include "extension_config.h"
#define inline
#undef protected
#undef private
@@ -1092,5 +1096,610 @@ HWTEST_F(ExtensionRecordManagerSecondTest, TerminateTimeout_0100, TestSize.Level
TAG_LOGI(AAFwkTag::TEST, "end.");
}
// ========== ExtensionRunningTimeoutMonitor TDD Tests ==========
class ExtensionRunningTimeoutMonitorTest : public testing::Test {
public:
static void SetUpTestCase();
static void TearDownTestCase();
void SetUp() override;
void TearDown() override;
};
void ExtensionRunningTimeoutMonitorTest::SetUpTestCase()
{}
void ExtensionRunningTimeoutMonitorTest::TearDownTestCase()
{}
void ExtensionRunningTimeoutMonitorTest::SetUp()
{
auto monitor = DelayedSingleton<AAFwk::ExtensionRunningTimeoutMonitor>::GetInstance();
monitor->runningExtensions_.clear();
monitor->cachedEvents_.clear();
}
void ExtensionRunningTimeoutMonitorTest::TearDown()
{
auto monitor = DelayedSingleton<AAFwk::ExtensionRunningTimeoutMonitor>::GetInstance();
monitor->StopMonitor();
monitor->runningExtensions_.clear();
monitor->cachedEvents_.clear();
}
/**
* @tc.name: OnExtensionStarted_0100
* @tc.desc: Test OnExtensionStarted records extension info correctly.
* @tc.type: FUNC
* @tc.require: issue
*/
HWTEST_F(ExtensionRunningTimeoutMonitorTest, OnExtensionStarted_0100, TestSize.Level1)
{
TAG_LOGI(AAFwkTag::TEST, "begin.");
auto monitor = DelayedSingleton<AAFwk::ExtensionRunningTimeoutMonitor>::GetInstance();
ASSERT_NE(monitor, nullptr);
monitor->OnExtensionStarted(100, "ServiceExtension", "com.test.bundle", "TestAbility");
EXPECT_EQ(monitor->runningExtensions_.size(), static_cast<size_t>(1));
EXPECT_EQ(monitor->runningExtensions_.count(100), static_cast<size_t>(1));
EXPECT_EQ(monitor->runningExtensions_[100].extensionTypeName, "ServiceExtension");
EXPECT_EQ(monitor->runningExtensions_[100].bundleName, "com.test.bundle");
EXPECT_EQ(monitor->runningExtensions_[100].abilityName, "TestAbility");
EXPECT_GT(monitor->runningExtensions_[100].startTimeMillis, static_cast<int64_t>(0));
TAG_LOGI(AAFwkTag::TEST, "end.");
}
/**
* @tc.name: OnExtensionStarted_0200
* @tc.desc: Test OnExtensionStarted with multiple extensions.
* @tc.type: FUNC
* @tc.require: issue
*/
HWTEST_F(ExtensionRunningTimeoutMonitorTest, OnExtensionStarted_0200, TestSize.Level1)
{
TAG_LOGI(AAFwkTag::TEST, "begin.");
auto monitor = DelayedSingleton<AAFwk::ExtensionRunningTimeoutMonitor>::GetInstance();
ASSERT_NE(monitor, nullptr);
monitor->OnExtensionStarted(1, "ServiceExtension", "com.test.a", "AbilityA");
monitor->OnExtensionStarted(2, "UIExtension", "com.test.b", "AbilityB");
monitor->OnExtensionStarted(3, "DataShareExtension", "com.test.c", "AbilityC");
EXPECT_EQ(monitor->runningExtensions_.size(), static_cast<size_t>(3));
EXPECT_EQ(monitor->runningExtensions_[1].extensionTypeName, "ServiceExtension");
EXPECT_EQ(monitor->runningExtensions_[2].extensionTypeName, "UIExtension");
EXPECT_EQ(monitor->runningExtensions_[3].extensionTypeName, "DataShareExtension");
TAG_LOGI(AAFwkTag::TEST, "end.");
}
/**
* @tc.name: OnExtensionStarted_0300
* @tc.desc: Test OnExtensionStarted with same recordId overwrites previous entry.
* @tc.type: FUNC
* @tc.require: issue
*/
HWTEST_F(ExtensionRunningTimeoutMonitorTest, OnExtensionStarted_0300, TestSize.Level1)
{
TAG_LOGI(AAFwkTag::TEST, "begin.");
auto monitor = DelayedSingleton<AAFwk::ExtensionRunningTimeoutMonitor>::GetInstance();
ASSERT_NE(monitor, nullptr);
monitor->OnExtensionStarted(100, "ServiceExtension", "com.test.old", "OldAbility");
EXPECT_EQ(monitor->runningExtensions_.size(), static_cast<size_t>(1));
monitor->OnExtensionStarted(100, "UIExtension", "com.test.new", "NewAbility");
EXPECT_EQ(monitor->runningExtensions_.size(), static_cast<size_t>(1));
EXPECT_EQ(monitor->runningExtensions_[100].extensionTypeName, "UIExtension");
EXPECT_EQ(monitor->runningExtensions_[100].bundleName, "com.test.new");
TAG_LOGI(AAFwkTag::TEST, "end.");
}
/**
* @tc.name: OnExtensionTerminated_0100
* @tc.desc: Test OnExtensionTerminated with unknown recordId does nothing.
* @tc.type: FUNC
* @tc.require: issue
*/
HWTEST_F(ExtensionRunningTimeoutMonitorTest, OnExtensionTerminated_0100, TestSize.Level1)
{
TAG_LOGI(AAFwkTag::TEST, "begin.");
auto monitor = DelayedSingleton<AAFwk::ExtensionRunningTimeoutMonitor>::GetInstance();
ASSERT_NE(monitor, nullptr);
monitor->OnExtensionTerminated(9999);
EXPECT_EQ(monitor->runningExtensions_.size(), static_cast<size_t>(0));
EXPECT_EQ(monitor->cachedEvents_.size(), static_cast<size_t>(0));
TAG_LOGI(AAFwkTag::TEST, "end.");
}
/**
* @tc.name: OnExtensionTerminated_0200
* @tc.desc: Test OnExtensionTerminated removes extension from running list.
* @tc.type: FUNC
* @tc.require: issue
*/
HWTEST_F(ExtensionRunningTimeoutMonitorTest, OnExtensionTerminated_0200, TestSize.Level1)
{
TAG_LOGI(AAFwkTag::TEST, "begin.");
auto monitor = DelayedSingleton<AAFwk::ExtensionRunningTimeoutMonitor>::GetInstance();
ASSERT_NE(monitor, nullptr);
monitor->OnExtensionStarted(100, "ServiceExtension", "com.test.bundle", "TestAbility");
EXPECT_EQ(monitor->runningExtensions_.size(), static_cast<size_t>(1));
// No timeout configured (default -1), so no event should be cached
monitor->OnExtensionTerminated(100);
EXPECT_EQ(monitor->runningExtensions_.size(), static_cast<size_t>(0));
TAG_LOGI(AAFwkTag::TEST, "end.");
}
/**
* @tc.name: OnExtensionTerminated_0300
* @tc.desc: Test OnExtensionTerminated detects timeout and caches event.
* @tc.type: FUNC
* @tc.require: issue
*/
HWTEST_F(ExtensionRunningTimeoutMonitorTest, OnExtensionTerminated_0300, TestSize.Level1)
{
TAG_LOGI(AAFwkTag::TEST, "begin.");
auto monitor = DelayedSingleton<AAFwk::ExtensionRunningTimeoutMonitor>::GetInstance();
ASSERT_NE(monitor, nullptr);
// Configure timeout for this extension type via ExtensionConfig
auto config = DelayedSingleton<AAFwk::ExtensionConfig>::GetInstance();
ASSERT_NE(config, nullptr);
AAFwk::ExtensionConfigItem item;
item.extensionRunningTimeoutTime = 1; // 1 second
config->configMap_["TestTimeoutExtension"] = item;
monitor->OnExtensionStarted(200, "TestTimeoutExtension", "com.test.timeout", "TimeoutAbility");
EXPECT_EQ(monitor->runningExtensions_.size(), static_cast<size_t>(1));
// Wait for timeout to be exceeded
std::this_thread::sleep_for(std::chrono::seconds(2));
monitor->OnExtensionTerminated(200);
EXPECT_EQ(monitor->runningExtensions_.size(), static_cast<size_t>(0));
EXPECT_EQ(monitor->cachedEvents_.size(), static_cast<size_t>(1));
auto &event = monitor->cachedEvents_.front();
EXPECT_EQ(event.extensionTypeName, "TestTimeoutExtension");
EXPECT_EQ(event.bundleName, "com.test.timeout");
EXPECT_EQ(event.abilityName, "TimeoutAbility");
EXPECT_GE(event.runningDuration, static_cast<int64_t>(1));
EXPECT_EQ(event.stillAlive, false);
EXPECT_EQ(event.cnt, 1);
// Clean up config
config->configMap_.erase("TestTimeoutExtension");
TAG_LOGI(AAFwkTag::TEST, "end.");
}
/**
* @tc.name: OnExtensionTerminated_0400
* @tc.desc: Test OnExtensionTerminated within timeout does not cache event.
* @tc.type: FUNC
* @tc.require: issue
*/
HWTEST_F(ExtensionRunningTimeoutMonitorTest, OnExtensionTerminated_0400, TestSize.Level1)
{
TAG_LOGI(AAFwkTag::TEST, "begin.");
auto monitor = DelayedSingleton<AAFwk::ExtensionRunningTimeoutMonitor>::GetInstance();
ASSERT_NE(monitor, nullptr);
// Configure long timeout
auto config = DelayedSingleton<AAFwk::ExtensionConfig>::GetInstance();
AAFwk::ExtensionConfigItem item;
item.extensionRunningTimeoutTime = 3600; // 1 hour
config->configMap_["TestLongTimeout"] = item;
monitor->OnExtensionStarted(300, "TestLongTimeout", "com.test.quick", "QuickAbility");
// Terminate immediately, well within 1 hour timeout
monitor->OnExtensionTerminated(300);
EXPECT_EQ(monitor->runningExtensions_.size(), static_cast<size_t>(0));
EXPECT_EQ(monitor->cachedEvents_.size(), static_cast<size_t>(0));
config->configMap_.erase("TestLongTimeout");
TAG_LOGI(AAFwkTag::TEST, "end.");
}
/**
* @tc.name: AddOrUpdateTimeoutEvent_0100
* @tc.desc: Test AddOrUpdateTimeoutEvent adds new event when no duplicate.
* @tc.type: FUNC
* @tc.require: issue
*/
HWTEST_F(ExtensionRunningTimeoutMonitorTest, AddOrUpdateTimeoutEvent_0100, TestSize.Level1)
{
TAG_LOGI(AAFwkTag::TEST, "begin.");
auto monitor = DelayedSingleton<AAFwk::ExtensionRunningTimeoutMonitor>::GetInstance();
ASSERT_NE(monitor, nullptr);
AAFwk::ExtensionTimeoutEvent event;
event.extensionTypeName = "ServiceExtension";
event.bundleName = "com.test.bundle";
event.abilityName = "Ability1";
event.runningDuration = 100;
event.stillAlive = false;
event.cnt = 1;
monitor->AddOrUpdateTimeoutEvent(event);
EXPECT_EQ(monitor->cachedEvents_.size(), static_cast<size_t>(1));
TAG_LOGI(AAFwkTag::TEST, "end.");
}
/**
* @tc.name: AddOrUpdateTimeoutEvent_0200
* @tc.desc: Test AddOrUpdateTimeoutEvent increments cnt for duplicate event.
* @tc.type: FUNC
* @tc.require: issue
*/
HWTEST_F(ExtensionRunningTimeoutMonitorTest, AddOrUpdateTimeoutEvent_0200, TestSize.Level1)
{
TAG_LOGI(AAFwkTag::TEST, "begin.");
auto monitor = DelayedSingleton<AAFwk::ExtensionRunningTimeoutMonitor>::GetInstance();
ASSERT_NE(monitor, nullptr);
AAFwk::ExtensionTimeoutEvent event1;
event1.extensionTypeName = "ServiceExtension";
event1.bundleName = "com.test.dup";
event1.abilityName = "DupAbility";
event1.runningDuration = 10;
event1.stillAlive = true;
event1.cnt = 1;
monitor->AddOrUpdateTimeoutEvent(event1);
EXPECT_EQ(monitor->cachedEvents_.size(), static_cast<size_t>(1));
EXPECT_EQ(monitor->cachedEvents_.front().cnt, 1);
// Same extension detected again
AAFwk::ExtensionTimeoutEvent event2;
event2.extensionTypeName = "ServiceExtension";
event2.bundleName = "com.test.dup";
event2.abilityName = "DupAbility";
event2.runningDuration = 20;
event2.stillAlive = false;
event2.cnt = 1;
monitor->AddOrUpdateTimeoutEvent(event2);
EXPECT_EQ(monitor->cachedEvents_.size(), static_cast<size_t>(1));
EXPECT_EQ(monitor->cachedEvents_.front().cnt, 2);
EXPECT_EQ(monitor->cachedEvents_.front().runningDuration, static_cast<int64_t>(20));
EXPECT_EQ(monitor->cachedEvents_.front().stillAlive, false);
TAG_LOGI(AAFwkTag::TEST, "end.");
}
/**
* @tc.name: AddOrUpdateTimeoutEvent_0300
* @tc.desc: Test AddOrUpdateTimeoutEvent discards event when cache is full.
* @tc.type: FUNC
* @tc.require: issue
*/
HWTEST_F(ExtensionRunningTimeoutMonitorTest, AddOrUpdateTimeoutEvent_0300, TestSize.Level1)
{
TAG_LOGI(AAFwkTag::TEST, "begin.");
auto monitor = DelayedSingleton<AAFwk::ExtensionRunningTimeoutMonitor>::GetInstance();
ASSERT_NE(monitor, nullptr);
// Fill cache to MAX_CACHED_EVENTS (5)
for (int32_t i = 0; i < monitor->MAX_CACHED_EVENTS; i++) {
AAFwk::ExtensionTimeoutEvent event;
event.extensionTypeName = "ServiceExtension";
event.bundleName = "com.test.bundle" + std::to_string(i);
event.abilityName = "Ability" + std::to_string(i);
event.runningDuration = i + 1;
event.cnt = 1;
monitor->AddOrUpdateTimeoutEvent(event);
}
EXPECT_EQ(monitor->cachedEvents_.size(), static_cast<size_t>(monitor->MAX_CACHED_EVENTS));
// 6th different event should be discarded
AAFwk::ExtensionTimeoutEvent overflowEvent;
overflowEvent.extensionTypeName = "ServiceExtension";
overflowEvent.bundleName = "com.test.overflow";
overflowEvent.abilityName = "OverflowAbility";
overflowEvent.runningDuration = 999;
overflowEvent.cnt = 1;
monitor->AddOrUpdateTimeoutEvent(overflowEvent);
EXPECT_EQ(monitor->cachedEvents_.size(), static_cast<size_t>(monitor->MAX_CACHED_EVENTS));
// Verify overflow event is not in cache
bool found = false;
for (const auto &e : monitor->cachedEvents_) {
if (e.bundleName == "com.test.overflow") {
found = true;
break;
}
}
EXPECT_FALSE(found);
TAG_LOGI(AAFwkTag::TEST, "end.");
}
/**
* @tc.name: IsDuplicateEvent_0100
* @tc.desc: Test IsDuplicateEvent returns true for matching events.
* @tc.type: FUNC
* @tc.require: issue
*/
HWTEST_F(ExtensionRunningTimeoutMonitorTest, IsDuplicateEvent_0100, TestSize.Level1)
{
TAG_LOGI(AAFwkTag::TEST, "begin.");
auto monitor = DelayedSingleton<AAFwk::ExtensionRunningTimeoutMonitor>::GetInstance();
ASSERT_NE(monitor, nullptr);
AAFwk::ExtensionTimeoutEvent event;
event.extensionTypeName = "ServiceExtension";
event.bundleName = "com.test.dup";
event.abilityName = "TestAbility";
monitor->cachedEvents_.push_back(event);
AAFwk::ExtensionTimeoutEvent candidate;
candidate.extensionTypeName = "ServiceExtension";
candidate.bundleName = "com.test.dup";
candidate.abilityName = "TestAbility";
std::list<AAFwk::ExtensionTimeoutEvent>::iterator dupIter;
EXPECT_TRUE(monitor->IsDuplicateEvent(candidate, dupIter));
EXPECT_EQ(dupIter->bundleName, "com.test.dup");
TAG_LOGI(AAFwkTag::TEST, "end.");
}
/**
* @tc.name: IsDuplicateEvent_0200
* @tc.desc: Test IsDuplicateEvent returns false for non-matching events.
* @tc.type: FUNC
* @tc.require: issue
*/
HWTEST_F(ExtensionRunningTimeoutMonitorTest, IsDuplicateEvent_0200, TestSize.Level1)
{
TAG_LOGI(AAFwkTag::TEST, "begin.");
auto monitor = DelayedSingleton<AAFwk::ExtensionRunningTimeoutMonitor>::GetInstance();
ASSERT_NE(monitor, nullptr);
AAFwk::ExtensionTimeoutEvent event;
event.extensionTypeName = "ServiceExtension";
event.bundleName = "com.test.exist";
event.abilityName = "ExistAbility";
monitor->cachedEvents_.push_back(event);
AAFwk::ExtensionTimeoutEvent candidate;
candidate.extensionTypeName = "UIExtension";
candidate.bundleName = "com.test.exist";
candidate.abilityName = "ExistAbility";
std::list<AAFwk::ExtensionTimeoutEvent>::iterator dupIter;
EXPECT_FALSE(monitor->IsDuplicateEvent(candidate, dupIter));
TAG_LOGI(AAFwkTag::TEST, "end.");
}
/**
* @tc.name: ReportTimeoutEvents_0100
* @tc.desc: Test ReportTimeoutEvents clears cache after reporting.
* @tc.type: FUNC
* @tc.require: issue
*/
HWTEST_F(ExtensionRunningTimeoutMonitorTest, ReportTimeoutEvents_0100, TestSize.Level1)
{
TAG_LOGI(AAFwkTag::TEST, "begin.");
auto monitor = DelayedSingleton<AAFwk::ExtensionRunningTimeoutMonitor>::GetInstance();
ASSERT_NE(monitor, nullptr);
AAFwk::ExtensionTimeoutEvent event;
event.extensionTypeName = "ServiceExtension";
event.bundleName = "com.test.report";
event.abilityName = "ReportAbility";
event.runningDuration = 100;
event.stillAlive = false;
event.cnt = 1;
monitor->cachedEvents_.push_back(event);
EXPECT_EQ(monitor->cachedEvents_.size(), static_cast<size_t>(1));
monitor->ReportTimeoutEvents();
EXPECT_EQ(monitor->cachedEvents_.size(), static_cast<size_t>(0));
TAG_LOGI(AAFwkTag::TEST, "end.");
}
/**
* @tc.name: ReportTimeoutEvents_0200
* @tc.desc: Test ReportTimeoutEvents with empty cache does nothing.
* @tc.type: FUNC
* @tc.require: issue
*/
HWTEST_F(ExtensionRunningTimeoutMonitorTest, ReportTimeoutEvents_0200, TestSize.Level1)
{
TAG_LOGI(AAFwkTag::TEST, "begin.");
auto monitor = DelayedSingleton<AAFwk::ExtensionRunningTimeoutMonitor>::GetInstance();
ASSERT_NE(monitor, nullptr);
EXPECT_EQ(monitor->cachedEvents_.size(), static_cast<size_t>(0));
monitor->ReportTimeoutEvents();
EXPECT_EQ(monitor->cachedEvents_.size(), static_cast<size_t>(0));
TAG_LOGI(AAFwkTag::TEST, "end.");
}
/**
* @tc.name: CheckAliveExtensions_0100
* @tc.desc: Test CheckAliveExtensions detects running extension exceeding timeout.
* @tc.type: FUNC
* @tc.require: issue
*/
HWTEST_F(ExtensionRunningTimeoutMonitorTest, CheckAliveExtensions_0100, TestSize.Level1)
{
TAG_LOGI(AAFwkTag::TEST, "begin.");
auto monitor = DelayedSingleton<AAFwk::ExtensionRunningTimeoutMonitor>::GetInstance();
ASSERT_NE(monitor, nullptr);
// Configure timeout
auto config = DelayedSingleton<AAFwk::ExtensionConfig>::GetInstance();
AAFwk::ExtensionConfigItem item;
item.extensionRunningTimeoutTime = 1; // 1 second
config->configMap_["AliveTestExtension"] = item;
monitor->OnExtensionStarted(400, "AliveTestExtension", "com.test.alive", "AliveAbility");
// Wait for timeout
std::this_thread::sleep_for(std::chrono::seconds(2));
monitor->CheckAliveExtensions();
EXPECT_EQ(monitor->cachedEvents_.size(), static_cast<size_t>(1));
auto &event = monitor->cachedEvents_.front();
EXPECT_EQ(event.bundleName, "com.test.alive");
EXPECT_EQ(event.abilityName, "AliveAbility");
EXPECT_EQ(event.stillAlive, true); // detected via alive check
EXPECT_GE(event.runningDuration, static_cast<int64_t>(1));
// Extension should still be in running list
EXPECT_EQ(monitor->runningExtensions_.size(), static_cast<size_t>(1));
config->configMap_.erase("AliveTestExtension");
TAG_LOGI(AAFwkTag::TEST, "end.");
}
/**
* @tc.name: CheckAliveExtensions_0200
* @tc.desc: Test CheckAliveExtensions skips extensions with no timeout config.
* @tc.type: FUNC
* @tc.require: issue
*/
HWTEST_F(ExtensionRunningTimeoutMonitorTest, CheckAliveExtensions_0200, TestSize.Level1)
{
TAG_LOGI(AAFwkTag::TEST, "begin.");
auto monitor = DelayedSingleton<AAFwk::ExtensionRunningTimeoutMonitor>::GetInstance();
ASSERT_NE(monitor, nullptr);
// No config for "UnconfiguredExtension", default timeout is -1
monitor->OnExtensionStarted(500, "UnconfiguredExtension", "com.test.noconfig", "NoConfigAbility");
std::this_thread::sleep_for(std::chrono::seconds(2));
monitor->CheckAliveExtensions();
// Should not create any events since timeout is -1 (disabled)
EXPECT_EQ(monitor->cachedEvents_.size(), static_cast<size_t>(0));
TAG_LOGI(AAFwkTag::TEST, "end.");
}
/**
* @tc.name: StartStopMonitor_0100
* @tc.desc: Test StartMonitor and StopMonitor do not crash.
* @tc.type: FUNC
* @tc.require: issue
*/
HWTEST_F(ExtensionRunningTimeoutMonitorTest, StartStopMonitor_0100, TestSize.Level1)
{
TAG_LOGI(AAFwkTag::TEST, "begin.");
auto monitor = DelayedSingleton<AAFwk::ExtensionRunningTimeoutMonitor>::GetInstance();
ASSERT_NE(monitor, nullptr);
monitor->StartMonitor();
monitor->StopMonitor();
// Double stop should not crash
monitor->StopMonitor();
TAG_LOGI(AAFwkTag::TEST, "end.");
}
/**
* @tc.name: ConcurrentAccess_0100
* @tc.desc: Test concurrent OnExtensionStarted and OnExtensionTerminated do not crash.
* @tc.type: FUNC
* @tc.require: issue
*/
HWTEST_F(ExtensionRunningTimeoutMonitorTest, ConcurrentAccess_0100, TestSize.Level1)
{
TAG_LOGI(AAFwkTag::TEST, "begin.");
auto monitor = DelayedSingleton<AAFwk::ExtensionRunningTimeoutMonitor>::GetInstance();
ASSERT_NE(monitor, nullptr);
const int32_t threadCount = 10;
std::vector<std::thread> threads;
for (int32_t i = 0; i < threadCount; i++) {
threads.emplace_back([monitor, i]() {
monitor->OnExtensionStarted(i, "ServiceExtension",
"com.test.concurrent", "Ability" + std::to_string(i));
});
}
for (auto &t : threads) {
t.join();
}
EXPECT_EQ(monitor->runningExtensions_.size(), static_cast<size_t>(threadCount));
threads.clear();
for (int32_t i = 0; i < threadCount; i++) {
threads.emplace_back([monitor, i]() {
monitor->OnExtensionTerminated(i);
});
}
for (auto &t : threads) {
t.join();
}
EXPECT_EQ(monitor->runningExtensions_.size(), static_cast<size_t>(0));
TAG_LOGI(AAFwkTag::TEST, "end.");
}
/**
* @tc.name: ExtensionConfigGetRunningTimeoutTime_0100
* @tc.desc: Test ExtensionConfig GetExtensionRunningTimeoutTime returns default when not configured.
* @tc.type: FUNC
* @tc.require: issue
*/
HWTEST_F(ExtensionRunningTimeoutMonitorTest, ExtensionConfigGetRunningTimeoutTime_0100, TestSize.Level1)
{
TAG_LOGI(AAFwkTag::TEST, "begin.");
auto config = DelayedSingleton<AAFwk::ExtensionConfig>::GetInstance();
ASSERT_NE(config, nullptr);
int32_t timeout = config->GetExtensionRunningTimeoutTime("NonExistentType");
EXPECT_EQ(timeout, AAFwk::DEFAULT_EXTENSION_RUNNING_TIMEOUT_TIME);
TAG_LOGI(AAFwkTag::TEST, "end.");
}
/**
* @tc.name: ExtensionConfigGetRunningTimeoutTime_0200
* @tc.desc: Test ExtensionConfig GetExtensionRunningTimeoutTime returns configured value.
* @tc.type: FUNC
* @tc.require: issue
*/
HWTEST_F(ExtensionRunningTimeoutMonitorTest, ExtensionConfigGetRunningTimeoutTime_0200, TestSize.Level1)
{
TAG_LOGI(AAFwkTag::TEST, "begin.");
auto config = DelayedSingleton<AAFwk::ExtensionConfig>::GetInstance();
ASSERT_NE(config, nullptr);
AAFwk::ExtensionConfigItem item;
item.extensionRunningTimeoutTime = 5000;
config->configMap_["ConfiguredType"] = item;
int32_t timeout = config->GetExtensionRunningTimeoutTime("ConfiguredType");
EXPECT_EQ(timeout, 5000);
config->configMap_.erase("ConfiguredType");
TAG_LOGI(AAFwkTag::TEST, "end.");
}
} // namespace AbilityRuntime
} // namespace OHOS
@@ -45,6 +45,8 @@ ohos_unittest("extension_record_manager_test") {
"${ability_runtime_services_path}/abilitymgr/src/extension_record/extension_record.cpp",
"${ability_runtime_services_path}/abilitymgr/src/extension_record/extension_record_factory.cpp",
"${ability_runtime_services_path}/abilitymgr/src/extension_record/extension_record_manager.cpp",
"${ability_runtime_services_path}/abilitymgr/src/extension_running_timeout_monitor.cpp",
"${ability_runtime_services_path}/abilitymgr/src/extension_config.cpp",
"${ability_runtime_services_path}/abilitymgr/src/ui_extension/preload_uiext_state_observer.cpp",
"${ability_runtime_services_path}/abilitymgr/src/ui_extension_record/ui_extension_record.cpp",
"${ability_runtime_services_path}/abilitymgr/src/ui_extension_record/ui_extension_record_factory.cpp",