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
qohosqabilityinstancesmanager.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
5#include <QtCore/private/qnapi_p.h>
6#include <QtCore/private/qohoslogger_p.h>
7#include <QtCore/qcryptographichash.h>
8#include <QtGui/qwindow.h>
9#include <chrono>
10#include <cstdint>
11#include <cstring>
12#include <map>
13#include <qohosdeviceinfo_p.h>
14#include <qohosjsutils.h>
15#include <qohosplatformwindow.h>
16#include <render/qohosview.h>
17#include <typeindex>
18#include <unistd.h>
19
21
22QT_BEGIN_NAMESPACE
23
24using namespace std::chrono_literals;
25
26namespace QtOhos {
27
28namespace {
29
30const auto qtInternalRequestIdWantParamKey = "io.qt.private.internalRequestId";
31const auto qAbilityInstanceIdWantParamKey = "io.qt.private.abilityInstanceId";
32const auto callerAbilityNameWantParamKey = "ohos.aafwk.param.callerAbilityName";
33const auto callerBundleNameWantParamKey = "ohos.aafwk.param.callerBundleName";
34const auto callerPidWantParamKey = "ohos.aafwk.param.callerPid";
35
36std::string getUtf8QtInternalRequestIdForThisProcess()
37{
38 static const auto internalRequestIdCommonData =
39 QByteArray::fromHex("eba08604c86e45ae3690621757b3f4e9");
40
41 std::int32_t thisProcessPid = ::getpid();
42
43 QCryptographicHash hash{QCryptographicHash::Sha1};
44 hash.addData(internalRequestIdCommonData);
45 hash.addData(QByteArray::number(thisProcessPid));
46
47 return hash.result().toHex().toStdString();
48}
49
50QNapi::Symbol getAbilityLaunchParamPropSymbol(JsState &jsState)
51{
52 struct SymbolTag
53 {
54 };
55
56 return jsState.getJsSymbolForType<SymbolTag>();
57}
58
59void sendWantToSelfQAbility(
60 QNapi::Object baseQAbility, QNapi::Object optStartOptions, const std::string &instanceId,
61 std::function<void(JsState &)> continueFunc)
62{
63 auto qAbilityInfo = QAbilityInstancesManager::readAbilityInfo(baseQAbility);
64
65 std::vector<QNapi::ValueWrapper> startAbilityArgs = {
66 QNapi::makeObject(
67 baseQAbility.Env(),
68 {
69 {"bundleName", qAbilityInfo.bundleName},
70 {"moduleName", qAbilityInfo.moduleName},
71 {"abilityName", qAbilityInfo.name},
72 {
73 "parameters",
74 QNapi::makeObject(
75 baseQAbility.Env(),
76 {
77 {qtInternalRequestIdWantParamKey, getUtf8QtInternalRequestIdForThisProcess()},
78 {qAbilityInstanceIdWantParamKey, instanceId},
79 })
80 },
81 }),
82 };
83
84 if (!optStartOptions.IsEmpty())
85 startAbilityArgs.push_back(optStartOptions);
86
87 baseQAbility.evalToPromiseOrRejectOnThrow("context.startAbility(*)", startAbilityArgs)
88 .onCatch(
89 [instanceId](const CallbackInfo &cbInfo) {
90 logJsCallbackError(cbInfo, "context.startAbility()");
91 qOhosReportFatalErrorAndAbort("Failed to start the Ability with instance id: %s", instanceId.c_str());
92 })
93 .onFinally(
94 [continueFunc = std::move(continueFunc)](const CallbackInfo &cbInfo) {
95 continueFunc(cbInfo.jsState());
96 });
97}
98
99QOhosSupplier<std::string> makeAbilityInstanceIdsGenerator()
100{
101 return [idsCounter = std::uint64_t(0)]() mutable {
102 auto instanceId = std::to_string(idsCounter);
103 ++idsCounter;
104 return instanceId;
105 };
106}
107
108class QAbilityPeerImpl : public virtual QAbilityPeer
109{
110public:
111 QAbilityPeerImpl(
112 JsState &jsState, std::string instanceId, QObjectThreadSafeRef qwindow,
113 QNapi::Object uiAbility);
114
115 ~QAbilityPeerImpl() = default;
116
117 std::string instanceId() final;
118 QNapi::Object qAbility() final;
119 QObjectThreadSafeRef qWindowRef() final;
120 std::optional<QNapi::Promise> qWindowDestroyPromise() final;
121 void forceResolveQWindowDestroyPromiseIfPresent(Napi::Env env) final;
122 std::shared_ptr<std::atomic_bool> destroyAllowedFlag() final;
123
124 void setQWindow(Napi::Env env, QObjectThreadSafeRef qwindow) final;
125
126private:
127 struct QWindowDestroyPromiseData
128 {
129 std::optional<QNapi::Promise::Deferred> deferred;
130 QNapi::Reference<QNapi::Promise> promise;
131 };
132
133 std::string m_instanceId;
134 QObjectThreadSafeRef m_qwindow;
135 std::shared_ptr<QWindowDestroyPromiseData> m_optQWindowDestroyPromiseData;
136 std::shared_ptr<std::atomic_bool> m_destroyAllowedFlag;
137 QNapi::Reference<QNapi::Object> m_uiAbility;
138};
139
140QAbilityPeerImpl::QAbilityPeerImpl(
141 JsState &, std::string instanceId, QObjectThreadSafeRef qwindow,
142 QNapi::Object uiAbility)
143 : m_instanceId(std::move(instanceId))
144 , m_destroyAllowedFlag(std::make_shared<std::atomic_bool>(false))
145 , m_uiAbility(Napi::Persistent(uiAbility))
146{
147 setQWindow(uiAbility.Env(), qwindow);
148}
149
150std::string QAbilityPeerImpl::instanceId()
151{
152 return m_instanceId;
153}
154
155QNapi::Object QAbilityPeerImpl::qAbility()
156{
157 return m_uiAbility.Value();
158}
159
160QObjectThreadSafeRef QAbilityPeerImpl::qWindowRef()
161{
162 return m_qwindow;
163}
164
165std::optional<QNapi::Promise> QAbilityPeerImpl::qWindowDestroyPromise()
166{
167 if (!m_optQWindowDestroyPromiseData || m_optQWindowDestroyPromiseData->promise.IsEmpty())
168 return std::nullopt;
169
170 auto promiseValue = m_optQWindowDestroyPromiseData->promise.Value();
171 m_optQWindowDestroyPromiseData->promise.Reset();
172
173 return promiseValue;
174}
175
176void QAbilityPeerImpl::forceResolveQWindowDestroyPromiseIfPresent(Napi::Env env)
177{
178 if (!m_optQWindowDestroyPromiseData || !m_optQWindowDestroyPromiseData->deferred)
179 return;
180
181 qOhosPrintfInfo(
182 "%s: force-resolving QWindow destroy Promise for id='%s'",
183 Q_FUNC_INFO, m_instanceId.c_str());
184 std::exchange(m_optQWindowDestroyPromiseData->deferred, std::nullopt)->Resolve(env.Undefined());
185}
186
187std::shared_ptr<std::atomic_bool> QAbilityPeerImpl::destroyAllowedFlag()
188{
189 return m_destroyAllowedFlag;
190}
191
192void QAbilityPeerImpl::setQWindow(Napi::Env env, QObjectThreadSafeRef qwindow)
193{
194 if (qwindow == m_qwindow)
195 return;
196
197 qOhosPrintfInfo(
198 "%s: setting QAbilityPeer's QWindow: %s / %s",
199 Q_FUNC_INFO, m_instanceId.c_str(), m_qwindow.refName().c_str());
200
201 if (m_qwindow != QObjectThreadSafeRef()) {
202 qOhosReportFatalErrorAndAbort(
203 "QAbilityPeerImpl: overwriting previously set qwindow: %s => %s",
204 m_qwindow.refName().c_str(), qwindow.refName().c_str());
205 }
206
207 m_qwindow = qwindow;
208
209 auto qWindowDestroyPromiseDeferred = QNapi::Promise::Deferred::New(env);
210 m_optQWindowDestroyPromiseData = std::make_shared<QWindowDestroyPromiseData>(
211 QWindowDestroyPromiseData{
212 .deferred = qWindowDestroyPromiseDeferred,
213 .promise = QNapi::Reference<QNapi::Promise>::makePersistentFrom(
214 qWindowDestroyPromiseDeferred.Promise()),
215 });
216
217 auto resolveQWindowDestroyPromiseFunc =
218 [qWindowRef = qwindow, weakPromiseData = QtOhos::makeWeakPtr(m_optQWindowDestroyPromiseData)]() {
219 qOhosPrintfDebug(
220 "%s: sending request to resolve QWindow destroy notify Promise if needed: %s",
221 Q_FUNC_INFO, qWindowRef.refName().c_str());
222 QOhosJsThreadGateway::invoke(
223 [qWindowRef, weakPromiseData](QOhosJsState &jsState) {
224 auto promiseData = weakPromiseData.lock();
225 if (promiseData && promiseData->deferred) {
226 qOhosPrintfDebug(
227 "%s: resolving QWindow destroy notify Promise: %s",
228 Q_FUNC_INFO, qWindowRef.refName().c_str());
229 auto napiUndefined = Napi::Env(jsState.env()).Undefined();
230 std::exchange(promiseData->deferred, std::nullopt)->Resolve(napiUndefined);
231 } else {
232 qOhosPrintfDebug(
233 "%s: QAbilityPeer already removed on QWindow's destroy notification: %s",
234 Q_FUNC_INFO, qWindowRef.refName().c_str());
235 }
236 });
237 };
238
239 QtOhos::invokeInQtThread(
240 [qWindowRef = qwindow, resolveQWindowDestroyPromiseFunc]() {
241 auto *qWindow = static_cast<QWindow *>(qWindowRef.data().data());
242 auto *platformWindow = qWindow != nullptr ? QOhosPlatformWindow::fromQWindowOrNull(qWindow) : nullptr;
243 auto *view = platformWindow != nullptr ? platformWindow->ownedViewOrNull() : nullptr;
244
245 if (view != nullptr) {
246 qCDebug(
247 QtForOhos, "%s: connecting to QOhosView's 'destroyed', win: %s",
248 Q_FUNC_INFO, qWindowRef.refName().c_str());
249 QObject::connect(
250 view, &QObject::destroyed,
251 [qWindowRef, resolveQWindowDestroyPromiseFunc](QObject *) {
252 qCDebug(
253 QtForOhos, "%s: got QOhosView's 'destroyed' signal, win: %s",
254 Q_FUNC_INFO, qWindowRef.refName().c_str());
255 resolveQWindowDestroyPromiseFunc();
256 });
257 } else {
258 qCDebug(
259 QtForOhos, "%s: QOhosView already destroyed, win: %s",
260 Q_FUNC_INFO, qWindowRef.refName().c_str());
261 resolveQWindowDestroyPromiseFunc();
262 }
263 });
264}
265
266class QUiAbilityPeerImpl final : public virtual QUiAbilityPeer, public QAbilityPeerImpl, public QUiAbilityPeerBackend
267{
268public:
269 QUiAbilityPeerImpl(
270 JsState &jsState, std::string instanceId, QObjectThreadSafeRef qwindow,
271 QNapi::Object uiAbility, QNapi::Object windowStage);
272
273 QNapi::Object uiContext() override;
274 QNapi::Object launchWant() override;
275 QNapi::Object launchParam() override;
276 QNapi::Object windowStage() override;
277 QNapi::Object window() override;
278
279 QNapi::Promise handleCloseRequestFromSystem(
280 QOhosJsState &jsState, const std::string &logContextStr, CloseAbilityRequestSource requestSource,
281 std::function<QNapi::Value(QOhosJsState &, CloseAbilityRequestResolution)> promiseValueFactory) override;
282
283 void handleOnContinueRequestFromSystem(
284 JsState &jsState, QNapi::Object wantParamsObj,
285 QOhosConsumer<JsState &, QOhosAbilityOnContinueResult> resultConsumer) override;
286
287 void setOnContinueRequestsHandler(
288 std::function<void(JsState &, QNapi::Object, QOhosConsumer<JsState &, QOhosAbilityOnContinueResult>)> requestsHandler) override;
289
290private:
291 QNapi::Reference<QNapi::Object> m_windowStage;
292 QNapi::Reference<QNapi::Object> m_window;
293 QNapi::Reference<QNapi::Object> m_launchParam;
294 std::shared_ptr<void> m_windowWillCloseCallbackHandle;
295 std::function<void(JsState &, QNapi::Object, QOhosConsumer<JsState &, QOhosAbilityOnContinueResult>)> m_onContinueRequestsHandler;
296};
297
298QUiAbilityPeerImpl::QUiAbilityPeerImpl(
299 JsState &jsState, std::string instanceId, QObjectThreadSafeRef qwindow,
300 QNapi::Object uiAbility, QNapi::Object windowStage)
301 : QAbilityPeerImpl(jsState, instanceId, qwindow, uiAbility)
302 , m_windowStage(Napi::Persistent(windowStage))
303 , m_window(Napi::Persistent(windowStage.eval<QNapi::Object>("getMainWindowSync()")))
304{
305 auto optLaunchParam = QNapi::getOptionalPropOrEmpty<QNapi::Object>(
306 uiAbility, getAbilityLaunchParamPropSymbol(jsState));
307 if (optLaunchParam.IsEmpty()) {
308 qOhosReportFatalErrorAndAbort(
309 "Ability with instanceId='%s' doesn't have 'launchParam' set. It shouldn't happen!",
310 instanceId.c_str());
311 }
312 m_launchParam = QNapi::Reference<>::makePersistentFrom(optLaunchParam);
313
314 m_windowWillCloseCallbackHandle = registerQOhosOnOffMethodsBasedEventHandler(
315 window(), "windowWillClose",
316 [this](const CallbackInfo &cbInfo) {
317 JsState &jsState = cbInfo.jsState();
318 return handleCloseRequestFromSystem(
319 jsState, "Window.windowWillClose",
320 CloseAbilityRequestSource::WindowWillClose,
321 [](QOhosJsState &jsState, CloseAbilityRequestResolution ohosRequestResolution) {
322 return QNapi::Boolean::New(
323 jsState.env(),
324 ohosRequestResolution == CloseAbilityRequestResolution::DontClose);
325 });
326 },
327 {
328 .optEventSourceAliveCheckFunc =
329 [qAbilityWeakRef = moveToSharedPtr(Napi::Weak(uiAbility))](QNapi::Object window) {
330 bool isWindowClosing = QOhosJsThreadGateway::eval(
331 [&](QOhosJsState &) {
332 return JsWindowsTracker::isWindowClosing(window);
333 },
334 Q_FUNC_INFO);
335 auto qAbilityValue = qAbilityWeakRef->Value();
336 return
337 !isWindowClosing
338 && qAbilityValue.IsObject()
339 && !QNapi::checkedCast<QNapi::Object>(qAbilityValue).eval<QNapi::Boolean>("context.isTerminating()");
340 },
341 .extraOnArg = {},
342 .extraOffArg = {},
343 .optOnCallExceptionHandler = [](const Napi::Error &error) {
344 constexpr auto capabilityNotSupportedErrorCode = 801;
345 QtOhos::rethrowUnlessJsBusinessErrorIs(
346 error, capabilityNotSupportedErrorCode, "on('windowWillClose')");
347 },
348 });
349}
350
351QNapi::Object QUiAbilityPeerImpl::uiContext()
352{
353 return window().eval<QNapi::Object>("getUIContext()");
354}
355
356QNapi::Object QUiAbilityPeerImpl::launchWant()
357{
358 return qAbility().get<QNapi::Object>("launchWant");
359}
360
361QNapi::Object QUiAbilityPeerImpl::launchParam()
362{
363 return m_launchParam.Value();
364}
365
366QNapi::Object QUiAbilityPeerImpl::windowStage()
367{
368 return m_windowStage.Value();
369}
370
371QNapi::Object QUiAbilityPeerImpl::window()
372{
373 return m_window.Value();
374}
375
376QOhosCloseEventContext::CloseRootCause mapCloseAbilityRequestSourceToRootCause(
378{
379 using CloseAbilityRequestSource = QUiAbilityPeerBackend::CloseAbilityRequestSource;
380 using CloseRootCause = QOhosCloseEventContext::CloseRootCause;
381
382 switch (requestSource) {
383 case CloseAbilityRequestSource::OnPrepareToTerminate:
384 return CloseRootCause::OnPrepareToTerminate;
385 case CloseAbilityRequestSource::WindowWillClose:
386 return CloseRootCause::WindowStageClose;
387 }
388
389 qOhosReportFatalErrorAndAbort("Unsupported CloseAbilityRequestSource: %d", static_cast<int>(requestSource));
390}
391
392QNapi::Promise QUiAbilityPeerImpl::handleCloseRequestFromSystem(
393 QOhosJsState &jsState, const std::string &logContextStr, CloseAbilityRequestSource requestSource,
394 std::function<QNapi::Value(QOhosJsState &, CloseAbilityRequestResolution)> promiseValueFactory)
395{
396 auto instanceId = this->instanceId();
397
398 qOhosPrintfInfo(
399 "%s: got close request from source %d, id: '%s'",
400 logContextStr.c_str(), static_cast<int>(requestSource), instanceId.c_str());
401
402 if (destroyAllowedFlag()->load()) {
403 qOhosPrintfDebug(
404 "%s: resolving immediately with 'false', id: '%s'",
405 Q_FUNC_INFO, instanceId.c_str());
406 JsWindowsTracker::tagWindowAsClosing(
407 window(), printfToString("%s => false", logContextStr.c_str()).c_str());
408 return makeResolvedPromise(
409 promiseValueFactory(jsState, CloseAbilityRequestResolution::Close));
410 }
411
412 struct PromiseResolveFuncContext
413 {
414 std::string logContextStr;
415 std::weak_ptr<QUiAbilityPeer> weakQAbilityPeer;
416 std::shared_ptr<QNapi::Promise::Deferred> promiseDeferred;
417 std::string instanceId;
418 std::function<QNapi::Value(QOhosJsState &, CloseAbilityRequestResolution)> promiseValueFactory;
419 };
420
421 auto promiseResolveFuncContext = moveToSharedPtr(
422 PromiseResolveFuncContext{
423 .logContextStr = logContextStr,
424 .weakQAbilityPeer = makeWeakPtr(shared_from_this()),
425 .promiseDeferred = std::make_shared<QNapi::Promise::Deferred>(jsState.env()),
426 .instanceId = instanceId,
427 .promiseValueFactory = std::move(promiseValueFactory),
428 });
429
430 auto promiseResolveFunc = moveToSharedPtr(
431 makeCallOnceConsumerWrapper<QOhosJsState &, CloseAbilityRequestResolution>(
432 [context = promiseResolveFuncContext](QOhosJsState &jsState, CloseAbilityRequestResolution ohosRequestResolution) {
433 auto qAbilityPeer = context->weakQAbilityPeer.lock();
434 if (qAbilityPeer && ohosRequestResolution == CloseAbilityRequestResolution::Close) {
435 JsWindowsTracker::tagWindowAsClosing(
436 qAbilityPeer->window(), printfToString("%s => false", context->logContextStr.c_str()).c_str());
437 }
438 context->promiseDeferred->Resolve(
439 context->promiseValueFactory(jsState, ohosRequestResolution));
440 qOhosPrintfInfo(
441 "%s: promise resolved as %d, id: '%s'",
442 context->logContextStr.c_str(), static_cast<int>(ohosRequestResolution), context->instanceId.c_str());
443 }));
444
445 constexpr auto promiseAutoresolveTimeout = 9s;
446 setJsTimeout(
447 jsState,
448 [logContextStr, promiseResolveFunc, instanceId](const QOhosCallbackInfo &cbInfo) {
449 if ((*promiseResolveFunc)(cbInfo.jsState(), CloseAbilityRequestResolution::DontClose)) {
450 qOhosPrintfInfo(
451 "%s: promise resolved by timeout, id: '%s'",
452 logContextStr.c_str(), instanceId.c_str());
453 }
454 },
455 promiseAutoresolveTimeout);
456
457 qWindowRef().visitInQtThreadIfAlive(
458 [logContextStr, requestSource, promiseResolveFunc, instanceId](auto &qWindowObj) {
459 qOhosPrintfInfo(
460 "%s: calling close(), id: '%s'",
461 logContextStr.c_str(), instanceId.c_str());
462 QOhosCloseEventContext::runWithCloseRootCauseAndCloseResolutionConsumerSet(
463 mapCloseAbilityRequestSourceToRootCause(requestSource),
464 [logContextStr, promiseResolveFunc, instanceId](QOhosCloseEventContext::CloseResolution closeResolution) {
465 auto ohosRequestResolution =
466 closeResolution == QOhosCloseEventContext::CloseResolution::Close
467 ? CloseAbilityRequestResolution::Close
468 : CloseAbilityRequestResolution::DontClose;
469 qOhosPrintfInfo(
470 "%s: requesting promise resolve to %d from Qt thread, id: '%s'",
471 logContextStr.c_str(), static_cast<int>(ohosRequestResolution), instanceId.c_str());
472 QOhosJsThreadGateway::invoke(
473 [logContextStr, promiseResolveFunc, ohosRequestResolution, instanceId](QOhosJsState &jsState) {
474 bool resolveCalled = (*promiseResolveFunc)(jsState, ohosRequestResolution);
475 qOhosPrintfInfo(
476 "%s: promise resolve to %d, called: %s, id: '%s'",
477 logContextStr.c_str(), static_cast<int>(ohosRequestResolution),
478 mapBoolToTrueFalseStr(resolveCalled), instanceId.c_str());
479 });
480 },
481 [&]() {
482 static_cast<QWindow &>(qWindowObj).close();
483 });
484 });
485
486 qOhosPrintfInfo("%s: returning Promise, id: '%s'", logContextStr.c_str(), instanceId.c_str());
487
488 return promiseResolveFuncContext->promiseDeferred->Promise();
489}
490
491void QUiAbilityPeerImpl::handleOnContinueRequestFromSystem(
492 JsState &jsState, QNapi::Object wantParamsObj, QOhosConsumer<JsState &, QOhosAbilityOnContinueResult> resultConsumer)
493{
494 if (m_onContinueRequestsHandler) {
495 m_onContinueRequestsHandler(jsState, wantParamsObj, std::move(resultConsumer));
496 } else {
497 qOhosPrintfDebug("%s: no handler set, rejecting", Q_FUNC_INFO);
498 QOhosJsThreadGateway::invoke(
499 [resultConsumer = std::move(resultConsumer)](QOhosJsState &jsState) {
500 resultConsumer(JsState::castFrom(jsState), QOhosAbilityOnContinueResult::REJECT);
501 });
502 }
503}
504
505void QUiAbilityPeerImpl::setOnContinueRequestsHandler(
506 std::function<void(JsState &, QNapi::Object, QOhosConsumer<JsState &, QOhosAbilityOnContinueResult>)> requestsHandler)
507{
508 if (m_onContinueRequestsHandler) {
509 qOhosReportFatalErrorAndAbort(
510 "%s: overwriting previously set requests handler, id='%s'",
511 Q_FUNC_INFO, instanceId().c_str());
512 }
513
514 m_onContinueRequestsHandler = std::move(requestsHandler);
515}
516
517class QAbilityInstancesManagerImpl final : public QAbilityInstancesManager
518{
519public:
520 explicit QAbilityInstancesManagerImpl(
521 std::function<void(JsState &, std::shared_ptr<QAbilityPeer>)> autoStartedInstanceStartupNotifyFunc);
522
523 bool isWantFromThisApp(QNapi::Object appQAbility, QNapi::Object want) const override;
524
525 std::optional<std::string> tryGetQAbilityInstanceIdFromWant(QNapi::Object appQAbility, QNapi::Object want) const override;
526 std::string getQAbilityInstanceIdOrPendingAutoStartedId(QNapi::Object qAbility) const override;
527
528 std::optional<std::string> pendingAutoStartedInstanceId() const override;
529
530 void registerPendingAutoStartedInstance() override;
531
532 void startNewInstance(
533 QNapi::Object baseQAbility, QObjectThreadSafeRef qwindow,
534 QNapi::Object optStartOptions,
535 std::function<void(JsState &, std::shared_ptr<QAbilityPeer>)> startupNotifyFunc) override;
536
537 void handleStartedUiInstance(JsState &jsState, QNapi::Object qAbility, QNapi::Object windowStage) override;
538
539 std::shared_ptr<QUiAbilityPeerBackend> getAbilityPeerBackend(std::shared_ptr<QUiAbilityPeer> uiAbilityPeer) override;
540
541private:
542 struct InstanceStartParams
543 {
544 QObjectThreadSafeRef qwindow;
545 std::function<void(JsState &, std::shared_ptr<QAbilityPeer>)> startupNotifyFunc;
546 };
547
548 void handleStartedInstance(
549 JsState &jsState, const std::string &abilityInstanceId,
550 const std::function<std::shared_ptr<QAbilityPeer>(const std::string &, const InstanceStartParams &)> &qAbilityPeerFactory);
551
552 std::shared_ptr<std::function<void(JsState &, std::shared_ptr<QAbilityPeer>)>> m_autoStartedInstanceStartupNotifyFunc;
553 std::shared_ptr<std::map<QUiAbilityPeer *, std::weak_ptr<QUiAbilityPeerImpl>>> m_ownedUiAbilityPeers;
554 QOhosSupplier<std::string> m_abilityInstanceIdsGenerator;
555 std::map<std::string, InstanceStartParams> m_pendingInstancesStartParams;
556 std::optional<std::string> m_pendingAutoStartedInstanceId;
557};
558
559QAbilityInstancesManagerImpl::QAbilityInstancesManagerImpl(
560 std::function<void(JsState &, std::shared_ptr<QAbilityPeer>)> autoStartedInstanceStartupNotifyFunc)
561 : m_autoStartedInstanceStartupNotifyFunc(moveToSharedPtr(std::move(autoStartedInstanceStartupNotifyFunc)))
562 , m_ownedUiAbilityPeers(std::make_shared<std::map<QUiAbilityPeer *, std::weak_ptr<QUiAbilityPeerImpl>>>())
563 , m_abilityInstanceIdsGenerator(makeAbilityInstanceIdsGenerator())
564{
565 registerPendingAutoStartedInstance();
566}
567
568std::optional<std::string> QAbilityInstancesManagerImpl::tryGetQAbilityInstanceIdFromWant(
569 QNapi::Object appQAbility, QNapi::Object want) const
570{
571 auto wantParameters = QNapi::getPropOrUndefined(want, "parameters");
572 if (wantParameters.IsUndefined())
573 return std::nullopt;
574
575 auto instanceIdParam = QNapi::getPropOrUndefined(wantParameters, qAbilityInstanceIdWantParamKey);
576
577 bool validInstanceId = isWantFromThisApp(appQAbility, want) && instanceIdParam.IsString();
578
579 return validInstanceId
580 ? std::optional(QNapi::checkedCast<QNapi::String>(instanceIdParam).Utf8Value())
581 : std::nullopt;
582}
583
584std::string QAbilityInstancesManagerImpl::getQAbilityInstanceIdOrPendingAutoStartedId(QNapi::Object qAbility) const
585{
586 auto optInstanceId = tryGetQAbilityInstanceIdFromWant(
587 qAbility, qAbility.get<QNapi::Object>("launchWant"));
588
589 if (!optInstanceId.has_value() && !m_pendingAutoStartedInstanceId.has_value()) {
590 qOhosReportFatalErrorAndAbort(
591 "Got Ability without '%s' param and no pending auto-started instance",
592 qAbilityInstanceIdWantParamKey);
593 }
594
595 return optInstanceId.has_value()
596 ? optInstanceId.value()
597 : m_pendingAutoStartedInstanceId.value();
598}
599
600bool QAbilityInstancesManagerImpl::isWantFromThisApp(QNapi::Object appQAbility, QNapi::Object want) const
601{
602 auto wantParameters = QNapi::getPropOrUndefined(want, "parameters");
603 if (wantParameters.IsUndefined())
604 return false;
605
606 auto callerAbilityNameParam = QNapi::getPropOrUndefined(wantParameters, callerAbilityNameWantParamKey);
607 auto callerBundleNameParam = QNapi::getPropOrUndefined(wantParameters, callerBundleNameWantParamKey);
608
609 auto appAbilityInfo = readAbilityInfo(appQAbility);
610
611 return
612 callerAbilityNameParam.IsString()
613 && QNapi::checkedCast<QNapi::String>(callerAbilityNameParam).Utf8Value() == appAbilityInfo.name
614 && callerBundleNameParam.IsString()
615 && QNapi::checkedCast<QNapi::String>(callerBundleNameParam).Utf8Value() == appAbilityInfo.bundleName;
616}
617
618std::optional<std::string> QAbilityInstancesManagerImpl::pendingAutoStartedInstanceId() const
619{
620 return m_pendingAutoStartedInstanceId;
621}
622
623void QAbilityInstancesManagerImpl::registerPendingAutoStartedInstance()
624{
625 if (m_pendingAutoStartedInstanceId.has_value()) {
626 qOhosReportFatalErrorAndAbort(
627 "%s: pending auto-started instance already registered (id='%s')",
628 Q_FUNC_INFO, m_pendingAutoStartedInstanceId.value().c_str());
629 }
630
631 m_pendingAutoStartedInstanceId = m_abilityInstanceIdsGenerator();
632
633 qOhosPrintfInfo(
634 "Registered pending auto-started Ability instance, id='%s'",
635 m_pendingAutoStartedInstanceId.value().c_str());
636}
637
638void QAbilityInstancesManagerImpl::startNewInstance(
639 QNapi::Object baseQAbility, QObjectThreadSafeRef qwindow,
640 QNapi::Object optStartOptions,
641 std::function<void(JsState &, std::shared_ptr<QAbilityPeer>)> startupNotifyFunc)
642{
643 struct Context
644 {
645 std::function<void(JsState &, std::shared_ptr<QAbilityPeer>)> startupNotifyFunc;
646 bool sendWantFinished = false;
647 std::shared_ptr<QAbilityPeer> optAbilityPeer;
648
649 void callNotifyIfReady(JsState &jsState)
650 {
651 qOhosPrintfDebug(
652 "%s: sendWantFinished=%s, startedAbilityPeer=%s",
653 Q_FUNC_INFO, mapBoolToTrueFalseStr(sendWantFinished), mapBoolToTrueFalseStr(!!optAbilityPeer));
654 if (sendWantFinished && optAbilityPeer)
655 startupNotifyFunc(jsState, optAbilityPeer);
656 }
657 };
658
659 auto context = std::make_shared<Context>();
660 context->startupNotifyFunc = std::move(startupNotifyFunc);
661
662 auto instanceId = m_abilityInstanceIdsGenerator();
663
664 InstanceStartParams startParams;
665 startParams.qwindow = qwindow;
666 startParams.startupNotifyFunc = [context](JsState &jsState, std::shared_ptr<QAbilityPeer> abilityPeer) {
667 context->optAbilityPeer = abilityPeer;
668 context->callNotifyIfReady(jsState);
669 };
670 m_pendingInstancesStartParams.emplace(instanceId, std::move(startParams));
671
672 sendWantToSelfQAbility(
673 baseQAbility, optStartOptions, instanceId,
674 [context](JsState &jsState) {
675 context->sendWantFinished = true;
676 context->callNotifyIfReady(jsState);
677 });
678}
679
680void QAbilityInstancesManagerImpl::handleStartedUiInstance(
681 JsState &jsState, QNapi::Object qAbility, QNapi::Object windowStage)
682{
683 handleStartedInstance(
684 jsState, getQAbilityInstanceIdOrPendingAutoStartedId(qAbility),
685 [&](const std::string &instanceId, const InstanceStartParams &startParams) {
686 auto uiAbilityPeer = std::make_shared<QUiAbilityPeerImpl>(
687 jsState, instanceId, startParams.qwindow, qAbility, windowStage);
688 QUiAbilityPeer *uiAbilityPeerKey = uiAbilityPeer.get();
689 m_ownedUiAbilityPeers->emplace(uiAbilityPeerKey, uiAbilityPeer);
690 return makeSharedPtrWithAttachedExtraData(
691 uiAbilityPeer,
692 makeDestroyNotifier(
693 [ownedUiAbilityPeers = m_ownedUiAbilityPeers, uiAbilityPeerKey]() {
694 ownedUiAbilityPeers->erase(uiAbilityPeerKey);
695 }));
696 });
697}
698
699std::shared_ptr<QUiAbilityPeerBackend> QAbilityInstancesManagerImpl::getAbilityPeerBackend(
700 std::shared_ptr<QUiAbilityPeer> uiAbilityPeer)
701{
702 auto ownedPeerEntryIter = m_ownedUiAbilityPeers->find(uiAbilityPeer.get());
703 auto ownedPeerImpl =
704 ownedPeerEntryIter != m_ownedUiAbilityPeers->end()
705 ? ownedPeerEntryIter->second.lock()
706 : nullptr;
707
708 if (!ownedPeerImpl) {
709 qOhosReportFatalErrorAndAbort(
710 "%s: got unknown QUiAbilityPeer %p with id='%s'",
711 Q_FUNC_INFO, uiAbilityPeer.get(), uiAbilityPeer->instanceId().c_str());
712 }
713
714 return ownedPeerImpl;
715}
716
717void QAbilityInstancesManagerImpl::handleStartedInstance(
718 JsState &jsState, const std::string &abilityInstanceId,
719 const std::function<std::shared_ptr<QAbilityPeer>(const std::string &, const InstanceStartParams &)> &qAbilityPeerFactory)
720{
721 auto startParamsIter = m_pendingInstancesStartParams.find(abilityInstanceId);
722 if (startParamsIter != m_pendingInstancesStartParams.end()) {
723 auto startParams = std::move(startParamsIter->second);
724 m_pendingInstancesStartParams.erase(startParamsIter);
725
726 auto qAbilityPeer = qAbilityPeerFactory(abilityInstanceId, startParams);
727 QtOhos::addJsQAbilityPeer(qAbilityPeer);
728 startParams.startupNotifyFunc(jsState, qAbilityPeer);
729 } else if (abilityInstanceId == m_pendingAutoStartedInstanceId) {
730 m_pendingAutoStartedInstanceId.reset();
731
732 InstanceStartParams startParams = {
733 .qwindow = QObjectThreadSafeRef(),
734 .startupNotifyFunc = [startupNotifyFunc = m_autoStartedInstanceStartupNotifyFunc](JsState &jsState, std::shared_ptr<QAbilityPeer> qAbilityPeer) {
735 (*startupNotifyFunc)(jsState, qAbilityPeer);
736 },
737 };
738 auto qAbilityPeer = qAbilityPeerFactory(abilityInstanceId, startParams);
739 QtOhos::addJsQAbilityPeer(qAbilityPeer);
740 startParams.startupNotifyFunc(jsState, qAbilityPeer);
741 } else {
742 qOhosPrintfError(
743 "%s: got unexpected started instance notification with id='%s', ignoring",
744 Q_FUNC_INFO, abilityInstanceId.c_str());
745 }
746}
747
748}
749
751
753
755
756bool QAbilityInstancesManager::isQtInternalWantFromThisProcess(QNapi::Object want)
757{
758 auto optWantParameters = QNapi::getOptionalPropOrEmpty<QNapi::Object>(want, "parameters");
759
760 auto optCallerPid = QNapi::getOptionalPropOrEmpty<QNapi::Number>(
761 optWantParameters, callerPidWantParamKey);
762 auto optQtInternalRequestId = QNapi::getOptionalPropOrEmpty<QNapi::String>(
763 optWantParameters, qtInternalRequestIdWantParamKey);
764
765 std::int32_t thisProcessPid = ::getpid();
766
767 return
768 !optCallerPid.IsEmpty() && optCallerPid.Int32Value() == thisProcessPid
769 && !optQtInternalRequestId.IsEmpty()
770 && optQtInternalRequestId.Utf8Value() == getUtf8QtInternalRequestIdForThisProcess();
771}
772
773void QAbilityInstancesManager::setLaunchParamOnAbilityObject(
774 JsState &jsState, QNapi::Object ability, QNapi::Object launchParam)
775{
776 ability.set(getAbilityLaunchParamPropSymbol(jsState), launchParam);
777}
778
779QAbilityInfo QAbilityInstancesManager::readAbilityInfo(const QNapi::Object &ability)
780{
781 auto abilityInfo = ability.eval<QNapi::Object>("context.abilityInfo");
782
783 return {
784 .name = abilityInfo.get<QNapi::String>("name"),
785 .bundleName = abilityInfo.get<QNapi::String>("bundleName"),
786 .moduleName = abilityInfo.get<QNapi::String>("moduleName"),
787 };
788}
789
791 std::function<void(JsState &, std::shared_ptr<QAbilityPeer>)> autoStartedInstanceStartupNotifyFunc)
792{
793 return std::make_shared<QAbilityInstancesManagerImpl>(std::move(autoStartedInstanceStartupNotifyFunc));
794}
795
796}
797
798QT_END_NAMESPACE
JsState & jsState() const
std::string const char * mapBoolToTrueFalseStr(bool value)
std::shared_ptr< QAbilityInstancesManager > makeQAbilityInstancesManager(std::function< void(JsState &, std::shared_ptr< QAbilityPeer >)> autoStartedInstanceStartupNotifyFunc)
void addJsQAbilityPeer(std::shared_ptr< QAbilityPeer > qAbilityPeer)