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