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 "js_display_listener.h"
17
18 #include <hitrace_meter.h>
19
20 #include "dm_common.h"
21 #include "js_runtime_utils.h"
22 #include "window_manager_hilog.h"
23 #include "js_display.h"
24
25 namespace OHOS {
26 namespace Rosen {
27 using namespace AbilityRuntime;
28 namespace {
29 constexpr HiviewDFX::HiLogLabel LABEL = {LOG_CORE, HILOG_DOMAIN_DISPLAY, "JsDisplayListener"};
30 }
31
JsDisplayListener(napi_env env)32 JsDisplayListener::JsDisplayListener(napi_env env) : env_(env), weakRef_(wptr<JsDisplayListener> (this))
33 {
34 WLOGFI("Constructor execution");
35 napi_add_env_cleanup_hook(env_, CleanEnv, this);
36 }
37
~JsDisplayListener()38 JsDisplayListener::~JsDisplayListener()
39 {
40 WLOGFI("Destructor execution");
41 napi_remove_env_cleanup_hook(env_, CleanEnv, this);
42 env_ = nullptr;
43 }
44
CleanEnv(void * obj)45 void JsDisplayListener::CleanEnv(void* obj)
46 {
47 JsDisplayListener* thisObj = reinterpret_cast<JsDisplayListener*>(obj);
48 if (!thisObj) {
49 WLOGE("obj is nullptr");
50 return;
51 }
52 WLOGFI("env_ is invalid, set to nullptr");
53 thisObj->env_ = nullptr;
54 }
55
AddCallback(const std::string & type,napi_value jsListenerObject)56 void JsDisplayListener::AddCallback(const std::string& type, napi_value jsListenerObject)
57 {
58 WLOGD("JsDisplayListener::AddCallback is called");
59 std::unique_ptr<NativeReference> callbackRef;
60 if (env_ == nullptr) {
61 WLOGFE("env_ nullptr");
62 return;
63 }
64 napi_ref result = nullptr;
65 napi_create_reference(env_, jsListenerObject, 1, &result);
66 callbackRef.reset(reinterpret_cast<NativeReference*>(result));
67 std::lock_guard<std::mutex> lock(mtx_);
68 jsCallBack_[type].emplace_back(std::move(callbackRef));
69 WLOGD("JsDisplayListener::AddCallback success jsCallBack_ size: %{public}u!",
70 static_cast<uint32_t>(jsCallBack_[type].size()));
71 }
72
RemoveAllCallback()73 void JsDisplayListener::RemoveAllCallback()
74 {
75 std::lock_guard<std::mutex> lock(mtx_);
76 jsCallBack_.clear();
77 }
78
RemoveCallback(napi_env env,const std::string & type,napi_value jsListenerObject)79 void JsDisplayListener::RemoveCallback(napi_env env, const std::string& type, napi_value jsListenerObject)
80 {
81 std::lock_guard<std::mutex> lock(mtx_);
82 auto it = jsCallBack_.find(type);
83 if (it == jsCallBack_.end()) {
84 WLOGE("JsDisplayListener::RemoveCallback no callback to remove");
85 return;
86 }
87 auto& listeners = it->second;
88 for (auto iter = listeners.begin(); iter != listeners.end();) {
89 bool isEquals = false;
90 napi_strict_equals(env, jsListenerObject, (*iter)->GetNapiValue(), &isEquals);
91 if (isEquals) {
92 listeners.erase(iter);
93 } else {
94 iter++;
95 }
96 }
97 WLOGI("JsDisplayListener::RemoveCallback success jsCallBack_ size: %{public}u!",
98 static_cast<uint32_t>(listeners.size()));
99 }
100
CallJsMethod(const std::string & methodName,napi_value const * argv,size_t argc)101 void JsDisplayListener::CallJsMethod(const std::string& methodName, napi_value const * argv, size_t argc)
102 {
103 if (methodName.empty()) {
104 WLOGFE("empty method name str, call method failed");
105 return;
106 }
107 WLOGD("CallJsMethod methodName = %{public}s", methodName.c_str());
108 if (env_ == nullptr) {
109 WLOGFE("env_ nullptr");
110 return;
111 }
112 for (auto& callback : jsCallBack_[methodName]) {
113 napi_value method = callback->GetNapiValue();
114 if (method == nullptr) {
115 WLOGFE("Failed to get method callback from object");
116 continue;
117 }
118 napi_call_function(env_, NapiGetUndefined(env_), method, argc, argv, nullptr);
119 }
120 }
121
OnCreate(DisplayId id)122 void JsDisplayListener::OnCreate(DisplayId id)
123 {
124 std::lock_guard<std::mutex> lock(mtx_);
125 WLOGI("JsDisplayListener::OnCreate is called, displayId: %{public}d", static_cast<uint32_t>(id));
126 if (jsCallBack_.empty()) {
127 WLOGFE("JsDisplayListener::OnCreate not register!");
128 return;
129 }
130 if (jsCallBack_.find(EVENT_ADD) == jsCallBack_.end()) {
131 WLOGE("JsDisplayListener::OnCreate not this event, return");
132 return;
133 }
134 auto napiTask = [self = weakRef_, id, env = env_]() {
135 HITRACE_METER_FMT(HITRACE_TAG_WINDOW_MANAGER, "JsDisplayListener::OnCreate");
136 auto thisListener = self.promote();
137 if (thisListener == nullptr || env == nullptr) {
138 WLOGFE("[NAPI]this listener or env is nullptr");
139 return;
140 }
141 napi_value argv[] = {CreateJsValue(env, static_cast<uint32_t>(id))};
142 thisListener->CallJsMethod(EVENT_ADD, argv, ArraySize(argv));
143 };
144
145 if (env_ != nullptr) {
146 napi_status ret = napi_send_event(env_, napiTask, napi_eprio_immediate);
147 if (ret != napi_status::napi_ok) {
148 WLOGFE("OnCreate: Failed to SendEvent.");
149 }
150 } else {
151 WLOGFE("OnCreate: env is nullptr");
152 }
153 }
154
OnDestroy(DisplayId id)155 void JsDisplayListener::OnDestroy(DisplayId id)
156 {
157 std::lock_guard<std::mutex> lock(mtx_);
158 WLOGI("JsDisplayListener::OnDestroy is called, displayId: %{public}d", static_cast<uint32_t>(id));
159 if (jsCallBack_.empty()) {
160 WLOGFE("JsDisplayListener::OnDestroy not register!");
161 return;
162 }
163 if (jsCallBack_.find(EVENT_REMOVE) == jsCallBack_.end()) {
164 WLOGE("JsDisplayListener::OnDestroy not this event, return");
165 return;
166 }
167 auto napiTask = [self = weakRef_, id, env = env_]() {
168 HITRACE_METER_FMT(HITRACE_TAG_WINDOW_MANAGER, "JsDisplayListener::OnDestroy");
169 auto thisListener = self.promote();
170 if (thisListener == nullptr || env == nullptr) {
171 WLOGFE("[NAPI]this listener or env is nullptr");
172 return;
173 }
174 napi_value argv[] = {CreateJsValue(env, static_cast<uint32_t>(id))};
175 thisListener->CallJsMethod(EVENT_REMOVE, argv, ArraySize(argv));
176 };
177
178 if (env_ != nullptr) {
179 napi_status ret = napi_send_event(env_, napiTask, napi_eprio_immediate);
180 if (ret != napi_status::napi_ok) {
181 WLOGFE("OnDestroy: Failed to SendEvent.");
182 }
183 } else {
184 WLOGFE("OnDestroy: env is nullptr");
185 }
186 }
187
OnChange(DisplayId id)188 void JsDisplayListener::OnChange(DisplayId id)
189 {
190 std::lock_guard<std::mutex> lock(mtx_);
191 WLOGD("JsDisplayListener::OnChange is called, displayId: %{public}d", static_cast<uint32_t>(id));
192 if (jsCallBack_.empty()) {
193 WLOGFE("JsDisplayListener::OnChange not register!");
194 return;
195 }
196 if (jsCallBack_.find(EVENT_CHANGE) == jsCallBack_.end()) {
197 WLOGE("JsDisplayListener::OnChange not this event, return");
198 return;
199 }
200 auto napiTask = [self = weakRef_, id, env = env_]() {
201 HITRACE_METER_FMT(HITRACE_TAG_WINDOW_MANAGER, "JsDisplayListener::OnChange");
202 auto thisListener = self.promote();
203 if (thisListener == nullptr || env == nullptr) {
204 WLOGFE("[NAPI]this listener or env is nullptr");
205 return;
206 }
207 napi_value argv[] = {CreateJsValue(env, static_cast<uint32_t>(id))};
208 thisListener->CallJsMethod(EVENT_CHANGE, argv, ArraySize(argv));
209 };
210
211 if (env_ != nullptr) {
212 napi_status ret = napi_send_event(env_, napiTask, napi_eprio_immediate);
213 if (ret != napi_status::napi_ok) {
214 WLOGFE("OnChange: Failed to SendEvent.");
215 }
216 } else {
217 WLOGFE("OnChange: env is nullptr");
218 }
219 }
220
OnPrivateWindow(bool hasPrivate)221 void JsDisplayListener::OnPrivateWindow(bool hasPrivate)
222 {
223 std::lock_guard<std::mutex> lock(mtx_);
224 WLOGI("OnPrivateWindow is called, private status: %{public}u", static_cast<uint32_t>(hasPrivate));
225 if (jsCallBack_.empty()) {
226 WLOGFE("OnPrivateWindow not register!");
227 return;
228 }
229 if (jsCallBack_.find(EVENT_PRIVATE_MODE_CHANGE) == jsCallBack_.end()) {
230 WLOGE("OnPrivateWindow not this event, return");
231 return;
232 }
233 auto napiTask = [self = weakRef_, hasPrivate, env = env_]() {
234 HITRACE_METER_FMT(HITRACE_TAG_WINDOW_MANAGER, "JsDisplayListener::OnPrivateWindow");
235 auto thisListener = self.promote();
236 if (thisListener == nullptr || env == nullptr) {
237 WLOGFE("[NAPI]this listener or env is nullptr");
238 return;
239 }
240 napi_value argv[] = {CreateJsValue(env, hasPrivate)};
241 thisListener->CallJsMethod(EVENT_PRIVATE_MODE_CHANGE, argv, ArraySize(argv));
242 };
243
244 if (env_ != nullptr) {
245 napi_status ret = napi_send_event(env_, napiTask, napi_eprio_immediate);
246 if (ret != napi_status::napi_ok) {
247 WLOGFE("OnPrivateWindow: Failed to SendEvent.");
248 }
249 } else {
250 WLOGFE("OnPrivateWindow: env is nullptr");
251 }
252 }
253
OnFoldStatusChanged(FoldStatus foldStatus)254 void JsDisplayListener::OnFoldStatusChanged(FoldStatus foldStatus)
255 {
256 std::lock_guard<std::mutex> lock(mtx_);
257 WLOGI("OnFoldStatusChanged is called, foldStatus: %{public}u", static_cast<uint32_t>(foldStatus));
258 if (jsCallBack_.empty()) {
259 WLOGFE("OnFoldStatusChanged not register!");
260 return;
261 }
262 if (jsCallBack_.find(EVENT_FOLD_STATUS_CHANGED) == jsCallBack_.end()) {
263 WLOGE("OnFoldStatusChanged not this event, return");
264 return;
265 }
266 auto napiTask = [self = weakRef_, foldStatus, env = env_] () {
267 HITRACE_METER_FMT(HITRACE_TAG_WINDOW_MANAGER, "JsDisplayListener::OnFoldStatusChanged");
268 auto thisListener = self.promote();
269 if (thisListener == nullptr || env == nullptr) {
270 WLOGFE("[NAPI]this listener or env is nullptr");
271 return;
272 }
273 napi_value argv[] = {CreateJsValue(env, foldStatus)};
274 thisListener->CallJsMethod(EVENT_FOLD_STATUS_CHANGED, argv, ArraySize(argv));
275 };
276
277 if (env_ != nullptr) {
278 napi_status ret = napi_send_event(env_, napiTask, napi_eprio_immediate);
279 if (ret != napi_status::napi_ok) {
280 WLOGFE("OnFoldStatusChanged: Failed to SendEvent.");
281 }
282 } else {
283 WLOGFE("OnFoldStatusChanged: env is nullptr");
284 }
285 }
286
OnFoldAngleChanged(std::vector<float> foldAngles)287 void JsDisplayListener::OnFoldAngleChanged(std::vector<float> foldAngles)
288 {
289 std::lock_guard<std::mutex> lock(mtx_);
290 if (jsCallBack_.empty()) {
291 WLOGFE("OnFoldAngleChanged not register!");
292 return;
293 }
294 if (jsCallBack_.find(EVENT_FOLD_ANGLE_CHANGED) == jsCallBack_.end()) {
295 WLOGE("OnFoldAngleChanged not this event, return");
296 return;
297 }
298 auto napiTask = [self = weakRef_, foldAngles, env = env_]() {
299 HITRACE_METER_FMT(HITRACE_TAG_WINDOW_MANAGER, "JsDisplayListener::OnFoldAngleChanged");
300 auto thisListener = self.promote();
301 if (thisListener == nullptr || env == nullptr) {
302 WLOGFE("[NAPI]this listener or env is nullptr");
303 return;
304 }
305 napi_value argv[] = {CreateNativeArray(env, foldAngles)};
306 thisListener->CallJsMethod(EVENT_FOLD_ANGLE_CHANGED, argv, ArraySize(argv));
307 };
308
309 if (env_ != nullptr) {
310 napi_status ret = napi_send_event(env_, napiTask, napi_eprio_immediate);
311 if (ret != napi_status::napi_ok) {
312 WLOGFE("OnFoldAngleChanged: Failed to SendEvent.");
313 }
314 } else {
315 WLOGFE("OnFoldAngleChanged: env is nullptr");
316 }
317 }
318
OnCaptureStatusChanged(bool isCapture)319 void JsDisplayListener::OnCaptureStatusChanged(bool isCapture)
320 {
321 std::lock_guard<std::mutex> lock(mtx_);
322 if (jsCallBack_.empty()) {
323 WLOGFE("OnCaptureStatusChanged not register!");
324 return;
325 }
326 if (jsCallBack_.find(EVENT_CAPTURE_STATUS_CHANGED) == jsCallBack_.end()) {
327 WLOGE("OnCaptureStatusChanged not this event, return");
328 return;
329 }
330 auto napiTask = [self = weakRef_, isCapture, env = env_]() {
331 HITRACE_METER_FMT(HITRACE_TAG_WINDOW_MANAGER, "JsDisplayListener::OnCaptureStatusChanged");
332 auto thisListener = self.promote();
333 if (thisListener == nullptr || env == nullptr) {
334 WLOGFE("[NAPI]this listener or env is nullptr");
335 return;
336 }
337 napi_value argv[] = {CreateJsValue(env, isCapture)};
338 thisListener->CallJsMethod(EVENT_CAPTURE_STATUS_CHANGED, argv, ArraySize(argv));
339 };
340
341 if (env_ != nullptr) {
342 napi_status ret = napi_send_event(env_, napiTask, napi_eprio_immediate);
343 if (ret != napi_status::napi_ok) {
344 WLOGFE("OnCaptureStatusChanged: Failed to SendEvent.");
345 }
346 } else {
347 WLOGFE("OnCaptureStatusChanged: env is nullptr");
348 }
349 }
350
OnDisplayModeChanged(FoldDisplayMode displayMode)351 void JsDisplayListener::OnDisplayModeChanged(FoldDisplayMode displayMode)
352 {
353 std::lock_guard<std::mutex> lock(mtx_);
354 WLOGI("OnDisplayModeChanged is called, displayMode: %{public}u", static_cast<uint32_t>(displayMode));
355 if (jsCallBack_.empty()) {
356 WLOGFE("OnDisplayModeChanged not register!");
357 return;
358 }
359 if (jsCallBack_.find(EVENT_DISPLAY_MODE_CHANGED) == jsCallBack_.end()) {
360 WLOGE("OnDisplayModeChanged not this event, return");
361 return;
362 }
363 auto napiTask = [self = weakRef_, displayMode, env = env_] () {
364 HITRACE_METER_FMT(HITRACE_TAG_WINDOW_MANAGER, "JsDisplayListener::OnDisplayModeChanged");
365 auto thisListener = self.promote();
366 if (thisListener == nullptr || env == nullptr) {
367 WLOGFE("[NAPI]this listener or env is nullptr");
368 return;
369 }
370 napi_value argv[] = {CreateJsValue(env, displayMode)};
371 thisListener->CallJsMethod(EVENT_DISPLAY_MODE_CHANGED, argv, ArraySize(argv));
372 };
373
374 if (env_ != nullptr) {
375 napi_status ret = napi_send_event(env_, napiTask, napi_eprio_immediate);
376 if (ret != napi_status::napi_ok) {
377 WLOGFE("OnDisplayModeChanged: Failed to SendEvent.");
378 }
379 } else {
380 WLOGFE("OnDisplayModeChanged: env is nullptr");
381 }
382 }
383
OnAvailableAreaChanged(DMRect area)384 void JsDisplayListener::OnAvailableAreaChanged(DMRect area)
385 {
386 std::lock_guard<std::mutex> lock(mtx_);
387 WLOGI("OnAvailableAreaChanged is called");
388 if (jsCallBack_.empty()) {
389 WLOGFE("OnAvailableAreaChanged not register!");
390 return;
391 }
392 if (jsCallBack_.find(EVENT_AVAILABLE_AREA_CHANGED) == jsCallBack_.end()) {
393 WLOGE("OnAvailableAreaChanged not this event, return");
394 return;
395 }
396 auto napiTask = [self = weakRef_, area, env = env_]() {
397 HITRACE_METER_FMT(HITRACE_TAG_WINDOW_MANAGER, "JsDisplayListener::OnAvailableAreaChanged");
398 auto thisListener = self.promote();
399 if (thisListener == nullptr || env == nullptr) {
400 WLOGFE("[NAPI]this listener or env is nullptr");
401 return;
402 }
403 napi_value argv[] = {CreateJsRectObject(env, area)};
404 thisListener->CallJsMethod(EVENT_AVAILABLE_AREA_CHANGED, argv, ArraySize(argv));
405 };
406
407 if (env_ != nullptr) {
408 napi_status ret = napi_send_event(env_, napiTask, napi_eprio_immediate);
409 if (ret != napi_status::napi_ok) {
410 WLOGFE("OnAvailableAreaChanged: Failed to SendEvent.");
411 }
412 } else {
413 WLOGFE("OnAvailableAreaChanged: env is nullptr");
414 }
415 }
416 } // namespace Rosen
417 } // namespace OHOS
418