* Copyright (c) 2021-2022 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 "connection_manager.h"
#include "ability_connection.h"
#include "ability_context.h"
#include "ability_manager_client.h"
#include "dfx_dump_catcher.h"
#ifdef SUPPORT_HICHECKER
#include "hichecker.h"
#endif
#include "hilog_tag_wrapper.h"
#include "ui_service_extension_connection_constants.h"
namespace OHOS {
namespace AbilityRuntime {
using namespace OHOS::HiviewDFX;
namespace {
constexpr const int64_t CONNECTING_TIMEOUT = 30000;
}
ConnectionManager& ConnectionManager::GetInstance()
{
static ConnectionManager connectionManager;
return connectionManager;
}
ErrCode ConnectionManager::ConnectAbility(const sptr<IRemoteObject>& connectCaller,
const AAFwk::Want& want, const sptr<AbilityConnectCallback>& connectCallback)
{
return ConnectAbilityInner(connectCaller, want, AAFwk::DEFAULT_INVAL_VALUE, connectCallback);
}
ErrCode ConnectionManager::ConnectAbilityWithAccount(const sptr<IRemoteObject>& connectCaller,
const AAFwk::Want& want, int accountId, const sptr<AbilityConnectCallback>& connectCallback)
{
return ConnectAbilityInner(connectCaller, want, accountId, connectCallback);
}
ErrCode ConnectionManager::ConnectUIServiceExtensionAbility(const sptr<IRemoteObject>& connectCaller,
const AAFwk::Want& want, const sptr<AbilityConnectCallback>& connectCallback)
{
return ConnectAbilityInner(connectCaller, want, AAFwk::DEFAULT_INVAL_VALUE, connectCallback,
AppExecFwk::ExtensionAbilityType::UI_SERVICE);
}
ErrCode ConnectionManager::ConnectExtensionAbilityWithExtensionType(const sptr<IRemoteObject>& connectCaller,
const AAFwk::Want& want, const sptr<AbilityConnectCallback>& connectCallback,
AppExecFwk::ExtensionAbilityType extensionType)
{
return ConnectAbilityInner(connectCaller, want, AAFwk::DEFAULT_INVAL_VALUE, connectCallback, extensionType);
}
ErrCode ConnectionManager::ConnectAbilityInner(const sptr<IRemoteObject>& connectCaller, const AAFwk::Want& want,
int accountId, const sptr<AbilityConnectCallback>& connectCallback, AppExecFwk::ExtensionAbilityType extensionType)
{
if (connectCaller == nullptr || connectCallback == nullptr) {
TAG_LOGE(AAFwkTag::CONNECTION, "null connectCaller or connectCallback");
return AAFwk::ERR_INVALID_CALLER;
}
AppExecFwk::ElementName connectReceiver = want.GetElement();
TAG_LOGD(AAFwkTag::CONNECTION, "connectReceiver: %{public}s",
(connectReceiver.GetBundleName() + ":" + connectReceiver.GetAbilityName()).c_str());
sptr<AbilityConnection> abilityConnection;
std::lock_guard<std::recursive_mutex> lock(connectionsLock_);
auto connectionIter = abilityConnections_.begin();
for (; connectionIter != abilityConnections_.end(); ++connectionIter) {
if (MatchConnection(connectCaller, want, accountId, *connectionIter) &&
!IsConnectingTimeout(connectionIter->first)) {
break;
}
}
if (connectionIter != abilityConnections_.end()) {
std::vector<sptr<AbilityConnectCallback>>& callbacks = connectionIter->second;
callbacks.push_back(connectCallback);
abilityConnection = connectionIter->first.abilityConnection;
if (!abilityConnection) {
TAG_LOGE(AAFwkTag::CONNECTION, "AbilityConnection not exist");
return AAFwk::ERR_INVALID_CALLER;
}
TAG_LOGI(AAFwkTag::CONNECTION, "abilityConnectionsSize: %{public}zu, ConnectionState: %{public}d",
abilityConnections_.size(), abilityConnection->GetConnectionState());
abilityConnection->AddConnectCallback(connectCallback);
TAG_LOGD(AAFwkTag::CONNECTION, "abilityConnection exist, callbackSize:%{public}zu", callbacks.size());
if (abilityConnection->GetConnectionState() == CONNECTION_STATE_CONNECTED) {
connectCallback->OnAbilityConnectDone(connectReceiver, abilityConnection->GetRemoteObject(),
abilityConnection->GetResultCode());
return ERR_OK;
} else if (abilityConnection->GetConnectionState() == CONNECTION_STATE_CONNECTING) {
return ERR_OK;
} else {
TAG_LOGE(AAFwkTag::CONNECTION, "abilityConnection disconnected");
abilityConnections_.erase(connectionIter);
return CreateConnection(connectCaller, want, accountId, connectCallback, extensionType);
}
} else {
return CreateConnection(connectCaller, want, accountId, connectCallback, extensionType);
}
}
void* ConnectionManager::GetUIServiceExtProxyPtr(const AAFwk::Want& want)
{
sptr<IRemoteObject> uiServiceExtProxySptr = want.GetRemoteObject(UISERVICEHOSTPROXY_KEY);
void* uiServiceExtProxy = nullptr;
if (uiServiceExtProxySptr != nullptr) {
uiServiceExtProxy = uiServiceExtProxySptr.GetRefPtr();
}
return uiServiceExtProxy;
}
bool ConnectionManager::MatchConnection(
const sptr<IRemoteObject>& connectCaller, const AAFwk::Want& connectReceiver, int32_t accountId,
const std::map<ConnectionInfo, std::vector<sptr<AbilityConnectCallback>>>::value_type& connection)
{
void* uiServiceExtProxy = GetUIServiceExtProxyPtr(connectReceiver);
if (uiServiceExtProxy != connection.first.uiServiceExtProxy) {
return false;
}
if (accountId != connection.first.userid) {
return false;
}
if (!connectReceiver.GetElement().GetAbilityName().empty()) {
return connectCaller == connection.first.connectCaller &&
connectReceiver.GetElement().GetBundleName() == connection.first.connectReceiver.GetBundleName() &&
connectReceiver.GetElement().GetModuleName() == connection.first.connectReceiver.GetModuleName() &&
connectReceiver.GetElement().GetAbilityName() == connection.first.connectReceiver.GetAbilityName();
} else {
return connectCaller == connection.first.connectCaller &&
connectReceiver.GetElement().GetBundleName() == connection.first.connectReceiver.GetBundleName() &&
connectReceiver.GetOperation() == connection.first.connectReceiver;
}
}
ErrCode ConnectionManager::CreateConnection(const sptr<IRemoteObject>& connectCaller, const AAFwk::Want& want,
int accountId, const sptr<AbilityConnectCallback>& connectCallback, AppExecFwk::ExtensionAbilityType extensionType)
{
TAG_LOGD(AAFwkTag::CONNECTION, "called");
sptr<AbilityConnection> abilityConnection = new AbilityConnection();
if (abilityConnection == nullptr) {
TAG_LOGE(AAFwkTag::CONNECTION, "null abilityConnection");
return AAFwk::ERR_INVALID_CALLER;
}
abilityConnection->AddConnectCallback(connectCallback);
abilityConnection->SetConnectionState(CONNECTION_STATE_CONNECTING);
ErrCode ret = ERR_OK;
ret = AAFwk::AbilityManagerClient::GetInstance()->ConnectAbilityWithExtensionType(
want, abilityConnection, connectCaller, accountId, extensionType);
std::lock_guard<std::recursive_mutex> lock(connectionsLock_);
if (ret == ERR_OK) {
ConnectionInfo connectionInfo(connectCaller, want.GetOperation(), abilityConnection, accountId);
connectionInfo.RecordConnectingTime();
void* uiServiceExtProxy = GetUIServiceExtProxyPtr(want);
connectionInfo.SetUIServiceExtProxyPtr(uiServiceExtProxy);
std::vector<sptr<AbilityConnectCallback>> callbacks;
callbacks.push_back(connectCallback);
abilityConnections_[connectionInfo] = callbacks;
} else {
TAG_LOGE(AAFwkTag::CONNECTION, "error:%{public}d", ret);
}
return ret;
}
ErrCode ConnectionManager::DisconnectAbility(const sptr<IRemoteObject>& connectCaller,
const AppExecFwk::ElementName& connectReceiver, const sptr<AbilityConnectCallback>& connectCallback)
{
AAFwk::Want wantReceiver;
wantReceiver.SetElement(connectReceiver);
return DisconnectAbility(connectCaller, wantReceiver, connectCallback);
}
ErrCode ConnectionManager::DisconnectAbility(const sptr<IRemoteObject>& connectCaller,
const AAFwk::Want& connectReceiver, const sptr<AbilityConnectCallback>& connectCallback,
int32_t accountId)
{
if (connectCaller == nullptr || connectCallback == nullptr) {
TAG_LOGE(AAFwkTag::CONNECTION, "null connectCaller or connectCallback");
return AAFwk::ERR_INVALID_CALLER;
}
auto element = connectReceiver.GetElement();
TAG_LOGD(AAFwkTag::CONNECTION, "connectReceiver: %{public}s",
(element.GetBundleName() + ":" + element.GetAbilityName()).c_str());
std::lock_guard<std::recursive_mutex> lock(connectionsLock_);
bool found = false;
auto item = abilityConnections_.begin();
if (!abilityConnections_.empty()) {
TAG_LOGI(AAFwkTag::CONNECTION, "Connection size:%{public}zu", abilityConnections_.size());
}
while (item != abilityConnections_.end()) {
if (!MatchConnection(connectCaller, connectReceiver, accountId, *item) ||
std::find(item->second.begin(), item->second.end(), connectCallback) == item->second.end()) {
item++;
continue;
}
found = true;
TAG_LOGD(AAFwkTag::CONNECTION, "callback size: %{public}zu", item->second.size());
auto iter = item->second.begin();
while (iter != item->second.end()) {
if (*iter == connectCallback) {
iter = item->second.erase(iter);
} else {
iter++;
}
}
sptr<AbilityConnection> abilityConnection = item->first.abilityConnection;
if (item->second.empty()) {
item = abilityConnections_.erase(item);
TAG_LOGI(AAFwkTag::CONNECTION, "no callback left, disconnectAbility");
auto ret = AAFwk::AbilityManagerClient::GetInstance()->DisconnectAbility(abilityConnection);
if (ret != ERR_OK) {
TAG_LOGE(AAFwkTag::CONNECTION, "disconnect err:%{public}d", ret);
return ret;
}
} else {
connectCallback->OnAbilityDisconnectDone(element, ERR_OK);
abilityConnection->RemoveConnectCallback(connectCallback);
TAG_LOGD(AAFwkTag::CONNECTION, "callbacks not empty, no need disconnectAbility");
item++;
}
}
if (!found) {
TAG_LOGE(AAFwkTag::CONNECTION, "not find conn");
return AAFwk::CONNECTION_NOT_EXIST;
}
return ERR_OK;
}
bool ConnectionManager::DisconnectCaller(const sptr<IRemoteObject>& connectCaller)
{
TAG_LOGD(AAFwkTag::CONNECTION, "call");
if (connectCaller == nullptr) {
TAG_LOGE(AAFwkTag::CONNECTION, "null connectCaller");
return false;
}
std::lock_guard<std::recursive_mutex> lock(connectionsLock_);
TAG_LOGD(AAFwkTag::CONNECTION, "abilityConnectionsSize:%{public}zu", abilityConnections_.size());
bool isDisconnect = false;
auto iter = abilityConnections_.begin();
while (iter != abilityConnections_.end()) {
ConnectionInfo connectionInfo = iter->first;
if (IsConnectCallerEqual(connectionInfo.connectCaller, connectCaller)) {
TAG_LOGD(AAFwkTag::CONNECTION, "DisconnectAbility");
ErrCode ret =
AAFwk::AbilityManagerClient::GetInstance()->DisconnectAbility(connectionInfo.abilityConnection);
if (ret != ERR_OK) {
TAG_LOGE(AAFwkTag::CONNECTION, "error:%{public}d", ret);
}
iter = abilityConnections_.erase(iter);
isDisconnect = true;
} else {
++iter;
}
}
TAG_LOGD(AAFwkTag::CONNECTION, "abilityConnectionsSize:%{public}zu", abilityConnections_.size());
return isDisconnect;
}
bool ConnectionManager::RemoveConnection(const sptr<AbilityConnection> connection)
{
std::lock_guard<std::recursive_mutex> lock(connectionsLock_);
TAG_LOGD(AAFwkTag::CONNECTION, "abilityConnectionsSize: %{public}zu", abilityConnections_.size());
bool isDisconnect = false;
auto iter = abilityConnections_.begin();
while (iter != abilityConnections_.end()) {
ConnectionInfo connectionInfo = iter->first;
if (connectionInfo.abilityConnection == connection) {
TAG_LOGD(AAFwkTag::CONNECTION, "Remove connection");
iter = abilityConnections_.erase(iter);
isDisconnect = true;
} else {
++iter;
}
}
return isDisconnect;
}
bool ConnectionManager::DisconnectNonexistentService(
const AppExecFwk::ElementName& element, const sptr<AbilityConnection> connection)
{
bool exit = false;
std::map<ConnectionInfo, std::vector<sptr<AbilityConnectCallback>>> abilityConnections;
{
std::lock_guard<std::recursive_mutex> lock(connectionsLock_);
abilityConnections = abilityConnections_;
}
TAG_LOGD(AAFwkTag::CONNECTION, "abilityConnectionsSize: %{public}zu", abilityConnections.size());
for (auto &&abilityConnection : abilityConnections) {
ConnectionInfo connectionInfo = abilityConnection.first;
if (connectionInfo.abilityConnection == connection &&
connectionInfo.connectReceiver.GetBundleName() == element.GetBundleName()) {
TAG_LOGD(AAFwkTag::CONNECTION, "find connection");
exit = true;
break;
}
}
if (!exit) {
TAG_LOGE(AAFwkTag::CONNECTION, "service need disconnect");
AAFwk::AbilityManagerClient::GetInstance()->DisconnectAbility(connection);
return true;
}
return false;
}
void ConnectionManager::ReportConnectionLeakEvent(const int pid, const int tid)
{
TAG_LOGD(AAFwkTag::CONNECTION, "pid:%{public}d, tid:%{public}d", pid, tid);
#ifdef SUPPORT_HICHECKER
if (HiChecker::Contains(HiviewDFX::Rule::RULE_CHECK_ABILITY_CONNECTION_LEAK)) {
DfxDumpCatcher dumpLog;
std::string stackTrace;
bool ret = dumpLog.DumpCatch(pid, tid, stackTrace);
if (ret) {
std::string cautionMsg = "TriggerRule:RULE_CHECK_ABILITY_CONNECTION_LEAK-pid=" +
std::to_string(pid) + "-tid=" + std::to_string(tid) + ", has leaked connection" +
", Are you missing a call to DisconnectAbility()";
TAG_LOGD(AAFwkTag::CONNECTION, "cautionMsg:%{public}s", cautionMsg.c_str());
Caution caution(HiviewDFX::Rule::RULE_CHECK_ABILITY_CONNECTION_LEAK, cautionMsg, stackTrace);
HiChecker::NotifyAbilityConnectionLeak(caution);
} else {
TAG_LOGE(AAFwkTag::CONNECTION, "dumpCatch stackTrace failed");
}
}
#endif
}
bool ConnectionManager::IsConnectCallerEqual(const sptr<IRemoteObject>& connectCaller,
const sptr<IRemoteObject>& connectCallerOther)
{
return connectCaller == connectCallerOther;
}
bool ConnectionManager::IsConnectReceiverEqual(AAFwk::Operation& connectReceiver,
const AppExecFwk::ElementName& connectReceiverOther)
{
return connectReceiver.GetBundleName() == connectReceiverOther.GetBundleName() &&
connectReceiver.GetAbilityName() == connectReceiverOther.GetAbilityName();
}
bool ConnectionManager::IsConnectingTimeout(const ConnectionInfo& info)
{
if (info.connectingTime == 0) {
return false;
}
if (!info.abilityConnection || info.abilityConnection->GetConnectionState() != CONNECTION_STATE_CONNECTING) {
return false;
}
auto now = std::chrono::steady_clock::now().time_since_epoch();
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(now).count() - info.connectingTime;
if (duration >= CONNECTING_TIMEOUT) {
TAG_LOGW(AAFwkTag::CONNECTION, "connecting timeout, no reuse");
return true;
}
return false;
}
}
}