1/*
2 * Copyright (c) 2024 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 "mission/dsched_sync_e2e.h"
17
18#include <filesystem>
19#include <fstream>
20#include <iostream>
21#include <parameter.h>
22
23#include "config_policy_utils.h"
24#include "parameters.h"
25#include "securec.h"
26
27namespace OHOS {
28namespace DistributedSchedule {
29namespace {
30const std::string TAG = "DmsKvSyncE2E";
31const std::string BMS_KV_BASE_DIR = "/data/service/el1/public/database/DistributedSchedule";
32const int32_t SLEEP_INTERVAL = 100 * 1000;  // 100ms
33const int32_t EL1 = 1;
34const int32_t MAX_TIMES = 600;              // 1min
35const char DETERMINE_DEVICE_TYPE_KEY[] = "persist.distributed_scene.sys_settings_data_sync";
36static const int32_t FORBID_SEND_FORBID_RECV = 0;
37static const int32_t ALLOW_SEND_ALLOW_RECV = 1;
38const std::string PARAM_DISTRIBUTED_DATAFILES_TRANS_CTRL = "persist.distributed_scene.datafiles_trans_ctrl";
39const std::string CONTINUE_CONFIG_RELATIVE_PATH = "etc/distributedhardware/dms/continue_config.json";
40const std::string ALLOW_APP_LIST_KEY = "allow_applist";
41constexpr int32_t MAX_CONFIG_PATH_LEN = 1024;
42}  // namespace
43
44std::shared_ptr<DmsKvSyncE2E> DmsKvSyncE2E::instance_ = nullptr;
45std::mutex DmsKvSyncE2E::mutex_;
46
47DmsKvSyncE2E::DmsKvSyncE2E()
48{
49    HILOGD("called.");
50    TryTwice([this] { return GetKvStore(); });
51    HILOGD("end.");
52}
53
54DmsKvSyncE2E::~DmsKvSyncE2E()
55{
56    HILOGD("called.");
57    dataManager_.CloseKvStore(appId_, storeId_);
58    HILOGD("end.");
59}
60
61std::shared_ptr<DmsKvSyncE2E> DmsKvSyncE2E::GetInstance()
62{
63    HILOGD("called.");
64    std::lock_guard<std::mutex> lock(mutex_);
65    if (instance_ == nullptr) {
66        instance_ = std::make_shared<DmsKvSyncE2E>();
67    }
68    HILOGD("end.");
69    return instance_;
70}
71
72void DmsKvSyncE2E::SetDeviceCfg()
73{
74    HILOGD("called.");
75    const char *syncType = "1";
76    const int bufferLen = 10;
77    char paramOutBuf[bufferLen] = {0};
78    int ret = GetParameter(DETERMINE_DEVICE_TYPE_KEY, "", paramOutBuf, bufferLen);
79    HILOGD("paramOutBuf: %{public}s, ret: %{public}d", paramOutBuf, ret);
80    if (ret > 0 && strncmp(paramOutBuf, syncType, strlen(syncType)) == 0) {
81        HILOGI("Determining the e2e device succeeded.");
82        isCfgDevices_ = true;
83    }
84
85    auto contralType = OHOS::system::GetIntParameter(PARAM_DISTRIBUTED_DATAFILES_TRANS_CTRL,
86        ALLOW_SEND_ALLOW_RECV);
87    HILOGI("contralType=%{public}d", contralType);
88    if (contralType == FORBID_SEND_FORBID_RECV) {
89        isForbidSendAndRecv_ = true;
90    }
91
92    int32_t result = LoadContinueConfig();
93    if (result != ERR_OK) {
94        HILOGE("Load continue config fail, result %{public}d.", result);
95    }
96}
97
98bool DmsKvSyncE2E::CheckDeviceCfg()
99{
100    HILOGD("called.");
101    return isCfgDevices_;
102}
103
104bool DmsKvSyncE2E::CheckCtrlRule()
105{
106    HILOGD("called.");
107    if (isCfgDevices_ && isForbidSendAndRecv_) {
108        HILOGE("The device is a special device and checkCtrlRule fail");
109        return false;
110    }
111    return true;
112}
113
114bool DmsKvSyncE2E::IsValidPath(const std::string &inFilePath, std::string &realFilePath)
115{
116    char path[PATH_MAX + 1] = { 0 };
117    if (inFilePath.empty() || inFilePath.length() > PATH_MAX || inFilePath.length() + 1 > MAX_CONFIG_PATH_LEN ||
118        realpath(inFilePath.c_str(), path) == nullptr) {
119        HILOGE("Get continue config file real path fail, inFilePath %{public}s.", GetAnonymStr(inFilePath).c_str());
120        return false;
121    }
122
123    realFilePath = std::string(path);
124    if (realFilePath.empty()) {
125        HILOGE("Real file path is empty.");
126        return false;
127    }
128    if (!std::filesystem::exists(realFilePath)) {
129        HILOGE("The real file path %{public}s does not exist in the file system.", GetAnonymStr(realFilePath).c_str());
130        realFilePath = "";
131        return false;
132    }
133    HILOGD("The real file path %{public}s exist in the file system.", GetAnonymStr(realFilePath).c_str());
134    return true;
135}
136
137bool DmsKvSyncE2E::UpdateWhiteList(const std::string &cfgJsonStr)
138{
139    cJSON *inJson = nullptr;
140    cJSON *allowList = nullptr;
141    bool isSuccess = false;
142    do {
143        inJson = cJSON_Parse(cfgJsonStr.c_str());
144        if (inJson == nullptr) {
145            HILOGE("parse continue config json file stream to json fail.");
146            break;
147        }
148
149        allowList = cJSON_GetObjectItem(inJson, ALLOW_APP_LIST_KEY.c_str());
150        if (allowList == nullptr || !cJSON_IsArray(allowList)) {
151            HILOGE("allow app list array is not in continue config json file.");
152            break;
153        }
154
155        std::lock_guard<std::mutex> lock(kvStorePtrMutex_);
156        for (int32_t i = 0; i < cJSON_GetArraySize(allowList); i++) {
157            cJSON *iAllowAppJson = cJSON_GetArrayItem(allowList, i);
158            if (!cJSON_IsString(iAllowAppJson)) {
159                HILOGE("allow app list [%{public}d] is not string.", i);
160                continue;
161            }
162
163            std::string iAllowAppStr = std::string(cJSON_GetStringValue(iAllowAppJson));
164            HILOGI("allow app list show [%{public}d] : [%{public}s].", i, iAllowAppStr.c_str());
165            whiteList_.push_back(iAllowAppStr);
166        }
167        isSuccess = true;
168    } while (false);
169
170    if (inJson != nullptr) {
171        cJSON_Delete(inJson);
172        inJson = nullptr;
173    }
174    return isSuccess;
175}
176
177int32_t DmsKvSyncE2E::LoadContinueConfig()
178{
179    HILOGD("Load continue config, continueCfgFullPath %{public}s.", GetAnonymStr(continueCfgFullPath_).c_str());
180    std::string tempPath = continueCfgFullPath_;
181    if ((continueCfgFullPath_.empty() || !IsValidPath(tempPath, continueCfgFullPath_))) {
182        char cfgPathBuf[MAX_CONFIG_PATH_LEN] = { 0 };
183        char *filePath  = GetOneCfgFile(CONTINUE_CONFIG_RELATIVE_PATH.c_str(), cfgPathBuf, MAX_CONFIG_PATH_LEN);
184        if (filePath == nullptr || filePath != cfgPathBuf) {
185            HILOGE("Not find continue config file, relative path %{public}s.",
186                GetAnonymStr(CONTINUE_CONFIG_RELATIVE_PATH).c_str());
187            continueCfgFullPath_ = "";
188            return ERR_OK;
189        }
190        continueCfgFullPath_ = std::string(filePath);
191        HILOGD("Get Continue config file full path success, cfgFullPath %{public}s.",
192            GetAnonymStr(continueCfgFullPath_).c_str());
193    }
194
195    tempPath = continueCfgFullPath_;
196    if (!IsValidPath(tempPath, continueCfgFullPath_)) {
197        HILOGE("Continue config full path is invalid, cfgFullPath %{public}s.",
198            GetAnonymStr(continueCfgFullPath_).c_str());
199        return DMS_PERMISSION_DENIED;
200    }
201    std::ifstream in;
202    in.open(continueCfgFullPath_.c_str(), std::ios::binary | std::ios::in);
203    if (!in.is_open()) {
204        HILOGE("Open continue config json file fail, cfgFullPath %{public}s.",
205            GetAnonymStr(continueCfgFullPath_).c_str());
206        return DMS_PERMISSION_DENIED;
207    }
208
209    std::string cfgFileContent;
210    in.seekg(0, std::ios::end);
211    cfgFileContent.resize(in.tellg());
212    in.seekg(0, std::ios::beg);
213    in.rdbuf()->sgetn(&cfgFileContent[0], cfgFileContent.size());
214    in.close();
215
216    if (!UpdateWhiteList(cfgFileContent)) {
217        HILOGE("Update allow app list fail, cfgFullPath %{public}s.",
218            GetAnonymStr(continueCfgFullPath_).c_str());
219        return DMS_PERMISSION_DENIED;
220    }
221    HILOGD("Load continue config success, cfgFullPath %{public}s.", GetAnonymStr(continueCfgFullPath_).c_str());
222    return ERR_OK;
223}
224
225bool DmsKvSyncE2E::CheckBundleContinueConfig(const std::string &bundleName)
226{
227    if (!isCfgDevices_) {
228        HILOGD("The device is a normal device");
229        return true;
230    }
231
232    std::lock_guard<std::mutex> lock(kvStorePtrMutex_);
233    auto it = std::find(whiteList_.begin(), whiteList_.end(), bundleName);
234    if (it == whiteList_.end()) {
235        HILOGE("Current app is not allow to continue in config file, bundleName %{public}s, cfgPath %{public}s.",
236            bundleName.c_str(), GetAnonymStr(continueCfgFullPath_).c_str());
237        return false;
238    }
239
240    HILOGD("Current app is allow to continue in config file, bundleName %{public}s, cfgPath %{public}s.",
241        bundleName.c_str(), GetAnonymStr(continueCfgFullPath_).c_str());
242    return true;
243}
244
245bool DmsKvSyncE2E::PushAndPullData()
246{
247    HILOGI("called.");
248    std::vector<std::string> networkIdList = DtbschedmgrDeviceInfoStorage::GetInstance().GetNetworkIdList();
249    if (networkIdList.empty()) {
250        HILOGE("GetNetworkIdList failed");
251        return false;
252    }
253    if (!CheckKvStore()) {
254        HILOGE("kvStore is nullptr");
255        return false;
256    }
257    DistributedKv::DataQuery dataQuery;
258    std::shared_ptr<DmsKvSyncCB> syncCallback = std::make_shared<DmsKvSyncCB>();
259    Status status = kvStorePtr_->Sync(networkIdList, DistributedKv::SyncMode::PUSH_PULL, dataQuery, syncCallback);
260    if (status != Status::SUCCESS) {
261        HILOGE("sync error: %{public}d", status);
262        return false;
263    }
264    HILOGI("Synchronizing");
265    return true;
266}
267
268bool DmsKvSyncE2E::PushAndPullData(const std::string &networkId)
269{
270    HILOGI("called.");
271    if (!CheckDeviceCfg() && IsSynchronized(networkId)) {
272        HILOGW("The normal device been synchronized : %{public}s", GetAnonymStr(networkId).c_str());
273        return false;
274    }
275    std::vector<std::string> networkIdList = {networkId};
276    if (!CheckKvStore()) {
277        HILOGE("kvStore is nullptr");
278        return false;
279    }
280
281    SetSyncRecord(networkId);
282    DistributedKv::DataQuery dataQuery;
283    std::shared_ptr<DmsKvSyncCB> syncCallback = std::make_shared<DmsKvSyncCB>();
284    Status status = kvStorePtr_->Sync(networkIdList, DistributedKv::SyncMode::PUSH_PULL, dataQuery, syncCallback);
285    if (status != Status::SUCCESS) {
286        HILOGE("sync error: %{public}d", status);
287        return false;
288    }
289    HILOGI("Synchronizing");
290    return true;
291}
292
293void DmsKvSyncE2E::SetSyncRecord(const std::string &networkId)
294{
295    std::lock_guard<std::mutex> lock(kvStorePtrMutex_);
296    deviceSyncRecord_.insert(std::make_pair(networkId, true));
297}
298
299void DmsKvSyncE2E::ClearSyncRecord(const std::string &networkId)
300{
301    std::lock_guard<std::mutex> lock(kvStorePtrMutex_);
302    auto it = deviceSyncRecord_.find(networkId);
303    if (it != deviceSyncRecord_.end()) {
304        deviceSyncRecord_.erase(it);
305        HILOGI("Successfully cleared synchronization records for: %{public}s", GetAnonymStr(networkId).c_str());
306    } else {
307        HILOGI("No need to clean up for: %{public}s", GetAnonymStr(networkId).c_str());
308    }
309}
310
311bool DmsKvSyncE2E::IsSynchronized(const std::string &networkId)
312{
313    std::lock_guard<std::mutex> lock(kvStorePtrMutex_);
314    if (deviceSyncRecord_.find(networkId) != deviceSyncRecord_.end() && deviceSyncRecord_[networkId]) {
315        return true;
316    }
317    return false;
318}
319
320bool DmsKvSyncE2E::CheckKvStore()
321{
322    HILOGD("called.");
323    std::lock_guard<std::mutex> lock(kvStorePtrMutex_);
324    if (kvStorePtr_ != nullptr) {
325        return true;
326    }
327    int32_t tryTimes = MAX_TIMES;
328    while (tryTimes > 0) {
329        Status status = GetKvStore();
330        if (status == Status::SUCCESS && kvStorePtr_ != nullptr) {
331            return true;
332        }
333        HILOGW("CheckKvStore, Times: %{public}d", tryTimes);
334        usleep(SLEEP_INTERVAL);
335        tryTimes--;
336    }
337    HILOGD("end.");
338    return kvStorePtr_ != nullptr;
339}
340
341Status DmsKvSyncE2E::GetKvStore()
342{
343    HILOGD("called.");
344    Options options = {
345        .createIfMissing = true,
346        .encrypt = false,
347        .autoSync = false,
348        .isPublic = true,
349        .securityLevel = SecurityLevel::S1,
350        .area = EL1,
351        .kvStoreType = KvStoreType::SINGLE_VERSION,
352        .baseDir = BMS_KV_BASE_DIR,
353        .dataType = DataType::TYPE_DYNAMICAL,
354        .cloudConfig = {
355            .enableCloud = true,
356            .autoSync = true
357        },
358    };
359    Status status = dataManager_.GetSingleKvStore(options, appId_, storeId_, kvStorePtr_);
360    if (status == Status::SUCCESS) {
361        HILOGD("get kvStore success");
362    } else if (status == DistributedKv::Status::STORE_META_CHANGED) {
363        HILOGE("This db meta changed, remove and rebuild it");
364        dataManager_.DeleteKvStore(appId_, storeId_, BMS_KV_BASE_DIR + appId_.appId);
365    }
366    HILOGD("end.");
367    return status;
368}
369
370void DmsKvSyncE2E::TryTwice(const std::function<Status()> &func) const
371{
372    HILOGD("called.");
373    Status status = func();
374    if (status != Status::SUCCESS) {
375        status = func();
376        HILOGW("error and try to call again, result = %{public}d", status);
377    }
378    HILOGD("end.");
379}
380
381DmsKvSyncCB::DmsKvSyncCB()
382{
383    HILOGD("create");
384}
385
386DmsKvSyncCB::~DmsKvSyncCB()
387{
388    HILOGD("destroy");
389}
390
391void DmsKvSyncCB::SyncCompleted(const std::map<std::string, DistributedKv::Status> &result)
392{
393    HILOGI("kvstore sync completed.");
394    for (auto ele : result) {
395        HILOGI("uuid: %{public}s , result: %{public}d", GetAnonymStr(ele.first).c_str(), ele.second);
396    }
397}
398}  // namespace DistributedSchedule
399}  // namespace OHOS
400