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 #include "theme_pack_manager.h"
16
17 #include "auto_mutex.h"
18 #include <dirent.h>
19 #include <cstdio>
20 #include <cstdlib>
21 #include <cstring>
22 #include "hilog_wrapper.h"
23 #include "theme_pack_resource.h"
24 #include <securec.h>
25 #include "utils/utils.h"
26
27 namespace OHOS {
28 namespace Global {
29 namespace Resource {
30 constexpr int FIRST_ELEMENT = 0;
31 constexpr int SECOND_ELEMENT = 1;
32 constexpr int THIRED_ELEMENT = 2;
33 static std::shared_ptr<ThemePackManager> themeMgr = nullptr;
34 static std::once_flag themeMgrFlag;
35 constexpr uint32_t SYSTEM_ID_BEGIN = 117440512; // 0x07000000
36 constexpr uint32_t SYSTEM_ID_END = 134217727; // 0x07FFFFFF
37 const std::string themeFlagA = "data/themes/a/app/flag";
38 const std::string themeFlagB = "data/themes/b/app/flag";
39 const std::string themeSkinA = "/data/themes/a/app/skin";
40 const std::string themeSkinB = "/data/themes/b/app/skin";
41 const std::string themeIconsA = "/data/themes/a/app/icons";
42 const std::string themeIconsB = "/data/themes/b/app/icons";
43 const std::string absoluteThemeFlagA = "data/service/el1/public/themes/<currentUserId>/a/app/flag";
44 const std::string absoluteThemeFlagB = "data/service/el1/public/themes/<currentUserId>/b/app/flag";
45 const std::string absoluteThemeSkinA = "/data/service/el1/public/themes/<currentUserId>/a/app/skin";
46 const std::string absoluteThemeSkinB = "/data/service/el1/public/themes/<currentUserId>/b/app/skin";
47 const std::string absoluteThemeIconsA = "/data/service/el1/public/themes/<currentUserId>/a/app/icons";
48 const std::string absoluteThemeIconsB = "/data/service/el1/public/themes/<currentUserId>/b/app/icons";
ThemePackManager()49 ThemePackManager::ThemePackManager()
50 {}
51
~ThemePackManager()52 ThemePackManager::~ThemePackManager()
53 {
54 skinResource_.clear();
55 iconResource_.clear();
56 iconMaskValues_.clear();
57 }
58
GetThemePackManager()59 std::shared_ptr<ThemePackManager> ThemePackManager::GetThemePackManager()
60 {
61 std::call_once(themeMgrFlag, [&] {
62 themeMgr = std::shared_ptr<ThemePackManager>(new ThemePackManager());
63 });
64 return themeMgr;
65 }
66
GetRootDir(const std::string & strCurrentDir)67 std::vector<std::string> ThemePackManager::GetRootDir(const std::string &strCurrentDir)
68 {
69 std::vector<std::string> vDir;
70 #if !defined(__WINNT__) && !defined(__IDE_PREVIEW__) && !defined(__ARKUI_CROSS__)
71 DIR *dir;
72 struct dirent *pDir;
73 if ((dir = opendir(strCurrentDir.c_str())) == nullptr) {
74 return vDir;
75 }
76 while ((pDir = readdir(dir)) != nullptr) {
77 if (strcmp(pDir->d_name, ".") == 0 || strcmp(pDir->d_name, "..") == 0) {
78 continue;
79 } else if (pDir->d_type == 4) { // 4 means dir
80 std::string strNextDir = strCurrentDir + "/" + pDir->d_name;
81 vDir.emplace_back(strNextDir);
82 } else if (pDir->d_type == 8) { // 8 means file
83 std::string filePath = strCurrentDir + "/" + pDir->d_name;
84 if (filePath.find("icon_mask") != std::string::npos) {
85 themeMask = filePath;
86 }
87 }
88 }
89 closedir(dir);
90 #endif
91 return vDir;
92 }
93
ClearSkinResource()94 void ThemePackManager::ClearSkinResource()
95 {
96 for (auto it = skinResource_.begin(); it != skinResource_.end();) {
97 if ((*it) == nullptr) {
98 continue;
99 }
100 // 1 means get the enable theme
101 if (!(*it)->IsNewResource()) {
102 it = skinResource_.erase(it);
103 } else {
104 ++it;
105 }
106 }
107 }
108
LoadThemeSkinResource(const std::string & bundleName,const std::string & moduleName,const std::vector<std::string> & rootDirs,int32_t userId)109 void ThemePackManager::LoadThemeSkinResource(const std::string &bundleName, const std::string &moduleName,
110 const std::vector<std::string> &rootDirs, int32_t userId)
111 {
112 AutoMutex mutex(this->lockSkin_);
113 for (size_t i = 0; i < skinResource_.size(); ++i) {
114 auto pThemeResource = skinResource_[i];
115 if (pThemeResource == nullptr) {
116 continue;
117 }
118 pThemeResource->SetNewResource(false);
119 }
120 if (rootDirs.empty()) {
121 ClearSkinResource();
122 return;
123 }
124 for (const auto &dir : rootDirs) {
125 auto pos = dir.rfind('/');
126 if (pos == std::string::npos) {
127 RESMGR_HILOGE(RESMGR_TAG, "invalid dir = %{public}s in LoadThemeSkinResource", dir.c_str());
128 continue;
129 }
130 std::string tempBundleName = dir.substr(pos + 1);
131 if (tempBundleName != bundleName && tempBundleName != "systemRes") {
132 continue;
133 }
134 auto pThemeResource = ThemeResource::LoadThemeResource(dir);
135 if (pThemeResource != nullptr) {
136 this->skinResource_.emplace_back(pThemeResource);
137 }
138 }
139 ClearSkinResource();
140 }
141
LoadThemeRes(const std::string & bundleName,const std::string & moduleName,int32_t userId)142 void ThemePackManager::LoadThemeRes(const std::string &bundleName, const std::string &moduleName, int32_t userId)
143 {
144 UpdateUserId(userId);
145 std::vector<std::string> rootDirs;
146 std::vector<std::string> iconDirs;
147 if (Utils::IsFileExist(themeFlagA)) {
148 rootDirs = GetRootDir(themeSkinA);
149 iconDirs = GetRootDir(themeIconsA);
150 } else if (Utils::IsFileExist(themeFlagB)) {
151 rootDirs = GetRootDir(themeSkinB);
152 iconDirs = GetRootDir(themeIconsB);
153 } else {
154 LoadSAThemeRes(bundleName, moduleName, userId, rootDirs, iconDirs);
155 }
156 LoadThemeSkinResource(bundleName, moduleName, rootDirs, userId);
157 LoadThemeIconsResource(bundleName, moduleName, iconDirs, userId);
158 return;
159 }
160
LoadSAThemeRes(const std::string & bundleName,const std::string & moduleName,int32_t userId,std::vector<std::string> & rootDirs,std::vector<std::string> & iconDirs)161 void ThemePackManager::LoadSAThemeRes(const std::string &bundleName, const std::string &moduleName,
162 int32_t userId, std::vector<std::string> &rootDirs, std::vector<std::string> &iconDirs)
163 {
164 if (Utils::IsFileExist(ReplaceUserIdInPath(absoluteThemeFlagA, userId))) {
165 rootDirs = GetRootDir(ReplaceUserIdInPath(absoluteThemeSkinA, userId));
166 iconDirs = GetRootDir(ReplaceUserIdInPath(absoluteThemeIconsA, userId));
167 } else if (Utils::IsFileExist(ReplaceUserIdInPath(absoluteThemeFlagB, userId))) {
168 rootDirs = GetRootDir(ReplaceUserIdInPath(absoluteThemeSkinB, userId));
169 iconDirs = GetRootDir(ReplaceUserIdInPath(absoluteThemeIconsB, userId));
170 }
171 return;
172 }
173
ReplaceUserIdInPath(const std::string & originalPath,int32_t userId)174 const std::string ThemePackManager::ReplaceUserIdInPath(const std::string &originalPath, int32_t userId)
175 {
176 std::string result = originalPath;
177 auto found = result.find("<currentUserId>");
178 if (found != std::string::npos) {
179 result.replace(found, 15, std::to_string(userId)); // 15 is the length of "<currentUserId>"
180 }
181 return result;
182 }
183
FindThemeResource(const std::pair<std::string,std::string> & bundleInfo,std::vector<std::shared_ptr<IdItem>> idItems,const ResConfigImpl & resConfig,bool isThemeSystemResEnable)184 const std::string ThemePackManager::FindThemeResource(const std::pair<std::string, std::string> &bundleInfo,
185 std::vector<std::shared_ptr<IdItem>> idItems, const ResConfigImpl &resConfig, bool isThemeSystemResEnable)
186 {
187 std::string result;
188 for (size_t i = 0; i < idItems.size(); i++) {
189 std::string resName = idItems[i]->GetItemResName();
190 uint32_t id = idItems[i]->GetItemResId();
191 ResType resType = idItems[i]->GetItemResType();
192 if (id >= SYSTEM_ID_BEGIN && id <= SYSTEM_ID_END) {
193 if (resType == ResType::COLOR && !isThemeSystemResEnable) {
194 break;
195 }
196 std::pair<std::string, std::string> tempInfo("systemRes", "entry");
197 result = GetThemeResource(tempInfo, resType, resName, resConfig);
198 } else {
199 result = GetThemeResource(bundleInfo, resType, resName, resConfig);
200 }
201 if (!result.empty()) {
202 break;
203 }
204 }
205 return result;
206 }
207
GetThemeResource(const std::pair<std::string,std::string> & bundInfo,const ResType & resType,const std::string & resName,const ResConfigImpl & resConfig)208 const std::string ThemePackManager::GetThemeResource(const std::pair<std::string, std::string> &bundInfo,
209 const ResType &resType, const std::string &resName, const ResConfigImpl &resConfig)
210 {
211 auto themeQualifierValue = GetThemeQualifierValue(bundInfo, resType, resName, resConfig);
212 if (themeQualifierValue == nullptr) {
213 RESMGR_HILOGD(RESMGR_TAG, "themeQualifierValue == nullptr");
214 return std::string("");
215 }
216 return themeQualifierValue->GetResValue();
217 }
218
GetThemeResourceList(const std::pair<std::string,std::string> & bundInfo,const ResType & resType,const std::string & resName)219 std::vector<std::shared_ptr<ThemeResource::ThemeValue> > ThemePackManager::GetThemeResourceList(
220 const std::pair<std::string, std::string> &bundInfo, const ResType &resType, const std::string &resName)
221 {
222 AutoMutex mutex(this->lockSkin_);
223 std::vector<std::shared_ptr<ThemeResource::ThemeValue> > result;
224 for (size_t i = 0; i < skinResource_.size(); ++i) {
225 auto pThemeResource = skinResource_[i];
226 if (pThemeResource == nullptr) {
227 continue;
228 }
229 std::string bundleName = pThemeResource->GetThemeResBundleName(pThemeResource->themePath_);
230 if (bundleName != bundInfo.first) {
231 continue;
232 }
233 result = pThemeResource->GetThemeValues(bundInfo, resType, resName);
234 }
235 return result;
236 }
237
GetThemeQualifierValue(const std::pair<std::string,std::string> & bundInfo,const ResType & resType,const std::string & resName,const ResConfigImpl & resConfig)238 const std::shared_ptr<ThemeResource::ThemeQualifierValue> ThemePackManager::GetThemeQualifierValue(
239 const std::pair<std::string, std::string> &bundInfo, const ResType &resType,
240 const std::string &resName, const ResConfigImpl &resConfig)
241 {
242 auto candidates = this->GetThemeResourceList(bundInfo, resType, resName);
243 if (candidates.size() == 0) {
244 return nullptr;
245 }
246 return GetBestMatchThemeResource(candidates, resConfig);
247 }
248
GetBestMatchThemeResource(const std::vector<std::shared_ptr<ThemeResource::ThemeValue>> & candidates,const ResConfigImpl & resConfig)249 const std::shared_ptr<ThemeResource::ThemeQualifierValue> ThemePackManager::GetBestMatchThemeResource(
250 const std::vector<std::shared_ptr<ThemeResource::ThemeValue> > &candidates, const ResConfigImpl &resConfig)
251 {
252 std::shared_ptr<ThemeResource::ThemeQualifierValue> result = nullptr;
253 std::shared_ptr<ThemeConfig> bestThemeConfig = nullptr;
254 for (auto iter = candidates.rbegin(); iter != candidates.rend(); iter++) {
255 const std::vector<std::shared_ptr<ThemeResource::ThemeQualifierValue> > ThemePaths =
256 (*iter)->GetThemeLimitPathsConst();
257 size_t len = ThemePaths.size();
258 for (size_t i = 0; i < len; i++) {
259 std::shared_ptr<ThemeResource::ThemeQualifierValue> path = ThemePaths[i];
260 auto themeConfig = path->GetThemeConfig();
261 if (!ThemeConfig::Match(themeConfig, resConfig)) {
262 continue;
263 }
264 if (bestThemeConfig == nullptr) {
265 bestThemeConfig = themeConfig;
266 result = path;
267 continue;
268 }
269 if (!bestThemeConfig->BestMatch(themeConfig, resConfig)) {
270 bestThemeConfig = themeConfig;
271 result = path;
272 }
273 }
274 }
275 return result;
276 }
277
ClearIconResource()278 void ThemePackManager::ClearIconResource()
279 {
280 for (auto it = iconResource_.begin(); it != iconResource_.end();) {
281 if ((*it) == nullptr) {
282 continue;
283 }
284 // 1 means get the enable theme
285 if (!(*it)->IsNewResource()) {
286 it = iconResource_.erase(it);
287 } else {
288 ++it;
289 }
290 }
291 iconMaskValues_.clear();
292 }
293
LoadThemeIconsResource(const std::string & bundleName,const std::string & moduleName,const std::vector<std::string> & rootDirs,int32_t userId)294 void ThemePackManager::LoadThemeIconsResource(const std::string &bundleName, const std::string &moduleName,
295 const std::vector<std::string> &rootDirs, int32_t userId)
296 {
297 AutoMutex mutex(this->lockIcon_);
298 for (size_t i = 0; i < iconResource_.size(); ++i) {
299 auto pThemeResource = iconResource_[i];
300 if (pThemeResource == nullptr) {
301 continue;
302 }
303 pThemeResource->SetNewResource(false);
304 }
305 if (rootDirs.empty()) {
306 ClearIconResource();
307 return;
308 }
309 for (const auto &dir : rootDirs) {
310 auto pos = dir.rfind('/');
311 if (pos == std::string::npos) {
312 RESMGR_HILOGE(RESMGR_TAG, "invalid dir = %{public}s in LoadThemeIconsResource", dir.c_str());
313 continue;
314 }
315 auto pThemeResource = ThemeResource::LoadThemeIconResource(dir);
316 if (pThemeResource != nullptr) {
317 this->iconResource_.emplace_back(pThemeResource);
318 }
319 }
320 ClearIconResource();
321 }
322
FindThemeIconResource(const std::pair<std::string,std::string> & bundleInfo,const std::string & iconName,const std::string & abilityName)323 const std::string ThemePackManager::FindThemeIconResource(const std::pair<std::string, std::string> &bundleInfo,
324 const std::string &iconName, const std::string &abilityName)
325 {
326 AutoMutex mutex(this->lockIcon_);
327 std::string result;
328 for (size_t i = 0; i < iconResource_.size(); i++) {
329 auto pThemeResource = iconResource_[i];
330 if (pThemeResource == nullptr) {
331 return std::string("");
332 }
333 result = pThemeResource->GetThemeAppIcon(bundleInfo, iconName, abilityName);
334 if (!result.empty()) {
335 break;
336 }
337 }
338 return result;
339 }
340
UpdateThemeId(uint32_t newThemeId)341 bool ThemePackManager::UpdateThemeId(uint32_t newThemeId)
342 {
343 AutoMutex mutex(this->lockThemeId_);
344 if (newThemeId != 0 && newThemeId != themeId_) {
345 RESMGR_HILOGI(RESMGR_TAG, "update theme, themeId_= %{public}d, newThemeId= %{public}d", themeId_, newThemeId);
346 themeId_ = newThemeId;
347 return true;
348 }
349 return false;
350 }
351
IsFirstLoadResource()352 bool ThemePackManager::IsFirstLoadResource()
353 {
354 if (isFirstCreate) {
355 isFirstCreate = false;
356 return true;
357 }
358 return false;
359 }
360
HasIconInTheme(const std::string & bundleName)361 bool ThemePackManager::HasIconInTheme(const std::string &bundleName)
362 {
363 AutoMutex mutex(this->lockIcon_);
364 bool result = false;
365 for (size_t i = 0; i < iconResource_.size(); i++) {
366 auto pThemeResource = iconResource_[i];
367 if (pThemeResource == nullptr) {
368 continue;
369 }
370 result = pThemeResource->HasIconInTheme(bundleName);
371 if (result) {
372 break;
373 }
374 }
375 return result;
376 }
377
GetOtherIconsInfo(const std::string & iconName,std::unique_ptr<uint8_t[]> & outValue,size_t & len,bool isGlobalMask)378 RState ThemePackManager::GetOtherIconsInfo(const std::string &iconName,
379 std::unique_ptr<uint8_t[]> &outValue, size_t &len, bool isGlobalMask)
380 {
381 AutoMutex mutex(this->lockIconValue_);
382 std::string iconPath;
383 std::string iconTag;
384 if (iconName.find("icon_mask") != std::string::npos && isGlobalMask) {
385 iconPath = themeMask;
386 iconTag = "global_" + iconName;
387 } else {
388 std::pair<std::string, std::string> bundleInfo;
389 bundleInfo.first = "other_icons";
390 iconPath = FindThemeIconResource(bundleInfo, iconName);
391 iconTag = "other_icons_" + iconName;
392 }
393
394 if (iconPath.empty()) {
395 RESMGR_HILOGE(RESMGR_TAG, "no found, iconName = %{public}s", iconName.c_str());
396 return ERROR_CODE_RES_NOT_FOUND_BY_NAME;
397 }
398
399 outValue = Utils::LoadResourceFile(iconPath, len);
400 if (outValue != nullptr && len != 0) {
401 auto tmpInfo = std::make_unique<uint8_t[]>(len);
402 errno_t ret = memcpy_s(tmpInfo.get(), len, outValue.get(), len);
403 if (ret != 0) {
404 RESMGR_HILOGE(RESMGR_TAG, "save fail, iconName = %{public}s, ret = %{public}d", iconName.c_str(), ret);
405 return SUCCESS;
406 }
407 iconMaskValues_.emplace_back(std::make_tuple(iconTag, std::move(tmpInfo), len));
408 return SUCCESS;
409 }
410 return ERROR_CODE_RES_NOT_FOUND_BY_NAME;
411 }
412
GetThemeIconFromCache(const std::string & iconTag,std::unique_ptr<uint8_t[]> & outValue,size_t & len)413 RState ThemePackManager::GetThemeIconFromCache(
414 const std::string &iconTag, std::unique_ptr<uint8_t[]> &outValue, size_t &len)
415 {
416 AutoMutex mutex(this->lockIconValue_);
417 if (iconMaskValues_.empty()) {
418 return NOT_FOUND;
419 }
420
421 for (const auto &iconValue : iconMaskValues_) {
422 std::string tag = std::get<FIRST_ELEMENT>(iconValue);
423 if (iconTag != tag) {
424 continue;
425 }
426 size_t length = std::get<THIRED_ELEMENT>(iconValue);
427 auto iconInfo = std::make_unique<uint8_t[]>(length);
428 auto tmpInfo = std::get<SECOND_ELEMENT>(iconValue).get();
429 errno_t ret = memcpy_s(iconInfo.get(), length, tmpInfo, length);
430 if (ret != 0) {
431 RESMGR_HILOGE(RESMGR_TAG, "get icon info fail, ret = %{public}d", ret);
432 continue;
433 }
434 len = length;
435 outValue = std::move(iconInfo);
436 return SUCCESS;
437 }
438 return NOT_FOUND;
439 }
440
IsUpdateByUserId(int32_t userId)441 bool ThemePackManager::IsUpdateByUserId(int32_t userId)
442 {
443 AutoMutex mutex(this->lockUserId_);
444 return currentUserId_ != userId;
445 }
446
UpdateUserId(int32_t userId)447 void ThemePackManager::UpdateUserId(int32_t userId)
448 {
449 AutoMutex mutex(this->lockUserId_);
450 if (currentUserId_ != userId) {
451 RESMGR_HILOGI(RESMGR_TAG,
452 "update userId, currentUserId_= %{public}d, userId= %{public}d", currentUserId_, userId);
453 currentUserId_ = userId;
454 }
455 }
456 } // namespace Resource
457 } // namespace Global
458 } // namespace OHOS
459