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_screen.h"
17
18 #include <cinttypes>
19 #include <hitrace_meter.h>
20
21 #include "screen.h"
22 #include "screen_info.h"
23 #include "window_manager_hilog.h"
24 #ifdef XPOWER_EVENT_ENABLE
25 #include "xpower_event_js.h"
26 #endif // XPOWER_EVENT_ENABLE
27
28 namespace OHOS {
29 namespace Rosen {
30 using namespace AbilityRuntime;
31 constexpr size_t ARGC_ONE = 1;
32 constexpr size_t ARGC_TWO = 2;
33
34 static thread_local std::map<ScreenId, std::shared_ptr<NativeReference>> g_JsScreenMap;
35 std::recursive_mutex g_mutex;
36
JsScreen(const sptr<Screen> & screen)37 JsScreen::JsScreen(const sptr<Screen>& screen) : screen_(screen)
38 {
39 }
40
~JsScreen()41 JsScreen::~JsScreen()
42 {
43 TLOGI(WmsLogTag::DMS, "JsScreen::~JsScreen is called");
44 }
45
Finalizer(napi_env env,void * data,void * hint)46 void JsScreen::Finalizer(napi_env env, void* data, void* hint)
47 {
48 TLOGI(WmsLogTag::DMS, "called");
49 auto jsScreen = std::unique_ptr<JsScreen>(static_cast<JsScreen*>(data));
50 if (jsScreen == nullptr) {
51 TLOGE(WmsLogTag::DMS, "jsScreen is null");
52 return;
53 }
54 sptr<Screen> screen = jsScreen->screen_;
55 if (screen == nullptr) {
56 TLOGE(WmsLogTag::DMS, "screen is null");
57 return;
58 }
59 ScreenId screenId = screen->GetId();
60 TLOGI(WmsLogTag::DMS, "screenId : %{public}" PRIu64"", screenId);
61 std::lock_guard<std::recursive_mutex> lock(g_mutex);
62 if (g_JsScreenMap.find(screenId) != g_JsScreenMap.end()) {
63 TLOGI(WmsLogTag::DMS, "screen is destroyed: %{public}" PRIu64"", screenId);
64 g_JsScreenMap.erase(screenId);
65 }
66 }
67
SetOrientation(napi_env env,napi_callback_info info)68 napi_value JsScreen::SetOrientation(napi_env env, napi_callback_info info)
69 {
70 JsScreen* me = CheckParamsAndGetThis<JsScreen>(env, info);
71 return (me != nullptr) ? me->OnSetOrientation(env, info) : nullptr;
72 }
73
NapiGetUndefined(napi_env env)74 napi_value NapiGetUndefined(napi_env env)
75 {
76 napi_value result = nullptr;
77 napi_get_undefined(env, &result);
78 return result;
79 }
80
GetType(napi_env env,napi_value value)81 napi_valuetype GetType(napi_env env, napi_value value)
82 {
83 napi_valuetype res = napi_undefined;
84 napi_typeof(env, value, &res);
85 return res;
86 }
87
OnSetOrientation(napi_env env,napi_callback_info info)88 napi_value JsScreen::OnSetOrientation(napi_env env, napi_callback_info info)
89 {
90 TLOGI(WmsLogTag::DMS, "called");
91 bool paramValidFlag = true;
92 Orientation orientation = Orientation::UNSPECIFIED;
93 size_t argc = 4;
94 napi_value argv[4] = {nullptr};
95 std::string errMsg = "";
96 napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr);
97 if (argc < ARGC_ONE) {
98 TLOGE(WmsLogTag::DMS, "OnSetOrientation Params not match, info argc: %{public}zu", argc);
99 errMsg = "Invalid args count, need one arg at least!";
100 paramValidFlag = false;
101 } else if (!ConvertFromJsValue(env, argv[0], orientation)) {
102 paramValidFlag = false;
103 TLOGE(WmsLogTag::DMS, "Failed to convert parameter to orientation");
104 errMsg = "Failed to convert parameter to orientation";
105 }
106 if (!paramValidFlag) {
107 TLOGE(WmsLogTag::DMS, "OnSetOrientation paramValidFlag error");
108 napi_throw(env, CreateJsError(env, static_cast<int32_t>(DmErrorCode::DM_ERROR_INVALID_PARAM), errMsg));
109 return NapiGetUndefined(env);
110 }
111 if (orientation < Orientation::BEGIN || orientation > Orientation::END) {
112 TLOGE(WmsLogTag::DMS, "Orientation param error! orientation value must from enum Orientation");
113 errMsg = "orientation value must from enum Orientation";
114 napi_throw(env, CreateJsError(env, static_cast<int32_t>(DmErrorCode::DM_ERROR_INVALID_PARAM), errMsg));
115 return NapiGetUndefined(env);
116 }
117 napi_value lastParam = nullptr;
118 if (argc >= ARGC_TWO && argv[ARGC_TWO - 1] != nullptr &&
119 GetType(env, argv[ARGC_TWO - 1]) == napi_function) {
120 lastParam = argv[ARGC_TWO - 1];
121 }
122 napi_value result = nullptr;
123 std::unique_ptr<NapiAsyncTask> napiAsyncTask = CreateEmptyAsyncTask(env, lastParam, &result);
124 auto asyncTask = [this, orientation, env, task = napiAsyncTask.get()]() {
125 HITRACE_METER_FMT(HITRACE_TAG_WINDOW_MANAGER, "JsScreen::OnSetOrientation");
126 DmErrorCode ret = DM_JS_TO_ERROR_CODE_MAP.at(screen_->SetOrientation(orientation));
127 if (ret == DmErrorCode::DM_OK) {
128 task->Resolve(env, NapiGetUndefined(env));
129 TLOGNI(WmsLogTag::DMS, "OnSetOrientation success");
130 } else {
131 task->Reject(env, CreateJsError(env, static_cast<int32_t>(ret), "JsScreen::OnSetOrientation failed."));
132 TLOGNE(WmsLogTag::DMS, "OnSetOrientation failed");
133 }
134 delete task;
135 };
136 NapiSendDmsEvent(env, asyncTask, napiAsyncTask, "OnSetOrientation");
137 return result;
138 }
139
SetScreenActiveMode(napi_env env,napi_callback_info info)140 napi_value JsScreen::SetScreenActiveMode(napi_env env, napi_callback_info info)
141 {
142 TLOGI(WmsLogTag::DMS, "called");
143 JsScreen* me = CheckParamsAndGetThis<JsScreen>(env, info);
144 #ifdef XPOWER_EVENT_ENABLE
145 if (me != nullptr) {
146 HiviewDFX::ReportXPowerJsStackSysEvent(env, "EPS_LCD_FREQ");
147 }
148 #endif // XPOWER_EVENT_ENABLE
149 return (me != nullptr) ? me->OnSetScreenActiveMode(env, info) : nullptr;
150 }
151
OnSetScreenActiveMode(napi_env env,napi_callback_info info)152 napi_value JsScreen::OnSetScreenActiveMode(napi_env env, napi_callback_info info)
153 {
154 TLOGI(WmsLogTag::DMS, "called");
155 bool paramValidFlag = true;
156 uint32_t modeId = 0;
157 size_t argc = 4;
158 napi_value argv[4] = {nullptr};
159 std::string errMsg = "";
160 napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr);
161 if (argc < ARGC_ONE) {
162 TLOGE(WmsLogTag::DMS, "OnSetScreenActiveMode Params not match %{public}zu", argc);
163 errMsg = "Invalid args count, need one arg at least!";
164 paramValidFlag = false;
165 } else {
166 if (!ConvertFromJsValue(env, argv[0], modeId)) {
167 TLOGE(WmsLogTag::DMS, "Failed to convert parameter to modeId");
168 errMsg = "Failed to convert parameter to modeId";
169 paramValidFlag = false;
170 }
171 }
172 if (!paramValidFlag) {
173 TLOGE(WmsLogTag::DMS, "OnSetScreenActiveMode paramValidFlag error");
174 napi_throw(env, CreateJsError(env, static_cast<int32_t>(DmErrorCode::DM_ERROR_INVALID_PARAM), errMsg));
175 return NapiGetUndefined(env);
176 }
177 napi_value lastParam = nullptr;
178 if (argc >= ARGC_TWO && argv[ARGC_TWO - 1] != nullptr &&
179 GetType(env, argv[ARGC_TWO - 1]) == napi_function) {
180 lastParam = argv[ARGC_TWO - 1];
181 }
182 napi_value result = nullptr;
183 std::unique_ptr<NapiAsyncTask> napiAsyncTask = CreateEmptyAsyncTask(env, lastParam, &result);
184 auto asyncTask = [this, modeId, env, task = napiAsyncTask.get()]() {
185 HITRACE_METER_FMT(HITRACE_TAG_WINDOW_MANAGER, "JsScreen::OnSetScreenActiveMode");
186 DmErrorCode ret = DM_JS_TO_ERROR_CODE_MAP.at(screen_->SetScreenActiveMode(modeId));
187 if (ret == DmErrorCode::DM_OK) {
188 task->Resolve(env, NapiGetUndefined(env));
189 TLOGNI(WmsLogTag::DMS, "OnSetScreenActiveMode success");
190 } else {
191 task->Reject(env, CreateJsError(env, static_cast<int32_t>(ret),
192 "JsScreen::OnSetScreenActiveMode failed."));
193 TLOGNE(WmsLogTag::DMS, "OnSetScreenActiveMode failed");
194 }
195 delete task;
196 };
197 NapiSendDmsEvent(env, asyncTask, napiAsyncTask, "OnSetScreenActiveMode");
198 return result;
199 }
200
SetDensityDpi(napi_env env,napi_callback_info info)201 napi_value JsScreen::SetDensityDpi(napi_env env, napi_callback_info info)
202 {
203 TLOGI(WmsLogTag::DMS, "called");
204 JsScreen* me = CheckParamsAndGetThis<JsScreen>(env, info);
205 return (me != nullptr) ? me->OnSetDensityDpi(env, info) : nullptr;
206 }
207
OnSetDensityDpi(napi_env env,napi_callback_info info)208 napi_value JsScreen::OnSetDensityDpi(napi_env env, napi_callback_info info)
209 {
210 TLOGI(WmsLogTag::DMS, "called");
211 bool paramValidFlag = true;
212 uint32_t densityDpi = 0;
213 size_t argc = 4;
214 std::string errMsg = "";
215 napi_value argv[4] = {nullptr};
216 napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr);
217 if (argc < ARGC_ONE) {
218 TLOGE(WmsLogTag::DMS, "OnSetDensityDpi Params not match %{public}zu", argc);
219 errMsg = "Invalid args count, need one arg at least!";
220 paramValidFlag = false;
221 } else {
222 if (!ConvertFromJsValue(env, argv[0], densityDpi)) {
223 TLOGE(WmsLogTag::DMS, "Failed to convert parameter to densityDpi");
224 errMsg = "Failed to convert parameter to densityDpi";
225 paramValidFlag = false;
226 }
227 }
228 if (!paramValidFlag) {
229 TLOGE(WmsLogTag::DMS, "OnSetDensityDpi paramValidFlag error");
230 napi_throw(env, CreateJsError(env, static_cast<int32_t>(DmErrorCode::DM_ERROR_INVALID_PARAM), errMsg));
231 return NapiGetUndefined(env);
232 }
233 napi_value lastParam = nullptr;
234 if (argc >= ARGC_TWO && argv[ARGC_TWO - 1] != nullptr &&
235 GetType(env, argv[ARGC_TWO - 1]) == napi_function) {
236 lastParam = argv[ARGC_TWO - 1];
237 }
238 napi_value result = nullptr;
239 std::unique_ptr<NapiAsyncTask> napiAsyncTask = CreateEmptyAsyncTask(env, lastParam, &result);
240 auto asyncTask = [this, densityDpi, env, task = napiAsyncTask.get()]() {
241 HITRACE_METER_FMT(HITRACE_TAG_WINDOW_MANAGER, "JsScreen::OnSetDensityDpi");
242 DmErrorCode ret = DM_JS_TO_ERROR_CODE_MAP.at(screen_->SetDensityDpi(densityDpi));
243 if (ret == DmErrorCode::DM_OK) {
244 task->Resolve(env, NapiGetUndefined(env));
245 TLOGNI(WmsLogTag::DMS, "OnSetDensityDpi success");
246 } else {
247 task->Reject(env, CreateJsError(env, static_cast<int32_t>(ret),
248 "JsScreen::OnSetDensityDpi failed."));
249 TLOGNE(WmsLogTag::DMS, "OnSetDensityDpi failed");
250 }
251 delete task;
252 };
253 NapiSendDmsEvent(env, asyncTask, napiAsyncTask, "OnSetDensityDpi");
254 return result;
255 }
256
NapiSendDmsEvent(napi_env env,std::function<void ()> asyncTask,std::unique_ptr<AbilityRuntime::NapiAsyncTask> & napiAsyncTask,const std::string & taskName)257 void JsScreen::NapiSendDmsEvent(napi_env env, std::function<void()> asyncTask,
258 std::unique_ptr<AbilityRuntime::NapiAsyncTask>& napiAsyncTask, const std::string& taskName)
259 {
260 if (napi_send_event(env, asyncTask, napi_eprio_immediate, taskName.c_str()) != napi_status::napi_ok) {
261 napiAsyncTask->Reject(env, CreateJsError(env,
262 static_cast<int32_t>(DmErrorCode::DM_ERROR_INVALID_SCREEN), "Send event failed!"));
263 } else {
264 napiAsyncTask.release();
265 TLOGE(WmsLogTag::DMS, "send event success");
266 }
267 }
268
FindJsDisplayObject(ScreenId screenId)269 std::shared_ptr<NativeReference> FindJsDisplayObject(ScreenId screenId)
270 {
271 TLOGD(WmsLogTag::DMS, "[NAPI]Try to find screen %{public}" PRIu64" in g_JsScreenMap", screenId);
272 std::lock_guard<std::recursive_mutex> lock(g_mutex);
273 if (g_JsScreenMap.find(screenId) == g_JsScreenMap.end()) {
274 TLOGI(WmsLogTag::DMS, "[NAPI]Can not find screen %{public}" PRIu64" in g_JsScreenMap", screenId);
275 return nullptr;
276 }
277 return g_JsScreenMap[screenId];
278 }
279
CreateJsScreenObject(napi_env env,sptr<Screen> & screen)280 napi_value CreateJsScreenObject(napi_env env, sptr<Screen>& screen)
281 {
282 TLOGD(WmsLogTag::DMS, "called");
283 napi_value objValue = nullptr;
284 std::shared_ptr<NativeReference> jsScreenObj = FindJsDisplayObject(screen->GetId());
285 if (jsScreenObj != nullptr && jsScreenObj->GetNapiValue() != nullptr) {
286 TLOGI(WmsLogTag::DMS, "[NAPI]FindJsScreenObject %{public}" PRIu64"", screen->GetId());
287 objValue = jsScreenObj->GetNapiValue();
288 }
289 if (objValue == nullptr) {
290 napi_create_object(env, &objValue);
291 }
292 if (objValue == nullptr) {
293 TLOGE(WmsLogTag::DMS, "Failed to convert prop to jsObject");
294 return NapiGetUndefined(env);
295 }
296 std::unique_ptr<JsScreen> jsScreen = std::make_unique<JsScreen>(screen);
297 napi_wrap(env, objValue, jsScreen.release(), JsScreen::Finalizer, nullptr, nullptr);
298 auto info = screen->GetScreenInfo();
299 if (info == nullptr) {
300 TLOGE(WmsLogTag::DMS, "Failed to GetScreenInfo");
301 return NapiGetUndefined(env);
302 }
303 ScreenId screenId = info->GetScreenId();
304 napi_set_named_property(env, objValue, "id",
305 CreateJsValue(env, screenId == SCREEN_ID_INVALID ? -1 : static_cast<int64_t>(screenId)));
306 ScreenId rsId = info->GetRsId();
307 napi_set_named_property(env, objValue, "rsId",
308 CreateJsValue(env, screenId == SCREEN_ID_INVALID ? -1 : static_cast<int64_t>(rsId)));
309 ScreenId parentId = info->GetParentId();
310 napi_set_named_property(env, objValue, "parent",
311 CreateJsValue(env, parentId == SCREEN_ID_INVALID ? -1 : static_cast<int64_t>(parentId)));
312 napi_set_named_property(env, objValue, "orientation", CreateJsValue(env, info->GetOrientation()));
313 napi_set_named_property(env, objValue, "serialNumber", CreateJsValue(env, info->GetSerialNumber()));
314 napi_set_named_property(env, objValue, "sourceMode", CreateJsValue(env, info->GetSourceMode()));
315 napi_set_named_property(env, objValue, "activeModeIndex", CreateJsValue(env, info->GetModeId()));
316 napi_set_named_property(env, objValue, "supportedModeInfo", CreateJsScreenModeArrayObject(env, info->GetModes()));
317 napi_set_named_property(env, objValue, "densityDpi", CreateJsValue(env,
318 static_cast<uint32_t>(info->GetVirtualPixelRatio() * DOT_PER_INCH))); // Dpi = Density(VPR) * 160.
319 if (jsScreenObj == nullptr || jsScreenObj->GetNapiValue() == nullptr) {
320 std::shared_ptr<NativeReference> JsScreenRef;
321 napi_ref result = nullptr;
322 napi_create_reference(env, objValue, 1, &result);
323 JsScreenRef.reset(reinterpret_cast<NativeReference*>(result));
324 std::lock_guard<std::recursive_mutex> lock(g_mutex);
325 g_JsScreenMap[screenId] = JsScreenRef;
326 const char *moduleName = "JsScreen";
327 BindNativeFunction(env, objValue, "setScreenActiveMode", moduleName, JsScreen::SetScreenActiveMode);
328 BindNativeFunction(env, objValue, "setOrientation", moduleName, JsScreen::SetOrientation);
329 BindNativeFunction(env, objValue, "setDensityDpi", moduleName, JsScreen::SetDensityDpi);
330 }
331 return objValue;
332 }
333
CreateJsScreenModeArrayObject(napi_env env,std::vector<sptr<SupportedScreenModes>> screenModes)334 napi_value CreateJsScreenModeArrayObject(napi_env env, std::vector<sptr<SupportedScreenModes>> screenModes)
335 {
336 napi_value arrayValue = nullptr;
337 napi_create_array_with_length(env, screenModes.size(), &arrayValue);
338 size_t i = 0;
339 for (const auto& mode : screenModes) {
340 napi_set_element(env, arrayValue, i++, CreateJsScreenModeObject(env, mode));
341 }
342 return arrayValue;
343 }
344
CreateJsScreenModeObject(napi_env env,const sptr<SupportedScreenModes> & mode)345 napi_value CreateJsScreenModeObject(napi_env env, const sptr<SupportedScreenModes>& mode)
346 {
347 TLOGD(WmsLogTag::DMS, "called");
348 napi_value objValue = nullptr;
349 napi_create_object(env, &objValue);
350 if (objValue == nullptr) {
351 TLOGE(WmsLogTag::DMS, "Failed to convert prop to jsObject");
352 return NapiGetUndefined(env);
353 }
354 uint32_t id = mode->id_;
355 uint32_t width = mode->width_;
356 uint32_t height = mode->height_;
357 uint32_t refreshRate = mode->refreshRate_;
358 napi_set_named_property(env, objValue, "id", CreateJsValue(env, id));
359 napi_set_named_property(env, objValue, "width", CreateJsValue(env, width));
360 napi_set_named_property(env, objValue, "height", CreateJsValue(env, height));
361 napi_set_named_property(env, objValue, "refreshRate", CreateJsValue(env, refreshRate));
362 return objValue;
363 }
364 } // namespace Rosen
365 } // namespace OHOS
366