1 /*
2 * Copyright (c) 2023 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 #define LOG_TAG "UtdClient"
16 #include <regex>
17 #include <thread>
18 #include <chrono>
19 #include <cinttypes>
20 #include "utd_client.h"
21 #include "logger.h"
22 #include "utd_graph.h"
23 #include "custom_utd_store.h"
24 #include "accesstoken_kit.h"
25 #include "ipc_skeleton.h"
26 #include "os_account_manager.h"
27 namespace OHOS {
28 namespace UDMF {
29 constexpr const int MAX_UTD_LENGTH = 256;
30 constexpr const int64_t RELOAD_INTERVAL = 10;
31 using namespace std::chrono;
32
UtdClient()33 UtdClient::UtdClient()
34 {
35 if (!Init()) {
36 LOG_WARN(UDMF_CLIENT, "construct UtdClient failed, try again.");
37 auto updateTask = []() {
38 std::this_thread::sleep_for(std::chrono::seconds(3));
39 UtdClient::GetInstance().Init();
40 };
41 std::thread(updateTask).detach();
42 }
43 LOG_INFO(UDMF_CLIENT, "construct UtdClient sucess.");
44 }
45
~UtdClient()46 UtdClient::~UtdClient()
47 {
48 }
49
GetInstance()50 UtdClient &UtdClient::GetInstance()
51 {
52 static auto instance = new UtdClient();
53 return *instance;
54 }
55
Init()56 bool UtdClient::Init()
57 {
58 bool result = true;
59 int32_t userId = DEFAULT_USER_ID;
60 bool isHap = IsHapTokenType();
61 if (!isHap && GetCurrentActiveUserId(userId) != Status::E_OK) {
62 result = false;
63 }
64
65 std::vector<TypeDescriptorCfg> customUtd;
66 {
67 std::lock_guard<std::mutex> lock(customUtdMutex_);
68 utdFileInfo_ = CustomUtdStore::GetInstance().GetCustomUtdInfo(isHap, userId);
69 customUtd = CustomUtdStore::GetInstance().GetCustomUtd(isHap, userId);
70 lastLoadTime_ = duration_cast<seconds>(system_clock::now().time_since_epoch()).count();
71 }
72
73 std::unique_lock<std::shared_mutex> lock(utdMutex_);
74 descriptorCfgs_ = PresetTypeDescriptors::GetInstance().GetPresetTypes();
75
76 LOG_INFO(UDMF_CLIENT, "get customUtd size:%{public}zu, file size:%{public}" PRIu64,
77 customUtd.size(), utdFileInfo_.size);
78 if (!customUtd.empty()) {
79 descriptorCfgs_.insert(descriptorCfgs_.end(), customUtd.begin(), customUtd.end());
80 }
81 UtdGraph::GetInstance().InitUtdGraph(descriptorCfgs_);
82 return result;
83 }
84
GetTypeDescriptor(const std::string & typeId,std::shared_ptr<TypeDescriptor> & descriptor)85 Status UtdClient::GetTypeDescriptor(const std::string &typeId, std::shared_ptr<TypeDescriptor> &descriptor)
86 {
87 {
88 std::shared_lock<std::shared_mutex> guard(utdMutex_);
89 for (const auto &utdTypeCfg : descriptorCfgs_) {
90 if (utdTypeCfg.typeId == typeId) {
91 descriptor = std::make_shared<TypeDescriptor>(utdTypeCfg);
92 return Status::E_OK;
93 }
94 }
95 }
96 if (typeId.find(FLEXIBLE_TYPE_FLAG) != typeId.npos) {
97 return GetFlexibleTypeDescriptor(typeId, descriptor);
98 }
99 if (TryReloadCustomUtd()) {
100 return GetTypeDescriptor(typeId, descriptor);
101 }
102 return Status::E_OK;
103 }
104
IsValidFileExtension(const std::string & fileExtension)105 bool UtdClient::IsValidFileExtension(const std::string &fileExtension)
106 {
107 if (fileExtension.empty()) {
108 return false;
109 }
110 if (fileExtension[0] != '.' || fileExtension.find("?") != fileExtension.npos ||
111 fileExtension.find(":") != fileExtension.npos || fileExtension.find("=") != fileExtension.npos ||
112 fileExtension.find("\\") != fileExtension.npos) {
113 return false;
114 }
115
116 return true;
117 }
118
IsValidMimeType(const std::string & mimeType)119 bool UtdClient::IsValidMimeType(const std::string &mimeType)
120 {
121 if (mimeType.empty()) {
122 return false;
123 }
124 if (mimeType.find("?") != mimeType.npos || mimeType.find(":") != mimeType.npos ||
125 mimeType.find("=") != mimeType.npos ||mimeType.find("\\") != mimeType.npos) {
126 return false;
127 }
128 return true;
129 }
130
GetFlexibleTypeDescriptor(const std::string & typeId,std::shared_ptr<TypeDescriptor> & descriptor)131 Status UtdClient::GetFlexibleTypeDescriptor(const std::string &typeId, std::shared_ptr<TypeDescriptor> &descriptor)
132 {
133 TypeDescriptorCfg flexibleTypeDescriptorCfg;
134 if (!FlexibleType::ParseFlexibleUtd(typeId, flexibleTypeDescriptorCfg)) {
135 LOG_ERROR(UDMF_CLIENT, "ParseFlexibleUtd failed, invalid typeId");
136 return Status::E_ERROR;
137 }
138 descriptor = std::make_shared<TypeDescriptor>(flexibleTypeDescriptorCfg);
139 return Status::E_OK;
140 }
141
GetUniformDataTypeByFilenameExtension(const std::string & fileExtension,std::string & typeId,std::string belongsTo)142 Status UtdClient::GetUniformDataTypeByFilenameExtension(const std::string &fileExtension, std::string &typeId,
143 std::string belongsTo)
144 {
145 std::string lowerFileExtension = fileExtension;
146 std::transform(lowerFileExtension.begin(), lowerFileExtension.end(), lowerFileExtension.begin(), ::tolower);
147 if (belongsTo != DEFAULT_TYPE_ID && !UtdGraph::GetInstance().IsValidType(belongsTo)) {
148 LOG_ERROR(UDMF_CLIENT, "invalid belongsTo.");
149 if (TryReloadCustomUtd()) {
150 return GetUniformDataTypeByFilenameExtension(fileExtension, typeId, belongsTo);
151 }
152 return Status::E_INVALID_PARAMETERS;
153 }
154 {
155 std::shared_lock<std::shared_mutex> guard(utdMutex_);
156 bool found = false;
157 for (const auto &utdTypeCfg : descriptorCfgs_) {
158 for (auto fileEx : utdTypeCfg.filenameExtensions) {
159 std::transform(fileEx.begin(), fileEx.end(), fileEx.begin(), ::tolower);
160 if (fileEx == lowerFileExtension && IsBelongingType(belongsTo, utdTypeCfg.typeId)) {
161 typeId = utdTypeCfg.typeId;
162 found = true;
163 break;
164 }
165 }
166 if (found) {
167 break;
168 }
169 }
170 }
171 if (typeId.empty() && TryReloadCustomUtd()) {
172 return GetUniformDataTypeByFilenameExtension(fileExtension, typeId, belongsTo);
173 }
174 if (typeId.empty()) {
175 if (!IsValidFileExtension(lowerFileExtension)) {
176 LOG_ERROR(UDMF_CLIENT, "invalid fileExtension.");
177 return Status::E_INVALID_PARAMETERS;
178 }
179 typeId = FlexibleType::GenFlexibleUtd("", lowerFileExtension, belongsTo);
180 }
181 return Status::E_OK;
182 }
183
GetUniformDataTypesByFilenameExtension(const std::string & fileExtension,std::vector<std::string> & typeIds,const std::string & belongsTo)184 Status UtdClient::GetUniformDataTypesByFilenameExtension(const std::string &fileExtension,
185 std::vector<std::string> &typeIds, const std::string &belongsTo)
186 {
187 if (belongsTo != DEFAULT_TYPE_ID && !UtdGraph::GetInstance().IsValidType(belongsTo)) {
188 LOG_ERROR(UDMF_CLIENT, "invalid belongsTo.");
189 if (TryReloadCustomUtd()) {
190 return GetUniformDataTypesByFilenameExtension(fileExtension, typeIds, belongsTo);
191 }
192 return Status::E_INVALID_PARAMETERS;
193 }
194 if (!IsValidFileExtension(fileExtension)) {
195 LOG_ERROR(UDMF_CLIENT, "invalid fileExtension.");
196 return Status::E_INVALID_PARAMETERS;
197 }
198
199 std::string lowerFileExtension = fileExtension;
200 std::transform(lowerFileExtension.begin(), lowerFileExtension.end(), lowerFileExtension.begin(), ::tolower);
201 std::vector<std::string> typeIdsInCfg;
202 {
203 std::shared_lock<std::shared_mutex> guard(utdMutex_);
204 for (const auto &utdTypeCfg : descriptorCfgs_) {
205 for (auto fileEx : utdTypeCfg.filenameExtensions) {
206 std::transform(fileEx.begin(), fileEx.end(), fileEx.begin(), ::tolower);
207 if (fileEx == lowerFileExtension) {
208 typeIdsInCfg.push_back(utdTypeCfg.typeId);
209 break;
210 }
211 }
212 }
213 }
214 if (typeIdsInCfg.empty() && TryReloadCustomUtd()) {
215 return GetUniformDataTypesByFilenameExtension(fileExtension, typeIds, belongsTo);
216 }
217 typeIds.clear();
218 for (const auto &typeId : typeIdsInCfg) {
219 // the find typeId is not belongsTo to the belongsTo.
220 if (!IsBelongingType(belongsTo, typeId)) {
221 continue;
222 }
223 typeIds.emplace_back(typeId);
224 }
225 if (typeIds.empty()) {
226 typeIds.emplace_back(FlexibleType::GenFlexibleUtd("", lowerFileExtension, belongsTo));
227 }
228 return Status::E_OK;
229 }
230
GetUniformDataTypeByMIMEType(const std::string & mimeType,std::string & typeId,std::string belongsTo)231 Status UtdClient::GetUniformDataTypeByMIMEType(const std::string &mimeType, std::string &typeId,
232 std::string belongsTo)
233 {
234 std::string lowerMimeType = mimeType;
235 std::transform(lowerMimeType.begin(), lowerMimeType.end(), lowerMimeType.begin(), ::tolower);
236 if (belongsTo != DEFAULT_TYPE_ID && !UtdGraph::GetInstance().IsValidType(belongsTo)) {
237 LOG_ERROR(UDMF_CLIENT, "invalid belongsTo.");
238 if (TryReloadCustomUtd()) {
239 return GetUniformDataTypeByMIMEType(mimeType, typeId, belongsTo);
240 }
241 return Status::E_INVALID_PARAMETERS;
242 }
243 typeId = GetTypeIdFromCfg(lowerMimeType, belongsTo);
244 if (typeId.empty()) {
245 if (!IsValidMimeType(mimeType)) {
246 LOG_ERROR(UDMF_CLIENT, "invalid mimeType.");
247 return Status::E_INVALID_PARAMETERS;
248 }
249 typeId = FlexibleType::GenFlexibleUtd(lowerMimeType, "", belongsTo);
250 }
251 return Status::E_OK;
252 }
253
GetTypeIdFromCfg(const std::string & mimeType,const std::string & belongsTo)254 std::string UtdClient::GetTypeIdFromCfg(const std::string &mimeType, const std::string &belongsTo)
255 {
256 if (mimeType.empty()) {
257 return "";
258 }
259 {
260 std::shared_lock<std::shared_mutex> guard(utdMutex_);
261 for (const auto &utdTypeCfg : descriptorCfgs_) {
262 for (auto mime : utdTypeCfg.mimeTypes) {
263 std::transform(mime.begin(), mime.end(), mime.begin(), ::tolower);
264 if (mime == mimeType && IsBelongingType(belongsTo, utdTypeCfg.typeId)) {
265 return utdTypeCfg.typeId;
266 }
267 }
268 }
269 }
270 if (mimeType.back() != '*') {
271 if (TryReloadCustomUtd()) {
272 return GetTypeIdFromCfg(mimeType, belongsTo);
273 }
274 return "";
275 }
276 {
277 std::shared_lock<std::shared_mutex> guard(utdMutex_);
278 std::string prefixType = mimeType.substr(0, mimeType.length() - 1);
279 for (const auto &utdTypeCfg : descriptorCfgs_) {
280 for (auto mime : utdTypeCfg.mimeTypes) {
281 std::transform(mime.begin(), mime.end(), mime.begin(), ::tolower);
282 if (mime.rfind(prefixType, 0) == 0 && utdTypeCfg.belongingToTypes.size() > 0
283 && IsBelongingType(belongsTo, utdTypeCfg.typeId)) {
284 return utdTypeCfg.belongingToTypes[0];
285 }
286 }
287 }
288 }
289 if (TryReloadCustomUtd()) {
290 return GetTypeIdFromCfg(mimeType);
291 }
292 return "";
293 }
294
GetUniformDataTypesByMIMEType(const std::string & mimeType,std::vector<std::string> & typeIds,const std::string & belongsTo)295 Status UtdClient::GetUniformDataTypesByMIMEType(const std::string &mimeType, std::vector<std::string> &typeIds,
296 const std::string &belongsTo)
297 {
298 if (belongsTo != DEFAULT_TYPE_ID && !UtdGraph::GetInstance().IsValidType(belongsTo)) {
299 LOG_ERROR(UDMF_CLIENT, "invalid belongsTo.");
300 if (TryReloadCustomUtd()) {
301 return GetUniformDataTypesByMIMEType(mimeType, typeIds, belongsTo);
302 }
303 return Status::E_INVALID_PARAMETERS;
304 }
305 if (!IsValidMimeType(mimeType)) {
306 LOG_ERROR(UDMF_CLIENT, "invalid mimeType.");
307 return Status::E_INVALID_PARAMETERS;
308 }
309
310 std::string lowerMimeType = mimeType;
311 std::transform(lowerMimeType.begin(), lowerMimeType.end(), lowerMimeType.begin(), ::tolower);
312 std::vector<std::string> typeIdsInCfg = GetTypeIdsFromCfg(lowerMimeType);
313 typeIds.clear();
314 for (const auto &typeId : typeIdsInCfg) {
315 // the find typeId is not belongsTo to the belongsTo.
316 if (!IsBelongingType(belongsTo, typeId)) {
317 continue;
318 }
319 typeIds.emplace_back(typeId);
320 }
321 if (typeIds.empty()) {
322 typeIds.emplace_back(FlexibleType::GenFlexibleUtd(lowerMimeType, "", belongsTo));
323 }
324 return Status::E_OK;
325 }
326
GetTypeIdsFromCfg(const std::string & mimeType)327 std::vector<std::string> UtdClient::GetTypeIdsFromCfg(const std::string &mimeType)
328 {
329 bool prefixMatch = false;
330 std::string prefixType;
331 if (!mimeType.empty() && mimeType.back() == '*') {
332 prefixType = mimeType.substr(0, mimeType.length() - 1);
333 prefixMatch = true;
334 }
335 std::vector<std::string> typeIdsInCfg;
336
337 {
338 std::shared_lock<std::shared_mutex> guard(utdMutex_);
339 for (const auto &utdTypeCfg : descriptorCfgs_) {
340 for (auto mime : utdTypeCfg.mimeTypes) {
341 std::transform(mime.begin(), mime.end(), mime.begin(), ::tolower);
342 if ((mime == mimeType) || (prefixMatch && mime.rfind(prefixType, 0) == 0)) {
343 typeIdsInCfg.push_back(utdTypeCfg.typeId);
344 break;
345 }
346 }
347 }
348 }
349 if (typeIdsInCfg.empty() && TryReloadCustomUtd()) {
350 return GetTypeIdsFromCfg(mimeType);
351 }
352 return typeIdsInCfg;
353 }
354
IsUtd(std::string typeId,bool & result)355 Status UtdClient::IsUtd(std::string typeId, bool &result)
356 {
357 try {
358 if (typeId.empty() || typeId.size() > MAX_UTD_LENGTH) {
359 result = false;
360 return Status::E_INVALID_PARAMETERS;
361 }
362 if (typeId[0] == '.' || find(typeId.begin(), typeId.end(), '/') != typeId.end()) {
363 result = false;
364 return Status::E_OK;
365 }
366 constexpr const char *preSetTypeIdRegexRule =
367 R"(^(general\.|openharmony\.|org\.|com\.|macos\.|debian\.|redhat\.|io\.|de\.|net\.)[a-z0-9-\.]+(\-[a-z0-9-]+)*$)";
368 if (std::regex_match(typeId, std::regex(preSetTypeIdRegexRule))) {
369 result = true;
370 return Status::E_OK;
371 }
372 constexpr const char *customUtdRegexRule = R"(^([A-Za-z]\w*)(\.\w+)+(\.[A-Za-z\d-]+)+)";
373 if (std::regex_match(typeId, std::regex(customUtdRegexRule))) {
374 result = true;
375 return Status::E_OK;
376 }
377 result = false;
378 } catch (...) {
379 LOG_ERROR(UDMF_CLIENT, "throw error");
380 result = false;
381 return Status::E_ERROR;
382 }
383 LOG_ERROR(UDMF_CLIENT, "is not utd");
384 return Status::E_OK;
385 }
386
IsHapTokenType()387 bool UtdClient::IsHapTokenType()
388 {
389 uint32_t tokenId = IPCSkeleton::GetSelfTokenID();
390 auto tokenType = Security::AccessToken::AccessTokenKit::GetTokenTypeFlag(tokenId);
391 LOG_DEBUG(UDMF_CLIENT, "GetTokenTypeFlag, tokenType = %{public}d.", tokenType);
392 if (tokenType == Security::AccessToken::TypeATokenTypeEnum::TOKEN_HAP) {
393 return true;
394 }
395 return false;
396 }
397
GetCurrentActiveUserId(int32_t & userId)398 Status UtdClient::GetCurrentActiveUserId(int32_t& userId)
399 {
400 std::vector<int32_t> localIds;
401 int32_t status = OHOS::AccountSA::OsAccountManager::QueryActiveOsAccountIds(localIds);
402 if (status != Status::E_OK || localIds.empty()) {
403 LOG_ERROR(UDMF_CLIENT, "Get OsAccountId fail, status:%{public}d", status);
404 return Status::E_ERROR;
405 }
406 userId = localIds[0];
407 return Status::E_OK;
408 }
409
InstallCustomUtds(const std::string & bundleName,const std::string & jsonStr,int32_t user)410 void UtdClient::InstallCustomUtds(const std::string &bundleName, const std::string &jsonStr, int32_t user)
411 {
412 if (IsHapTokenType()) {
413 return;
414 }
415 LOG_INFO(UDMF_CLIENT, "start, bundleName:%{public}s, user:%{public}d", bundleName.c_str(), user);
416 std::lock_guard<std::mutex> lock(updateUtdMutex_);
417 std::vector<TypeDescriptorCfg> customTyepCfgs = CustomUtdStore::GetInstance().GetCustomUtd(false, user);
418 if (!CustomUtdStore::GetInstance().UninstallCustomUtds(bundleName, user, customTyepCfgs)) {
419 LOG_ERROR(UDMF_CLIENT, "custom utd installed failed. bundleName:%{public}s, user:%{public}d",
420 bundleName.c_str(), user);
421 return;
422 }
423 UpdateGraph(customTyepCfgs);
424 if (!jsonStr.empty()) {
425 if (!CustomUtdStore::GetInstance().InstallCustomUtds(bundleName, jsonStr, user, customTyepCfgs)) {
426 LOG_ERROR(UDMF_CLIENT, "no custom utd installed. bundleName:%{public}s, user:%{public}d",
427 bundleName.c_str(), user);
428 return;
429 }
430 UpdateGraph(customTyepCfgs);
431 }
432 }
433
UninstallCustomUtds(const std::string & bundleName,int32_t user)434 void UtdClient::UninstallCustomUtds(const std::string &bundleName, int32_t user)
435 {
436 if (IsHapTokenType()) {
437 return;
438 }
439 LOG_INFO(UDMF_CLIENT, "start, bundleName:%{public}s, user:%{public}d", bundleName.c_str(), user);
440 std::lock_guard<std::mutex> lock(updateUtdMutex_);
441 std::vector<TypeDescriptorCfg> customTyepCfgs = CustomUtdStore::GetInstance().GetCustomUtd(false, user);
442 if (!CustomUtdStore::GetInstance().UninstallCustomUtds(bundleName, user, customTyepCfgs)) {
443 LOG_ERROR(UDMF_CLIENT, "custom utd installed failed. bundleName:%{public}s, user:%{public}d",
444 bundleName.c_str(), user);
445 return;
446 }
447 UpdateGraph(customTyepCfgs);
448 }
449
UpdateGraph(const std::vector<TypeDescriptorCfg> & customTyepCfgs)450 void UtdClient::UpdateGraph(const std::vector<TypeDescriptorCfg> &customTyepCfgs)
451 {
452 std::vector<TypeDescriptorCfg> allTypeCfgs = PresetTypeDescriptors::GetInstance().GetPresetTypes();
453 allTypeCfgs.insert(allTypeCfgs.end(), customTyepCfgs.begin(), customTyepCfgs.end());
454 LOG_INFO(UDMF_CLIENT, "customTyepSize:%{public}zu, allTypeSize:%{public}zu",
455 customTyepCfgs.size(), allTypeCfgs.size());
456 auto graph = UtdGraph::GetInstance().ConstructNewGraph(allTypeCfgs);
457 std::unique_lock<std::shared_mutex> lock(utdMutex_);
458 UtdGraph::GetInstance().Update(std::move(graph));
459 descriptorCfgs_ = allTypeCfgs;
460 }
461
TryReloadCustomUtd()462 bool UtdClient::TryReloadCustomUtd()
463 {
464 {
465 std::lock_guard<std::mutex> lock(customUtdMutex_);
466 auto now = duration_cast<seconds>(system_clock::now().time_since_epoch()).count();
467 auto delay = now - lastLoadTime_;
468 if (delay <= RELOAD_INTERVAL) {
469 LOG_DEBUG(UDMF_CLIENT, "interval time too short");
470 return false;
471 }
472 lastLoadTime_ = now;
473 }
474
475 bool isHap = IsHapTokenType();
476 int32_t userId = DEFAULT_USER_ID;
477 if (!isHap) {
478 int32_t status = GetCurrentActiveUserId(userId);
479 LOG_DEBUG(UDMF_CLIENT, "get user id status=%{public}d", status);
480 if (status != Status::E_OK) {
481 return false;
482 }
483 }
484 UtdFileInfo info = CustomUtdStore::GetInstance().GetCustomUtdInfo(isHap, userId);
485 if (info.size == utdFileInfo_.size && info.lastTime == utdFileInfo_.lastTime) {
486 LOG_DEBUG(UDMF_CLIENT, "utd file not change");
487 return false;
488 }
489 LOG_INFO(UDMF_CLIENT, "utd file has change, try reload custom utd info");
490 if (Init()) {
491 std::lock_guard<std::mutex> lock(customUtdMutex_);
492 utdFileInfo_ = info;
493 return true;
494 }
495 return false;
496 }
497
IsBelongingType(const std::string & belongsTo,const std::string & typeId) const498 bool UtdClient::IsBelongingType(const std::string &belongsTo, const std::string &typeId) const
499 {
500 return belongsTo == DEFAULT_TYPE_ID ||
501 belongsTo == typeId ||
502 UtdGraph::GetInstance().IsLowerLevelType(belongsTo, typeId);
503 }
504 } // namespace UDMF
505 } // namespace OHOS
506