* 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 "notification_bar.h"
#include <cstddef>
#include <cstdint>
#include <string>
#include <dlfcn.h>
#include "cxx.h"
#include "image_source.h"
#include "locale_config.h"
#include "log.h"
#include "notification.h"
#include "notification_content.h"
#include "notification_local_live_view_button.h"
#include "notification_local_live_view_content.h"
#include "resource_manager.h"
#include "service/notification_bar/mod.rs.h"
#include "task/config.rs.h"
#include "want_agent_helper.h"
#include "sys_event.h"
#include "system_ability_definition.h"
#include "iservice_registry.h"
#include "app_mgr_interface.h"
#include "init_param.h"
#include "parameters.h"
namespace OHOS::Request {
using namespace Global;
std::mutex g_requestIntlUtilSoMtx;
static constexpr int32_t REQUEST_SERVICE_ID = 3815;
static constexpr int32_t REQUEST_STYLE_SIMPLE = 8;
static const std::string CLOSE_ICON_PATH = "/etc/request/xmark.svg";
static const std::string CLOSE_ICON_PATH_DARK = "/etc/request/xmark_dark.svg";
constexpr const char* SYSTEM_COLORMODE = "persist.ace.darkmode";
const std::vector<std::string> RESOURCE_STRING_KEYS = {
"request_agent_download_file",
"request_agent_download_success",
"request_agent_download_fail",
"request_agent_upload_file",
"request_agent_upload_success",
"request_agent_upload_fail",
"request_agent_task_count",
"request_agent_download_complete"
};
struct RequestSystemResourceStringInfo {
std::unordered_map<std::string, std::string> stringMap;
std::string curSystemLanguage;
std::mutex mtx;
};
RequestSystemResourceStringInfo g_resourceStringInfo;
bool IsSystemLanguageConsistent(const std::string &curLanguage)
{
return g_resourceStringInfo.curSystemLanguage == curLanguage;
}
int UpdateSystemResourceStringMap(std::string &curLanguage)
{
auto resourceMgr = Resource::GetSystemResourceManagerNoSandBox();
if (resourceMgr == nullptr) {
REQUEST_HILOGE("GetSystemResourceManagerNoSandBox failed");
return -1;
}
std::unique_ptr<Resource::ResConfig> config(Resource::CreateResConfig());
if (config == nullptr) {
REQUEST_HILOGE("Create ResConfig failed");
return -1;
}
UErrorCode status = U_ZERO_ERROR;
icu::Locale locale = icu::Locale::forLanguageTag(curLanguage, status);
config->SetLocaleInfo(locale);
resourceMgr->UpdateResConfig(*config);
g_resourceStringInfo.curSystemLanguage = curLanguage.c_str();
std::string outValue;
for (size_t i = 0; i < RESOURCE_STRING_KEYS.size(); ++i) {
auto ret = resourceMgr->GetStringByName(RESOURCE_STRING_KEYS[i].c_str(), outValue);
if (ret != Resource::RState::SUCCESS) {
REQUEST_HILOGE("Get system resource string %{public}s failed: %{public}d",
RESOURCE_STRING_KEYS[i].c_str(), ret);
g_resourceStringInfo.curSystemLanguage.clear();
}
g_resourceStringInfo.stringMap[RESOURCE_STRING_KEYS[i]] = outValue;
}
Resource::ReleaseSystemResourceManager();
return 0;
}
void DynamicGetSystemLanguage(char* buffer, size_t bufferSize)
{
std::lock_guard<std::mutex> lock(g_requestIntlUtilSoMtx);
const char *libApiLanguageTransferPath = "/system/lib64/libdownload_language_transfer.z.so";
using GetSystemLanguageFunc = void (*)(char *buffer, size_t bufferSize);
static GetSystemLanguageFunc getSystemLanguageFunc = nullptr;
static bool initialized = false;
if (!initialized) {
void *handle = dlopen(libApiLanguageTransferPath, RTLD_NOW);
if (handle == nullptr) {
const char *err = dlerror();
REQUEST_HILOGE("libdownload_language_transfer.z.so dlopen failed: %{public}s", err ? err : "unknown");
return;
}
getSystemLanguageFunc = reinterpret_cast<GetSystemLanguageFunc>(
dlsym(handle, "GetSystemLanguageByIntl"));
if (getSystemLanguageFunc == nullptr) {
const char *err = dlerror();
REQUEST_HILOGE("libdownload_language_transfer.z.so dlsym GetSystemLanguageByIntlwl failed: %{public}s",
err ? err : "unknown");
dlclose(handle);
return;
}
initialized = true;
}
if (getSystemLanguageFunc == nullptr) {
return;
}
getSystemLanguageFunc(buffer, bufferSize);
}
rust::string GetSystemResourceString(const rust::str name)
{
std::lock_guard<std::mutex> lock(g_resourceStringInfo.mtx);
char curLanguage[256] = "zh-Hans";
DynamicGetSystemLanguage(curLanguage, sizeof(curLanguage));
if (IsSystemLanguageConsistent(curLanguage) != true) {
std::string strCurLanguage(curLanguage);
int ret = UpdateSystemResourceStringMap(strCurLanguage);
if (ret != 0) {
return "";
}
}
return rust::string(g_resourceStringInfo.stringMap[name.data()]);
}
rust::string GetSystemLanguage()
{
char curLanguage[256] = "zh-Hans";
DynamicGetSystemLanguage(curLanguage, sizeof(curLanguage));
return rust::string(curLanguage);
}
std::shared_ptr<Media::PixelMap> CreatePixelMap()
{
static std::shared_ptr<Media::PixelMap> cachedPixelMap;
static std::mutex updateMutex;
static std::string cachedColorMode;
std::unique_lock<std::mutex> lock(updateMutex);
auto currentColorMode = GetCurrentSystemColorMode();
if (cachedPixelMap && cachedColorMode == currentColorMode) {
return cachedPixelMap;
}
auto newPixelMap = CreatePixelMapByColorMode(currentColorMode);
if (newPixelMap) {
cachedPixelMap = newPixelMap;
cachedColorMode = currentColorMode;
}
return cachedPixelMap;
}
std::string GetCurrentSystemColorMode()
{
auto systemAbilityManager = SystemAbilityManagerClient::GetInstance().GetSystemAbilityManager();
if (systemAbilityManager == nullptr) {
REQUEST_HILOGE("get SystemAbilityManager failed.");
SysEventLog::SendSysEventLog(FAULT_EVENT, SAMGR_FAULT_00, "get SAM failed");
return "";
}
auto systemAbility = systemAbilityManager->GetSystemAbility(APP_MGR_SERVICE_ID);
if (systemAbility == nullptr) {
REQUEST_HILOGE("get SystemAbility failed.");
return "";
}
AppExecFwk::Configuration config;
sptr<AppExecFwk::IAppMgr> appObject = iface_cast<AppExecFwk::IAppMgr>(systemAbility);
if (appObject == nullptr) {
REQUEST_HILOGE("get appObject failed.");
return "";
}
int ret = appObject->GetConfiguration(config);
if (ret != ERR_OK) {
REQUEST_HILOGE("get configuration failed, ret = %{public}d", ret);
}
return config.GetItem(OHOS::AAFwk::GlobalConfigurationKey::SYSTEM_COLORMODE);
}
std::shared_ptr<Media::PixelMap> CreatePixelMapByColorMode(const std::string& colorMode)
{
const char* iconPath = GetIconPathByColorMode(colorMode);
if (iconPath == nullptr) {
REQUEST_HILOGE("iconPath is null for color mode: %{public}s", colorMode.c_str());
return nullptr;
}
Media::SourceOptions opts;
uint32_t errorCode = 0;
auto source = Media::ImageSource::CreateImageSource(iconPath, opts, errorCode);
if (source == nullptr) {
REQUEST_HILOGE("create image source failed for path: %{public}s", iconPath);
return nullptr;
}
Media::DecodeOptions decodeOpts;
std::unique_ptr<Media::PixelMap> pixel = source->CreatePixelMap(decodeOpts, errorCode);
if (pixel == nullptr) {
REQUEST_HILOGE("create pixel map failed, error: %{public}u", errorCode);
return nullptr;
}
return std::move(pixel);
}
const char* GetIconPathByColorMode(const std::string& colorMode)
{
return (colorMode == AppExecFwk::ConfigurationInner::COLOR_MODE_DARK)
? CLOSE_ICON_PATH_DARK.c_str()
: CLOSE_ICON_PATH.c_str();
}
void BasicRequestSettings(Notification::NotificationRequest &request, int32_t uid)
{
request.SetCreatorUid(REQUEST_SERVICE_ID);
request.SetOwnerUid(uid);
request.SetIsAgentNotification(true);
}
std::shared_ptr<OHOS::Notification::NotificationContent> NormalContent(const NotifyContent &content)
{
auto normalContent = std::make_shared<Notification::NotificationNormalContent>();
normalContent->SetTitle(std::string(content.title));
normalContent->SetText(std::string(content.text));
return std::make_shared<Notification::NotificationContent>(normalContent);
}
std::shared_ptr<OHOS::Notification::NotificationContent> LiveViewContent(const NotifyContent &content)
{
auto liveViewContent = std::make_shared<Notification::NotificationLocalLiveViewContent>();
liveViewContent->SetContentType(static_cast<int32_t>(Notification::NotificationContent::Type::LOCAL_LIVE_VIEW));
liveViewContent->SetType(REQUEST_STYLE_SIMPLE);
liveViewContent->SetText(std::string(content.text));
liveViewContent->SetTitle(std::string(content.title));
if (content.x_mark || content.progress_circle.open) {
liveViewContent->addFlag(Notification::NotificationLocalLiveViewContent::LiveViewContentInner::BUTTON);
}
if (content.x_mark) {
auto button = liveViewContent->GetButton();
auto icon = CreatePixelMap();
if (icon != nullptr) {
button.addSingleButtonName("cancel");
button.addSingleButtonIcon(icon);
liveViewContent->SetButton(button);
}
}
if (content.progress_circle.open) {
liveViewContent->addFlag(Notification::NotificationLocalLiveViewContent::LiveViewContentInner::PROGRESS);
Notification::NotificationProgress progress;
progress.SetIsPercentage(true);
progress.SetCurrentValue(content.progress_circle.current);
progress.SetMaxValue(content.progress_circle.total);
liveViewContent->SetProgress(progress);
}
return std::make_shared<Notification::NotificationContent>(liveViewContent);
}
rust::string GetWantAgentBundle(rust::str wantAgent)
{
auto agent = AbilityRuntime::WantAgent::WantAgentHelper::FromString(std::string(wantAgent));
if (agent == nullptr) {
return rust::string("");
}
auto want = AbilityRuntime::WantAgent::WantAgentHelper::GetWant(agent);
if (want == nullptr) {
return rust::string("");
}
return rust::string(want->GetElement().GetBundleName());
}
int PublishNotification(const NotifyContent &content)
{
Notification::NotificationRequest request(content.request_id);
BasicRequestSettings(request, content.uid);
request.SetInProgress(content.progress_circle.open);
if (content.live_view) {
request.SetSlotType(Notification::NotificationConstant::SlotType::LIVE_VIEW);
request.SetContent(LiveViewContent(content));
} else {
request.SetContent(NormalContent(content));
}
if (!content.want_agent.empty()) {
request.SetWantAgent(
OHOS::AbilityRuntime::WantAgent::WantAgentHelper::FromString(std::string(content.want_agent)));
}
return Notification::NotificationHelper::PublishNotification(request);
}
NotificationSubscriber::NotificationSubscriber(rust::Box<TaskManagerWrapper> taskManager)
: _taskManager(std::move(taskManager)){};
void NotificationSubscriber::OnConnected()
{
RegisterSystemParameterListener();
};
void NotificationSubscriber::OnDisconnected(){};
void NotificationSubscriber::OnDied(){};
void NotificationSubscriber::OnResponse(
int32_t notificationId, sptr<Notification::NotificationButtonOption> buttonOption)
{
if (buttonOption == nullptr) {
REQUEST_HILOGE("buttonOption empty");
return;
}
if (buttonOption->GetButtonName() == "stop") {
this->_taskManager->pause_task(static_cast<uint32_t>(notificationId));
} else if (buttonOption->GetButtonName() == "start") {
this->_taskManager->resume_task(static_cast<uint32_t>(notificationId));
} else if (buttonOption->GetButtonName() == "cancel") {
this->_taskManager->stop_task(static_cast<uint32_t>(notificationId));
Notification::NotificationHelper::CancelNotification(notificationId);
}
};
void NotificationSubscriber::RegisterSystemParameterListener()
{
REQUEST_HILOGI("register system parameter modify lister");
auto colorModeResult = SystemWatchParameter(SYSTEM_COLORMODE, ChangeColorModeCallback, nullptr);
if (colorModeResult != ERR_OK) {
REQUEST_HILOGE("register color mode listener fail:%{public}d", colorModeResult);
}
};
void NotificationSubscriber::ChangeColorModeCallback(const char *key, const char *value, void *context)
{
REQUEST_HILOGI("Color mode changed");
RepublishProgressNotify();
}
void SubscribeNotification(rust::Box<TaskManagerWrapper> taskManager)
{
static auto subscriber = std::make_unique<NotificationSubscriber>(std::move(taskManager));
Notification::NotificationHelper::SubscribeLocalLiveViewNotification(*subscriber);
}
void RepublishProgressNotify()
{
std::vector<sptr<Notification::NotificationRequest>> notificationRequests;
ErrCode ret = Notification::NotificationHelper::GetActiveNotifications(notificationRequests);
if (ret != ERR_OK) {
REQUEST_HILOGE("get all active notification fail!");
return;
}
for (const auto& notificationRequest : notificationRequests) {
if (notificationRequest == nullptr) {
continue;
}
int32_t notificationId = notificationRequest->GetNotificationId();
if (notificationRequest->GetCreatorUid() != REQUEST_SERVICE_ID ||
notificationRequest->GetSlotType() != Notification::NotificationConstant::SlotType::LIVE_VIEW) {
continue;
}
auto content = notificationRequest->GetContent();
if (content == nullptr) {
REQUEST_HILOGE("Notification content is null for id: %{public}d", notificationId);
continue;
}
auto const &normalContent = content->GetNotificationContent();
auto liveViewContent = std::static_pointer_cast<Notification::NotificationLocalLiveViewContent>(normalContent);
if (liveViewContent == nullptr) {
REQUEST_HILOGE("Failed to cast to LiveViewContent for id: %{public}d", notificationId);
continue;
}
auto icon = CreatePixelMap();
if (icon != nullptr) {
auto button = liveViewContent->GetButton();
if (button.GetAllButtonNames().empty() || button.GetAllButtonIcons().empty()) {
continue;
}
REQUEST_HILOGI("Re-publishing notification for id: %{public}d", notificationId);
button.ClearButtonIcons();
button.addSingleButtonName("cancel");
button.addSingleButtonIcon(icon);
liveViewContent->SetButton(button);
}
notificationRequest->SetContent(std::make_shared<Notification::NotificationContent>(liveViewContent));
Notification::NotificationHelper::PublishContinuousTaskNotification(*notificationRequest);
}
}
}