Revision control
++ // Copyright (C) 2025 The Qt Company Ltd.
++ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
++
++ #include <QtCore/private/qcore_ohos_p.h>
++ #include <QtCore/private/qohoslogger_p.h>
++ #include <ohossensorsmanager.h>
++ #include <ohossensorutils.h>
++ #include <algorithm>
++ #include <chrono>
++ #include <optional>
++
++ namespace {
++
++ std::optional<std::vector<float>> tryGetSensorEventData(::Sensor_Event *sensorEvent)
++ {
++ float *data = nullptr;
++ std::uint32_t len = 0;
++ std::int32_t getDataResult = ::OH_SensorEvent_GetData(sensorEvent, &data, &len);
++ if (getDataResult != ::SENSOR_SUCCESS) {
++ qOhosPrintfError("%s: OH_SensorEvent_GetData failed, code=%d", Q_FUNC_INFO, getDataResult);
++ return std::nullopt;
++ }
++ if (data == nullptr) {
++ qOhosPrintfError("%s: data pointer is null", Q_FUNC_INFO);
++ return std::nullopt;
++ }
++ return std::vector<float>(data, data + len);
++ }
++
++ std::shared_ptr<std::vector<::Sensor_Info *>> tryCreateInfos(std::uint32_t count)
++ {
++ ::Sensor_Info **sensors = ::OH_Sensor_CreateInfos(count);
++ if (sensors == nullptr) {
++ qOhosPrintfError("%s: cannot create sensor infos objects", Q_FUNC_INFO);
++ return nullptr;
++ }
++
++ auto sensorsWrapper = std::shared_ptr<void>(
++ sensors,
++ [sensors, count](auto *) {
++ std::uint32_t destroyInfosResult = ::OH_Sensor_DestroyInfos(sensors, count);
++ if (destroyInfosResult != ::SENSOR_SUCCESS) {
++ qOhosPrintfError(
++ "%s: cannot destroy sensor infos, error: %d", Q_FUNC_INFO, destroyInfosResult);
++ }
++ }
++ );
++
++ return QtOhos::makeSharedPtrWithAttachedExtraData(
++ std::make_shared<std::vector<::Sensor_Info *>>(sensors, sensors + count),
++ sensorsWrapper);
++ }
++
++ std::shared_ptr<std::vector<::Sensor_Info *>> tryGetAvailableSensorsInfo()
++ {
++ std::uint32_t sensorsCount = 0;
++ std::uint32_t getSensorsCountResult = ::OH_Sensor_GetInfos(nullptr, &sensorsCount);
++ if (getSensorsCountResult != ::SENSOR_SUCCESS) {
++ qOhosPrintfError(
++ "%s: cannot get sensors count, error: %d", Q_FUNC_INFO, getSensorsCountResult);
++ return nullptr;
++ }
++
++ auto sensors = tryCreateInfos(sensorsCount);
++ if (!sensors) {
++ return nullptr;
++ }
++
++ std::uint32_t getInfosResult = ::OH_Sensor_GetInfos(sensors->data(), &sensorsCount);
++ if (getInfosResult != ::SENSOR_SUCCESS) {
++ qOhosPrintfError(
++ "%s: cannot get sensors infos, error: %d", Q_FUNC_INFO, getInfosResult);
++ return nullptr;
++ }
++
++ return sensors;
++ }
++
++ }
++
++ OhosSensorsManager::OhosSensorsManager()
++ {
++ QOhosJsThreadGateway::runAndWait(
++ [&](QOhosJsState &) {
++ m_jsScopeData = QtOhos::makeProxyWithJsThreadDeleter(std::make_shared<JsScopeData>());
++
++ m_jsScopeData->availableSensorsInfo = tryGetAvailableSensorsInfo();
++
++ m_jsScopeData->subscriber = tryCreateSensorResource<::Sensor_Subscriber>(
++ ::OH_Sensor_CreateSubscriber, ::OH_Sensor_DestroySubscriber);
++ if (!m_jsScopeData->subscriber) {
++ qOhosReportFatalErrorAndAbort("%s: Failed to create sensor subscriber.", Q_FUNC_INFO);
++ }
++
++ std::int32_t setCallbackRetVal = ::OH_SensorSubscriber_SetCallback(
++ m_jsScopeData->subscriber.get(), OhosSensorsManager::sensorEventCallbackHandler);
++ if (setCallbackRetVal != ::SENSOR_SUCCESS) {
++ qOhosReportFatalErrorAndAbort(
++ "%s: OH_SensorSubscriber_SetCallback fail with error code: %d",
++ Q_FUNC_INFO, setCallbackRetVal);
++ }
++ });
++ }
++
++ OhosSensorsManager *OhosSensorsManager::instance()
++ {
++ static OhosSensorsManager sensorManager;
++ return &sensorManager;
++ }
++
++ std::vector<::Sensor_Type> OhosSensorsManager::getAvailableSensorsTypes()
++ {
++ return QOhosJsThreadGateway::eval(
++ [&](QOhosJsState &) {
++ std::vector<::Sensor_Type> result;
++
++ auto sensorsInfo = m_jsScopeData->availableSensorsInfo;
++ if (sensorsInfo) {
++ for (auto *sensorInfo : *sensorsInfo) {
++ auto sensorType = tryGetSensorInfoField<::Sensor_Type>(sensorInfo, ::OH_SensorInfo_GetType);
++ if (sensorType.has_value()) {
++ result.push_back(sensorType.value());
++ }
++ }
++ }
++
++ return result;
++ });
++ }
++
++ std::optional<OhosSensorCapabilities> OhosSensorsManager::tryGetSensorCapabilities(QSensor *sensor)
++ {
++ auto ohosSensorType = tryMapQtToOhosSensorType(sensor->type());
++ if (!ohosSensorType.has_value()) {
++ qOhosPrintfError(
++ "%s: Cannot map Qt sensor type: %s, to OHOS Sensor_Type",
++ Q_FUNC_INFO, sensor->type().toStdString().c_str());
++ return std::nullopt;
++ }
++
++ return QOhosJsThreadGateway::eval(
++ [&](QOhosJsState &) -> std::optional<OhosSensorCapabilities> {
++ auto availableSensorsInfo = m_jsScopeData->availableSensorsInfo;
++ if (!availableSensorsInfo) {
++ return std::nullopt;
++ }
++
++ auto sensorInfo = std::find_if(
++ availableSensorsInfo->begin(),
++ availableSensorsInfo->end(),
++ [&](auto *info) {
++ return tryGetSensorInfoField<::Sensor_Type>(info, ::OH_SensorInfo_GetType) == ohosSensorType;
++ });
++
++ if (sensorInfo == availableSensorsInfo->end()) {
++ qOhosPrintfError(
++ "%s: Cannot find a sensor info for a sensor of type: %d",
++ Q_FUNC_INFO, ohosSensorType.value());
++ return std::nullopt;
++ }
++
++ auto resolution = tryGetSensorInfoField<float>(*sensorInfo, ::OH_SensorInfo_GetResolution);
++ if (!resolution.has_value()) {
++ qOhosPrintfError(
++ "%s: Cannot get resolution for a sensor of type: %d", Q_FUNC_INFO, ohosSensorType.value());
++ return std::nullopt;
++ }
++
++ auto minSamplingIntervalInNs = tryGetSensorInfoField<std::int64_t>(
++ *sensorInfo, ::OH_SensorInfo_GetMinSamplingInterval);
++ if (!minSamplingIntervalInNs.has_value()) {
++ qOhosPrintfError(
++ "%s: Cannot get minimal sampling interval for a sensor of type: %d",
++ Q_FUNC_INFO, ohosSensorType.value());
++ return std::nullopt;
++ }
++
++ auto maxSamplingIntervalInNs = tryGetSensorInfoField<std::int64_t>(
++ *sensorInfo, ::OH_SensorInfo_GetMaxSamplingInterval);
++ if (!maxSamplingIntervalInNs.has_value()) {
++ qOhosPrintfError(
++ "%s: Cannot get maximal sampling interval for a sensor of type: %d",
++ Q_FUNC_INFO, ohosSensorType.value());
++ return std::nullopt;
++ }
++
++ auto samplingIntervalToDataRate =
++ [](std::int64_t intervalInNs) -> int {
++ return intervalInNs != 0
++ ? 1e9 / intervalInNs
++ : 0;
++ };
++
++ return OhosSensorCapabilities{
++ resolution.value(),
++ samplingIntervalToDataRate(maxSamplingIntervalInNs.value()),
++ samplingIntervalToDataRate(minSamplingIntervalInNs.value())
++ };
++ });
++ }
++
++ void OhosSensorsManager::subscribeSensor(QSensor *sensor, QOhosConsumer<const OhosSensorData &> sensorDataConsumer)
++ {
++ auto subscribedSensor = m_subscribedSensors.find(sensor);
++ if (subscribedSensor != m_subscribedSensors.end()) {
++ qOhosPrintfError(
++ "%s: Sensor %s already subscribed.",
++ Q_FUNC_INFO, sensor->type().toStdString().c_str());
++ return;
++ }
++
++ auto ohosSensorType = tryMapQtToOhosSensorType(sensor->type());
++ if (!ohosSensorType.has_value()) {
++ qOhosPrintfError(
++ "%s: Cannot map Qt sensor type: %s, to OHOS Sensor_Type",
++ Q_FUNC_INFO, sensor->type().toStdString().c_str());
++ return;
++ }
++
++ auto sensorSubscriptionHandle = QOhosJsThreadGateway::eval(
++ [&](QOhosJsState &) -> std::shared_ptr<void> {
++ auto sensorSubId = tryCreateSensorResource<::Sensor_SubscriptionId>(
++ ::OH_Sensor_CreateSubscriptionId, ::OH_Sensor_DestroySubscriptionId);
++ if (!sensorSubId) {
++ qOhosPrintfError("%s: OH_Sensor_CreateSubscriptionId return nullptr", Q_FUNC_INFO);
++ return nullptr;
++ }
++
++ std::int32_t idRetVal = ::OH_SensorSubscriptionId_SetType(sensorSubId.get(), ohosSensorType.value());
++ if (idRetVal != ::SENSOR_SUCCESS) {
++ qOhosPrintfError("%s: OH_Sensor_CreateSubscriptionId fail with error code: %d",
++ Q_FUNC_INFO, idRetVal);
++ return nullptr;
++ }
++
++ auto sensorSubAttr = tryCreateSensorResource<::Sensor_SubscriptionAttribute>(
++ ::OH_Sensor_CreateSubscriptionAttribute, ::OH_Sensor_DestroySubscriptionAttribute);
++ if (!sensorSubAttr) {
++ qOhosPrintfError("%s: OH_Sensor_CreateSubscriptionAttribute returned nullptr", Q_FUNC_INFO);
++ return nullptr;
++ }
++
++ auto sensorSamplePeriod = std::chrono::duration<double>(1.0 / sensor->dataRate());
++ std::int32_t sensorSubAttrRetVal = ::OH_SensorSubscriptionAttribute_SetSamplingInterval(
++ sensorSubAttr.get(),
++ std::chrono::duration_cast<std::chrono::nanoseconds>(sensorSamplePeriod).count());
++ if (sensorSubAttrRetVal != ::SENSOR_SUCCESS) {
++ qOhosPrintfError(
++ "%s: OH_SensorSubscriptionAttribute_SetSamplingInterval fail with error code: %d",
++ Q_FUNC_INFO, sensorSubAttrRetVal);
++ return nullptr;
++ }
++
++ std::int32_t subscribeRetVal = ::OH_Sensor_Subscribe(
++ sensorSubId.get(), sensorSubAttr.get(), m_jsScopeData->subscriber.get());
++ if (subscribeRetVal != ::SENSOR_SUCCESS) {
++ qOhosPrintfError("%s: OH_Sensor_Subscribe fail with error code: %d",
++ Q_FUNC_INFO, subscribeRetVal);
++ return nullptr;
++ }
++
++ auto sensorSubscriptionHandle = std::shared_ptr<void>(
++ sensorSubId.get(),
++ [sensorSubId = std::move(sensorSubId),
++ weakSubscriber = std::weak_ptr<::Sensor_Subscriber>(m_jsScopeData->subscriber)](void *) mutable {
++ auto subscriber = weakSubscriber.lock();
++ if (subscriber) {
++ std::int32_t unsubRetVal = ::OH_Sensor_Unsubscribe(sensorSubId.get(), subscriber.get());
++ if (unsubRetVal != ::SENSOR_SUCCESS) {
++ qOhosPrintfError(
++ "%s: OH_Sensor_Unsubscribe fail with error code: %d",
++ Q_FUNC_INFO, unsubRetVal);
++ }
++ }
++ sensorSubId.reset();
++ });
++
++ return QtOhos::makeProxyWithJsThreadDeleter(std::move(sensorSubscriptionHandle));
++ });
++
++ if (sensorSubscriptionHandle) {
++ m_subscribedSensors.emplace(sensor, SensorSubscriptionHandle{sensorSubscriptionHandle, sensorDataConsumer});
++ }
++ }
++
++ void OhosSensorsManager::unsubscribeSensor(QSensor *sensor)
++ {
++ auto subscribedSensor = m_subscribedSensors.find(sensor);
++ if (subscribedSensor == m_subscribedSensors.end()) {
++ qOhosPrintfError(
++ "%s: Sensor %s not subscribed. Cannot unsubscribe.",
++ Q_FUNC_INFO, sensor->type().toStdString().c_str());
++ return;
++ }
++
++ m_subscribedSensors.erase(subscribedSensor);
++ }
++
++ void OhosSensorsManager::sensorEventCallbackHandler(::Sensor_Event *event)
++ {
++ if (event == nullptr) {
++ qOhosPrintfInfo("%s: sensor event is null", Q_FUNC_INFO);
++ return;
++ }
++
++ auto sensorType = tryGetSensorEventField<::Sensor_Type>(event, ::OH_SensorEvent_GetType);
++ if (!sensorType.has_value()) {
++ qOhosPrintfError("%s: Cannot obtain sensor type from event.", Q_FUNC_INFO);
++ return;
++ }
++
++ auto timestamp = tryGetSensorEventField<std::int64_t>(event, ::OH_SensorEvent_GetTimestamp);
++ if (!timestamp.has_value()) {
++ qOhosPrintfError("%s: Cannot obtain timestamp from event.", Q_FUNC_INFO);
++ return;
++ }
++
++ auto accuracy = tryGetSensorEventField<::Sensor_Accuracy>(event, ::OH_SensorEvent_GetAccuracy);
++ if (!accuracy.has_value()) {
++ qOhosPrintfError("%s: Cannot obtain sensor accuracy from event.", Q_FUNC_INFO);
++ return;
++ }
++
++ auto data = tryGetSensorEventData(event);
++ if (!data.has_value()) {
++ qOhosPrintfError("%s: Cannot obtain sensor data from event.", Q_FUNC_INFO);
++ return;
++ }
++
++ OhosSensorData sensorData{timestamp.value(), accuracy.value(), data.value()};
++
++ QtOhos::invokeInQtThread(
++ [sensorType, sensorData]() {
++ auto sensorDataConsumer =
++ OhosSensorsManager::instance()->tryGetSensorConsumerForGivenOhosSensorType(sensorType.value());
++ if (sensorDataConsumer != nullptr) {
++ sensorDataConsumer(sensorData);
++ }
++ });
++ }
++
++ QOhosConsumer<const OhosSensorData &>
++ OhosSensorsManager::tryGetSensorConsumerForGivenOhosSensorType(::Sensor_Type ohosSensorType)
++ {
++ auto sensor = std::find_if(
++ m_subscribedSensors.begin(),
++ m_subscribedSensors.end(),
++ [&](const auto &kv) {
++ return kv.first->identifier() == QByteArray::number(ohosSensorType);
++ });
++
++ if (sensor == m_subscribedSensors.end()) {
++ qOhosPrintfError(
++ "%s: Sensor with identifier %d is not subscribed.",
++ Q_FUNC_INFO, ohosSensorType);
++ return nullptr;
++ }
++
++ return sensor->second.sensorDataConsumer;
++ }