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 "resource_manager_addon.h"
17 
18 #include "ability.h"
19 #include "foundation/ability/ability_runtime/interfaces/kits/native/appkit/ability_runtime/context/context.h"
20 #include "hilog/log.h"
21 #include "hisysevent_adapter.h"
22 #include "hitrace_meter.h"
23 #include "js_runtime_utils.h"
24 
25 #include "napi/native_api.h"
26 #include "napi/native_common.h"
27 #include "res_common.h"
28 #include "resource_manager_napi_async_impl.h"
29 
30 namespace OHOS {
31 namespace Global {
32 namespace Resource {
33 #define GET_PARAMS(env, info, num)    \
34     size_t argc = num;                \
35     napi_value argv[num] = {nullptr}; \
36     napi_value thisVar = nullptr;     \
37     void *data = nullptr;             \
38     napi_get_cb_info(env, info, &argc, argv, &thisVar, &data)
39 
40 using namespace OHOS::AppExecFwk;
41 
ExecuteGetResMgr(napi_env env,void * data)42 static void ExecuteGetResMgr(napi_env env, void* data)
43 {
44     if (data == nullptr) {
45         return;
46     }
47     ResMgrDataContext *asyncContext = static_cast<ResMgrDataContext*>(data);
48 
49     asyncContext->createValueFunc_ = [](napi_env env, ResMgrDataContext &context) -> napi_value {
50         std::string traceVal = "Create ResourceManager";
51         StartTrace(HITRACE_TAG_GLOBAL_RESMGR, traceVal);
52         napi_value result = ResourceManagerAddon::Create(env, context.bundleName_, context.resMgr_, nullptr);
53         FinishTrace(HITRACE_TAG_GLOBAL_RESMGR);
54         if (result == nullptr) {
55             context.SetErrorMsg("Failed to get ResourceManagerAddon");
56             ReportInitResourceManagerFail(context.bundleName_, "failed to get ResourceManagerAddon");
57             return nullptr;
58         }
59         return result;
60     };
61 }
62 
GetGlobalAbility(napi_env env)63 Ability* GetGlobalAbility(napi_env env)
64 {
65     napi_value global;
66     napi_status status = napi_get_global(env, &global);
67     if (status != napi_ok) {
68         RESMGR_HILOGE(RESMGR_JS_TAG, "Failed to get global");
69         return nullptr;
70     }
71 
72     napi_value abilityObj;
73     status = napi_get_named_property(env, global, "ability", &abilityObj);
74     if (status != napi_ok || abilityObj == nullptr) {
75         RESMGR_HILOGI(RESMGR_JS_TAG, "Failed to get ability property");
76         return nullptr;
77     }
78 
79     Ability* ability = nullptr;
80     status = napi_get_value_external(env, abilityObj, (void **)&ability);
81     if (status == napi_ok && ability != nullptr) {
82         return ability;
83     }
84 
85     return nullptr;
86 }
87 
InitAsyncContext(napi_env env,const std::string & bundleName,Ability * ability,const std::shared_ptr<AbilityRuntime::Context> & context,ResMgrDataContext & asyncContext)88 static bool InitAsyncContext(napi_env env, const std::string &bundleName, Ability* ability,
89     const std::shared_ptr<AbilityRuntime::Context>& context, ResMgrDataContext &asyncContext)
90 {
91     std::shared_ptr<ResourceManager> resMgr;
92     if (ability != nullptr) {
93         if (bundleName.empty()) {
94             resMgr = ability->GetResourceManager();
95         } else {
96             std::shared_ptr<Context> bundleContext = ability->CreateBundleContext(bundleName, 0);
97             if (bundleContext != nullptr) {
98                 resMgr = bundleContext->GetResourceManager();
99             }
100         }
101     } else if (context != nullptr) {
102         if (bundleName.empty()) {
103             resMgr = context->GetResourceManager();
104         } else {
105             std::shared_ptr<OHOS::AbilityRuntime::Context> bundleContext = context->CreateBundleContext(bundleName);
106             if (bundleContext != nullptr) {
107                 resMgr = bundleContext->GetResourceManager();
108             }
109         }
110     }
111     asyncContext.resMgr_ = resMgr;
112     asyncContext.bundleName_ = bundleName;
113     return resMgr != nullptr;
114 }
115 
getResult(napi_env env,std::unique_ptr<ResMgrDataContext> & asyncContext,std::string & bundleName,const std::shared_ptr<AbilityRuntime::Context> & abilityRuntimeContext)116 static napi_value getResult(napi_env env, std::unique_ptr<ResMgrDataContext> &asyncContext,
117     std::string &bundleName, const std::shared_ptr<AbilityRuntime::Context> &abilityRuntimeContext)
118 {
119     napi_value result = nullptr;
120     if (asyncContext->callbackRef_ == nullptr) {
121         napi_create_promise(env, &asyncContext->deferred_, &result);
122     } else {
123         napi_get_undefined(env, &result);
124     }
125 
126     if (!InitAsyncContext(env, bundleName, GetGlobalAbility(env), abilityRuntimeContext, *asyncContext)) {
127         RESMGR_HILOGE(RESMGR_JS_TAG, "init async context failed");
128         ReportInitResourceManagerFail(bundleName, "failed to init async context");
129         return nullptr;
130     }
131 
132     napi_value resource = nullptr;
133     napi_create_string_utf8(env, "getResourceManager", NAPI_AUTO_LENGTH, &resource);
134     napi_status status = napi_create_async_work(env, nullptr, resource, ExecuteGetResMgr,
135         ResourceManagerNapiAsyncImpl::Complete, static_cast<void*>(asyncContext.get()), &asyncContext->work_);
136     if (status != napi_ok) {
137         RESMGR_HILOGE(RESMGR_JS_TAG, "Failed to create async work for getResourceManager %{public}d", status);
138         return result;
139     }
140     status = napi_queue_async_work_with_qos(env, asyncContext->work_, napi_qos_user_initiated);
141     if (status != napi_ok) {
142         RESMGR_HILOGE(RESMGR_JS_TAG, "Failed to queue async work for getResourceManager %{public}d", status);
143         return result;
144     }
145     asyncContext.release();
146     return result;
147 }
148 
GetResourceManager(napi_env env,napi_callback_info info)149 static napi_value GetResourceManager(napi_env env, napi_callback_info info)
150 {
151     GET_PARAMS(env, info, 3);
152 
153     std::unique_ptr<ResMgrDataContext> asyncContext = std::make_unique<ResMgrDataContext>();
154     std::shared_ptr<AbilityRuntime::Context> abilityRuntimeContext;
155     std::string bundleName;
156     for (size_t i = 0; i < argc; i++) {
157         napi_valuetype valueType;
158         napi_typeof(env, argv[i], &valueType);
159         if (i == 0 && valueType == napi_object) {
160             using WeakContextPtr = std::weak_ptr<AbilityRuntime::Context> *;
161             WeakContextPtr objContext;
162             napi_status status = napi_unwrap(env, argv[0], reinterpret_cast<void **>(&objContext));
163             if (status != napi_ok || objContext == nullptr) {
164                 RESMGR_HILOGE(RESMGR_JS_TAG, "Failed to get objContext");
165                 return nullptr;
166             }
167             auto context = objContext->lock();
168             if (context == nullptr) {
169                 RESMGR_HILOGE(RESMGR_JS_TAG, "Failed to get context");
170                 return nullptr;
171             }
172             abilityRuntimeContext = context;
173         } else if ((i == 0 || i == 1) && valueType == napi_string) {
174             size_t len = 0;
175             napi_status status = napi_get_value_string_utf8(env, argv[i], nullptr, 0, &len);
176             if (status != napi_ok) {
177                 RESMGR_HILOGE(RESMGR_JS_TAG, "Failed to get bundle name length");
178                 return nullptr;
179             }
180             std::vector<char> buf(len + 1);
181             status = napi_get_value_string_utf8(env, argv[i], buf.data(), len + 1, &len);
182             if (status != napi_ok) {
183                 RESMGR_HILOGE(RESMGR_JS_TAG, "Failed to get bundle name");
184                 return nullptr;
185             }
186             bundleName = buf.data();
187         } else if ((i == 0 || i == 1 || i == 2) && valueType == napi_function) { // 2 means the third parameter
188             napi_create_reference(env, argv[i], 1, &asyncContext->callbackRef_);
189             break;
190         } else {
191             // self resourcemanager with promise
192         }
193     }
194 
195     napi_value result = getResult(env, asyncContext, bundleName, abilityRuntimeContext);
196     return result;
197 }
198 
GetSystemResourceManager(napi_env env,napi_callback_info info)199 static napi_value GetSystemResourceManager(napi_env env, napi_callback_info info)
200 {
201     return ResourceManagerAddon::GetSystemResMgr(env);
202 }
203 
SetEnumItem(napi_env env,napi_value object,const char * name,int32_t value)204 static napi_status SetEnumItem(napi_env env, napi_value object, const char* name, int32_t value)
205 {
206     napi_status status;
207     napi_value itemName;
208     napi_value itemValue;
209 
210     NAPI_CALL_BASE(env, status = napi_create_string_utf8(env, name, NAPI_AUTO_LENGTH, &itemName), status);
211     NAPI_CALL_BASE(env, status = napi_create_int32(env, value, &itemValue), status);
212 
213     NAPI_CALL_BASE(env, status = napi_set_property(env, object, itemName, itemValue), status);
214     NAPI_CALL_BASE(env, status = napi_set_property(env, object, itemValue, itemName), status);
215 
216     return napi_ok;
217 }
218 
InitDirectionObject(napi_env env)219 static napi_value InitDirectionObject(napi_env env)
220 {
221     napi_value object;
222     NAPI_CALL(env, napi_create_object(env, &object));
223 
224     NAPI_CALL(env, SetEnumItem(env, object, "DIRECTION_VERTICAL", DIRECTION_VERTICAL));
225     NAPI_CALL(env, SetEnumItem(env, object, "DIRECTION_HORIZONTAL", DIRECTION_HORIZONTAL));
226     return object;
227 }
228 
InitDeviceTypeObject(napi_env env)229 static napi_value InitDeviceTypeObject(napi_env env)
230 {
231     napi_value object;
232     NAPI_CALL(env, napi_create_object(env, &object));
233 
234     NAPI_CALL(env, SetEnumItem(env, object, "DEVICE_TYPE_PHONE", DEVICE_PHONE));
235     NAPI_CALL(env, SetEnumItem(env, object, "DEVICE_TYPE_TABLET", DEVICE_TABLET));
236     NAPI_CALL(env, SetEnumItem(env, object, "DEVICE_TYPE_CAR", DEVICE_CAR));
237     NAPI_CALL(env, SetEnumItem(env, object, "DEVICE_TYPE_PC", DEVICE_PAD));
238     NAPI_CALL(env, SetEnumItem(env, object, "DEVICE_TYPE_TV", DEVICE_TV));
239     NAPI_CALL(env, SetEnumItem(env, object, "DEVICE_TYPE_WEARABLE", DEVICE_WEARABLE));
240     NAPI_CALL(env, SetEnumItem(env, object, "DEVICE_TYPE_2IN1", DEVICE_TWOINONE));
241     return object;
242 }
243 
InitScreenDensityObject(napi_env env)244 static napi_value InitScreenDensityObject(napi_env env)
245 {
246     napi_value object;
247     NAPI_CALL(env, napi_create_object(env, &object));
248 
249     NAPI_CALL(env, SetEnumItem(env, object, "SCREEN_SDPI", SCREEN_DENSITY_SDPI));
250     NAPI_CALL(env, SetEnumItem(env, object, "SCREEN_MDPI", SCREEN_DENSITY_MDPI));
251     NAPI_CALL(env, SetEnumItem(env, object, "SCREEN_LDPI", SCREEN_DENSITY_LDPI));
252     NAPI_CALL(env, SetEnumItem(env, object, "SCREEN_XLDPI", SCREEN_DENSITY_XLDPI));
253     NAPI_CALL(env, SetEnumItem(env, object, "SCREEN_XXLDPI", SCREEN_DENSITY_XXLDPI));
254     NAPI_CALL(env, SetEnumItem(env, object, "SCREEN_XXXLDPI", SCREEN_DENSITY_XXXLDPI));
255     return object;
256 }
257 
InitColorModeObject(napi_env env)258 static napi_value InitColorModeObject(napi_env env)
259 {
260     napi_value object;
261     NAPI_CALL(env, napi_create_object(env, &object));
262 
263     NAPI_CALL(env, SetEnumItem(env, object, "DARK", DARK));
264     NAPI_CALL(env, SetEnumItem(env, object, "LIGHT", LIGHT));
265     return object;
266 }
267 
ResMgrInit(napi_env env,napi_value exports)268 static napi_value ResMgrInit(napi_env env, napi_value exports)
269 {
270     std::string traceVal = "GetResourceManager";
271     StartTrace(HITRACE_TAG_GLOBAL_RESMGR, traceVal);
272     napi_property_descriptor creatorProp[] = {
273         DECLARE_NAPI_FUNCTION("getResourceManager", GetResourceManager),
274         DECLARE_NAPI_FUNCTION("getSystemResourceManager", GetSystemResourceManager),
275     };
276     napi_status status = napi_define_properties(env, exports, sizeof(creatorProp) / sizeof(creatorProp[0]),
277         creatorProp);
278     FinishTrace(HITRACE_TAG_GLOBAL_RESMGR);
279     if (status != napi_ok) {
280         RESMGR_HILOGE(RESMGR_JS_TAG, "Failed to set getResourceManager at init");
281         return nullptr;
282     }
283 
284     napi_property_descriptor static_prop[] = {
285         DECLARE_NAPI_PROPERTY("Direction", InitDirectionObject(env)),
286         DECLARE_NAPI_PROPERTY("DeviceType", InitDeviceTypeObject(env)),
287         DECLARE_NAPI_PROPERTY("ScreenDensity", InitScreenDensityObject(env)),
288         DECLARE_NAPI_PROPERTY("ColorMode", InitColorModeObject(env)),
289     };
290 
291     status = napi_define_properties(env, exports, sizeof(static_prop) / sizeof(static_prop[0]), static_prop);
292     if (status != napi_ok) {
293         RESMGR_HILOGE(RESMGR_JS_TAG, "failed to define properties for exports");
294         return nullptr;
295     }
296 
297     return exports;
298 }
299 
300 static napi_module g_resourceManagerModule = {
301     .nm_version = 1,
302     .nm_flags = 0,
303     .nm_filename = nullptr,
304     .nm_register_func = ResMgrInit,
305     .nm_modname = "resourceManager",
306     .nm_priv = ((void*)0),
307     .reserved = {0}
308 };
309 
ResMgrRegister()310 extern "C" __attribute__((constructor)) void ResMgrRegister()
311 {
312     napi_module_register(&g_resourceManagerModule);
313 }
314 } // namespace Resource
315 } // namespace Global
316 } // namespace OHOS