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
16 #include "form_module_preloader.h"
17
18 #include <mutex>
19 #include <unordered_map>
20
21 #include "adapter/ohos/entrance/utils.h"
22 #include "base/log/log.h"
23 #include "base/json/json_util.h"
24 #include "frameworks/bridge/declarative_frontend/engine/jsi/jsi_declarative_engine.h"
25
26
27 namespace OHOS::Ace {
28 namespace {
29 // KEY: bundleName, VALUE: formModuleList
30 std::unordered_map<std::string, std::unordered_set<std::string>> gFormModuleMap_;
31
32 std::mutex gMapLock_;
33 }
34
OHOS_ACE_PreloadAceModuleCard(void * runtime,const char * bundleName)35 extern "C" ACE_FORCE_EXPORT void OHOS_ACE_PreloadAceModuleCard(void* runtime, const char* bundleName)
36 {
37 std::unordered_set<std::string> formModuleList;
38 if (!FormModulePreloader::CreateFormModuleList(std::string(bundleName), formModuleList)) {
39 LOGW("CreateFormModuleList failed, will load all modules later.");
40 }
41 Framework::JsiDeclarativeEngineInstance::PreloadAceModuleCard(runtime, formModuleList);
42 }
43
OHOS_ACE_ReloadAceModuleCard(void * runtime,const char * bundleName)44 extern "C" ACE_FORCE_EXPORT void OHOS_ACE_ReloadAceModuleCard(void* runtime, const char* bundleName)
45 {
46 std::unordered_set<std::string> formModuleList;
47 bool ret = FormModulePreloader::GetNewFormModuleList(std::string(bundleName), formModuleList);
48 if (ret && formModuleList.empty()) {
49 LOGI("There are no new components to load.");
50 return;
51 } else if (!ret) {
52 LOGW("GetNewFormModuleList failed, will load all modules later.");
53 formModuleList.clear(); // JsiDeclarativeEngineInstance will load all module if input list is empty.
54 }
55 Framework::JsiDeclarativeEngineInstance::ReloadAceModuleCard(runtime, formModuleList);
56 }
57
CreateFormModuleList(const std::string & bundleName,std::unordered_set<std::string> & formModuleList)58 bool FormModulePreloader::CreateFormModuleList(
59 const std::string& bundleName, std::unordered_set<std::string>& formModuleList)
60 {
61 if (ReadFormModuleList(bundleName, formModuleList, false)) {
62 std::lock_guard<std::mutex> lock(gMapLock_);
63 gFormModuleMap_.emplace(bundleName, formModuleList);
64 return true;
65 }
66 return false;
67 }
68
GetNewFormModuleList(const std::string & bundleName,std::unordered_set<std::string> & formModuleList)69 bool FormModulePreloader::GetNewFormModuleList(
70 const std::string& bundleName, std::unordered_set<std::string>& formModuleList)
71 {
72 {
73 std::lock_guard<std::mutex> lock(gMapLock_);
74 if (gFormModuleMap_.find(bundleName) == gFormModuleMap_.end()) {
75 // This means that reading the list of components fails on preload
76 LOGW("All modules of bundle %{public}s have been loaded.", bundleName.c_str());
77 return true;
78 }
79 }
80 return ReadFormModuleList(bundleName, formModuleList, true);
81 }
82
ReadFormModuleList(const std::string & bundleName,std::unordered_set<std::string> & formModuleList,bool isReloadCondition)83 bool FormModulePreloader::ReadFormModuleList(
84 const std::string& bundleName, std::unordered_set<std::string>& formModuleList, bool isReloadCondition)
85 {
86 std::vector<std::string> hapPaths;
87 GetHapPathsByBundleName(bundleName, hapPaths);
88 if (hapPaths.empty()) {
89 LOGE("hapPath of bundle %{public}s is empty.", bundleName.c_str());
90 return false;
91 }
92 LOGI("hapPaths size of bundle %{public}s is %{public}zu", bundleName.c_str(), hapPaths.size());
93 for (const std::string& hapPath : hapPaths) {
94 // Create HapAssetProvider
95 RefPtr<AssetManager> flutterAssetManager = CreateAssetManager(hapPath);
96 if (flutterAssetManager == nullptr) {
97 LOGE("CreateAssetManager failed, hapPath: %{private}s.", hapPath.c_str());
98 return false;
99 }
100 // Read component_collection.json
101 std::unordered_set<std::string> formEtsFilePaths;
102 if (!GetFormEtsFilePath(flutterAssetManager, formEtsFilePaths)) {
103 LOGE("Read form_config.json failed, hapPath: %{private}s.", hapPath.c_str());
104 return false;
105 }
106 std::string content;
107 if (!ReadFileFromAssetManager(flutterAssetManager, "component_collection.json", content)) {
108 LOGE("Read component_collection.json failed, hapPath: %{private}s.", hapPath.c_str());
109 return false;
110 }
111 // Parse component_collection.json
112 if (!ParseComponentCollectionJson(bundleName, formEtsFilePaths, content, formModuleList, isReloadCondition)) {
113 LOGE("Parse component_collection.json failed, hapPath: %{private}s.", hapPath.c_str());
114 return false;
115 }
116 }
117 return true;
118 }
119
ParseComponentCollectionJson(const std::string & bundleName,const std::unordered_set<std::string> & formEtsFilePaths,const std::string & content,std::unordered_set<std::string> & formModuleList,bool isReloadCondition)120 bool FormModulePreloader::ParseComponentCollectionJson(
121 const std::string& bundleName, const std::unordered_set<std::string>& formEtsFilePaths,
122 const std::string& content, std::unordered_set<std::string>& formModuleList, bool isReloadCondition)
123 {
124 auto collectionJson = JsonUtil::ParseJsonString(content);
125 if (collectionJson == nullptr || collectionJson->IsNull()) {
126 LOGE("Parse component_collection.json failed");
127 return false;
128 }
129 for (auto child = collectionJson->GetChild(); child && !child->IsNull(); child = child->GetNext()) {
130 std::string etsPath = child->GetKey();
131 if (!IsFormEtsFilePath(formEtsFilePaths, etsPath)) {
132 continue;
133 }
134 auto item = collectionJson->GetValue(etsPath);
135 if (item == nullptr || !item->IsValid() || !item->IsArray()) {
136 LOGE("Parse component_collection.json failed, etsPath: %{private}s.", etsPath.c_str());
137 return false;
138 }
139 int32_t len = item->GetArraySize();
140 for (int32_t index = 0; index < len; ++index) {
141 auto component = item->GetArrayItem(index);
142 if (component == nullptr || !component->IsString()) {
143 LOGE("Read view failed, etsPath: %{private}s.", etsPath.c_str());
144 return false;
145 }
146 std::string componentName = component->GetString();
147 if (!isReloadCondition) {
148 formModuleList.emplace(componentName);
149 continue;
150 }
151 std::lock_guard<std::mutex> lock(gMapLock_);
152 if (gFormModuleMap_[bundleName].find(componentName) == gFormModuleMap_[bundleName].end()) {
153 LOGD("Add new module: %{public}s", componentName.c_str());
154 formModuleList.emplace(componentName);
155 gFormModuleMap_[bundleName].emplace(bundleName);
156 }
157 }
158 }
159 return true;
160 }
161
GetHapPathsByBundleName(const std::string & bundleName,std::vector<std::string> & hapPaths)162 void FormModulePreloader::GetHapPathsByBundleName(const std::string& bundleName, std::vector<std::string>& hapPaths)
163 {
164 // Create AssetProvider and get all hap-paths of target bundle
165 std::string packagePath = "/data/bundles/" + bundleName + "/";
166 std::vector<std::string> basePaths;
167 basePaths.push_back("/");
168 auto assetProvider = CreateAssetProvider(packagePath, basePaths, false);
169 if (assetProvider == nullptr) {
170 LOGE("CreateAssetProvider failed, basePath: %{private}s.", packagePath.c_str());
171 return;
172 }
173 assetProvider->GetAssetList("", hapPaths);
174 for (auto iter = hapPaths.begin(); iter != hapPaths.end();) {
175 if (!std::regex_match(*iter, std::regex(".*\\.hap"))) {
176 iter = hapPaths.erase(iter);
177 } else {
178 *iter = packagePath + *iter;
179 ++iter;
180 }
181 }
182 }
183
ReadFileFromAssetManager(const RefPtr<AssetManager> & assetManager,const std::string & fileName,std::string & content)184 bool FormModulePreloader::ReadFileFromAssetManager(
185 const RefPtr<AssetManager>& assetManager, const std::string& fileName, std::string& content)
186 {
187 if (assetManager == nullptr) {
188 LOGE("assetManager is null.");
189 return false;
190 }
191 auto jsAsset = assetManager->GetAsset(fileName);
192 if (jsAsset == nullptr) {
193 LOGE("uri: %{private}s Asset is null", fileName.c_str());
194 return false;
195 }
196 auto bufLen = jsAsset->GetSize();
197 auto buffer = jsAsset->GetData();
198 if ((buffer == nullptr) || (bufLen <= 0)) {
199 LOGE("uri: %{private}s buffer is null", fileName.c_str());
200 return false;
201 }
202 content.assign(buffer, buffer + bufLen);
203 return true;
204 }
205
GetFormEtsFilePath(const RefPtr<AssetManager> & assetManager,std::unordered_set<std::string> & filePaths)206 bool FormModulePreloader::GetFormEtsFilePath(
207 const RefPtr<AssetManager>& assetManager, std::unordered_set<std::string>& filePaths)
208 {
209 // Read form_config.json
210 std::string content;
211 if (!ReadFileFromAssetManager(assetManager, "form_config.json", content)) {
212 LOGE("Read form_config failed");
213 return false;
214 }
215 auto collectionJson = JsonUtil::ParseJsonString(content);
216 if (collectionJson == nullptr || collectionJson->IsNull()) {
217 LOGE("ParseJsonString failed.");
218 return false;
219 }
220 auto formJson = collectionJson->GetValue("forms");
221 if (formJson == nullptr || formJson->IsNull() || !formJson->IsArray()) {
222 LOGE("Get form-config from json failed.");
223 return false;
224 }
225 // Read FormEtsFilePath
226 int32_t len = formJson->GetArraySize();
227 for (int32_t index = 0; index < len; ++index) {
228 auto config = formJson->GetArrayItem(index);
229 if (config == nullptr || config->IsNull()) {
230 LOGE("Form-config is invalid.");
231 return false;
232 }
233 auto path = config->GetValue("src");
234 if (path == nullptr || !path->IsString()) {
235 LOGE("Read src from form-config failed.");
236 return false;
237 }
238 filePaths.emplace(path->GetString());
239 }
240 return true;
241 }
242
IsFormEtsFilePath(const std::unordered_set<std::string> & formEtsFilePaths,std::string path)243 bool FormModulePreloader::IsFormEtsFilePath(
244 const std::unordered_set<std::string>& formEtsFilePaths, std::string path)
245 {
246 std::replace(path.begin(), path.end(), '\\', '/');
247 std::string rootPath = "src/main";
248 auto pos = path.find(rootPath);
249 if (pos == std::string::npos) {
250 return formEtsFilePaths.find(path) != formEtsFilePaths.end();
251 }
252 // Convert to relative path
253 std::string relativePath = path.substr(pos + static_cast<int32_t>(rootPath.length()));
254 relativePath.insert(relativePath.begin(), '.');
255 return formEtsFilePaths.find(relativePath) != formEtsFilePaths.end();
256 }
257
CreateAssetManager(const std::string & hapPath)258 RefPtr<AssetManager> FormModulePreloader::CreateAssetManager(const std::string& hapPath)
259 {
260 RefPtr<AssetManager> flutterAssetManager = Referenced::MakeRefPtr<FlutterAssetManager>();
261 if (flutterAssetManager == nullptr) {
262 LOGE("Create flutterAssetManager failed.");
263 return nullptr;
264 }
265 std::vector<std::string> basePaths;
266 basePaths.emplace_back("");
267 basePaths.emplace_back("ets/");
268 basePaths.emplace_back("resources/base/profile/");
269 auto assetProvider = CreateAssetProvider(hapPath, basePaths, false);
270 if (assetProvider == nullptr) {
271 LOGE("CreateAssetProvider failed");
272 return nullptr;
273 }
274 flutterAssetManager->PushBack(std::move(assetProvider));
275 return flutterAssetManager;
276 }
277 } // namespace OHOS::Ace
278