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