8#include <QtCore/private/qcore_ohos_p.h>
9#include <QtCore/private/qnapi_p.h>
10#include <QtCore/private/qohoscommon_p.h>
11#include <QtCore/private/qohosjstools_p.h>
12#include <QtCore/private/qohoslogger_p.h>
13#include <QtOhosAppKit/private/qohosappbundleinfo_p.h>
14#include <QtOhosAppKit/private/qohosabilitycontext_p.h>
26#include <unordered_map>
40using QOhosSupplier =
std::function<T()>;
42int getCurrentApplicationVersionCode()
44 return QOhosJsThreadGateway::eval(
45 [](QOhosJsState &jsState) {
46 auto applicationInfoFlag = jsState.eval<QNapi::Number>(
47 "@ohos.bundle.bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_APPLICATION");
48 auto bundleInfo = jsState.eval<QNapi::Object>(
49 "@ohos.bundle.bundleManager.getBundleInfoForSelfSync(*)", {applicationInfoFlag});
50 int versionCode = bundleInfo.get<QNapi::Number>(
"versionCode");
57Q_NORETURN
void killCurrentProcess()
59 ::kill(getpid(), SIGKILL);
63std::optional<
std::uint32_t> tryGetCodeFromJsBusinessError(
const Napi::Error &error)
65 if (!error.Value().IsObject())
68 auto errorObject = QNapi::checkedCast<QNapi::Object>(error.Value());
69 auto optErrorCode = QNapi::getOptionalPropOrEmpty<QNapi::Number>(errorObject,
"code");
71 return !optErrorCode.IsEmpty()
72 ?
std::make_optional(optErrorCode.Uint32Value())
76Q_NORETURN
void restartAppImpl(std::optional<QJsonObject> want)
78 QOhosJsThreadGateway::runAndWait(
79 [&](QOhosJsState &jsState) {
80 auto napiWant = want.has_value()
81 ? QNapi::checkedCast<QNapi::Object>(QOhosJsEnv::toNapiValue(jsState.env(), want.value()))
82 : jsState.appLaunchWant();
84 constexpr auto sleepTimeBeforeRetry = std::chrono::seconds(3);
86 unsigned remainingTries = 3;
92 "%s: calling restartApp() using Want: %s",
93 Q_FUNC_INFO, QNapi::toJsonString(napiWant).c_str());
95 auto optQAbility = jsState.defaultQAbility();
96 if (!optQAbility.has_value())
97 qOhosReportFatalErrorAndAbort(
"%s: no default UIAbility available to restart the app", Q_FUNC_INFO);
100 optQAbility.value().eval(
101 "context.getApplicationContext().restartApp(*)", {napiWant});
103 qOhosPrintfWarning(
"%s: restartApp() call unexpectedly returned, killing self", Q_FUNC_INFO);
104 killCurrentProcess();
105 }
catch (
const Napi::Error &error) {
106 constexpr std::uint32_t restartTooFrequentlyErrorCode = 16000064;
108 auto errorCode = tryGetCodeFromJsBusinessError(error);
110 if (errorCode == restartTooFrequentlyErrorCode && remainingTries != 0) {
112 "%s: restartApp() returned with error %u, sleeping before retry",
113 Q_FUNC_INFO, restartTooFrequentlyErrorCode);
115 std::this_thread::sleep_for(sleepTimeBeforeRetry);
117 auto errorCodeStr = errorCode.has_value()
118 ? std::to_string(errorCode.value())
121 "%s: restartApp() returned with error %s, killing self",
122 Q_FUNC_INFO, errorCodeStr.c_str());
124 killCurrentProcess();
131 qOhosReportFatalErrorAndAbort(
"%s: unexpected return from the JS thread call", Q_FUNC_INFO);
134struct SerialPortPermissionsState
136 std::unordered_map<std::uint32_t, std::vector<QOhosConsumer<std::shared_ptr<
void>>>> m_pendingSerialPortsPermissionRequestsConsumers;
137 std::unordered_map<std::uint32_t, std::weak_ptr<
void>> m_grantedSerialPortsPermissionContexts;
140std::shared_ptr<SerialPortPermissionsState> serialPortPermissionsState()
142 static auto state =
std::make_shared<SerialPortPermissionsState>();
146std::optional<
std::uint32_t> tryConvertPortNameToSystemPortId(
const QString &portName)
148 constexpr const char *serialPortPrefix =
"COM";
149 const QString prefix = QLatin1String(serialPortPrefix);
151 if (!portName.startsWith(prefix))
154 bool parsedOk =
false;
155 const uint parsedValue = portName.mid(prefix.length()).toUInt(&parsedOk);
159 return static_cast<
std::uint32_t>(parsedValue);
162bool hasSerialPortAccessRightJsImpl(QOhosJsState &jsState,
std::uint32_t serialPortId)
165 return jsState.eval<QNapi::Boolean>(
"@ohos.usbManager.serial.hasSerialRight(*)", {serialPortId});
166 }
catch (
const Napi::Error &error) {
168 "%s: hasSerialRight for port %d failed with error: %s",
169 Q_FUNC_INFO, serialPortId, error.what());
174void requestSerialPortAccessRightJsImpl(
175 QOhosJsState &jsState,
std::uint32_t serialPortId, QOhosConsumer<
bool> resultConsumer)
177 jsState.evalToPromiseOrRejectOnThrow(
178 "@ohos.usbManager.serial.requestSerialRight(*)", {serialPortId})
179 .withContext(
std::move(resultConsumer))
181 [](
const QOhosCallbackInfo &cbInfo,
auto &resultConsumer) {
182 bool granted = cbInfo.getFirstArg<QNapi::Boolean>(Q_FUNC_INFO);
183 resultConsumer(granted);
186 [](
const QOhosCallbackInfo &cbInfo,
auto &resultConsumer) {
187 QtOhos::logJsCallbackError(
188 cbInfo,
"@ohos.usbManager.serial.requestSerialRight() failed");
189 resultConsumer(
false);
193void cancelSerialPortAccessRightJsImpl(QOhosJsState &jsState,
std::uint32_t serialPortId)
195 if (!hasSerialPortAccessRightJsImpl(jsState, serialPortId))
199 jsState.eval(
"@ohos.usbManager.serial.cancelSerialRight(*)", {serialPortId});
200 }
catch (
const Napi::Error &error) {
202 "%s: cancelSerialRight(%u) failed with error (ignoring): %s",
203 Q_FUNC_INFO, serialPortId, error.what());
207void processSerialPortPermissionResponse(
std::uint32_t serialPortId,
bool granted)
209 auto self = serialPortPermissionsState();
211 auto permissionContext = granted
212 ? QtOhos::makeDestroyNotifier(
213 [serialPortId, weakSelf = QtOhos::makeWeakPtr(serialPortPermissionsState())]() {
214 QtOhos::invokeInQtThread(
215 [serialPortId, weakSelf]() {
216 QOhosJsThreadGateway::runAndWait(
217 [&](QOhosJsState &jsState) {
218 cancelSerialPortAccessRightJsImpl(jsState, serialPortId);
222 auto self = weakSelf.lock();
224 self->m_grantedSerialPortsPermissionContexts.erase(serialPortId);
229 if (permissionContext)
230 self->m_grantedSerialPortsPermissionContexts[serialPortId] = permissionContext;
232 for (
const auto &asyncPermissionRequestConsumer : self->m_pendingSerialPortsPermissionRequestsConsumers[serialPortId])
233 asyncPermissionRequestConsumer(permissionContext);
235 self->m_pendingSerialPortsPermissionRequestsConsumers.erase(serialPortId);
238void requestSerialPortAccessRight(
239 const QString &portName, QObject *resultConsumerQtContext,
240 QOhosConsumer<std::shared_ptr<
void>> resultConsumer)
242 auto resultConsumerQtContextRef = QtOhos::makeQThreadSafeRef(resultConsumerQtContext);
243 auto asyncResultConsumer = [resultConsumerQtContextRef, resultConsumer = std::move(resultConsumer)](
std::shared_ptr<
void> permissionContext) {
244 resultConsumerQtContextRef.visitInQtThreadIfAlive(
245 [resultConsumer = std::move(resultConsumer), permissionContext](
auto &resultConsumerQtContext) {
246 QMetaObject::invokeMethod(
247 &resultConsumerQtContext,
248 [resultConsumer = std::move(resultConsumer), permissionContext]() {
249 resultConsumer(permissionContext);
251 Qt::QueuedConnection);
255 const auto optSerialPortId = tryConvertPortNameToSystemPortId(portName);
256 if (!optSerialPortId.has_value()) {
258 "%s: cannot convert serial port name '%s' to port id.",
259 Q_FUNC_INFO, portName.toStdString().c_str());
261 asyncResultConsumer(
nullptr);
265 QtOhos::invokeInQtThread(
266 [serialPortId = optSerialPortId.value(), weakSelf = QtOhos::makeWeakPtr(serialPortPermissionsState()), asyncResultConsumer = std::move(asyncResultConsumer)]() {
267 auto self = weakSelf.lock();
271 auto alreadyGrantedPermissionContextIt =
272 self->m_grantedSerialPortsPermissionContexts.find(serialPortId);
273 auto optAlreadyGrantedPermissionContext =
274 alreadyGrantedPermissionContextIt != self->m_grantedSerialPortsPermissionContexts.end()
275 ? alreadyGrantedPermissionContextIt->second.lock()
278 if (optAlreadyGrantedPermissionContext) {
279 asyncResultConsumer(optAlreadyGrantedPermissionContext);
283 self->m_pendingSerialPortsPermissionRequestsConsumers[serialPortId].push_back(
284 std::move(asyncResultConsumer));
286 if (self->m_pendingSerialPortsPermissionRequestsConsumers[serialPortId].size() == 1) {
287 QOhosJsThreadGateway::invoke(
288 [serialPortId, weakSelf](QOhosJsState &jsState) {
289 requestSerialPortAccessRightJsImpl(
292 [serialPortId, weakSelf](
bool granted) {
293 QtOhos::invokeInQtThread(
294 [serialPortId, weakSelf, granted]() {
295 auto self = weakSelf.lock();
297 processSerialPortPermissionResponse(serialPortId, granted);
305class QOhosAppContextImpl :
public AppContext
308 QOhosAppContextImpl();
310 bool hasSerialPortAccessRight(
const QString &portName)
const override;
311 void requestSerialPortAccessRightIfNeeded(
312 const QString &portName, QObject *context,
313 std::function<
void(std::shared_ptr<QObject>)> callback)
override;
314 std::shared_ptr<BundleInfo> bundleInfo()
const override;
315 Q_NORETURN
void restartApp(
const std::optional<Want> &want)
override;
317 double fontSizeScale()
const override;
320 QOhosSupplier<
double> m_fontSizeScaleSupplier;
324std::shared_ptr<QObject> makeQObjectLifetimeHandleOrNull(
std::shared_ptr<T> handle)
329 return std::shared_ptr<QObject>(
331 [handle](QObject *ptr) {
339
340
341
342
343
350
351
352
353
361
362
363
364
365
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
407
408
409
410
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443Q_NORETURN
void QOhosAppContextImpl::restartApp(
const std::optional<Want> &want)
445 restartAppImpl(want ? std::optional(convertWantToJsonObject(*want)) : std::nullopt);
448QOhosAppContextImpl::QOhosAppContextImpl()
450 qRegisterMetaType<std::shared_ptr<QObject>>();
452 m_fontSizeScaleSupplier = makeQOhosDataSource<
double>(
453 [](QOhosJsState &jsState) ->
double {
454 auto optQAbility = jsState.defaultQAbility();
455 if (!optQAbility.has_value())
457 return optQAbility->eval<QNapi::Number>(
"context.config.fontSizeScale");
459 [](QOhosJsState &jsState, QOhosConsumer<
double> valueUpdatesConsumer) {
460 return registerOhosAppContextEnvironmentCallback(
464 "onConfigurationUpdated",
465 [valueUpdatesConsumer = std::move(valueUpdatesConsumer)](
const QOhosCallbackInfo &cbInfo) {
466 auto config = cbInfo.getFirstArg<QNapi::Object>(Q_FUNC_INFO);
467 valueUpdatesConsumer(config.get<QNapi::Number>(
"fontSizeScale"));
472 [
this](
double fontSizeScale) {
473 Q_EMIT fontSizeScaleChanged(fontSizeScale);
475 QtOhos::invokeInQtThread,
479double QOhosAppContextImpl::fontSizeScale()
const
481 return m_fontSizeScaleSupplier();
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501bool QOhosAppContextImpl::hasSerialPortAccessRight(
const QString &portName)
const
503 const auto optSerialPortId = tryConvertPortNameToSystemPortId(portName);
504 if (!optSerialPortId.has_value()) {
506 "%s: cannot convert serial port name '%s' to port id.",
507 Q_FUNC_INFO, portName.toStdString().c_str());
511 return QOhosJsThreadGateway::eval(
512 [&](QOhosJsState &jsState) {
513 return hasSerialPortAccessRightJsImpl(jsState, optSerialPortId.value());
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542void QOhosAppContextImpl::requestSerialPortAccessRightIfNeeded(
543 const QString &portName, QObject *context,
544 std::function<
void(std::shared_ptr<QObject>)> callback)
546 requestSerialPortAccessRight(
548 [callback = std::move(callback)](
std::shared_ptr<
void> serialPortAccessRightContext) {
549 callback(makeQObjectLifetimeHandleOrNull(serialPortAccessRightContext));
554
555
556
557
558
559std::shared_ptr<BundleInfo> QOhosAppContextImpl::bundleInfo()
const
561 return createBundleInfo(getCurrentApplicationVersionCode());