* Copyright (c) 2023-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 "picture_in_picture_manager.h"
#include <algorithm>
#include <functional>
#include "navigation_controller.h"
#include "parameters.h"
#include "picture_in_picture_controller.h"
#include "window_adapter.h"
#include "window_manager_hilog.h"
#include "window_scene_session_impl.h"
#include "window_session_impl.h"
namespace OHOS {
namespace Rosen {
namespace {
const std::string ACTION_CLOSE = "close";
const std::string ACTION_REQUEST_CLOSE = "request_close";
const std::string ACTION_PANEL_CLOSE = "panel_close";
const std::string ACTION_DUMPSTER_CLOSE = "dumpster_close";
const std::string ACTION_PRE_RESTORE = "pre_restore";
const std::string ACTION_RESTORE = "restore";
const std::string ACTION_DESTROY = "destroy";
const std::string ACTION_PREPARE_SOURCE = "prepare_source";
const std::string ACTION_LOCATE_SOURCE = "locate_source";
const std::string ACTION_BACKGROUND_AUTO_START = "background_auto_start";
const std::map<std::string, std::function<void(uint32_t)>> PIP_ACTION_MAP {
{ACTION_CLOSE, PictureInPictureManager::DoActionClose},
{ACTION_REQUEST_CLOSE, PictureInPictureManager::DoActionCloseByRequest},
{ACTION_PANEL_CLOSE, PictureInPictureManager::DoActionCloseByPanel},
{ACTION_DUMPSTER_CLOSE, PictureInPictureManager::DoActionCloseByDumpster},
{ACTION_PRE_RESTORE, PictureInPictureManager::DoPreRestore},
{ACTION_RESTORE, PictureInPictureManager::DoRestore},
{ACTION_PREPARE_SOURCE, PictureInPictureManager::DoPrepareSource},
{ACTION_LOCATE_SOURCE, PictureInPictureManager::DoLocateSource},
{ACTION_DESTROY, PictureInPictureManager::DoDestroy},
{ACTION_BACKGROUND_AUTO_START, PictureInPictureManager::AutoStartPipWindow},
};
bool ShouldAutoStartDirectly(const sptr<PictureInPictureControllerBase>& pipController)
{
if (pipController == nullptr) {
return false;
}
return pipController->GetPiPNavigationId().empty() || pipController->IsTypeNodeEnabled();
}
NavigationController* GetNavControllerForAutoStart(const sptr<WindowSessionImpl>& mainWindow,
const sptr<PictureInPictureControllerBase>& pipController)
{
if (mainWindow == nullptr || pipController == nullptr) {
return nullptr;
}
return NavigationController::GetNavigationController(mainWindow->GetUIContent(),
pipController->GetPiPNavigationId());
}
}
std::map<int32_t, wptr<PictureInPictureControllerBase>> PictureInPictureManager::autoStartControllerMap_ = {};
std::map<uint32_t, std::vector<wptr<PictureInPictureControllerBase>>>
PictureInPictureManager::mainWindowToAutoStartControllersMap_ = {};
std::map<uint32_t, sptr<PictureInPictureControllerBase>> PictureInPictureManager::windowToControllerMap_ = {};
std::mutex PictureInPictureManager::controllerMapMutex_;
std::mutex PictureInPictureManager::AutoStartControllerMapMutex_;
std::shared_ptr<NativeReference> PictureInPictureManager::innerCallbackRef_ = nullptr;
PiPMultiConfig PictureInPictureManager::pipMultiConfig_ = GetDefaultPiPMultiConfig();
PictureInPictureManager::PictureInPictureManager()
{
}
PictureInPictureManager::~PictureInPictureManager()
{
}
bool PictureInPictureManager::IsSupportPiP()
{
bool isSupportPiPFlag = false;
SingletonContainer::Get<WindowAdapter>().GetIsPipEnabled(isSupportPiPFlag);
return isSupportPiPFlag;
}
bool PictureInPictureManager::ShouldAbortPipStart()
{
std::lock_guard<std::mutex> lock(controllerMapMutex_);
for (auto& pair : windowToControllerMap_) {
auto& controller = pair.second;
if (controller != nullptr && controller->GetControllerState() == PiPWindowState::STATE_STARTING) {
return true;
}
}
return false;
}
void PictureInPictureManager::PutPipControllerInfo(uint32_t windowId,
sptr<PictureInPictureControllerBase> pipController)
{
TLOGI(WmsLogTag::WMS_PIP, "windowId %{public}u", windowId);
std::lock_guard<std::mutex> lock(controllerMapMutex_);
auto iter = windowToControllerMap_.find(windowId);
if (iter != windowToControllerMap_.end()) {
UpdatePipGroupCountLocked(iter->second, false);
iter->second = pipController;
} else {
windowToControllerMap_.insert(std::make_pair(windowId, pipController));
}
UpdatePipGroupCountLocked(pipController, true);
}
void PictureInPictureManager::RemovePipControllerInfo(uint32_t windowId)
{
TLOGI(WmsLogTag::WMS_PIP, "windowId %{public}u", windowId);
std::lock_guard<std::mutex> lock(controllerMapMutex_);
auto iter = windowToControllerMap_.find(windowId);
if (iter != windowToControllerMap_.end()) {
UpdatePipGroupCountLocked(iter->second, false);
windowToControllerMap_.erase(iter);
}
}
sptr<PictureInPictureControllerBase> PictureInPictureManager::GetPipControllerInfo(uint32_t windowId)
{
std::lock_guard<std::mutex> lock(controllerMapMutex_);
if (windowToControllerMap_.empty() || windowToControllerMap_.find(windowId) == windowToControllerMap_.end()) {
TLOGE(WmsLogTag::WMS_PIP, "error, windowId: %{public}u not registered!", windowId);
return nullptr;
}
TLOGI(WmsLogTag::WMS_PIP, "windowId: %{public}u", windowId);
return windowToControllerMap_[windowId];
}
bool PictureInPictureManager::HasActiveController()
{
std::lock_guard<std::mutex> lock(controllerMapMutex_);
for (auto& pair : windowToControllerMap_) {
if (IsControllerStateActive(pair.second->GetControllerState())) {
return true;
}
}
return false;
}
bool PictureInPictureManager::IsActiveController(wptr<PictureInPictureControllerBase> pipController)
{
std::lock_guard<std::mutex> lock(controllerMapMutex_);
auto promoted = pipController.promote();
if (promoted == nullptr) {
return false;
}
for (auto& pair : windowToControllerMap_) {
if (pair.second == promoted) {
return IsControllerStateActive(pair.second->GetControllerState());
}
}
return false;
}
bool PictureInPictureManager::IsControllerStateActive(PiPWindowState state)
{
return state == PiPWindowState::STATE_STARTED ||
state == PiPWindowState::STATE_STARTING ||
state == PiPWindowState::STATE_RESTORING;
}
void PictureInPictureManager::AttachAutoStartController(int32_t handleId,
wptr<PictureInPictureControllerBase> pipController)
{
TLOGI(WmsLogTag::WMS_PIP, "handleId: %{public}d", handleId);
if (pipController == nullptr) {
return;
}
auto controller = pipController.promote();
if (controller == nullptr) {
return;
}
EvictOldestControllerIfNeeded(controller);
std::lock_guard<std::mutex> lock(AutoStartControllerMapMutex_);
autoStartControllerMap_[handleId] = pipController;
auto it = mainWindowToAutoStartControllersMap_.find(controller->GetMainWindowId());
if (it != mainWindowToAutoStartControllersMap_.end()) {
for (const auto& wptr : it->second) {
if (wptr.GetRefPtr() == pipController.GetRefPtr()) {
TLOGI(WmsLogTag::WMS_PIP, "controller already registered for mainWindow: %{public}u",
controller->GetMainWindowId());
return;
}
}
}
mainWindowToAutoStartControllersMap_[controller->GetMainWindowId()].push_back(pipController);
}
void PictureInPictureManager::DetachAutoStartController(int32_t handleId,
wptr<PictureInPictureControllerBase> pipController)
{
std::lock_guard<std::mutex> lock(AutoStartControllerMapMutex_);
TLOGI(WmsLogTag::WMS_PIP, "handleId: %{public}d", handleId);
auto mit = autoStartControllerMap_.find(handleId);
if (mit == autoStartControllerMap_.end() || mit->second != pipController) {
TLOGW(WmsLogTag::WMS_PIP, "no same pip controller");
return;
}
autoStartControllerMap_.erase(mit);
TLOGI(WmsLogTag::WMS_PIP, "autoStartControllerMap_.size: %{public}u", autoStartControllerMap_.size());
auto controller = pipController.promote();
if (controller != nullptr) {
uint32_t mainWindowId = controller->GetMainWindowId();
auto it = mainWindowToAutoStartControllersMap_.find(mainWindowId);
if (it != mainWindowToAutoStartControllersMap_.end()) {
auto& controllers = it->second;
controllers.erase(std::remove_if(controllers.begin(), controllers.end(),
[&pipController](const wptr<PictureInPictureControllerBase>& w) {
return w.GetRefPtr() == pipController.GetRefPtr();
}),
controllers.end());
if (controllers.empty()) {
mainWindowToAutoStartControllersMap_.erase(it);
}
}
}
}
void PictureInPictureManager::EvictOldestControllerIfNeeded(const sptr<PictureInPictureControllerBase>& controller)
{
PiPGroupConfig group;
auto type = static_cast<PiPTemplateType>(controller->GetPipTemplate());
if (!pipMultiConfig_.FindGroupConfig(type, group)) {
return;
}
uint32_t countInGroup = 0;
sptr<PictureInPictureControllerBase> oldest = nullptr;
for (const auto& [mainWindowId, controllers] : mainWindowToAutoStartControllersMap_) {
for (const auto& wptrExisting : controllers) {
auto existing = wptrExisting.promote();
if (existing == nullptr) {
continue;
}
PiPGroupConfig existingGroup;
auto existingType = static_cast<PiPTemplateType>(existing->GetPipTemplate());
if (!pipMultiConfig_.FindGroupConfig(existingType, existingGroup) ||
existingGroup.groupId != group.groupId) {
continue;
}
++countInGroup;
if (oldest == nullptr || existing->GetCreateTimestamp() < oldest->GetCreateTimestamp()) {
oldest = existing;
}
}
}
if (group.maxCount > 0 && countInGroup >= group.maxCount && oldest != nullptr) {
DetachAutoStartController(oldest->GetHandleId(), oldest);
}
}
bool PictureInPictureManager::IsAutoStartControllerMapEmpty()
{
std::lock_guard<std::mutex> lock(AutoStartControllerMapMutex_);
return autoStartControllerMap_.empty();
}
bool PictureInPictureManager::IsAttachedToSameWindow(uint32_t windowId)
{
TLOGI(WmsLogTag::WMS_PIP, "windowId: %{public}u", windowId);
std::lock_guard<std::mutex> lock(controllerMapMutex_);
for (auto& pair : windowToControllerMap_) {
auto& controller = pair.second;
if (controller != nullptr && controller->GetMainWindowId() == windowId) {
return true;
}
}
return false;
}
sptr<Window> PictureInPictureManager::GetCurrentWindowByMainWindowId(uint32_t windowId)
{
std::lock_guard<std::mutex> lock(controllerMapMutex_);
for (auto& pair : windowToControllerMap_) {
auto& controller = pair.second;
if (controller != nullptr && controller->GetMainWindowId() == windowId) {
return controller->GetPipWindow();
}
}
return nullptr;
}
sptr<Window> PictureInPictureManager::GetSameGroupWindowByMainWindowId(uint32_t windowId,
PiPTemplateType type)
{
std::lock_guard<std::mutex> lock(controllerMapMutex_);
PiPGroupConfig group;
if (!pipMultiConfig_.FindGroupConfig(type, group)) {
return nullptr;
}
auto firstController = static_cast<sptr<PictureInPictureControllerBase>>(nullptr);
for (auto& pair : windowToControllerMap_) {
auto& controller = pair.second;
auto type = static_cast<PiPTemplateType>(controller->GetPipTemplate());
if (controller != nullptr && controller->GetMainWindowId() == windowId &&
std::find(group.types.begin(), group.types.end(), type) != group.types.end()) {
if (firstController == nullptr || firstController->GetStartTimestamp() > controller->GetStartTimestamp()) {
firstController = controller;
}
}
}
if (firstController != nullptr) {
return firstController->GetPipWindow();
}
return nullptr;
}
sptr<Window> PictureInPictureManager::GetCurrentWindow()
{
std::lock_guard<std::mutex> lock(controllerMapMutex_);
if (!windowToControllerMap_.empty() && windowToControllerMap_.begin()->second != nullptr) {
return windowToControllerMap_.begin()->second->GetPipWindow();
}
return nullptr;
}
void PictureInPictureManager::DoPreRestore(uint32_t windowId)
{
TLOGI(WmsLogTag::WMS_PIP, "windowId: %{public}u", windowId);
if (auto controller = GetPipControllerInfo(windowId)) {
controller->PreRestorePictureInPicture();
}
}
void PictureInPictureManager::DoRestore(uint32_t windowId)
{
TLOGI(WmsLogTag::WMS_PIP, "windowId: %{public}u", windowId);
if (auto controller = GetPipControllerInfo(windowId)) {
controller->RestorePictureInPictureWindow();
}
}
void PictureInPictureManager::DoPrepareSource(uint32_t windowId)
{
TLOGI(WmsLogTag::WMS_PIP, "windowId: %{public}u", windowId);
if (auto controller = GetPipControllerInfo(windowId)) {
controller->PrepareSource();
}
}
void PictureInPictureManager::DoLocateSource(uint32_t windowId)
{
TLOGI(WmsLogTag::WMS_PIP, "windowId: %{public}u", windowId);
if (auto controller = GetPipControllerInfo(windowId)) {
controller->LocateSource();
}
}
void PictureInPictureManager::DoCloseByMainWindowId(uint32_t mainWindowId)
{
TLOGI(WmsLogTag::WMS_PIP, "mainWindowId: %{public}u", mainWindowId);
std::vector<uint32_t> idList;
{
std::lock_guard<std::mutex> lock(controllerMapMutex_);
for (const auto& controller : windowToControllerMap_) {
if (controller.second != nullptr && controller.second->GetMainWindowId() == mainWindowId) {
idList.push_back(controller.first);
}
}
}
for (const auto& id : idList) {
DoClose(id, true, true);
}
TLOGI(WmsLogTag::WMS_PIP, "done");
}
void PictureInPictureManager::DoClose(uint32_t windowId, bool destroyWindow, bool byPriority)
{
TLOGI(WmsLogTag::WMS_PIP, "windowId: %{public}u, destroyWindow:%{public}d, byPriority:%{public}d",
windowId, destroyWindow, byPriority);
if (auto controller = GetPipControllerInfo(windowId)) {
StopPipType currentStopType = StopPipType::NULL_STOP;
if (!byPriority) {
currentStopType = StopPipType::USER_STOP;
} else {
currentStopType = StopPipType::OTHER_PACKAGE_STOP;
}
controller->SetStateChangeReason(PiPStateChangeReason::OTHER);
controller->StopPictureInPicture(destroyWindow, currentStopType, !byPriority);
}
}
void PictureInPictureManager::DoCloseWithReason(uint32_t windowId, bool destroyWindow, bool byPriority,
PiPStateChangeReason reason)
{
TLOGI(WmsLogTag::WMS_PIP, "windowId: %{public}u, destroyWindow:%{public}d, byPriority:%{public}d",
windowId, destroyWindow, byPriority);
if (auto controller = GetPipControllerInfo(windowId)) {
StopPipType currentStopType = StopPipType::NULL_STOP;
if (!byPriority) {
currentStopType = StopPipType::USER_STOP;
} else {
currentStopType = StopPipType::OTHER_PACKAGE_STOP;
}
controller->SetStateChangeReason(reason);
controller->StopPictureInPicture(destroyWindow, currentStopType, !byPriority);
}
}
void PictureInPictureManager::DoActionClose(uint32_t windowId)
{
TLOGI(WmsLogTag::WMS_PIP, "windowId: %{public}u", windowId);
DoClose(windowId, true, false);
}
void PictureInPictureManager::DoActionCloseByRequest(uint32_t windowId)
{
TLOGI(WmsLogTag::WMS_PIP, "windowId: %{public}u", windowId);
DoCloseWithReason(windowId, true, false, PiPStateChangeReason::REQUEST_DELETE);
}
void PictureInPictureManager::DoActionCloseByPanel(uint32_t windowId)
{
TLOGI(WmsLogTag::WMS_PIP, "windowId: %{public}u", windowId);
DoCloseWithReason(windowId, true, false, PiPStateChangeReason::PANEL_ACTION_DELETE);
}
void PictureInPictureManager::DoActionCloseByDumpster(uint32_t windowId)
{
TLOGI(WmsLogTag::WMS_PIP, "windowId: %{public}u", windowId);
DoCloseWithReason(windowId, true, false, PiPStateChangeReason::DRAG_DELETE);
}
void PictureInPictureManager::DoDestroy(uint32_t windowId)
{
TLOGI(WmsLogTag::WMS_PIP, "windowId: %{public}u", windowId);
if (auto controller = GetPipControllerInfo(windowId)) {
controller->DestroyPictureInPictureWindow();
}
}
void PictureInPictureManager::DoActionEvent(uint32_t windowId, const std::string& actionName, int32_t status)
{
TLOGI(WmsLogTag::WMS_PIP, "windowId: %{public}u, actionName: %{public}s", windowId, actionName.c_str());
auto func = PIP_ACTION_MAP.find(actionName);
if (func == PIP_ACTION_MAP.end()) {
TLOGI(WmsLogTag::WMS_PIP, "through pass");
if (auto controller = GetPipControllerInfo(windowId)) {
controller->DoActionEvent(actionName, status);
}
return;
}
func->second(windowId);
}
void PictureInPictureManager::DoControlEvent(uint32_t windowId, PiPControlType controlType, PiPControlStatus status)
{
TLOGI(WmsLogTag::WMS_PIP, "windowId: %{public}u, controlType:%{public}u, status:%{public}d",
windowId, controlType, status);
if (auto controller = GetPipControllerInfo(windowId)) {
controller->DoControlEvent(controlType, status);
}
}
void PictureInPictureManager::AutoStartPipWindow(uint32_t windowId)
{
TLOGI(WmsLogTag::WMS_PIP, "start, mainWindowId: %{public}u", windowId);
auto it = mainWindowToAutoStartControllersMap_.find(windowId);
if (it == mainWindowToAutoStartControllersMap_.end() || it->second.empty()) {
TLOGE(WmsLogTag::WMS_PIP, "no autoStartController registered for mainWindow: %{public}u", windowId);
return;
}
int32_t count = 0;
for (const auto& wptrPipController : it->second) {
TLOGI(WmsLogTag::WMS_PIP, "count: %{public}d, mainWindowId: %{public}u", ++count, windowId);
auto pipController = wptrPipController.promote();
if (pipController == nullptr) {
TLOGE(WmsLogTag::WMS_PIP, "autoStartController is nullptr, mainWindowId:%{public}u", windowId);
continue;
}
pipController->SetStateChangeReason(PiPStateChangeReason::AUTO_START);
if (ShouldAutoStartDirectly(pipController)) {
TLOGI(WmsLogTag::WMS_PIP, "No use navigation for auto start");
pipController->StartPictureInPicture(StartPipType::AUTO_START);
continue;
}
sptr<WindowSessionImpl> mainWindow =
WindowSceneSessionImpl::GetMainWindowWithId(pipController->GetMainWindowId());
auto navController = GetNavControllerForAutoStart(mainWindow, pipController);
if (!navController) {
TLOGE(WmsLogTag::WMS_PIP, "navController is nullptr");
continue;
}
if (!navController->IsNavDestinationInTopStack()) {
TLOGE(WmsLogTag::WMS_PIP, "Top is not navDestination");
continue;
}
int32_t handleId = navController->GetTopHandle();
auto mapIt = autoStartControllerMap_.find(handleId);
if (mapIt == autoStartControllerMap_.end()) {
TLOGE(WmsLogTag::WMS_PIP, "GetNavController info error, handleId %{public}d not registered", handleId);
continue;
}
auto handlePipController = mapIt->second.promote();
if (handlePipController == nullptr) {
TLOGE(WmsLogTag::WMS_PIP, "handlePipController is nullptr, handleId %{public}d not registered", handleId);
continue;
}
handlePipController->StartPictureInPicture(StartPipType::AUTO_START);
}
TLOGI(WmsLogTag::WMS_PIP, "finish, mainWindowId: %{public}u", windowId);
}
void PictureInPictureManager::PipSizeChange(uint32_t windowId, double width, double height, double scale)
{
TLOGI(WmsLogTag::WMS_PIP, "windowId: %{public}u", windowId);
if (auto controller = GetPipControllerInfo(windowId)) {
controller->PipSizeChange(width, height, scale);
}
}
void PictureInPictureManager::DoActiveStatusChangeEvent(uint32_t windowId, bool status)
{
TLOGI(WmsLogTag::WMS_PIP, "windowId: %{public}u, status: %{public}d", windowId, status);
if (auto controller = GetPipControllerInfo(windowId)) {
controller->ActiveStatusChange(status);
}
}
bool PictureInPictureManager::GetPipEnabled()
{
bool isPipEnabled = false;
SingletonContainer::Get<WindowAdapter>().GetIsPipEnabled(isPipEnabled);
return isPipEnabled;
}
void PictureInPictureManager::SetMultiPipConfig(const PiPMultiConfig& config)
{
std::lock_guard<std::mutex> lock(controllerMapMutex_);
pipMultiConfig_ = config;
pipMultiConfig_.ResetGroupCounts();
for (const auto& pair : windowToControllerMap_) {
UpdatePipGroupCountLocked(pair.second, true);
}
}
const PiPMultiConfig& PictureInPictureManager::GetMultiPipConfig()
{
return pipMultiConfig_;
}
bool PictureInPictureManager::IsTemplateTypeSupported(PiPTemplateType type)
{
std::lock_guard<std::mutex> lock(controllerMapMutex_);
PiPGroupConfig group;
return pipMultiConfig_.FindGroupConfig(type, group);
}
bool PictureInPictureManager::IsPipGroupLimitReached(PiPTemplateType type)
{
std::lock_guard<std::mutex> lock(controllerMapMutex_);
PiPGroupConfig group;
if (!pipMultiConfig_.FindGroupConfig(type, group)) {
TLOGE(WmsLogTag::WMS_PIP, "no group config for type %{public}u", static_cast<uint32_t>(type));
return false;
}
return pipMultiConfig_.GetGroupCount(group.groupId) >= group.maxCount;
}
void PictureInPictureManager::UpdatePipGroupCountLocked(const sptr<PictureInPictureControllerBase>& controller,
bool increase)
{
if (controller == nullptr) {
return;
}
auto type = static_cast<PiPTemplateType>(controller->GetPipTemplate());
PiPGroupConfig group;
if (!pipMultiConfig_.FindGroupConfig(type, group)) {
return;
}
if (increase) {
pipMultiConfig_.IncreaseGroupCount(group.groupId);
} else {
pipMultiConfig_.DecreaseGroupCount(group.groupId);
}
}
}
}