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 <optional>
15#include <qohosdeviceinfo_p.h>
16#include <qohosjsutils.h>
17#include <qohosplatformwindow.h>
18#include <render/qohosview.h>
19#include <typeindex>
20#include <unistd.h>
21
23
25
26using namespace std::chrono_literals;
27
28namespace QtOhos {
29
30namespace {
31
32const auto qtInternalRequestIdWantParamKey = "io.qt.private.internalRequestId";
33const auto qAbilityInstanceIdWantParamKey = "io.qt.private.abilityInstanceId";
34const auto callerAbilityNameWantParamKey = "ohos.aafwk.param.callerAbilityName";
35const auto callerBundleNameWantParamKey = "ohos.aafwk.param.callerBundleName";
36const auto callerPidWantParamKey = "ohos.aafwk.param.callerPid";
37
38std::string getUtf8QtInternalRequestIdForThisProcess()
39{
40 static const auto internalRequestIdCommonData =
41 QByteArray::fromHex("eba08604c86e45ae3690621757b3f4e9");
42
43 std::int32_t thisProcessPid = ::getpid();
44
45 QCryptographicHash hash{QCryptographicHash::Sha1};
46 hash.addData(internalRequestIdCommonData);
47 hash.addData(QByteArray::number(thisProcessPid));
48
49 return hash.result().toHex().toStdString();
50}
51
52QNapi::Symbol getAbilityLaunchParamPropSymbol(JsState &jsState)
53{
54 struct SymbolTag
55 {
56 };
57
58 return jsState.getJsSymbolForType<SymbolTag>();
59}
60
61void sendWantToSelfQAbility(
62 std::shared_ptr<QAbilityEngine> abilityEngine,
63 QNapi::Object baseQAbility, QNapi::Object optStartOptions, const std::string &instanceId,
64 std::function<void(JsState &)> continueFunc)
65{
66 auto qAbilityInfo = abilityEngine->readAbilityInfo(baseQAbility);
67
68 std::vector<QNapi::ValueWrapper> startAbilityArgs = {
69 QNapi::makeObject(
70 baseQAbility.Env(),
71 {
72 {"bundleName", qAbilityInfo.bundleName},
73 {"moduleName", qAbilityInfo.moduleName},
74 {"abilityName", qAbilityInfo.name},
75 {
76 "parameters",
77 QNapi::makeObject(
78 baseQAbility.Env(),
79 {
80 {qtInternalRequestIdWantParamKey, getUtf8QtInternalRequestIdForThisProcess()},
81 {qAbilityInstanceIdWantParamKey, instanceId},
82 })
83 },
84 }),
85 };
86
87 if (!optStartOptions.IsEmpty())
88 startAbilityArgs.push_back(optStartOptions);
89
90 baseQAbility.evalToPromiseOrRejectOnThrow("context.startAbility(*)", startAbilityArgs)
91 .onCatch(
92 [instanceId](const CallbackInfo &cbInfo) {
93 logJsCallbackError(cbInfo, "context.startAbility()");
94 qOhosReportFatalErrorAndAbort("Failed to start the Ability with instance id: %s", instanceId.c_str());
95 })
96 .onFinally(
97 [continueFunc = std::move(continueFunc)](const CallbackInfo &cbInfo) {
98 continueFunc(cbInfo.jsState());
99 });
100}
101
102QOhosSupplier<std::string> makeAbilityInstanceIdsGenerator()
103{
104 return [idsCounter = std::uint64_t(0)]() mutable {
105 auto instanceId = std::to_string(idsCounter);
106 ++idsCounter;
107 return instanceId;
108 };
109}
110
111class QAbilityPeerImpl : public virtual QAbilityPeer
112{
113public:
114 QAbilityPeerImpl(
115 JsState &jsState, std::shared_ptr<QAbilityEngine> abilityEngine,
116 std::string instanceId, QObjectThreadSafeRef qwindow,
117 QNapi::Object uiAbility);
118
119 ~QAbilityPeerImpl() = default;
120
121 std::string instanceId() final;
122 QNapi::Object qAbility() final;
123 QObjectThreadSafeRef qWindowRef() final;
124 QOhosOptional<QNapi::Promise> qWindowDestroyPromise() final;
125 void forceResolveQWindowDestroyPromiseIfPresent(Napi::Env env) final;
126 std::shared_ptr<std::atomic_bool> destroyAllowedFlag() final;
127
128 void setQWindow(Napi::Env env, QObjectThreadSafeRef qwindow) final;
129
130private:
131 struct QWindowDestroyPromiseData
132 {
133 std::optional<QNapi::Promise::Deferred> deferred;
134 QNapi::Reference<QNapi::Promise> promise;
135 };
136
137 std::shared_ptr<QAbilityEngine> m_abilityEngine;
138 std::string m_instanceId;
139 QObjectThreadSafeRef m_qwindow;
140 std::shared_ptr<QWindowDestroyPromiseData> m_optQWindowDestroyPromiseData;
141 std::shared_ptr<std::atomic_bool> m_destroyAllowedFlag;
142 QNapi::Reference<QNapi::Object> m_uiAbility;
143};
144
145QAbilityPeerImpl::QAbilityPeerImpl(
146 JsState &, std::shared_ptr<QAbilityEngine> abilityEngine,
147 std::string instanceId, QObjectThreadSafeRef qwindow,
148 QNapi::Object uiAbility)
149 : m_abilityEngine(abilityEngine)
150 , m_instanceId(std::move(instanceId))
151 , m_destroyAllowedFlag(std::make_shared<std::atomic_bool>(false))
152 , m_uiAbility(Napi::Persistent(uiAbility))
153{
154 setQWindow(uiAbility.Env(), qwindow);
155}
156
157std::string QAbilityPeerImpl::instanceId()
158{
159 return m_instanceId;
160}
161
162QNapi::Object QAbilityPeerImpl::qAbility()
163{
164 return m_uiAbility.Value();
165}
166
167QObjectThreadSafeRef QAbilityPeerImpl::qWindowRef()
168{
169 return m_qwindow;
170}
171
172QOhosOptional<QNapi::Promise> QAbilityPeerImpl::qWindowDestroyPromise()
173{
174 if (!m_optQWindowDestroyPromiseData || m_optQWindowDestroyPromiseData->promise.IsEmpty())
175 return {};
176
177 auto promiseValue = m_optQWindowDestroyPromiseData->promise.Value();
178 m_optQWindowDestroyPromiseData->promise.Reset();
179
180 return makeQOhosOptional(promiseValue);
181}
182
183void QAbilityPeerImpl::forceResolveQWindowDestroyPromiseIfPresent(Napi::Env env)
184{
185 if (!m_optQWindowDestroyPromiseData || !m_optQWindowDestroyPromiseData->deferred)
186 return;
187
188 qOhosPrintfInfo(
189 "%s: force-resolving QWindow destroy Promise for id='%s'",
190 Q_FUNC_INFO, m_instanceId.c_str());
191 std::exchange(m_optQWindowDestroyPromiseData->deferred, std::nullopt)->Resolve(env.Undefined());
192}
193
194std::shared_ptr<std::atomic_bool> QAbilityPeerImpl::destroyAllowedFlag()
195{
196 return m_destroyAllowedFlag;
197}
198
199void QAbilityPeerImpl::setQWindow(Napi::Env env, QObjectThreadSafeRef qwindow)
200{
201 if (qwindow == m_qwindow)
202 return;
203
204 qOhosPrintfInfo(
205 "%s: setting QAbilityPeer's QWindow: %s / %s",
206 Q_FUNC_INFO, m_instanceId.c_str(), m_qwindow.refName().c_str());
207
208 if (m_qwindow != QObjectThreadSafeRef()) {
209 qOhosReportFatalErrorAndAbort(
210 "QAbilityPeerImpl: overwriting previously set qwindow: %s => %s",
211 m_qwindow.refName().c_str(), qwindow.refName().c_str());
212 }
213
214 m_qwindow = qwindow;
215
216 auto qWindowDestroyPromiseDeferred = QNapi::Promise::Deferred::New(env);
217 m_optQWindowDestroyPromiseData = std::make_shared<QWindowDestroyPromiseData>(
218 QWindowDestroyPromiseData{
219 .deferred = qWindowDestroyPromiseDeferred,
220 .promise = QNapi::Reference<QNapi::Promise>::makePersistentFrom(
221 qWindowDestroyPromiseDeferred.Promise()),
222 });
223
224 auto resolveQWindowDestroyPromiseFunc =
225 [qWindowRef = qwindow, weakPromiseData = QtOhos::makeWeakPtr(m_optQWindowDestroyPromiseData)]() {
226 qOhosPrintfDebug(
227 "%s: sending request to resolve QWindow destroy notify Promise if needed: %s",
228 Q_FUNC_INFO, qWindowRef.refName().c_str());
230 [qWindowRef, weakPromiseData](JsState &jsState) {
231 auto promiseData = weakPromiseData.lock();
232 if (promiseData && promiseData->deferred) {
233 qOhosPrintfDebug(
234 "%s: resolving QWindow destroy notify Promise: %s",
235 Q_FUNC_INFO, qWindowRef.refName().c_str());
236 auto napiUndefined = Napi::Env(jsState.env()).Undefined();
237 std::exchange(promiseData->deferred, std::nullopt)->Resolve(napiUndefined);
238 } else {
239 qOhosPrintfDebug(
240 "%s: QAbilityPeer already removed on QWindow's destroy notification: %s",
241 Q_FUNC_INFO, qWindowRef.refName().c_str());
242 }
243 });
244 };
245
246 QtOhos::invokeInQtThread(
247 [qWindowRef = qwindow, resolveQWindowDestroyPromiseFunc]() {
248 auto *qWindow = static_cast<QWindow *>(qWindowRef.data().data());
249 auto *platformWindow = qWindow != nullptr ? QOhosPlatformWindow::fromQWindowOrNull(qWindow) : nullptr;
250 auto *view = platformWindow != nullptr ? platformWindow->ownedViewOrNull() : nullptr;
251
252 if (view != nullptr) {
253 qCDebug(
254 QtForOhos, "%s: connecting to QOhosView's 'destroyed', win: %s",
255 Q_FUNC_INFO, qWindowRef.refName().c_str());
256 QObject::connect(
257 view, &QObject::destroyed,
258 [qWindowRef, resolveQWindowDestroyPromiseFunc](QObject *) {
259 qCDebug(
260 QtForOhos, "%s: got QOhosView's 'destroyed' signal, win: %s",
261 Q_FUNC_INFO, qWindowRef.refName().c_str());
262 resolveQWindowDestroyPromiseFunc();
263 });
264 } else {
265 qCDebug(
266 QtForOhos, "%s: QOhosView already destroyed, win: %s",
267 Q_FUNC_INFO, qWindowRef.refName().c_str());
268 resolveQWindowDestroyPromiseFunc();
269 }
270 });
271}
272
273class QUiAbilityPeerImpl final : public virtual QUiAbilityPeer, public QAbilityPeerImpl, public QUiAbilityPeerBackend
274{
275public:
276 QUiAbilityPeerImpl(
277 JsState &jsState, std::shared_ptr<QAbilityEngine> abilityEngine,
278 std::string instanceId, QObjectThreadSafeRef qwindow,
279 QNapi::Object uiAbility, QNapi::Object windowStage);
280
281 QNapi::Object uiContext() override;
282 QNapi::Object launchWant() override;
283 QNapi::Object launchParam() override;
284 QNapi::Object windowStage() override;
285 QNapi::Object window() override;
286 bool isTerminating() override;
287
288 QNapi::Promise handleCloseRequestFromSystem(
289 JsState &jsState, const std::string &logContextStr, CloseAbilityRequestSource requestSource,
290 std::function<QNapi::Value(JsState &, CloseAbilityRequestResolution)> promiseValueFactory) override;
291
292 void handleOnContinueRequestFromSystem(
293 JsState &jsState, QNapi::Object wantParamsObj,
294 QOhosConsumer<JsState &, QOhosAbilityOnContinueResult> resultConsumer) override;
295
296 void setOnContinueRequestsHandler(
297 std::function<void(JsState &, QNapi::Object, QOhosConsumer<JsState &, QOhosAbilityOnContinueResult>)> requestsHandler) override;
298
299private:
300 QNapi::Reference<QNapi::Object> m_windowStage;
301 QNapi::Reference<QNapi::Object> m_window;
302 QNapi::Reference<QNapi::Object> m_launchParam;
303 std::shared_ptr<void> m_windowWillCloseCallbackHandle;
304 std::function<void(JsState &, QNapi::Object, QOhosConsumer<JsState &, QOhosAbilityOnContinueResult>)> m_onContinueRequestsHandler;
305};
306
307QUiAbilityPeerImpl::QUiAbilityPeerImpl(
308 JsState &jsState, std::shared_ptr<QAbilityEngine> abilityEngine,
309 std::string instanceId, QObjectThreadSafeRef qwindow,
310 QNapi::Object uiAbility, QNapi::Object windowStage)
311 : QAbilityPeerImpl(jsState, abilityEngine, instanceId, qwindow, uiAbility)
312 , m_windowStage(Napi::Persistent(windowStage))
313 , m_window(Napi::Persistent(windowStage.call<QNapi::Object>("getMainWindowSync")))
314{
315 auto optLaunchParam = QNapi::getOptionalPropOrEmpty<QNapi::Object>(
316 uiAbility, getAbilityLaunchParamPropSymbol(jsState));
317 if (optLaunchParam.IsEmpty()) {
318 qOhosReportFatalErrorAndAbort(
319 "Ability with instanceId='%s' doesn't have 'launchParam' set. It shouldn't happen!",
320 instanceId.c_str());
321 }
322 m_launchParam = QNapi::Reference<>::makePersistentFrom(optLaunchParam);
323
324 m_windowWillCloseCallbackHandle = registerOnOffMethodsBasedEventHandler(
325 window(), "windowWillClose",
326 [this](const CallbackInfo &cbInfo) {
327 JsState &jsState = cbInfo.jsState();
328 return handleCloseRequestFromSystem(
329 jsState, "Window.windowWillClose",
330 CloseAbilityRequestSource::WindowWillClose,
331 [](JsState &jsState, CloseAbilityRequestResolution ohosRequestResolution) {
332 return QNapi::Boolean::New(
333 jsState.env(),
334 ohosRequestResolution == CloseAbilityRequestResolution::DontClose);
335 });
336 },
337 {
338 .optEventSourceAliveCheckFunc =
339 [qAbilityWeakRef = moveToSharedPtr(Napi::Weak(uiAbility))](QNapi::Object window) {
340 bool isWindowClosing = evalInJsThread(
341 [&](JsState &) {
342 return JsWindowsTracker::isWindowClosing(window);
343 },
344 Q_FUNC_INFO);
345 auto qAbilityValue = qAbilityWeakRef->Value();
346 return
347 !isWindowClosing
348 && qAbilityValue.IsObject()
349 && !QNapi::checkedCast<QNapi::Object>(qAbilityValue).eval<QNapi::Boolean>("context.isTerminating()");
350 },
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().call<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().call<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 JsState &jsState, const std::string &logContextStr, CloseAbilityRequestSource requestSource,
407 std::function<QNapi::Value(JsState &, 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(JsState &, 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<JsState &, CloseAbilityRequestResolution>(
445 [context = promiseResolveFuncContext](JsState &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 CallbackInfo &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 QOhosOptional<std::string> tryGetQAbilityInstanceIdFromWant(QNapi::Object appQAbility, QNapi::Object want) const override;
542 std::string getQAbilityInstanceIdOrPendingAutoStartedId(QNapi::Object qAbility) const override;
543
544 QOhosOptional<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 QOhosOptional<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
587QOhosOptional<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 ? makeQOhosOptional(QNapi::checkedCast<QNapi::String>(instanceIdParam).Utf8Value())
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
642QOhosOptional<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
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)
std::nullopt_t makeEmptyQOhosOptional()