• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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