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 #include "napi_hiappevent_watch.h"
16
17 #include <string>
18
19 #include "app_event_observer_mgr.h"
20 #include "app_event_stat.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 #undef LOG_DOMAIN
31 #define LOG_DOMAIN 0xD002D07
32
33 #undef LOG_TAG
34 #define LOG_TAG "NapiWatch"
35
36 namespace OHOS {
37 namespace HiviewDFX {
38 namespace NapiHiAppEventWatch {
39 namespace {
40 const std::string NAME_PROPERTY = "name";
41 const std::string COND_PROPERTY = "triggerCondition";
42 const std::string COND_PROPS[] = { "row", "size", "timeOut" };
43 const std::string FILTERS_PROPERTY = "appEventFilters";
44 const std::string FILTERS_DOAMIN_PROP = "domain";
45 const std::string FILTERS_TYPES_PROP = "eventTypes";
46 const std::string FILTERS_NAMES_PROP = "names";
47 const std::string TRIGGER_PROPERTY = "onTrigger";
48 const std::string RECEIVE_PROPERTY = "onReceive";
49 constexpr int BIT_MASK = 1;
50 constexpr unsigned int BIT_ALL_TYPES = 0xff;
51
IsValidName(const napi_env env,const napi_value name,int & errCode)52 bool IsValidName(const napi_env env, const napi_value name, int& errCode)
53 {
54 if (name == nullptr) {
55 NapiUtil::ThrowError(env, NapiError::ERR_PARAM, NapiUtil::CreateErrMsg(NAME_PROPERTY));
56 errCode = NapiError::ERR_PARAM;
57 return false;
58 }
59 if (!NapiUtil::IsString(env, name)) {
60 NapiUtil::ThrowError(env, NapiError::ERR_PARAM, NapiUtil::CreateErrMsg(NAME_PROPERTY, "string"));
61 errCode = NapiError::ERR_PARAM;
62 return false;
63 }
64 if (!IsValidWatcherName(NapiUtil::GetString(env, name))) {
65 NapiUtil::ThrowError(env, NapiError::ERR_INVALID_WATCHER_NAME, "Invalid watcher name.");
66 errCode = NapiError::ERR_INVALID_WATCHER_NAME;
67 return false;
68 }
69 return true;
70 }
71
IsValidCondition(const napi_env env,const napi_value cond,int & errCode)72 bool IsValidCondition(const napi_env env, const napi_value cond, int& errCode)
73 {
74 if (cond == nullptr) {
75 return true;
76 }
77 if (!NapiUtil::IsObject(env, cond)) {
78 NapiUtil::ThrowError(env, NapiError::ERR_PARAM, NapiUtil::CreateErrMsg(COND_PROPERTY, "TriggerCondition"));
79 errCode = NapiError::ERR_PARAM;
80 return false;
81 }
82 for (auto& propName : COND_PROPS) {
83 napi_value propValue = NapiUtil::GetProperty(env, cond, propName);
84 if (propValue == nullptr) {
85 continue;
86 }
87 if (!NapiUtil::IsNumber(env, propValue)) {
88 NapiUtil::ThrowError(env, NapiError::ERR_PARAM, NapiUtil::CreateErrMsg(propName, "number"));
89 errCode = NapiError::ERR_PARAM;
90 return false;
91 }
92 }
93 return true;
94 }
95
IsValidFilter(const napi_env env,const napi_value filter,int & errCode)96 bool IsValidFilter(const napi_env env, const napi_value filter, int& errCode)
97 {
98 napi_value domain = NapiUtil::GetProperty(env, filter, FILTERS_DOAMIN_PROP);
99 if (domain == nullptr) {
100 NapiUtil::ThrowError(env, NapiError::ERR_PARAM, NapiUtil::CreateErrMsg(FILTERS_DOAMIN_PROP));
101 errCode = NapiError::ERR_PARAM;
102 return false;
103 }
104 if (!NapiUtil::IsString(env, domain)) {
105 NapiUtil::ThrowError(env, NapiError::ERR_PARAM, NapiUtil::CreateErrMsg(FILTERS_DOAMIN_PROP, "string"));
106 errCode = NapiError::ERR_PARAM;
107 return false;
108 }
109 if (!IsValidDomain(NapiUtil::GetString(env, domain))) {
110 NapiUtil::ThrowError(env, NapiError::ERR_INVALID_FILTER_DOMAIN, "Invalid filtering event domain.");
111 errCode = NapiError::ERR_INVALID_FILTER_DOMAIN;
112 return false;
113 }
114 napi_value types = NapiUtil::GetProperty(env, filter, FILTERS_TYPES_PROP);
115 if (types != nullptr && !NapiUtil::IsArrayType(env, types, napi_number)) {
116 NapiUtil::ThrowError(env, NapiError::ERR_PARAM, NapiUtil::CreateErrMsg(FILTERS_TYPES_PROP, "EventType[]"));
117 errCode = NapiError::ERR_PARAM;
118 return false;
119 }
120 napi_value names = NapiUtil::GetProperty(env, filter, FILTERS_NAMES_PROP);
121 if (names != nullptr && !NapiUtil::IsArrayType(env, names, napi_string)) {
122 NapiUtil::ThrowError(env, NapiError::ERR_PARAM, NapiUtil::CreateErrMsg(FILTERS_NAMES_PROP, "string[]"));
123 errCode = NapiError::ERR_PARAM;
124 return false;
125 }
126 return true;
127 }
128
IsValidFilters(const napi_env env,const napi_value filters,int & errCode)129 bool IsValidFilters(const napi_env env, const napi_value filters, int& errCode)
130 {
131 if (filters == nullptr) {
132 return true;
133 }
134 if (!NapiUtil::IsArrayType(env, filters, napi_object)) {
135 NapiUtil::ThrowError(env, NapiError::ERR_PARAM, NapiUtil::CreateErrMsg(FILTERS_PROPERTY, "AppEventFilter[]"));
136 errCode = NapiError::ERR_PARAM;
137 return false;
138 }
139
140 size_t len = NapiUtil::GetArrayLength(env, filters);
141 for (size_t i = 0; i < len; i++) {
142 napi_value filter = NapiUtil::GetElement(env, filters, i);
143 if (!IsValidFilter(env, filter, errCode)) {
144 return false;
145 }
146 }
147 return true;
148 }
149
IsValidTrigger(const napi_env env,const napi_value trigger,int & errCode)150 bool IsValidTrigger(const napi_env env, const napi_value trigger, int& errCode)
151 {
152 if (trigger == nullptr) {
153 return true;
154 }
155 if (!NapiUtil::IsFunction(env, trigger)) {
156 NapiUtil::ThrowError(env, NapiError::ERR_PARAM, NapiUtil::CreateErrMsg(TRIGGER_PROPERTY, "function"));
157 errCode = NapiError::ERR_PARAM;
158 return false;
159 }
160 return true;
161 }
162
IsValidReceive(const napi_env env,const napi_value receive,int & errCode)163 bool IsValidReceive(const napi_env env, const napi_value receive, int& errCode)
164 {
165 if (receive == nullptr) {
166 return true;
167 }
168 if (!NapiUtil::IsFunction(env, receive)) {
169 NapiUtil::ThrowError(env, NapiError::ERR_PARAM, NapiUtil::CreateErrMsg(RECEIVE_PROPERTY, "function"));
170 errCode = NapiError::ERR_PARAM;
171 return false;
172 }
173 return true;
174 }
175
IsValidWatcher(const napi_env env,const napi_value watcher,int & errCode)176 bool IsValidWatcher(const napi_env env, const napi_value watcher, int& errCode)
177 {
178 if (!NapiUtil::IsObject(env, watcher)) {
179 NapiUtil::ThrowError(env, NapiError::ERR_PARAM, NapiUtil::CreateErrMsg("watcher", "Watcher"));
180 errCode = NapiError::ERR_PARAM;
181 return false;
182 }
183 return IsValidName(env, NapiUtil::GetProperty(env, watcher, NAME_PROPERTY), errCode)
184 && IsValidCondition(env, NapiUtil::GetProperty(env, watcher, COND_PROPERTY), errCode)
185 && IsValidFilters(env, NapiUtil::GetProperty(env, watcher, FILTERS_PROPERTY), errCode)
186 && IsValidTrigger(env, NapiUtil::GetProperty(env, watcher, TRIGGER_PROPERTY), errCode)
187 && IsValidReceive(env, NapiUtil::GetProperty(env, watcher, RECEIVE_PROPERTY), errCode);
188 }
189
GetConditionValue(const napi_env env,const napi_value cond,const std::string & name)190 int GetConditionValue(const napi_env env, const napi_value cond, const std::string& name)
191 {
192 if (auto value = NapiUtil::GetProperty(env, cond, name); value != nullptr) {
193 return NapiUtil::GetInt32(env, value);
194 }
195 return 0;
196 }
197
GetName(const napi_env env,const napi_value watcher)198 std::string GetName(const napi_env env, const napi_value watcher)
199 {
200 return NapiUtil::GetString(env, NapiUtil::GetProperty(env, watcher, NAME_PROPERTY));
201 }
202
GetCondition(const napi_env env,const napi_value watcher)203 TriggerCondition GetCondition(const napi_env env, const napi_value watcher)
204 {
205 TriggerCondition resCond = {
206 .row = 0,
207 .size = 0,
208 .timeout = 0
209 };
210 napi_value cond = NapiUtil::GetProperty(env, watcher, COND_PROPERTY);
211 if (cond == nullptr) {
212 return resCond;
213 }
214
215 size_t index = 0;
216 int row = GetConditionValue(env, cond, COND_PROPS[index++]);
217 if (row < 0) {
218 NapiUtil::ThrowError(env, NapiError::ERR_INVALID_COND_ROW, "Invalid row value.");
219 return resCond;
220 }
221 resCond.row = row;
222
223 int size = GetConditionValue(env, cond, COND_PROPS[index++]);
224 if (size < 0) {
225 NapiUtil::ThrowError(env, NapiError::ERR_INVALID_COND_SIZE, "Invalid size value.");
226 return resCond;
227 }
228 resCond.size = size;
229
230 int timeout = GetConditionValue(env, cond, COND_PROPS[index++]);
231 if (timeout < 0) {
232 NapiUtil::ThrowError(env, NapiError::ERR_INVALID_COND_TIMEOUT, "Invalid timeout value.");
233 return resCond;
234 }
235 resCond.timeout = timeout * HiAppEvent::TIMEOUT_STEP;
236 return resCond;
237 }
238
GetFilters(const napi_env env,const napi_value watcher,std::vector<AppEventFilter> & filters)239 void GetFilters(const napi_env env, const napi_value watcher, std::vector<AppEventFilter>& filters)
240 {
241 napi_value filtersValue = NapiUtil::GetProperty(env, watcher, FILTERS_PROPERTY);
242 if (filtersValue == nullptr) {
243 return;
244 }
245 size_t len = NapiUtil::GetArrayLength(env, filtersValue);
246 for (size_t i = 0; i < len; i++) {
247 napi_value filterValue = NapiUtil::GetElement(env, filtersValue, i);
248 std::string domain = NapiUtil::GetString(env, NapiUtil::GetProperty(env, filterValue, FILTERS_DOAMIN_PROP));
249 napi_value namesValue = NapiUtil::GetProperty(env, filterValue, FILTERS_NAMES_PROP);
250 std::unordered_set<std::string> names;
251 if (namesValue != nullptr) {
252 NapiUtil::GetStringsToSet(env, namesValue, names);
253 }
254 napi_value typesValue = NapiUtil::GetProperty(env, filterValue, FILTERS_TYPES_PROP);
255 if (typesValue == nullptr) {
256 filters.emplace_back(AppEventFilter(domain, names, BIT_ALL_TYPES));
257 continue;
258 }
259 std::vector<int> types;
260 NapiUtil::GetInt32s(env, typesValue, types);
261 unsigned int filterType = 0;
262 for (auto type : types) {
263 if (!IsValidEventType(type)) {
264 std::string errMsg = NapiUtil::CreateErrMsg(FILTERS_TYPES_PROP, "EventType[]");
265 NapiUtil::ThrowError(env, NapiError::ERR_PARAM, errMsg);
266 continue;
267 }
268 filterType |= (BIT_MASK << type);
269 }
270 filterType = filterType > 0 ? filterType : BIT_ALL_TYPES;
271 filters.emplace_back(AppEventFilter(domain, names, filterType));
272 }
273 }
274
CreateHolder(const napi_env env,size_t argc,const napi_value argv[])275 napi_value CreateHolder(const napi_env env, size_t argc, const napi_value argv[])
276 {
277 napi_value constructor = nullptr;
278 if (napi_get_reference_value(env, NapiAppEventHolder::constructor_, &constructor) != napi_ok) {
279 HILOG_ERROR(LOG_CORE, "failed to get constructor of the holder");
280 return NapiUtil::CreateNull(env);
281 }
282 napi_value holder = nullptr;
283 if (napi_new_instance(env, constructor, argc, argv, &holder) != napi_ok) {
284 HILOG_ERROR(LOG_CORE, "failed to get new instance for holder");
285 return NapiUtil::CreateNull(env);
286 }
287 return holder;
288 }
289 }
290
AddWatcher(const napi_env env,const napi_value watcher,uint64_t beginTime)291 napi_value AddWatcher(const napi_env env, const napi_value watcher, uint64_t beginTime)
292 {
293 int errCode = NapiError::ERR_OK;
294 if (!IsValidWatcher(env, watcher, errCode)) {
295 HILOG_ERROR(LOG_CORE, "invalid watcher");
296 AppEventStat::WriteApiEndEventAsync("addWatcher", beginTime, AppEventStat::FAILED, errCode);
297 return NapiUtil::CreateNull(env);
298 }
299
300 // 1. build watcher object
301 std::vector<AppEventFilter> filters;
302 GetFilters(env, watcher, filters);
303 std::string name = GetName(env, watcher);
304 TriggerCondition cond = GetCondition(env, watcher);
305 auto watcherPtr = std::make_shared<NapiAppEventWatcher>(name, filters, cond);
306
307 // 2. set trigger if any
308 napi_value trigger = NapiUtil::GetProperty(env, watcher, TRIGGER_PROPERTY);
309 if (trigger != nullptr) {
310 watcherPtr->InitTrigger(env, trigger);
311 }
312
313 // 3. set receive if any
314 napi_value receiver = NapiUtil::GetProperty(env, watcher, RECEIVE_PROPERTY);
315 if (receiver != nullptr) {
316 watcherPtr->InitReceiver(env, receiver);
317 }
318
319 // 4. add the watcher to Manager
320 int64_t observerSeq = AppEventObserverMgr::GetInstance().RegisterObserver(watcherPtr);
321 if (observerSeq <= 0) {
322 HILOG_ERROR(LOG_CORE, "invalid observer sequence");
323 AppEventStat::WriteApiEndEventAsync("addWatcher", beginTime, AppEventStat::FAILED, NapiError::ERR_OK);
324 return NapiUtil::CreateNull(env);
325 }
326
327 // 5. create holder and add holder to the watcher
328 constexpr size_t holderParamNum = 2;
329 napi_value holderParams[holderParamNum] = {
330 NapiUtil::CreateString(env, name),
331 NapiUtil::CreateInt64(env, observerSeq)
332 };
333 napi_value holder = CreateHolder(env, holderParamNum, holderParams);
334 watcherPtr->InitHolder(env, holder);
335 AppEventStat::WriteApiEndEventAsync("addWatcher", beginTime, AppEventStat::SUCCESS, NapiError::ERR_OK);
336 return holder;
337 }
338
RemoveWatcher(const napi_env env,const napi_value watcher,uint64_t beginTime)339 napi_value RemoveWatcher(const napi_env env, const napi_value watcher, uint64_t beginTime)
340 {
341 if (!NapiUtil::IsObject(env, watcher)) {
342 AppEventStat::WriteApiEndEventAsync("removeWatcher", beginTime, AppEventStat::FAILED, NapiError::ERR_PARAM);
343 NapiUtil::ThrowError(env, NapiError::ERR_PARAM, NapiUtil::CreateErrMsg("watcher", "Watcher"));
344 return NapiUtil::CreateUndefined(env);
345 }
346 int errCode = NapiError::ERR_OK;
347 if (!IsValidName(env, NapiUtil::GetProperty(env, watcher, NAME_PROPERTY), errCode)) {
348 AppEventStat::WriteApiEndEventAsync("removeWatcher", beginTime, AppEventStat::FAILED, errCode);
349 return NapiUtil::CreateUndefined(env);
350 }
351 (void)AppEventObserverMgr::GetInstance().UnregisterObserver(GetName(env, watcher));
352 AppEventStat::WriteApiEndEventAsync("removeWatcher", beginTime, AppEventStat::SUCCESS, NapiError::ERR_OK);
353 return NapiUtil::CreateUndefined(env);
354 }
355 } // namespace NapiHiAppEventConfig
356 } // namespace HiviewDFX
357 } // namespace OHOS
358