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