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