!19629 merge revert/agent-connection into master

Revert "bugfix: agent connection not reused"

Created-by: yangxuguang-huawei
Commit-by: yangxuguang-huawei
Merged-by: openharmony_ci
Description: **IssueNo**:
https://gitcode.com/openharmony/ability_ability_runtime/issues/15487

**Description**:

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

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

**TDD Result**:
已验证OK

**XTS Result**:
已验证OK

### 是否已执行L0用例
- [x] 已验证
- [ ] 不涉及。如不涉及,请写明理由


See merge request: openharmony/ability_ability_runtime!19629
This commit is contained in:
openharmony_ci
2026-06-18 14:25:20 +08:00
15 changed files with 229 additions and 2130 deletions
@@ -76,13 +76,15 @@ int64_t InsertAgentConnection(sptr<EtsAgentConnection> connection, const AAFwk::
void FindAgentConnection(int64_t connectId, sptr<EtsAgentConnection> &connection);
/**
* Find agent connection by target identity.
* Find agent connection by want and callback.
*
* @param env The ANI environment.
* @param want The want information to match.
* @param agentId The agent ID to match.
* @param callback The callback object to match.
* @param connection Output parameter for the found connection.
*/
void FindAgentConnection(const AAFwk::Want &want, const std::string &agentId, sptr<EtsAgentConnection> &connection);
void FindAgentConnection(ani_env *env, AAFwk::Want &want, ani_object callback,
sptr<EtsAgentConnection> &connection);
}
class EtsAgentConnectorStubImpl;
@@ -162,7 +164,7 @@ public:
*
* @param duplicatedCallback The callback object from ETS.
*/
bool AddDuplicatedPendingCallback(ani_object duplicatedCallback);
void AddDuplicatedPendingCallback(ani_object duplicatedCallback);
/**
* Resolve all duplicated pending callbacks with the proxy.
@@ -37,22 +37,6 @@ constexpr const char *SIGNATURE_AGENT_EXTENSION_CALLBACK =
"application.AgentExtensionConnectCallback.AgentExtensionConnectCallback";
constexpr const char *SIGNATURE_ON_DATA_AND_AUTH = "C{std.core.String}:";
constexpr const char *SIGNATURE_VOID = ":";
bool IsSameAgentConnection(const AAFwk::Want &storedWant, const AAFwk::Want &want, const std::string &agentId)
{
std::string existingId = storedWant.GetStringParam(AGENTID_KEY);
if (existingId.empty() || agentId.empty() || existingId != agentId) {
return false;
}
const auto &storedElement = storedWant.GetElement();
const auto &element = want.GetElement();
if (storedElement.GetBundleName() != element.GetBundleName() ||
storedElement.GetAbilityName() != element.GetAbilityName()) {
return false;
}
return storedElement.GetModuleName().empty() || element.GetModuleName().empty() ||
storedElement.GetModuleName() == element.GetModuleName();
}
} // namespace
namespace AgentConnectionUtils {
@@ -117,13 +101,21 @@ void FindAgentConnection(int64_t connectId, sptr<EtsAgentConnection> &connection
}
}
void FindAgentConnection(const AAFwk::Want &want, const std::string &agentId, sptr<EtsAgentConnection> &connection)
void FindAgentConnection(ani_env *env, AAFwk::Want &want, ani_object callback,
sptr<EtsAgentConnection> &connection)
{
TAG_LOGD(AAFwkTag::SER_ROUTER, "FindAgentConnection by target");
TAG_LOGD(AAFwkTag::SER_ROUTER, "FindAgentConnection by want+callback");
std::lock_guard<std::recursive_mutex> lock(g_agentConnectsLock_);
auto item = std::find_if(g_agentConnects.begin(), g_agentConnects.end(),
[&want, &agentId](const auto &obj) {
return IsSameAgentConnection(obj.first.want, want, agentId);
[&want, env, callback](const auto &obj) {
std::string existingId = obj.first.want.GetStringParam(AGENTID_KEY);
std::string agentId = want.GetStringParam(AGENTID_KEY);
bool wantEquals = obj.first.want.GetElement() == want.GetElement() &&
!existingId.empty() && !agentId.empty() && existingId == agentId;
ani_ref tempCallbackRef = obj.second->GetEtsConnectionObject();
bool callbackObjectEquals =
EtsAgentConnection::IsEtsCallbackObjectEquals(env, tempCallbackRef, callback);
return wantEquals && callbackObjectEquals;
});
if (item == g_agentConnects.end()) {
TAG_LOGD(AAFwkTag::SER_ROUTER, "Connection not found");
@@ -284,14 +276,13 @@ void EtsAgentConnection::ReleaseObjectReference(ani_ref etsObjRef)
TAG_LOGE(AAFwkTag::SER_ROUTER, "etsVm_ or etsObjRef null");
return;
}
bool isAttachThread = false;
ani_env *env = AppExecFwk::AttachAniEnv(etsVm_, isAttachThread);
if (env == nullptr) {
TAG_LOGE(AAFwkTag::SER_ROUTER, "AttachAniEnv failed");
ani_env *env = nullptr;
ani_status status = ANI_ERROR;
if ((status = etsVm_->GetEnv(ANI_VERSION_1, &env)) != ANI_OK || env == nullptr) {
TAG_LOGE(AAFwkTag::SER_ROUTER, "GetEnv failed status: %{public}d", status);
return;
}
ReleaseObjectReference(env, etsObjRef);
AppExecFwk::DetachAniEnv(etsVm_, isAttachThread);
}
void EtsAgentConnection::CallObjectMethod(ani_env *env, const char *methodName, const char *signature, ...)
@@ -380,31 +371,14 @@ void EtsAgentConnection::SetAniAsyncCallback(ani_object asyncCallback)
TAG_LOGD(AAFwkTag::SER_ROUTER, "SetAniAsyncCallback success");
}
bool EtsAgentConnection::AddDuplicatedPendingCallback(ani_object duplicatedCallback)
void EtsAgentConnection::AddDuplicatedPendingCallback(ani_object duplicatedCallback)
{
TAG_LOGD(AAFwkTag::SER_ROUTER, "AddDuplicatedPendingCallback");
if (duplicatedCallback == nullptr) {
TAG_LOGE(AAFwkTag::SER_ROUTER, "duplicatedCallback is null");
return false;
return;
}
if (etsVm_ == nullptr) {
TAG_LOGE(AAFwkTag::SER_ROUTER, "etsVm_ is null");
return false;
}
ani_env *env = nullptr;
ani_status status = ANI_ERROR;
if ((status = etsVm_->GetEnv(ANI_VERSION_1, &env)) != ANI_OK || env == nullptr) {
TAG_LOGE(AAFwkTag::SER_ROUTER, "GetEnv failed status: %{public}d", status);
return false;
}
ani_ref globalRef = nullptr;
if ((status = env->GlobalReference_Create(duplicatedCallback, &globalRef)) != ANI_OK ||
globalRef == nullptr) {
TAG_LOGE(AAFwkTag::SER_ROUTER, "GlobalReference_Create failed status: %{public}d", status);
return false;
}
duplicatedPendingCallbacks_.push_back(globalRef);
return true;
duplicatedPendingCallbacks_.push_back(duplicatedCallback);
}
void EtsAgentConnection::ResolveDuplicatedPendingCallbacks(ani_env *env, ani_object proxyObj)
@@ -57,12 +57,12 @@ class EtsAgentServiceConnection;
std::map<int64_t, sptr<EtsAgentServiceConnection>> g_serviceConnections;
int64_t g_serviceConnectionSerialNumber = 0;
bool CheckConnectAlreadyExist(ani_env *env, const AAFwk::Want &want, const std::string &agentId,
ani_object asyncCallback)
bool CheckConnectAlreadyExist(ani_env *env, AAFwk::Want &want,
ani_object callback, ani_object asyncCallback)
{
TAG_LOGD(AAFwkTag::SER_ROUTER, "CheckConnectAlreadyExist called");
sptr<EtsAgentConnection> connection = nullptr;
AgentConnectionUtils::FindAgentConnection(want, agentId, connection);
AgentConnectionUtils::FindAgentConnection(env, want, callback, connection);
if (connection == nullptr) {
TAG_LOGD(AAFwkTag::SER_ROUTER, "null connection");
return false;
@@ -70,10 +70,7 @@ bool CheckConnectAlreadyExist(ani_env *env, const AAFwk::Want &want, const std::
ani_ref proxy = connection->GetProxyObject();
if (proxy == nullptr) {
TAG_LOGW(AAFwkTag::SER_ROUTER, "null proxy");
if (!connection->AddDuplicatedPendingCallback(asyncCallback)) {
AsyncCallback(env, SIGNATURE_AGENT_ASYNC_CALLBACK_WRAPPER, asyncCallback,
EtsErrorUtil::CreateError(env, AbilityErrorCode::ERROR_CODE_INNER), nullptr);
}
connection->AddDuplicatedPendingCallback(asyncCallback);
} else {
TAG_LOGI(AAFwkTag::SER_ROUTER, "Resolve, got proxy object");
AsyncCallback(env, SIGNATURE_AGENT_ASYNC_CALLBACK_WRAPPER, asyncCallback,
@@ -534,7 +531,7 @@ void EtsAgentManager::ConnectAgentExtensionAbility(ani_env *env, ani_object aniW
want.GetElement().GetBundleName().c_str(), want.GetElement().GetAbilityName().c_str());
// Check for duplicate connection
if (CheckConnectAlreadyExist(env, want, agentId, asyncCallback)) {
if (CheckConnectAlreadyExist(env, want, callbackObj, asyncCallback)) {
TAG_LOGI(AAFwkTag::SER_ROUTER, "Duplicate connection found");
return;
}
@@ -77,13 +77,15 @@ int64_t InsertAgentConnection(sptr<JSAgentConnection> connection, const AAFwk::W
void FindAgentConnection(int64_t connectId, sptr<JSAgentConnection> &connection);
/**
* Find agent connection by target identity.
* Find agent connection by want and callback.
*
* @param env The N-API environment.
* @param want The want information to match.
* @param agentId The agent ID to match.
* @param callback The callback object to match.
* @param connection Output parameter for the found connection.
*/
void FindAgentConnection(const AAFwk::Want &want, const std::string &agentId, sptr<JSAgentConnection> &connection);
void FindAgentConnection(napi_env env, AAFwk::Want &want, napi_value callback,
sptr<JSAgentConnection> &connection);
}
class JsAgentConnectorStubImpl;
@@ -33,22 +33,6 @@ constexpr int32_t ARGC_ONE = 1;
static std::map<ConnectionKey, sptr<JSAgentConnection>, KeyCompare> g_agentConnects;
static std::recursive_mutex g_agentConnectsLock_;
static int64_t g_agentSerialNumber = 0;
bool IsSameAgentConnection(const AAFwk::Want &storedWant, const AAFwk::Want &want, const std::string &agentId)
{
std::string existingId = storedWant.GetStringParam(AGENTID_KEY);
if (existingId.empty() || agentId.empty() || existingId != agentId) {
return false;
}
const auto &storedElement = storedWant.GetElement();
const auto &element = want.GetElement();
if (storedElement.GetBundleName() != element.GetBundleName() ||
storedElement.GetAbilityName() != element.GetAbilityName()) {
return false;
}
return storedElement.GetModuleName().empty() || element.GetModuleName().empty() ||
storedElement.GetModuleName() == element.GetModuleName();
}
} // namespace
namespace AgentConnectionUtils {
@@ -109,13 +93,21 @@ void FindAgentConnection(int64_t connectId, sptr<JSAgentConnection> &connection)
}
}
void FindAgentConnection(const AAFwk::Want &want, const std::string &agentId, sptr<JSAgentConnection> &connection)
void FindAgentConnection(napi_env env, AAFwk::Want &want, napi_value callback,
sptr<JSAgentConnection> &connection)
{
TAG_LOGD(AAFwkTag::SER_ROUTER, "FindAgentConnection by target");
TAG_LOGD(AAFwkTag::SER_ROUTER, "FindAgentConnection by want+callback");
std::lock_guard<std::recursive_mutex> lock(g_agentConnectsLock_);
auto item = std::find_if(g_agentConnects.begin(), g_agentConnects.end(),
[&want, &agentId](const auto &obj) {
return IsSameAgentConnection(obj.first.want, want, agentId);
[&want, env, callback](const auto &obj) {
std::string exstingId = obj.first.want.GetStringParam(AGENTID_KEY);
std::string agentId = want.GetStringParam(AGENTID_KEY);
bool wantEquals = obj.first.want.GetElement() == want.GetElement() &&
!exstingId.empty() && !agentId.empty() && exstingId == agentId;
std::unique_ptr<NativeReference> &tempCallbackPtr = obj.second->GetJsConnectionObject();
bool callbackObjectEquals =
JSAgentConnection::IsJsCallbackObjectEquals(env, tempCallbackPtr, callback);
return wantEquals && callbackObjectEquals;
});
if (item == g_agentConnects.end()) {
TAG_LOGD(AAFwkTag::SER_ROUTER, "Connection not found");
@@ -54,7 +54,6 @@ constexpr int32_t ARG_INDEX_0 = 0;
constexpr int32_t ARG_INDEX_1 = 1;
constexpr int32_t ARG_INDEX_2 = 2;
constexpr int64_t INVALID_CONNECT_ID = -1;
constexpr const char *INTERNAL_ERROR_MSG = "Internal error.";
std::mutex g_serviceConnectionsLock;
class JSAgentServiceConnection;
@@ -233,12 +232,13 @@ void RemoveServiceConnection(int64_t connectionId)
}
// Helper function to check for duplicate connections
bool CheckConnectAlreadyExist(napi_env env, const AAFwk::Want &want, const std::string &agentId, napi_value &result)
bool CheckConnectAlreadyExist(napi_env env, AAFwk::Want &want,
napi_value callback, napi_value &result)
{
TAG_LOGD(AAFwkTag::SER_ROUTER, "CheckConnectAlreadyExist called");
sptr<JSAgentConnection> connection = nullptr;
AgentConnectionUtils::FindAgentConnection(want, agentId, connection);
AgentConnectionUtils::FindAgentConnection(env, want, callback, connection);
if (connection == nullptr) {
TAG_LOGD(AAFwkTag::SER_ROUTER, "No duplicate connection found");
return false;
@@ -251,27 +251,13 @@ bool CheckConnectAlreadyExist(napi_env env, const AAFwk::Want &want, const std::
TAG_LOGD(AAFwkTag::SER_ROUTER, "Proxy not ready, queuing pending task");
std::unique_ptr<NapiAsyncTask> asyncTask =
CreateAsyncTaskWithLastParam(env, nullptr, nullptr, nullptr, &result);
if (asyncTask == nullptr) {
TAG_LOGE(AAFwkTag::SER_ROUTER, "Create async task failed");
ThrowError(env, static_cast<int32_t>(AbilityErrorCode::ERROR_CODE_INNER), INTERNAL_ERROR_MSG);
result = CreateJsUndefined(env);
return true;
}
connection->AddDuplicatedPendingTask(asyncTask);
return true;
}
// Connection exists and proxy is ready, resolve immediately
TAG_LOGD(AAFwkTag::SER_ROUTER, "Resolving with existing proxy");
std::unique_ptr<NapiAsyncTask> asyncTask =
CreateAsyncTaskWithLastParam(env, nullptr, nullptr, nullptr, &result);
if (asyncTask == nullptr) {
TAG_LOGE(AAFwkTag::SER_ROUTER, "Create async task failed");
ThrowError(env, static_cast<int32_t>(AbilityErrorCode::ERROR_CODE_INNER), INTERNAL_ERROR_MSG);
result = CreateJsUndefined(env);
return true;
}
asyncTask->ResolveWithNoError(env, proxy);
result = proxy;
return true;
}
@@ -580,7 +566,7 @@ napi_value JsAgentManager::OnConnectAgentExtensionAbility(napi_env env, size_t a
// 2. Check for duplicate connection
napi_value result = nullptr;
bool duplicated = CheckConnectAlreadyExist(env, want, agentId, result);
bool duplicated = CheckConnectAlreadyExist(env, want, callbackObject, result);
if (duplicated) {
TAG_LOGI(AAFwkTag::SER_ROUTER, "Duplicated connection found");
return result;
@@ -669,11 +655,6 @@ napi_value JsAgentManager::ScheduleAgentConnection(napi_env env, const AAFwk::Wa
napi_value result = nullptr;
std::unique_ptr<NapiAsyncTask> asyncTask =
CreateAsyncTaskWithLastParam(env, nullptr, nullptr, nullptr, &result);
if (asyncTask == nullptr) {
TAG_LOGE(AAFwkTag::SER_ROUTER, "Create async task failed");
ThrowError(env, static_cast<int32_t>(AbilityErrorCode::ERROR_CODE_INNER), INTERNAL_ERROR_MSG);
return CreateJsUndefined(env);
}
std::shared_ptr<NapiAsyncTask> asyncTaskShared = std::move(asyncTask);
connection->SetNapiAsyncTask(asyncTaskShared);
@@ -17,8 +17,7 @@
#define OHOS_AGENT_RUNTIME_AGENT_CONNECTION_MANAGER_H
#include <chrono>
#include <functional>
#include <list>
#include <map>
#include <mutex>
#include <vector>
@@ -40,7 +39,7 @@ class AgentConnection;
* @struct AgentConnectionInfo
* @brief Stores information about an agent connection.
*
* The connection key consists of: agentId + connectReceiver
* The connection key consists of: agentId + agentExtProxy + connectReceiver
*/
struct AgentConnectionInfo {
// Target agent identifier (from Want parameter AGENTID_KEY)
@@ -50,8 +49,8 @@ struct AgentConnectionInfo {
AAFwk::Operation connectReceiver;
// The connection object
sptr<AgentConnection> agentConnection;
// Agent extension host proxy from Want parameter AGENTEXTENSIONHOSTPROXY_KEY.
// It is per API call and is not part of the reusable connection identity.
// Agent extension proxy for bidirectional communication
// (from Want parameter AGENTEXTENSIONHOSTPROXY_KEY)
void *agentExtProxy = nullptr;
// Connection timestamp for timeout detection
int64_t connectingTime = 0;
@@ -84,6 +83,47 @@ struct AgentConnectionInfo {
auto now = std::chrono::steady_clock::now().time_since_epoch();
connectingTime = std::chrono::duration_cast<std::chrono::milliseconds>(now).count();
}
/**
* @brief Comparison operator for map key ordering.
*
* Compares by: agentId -> agentExtProxy -> connectingTime -> connectReceiver
* (bundleName -> moduleName -> abilityName)
*
* @param that The other AgentConnectionInfo to compare with.
* @return Returns true if this is less than that.
*/
inline bool operator<(const AgentConnectionInfo &that) const
{
if (agentId < that.agentId) {
return true;
}
if (agentExtProxy < that.agentExtProxy) {
return true;
}
if (connectingTime < that.connectingTime) {
return true;
}
if (connectReceiver.GetBundleName() < that.connectReceiver.GetBundleName()) {
return true;
}
if (connectReceiver.GetBundleName() == that.connectReceiver.GetBundleName() &&
connectReceiver.GetModuleName() < that.connectReceiver.GetModuleName()) {
return true;
}
if (connectReceiver.GetBundleName() == that.connectReceiver.GetBundleName() &&
connectReceiver.GetModuleName() == that.connectReceiver.GetModuleName() &&
connectReceiver.GetAbilityName() < that.connectReceiver.GetAbilityName()) {
return true;
}
if (connectReceiver.GetBundleName() == that.connectReceiver.GetBundleName() &&
connectReceiver.GetModuleName() == that.connectReceiver.GetModuleName() &&
connectReceiver.GetAbilityName() == that.connectReceiver.GetAbilityName() &&
!(connectReceiver == that.connectReceiver)) {
return true;
}
return false;
}
};
/**
@@ -130,7 +170,7 @@ private:
* @brief Manages connections to AgentExtensionAbility instances.
*
* This class is similar to ConnectionManager but specifically for agent extensions.
* Connections are identified by: agentId + connectReceiver.
* Connections are identified by: agentId + agentExtProxy + connectReceiver.
*/
class AgentConnectionManager {
public:
@@ -215,19 +255,16 @@ private:
*/
void *GetAgentExtProxyPtr(const AAFwk::Want &want);
/**
* @brief Match connection by agentId + connectReceiver.
* @brief Match connection by agentId + agentExtProxy + connectReceiver.
*
* @param agentId The agent ID to match.
* @param connectReceiver The connect receiver (Want) to match.
* @param connection The existing connection entry.
* @return Returns true if matches, false otherwise.
*/
using AgentConnectionCallbacks = std::vector<sptr<AbilityRuntime::AbilityConnectCallback>>;
using AgentConnectionRecord = std::pair<AgentConnectionInfo, AgentConnectionCallbacks>;
using AgentConnectionRecords = std::list<AgentConnectionRecord>;
bool MatchConnection(const std::string &agentId, const AAFwk::Want &connectReceiver,
const AgentConnectionRecord &connection);
const std::map<AgentConnectionInfo,
std::vector<sptr<AbilityRuntime::AbilityConnectCallback>>>::value_type &connection);
/**
* @brief Create a new connection to the agent extension.
*
@@ -256,7 +293,7 @@ private:
private:
std::mutex connectionsLock_;
AgentConnectionRecords agentConnections_;
std::map<AgentConnectionInfo, std::vector<sptr<AbilityRuntime::AbilityConnectCallback>>> agentConnections_;
};
} // namespace AgentRuntime
} // namespace OHOS
@@ -242,23 +242,22 @@ void *AgentConnectionManager::GetAgentExtProxyPtr(const AAFwk::Want &want)
}
bool AgentConnectionManager::MatchConnection(const std::string &agentId, const AAFwk::Want &connectReceiver,
const AgentConnectionRecord &connection)
const std::map<AgentConnectionInfo, std::vector<sptr<AbilityConnectCallback>>>::value_type &connection)
{
// 1. Match by agentId
if (agentId != connection.first.agentId) {
return false;
}
// 2. Match by connectReceiver (bundleName, moduleName, abilityName).
// AGENTEXTENSIONHOSTPROXY_KEY is per connect call, so it must not split reusable connections.
const AAFwk::Operation &storedReceiver = connection.first.connectReceiver;
const AppExecFwk::ElementName &element = connectReceiver.GetElement();
if (element.GetBundleName() != storedReceiver.GetBundleName() ||
element.GetAbilityName() != storedReceiver.GetAbilityName()) {
// 2. Match by agentExtProxy
void *agentExtProxy = GetAgentExtProxyPtr(connectReceiver);
if (agentExtProxy != connection.first.agentExtProxy) {
return false;
}
const std::string &moduleName = element.GetModuleName();
const std::string &storedModuleName = storedReceiver.GetModuleName();
return moduleName.empty() || storedModuleName.empty() || moduleName == storedModuleName;
// 3. Match by connectReceiver (bundleName, moduleName, abilityName)
const AAFwk::Operation &storedReceiver = connection.first.connectReceiver;
return connectReceiver.GetElement().GetBundleName() == storedReceiver.GetBundleName() &&
connectReceiver.GetElement().GetModuleName() == storedReceiver.GetModuleName() &&
connectReceiver.GetElement().GetAbilityName() == storedReceiver.GetAbilityName();
}
ErrCode AgentConnectionManager::CreateConnection(const AAFwk::Want &want,
@@ -282,19 +281,14 @@ ErrCode AgentConnectionManager::CreateConnection(const AAFwk::Want &want,
{
std::lock_guard<std::mutex> lock(connectionsLock_);
agentConnections_.emplace_back(connectionInfo, AgentConnectionCallbacks { connectCallback });
agentConnections_[connectionInfo] = { connectCallback };
}
ErrCode ret = AgentManagerClient::GetInstance().ConnectAgentExtensionAbility(want, agentConnection);
if (ret != ERR_OK) {
TAG_LOGE(AAFwkTag::SER_ROUTER, "error:%{public}d", ret);
std::lock_guard<std::mutex> lock(connectionsLock_);
for (auto iter = agentConnections_.begin(); iter != agentConnections_.end(); ++iter) {
if (iter->first.agentConnection == agentConnection) {
agentConnections_.erase(iter);
break;
}
}
agentConnections_.erase(connectionInfo);
}
return ret;
}
@@ -16,7 +16,6 @@
#ifndef OHOS_AGENT_RUNTIME_FRAMEWORK_AGENT_MANAGER_SERVICE_H
#define OHOS_AGENT_RUNTIME_FRAMEWORK_AGENT_MANAGER_SERVICE_H
#include <list>
#include <memory>
#include <map>
#include <mutex>
@@ -32,7 +31,6 @@
#include "task_handler_wrap.h"
#include "agent_host_connection.h"
#include "agent_host_connection_types.h"
#include "element_name.h"
namespace OHOS {
namespace AgentRuntime {
@@ -85,41 +83,6 @@ public:
private:
friend class AgentServiceConnection;
friend class AgentHostConnection;
/**
* @struct StandardAgentKey
* @brief Identity key for deduplicating non-low-code agent connections: callerUid + agentId + target.
*/
struct StandardAgentKey {
int32_t callerUid = 0;
std::string agentId;
std::string bundleName;
std::string moduleName;
std::string abilityName;
};
enum class StandardAgentState : int32_t {
CONNECTING,
CONNECTED,
};
/**
* @struct StandardAgentSession
* @brief Collapsed session for standard agent connections sharing the same (callerUid, agentId, target).
*/
struct StandardAgentSession {
StandardAgentKey key;
StandardAgentState state = StandardAgentState::CONNECTING;
sptr<AAFwk::IAbilityConnection> serviceConnection = nullptr;
std::vector<sptr<AAFwk::IAbilityConnection>> pendingCallbacks;
std::vector<sptr<AAFwk::IAbilityConnection>> connectedCallbacks;
std::vector<sptr<IRemoteObject>> callerRemotes;
AppExecFwk::ElementName cachedElement;
sptr<IRemoteObject> cachedRemoteObject = nullptr;
int32_t cachedResultCode = ERR_OK;
bool isDisconnecting = false;
};
/**
* @struct TrackedConnectionRecord
* @brief Stores one caller-visible connection together with the service-side wrapper used to talk to AMS.
@@ -133,21 +96,6 @@ private:
bool isLowCode = false;
bool countTowardsCallerLimit = true;
bool isDisconnecting = false;
StandardAgentKey standardKey;
};
using TrackedConnectionIter = std::map<sptr<IRemoteObject>, TrackedConnectionRecord>::iterator;
using StandardSessionIter = std::list<std::shared_ptr<StandardAgentSession>>::iterator;
struct AgentDisconnectPlan {
sptr<AAFwk::IAbilityConnection> serviceConnection = nullptr;
sptr<AgentHostConnection> hostConnection = nullptr;
sptr<IRemoteObject> callerRemote = nullptr;
AgentHostKey hostKey;
bool hasHostKey = false;
bool isStandard = false;
int32_t standardCallerUid = 0;
StandardAgentKey standardKey;
};
AgentManagerService();
@@ -195,13 +143,8 @@ private:
/**
* @brief Finds the tracked record that matches the caller callback object or the fallback caller uid path.
*/
TrackedConnectionIter FindTrackedConnectionLocked(const sptr<AAFwk::IAbilityConnection> &connection,
int32_t callerUid);
int32_t PrepareAgentDisconnectLocked(const sptr<AAFwk::IAbilityConnection> &connection,
AgentDisconnectPlan &plan);
int32_t PrepareLowCodeDisconnectLocked(TrackedConnectionIter it, AgentDisconnectPlan &plan);
int32_t PrepareStandardDisconnectLocked(TrackedConnectionIter it, AgentDisconnectPlan &plan);
void RollbackAgentDisconnectLocked(const AgentDisconnectPlan &plan);
std::map<sptr<IRemoteObject>, TrackedConnectionRecord>::iterator FindTrackedConnectionLocked(
const sptr<AAFwk::IAbilityConnection> &connection, int32_t callerUid);
void ReleaseCallerConnectionCountByUidLocked(int32_t callerUid);
/**
* @brief Forwards connect/disconnect completion from the service-side wrapper back to the original caller.
@@ -214,12 +157,6 @@ private:
const sptr<IRemoteObject> &callerRemote);
void HandleCallerConnectionDied(const wptr<IRemoteObject> &remote);
void HandleCallerConnectionDied(const sptr<IRemoteObject> &remote);
void CleanupDeadStandardConnectionLocked(TrackedConnectionIter it, const sptr<IRemoteObject> &remote,
sptr<AAFwk::IAbilityConnection> &serviceConnection);
void CleanupDeadLowCodeConnectionLocked(TrackedConnectionIter it, const sptr<IRemoteObject> &remote,
sptr<AgentHostConnection> &hostConnection);
void DisconnectDeadServiceConnection(const sptr<AAFwk::IAbilityConnection> &serviceConnection);
void DisconnectDeadHostConnection(const sptr<AgentHostConnection> &hostConnection);
/**
* @brief Connect path for LOW_CODE agents, which reuse one shared host session per target host.
*/
@@ -249,38 +186,6 @@ private:
* @brief Extracts the host-session identity from the target element and caller user context.
*/
AgentHostKey BuildAgentHostKey(const AAFwk::Want &want, int32_t callingUid) const;
/**
* @brief Builds the identity key for standard agent session deduplication.
*/
StandardAgentKey BuildStandardAgentKey(int32_t callerUid, const std::string &agentId,
const AAFwk::Want &want) const;
bool IsStandardAgentKeyEqual(const StandardAgentKey &left, const StandardAgentKey &right) const;
bool IsStandardAgentKeyMatched(const StandardAgentKey &storedKey, const StandardAgentKey &incomingKey) const;
/**
* @brief Finds an existing standard session matching the given key.
*/
std::shared_ptr<StandardAgentSession> FindStandardSessionLocked(const StandardAgentKey &key) const;
StandardSessionIter FindStandardSessionByServiceRemoteLocked(const sptr<IRemoteObject> &serviceRemote);
/**
* @brief Registers a caller callback against an existing standard session without consuming another quota slot.
*/
int32_t RegisterStandardSessionCallerLocked(const std::shared_ptr<StandardAgentSession> &session,
const sptr<AAFwk::IAbilityConnection> &connection, bool countTowardsCallerLimit);
/**
* @brief Creates a new collapsed standard session, registers one quota, and issues the real AMS connect.
*/
int32_t CreateStandardAgentSession(const AAFwk::Want &connectWant, const std::string &agentId,
const StandardAgentKey &key, const sptr<AAFwk::IAbilityConnection> &connection);
/**
* @brief Handles AMS connect completion for a standard collapsed session.
*/
void HandleStandardAgentConnectDone(const sptr<IRemoteObject> &serviceRemote,
const AppExecFwk::ElementName &element, const sptr<IRemoteObject> &remoteObject, int32_t resultCode);
/**
* @brief Handles AMS disconnect completion for a standard collapsed session.
*/
void HandleStandardAgentDisconnectDone(const sptr<IRemoteObject> &serviceRemote,
const AppExecFwk::ElementName &element, int32_t resultCode);
/**
* @brief Marks the target low-code agent as invoked so future callers can detect duplicate activation.
*/
@@ -318,7 +223,6 @@ private:
std::mutex agentHostMutex_;
std::map<AgentHostKey, std::shared_ptr<AgentHostSession>> agentHostSessions_;
std::map<AgentOwnerKey, std::shared_ptr<AgentHostSession>> agentOwners_;
std::list<std::shared_ptr<StandardAgentSession>> standardSessions_;
};
} // namespace AgentRuntime
} // namespace OHOS
@@ -25,14 +25,12 @@ public:
explicit AgentServiceConnection(const sptr<AAFwk::IAbilityConnection> &connection);
~AgentServiceConnection() override = default;
void SetStandardSessionMode();
void OnAbilityConnectDone(
const AppExecFwk::ElementName &element, const sptr<IRemoteObject> &remoteObject, int resultCode) override;
void OnAbilityDisconnectDone(const AppExecFwk::ElementName &element, int resultCode) override;
private:
sptr<AAFwk::IAbilityConnection> callerConnection_;
bool isStandardSession_ = false;
};
} // namespace AgentRuntime
} // namespace OHOS
@@ -81,19 +81,6 @@ bool IsMatchedAgentCardTarget(const AAFwk::Want &want, const AgentCard &card)
return element.GetModuleName().empty() || card.appInfo->moduleName.empty() ||
element.GetModuleName() == card.appInfo->moduleName;
}
void NormalizeConnectAgentTarget(AAFwk::Want &want, const AgentCard &card)
{
if (card.appInfo == nullptr || card.appInfo->moduleName.empty()) {
return;
}
const auto &element = want.GetElement();
if (!element.GetModuleName().empty()) {
return;
}
want.SetElementName(element.GetBundleName(), element.GetAbilityName());
want.SetModuleName(card.appInfo->moduleName);
}
}
sptr<AgentManagerService> AgentManagerService::GetInstance()
@@ -139,7 +126,6 @@ void AgentManagerService::OnStop() noexcept
std::lock_guard<std::mutex> lock(connectionLock_);
trackedConnections_.clear();
callerConnectionCounts_.clear();
standardSessions_.clear();
}
std::lock_guard<std::mutex> hostLock(agentHostMutex_);
agentHostSessions_.clear();
@@ -359,40 +345,23 @@ int32_t AgentManagerService::ConnectAgentExtensionAbility(const AAFwk::Want &wan
TAG_LOGI(AAFwkTag::SER_ROUTER, "connecting %{public}s-%{public}s",
connectWant.GetBundle().c_str(), agentId.c_str());
// Step 5: deduplicate standard agent connections at the service side.
StandardAgentKey key = BuildStandardAgentKey(callerUid, agentId, connectWant);
AppExecFwk::ElementName cachedElement;
sptr<IRemoteObject> cachedRemoteObject = nullptr;
int32_t cachedResultCode = ERR_OK;
bool notifyCached = false;
{
std::lock_guard<std::mutex> lock(connectionLock_);
auto existing = FindStandardSessionLocked(key);
if (existing != nullptr) {
ret = RegisterStandardSessionCallerLocked(existing, connection, false);
if (ret != ERR_OK) {
return ret;
}
if (existing->state == StandardAgentState::CONNECTING) {
TAG_LOGI(AAFwkTag::SER_ROUTER, "standard session connecting, append caller");
return ERR_OK;
}
if (existing->state == StandardAgentState::CONNECTED) {
TAG_LOGI(AAFwkTag::SER_ROUTER, "standard session connected, return cached proxy");
cachedElement = existing->cachedElement;
cachedRemoteObject = existing->cachedRemoteObject;
cachedResultCode = existing->cachedResultCode;
notifyCached = true;
}
}
}
if (notifyCached) {
connection->OnAbilityConnectDone(cachedElement, cachedRemoteObject, cachedResultCode);
return ERR_OK;
// Step 5: create the tracked wrapper connection before talking to AMS.
sptr<AAFwk::IAbilityConnection> serviceConnection;
ret = RegisterTrackedConnectionAndGetServiceConnection(connection, callerUid, true, serviceConnection);
if (ret != ERR_OK) {
return ret;
}
// Step 6: no existing session — create one and issue the real AMS connect.
return CreateStandardAgentSession(connectWant, agentId, key, connection);
// Step 6: issue the actual extension connect request through AMS.
ret = AAFwk::AbilityManagerClient::GetInstance()->ConnectAbilityWithExtensionType(
connectWant, serviceConnection, nullptr, AAFwk::DEFAULT_INVAL_VALUE, AppExecFwk::ExtensionAbilityType::AGENT);
if (ret != ERR_OK) {
TAG_LOGE(AAFwkTag::SER_ROUTER, "ConnectAbilityWithExtensionType failed: %{public}d", ret);
ReleaseTrackedConnection(connection);
return ret;
}
return ERR_OK;
}
int32_t AgentManagerService::ConnectServiceExtensionAbility(const sptr<IRemoteObject> &callerToken,
@@ -488,7 +457,6 @@ int32_t AgentManagerService::ResolveConnectAgentTarget(const AAFwk::Want &want,
TAG_LOGE(AAFwkTag::SER_ROUTER, "low-code target mismatch");
return AAFwk::INVALID_PARAMETERS_ERR;
}
NormalizeConnectAgentTarget(connectWant, card);
// Capture the caller UID once the target metadata is known-good.
callingUid = IPCSkeleton::GetCallingUid();
@@ -610,27 +578,73 @@ int32_t AgentManagerService::DisconnectAgentExtensionAbility(const sptr<AAFwk::I
return AAFwk::INVALID_PARAMETERS_ERR;
}
AgentDisconnectPlan plan;
sptr<AAFwk::IAbilityConnection> serviceConnection = nullptr;
sptr<AgentHostConnection> hostConnection = nullptr;
sptr<IRemoteObject> callerRemote = nullptr;
AgentHostKey hostKey;
bool hasHostKey = false;
{
std::scoped_lock lock(connectionLock_, agentHostMutex_);
auto ret = PrepareAgentDisconnectLocked(connection, plan);
if (ret != ERR_OK) {
return ret;
auto it = FindTrackedConnectionLocked(connection, IPCSkeleton::GetCallingUid());
if (it == trackedConnections_.end()) {
TAG_LOGE(AAFwkTag::SER_ROUTER, "Connection not tracked");
return ERR_INVALID_VALUE;
}
callerRemote = it->first;
if (it->second.isDisconnecting) {
TAG_LOGI(AAFwkTag::SER_ROUTER, "Connection is already disconnecting");
return ERR_OK;
}
if (it->second.isLowCode) {
auto sessionIter = agentHostSessions_.find(it->second.hostKey);
if (sessionIter == agentHostSessions_.end() || sessionIter->second == nullptr) {
TAG_LOGE(AAFwkTag::SER_ROUTER, "Low-code host session missing");
return ERR_INVALID_VALUE;
}
auto session = sessionIter->second;
if (session->isDisconnecting) {
return ERR_OK;
}
session->isDisconnecting = true;
it->second.isDisconnecting = true;
hostKey = it->second.hostKey;
hasHostKey = true;
if (!ReleaseCallerConnectionCountLocked(callerRemote)) {
session->isDisconnecting = false;
it->second.isDisconnecting = false;
TAG_LOGE(AAFwkTag::SER_ROUTER, "Release caller connection count failed");
return ERR_INVALID_VALUE;
}
hostConnection = session->hostConnection;
} else {
it->second.isDisconnecting = true;
if (!ReleaseCallerConnectionCountLocked(callerRemote)) {
TAG_LOGE(AAFwkTag::SER_ROUTER, "Release caller connection count failed");
return ERR_INVALID_VALUE;
}
serviceConnection = it->second.serviceConnection;
}
}
if (plan.serviceConnection == nullptr && plan.hostConnection == nullptr) {
return ERR_OK;
}
sptr<AAFwk::IAbilityConnection> disconnectConnection = plan.serviceConnection;
if (plan.hostConnection != nullptr) {
disconnectConnection = plan.hostConnection;
sptr<AAFwk::IAbilityConnection> disconnectConnection = serviceConnection;
if (hostConnection != nullptr) {
disconnectConnection = hostConnection;
}
auto ret = IN_PROCESS_CALL(AAFwk::AbilityManagerClient::GetInstance()->DisconnectAbility(disconnectConnection));
if (ret != ERR_OK) {
TAG_LOGE(AAFwkTag::SER_ROUTER, "DisconnectAbility failed: %{public}d", ret);
std::scoped_lock lock(connectionLock_, agentHostMutex_);
RollbackAgentDisconnectLocked(plan);
auto it = trackedConnections_.find(callerRemote);
if (hasHostKey) {
auto sessionIter = agentHostSessions_.find(hostKey);
if (sessionIter != agentHostSessions_.end() && sessionIter->second != nullptr) {
sessionIter->second->isDisconnecting = false;
}
}
if (it != trackedConnections_.end() && it->second.isDisconnecting) {
it->second.isDisconnecting = false;
callerConnectionCounts_[it->second.callerUid]++;
}
return ret;
}
@@ -664,7 +678,8 @@ sptr<IRemoteObject> AgentManagerService::GetConnectionIdentityRemote(
return connection->AsObject();
}
AgentManagerService::TrackedConnectionIter AgentManagerService::FindTrackedConnectionLocked(
std::map<sptr<IRemoteObject>, AgentManagerService::TrackedConnectionRecord>::iterator
AgentManagerService::FindTrackedConnectionLocked(
const sptr<AAFwk::IAbilityConnection> &connection, int32_t callerUid)
{
auto end = trackedConnections_.end();
@@ -705,115 +720,6 @@ AgentManagerService::TrackedConnectionIter AgentManagerService::FindTrackedConne
return matched;
}
int32_t AgentManagerService::PrepareAgentDisconnectLocked(const sptr<AAFwk::IAbilityConnection> &connection,
AgentDisconnectPlan &plan)
{
auto it = FindTrackedConnectionLocked(connection, IPCSkeleton::GetCallingUid());
if (it == trackedConnections_.end()) {
TAG_LOGE(AAFwkTag::SER_ROUTER, "Connection not tracked");
return ERR_INVALID_VALUE;
}
plan.callerRemote = it->first;
if (it->second.isDisconnecting) {
TAG_LOGI(AAFwkTag::SER_ROUTER, "Connection is already disconnecting");
return ERR_OK;
}
if (it->second.isLowCode) {
return PrepareLowCodeDisconnectLocked(it, plan);
}
return PrepareStandardDisconnectLocked(it, plan);
}
int32_t AgentManagerService::PrepareLowCodeDisconnectLocked(TrackedConnectionIter it, AgentDisconnectPlan &plan)
{
auto sessionIter = agentHostSessions_.find(it->second.hostKey);
if (sessionIter == agentHostSessions_.end() || sessionIter->second == nullptr) {
TAG_LOGE(AAFwkTag::SER_ROUTER, "Low-code host session missing");
return ERR_INVALID_VALUE;
}
auto session = sessionIter->second;
if (session->isDisconnecting) {
return ERR_OK;
}
session->isDisconnecting = true;
it->second.isDisconnecting = true;
plan.hostKey = it->second.hostKey;
plan.hasHostKey = true;
if (!ReleaseCallerConnectionCountLocked(plan.callerRemote)) {
session->isDisconnecting = false;
it->second.isDisconnecting = false;
TAG_LOGE(AAFwkTag::SER_ROUTER, "Release caller connection count failed");
return ERR_INVALID_VALUE;
}
plan.hostConnection = session->hostConnection;
return ERR_OK;
}
int32_t AgentManagerService::PrepareStandardDisconnectLocked(TrackedConnectionIter it, AgentDisconnectPlan &plan)
{
const auto &stdKey = it->second.standardKey;
if (stdKey.callerUid != 0 && !stdKey.agentId.empty()) {
auto sessionIt = std::find_if(standardSessions_.begin(), standardSessions_.end(),
[this, &stdKey](const auto &item) {
return item != nullptr && IsStandardAgentKeyEqual(item->key, stdKey);
});
if (sessionIt != standardSessions_.end() && *sessionIt != nullptr) {
auto &session = *sessionIt;
if (session->isDisconnecting) {
TAG_LOGI(AAFwkTag::SER_ROUTER, "standard session already disconnecting");
return ERR_OK;
}
session->isDisconnecting = true;
it->second.isDisconnecting = true;
plan.serviceConnection = session->serviceConnection;
plan.isStandard = true;
plan.standardCallerUid = session->key.callerUid;
plan.standardKey = session->key;
ReleaseCallerConnectionCountByUidLocked(plan.standardCallerUid);
return ERR_OK;
}
}
it->second.isDisconnecting = true;
if (!ReleaseCallerConnectionCountLocked(plan.callerRemote)) {
TAG_LOGE(AAFwkTag::SER_ROUTER, "Release caller connection count failed");
return ERR_INVALID_VALUE;
}
plan.serviceConnection = it->second.serviceConnection;
return ERR_OK;
}
void AgentManagerService::RollbackAgentDisconnectLocked(const AgentDisconnectPlan &plan)
{
auto it = trackedConnections_.find(plan.callerRemote);
if (plan.hasHostKey) {
auto sessionIter = agentHostSessions_.find(plan.hostKey);
if (sessionIter != agentHostSessions_.end() && sessionIter->second != nullptr) {
sessionIter->second->isDisconnecting = false;
}
}
if (plan.isStandard) {
auto sessionIt = std::find_if(standardSessions_.begin(), standardSessions_.end(),
[this, &plan](const auto &item) {
return item != nullptr && IsStandardAgentKeyEqual(item->key, plan.standardKey);
});
if (sessionIt != standardSessions_.end() && *sessionIt != nullptr) {
(*sessionIt)->isDisconnecting = false;
}
if (plan.standardCallerUid != 0) {
callerConnectionCounts_[plan.standardCallerUid]++;
}
}
if (it != trackedConnections_.end() && it->second.isDisconnecting) {
it->second.isDisconnecting = false;
if (!plan.isStandard) {
callerConnectionCounts_[it->second.callerUid]++;
}
}
}
int32_t AgentManagerService::DisconnectServiceExtensionAbility(const sptr<IRemoteObject> &callerToken,
const sptr<AAFwk::IAbilityConnection> &connection)
{
@@ -1067,66 +973,53 @@ void AgentManagerService::TransferLowCodeCallerLimitLocked(const std::shared_ptr
}
}
void AgentManagerService::CleanupDeadStandardConnectionLocked(TrackedConnectionIter it,
const sptr<IRemoteObject> &remote, sptr<AAFwk::IAbilityConnection> &serviceConnection)
void AgentManagerService::HandleCallerConnectionDied(const sptr<IRemoteObject> &remote)
{
serviceConnection = it->second.serviceConnection;
auto &stdKey = it->second.standardKey;
if (stdKey.callerUid != 0 && !stdKey.agentId.empty()) {
auto sessionIt = std::find_if(standardSessions_.begin(), standardSessions_.end(),
[this, &stdKey](const auto &item) {
return item != nullptr && IsStandardAgentKeyEqual(item->key, stdKey);
});
if (sessionIt != standardSessions_.end() && *sessionIt != nullptr) {
serviceConnection = (*sessionIt)->serviceConnection;
for (const auto &callerRemote : (*sessionIt)->callerRemotes) {
if (callerRemote != remote) {
ReleaseTrackedConnectionByRemoteLocked(callerRemote);
sptr<AAFwk::IAbilityConnection> serviceConnection = nullptr;
sptr<AgentHostConnection> hostConnection = nullptr;
{
std::scoped_lock lock(connectionLock_, agentHostMutex_);
if (remote == nullptr) {
return;
}
auto it = trackedConnections_.find(remote);
if (it == trackedConnections_.end()) {
return;
}
if (!it->second.isLowCode) {
serviceConnection = it->second.serviceConnection;
ReleaseTrackedConnectionByRemoteLocked(remote);
} else {
auto sessionIter = agentHostSessions_.find(it->second.hostKey);
if (sessionIter != agentHostSessions_.end() && sessionIter->second != nullptr) {
auto session = sessionIter->second;
session->callerConnections.erase(remote);
for (auto agentIter = session->agents.begin(); agentIter != session->agents.end();) {
if (agentIter->second.callerRemote == remote) {
agentOwners_.erase(AgentOwnerKey { session->hostUid, agentIter->first });
agentIter = session->agents.erase(agentIter);
continue;
}
++agentIter;
}
if (!session->isDisconnecting && session->agents.empty()) {
session->isDisconnecting = true;
hostConnection = session->hostConnection;
} else {
TransferLowCodeCallerLimitLocked(session, remote);
}
}
standardSessions_.erase(sessionIt);
ReleaseTrackedConnectionByRemoteLocked(remote);
}
}
ReleaseTrackedConnectionByRemoteLocked(remote);
}
void AgentManagerService::CleanupDeadLowCodeConnectionLocked(TrackedConnectionIter it,
const sptr<IRemoteObject> &remote, sptr<AgentHostConnection> &hostConnection)
{
auto sessionIter = agentHostSessions_.find(it->second.hostKey);
if (sessionIter != agentHostSessions_.end() && sessionIter->second != nullptr) {
auto session = sessionIter->second;
session->callerConnections.erase(remote);
for (auto agentIter = session->agents.begin(); agentIter != session->agents.end();) {
if (agentIter->second.callerRemote == remote) {
agentOwners_.erase(AgentOwnerKey { session->hostUid, agentIter->first });
agentIter = session->agents.erase(agentIter);
continue;
}
++agentIter;
}
if (!session->isDisconnecting && session->agents.empty()) {
session->isDisconnecting = true;
hostConnection = session->hostConnection;
} else {
TransferLowCodeCallerLimitLocked(session, remote);
}
}
ReleaseTrackedConnectionByRemoteLocked(remote);
}
void AgentManagerService::DisconnectDeadServiceConnection(const sptr<AAFwk::IAbilityConnection> &serviceConnection)
{
if (serviceConnection != nullptr) {
auto ret = IN_PROCESS_CALL(AAFwk::AbilityManagerClient::GetInstance()->DisconnectAbility(serviceConnection));
if (ret != ERR_OK) {
TAG_LOGW(AAFwkTag::SER_ROUTER, "DisconnectAbility after caller death failed: %{public}d", ret);
}
}
}
void AgentManagerService::DisconnectDeadHostConnection(const sptr<AgentHostConnection> &hostConnection)
{
if (hostConnection != nullptr) {
auto ret = IN_PROCESS_CALL(AAFwk::AbilityManagerClient::GetInstance()->DisconnectAbility(hostConnection));
if (ret != ERR_OK) {
@@ -1143,30 +1036,6 @@ void AgentManagerService::DisconnectDeadHostConnection(const sptr<AgentHostConne
}
}
void AgentManagerService::HandleCallerConnectionDied(const sptr<IRemoteObject> &remote)
{
sptr<AAFwk::IAbilityConnection> serviceConnection = nullptr;
sptr<AgentHostConnection> hostConnection = nullptr;
{
std::scoped_lock lock(connectionLock_, agentHostMutex_);
if (remote == nullptr) {
return;
}
auto it = trackedConnections_.find(remote);
if (it == trackedConnections_.end()) {
return;
}
if (it->second.isLowCode) {
CleanupDeadLowCodeConnectionLocked(it, remote, hostConnection);
} else {
CleanupDeadStandardConnectionLocked(it, remote, serviceConnection);
}
}
DisconnectDeadServiceConnection(serviceConnection);
DisconnectDeadHostConnection(hostConnection);
}
void AgentManagerService::HandleCallerConnectionDied(const wptr<IRemoteObject> &remote)
{
auto remoteObject = remote.promote();
@@ -1519,207 +1388,5 @@ void AgentManagerService::ClearAgentHostSessionLocked(const AgentHostKey &key)
}
agentHostSessions_.erase(sessionIter);
}
AgentManagerService::StandardAgentKey AgentManagerService::BuildStandardAgentKey(
int32_t callerUid, const std::string &agentId, const AAFwk::Want &want) const
{
StandardAgentKey key;
key.callerUid = callerUid;
key.agentId = agentId;
const auto &element = want.GetElement();
key.bundleName = element.GetBundleName();
key.moduleName = element.GetModuleName();
key.abilityName = element.GetAbilityName();
return key;
}
bool AgentManagerService::IsStandardAgentKeyEqual(const StandardAgentKey &left, const StandardAgentKey &right) const
{
return left.callerUid == right.callerUid && left.agentId == right.agentId &&
left.bundleName == right.bundleName && left.moduleName == right.moduleName &&
left.abilityName == right.abilityName;
}
bool AgentManagerService::IsStandardAgentKeyMatched(
const StandardAgentKey &storedKey, const StandardAgentKey &incomingKey) const
{
return storedKey.callerUid == incomingKey.callerUid && storedKey.agentId == incomingKey.agentId &&
storedKey.bundleName == incomingKey.bundleName && storedKey.abilityName == incomingKey.abilityName &&
(storedKey.moduleName.empty() || incomingKey.moduleName.empty() ||
storedKey.moduleName == incomingKey.moduleName);
}
std::shared_ptr<AgentManagerService::StandardAgentSession> AgentManagerService::FindStandardSessionLocked(
const StandardAgentKey &key) const
{
for (const auto &entry : standardSessions_) {
if (entry != nullptr && IsStandardAgentKeyMatched(entry->key, key)) {
return entry;
}
}
return nullptr;
}
AgentManagerService::StandardSessionIter AgentManagerService::FindStandardSessionByServiceRemoteLocked(
const sptr<IRemoteObject> &serviceRemote)
{
return std::find_if(standardSessions_.begin(), standardSessions_.end(),
[&serviceRemote](const auto &item) {
return item != nullptr && item->serviceConnection != nullptr &&
item->serviceConnection->AsObject() == serviceRemote;
});
}
int32_t AgentManagerService::RegisterStandardSessionCallerLocked(
const std::shared_ptr<StandardAgentSession> &session,
const sptr<AAFwk::IAbilityConnection> &connection, bool countTowardsCallerLimit)
{
if (session == nullptr || connection == nullptr || session->serviceConnection == nullptr) {
return ERR_INVALID_VALUE;
}
auto callerRemote = GetConnectionIdentityRemote(connection);
if (callerRemote == nullptr) {
return ERR_INVALID_VALUE;
}
auto trackedIt = trackedConnections_.find(callerRemote);
if (trackedIt == trackedConnections_.end()) {
auto ret = TryRegisterConnectionLocked(connection, session->key.callerUid,
session->serviceConnection, nullptr, countTowardsCallerLimit);
if (ret != ERR_OK) {
return ret;
}
trackedIt = trackedConnections_.find(callerRemote);
} else if (trackedIt->second.standardKey.callerUid != 0 &&
!IsStandardAgentKeyEqual(trackedIt->second.standardKey, session->key)) {
TAG_LOGE(AAFwkTag::SER_ROUTER, "Connection already belongs to another standard session");
return ERR_INVALID_VALUE;
}
if (trackedIt != trackedConnections_.end()) {
trackedIt->second.standardKey = session->key;
}
if (std::find(session->callerRemotes.begin(), session->callerRemotes.end(), callerRemote) ==
session->callerRemotes.end()) {
session->callerRemotes.push_back(callerRemote);
}
auto &callbacks = session->state == StandardAgentState::CONNECTED ?
session->connectedCallbacks : session->pendingCallbacks;
if (std::find(callbacks.begin(), callbacks.end(), connection) == callbacks.end()) {
callbacks.push_back(connection);
}
return ERR_OK;
}
int32_t AgentManagerService::CreateStandardAgentSession(const AAFwk::Want &connectWant,
const std::string &agentId, const StandardAgentKey &key,
const sptr<AAFwk::IAbilityConnection> &connection)
{
auto serviceConnection = sptr<AgentServiceConnection>::MakeSptr(connection);
if (serviceConnection == nullptr) {
return ERR_INVALID_VALUE;
}
serviceConnection->SetStandardSessionMode();
auto session = std::make_shared<StandardAgentSession>();
session->key = key;
session->state = StandardAgentState::CONNECTING;
session->serviceConnection = serviceConnection;
{
std::lock_guard<std::mutex> lock(connectionLock_);
auto ret = RegisterStandardSessionCallerLocked(session, connection, true);
if (ret != ERR_OK) {
return ret;
}
standardSessions_.emplace_back(session);
}
auto ret = AAFwk::AbilityManagerClient::GetInstance()->ConnectAbilityWithExtensionType(
connectWant, serviceConnection, nullptr, AAFwk::DEFAULT_INVAL_VALUE, AppExecFwk::ExtensionAbilityType::AGENT);
if (ret != ERR_OK) {
TAG_LOGE(AAFwkTag::SER_ROUTER, "ConnectAbilityWithExtensionType failed: %{public}d", ret);
ReleaseTrackedConnection(connection);
std::lock_guard<std::mutex> lock(connectionLock_);
auto sessionIt = FindStandardSessionByServiceRemoteLocked(serviceConnection->AsObject());
if (sessionIt != standardSessions_.end()) {
standardSessions_.erase(sessionIt);
}
return ret;
}
return ERR_OK;
}
void AgentManagerService::HandleStandardAgentConnectDone(
const sptr<IRemoteObject> &serviceRemote,
const AppExecFwk::ElementName &element, const sptr<IRemoteObject> &remoteObject, int32_t resultCode)
{
std::vector<sptr<AAFwk::IAbilityConnection>> callbacks;
{
std::lock_guard<std::mutex> lock(connectionLock_);
auto sessionIt = FindStandardSessionByServiceRemoteLocked(serviceRemote);
if (sessionIt == standardSessions_.end() || *sessionIt == nullptr) {
return;
}
auto &session = *sessionIt;
session->cachedElement = element;
session->cachedRemoteObject = remoteObject;
session->cachedResultCode = resultCode;
if (resultCode == ERR_OK && remoteObject != nullptr) {
session->state = StandardAgentState::CONNECTED;
callbacks = session->pendingCallbacks;
session->pendingCallbacks.clear();
// Move all pending callers to connectedCallbacks for disconnect notification
session->connectedCallbacks.insert(session->connectedCallbacks.end(),
callbacks.begin(), callbacks.end());
} else {
// Connect failed: clean up the entire collapsed session
callbacks = session->pendingCallbacks;
session->pendingCallbacks.clear();
standardSessions_.erase(sessionIt);
}
}
for (const auto &cb : callbacks) {
if (cb != nullptr) {
cb->OnAbilityConnectDone(element, remoteObject, resultCode);
}
if (resultCode != ERR_OK) {
ReleaseTrackedConnection(cb);
}
}
}
void AgentManagerService::HandleStandardAgentDisconnectDone(
const sptr<IRemoteObject> &serviceRemote,
const AppExecFwk::ElementName &element, int32_t resultCode)
{
std::vector<sptr<AAFwk::IAbilityConnection>> callbacks;
{
std::lock_guard<std::mutex> lock(connectionLock_);
auto sessionIt = FindStandardSessionByServiceRemoteLocked(serviceRemote);
if (sessionIt == standardSessions_.end() || *sessionIt == nullptr) {
return;
}
auto &session = *sessionIt;
// Collect all callers: pending + connected
callbacks = session->pendingCallbacks;
session->pendingCallbacks.clear();
callbacks.insert(callbacks.end(),
session->connectedCallbacks.begin(), session->connectedCallbacks.end());
session->connectedCallbacks.clear();
// Erase session bookkeeping
standardSessions_.erase(sessionIt);
}
for (const auto &cb : callbacks) {
if (cb != nullptr) {
cb->OnAbilityDisconnectDone(element, resultCode);
HandleConnectionDone(cb, resultCode, true);
}
}
}
} // namespace AgentRuntime
} // namespace OHOS
@@ -23,21 +23,9 @@ AgentServiceConnection::AgentServiceConnection(const sptr<AAFwk::IAbilityConnect
: callerConnection_(connection)
{}
void AgentServiceConnection::SetStandardSessionMode()
{
isStandardSession_ = true;
}
void AgentServiceConnection::OnAbilityConnectDone(
const AppExecFwk::ElementName &element, const sptr<IRemoteObject> &remoteObject, int resultCode)
{
if (isStandardSession_) {
auto service = AgentManagerService::GetInstance();
if (service != nullptr) {
service->HandleStandardAgentConnectDone(AsObject(), element, remoteObject, resultCode);
}
return;
}
if (callerConnection_ != nullptr) {
callerConnection_->OnAbilityConnectDone(element, remoteObject, resultCode);
}
@@ -49,13 +37,6 @@ void AgentServiceConnection::OnAbilityConnectDone(
void AgentServiceConnection::OnAbilityDisconnectDone(const AppExecFwk::ElementName &element, int resultCode)
{
if (isStandardSession_) {
auto service = AgentManagerService::GetInstance();
if (service != nullptr) {
service->HandleStandardAgentDisconnectDone(AsObject(), element, resultCode);
}
return;
}
if (callerConnection_ != nullptr) {
callerConnection_->OnAbilityDisconnectDone(element, resultCode);
}
@@ -884,9 +884,9 @@ HWTEST_F(AgentConnectionManagerTest, MatchConnection_001, TestSize.Level1)
}
/**
* @tc.name : MatchConnection_ShouldReturnTrue_WhenAgentExtProxyDiffers
* @tc.name : MatchConnection_ShouldReturnFalse_WhenAgentExtProxyDiffers
* @tc.number: MatchConnection_002
* @tc.desc : Test MatchConnection returns true when only agentExtProxy differs
* @tc.desc : Test MatchConnection returns false when agentExtProxy differs
*/
HWTEST_F(AgentConnectionManagerTest, MatchConnection_002, TestSize.Level1)
{
@@ -908,7 +908,7 @@ HWTEST_F(AgentConnectionManagerTest, MatchConnection_002, TestSize.Level1)
auto &connectionEntry = *AgentConnectionManager::GetInstance().agentConnections_.begin();
auto result = AgentConnectionManager::GetInstance().MatchConnection("testAgent", want, connectionEntry);
EXPECT_TRUE(result);
EXPECT_FALSE(result);
}
/**
@@ -959,30 +959,6 @@ HWTEST_F(AgentConnectionManagerTest, MatchConnection_004, TestSize.Level1)
EXPECT_FALSE(result);
}
/**
* @tc.name : MatchConnection_ShouldReturnTrue_WhenModuleNameIsEmpty
* @tc.number: MatchConnection_007
* @tc.desc : Test MatchConnection returns true when the user-passed Want does not specify moduleName
*/
HWTEST_F(AgentConnectionManagerTest, MatchConnection_007, TestSize.Level1)
{
Want want;
want.SetParam(AGENTID_KEY, std::string("testAgent"));
want.SetElementName("", "test.bundle", "test.ability");
Want want2;
want2.SetParam(AGENTID_KEY, std::string("testAgent"));
want2.SetElementName("", "test.bundle", "test.ability", "test.module");
MyFlag::retConnectAgentExtensionAbility = ERR_OK;
sptr<MockAbilityConnectCallback> callback = new MockAbilityConnectCallback();
AgentConnectionManager::GetInstance().ConnectAgentExtensionAbility(want2, callback);
auto &connectionEntry = *AgentConnectionManager::GetInstance().agentConnections_.begin();
auto result = AgentConnectionManager::GetInstance().MatchConnection("testAgent", want, connectionEntry);
EXPECT_TRUE(result);
}
/**
* @tc.name : MatchConnection_ShouldReturnFalse_WhenAbilityNameDiffers
* @tc.number: MatchConnection_005
@@ -1264,62 +1240,6 @@ HWTEST_F(AgentConnectionManagerTest, ConnectAbilityInner_002, TestSize.Level1)
EXPECT_EQ(AgentConnectionManager::GetInstance().agentConnections_.size(), static_cast<size_t>(1));
}
/**
* @tc.name : ConnectAbilityInner_ShouldReuseConnection_WhenHostProxyDiffers
* @tc.number: ConnectAbilityInner_004
* @tc.desc : Test ConnectAbilityInner reuses connection for same agent and target with different host proxies
*/
HWTEST_F(AgentConnectionManagerTest, ConnectAbilityInner_004, TestSize.Level1)
{
Want want1;
want1.SetParam(AGENTID_KEY, std::string("testAgent"));
want1.SetElementName("", "test.bundle", "test.ability", "test.module");
sptr<IRemoteObject> remoteObj1 = sptr<MockIRemoteObject>::MakeSptr();
want1.SetParam(AGENTEXTENSIONHOSTPROXY_KEY, remoteObj1);
Want want2;
want2.SetParam(AGENTID_KEY, std::string("testAgent"));
want2.SetElementName("", "test.bundle", "test.ability", "test.module");
sptr<IRemoteObject> remoteObj2 = sptr<MockIRemoteObject>::MakeSptr();
want2.SetParam(AGENTEXTENSIONHOSTPROXY_KEY, remoteObj2);
MyFlag::retConnectAgentExtensionAbility = ERR_OK;
sptr<MockAbilityConnectCallback> callback1 = new MockAbilityConnectCallback();
auto result = AgentConnectionManager::GetInstance().ConnectAgentExtensionAbility(want1, callback1);
EXPECT_EQ(result, ERR_OK);
sptr<MockAbilityConnectCallback> callback2 = new MockAbilityConnectCallback();
result = AgentConnectionManager::GetInstance().ConnectAgentExtensionAbility(want2, callback2);
EXPECT_EQ(result, ERR_OK);
EXPECT_EQ(AgentConnectionManager::GetInstance().agentConnections_.size(), static_cast<size_t>(1));
EXPECT_EQ(AgentConnectionManager::GetInstance().agentConnections_.begin()->second.size(), static_cast<size_t>(2));
}
/**
* @tc.name : ConnectAbilityInner_ShouldCreateNewConnection_WhenExplicitModuleNameDiffers
* @tc.number: ConnectAbilityInner_005
* @tc.desc : Test ConnectAbilityInner keeps same abilityName in different explicit modules separate
*/
HWTEST_F(AgentConnectionManagerTest, ConnectAbilityInner_005, TestSize.Level1)
{
Want want1;
want1.SetParam(AGENTID_KEY, std::string("testAgent"));
want1.SetElementName("", "test.bundle", "test.ability", "moduleA");
Want want2;
want2.SetParam(AGENTID_KEY, std::string("testAgent"));
want2.SetElementName("", "test.bundle", "test.ability", "moduleB");
MyFlag::retConnectAgentExtensionAbility = ERR_OK;
sptr<MockAbilityConnectCallback> callback1 = new MockAbilityConnectCallback();
EXPECT_EQ(AgentConnectionManager::GetInstance().ConnectAgentExtensionAbility(want1, callback1), ERR_OK);
sptr<MockAbilityConnectCallback> callback2 = new MockAbilityConnectCallback();
EXPECT_EQ(AgentConnectionManager::GetInstance().ConnectAgentExtensionAbility(want2, callback2), ERR_OK);
EXPECT_EQ(AgentConnectionManager::GetInstance().agentConnections_.size(), static_cast<size_t>(2));
}
/**
* @tc.name : ConnectAbilityInner_ShouldRemoveConnection_WhenStateIsDisconnected (L394 coverage)
* @tc.number: ConnectAbilityInner_003
@@ -71,7 +71,6 @@ public:
auto service = AgentManagerService::GetInstance();
service->trackedConnections_.clear();
service->callerConnectionCounts_.clear();
service->standardSessions_.clear();
}
};
@@ -148,201 +147,5 @@ HWTEST_F(AgentServiceConnectionTest, OnAbilityDisconnectDone_001, TestSize.Level
EXPECT_TRUE(service->trackedConnections_.empty());
EXPECT_TRUE(service->callerConnectionCounts_.empty());
}
/**
* @tc.name : SetStandardSessionMode_001
* @tc.number: SetStandardSessionMode_001
* @tc.desc : Test SetStandardSessionMode sets the internal flag
*/
HWTEST_F(AgentServiceConnectionTest, SetStandardSessionMode_001, TestSize.Level1)
{
auto callerConnection = sptr<TrackingAbilityConnection>::MakeSptr();
auto agentConnection = sptr<AgentServiceConnection>::MakeSptr(callerConnection);
EXPECT_FALSE(agentConnection->isStandardSession_);
agentConnection->SetStandardSessionMode();
EXPECT_TRUE(agentConnection->isStandardSession_);
}
/**
* @tc.name : OnAbilityConnectDone_003
* @tc.number: OnAbilityConnectDone_003
* @tc.desc : Test standard session mode connect done routes to HandleStandardAgentConnectDone and notifies all
* pending callers
*/
HWTEST_F(AgentServiceConnectionTest, OnAbilityConnectDone_003, TestSize.Level1)
{
auto service = AgentManagerService::GetInstance();
// Build a standard session with two pending callers
auto caller1 = sptr<TrackingAbilityConnection>::MakeSptr();
auto caller2 = sptr<TrackingAbilityConnection>::MakeSptr();
AgentManagerService::StandardAgentKey key;
key.callerUid = 100;
key.agentId = "testAgent";
key.bundleName = "test.bundle";
key.abilityName = "TestAbility";
auto session = std::make_shared<AgentManagerService::StandardAgentSession>();
session->key = key;
session->state = AgentManagerService::StandardAgentState::CONNECTING;
session->pendingCallbacks.push_back(caller1);
session->pendingCallbacks.push_back(caller2);
auto agentConnection = sptr<AgentServiceConnection>::MakeSptr(nullptr);
agentConnection->SetStandardSessionMode();
session->serviceConnection = agentConnection;
// Register the session so HandleStandardAgentConnectDone can find it
service->standardSessions_.emplace_back(session);
// Simulate AMS connect done
AppExecFwk::ElementName element("", "test.bundle", "TestAbility");
sptr<IRemoteObject> remoteObject = new (std::nothrow) IPCObjectStub(u"test.remote");
agentConnection->OnAbilityConnectDone(element, remoteObject, ERR_OK);
// Both pending callers should be notified
EXPECT_EQ(caller1->connectDoneCount, 1);
EXPECT_EQ(caller1->lastResultCode, ERR_OK);
EXPECT_EQ(caller1->lastRemoteObject, remoteObject);
EXPECT_EQ(caller2->connectDoneCount, 1);
EXPECT_EQ(caller2->lastResultCode, ERR_OK);
EXPECT_EQ(caller2->lastRemoteObject, remoteObject);
// Session should be CONNECTED and pendingCallbacks should be empty
EXPECT_EQ(session->state, AgentManagerService::StandardAgentState::CONNECTED);
EXPECT_TRUE(session->pendingCallbacks.empty());
// Both should be in connectedCallbacks now
EXPECT_EQ(session->connectedCallbacks.size(), 2);
}
/**
* @tc.name : OnAbilityConnectDone_004
* @tc.number: OnAbilityConnectDone_004
* @tc.desc : Test standard session mode connect failure cleans up session and notifies pending callers
*/
HWTEST_F(AgentServiceConnectionTest, OnAbilityConnectDone_004, TestSize.Level1)
{
auto service = AgentManagerService::GetInstance();
auto caller1 = sptr<TrackingAbilityConnection>::MakeSptr();
AgentManagerService::StandardAgentKey key;
key.callerUid = 100;
key.agentId = "testAgent";
key.bundleName = "test.bundle";
key.abilityName = "TestAbility";
auto session = std::make_shared<AgentManagerService::StandardAgentSession>();
session->key = key;
session->state = AgentManagerService::StandardAgentState::CONNECTING;
session->pendingCallbacks.push_back(caller1);
auto agentConnection = sptr<AgentServiceConnection>::MakeSptr(nullptr);
agentConnection->SetStandardSessionMode();
session->serviceConnection = agentConnection;
service->standardSessions_.emplace_back(session);
// Simulate AMS connect failure
AppExecFwk::ElementName element("", "test.bundle", "TestAbility");
agentConnection->OnAbilityConnectDone(element, nullptr, ERR_INVALID_VALUE);
// Caller should be notified of failure
EXPECT_EQ(caller1->connectDoneCount, 1);
EXPECT_EQ(caller1->lastResultCode, ERR_INVALID_VALUE);
// Session should be cleaned up
EXPECT_TRUE(service->standardSessions_.empty());
}
/**
* @tc.name : OnAbilityDisconnectDone_002
* @tc.number: OnAbilityDisconnectDone_002
* @tc.desc : Test standard session mode disconnect done notifies all callers and cleans up session
*/
HWTEST_F(AgentServiceConnectionTest, OnAbilityDisconnectDone_002, TestSize.Level1)
{
auto service = AgentManagerService::GetInstance();
auto caller1 = sptr<TrackingAbilityConnection>::MakeSptr();
auto caller2 = sptr<TrackingAbilityConnection>::MakeSptr();
AgentManagerService::StandardAgentKey key;
key.callerUid = 100;
key.agentId = "testAgent";
key.bundleName = "test.bundle";
key.abilityName = "TestAbility";
auto session = std::make_shared<AgentManagerService::StandardAgentSession>();
session->key = key;
session->state = AgentManagerService::StandardAgentState::CONNECTED;
session->connectedCallbacks.push_back(caller1);
session->connectedCallbacks.push_back(caller2);
auto agentConnection = sptr<AgentServiceConnection>::MakeSptr(nullptr);
agentConnection->SetStandardSessionMode();
session->serviceConnection = agentConnection;
// Also set up a tracked connection for the first caller so HandleConnectionDone can clean up
AgentManagerService::TrackedConnectionRecord record;
record.callerUid = 100;
record.isDisconnecting = true;
service->trackedConnections_.emplace(caller1->AsObject(), record);
service->standardSessions_.emplace_back(session);
// Simulate AMS disconnect done
AppExecFwk::ElementName element("", "test.bundle", "TestAbility");
agentConnection->OnAbilityDisconnectDone(element, ERR_OK);
// Both callers should be notified
EXPECT_EQ(caller1->disconnectDoneCount, 1);
EXPECT_EQ(caller1->lastResultCode, ERR_OK);
EXPECT_EQ(caller2->disconnectDoneCount, 1);
EXPECT_EQ(caller2->lastResultCode, ERR_OK);
// Session should be cleaned up
EXPECT_TRUE(service->standardSessions_.empty());
EXPECT_TRUE(service->trackedConnections_.empty());
EXPECT_TRUE(service->callerConnectionCounts_.empty());
}
/**
* @tc.name : OnAbilityConnectDone_005
* @tc.number: OnAbilityConnectDone_005
* @tc.desc : Test standard session mode does not forward to callerConnection (which is null)
*/
HWTEST_F(AgentServiceConnectionTest, OnAbilityConnectDone_005, TestSize.Level1)
{
auto service = AgentManagerService::GetInstance();
AgentManagerService::StandardAgentKey key;
key.callerUid = 100;
key.agentId = "testAgent";
key.bundleName = "test.bundle";
key.abilityName = "TestAbility";
auto session = std::make_shared<AgentManagerService::StandardAgentSession>();
session->key = key;
session->state = AgentManagerService::StandardAgentState::CONNECTING;
// No pending callbacks — session mode should still work without crash
auto agentConnection = sptr<AgentServiceConnection>::MakeSptr(nullptr);
agentConnection->SetStandardSessionMode();
session->serviceConnection = agentConnection;
service->standardSessions_.emplace_back(session);
AppExecFwk::ElementName element("", "test.bundle", "TestAbility");
sptr<IRemoteObject> remoteObject = new (std::nothrow) IPCObjectStub(u"test.remote");
// Should not crash even with no pending callbacks
agentConnection->OnAbilityConnectDone(element, remoteObject, ERR_OK);
EXPECT_EQ(session->state, AgentManagerService::StandardAgentState::CONNECTED);
EXPECT_TRUE(session->pendingCallbacks.empty());
EXPECT_TRUE(session->connectedCallbacks.empty());
}
} // namespace AgentRuntime
} // namespace OHOS