* Copyright (c) 2023 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "socket_session_manager.h"
#include <algorithm>
#include <sys/socket.h>
#include <unistd.h>
#include "iservice_registry.h"
#include "system_ability_definition.h"
#include "devicestatus_define.h"
#undef LOG_TAG
#define LOG_TAG "SocketSessionManager"
namespace OHOS {
namespace Msdp {
namespace DeviceStatus {
namespace {
constexpr int32_t MAX_EPOLL_EVENTS { 64 };
}
int32_t SocketSessionManager::Init()
{
return epollMgr_.Open();
}
void SocketSessionManager::RegisterApplicationState()
{
CALL_DEBUG_ENTER;
auto appMgr = GetAppMgr();
CHKPV(appMgr);
appStateObserver_ = sptr<AppStateObserver>::MakeSptr(*this);
auto err = appMgr->RegisterApplicationStateObserver(appStateObserver_);
if (err != RET_OK) {
appStateObserver_ = nullptr;
FI_HILOGE("IAppMgr::RegisterApplicationStateObserver fail, error:%{public}d", err);
}
}
void SocketSessionManager::AppStateObserver::OnProcessDied(const AppExecFwk::ProcessData &processData)
{
FI_HILOGI("\'%{public}s\' died, pid:%{public}d", processData.bundleName.c_str(), processData.pid);
socketSessionManager_.ReleaseSessionByPid(processData.pid);
}
int32_t SocketSessionManager::AllocSocketFd(const std::string& programName, int32_t moduleType, int32_t tokenType,
int32_t uid, int32_t pid, int32_t& clientFd)
{
CALL_DEBUG_ENTER;
int32_t sockFds[2] { -1, -1 };
if (::socketpair(AF_UNIX, SOCK_STREAM, 0, sockFds) != 0) {
FI_HILOGE("Call socketpair failed, errno:%{public}s", ::strerror(errno));
return RET_ERR;
}
static constexpr size_t BUFFER_SIZE { 32 * 1024 };
static constexpr size_t NATIVE_BUFFER_SIZE { 64 * 1024 };
std::shared_ptr<SocketSession> session { nullptr };
if (!SetBufferSize(sockFds[0], BUFFER_SIZE)) {
goto CLOSE_SOCK;
}
if (!SetBufferSize(sockFds[1], tokenType == TokenType::TOKEN_NATIVE ? NATIVE_BUFFER_SIZE : BUFFER_SIZE)) {
goto CLOSE_SOCK;
}
session = std::make_shared<SocketSession>(programName, moduleType, tokenType, sockFds[0], uid, pid);
if (epollMgr_.Add(*session) != RET_OK) {
goto CLOSE_SOCK;
}
if (!AddSession(session)) {
FI_HILOGE("AddSession failed, errCode:%{public}d", ADD_SESSION_FAIL);
goto CLOSE_SOCK;
}
clientFd = sockFds[1];
return RET_OK;
CLOSE_SOCK:
if (::close(sockFds[0]) != 0) {
FI_HILOGE("Close(%{public}d) failed:%{public}s", sockFds[0], ::strerror(errno));
}
if (::close(sockFds[1]) != 0) {
FI_HILOGE("Close(%{public}d) failed:%{public}s", sockFds[1], ::strerror(errno));
}
return RET_ERR;
}
bool SocketSessionManager::SetBufferSize(int32_t sockFd, int32_t bufSize)
{
if (::setsockopt(sockFd, SOL_SOCKET, SO_SNDBUF, &bufSize, sizeof(bufSize)) != 0) {
FI_HILOGE("Set sockopt(%{public}d) failed:%{public}s", sockFd, ::strerror(errno));
return false;
}
if (::setsockopt(sockFd, SOL_SOCKET, SO_RCVBUF, &bufSize, sizeof(bufSize)) != 0) {
FI_HILOGE("Set sockopt(%{public}d) failed:%{public}s", sockFd, ::strerror(errno));
return false;
}
return true;
}
SocketSessionPtr SocketSessionManager::FindSessionByPid(int32_t pid) const
{
auto iter = std::find_if(sessions_.cbegin(), sessions_.cend(),
[pid](const auto &item) {
return ((item.second != nullptr) && (item.second->GetPid() == pid));
});
return (iter != sessions_.cend() ? iter->second : nullptr);
}
void SocketSessionManager::Dispatch(const struct epoll_event &ev)
{
if ((ev.events & EPOLLIN) == EPOLLIN) {
DispatchOne();
} else if ((ev.events & (EPOLLHUP | EPOLLERR)) != 0) {
FI_HILOGE("Epoll hangup:%{public}s", ::strerror(errno));
}
}
void SocketSessionManager::DispatchOne()
{
struct epoll_event evs[MAX_EPOLL_EVENTS];
int32_t cnt = epollMgr_.WaitTimeout(evs, MAX_EPOLL_EVENTS, 0);
for (int32_t index = 0; index < cnt; ++index) {
IEpollEventSource *source = reinterpret_cast<IEpollEventSource *>(evs[index].data.ptr);
CHKPC(source);
if ((evs[index].events & EPOLLIN) == EPOLLIN) {
source->Dispatch(evs[index]);
} else if ((evs[index].events & (EPOLLHUP | EPOLLERR)) != 0) {
FI_HILOGE("Epoll hangup:%{public}s", ::strerror(errno));
ReleaseSession(source->GetFd());
}
}
}
void SocketSessionManager::ReleaseSession(int32_t fd)
{
CALL_DEBUG_ENTER;
if (auto iter = sessions_.find(fd); iter != sessions_.end()) {
auto session = iter->second;
sessions_.erase(iter);
if (session != nullptr) {
epollMgr_.Remove(*session);
NotifySessionDeleted(session);
}
}
DumpSession("DelSession");
}
void SocketSessionManager::ReleaseSessionByPid(int32_t pid)
{
CALL_DEBUG_ENTER;
auto iter = std::find_if(sessions_.cbegin(), sessions_.cend(),
[pid](const auto &item) {
return ((item.second != nullptr) && (item.second->GetPid() == pid));
});
if (iter != sessions_.end()) {
auto session = iter->second;
if (session != nullptr) {
epollMgr_.Remove(*session);
NotifySessionDeleted(session);
}
sessions_.erase(iter);
}
DumpSession("DelSession");
}
sptr<AppExecFwk::IAppMgr> SocketSessionManager::GetAppMgr()
{
CALL_DEBUG_ENTER;
auto saMgr = SystemAbilityManagerClient::GetInstance().GetSystemAbilityManager();
CHKPP(saMgr);
auto appMgrObj = saMgr->GetSystemAbility(APP_MGR_SERVICE_ID);
CHKPP(appMgrObj);
return iface_cast<AppExecFwk::IAppMgr>(appMgrObj);
}
std::shared_ptr<SocketSession> SocketSessionManager::FindSession(int32_t fd) const
{
auto iter = sessions_.find(fd);
return (iter != sessions_.cend() ? iter->second : nullptr);
}
void SocketSessionManager::DumpSession(const std::string &title) const
{
FI_HILOGD("in %s:%s", __func__, title.c_str());
int32_t i = 0;
for (auto &[_, session] : sessions_) {
CHKPC(session);
FI_HILOGD("%{public}d, %s", i, session->ToString().c_str());
i++;
}
}
bool SocketSessionManager::AddSession(std::shared_ptr<SocketSession> session)
{
CALL_DEBUG_ENTER;
CHKPF(session);
if (sessions_.size() >= MAX_SESSION_ALARM) {
FI_HILOGE("The number of connections exceeds limit(%{public}zu)", MAX_SESSION_ALARM);
return false;
}
auto [_, inserted] = sessions_.emplace(session->GetFd(), session);
if (!inserted) {
FI_HILOGE("Session(%{public}d) has been recorded", session->GetFd());
return false;
}
DumpSession("AddSession");
return true;
}
void SocketSessionManager::AddSessionDeletedCallback(int32_t pid, std::function<void(SocketSessionPtr)> callback)
{
if (callback == nullptr) {
FI_HILOGE("Callback is none");
return;
}
auto [_, inserted] = callbacks_.emplace(pid, callback);
if (!inserted) {
FI_HILOGW("Duplication of session-lost callback for (%{public}d)", pid);
}
FI_HILOGI("Start watching socket-session(%{public}d)", pid);
}
void SocketSessionManager::RemoveSessionDeletedCallback(int32_t pid)
{
FI_HILOGI("Stop watching socket-session(%{public}d)", pid);
callbacks_.erase(pid);
}
void SocketSessionManager::NotifySessionDeleted(std::shared_ptr<SocketSession> session)
{
CALL_DEBUG_ENTER;
FI_HILOGD("Session lost, pid:%{public}d", session->GetPid());
if (auto iter = callbacks_.find(session->GetPid()); iter != callbacks_.end()) {
if (iter->second) {
iter->second(session);
}
callbacks_.erase(iter);
}
}
}
}
}