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