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 #include "js_screen_listener.h"
16 #include "js_runtime_utils.h"
17 #include "window_manager_hilog.h"
18 #include "js_screen.h"
19 
20 namespace OHOS {
21 namespace Rosen {
22 using namespace AbilityRuntime;
23 namespace {
24 constexpr HiviewDFX::HiLogLabel LABEL = {LOG_CORE, HILOG_DOMAIN_DISPLAY, "JsScreenListener"};
25 }
26 inline uint32_t SCREEN_DISCONNECT_TYPE = 0;
27 inline uint32_t SCREEN_CONNECT_TYPE = 1;
28 
JsScreenListener(napi_env env)29 JsScreenListener::JsScreenListener(napi_env env) : env_(env)
30 {
31     TLOGI(WmsLogTag::DMS, "Constructor execution");
32     napi_add_env_cleanup_hook(env_, CleanEnv, this);
33 }
34 
~JsScreenListener()35 JsScreenListener::~JsScreenListener()
36 {
37     TLOGI(WmsLogTag::DMS, "Destructor execution");
38     napi_remove_env_cleanup_hook(env_, CleanEnv, this);
39     env_ = nullptr;
40 }
41 
CleanEnv(void * obj)42 void JsScreenListener::CleanEnv(void* obj)
43 {
44     JsScreenListener* thisObj = reinterpret_cast<JsScreenListener*>(obj);
45     if (!thisObj) {
46         TLOGE(WmsLogTag::DMS, "obj is nullptr");
47         return;
48     }
49     TLOGI(WmsLogTag::DMS, "env_ is invalid, set to nullptr");
50     thisObj->env_ = nullptr;
51 }
52 
AddCallback(const std::string & type,napi_value jsListenerObject)53 void JsScreenListener::AddCallback(const std::string& type, napi_value jsListenerObject)
54 {
55     WLOGI("JsScreenListener::AddCallback is called");
56     std::lock_guard<std::mutex> lock(mtx_);
57     std::unique_ptr<NativeReference> callbackRef;
58     napi_ref result = nullptr;
59     napi_create_reference(env_, jsListenerObject, 1, &result);
60     callbackRef.reset(reinterpret_cast<NativeReference*>(result));
61     jsCallBack_[type].emplace_back(std::move(callbackRef));
62     WLOGI("JsScreenListener::AddCallback success jsCallBack_ size: %{public}u!",
63         static_cast<uint32_t>(jsCallBack_[type].size()));
64 }
65 
RemoveAllCallback()66 void JsScreenListener::RemoveAllCallback()
67 {
68     std::lock_guard<std::mutex> lock(mtx_);
69     jsCallBack_.clear();
70 }
71 
RemoveCallback(napi_env env,const std::string & type,napi_value jsListenerObject)72 void JsScreenListener::RemoveCallback(napi_env env, const std::string& type, napi_value jsListenerObject)
73 {
74     std::lock_guard<std::mutex> lock(mtx_);
75     auto it = jsCallBack_.find(type);
76     if (it == jsCallBack_.end()) {
77         WLOGE("JsScreenListener::RemoveCallback no callback to remove");
78         return;
79     }
80     auto& listeners = it->second;
81     for (auto iter = listeners.begin(); iter != listeners.end();) {
82         bool isEquals = false;
83         napi_strict_equals(env, jsListenerObject, (*iter)->GetNapiValue(), &isEquals);
84         if (isEquals) {
85             listeners.erase(iter);
86         } else {
87             iter++;
88         }
89     }
90     WLOGI("JsScreenListener::RemoveCallback success jsCallBack_ size: %{public}u!",
91         static_cast<uint32_t>(listeners.size()));
92 }
93 
CallJsMethod(const std::string & methodName,napi_value const * argv,size_t argc)94 void JsScreenListener::CallJsMethod(const std::string& methodName, napi_value const * argv, size_t argc)
95 {
96     if (methodName.empty()) {
97         WLOGFE("empty method name str, call method failed");
98         return;
99     }
100     WLOGD("CallJsMethod methodName = %{public}s", methodName.c_str());
101     if (env_ == nullptr) {
102         WLOGFE("env_ nullptr");
103         return;
104     }
105     for (auto& callback : jsCallBack_[methodName]) {
106         napi_value method = callback->GetNapiValue();
107         if (method == nullptr) {
108             WLOGFE("Failed to get method callback from object");
109             continue;
110         }
111         napi_call_function(env_, NapiGetUndefined(env_), method, argc, argv, nullptr);
112     }
113 }
114 
OnConnect(ScreenId id)115 void JsScreenListener::OnConnect(ScreenId id)
116 {
117     std::lock_guard<std::mutex> lock(mtx_);
118     WLOGI("JsScreenListener::OnConnect is called");
119     if (jsCallBack_.empty()) {
120         WLOGFE("JsScreenListener::OnConnect not register!");
121         return;
122     }
123     if (jsCallBack_.find(EVENT_CONNECT) == jsCallBack_.end()) {
124         WLOGE("JsScreenListener::OnConnect not this event, return");
125         return;
126     }
127     sptr<JsScreenListener> listener = this; // Avoid this be destroyed when using.
128     std::unique_ptr<NapiAsyncTask::CompleteCallback> complete = std::make_unique<NapiAsyncTask::CompleteCallback> (
129         [this, listener, id] (napi_env env, NapiAsyncTask &task, int32_t status) {
130             napi_value argv[] = {CreateJsValue(env_, static_cast<uint32_t>(id))};
131             CallJsMethod(EVENT_CONNECT, argv, ArraySize(argv));
132         }
133     );
134 
135     napi_ref callback = nullptr;
136     std::unique_ptr<NapiAsyncTask::ExecuteCallback> execute = nullptr;
137     NapiAsyncTask::Schedule("JsScreenListener::OnConnect", env_, std::make_unique<NapiAsyncTask>(
138             callback, std::move(execute), std::move(complete)));
139 }
140 
OnDisconnect(ScreenId id)141 void JsScreenListener::OnDisconnect(ScreenId id)
142 {
143     std::lock_guard<std::mutex> lock(mtx_);
144     WLOGI("JsScreenListener::OnDisconnect is called");
145     if (jsCallBack_.empty()) {
146         WLOGFE("JsScreenListener::OnDisconnect not register!");
147         return;
148     }
149     if (jsCallBack_.find(EVENT_DISCONNECT) == jsCallBack_.end()) {
150         WLOGE("JsScreenListener::OnDisconnect not this event, return");
151         return;
152     }
153     sptr<JsScreenListener> listener = this; // Avoid this be destroyed when using.
154     std::unique_ptr<NapiAsyncTask::CompleteCallback> complete = std::make_unique<NapiAsyncTask::CompleteCallback> (
155         [this, listener, id] (napi_env env, NapiAsyncTask &task, int32_t status) {
156             napi_value argv[] = {CreateJsValue(env_, static_cast<uint32_t>(id))};
157             CallJsMethod(EVENT_DISCONNECT, argv, ArraySize(argv));
158         }
159     );
160 
161     napi_ref callback = nullptr;
162     std::unique_ptr<NapiAsyncTask::ExecuteCallback> execute = nullptr;
163     NapiAsyncTask::Schedule("JsScreenListener::OnDisconnect", env_, std::make_unique<NapiAsyncTask>(
164             callback, std::move(execute), std::move(complete)));
165 }
166 
OnChange(ScreenId id)167 void JsScreenListener::OnChange(ScreenId id)
168 {
169     std::lock_guard<std::mutex> lock(mtx_);
170     WLOGI("JsScreenListener::OnChange is called");
171     if (jsCallBack_.empty()) {
172         WLOGFE("JsScreenListener::OnChange not register!");
173         return;
174     }
175     if (jsCallBack_.find(EVENT_CHANGE) == jsCallBack_.end()) {
176         WLOGE("JsScreenListener::OnChange not this event, return");
177         return;
178     }
179     sptr<JsScreenListener> listener = this; // Avoid this be destroyed when using.
180     std::unique_ptr<NapiAsyncTask::CompleteCallback> complete = std::make_unique<NapiAsyncTask::CompleteCallback> (
181         [this, listener, id] (napi_env env, NapiAsyncTask &task, int32_t status) {
182             napi_value argv[] = {CreateJsValue(env_, static_cast<uint32_t>(id))};
183             CallJsMethod(EVENT_CHANGE, argv, ArraySize(argv));
184         }
185     );
186 
187     napi_ref callback = nullptr;
188     std::unique_ptr<NapiAsyncTask::ExecuteCallback> execute = nullptr;
189     NapiAsyncTask::Schedule("JsScreenListener::OnChange", env_, std::make_unique<NapiAsyncTask>(
190             callback, std::move(execute), std::move(complete)));
191 }
192 
CreateScreenIdArray(napi_env env,const std::vector<ScreenId> & data)193 napi_value JsScreenListener::CreateScreenIdArray(napi_env env, const std::vector<ScreenId>& data)
194 {
195     napi_value arrayValue = nullptr;
196     napi_create_array_with_length(env, data.size(), &arrayValue);
197     if (arrayValue == nullptr) {
198         WLOGFE("Failed to create screenid array");
199         return NapiGetUndefined(env);
200     }
201     uint32_t index = 0;
202     for (const auto& item : data) {
203         napi_set_element(env, arrayValue, index++, CreateJsValue(env, static_cast<uint32_t>(item)));
204     }
205     return arrayValue;
206 }
207 } // namespace Rosen
208 } // namespace OHOS
209