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