1 /*
2 * Copyright (c) 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
16 #include "request_suspend_delay.h"
17
18 #include <uv.h>
19
20 #include "singleton.h"
21 #ifdef SUPPORT_JSSTACK
22 #include "xpower_event_js.h"
23 #endif
24
25 #include "background_task_manager.h"
26 #include "transient_task_log.h"
27
28 namespace OHOS {
29 namespace BackgroundTaskMgr {
30 std::map<int32_t, std::shared_ptr<ExpiredCallback>> callbackInstances_;
31 std::mutex callbackLock_;
32 static const uint32_t REQUEST_SUSPEND_DELAY_PARAMS = 2;
33
34 struct CallbackReceiveDataWorker {
35 napi_env env = nullptr;
36 napi_ref ref = nullptr;
37 std::shared_ptr<ExpiredCallback> callback = nullptr;
38 };
39
CallbackInstance()40 CallbackInstance::CallbackInstance() {}
41
~CallbackInstance()42 CallbackInstance::~CallbackInstance()
43 {
44 if (expiredCallbackInfo_.ref == nullptr) {
45 return;
46 }
47 DeleteNapiRef();
48 }
49
UvQueueWorkDeleteRef(uv_work_t * work,int32_t status)50 void UvQueueWorkDeleteRef(uv_work_t *work, int32_t status)
51 {
52 if (work == nullptr) {
53 return;
54 }
55 CallbackReceiveDataWorker *dataWorkerData = static_cast<CallbackReceiveDataWorker *>(work->data);
56 if (dataWorkerData == nullptr) {
57 delete work;
58 work = nullptr;
59 return;
60 }
61 napi_delete_reference(dataWorkerData->env, dataWorkerData->ref);
62 delete dataWorkerData;
63 dataWorkerData = nullptr;
64 delete work;
65 }
66
DeleteNapiRef()67 void CallbackInstance::DeleteNapiRef()
68 {
69 uv_loop_s *loop = nullptr;
70 napi_get_uv_event_loop(expiredCallbackInfo_.env, &loop);
71 if (loop == nullptr) {
72 return;
73 }
74 CallbackReceiveDataWorker *dataWorker = new (std::nothrow) CallbackReceiveDataWorker();
75 if (dataWorker == nullptr) {
76 return;
77 }
78
79 dataWorker->env = expiredCallbackInfo_.env;
80 dataWorker->ref = expiredCallbackInfo_.ref;
81 uv_work_t *work = new (std::nothrow) uv_work_t;
82 if (work == nullptr) {
83 BGTASK_LOGE("DeleteNapiRef new work failed");
84 delete dataWorker;
85 dataWorker = nullptr;
86 return;
87 }
88 work->data = static_cast<void *>(dataWorker);
89
90 int32_t ret = uv_queue_work(loop, work, [](uv_work_t *work) {}, UvQueueWorkDeleteRef);
91 if (ret != 0) {
92 delete dataWorker;
93 dataWorker = nullptr;
94 delete work;
95 work = nullptr;
96 }
97 }
98
UvQueueWorkOnExpired(uv_work_t * work,int32_t status)99 void UvQueueWorkOnExpired(uv_work_t *work, int32_t status)
100 {
101 BGTASK_LOGD("OnExpired uv_work_t start");
102
103 if (work == nullptr) {
104 BGTASK_LOGE("UvQueueWorkOnExpired work is null");
105 return;
106 }
107
108 CallbackReceiveDataWorker *dataWorkerData = static_cast<CallbackReceiveDataWorker *>(work->data);
109 if (dataWorkerData == nullptr) {
110 BGTASK_LOGE("UvQueueWorkOnExpired dataWorkerData is null");
111 delete work;
112 work = nullptr;
113 return;
114 }
115
116 Common::SetCallback(dataWorkerData->env, dataWorkerData->ref, Common::NapiGetNull(dataWorkerData->env));
117
118 std::lock_guard<std::mutex> lock(callbackLock_);
119 auto findCallback = std::find_if(callbackInstances_.begin(), callbackInstances_.end(),
120 [&](const auto& callbackInstance) { return callbackInstance.second == dataWorkerData->callback; }
121 );
122 if (findCallback != callbackInstances_.end()) {
123 callbackInstances_.erase(findCallback);
124 }
125
126 delete dataWorkerData;
127 dataWorkerData = nullptr;
128 delete work;
129 }
130
OnExpired()131 void CallbackInstance::OnExpired()
132 {
133 std::lock_guard<std::mutex> lock(callbackLock_);
134 auto findCallback = std::find_if(callbackInstances_.begin(), callbackInstances_.end(),
135 [&](const auto& callbackInstance) { return callbackInstance.second.get() == this; }
136 );
137 if (findCallback == callbackInstances_.end()) {
138 BGTASK_LOGI("expired callback is not found");
139 return;
140 }
141
142 if (expiredCallbackInfo_.ref == nullptr) {
143 BGTASK_LOGE("expired callback unset");
144 callbackInstances_.erase(findCallback);
145 return;
146 }
147
148 uv_loop_s *loop = nullptr;
149 napi_get_uv_event_loop(expiredCallbackInfo_.env, &loop);
150 if (loop == nullptr) {
151 BGTASK_LOGE("loop instance is nullptr");
152 callbackInstances_.erase(findCallback);
153 return;
154 }
155
156 CallbackReceiveDataWorker *dataWorker = new (std::nothrow) CallbackReceiveDataWorker();
157 if (dataWorker == nullptr) {
158 BGTASK_LOGE("new dataWorker failed");
159 callbackInstances_.erase(findCallback);
160 return;
161 }
162
163 dataWorker->env = expiredCallbackInfo_.env;
164 dataWorker->ref = expiredCallbackInfo_.ref;
165 dataWorker->callback = shared_from_this();
166
167 uv_work_t *work = new (std::nothrow) uv_work_t;
168 if (work == nullptr) {
169 BGTASK_LOGW("OnExpired new work failed");
170 delete dataWorker;
171 dataWorker = nullptr;
172 callbackInstances_.erase(findCallback);
173 return;
174 }
175
176 work->data = static_cast<void *>(dataWorker);
177
178 int32_t ret = uv_queue_work(loop, work, [](uv_work_t *work) {}, UvQueueWorkOnExpired);
179 if (ret != 0) {
180 delete dataWorker;
181 dataWorker = nullptr;
182 delete work;
183 work = nullptr;
184 callbackInstances_.erase(findCallback);
185 }
186 }
187
SetCallbackInfo(const napi_env & env,const napi_ref & ref)188 void CallbackInstance::SetCallbackInfo(const napi_env &env, const napi_ref &ref)
189 {
190 expiredCallbackInfo_.env = env;
191 expiredCallbackInfo_.ref = ref;
192 }
193
GetExpiredCallback(const napi_env & env,const napi_value & value,std::shared_ptr<CallbackInstance> & callback)194 napi_value GetExpiredCallback(
195 const napi_env &env, const napi_value &value, std::shared_ptr<CallbackInstance> &callback)
196 {
197 napi_ref result = nullptr;
198 callback = std::make_shared<CallbackInstance>();
199 callback->Init();
200
201 napi_create_reference(env, value, 1, &result);
202 callback->SetCallbackInfo(env, result);
203
204 return Common::NapiGetNull(env);
205 }
206
ParseParameters(const napi_env & env,const napi_callback_info & info,std::u16string & reason,std::shared_ptr<CallbackInstance> & callback,bool isThrow)207 napi_value ParseParameters(const napi_env &env, const napi_callback_info &info,
208 std::u16string &reason, std::shared_ptr<CallbackInstance> &callback, bool isThrow)
209 {
210 size_t argc = REQUEST_SUSPEND_DELAY_PARAMS;
211 napi_value argv[REQUEST_SUSPEND_DELAY_PARAMS] = {nullptr};
212 NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, NULL, NULL));
213 if (argc != REQUEST_SUSPEND_DELAY_PARAMS) {
214 Common::HandleParamErr(env, ERR_PARAM_NUMBER_ERR, isThrow);
215 return nullptr;
216 }
217
218 // argv[0] : reason
219 if (Common::GetU16StringValue(env, argv[0], reason) == nullptr) {
220 BGTASK_LOGE("ParseParameters failed, reason is nullptr.");
221 Common::HandleParamErr(env, ERR_REASON_NULL_OR_TYPE_ERR, isThrow);
222 return nullptr;
223 }
224
225 // arg[1] : callback
226 napi_valuetype valuetype = napi_undefined;
227 NAPI_CALL(env, napi_typeof(env, argv[1], &valuetype));
228 if (valuetype != napi_function) {
229 Common::HandleParamErr(env, ERR_CALLBACK_NULL_OR_TYPE_ERR, isThrow);
230 return nullptr;
231 }
232
233 if (GetExpiredCallback(env, argv[1], callback) == nullptr) {
234 BGTASK_LOGE("ExpiredCallback parse failed");
235 Common::HandleParamErr(env, ERR_CALLBACK_NULL_OR_TYPE_ERR, isThrow);
236 return nullptr;
237 }
238 return Common::NapiGetNull(env);
239 }
240
RequestSuspendDelay(napi_env env,napi_callback_info info,bool isThrow)241 napi_value RequestSuspendDelay(napi_env env, napi_callback_info info, bool isThrow)
242 {
243 #ifdef SUPPORT_JSSTACK
244 HiviewDFX::ReportXPowerJsStackSysEvent(env, "TRANSIENT_TASK_APPLY");
245 #endif
246 std::shared_ptr<CallbackInstance> callback = nullptr;
247 std::u16string reason;
248 if (ParseParameters(env, info, reason, callback, isThrow) == nullptr) {
249 return Common::NapiGetNull(env);
250 }
251
252 std::shared_ptr<DelaySuspendInfo> delaySuspendInfo {nullptr};
253 ErrCode errCode = DelayedSingleton<BackgroundTaskManager>::GetInstance()->
254 RequestSuspendDelay(reason, *callback, delaySuspendInfo);
255 Common::HandleErrCode(env, errCode, isThrow);
256 if (!delaySuspendInfo) {
257 return Common::NapiGetNull(env);
258 }
259 {
260 std::lock_guard<std::mutex> lock(callbackLock_);
261 callbackInstances_[delaySuspendInfo->GetRequestId()] = callback;
262 }
263
264 napi_value result = nullptr;
265 napi_create_object(env, &result);
266 if (!Common::SetDelaySuspendInfo(env, delaySuspendInfo, result)) {
267 BGTASK_LOGW("Set DelaySuspendInfo object failed");
268 }
269 return result;
270 }
271
RequestSuspendDelay(napi_env env,napi_callback_info info)272 napi_value RequestSuspendDelay(napi_env env, napi_callback_info info)
273 {
274 return RequestSuspendDelay(env, info, false);
275 }
276
RequestSuspendDelayThrow(napi_env env,napi_callback_info info)277 napi_value RequestSuspendDelayThrow(napi_env env, napi_callback_info info)
278 {
279 return RequestSuspendDelay(env, info, true);
280 }
281 } // namespace BackgroundTaskMgr
282 } // namespace OHOS