* Copyright (c) 2024 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 "session/host/include/multi_instance_manager.h"
#include <bundlemgr/launcher_service.h>
#include "interfaces/include/ws_common.h"
#include "window_manager_hilog.h"
#include "window_helper.h"
namespace OHOS::Rosen {
namespace {
const std::string APP_INSTANCE_KEY_PREFIX = "app_instance_";
constexpr uint32_t DEFAULT_INSTANCE_ID = 0u;
}
MultiInstanceManager& MultiInstanceManager::GetInstance()
{
static MultiInstanceManager instance;
return instance;
}
bool MultiInstanceManager::IsSupportMultiInstance(const SystemSessionConfig& systemConfig)
{
return systemConfig.IsPcWindow();
}
void MultiInstanceManager::Init(const sptr<AppExecFwk::IBundleMgr>& bundleMgr,
const std::shared_ptr<TaskScheduler>& taskScheduler)
{
if (bundleMgr == nullptr || taskScheduler == nullptr) {
TLOGE(WmsLogTag::WMS_LIFE, "bundleMgr or taskScheduler is nullptr");
return;
}
bundleMgr_ = bundleMgr;
taskScheduler_ = taskScheduler;
}
void MultiInstanceManager::SetCurrentUserId(int32_t userId)
{
userId_ = userId;
if (taskScheduler_ == nullptr) {
TLOGE(WmsLogTag::WMS_LIFE, "taskScheduler is nullptr");
return;
}
const char* const where = __func__;
auto task = [this, where] {
std::vector<AppExecFwk::ApplicationInfo> appInfos;
auto flag = static_cast<int32_t>(AppExecFwk::GetApplicationFlag::GET_APPLICATION_INFO_DEFAULT);
if (!bundleMgr_ || bundleMgr_->GetApplicationInfosV9(flag, userId_, appInfos)) {
TLOGNE(WmsLogTag::WMS_LIFE, "%{public}s:get application infos fail", where);
return;
}
std::unique_lock<std::shared_mutex> lock(appInfoMutex_);
appInfoMap_.clear();
for (auto& appInfo : appInfos) {
appInfoMap_[appInfo.bundleName] = appInfo;
}
TLOGNI(WmsLogTag::WMS_LIFE, "%{public}s:application infos init", where);
};
taskScheduler_->PostAsyncTask(task, where);
}
bool MultiInstanceManager::IsMultiInstance(const std::string& bundleName)
{
std::unique_lock<std::shared_mutex> lock(appInfoMutex_);
auto iter = appInfoMap_.find(bundleName);
if (iter == appInfoMap_.end()) {
AppExecFwk::ApplicationInfo appInfo;
if (bundleMgr_ && bundleMgr_->GetApplicationInfo(
bundleName, AppExecFwk::ApplicationFlag::GET_BASIC_APPLICATION_INFO, userId_, appInfo)) {
appInfoMap_[bundleName] = appInfo;
return appInfo.multiAppMode.multiAppModeType == AppExecFwk::MultiAppModeType::MULTI_INSTANCE;
}
TLOGE(WmsLogTag::WMS_LIFE, "App info not found, bundleName:%{public}s", bundleName.c_str());
return false;
}
return iter->second.multiAppMode.multiAppModeType == AppExecFwk::MultiAppModeType::MULTI_INSTANCE;
}
uint32_t MultiInstanceManager::GetMaxInstanceCount(const std::string& bundleName)
{
if (!IsMultiInstance(bundleName)) {
return 0u;
}
std::shared_lock<std::shared_mutex> lock(appInfoMutex_);
auto iter = appInfoMap_.find(bundleName);
if (iter != appInfoMap_.end()) {
return iter->second.multiAppMode.maxCount;
}
return 0u;
}
uint32_t MultiInstanceManager::GetInstanceCount(const std::string& bundleName)
{
std::shared_lock<std::shared_mutex> lock(mutex_);
auto iter = bundleInstanceIdListMap_.find(bundleName);
if (iter == bundleInstanceIdListMap_.end()) {
return 0u;
} else {
return static_cast<uint32_t>(iter->second.size());
}
}
std::string MultiInstanceManager::GetLastInstanceKey(const std::string& bundleName)
{
std::shared_lock<std::shared_mutex> lock(mutex_);
auto iter = bundleInstanceIdListMap_.find(bundleName);
if (iter == bundleInstanceIdListMap_.end() || iter->second.size() == 0) {
TLOGE(WmsLogTag::WMS_LIFE, "not found last instance key, bundleName:%{public}s", bundleName.c_str());
return "";
} else {
TLOGI(WmsLogTag::WMS_LIFE, "bundleName:%{public}s instanceKey:app_instance_%{public}u",
bundleName.c_str(), iter->second.back());
return APP_INSTANCE_KEY_PREFIX + std::to_string(iter->second.back());
}
}
std::string MultiInstanceManager::CreateNewInstanceKey(const std::string& bundleName, const std::string& instanceKey)
{
auto maxInstanceCount = GetMaxInstanceCount(bundleName);
uint32_t instanceId = DEFAULT_INSTANCE_ID;
if (!instanceKey.empty()) {
if (!ConvertInstanceKeyToInstanceId(instanceKey, instanceId) || instanceId >= maxInstanceCount) {
TLOGE(WmsLogTag::WMS_LIFE, "invalid instanceKey, bundleName:%{public}s instanceKey:%{public}s",
bundleName.c_str(), instanceKey.c_str());
return instanceKey;
}
AddInstanceId(bundleName, instanceId);
return instanceKey;
}
instanceId = FindMinimumAvailableInstanceId(bundleName, maxInstanceCount);
AddInstanceId(bundleName, instanceId);
return APP_INSTANCE_KEY_PREFIX + std::to_string(instanceId);
}
bool MultiInstanceManager::IsValidInstanceKey(const std::string& bundleName, const std::string& instanceKey)
{
if (instanceKey.find(APP_INSTANCE_KEY_PREFIX) == -1ul) {
TLOGE(WmsLogTag::WMS_LIFE, "bundleName:%{public}s with invalid instanceKey:%{public}s",
bundleName.c_str(), instanceKey.c_str());
return false;
}
auto maxInstanceCount = GetMaxInstanceCount(bundleName);
auto instanceId = DEFAULT_INSTANCE_ID;
if (!ConvertInstanceKeyToInstanceId(instanceKey, instanceId) || instanceId >= maxInstanceCount) {
TLOGE(WmsLogTag::WMS_LIFE, "invalid instanceKey, bundleName:%{public}s instanceKey:%{public}s",
bundleName.c_str(), instanceKey.c_str());
return false;
}
return true;
}
bool MultiInstanceManager::IsInstanceKeyExist(const std::string& bundleName, const std::string& instanceKey)
{
std::shared_lock<std::shared_mutex> lock(mutex_);
auto iter = bundleInstanceIdListMap_.find(bundleName);
if (iter == bundleInstanceIdListMap_.end()) {
TLOGE(WmsLogTag::WMS_LIFE, "instanceIdList not found, bundleName:%{public}s instanceKey:%{public}s",
bundleName.c_str(), instanceKey.c_str());
return false;
}
auto instanceId = DEFAULT_INSTANCE_ID;
if (!ConvertInstanceKeyToInstanceId(instanceKey, instanceId)) {
TLOGE(WmsLogTag::WMS_LIFE, "invalid instanceKey, bundleName:%{public}s instanceKey:%{public}s",
bundleName.c_str(), instanceKey.c_str());
return false;
}
return std::find(iter->second.begin(), iter->second.end(), instanceId) != iter->second.end();
}
void MultiInstanceManager::RemoveInstanceKey(const std::string& bundleName, const std::string& instanceKey)
{
auto instanceId = DEFAULT_INSTANCE_ID;
if (!ConvertInstanceKeyToInstanceId(instanceKey, instanceId)) {
TLOGE(WmsLogTag::WMS_LIFE, "invalid instanceKey, bundleName:%{public}s instanceKey:%{public}s",
bundleName.c_str(), instanceKey.c_str());
return;
}
RemoveInstanceId(bundleName, instanceId);
}
uint32_t MultiInstanceManager::FindMinimumAvailableInstanceId(const std::string& bundleName,
uint32_t maxInstanceCount)
{
std::shared_lock<std::shared_mutex> lock(mutex_);
auto iter = bundleInstanceUsageMap_.find(bundleName);
if (iter == bundleInstanceUsageMap_.end()) {
TLOGE(WmsLogTag::WMS_LIFE, "not found available instanceId");
return DEFAULT_INSTANCE_ID;
}
for (auto i = 0u; i < maxInstanceCount; i++) {
if (iter->second[i] == 0) {
return i;
}
}
TLOGE(WmsLogTag::WMS_LIFE, "not found available instanceId");
return DEFAULT_INSTANCE_ID;
}
void MultiInstanceManager::RefreshAppInfo(const std::string& bundleName)
{
std::unique_lock<std::shared_mutex> lock(appInfoMutex_);
AppExecFwk::ApplicationInfo appInfo;
if (bundleMgr_ && bundleMgr_->GetApplicationInfo(
bundleName, AppExecFwk::ApplicationFlag::GET_BASIC_APPLICATION_INFO, userId_, appInfo)) {
appInfoMap_[bundleName] = appInfo;
} else {
appInfoMap_.erase(bundleName);
}
}
void MultiInstanceManager::IncreaseInstanceKeyRefCount(const sptr<SceneSession>& sceneSession)
{
if (!sceneSession) {
TLOGE(WmsLogTag::WMS_LIFE, "sceneSession is nullptr");
return;
}
if (!WindowHelper::IsMainWindow(sceneSession->GetWindowType())) {
TLOGE(WmsLogTag::WMS_LIFE, "sceneSession is not main window");
return;
}
const auto& instanceKey = sceneSession->GetSessionInfo().appInstanceKey_;
if (instanceKey.empty()) {
TLOGD(WmsLogTag::WMS_LIFE, "instanceKey is empty");
return;
}
const auto& bundleName = sceneSession->GetSessionInfo().bundleName_;
const auto bundleInstanceKey = bundleName + instanceKey;
auto iter = instanceKeyRefCountMap_.find(bundleInstanceKey);
if (iter == instanceKeyRefCountMap_.end()) {
TLOGD(WmsLogTag::WMS_LIFE, "bundleInstanceKey not exist.");
instanceKeyRefCountMap_.emplace(bundleInstanceKey, 1);
} else {
++(iter->second);
}
}
void MultiInstanceManager::DecreaseInstanceKeyRefCount(const sptr<SceneSession>& sceneSession)
{
if (!sceneSession) {
TLOGE(WmsLogTag::WMS_LIFE, "sceneSession is nullptr");
return;
}
if (!WindowHelper::IsMainWindow(sceneSession->GetWindowType())) {
TLOGE(WmsLogTag::WMS_LIFE, "sceneSession is not main window");
return;
}
const auto& instanceKey = sceneSession->GetSessionInfo().appInstanceKey_;
if (instanceKey.empty()) {
TLOGD(WmsLogTag::WMS_LIFE, "instanceKey is empty");
return;
}
const auto& bundleName = sceneSession->GetSessionInfo().bundleName_;
DecreaseInstanceKeyRefCountByBundleNameAndInstanceKey(bundleName, instanceKey);
}
void MultiInstanceManager::DecreaseInstanceKeyRefCountByBundleNameAndInstanceKey(const std::string& bundleName,
const std::string& instanceKey)
{
const auto bundleInstanceKey = bundleName + instanceKey;
auto iter = instanceKeyRefCountMap_.find(bundleInstanceKey);
if (iter == instanceKeyRefCountMap_.end()) {
TLOGD(WmsLogTag::WMS_LIFE, "bundleInstanceKey not exist.");
return;
}
if (--(iter->second) <= 0) {
instanceKeyRefCountMap_.erase(bundleInstanceKey);
RemoveInstanceKey(bundleName, instanceKey);
}
}
void MultiInstanceManager::FillInstanceKeyIfNeed(const sptr<SceneSession>& sceneSession)
{
if (!sceneSession) {
TLOGE(WmsLogTag::WMS_LIFE, "sceneSession is nullptr");
return;
}
if (!WindowHelper::IsMainWindow(sceneSession->GetWindowType())) {
TLOGD(WmsLogTag::WMS_LIFE, "sceneSession is not main window");
return;
}
const auto& bundleName = sceneSession->GetSessionInfo().bundleName_;
uint32_t maxInstanceCount = GetMaxInstanceCount(bundleName);
if (maxInstanceCount <= 0) {
TLOGD(WmsLogTag::WMS_LIFE, "maxInstanceCount is not valid");
return;
}
const auto& sessionInfo = sceneSession->GetSessionInfo();
if (sessionInfo.appInstanceKey_.empty()) {
uint32_t instanceCount = GetInstanceCount(bundleName);
if (sessionInfo.isNewAppInstance_ && instanceCount < maxInstanceCount) {
sceneSession->SetAppInstanceKey(CreateNewInstanceKey(bundleName));
} else {
sceneSession->SetAppInstanceKey(GetLastInstanceKey(bundleName));
}
} else if (!IsInstanceKeyExist(sessionInfo.bundleName_, sessionInfo.appInstanceKey_)) {
TLOGI(WmsLogTag::WMS_LIFE, "create not exist instanceKey, bundleName:%{public}s instanceKey:%{public}s",
bundleName.c_str(), sessionInfo.appInstanceKey_.c_str());
CreateNewInstanceKey(sessionInfo.bundleName_, sessionInfo.appInstanceKey_);
}
}
bool MultiInstanceManager::MultiInstancePendingSessionActivation(SessionInfo& info)
{
auto maxInstanceCount = GetMaxInstanceCount(info.bundleName_);
TLOGI(WmsLogTag::WMS_LIFE, "id:%{public}d maxInstanceCount:%{public}d", info.persistentId_, maxInstanceCount);
if (maxInstanceCount <= 0) {
return true;
}
if (info.appInstanceKey_.empty()) {
if (info.persistentId_ != 0) {
TLOGE(WmsLogTag::WMS_LIFE, "empty instance key, persistentId:%{public}d", info.persistentId_);
return false;
}
info.isNewAppInstance_ = true;
return true;
} else if (!IsValidInstanceKey(info.bundleName_, info.appInstanceKey_)) {
TLOGE(WmsLogTag::WMS_LIFE, "invalid instancekey:%{public}s", info.appInstanceKey_.c_str());
return false;
} else if (!IsInstanceKeyExist(info.bundleName_, info.appInstanceKey_)) {
TLOGI(WmsLogTag::WMS_LIFE, "id:%{public}d, create not exist instanceKey:%{public}s",
info.persistentId_, info.appInstanceKey_.c_str());
CreateNewInstanceKey(info.bundleName_, info.appInstanceKey_);
}
info.isNewAppInstance_ = false;
return true;
}
void MultiInstanceManager::AddInstanceId(const std::string& bundleName, uint32_t instanceId)
{
std::unique_lock<std::shared_mutex> lock(mutex_);
auto iter = bundleInstanceIdListMap_.find(bundleName);
if (iter == bundleInstanceIdListMap_.end()) {
bundleInstanceIdListMap_.emplace(bundleName, std::vector{ instanceId });
} else {
iter->second.push_back(instanceId);
}
auto usageMapIter = bundleInstanceUsageMap_.find(bundleName);
if (usageMapIter == bundleInstanceUsageMap_.end()) {
std::bitset<MAX_INSTANCE_COUNT> bitset;
bitset.reset();
bitset[instanceId] = 1;
bundleInstanceUsageMap_.emplace(bundleName, bitset);
} else {
usageMapIter->second[instanceId] = 1;
}
std::ostringstream oss;
for (auto id : bundleInstanceIdListMap_.find(bundleName)->second) {
oss << id << ",";
}
TLOGI(WmsLogTag::WMS_LIFE, "bundleName:%{public}s instanceId:%{public}d idList:%{public}s",
bundleName.c_str(), instanceId, oss.str().c_str());
}
void MultiInstanceManager::RemoveInstanceId(const std::string& bundleName, uint32_t instanceId)
{
std::unique_lock<std::shared_mutex> lock(mutex_);
auto iter = bundleInstanceIdListMap_.find(bundleName);
if (iter == bundleInstanceIdListMap_.end()) {
TLOGE(WmsLogTag::WMS_LIFE, "instanceIdList not found, bundleName:%{public}s instanceId:%{public}d",
bundleName.c_str(), instanceId);
return;
}
auto& instanceIdList = iter->second;
for (auto it = instanceIdList.begin(); it != instanceIdList.end(); ++it) {
if (*it == instanceId) {
instanceIdList.erase(it);
break;
}
}
auto usageMapIter = bundleInstanceUsageMap_.find(bundleName);
if (usageMapIter == bundleInstanceUsageMap_.end()) {
TLOGE(WmsLogTag::WMS_LIFE, "instanceUsage not found, bundleName:%{public}s instanceId:%{public}d",
bundleName.c_str(), instanceId);
return;
}
usageMapIter->second[instanceId] = 0;
std::ostringstream oss;
for (auto id : bundleInstanceIdListMap_.find(bundleName)->second) {
oss << id << ",";
}
TLOGI(WmsLogTag::WMS_LIFE, "bundleName:%{public}s instanceId:%{public}d idList:%{public}s",
bundleName.c_str(), instanceId, oss.str().c_str());
}
bool MultiInstanceManager::ConvertInstanceKeyToInstanceId(const std::string& instanceKey, uint32_t& instanceId) const
{
if (instanceKey.empty() || instanceKey.find(APP_INSTANCE_KEY_PREFIX) == -1ul) {
return false;
}
auto prefixSize = APP_INSTANCE_KEY_PREFIX.size();
const auto& instanceIdStr = instanceKey.substr(prefixSize, instanceKey.size() - prefixSize);
if (!WindowHelper::IsNumber(instanceIdStr)) {
return false;
}
auto instanceIdNum = std::stoi(instanceIdStr);
if (instanceIdNum < 0) {
return false;
}
instanceId = static_cast<uint32_t>(instanceIdNum);
return true;
}
AppExecFwk::ApplicationInfo MultiInstanceManager::GetApplicationInfo(const std::string& bundleName) const
{
std::shared_lock<std::shared_mutex> lock(appInfoMutex_);
AppExecFwk::ApplicationInfo applicationInfo;
if (appInfoMap_.count(bundleName)) {
applicationInfo = appInfoMap_.at(bundleName);
}
return applicationInfo;
}
}