1 /*
2 * Copyright (c) 2024 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_application.h"
17
18 #include "ability_runtime_error_util.h"
19 #include "accesstoken_kit.h"
20 #include "context_impl.h"
21 #include "hilog_tag_wrapper.h"
22 #include "js_application_context_utils.h"
23 #include "js_error_utils.h"
24 #include "js_runtime_utils.h"
25 #include "js_context_utils.h"
26 #include "napi_base_context.h"
27
28 namespace OHOS {
29 namespace AbilityRuntime {
30 namespace {
31 constexpr size_t ARGC_ZERO = 0;
32 constexpr size_t ARGC_ONE = 1;
33 constexpr size_t ARGC_TWO = 2;
34 constexpr const char* PERMISSION_GET_BUNDLE_INFO = "ohos.permission.GET_BUNDLE_INFO_PRIVILEGED";
35 }
Finalizer(napi_env env,void * data,void * hint)36 void JsApplication::Finalizer(napi_env env, void *data, void *hint)
37 {
38 TAG_LOGD(AAFwkTag::APPKIT, "Called.");
39 std::unique_ptr<JsApplication>(static_cast<JsApplication *>(data));
40 }
41
GetApplicationContext(napi_env env,napi_callback_info info)42 napi_value JsApplication::GetApplicationContext(napi_env env, napi_callback_info info)
43 {
44 GET_NAPI_INFO_AND_CALL(env, info, JsApplication, OnGetApplicationContext);
45 }
46
OnGetApplicationContext(napi_env env,NapiCallbackInfo & info)47 napi_value JsApplication::OnGetApplicationContext(napi_env env, NapiCallbackInfo &info)
48 {
49 TAG_LOGD(AAFwkTag::APPKIT, "Called.");
50 napi_value value = JsApplicationContextUtils::CreateJsApplicationContext(env);
51 auto systemModule = JsRuntime::LoadSystemModuleByEngine(env, "application.ApplicationContext", &value, 1);
52 if (systemModule == nullptr) {
53 TAG_LOGE(AAFwkTag::APPKIT, "invalid systemModule");
54 AbilityRuntimeErrorUtil::Throw(env, ERR_ABILITY_RUNTIME_EXTERNAL_INTERNAL_ERROR);
55 return CreateJsUndefined(env);
56 }
57 napi_value object = systemModule->GetNapiValue();
58 if (!CheckTypeForNapiValue(env, object, napi_object)) {
59 TAG_LOGE(AAFwkTag::APPKIT, "failed to get context native object");
60 AbilityRuntimeErrorUtil::Throw(env, ERR_ABILITY_RUNTIME_EXTERNAL_INTERNAL_ERROR);
61 return CreateJsUndefined(env);
62 }
63 return object;
64 }
65
CreateModuleContext(napi_env env,napi_callback_info info)66 napi_value JsApplication::CreateModuleContext(napi_env env, napi_callback_info info)
67 {
68 GET_NAPI_INFO_AND_CALL(env, info, JsApplication, OnCreateModuleContext);
69 }
70
CreateBundleContext(napi_env env,napi_callback_info info)71 napi_value JsApplication::CreateBundleContext(napi_env env, napi_callback_info info)
72 {
73 GET_NAPI_INFO_AND_CALL(env, info, JsApplication, OnCreateBundleContext);
74 }
75
OnCreateModuleContext(napi_env env,NapiCallbackInfo & info)76 napi_value JsApplication::OnCreateModuleContext(napi_env env, NapiCallbackInfo &info)
77 {
78 TAG_LOGD(AAFwkTag::APPKIT, "Called");
79 if (info.argc < ARGC_TWO) {
80 TAG_LOGE(AAFwkTag::APPKIT, "invalid argc");
81 ThrowTooFewParametersError(env);
82 return CreateJsUndefined(env);
83 }
84
85 bool stageMode = false;
86 napi_status status = OHOS::AbilityRuntime::IsStageContext(env, info.argv[ARGC_ZERO], stageMode);
87 if (status != napi_ok || !stageMode) {
88 TAG_LOGE(AAFwkTag::APPKIT, "not stageMode");
89 ThrowInvalidParamError(env, "Parse param context failed, must be a context of stageMode.");
90 return CreateJsUndefined(env);
91 }
92
93 auto context = OHOS::AbilityRuntime::GetStageModeContext(env, info.argv[ARGC_ZERO]);
94 if (context == nullptr) {
95 TAG_LOGE(AAFwkTag::APPKIT, "null context");
96 ThrowInvalidParamError(env, "Parse param context failed, must not be nullptr.");
97 return CreateJsUndefined(env);
98 }
99
100 auto inputContextPtr = Context::ConvertTo<Context>(context);
101 if (inputContextPtr == nullptr) {
102 TAG_LOGE(AAFwkTag::APPKIT, "Convert to context failed");
103 ThrowInvalidParamError(env, "Parse param context failed, must be a context.");
104 return CreateJsUndefined(env);
105 }
106
107 std::shared_ptr<std::shared_ptr<Context>> moduleContext = std::make_shared<std::shared_ptr<Context>>();
108 std::shared_ptr<ContextImpl> contextImpl = std::make_shared<ContextImpl>();
109 if (contextImpl == nullptr) {
110 TAG_LOGE(AAFwkTag::APPKIT, "null contextImpl");
111 ThrowInvalidParamError(env, "create context failed.");
112 return CreateJsUndefined(env);
113 }
114 contextImpl->SetProcessName(context->GetProcessName());
115 std::string moduleName = "";
116 std::string bundleName = "";
117 if (info.argc == ARGC_TWO) {
118 TAG_LOGD(AAFwkTag::APPKIT, "Called");
119 if (!ConvertFromJsValue(env, info.argv[ARGC_ONE], moduleName)) {
120 TAG_LOGE(AAFwkTag::APPKIT, "Parse failed");
121 ThrowInvalidParamError(env, "Parse param moduleName failed, moduleName must be string.");
122 return CreateJsUndefined(env);
123 }
124 } else {
125 TAG_LOGD(AAFwkTag::APPKIT, "Called");
126 if (!CheckCallerIsSystemApp()) {
127 TAG_LOGE(AAFwkTag::APPKIT, "no system app");
128 ThrowNotSystemAppError(env);
129 return CreateJsUndefined(env);
130 }
131
132 if (!CheckCallerPermission(PERMISSION_GET_BUNDLE_INFO)) {
133 TAG_LOGE(AAFwkTag::APPKIT, "no permission");
134 ThrowNoPermissionError(env, PERMISSION_GET_BUNDLE_INFO);
135 return CreateJsUndefined(env);
136 }
137
138 if (!ConvertFromJsValue(env, info.argv[ARGC_TWO], moduleName)
139 || !ConvertFromJsValue(env, info.argv[ARGC_ONE], bundleName)) {
140 TAG_LOGE(AAFwkTag::APPKIT, "Parse failed");
141 ThrowInvalidParamError(env, "Parse param failed, moduleName and bundleName must be string.");
142 return CreateJsUndefined(env);
143 }
144 }
145 TAG_LOGD(AAFwkTag::APPKIT, "moduleName: %{public}s, bundlename: %{public}s",
146 moduleName.c_str(), bundleName.c_str());
147 NapiAsyncTask::ExecuteCallback execute = [moduleName, bundleName, contextImpl,
148 moduleContext, inputContextPtr]() {
149 if (bundleName.empty()) {
150 *moduleContext = contextImpl->CreateModuleContext(moduleName, inputContextPtr);
151 } else {
152 *moduleContext = contextImpl->CreateModuleContext(bundleName, moduleName, inputContextPtr);
153 }
154 };
155
156 NapiAsyncTask::CompleteCallback complete;
157 SetCreateCompleteCallback(moduleContext, complete);
158
159 napi_value result = nullptr;
160 NapiAsyncTask::ScheduleHighQos("JsApplication::OnCreateModuleContext",
161 env, CreateAsyncTaskWithLastParam(env, nullptr, std::move(execute), std::move(complete), &result));
162
163 return result;
164 }
165
CheckCallerIsSystemApp()166 bool JsApplication::CheckCallerIsSystemApp()
167 {
168 auto selfToken = IPCSkeleton::GetSelfTokenID();
169 if (!Security::AccessToken::TokenIdKit::IsSystemAppByFullTokenID(selfToken)) {
170 return false;
171 }
172 return true;
173 }
174
CheckCallerPermission(const std::string & permission)175 bool JsApplication::CheckCallerPermission(const std::string &permission)
176 {
177 auto selfToken = IPCSkeleton::GetSelfTokenID();
178 int ret = Security::AccessToken::AccessTokenKit::VerifyAccessToken(selfToken, permission);
179 if (ret != Security::AccessToken::PermissionState::PERMISSION_GRANTED) {
180 return false;
181 }
182 return true;
183 }
184
185
OnCreateBundleContext(napi_env env,NapiCallbackInfo & info)186 napi_value JsApplication::OnCreateBundleContext(napi_env env, NapiCallbackInfo &info)
187 {
188 TAG_LOGD(AAFwkTag::APPKIT, "Called");
189 if (!CheckCallerIsSystemApp()) {
190 TAG_LOGE(AAFwkTag::APPKIT, "no system app");
191 ThrowNotSystemAppError(env);
192 return CreateJsUndefined(env);
193 }
194
195 if (info.argc < ARGC_TWO) {
196 TAG_LOGE(AAFwkTag::APPKIT, "invalid argc");
197 ThrowTooFewParametersError(env);
198 return CreateJsUndefined(env);
199 }
200
201 if (!CheckCallerPermission(PERMISSION_GET_BUNDLE_INFO)) {
202 TAG_LOGE(AAFwkTag::APPKIT, "no permission");
203 ThrowNoPermissionError(env, PERMISSION_GET_BUNDLE_INFO);
204 return CreateJsUndefined(env);
205 }
206
207 bool stageMode = false;
208 napi_status status = OHOS::AbilityRuntime::IsStageContext(env, info.argv[ARGC_ZERO], stageMode);
209 if (status != napi_ok || !stageMode) {
210 TAG_LOGE(AAFwkTag::APPKIT, "not stageMode");
211 ThrowInvalidParamError(env, "Parse param context failed, must be a context of stageMode.");
212 return CreateJsUndefined(env);
213 }
214
215 auto context = OHOS::AbilityRuntime::GetStageModeContext(env, info.argv[ARGC_ZERO]);
216 if (context == nullptr) {
217 TAG_LOGE(AAFwkTag::APPKIT, "null context");
218 ThrowInvalidParamError(env, "Parse param context failed, must not be nullptr.");
219 return CreateJsUndefined(env);
220 }
221
222 auto inputContextPtr = Context::ConvertTo<Context>(context);
223 if (inputContextPtr == nullptr) {
224 TAG_LOGE(AAFwkTag::APPKIT, "Convert to context failed");
225 ThrowInvalidParamError(env, "Parse param context failed, must be a context.");
226 return CreateJsUndefined(env);
227 }
228
229 std::string bundleName;
230 if (!ConvertFromJsValue(env, info.argv[ARGC_ONE], bundleName)) {
231 TAG_LOGE(AAFwkTag::APPKIT, "Parse bundleName failed");
232 ThrowInvalidParamError(env, "Parse param bundleName failed, bundleName must be string.");
233 return CreateJsUndefined(env);
234 }
235
236 auto bundleContext = std::make_shared<std::shared_ptr<Context>>();
237 std::shared_ptr<ContextImpl> contextImpl = std::make_shared<ContextImpl>();
238 contextImpl->SetProcessName(context->GetProcessName());
239
240 NapiAsyncTask::ExecuteCallback execute = [bundleName, contextImpl,
241 bundleContext, inputContextPtr]() {
242 contextImpl->CreateBundleContext(*bundleContext, bundleName, inputContextPtr);
243 };
244
245 NapiAsyncTask::CompleteCallback complete;
246 SetCreateCompleteCallback(bundleContext, complete);
247
248 napi_value result = nullptr;
249 NapiAsyncTask::ScheduleHighQos("JsApplication::OnCreateBundleContext",
250 env, CreateAsyncTaskWithLastParam(env, nullptr, std::move(execute), std::move(complete), &result));
251
252 return result;
253 }
254
SetCreateCompleteCallback(std::shared_ptr<std::shared_ptr<Context>> contextPtr,NapiAsyncTask::CompleteCallback & complete)255 void JsApplication::SetCreateCompleteCallback(std::shared_ptr<std::shared_ptr<Context>> contextPtr,
256 NapiAsyncTask::CompleteCallback &complete)
257 {
258 TAG_LOGD(AAFwkTag::APPKIT, "Called");
259 complete = [contextPtr](napi_env env, NapiAsyncTask &task, int32_t status) {
260 auto context = *contextPtr;
261 if (!context) {
262 TAG_LOGE(AAFwkTag::APPKIT, "failed to create context");
263 task.Reject(env, CreateJsError(env,
264 static_cast<int32_t>(AbilityErrorCode::ERROR_CODE_INVALID_PARAM), "invalid param."));
265 return;
266 }
267 napi_value value = CreateJsBaseContext(env, context, true);
268 auto systemModule = JsRuntime::LoadSystemModuleByEngine(env, "application.Context", &value, 1);
269 if (systemModule == nullptr) {
270 TAG_LOGW(AAFwkTag::APPKIT, "invalid systemModule");
271 task.Reject(env, CreateJsError(env,
272 static_cast<int32_t>(AbilityErrorCode::ERROR_CODE_INVALID_PARAM), "invalid param."));
273 return;
274 }
275
276 napi_value object = systemModule->GetNapiValue();
277 if (!CheckTypeForNapiValue(env, object, napi_object)) {
278 TAG_LOGE(AAFwkTag::APPKIT, "Failed to get object");
279 task.Reject(env, CreateJsError(env,
280 static_cast<int32_t>(AbilityErrorCode::ERROR_CODE_INVALID_PARAM), "invalid param."));
281 return;
282 }
283
284 auto workContext = new (std::nothrow) std::weak_ptr<Context>(context);
285 napi_coerce_to_native_binding_object(env, object, DetachCallbackFunc, AttachBaseContext, workContext, nullptr);
286 napi_status ret = napi_wrap(env, object, workContext,
287 [](napi_env, void *data, void *) {
288 TAG_LOGD(AAFwkTag::APPKIT, "Finalizer for weak_ptr module context is called");
289 delete static_cast<std::weak_ptr<Context> *>(data);
290 },
291 nullptr, nullptr);
292 if (ret != napi_ok && workContext != nullptr) {
293 TAG_LOGE(AAFwkTag::APPKIT, "napi_wrap Failed: %{public}d", ret);
294 delete workContext;
295 return;
296 }
297 task.ResolveWithNoError(env, object);
298 };
299 }
300
CreateJsContext(napi_env env,const std::shared_ptr<Context> & context)301 napi_value JsApplication::CreateJsContext(napi_env env, const std::shared_ptr<Context> &context)
302 {
303 napi_value value = CreateJsBaseContext(env, context, true);
304 auto systemModule = JsRuntime::LoadSystemModuleByEngine(env, "application.Context", &value, 1);
305 if (systemModule == nullptr) {
306 TAG_LOGW(AAFwkTag::APPKIT, "invalid systemModule");
307 ThrowInvalidParamError(env, "invalid param.");
308 return CreateJsUndefined(env);
309 }
310 napi_value object = systemModule->GetNapiValue();
311 if (!CheckTypeForNapiValue(env, object, napi_object)) {
312 TAG_LOGE(AAFwkTag::APPKIT, "Failed to get object");
313 ThrowInvalidParamError(env, "invalid param.");
314 return CreateJsUndefined(env);
315 }
316
317 auto workContext = new (std::nothrow) std::weak_ptr<Context>(context);
318 napi_coerce_to_native_binding_object(env, object, DetachCallbackFunc, AttachBaseContext, workContext, nullptr);
319 napi_status status = napi_wrap(env, object, workContext,
320 [](napi_env, void *data, void *) {
321 TAG_LOGD(AAFwkTag::APPKIT, "Finalizer for weak_ptr module context is called");
322 delete static_cast<std::weak_ptr<Context> *>(data);
323 },
324 nullptr, nullptr);
325 if (status != napi_ok && workContext != nullptr) {
326 TAG_LOGE(AAFwkTag::APPKIT, "napi_wrap Failed: %{public}d", status);
327 delete workContext;
328 ThrowInvalidParamError(env, "invalid param.");
329 return CreateJsUndefined(env);
330 }
331
332 return object;
333 }
334
ApplicationInit(napi_env env,napi_value exportObj)335 napi_value ApplicationInit(napi_env env, napi_value exportObj)
336 {
337 TAG_LOGD(AAFwkTag::APPKIT, "Called");
338 if (env == nullptr || exportObj == nullptr) {
339 TAG_LOGE(AAFwkTag::APPKIT, "null env or exportObj");
340 return nullptr;
341 }
342
343 auto jsApplication = std::make_unique<JsApplication>();
344 napi_wrap(env, exportObj, jsApplication.release(), JsApplication::Finalizer, nullptr, nullptr);
345
346 const char *moduleName = "application";
347 BindNativeFunction(env, exportObj, "getApplicationContext", moduleName,
348 JsApplication::GetApplicationContext);
349
350 BindNativeFunction(env, exportObj, "createModuleContext", moduleName,
351 JsApplication::CreateModuleContext);
352
353 BindNativeFunction(env, exportObj, "createBundleContext", moduleName,
354 JsApplication::CreateBundleContext);
355 return CreateJsUndefined(env);
356 }
357 } // namespace AbilityRuntime
358 } // namespace OHOS