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