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)
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 sptr<JsDisplayListener> listener = this; // Avoid this be destroyed when using.
135 std::unique_ptr<NapiAsyncTask::CompleteCallback> complete = std::make_unique<NapiAsyncTask::CompleteCallback> (
136 [this, listener, id] (napi_env env, NapiAsyncTask &task, int32_t status) {
137 napi_value argv[] = {CreateJsValue(env_, static_cast<uint32_t>(id))};
138 CallJsMethod(EVENT_ADD, argv, ArraySize(argv));
139 }
140 );
141
142 napi_ref callback = nullptr;
143 std::unique_ptr<NapiAsyncTask::ExecuteCallback> execute = nullptr;
144 NapiAsyncTask::Schedule("JsDisplayListener::OnCreate", env_, std::make_unique<NapiAsyncTask>(
145 callback, std::move(execute), std::move(complete)));
146 }
147
OnDestroy(DisplayId id)148 void JsDisplayListener::OnDestroy(DisplayId id)
149 {
150 std::lock_guard<std::mutex> lock(mtx_);
151 WLOGI("JsDisplayListener::OnDestroy is called, displayId: %{public}d", static_cast<uint32_t>(id));
152 if (jsCallBack_.empty()) {
153 WLOGFE("JsDisplayListener::OnDestroy not register!");
154 return;
155 }
156 if (jsCallBack_.find(EVENT_REMOVE) == jsCallBack_.end()) {
157 WLOGE("JsDisplayListener::OnDestroy not this event, return");
158 return;
159 }
160 sptr<JsDisplayListener> listener = this; // Avoid this be destroyed when using.
161 std::unique_ptr<NapiAsyncTask::CompleteCallback> complete = std::make_unique<NapiAsyncTask::CompleteCallback> (
162 [this, listener, id] (napi_env env, NapiAsyncTask &task, int32_t status) {
163 napi_value argv[] = {CreateJsValue(env_, static_cast<uint32_t>(id))};
164 CallJsMethod(EVENT_REMOVE, argv, ArraySize(argv));
165 }
166 );
167
168 napi_ref callback = nullptr;
169 std::unique_ptr<NapiAsyncTask::ExecuteCallback> execute = nullptr;
170 NapiAsyncTask::Schedule("JsDisplayListener::OnDestroy", env_, std::make_unique<NapiAsyncTask>(
171 callback, std::move(execute), std::move(complete)));
172 }
173
OnChange(DisplayId id)174 void JsDisplayListener::OnChange(DisplayId id)
175 {
176 std::lock_guard<std::mutex> lock(mtx_);
177 WLOGD("JsDisplayListener::OnChange is called, displayId: %{public}d", static_cast<uint32_t>(id));
178 if (jsCallBack_.empty()) {
179 WLOGFE("JsDisplayListener::OnChange not register!");
180 return;
181 }
182 if (jsCallBack_.find(EVENT_CHANGE) == jsCallBack_.end()) {
183 WLOGE("JsDisplayListener::OnChange not this event, return");
184 return;
185 }
186 sptr<JsDisplayListener> listener = this; // Avoid this be destroyed when using.
187 auto napiTask = [this, listener, id, env = env_]() {
188 HITRACE_METER_FMT(HITRACE_TAG_WINDOW_MANAGER, "JsDisplayListener::OnChange");
189 napi_value argv[] = {CreateJsValue(env, static_cast<uint32_t>(id))};
190 CallJsMethod(EVENT_CHANGE, argv, ArraySize(argv));
191 };
192
193 if (env_ != nullptr) {
194 napi_status ret = napi_send_event(env_, napiTask, napi_eprio_immediate);
195 if (ret != napi_status::napi_ok) {
196 WLOGFE("OnChange: Failed to SendEvent.");
197 }
198 } else {
199 WLOGFE("OnChange: env is nullptr");
200 }
201 }
202
OnPrivateWindow(bool hasPrivate)203 void JsDisplayListener::OnPrivateWindow(bool hasPrivate)
204 {
205 std::lock_guard<std::mutex> lock(mtx_);
206 WLOGI("OnPrivateWindow is called, private status: %{public}u", static_cast<uint32_t>(hasPrivate));
207 if (jsCallBack_.empty()) {
208 WLOGFE("OnPrivateWindow not register!");
209 return;
210 }
211 if (jsCallBack_.find(EVENT_PRIVATE_MODE_CHANGE) == jsCallBack_.end()) {
212 WLOGE("OnPrivateWindow not this event, return");
213 return;
214 }
215 sptr<JsDisplayListener> listener = this; // Avoid this be destroyed when using.
216 std::unique_ptr<NapiAsyncTask::CompleteCallback> complete = std::make_unique<NapiAsyncTask::CompleteCallback> (
217 [this, listener, hasPrivate] (napi_env env, NapiAsyncTask &task, int32_t status) {
218 napi_value argv[] = {CreateJsValue(env_, hasPrivate)};
219 CallJsMethod(EVENT_PRIVATE_MODE_CHANGE, argv, ArraySize(argv));
220 }
221 );
222
223 napi_ref callback = nullptr;
224 std::unique_ptr<NapiAsyncTask::ExecuteCallback> execute = nullptr;
225 NapiAsyncTask::Schedule("JsDisplayListener::OnPrivateWindow", env_, std::make_unique<NapiAsyncTask>(
226 callback, std::move(execute), std::move(complete)));
227 }
228
OnFoldStatusChanged(FoldStatus foldStatus)229 void JsDisplayListener::OnFoldStatusChanged(FoldStatus foldStatus)
230 {
231 std::lock_guard<std::mutex> lock(mtx_);
232 WLOGI("OnFoldStatusChanged is called, foldStatus: %{public}u", static_cast<uint32_t>(foldStatus));
233 if (jsCallBack_.empty()) {
234 WLOGFE("OnFoldStatusChanged not register!");
235 return;
236 }
237 if (jsCallBack_.find(EVENT_FOLD_STATUS_CHANGED) == jsCallBack_.end()) {
238 WLOGE("OnFoldStatusChanged not this event, return");
239 return;
240 }
241 sptr<JsDisplayListener> listener = this; // Avoid this be destroyed when using.
242 auto napiTask = [this, listener, foldStatus, env = env_] () {
243 HITRACE_METER_FMT(HITRACE_TAG_WINDOW_MANAGER, "JsDisplayListener::OnFoldStatusChanged");
244 napi_value argv[] = {CreateJsValue(env, foldStatus)};
245 CallJsMethod(EVENT_FOLD_STATUS_CHANGED, argv, ArraySize(argv));
246 };
247
248 if (env_ != nullptr) {
249 napi_status ret = napi_send_event(env_, napiTask, napi_eprio_immediate);
250 if (ret != napi_status::napi_ok) {
251 WLOGFE("OnFoldStatusChanged: Failed to SendEvent.");
252 }
253 } else {
254 WLOGFE("OnFoldStatusChanged: env is nullptr");
255 }
256 }
257
OnFoldAngleChanged(std::vector<float> foldAngles)258 void JsDisplayListener::OnFoldAngleChanged(std::vector<float> foldAngles)
259 {
260 std::lock_guard<std::mutex> lock(mtx_);
261 if (jsCallBack_.empty()) {
262 WLOGFE("OnFoldAngleChanged not register!");
263 return;
264 }
265 if (jsCallBack_.find(EVENT_FOLD_ANGLE_CHANGED) == jsCallBack_.end()) {
266 WLOGE("OnFoldAngleChanged not this event, return");
267 return;
268 }
269 sptr<JsDisplayListener> listener = this; // Avoid this be destroyed when using.
270 std::unique_ptr<NapiAsyncTask::CompleteCallback> complete = std::make_unique<NapiAsyncTask::CompleteCallback> (
271 [this, listener, foldAngles] (napi_env env, NapiAsyncTask &task, int32_t status) {
272 napi_value argv[] = {CreateNativeArray(env_, foldAngles)};
273 CallJsMethod(EVENT_FOLD_ANGLE_CHANGED, argv, ArraySize(argv));
274 }
275 );
276
277 napi_ref callback = nullptr;
278 std::unique_ptr<NapiAsyncTask::ExecuteCallback> execute = nullptr;
279 NapiAsyncTask::Schedule("JsDisplayListener::OnFoldAngleChanged", env_, std::make_unique<NapiAsyncTask>(
280 callback, std::move(execute), std::move(complete)));
281 }
282
OnCaptureStatusChanged(bool isCapture)283 void JsDisplayListener::OnCaptureStatusChanged(bool isCapture)
284 {
285 std::lock_guard<std::mutex> lock(mtx_);
286 if (jsCallBack_.empty()) {
287 WLOGFE("OnCaptureStatusChanged not register!");
288 return;
289 }
290 if (jsCallBack_.find(EVENT_CAPTURE_STATUS_CHANGED) == jsCallBack_.end()) {
291 WLOGE("OnCaptureStatusChanged not this event, return");
292 return;
293 }
294 sptr<JsDisplayListener> listener = this; // Avoid this be destroyed when using.
295 std::unique_ptr<NapiAsyncTask::CompleteCallback> complete = std::make_unique<NapiAsyncTask::CompleteCallback> (
296 [this, listener, isCapture] (napi_env env, NapiAsyncTask &task, int32_t status) {
297 napi_value argv[] = {CreateJsValue(env_, isCapture)};
298 CallJsMethod(EVENT_CAPTURE_STATUS_CHANGED, argv, ArraySize(argv));
299 }
300 );
301
302 napi_ref callback = nullptr;
303 std::unique_ptr<NapiAsyncTask::ExecuteCallback> execute = nullptr;
304 NapiAsyncTask::Schedule("JsDisplayListener::OnCaptureStatusChanged", env_, std::make_unique<NapiAsyncTask>(
305 callback, std::move(execute), std::move(complete)));
306 }
307
OnDisplayModeChanged(FoldDisplayMode displayMode)308 void JsDisplayListener::OnDisplayModeChanged(FoldDisplayMode displayMode)
309 {
310 std::lock_guard<std::mutex> lock(mtx_);
311 WLOGI("OnDisplayModeChanged is called, displayMode: %{public}u", static_cast<uint32_t>(displayMode));
312 if (jsCallBack_.empty()) {
313 WLOGFE("OnDisplayModeChanged not register!");
314 return;
315 }
316 if (jsCallBack_.find(EVENT_DISPLAY_MODE_CHANGED) == jsCallBack_.end()) {
317 WLOGE("OnDisplayModeChanged not this event, return");
318 return;
319 }
320 sptr<JsDisplayListener> listener = this; // Avoid this be destroyed when using.
321 auto napiTask = [this, listener, displayMode, env = env_] () {
322 HITRACE_METER_FMT(HITRACE_TAG_WINDOW_MANAGER, "JsDisplayListener::OnDisplayModeChanged");
323 napi_value argv[] = {CreateJsValue(env, displayMode)};
324 CallJsMethod(EVENT_DISPLAY_MODE_CHANGED, argv, ArraySize(argv));
325 };
326
327 if (env_ != nullptr) {
328 napi_status ret = napi_send_event(env_, napiTask, napi_eprio_immediate);
329 if (ret != napi_status::napi_ok) {
330 WLOGFE("OnDisplayModeChanged: Failed to SendEvent.");
331 }
332 } else {
333 WLOGFE("OnDisplayModeChanged: env is nullptr");
334 }
335 }
336
OnAvailableAreaChanged(DMRect area)337 void JsDisplayListener::OnAvailableAreaChanged(DMRect area)
338 {
339 std::lock_guard<std::mutex> lock(mtx_);
340 WLOGI("OnAvailableAreaChanged is called");
341 if (jsCallBack_.empty()) {
342 WLOGFE("OnAvailableAreaChanged not register!");
343 return;
344 }
345 if (jsCallBack_.find(EVENT_AVAILABLE_AREA_CHANGED) == jsCallBack_.end()) {
346 WLOGE("OnAvailableAreaChanged not this event, return");
347 return;
348 }
349 sptr<JsDisplayListener> listener = this; // Avoid this be destroyed when using.
350 std::unique_ptr<NapiAsyncTask::CompleteCallback> complete = std::make_unique<NapiAsyncTask::CompleteCallback> (
351 [this, listener, area] (napi_env env, NapiAsyncTask &task, int32_t status) {
352 napi_value argv[] = {CreateJsRectObject(env_, area)};
353 CallJsMethod(EVENT_AVAILABLE_AREA_CHANGED, argv, ArraySize(argv));
354 }
355 );
356
357 napi_ref callback = nullptr;
358 std::unique_ptr<NapiAsyncTask::ExecuteCallback> execute = nullptr;
359 NapiAsyncTask::Schedule("JsDisplayListener::OnAvailableAreaChanged", env_, std::make_unique<NapiAsyncTask>(
360 callback, std::move(execute), std::move(complete)));
361 }
362 } // namespace Rosen
363 } // namespace OHOS
364