/* * Copyright (c) 2024 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "js_application.h" #include "ability_runtime_error_util.h" #include "accesstoken_kit.h" #include "context_impl.h" #include "hilog_tag_wrapper.h" #include "js_application_context_utils.h" #include "js_error_utils.h" #include "js_runtime_utils.h" #include "js_context_utils.h" #include "napi_base_context.h" namespace OHOS { namespace AbilityRuntime { namespace { constexpr size_t ARGC_ZERO = 0; constexpr size_t ARGC_ONE = 1; constexpr size_t ARGC_TWO = 2; constexpr const char* PERMISSION_GET_BUNDLE_INFO = "ohos.permission.GET_BUNDLE_INFO_PRIVILEGED"; } void JsApplication::Finalizer(napi_env env, void *data, void *hint) { TAG_LOGD(AAFwkTag::APPKIT, "Called."); std::unique_ptr<JsApplication>(static_cast<JsApplication *>(data)); } napi_value JsApplication::GetApplicationContext(napi_env env, napi_callback_info info) { GET_NAPI_INFO_AND_CALL(env, info, JsApplication, OnGetApplicationContext); } napi_value JsApplication::OnGetApplicationContext(napi_env env, NapiCallbackInfo &info) { TAG_LOGD(AAFwkTag::APPKIT, "Called."); napi_value value = JsApplicationContextUtils::CreateJsApplicationContext(env); auto systemModule = JsRuntime::LoadSystemModuleByEngine(env, "application.ApplicationContext", &value, 1); if (systemModule == nullptr) { TAG_LOGE(AAFwkTag::APPKIT, "Invalid systemModule."); AbilityRuntimeErrorUtil::Throw(env, ERR_ABILITY_RUNTIME_EXTERNAL_INTERNAL_ERROR); return CreateJsUndefined(env); } napi_value object = systemModule->GetNapiValue(); if (!CheckTypeForNapiValue(env, object, napi_object)) { TAG_LOGE(AAFwkTag::APPKIT, "Failed to get context native object."); AbilityRuntimeErrorUtil::Throw(env, ERR_ABILITY_RUNTIME_EXTERNAL_INTERNAL_ERROR); return CreateJsUndefined(env); } return object; } napi_value JsApplication::CreateModuleContext(napi_env env, napi_callback_info info) { GET_NAPI_INFO_AND_CALL(env, info, JsApplication, OnCreateModuleContext); } napi_value JsApplication::CreateBundleContext(napi_env env, napi_callback_info info) { GET_NAPI_INFO_AND_CALL(env, info, JsApplication, OnCreateBundleContext); } napi_value JsApplication::OnCreateModuleContext(napi_env env, NapiCallbackInfo &info) { TAG_LOGD(AAFwkTag::APPKIT, "Called"); if (info.argc < ARGC_TWO) { TAG_LOGE(AAFwkTag::APPKIT, "invalid argc"); ThrowTooFewParametersError(env); return CreateJsUndefined(env); } bool stageMode = false; napi_status status = OHOS::AbilityRuntime::IsStageContext(env, info.argv[ARGC_ZERO], stageMode); if (status != napi_ok || !stageMode) { TAG_LOGE(AAFwkTag::APPKIT, "not stageMode"); ThrowInvalidParamError(env, "Parse param context failed, must be a context of stageMode."); return CreateJsUndefined(env); } auto context = OHOS::AbilityRuntime::GetStageModeContext(env, info.argv[ARGC_ZERO]); if (context == nullptr) { TAG_LOGE(AAFwkTag::APPKIT, "null context"); ThrowInvalidParamError(env, "Parse param context failed, must not be nullptr."); return CreateJsUndefined(env); } auto inputContextPtr = Context::ConvertTo<Context>(context); if (inputContextPtr == nullptr) { TAG_LOGE(AAFwkTag::APPKIT, "Convert to context failed"); ThrowInvalidParamError(env, "Parse param context failed, must be a context."); return CreateJsUndefined(env); } std::shared_ptr<std::shared_ptr<Context>> moduleContext = std::make_shared<std::shared_ptr<Context>>(); std::shared_ptr<ContextImpl> contextImpl = std::make_shared<ContextImpl>(); if (contextImpl == nullptr) { TAG_LOGE(AAFwkTag::APPKIT, "null contextImpl"); ThrowInvalidParamError(env, "create context failed."); return CreateJsUndefined(env); } std::string moduleName = ""; std::string bundleName = ""; if (info.argc == ARGC_TWO) { TAG_LOGD(AAFwkTag::APPKIT, "Called"); if (!ConvertFromJsValue(env, info.argv[ARGC_ONE], moduleName)) { TAG_LOGE(AAFwkTag::APPKIT, "Parse failed"); ThrowInvalidParamError(env, "Parse param moduleName failed, moduleName must be string."); return CreateJsUndefined(env); } } else { TAG_LOGD(AAFwkTag::APPKIT, "Called"); if (!CheckCallerIsSystemApp()) { TAG_LOGE(AAFwkTag::APPKIT, "no system app"); ThrowNotSystemAppError(env); return CreateJsUndefined(env); } if (!CheckCallerPermission(PERMISSION_GET_BUNDLE_INFO)) { TAG_LOGE(AAFwkTag::APPKIT, "no permission"); ThrowNoPermissionError(env, PERMISSION_GET_BUNDLE_INFO); return CreateJsUndefined(env); } if (!ConvertFromJsValue(env, info.argv[ARGC_TWO], moduleName) || !ConvertFromJsValue(env, info.argv[ARGC_ONE], bundleName)) { TAG_LOGE(AAFwkTag::APPKIT, "Parse failed"); ThrowInvalidParamError(env, "Parse param failed, moduleName and bundleName must be string."); return CreateJsUndefined(env); } } TAG_LOGD(AAFwkTag::APPKIT, "moduleName: %{public}s, bundlename: %{public}s", moduleName.c_str(), bundleName.c_str()); NapiAsyncTask::ExecuteCallback execute = [moduleName, bundleName, contextImpl, moduleContext, inputContextPtr]() { if (bundleName.empty()) { *moduleContext = contextImpl->CreateModuleContext(moduleName, inputContextPtr); } else { *moduleContext = contextImpl->CreateModuleContext(bundleName, moduleName, inputContextPtr); } }; NapiAsyncTask::CompleteCallback complete; SetCreateCompleteCallback(moduleContext, complete); napi_value result = nullptr; NapiAsyncTask::ScheduleHighQos("JsApplication::OnCreateModuleContext", env, CreateAsyncTaskWithLastParam(env, nullptr, std::move(execute), std::move(complete), &result)); return result; } bool JsApplication::CheckCallerIsSystemApp() { auto selfToken = IPCSkeleton::GetSelfTokenID(); if (!Security::AccessToken::TokenIdKit::IsSystemAppByFullTokenID(selfToken)) { return false; } return true; } bool JsApplication::CheckCallerPermission(const std::string &permission) { auto selfToken = IPCSkeleton::GetSelfTokenID(); int ret = Security::AccessToken::AccessTokenKit::VerifyAccessToken(selfToken, permission); if (ret != Security::AccessToken::PermissionState::PERMISSION_GRANTED) { return false; } return true; } napi_value JsApplication::OnCreateBundleContext(napi_env env, NapiCallbackInfo &info) { TAG_LOGD(AAFwkTag::APPKIT, "Called"); if (!CheckCallerIsSystemApp()) { TAG_LOGE(AAFwkTag::APPKIT, "no system app"); ThrowNotSystemAppError(env); return CreateJsUndefined(env); } if (info.argc < ARGC_TWO) { TAG_LOGE(AAFwkTag::APPKIT, "invalid argc"); ThrowTooFewParametersError(env); return CreateJsUndefined(env); } if (!CheckCallerPermission(PERMISSION_GET_BUNDLE_INFO)) { TAG_LOGE(AAFwkTag::APPKIT, "no permission"); ThrowNoPermissionError(env, PERMISSION_GET_BUNDLE_INFO); return CreateJsUndefined(env); } bool stageMode = false; napi_status status = OHOS::AbilityRuntime::IsStageContext(env, info.argv[ARGC_ZERO], stageMode); if (status != napi_ok || !stageMode) { TAG_LOGE(AAFwkTag::APPKIT, "not stageMode"); ThrowInvalidParamError(env, "Parse param context failed, must be a context of stageMode."); return CreateJsUndefined(env); } auto context = OHOS::AbilityRuntime::GetStageModeContext(env, info.argv[ARGC_ZERO]); if (context == nullptr) { TAG_LOGE(AAFwkTag::APPKIT, "null context"); ThrowInvalidParamError(env, "Parse param context failed, must not be nullptr."); return CreateJsUndefined(env); } auto inputContextPtr = Context::ConvertTo<Context>(context); if (inputContextPtr == nullptr) { TAG_LOGE(AAFwkTag::APPKIT, "Convert to context failed"); ThrowInvalidParamError(env, "Parse param context failed, must be a context."); return CreateJsUndefined(env); } std::string bundleName; if (!ConvertFromJsValue(env, info.argv[ARGC_ONE], bundleName)) { TAG_LOGE(AAFwkTag::APPKIT, "Parse bundleName failed"); ThrowInvalidParamError(env, "Parse param bundleName failed, bundleName must be string."); return CreateJsUndefined(env); } auto bundleContext = std::make_shared<std::shared_ptr<Context>>(); std::shared_ptr<ContextImpl> contextImpl = std::make_shared<ContextImpl>(); NapiAsyncTask::ExecuteCallback execute = [bundleName, contextImpl, bundleContext, inputContextPtr]() { contextImpl->CreateBundleContext(*bundleContext, bundleName, inputContextPtr); }; NapiAsyncTask::CompleteCallback complete; SetCreateCompleteCallback(bundleContext, complete); napi_value result = nullptr; NapiAsyncTask::ScheduleHighQos("JsApplication::OnCreateBundleContext", env, CreateAsyncTaskWithLastParam(env, nullptr, std::move(execute), std::move(complete), &result)); return result; } void JsApplication::SetCreateCompleteCallback(std::shared_ptr<std::shared_ptr<Context>> contextPtr, NapiAsyncTask::CompleteCallback &complete) { TAG_LOGD(AAFwkTag::APPKIT, "Called"); complete = [contextPtr](napi_env env, NapiAsyncTask &task, int32_t status) { auto context = *contextPtr; if (!context) { TAG_LOGE(AAFwkTag::APPKIT, "failed to create context"); task.Reject(env, CreateJsError(env, static_cast<int32_t>(AbilityErrorCode::ERROR_CODE_INVALID_PARAM), "invalid param.")); return; } napi_value value = CreateJsBaseContext(env, context, true); auto systemModule = JsRuntime::LoadSystemModuleByEngine(env, "application.Context", &value, 1); if (systemModule == nullptr) { TAG_LOGW(AAFwkTag::APPKIT, "invalid systemModule"); task.Reject(env, CreateJsError(env, static_cast<int32_t>(AbilityErrorCode::ERROR_CODE_INVALID_PARAM), "invalid param.")); return; } napi_value object = systemModule->GetNapiValue(); if (!CheckTypeForNapiValue(env, object, napi_object)) { TAG_LOGE(AAFwkTag::APPKIT, "Failed to get object"); task.Reject(env, CreateJsError(env, static_cast<int32_t>(AbilityErrorCode::ERROR_CODE_INVALID_PARAM), "invalid param.")); return; } auto workContext = new (std::nothrow) std::weak_ptr<Context>(context); napi_coerce_to_native_binding_object(env, object, DetachCallbackFunc, AttachBaseContext, workContext, nullptr); napi_status ret = napi_wrap(env, object, workContext, [](napi_env, void *data, void *) { TAG_LOGD(AAFwkTag::APPKIT, "Finalizer for weak_ptr module context is called"); delete static_cast<std::weak_ptr<Context> *>(data); }, nullptr, nullptr); if (ret != napi_ok && workContext != nullptr) { TAG_LOGE(AAFwkTag::APPKIT, "napi_wrap Failed: %{public}d", ret); delete workContext; return; } task.ResolveWithNoError(env, object); }; } napi_value JsApplication::CreateJsContext(napi_env env, const std::shared_ptr<Context> &context) { napi_value value = CreateJsBaseContext(env, context, true); auto systemModule = JsRuntime::LoadSystemModuleByEngine(env, "application.Context", &value, 1); if (systemModule == nullptr) { TAG_LOGW(AAFwkTag::APPKIT, "invalid systemModule"); ThrowInvalidParamError(env, "invalid param."); return CreateJsUndefined(env); } napi_value object = systemModule->GetNapiValue(); if (!CheckTypeForNapiValue(env, object, napi_object)) { TAG_LOGE(AAFwkTag::APPKIT, "Failed to get object"); ThrowInvalidParamError(env, "invalid param."); return CreateJsUndefined(env); } auto workContext = new (std::nothrow) std::weak_ptr<Context>(context); napi_coerce_to_native_binding_object(env, object, DetachCallbackFunc, AttachBaseContext, workContext, nullptr); napi_status status = napi_wrap(env, object, workContext, [](napi_env, void *data, void *) { TAG_LOGD(AAFwkTag::APPKIT, "Finalizer for weak_ptr module context is called"); delete static_cast<std::weak_ptr<Context> *>(data); }, nullptr, nullptr); if (status != napi_ok && workContext != nullptr) { TAG_LOGE(AAFwkTag::APPKIT, "napi_wrap Failed: %{public}d", status); delete workContext; ThrowInvalidParamError(env, "invalid param."); return CreateJsUndefined(env); } return object; } napi_value ApplicationInit(napi_env env, napi_value exportObj) { TAG_LOGD(AAFwkTag::APPKIT, "Called"); if (env == nullptr || exportObj == nullptr) { TAG_LOGE(AAFwkTag::APPKIT, "Env or exportObj is nullptr."); return nullptr; } auto jsApplication = std::make_unique<JsApplication>(); napi_wrap(env, exportObj, jsApplication.release(), JsApplication::Finalizer, nullptr, nullptr); const char *moduleName = "application"; BindNativeFunction(env, exportObj, "getApplicationContext", moduleName, JsApplication::GetApplicationContext); BindNativeFunction(env, exportObj, "createModuleContext", moduleName, JsApplication::CreateModuleContext); BindNativeFunction(env, exportObj, "createBundleContext", moduleName, JsApplication::CreateBundleContext); return CreateJsUndefined(env); } } // namespace AbilityRuntime } // namespace OHOS