1 /*
2  * Copyright (c) 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 #include "napi_data_ability_observer.h"
16 
17 #include <uv.h>
18 
19 #include "hilog_tag_wrapper.h"
20 
21 using namespace OHOS::AAFwk;
22 using namespace OHOS::AppExecFwk;
23 
24 namespace OHOS {
25 namespace AppExecFwk {
ReleaseJSCallback()26 void NAPIDataAbilityObserver::ReleaseJSCallback()
27 {
28     std::lock_guard<std::mutex> lock(mutex_);
29     if (ref_ == nullptr) {
30         TAG_LOGE(AAFwkTag::FA, "null ref_");
31         return;
32     }
33 
34     if (isCallingback_) {
35         needRelease_ = true;
36         TAG_LOGW(AAFwkTag::FA, "calling back");
37         return;
38     }
39 
40     SafeReleaseJSCallback();
41     TAG_LOGI(AAFwkTag::FA, "end");
42 }
43 
SafeReleaseJSCallback()44 void NAPIDataAbilityObserver::SafeReleaseJSCallback()
45 {
46     uv_loop_s* loop = nullptr;
47     napi_get_uv_event_loop(env_, &loop);
48     if (loop == nullptr) {
49         TAG_LOGE(AAFwkTag::FA, "null loop");
50         return;
51     }
52 
53     struct DelRefCallbackInfo {
54         napi_env env_;
55         napi_ref ref_;
56     };
57 
58     DelRefCallbackInfo* delRefCallbackInfo = new DelRefCallbackInfo {
59         .env_ = env_,
60         .ref_ = ref_,
61     };
62 
63     uv_work_t* work = new uv_work_t;
64     work->data = static_cast<void*>(delRefCallbackInfo);
65     int ret = uv_queue_work_with_qos(
66         loop, work, [](uv_work_t* work) {},
67         [](uv_work_t* work, int status) {
68             // JS Thread
69             if (work == nullptr) {
70                 TAG_LOGE(AAFwkTag::FA, "null work");
71                 return;
72             }
73             auto delRefCallbackInfo =  reinterpret_cast<DelRefCallbackInfo*>(work->data);
74             if (delRefCallbackInfo == nullptr) {
75                 TAG_LOGE(AAFwkTag::FA, "null delRefCallbackInfo");
76                 delete work;
77                 work = nullptr;
78                 return;
79             }
80 
81             napi_delete_reference(delRefCallbackInfo->env_, delRefCallbackInfo->ref_);
82             delete delRefCallbackInfo;
83             delRefCallbackInfo = nullptr;
84             delete work;
85             work = nullptr;
86         }, uv_qos_user_initiated);
87     if (ret != 0) {
88         if (delRefCallbackInfo != nullptr) {
89             delete delRefCallbackInfo;
90             delRefCallbackInfo = nullptr;
91         }
92         if (work != nullptr) {
93             delete work;
94             work = nullptr;
95         }
96     }
97     ref_ = nullptr;
98 }
99 
SetEnv(const napi_env & env)100 void NAPIDataAbilityObserver::SetEnv(const napi_env &env)
101 {
102     env_ = env;
103     TAG_LOGI(AAFwkTag::FA, "end");
104 }
105 
SetCallbackRef(const napi_ref & ref)106 void NAPIDataAbilityObserver::SetCallbackRef(const napi_ref &ref)
107 {
108     ref_ = ref;
109     TAG_LOGI(AAFwkTag::FA, "end");
110 }
111 
OnChangeJSThreadWorker(uv_work_t * work,int status)112 static void OnChangeJSThreadWorker(uv_work_t *work, int status)
113 {
114     TAG_LOGI(AAFwkTag::FA, "called");
115     if (work == nullptr) {
116         TAG_LOGE(AAFwkTag::FA, "null work");
117         return;
118     }
119     DAHelperOnOffCB *onCB = (DAHelperOnOffCB *)work->data;
120     if (onCB == nullptr) {
121         TAG_LOGE(AAFwkTag::FA, "null onCB");
122         delete work;
123         work = nullptr;
124         return;
125     }
126 
127     if (onCB->observer != nullptr) {
128         onCB->observer->CallJsMethod();
129     }
130 
131     delete onCB;
132     onCB = nullptr;
133     delete work;
134     work = nullptr;
135     TAG_LOGI(AAFwkTag::FA, "end");
136 }
137 
CallJsMethod()138 void NAPIDataAbilityObserver::CallJsMethod()
139 {
140     {
141         std::lock_guard<std::mutex> lock(mutex_);
142         if (ref_ == nullptr || env_ == nullptr) {
143             TAG_LOGW(AAFwkTag::FA, "invalid observer");
144             return;
145         }
146         isCallingback_ = true;
147     }
148     napi_value result[ARGS_TWO] = {nullptr};
149     result[PARAM0] = GetCallbackErrorValue(env_, NO_ERROR);
150     napi_value callback = nullptr;
151     napi_value undefined = nullptr;
152     napi_get_undefined(env_, &undefined);
153     napi_value callResult = nullptr;
154     napi_get_reference_value(env_, ref_, &callback);
155     napi_call_function(env_, undefined, callback, ARGS_TWO, &result[PARAM0], &callResult);
156 
157     {
158         std::lock_guard<std::mutex> lock(mutex_);
159         if (needRelease_ && ref_ != nullptr) {
160             TAG_LOGI(AAFwkTag::FA, "to delete callback");
161             napi_delete_reference(env_, ref_);
162             ref_ = nullptr;
163             needRelease_ = false;
164         }
165         isCallingback_ = false;
166     }
167 }
168 
OnChange()169 void NAPIDataAbilityObserver::OnChange()
170 {
171     if (ref_ == nullptr) {
172         TAG_LOGE(AAFwkTag::FA, "null ret");
173         return;
174     }
175     uv_loop_s *loop = nullptr;
176     napi_get_uv_event_loop(env_, &loop);
177     if (loop == nullptr) {
178         TAG_LOGE(AAFwkTag::FA, "null loop");
179         return;
180     }
181 
182     uv_work_t *work = new uv_work_t;
183     DAHelperOnOffCB *onCB = new DAHelperOnOffCB;
184     onCB->observer = this;
185     work->data = static_cast<void *>(onCB);
186     int rev = uv_queue_work(
187         loop,
188         work,
189         [](uv_work_t *work) {},
190         OnChangeJSThreadWorker);
191     if (rev != 0) {
192         if (onCB != nullptr) {
193             delete onCB;
194             onCB = nullptr;
195         }
196         if (work != nullptr) {
197             delete work;
198             work = nullptr;
199         }
200     }
201     TAG_LOGI(AAFwkTag::FA, "end");
202 }
203 
204 }  // namespace AppExecFwk
205 }  // namespace OHOS