/* * Copyright (c) 2023 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_media_control_extension.h" #include "ability_info.h" #include "ability_manager_client.h" #include "hitrace_meter.h" #include "hilog_wrapper.h" #include "js_extension_common.h" #include "js_extension_context.h" #include "js_runtime.h" #include "js_runtime_utils.h" #include "js_media_control_extension_context.h" #include "napi/native_api.h" #include "napi/native_node_api.h" #include "napi_common_configuration.h" #include "napi_common_want.h" #include "napi_remote_object.h" #include "ui_extension_window_command.h" namespace OHOS { namespace AbilityRuntime { using namespace OHOS::AppExecFwk; namespace { constexpr size_t ARGC_ONE = 1; constexpr size_t ARGC_TWO = 2; } NativeValue *AttachMediaControlExtensionContext(NativeEngine *engine, void *value, void *) { HILOG_DEBUG("AttachMediaControlExtensionContext"); if (value == nullptr) { HILOG_ERROR("invalid parameter."); return nullptr; } auto ptr = reinterpret_cast *>(value)->lock(); if (ptr == nullptr) { HILOG_ERROR("invalid context."); return nullptr; } NativeValue *object = JsMediaControlExtensionContext::CreateJsMediaControlExtensionContext(*engine, ptr); auto contextObj = JsRuntime::LoadSystemModuleByEngine(engine, "application.MediaControlExtensionContext", &object, 1)->Get(); if (contextObj == nullptr) { HILOG_ERROR("load context error."); return nullptr; } NativeObject *nObject = ConvertNativeValueTo(contextObj); nObject->ConvertToNativeBindingObject(engine, DetachCallbackFunc, AttachMediaControlExtensionContext, value, nullptr); auto workContext = new (std::nothrow) std::weak_ptr(ptr); nObject->SetNativePointer(workContext, [](NativeEngine *, void *data, void *) { HILOG_DEBUG("Finalizer for weak_ptr ui extension context is called"); delete static_cast *>(data); }, nullptr); return contextObj; } JsMediaControlExtension* JsMediaControlExtension::Create(const std::unique_ptr& runtime) { return new JsMediaControlExtension(static_cast(*runtime)); } JsMediaControlExtension::JsMediaControlExtension(JsRuntime& jsRuntime) : jsRuntime_(jsRuntime) {} JsMediaControlExtension::~JsMediaControlExtension() { HILOG_DEBUG("Js ui extension destructor."); auto context = GetContext(); if (context) { context->Unbind(); } jsRuntime_.FreeNativeReference(std::move(jsObj_)); jsRuntime_.FreeNativeReference(std::move(shellContextRef_)); } void JsMediaControlExtension::Init(const std::shared_ptr &record, const std::shared_ptr &application, std::shared_ptr &handler, const sptr &token) { HILOG_DEBUG("JsMediaControlExtension begin init"); MediaControlExtension::Init(record, application, handler, token); if (Extension::abilityInfo_->srcEntrance.empty()) { HILOG_ERROR("JsMediaControlExtension Init abilityInfo srcEntrance is empty"); return; } std::string srcPath(Extension::abilityInfo_->moduleName + "/"); srcPath.append(Extension::abilityInfo_->srcEntrance); srcPath.erase(srcPath.rfind('.')); srcPath.append(".abc"); std::string moduleName(Extension::abilityInfo_->moduleName); moduleName.append("::").append(abilityInfo_->name); HandleScope handleScope(jsRuntime_); auto& engine = jsRuntime_.GetNativeEngine(); jsObj_ = jsRuntime_.LoadModule( moduleName, srcPath, abilityInfo_->hapPath, abilityInfo_->compileMode == CompileMode::ES_MODULE); if (jsObj_ == nullptr) { HILOG_ERROR("Failed to get jsObj_"); return; } NativeObject* obj = ConvertNativeValueTo(jsObj_->Get()); if (obj == nullptr) { HILOG_ERROR("Failed to get JsMediaControlExtension object"); return; } BindContext(engine, obj); SetExtensionCommon( JsExtensionCommon::Create(jsRuntime_, static_cast(*jsObj_), shellContextRef_)); } void JsMediaControlExtension::BindContext(NativeEngine& engine, NativeObject* obj) { auto context = GetContext(); if (context == nullptr) { HILOG_ERROR("Failed to get context"); return; } HILOG_DEBUG("BindContext CreateJsMediaControlExtensionContext."); NativeValue* contextObj = JsMediaControlExtensionContext::CreateJsMediaControlExtensionContext(engine, context); if (contextObj == nullptr) { HILOG_ERROR("Create js ui extension context error."); return; } shellContextRef_ = JsRuntime::LoadSystemModuleByEngine(&engine, "application.MediaControlExtensionContext", &contextObj, ARGC_ONE); contextObj = shellContextRef_->Get(); NativeObject *nativeObj = ConvertNativeValueTo(contextObj); if (nativeObj == nullptr) { HILOG_ERROR("Failed to get context native object"); return; } auto workContext = new (std::nothrow) std::weak_ptr(context); nativeObj->ConvertToNativeBindingObject(&engine, DetachCallbackFunc, AttachMediaControlExtensionContext, workContext, nullptr); context->Bind(jsRuntime_, shellContextRef_.get()); obj->SetProperty("context", contextObj); nativeObj->SetNativePointer(workContext, [](NativeEngine*, void* data, void*) { HILOG_DEBUG("Finalizer for weak_ptr ui extension context is called"); delete static_cast*>(data); }, nullptr); HILOG_DEBUG("Init end."); } void JsMediaControlExtension::OnStart(const AAFwk::Want &want) { HILOG_DEBUG("JsMediaControlExtension OnStart begin."); Extension::OnStart(want); HandleScope handleScope(jsRuntime_); NativeEngine* nativeEngine = &jsRuntime_.GetNativeEngine(); napi_value napiWant = OHOS::AppExecFwk::WrapWant(reinterpret_cast(nativeEngine), want); NativeValue* nativeWant = reinterpret_cast(napiWant); NativeValue* argv[] = {nativeWant}; CallObjectMethod("onCreate", argv, ARGC_ONE); HILOG_DEBUG("JsMediaControlExtension OnStart end."); } void JsMediaControlExtension::OnStop() { MediaControlExtension::OnStop(); HILOG_DEBUG("JsMediaControlExtension OnStop begin."); HandleScope handleScope(jsRuntime_); CallObjectMethod("onDestroy"); HILOG_DEBUG("JsMediaControlExtension OnStop end."); } sptr JsMediaControlExtension::OnConnect(const AAFwk::Want &want) { HandleScope handleScope(jsRuntime_); NativeValue *result = CallOnConnect(want); NativeEngine* nativeEngine = &jsRuntime_.GetNativeEngine(); auto remoteObj = NAPI_ohos_rpc_getNativeRemoteObject( reinterpret_cast(nativeEngine), reinterpret_cast(result)); if (remoteObj == nullptr) { HILOG_ERROR("remoteObj is nullptr."); } return remoteObj; } void JsMediaControlExtension::OnDisconnect(const AAFwk::Want &want) { HITRACE_METER_NAME(HITRACE_TAG_ABILITY_MANAGER, __PRETTY_FUNCTION__); Extension::OnDisconnect(want); HILOG_DEBUG("JsMediaControlExtension OnDisconnect begin."); CallOnDisconnect(want, false); HILOG_DEBUG("JsMediaControlExtension OnDisconnect end."); } void JsMediaControlExtension::OnCommandWindow(const AAFwk::Want &want, const sptr &sessionInfo, AAFwk::WindowCommand winCmd) { if (sessionInfo == nullptr) { HILOG_ERROR("sessionInfo is nullptr."); return; } HILOG_DEBUG("begin. persistentId: %{private}d, winCmd: %{public}d", sessionInfo->persistentId, winCmd); Extension::OnCommandWindow(want, sessionInfo, winCmd); switch (winCmd) { case AAFwk::WIN_CMD_FOREGROUND: ForegroundWindow(want, sessionInfo); break; case AAFwk::WIN_CMD_BACKGROUND: BackgroundWindow(sessionInfo); break; case AAFwk::WIN_CMD_DESTROY: DestroyWindow(sessionInfo); break; default: HILOG_DEBUG("unsupported cmd."); break; } auto context = GetContext(); if (context == nullptr) { HILOG_ERROR("Failed to get context"); return; } AAFwk::AbilityCommand abilityCmd; if (uiWindowMap_.empty()) { abilityCmd = AAFwk::ABILITY_CMD_DESTROY; } else if (foregroundWindows_.empty()) { abilityCmd = AAFwk::ABILITY_CMD_BACKGROUND; } else { abilityCmd = AAFwk::ABILITY_CMD_FOREGROUND; } AAFwk::AbilityManagerClient::GetInstance()->ScheduleCommandAbilityWindowDone( context->GetToken(), sessionInfo, winCmd, abilityCmd); HILOG_DEBUG("end."); } void JsMediaControlExtension::OnCommand(const AAFwk::Want &want, bool restart, int startId) { Extension::OnCommand(want, restart, startId); HILOG_DEBUG("JsMediaControlExtension OnCommand begin restart=%{public}s,startId=%{public}d.", restart ? "true" : "false", startId); // wrap want HandleScope handleScope(jsRuntime_); NativeEngine* nativeEngine = &jsRuntime_.GetNativeEngine(); napi_value napiWant = OHOS::AppExecFwk::WrapWant(reinterpret_cast(nativeEngine), want); NativeValue* nativeWant = reinterpret_cast(napiWant); // wrap startId napi_value napiStartId = nullptr; napi_create_int32(reinterpret_cast(nativeEngine), startId, &napiStartId); NativeValue* nativeStartId = reinterpret_cast(napiStartId); NativeValue* argv[] = {nativeWant, nativeStartId}; CallObjectMethod("onRequest", argv, ARGC_TWO); HILOG_DEBUG("JsMediaControlExtension OnCommand end."); } void JsMediaControlExtension::OnForeground(const Want &want) { HITRACE_METER_NAME(HITRACE_TAG_ABILITY_MANAGER, __PRETTY_FUNCTION__); HILOG_DEBUG("JsMediaControlExtension OnForeground begin."); Extension::OnForeground(want); HandleScope handleScope(jsRuntime_); CallObjectMethod("onForeground"); HILOG_DEBUG("JsMediaControlExtension OnForeground end."); } void JsMediaControlExtension::OnBackground() { HITRACE_METER_NAME(HITRACE_TAG_ABILITY_MANAGER, __PRETTY_FUNCTION__); HILOG_DEBUG("JsMediaControlExtension OnBackground begin."); HandleScope handleScope(jsRuntime_); CallObjectMethod("onBackground"); Extension::OnBackground(); HILOG_DEBUG("JsMediaControlExtension OnBackground end."); } void JsMediaControlExtension::ForegroundWindow(const AAFwk::Want &want, const sptr &sessionInfo) { HILOG_DEBUG("begin."); if (sessionInfo == nullptr) { HILOG_ERROR("SessionInfo is nullptr."); return; } auto persistentId = sessionInfo->persistentId; if (uiWindowMap_.find(persistentId) == uiWindowMap_.end()) { sptr option = new Rosen::WindowOption(); auto context = GetContext(); if (context == nullptr || context->GetAbilityInfo() == nullptr) { HILOG_ERROR("Failed to get context"); return; } option->SetWindowName(context->GetBundleName() + context->GetAbilityInfo()->name); option->SetWindowType(Rosen::WindowType::WINDOW_TYPE_UI_EXTENSION); option->SetWindowSessionType(Rosen::WindowSessionType::EXTENSION_SESSION); auto uiWindow = Rosen::Window::Create(option, GetContext(), sessionInfo->sessionToken); if (uiWindow == nullptr) { HILOG_ERROR("create ui window error."); return; } HandleScope handleScope(jsRuntime_); NativeEngine* nativeEngine = &jsRuntime_.GetNativeEngine(); NativeValue* pathNative = CallObjectMethod("onLoadContent"); std::string contextPath; if (!ConvertFromJsValue(*nativeEngine, pathNative, contextPath)) { HILOG_ERROR("Failed to convert parameter to context path"); return; } HILOG_DEBUG("contextPath is %{private}s", contextPath.c_str()); uiWindow->SetUIContent(contextPath, nativeEngine, nullptr); uiWindowMap_[persistentId] = uiWindow; } auto& uiWindow = uiWindowMap_[persistentId]; if (uiWindow) { uiWindow->Show(); foregroundWindows_.emplace(persistentId); } HILOG_DEBUG("end."); } void JsMediaControlExtension::BackgroundWindow(const sptr &sessionInfo) { HILOG_DEBUG("begin."); if (sessionInfo == nullptr) { HILOG_ERROR("SessionInfo is nullptr."); return; } auto persistentId = sessionInfo->persistentId; if (uiWindowMap_.find(persistentId) == uiWindowMap_.end()) { HILOG_ERROR("Fail to find uiWindow, persistentId=%{private}d", persistentId); return; } auto& uiWindow = uiWindowMap_[persistentId]; if (uiWindow) { uiWindow->Hide(); foregroundWindows_.erase(persistentId); } HILOG_DEBUG("end."); } void JsMediaControlExtension::DestroyWindow(const sptr &sessionInfo) { HILOG_DEBUG("begin."); if (sessionInfo == nullptr) { HILOG_ERROR("SessionInfo is nullptr."); return; } auto persistentId = sessionInfo->persistentId; if (uiWindowMap_.find(persistentId) == uiWindowMap_.end()) { HILOG_ERROR("Fail to find uiWindow, persistentId=%{private}d", persistentId); return; } auto& uiWindow = uiWindowMap_[persistentId]; if (uiWindow) { uiWindow->Destroy(); uiWindowMap_.erase(persistentId); foregroundWindows_.erase(persistentId); } HILOG_DEBUG("end."); } NativeValue* JsMediaControlExtension::CallObjectMethod(const char* name, NativeValue* const* argv, size_t argc) { HILOG_DEBUG("JsMediaControlExtension CallObjectMethod(%{public}s), begin", name); if (!jsObj_) { HILOG_ERROR("Not found MediaControlExtension.js"); return nullptr; } auto& nativeEngine = jsRuntime_.GetNativeEngine(); NativeValue* value = jsObj_->Get(); NativeObject* obj = ConvertNativeValueTo(value); if (obj == nullptr) { HILOG_ERROR("Failed to get MediaControlExtension object"); return nullptr; } NativeValue* method = obj->GetProperty(name); if (method == nullptr || method->TypeOf() != NATIVE_FUNCTION) { HILOG_ERROR("Failed to get '%{public}s' from MediaControlExtension object", name); return nullptr; } HILOG_DEBUG("JsMediaControlExtension CallFunction(%{public}s), success", name); return nativeEngine.CallFunction(value, method, argv, argc); } NativeValue *JsMediaControlExtension::CallOnConnect(const AAFwk::Want &want) { HITRACE_METER_NAME(HITRACE_TAG_ABILITY_MANAGER, __PRETTY_FUNCTION__); Extension::OnConnect(want); HILOG_DEBUG("JsMediaControlExtension CallOnConnect begin."); HandleScope handleScope(jsRuntime_); NativeEngine* nativeEngine = &jsRuntime_.GetNativeEngine(); napi_value napiWant = OHOS::AppExecFwk::WrapWant(reinterpret_cast(nativeEngine), want); auto* nativeWant = reinterpret_cast(napiWant); NativeValue* argv[] = {nativeWant}; if (!jsObj_) { HILOG_ERROR("Not found MediaControlExtension.js"); return nullptr; } NativeValue* value = jsObj_->Get(); auto* obj = ConvertNativeValueTo(value); if (obj == nullptr) { HILOG_ERROR("Failed to get MediaControlExtension object"); return nullptr; } NativeValue* method = obj->GetProperty("onConnect"); if (method == nullptr) { HILOG_ERROR("Failed to get onConnect from MediaControlExtension object"); return nullptr; } NativeValue* remoteNative = nativeEngine->CallFunction(value, method, argv, ARGC_ONE); if (remoteNative == nullptr) { HILOG_ERROR("remoteNative is nullptr."); } HILOG_DEBUG("JsMediaControlExtension CallOnConnect end."); return remoteNative; } NativeValue *JsMediaControlExtension::CallOnDisconnect(const AAFwk::Want &want, bool withResult) { HandleEscape handleEscape(jsRuntime_); NativeEngine *nativeEngine = &jsRuntime_.GetNativeEngine(); napi_value napiWant = OHOS::AppExecFwk::WrapWant(reinterpret_cast(nativeEngine), want); NativeValue *nativeWant = reinterpret_cast(napiWant); NativeValue *argv[] = { nativeWant }; if (!jsObj_) { HILOG_ERROR("Not found MediaControlExtension.js"); return nullptr; } NativeValue *value = jsObj_->Get(); NativeObject *obj = ConvertNativeValueTo(value); if (obj == nullptr) { HILOG_ERROR("Failed to get MediaControlExtension object"); return nullptr; } NativeValue *method = obj->GetProperty("onDisconnect"); if (method == nullptr) { HILOG_ERROR("Failed to get onDisconnect from MediaControlExtension object"); return nullptr; } if (withResult) { return handleEscape.Escape(nativeEngine->CallFunction(value, method, argv, ARGC_ONE)); } else { nativeEngine->CallFunction(value, method, argv, ARGC_ONE); return nullptr; } } void JsMediaControlExtension::OnConfigurationUpdated(const AppExecFwk::Configuration& configuration) { Extension::OnConfigurationUpdated(configuration); HILOG_DEBUG("JsMediaControlExtension OnConfigurationUpdated called."); HandleScope handleScope(jsRuntime_); auto& nativeEngine = jsRuntime_.GetNativeEngine(); // Notify extension context auto fullConfig = GetContext()->GetConfiguration(); if (!fullConfig) { HILOG_ERROR("configuration is nullptr."); return; } JsExtensionContext::ConfigurationUpdated(&nativeEngine, shellContextRef_, fullConfig); napi_value napiConfiguration = OHOS::AppExecFwk::WrapConfiguration( reinterpret_cast(&nativeEngine), *fullConfig); NativeValue* jsConfiguration = reinterpret_cast(napiConfiguration); CallObjectMethod("onConfigurationUpdate", &jsConfiguration, ARGC_ONE); } void JsMediaControlExtension::Dump(const std::vector ¶ms, std::vector &info) { Extension::Dump(params, info); HILOG_DEBUG("JsMediaControlExtension Dump called."); HandleScope handleScope(jsRuntime_); auto& nativeEngine = jsRuntime_.GetNativeEngine(); // create js array object of params NativeValue* arrayValue = nativeEngine.CreateArray(params.size()); NativeArray* array = ConvertNativeValueTo(arrayValue); uint32_t index = 0; for (const auto ¶m : params) { array->SetElement(index++, CreateJsValue(nativeEngine, param)); } NativeValue* argv[] = { arrayValue }; if (!jsObj_) { HILOG_ERROR("Not found MediaControlExtension.js"); return; } NativeValue* value = jsObj_->Get(); NativeObject* obj = ConvertNativeValueTo(value); if (obj == nullptr) { HILOG_ERROR("Failed to get MediaControlExtension object"); return; } NativeValue* method = obj->GetProperty("onDump"); if (method == nullptr || method->TypeOf() != NATIVE_FUNCTION) { method = obj->GetProperty("dump"); if (method == nullptr || method->TypeOf() != NATIVE_FUNCTION) { HILOG_ERROR("Failed to get onDump from MediaControlExtension object"); return; } } NativeValue* dumpInfo = nativeEngine.CallFunction(value, method, argv, ARGC_ONE); if (dumpInfo == nullptr) { HILOG_ERROR("dumpInfo is nullptr."); return; } NativeArray* dumpInfoNative = ConvertNativeValueTo(dumpInfo); if (dumpInfoNative == nullptr) { HILOG_ERROR("dumpInfoNative is nullptr."); return; } for (uint32_t i = 0; i < dumpInfoNative->GetLength(); i++) { std::string dumpInfoStr; if (!ConvertFromJsValue(nativeEngine, dumpInfoNative->GetElement(i), dumpInfoStr)) { HILOG_ERROR("Parse dumpInfoStr failed"); return; } info.push_back(dumpInfoStr); } HILOG_DEBUG("Dump info size: %{public}zu", info.size()); } } }