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