1 /*
2  * Copyright (c) 2021-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_cache_mgr.h"
17 
18 #include <sstream>
19 
20 #include "fms_log_wrapper.h"
21 #include "nlohmann/json.hpp"
22 #include "scope_guard.h"
23 #include "form_util.h"
24 #include "form_report.h"
25 
26 namespace OHOS {
27 namespace AppExecFwk {
28 namespace {
29 const std::string JSON_EMPTY_STRING = "{}";
30 const std::string JSON_NULL_STRING = "null";
31 
32 const std::string FORM_CACHE_TABLE = "form_cache";
33 const std::string FORM_ID = "FORM_ID";
34 const std::string DATA_CACHE = "DATA_CACHE";
35 const int32_t DATA_CACHE_INDEX = 1;
36 const std::string FORM_IMAGES = "FORM_IMAGES";
37 const int32_t FORM_IMAGES_INDEX = 2;
38 const std::string CACHE_STATE = "CACHE_STATE";
39 const int32_t CACHE_STATE_INDEX = 3;
40 
41 const std::string IMG_CACHE_TABLE = "img_cache";
42 const std::string IMAGE_ID = "IMAGE_ID";
43 const int32_t IMAGE_ID_INDEX = 0;
44 const std::string IMAGE_BIT = "IMAGE_BIT";
45 const int32_t IMAGE_BIT_INDEX = 1;
46 const std::string IMAGE_SIZE = "IMAGE_SIZE";
47 const int32_t IMAGE_SIZE_INDEX = 2;
48 
49 const int32_t INVALID_INDEX = -1;
50 
HasContent(const std::string &str)51 inline bool HasContent(const std::string &str)
52 {
53     return !str.empty() && str != JSON_EMPTY_STRING && str != JSON_NULL_STRING;
54 }
55 }
56 
FormCacheMgr()57 FormCacheMgr::FormCacheMgr()
58 {
59     HILOG_INFO("create");
60     CreateFormCacheTable();
61 }
62 
~FormCacheMgr()63 FormCacheMgr::~FormCacheMgr()
64 {
65     HILOG_INFO("destroy");
66 }
67 
CreateFormCacheTable()68 void FormCacheMgr::CreateFormCacheTable()
69 {
70     FormRdbTableConfig formRdbCacheTableConfig;
71     formRdbCacheTableConfig.tableName = FORM_CACHE_TABLE;
72     formRdbCacheTableConfig.createTableSql = "CREATE TABLE IF NOT EXISTS " + FORM_CACHE_TABLE
73         + " (FORM_ID TEXT NOT NULL PRIMARY KEY, DATA_CACHE TEXT, FORM_IMAGES TEXT, CACHE_STATE INTEGER);";
74     if (FormRdbDataMgr::GetInstance().InitFormRdbTable(formRdbCacheTableConfig) != ERR_OK) {
75         HILOG_ERROR("Form cache mgr init form rdb cache table fail");
76     }
77 
78     FormRdbTableConfig formRdbImgTableConfig;
79     formRdbImgTableConfig.tableName = IMG_CACHE_TABLE;
80     formRdbImgTableConfig.createTableSql = "CREATE TABLE IF NOT EXISTS " + IMG_CACHE_TABLE
81         + " (IMAGE_ID INTEGER PRIMARY KEY AUTOINCREMENT, IMAGE_BIT BLOB, IMAGE_SIZE TEXT);";
82     if (FormRdbDataMgr::GetInstance().InitFormRdbTable(formRdbImgTableConfig) != ERR_OK) {
83         HILOG_ERROR("Form cache mgr init form rdb img table fail");
84     }
85 }
86 
Start()87 void FormCacheMgr::Start()
88 {
89     HILOG_INFO("Start");
90     ResetCacheStateAfterReboot();
91 }
92 
GetData(const int64_t formId, std::string &data, std::map<std::string, std::pair<sptr<FormAshmem>, int32_t>> &imageDataMap) const93 bool FormCacheMgr::GetData(const int64_t formId, std::string &data,
94     std::map<std::string, std::pair<sptr<FormAshmem>, int32_t>> &imageDataMap) const
95 {
96     HILOG_DEBUG("GetData start");
97     std::lock_guard<std::mutex> lock(cacheMutex_);
98     FormCache formCache;
99     formCache.formId = formId;
100     bool ret = GetDataCacheFromDb(formId, formCache);
101     if (!ret) {
102         HILOG_ERROR("no data in db");
103         return false;
104     }
105 
106     bool hasContent = false;
107     if (HasContent(formCache.dataCache)) {
108         nlohmann::json dataCacheObj = nlohmann::json::parse(formCache.dataCache, nullptr, false);
109         if (dataCacheObj.is_discarded() || !dataCacheObj.is_object()) {
110             HILOG_ERROR("GetData failed due to dataCache is discarded");
111             return false;
112         }
113 
114         data = formCache.dataCache;
115         hasContent = true;
116     }
117 
118     if (HasContent(formCache.imgCache)) {
119         ret = InnerGetImageData(formCache, imageDataMap);
120         if (!ret) {
121             HILOG_ERROR("InnerGetImageData failed");
122             return false;
123         }
124 
125         hasContent = true;
126     }
127 
128     return hasContent;
129 }
130 
InnerGetImageData( const FormCache &formCache, std::map<std::string, std::pair<sptr<FormAshmem>, int32_t>> &imageDataMap) const131 bool FormCacheMgr::InnerGetImageData(
132     const FormCache &formCache,
133     std::map<std::string, std::pair<sptr<FormAshmem>, int32_t>> &imageDataMap) const
134 {
135     HILOG_DEBUG("InnerGetImageData start");
136     nlohmann::json imgCacheObj = nlohmann::json::parse(formCache.imgCache, nullptr, false);
137     if (imgCacheObj.is_discarded() || !imgCacheObj.is_object()) {
138         HILOG_ERROR("imgCacheObj is discarded");
139         return false;
140     }
141 
142     for (auto && [key, value] : imgCacheObj.items()) {
143         int64_t rowId;
144         std::stringstream ss;
145         ss << value.dump();
146         ss >> rowId;
147         std::vector<uint8_t> blob;
148         int32_t size = 0;
149         if (!GetImgCacheFromDb(rowId, blob, size)) {
150             HILOG_ERROR("GetImgCacheFromDb failed");
151             return false;
152         }
153 
154         if (blob.size() <= 0) {
155             HILOG_ERROR("GetImgCacheFromDb failed due to blob is empty");
156             return false;
157         }
158 
159         sptr<FormAshmem> formAshmem = new (std::nothrow) FormAshmem();
160         if (formAshmem == nullptr) {
161             HILOG_ERROR("Alloc ashmem failed");
162             return false;
163         }
164 
165         if (!formAshmem->WriteToAshmem(key, reinterpret_cast<char *>(blob.data()),
166             static_cast<int32_t>(blob.size()))) {
167             HILOG_ERROR("Write to ashmem failed");
168             return false;
169         }
170 
171         imageDataMap[key] = std::make_pair(formAshmem, size);
172     }
173 
174     return true;
175 }
176 
AddData(int64_t formId, const FormProviderData &formProviderData)177 bool FormCacheMgr::AddData(int64_t formId, const FormProviderData &formProviderData)
178 {
179     HILOG_INFO("formId:%{public}" PRId64, formId);
180     std::lock_guard<std::mutex> lock(cacheMutex_);
181 
182     FormCache formCache;
183     formCache.formId = formId;
184     GetDataCacheFromDb(formId, formCache);
185     if (!AddImgData(formProviderData, formCache)) {
186         HILOG_ERROR("AddImgData failed");
187         return false;
188     }
189 
190     if (!AddCacheData(formProviderData, formCache)) {
191         HILOG_ERROR("AddCacheData failed");
192         return false;
193     }
194 
195     // Save dataCache and imgCache
196     formCache.cacheState = CacheState::DEFAULT;
197     FormReport::GetInstance().SetDurationEndTime(formId, FormUtil::GetCurrentSteadyClockMillseconds());
198     return SaveDataCacheToDb(formId, formCache);
199 }
200 
AddImgData( const FormProviderData &formProviderData, FormCache &formCache)201 bool FormCacheMgr::AddImgData(
202     const FormProviderData &formProviderData, FormCache &formCache)
203 {
204     nlohmann::json newImgDbData;
205     if (!AddImgDataToDb(formProviderData, newImgDbData)) {
206         HILOG_ERROR("AddImgDataToDb failed");
207         return false;
208     }
209 
210     if (newImgDbData.empty()) {
211         HILOG_INFO("No new imgData");
212         return true;
213     }
214 
215     if (HasContent(formCache.imgCache)) {
216         nlohmann::json imgCacheObj = nlohmann::json::parse(formCache.imgCache, nullptr, false);
217         if (imgCacheObj.is_discarded() || !imgCacheObj.is_object()) {
218             HILOG_ERROR("parse data failed");
219             return false;
220         }
221         // delete old images
222         for (auto && [key, value] : imgCacheObj.items()) {
223             DeleteImgCacheInDb(imgCacheObj[key].dump());
224         }
225     }
226 
227     formCache.imgCache = newImgDbData.dump();
228 
229     return true;
230 }
231 
AddCacheData( const FormProviderData &formProviderData, FormCache &formCache)232 bool FormCacheMgr::AddCacheData(
233     const FormProviderData &formProviderData, FormCache &formCache)
234 {
235     auto newDataStr = formProviderData.GetDataString();
236     nlohmann::json newDataObj;
237     if (HasContent(newDataStr)) {
238         newDataObj = nlohmann::json::parse(newDataStr, nullptr, false);
239         if (newDataObj.is_discarded() || !newDataObj.is_object()) {
240             HILOG_ERROR("parse data failed");
241             return false;
242         }
243 
244         newDataObj.erase("formImages");
245     }
246 
247     if (newDataObj.empty()) {
248         HILOG_INFO("No new cacheData");
249         return true;
250     }
251 
252     if (!HasContent(formCache.dataCache)) {
253         // No dataCache in db
254         formCache.dataCache = newDataObj.dump();
255         return true;
256     }
257 
258     nlohmann::json dataCacheObj = nlohmann::json::parse(formCache.dataCache, nullptr, false);
259     if (dataCacheObj.is_discarded() || !dataCacheObj.is_object()) {
260         HILOG_ERROR("parse data failed");
261         return false;
262     }
263 
264     // Update dataCache
265     for (auto && [key, value] : newDataObj.items()) {
266         dataCacheObj[key] = value;
267     }
268     formCache.dataCache = dataCacheObj.dump();
269     return true;
270 }
271 
AddImgDataToDb( const FormProviderData &formProviderData, nlohmann::json &imgDataJson)272 bool FormCacheMgr::AddImgDataToDb(
273     const FormProviderData &formProviderData, nlohmann::json &imgDataJson)
274 {
275     auto imgCache = formProviderData.GetImageDataMap();
276     HILOG_DEBUG("AddImgDataToDb imgCache size:%{public}zu", imgCache.size());
277     for (const auto &iter : imgCache) {
278         int64_t rowId = INVALID_INDEX;
279         std::vector<uint8_t> value;
280         bool ret = GetImageDataFromAshmem(
281             iter.first, iter.second.first->GetAshmem(), iter.second.first->GetAshmemSize(), value);
282         if (!ret) {
283             HILOG_ERROR("fail get img data imgName:%{public}s", iter.first.c_str());
284             return false;
285         }
286 
287         ret = SaveImgCacheToDb(value, iter.second.second, rowId);
288         if (!ret || rowId == INVALID_INDEX) {
289             HILOG_ERROR("fail save img data imgName:%{public}s", iter.first.c_str());
290             return false;
291         }
292 
293         imgDataJson[iter.first] = rowId;
294     }
295 
296     return true;
297 }
298 
GetImageDataFromAshmem( const std::string& picName, const sptr<Ashmem> &ashmem, int32_t len, std::vector<uint8_t> &value)299 bool FormCacheMgr::GetImageDataFromAshmem(
300     const std::string& picName, const sptr<Ashmem> &ashmem, int32_t len, std::vector<uint8_t> &value)
301 {
302     HILOG_DEBUG("GetImageDataFromAshmem start picName:%{public}s", picName.c_str());
303     if (ashmem == nullptr) {
304         HILOG_ERROR("null ashmem when picName:%{public}s", picName.c_str());
305         return false;
306     }
307 
308     bool ret = ashmem->MapReadOnlyAshmem();
309     if (!ret) {
310         HILOG_ERROR("MapReadOnlyAshmem fail, fail reason:%{public}s, picName:%{public}s",
311             strerror(errno), picName.c_str());
312         return false;
313     }
314 
315     ScopeGuard stateGuard([ashmem] {
316         if (ashmem) {
317             ashmem->UnmapAshmem();
318         }
319     });
320     const uint8_t* imageData = reinterpret_cast<const uint8_t*>(ashmem->ReadFromAshmem(len, 0));
321     if (imageData == nullptr) {
322         HILOG_ERROR("ReadFromAshmem failed picName:%{public}s", picName.c_str());
323         return false;
324     }
325 
326     value = std::vector<uint8_t>(imageData, imageData + len);
327     return true;
328 }
329 
DeleteData(const int64_t formId)330 bool FormCacheMgr::DeleteData(const int64_t formId)
331 {
332     HILOG_INFO("formId:%{public}" PRId64, formId);
333     std::lock_guard<std::mutex> lock(cacheMutex_);
334     FormCache formCache;
335     bool ret = GetDataCacheFromDb(formId, formCache);
336     if (!ret) {
337         HILOG_INFO("No DataCache when delete");
338         return true;
339     }
340 
341     InnerDeleteImageData(formCache);
342     return DeleteDataCacheInDb(formId);
343 }
344 
NeedAcquireProviderData(const int64_t formId) const345 bool FormCacheMgr::NeedAcquireProviderData(const int64_t formId) const
346 {
347     HILOG_DEBUG("NeedAcquireProviderData");
348     FormCache formCache;
349     bool ret = GetDataCacheFromDb(formId, formCache);
350     if (!ret) {
351         HILOG_ERROR("No DataCache");
352         return true;
353     }
354 
355     bool hasContent = HasContent(formCache.dataCache) || HasContent(formCache.imgCache);
356     bool isRebootState = formCache.cacheState == CacheState::REBOOT;
357     return !hasContent || isRebootState;
358 }
359 
GetDataCacheFromDb(int64_t formId, FormCache &formCache) const360 bool FormCacheMgr::GetDataCacheFromDb(int64_t formId, FormCache &formCache) const
361 {
362     NativeRdb::AbsRdbPredicates absRdbPredicates(FORM_CACHE_TABLE);
363     absRdbPredicates.EqualTo(FORM_ID, std::to_string(formId));
364     auto absSharedResultSet = FormRdbDataMgr::GetInstance().QueryData(absRdbPredicates);
365     if (absSharedResultSet == nullptr) {
366         HILOG_ERROR("GetDataCacheFromDb failed");
367         return false;
368     }
369 
370     ScopeGuard stateGuard([absSharedResultSet] {
371         if (absSharedResultSet) {
372             absSharedResultSet->Close();
373         }
374     });
375     if (!absSharedResultSet->HasBlock()) {
376         HILOG_ERROR("absSharedResultSet has no block");
377         return false;
378     }
379 
380     int ret = absSharedResultSet->GoToFirstRow();
381     if (ret != NativeRdb::E_OK) {
382         HILOG_ERROR("GoToFirstRow failed, ret:%{public}d", ret);
383         return false;
384     }
385 
386     ret = absSharedResultSet->GetString(DATA_CACHE_INDEX, formCache.dataCache);
387     if (ret != NativeRdb::E_OK) {
388         HILOG_DEBUG("GetString dataCache failed, ret:%{public}d", ret);
389     }
390 
391     ret = absSharedResultSet->GetString(FORM_IMAGES_INDEX, formCache.imgCache);
392     if (ret != NativeRdb::E_OK) {
393         HILOG_DEBUG("GetString imgCache failed, ret:%{public}d", ret);
394     }
395 
396     int32_t cacheState;
397     ret = absSharedResultSet->GetInt(CACHE_STATE_INDEX, cacheState);
398     if (ret != NativeRdb::E_OK) {
399         HILOG_DEBUG("GetInt cacheState failed, ret:%{public}d", ret);
400     }
401     formCache.cacheState = static_cast<CacheState>(cacheState);
402     return true;
403 }
404 
SaveDataCacheToDb(int64_t formId, const FormCache &formCache)405 bool FormCacheMgr::SaveDataCacheToDb(int64_t formId, const FormCache &formCache)
406 {
407     NativeRdb::ValuesBucket valuesBucket;
408     valuesBucket.PutString(FORM_ID, std::to_string(formId));
409     valuesBucket.PutString(DATA_CACHE, formCache.dataCache);
410     valuesBucket.PutString(FORM_IMAGES, formCache.imgCache);
411     valuesBucket.PutInt(CACHE_STATE, static_cast<int>(formCache.cacheState));
412     int64_t rowId;
413     bool ret = FormRdbDataMgr::GetInstance().InsertData(FORM_CACHE_TABLE, valuesBucket, rowId);
414     if (!ret) {
415         HILOG_ERROR("SaveDataCacheToDb formId:%{public}s failed.", std::to_string(formId).c_str());
416         return false;
417     }
418     return true;
419 }
420 
InnerDeleteImageData(const FormCache &formCache)421 bool FormCacheMgr::InnerDeleteImageData(const FormCache &formCache)
422 {
423     if (!HasContent(formCache.imgCache)) {
424         HILOG_INFO("Has no imgCache when delete");
425         return true;
426     }
427 
428     nlohmann::json imgCacheObj = nlohmann::json::parse(formCache.imgCache, nullptr, false);
429     if (imgCacheObj.is_discarded() || !imgCacheObj.is_object()) {
430         HILOG_ERROR("parse data failed");
431         return false;
432     }
433 
434     for (auto && [key, value] : imgCacheObj.items()) {
435         DeleteImgCacheInDb(value.dump());
436     }
437 
438     return true;
439 }
440 
DeleteDataCacheInDb(int64_t formId)441 bool FormCacheMgr::DeleteDataCacheInDb(int64_t formId)
442 {
443     NativeRdb::AbsRdbPredicates absRdbPredicates(FORM_CACHE_TABLE);
444     absRdbPredicates.EqualTo(FORM_ID, std::to_string(formId));
445     return FormRdbDataMgr::GetInstance().DeleteData(absRdbPredicates);
446 }
447 
GetImgCacheFromDb( int64_t rowId, std::vector<uint8_t> &blob, int32_t &size) const448 bool FormCacheMgr::GetImgCacheFromDb(
449     int64_t rowId, std::vector<uint8_t> &blob, int32_t &size) const
450 {
451     NativeRdb::AbsRdbPredicates absRdbPredicates(IMG_CACHE_TABLE);
452     absRdbPredicates.EqualTo(IMAGE_ID, std::to_string(rowId));
453     auto absSharedResultSet = FormRdbDataMgr::GetInstance().QueryData(absRdbPredicates);
454     if (absSharedResultSet == nullptr) {
455         HILOG_ERROR("GetImgCacheFromDb failed");
456         return false;
457     }
458 
459     ScopeGuard stateGuard([absSharedResultSet] {
460         if (absSharedResultSet) {
461             absSharedResultSet->Close();
462         }
463     });
464     if (!absSharedResultSet->HasBlock()) {
465         HILOG_ERROR("absSharedResultSet has no block");
466         return false;
467     }
468 
469     int ret = absSharedResultSet->GoToFirstRow();
470     if (ret != NativeRdb::E_OK) {
471         HILOG_ERROR("GoToFirstRow failed,ret:%{public}d", ret);
472         return false;
473     }
474 
475     ret = absSharedResultSet->GetBlob(IMAGE_BIT_INDEX, blob);
476     if (ret != NativeRdb::E_OK) {
477         HILOG_ERROR("GetBlob failed, ret:%{public}d", ret);
478         return false;
479     }
480 
481     ret = absSharedResultSet->GetInt(IMAGE_SIZE_INDEX, size);
482     if (ret != NativeRdb::E_OK) {
483         HILOG_ERROR("GetInt size failed, ret:%{public}d", ret);
484         return false;
485     }
486 
487     return true;
488 }
489 
SaveImgCacheToDb(const std::vector<uint8_t> &value, int32_t size, int64_t &rowId)490 bool FormCacheMgr::SaveImgCacheToDb(const std::vector<uint8_t> &value, int32_t size, int64_t &rowId)
491 {
492     NativeRdb::ValuesBucket valuesBucket;
493     valuesBucket.PutBlob(IMAGE_BIT, value);
494     valuesBucket.PutInt(IMAGE_SIZE, size);
495     bool ret = FormRdbDataMgr::GetInstance().InsertData(IMG_CACHE_TABLE, valuesBucket, rowId);
496     if (!ret) {
497         HILOG_ERROR("SaveImgCacheToDb failed");
498         return false;
499     }
500     return true;
501 }
502 
DeleteImgCacheInDb(const std::string &rowId)503 bool FormCacheMgr::DeleteImgCacheInDb(const std::string &rowId)
504 {
505     if (rowId.empty()) {
506         return false;
507     }
508 
509     NativeRdb::AbsRdbPredicates absRdbPredicates(IMG_CACHE_TABLE);
510     absRdbPredicates.EqualTo(IMAGE_ID, rowId);
511     return FormRdbDataMgr::GetInstance().DeleteData(absRdbPredicates);
512 }
513 
ResetCacheStateAfterReboot()514 void FormCacheMgr::ResetCacheStateAfterReboot()
515 {
516     std::string sql = "UPDATE " + FORM_CACHE_TABLE + " SET " + CACHE_STATE + " = 1;";
517     FormRdbDataMgr::GetInstance().ExecuteSql(sql);
518 }
519 }  // namespace AppExecFwk
520 }  // namespace OHOS
521