1 /*
2  * Copyright (c) 2023-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 "listener/kv_data_change_listener.h"
17 
18 #include <algorithm>
19 #include <cinttypes>
20 
21 #include "datetime_ex.h"
22 #include "string_ex.h"
23 
24 #include "distributed_device_profile_constants.h"
25 #include "distributed_device_profile_errors.h"
26 #include "device_profile_manager.h"
27 #include "profile_utils.h"
28 #include "profile_cache.h"
29 #include "subscribe_profile_manager.h"
30 #include "subscribe_profile_manager.h"
31 #include "distributed_device_profile_log.h"
32 
33 #include "types.h"
34 
35 namespace OHOS {
36 namespace DistributedDeviceProfile {
37 namespace {
38     const std::string TAG = "KvDataChangeListener";
39     const std::string STATIC_STORE_ID = "dp_kv_static_store";
40 }
41 
KvDataChangeListener(const std::string & storeId)42 KvDataChangeListener::KvDataChangeListener(const std::string& storeId)
43 {
44     HILOGD("construct!");
45     storeId_ = storeId;
46 }
47 
~KvDataChangeListener()48 KvDataChangeListener::~KvDataChangeListener()
49 {
50     HILOGD("destruct!");
51 }
52 
OnChange(const DistributedKv::ChangeNotification & changeNotification)53 void KvDataChangeListener::OnChange(const DistributedKv::ChangeNotification& changeNotification)
54 {
55     HILOGI("storeId=%{public}s", storeId_.c_str());
56     if (storeId_ == STATIC_STORE_ID) {
57         return;
58     }
59     if (!changeNotification.GetInsertEntries().empty() &&
60         changeNotification.GetInsertEntries().size() <= MAX_DB_RECORD_SIZE) {
61         HandleAddChange(changeNotification.GetInsertEntries());
62     }
63     if (!changeNotification.GetUpdateEntries().empty() &&
64         changeNotification.GetUpdateEntries().size() <= MAX_DB_RECORD_SIZE) {
65         HandleUpdateChange(changeNotification.GetUpdateEntries());
66     }
67     if (!changeNotification.GetDeleteEntries().empty() &&
68         changeNotification.GetDeleteEntries().size() <= MAX_DB_RECORD_SIZE) {
69         HandleDeleteChange(changeNotification.GetDeleteEntries());
70     }
71 }
72 
OnChange(const DistributedKv::DataOrigin & origin,Keys && keys)73 void KvDataChangeListener::OnChange(const DistributedKv::DataOrigin& origin, Keys&& keys)
74 {
75     HILOGI("Cloud data Change. store=%{public}s", origin.store.c_str());
76     if (origin.store == STATIC_STORE_ID) {
77         return;
78     }
79     std::vector<DistributedKv::Entry> insertRecords = DeviceProfileManager::GetInstance()
80         .GetEntriesByKeys(keys[ChangeOp::OP_INSERT]);
81     if (!insertRecords.empty() && insertRecords.size() <= MAX_DB_RECORD_SIZE) {
82         HandleAddChange(insertRecords);
83     }
84     std::vector<DistributedKv::Entry> updateRecords = DeviceProfileManager::GetInstance()
85         .GetEntriesByKeys(keys[ChangeOp::OP_UPDATE]);
86     if (!updateRecords.empty() && updateRecords.size() <= MAX_DB_RECORD_SIZE) {
87         HandleUpdateChange(updateRecords);
88     }
89     std::vector<std::string> delKeys = keys[ChangeOp::OP_DELETE];
90     if (!delKeys.empty() && delKeys.size() <= MAX_DB_RECORD_SIZE) {
91         std::vector<DistributedKv::Entry> deleteRecords;
92         for (const auto& key : delKeys) {
93             DistributedKv::Entry entry;
94             DistributedKv::Key kvKey(key);
95             entry.key = kvKey;
96             deleteRecords.emplace_back(entry);
97         }
98         HandleDeleteChange(deleteRecords);
99     }
100 }
101 
OnSwitchChange(const DistributedKv::SwitchNotification & notification)102 void KvDataChangeListener::OnSwitchChange(const DistributedKv::SwitchNotification& notification)
103 {
104     HILOGI("Switch data change, deviceId: %{public}s", ProfileUtils::GetAnonyString(notification.deviceId).c_str());
105     if (notification.deviceId.empty()) {
106         HILOGE("params are valid");
107         return;
108     }
109     // is local or is online
110     std::string netWorkId = notification.deviceId;
111     std::string udid;
112     int32_t res = ProfileCache::GetInstance().GetUdidByNetWorkId(netWorkId, udid);
113     if (res != DP_SUCCESS || udid.empty()) {
114         HILOGD("get udid fail, netWorkId is invalid: %{public}s",
115             ProfileUtils::GetAnonyString(netWorkId).c_str());
116         return;
117     }
118     HandleSwitchUpdateChange(udid, notification.data.value);
119 }
120 
FilterEntries(const std::vector<DistributedKv::Entry> & records,std::map<std::string,std::string> & entriesMap,bool isDelete)121 void KvDataChangeListener::FilterEntries(const std::vector<DistributedKv::Entry>& records,
122     std::map<std::string, std::string>& entriesMap, bool isDelete)
123 {
124     std::map<std::string, std::string> ohSuffix2NonMaps;
125     std::map<std::string, std::string> non2OhSuffixMaps;
126     for (const auto& item : records) {
127         std::string dbKey = item.key.ToString();
128         std::vector<std::string> res;
129         if (ProfileUtils::SplitString(dbKey, SEPARATOR, res) != DP_SUCCESS || res.size() < NUM_3) {
130             HILOGW("invalid dbkey(%{public}s)", ProfileUtils::GetDbKeyAnonyString(dbKey).c_str());
131             continue;
132         }
133         if (res[0] != DEV_PREFIX && res[0] != SVR_PREFIX && res[0] != CHAR_PREFIX) {
134             HILOGW("%{public}s is invalid dbKey", ProfileUtils::GetDbKeyAnonyString(dbKey).c_str());
135             continue;
136         }
137         if (res[0] == CHAR_PREFIX && res.back() == CHARACTERISTIC_KEY) {
138             HILOGW("%{public}s is charProfileKey", ProfileUtils::GetDbKeyAnonyString(dbKey).c_str());
139             continue;
140         }
141         entriesMap[dbKey] = item.value.ToString();
142         if (ProfileUtils::EndsWith(res[NUM_2], OH_PROFILE_SUFFIX)) {
143             res[NUM_2] = ProfileUtils::CheckAndRemoveOhSuffix(res[NUM_2]);
144             ohSuffix2NonMaps[dbKey] = ProfileUtils::JoinString(res, SEPARATOR);
145             continue;
146         }
147         if (ProfileUtils::IsNeedAddOhSuffix(res[NUM_2], res.front() == SVR_PREFIX)) {
148             res[NUM_2] = ProfileUtils::CheckAndAddOhSuffix(res[NUM_2], res.front() == SVR_PREFIX);
149             non2OhSuffixMaps[dbKey] = ProfileUtils::JoinString(res, SEPARATOR);
150             continue;
151         }
152     }
153     for (const auto& [ohSuffixKey, nonOhSuffixKey] : ohSuffix2NonMaps) {
154         entriesMap.erase(nonOhSuffixKey);
155         non2OhSuffixMaps.erase(nonOhSuffixKey);
156     }
157     ohSuffix2NonMaps.clear();
158     if (isDelete) { return; }
159     if (non2OhSuffixMaps.empty()) { return; }
160     std::vector<std::string> ohSuffixKeys;
161     for (const auto& [nonOhSuffixKey, ohSuffixKey] : non2OhSuffixMaps) {
162         ohSuffix2NonMaps[ohSuffixKey] = nonOhSuffixKey;
163         ohSuffixKeys.emplace_back(ohSuffixKey);
164     }
165     std::vector<DistributedKv::Entry> entries = DeviceProfileManager::GetInstance().GetEntriesByKeys(ohSuffixKeys);
166     if (entries.empty()) { return; }
167     for (const auto& item : entries) {
168         entriesMap.erase(ohSuffix2NonMaps[item.key.ToString()]);
169     }
170 }
171 
HandleAddChange(const std::vector<DistributedKv::Entry> & insertRecords)172 void KvDataChangeListener::HandleAddChange(const std::vector<DistributedKv::Entry>& insertRecords)
173 {
174     HILOGD("Handle kv data add change!");
175     std::map<std::string, std::string> entries;
176     FilterEntries(insertRecords, entries, false);
177     for (const auto& [dbKey, dbValue] : entries) {
178         ProfileType profileType = ProfileUtils::GetProfileType(dbKey);
179         SubscribeProfileManager::GetInstance().NotifyProfileChange(profileType, ChangeType::ADD, dbKey, dbValue);
180     }
181 }
182 
HandleUpdateChange(const std::vector<DistributedKv::Entry> & updateRecords)183 void KvDataChangeListener::HandleUpdateChange(const std::vector<DistributedKv::Entry>& updateRecords)
184 {
185     HILOGD("Handle kv data update change!");
186     std::map<std::string, std::string> entries;
187     FilterEntries(updateRecords, entries, false);
188     for (const auto& [dbKey, dbValue] : entries) {
189         ProfileType profileType = ProfileUtils::GetProfileType(dbKey);
190         SubscribeProfileManager::GetInstance().NotifyProfileChange(profileType, ChangeType::UPDATE, dbKey, dbValue);
191     }
192 }
193 
HandleDeleteChange(const std::vector<DistributedKv::Entry> & deleteRecords)194 void KvDataChangeListener::HandleDeleteChange(const std::vector<DistributedKv::Entry>& deleteRecords)
195 {
196     HILOGD("Handle kv data delete change!");
197     std::map<std::string, std::string> entries;
198     FilterEntries(deleteRecords, entries, true);
199     for (const auto& [dbKey, dbValue] : entries) {
200         ProfileType profileType = ProfileUtils::GetProfileType(dbKey);
201         SubscribeProfileManager::GetInstance().NotifyProfileChange(profileType, ChangeType::DELETE, dbKey, dbValue);
202     }
203 }
204 
HandleSwitchUpdateChange(const std::string udid,uint32_t switchValue)205 void KvDataChangeListener::HandleSwitchUpdateChange(const std::string udid, uint32_t switchValue)
206 {
207     HILOGI("udid: %{public}s, switch: %{public}u", ProfileUtils::GetAnonyString(udid).c_str(), switchValue);
208     std::string serviceName;
209 
210     for (int32_t i = (int32_t)SwitchFlag::SWITCH_FLAG_MIN + NUM_1;
211         i < (int32_t)SwitchFlag::SWITCH_FLAG_MAX; ++i) {
212         std::string itemSwitchValue = std::to_string((switchValue >> i) & NUM_1);
213         int32_t res = ProfileCache::GetInstance().GetServiceNameByPos(i, SWITCH_SERVICE_MAP, serviceName);
214         if (res != DP_SUCCESS || serviceName.empty()) {
215             HILOGE("GetServiceNameByPos failed, pos:%{public}d", i);
216             return;
217         }
218         res = GenerateSwitchNotify(udid, serviceName, SWITCH_STATUS,
219         itemSwitchValue, ChangeType::UPDATE);
220         if (res != DP_SUCCESS) {
221             HILOGE("GenerateSwitchNotify failed, res: %{public}d", res);
222             return;
223         }
224         if (udid == ProfileCache::GetInstance().GetLocalUdid()) {
225             ProfileCache::GetInstance().SetCurSwitch(switchValue);
226             HILOGD("update curLocalSwitch: %{public}d", ProfileCache::GetInstance().GetSwitch());
227         }
228     }
229 }
230 
GenerateSwitchNotify(const std::string & udid,const std::string & serviceName,const std::string & characteristicProfileKey,const std::string & characteristicProfileValue,ChangeType changeType)231 int32_t KvDataChangeListener::GenerateSwitchNotify(const std::string& udid, const std::string& serviceName,
232     const std::string& characteristicProfileKey, const std::string& characteristicProfileValue, ChangeType changeType)
233 {
234     if (!ProfileUtils::IsKeyValid(udid) ||
235         !ProfileUtils::IsKeyValid(serviceName) ||
236         !ProfileUtils::IsKeyValid(characteristicProfileKey) ||
237         !ProfileUtils::IsKeyValid(characteristicProfileValue)) {
238         HILOGE("Params are invalid!");
239         return DP_INVALID_PARAMS;
240     }
241 
242     CharacteristicProfile newSwitchProfile = {udid, serviceName, characteristicProfileKey,
243         characteristicProfileValue};
244     HILOGI("Gen SwitchProfile :%{public}s", newSwitchProfile.dump().c_str());
245     if (ProfileCache::GetInstance().IsCharProfileExist(newSwitchProfile)) {
246         HILOGW("switch is not change");
247         return DP_SUCCESS;
248     }
249     const CharacteristicProfile cacheProfile = newSwitchProfile;
250     ProfileCache::GetInstance().AddCharProfile(cacheProfile);
251     std::string dbKey = ProfileUtils::GetDbKeyByProfile(newSwitchProfile);
252     ProfileType profileType = ProfileUtils::GetProfileType(dbKey);
253     int32_t res = SubscribeProfileManager::GetInstance().NotifyProfileChange(profileType, changeType, dbKey,
254         newSwitchProfile.GetCharacteristicValue());
255     if (res != DP_SUCCESS) {
256         HILOGE("NotifyProfileChange failed");
257         return DP_GENERATE_SWITCH_NOTIFY_FAIL;
258     }
259     return DP_SUCCESS;
260 }
261 } // namespace DeviceProfile
262 } // namespace OHOS
263