1 /*
2 * Copyright (c) 2021-2022 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 #include "napi_hiappevent_watch.h"
16
17 #include <string>
18
19 #include "app_event_cache.h"
20 #include "app_event_watcher_mgr.h"
21 #include "hiappevent_base.h"
22 #include "hiappevent_config.h"
23 #include "hiappevent_verify.h"
24 #include "hilog/log.h"
25 #include "napi_app_event_holder.h"
26 #include "napi_app_event_watcher.h"
27 #include "napi_error.h"
28 #include "napi_util.h"
29
30 namespace OHOS {
31 namespace HiviewDFX {
32 namespace NapiHiAppEventWatch {
33 namespace {
34 constexpr HiLogLabel LABEL = { LOG_CORE, HIAPPEVENT_DOMAIN, "Napi_HiAppEvent_Watch" };
35 const std::string NAME_PROPERTY = "name";
36 const std::string COND_PROPERTY = "triggerCondition";
37 const std::string COND_PROPS[] = { "row", "size", "timeOut" };
38 const std::string FILTERS_PROPERTY = "appEventFilters";
39 const std::string FILTERS_DOAMIN_PROP = "domain";
40 const std::string FILTERS_TYPES_PROP = "eventTypes";
41 const std::string TRIGGER_PROPERTY = "onTrigger";
42 constexpr int BIT_MASK = 1;
43 constexpr unsigned int BIT_ALL_TYPES = 0xff;
44
IsValidName(const napi_env env,const napi_value name)45 bool IsValidName(const napi_env env, const napi_value name)
46 {
47 if (name == nullptr) {
48 NapiUtil::ThrowError(env, NapiError::ERR_PARAM, NapiUtil::CreateErrMsg(NAME_PROPERTY));
49 return false;
50 }
51 if (!NapiUtil::IsString(env, name)) {
52 NapiUtil::ThrowError(env, NapiError::ERR_PARAM, NapiUtil::CreateErrMsg(NAME_PROPERTY, "string"));
53 return false;
54 }
55 if (!IsValidWatcherName(NapiUtil::GetString(env, name))) {
56 NapiUtil::ThrowError(env, NapiError::ERR_INVALID_WATCHER_NAME, "Invalid watcher name.");
57 return false;
58 }
59 return true;
60 }
61
IsValidCondition(const napi_env env,const napi_value cond)62 bool IsValidCondition(const napi_env env, const napi_value cond)
63 {
64 if (cond == nullptr) {
65 return true;
66 }
67 if (!NapiUtil::IsObject(env, cond)) {
68 NapiUtil::ThrowError(env, NapiError::ERR_PARAM, NapiUtil::CreateErrMsg(COND_PROPERTY, "TriggerCondition"));
69 return false;
70 }
71 for (auto& propName : COND_PROPS) {
72 napi_value propValue = NapiUtil::GetProperty(env, cond, propName);
73 if (propValue == nullptr) {
74 continue;
75 }
76 if (!NapiUtil::IsNumber(env, propValue)) {
77 NapiUtil::ThrowError(env, NapiError::ERR_PARAM, NapiUtil::CreateErrMsg(propName, "number"));
78 return false;
79 }
80 }
81 return true;
82 }
83
IsValidFilter(const napi_env env,const napi_value filter)84 bool IsValidFilter(const napi_env env, const napi_value filter)
85 {
86 napi_value domain = NapiUtil::GetProperty(env, filter, FILTERS_DOAMIN_PROP);
87 if (domain == nullptr) {
88 NapiUtil::ThrowError(env, NapiError::ERR_PARAM, NapiUtil::CreateErrMsg(FILTERS_DOAMIN_PROP));
89 return false;
90 }
91 if (!NapiUtil::IsString(env, domain)) {
92 NapiUtil::ThrowError(env, NapiError::ERR_PARAM, NapiUtil::CreateErrMsg(FILTERS_DOAMIN_PROP, "string"));
93 return false;
94 }
95 if (!IsValidDomain(NapiUtil::GetString(env, domain))) {
96 NapiUtil::ThrowError(env, NapiError::ERR_INVALID_FILTER_DOMAIN, "Invalid filtering event domain.");
97 return false;
98 }
99 napi_value types = NapiUtil::GetProperty(env, filter, FILTERS_TYPES_PROP);
100 if (types == nullptr) {
101 return true;
102 }
103 if (!NapiUtil::IsArrayType(env, types, napi_number)) {
104 NapiUtil::ThrowError(env, NapiError::ERR_PARAM, NapiUtil::CreateErrMsg(FILTERS_TYPES_PROP, "EventType[]"));
105 return false;
106 }
107 return true;
108 }
109
110
IsValidFilters(const napi_env env,const napi_value filters)111 bool IsValidFilters(const napi_env env, const napi_value filters)
112 {
113 if (filters == nullptr) {
114 return true;
115 }
116 if (!NapiUtil::IsArrayType(env, filters, napi_object)) {
117 NapiUtil::ThrowError(env, NapiError::ERR_PARAM, NapiUtil::CreateErrMsg(FILTERS_PROPERTY, "AppEventFilter[]"));
118 return false;
119 }
120
121 size_t len = NapiUtil::GetArrayLength(env, filters);
122 for (size_t i = 0; i < len; i++) {
123 napi_value filter = NapiUtil::GetElement(env, filters, i);
124 if (!IsValidFilter(env, filter)) {
125 return false;
126 }
127 }
128 return true;
129 }
130
IsValidTrigger(const napi_env env,const napi_value trigger)131 bool IsValidTrigger(const napi_env env, const napi_value trigger)
132 {
133 if (trigger == nullptr) {
134 return true;
135 }
136 if (!NapiUtil::IsFunction(env, trigger)) {
137 NapiUtil::ThrowError(env, NapiError::ERR_PARAM, NapiUtil::CreateErrMsg(TRIGGER_PROPERTY, "function"));
138 return false;
139 }
140 return true;
141 }
142
IsValidWatcher(const napi_env env,const napi_value watcher)143 bool IsValidWatcher(const napi_env env, const napi_value watcher)
144 {
145 if (!NapiUtil::IsObject(env, watcher)) {
146 NapiUtil::ThrowError(env, NapiError::ERR_PARAM, NapiUtil::CreateErrMsg("watcher", "Watcher"));
147 return false;
148 }
149 return IsValidName(env, NapiUtil::GetProperty(env, watcher, NAME_PROPERTY))
150 && IsValidCondition(env, NapiUtil::GetProperty(env, watcher, COND_PROPERTY))
151 && IsValidFilters(env, NapiUtil::GetProperty(env, watcher, FILTERS_PROPERTY))
152 && IsValidTrigger(env, NapiUtil::GetProperty(env, watcher, TRIGGER_PROPERTY));
153 }
154
GetConditionValue(const napi_env env,const napi_value cond,const std::string & name)155 int GetConditionValue(const napi_env env, const napi_value cond, const std::string& name)
156 {
157 if (auto value = NapiUtil::GetProperty(env, cond, name); value != nullptr) {
158 return NapiUtil::GetInt32(env, value);
159 }
160 return 0;
161 }
162
GetName(const napi_env env,const napi_value watcher)163 std::string GetName(const napi_env env, const napi_value watcher)
164 {
165 return NapiUtil::GetString(env, NapiUtil::GetProperty(env, watcher, NAME_PROPERTY));
166 }
167
GetCondition(const napi_env env,const napi_value watcher)168 TriggerCondition GetCondition(const napi_env env, const napi_value watcher)
169 {
170 TriggerCondition resCond = {
171 .row = 0,
172 .size = 0,
173 .timeOut = 0
174 };
175 napi_value cond = NapiUtil::GetProperty(env, watcher, COND_PROPERTY);
176 if (cond == nullptr) {
177 return resCond;
178 }
179
180 size_t index = 0;
181 int row = GetConditionValue(env, cond, COND_PROPS[index++]);
182 if (row < 0) {
183 NapiUtil::ThrowError(env, NapiError::ERR_INVALID_COND_ROW, "Invalid row value.");
184 return resCond;
185 }
186
187 resCond.row = row;
188 int size = GetConditionValue(env, cond, COND_PROPS[index++]);
189 if (size < 0) {
190 NapiUtil::ThrowError(env, NapiError::ERR_INVALID_COND_SIZE, "Invalid size value.");
191 return resCond;
192 }
193
194 resCond.size = size;
195 int timeOut = GetConditionValue(env, cond, COND_PROPS[index++]);
196 if (timeOut < 0) {
197 NapiUtil::ThrowError(env, NapiError::ERR_INVALID_COND_TIMEOUT, "Invalid timeout value.");
198 return resCond;
199 }
200 return resCond;
201 }
202
GetFilters(const napi_env env,const napi_value watcher,std::map<std::string,unsigned int> & filters)203 void GetFilters(const napi_env env, const napi_value watcher, std::map<std::string, unsigned int>& filters)
204 {
205 napi_value filtersValue = NapiUtil::GetProperty(env, watcher, FILTERS_PROPERTY);
206 if (filtersValue == nullptr) {
207 return;
208 }
209 size_t len = NapiUtil::GetArrayLength(env, filtersValue);
210 for (size_t i = 0; i < len; i++) {
211 napi_value filterValue = NapiUtil::GetElement(env, filtersValue, i);
212 std::string domain = NapiUtil::GetString(env, NapiUtil::GetProperty(env, filterValue, FILTERS_DOAMIN_PROP));
213 napi_value typesValue = NapiUtil::GetProperty(env, filterValue, FILTERS_TYPES_PROP);
214 if (typesValue == nullptr) {
215 filters[domain] = BIT_ALL_TYPES;
216 continue;
217 }
218 std::vector<int> types;
219 NapiUtil::GetInt32s(env, typesValue, types);
220 unsigned int filterType = 0;
221 for (auto type : types) {
222 if (!IsValidEventType(type)) {
223 std::string errMsg = NapiUtil::CreateErrMsg(FILTERS_TYPES_PROP, "EventType[]");
224 NapiUtil::ThrowError(env, NapiError::ERR_PARAM, errMsg);
225 continue;
226 }
227 filterType |= (BIT_MASK << type);
228 }
229 filters[domain] = filterType > 0 ? filterType : BIT_ALL_TYPES;
230 }
231 }
232
CreateHolder(const napi_env env,const napi_value watcherName)233 napi_value CreateHolder(const napi_env env, const napi_value watcherName)
234 {
235 napi_value constructor = nullptr;
236 if (napi_get_reference_value(env, NapiAppEventHolder::constructor_, &constructor) != napi_ok) {
237 HiLog::Error(LABEL, "failed to get constructor of the holder");
238 return NapiUtil::CreateNull(env);
239 }
240 napi_value holder = nullptr;
241 if (napi_new_instance(env, constructor, 1, &watcherName, &holder) != napi_ok) { // param num is 1
242 HiLog::Error(LABEL, "failed to get new instance for holder");
243 return NapiUtil::CreateNull(env);
244 }
245 return holder;
246 }
247 }
248
AddWatcher(const napi_env env,const napi_value watcher)249 napi_value AddWatcher(const napi_env env, const napi_value watcher)
250 {
251 if (!IsValidWatcher(env, watcher)) {
252 HiLog::Error(LABEL, "invalid watcher");
253 return NapiUtil::CreateNull(env);
254 }
255
256 // 1. if DB is not opened, need to open DB first
257 if (!AppEventCache::GetInstance()->IsOpen()) {
258 std::string dir = HiAppEventConfig::GetInstance().GetStorageDir();
259 if (dir.empty() || AppEventCache::GetInstance()->Open(dir) != 0) {
260 HiLog::Error(LABEL, "failed to open db");
261 return NapiUtil::CreateNull(env);
262 }
263 }
264
265 // 2. build watcher object
266 std::map<std::string, unsigned int> filters;
267 GetFilters(env, watcher, filters);
268 std::string name = GetName(env, watcher);
269 TriggerCondition cond = GetCondition(env, watcher);
270 auto watcherPtr = std::make_shared<NapiAppEventWatcher>(name, filters, cond);
271
272 // 3. create holder and add holder to the watcher
273 napi_value holder = CreateHolder(env, NapiUtil::GetProperty(env, watcher, NAME_PROPERTY));
274 watcherPtr->InitHolder(env, holder);
275
276 // 4. set trigger if any
277 napi_value trigger = NapiUtil::GetProperty(env, watcher, TRIGGER_PROPERTY);
278 if (trigger != nullptr) {
279 watcherPtr->InitTrigger(env, trigger);
280 }
281
282 // 5. add the watcher to watcherManager
283 AppEventWatcherMgr::GetInstance()->AddWatcher(watcherPtr);
284 return holder;
285 }
286
RemoveWatcher(const napi_env env,const napi_value watcher)287 napi_value RemoveWatcher(const napi_env env, const napi_value watcher)
288 {
289 if (!NapiUtil::IsObject(env, watcher)) {
290 NapiUtil::ThrowError(env, NapiError::ERR_PARAM, NapiUtil::CreateErrMsg("watcher", "Watcher"));
291 return NapiUtil::CreateUndefined(env);
292 }
293 if (!IsValidName(env, NapiUtil::GetProperty(env, watcher, NAME_PROPERTY))) {
294 return NapiUtil::CreateUndefined(env);
295 }
296 AppEventWatcherMgr::GetInstance()->RemoveWatcher(GetName(env, watcher));
297 return NapiUtil::CreateUndefined(env);
298 }
299 } // namespace NapiHiAppEventConfig
300 } // namespace HiviewDFX
301 } // namespace OHOS
302