1 /*
2 * Copyright (c) 2025 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "pasteboard_disposable_manager.h"
17 #include <thread>
18
19 #include "ffrt/ffrt_utils.h"
20 #include "ipc_skeleton.h"
21 #include "parameters.h"
22 #include "pasteboard_error.h"
23 #include "pasteboard_hilog.h"
24 #include "permission/permission_utils.h"
25
26 namespace OHOS::MiscServices {
GetInstance()27 DisposableManager &DisposableManager::GetInstance()
28 {
29 static DisposableManager instance;
30 return instance;
31 }
32
ProcessNoMatchInfo(const std::vector<DisposableInfo> & noMatchInfoList)33 void DisposableManager::ProcessNoMatchInfo(const std::vector<DisposableInfo> &noMatchInfoList)
34 {
35 for (const auto &item : noMatchInfoList) {
36 if (item.observer == nullptr) {
37 PASTEBOARD_HILOGE(PASTEBOARD_MODULE_SERVICE, "observer is null, pid=%{public}d", item.pid);
38 continue;
39 }
40 item.observer->OnTextReceived("", IPasteboardDisposableObserver::ERR_TARGET_MISMATCH);
41 }
42 }
43
ProcessMatchedInfo(const std::vector<DisposableInfo> & matchedInfoList,PasteData & pasteData,const sptr<IPasteboardDelayGetter> & delayGetter,const sptr<IPasteboardEntryGetter> & entryGetter)44 void DisposableManager::ProcessMatchedInfo(const std::vector<DisposableInfo> &matchedInfoList, PasteData &pasteData,
45 const sptr<IPasteboardDelayGetter> &delayGetter, const sptr<IPasteboardEntryGetter> &entryGetter)
46 {
47 std::string text = GetPlainText(pasteData, delayGetter, entryGetter);
48 PASTEBOARD_HILOGI(PASTEBOARD_MODULE_SERVICE, "textLen=%{public}zu", text.length());
49
50 for (const auto &item : matchedInfoList) {
51 if (item.observer == nullptr) {
52 PASTEBOARD_HILOGE(PASTEBOARD_MODULE_SERVICE, "observer is null, pid=%{public}d", item.pid);
53 continue;
54 }
55 if (pasteData.GetShareOption() == ShareOption::InApp) {
56 item.observer->OnTextReceived("", IPasteboardDisposableObserver::ERR_DATA_IN_APP);
57 continue;
58 }
59 if (item.type != DisposableType::PLAIN_TEXT) {
60 item.observer->OnTextReceived("", IPasteboardDisposableObserver::ERR_TYPE_NOT_SUPPORT);
61 continue;
62 }
63 if (!PermissionUtils::IsPermissionGranted(PermissionUtils::PERMISSION_READ_PASTEBOARD, item.tokenId)) {
64 item.observer->OnTextReceived("", IPasteboardDisposableObserver::ERR_NO_PERMISSION);
65 continue;
66 }
67 if (text.empty()) {
68 item.observer->OnTextReceived("", IPasteboardDisposableObserver::ERR_NO_TEXT);
69 continue;
70 }
71 if (text.length() > item.maxLen) {
72 item.observer->OnTextReceived("", IPasteboardDisposableObserver::ERR_LENGTH_MISMATCH);
73 continue;
74 }
75 item.observer->OnTextReceived(text, IPasteboardDisposableObserver::ERR_OK);
76 }
77 }
78
TryProcessDisposableData(const std::string & bundleName,PasteData & pasteData,const sptr<IPasteboardDelayGetter> & delayGetter,const sptr<IPasteboardEntryGetter> & entryGetter)79 bool DisposableManager::TryProcessDisposableData(const std::string &bundleName, PasteData &pasteData,
80 const sptr<IPasteboardDelayGetter> &delayGetter, const sptr<IPasteboardEntryGetter> &entryGetter)
81 {
82 std::vector<DisposableInfo> matchedInfoList;
83 std::vector<DisposableInfo> noMatchInfoList;
84 {
85 std::lock_guard lock(disposableInfoMutex_);
86 PASTEBOARD_CHECK_AND_RETURN_RET_LOGD(!disposableInfoList_.empty(), false, PASTEBOARD_MODULE_SERVICE,
87 "no disposable observer");
88
89 std::partition_copy(disposableInfoList_.begin(), disposableInfoList_.end(),
90 std::back_inserter(matchedInfoList), std::back_inserter(noMatchInfoList),
91 [bundleName](const DisposableInfo &info) { return info.targetBundleName == bundleName; });
92 disposableInfoList_.clear();
93 }
94
95 ProcessNoMatchInfo(noMatchInfoList);
96 noMatchInfoList.clear();
97
98 PASTEBOARD_CHECK_AND_RETURN_RET_LOGE(!matchedInfoList.empty(), false, PASTEBOARD_MODULE_SERVICE,
99 "no matched disposable observer, bundleName=%{public}s", bundleName.c_str());
100 ProcessMatchedInfo(matchedInfoList, pasteData, delayGetter, entryGetter);
101 return true;
102 }
103
GetPlainText(PasteData & pasteData,const sptr<IPasteboardDelayGetter> & delayGetter,const sptr<IPasteboardEntryGetter> & entryGetter)104 std::string DisposableManager::GetPlainText(PasteData &pasteData,
105 const sptr<IPasteboardDelayGetter> &delayGetter, const sptr<IPasteboardEntryGetter> &entryGetter)
106 {
107 if (pasteData.IsDelayData() && delayGetter != nullptr) {
108 delayGetter->GetPasteData("", pasteData);
109 }
110
111 std::string allText;
112 auto mimeTypes = pasteData.GetMimeTypes();
113 auto iter = std::find(mimeTypes.begin(), mimeTypes.end(), MIMETYPE_TEXT_PLAIN);
114 PASTEBOARD_CHECK_AND_RETURN_RET_LOGE(iter != mimeTypes.end(), allText, PASTEBOARD_MODULE_SERVICE, "no plain text");
115
116 for (auto record : pasteData.AllRecords()) {
117 if (record == nullptr) {
118 continue;
119 }
120 auto entry = record->GetEntryByMimeType(MIMETYPE_TEXT_PLAIN);
121 if (entry == nullptr) {
122 continue;
123 }
124 std::string utdId = entry->GetUtdId();
125 if (pasteData.IsDelayRecord() && entryGetter != nullptr && !entry->HasContent(utdId)) {
126 int32_t ret = entryGetter->GetRecordValueByType(record->GetRecordId(), *entry);
127 if (ret != static_cast<int32_t>(PasteboardError::E_OK)) {
128 PASTEBOARD_HILOGE(PASTEBOARD_MODULE_SERVICE, "get entry failed, recordId=%{public}u, type=%{public}s",
129 record->GetRecordId(), utdId.c_str());
130 continue;
131 }
132 }
133 std::shared_ptr<std::string> text = entry->ConvertToPlainText();
134 if (text == nullptr) {
135 PASTEBOARD_HILOGE(PASTEBOARD_MODULE_SERVICE, "to text failed, recordId=%{public}u", record->GetRecordId());
136 continue;
137 }
138 allText += (*text);
139 }
140 return allText;
141 }
142
AddDisposableInfo(const DisposableInfo & info)143 int32_t DisposableManager::AddDisposableInfo(const DisposableInfo &info)
144 {
145 PASTEBOARD_CHECK_AND_RETURN_RET_LOGE(info.observer != nullptr,
146 static_cast<int32_t>(PasteboardError::INVALID_PARAM_ERROR), PASTEBOARD_MODULE_SERVICE,
147 "param invalid, observer is null");
148 int32_t typeInt = static_cast<int32_t>(info.type);
149 PASTEBOARD_CHECK_AND_RETURN_RET_LOGE(typeInt >= 0 && typeInt < static_cast<int32_t>(DisposableType::MAX),
150 static_cast<int32_t>(PasteboardError::INVALID_PARAM_ERROR), PASTEBOARD_MODULE_SERVICE,
151 "param invalid, type=%{public}d", typeInt);
152
153 bool hasPerm = PermissionUtils::IsPermissionGranted(PermissionUtils::PERMISSION_READ_PASTEBOARD, info.tokenId);
154 PASTEBOARD_CHECK_AND_RETURN_RET_LOGE(hasPerm,
155 static_cast<int32_t>(PasteboardError::PERMISSION_VERIFICATION_ERROR), PASTEBOARD_MODULE_SERVICE,
156 "check permission failed, pid=%{public}d, tokenId=%{public}u", info.pid, info.tokenId);
157
158 PASTEBOARD_HILOGI(PASTEBOARD_MODULE_SERVICE, "pid=%{public}d, bundleName=%{public}s, type=%{public}d, "
159 "maxLen=%{public}u", info.pid, info.targetBundleName.c_str(), typeInt, info.maxLen);
160
161 FFRTTask expirationTask = [this, pid = info.pid] {
162 std::thread thread([=]() {
163 RemoveDisposableInfo(pid, true);
164 });
165 thread.detach();
166 };
167 std::string taskName = "disposable_expiration[pid=" + std::to_string(info.pid) + "]";
168 int32_t timeout = system::GetIntParameter("pasteboard.disposable_expiration", DISPOSABLE_EXPIRATION_DEFAULT,
169 DISPOSABLE_EXPIRATION_MIN, DISPOSABLE_EXPIRATION_MAX);
170 auto timer = FFRTPool::GetTimer("pasteboard_service");
171 timer->SetTimer(taskName, expirationTask, timeout);
172
173 std::lock_guard lock(disposableInfoMutex_);
174 auto iter = std::find_if(disposableInfoList_.begin(), disposableInfoList_.end(),
175 [pid = info.pid](const DisposableInfo &item) { return item.pid == pid; });
176 if (iter == disposableInfoList_.end()) {
177 disposableInfoList_.push_back(info);
178 } else {
179 *iter = info;
180 }
181 return static_cast<int32_t>(PasteboardError::E_OK);
182 }
183
RemoveDisposableInfo(pid_t pid,bool needNotify)184 void DisposableManager::RemoveDisposableInfo(pid_t pid, bool needNotify)
185 {
186 std::lock_guard lock(disposableInfoMutex_);
187 auto iter = std::find_if(disposableInfoList_.begin(), disposableInfoList_.end(),
188 [pid](const DisposableInfo &info) { return info.pid == pid; });
189 PASTEBOARD_CHECK_AND_RETURN_LOGD(iter != disposableInfoList_.end(), PASTEBOARD_MODULE_SERVICE,
190 "disposable info not find, pid=%{public}d", pid);
191
192 if (needNotify) {
193 if (iter->observer == nullptr) {
194 PASTEBOARD_HILOGE(PASTEBOARD_MODULE_SERVICE, "observer is null, pid=%{public}d", pid);
195 } else {
196 iter->observer->OnTextReceived("", IPasteboardDisposableObserver::ERR_TIMEOUT);
197 }
198 }
199
200 disposableInfoList_.erase(iter);
201 }
202
203 } // namespace OHOS::MiscServices