* Copyright (c) 2025 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 "sts_print_extension.h"
#include "print_manager_client.h"
#include "print_log.h"
#include "print_utils.h"
#include "common_fun_ani.h"
#include "ohos_application.h"
#include "print_extension_context.h"
#include "print_base_ani_util.h"
#include "ani_print_job_helper.h"
namespace OHOS {
namespace AbilityRuntime {
using namespace OHOS::AppExecFwk;
using namespace OHOS::Print;
StsPrintExtension *StsPrintExtension::stsExtension_ = nullptr;
StsPrintExtension* StsPrintExtension::Create(const std::unique_ptr<Runtime>& runtime)
{
PRINT_HILOGD("StsPrintExtension begin Create");
stsExtension_ = new StsPrintExtension(static_cast<ETSRuntime&>(*runtime));
return stsExtension_;
}
StsPrintExtension::StsPrintExtension(ETSRuntime &etsRuntime) : etsRuntime_(etsRuntime) {}
StsPrintExtension::~StsPrintExtension()
{
auto context = GetContext();
if (context) {
context->Unbind();
}
stsExtension_ = nullptr;
}
void StsPrintExtension::Init(const std::shared_ptr<AbilityLocalRecord> &record,
const std::shared_ptr<OHOSApplication> &application,
std::shared_ptr<AbilityHandler> &handler,
const sptr<IRemoteObject> &token)
{
PRINT_HILOGI("PrintExtension Init begin");
if (record == nullptr) {
PRINT_HILOGE("record null");
return;
}
PrintExtension::Init(record, application, handler, token);
if (Extension::abilityInfo_ == nullptr || Extension::abilityInfo_->srcEntrance.empty()) {
PRINT_HILOGE("StsPrintExtension Init abilityInfo error");
return;
}
if (record != nullptr) {
token_ = record->GetToken();
}
std::string srcPath(Extension::abilityInfo_->moduleName + "/");
srcPath.append(Extension::abilityInfo_->srcEntrance);
auto pos = srcPath.rfind(".");
if (pos != std::string::npos) {
srcPath.erase(pos);
srcPath.append(".abc");
}
std::string moduleName(Extension::abilityInfo_->moduleName);
moduleName.append("::").append(abilityInfo_->name);
PRINT_HILOGD("srcPath: %{public}s, moduleName:%{public}s", srcPath.c_str(), moduleName.c_str());
etsObj_ = etsRuntime_.LoadModule(
moduleName, srcPath, abilityInfo_->hapPath,
abilityInfo_->compileMode == AppExecFwk::CompileMode::ES_MODULE, false, abilityInfo_->srcEntrance);
if (etsObj_ == nullptr) {
PRINT_HILOGE("etsObj_ null");
return;
}
auto env = etsRuntime_.GetAniEnv();
if (env == nullptr) {
PRINT_HILOGE("null env");
return;
}
BindContext(env, record->GetWant(), application);
PRINT_HILOGI("PrintExtension Init end");
}
void StsPrintExtension::ResetEnv(ani_env* env)
{
if (env == nullptr) {
PRINT_HILOGE("null env");
return;
}
env->DescribeError();
env->ResetError();
}
void StsPrintExtension::OnStart(const AAFwk::Want &want)
{
PRINT_HILOGI("PrintExtension begin OnStart");
auto env = etsRuntime_.GetAniEnv();
if (!env) {
PRINT_HILOGE("env not found Ability.sts");
return;
}
const char *signature =
"C{@ohos.app.ability.Want.Want}:";
bool ret = CallObjectMethod(false, "onCreate", signature, nullptr);
if (!ret) {
PRINT_HILOGE("CallObjectMethod onCreate failed, stop extension");
return;
}
PrintManagerClient::GetInstance().LoadExtSuccess(extensionId_);
RegisterCb();
PRINT_HILOGD("%{public}s end.", __func__);
}
void StsPrintExtension::OnStop()
{
PRINT_HILOGI("PrintExtension begin OnStop");
CallObjectMethod(false, "onDestroy", nullptr);
OnStopCallBack();
}
sptr<IRemoteObject> StsPrintExtension::OnConnect(const AAFwk::Want &want)
{
PRINT_HILOGI("PrintExtension begin OnConnect");
sptr<IRemoteObject> remoteObj = nullptr;
return remoteObj;
}
void StsPrintExtension::OnDisconnect(const AAFwk::Want &want)
{
PRINT_HILOGI("PrintExtension begin OnDisconnect");
Extension::OnDisconnect(want);
}
void StsPrintExtension::OnCommand(const AAFwk::Want &want, bool restart, int startId)
{
PRINT_HILOGI("PrintExtension begin OnCommand");
}
bool StsPrintExtension::CallObjectMethod(bool withResult, const char *name, const char *signature, ...)
{
if (etsObj_ == nullptr) {
PRINT_HILOGE("etsObj_ nullptr");
return false;
}
auto env = etsRuntime_.GetAniEnv();
ResetEnv(env);
if (env == nullptr) {
PRINT_HILOGE("env nullptr");
return false;
}
ani_status status = ANI_OK;
ani_method method = nullptr;
if ((status = env->Class_FindMethod(etsObj_->aniCls, name, signature, &method)) != ANI_OK) {
PRINT_HILOGE("status: %{public}d", status);
return false;
}
PRINT_CALL_BASE(env, env->ResetError(), false);
if (withResult) {
ani_boolean res = 0;
va_list args;
va_start(args, signature);
if ((status = env->Object_CallMethod_Boolean(etsObj_->aniObj, method, &res, args)) != ANI_OK) {
PRINT_HILOGE("status: %{public}d", status);
etsRuntime_.HandleUncaughtError();
}
va_end(args);
return res;
}
va_list args;
va_start(args, signature);
if ((status = env->Object_CallMethod_Void_V(etsObj_->aniObj, method, args)) != ANI_OK) {
PRINT_HILOGE("status: %{public}d", status);
va_end(args);
etsRuntime_.HandleUncaughtError();
return false;
}
va_end(args);
return false;
}
void StsPrintExtension::BindExtensionInfo(ani_env* aniEnv, ani_class contextClass, ani_object contextObj,
std::shared_ptr<OHOS::AbilityRuntime::Context> context, std::shared_ptr<OHOS::AppExecFwk::AbilityInfo> abilityInfo)
{
PRINT_HILOGI("BindExtensionInfo enter");
auto hapModuleInfo = context->GetHapModuleInfo();
ani_status status = ANI_OK;
if (abilityInfo && hapModuleInfo) {
auto isExist = [&abilityInfo](const OHOS::AppExecFwk::ExtensionAbilityInfo& info) {
PRINT_HILOGE("%{public}s, %{public}s", info.bundleName.c_str(), info.name.c_str());
return info.bundleName == abilityInfo->bundleName && info.name == abilityInfo->name;
};
auto infoIter = std::find_if(
hapModuleInfo->extensionInfos.begin(), hapModuleInfo->extensionInfos.end(), isExist);
if (infoIter == hapModuleInfo->extensionInfos.end()) {
PRINT_HILOGE("set extensionAbilityInfo fail");
return;
}
ani_field extensionAbilityInfoField;
status = aniEnv->Class_FindField(contextClass, "extensionAbilityInfo", &extensionAbilityInfoField);
if (status != ANI_OK) {
PRINT_HILOGE("status: %{public}d", status);
return;
}
ani_object extAbilityInfoObj = OHOS::AppExecFwk::CommonFunAni::ConvertExtensionInfo(aniEnv, *infoIter);
status = aniEnv->Object_SetField_Ref(contextObj, extensionAbilityInfoField,
reinterpret_cast<ani_ref>(extAbilityInfoObj));
if (status != ANI_OK) {
PRINT_HILOGE("status: %{public}d", status);
return;
}
}
}
ani_object StsPrintExtension::CreateStsPrintExtensionContext(ani_env *env,
std::shared_ptr<PrintExtensionContext> context,
const std::shared_ptr<OHOS::AppExecFwk::OHOSApplication> &application)
{
PRINT_HILOGI("PrintExtension begin CreateStsPrintExtensionContext");
ani_class cls = nullptr;
ani_status status = ANI_ERROR;
ani_method method = nullptr;
ani_object contextObj = nullptr;
if ((env->FindClass("PrintExtensionContext.PrintExtensionContext", &cls)) != ANI_OK) {
PRINT_HILOGE("status: %{public}d", status);
return nullptr;
}
if ((status = env->Class_FindMethod(cls, "<ctor>", ":", &method)) != ANI_OK) {
PRINT_HILOGE("status: %{public}d", status);
return nullptr;
}
if ((status = env->Object_New(cls, method, &contextObj)) != ANI_OK) {
PRINT_HILOGE("status: %{public}d", status);
return nullptr;
}
if (application == nullptr) {
PRINT_HILOGE("application null");
return nullptr;
}
BindExtensionInfo(env, cls, contextObj, context, context->GetAbilityInfo());
return contextObj;
}
ani_object StsPrintExtension::CreateSTSContext(ani_env* env, std::shared_ptr<PrintExtensionContext> context,
int32_t screenMode, const std::shared_ptr<OHOSApplication> &application)
{
ani_object obj = CreateStsPrintExtensionContext(env, context, application);
return obj;
}
void StsPrintExtension::BindContext(ani_env*env, std::shared_ptr<AAFwk::Want> want,
const std::shared_ptr<OHOSApplication> &application)
{
if (env == nullptr || want == nullptr) {
PRINT_HILOGE("Want info is null or env is null");
return;
}
auto context = GetContext();
if (context == nullptr) {
PRINT_HILOGE("context null");
return;
}
int32_t screenMode = want->GetIntParam(AAFwk::SCREEN_MODE_KEY, AAFwk::IDLE_SCREEN_MODE);
ani_object contextObj = CreateSTSContext(env, context, screenMode, application);
if (contextObj == nullptr) {
PRINT_HILOGE("null contextObj");
return;
}
ani_field contextField;
auto status = env->Class_FindField(etsObj_->aniCls, "context", &contextField);
if (status != ANI_OK) {
PRINT_HILOGE("status: %{public}d", status);
ResetEnv(env);
return;
}
ani_ref contextRef = nullptr;
if ((status = env->GlobalReference_Create(contextObj, &contextRef)) != ANI_OK) {
PRINT_HILOGE("status: %{public}d", status);
return;
}
if ((status = env->Object_SetField_Ref(etsObj_->aniObj, contextField, contextRef)) != ANI_OK) {
PRINT_HILOGE("status: %{public}d", status);
ani_status delStatus = env->GlobalReference_Delete(contextRef);
if (delStatus != ANI_OK) {
PRINT_HILOGW("GlobalReference_Delete failed, status: %{public}d", delStatus);
}
ResetEnv(env);
}
}
bool StsPrintExtension::Callback(const std::string &funcName)
{
PRINT_HILOGD("call %{public}s", funcName.c_str());
if (etsObj_ == nullptr) {
PRINT_HILOGE("etsObj_ is null");
return false;
}
return CallObjectMethod(false, funcName.c_str(), nullptr);
}
bool StsPrintExtension::Callback(const std::string &funcName, const std::string &printerId)
{
PRINT_HILOGD("call %{public}s", funcName.c_str());
if (etsObj_ == nullptr) {
PRINT_HILOGE("etsObj_ is null");
return false;
}
auto env = etsRuntime_.GetAniEnv();
if (env == nullptr) {
PRINT_HILOGE("env is null");
return false;
}
ani_string stsPrinterId = CreateAniString(env, printerId);
PRINT_CHECK_NULL_AND_RETURN(stsPrinterId, false);
return CallObjectMethod(false, funcName.c_str(), "C{@ohos.lang.String}:", stsPrinterId);
}
bool StsPrintExtension::Callback(const std::string &funcName, const Print::PrintJob &job)
{
PRINT_HILOGD("call %{public}s", funcName.c_str());
if (etsObj_ == nullptr) {
PRINT_HILOGE("etsObj_ is null");
return false;
}
auto env = etsRuntime_.GetAniEnv();
if (env == nullptr) {
PRINT_HILOGE("env is null");
return false;
}
ani_object stsJob = AniPrintJobHelper::CreatePrintJob(env, job);
return CallObjectMethod(false, funcName.c_str(), "C{@ohos.print.PrintJob}:", stsJob);
}
void StsPrintExtension::RegisterCb()
{
RegisterDiscoveryCb();
RegisterConnectionCb();
RegisterPrintJobCb();
RegisterPreviewCb();
RegisterQueryCapCb();
RegisterExtensionCb();
}
void StsPrintExtension::RegisterDiscoveryCb()
{
PRINT_HILOGD("Register Print Extension Callback");
PrintManagerClient::GetInstance().RegisterExtCallback(extensionId_, PRINT_EXTCB_START_DISCOVERY,
[]() -> bool {
if (StsPrintExtension::stsExtension_ == nullptr) {
PRINT_HILOGE("stsExtension_ is null");
return false;
}
return StsPrintExtension::stsExtension_->Callback("onStartDiscoverPrinter");
});
PrintManagerClient::GetInstance().RegisterExtCallback(extensionId_, PRINT_EXTCB_STOP_DISCOVERY,
[]() -> bool {
if (StsPrintExtension::stsExtension_ == nullptr) {
PRINT_HILOGE("stsExtension_ is null");
return false;
}
return StsPrintExtension::stsExtension_->Callback("onStopDiscoverPrinter");
});
}
void StsPrintExtension::RegisterConnectionCb()
{
PrintManagerClient::GetInstance().RegisterExtCallback(extensionId_, PRINT_EXTCB_CONNECT_PRINTER,
[](const std::string &printId) -> bool {
if (StsPrintExtension::stsExtension_ == nullptr) {
PRINT_HILOGE("stsExtension_ is null");
return false;
}
std::string realPrinterId = PrintUtils::GetLocalId(printId, stsExtension_->extensionId_);
return StsPrintExtension::stsExtension_->Callback("onConnectPrinter", realPrinterId);
});
PrintManagerClient::GetInstance().RegisterExtCallback(extensionId_, PRINT_EXTCB_DISCONNECT_PRINTER,
[](const std::string &printId) -> bool {
if (StsPrintExtension::stsExtension_ == nullptr) {
PRINT_HILOGE("stsExtension_ is null");
return false;
}
std::string realPrinterId = PrintUtils::GetLocalId(printId, stsExtension_->extensionId_);
return StsPrintExtension::stsExtension_->Callback("onDisconnectPrinter", realPrinterId);
});
}
void StsPrintExtension::RegisterPrintJobCb()
{
PrintManagerClient::GetInstance().RegisterExtCallback(extensionId_, PRINT_EXTCB_START_PRINT,
[](const PrintJob &job) -> bool {
if (StsPrintExtension::stsExtension_ == nullptr) {
PRINT_HILOGE("stsExtension_ is null");
return false;
}
return StsPrintExtension::stsExtension_->Callback("onStartPrintJob", job);
});
PrintManagerClient::GetInstance().RegisterExtCallback(extensionId_, PRINT_EXTCB_CANCEL_PRINT,
[](const PrintJob &job) -> bool {
if (StsPrintExtension::stsExtension_ == nullptr) {
PRINT_HILOGE("stsExtension_ is null");
return false;
}
return StsPrintExtension::stsExtension_->Callback("onCancelPrintJob", job);
});
}
void StsPrintExtension::RegisterPreviewCb()
{
PrintManagerClient::GetInstance().RegisterExtCallback(extensionId_, PRINT_EXTCB_REQUEST_PREVIEW,
[](const PrintJob &job) -> bool {
if (StsPrintExtension::stsExtension_ == nullptr) {
PRINT_HILOGE("stsExtension_ is null");
return false;
}
return StsPrintExtension::stsExtension_->Callback("onRequestPreview", job);
});
}
void StsPrintExtension::RegisterQueryCapCb()
{
PrintManagerClient::GetInstance().RegisterExtCallback(extensionId_, PRINT_EXTCB_REQUEST_CAP,
[](const std::string &printId) -> bool {
if (StsPrintExtension::stsExtension_ == nullptr) {
PRINT_HILOGE("stsExtension_ is null");
return false;
}
std::string realPrinterId = PrintUtils::GetLocalId(printId, stsExtension_->extensionId_);
return StsPrintExtension::stsExtension_->Callback("onRequestPrinterCapability", realPrinterId);
});
}
void StsPrintExtension::RegisterExtensionCb()
{
PrintManagerClient::GetInstance().RegisterExtCallback(extensionId_, PRINT_EXTCB_DESTROY_EXTENSION,
[]() -> bool {
if (StsPrintExtension::stsExtension_ == nullptr) {
PRINT_HILOGE("stsExtension_ is null");
return false;
}
return StsPrintExtension::stsExtension_->Callback("onDestroy");
});
}
}
}