Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qohosjsmain.cpp
Go to the documentation of this file.
1// Copyright (C) 2025 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qohosjsmain.h"
5
6#include <qplugin.h>
7#include <dlfcn.h>
8#include <node_api.h>
9#include <napi/native_api.h>
10#include <hilog/log.h>
11#include <pthread.h>
12#include <qos/qos.h>
13#include <QtCore/qbytearray.h>
14#include <QtCore/qdebug.h>
15#include <QtCore/qglobal.h>
16#include <QtCore/qjsonarray.h>
17#include <QtCore/qjsondocument.h>
18#include <QtCore/qjsonobject.h>
19#include <QtCore/qmap.h>
20#include <QtCore/qwaitcondition.h>
21#include <QtCore/qdir.h>
22#include <QtCore/private/qcore_unix_p.h>
23#include <QtCore/qvariant.h>
24#include <QtCore/private/qnapi_p.h>
25#include <QtCore/private/qohosappcontext_p.h>
26#include <QtCore/private/qohoscommon_p.h>
27#include <QtCore/private/qohospermissionshelper_p.h>
28#include <QtCore/private/qcoreapplication_p.h>
31#include <algorithm>
32#include <cerrno>
33#include <chrono>
34#include <cstdio>
35#include <cstring>
36#include <limits>
37#include <qohossinglethreadexecutor.h>
38#include <qpa/qwindowsysteminterface.h>
39#include <map>
40#include <qohosapppermissions_p.h>
41#include <qohosdeviceinfo_p.h>
42#include <qohospermissionshelperimpl.h>
43#include <qohosplugincore.h>
44#include <qohosqpaenums.h>
45#include <signal.h>
46#include <string>
47#include <sys/resource.h>
48#include <type_traits>
49#include <cstdlib>
50#include <optional>
51#include <unordered_map>
52
53#include "qarkui/qxcomponentregistry.h"
56#include "qohosjsutils.h"
60#include "qohosutils.h"
61#include "qohoswatchdog.h"
64#include "render/qxcomponent.h"
65
67
68using namespace std::chrono_literals;
69
71
73struct {
76} s_hotStartIteration;
78extern "C" typedef int (*Main)(int, char **); //use the standard main method to start the application
79
81
83static std::string s_appSharedLibName;
85static std::vector<QNapi::Reference<QNapi::Object>> foregroundAbilities;
86static int s_appExitCode = 0;
87static std::string s_exitCodeFilePath;
88
89static bool s_hotStartEnabled = false;
90
91namespace QtOhos {
92
93namespace {
94
95constexpr const char *enableHotStartEnvVariableName = "IO__QT__OHOS__ENABLE_HOT_START";
96
97constexpr const char *qtMainThreadStackSizeEnvVariableName = "IO__QT__OHOS__QT_MAIN_THREAD_STACK_SIZE";
98
99constexpr std::size_t defaultQtThreadStackSize = 8 * 1024 * 1024;
100
101constexpr auto minSupportedOhosSdkApiVersion = 23;
103
104std::atomic<bool> experimentalEnableGlBackinStore{false};
105std::atomic<bool> debugUseBasicStyleAndTheme{false};
106std::atomic<bool> debugDrawQtRasterBackingStoreFlushedRegion{false};
107std::atomic<bool> vsyncOnSoftwareBackingStoreEnabled{true};
108std::atomic<bool> enableNativeNodeApiKeyEvents{true};
109std::atomic<bool> enableNativeNodeApiMouseEvents{true};
110QtRunMode currentQtRunMode = QtRunMode::Normal;
111
112const auto callerPidWantArgName = "ohos.aafwk.param.callerPid";
113const auto qtAppProcessIdWantArgName = "io.qt.private.appProcessId";
114
115const char *mapQtRunModeToString(QtRunMode qtRunMode)
116{
117 switch (qtRunMode) {
119 return "Normal";
121 return "NoUiChildProcess";
122 }
123
124 qOhosReportFatalErrorAndAbort(
125 "%s: got unknown QtRunMode value: %d",
126 Q_FUNC_INFO, static_cast<int>(qtRunMode));
127}
128
129struct QtAppStartConfig
130{
131 std::string appLibraryPath;
132 bool watchdogEnabled = true;
133};
134
135class AppContextDirs
136{
137public:
138 static AppContextDirs mapFromNapiObject(QNapi::Object appContextObj);
139 static AppContextDirs mapFromQOhosAppContextProperties(const QMap<QOhosAppContext::Type, QString> &props);
140
141 QMap<QOhosAppContext::Type, QString> mapToQOhosAppContextProperties() const;
142 QJsonObject mapToQJsonObject() const;
143
144 std::string bundleCodeDir;
145 std::string cacheDir;
146 std::string filesDir;
147 std::string preferencesDir;
148 std::string tempDir;
149 std::string databaseDir;
150 std::string distributedFilesDir;
151 std::string resourceDir;
152
153private:
154 static const std::pair<const char *, std::string AppContextDirs::*> propsNames[];
155 static const std::pair<QOhosAppContext::Type, std::string AppContextDirs::*> qOhosAppContextPropsMap[];
156};
157
158const std::pair<const char *, std::string AppContextDirs::*> AppContextDirs::propsNames[] = {
159 {"bundleCodeDir", &AppContextDirs::bundleCodeDir},
160 {"cacheDir", &AppContextDirs::cacheDir},
161 {"filesDir", &AppContextDirs::filesDir},
162 {"preferencesDir", &AppContextDirs::preferencesDir},
163 {"tempDir", &AppContextDirs::tempDir},
164 {"databaseDir", &AppContextDirs::databaseDir},
165 {"distributedFilesDir", &AppContextDirs::distributedFilesDir},
166 {"resourceDir", &AppContextDirs::resourceDir},
167};
168
169const std::pair<QOhosAppContext::Type, std::string AppContextDirs::*> AppContextDirs::qOhosAppContextPropsMap[] = {
170 {QOhosAppContext::Type::bundleCodeDir, &AppContextDirs::bundleCodeDir},
171 {QOhosAppContext::Type::cacheDir, &AppContextDirs::cacheDir},
172 {QOhosAppContext::Type::filesDir, &AppContextDirs::filesDir},
173 {QOhosAppContext::Type::preferencesDir, &AppContextDirs::preferencesDir},
174 {QOhosAppContext::Type::tempDir, &AppContextDirs::tempDir},
175 {QOhosAppContext::Type::databaseDir, &AppContextDirs::databaseDir},
176 {QOhosAppContext::Type::distributedFilesDir, &AppContextDirs::distributedFilesDir},
177 {QOhosAppContext::Type::resourceDir, &AppContextDirs::resourceDir},
178};
179
180AppContextDirs AppContextDirs::mapFromNapiObject(QNapi::Object appContextObj)
181{
182 AppContextDirs appContextDirs;
183 for (const auto &propEntry : propsNames)
184 appContextDirs.*propEntry.second = appContextObj.get<QNapi::String>(propEntry.first);
185
186 return appContextDirs;
187}
188
189AppContextDirs AppContextDirs::mapFromQOhosAppContextProperties(
190 const QMap<QOhosAppContext::Type, QString> &props)
191{
192 AppContextDirs appContextDirs;
193 for (const auto &propEntry : qOhosAppContextPropsMap)
194 appContextDirs.*propEntry.second = props[propEntry.first].toStdString();
195 return appContextDirs;
196}
197
198QMap<QOhosAppContext::Type, QString> AppContextDirs::mapToQOhosAppContextProperties() const
199{
200 QMap<QOhosAppContext::Type, QString> qOhosAppContextProps;
201 for (const auto &propEntry : qOhosAppContextPropsMap)
202 qOhosAppContextProps[propEntry.first] = QString::fromStdString(this->*propEntry.second);
203 return qOhosAppContextProps;
204}
205
206QJsonObject AppContextDirs::mapToQJsonObject() const
207{
208 QJsonObject json;
209 for (const auto &propEntry : propsNames)
210 json[QString::fromUtf8(propEntry.first)] = QString::fromStdString(this->*propEntry.second);
211 return json;
212}
213
214std::vector<std::string> splitString(const std::string &inputStr, char separator)
215{
216 constexpr auto separatorSize = 1;
217
218 std::vector<std::string> result;
219 std::size_t currentPos = 0;
220 while (currentPos < inputStr.size()) {
221 auto separatorPos = inputStr.find(separator, currentPos);
222 result.push_back(inputStr.substr(currentPos, separatorPos));
223 currentPos = separatorPos != std::string::npos
224 ? separatorPos + separatorSize
225 : inputStr.size();
226 }
227
228 return result;
229}
230
231std::pair<QOhosConsumer<bool>, std::function<void()>> makeConditionFlagMTAccessors(
232 std::string conditionName)
233{
234 struct Context
235 {
236 std::string conditionName;
237 std::mutex conditionMutex;
238 std::condition_variable conditionCv;
239 bool condition = false;
240 };
241
242 auto context = std::make_shared<Context>();
243 context->conditionName = std::move(conditionName);
244
245 return {
246 [context](bool condition) {
247 qOhosPrintfDebug(
248 "%s: setting condition '%s' to %s",
249 Q_FUNC_INFO, context->conditionName.c_str(), mapBoolToTrueFalseStr(condition));
250 {
251 std::lock_guard<std::mutex> conditionLock(context->conditionMutex);
252 if (condition != context->condition) {
253 context->condition = condition;
254 context->conditionCv.notify_all();
255 }
256 }
257 },
258 [context]() {
259 qOhosPrintfDebug("%s: waiting for condition '%s'", Q_FUNC_INFO, context->conditionName.c_str());
260 {
261 std::unique_lock<std::mutex> conditionLock(context->conditionMutex);
262 context->conditionCv.wait(
263 conditionLock,
264 [&]() {
265 return context->condition;
266 });
267 }
268 qOhosPrintfDebug("%s: condition '%s' met", Q_FUNC_INFO, context->conditionName.c_str());
269 },
270 };
271}
272
273template<typename FuncResult, typename ...FuncArgs, typename FuncFactory>
274auto makeLazyInitFunc(FuncFactory funcFactory) -> std::function<FuncResult(FuncArgs...)>
275{
276 using Func = std::function<FuncResult(FuncArgs...)>;
277 return [func = Func(), factory = QOhosSupplier<Func>(std::move(funcFactory))](FuncArgs ...args) mutable {
278 if (!func)
279 func = std::exchange(factory, nullptr)();
280 return func(std::forward<FuncArgs>(args)...);
281 };
282}
283
284std::function<int(std::vector<std::string>)> openLibraryWithMainFunctionOrFail(const std::string &libraryPath)
285{
286 void *mainLibraryHnd = dlopen(libraryPath.c_str(), RTLD_LAZY);
287 if (Q_UNLIKELY(!mainLibraryHnd)) {
288 qOhosReportFatalErrorAndAbort(
289 "%s: dlopen() failed to open library '%s': %s",
290 Q_FUNC_INFO, libraryPath.c_str(), dlerror());
291 }
292
293 auto *mainFunc = reinterpret_cast<Main>(dlsym(mainLibraryHnd, "main"));
294 if (Q_UNLIKELY(!mainFunc)) {
295 qOhosReportFatalErrorAndAbort(
296 "%s: dlsym() failed to find 'main' symbol in library '%s': %s",
297 Q_FUNC_INFO, libraryPath.c_str(), dlerror());
298 }
299
300 qOhosPrintfDebug("%s: opened library '%s' with main function", Q_FUNC_INFO, libraryPath.c_str());
301
302 return [libraryPath, mainFunc](std::vector<std::string> mainArgs) {
303 auto mainArgsPointers = std::vector<char *>();
304 for (auto &arg : mainArgs)
305 mainArgsPointers.push_back(&arg[0]);
306 mainArgsPointers.push_back(nullptr);
307
308 int argc = mainArgs.size();
309 char **argv = &mainArgsPointers[0];
310
311 qOhosPrintfDebug(
312 "%s: calling 'main' function in library '%s' (argc=%d)",
313 Q_FUNC_INFO, libraryPath.c_str(), argc);
314
315 for (int i = 0; i < argc; ++i)
316 qOhosPrintfDebug("%s: 'main' function argv[%d]='%s'", Q_FUNC_INFO, i, argv[i]);
317
318 int mainResult = mainFunc(argc, argv);
319
320 qOhosPrintfDebug(
321 "%s: 'main' function in library '%s' returned %d",
322 Q_FUNC_INFO, libraryPath.c_str(), mainResult);
323
324 return mainResult;
325 };
326}
327
328class QUiAbilityEngine : public QAbilityEngine
329{
330public:
331 QUiAbilityEngine();
332 ~QUiAbilityEngine();
333
334 QAbilityInfo readAbilityInfo(const QNapi::Object &ability) const override;
335};
336
337QUiAbilityEngine::QUiAbilityEngine() = default;
338
339QUiAbilityEngine::~QUiAbilityEngine() = default;
340
341QAbilityInfo QUiAbilityEngine::readAbilityInfo(const QNapi::Object &ability) const
342{
343 auto abilityInfo = ability.eval<QNapi::Object>("context.abilityInfo");
344
345 return {
346 .name = abilityInfo.get<QNapi::String>("name"),
347 .bundleName = abilityInfo.get<QNapi::String>("bundleName"),
348 .moduleName = abilityInfo.get<QNapi::String>("moduleName"),
349 };
350}
351
352void redirectStandardDescriptorsToFile(const std::string &redirectedStdoutPath)
353{
354 int openResult = qt_safe_open(redirectedStdoutPath.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0666);
355 auto openErrno = errno;
356 if (openResult < 0) {
357 // Non-fatal: the test still needs to write its exit code.
358 qOhosPrintfWarning("%s: error opening file '%s' for redirected stdout: %s",
359 Q_FUNC_INFO, redirectedStdoutPath.c_str(), std::strerror(openErrno));
360 return;
361 }
362
363 ::fflush(stdout);
364
365 if (qt_safe_dup2(openResult, STDOUT_FILENO) < 0) {
366 auto dup2Errno = errno;
367 qOhosPrintfWarning("%s: dup2() failed on redirecting stdout to '%s': %s",
368 Q_FUNC_INFO, redirectedStdoutPath.c_str(), std::strerror(dup2Errno));
369 }
370
371 qt_safe_close(openResult);
372}
373
374QOhosConsumer<std::vector<std::string>> makeAppMainFuncLauncher(
375 const QtAppStartConfig &appStartConfig, QOhosConsumer<int> funcExitHandler)
376{
377 auto appLibraryMainFunc = makeLazyInitFunc<int, std::vector<std::string>>(
378 [appLibraryPath = appStartConfig.appLibraryPath]() {
379 return openLibraryWithMainFunctionOrFail(appLibraryPath);
380 });
381
382 return [appLibraryMainFunc = std::move(appLibraryMainFunc), funcExitHandler = std::move(funcExitHandler), appStartConfig](std::vector<std::string> appArgs) {
383 auto __dbg = make_QCScopedDebugJS("startApplicationMainFunction");
384 std::vector<std::string> mainArgs;
385 mainArgs.push_back(appStartConfig.appLibraryPath);
386 mainArgs.insert(mainArgs.end(), appArgs.begin(), appArgs.end());
387
388 auto qtWatchdog =
389 appStartConfig.watchdogEnabled
391 : std::shared_ptr<void>();
392
393 int exitCode = appLibraryMainFunc(std::move(mainArgs));
394
395 funcExitHandler(exitCode);
396 };
397}
398
399std::optional<std::vector<std::string>> tryMapJsonArrayToStrings(const std::string &inputJson)
400{
401 auto doc = QJsonDocument::fromJson(QByteArray::fromStdString(inputJson));
402 if (doc.isNull()) {
403 qOhosPrintfWarning("%s: input is not valid JSON string", Q_FUNC_INFO);
404 return std::nullopt;
405 }
406 if (!doc.isArray()) {
407 qOhosPrintfWarning("%s: input JSON does not contain an array", Q_FUNC_INFO);
408 return std::nullopt;
409 }
410
411 const auto inputArray = doc.array();
412
413 std::vector<std::string> result;
414 for (const auto &elem : inputArray) {
415 if (!elem.isString()) {
416 qOhosPrintfWarning("%s: input array's element is not a string", Q_FUNC_INFO);
417 return std::nullopt;
418 }
419 result.push_back(elem.toString().toStdString());
420 }
421
422 return result;
423}
424
425std::optional<std::vector<std::pair<std::string, std::string>>>
426tryMapJsonObjectToStringPairs(const std::string &inputJson)
427{
428 auto doc = QJsonDocument::fromJson(QByteArray::fromStdString(inputJson));
429 if (doc.isNull()) {
430 qOhosPrintfWarning("%s: input is not valid JSON string", Q_FUNC_INFO);
431 return std::nullopt;
432 }
433 if (!doc.isObject()) {
434 qOhosPrintfWarning("%s: input JSON does not contain an object", Q_FUNC_INFO);
435 return std::nullopt;
436 }
437
438 const auto inputObject = doc.object();
439
440 std::vector<std::pair<std::string, std::string>> result;
441 for (auto it = inputObject.constBegin(); it != inputObject.constEnd(); ++it) {
442 if (!it->isString()) {
443 qOhosPrintfWarning("%s: input object's value is not a string", Q_FUNC_INFO);
444 return std::nullopt;
445 }
446 result.emplace_back(it.key().toStdString(), it->toString().toStdString());
447 }
448
449 return result;
450}
451
452template<typename T>
453std::enable_if_t<std::is_base_of<QNapi::Value, T>::value, T>
454getWantParamOrEmptyIfNotPresent(QNapi::Object want, const std::string &paramName)
455{
456 return QNapi::getOptionalPropOrEmpty<T>(
457 QNapi::getOptionalPropOrEmpty<QNapi::Object>(want, "parameters"),
458 paramName, "parameters of Want");
459}
460
461std::vector<std::string> getQtAppArgsFromWant(QNapi::Object want)
462{
463 using namespace std::string_literals;
464
465 const auto *qtObsoleteUseUriAsArgPropName = "io.qt.useUriAsArg";
466 const auto *qtAppArgsPropName = "io.qt.appArgs";
467 const auto *qtAppArgsJsonPropName = "io.qt.appArgsJson";
468
469 Napi::HandleScope getArgsScope(want.Env());
470
471 std::vector<std::string> result;
472
473 auto optWantParams = QNapi::getOptionalPropOrEmpty<QNapi::Object>(want, "parameters");
474 if (!optWantParams.IsEmpty() && optWantParams.Has(qtObsoleteUseUriAsArgPropName)) {
475 qOhosPrintfWarning(
476 "Qt: Want parameter '%s' is obsolete and is ignored. The ability launch URI is no "
477 "longer passed as an application argument.",
478 qtObsoleteUseUriAsArgPropName);
479 }
480
481 auto optQtAppArgs = getWantParamOrEmptyIfNotPresent<QNapi::Array>(want, qtAppArgsPropName);
482 auto optQtAppArgsJson = getWantParamOrEmptyIfNotPresent<QNapi::String>(want, qtAppArgsJsonPropName);
483
484 if (!optQtAppArgs.IsEmpty()) {
485 if (!QNapi::arrayElementTypesMatch<QNapi::String>(optQtAppArgs)) {
486 throw QNapi::makeLoggedException(
487 want.Env(), "Want parameter '"s + qtAppArgsPropName + "' is not an array of strings"s);
488 }
489
490 auto qtAppArgsStrings = QNapi::getArrayElements<std::vector<std::string>, QNapi::String>(optQtAppArgs);
491 result.insert(result.end(), qtAppArgsStrings.begin(), qtAppArgsStrings.end());
492 } else if (!optQtAppArgsJson.IsEmpty()) {
493 auto optQtAppArgsJsonStrings = tryMapJsonArrayToStrings(optQtAppArgsJson);
494 if (!optQtAppArgsJsonStrings) {
495 throw QNapi::makeLoggedException(
496 want.Env(), "Want parameter '"s + qtAppArgsJsonPropName + "' is invalid"s);
497 }
498 result.insert(result.end(), optQtAppArgsJsonStrings->begin(), optQtAppArgsJsonStrings->end());
499 }
500
501 return result;
502}
503
504void requestAppPermissionsInBackground(JsState &jsState, const std::vector<std::string> &permissionsNames)
505{
506 for (const auto &permissionName : permissionsNames) {
507 qOhosPrintfInfo(
508 "Qt: automatically requesting application permission: '%s'",
509 permissionName.c_str());
510
511 QOhosAppPermissions::requestAppPermissionFromUser(
512 jsState, permissionName,
513 [permissionName](QOhosJsState &, bool permissionGranted) {
514 if (permissionGranted) {
515 qOhosPrintfInfo(
516 "Qt: automatically requested application permission granted: '%s'",
517 permissionName.c_str());
518 } else {
519 qOhosPrintfWarning(
520 "Qt: automatically requested application permission rejected: '%s'",
521 permissionName.c_str());
522 }
523 });
524 }
525}
526
527struct AppProcessLaunchOptions
528{
529 QNapi::Boolean useDefaultUiAbilityInstanceInQt;
530 QNapi::String appSharedLibNameOverride;
531 QNapi::Boolean experimentalGlBackingStore;
532 QNapi::Boolean debugDrawQtRasterBackingStoreFlushedRegion;
533 QNapi::Boolean debugUseBasicStyleAndTheme;
534 QNapi::Boolean enableVsyncOnSoftwareBackingStore;
535 QNapi::Boolean watchdogEnabled;
536 QNapi::String redirectStdoutToFile;
537 QNapi::String exitCodeFile;
538 QNapi::String autoRequestPermissions;
539 QNapi::Boolean enableNativeNodeApiKeyEvents;
540 QNapi::Boolean enableNativeNodeApiMouseEvents;
541 QNapi::String envVarsJson;
542};
543
544AppProcessLaunchOptions getProcessLaunchOptionsFromWant(QNapi::Object launchWant)
545{
546 auto assignWantParamIfPresent = [&](auto &outputValue, const char *paramName) {
547 using Param = std::remove_reference_t<decltype(outputValue)>;
548 outputValue = getWantParamOrEmptyIfNotPresent<Param>(launchWant, paramName);
549 };
550
551 AppProcessLaunchOptions launchOpts;
552
553 assignWantParamIfPresent(
554 launchOpts.useDefaultUiAbilityInstanceInQt,
555 "io.qt.useDefaultUiAbilityInstanceInQt");
556 assignWantParamIfPresent(
557 launchOpts.appSharedLibNameOverride,
558 "io.qt.appSharedLibNameOverride");
559 assignWantParamIfPresent(
560 launchOpts.experimentalGlBackingStore,
561 "io.qt.experimental.enableGlBackingStore");
562 assignWantParamIfPresent(
563 launchOpts.debugDrawQtRasterBackingStoreFlushedRegion,
564 "io.qt.debug.drawQtRasterBackingStoreFlushedRegion");
565 assignWantParamIfPresent(
566 launchOpts.debugUseBasicStyleAndTheme,
567 "io.qt.debug.useBasicStyleAndTheme");
568 assignWantParamIfPresent(
569 launchOpts.enableVsyncOnSoftwareBackingStore,
570 "io.qt.experimental.enableVsyncOnSoftwareBackingStore");
571 assignWantParamIfPresent(
572 launchOpts.watchdogEnabled,
573 "io.qt.watchdogEnabled");
574 assignWantParamIfPresent(
575 launchOpts.redirectStdoutToFile,
576 "io.qt.debug.redirectedStdoutPath");
577 assignWantParamIfPresent(
578 launchOpts.exitCodeFile,
579 "io.qt.debug.exitCodePath");
580 assignWantParamIfPresent(
581 launchOpts.autoRequestPermissions,
582 "io.qt.debug.autoRequestPermissions");
583 assignWantParamIfPresent(
584 launchOpts.enableNativeNodeApiKeyEvents,
585 "io.qt.experimental.enableNativeNodeApiKeyEvents");
586 assignWantParamIfPresent(
587 launchOpts.enableNativeNodeApiMouseEvents,
588 "io.qt.experimental.enableNativeNodeApiMouseEvents");
589 assignWantParamIfPresent(
590 launchOpts.envVarsJson,
591 "io.qt.envVarsJson");
592
593 return launchOpts;
594}
595
596void setGlobalFlagsFromAppProcessLaunchOptions(const AppProcessLaunchOptions &launchOpts)
597{
598 if (!launchOpts.useDefaultUiAbilityInstanceInQt.IsEmpty())
599 s_autoStartedAbilityInstanceWaitingForQtWindow = launchOpts.useDefaultUiAbilityInstanceInQt.Value();
600
601 if (!launchOpts.experimentalGlBackingStore.IsEmpty())
602 experimentalEnableGlBackinStore = launchOpts.experimentalGlBackingStore.Value();
603
604 if (!launchOpts.debugUseBasicStyleAndTheme.IsEmpty())
605 debugUseBasicStyleAndTheme = launchOpts.debugUseBasicStyleAndTheme.Value();
606
607 if (!launchOpts.enableVsyncOnSoftwareBackingStore.IsEmpty())
608 vsyncOnSoftwareBackingStoreEnabled = launchOpts.enableVsyncOnSoftwareBackingStore.Value();
609
610 debugDrawQtRasterBackingStoreFlushedRegion =
611 launchOpts.debugDrawQtRasterBackingStoreFlushedRegion.IsEmpty()
612 ? false
613 : launchOpts.debugDrawQtRasterBackingStoreFlushedRegion.Value();
614
615 if (!launchOpts.enableNativeNodeApiKeyEvents.IsEmpty())
616 enableNativeNodeApiKeyEvents = launchOpts.enableNativeNodeApiKeyEvents.Value();
617
618 if (!launchOpts.enableNativeNodeApiMouseEvents.IsEmpty())
619 enableNativeNodeApiMouseEvents = launchOpts.enableNativeNodeApiMouseEvents.Value();
620
621 if (!launchOpts.envVarsJson.IsEmpty()) {
622 auto optEnvVars = tryMapJsonObjectToStringPairs(launchOpts.envVarsJson.Utf8Value());
623 if (optEnvVars) {
624 for (const auto &[name, value] : *optEnvVars)
625 qputenv(name.c_str(), QByteArray::fromStdString(value));
626 }
627 }
628}
629
630void terminateAllAbilityInstances(JsState &jsState, const char *logContext)
631{
633 [&](auto qAbilityPeer) {
634 qOhosPrintfInfo(
635 "Qt: terminating QAbility with instanceId='%s'",
636 qAbilityPeer->instanceId().c_str());
637 auto optQUiAbilityPeer = QUiAbilityPeer::tryCastFromQAbilityPeerOrNull(qAbilityPeer);
638 if (optQUiAbilityPeer)
639 JsWindowsTracker::tagWindowAsClosing(optQUiAbilityPeer->window(), logContext);
640 qAbilityPeer->qAbility().eval("context.terminateSelf()");
641 });
642}
643
644std::optional<std::size_t> tryGetMaxStackSizeHardLimit()
645{
646 struct ::rlimit limit;
647 if (::getrlimit(RLIMIT_STACK, &limit) != 0) {
648 auto getrlimitErrno = errno;
649 qOhosPrintfWarning(
650 "%s: error reading stack size hard limit (assuming no limit): %s",
651 Q_FUNC_INFO, std::strerror(getrlimitErrno));
652 return {};
653 }
654
655 return limit.rlim_max != RLIM_INFINITY
656 ? std::optional<std::size_t>(limit.rlim_max)
657 : std::nullopt;
658}
659
660std::optional<std::size_t> tryGetQtThreadStackSizeFromEnv()
661{
662 int stackSizeFromEnv = qEnvironmentVariableIntValue(qtMainThreadStackSizeEnvVariableName);
663 return stackSizeFromEnv > 0
664 ? std::optional(static_cast<std::size_t>(stackSizeFromEnv))
665 : std::nullopt;
666}
667
668std::size_t getPreferredStackSizeForQtThread()
669{
670 constexpr std::size_t qtRequiredMinStackSize = 40960;
671 std::size_t pthreadStackMin = PTHREAD_STACK_MIN;
672 auto minStackSize = std::max({qtRequiredMinStackSize, pthreadStackMin});
673 auto maxStackSize = tryGetMaxStackSizeHardLimit().value_or(std::numeric_limits<std::size_t>::max());
674
675 auto optRequestedStackSize = tryGetQtThreadStackSizeFromEnv();
676
677 if (optRequestedStackSize.has_value()) {
678 auto requestedStackSize = optRequestedStackSize.value();
679 if (requestedStackSize < minStackSize) {
680 qOhosPrintfWarning(
681 "%s: requested stack size (%zu) is below minimum (pthread min: %zu, Qt min: %zu), increasing",
682 Q_FUNC_INFO, requestedStackSize, pthreadStackMin, qtRequiredMinStackSize);
683 }
684 if (requestedStackSize > maxStackSize) {
685 qOhosPrintfWarning(
686 "%s: requested stack size (%zu) is above maximum (hard limit: %zu), decreasing",
687 Q_FUNC_INFO, requestedStackSize, maxStackSize);
688 }
689 }
690
691 auto preferredStackSize = qBound(
692 minStackSize, optRequestedStackSize.value_or(defaultQtThreadStackSize), maxStackSize);
693
694 qOhosPrintfInfo("%s: preferred stack size for Qt Thread: %zu", Q_FUNC_INFO, preferredStackSize);
695
696 return preferredStackSize;
697}
698
699QOhosConsumer<std::vector<std::string>> makeQtThreadWithMainFuncLauncher(
700 QOhosConsumer<std::vector<std::string>> baseMainFuncLauncher)
701{
702 SingleThreadExecutorConfig qtThreadExecutorConfig = {
703 .threadPreferredStackSize = getPreferredStackSizeForQtThread(),
704 };
705 auto qtThreadExecutor = makeSingleThreadExecutor(qtThreadExecutorConfig);
706
707 struct InitContext
708 {
709 std::mutex initializedMutex;
710 std::condition_variable initializedCv;
711 bool initialized = false;
712 };
713
714 auto initContext = std::make_shared<InitContext>();
715
716 qtThreadExecutor(
717 [initContext] {
718 pthread_setname_np(pthread_self(), "QtMainThread");
719 //
720 // Following call to QThread::currentThread() forces this thread to be
721 // the main Qt/GUI thread. It sets the QCoreApplication::theMainThread field
722 // if this call is the first one.
723 //
724 auto *currentThread = QThread::currentThread();
725 QThread *mainThread = QCoreApplicationPrivate::theMainThread;
726 if (mainThread != currentThread) {
727 qOhosReportFatalErrorAndAbort(
728 "%s: mainThread (%p) != currentThread (%p). Qt API was likely used before Qt initialization. Aborting.",
729 Q_FUNC_INFO, mainThread, currentThread);
730 }
731
732 qt_setQOhosPermissionsHelper(getQOhosPermissionsHelperImpl());
733
734 QtOhos::initQtThreadState();
735
736 {
737 std::lock_guard<std::mutex> initializedLock(initContext->initializedMutex);
738 initContext->initialized = true;
739 initContext->initializedCv.notify_one();
740 }
741 });
742
743 {
744 std::unique_lock<std::mutex> initializedLock(initContext->initializedMutex);
745 initContext->initializedCv.wait(
746 initializedLock,
747 [&]() {
748 return initContext->initialized;
749 });
750 }
751
752 auto sharedBaseMainFuncLauncher = moveToSharedPtr(std::move(baseMainFuncLauncher));
753
754 return [qtThreadExecutor = std::move(qtThreadExecutor), sharedBaseMainFuncLauncher](std::vector<std::string> appArgs) {
755 qtThreadExecutor(
756 [sharedBaseMainFuncLauncher, appArgs = std::move(appArgs)]() mutable {
757 (*sharedBaseMainFuncLauncher)(std::move(appArgs));
758 });
759 };
760}
761
762std::shared_ptr<QAbilityInstancesManager> &getQAbilityInstancesManagerPtr()
763{
764 static std::shared_ptr<QAbilityInstancesManager> instancePtr;
765 return instancePtr;
766}
767
768QAbilityInstancesManager &getQAbilityInstancesManager()
769{
770 return *getQAbilityInstancesManagerPtr();
771}
772
773void handleDefaultQAbilityInstanceStartup(JsState &jsState, std::shared_ptr<QAbilityPeer> qAbilityPeer)
774{
775 QNapi::Object launchWant = qAbilityPeer->launchWant();
776
777 if (!s_qtAppThreadMainFuncLauncher) {
778 auto qtAppThreadIdleSetFunc = std::make_shared<QOhosConsumer<bool>>();
779 std::function<void()> qtAppThreadIdleStateWaitFunc;
780 std::tie(*qtAppThreadIdleSetFunc, qtAppThreadIdleStateWaitFunc) = makeConditionFlagMTAccessors("Qt thread idle");
781
782 auto launchOpts = getProcessLaunchOptionsFromWant(launchWant);
783
784 setGlobalFlagsFromAppProcessLaunchOptions(launchOpts);
785
786 if (!launchOpts.redirectStdoutToFile.IsEmpty())
787 redirectStandardDescriptorsToFile(launchOpts.redirectStdoutToFile.Utf8Value());
788
789 if (!launchOpts.exitCodeFile.IsEmpty())
790 s_exitCodeFilePath = launchOpts.exitCodeFile.Utf8Value();
791
792 if (!launchOpts.autoRequestPermissions.IsEmpty())
793 requestAppPermissionsInBackground(jsState, splitString(launchOpts.autoRequestPermissions, ','));
794
795 std::string appSharedLibName =
796 !launchOpts.appSharedLibNameOverride.IsEmpty()
797 ? launchOpts.appSharedLibNameOverride
799
800 auto appMainFuncLauncher = makeAppMainFuncLauncher(
801 {
802 .appLibraryPath = s_appSharedLibsDirPath + "/" + appSharedLibName,
803 .watchdogEnabled = !launchOpts.watchdogEnabled.IsEmpty()
804 ? launchOpts.watchdogEnabled.Value()
805 : true,
806 },
807 [qtAppThreadIdleSetFunc](int exitCode) {
808 if (s_hotStartEnabled)
809 s_autoStartedAbilityInstanceWaitingForQtWindow = true;
810 else
811 s_appExitCode = exitCode;
812
813 qOhosPrintfInfo("Qt: asynchronously terminating remaining QAbility instances, if any");
814 QtOhos::invokeInJsThread(
815 [](QtOhos::JsState &jsState) {
816 if (s_hotStartEnabled)
817 getQAbilityInstancesManager().registerPendingAutoStartedInstance();
818
819 qOhosPrintfInfo(
820 "Qt: force-resolving pending QWindow destroy Promises before termination if needed");
821 jsState.visitEachQAbilityPeer(
822 [&](std::shared_ptr<QtOhos::QAbilityPeer> peer) {
823 peer->forceResolveQWindowDestroyPromiseIfPresent(
824 Napi::Env(jsState.env()));
825 });
826
827 terminateAllAbilityInstances(jsState, "Qt main() exit");
828 });
829
830 (*qtAppThreadIdleSetFunc)(true);
831 });
832
833 auto mainFuncLauncher = moveToSharedPtr(
834 makeQtThreadWithMainFuncLauncher(std::move(appMainFuncLauncher)));
835 s_qtAppThreadMainFuncLauncher = [mainFuncLauncher, qtAppThreadIdleSetFunc](std::vector<std::string> appArgs) {
836 (*qtAppThreadIdleSetFunc)(false);
837 s_hotStartIteration.activeInQtThread = s_hotStartIteration.activeInQtThread.value_or(0) + 1;
838 (*mainFuncLauncher)(std::move(appArgs));
839 };
840 s_qtAppThreadIdleStateWaitFunc = std::move(qtAppThreadIdleStateWaitFunc);
841 }
842
843 s_hotStartIteration.lastRequestedInJsThread = s_hotStartIteration.lastRequestedInJsThread.value_or(0) + 1;
845 s_appArgs
846 ? *s_appArgs
847 : getQtAppArgsFromWant(launchWant));
848}
849
850std::map<std::string, QNapi::Reference<QNapi::Function>> makeJsModulesFactoriesMap(
851 const QNapi::Object &jsModulesFactoriesObj)
852{
853 std::map<std::string, QNapi::Reference<QNapi::Function>> jsModulesFactoriesMap;
854 for (const auto &prop : jsModulesFactoriesObj) {
855 if (prop.first.IsString()) {
856 auto propName = QNapi::checkedCast<QNapi::String>(prop.first);
857 QNapi::Value propValue = prop.second;
858 if (propValue.IsFunction()) {
859 jsModulesFactoriesMap.emplace(
860 propName.Utf8Value(),
861 QNapi::Reference<QNapi::Function>::makePersistentFrom(
862 QNapi::checkedCast<QNapi::Function>(propValue)));
863 }
864 }
865 }
866
867 return jsModulesFactoriesMap;
868}
869
870class AppFunctionsImpl : public AppFunctions
871{
872public:
873 void startQAbilityInstance(
874 QNapi::Object baseQAbility, QObjectThreadSafeRef qwindow,
875 QNapi::Object optStartOptions,
876 std::function<void(JsState &, std::shared_ptr<QAbilityPeer>)> startupNotifyFunc) override;
877
878 void startAppProcess(
879 QNapi::Object baseQAbility, const std::string &processId, QNapi::Object requestWant,
880 QNapi::Object optStartOptions, std::function<void(JsState &)> continueFunc) override;
881
882 void startNoUiChildProcess(JsState &jsState, const std::string &libraryName, const std::vector<std::string> &args) override;
883};
884
885void AppFunctionsImpl::startQAbilityInstance(
886 QNapi::Object baseQAbility, QObjectThreadSafeRef qwindow,
887 QNapi::Object optStartOptions,
888 std::function<void(JsState &, std::shared_ptr<QAbilityPeer>)> startupNotifyFunc)
889{
890 getQAbilityInstancesManager().startNewInstance(
891 baseQAbility, qwindow, optStartOptions, std::move(startupNotifyFunc));
892}
893
894void AppFunctionsImpl::startAppProcess(
895 QNapi::Object baseQAbility, const std::string &processId, QNapi::Object requestWant,
896 QNapi::Object optStartOptions, std::function<void(JsState &)> continueFunc)
897{
898 auto __dbg = make_QCScopedDebugJS("AppFunctionsImpl::startAppProcess");
899
900 static const char * const clonedWantPropsNames[] = {
901 "uri",
902 "type",
903 "action",
904 "flags",
905 "entities",
906 };
907
908 auto env = baseQAbility.Env();
909
910 auto qAbilityInfo = getQAbilityInstancesManager().abilityEngine()->readAbilityInfo(baseQAbility);
911
912 auto startWantParams = QNapi::Object::New(env);
913 auto requestWantParams = QNapi::getOptionalPropOrEmpty<QNapi::Object>(requestWant, "parameters");
914 if (!requestWantParams.IsEmpty()) {
915 for (const auto &requestWantParamEntry : requestWantParams) {
916 startWantParams.Set(
917 requestWantParamEntry.first, static_cast<QNapi::Value>(requestWantParamEntry.second));
918 }
919 }
920 startWantParams.Set(qtAppProcessIdWantArgName, processId);
921
922 auto startWant = QNapi::makeObject(
923 env,
924 {
925 {"bundleName", qAbilityInfo.bundleName},
926 {"moduleName", qAbilityInfo.moduleName},
927 {"abilityName", qAbilityInfo.name},
928 {"parameters", startWantParams},
929 });
930
931 for (const auto &propName : clonedWantPropsNames) {
932 auto optProp = QNapi::getOptionalPropOrEmpty<QNapi::Value>(requestWant, propName);
933 if (!optProp.IsEmpty())
934 startWant.Set(propName, optProp);
935 }
936
937 std::vector<QNapi::ValueWrapper> startAbilityArgs = {startWant};
938 if (!optStartOptions.IsEmpty())
939 startAbilityArgs.push_back(optStartOptions);
940
941 baseQAbility.evalToPromiseOrRejectOnThrow("context.startAbility(*)", startAbilityArgs)
942 .onCatch(QtOhos::makeErrorLoggingJsCallback("startAbility()"))
943 .onFinally(
944 [continueFunc = std::move(continueFunc)](const CallbackInfo &cbInfo) {
945 continueFunc(cbInfo.jsState());
946 });
947}
948
949void AppFunctionsImpl::startNoUiChildProcess(
950 JsState &jsState, const std::string &libraryName, const std::vector<std::string> &args)
951{
952 const auto *childProcessSrcEntry = "./ets/process/QChildProcess.ets";
953
954 // FIXME:
955 // We want to use childProcessManager.StartMode.APP_SPAWN_FORK here, but the "StartMode"
956 // is defined as "const enum" in the TS code, which makes it a compile-time-only thing
957 // (contrary to non-const enums, which are real objects, const enums don't exist at runtime).
958 // We should consider adding a separate mechanism for handling const enums in the code
959 // if we have more of them in the future.
960 constexpr int startModeAppSpawnFork = 1;
961
962 auto appContextDirs = AppContextDirs::mapFromQOhosAppContextProperties(QOhosAppContext::getAllProperties());
963
964 QJsonArray argsArray;
965 std::transform(args.begin(), args.end(), std::back_inserter(argsArray), QString::fromStdString);
966
967 QJsonObject childSetupJson = {
968 {QString::fromUtf8("appContext"), appContextDirs.mapToQJsonObject()},
969 {QString::fromUtf8("appName"), QString::fromStdString(libraryName)},
970 {QString::fromUtf8("appArgs"), argsArray},
971 };
972
973 jsState.eval(
974 "@ohos.app.ability.childProcessManager.startChildProcess(*)",
975 {
976 childProcessSrcEntry,
977 startModeAppSpawnFork,
978 [childSetupJson](const CallbackInfo &cbInfo) {
979 QNapi::Value error;
980 QNapi::Value data;
981 cbInfo.getLeadingArgs(Q_FUNC_INFO, error, data);
982
983 int childPid = data.IsNumber()
984 ? QNapi::checkedCast<QNapi::Number>(data)
985 : -1;
986
987 if (childPid > 0) {
988 qOhosPrintfDebug("%s: child started: %d", Q_FUNC_INFO, childPid);
989 sendChildProcessSetupData(childPid, childSetupJson);
990 } else {
991 qOhosPrintfError("%s: child NOT started", Q_FUNC_INFO);
992 }
993 },
994 });
995}
996
997void handleAbilityOnForeground(const CallbackInfo &cbInfo)
998{
999 const auto qAbility = cbInfo.getFirstArg<QNapi::Object>(Q_FUNC_INFO);
1000
1001 if (foregroundAbilities.empty()) {
1002 QtOhos::invokeInQtThread([]() {
1003 const auto setQosRes = OH_QoS_SetThreadQoS(QoS_Level::QOS_USER_INTERACTIVE);
1004 if (setQosRes != 0) {
1005 qOhosWarning(QtForOhos)
1006 << "Setting QoS level of Qt thread to USER_INTERACTIVE failed, error code:"
1007 << setQosRes;
1008 }
1009 updateApplicationState(Qt::ApplicationActive);
1010 });
1011 }
1012
1013 foregroundAbilities.push_back(Napi::Persistent(qAbility));
1014}
1015
1016void handleAbilityOnBackground(const CallbackInfo &cbInfo)
1017{
1018 const auto qAbility = cbInfo.getFirstArg<QNapi::Object>(Q_FUNC_INFO);
1019
1020 const auto qAbilityRef = Napi::Persistent(qAbility);
1021 foregroundAbilities.erase(
1022 std::remove(foregroundAbilities.begin(), foregroundAbilities.end(), qAbilityRef),
1023 foregroundAbilities.end());
1024
1025 if (foregroundAbilities.empty()) {
1026 QtOhos::invokeInQtThread([]() {
1027 const auto resetQosRes = OH_QoS_ResetThreadQoS();
1028 if (resetQosRes != 0) {
1029 qOhosWarning(QtForOhos)
1030 << "Resetting QoS level of Qt thread failed, error code:"
1031 << resetQosRes;
1032 }
1033 updateApplicationState(Qt::ApplicationHidden);
1034 updateApplicationState(Qt::ApplicationInactive);
1035 });
1036 }
1037}
1038
1039QNapi::Value handleAbilityOnContinue(const CallbackInfo &cbInfo)
1040{
1041 QNapi::Object qAbility;
1042 QNapi::Object wantParamsObj;
1043 cbInfo.getLeadingArgs(Q_FUNC_INFO, qAbility, wantParamsObj);
1044
1046 cbInfo.jsState().tryGetQAbilityPeerByInstance(qAbility));
1047 if (!uiAbilityPeer) {
1048 qOhosPrintfWarning("%s: got unknown Ability, rejecting", Q_FUNC_INFO);
1049 return makeResolvedPromise(
1050 cbInfo.jsState().mapOhosEnumToJs(
1051 QOhosAbilityOnContinueResult::REJECT));
1052 }
1053
1054 return adaptAsyncCallResultToJsPromise<QOhosAbilityOnContinueResult>(
1055 cbInfo.jsState(),
1056 [](QOhosJsState &jsState, auto result) {
1057 return jsState.mapOhosEnumToJs(result);
1058 },
1059 [&](QOhosJsState &jsState, auto resultConsumer) {
1060 getQAbilityInstancesManager().getAbilityPeerBackend(uiAbilityPeer)->handleOnContinueRequestFromSystem(
1061 static_cast<JsState &>(jsState), wantParamsObj, std::move(resultConsumer));
1062 });
1063}
1064
1065std::string targetLibraryDirectory() {
1066 #if defined(Q_PROCESSOR_ARM_64)
1067 return "/libs/arm64";
1068 #elif defined(Q_PROCESSOR_ARM_32)
1069 return "/libs/arm";
1070 #elif defined(Q_PROCESSOR_X86_64)
1071 return "/libs/x86_64";
1072 #else
1073 #error "Unknown system architecture, aborting!"
1074 #endif
1075}
1076
1077void tryDetectBrokenWant(JsState &jsState, QNapi::Object want)
1078{
1079 Napi::HandleScope checkScope(want.Env());
1080
1081 auto optDefaultQAbility = jsState.defaultQAbility();
1082
1083 if (optDefaultQAbility) {
1084 bool fromThisApp = getQAbilityInstancesManager().isWantFromThisApp(optDefaultQAbility.value(), want);
1085 auto optCallerPid = getWantParamOrEmptyIfNotPresent<QNapi::Number>(want, callerPidWantArgName);
1086 if (fromThisApp && !optCallerPid.IsEmpty() && ::kill(optCallerPid.Int64Value(), 0) != 0) {
1087 qOhosPrintfError(
1088 "%s: got Want from non-existing app process (pid: %lld), which most likely means that we received"
1089 " broken Want (platform bug). That's fatal error for us!",
1090 Q_FUNC_INFO, static_cast<long long>(optCallerPid.Int64Value()));
1091 std::abort();
1092 }
1093 }
1094}
1095
1096std::string readInitialBytesOfFile(const std::string &filePath, std::size_t maxReadSize)
1097{
1098 FILE *inputFile = std::fopen(filePath.c_str(), "rb");
1099 if (inputFile == nullptr) {
1100 auto fopenErrno = errno;
1101 qOhosReportFatalErrorAndAbort(
1102 "%s: can't open file '%s': %s",
1103 Q_FUNC_INFO, filePath.c_str(), std::strerror(fopenErrno));
1104 }
1105
1106 std::unique_ptr<FILE, decltype(&std::fclose)> inputFileCloseGuard(inputFile, &std::fclose);
1107
1108 auto readBuffer = std::string(maxReadSize, '\0');
1109
1110 auto bytesRead = std::fread(&readBuffer[0], 1, maxReadSize, inputFile);
1111 if (std::ferror(inputFile)) {
1112 qOhosReportFatalErrorAndAbort(
1113 "%s: error reading '%s': (%zu bytes read)",
1114 Q_FUNC_INFO, filePath.c_str(), bytesRead);
1115 }
1116
1117 readBuffer.resize(bytesRead);
1118 readBuffer.shrink_to_fit();
1119
1120 return readBuffer;
1121}
1122
1123std::string readCurrentProcessNameFromProcFs()
1124{
1125 const auto *procCmdlinePath = "/proc/self/cmdline";
1126 auto procCmdlineData = readInitialBytesOfFile(procCmdlinePath, 64 * 1024);
1127
1128 auto processNameTerminatorPos = procCmdlineData.find('\0');
1129 if (processNameTerminatorPos == std::string::npos)
1130 qOhosReportFatalErrorAndAbort("%s: found unexpected content in '%s'", Q_FUNC_INFO, procCmdlinePath);
1131
1132 procCmdlineData.resize(processNameTerminatorPos);
1133 procCmdlineData.shrink_to_fit();
1134
1135 return procCmdlineData;
1136}
1137
1138bool isThisEmbeddedUIExtensionProcess(const QNapi::Object &appContext)
1139{
1140 std::string appName = appContext.eval<QNapi::String>("applicationInfo.name");
1141 return readCurrentProcessNameFromProcFs() == appName + ":embeddedUI";
1142}
1143
1144void handleAbilityStageOnCreate(const CallbackInfo &cbInfo)
1145{
1146 auto abilityStage = cbInfo.getFirstArg<QNapi::Object>(Q_FUNC_INFO);
1147
1148 if (qEnvironmentVariableIntValue(enableHotStartEnvVariableName) != 0) {
1149 auto appContext = abilityStage.eval<QNapi::Object>("context.getApplicationContext()");
1150 if (!isThisEmbeddedUIExtensionProcess(appContext)) {
1151 try {
1152 appContext.eval<QNapi::Value>("setSupportedProcessCache(*)", {true});
1153 s_hotStartEnabled = true;
1154 } catch (const Napi::Error &error) {
1155 qOhosPrintfError("setSupportedProcessCache() failed with error: %s", error.what());
1156 }
1157 }
1158 }
1159
1160 qOhosPrintfInfo(
1161 "AbilityStage::onCreate: hot start enabled: %s",
1163}
1164
1165QNapi::Value handleAbilityStageOnNewProcessRequest(const CallbackInfo &cbInfo)
1166{
1167 auto __dbg = make_QCScopedDebugJS(Q_FUNC_INFO);
1168
1169 QNapi::Object qAbilityStage;
1170 QNapi::Object want;
1171 cbInfo.getLeadingArgs(Q_FUNC_INFO, qAbilityStage, want);
1172
1173 qOhosPrintfInfo("AbilityStage::onNewProcessRequest: input Want: %s", QNapi::toJsonString(want).c_str());
1174
1175 tryDetectBrokenWant(cbInfo.jsState(), want);
1176
1177 auto optProcessIdParam = getWantParamOrEmptyIfNotPresent<QNapi::String>(want, qtAppProcessIdWantArgName);
1178
1179 std::string processId = !optProcessIdParam.IsEmpty() ? optProcessIdParam : std::string();
1180
1181 qOhosPrintfInfo("AbilityStage::onNewProcessRequest: returning processId='%s'", processId.c_str());
1182
1183 return QNapi::String::New(cbInfo.Env(), processId);
1184}
1185
1186QNapi::Value handleAbilityStageOnAcceptWant(const CallbackInfo &cbInfo)
1187{
1188 using namespace std::string_literals;
1189
1190 QNapi::Object qAbilityStage;
1191 QNapi::Object want;
1192 cbInfo.getLeadingArgs(Q_FUNC_INFO, qAbilityStage, want);
1193
1194 qOhosPrintfInfo("AbilityStage::onAcceptWant: input Want: %s", QNapi::toJsonString(want).c_str());
1195
1196 tryDetectBrokenWant(cbInfo.jsState(), want);
1197
1198 auto optProcessIdParam = getWantParamOrEmptyIfNotPresent<QNapi::String>(want, qtAppProcessIdWantArgName);
1199 if (!optProcessIdParam.IsEmpty()) {
1200 std::string processIdParamStr = QNapi::checkedCast<QNapi::String>(optProcessIdParam);
1201 auto instanceId = "//"s + processIdParamStr;
1202 qOhosPrintfInfo("AbilityStage::onAcceptWant: returning instanceId='%s'", instanceId.c_str());
1203 return QNapi::String::New(cbInfo.Env(), instanceId);
1204 }
1205
1206 std::shared_ptr<QAbilityPeer> defaultQAbilityPeer = cbInfo.jsState().defaultQAbilityPeer();
1207
1208 auto defaultQAbility = defaultQAbilityPeer->qAbility();
1209 auto receivedQAbilityInstanceId =
1210 !defaultQAbility.IsEmpty()
1211 ? getQAbilityInstancesManager().tryGetQAbilityInstanceIdFromWant(defaultQAbility, want)
1212 : std::nullopt;
1213
1214 auto qAbilityInstanceId =
1215 receivedQAbilityInstanceId.has_value()
1216 ? receivedQAbilityInstanceId.value()
1217 : getQAbilityInstancesManager().pendingAutoStartedInstanceId().value_or(
1218 defaultQAbilityPeer->instanceId());
1219
1220 qOhosPrintfInfo("AbilityStage::onAcceptWant: returning instanceId='%s'", qAbilityInstanceId.c_str());
1221
1222 return QNapi::String::New(cbInfo.Env(), qAbilityInstanceId);
1223}
1224
1225void asyncRunTaskInTemporaryThread(std::function<void()> task, std::function<void(JsState &)> continueFunc)
1226{
1227 auto taskRunnerThread = std::thread(
1228 [task = std::move(task), continueFunc = std::move(continueFunc)]() {
1229 task();
1230 QtOhos::invokeInJsThread(std::move(continueFunc));
1231 });
1232 taskRunnerThread.detach();
1233}
1234
1235void asyncRunTaskInTemporaryThreadWithTimeout(
1236 QOhosJsState &jsState, std::function<void()> task, std::chrono::milliseconds waitTimeout,
1237 QOhosConsumer<QOhosJsState &, bool> successConsumer)
1238{
1239 auto successNotifyFunc = moveToSharedPtr(
1240 makeCallOnceConsumerWrapper<QOhosJsState &, bool>(
1241 std::move(successConsumer)));
1242
1243 setJsTimeout(
1244 jsState,
1245 [successNotifyFunc](const QOhosCallbackInfo &cbInfo) {
1246 (*successNotifyFunc)(cbInfo.jsState(), false);
1247 },
1248 waitTimeout);
1249
1250 asyncRunTaskInTemporaryThread(
1251 std::move(task),
1252 [successNotifyFunc](JsState &jsState) {
1253 (*successNotifyFunc)(jsState, true);
1254 });
1255}
1256
1257QNapi::Value handleAbilityStageOnPrepareTerminationAsync(const CallbackInfo &cbInfo)
1258{
1259 qOhosPrintfInfo("%s", Q_FUNC_INFO);
1260
1261 return makeResolvedPromise(
1262 cbInfo.jsState().eval<QNapi::Number>(
1263 "@ohos.app.ability.AbilityConstant.PrepareTermination.TERMINATE_IMMEDIATELY"));
1264}
1265
1266void handleAbilityStageOnDestroy(const CallbackInfo &)
1267{
1268 qOhosPrintfInfo("AbilityStage::onDestroy: start");
1269
1270 qOhosPrintfInfo("AbilityStage::onDestroy: destroying the Qt thread object");
1272
1273 if (!s_exitCodeFilePath.empty()) {
1274 if (FILE *f = fopen(s_exitCodeFilePath.c_str(), "w")) {
1275 fprintf(f, "%d\n", s_appExitCode);
1276 fclose(f);
1277 }
1278 }
1279
1280 qOhosPrintfInfo("AbilityStage::onDestroy: calling _Exit(%d)", s_appExitCode);
1281 std::_Exit(s_appExitCode);
1282}
1283
1284void handleAbilityOnNewWant(const CallbackInfo &cbInfo)
1285{
1286 QNapi::Object qAbility;
1287 QNapi::Object want;
1288 QNapi::Object launchParam;
1289 cbInfo.getLeadingArgs(Q_FUNC_INFO, qAbility, want, launchParam);
1290
1291 qOhosPrintfDebug(
1292 "%s: input Want: %s, launchParam: %s",
1293 Q_FUNC_INFO, QNapi::toJsonString(want).c_str(), QNapi::toJsonString(launchParam).c_str());
1294
1295 tryDetectBrokenWant(cbInfo.jsState(), want);
1296
1297 if (!QAbilityInstancesManager::isQtInternalWantFromThisProcess(want))
1298 dispatchNewWant(qAbility, want, launchParam);
1299 else
1300 qOhosPrintfDebug("%s: received qt-internal Want from current process, nothing to do", Q_FUNC_INFO);
1301}
1302
1303void loadWindowStageContentPage(JsState &jsState, QNapi::Object &qAbility, const QNapi::Object &windowStage)
1304{
1305 auto *jsEnv = jsState.env();
1306
1307 auto qAbilityInstanceId = getQAbilityInstancesManager().getQAbilityInstanceIdOrPendingAutoStartedId(qAbility);
1308 auto xComponentId = QXComponentId::createForNativeNodeMainWindow(qAbilityInstanceId);
1309
1310 auto qAbilityRef = moveToSharedPtr(QNapi::Reference<>::makePersistentFrom(qAbility));
1311 auto windowStageRef = moveToSharedPtr(QNapi::Reference<>::makePersistentFrom(windowStage));
1312
1313 auto localStorage = jsState.eval<QNapi::Object>("LocalStorage.makeNewLocalStorage()");
1314 localStorage.eval(
1315 "setOrCreate(*)",
1316 {
1317 "createInfo",
1318 QNapi::makeObject(
1319 jsEnv,
1320 {
1321 {"xComponentId", xComponentId.toNapiValue(jsEnv)},
1322 {"onDisAppear", []() {}},
1323 {
1324 "onAttach",
1325 [qAbilityRef, windowStageRef](const QtOhos::CallbackInfo &cbInfo) {
1326 getQAbilityInstancesManager().handleStartedUiInstance(
1327 cbInfo.jsState(), qAbilityRef->Value(), windowStageRef->Value());
1328 }},
1329 {
1330 "onAppear",
1331 [xComponentId]() {
1332 qOhosPrintfDebug("XComponentId: %s onAppear", xComponentId.stringId().c_str());
1333 }
1334 },
1335 }),
1336 });
1337 qAbility.set("localStorage", localStorage);
1338
1339 const std::string mainWindowNativeNodePagePath = "pages/MainWindowNativeNode";
1340 windowStage.evalToPromiseOrRejectOnThrow("loadContent(*)", {mainWindowNativeNodePagePath, localStorage})
1341 .onThen([qAbilityRef](const CallbackInfo &) {
1342 auto launchWant = qAbilityRef->eval<QNapi::Object>("launchWant");
1343 qOhosPrintfDebug("%s: launchWant: %s", Q_FUNC_INFO, QNapi::toJsonString(launchWant).c_str());
1344 })
1345 .onCatch([qAbilityInstanceId](const CallbackInfo &cbInfo) {
1346 QtOhos::logJsCallbackError(cbInfo, "windowStage.loadContent failed");
1347 qOhosReportFatalErrorAndAbort(
1348 "%s: Failed to loadContent for QAbility instance(qAbilityInstanceId: %s)", Q_FUNC_INFO, qAbilityInstanceId.c_str());
1349 });
1350}
1351
1352QNapi::Symbol getAbilityWindowStageCreatedOrRestoredPropSymbol(JsState &jsState)
1353{
1354 struct Symbol
1355 {
1356 };
1357 return jsState.getJsSymbolForType<Symbol>();
1358}
1359
1360void handleAbilityOnWindowStageCreate(const CallbackInfo &cbInfo)
1361{
1362 QNapi::Object qAbility;
1363 QNapi::Object windowStage;
1364 cbInfo.getLeadingArgs(Q_FUNC_INFO, qAbility, windowStage);
1365
1366 qAbility.set(getAbilityWindowStageCreatedOrRestoredPropSymbol(cbInfo.jsState()), true);
1367 loadWindowStageContentPage(cbInfo.jsState(), qAbility, windowStage);
1368}
1369
1370void handleAbilityOnWindowStageRestore(const CallbackInfo &cbInfo)
1371{
1372 QNapi::Object qAbility;
1373 QNapi::Object windowStage;
1374 cbInfo.getLeadingArgs(Q_FUNC_INFO, qAbility, windowStage);
1375
1376 auto optCreatedOrRestoredProp = QNapi::getOptionalPropOrEmpty<QNapi::Boolean>(
1377 qAbility, getAbilityWindowStageCreatedOrRestoredPropSymbol(cbInfo.jsState()));
1378 bool createdOrRestoredProp = !optCreatedOrRestoredProp.IsEmpty()
1379 ? optCreatedOrRestoredProp.Value()
1380 : false;
1381
1382 if (!createdOrRestoredProp) {
1383 qAbility.set(getAbilityWindowStageCreatedOrRestoredPropSymbol(cbInfo.jsState()), true);
1384 loadWindowStageContentPage(cbInfo.jsState(), qAbility, windowStage);
1385 } else {
1386 qOhosPrintfDebug(
1387 "%s: window page already created or restored. Skip page reloading", Q_FUNC_INFO);
1388 }
1389}
1390
1391void handleAbilityOnWindowStageDestroy(const CallbackInfo &)
1392{
1393}
1394
1395QNapi::Value handleAbilityOnPrepareToTerminate(const CallbackInfo &cbInfo)
1396{
1397 auto __dbg = make_QCScopedDebugJS(Q_FUNC_INFO);
1398 auto ability = cbInfo.getFirstArg<QNapi::Object>(Q_FUNC_INFO);
1399
1400 auto abilityPeer = cbInfo.jsState().tryGetQAbilityPeerByInstance(ability);
1401 if (!abilityPeer) {
1402 qOhosPrintfWarning("%s: unrecognized ability, returning immediately", Q_FUNC_INFO);
1403 return QNapi::Boolean::New(cbInfo.Env(), false);
1404 }
1405
1406 bool destroyAllowed = abilityPeer->destroyAllowedFlag()->load();
1407 qOhosPrintfDebug(
1408 "%s: ability id: '%s', destroyAllowed=%s",
1409 Q_FUNC_INFO, abilityPeer->instanceId().c_str(), mapBoolToTrueFalseStr(destroyAllowed));
1410
1411 if (!destroyAllowed) {
1412 QtOhos::invokeInQtThread(
1413 [qwindowRef = abilityPeer->qWindowRef()]() {
1414 auto *qwindow = qobject_cast<QWindow *>(qwindowRef.data());
1415 if (qwindow != nullptr) {
1416 qOhosPrintfInfo("handleAbilityOnPrepareToTerminate: calling QWindow::close()");
1417 QOhosCloseEventContext::runWithCloseRootCauseSet(
1418 QOhosCloseEventContext::CloseRootCause::OnPrepareToTerminate,
1419 [&]() {
1420 qwindow->close();
1421 });
1422 } else {
1423 qOhosPrintfDebug("%s: QWindow is null", Q_FUNC_INFO);
1424 }
1425 });
1426 }
1427
1428 return QNapi::Boolean::New(cbInfo.Env(), !destroyAllowed);
1429}
1430
1431QNapi::Value handleAbilityOnPrepareToTerminateAsync(const CallbackInfo &cbInfo)
1432{
1433 auto __dbg = make_QCScopedDebugJS(Q_FUNC_INFO);
1434
1435 auto qAbility = cbInfo.getFirstArg<QNapi::Object>(Q_FUNC_INFO);
1436
1437 JsState &jsState = cbInfo.jsState();
1438
1439 auto optQUiAbilityPeer = QUiAbilityPeer::tryCastFromQAbilityPeerOrNull(
1440 jsState.tryGetQAbilityPeerByInstance(qAbility));
1441
1442 if (optQUiAbilityPeer) {
1443 return getQAbilityInstancesManager().getAbilityPeerBackend(optQUiAbilityPeer)->handleCloseRequestFromSystem(
1444 jsState, "UIAbility::onPrepareToTerminateAsync",
1446 [](QOhosJsState &jsState, QUiAbilityPeerBackend::CloseAbilityRequestResolution ohosRequestResolution) {
1447 return QNapi::Boolean::New(
1448 jsState.env(),
1449 ohosRequestResolution == QUiAbilityPeerBackend::CloseAbilityRequestResolution::DontClose);
1450 });
1451 } else {
1452 qOhosPrintfWarning("%s: no matching QAbilityPeer, resolving immediately with 'false'", Q_FUNC_INFO);
1453 return makeResolvedPromise(QNapi::Boolean::New(cbInfo.Env(), false));
1454 }
1455}
1456
1457void handleAbilityOnCreate(const CallbackInfo &cbInfo)
1458{
1459 auto __dbg = make_QCScopedDebugJS(Q_FUNC_INFO);
1460
1461 QNapi::Object ability;
1462 QNapi::Object want;
1463 QNapi::Object launchParam;
1464 cbInfo.getLeadingArgs(Q_FUNC_INFO, ability, want, launchParam);
1465
1466 QAbilityInstancesManager::setLaunchParamOnAbilityObject(cbInfo.jsState(), ability, launchParam);
1467}
1468
1469QNapi::Value handleAbilityOnDestroy(const CallbackInfo &cbInfo)
1470{
1471 auto __dbg = make_QCScopedDebugJS(Q_FUNC_INFO);
1472
1473 auto qAbility = cbInfo.getFirstArg<QNapi::Object>(Q_FUNC_INFO);
1474
1475 auto qAbilityPeer = cbInfo.jsState().tryGetQAbilityPeerByInstance(qAbility);
1476 if (!qAbilityPeer) {
1477 qOhosPrintfDebug("%s: no matching QAbilityPeer, returning resolved Promise", Q_FUNC_INFO);
1478 return makeResolvedPromise(cbInfo.Env());
1479 }
1480
1481 auto optQWindowDestroyPromise = qAbilityPeer->qWindowDestroyPromise();
1482
1483 auto initialPromise = optQWindowDestroyPromise.has_value()
1484 ? optQWindowDestroyPromise.value()
1485 : makeResolvedPromise(cbInfo.Env());
1486
1487 auto resultPromiseDeferred = std::make_shared<QNapi::Promise::Deferred>(cbInfo.Env());
1488
1489 initialPromise.onFinally(
1490 [qAbilityPeer, resultPromiseDeferred](const CallbackInfo &cbInfo) {
1491 qOhosPrintfDebug("%s: initial Promise resolved for id='%s'", Q_FUNC_INFO, qAbilityPeer->instanceId().c_str());
1492
1493 QtOhos::removeMatchingJsQAbilityPeer(qAbilityPeer->qAbility());
1494
1495 if (!cbInfo.jsState().defaultQAbility()) {
1497
1498 qOhosPrintfInfo("Qt: requested Qt app quit, waiting for the main() function to return");
1499
1500 constexpr auto resultPromiseAutoresolveTimeout = 5s;
1501
1502 asyncRunTaskInTemporaryThreadWithTimeout(
1503 cbInfo.jsState(),
1504 []() {
1506 },
1507 resultPromiseAutoresolveTimeout,
1508 [instanceId = qAbilityPeer->instanceId(), resultPromiseDeferred](QOhosJsState &jsState, bool threadExited) {
1509 qOhosPrintfInfo(
1510 "Qt: end waiting for Qt app's main() function, returned: %s",
1511 mapBoolToTrueFalseStr(threadExited));
1512 qOhosPrintfDebug("%s: resolving result Promise for id='%s'", Q_FUNC_INFO, instanceId.c_str());
1513 resultPromiseDeferred->Resolve(Napi::Env(jsState.env()).Undefined());
1514 });
1515 } else {
1516 resultPromiseDeferred->Resolve(cbInfo.Env().Undefined());
1517 }
1518 });
1519
1520 qOhosPrintfDebug("%s: returning Promise for id='%s'", Q_FUNC_INFO, qAbilityPeer->instanceId().c_str());
1521
1522 return resultPromiseDeferred->Promise();
1523}
1524
1525void initDeviceInfo(JsState &jsState)
1526{
1527 using Type = QOhosDeviceInfo::Type;
1528
1529 static const std::pair<Type, const char *> strPropertiesMap[] = {
1530 {Type::deviceType, "deviceType"},
1531 {Type::manufacture, "manufacture"},
1532 {Type::brand, "brand"},
1533 {Type::marketName, "marketName"},
1534 {Type::productSeries, "productSeries"},
1535 {Type::productModel, "productModel"},
1536 {Type::softwareModel, "softwareModel"},
1537 {Type::hardwareModel, "hardwareModel"},
1538 {Type::hardwareProfile, "hardwareProfile"},
1539 {Type::serial, "serial"},
1540 {Type::bootloaderVersion, "bootloaderVersion"},
1541 {Type::abiList, "abiList"},
1542 {Type::securityPatchTag, "securityPatchTag"},
1543 {Type::displayVersion, "displayVersion"},
1544 {Type::incrementalVersion, "incrementalVersion"},
1545 {Type::osReleaseType, "osReleaseType"},
1546 {Type::osFullName, "osFullName"},
1547 {Type::versionId, "versionId"},
1548 {Type::buildType, "buildType"},
1549 {Type::buildUser, "buildUser"},
1550 {Type::buildHost, "buildHost"},
1551 {Type::buildTime, "buildTime"},
1552 {Type::buildRootHash, "buildRootHash"},
1553 {Type::udid, "udid"},
1554 {Type::distributionOSName, "distributionOSName"},
1555 {Type::distributionOSVersion, "distributionOSVersion"},
1556 {Type::distributionOSReleaseType, "distributionOSReleaseType"},
1557 };
1558
1559 static const std::pair<Type, const char *> intPropertiesMap[] = {
1560 {Type::majorVersion, "majorVersion"},
1561 {Type::seniorVersion, "seniorVersion"},
1562 {Type::featureVersion, "featureVersion"},
1563 {Type::buildVersion, "buildVersion"},
1564 {Type::sdkApiVersion, "sdkApiVersion"},
1565 {Type::firstApiVersion, "firstApiVersion"},
1566 {Type::distributionOSApiVersion, "distributionOSApiVersion"},
1567 };
1568
1569 auto deviceInfoObj = jsState.eval<QNapi::Object>("@ohos.deviceInfo");
1570
1571 QMap<Type, QVariant> deviceInfo;
1572 for (const auto &propEntry : strPropertiesMap)
1573 deviceInfo[propEntry.first] = QString::fromStdString(deviceInfoObj.get<QNapi::String>(propEntry.second));
1574 for (const auto &propEntry : intPropertiesMap)
1575 deviceInfo[propEntry.first] = static_cast<int>(deviceInfoObj.get<QNapi::Number>(propEntry.second));
1576
1577 QOhosDeviceInfo::init(std::move(deviceInfo));
1578}
1579
1580void initAppData(JsState &jsState, QNapi::Object appContext)
1581{
1582 initDeviceInfo(jsState);
1583
1584 auto systemLocaleId = QString::fromStdString(
1585 jsState.eval<QNapi::String>("@ohos.intl.Locale<new>().toString()"));
1586 auto systemPreferredLanguages = QNapi::getArrayElements<QStringList, QNapi::String>(
1587 jsState.eval<QNapi::Array>("@ohos.i18n.System.getPreferredLanguageList()"),
1588 &QString::fromStdString);
1589 QtOhos::invokeInQtThread(
1590 [systemLocaleId, systemPreferredLanguages]() {
1591 QOhosPlatformIntegration::setSystemLocale(new QOhosSystemLocale(systemLocaleId, systemPreferredLanguages));
1592 });
1593
1595 appContext.eval("setColorMode(*)", {jsState.mapOhosEnumToJs(defaultColorMode)});
1596}
1597
1598std::string buildFcLangEnvVariableValue(JsState &jsState)
1599{
1600 std::string language = jsState.eval<QNapi::String>("@ohos.intl.Locale<new>().language");
1601 std::string region = jsState.eval<QNapi::String>("@ohos.intl.Locale<new>().region");
1602
1603 return language + "_" + region + ".UTF-8";
1604}
1605
1606std::shared_ptr<QAbilityEngine> makeAbilityEngineForQtRunMode(QtRunMode qtRunMode)
1607{
1608 switch (qtRunMode) {
1609 case QtRunMode::Normal:
1611 return std::make_shared<QUiAbilityEngine>();
1612 }
1613
1614 qOhosReportFatalErrorAndAbort("%s: Invalid qtRunMode: %d", Q_FUNC_INFO, static_cast<int>(qtRunMode));
1615}
1616
1617void setupQtApplicationImpl(JsState &jsState, QNapi::Object appStartupObj, QtRunMode qtRunMode)
1618{
1619 auto appContext = appStartupObj.get<QNapi::Object>("appContext");
1620 auto appContextDirs = AppContextDirs::mapFromNapiObject(appContext);
1621 if (appContextDirs.resourceDir.empty()) {
1622 auto optResourceDirProp = QNapi::getOptionalPropOrEmpty<QNapi::String>(appStartupObj, "resourceDir");
1623 std::string resourceDir = !optResourceDirProp.IsEmpty() ? optResourceDirProp : std::string();
1624 appContextDirs.resourceDir = !resourceDir.empty()
1625 ? resourceDir
1626 : appContextDirs.bundleCodeDir + "/entry/resources/resfile";
1627 }
1628
1629 auto jsModulesFactories = appStartupObj.get<QNapi::Object>("modulesFactories");
1630
1631 qOhosPrintfDebug("%s: setting up Qt in %s mode", Q_FUNC_INFO, mapQtRunModeToString(qtRunMode));
1632
1633 getQAbilityInstancesManagerPtr() =
1634 makeQAbilityInstancesManager(
1635 makeAbilityEngineForQtRunMode(qtRunMode),
1636 &handleDefaultQAbilityInstanceStartup);
1637
1638 currentQtRunMode = qtRunMode;
1640 jsModulesFactories.Env(), makeJsModulesFactoriesMap(jsModulesFactories),
1641 std::make_shared<AppFunctionsImpl>(), qtRunMode);
1642
1643 QOhosAppContext::init(appContextDirs.mapToQOhosAppContextProperties());
1644 initAppData(jsState, appContext);
1645
1646 const auto ohosSdkApiVersion = QOhosDeviceInfo::sdkApiVersion();
1647 if (ohosSdkApiVersion < minSupportedOhosSdkApiVersion) {
1648 qOhosReportFatalErrorAndAbort(
1649 "%s: unsupported OHOS version! Current API version: %d, minimum supported version: %d. Aborting.",
1650 Q_FUNC_INFO, ohosSdkApiVersion, minSupportedOhosSdkApiVersion);
1651 }
1652
1653 const auto recognizedDeviceType = QOhosDeviceInfo::tryGetRecognizedDeviceType();
1654 if (!recognizedDeviceType.has_value()) {
1655 qOhosReportFatalErrorAndAbort(
1656 "%s: Unrecognized device type: %s. Qt does not support unrecognized devices. Aborting.",
1657 Q_FUNC_INFO, qPrintable(QOhosDeviceInfo::getProperty(QOhosDeviceInfo::Type::deviceType).toString()));
1658 }
1659
1660 bool allowUnsupportedDevices =
1661 qEnvironmentVariable("QT_IO_EXPERIMENTAL_ALLOW_UNSUPPORTED_DEVICES", {})
1662 == QLatin1String("true");
1663 if (!QOhosDeviceInfo::isCurrentDeviceSupported() && !allowUnsupportedDevices) {
1664 qOhosReportFatalErrorAndAbort(
1665 "%s: Unsupported device type: %s. Aborting.",
1666 Q_FUNC_INFO, qPrintable(QOhosDeviceInfo::getProperty(QOhosDeviceInfo::Type::deviceType).toString()));
1667 }
1668
1669 s_appSharedLibName = appStartupObj.get<QNapi::String>("appName");
1670
1671 qOhosPrintfDebug("setupQtApplication() - sharedLibraryName: %s", s_appSharedLibName.c_str());
1672 qOhosPrintfDebug("setupQtApplication() - bundleCodeDir: %s", appContextDirs.bundleCodeDir.c_str());
1673
1674 s_appSharedLibsDirPath = appContextDirs.bundleCodeDir + targetLibraryDirectory();
1675 qOhosPrintfDebug("setupQtApplication() - Shared libraries directory: %s", s_appSharedLibsDirPath.c_str());
1676
1677 auto jsAppArgs = QNapi::getPropOrUndefined(appStartupObj, "appArgs");
1678 if (jsAppArgs.IsArray()) {
1679 s_appArgs = std::make_unique<std::vector<std::string>>(
1680 QNapi::getArrayElements<std::vector<std::string>, QNapi::String>(
1681 QNapi::checkedCast<QNapi::Array>(jsAppArgs)));
1682 }
1683
1684 struct {
1685 const char *variable;
1686 std::string value;
1687 } env_variables[] = {
1688 {"QT_QPA_PLATFORM_PLUGIN_PATH", s_appSharedLibsDirPath },
1689 {"QML_DISABLE_DISK_CACHE", "1"},
1690 {"QML2_IMPORT_PATH", appContextDirs.resourceDir + "/qml" },
1691 // FIXME: temporary measure for preventing QtQuick2-based apps from crashing
1692 {"QV4_FORCE_INTERPRETER", "1"},
1693 {"QT_PRINTER_MODULE", "ohosprintersupport"},
1694 {"TMPDIR", QOhosAppContext::getProperty(QOhosAppContext::Type::tempDir).toStdString()},
1695 {"HOME", QOhosAppContext::getProperty(QOhosAppContext::Type::filesDir).toStdString()},
1696 {"FC_LANG", buildFcLangEnvVariableValue(jsState)},
1697 };
1698
1699 for (const auto &e : env_variables) {
1700 if (::setenv(e.variable, e.value.c_str(), 1) != 0)
1701 qOhosReportFatalErrorAndAbort("%s: Cannot set '%s' environment variable", Q_FUNC_INFO, e.variable);
1702 }
1703
1704 if (::chdir(appContextDirs.filesDir.c_str()) != 0) {
1705 auto chdirErrno = errno;
1706 qOhosPrintfWarning(
1707 "%s: failed to change current directory to '%s': %s",
1708 Q_FUNC_INFO, appContextDirs.filesDir.c_str(), std::strerror(chdirErrno));
1709 }
1710}
1711
1712QtRunMode getQtRunModeFromAppStartupObj(QNapi::Object appStartupObj)
1713{
1714 std::unordered_map<std::string, QtRunMode> abilityClassNameToQtRunModeMap = {
1715 {"QAbility", QtRunMode::Normal},
1716 };
1717
1718 const auto optAbilityClassName = QNapi::getOptionalPropOrEmpty<QNapi::String>(appStartupObj, "abilityClassName");
1719 if (!optAbilityClassName.IsEmpty()) {
1720 const std::string abilityClassName = optAbilityClassName;
1721 if (abilityClassNameToQtRunModeMap.find(abilityClassName) != abilityClassNameToQtRunModeMap.end())
1722 return abilityClassNameToQtRunModeMap[abilityClassName];
1723
1724 qOhosReportFatalErrorAndAbort(
1725 "%s: got unsupported name of the Ability class: '%s'", Q_FUNC_INFO, abilityClassName.c_str());
1726 }
1727
1728 return QtRunMode::Normal;
1729}
1730
1731void setupQtApplication(const CallbackInfo &cbInfo)
1732{
1733 auto appStartupObj = cbInfo.getFirstArg<QNapi::Object>(Q_FUNC_INFO);
1734 auto qtRunMode = getQtRunModeFromAppStartupObj(appStartupObj);
1735 setupQtApplicationImpl(cbInfo.jsState(), appStartupObj, qtRunMode);
1736}
1737
1738void runQtChildProcess(const CallbackInfo &cbInfo)
1739{
1740 auto __dbg = make_QCScopedDebugJS(Q_FUNC_INFO);
1741
1742 auto paramsObj = cbInfo.getFirstArg<QNapi::Object>(Q_FUNC_INFO);
1743
1745
1746 auto childSetupObj = readChildProcessSetupData(cbInfo.Env());
1747 childSetupObj["modulesFactories"] = paramsObj.get<QNapi::Object>("modulesFactories");
1748
1749 setupQtApplicationImpl(cbInfo.jsState(), childSetupObj, QtRunMode::NoUiChildProcess);
1750
1751 auto appArgs = s_appArgs ? *s_appArgs : std::vector<std::string>();
1752
1753 QThread::currentThread();
1754 QtOhos::initQtThreadState();
1755 auto appMainFuncLauncher = makeAppMainFuncLauncher(
1756 {
1757 .appLibraryPath = s_appSharedLibsDirPath + "/" + s_appSharedLibName,
1758 },
1759 [](int) {
1760 });
1761 appMainFuncLauncher(appArgs);
1762}
1763
1764QNapi::Value makeXComponentIdForMainWindowWithQAbilityInstanceId(const CallbackInfo &cbInfo)
1765{
1766 std::string qAbilityInstanceId = cbInfo.getFirstArg<QNapi::String>(Q_FUNC_INFO);
1767 return QXComponentId::createForNativeNodeMainWindow(qAbilityInstanceId).toNapiValue(cbInfo.Env());
1768}
1769
1770QNapi::Value checkIsAdapterCApiSupported(const CallbackInfo &cbInfo)
1771{
1772 constexpr bool adapterCApiSupported = true;
1773 return QNapi::Boolean::New(cbInfo.Env(), adapterCApiSupported);
1774}
1775
1776}
1777
1779{
1780 return currentQtRunMode == QtRunMode::NoUiChildProcess;
1781}
1782
1784{
1785 return vsyncOnSoftwareBackingStoreEnabled;
1786}
1787
1789{
1790 auto __dbg = make_QCScopedDebugJS("quitApplicationFromJsThread");
1791 auto hotStartIterationToQuit = s_hotStartIteration.lastRequestedInJsThread;
1792 QtOhos::invokeInQtThread(
1793 [hotStartIterationToQuit]() {
1794 if (s_hotStartIteration.activeInQtThread == hotStartIterationToQuit)
1795 QCoreApplication::quit();
1796 });
1797}
1798
1800{
1801 qOhosDebug(QtForOhos) << "QOhos updateApplicationState" << state;
1802
1804 return;
1805
1806 if (state <= Qt::ApplicationInactive) {
1807 // NOTE: sometimes we will receive two consecutive suspended notifications,
1808 // In the second suspended notification, QWindowSystemInterface::flushWindowSystemEvents()
1809 // will deadlock since the dispatcher has been stopped in the first suspended notification.
1810 // To avoid the deadlock we simply return if we found the event dispatcher has been stopped.
1812 return;
1813
1814 // Don't send timers and sockets events anymore if we are going to hide all windows
1816 QWindowSystemInterface::handleApplicationStateChanged(Qt::ApplicationState(state));
1817 } else {
1819 QWindowSystemInterface::handleApplicationStateChanged(Qt::ApplicationState(state));
1821 }
1822}
1823
1825{
1826 static bool block = qEnvironmentVariableIntValue("QT_BLOCK_EVENT_LOOPS_WHEN_SUSPENDED") != 0;
1827 return block;
1828}
1829
1831{
1832 return experimentalEnableGlBackinStore;
1833}
1834
1836{
1837 return debugDrawQtRasterBackingStoreFlushedRegion;
1838}
1839
1841{
1842 return debugUseBasicStyleAndTheme;
1843}
1844
1846{
1847 return enableNativeNodeApiKeyEvents;
1848}
1849
1851{
1852 return enableNativeNodeApiMouseEvents;
1853}
1854
1856{
1857 return std::exchange(s_autoStartedAbilityInstanceWaitingForQtWindow, false);
1858}
1859
1860}
1861
1862QT_END_NAMESPACE
1863
1864EXTERN_C_START
1865
1866static napi_value Init(napi_env env, napi_value exports)
1867{
1868 auto __dbg = make_QCScopedDebugJS("qohosjsmain Init");
1869
1870 QNapi::Object(env, exports).DefineProperties(
1871 {
1872 Napi::PropertyDescriptor::Function("handleAbilityStageOnCreate", QtOhos::handleAbilityStageOnCreate),
1873 Napi::PropertyDescriptor::Function("handleAbilityStageOnNewProcessRequest", QtOhos::handleAbilityStageOnNewProcessRequest),
1874 Napi::PropertyDescriptor::Function("handleAbilityStageOnAcceptWant", QtOhos::handleAbilityStageOnAcceptWant),
1875 Napi::PropertyDescriptor::Function("handleAbilityStageOnPrepareTerminationAsync", QtOhos::handleAbilityStageOnPrepareTerminationAsync),
1876 Napi::PropertyDescriptor::Function("handleAbilityStageOnDestroy", QtOhos::handleAbilityStageOnDestroy),
1877 Napi::PropertyDescriptor::Function("handleAbilityOnNewWant", QtOhos::handleAbilityOnNewWant),
1878 Napi::PropertyDescriptor::Function("handleAbilityOnWindowStageCreate", QtOhos::handleAbilityOnWindowStageCreate),
1879 Napi::PropertyDescriptor::Function("handleAbilityOnWindowStageRestore", QtOhos::handleAbilityOnWindowStageRestore),
1880 Napi::PropertyDescriptor::Function("handleAbilityOnWindowStageDestroy", QtOhos::handleAbilityOnWindowStageDestroy),
1881 Napi::PropertyDescriptor::Function("onStageDestroy", QtOhos::handleAbilityOnWindowStageDestroy),
1882 Napi::PropertyDescriptor::Function("handleAbilityOnPrepareToTerminate", QtOhos::handleAbilityOnPrepareToTerminate),
1883 Napi::PropertyDescriptor::Function("handleAbilityOnPrepareToTerminateAsync", QtOhos::handleAbilityOnPrepareToTerminateAsync),
1884 Napi::PropertyDescriptor::Function("handleAbilityOnCreate", QtOhos::handleAbilityOnCreate),
1885 Napi::PropertyDescriptor::Function("handleAbilityOnDestroy", QtOhos::handleAbilityOnDestroy),
1886 Napi::PropertyDescriptor::Function("handleAbilityOnBackground", QtOhos::handleAbilityOnBackground),
1887 Napi::PropertyDescriptor::Function("handleAbilityOnContinue", QtOhos::handleAbilityOnContinue),
1888 Napi::PropertyDescriptor::Function("onBackground", QtOhos::handleAbilityOnBackground),
1889 Napi::PropertyDescriptor::Function("handleAbilityOnForeground", QtOhos::handleAbilityOnForeground),
1890 Napi::PropertyDescriptor::Function("onForeground", QtOhos::handleAbilityOnForeground),
1891 Napi::PropertyDescriptor::Function("setupQtApplication", QtOhos::setupQtApplication),
1892 Napi::PropertyDescriptor::Function("runQtChildProcess", QtOhos::runQtChildProcess),
1893 Napi::PropertyDescriptor::Function(
1894 "makeXComponentIdForMainWindowWithQAbilityInstanceId",
1895 QtOhos::makeXComponentIdForMainWindowWithQAbilityInstanceId),
1896 Napi::PropertyDescriptor::Function("checkIsAdapterCApiSupported", QtOhos::checkIsAdapterCApiSupported),
1897 });
1898
1899 // Here put elements that
1900 QArkUi::QXComponentRegistry::Init(env, exports);
1901
1902 return exports;
1903}
1904
1905EXTERN_C_END
1906
1907extern "C" __attribute__((constructor)) void RegisterEntryModule(void)
1908{
1909 static napi_module qtMainModule = {
1910 .nm_version = 1,
1911 .nm_flags = 0,
1912 .nm_filename = nullptr,
1913 .nm_register_func = Init,
1914 .nm_modname = "qohos",
1915 .nm_priv = nullptr,
1916 .reserved = {nullptr},
1917 };
1918
1919 napi_module_register(&qtMainModule);
1920}
static QOhosEventDispatcherStopper * instance()
static QOhosPlatformIntegration * instance()
static QXComponentId createForNativeNodeMainWindow(const std::string &qAbilityInstanceId)
JsState & jsState() const
virtual void visitEachQAbilityPeer(const std::function< void(std::shared_ptr< QAbilityPeer >)> &visitor)=0
static std::shared_ptr< QUiAbilityPeer > tryCastFromQAbilityPeerOrNull(std::shared_ptr< QAbilityPeer > qAbilityPeer)
bool isCurrentDeviceSupported()
void init(QMap< Type, QVariant > devinfo)
std::optional< RecognizedDeviceType > tryGetRecognizedDeviceType()
Combined button and popup list for selecting options.
std::shared_ptr< void > makeWatchdog()
bool acquireAndCleanPendingAutoStartedInstanceWindowFlag()
std::string const char * mapBoolToTrueFalseStr(bool value)
bool blockEventLoopsWhenSuspended()
void removeMatchingJsQAbilityPeer(QNapi::Object qAbility)
void updateApplicationState(int state)
bool isOhosNoUiChildMode()
void initJsThreadState(napi_env env, std::map< std::string, QNapi::Reference< QNapi::Function > > &&jsModulesFactories, std::shared_ptr< AppFunctions > appFunctions, QtRunMode qtRunMode)
bool isGlBackingStoreDefaultEnabled()
void quitApplicationFromJsThread()
bool isVsyncOnSoftwareBackingStoreEnabled()
bool isNativeNodeApiMouseEventsEnabled()
QtOhosQpa::enums::ohos::app::ability::AbilityConstant::OnContinueResult QOhosAbilityOnContinueResult
bool isNativeNodeApiKeyEventsEnabled()
bool isDebugUseBasicStyleAndThemeEnabled()
bool isDebugDrawQtRasterBackingStoreFlushedRegionEnabled()
static bool s_hotStartEnabled
static bool s_autoStartedAbilityInstanceWaitingForQtWindow
QT_END_NAMESPACE static EXTERN_C_START napi_value Init(napi_env env, napi_value exports)
std::function< void()> s_qtAppThreadIdleStateWaitFunc
std::optional< std::uint64_t > lastRequestedInJsThread
static std::string s_appSharedLibName
static QList< QByteArray > s_applicationParams
static std::unique_ptr< std::vector< std::string > > s_appArgs
int(* Main)(int, char **)
static int s_appExitCode
static std::string s_appSharedLibsDirPath
std::optional< std::uint64_t > activeInQtThread
static std::string s_exitCodeFilePath
QOhosConsumer< std::vector< std::string > > s_qtAppThreadMainFuncLauncher
virtual std::optional< std::string > pendingAutoStartedInstanceId() const =0
virtual std::shared_ptr< QUiAbilityPeerBackend > getAbilityPeerBackend(std::shared_ptr< QUiAbilityPeer > uiAbilityPeer)=0