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