mirror of
https://github.com/openharmony/security_security_component_manager.git
synced 2026-08-24 14:33:25 -04:00
feat: customize for save button
Signed-off-by: Haryslee <lihao189@huawei.com>
This commit is contained in:
@@ -37,7 +37,7 @@ public:
|
||||
int32_t RegisterWriteToRawdata(SecCompType type, const std::string& componentInfo, SecCompRawdata& rawData);
|
||||
int32_t UpdateWriteToRawdata(int32_t scId, const std::string& componentInfo, SecCompRawdata& rawData);
|
||||
int32_t UnregisterWriteToRawdata(int32_t scId, SecCompRawdata& rawData);
|
||||
int32_t ReportWriteToRawdata(SecCompInfo& secCompInfo, SecCompRawdata& rawData);
|
||||
int32_t ReportWriteToRawdata(SecCompInfo& secCompInfo, SecCompRawdata& rawData, std::string& message);
|
||||
int32_t PreRegisterWriteToRawdata(SecCompRawdata& rawData);
|
||||
int32_t RegisterSecurityComponent(SecCompType type, const std::string& componentInfo, int32_t& scId);
|
||||
int32_t UpdateSecurityComponent(int32_t scId, const std::string& componentInfo);
|
||||
@@ -49,6 +49,7 @@ public:
|
||||
bool IsServiceExist();
|
||||
bool LoadService();
|
||||
bool IsSystemAppCalling();
|
||||
bool HasCustomPermissionForSecComp();
|
||||
|
||||
void FinishStartSASuccess(const sptr<IRemoteObject>& remoteObject);
|
||||
void FinishStartSAFail();
|
||||
|
||||
@@ -21,7 +21,7 @@ namespace OHOS {
|
||||
namespace Security {
|
||||
namespace SecurityComponent {
|
||||
namespace {
|
||||
static constexpr int32_t MAX_FUNC_ASM_SIZE = 0x280;
|
||||
static constexpr int32_t MAX_FUNC_ASM_SIZE = 0x380;
|
||||
static constexpr OHOS::HiviewDFX::HiLogLabel LABEL = {
|
||||
LOG_CORE, SECURITY_DOMAIN_SECURITY_COMPONENT, "SecCompCallerAuthorization"};
|
||||
static constexpr size_t MAX_CALLER_SIZE = 10;
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
*/
|
||||
#include "sec_comp_client.h"
|
||||
|
||||
#include "accesstoken_kit.h"
|
||||
#include "ipc_skeleton.h"
|
||||
#include "iservice_registry.h"
|
||||
#include "sec_comp_click_event_parcel.h"
|
||||
@@ -236,7 +237,7 @@ int32_t SecCompClient::UnregisterSecurityComponent(int32_t scId)
|
||||
return serviceRes;
|
||||
}
|
||||
|
||||
int32_t SecCompClient::ReportWriteToRawdata(SecCompInfo& secCompInfo, SecCompRawdata& rawData)
|
||||
int32_t SecCompClient::ReportWriteToRawdata(SecCompInfo& secCompInfo, SecCompRawdata& rawData, std::string& message)
|
||||
{
|
||||
MessageParcel dataParcel;
|
||||
|
||||
@@ -250,6 +251,11 @@ int32_t SecCompClient::ReportWriteToRawdata(SecCompInfo& secCompInfo, SecCompRaw
|
||||
return SC_SERVICE_ERROR_PARCEL_OPERATE_FAIL;
|
||||
}
|
||||
|
||||
if (!dataParcel.WriteString(message)) {
|
||||
SC_LOG_ERROR(LABEL, "Report write message failed.");
|
||||
return SC_SERVICE_ERROR_PARCEL_OPERATE_FAIL;
|
||||
}
|
||||
|
||||
sptr<SecCompClickEventParcel> parcel = new (std::nothrow) SecCompClickEventParcel();
|
||||
if (parcel == nullptr) {
|
||||
SC_LOG_ERROR(LABEL, "Report new click event parcel failed.");
|
||||
@@ -279,7 +285,7 @@ int32_t SecCompClient::ReportSecurityComponentClickEvent(SecCompInfo& secCompInf
|
||||
|
||||
std::lock_guard<std::mutex> lock(useIPCMutex_);
|
||||
SecCompRawdata rawData;
|
||||
int32_t res = ReportWriteToRawdata(secCompInfo, rawData);
|
||||
int32_t res = ReportWriteToRawdata(secCompInfo, rawData, message);
|
||||
if (res != SC_OK) {
|
||||
return res;
|
||||
}
|
||||
@@ -333,6 +339,25 @@ bool SecCompClient::IsSystemAppCalling()
|
||||
return Security::AccessToken::TokenIdKit::IsSystemAppByFullTokenID(selfToken);
|
||||
}
|
||||
|
||||
bool SecCompClient::HasCustomPermissionForSecComp()
|
||||
{
|
||||
auto proxy = GetProxy(true);
|
||||
if (proxy == nullptr) {
|
||||
SC_LOG_ERROR(LABEL, "Proxy is null");
|
||||
return false;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(useIPCMutex_);
|
||||
bool hasCustomPermission;
|
||||
int32_t res = proxy->HasCustomPermissionForSecComp(hasCustomPermission);
|
||||
if (res != SC_OK) {
|
||||
SC_LOG_ERROR(LABEL, "Get custom permission status failed, result: %{public}d.", res);
|
||||
return false;
|
||||
}
|
||||
|
||||
return hasCustomPermission;
|
||||
}
|
||||
|
||||
int32_t SecCompClient::PreRegisterWriteToRawdata(SecCompRawdata& rawData)
|
||||
{
|
||||
MessageParcel dataParcel;
|
||||
|
||||
@@ -170,6 +170,11 @@ bool SecCompKit::IsSystemAppCalling()
|
||||
{
|
||||
return SecCompClient::GetInstance().IsSystemAppCalling();
|
||||
}
|
||||
|
||||
bool SecCompKit::HasCustomPermissionForSecComp()
|
||||
{
|
||||
return SecCompClient::GetInstance().HasCustomPermissionForSecComp();
|
||||
}
|
||||
} // namespace SecurityComponent
|
||||
} // namespace Security
|
||||
} // namespace OHOS
|
||||
|
||||
@@ -241,3 +241,25 @@ HWTEST_F(SecCompKitTest, OnLoadSystemAbilitySuccess001, TestSize.Level0)
|
||||
loadCallback->OnLoadSystemAbilitySuccess(systemAbilityId, nullptr);
|
||||
loadCallback->OnLoadSystemAbilityFail(systemAbilityId);
|
||||
}
|
||||
|
||||
/**
|
||||
* @tc.name: IsSystemAppCalling001
|
||||
* @tc.desc: Test whether the app is system app.
|
||||
* @tc.type: FUNC
|
||||
* @tc.require:
|
||||
*/
|
||||
HWTEST_F(SecCompKitTest, IsSystemAppCalling001, TestSize.Level0)
|
||||
{
|
||||
EXPECT_EQ(false, SecCompKit::IsSystemAppCalling());
|
||||
}
|
||||
|
||||
/**
|
||||
* @tc.name: HasCustomPermissionForSecComp001
|
||||
* @tc.desc: Test whether the app has custom permission for security component.
|
||||
* @tc.type: FUNC
|
||||
* @tc.require:
|
||||
*/
|
||||
HWTEST_F(SecCompKitTest, HasCustomPermissionForSecComp001, TestSize.Level0)
|
||||
{
|
||||
EXPECT_EQ(false, SecCompKit::HasCustomPermissionForSecComp());
|
||||
}
|
||||
|
||||
@@ -94,6 +94,8 @@ void TestCommon::BuildLocationComponentInfo(nlohmann::json& jsonComponent)
|
||||
jsonComponent[JsonTagConstants::JSON_FOREGROUND_BLUR_RADIUS_TAG] = 0.0;
|
||||
jsonComponent[JsonTagConstants::JSON_IS_OVERLAY_TEXT_SET_TAG] = false;
|
||||
jsonComponent[JsonTagConstants::JSON_IS_OVERLAY_NODE_SET_TAG] = false;
|
||||
jsonComponent[JsonTagConstants::JSON_IS_CUSTOMIZABLE] = false;
|
||||
jsonComponent[JsonTagConstants::JSON_TIP_POSITION] = TipPosition::ABOVE_BOTTOM;
|
||||
}
|
||||
|
||||
void TestCommon::BuildSaveComponentInfo(nlohmann::json& jsonComponent)
|
||||
@@ -123,6 +125,8 @@ void TestCommon::BuildSaveComponentInfo(nlohmann::json& jsonComponent)
|
||||
jsonComponent[JsonTagConstants::JSON_FOREGROUND_BLUR_RADIUS_TAG] = 0.0;
|
||||
jsonComponent[JsonTagConstants::JSON_IS_OVERLAY_TEXT_SET_TAG] = false;
|
||||
jsonComponent[JsonTagConstants::JSON_IS_OVERLAY_NODE_SET_TAG] = false;
|
||||
jsonComponent[JsonTagConstants::JSON_IS_CUSTOMIZABLE] = false;
|
||||
jsonComponent[JsonTagConstants::JSON_TIP_POSITION] = TipPosition::ABOVE_BOTTOM;
|
||||
}
|
||||
|
||||
void TestCommon::BuildPasteComponentInfo(nlohmann::json& jsonComponent)
|
||||
@@ -152,6 +156,8 @@ void TestCommon::BuildPasteComponentInfo(nlohmann::json& jsonComponent)
|
||||
jsonComponent[JsonTagConstants::JSON_FOREGROUND_BLUR_RADIUS_TAG] = 0.0;
|
||||
jsonComponent[JsonTagConstants::JSON_IS_OVERLAY_TEXT_SET_TAG] = false;
|
||||
jsonComponent[JsonTagConstants::JSON_IS_OVERLAY_NODE_SET_TAG] = false;
|
||||
jsonComponent[JsonTagConstants::JSON_IS_CUSTOMIZABLE] = false;
|
||||
jsonComponent[JsonTagConstants::JSON_TIP_POSITION] = TipPosition::ABOVE_BOTTOM;
|
||||
}
|
||||
} // namespace SecurityComponent
|
||||
} // namespace Security
|
||||
|
||||
@@ -77,12 +77,14 @@ const std::string JsonTagConstants::JSON_LINEAR_GRADIENT_BLUR_RADIUS_TAG = "blur
|
||||
const std::string JsonTagConstants::JSON_FOREGROUND_BLUR_RADIUS_TAG = "foregroundBlurRadius";
|
||||
const std::string JsonTagConstants::JSON_IS_OVERLAY_TEXT_SET_TAG = "isOverlayTextSet";
|
||||
const std::string JsonTagConstants::JSON_IS_OVERLAY_NODE_SET_TAG = "isOverlayNodeCovered";
|
||||
const std::string JsonTagConstants::JSON_TIP_POSITION = "tipPosition";
|
||||
const std::string JsonTagConstants::JSON_IS_CUSTOMIZABLE = "isCustomizable";
|
||||
|
||||
bool SecCompBase::ParseNonCompatibleChange(const nlohmann::json& json)
|
||||
{
|
||||
std::string tag = JsonTagConstants::JSON_NON_COMPATIBLE_CHANGE_TAG;
|
||||
if ((json.find(tag) == json.end()) || !json.at(tag).is_boolean()) {
|
||||
SC_LOG_ERROR(LABEL, "json: %{public}s tag invalid.", tag.c_str());
|
||||
SC_LOG_ERROR(LABEL, "Json: %{public}s tag invalid.", tag.c_str());
|
||||
return false;
|
||||
}
|
||||
hasNonCompatibleChange_ = json.at(tag).get<bool>();
|
||||
@@ -96,15 +98,15 @@ bool SecCompBase::ParseNonCompatibleChange(const nlohmann::json& json)
|
||||
if (!ParseDimension(json, JsonTagConstants::JSON_LINEAR_GRADIENT_BLUR_RADIUS_TAG, blurRadius_)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
if (!ParseDimension(json, JsonTagConstants::JSON_FOREGROUND_BLUR_RADIUS_TAG, foregroundBlurRadius_)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
if (!ParseBool(json, JsonTagConstants::JSON_IS_OVERLAY_TEXT_SET_TAG, isOverlayTextSet_)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
if (!ParseBool(json, JsonTagConstants::JSON_IS_OVERLAY_NODE_SET_TAG, isOverlayNodeCovered_)) {
|
||||
return false;
|
||||
}
|
||||
@@ -115,7 +117,7 @@ bool SecCompBase::ParseNonCompatibleChange(const nlohmann::json& json)
|
||||
bool SecCompBase::ParseDimension(const nlohmann::json& json, const std::string& tag, DimensionT& res)
|
||||
{
|
||||
if ((json.find(tag) == json.end()) || !json.at(tag).is_number_float()) {
|
||||
SC_LOG_ERROR(LABEL, "json: %{public}s tag invalid.", tag.c_str());
|
||||
SC_LOG_ERROR(LABEL, "Json: %{public}s tag invalid.", tag.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -126,7 +128,7 @@ bool SecCompBase::ParseDimension(const nlohmann::json& json, const std::string&
|
||||
bool SecCompBase::ParseColor(const nlohmann::json& json, const std::string& tag, SecCompColor& res)
|
||||
{
|
||||
if ((json.find(tag) == json.end()) || !json.at(tag).is_number()) {
|
||||
SC_LOG_ERROR(LABEL, "json: %{public}s tag invalid.", tag.c_str());
|
||||
SC_LOG_ERROR(LABEL, "Json: %{public}s tag invalid.", tag.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -137,7 +139,7 @@ bool SecCompBase::ParseColor(const nlohmann::json& json, const std::string& tag,
|
||||
bool SecCompBase::ParseBool(const nlohmann::json& json, const std::string& tag, bool& res)
|
||||
{
|
||||
if ((json.find(tag) == json.end()) || !json.at(tag).is_boolean()) {
|
||||
SC_LOG_ERROR(LABEL, "json: %{public}s tag invalid.", tag.c_str());
|
||||
SC_LOG_ERROR(LABEL, "Json: %{public}s tag invalid.", tag.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -148,7 +150,7 @@ bool SecCompBase::ParseBool(const nlohmann::json& json, const std::string& tag,
|
||||
bool SecCompBase::ParseString(const nlohmann::json& json, const std::string& tag, std::string& res)
|
||||
{
|
||||
if ((json.find(tag) == json.end()) || !json.at(tag).is_string()) {
|
||||
SC_LOG_ERROR(LABEL, "json: %{public}s tag invalid.", tag.c_str());
|
||||
SC_LOG_ERROR(LABEL, "Json: %{public}s tag invalid.", tag.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -159,7 +161,7 @@ bool SecCompBase::ParseString(const nlohmann::json& json, const std::string& tag
|
||||
bool SecCompBase::ParsePadding(const nlohmann::json& json, const std::string& tag, PaddingSize& res)
|
||||
{
|
||||
if ((json.find(tag) == json.end()) || !json.at(tag).is_object()) {
|
||||
SC_LOG_ERROR(LABEL, "json: %{public}s tag invalid.", tag.c_str());
|
||||
SC_LOG_ERROR(LABEL, "Json: %{public}s tag invalid.", tag.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -182,7 +184,7 @@ bool SecCompBase::ParsePadding(const nlohmann::json& json, const std::string& ta
|
||||
bool SecCompBase::ParseBorderRadius(const nlohmann::json& json, const std::string& tag, BorderRadius& res)
|
||||
{
|
||||
if ((json.find(tag) == json.end()) || !json.at(tag).is_object()) {
|
||||
SC_LOG_ERROR(LABEL, "json: %{public}s tag invalid.", tag.c_str());
|
||||
SC_LOG_ERROR(LABEL, "Json: %{public}s tag invalid.", tag.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -205,7 +207,7 @@ bool SecCompBase::ParseBorderRadius(const nlohmann::json& json, const std::strin
|
||||
bool SecCompBase::ParseColors(const nlohmann::json& json, const std::string& tag)
|
||||
{
|
||||
if ((json.find(tag) == json.end()) || !json.at(tag).is_object()) {
|
||||
SC_LOG_ERROR(LABEL, "json: %{public}s tag invalid.", tag.c_str());
|
||||
SC_LOG_ERROR(LABEL, "Json: %{public}s tag invalid.", tag.c_str());
|
||||
return false;
|
||||
}
|
||||
auto jsonColors = json.at(tag);
|
||||
@@ -224,7 +226,7 @@ bool SecCompBase::ParseColors(const nlohmann::json& json, const std::string& tag
|
||||
bool SecCompBase::ParseBorders(const nlohmann::json& json, const std::string& tag)
|
||||
{
|
||||
if ((json.find(tag) == json.end()) || !json.at(tag).is_object()) {
|
||||
SC_LOG_ERROR(LABEL, "json: %{public}s tag invalid.", tag.c_str());
|
||||
SC_LOG_ERROR(LABEL, "Json: %{public}s tag invalid.", tag.c_str());
|
||||
return false;
|
||||
}
|
||||
auto jsonBorder = json.at(tag);
|
||||
@@ -234,7 +236,7 @@ bool SecCompBase::ParseBorders(const nlohmann::json& json, const std::string& ta
|
||||
bool SecCompBase::ParseSize(const nlohmann::json& json, const std::string& tag)
|
||||
{
|
||||
if ((json.find(tag) == json.end()) || !json.at(tag).is_object()) {
|
||||
SC_LOG_ERROR(LABEL, "json: %{public}s tag invalid.", tag.c_str());
|
||||
SC_LOG_ERROR(LABEL, "Json: %{public}s tag invalid.", tag.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -266,7 +268,7 @@ bool SecCompBase::ParseSize(const nlohmann::json& json, const std::string& tag)
|
||||
bool SecCompBase::ParseParent(const nlohmann::json& json, const std::string& tag)
|
||||
{
|
||||
if ((json.find(tag) == json.end()) || !json.at(tag).is_object()) {
|
||||
SC_LOG_ERROR(LABEL, "json: %{public}s tag invalid.", tag.c_str());
|
||||
SC_LOG_ERROR(LABEL, "Json: %{public}s tag invalid.", tag.c_str());
|
||||
return false;
|
||||
}
|
||||
auto jsonParent = json.at(tag);
|
||||
@@ -297,7 +299,7 @@ bool SecCompBase::ParseParent(const nlohmann::json& json, const std::string& tag
|
||||
bool SecCompBase::ParseRect(const nlohmann::json& json, const std::string& tag, SecCompRect& rect)
|
||||
{
|
||||
if ((json.find(tag) == json.end()) || !json.at(tag).is_object()) {
|
||||
SC_LOG_ERROR(LABEL, "json: %{public}s tag invalid.", tag.c_str());
|
||||
SC_LOG_ERROR(LABEL, "Json: %{public}s tag invalid.", tag.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -324,7 +326,7 @@ bool SecCompBase::ParseRect(const nlohmann::json& json, const std::string& tag,
|
||||
bool SecCompBase::ParseType(const nlohmann::json& json, const std::string& tag)
|
||||
{
|
||||
if ((json.find(tag) == json.end()) || !json.at(tag).is_number()) {
|
||||
SC_LOG_ERROR(LABEL, "json: %{public}s tag invalid.", tag.c_str());
|
||||
SC_LOG_ERROR(LABEL, "Json: %{public}s tag invalid.", tag.c_str());
|
||||
return false;
|
||||
}
|
||||
int32_t value = json.at(tag).get<int32_t>();
|
||||
@@ -341,7 +343,7 @@ bool SecCompBase::ParseType(const nlohmann::json& json, const std::string& tag)
|
||||
bool SecCompBase::ParseValue(const nlohmann::json& json, const std::string& tag, int32_t& value)
|
||||
{
|
||||
if ((json.find(tag) == json.end()) || !json.at(tag).is_number()) {
|
||||
SC_LOG_ERROR(LABEL, "json: %{public}s tag invalid.", tag.c_str());
|
||||
SC_LOG_ERROR(LABEL, "Json: %{public}s tag invalid.", tag.c_str());
|
||||
return false;
|
||||
}
|
||||
value = json.at(tag).get<int32_t>();
|
||||
@@ -352,7 +354,7 @@ bool SecCompBase::ParseValue(const nlohmann::json& json, const std::string& tag,
|
||||
bool SecCompBase::ParseDisplayId(const nlohmann::json& json, const std::string& tag)
|
||||
{
|
||||
if ((json.find(tag) == json.end()) || !json.at(tag).is_number()) {
|
||||
SC_LOG_ERROR(LABEL, "json: %{public}s tag invalid.", tag.c_str());
|
||||
SC_LOG_ERROR(LABEL, "Json: %{public}s tag invalid.", tag.c_str());
|
||||
return false;
|
||||
}
|
||||
displayId_ = json.at(tag).get<uint64_t>();
|
||||
@@ -363,7 +365,7 @@ bool SecCompBase::ParseDisplayId(const nlohmann::json& json, const std::string&
|
||||
bool SecCompBase::ParseCrossAxisState(const nlohmann::json& json, const std::string& tag)
|
||||
{
|
||||
if ((json.find(tag) == json.end()) || !json.at(tag).is_number()) {
|
||||
SC_LOG_ERROR(LABEL, "json: %{public}s tag invalid.", tag.c_str());
|
||||
SC_LOG_ERROR(LABEL, "Json: %{public}s tag invalid.", tag.c_str());
|
||||
return false;
|
||||
}
|
||||
int32_t value = json.at(tag).get<int32_t>();
|
||||
@@ -381,13 +383,83 @@ bool SecCompBase::ParseWearable(const nlohmann::json& json, const std::string& t
|
||||
{
|
||||
if ((json.find(tag) == json.end()) ||
|
||||
!json.at(tag).is_boolean()) {
|
||||
SC_LOG_ERROR(LABEL, "json: %{public}s tag invalid.", tag.c_str());
|
||||
SC_LOG_ERROR(LABEL, "Json: %{public}s tag invalid.", tag.c_str());
|
||||
return false;
|
||||
}
|
||||
isWearableDevice_ = json.at(tag).get<bool>();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SecCompBase::ParseTipPosition(const nlohmann::json& json, const std::string& tag)
|
||||
{
|
||||
if ((json.find(tag) == json.end()) || !json.at(tag).is_number()) {
|
||||
SC_LOG_ERROR(LABEL, "Json: %{public}s tag invalid.", tag.c_str());
|
||||
return false;
|
||||
}
|
||||
int32_t value = json.at(tag).get<int32_t>();
|
||||
if ((value < static_cast<int32_t>(TipPosition::ABOVE_BOTTOM)) ||
|
||||
(value > static_cast<int32_t>(TipPosition::BELOW_TOP))) {
|
||||
SC_LOG_ERROR(LABEL, "Save tip position: %{public}d is invalid.", value);
|
||||
return false;
|
||||
}
|
||||
|
||||
tipPosition_ = static_cast<TipPosition>(value);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SecCompBase::ParseComponentInfo(const nlohmann::json& json, std::string& message, bool isClicked)
|
||||
{
|
||||
if (!ParseRect(json, JsonTagConstants::JSON_RECT, rect_)) {
|
||||
return false;
|
||||
}
|
||||
if (!ParseSize(json, JsonTagConstants::JSON_SIZE_TAG)) {
|
||||
return false;
|
||||
}
|
||||
if (!ParseColors(json, JsonTagConstants::JSON_COLORS_TAG)) {
|
||||
return false;
|
||||
}
|
||||
if (!ParseBorders(json, JsonTagConstants::JSON_BORDER_TAG)) {
|
||||
return false;
|
||||
}
|
||||
if (!ParseStyle(json, JsonTagConstants::JSON_STYLE_TAG, message, isClicked)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SecCompBase::ParseWindowInfo(const nlohmann::json& json)
|
||||
{
|
||||
if (!ParseRect(json, JsonTagConstants::JSON_WINDOW_RECT, windowRect_)) {
|
||||
return false;
|
||||
}
|
||||
if (!ParseValue(json, JsonTagConstants::JSON_WINDOW_ID, windowId_)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SecCompBase::ParseDisplayInfo(const nlohmann::json& json)
|
||||
{
|
||||
if (!ParseDisplayId(json, JsonTagConstants::JSON_DISPLAY_ID)) {
|
||||
return false;
|
||||
}
|
||||
if (!ParseCrossAxisState(json, JsonTagConstants::JSON_CROSS_AXIS_STATE)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SecCompBase::ParseCustomInfo(const nlohmann::json& json)
|
||||
{
|
||||
if (!ParseBool(json, JsonTagConstants::JSON_IS_CUSTOMIZABLE, isCustomizable_)) {
|
||||
return false;
|
||||
}
|
||||
if (!ParseTipPosition(json, JsonTagConstants::JSON_TIP_POSITION)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SecCompBase::FromJson(const nlohmann::json& jsonSrc, std::string& message, bool isClicked)
|
||||
{
|
||||
SC_LOG_DEBUG(LABEL, "Button info %{public}s.", jsonSrc.dump().c_str());
|
||||
@@ -400,34 +472,19 @@ bool SecCompBase::FromJson(const nlohmann::json& jsonSrc, std::string& message,
|
||||
if (!ParseWearable(jsonSrc, JsonTagConstants::JSON_IS_WEARABLE)) {
|
||||
return false;
|
||||
}
|
||||
if (!ParseRect(jsonSrc, JsonTagConstants::JSON_RECT, rect_)) {
|
||||
return false;
|
||||
}
|
||||
if (!ParseRect(jsonSrc, JsonTagConstants::JSON_WINDOW_RECT, windowRect_)) {
|
||||
return false;
|
||||
}
|
||||
if (!ParseSize(jsonSrc, JsonTagConstants::JSON_SIZE_TAG)) {
|
||||
return false;
|
||||
}
|
||||
if (!ParseColors(jsonSrc, JsonTagConstants::JSON_COLORS_TAG)) {
|
||||
return false;
|
||||
}
|
||||
if (!ParseBorders(jsonSrc, JsonTagConstants::JSON_BORDER_TAG)) {
|
||||
return false;
|
||||
}
|
||||
if (!ParseParent(jsonSrc, JsonTagConstants::JSON_PARENT_TAG)) {
|
||||
return false;
|
||||
}
|
||||
if (!ParseStyle(jsonSrc, JsonTagConstants::JSON_STYLE_TAG, message, isClicked)) {
|
||||
if (!ParseComponentInfo(jsonSrc, message, isClicked)) {
|
||||
return false;
|
||||
}
|
||||
if (!ParseValue(jsonSrc, JsonTagConstants::JSON_WINDOW_ID, windowId_)) {
|
||||
if (!ParseWindowInfo(jsonSrc)) {
|
||||
return false;
|
||||
}
|
||||
if (!ParseDisplayId(jsonSrc, JsonTagConstants::JSON_DISPLAY_ID)) {
|
||||
if (!ParseDisplayInfo(jsonSrc)) {
|
||||
return false;
|
||||
}
|
||||
if (!ParseCrossAxisState(jsonSrc, JsonTagConstants::JSON_CROSS_AXIS_STATE)) {
|
||||
if (!ParseCustomInfo(jsonSrc)) {
|
||||
return false;
|
||||
}
|
||||
if (!ParseNonCompatibleChange(jsonSrc)) {
|
||||
@@ -521,6 +578,8 @@ void SecCompBase::ToJson(nlohmann::json& jsonRes) const
|
||||
jsonRes[JsonTagConstants::JSON_FOREGROUND_BLUR_RADIUS_TAG] = foregroundBlurRadius_;
|
||||
jsonRes[JsonTagConstants::JSON_IS_OVERLAY_TEXT_SET_TAG] = isOverlayTextSet_;
|
||||
jsonRes[JsonTagConstants::JSON_IS_OVERLAY_NODE_SET_TAG] = isOverlayNodeCovered_;
|
||||
jsonRes[JsonTagConstants::JSON_IS_CUSTOMIZABLE] = isCustomizable_;
|
||||
jsonRes[JsonTagConstants::JSON_TIP_POSITION] = tipPosition_;
|
||||
}
|
||||
|
||||
std::string SecCompBase::ToJsonStr() const
|
||||
@@ -533,7 +592,7 @@ std::string SecCompBase::ToJsonStr() const
|
||||
bool SecCompBase::CompareComponentBasicInfo(SecCompBase *other, bool isRectCheck) const
|
||||
{
|
||||
if (other == nullptr) {
|
||||
SC_LOG_ERROR(LABEL, "other is nullptr.");
|
||||
SC_LOG_ERROR(LABEL, "Other is nullptr.");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -558,7 +617,7 @@ bool SecCompBase::CompareComponentBasicInfo(SecCompBase *other, bool isRectCheck
|
||||
bool SecCompBase::ParseStyle(const nlohmann::json& json, const std::string& tag, std::string& message, bool isClicked)
|
||||
{
|
||||
if ((json.find(tag) == json.end()) || !json.at(tag).is_object()) {
|
||||
SC_LOG_ERROR(LABEL, "json: %{public}s tag invalid.", tag.c_str());
|
||||
SC_LOG_ERROR(LABEL, "Json: %{public}s tag invalid.", tag.c_str());
|
||||
return false;
|
||||
}
|
||||
auto jsonStyle = json.at(tag);
|
||||
@@ -580,14 +639,14 @@ bool SecCompBase::ParseStyle(const nlohmann::json& json, const std::string& tag,
|
||||
text_ = jsonStyle.at(JsonTagConstants::JSON_TEXT_TAG).get<int32_t>();
|
||||
icon_ = jsonStyle.at(JsonTagConstants::JSON_ICON_TAG).get<int32_t>();
|
||||
if (!IsTextIconTypeValid(message, isClicked)) {
|
||||
SC_LOG_ERROR(LABEL, "text or icon is invalid.");
|
||||
SC_LOG_ERROR(LABEL, "Text or icon is invalid.");
|
||||
return false;
|
||||
}
|
||||
bg_ = static_cast<SecCompBackground>(jsonStyle.at(JsonTagConstants::JSON_BG_TAG).get<int32_t>());
|
||||
if ((bg_ != SecCompBackground::NO_BG_TYPE) && (bg_ != SecCompBackground::CAPSULE) &&
|
||||
(bg_ != SecCompBackground::CIRCLE) && (bg_ != SecCompBackground::NORMAL) &&
|
||||
(bg_ != SecCompBackground::ROUNDED_RECTANGLE)) {
|
||||
SC_LOG_ERROR(LABEL, "bg is invalid.");
|
||||
SC_LOG_ERROR(LABEL, "Background is invalid.");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
||||
@@ -94,6 +94,8 @@ public:
|
||||
static const std::string JSON_FOREGROUND_BLUR_RADIUS_TAG;
|
||||
static const std::string JSON_IS_OVERLAY_TEXT_SET_TAG;
|
||||
static const std::string JSON_IS_OVERLAY_NODE_SET_TAG;
|
||||
static const std::string JSON_IS_CUSTOMIZABLE;
|
||||
static const std::string JSON_TIP_POSITION;
|
||||
};
|
||||
|
||||
class __attribute__((visibility("default"))) SecCompBase {
|
||||
@@ -159,6 +161,8 @@ public:
|
||||
bool isIconExceeded_ = false;
|
||||
bool isBorderCovered_ = false;
|
||||
bool isWearableDevice_ = false;
|
||||
TipPosition tipPosition_ = TipPosition::ABOVE_BOTTOM;
|
||||
bool isCustomizable_ = false;
|
||||
protected:
|
||||
virtual bool IsTextIconTypeValid(std::string& message, bool isClicked) = 0;
|
||||
virtual bool IsCorrespondenceType() = 0;
|
||||
@@ -180,9 +184,14 @@ private:
|
||||
bool ParseValue(const nlohmann::json& json, const std::string& tag, int32_t& value);
|
||||
bool ParseDisplayId(const nlohmann::json& json, const std::string& tag);
|
||||
bool ParseCrossAxisState(const nlohmann::json& json, const std::string& tag);
|
||||
bool ParseTipPosition(const nlohmann::json& json, const std::string& tag);
|
||||
bool ParseWearable(const nlohmann::json& json, const std::string& tag);
|
||||
void ToJsonRect(nlohmann::json& jsonRes) const;
|
||||
void ToJsonSize(nlohmann::json& jsonRes) const;
|
||||
bool ParseComponentInfo(const nlohmann::json& json, std::string& message, bool isClicked);
|
||||
bool ParseWindowInfo(const nlohmann::json& json);
|
||||
bool ParseDisplayInfo(const nlohmann::json& json);
|
||||
bool ParseCustomInfo(const nlohmann::json& json);
|
||||
};
|
||||
} // namespace SecurityComponent
|
||||
} // namespace Security
|
||||
|
||||
@@ -76,6 +76,16 @@ enum CrossAxisState {
|
||||
STATE_NO_CROSS,
|
||||
};
|
||||
|
||||
enum TipPosition {
|
||||
ABOVE_BOTTOM = 0,
|
||||
BELOW_TOP,
|
||||
};
|
||||
|
||||
enum NotifyType {
|
||||
DIALOG = 0,
|
||||
TOAST,
|
||||
};
|
||||
|
||||
inline bool IsComponentTypeValid(int32_t type)
|
||||
{
|
||||
return (type > UNKNOWN_SC_TYPE && type < MAX_SC_TYPE);
|
||||
|
||||
@@ -36,6 +36,7 @@ public:
|
||||
static bool IsServiceExist();
|
||||
static bool LoadService();
|
||||
static bool IsSystemAppCalling();
|
||||
static bool HasCustomPermissionForSecComp();
|
||||
};
|
||||
} // namespace SecurityComponent
|
||||
} // namespace Security
|
||||
|
||||
@@ -23,5 +23,6 @@ interface OHOS.Security.SecurityComponent.ISecCompService {
|
||||
void ReportSecurityComponentClickEvent([in] IRemoteObject callerToken, [in] IRemoteObject dialogCallback,
|
||||
[in] SecCompRawdata rawData, [out] SecCompRawdata rawReply);
|
||||
void VerifySavePermission([in] unsigned int tokenId, [out] boolean ret);
|
||||
void HasCustomPermissionForSecComp([out] boolean hasCustomPermission);
|
||||
void PreRegisterSecCompProcess([in] SecCompRawdata rawData, [out] SecCompRawdata rawReply);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,13 +52,21 @@ const std::string WINDOW_ID_KEY = "ohos.ability.params.windowId";
|
||||
const std::string CALLER_UID_KEY = "ohos.caller.uid";
|
||||
const std::string DISPLAY_WIDTH = "ohos.display.width";
|
||||
const std::string DISPLAY_HEIGHT = "ohos.display.height";
|
||||
const std::string DISPLAY_TOP = "ohos.display.top";
|
||||
const std::string DIALOG_OFFSET = "ohos.dialog.offset";
|
||||
const std::string NOTIFY_TYPE = "ohos.ability.notify.type";
|
||||
const std::string TOAST_POSITION = "ohos.toast.position";
|
||||
const std::string TOAST_OFFSET = "ohos.toast.offset";
|
||||
|
||||
constexpr int32_t DISPLAY_HALF_RATIO = 2;
|
||||
constexpr uint32_t MAX_CFG_FILE_SIZE = 100 * 1024; // 100k
|
||||
constexpr uint64_t LOCATION_BUTTON_FIRST_USE = 1 << 0;
|
||||
constexpr uint64_t SAVE_BUTTON_FIRST_USE = 1 << 1;
|
||||
constexpr int32_t ABOVE_BOTTOM_OFFSET = 80;
|
||||
constexpr int32_t BELOW_TOP_OFFSET = 112;
|
||||
static std::mutex g_instanceMutex;
|
||||
static std::unordered_map<TipPosition, int32_t> tipPositionsMap = {{TipPosition::ABOVE_BOTTOM, ABOVE_BOTTOM_OFFSET},
|
||||
{TipPosition::BELOW_TOP, BELOW_TOP_OFFSET}};
|
||||
}
|
||||
|
||||
void SecCompDialogSrvCallback::OnDialogClosed(int32_t result)
|
||||
@@ -302,10 +310,10 @@ int32_t FirstUseDialog::GrantDialogWaitEntity(int32_t scId)
|
||||
return res;
|
||||
}
|
||||
|
||||
bool FirstUseDialog::SetDialogInfo(AAFwk::Want& want, const uint64_t displayId, const CrossAxisState crossAxisState)
|
||||
bool FirstUseDialog::SetDisplayInfo(AAFwk::Want& want, const DisplayInfo& displayInfo)
|
||||
{
|
||||
sptr<OHOS::Rosen::Display> display =
|
||||
OHOS::Rosen::DisplayManager::GetInstance().GetDisplayById(displayId);
|
||||
OHOS::Rosen::DisplayManager::GetInstance().GetDisplayById(displayInfo.displayId);
|
||||
if (display == nullptr) {
|
||||
SC_LOG_ERROR(LABEL, "Get display manager failed");
|
||||
return false;
|
||||
@@ -321,7 +329,7 @@ bool FirstUseDialog::SetDialogInfo(AAFwk::Want& want, const uint64_t displayId,
|
||||
int32_t height = info->GetHeight();
|
||||
int32_t offset = 0;
|
||||
/* crossAxisState is CROSS */
|
||||
if (crossAxisState == CrossAxisState::STATE_CROSS) {
|
||||
if (displayInfo.crossAxisState == CrossAxisState::STATE_CROSS) {
|
||||
height = info->GetPhysicalHeight();
|
||||
offset = static_cast<int32_t>(info->GetAvailableHeight()) / DISPLAY_HALF_RATIO;
|
||||
}
|
||||
@@ -334,6 +342,36 @@ bool FirstUseDialog::SetDialogInfo(AAFwk::Want& want, const uint64_t displayId,
|
||||
return true;
|
||||
}
|
||||
|
||||
void FirstUseDialog::StartToastAbility(const std::shared_ptr<SecCompEntity> entity,
|
||||
const sptr<IRemoteObject> callerToken, const DisplayInfo& displayInfo)
|
||||
{
|
||||
if (!entity->AllowToShowToast()) {
|
||||
return;
|
||||
}
|
||||
bool needToShow = AccessToken::AccessTokenKit::IsToastShownNeeded(entity->pid_);
|
||||
if (!needToShow) {
|
||||
SC_LOG_INFO(LABEL, "Process pid=%{public}d needs not to show", entity->pid_);
|
||||
return;
|
||||
}
|
||||
AAFwk::Want want;
|
||||
want.SetElementName(GRANT_ABILITY_BUNDLE_NAME, GRANT_ABILITY_ABILITY_NAME);
|
||||
want.SetParam(NOTIFY_TYPE, NotifyType::TOAST);
|
||||
want.SetParam(TOAST_POSITION, entity->componentInfo_->tipPosition_);
|
||||
want.SetParam(TOAST_OFFSET, tipPositionsMap[entity->componentInfo_->tipPosition_]);
|
||||
int32_t superFoldOffsetY = 0;
|
||||
if (entity->IsInPCVirtualScreen(displayInfo.crossAxisState)) {
|
||||
superFoldOffsetY = displayInfo.superFoldOffsetY;
|
||||
}
|
||||
want.SetParam(DISPLAY_TOP, superFoldOffsetY);
|
||||
if (!SetDisplayInfo(want, displayInfo)) {
|
||||
SC_LOG_ERROR(LABEL, "Set display info failed.");
|
||||
return;
|
||||
}
|
||||
|
||||
int startRes = AAFwk::AbilityManagerClient::GetInstance()->StartExtensionAbility(want, callerToken);
|
||||
SC_LOG_INFO(LABEL, "Start toast ability res %{public}d", startRes);
|
||||
}
|
||||
|
||||
void FirstUseDialog::StartDialogAbility(std::shared_ptr<SecCompEntity> entity, sptr<IRemoteObject> callerToken,
|
||||
sptr<IRemoteObject> dialogCallback, const DisplayInfo& displayInfo)
|
||||
{
|
||||
@@ -344,7 +382,7 @@ void FirstUseDialog::StartDialogAbility(std::shared_ptr<SecCompEntity> entity, s
|
||||
} else if (type == SAVE_COMPONENT) {
|
||||
typeNum = 1;
|
||||
} else {
|
||||
SC_LOG_ERROR(LABEL, "unknown type.");
|
||||
SC_LOG_ERROR(LABEL, "Unknown type.");
|
||||
return;
|
||||
}
|
||||
int32_t scId = entity->scId_;
|
||||
@@ -361,15 +399,16 @@ void FirstUseDialog::StartDialogAbility(std::shared_ptr<SecCompEntity> entity, s
|
||||
want.SetParam(TOKEN_KEY, callerToken);
|
||||
want.SetParam(CALLBACK_KEY, srvCallback);
|
||||
want.SetParam(WINDOW_ID_KEY, displayInfo.windowId);
|
||||
want.SetParam(NOTIFY_TYPE, NotifyType::DIALOG);
|
||||
int32_t uid = IPCSkeleton::GetCallingUid();
|
||||
want.SetParam(CALLER_UID_KEY, uid);
|
||||
if (!SetDialogInfo(want, displayInfo.displayId, displayInfo.crossAxisState)) {
|
||||
SC_LOG_ERROR(LABEL, "Set dialog info failed.");
|
||||
if (!SetDisplayInfo(want, displayInfo)) {
|
||||
SC_LOG_ERROR(LABEL, "Set display info failed.");
|
||||
return;
|
||||
}
|
||||
|
||||
int startRes = AAFwk::AbilityManagerClient::GetInstance()->StartExtensionAbility(want, callerToken);
|
||||
SC_LOG_INFO(LABEL, "start ability res %{public}d", startRes);
|
||||
SC_LOG_INFO(LABEL, "Start ability res %{public}d", startRes);
|
||||
if (startRes != 0) {
|
||||
dialogWaitMap_.erase(scId);
|
||||
}
|
||||
@@ -460,7 +499,8 @@ int32_t FirstUseDialog::NotifyFirstUseDialog(std::shared_ptr<SecCompEntity> enti
|
||||
|
||||
uint64_t compTypes = firstUseMap_[tokenId];
|
||||
if ((compTypes & typeMask) == typeMask) {
|
||||
SC_LOG_INFO(LABEL, "no need notify again.");
|
||||
SC_LOG_INFO(LABEL, "no need notify dialog again.");
|
||||
StartToastAbility(entity, callerToken, displayInfo);
|
||||
return SC_OK;
|
||||
}
|
||||
StartDialogAbility(entity, callerToken, dialogCallback, displayInfo);
|
||||
|
||||
@@ -56,6 +56,7 @@ struct DisplayInfo {
|
||||
const uint64_t displayId;
|
||||
const CrossAxisState crossAxisState;
|
||||
const int32_t windowId;
|
||||
const int32_t superFoldOffsetY;
|
||||
};
|
||||
|
||||
static FirstUseDialog& GetInstance();
|
||||
@@ -82,7 +83,9 @@ private:
|
||||
void SaveFirstUseRecord(void);
|
||||
void StartDialogAbility(std::shared_ptr<SecCompEntity> entity, sptr<IRemoteObject> callerToken,
|
||||
sptr<IRemoteObject> dialogCallback, const DisplayInfo& displayInfo);
|
||||
bool SetDialogInfo(AAFwk::Want& want, const uint64_t displayId, const CrossAxisState crossAxisState);
|
||||
void StartToastAbility(const std::shared_ptr<SecCompEntity> entity, const sptr<IRemoteObject> callerToken,
|
||||
const DisplayInfo& displayInfo);
|
||||
bool SetDisplayInfo(AAFwk::Want& want, const DisplayInfo& displayInfo);
|
||||
void SendSaveEventHandler(void);
|
||||
|
||||
std::mutex useMapMutex_;
|
||||
|
||||
@@ -121,7 +121,7 @@ bool SecCompEntity::IsInPCVirtualScreen(const CrossAxisState crossAxisState) con
|
||||
}
|
||||
|
||||
int32_t SecCompEntity::CheckClickInfo(SecCompClickEvent& clickInfo, int32_t superFoldOffsetY,
|
||||
const CrossAxisState crossAxisState, std::string& message) const
|
||||
const CrossAxisState crossAxisState, std::string& message)
|
||||
{
|
||||
bool isInPCVirtualScreen = IsInPCVirtualScreen(crossAxisState);
|
||||
SC_LOG_INFO(LABEL, "The cross axis state: %{public}d, the fold offset y: %{public}d.",
|
||||
@@ -130,10 +130,13 @@ int32_t SecCompEntity::CheckClickInfo(SecCompClickEvent& clickInfo, int32_t supe
|
||||
clickInfo.point.touchY += superFoldOffsetY;
|
||||
componentInfo_->rect_.y_ += superFoldOffsetY;
|
||||
}
|
||||
message.clear();
|
||||
if (!WindowInfoHelper::CheckOtherWindowCoverComp(componentInfo_->windowId_,
|
||||
componentInfo_->rect_, message)) {
|
||||
SC_LOG_ERROR(LABEL, "Component may be covered by other window");
|
||||
return SC_SERVICE_ERROR_CLICK_EVENT_INVALID;
|
||||
if (!AllowToBypassSecurityCheck(message)) {
|
||||
return SC_SERVICE_ERROR_CLICK_EVENT_INVALID;
|
||||
}
|
||||
}
|
||||
|
||||
int32_t res = SC_SERVICE_ERROR_CLICK_EVENT_INVALID;
|
||||
|
||||
@@ -45,9 +45,46 @@ public:
|
||||
return isGrant_;
|
||||
}
|
||||
|
||||
void SetCustomAuthorizationStatus(bool isCustomAuthorized)
|
||||
{
|
||||
isCustomAuthorized_ = isCustomAuthorized;
|
||||
}
|
||||
|
||||
bool AllowToBypassSecurityCheck(const std::string& errMessage)
|
||||
{
|
||||
if (errMessage.empty()) {
|
||||
return false;
|
||||
}
|
||||
if (!isCustomAuthorized_) {
|
||||
return false;
|
||||
}
|
||||
if (GetType() != SecCompType::SAVE_COMPONENT) {
|
||||
return false;
|
||||
}
|
||||
bypassSecurityCheck_ = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AllowToShowToast() const
|
||||
{
|
||||
if (componentInfo_ == nullptr) {
|
||||
return false;
|
||||
}
|
||||
if (!isCustomAuthorized_) {
|
||||
return false;
|
||||
}
|
||||
if (GetType() != SecCompType::SAVE_COMPONENT) {
|
||||
return false;
|
||||
}
|
||||
if (!(componentInfo_->isCustomizable_ || bypassSecurityCheck_)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CompareComponentBasicInfo(SecCompBase* other, bool isRectCheck) const;
|
||||
int32_t CheckClickInfo(SecCompClickEvent& clickInfo, int32_t superFoldOffsetY, const CrossAxisState crossAxisState,
|
||||
std::string& message) const;
|
||||
std::string& message);
|
||||
bool IsInPCVirtualScreen(const CrossAxisState crossAxisState) const;
|
||||
|
||||
std::shared_ptr<SecCompBase> componentInfo_;
|
||||
@@ -55,6 +92,8 @@ public:
|
||||
int32_t scId_;
|
||||
int32_t pid_;
|
||||
int32_t uid_;
|
||||
bool isCustomAuthorized_ = false;
|
||||
bool bypassSecurityCheck_ = false;
|
||||
|
||||
private:
|
||||
int32_t CheckKeyEvent(const SecCompClickEvent& clickInfo) const;
|
||||
|
||||
@@ -64,6 +64,7 @@ SecCompBase* SecCompInfoHelper::ParseComponent(SecCompType type, const nlohmann:
|
||||
std::string& message, bool isClicked)
|
||||
{
|
||||
SecCompBase* comp = nullptr;
|
||||
message.clear();
|
||||
switch (type) {
|
||||
case LOCATION_COMPONENT:
|
||||
comp = ConstructComponent<LocationButton>(jsonComponent, message, isClicked);
|
||||
@@ -189,6 +190,7 @@ bool SecCompInfoHelper::CheckRectValid(const SecCompRect& rect, const SecCompRec
|
||||
return false;
|
||||
}
|
||||
|
||||
message.clear();
|
||||
if (IsOutOfScreen(rect, curScreenWidth, curScreenHeight, message, screenInfo.isWearable)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -42,6 +42,7 @@ static constexpr unsigned long REPORT_REMOTE_OBJECT_SIZE = 2UL;
|
||||
static std::mutex g_instanceMutex;
|
||||
const std::string START_DIALOG = "start dialog, onclick will be trap after dialog closed.";
|
||||
constexpr int32_t SA_ID_SECURITY_COMPONENT_SERVICE = 3506;
|
||||
const std::string CUSTOMIZE_SAVE_BUTTON = "ohos.permission.CUSTOMIZE_SAVE_BUTTON";
|
||||
}
|
||||
|
||||
SecCompManager::SecCompManager()
|
||||
@@ -403,6 +404,8 @@ int32_t SecCompManager::RegisterSecurityComponent(SecCompType type,
|
||||
int32_t registerId = CreateScId();
|
||||
std::shared_ptr<SecCompEntity> entity =
|
||||
std::make_shared<SecCompEntity>(component, caller.tokenId, registerId, caller.pid, caller.uid);
|
||||
bool isCustomAuthorized = SecCompManager::GetInstance().HasCustomPermissionForSecComp();
|
||||
entity->SetCustomAuthorizationStatus(isCustomAuthorized);
|
||||
int32_t ret = AddSecurityComponentToList(caller.pid, caller.tokenId, entity);
|
||||
if (ret == SC_OK) {
|
||||
scId = registerId;
|
||||
@@ -483,7 +486,13 @@ int32_t SecCompManager::CheckClickSecurityComponentInfo(std::shared_ptr<SecCompE
|
||||
HiviewDFX::HiSysEvent::EventType::SECURITY, "CALLER_UID", uid, "CALLER_BUNDLE_NAME", bundleName,
|
||||
"CALLER_PID", IPCSkeleton::GetCallingPid(), "SC_ID", scId, "CALL_SCENE", "CLICK", "SC_TYPE",
|
||||
sc->GetType());
|
||||
return SC_SERVICE_ERROR_COMPONENT_INFO_INVALID;
|
||||
/**
|
||||
* If the ptr of reportComponentInfo is not nullptr, the string of message is not empty only when the icon of
|
||||
* save button is picture.
|
||||
*/
|
||||
if (!(reportComponentInfo && sc->AllowToBypassSecurityCheck(message))) {
|
||||
return SC_SERVICE_ERROR_COMPONENT_INFO_INVALID;
|
||||
}
|
||||
}
|
||||
if (report && (report->isClipped_ || report->hasNonCompatibleChange_)) {
|
||||
HiSysEventWrite(HiviewDFX::HiSysEvent::Domain::SEC_COMPONENT, "CLIP_CHECK_FAILED",
|
||||
@@ -491,12 +500,7 @@ int32_t SecCompManager::CheckClickSecurityComponentInfo(std::shared_ptr<SecCompE
|
||||
"CALLER_BUNDLE_NAME", bundleName, "COMPONENT_INFO", jsonComponent.dump().c_str());
|
||||
}
|
||||
|
||||
SecCompInfoHelper::ScreenInfo screenInfo = {
|
||||
.displayId = report->displayId_,
|
||||
.crossAxisState = report->crossAxisState_,
|
||||
.isWearable = report->isWearableDevice_
|
||||
};
|
||||
|
||||
SecCompInfoHelper::ScreenInfo screenInfo = {report->displayId_, report->crossAxisState_, report->isWearableDevice_};
|
||||
if ((!SecCompInfoHelper::CheckRectValid(reportComponentInfo->rect_, reportComponentInfo->windowRect_,
|
||||
screenInfo, message))) {
|
||||
SC_LOG_ERROR(LABEL, "compare component info failed.");
|
||||
@@ -504,7 +508,9 @@ int32_t SecCompManager::CheckClickSecurityComponentInfo(std::shared_ptr<SecCompE
|
||||
HiviewDFX::HiSysEvent::EventType::SECURITY, "CALLER_UID", uid, "CALLER_BUNDLE_NAME", bundleName,
|
||||
"CALLER_PID", IPCSkeleton::GetCallingPid(), "SC_ID", scId, "CALL_SCENE", "CLICK", "SC_TYPE",
|
||||
sc->GetType());
|
||||
return SC_SERVICE_ERROR_COMPONENT_INFO_INVALID;
|
||||
if (!sc->AllowToBypassSecurityCheck(message)) {
|
||||
return SC_SERVICE_ERROR_COMPONENT_INFO_INVALID;
|
||||
}
|
||||
}
|
||||
int32_t enhanceRes =
|
||||
SecCompEnhanceAdapter::CheckComponentInfoEnhance(caller.pid, reportComponentInfo, jsonComponent);
|
||||
@@ -554,8 +560,8 @@ void SecCompManager::GetFoldOffsetY(const CrossAxisState crossAxisState)
|
||||
SC_LOG_INFO(LABEL, "height: %{public}d, posY: %{public}d", rect.height_, rect.posY_);
|
||||
}
|
||||
|
||||
int32_t SecCompManager::ReportSecurityComponentClickEvent(SecCompInfo& info, const nlohmann::json& compJson,
|
||||
const SecCompCallerInfo& caller, const std::vector<sptr<IRemoteObject>>& remote, std::string& message)
|
||||
int32_t SecCompManager::CheckClickEventParams(const SecCompCallerInfo& caller,
|
||||
const std::vector<sptr<IRemoteObject>>& remote)
|
||||
{
|
||||
if (remote.size() < REPORT_REMOTE_OBJECT_SIZE) {
|
||||
SC_LOG_ERROR(LABEL, "remote object size is invalid");
|
||||
@@ -565,26 +571,35 @@ int32_t SecCompManager::ReportSecurityComponentClickEvent(SecCompInfo& info, con
|
||||
SC_LOG_ERROR(LABEL, "app is in MaliciousAppList, never allow it");
|
||||
return SC_ENHANCE_ERROR_IN_MALICIOUS_LIST;
|
||||
}
|
||||
return SC_OK;
|
||||
}
|
||||
|
||||
int32_t SecCompManager::ReportSecurityComponentClickEvent(SecCompInfo& info, const nlohmann::json& compJson,
|
||||
const SecCompCallerInfo& caller, const std::vector<sptr<IRemoteObject>>& remote, std::string& message)
|
||||
{
|
||||
int32_t res = CheckClickEventParams(caller, remote);
|
||||
if (res != SC_OK) {
|
||||
return res;
|
||||
}
|
||||
OHOS::Utils::UniqueReadGuard<OHOS::Utils::RWLock> lk(this->componentInfoLock_);
|
||||
std::shared_ptr<SecCompEntity> sc = GetSecurityComponentFromList(caller.pid, info.scId);
|
||||
if (sc == nullptr) {
|
||||
SC_LOG_ERROR(LABEL, "Can not find target component");
|
||||
return SC_SERVICE_ERROR_COMPONENT_NOT_EXIST;
|
||||
}
|
||||
int32_t res = CheckClickSecurityComponentInfo(sc, info.scId, compJson, caller, message);
|
||||
if (!message.empty()) {
|
||||
if (!sc->AllowToBypassSecurityCheck(message)) {
|
||||
return SC_SERVICE_ERROR_CLICK_EVENT_INVALID;
|
||||
}
|
||||
}
|
||||
res = CheckClickSecurityComponentInfo(sc, info.scId, compJson, caller, message);
|
||||
if (res != SC_OK) {
|
||||
return res;
|
||||
}
|
||||
SecCompBase* reportComponentInfo = SecCompInfoHelper::ParseComponent(sc->GetType(), compJson, message, true);
|
||||
std::shared_ptr<SecCompBase> report(reportComponentInfo);
|
||||
if (report == nullptr) {
|
||||
return SC_SERVICE_ERROR_COMPONENT_INFO_INVALID;
|
||||
}
|
||||
|
||||
GetFoldOffsetY(report->crossAxisState_);
|
||||
GetFoldOffsetY(sc->componentInfo_->crossAxisState_);
|
||||
|
||||
res = sc->CheckClickInfo(info.clickInfo, superFoldOffsetY_, report->crossAxisState_, message);
|
||||
res = sc->CheckClickInfo(info.clickInfo, superFoldOffsetY_, sc->componentInfo_->crossAxisState_, message);
|
||||
if (res != SC_OK) {
|
||||
ReportEvent("CLICK_INFO_CHECK_FAILED", HiviewDFX::HiSysEvent::EventType::SECURITY,
|
||||
info.scId, sc->GetType());
|
||||
@@ -595,7 +610,8 @@ int32_t SecCompManager::ReportSecurityComponentClickEvent(SecCompInfo& info, con
|
||||
return SC_SERVICE_ERROR_CLICK_EVENT_INVALID;
|
||||
}
|
||||
|
||||
const FirstUseDialog::DisplayInfo displayInfo = {report->displayId_, report->crossAxisState_, report->windowId_};
|
||||
const FirstUseDialog::DisplayInfo displayInfo = {sc->componentInfo_->displayId_,
|
||||
sc->componentInfo_->crossAxisState_, sc->componentInfo_->windowId_, superFoldOffsetY_};
|
||||
|
||||
if (FirstUseDialog::GetInstance().NotifyFirstUseDialog(sc, remote[0], remote[1], displayInfo) ==
|
||||
SC_SERVICE_ERROR_WAIT_FOR_DIALOG_CLOSE) {
|
||||
@@ -662,6 +678,17 @@ bool SecCompManager::Initialize()
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SecCompManager::HasCustomPermissionForSecComp()
|
||||
{
|
||||
uint32_t callingTokenID = IPCSkeleton::GetCallingTokenID();
|
||||
if (AccessToken::AccessTokenKit::VerifyAccessToken(callingTokenID, CUSTOMIZE_SAVE_BUTTON) ==
|
||||
AccessToken::TypePermissionState::PERMISSION_GRANTED) {
|
||||
return true;
|
||||
}
|
||||
SC_LOG_INFO(LABEL, "Permission denied(tokenID=%{public}d)", callingTokenID);
|
||||
return false;
|
||||
}
|
||||
} // namespace SecurityComponent
|
||||
} // namespace Security
|
||||
} // namespace OHOS
|
||||
|
||||
@@ -59,6 +59,7 @@ public:
|
||||
int32_t UnregisterSecurityComponent(int32_t scId, const SecCompCallerInfo& caller);
|
||||
int32_t ReportSecurityComponentClickEvent(SecCompInfo& secCompInfo, const nlohmann::json& jsonComponent,
|
||||
const SecCompCallerInfo& caller, const std::vector<sptr<IRemoteObject>>& remote, std::string& message);
|
||||
int32_t CheckClickEventParams(const SecCompCallerInfo& caller, const std::vector<sptr<IRemoteObject>>& remote);
|
||||
void NotifyProcessForeground(int32_t pid);
|
||||
void NotifyProcessBackground(int32_t pid);
|
||||
void NotifyProcessDied(int32_t pid, bool isProcessCached);
|
||||
@@ -67,6 +68,7 @@ public:
|
||||
void ExitSaProcess();
|
||||
void ExitWhenAppMgrDied();
|
||||
int32_t AddSecurityComponentProcess(const SecCompCallerInfo& caller);
|
||||
bool HasCustomPermissionForSecComp();
|
||||
|
||||
private:
|
||||
SecCompManager();
|
||||
|
||||
@@ -531,6 +531,12 @@ int32_t SecCompService::ReportSecurityComponentClickEvent(const sptr<IRemoteObje
|
||||
res = SC_SERVICE_ERROR_PARCEL_OPERATE_FAIL;
|
||||
break;
|
||||
}
|
||||
std::string message;
|
||||
if (!deserializedData.ReadString(message)) {
|
||||
SC_LOG_ERROR(LABEL, "Report read message failed");
|
||||
res = SC_SERVICE_ERROR_PARCEL_OPERATE_FAIL;
|
||||
break;
|
||||
}
|
||||
sptr<SecCompClickEventParcel> clickInfoParcel = deserializedData.ReadParcelable<SecCompClickEventParcel>();
|
||||
if (clickInfoParcel == nullptr) {
|
||||
SC_LOG_ERROR(LABEL, "Report read clickInfo info failed");
|
||||
@@ -539,7 +545,6 @@ int32_t SecCompService::ReportSecurityComponentClickEvent(const sptr<IRemoteObje
|
||||
}
|
||||
|
||||
SecCompInfo secCompInfo{ scId, componentInfo, clickInfoParcel->clickInfoParams_ };
|
||||
std::string message;
|
||||
res = ReportSecurityComponentClickEventBody(secCompInfo, callerToken, dialogCallback, message);
|
||||
res = ReportWriteToRawdata(res, message, rawReply);
|
||||
} while (0);
|
||||
@@ -626,6 +631,12 @@ int32_t SecCompService::VerifySavePermission(AccessToken::AccessTokenID tokenId,
|
||||
return SC_OK;
|
||||
}
|
||||
|
||||
int32_t SecCompService::HasCustomPermissionForSecComp(bool& hasCustomPermission)
|
||||
{
|
||||
hasCustomPermission = SecCompManager::GetInstance().HasCustomPermissionForSecComp();
|
||||
return SC_OK;
|
||||
}
|
||||
|
||||
bool SecCompService::IsMediaLibraryCalling()
|
||||
{
|
||||
int32_t uid = IPCSkeleton::GetCallingUid();
|
||||
|
||||
@@ -52,6 +52,7 @@ public:
|
||||
const sptr<IRemoteObject>& dialogCallback, const SecCompRawdata& rawData, SecCompRawdata& rawReply) override;
|
||||
int32_t VerifySavePermission(AccessToken::AccessTokenID tokenId, bool& isGranted) override;
|
||||
int32_t PreRegisterSecCompProcess(const SecCompRawdata& rawData, SecCompRawdata& rawReply) override;
|
||||
int32_t HasCustomPermissionForSecComp(bool& hasCustomPermission) override;
|
||||
|
||||
int Dump(int fd, const std::vector<std::u16string>& args) override;
|
||||
|
||||
|
||||
@@ -62,6 +62,11 @@ public:
|
||||
return static_cast<int32_t>(idInner->dlpFlag);
|
||||
}
|
||||
|
||||
static bool IsToastShownNeeded(int32_t pid)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
static std::mutex mutex_;
|
||||
static std::map<AccessTokenID, std::set<std::string>> permMap_;
|
||||
static int getHapTokenInfoRes;
|
||||
|
||||
@@ -459,6 +459,50 @@ HWTEST_F(FirstUseDialogTest, NotifyFirstUseDialog001, TestSize.Level0)
|
||||
sleep(3);
|
||||
}
|
||||
|
||||
/*
|
||||
* @tc.name: NotifyFirstUseDialog002
|
||||
* @tc.desc: Test NotifyFirstUseDialog
|
||||
* @tc.type: FUNC
|
||||
* @tc.require:
|
||||
*/
|
||||
HWTEST_F(FirstUseDialogTest, NotifyFirstUseDialog002, TestSize.Level0)
|
||||
{
|
||||
FirstUseDialog diag;
|
||||
diag.secHandler_ = nullptr;
|
||||
|
||||
const FirstUseDialog::DisplayInfo displayInfo = {0, CrossAxisState::STATE_INVALID, 0};
|
||||
|
||||
// entity
|
||||
std::shared_ptr<SecCompEntity> entity = std::make_shared<SecCompEntity>(nullptr, 0, 0, 0, 0);
|
||||
std::shared_ptr<AppExecFwk::EventRunner> runner = AppExecFwk::EventRunner::Create(true);
|
||||
ASSERT_NE(nullptr, runner);
|
||||
// handler
|
||||
std::shared_ptr<SecEventHandler> handler = std::make_shared<SecEventHandler>(runner);
|
||||
diag.secHandler_ = handler;
|
||||
|
||||
sptr<TestRemoteObject> testRemoteObject = new TestRemoteObject(std::u16string());
|
||||
// first use save button
|
||||
entity->componentInfo_ = std::make_shared<SaveButton>();
|
||||
entity->componentInfo_->type_ = SAVE_COMPONENT;
|
||||
entity->tokenId_ = 0;
|
||||
EXPECT_EQ(diag.NotifyFirstUseDialog(entity, testRemoteObject, testRemoteObject, displayInfo),
|
||||
SC_SERVICE_ERROR_WAIT_FOR_DIALOG_CLOSE);
|
||||
EXPECT_EQ(0, static_cast<uint64_t>(diag.firstUseMap_[0]));
|
||||
|
||||
// second use save button
|
||||
EXPECT_EQ(diag.NotifyFirstUseDialog(entity, testRemoteObject, testRemoteObject, displayInfo),
|
||||
SC_SERVICE_ERROR_WAIT_FOR_DIALOG_CLOSE);
|
||||
EXPECT_EQ(0, static_cast<uint64_t>(diag.firstUseMap_[0]));
|
||||
|
||||
entity->isCustomAuthorized_ = true;
|
||||
std::string message = "message";
|
||||
EXPECT_EQ(true, entity->AllowToBypassSecurityCheck(message));
|
||||
diag.StartToastAbility(entity, testRemoteObject, displayInfo);
|
||||
|
||||
// wait for event handler done
|
||||
sleep(3);
|
||||
}
|
||||
|
||||
/*
|
||||
* @tc.name: GrantDialogWaitEntity001
|
||||
* @tc.desc: Test GrantDialogWaitEntity
|
||||
|
||||
+3
-1
@@ -844,6 +844,8 @@ HWTEST_F(SecCompServiceMockTest, ReportSecurityComponentClickEvent002, TestSize.
|
||||
MessageParcel data;
|
||||
data.WriteInt32(scId);
|
||||
data.WriteString(locationInfo);
|
||||
std::string message = "";
|
||||
data.WriteString(message);
|
||||
sptr<SecCompClickEventParcel> parcel = new (std::nothrow) SecCompClickEventParcel();
|
||||
ASSERT_NE(nullptr, parcel);
|
||||
parcel->clickInfoParams_ = clickInfo1;
|
||||
@@ -857,4 +859,4 @@ HWTEST_F(SecCompServiceMockTest, ReportSecurityComponentClickEvent002, TestSize.
|
||||
secCompService_->ReportSecurityComponentClickEvent(nullptr, nullptr, rawdata, rawReply));
|
||||
ASSERT_TRUE(SecCompEnhanceAdapter::EnhanceSrvDeserialize(rawReply, reply));
|
||||
EXPECT_EQ(SC_OK, reply.ReadInt32());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,6 +58,12 @@ public:
|
||||
{
|
||||
return 0;
|
||||
};
|
||||
|
||||
int32_t HasCustomPermissionForSecComp(bool& hasCustomPermission) override
|
||||
{
|
||||
hasCustomPermission = false;
|
||||
return 0;
|
||||
};
|
||||
};
|
||||
|
||||
class SecCompStubMockTest : public testing::Test {
|
||||
|
||||
@@ -57,6 +57,12 @@ public:
|
||||
{
|
||||
return 0;
|
||||
};
|
||||
|
||||
int32_t HasCustomPermissionForSecComp(bool& hasCustomPermission) override
|
||||
{
|
||||
hasCustomPermission = false;
|
||||
return 0;
|
||||
};
|
||||
};
|
||||
|
||||
class SecCompStubTest : public testing::Test {
|
||||
|
||||
@@ -90,6 +90,8 @@ void ServiceTestCommon::BuildLocationComponentJson(nlohmann::json& jsonComponent
|
||||
jsonComponent[JsonTagConstants::JSON_FOREGROUND_BLUR_RADIUS_TAG] = 0.0;
|
||||
jsonComponent[JsonTagConstants::JSON_IS_OVERLAY_TEXT_SET_TAG] = false;
|
||||
jsonComponent[JsonTagConstants::JSON_IS_OVERLAY_NODE_SET_TAG] = false;
|
||||
jsonComponent[JsonTagConstants::JSON_IS_CUSTOMIZABLE] = false;
|
||||
jsonComponent[JsonTagConstants::JSON_TIP_POSITION] = TipPosition::ABOVE_BOTTOM;
|
||||
}
|
||||
|
||||
void ServiceTestCommon::BuildSaveComponentJson(nlohmann::json& jsonComponent)
|
||||
@@ -164,6 +166,8 @@ void ServiceTestCommon::BuildSaveComponentJson(nlohmann::json& jsonComponent)
|
||||
jsonComponent[JsonTagConstants::JSON_FOREGROUND_BLUR_RADIUS_TAG] = 0.0;
|
||||
jsonComponent[JsonTagConstants::JSON_IS_OVERLAY_TEXT_SET_TAG] = false;
|
||||
jsonComponent[JsonTagConstants::JSON_IS_OVERLAY_NODE_SET_TAG] = false;
|
||||
jsonComponent[JsonTagConstants::JSON_IS_CUSTOMIZABLE] = false;
|
||||
jsonComponent[JsonTagConstants::JSON_TIP_POSITION] = TipPosition::ABOVE_BOTTOM;
|
||||
}
|
||||
|
||||
void ServiceTestCommon::BuildPasteComponentJson(nlohmann::json& jsonComponent)
|
||||
@@ -238,6 +242,8 @@ void ServiceTestCommon::BuildPasteComponentJson(nlohmann::json& jsonComponent)
|
||||
jsonComponent[JsonTagConstants::JSON_FOREGROUND_BLUR_RADIUS_TAG] = 0.0;
|
||||
jsonComponent[JsonTagConstants::JSON_IS_OVERLAY_TEXT_SET_TAG] = false;
|
||||
jsonComponent[JsonTagConstants::JSON_IS_OVERLAY_NODE_SET_TAG] = false;
|
||||
jsonComponent[JsonTagConstants::JSON_IS_CUSTOMIZABLE] = false;
|
||||
jsonComponent[JsonTagConstants::JSON_TIP_POSITION] = TipPosition::ABOVE_BOTTOM;
|
||||
}
|
||||
} // namespace SecurityComponent
|
||||
} // namespace Security
|
||||
|
||||
@@ -107,6 +107,8 @@ std::string CompoRandomGenerator::ConstructLocationJson()
|
||||
jsonComponent[JsonTagConstants::JSON_FOREGROUND_BLUR_RADIUS_TAG] = 0.0;
|
||||
jsonComponent[JsonTagConstants::JSON_IS_OVERLAY_TEXT_SET_TAG] = false;
|
||||
jsonComponent[JsonTagConstants::JSON_IS_OVERLAY_NODE_SET_TAG] = false;
|
||||
jsonComponent[JsonTagConstants::JSON_IS_CUSTOMIZABLE] = false;
|
||||
jsonComponent[JsonTagConstants::JSON_TIP_POSITION] = TipPosition::ABOVE_BOTTOM;
|
||||
compoJson_ = jsonComponent;
|
||||
return compoJson_.dump();
|
||||
}
|
||||
@@ -160,6 +162,8 @@ std::string CompoRandomGenerator::ConstructSaveJson()
|
||||
jsonComponent[JsonTagConstants::JSON_FOREGROUND_BLUR_RADIUS_TAG] = 0.0;
|
||||
jsonComponent[JsonTagConstants::JSON_IS_OVERLAY_TEXT_SET_TAG] = false;
|
||||
jsonComponent[JsonTagConstants::JSON_IS_OVERLAY_NODE_SET_TAG] = false;
|
||||
jsonComponent[JsonTagConstants::JSON_IS_CUSTOMIZABLE] = false;
|
||||
jsonComponent[JsonTagConstants::JSON_TIP_POSITION] = TipPosition::ABOVE_BOTTOM;
|
||||
compoJson_ = jsonComponent;
|
||||
return compoJson_.dump();
|
||||
}
|
||||
@@ -213,6 +217,8 @@ std::string CompoRandomGenerator::ConstructPasteJson()
|
||||
jsonComponent[JsonTagConstants::JSON_FOREGROUND_BLUR_RADIUS_TAG] = 0.0;
|
||||
jsonComponent[JsonTagConstants::JSON_IS_OVERLAY_TEXT_SET_TAG] = false;
|
||||
jsonComponent[JsonTagConstants::JSON_IS_OVERLAY_NODE_SET_TAG] = false;
|
||||
jsonComponent[JsonTagConstants::JSON_IS_CUSTOMIZABLE] = false;
|
||||
jsonComponent[JsonTagConstants::JSON_TIP_POSITION] = TipPosition::ABOVE_BOTTOM;
|
||||
compoJson_ = jsonComponent;
|
||||
return compoJson_.dump();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user