diff --git a/bundle.json b/bundle.json old mode 100755 new mode 100644 index 1068b7f5cdaa41b24aac0c98df3496986f6711da..1f60658af12fe30ba5ec55111265e8d8825c517c --- a/bundle.json +++ b/bundle.json @@ -36,7 +36,8 @@ "hicollie", "init", "selinux_adapter", - "cJSON" + "cJSON", + "runtime_core" ], "third_party": [] }, @@ -45,6 +46,7 @@ "base_group": [], "fwk_group": [ "//base/sensors/sensor/frameworks/js/napi:sensor_js_target", + "//base/sensors/sensor/frameworks/js/ani:sensor_ani_target", "//base/sensors/sensor/frameworks/cj:cj_sensor_ffi", "//base/sensors/sensor/frameworks/native:sensor_target", "//base/sensors/sensor/frameworks/native:ohsensor", diff --git a/frameworks/js/ani/BUILD.gn b/frameworks/js/ani/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..c56092f3b88265d944156277f43425d535aa2682 --- /dev/null +++ b/frameworks/js/ani/BUILD.gn @@ -0,0 +1,77 @@ +# Copyright (c) 2025 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import("//build/config/components/ets_frontend/ets2abc_config.gni") +import("//build/ohos.gni") +import("./../../../sensor.gni") + +ohos_shared_library("sensor_ani") { + branch_protector_ret = "pac_ret" + sanitize = { + cfi = true + cfi_cross_dso = true + debug = false + } + + sources = [ "src/sensor_ani.cpp" ] + include_dirs = [ + "include", + "$SUBSYSTEM_DIR/frameworks/native/include", + "$SUBSYSTEM_DIR/interfaces/inner_api", + "$SUBSYSTEM_DIR/utils/common/include", + ] + deps = [ + "$SUBSYSTEM_DIR/frameworks/native:sensor_interface_native", + "$SUBSYSTEM_DIR/utils/common:libsensor_utils", + ] + + external_deps = [ + "bundle_framework:appexecfwk_base", + "bundle_framework:appexecfwk_core", + "c_utils:utils", + "eventhandler:libeventhandler", + "hilog:libhilog", + "ipc:ipc_single", + "runtime_core:ani", + "safwk:system_ability_fwk", + "samgr:samgr_proxy", + ] + + subsystem_name = "sensors" + part_name = "sensor" + output_extension = "so" +} + +generate_static_abc("sensor_abc") { + base_url = "./ets" + files = [ "./ets/@ohos.sensor.ets" ] + dst_file = "$target_out_dir/sensor.abc" + out_puts = [ "$target_out_dir/sensor.abc" ] + is_boot_abc = "True" + device_dst_file = "/system/framework/sensor.abc" +} + +ohos_prebuilt_etc("sensor_abc_etc") { + source = "$target_out_dir/sensor.abc" + module_install_dir = "framework" + subsystem_name = "sensors" + part_name = "sensor" + deps = [ ":sensor_abc" ] +} + +group("sensor_ani_target") { + deps = [ + ":sensor_abc_etc", + ":sensor_ani", + ] +} diff --git a/frameworks/js/ani/ets/@ohos.sensor.ets b/frameworks/js/ani/ets/@ohos.sensor.ets new file mode 100644 index 0000000000000000000000000000000000000000..31b4c5526f534d93dcd08d63432afffcc5167804 --- /dev/null +++ b/frameworks/js/ani/ets/@ohos.sensor.ets @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import type { Callback } from '@ohos.base'; +import { BusinessError } from '@ohos.base'; + +export default namespace sensor { + loadLibrary("sensor_ani"); + + export native function on(type: 'ORIENTATION', callback: Callback, options?: Options): void; + export native function off(type: 'ORIENTATION', callback?: Callback): void; + export type SensorFrequency = 'game' | 'ui' | 'normal'; + export interface Options { + interval?: number | SensorFrequency; + } + + export enum SensorAccuracy { + ACCURACY_UNRELIABLE = 0, + ACCURACY_LOW = 1, + ACCURACY_MEDIUM = 2, + ACCURACY_HIGH = 3 + } + + export interface Response { + timestamp: number; + accuracy: SensorAccuracy; + } + + export interface OrientationResponse extends Response { + alpha: number; + beta: number; + gamma: number; + } + + class OrientationResponseImpl implements OrientationResponse { + timestamp: number; + accuracy: SensorAccuracy; + alpha: number; + beta: number; + gamma: number; + } +} \ No newline at end of file diff --git a/frameworks/js/ani/include/ani_utils.h b/frameworks/js/ani/include/ani_utils.h new file mode 100644 index 0000000000000000000000000000000000000000..8f2605f0e3f3ad9c2e8399e9d0f56efe06bbe684 --- /dev/null +++ b/frameworks/js/ani/include/ani_utils.h @@ -0,0 +1,413 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANI_UTILS_H +#define ANI_UTILS_H + +#include + +#include +#include +#include +#include +#include +#include +#include + +template +class NativeObjectWrapper { +public: + static ani_object Create([[maybe_unused]] ani_env *env, [[maybe_unused]] ani_class clazz) + { + T* nativePtr = new T; + return Wrap(env, clazz, nativePtr); + } + + static ani_object Wrap([[maybe_unused]] ani_env *env, [[maybe_unused]] ani_class clazz, T* nativePtr) + { + ani_method ctor; + if (ANI_OK != env->Class_FindMethod(clazz, "", "J:V", &ctor)) { + std::cerr << "Not found ''" << std::endl; + ani_object nullobj = nullptr; + return nullobj; + } + + ani_object obj; + if (ANI_OK != env->Object_New(clazz, ctor, &obj, reinterpret_cast(nativePtr))) { + std::cerr << "Object_New failed" << std::endl; + } + return obj; + } + + static T* Unwrap(ani_env *env, ani_object object, const char* propName = "nativePtr") + { + ani_long nativePtr; + if (ANI_OK != env->Object_GetFieldByName_Long(object, propName, &nativePtr)) { + return nullptr; + } + return reinterpret_cast(nativePtr); + } + + static ani_status Attach(ani_env *env, ani_object object, T* nativePtr, const char* propName = "nativePtr") + { + return env->Object_SetFieldByName_Long(object, propName, reinterpret_cast(nativePtr)); + } +}; + +class AniStringUtils { +public: + static std::string ToStd(ani_env *env, ani_string ani_str) + { + ani_size strSize; + env->String_GetUTF8Size(ani_str, &strSize); + + std::vector buffer(strSize + 1); // +1 for null terminator + char* utf8_buffer = buffer.data(); + + //String_GetUTF8 Supportted by https://gitee.com/openharmony/arkcompiler_runtime_core/pulls/3416 + ani_size bytes_written = 0; + env->String_GetUTF8(ani_str, utf8_buffer, strSize + 1, &bytes_written); + + utf8_buffer[bytes_written] = '\0'; + std::string content = std::string(utf8_buffer); + return content; + } + + static ani_string ToAni(ani_env* env, const std::string& str) + { + ani_string aniStr = nullptr; + if (ANI_OK != env->String_NewUTF8(str.data(), str.size(), &aniStr)) { + std::cerr << "[ANI] Unsupported ANI_VERSION_1" << std::endl; + return nullptr; + } + return aniStr; + } +}; + +class UnionAccessor { +public: + UnionAccessor(ani_env *env, ani_object &obj) : env_(env), obj_(obj) + { + } + + bool IsInstanceOf(const std::string& cls_name) + { + ani_class cls; + env_->FindClass(cls_name.c_str(), &cls); + + ani_boolean ret; + env_->Object_InstanceOf(obj_, cls, &ret); + return ret; + } + + template + bool IsInstanceOfType(); + + template + bool TryConvert(T &value); + + template + bool TryConvertArray(std::vector &value); + +private: + ani_env *env_; + ani_object obj_; +}; + +template<> +bool UnionAccessor::IsInstanceOfType() +{ + return IsInstanceOf("Lstd/core/Boolean;"); +} + +template<> +bool UnionAccessor::IsInstanceOfType() +{ + return IsInstanceOf("Lstd/core/Int;"); +} + +template<> +bool UnionAccessor::IsInstanceOfType() +{ + return IsInstanceOf("Lstd/core/Double;"); +} + +template<> +bool UnionAccessor::IsInstanceOfType() +{ + return IsInstanceOf("Lstd/core/String;"); +} + +template<> +bool UnionAccessor::TryConvert(bool &value) +{ + if (!IsInstanceOfType()) { + return false; + } + + ani_boolean aniValue; + auto ret = env_->Object_CallMethodByName_Boolean(obj_, "unboxed", nullptr, &aniValue); + if (ret != ANI_OK) { + return false; + } + value = static_cast(aniValue); + return true; +} + +template<> +bool UnionAccessor::TryConvert(int &value) +{ + if (!IsInstanceOfType()) { + return false; + } + + ani_int aniValue; + auto ret = env_->Object_CallMethodByName_Int(obj_, "unboxed", nullptr, &aniValue); + if (ret != ANI_OK) { + return false; + } + value = static_cast(aniValue); + return true; +} + +template<> +bool UnionAccessor::TryConvert(double &value) +{ + if (!IsInstanceOfType()) { + return false; + } + + ani_double aniValue; + auto ret = env_->Object_CallMethodByName_Double(obj_, "unboxed", nullptr, &aniValue); + if (ret != ANI_OK) { + return false; + } + value = static_cast(aniValue); + return true; +} + +template<> +bool UnionAccessor::TryConvert(std::string &value) +{ + if (!IsInstanceOfType()) { + return false; + } + + value = AniStringUtils::ToStd(env_, static_cast(obj_)); + return true; +} + +template<> +bool UnionAccessor::TryConvertArray(std::vector &value) +{ + ani_double length; + if (ANI_OK != env_->Object_GetPropertyByName_Double(obj_, "length", &length)) { + std::cerr << "Object_GetPropertyByName_Double length failed" << std::endl; + return false; + } + for (int i = 0; i < int(length); i++) { + ani_ref ref; + if (ANI_OK != env_->Object_CallMethodByName_Ref(obj_, "$_get", "I:Lstd/core/Object;", &ref, (ani_int)i)) { + std::cerr << "Object_GetPropertyByName_Ref failed" << std::endl; + return false; + } + ani_boolean val; + if (ANI_OK != env_->Object_CallMethodByName_Boolean(static_cast(ref), "unboxed", nullptr, &val)) { + std::cerr << "Object_CallMethodByName_Double unbox failed" << std::endl; + return false; + } + value.push_back(static_cast(val)); + } + return true; +} + +template<> +bool UnionAccessor::TryConvertArray(std::vector &value) +{ + ani_double length; + if (ANI_OK != env_->Object_GetPropertyByName_Double(obj_, "length", &length)) { + std::cerr << "Object_GetPropertyByName_Double length failed" << std::endl; + return false; + } + for (int i = 0; i < int(length); i++) { + ani_ref ref; + if (ANI_OK != env_->Object_CallMethodByName_Ref(obj_, "$_get", "I:Lstd/core/Object;", &ref, (ani_int)i)) { + std::cerr << "Object_GetPropertyByName_Ref failed" << std::endl; + return false; + } + ani_int intValue; + if (ANI_OK != env_->Object_CallMethodByName_Int(static_cast(ref), "unboxed", nullptr, &intValue)) { + std::cerr << "Object_CallMethodByName_Double unbox failed" << std::endl; + return false; + } + value.push_back(static_cast(intValue)); + } + return true; +} + +template<> +bool UnionAccessor::TryConvertArray(std::vector &value) +{ + ani_double length; + if (ANI_OK != env_->Object_GetPropertyByName_Double(obj_, "length", &length)) { + std::cerr << "Object_GetPropertyByName_Double length failed" << std::endl; + return false; + } + for (int i = 0; i < int(length); i++) { + ani_ref ref; + if (ANI_OK != env_->Object_CallMethodByName_Ref(obj_, "$_get", "I:Lstd/core/Object;", &ref, (ani_int)i)) { + std::cerr << "Object_GetPropertyByName_Ref failed" << std::endl; + return false; + } + ani_double val; + if (ANI_OK != env_->Object_CallMethodByName_Double(static_cast(ref), "unboxed", nullptr, &val)) { + std::cerr << "Object_CallMethodByName_Double unbox failed" << std::endl; + return false; + } + value.push_back(static_cast(val)); + } + return true; +} + +template<> +bool UnionAccessor::TryConvertArray(std::vector &value) +{ + std::cout << "TryConvertArray std::vector" << std::endl; + ani_ref buffer; + if (ANI_OK != env_->Object_GetFieldByName_Ref(obj_, "buffer", &buffer)) { + std::cout << "Object_GetFieldByName_Ref failed" << std::endl; + return false; + } + void* data; + size_t length; + if (ANI_OK != env_->ArrayBuffer_GetInfo(static_cast(buffer), &data, &length)) { + std::cerr << "ArrayBuffer_GetInfo failed" << std::endl; + return false; + } + std::cout << "Length of buffer is " << length << std::endl; + for (size_t i = 0; i < length; i++) { + value.push_back(static_cast(data)[i]); + } + return true; +} + +template<> +bool UnionAccessor::TryConvertArray(std::vector &value) +{ + ani_double length; + if (ANI_OK != env_->Object_GetPropertyByName_Double(obj_, "length", &length)) { + std::cerr << "Object_GetPropertyByName_Double length failed" << std::endl; + return false; + } + + for (int i = 0; i < int(length); i++) { + ani_ref ref; + if (ANI_OK != env_->Object_CallMethodByName_Ref(obj_, "$_get", "I:Lstd/core/Object;", &ref, (ani_int)i)) { + std::cerr << "Object_GetPropertyByName_Double length failed" << std::endl; + return false; + } + value.push_back(AniStringUtils::ToStd(env_, static_cast(ref))); + } + return true; +} + +class OptionalAccessor { +public: + OptionalAccessor(ani_env *env, ani_object &obj) : env_(env), obj_(obj) + { + } + + bool IsUndefined() + { + ani_boolean isUndefined; + env_->Reference_IsUndefined(obj_, &isUndefined); + return isUndefined; + } + + template + std::optional Convert(); + +private: + ani_env *env_; + ani_object obj_; +}; + +template<> +std::optional OptionalAccessor::Convert() +{ + if (IsUndefined()) { + return std::nullopt; + } + + ani_double aniValue; + auto ret = env_->Object_CallMethodByName_Double(obj_, "doubleValue", nullptr, &aniValue); + if (ret != ANI_OK) { + return std::nullopt; + } + auto value = static_cast(aniValue); + return value; +} + +template<> +std::optional OptionalAccessor::Convert() +{ + if (IsUndefined()) { + return std::nullopt; + } + + ani_size strSize; + env_->String_GetUTF8Size(static_cast(obj_), &strSize); + + std::vector buffer(strSize + 1); + char* utf8_buffer = buffer.data(); + + ani_size bytes_written = 0; + env_->String_GetUTF8(static_cast(obj_), utf8_buffer, strSize + 1, &bytes_written); + + utf8_buffer[bytes_written] = '\0'; + std::string content = std::string(utf8_buffer); + return content; +} + +class AniLocalScopeGuard { +public: + AniLocalScopeGuard(ani_env *env, size_t nrRefs) : env_(env) + { + status_ = env_->CreateLocalScope(nrRefs); + } + + ~AniLocalScopeGuard() + { + if (ANI_OK != status_) { + return; + } + env_->DestroyLocalScope(); + } + + bool IsStatusOK() + { + return ANI_OK == status_; + } + + ani_status GetStatus() + { + return status_; + } + +private: + ani_env *env_ = nullptr; + ani_status status_ = ANI_ERROR; +}; +#endif \ No newline at end of file diff --git a/frameworks/js/ani/include/sensor_ani.h b/frameworks/js/ani/include/sensor_ani.h new file mode 100644 index 0000000000000000000000000000000000000000..be11ac1711c37a0a90df2978ea5e93a182a7a27d --- /dev/null +++ b/frameworks/js/ani/include/sensor_ani.h @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef SENSOR_ANI_H +#define SENSOR_ANI_H + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "refbase.h" +#include "sensor_agent_type.h" +#include "sensor_errors.h" +#include "sensor_log.h" + +#undef LOG_TAG +#define LOG_TAG "SensorAniAPI" + +namespace OHOS { +namespace Sensors { +using std::vector; +using std::string; +using namespace OHOS::HiviewDFX; +constexpr int32_t THREE_DIMENSIONAL_MATRIX_LENGTH = 9; +constexpr static int32_t DATA_LENGTH = 16; +constexpr int32_t CALLBACK_NUM = 3; +enum CallbackDataType { + SUBSCRIBE_FAIL = -2, + FAIL = -1, + OFF_CALLBACK = 0, + ON_CALLBACK = 1, + ONCE_CALLBACK = 2, + GET_GEOMAGNETIC_FIELD = 3, + GET_ALTITUDE = 4, + GET_GEOMAGNETIC_DIP = 5, + GET_ANGLE_MODIFY = 6, + CREATE_ROTATION_MATRIX = 7, + TRANSFORM_COORDINATE_SYSTEM = 8, + CREATE_QUATERNION = 9, + GET_DIRECTION = 10, + ROTATION_INCLINATION_MATRIX = 11, + GET_SENSOR_LIST = 12, + GET_SINGLE_SENSOR = 13, + SUBSCRIBE_CALLBACK = 14, + SUBSCRIBE_COMPASS = 15, + GET_BODY_STATE = 16, +}; + +struct GeomagneticData { + float x; + float y; + float z; + float geomagneticDip; + float deflectionAngle; + float levelIntensity; + float totalIntensity; +}; + +struct RationMatrixData { + float rotationMatrix[THREE_DIMENSIONAL_MATRIX_LENGTH]; + float inclinationMatrix[THREE_DIMENSIONAL_MATRIX_LENGTH]; +}; + +struct CallbackSensorData { + int32_t sensorTypeId; + uint32_t dataLength; + float data[DATA_LENGTH]; + int64_t timestamp; + int32_t sensorAccuracy; +}; + +struct ReserveData { + float reserve[DATA_LENGTH]; + int32_t length; +}; + +union CallbackData { + CallbackSensorData sensorData; + GeomagneticData geomagneticData; + RationMatrixData rationMatrixData; + ReserveData reserveData; +}; + +struct BusinessError { + int32_t code { 0 }; + string message; + string name; + string stack; +}; + +class AsyncCallbackInfo : public RefBase { +public: + ani_vm *vm = nullptr; + ani_env *env = nullptr; + ani_ref callback[CALLBACK_NUM] = { 0 }; + CallbackData data; + BusinessError error; + CallbackDataType type; + vector sensorInfos; + AsyncCallbackInfo(ani_vm *vm, ani_env *env, CallbackDataType type) : vm(vm), env(env), type(type) {} + ~AsyncCallbackInfo() + { + CALL_LOG_ENTER; + if (type != ONCE_CALLBACK) { + for (int32_t i = 0; i < CALLBACK_NUM; ++i) { + if (callback[i] != nullptr) { + SEN_HILOGD("Delete reference, i:%{public}d", i); + env->GlobalReference_Delete(callback[i]); + callback[i] = nullptr; + env = nullptr; + vm = nullptr; + } + } + } + } + +private: +}; +} // namespace Sensors +} // namespace OHOS +#endif // SENSOR_ANI_H \ No newline at end of file diff --git a/frameworks/js/ani/src/sensor_ani.cpp b/frameworks/js/ani/src/sensor_ani.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6871e423dcc2346b8e40d4058d3a7d0a90a8ebb5 --- /dev/null +++ b/frameworks/js/ani/src/sensor_ani.cpp @@ -0,0 +1,708 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "sensor_ani.h" +#include "securec.h" +#include "ani_utils.h" +#include "sensor_agent.h" +#include "event_handler.h" + +#undef LOG_TAG +#define LOG_TAG "SensorAniAPI" + +using namespace OHOS; +using namespace OHOS::Sensors; + +constexpr int32_t REPORTING_INTERVAL = 200000000; +constexpr int32_t INVALID_SENSOR_ID = -1; +constexpr float BODY_STATE_EXCEPT = 1.0f; +constexpr float THRESHOLD = 0.000001f; +constexpr int32_t ANI_SCOPE_SIZE = 16; + +static std::unordered_map g_sensorTypeToClassName = { + {256, "LOrientationResponseImpl;"}, +}; + +std::map> g_sensorAttributeList = { + { 0, { "x" } }, + { SENSOR_TYPE_ID_ACCELEROMETER, { "x", "y", "z" } }, + { SENSOR_TYPE_ID_GYROSCOPE, { "x", "y", "z" } }, + { SENSOR_TYPE_ID_AMBIENT_LIGHT, { "intensity", "colorTemperature", "infraredLuminance" } }, + { SENSOR_TYPE_ID_MAGNETIC_FIELD, { "x", "y", "z" } }, + { SENSOR_TYPE_ID_BAROMETER, { "pressure" } }, + { SENSOR_TYPE_ID_HALL, { "status" } }, + { SENSOR_TYPE_ID_TEMPERATURE, { "temperature" } }, + { SENSOR_TYPE_ID_PROXIMITY, { "distance" } }, + { SENSOR_TYPE_ID_HUMIDITY, { "humidity" } }, + { SENSOR_TYPE_ID_ORIENTATION, { "alpha", "beta", "gamma" } }, + { SENSOR_TYPE_ID_GRAVITY, { "x", "y", "z" } }, + { SENSOR_TYPE_ID_LINEAR_ACCELERATION, { "x", "y", "z" } }, + { SENSOR_TYPE_ID_ROTATION_VECTOR, { "x", "y", "z", "w" } }, + { SENSOR_TYPE_ID_AMBIENT_TEMPERATURE, { "temperature" } }, + { SENSOR_TYPE_ID_MAGNETIC_FIELD_UNCALIBRATED, { "x", "y", "z", "biasX", "biasY", "biasZ" } }, + { SENSOR_TYPE_ID_GYROSCOPE_UNCALIBRATED, { "x", "y", "z", "biasX", "biasY", "biasZ" } }, + { SENSOR_TYPE_ID_SIGNIFICANT_MOTION, { "scalar" } }, + { SENSOR_TYPE_ID_PEDOMETER_DETECTION, { "scalar" } }, + { SENSOR_TYPE_ID_PEDOMETER, { "steps" } }, + { SENSOR_TYPE_ID_HEART_RATE, { "heartRate" } }, + { SENSOR_TYPE_ID_WEAR_DETECTION, { "value" } }, + { SENSOR_TYPE_ID_ACCELEROMETER_UNCALIBRATED, { "x", "y", "z", "biasX", "biasY", "biasZ" } }, + { SENSOR_TYPE_ID_COLOR, { "lightIntensity", "colorTemperature" } }, + { SENSOR_TYPE_ID_SAR, { "absorptionRatio" } } +}; + +static std::unordered_map stringToNumberMap = { + {"ORIENTATION", 256}, +}; + +static std::map g_samplingPeriod = { + {"normal", 200000000}, + {"ui", 60000000}, + {"game", 20000000}, +}; + +using ConvertDataFunc = bool(*)(sptr asyncCallbackInfo, std::vector &data); +static bool ConvertToSensorData(sptr asyncCallbackInfo, std::vector &data); + +static std::map g_convertfuncList = { + {ON_CALLBACK, ConvertToSensorData}, +}; + +static std::mutex mutex_; +static std::mutex bodyMutex_; +static float g_bodyState = -1.0f; +static std::map>> g_subscribeCallbacks; +static std::mutex onMutex_; +static std::map>> g_onCallbackInfos; +static thread_local std::shared_ptr mainHandler = nullptr; + +static void ThrowBusinessError(ani_env *env, int errCode, std::string&& errMsg) +{ + SEN_HILOGD("Begin ThrowBusinessError."); + static const char *errorClsName = "L@ohos/base/BusinessError;"; + ani_class cls {}; + if (ANI_OK != env->FindClass(errorClsName, &cls)) { + SEN_HILOGE("find class BusinessError %{public}s failed", errorClsName); + return; + } + ani_method ctor; + if (ANI_OK != env->Class_FindMethod(cls, "", ":V", &ctor)) { + SEN_HILOGE("find method BusinessError.constructor failed"); + return; + } + ani_object errorObject; + if (ANI_OK != env->Object_New(cls, ctor, &errorObject)) { + SEN_HILOGE("create BusinessError object failed"); + return; + } + ani_double aniErrCode = static_cast(errCode); + ani_string errMsgStr; + if (ANI_OK != env->String_NewUTF8(errMsg.c_str(), errMsg.size(), &errMsgStr)) { + SEN_HILOGE("convert errMsg to ani_string failed"); + return; + } + if (ANI_OK != env->Object_SetFieldByName_Double(errorObject, "code", aniErrCode)) { + SEN_HILOGE("set error code failed"); + return; + } + if (ANI_OK != env->Object_SetPropertyByName_Ref(errorObject, "message", errMsgStr)) { + SEN_HILOGE("set error message failed"); + return; + } + env->ThrowError(static_cast(errorObject)); + return; +} + +static bool CheckSubscribe(int32_t sensorTypeId) +{ + std::lock_guard onCallbackLock(onMutex_); + auto iter = g_onCallbackInfos.find(sensorTypeId); + return iter != g_onCallbackInfos.end(); +} + +static bool CopySensorData(sptr callbackInfo, SensorEvent *event) +{ + CHKPF(callbackInfo); + CHKPF(event); + int32_t sensorTypeId = event->sensorTypeId; + callbackInfo->data.sensorData.sensorTypeId = sensorTypeId; + callbackInfo->data.sensorData.dataLength = event->dataLen; + callbackInfo->data.sensorData.timestamp = event->timestamp; + callbackInfo->data.sensorData.sensorAccuracy = event->option; + CHKPF(event->data); + if (event->dataLen < sizeof(float)) { + SEN_HILOGE("Event dataLen less than float size"); + return false; + } + auto data = reinterpret_cast(event->data); + if (sensorTypeId == SENSOR_TYPE_ID_WEAR_DETECTION && callbackInfo->type == SUBSCRIBE_CALLBACK) { + std::lock_guard onBodyLock(bodyMutex_); + g_bodyState = *data; + callbackInfo->data.sensorData.data[0] = + (fabs(g_bodyState - BODY_STATE_EXCEPT) < THRESHOLD) ? true : false; + return true; + } + if (memcpy_s(callbackInfo->data.sensorData.data, sizeof(callbackInfo->data.sensorData.data), + data, event->dataLen) != EOK) { + SEN_HILOGE("Copy data failed"); + return false; + } + return true; +} + +static bool CheckSystemSubscribe(int32_t sensorTypeId) +{ + std::lock_guard subscribeLock(mutex_); + auto iter = g_subscribeCallbacks.find(sensorTypeId); + if (iter == g_subscribeCallbacks.end()) { + return false; + } + return true; +} + +static ani_boolean IsInstanceOf(ani_env *env, const std::string &cls_name, ani_object obj) +{ + ani_class cls; + if (ANI_OK != env->FindClass(cls_name.c_str(), &cls)) { + SEN_HILOGE("FindClass failed"); + return ANI_FALSE; + } + + ani_boolean ret; + env->Object_InstanceOf(obj, cls, &ret); + return ret; +} + +static bool SendEventToMainThread(const std::function func) +{ + if (func == nullptr) { + SEN_HILOGE("func is nullptr!"); + return false; + } + + if (!mainHandler) { + auto runner = OHOS::AppExecFwk::EventRunner::GetMainEventRunner(); + if (!runner) { + SEN_HILOGE("get main event runner failed!"); + return false; + } + mainHandler = std::make_shared(runner); + } + mainHandler->PostTask(func, "", 0, OHOS::AppExecFwk::EventQueue::Priority::HIGH, {}); + return true; +} + +static bool ValidateAndInitialize(sptr asyncCallbackInfo, ani_object &obj) +{ + CHKPF(asyncCallbackInfo); + int32_t sensorTypeId = asyncCallbackInfo->data.sensorData.sensorTypeId; + if (g_sensorAttributeList.find(sensorTypeId) == g_sensorAttributeList.end()) { + SEN_HILOGE("Invalid sensor type"); + return false; + } + if (sensorTypeId == SENSOR_TYPE_ID_WEAR_DETECTION && asyncCallbackInfo->type == SUBSCRIBE_CALLBACK) { + return false; + } + size_t size = g_sensorAttributeList[sensorTypeId].size(); + uint32_t dataLength = asyncCallbackInfo->data.sensorData.dataLength / sizeof(float); + if (size > dataLength) { + SEN_HILOGE("Data length mismatch"); + return false; + } + + if (g_sensorTypeToClassName.find(sensorTypeId) == g_sensorTypeToClassName.end()) { + SEN_HILOGE("Find class by sensorType failed"); + return false; + } + + ani_namespace ns; + static const char *namespaceName = "L@ohos/sensor/sensor;"; + if (ANI_OK != asyncCallbackInfo->env->FindNamespace(namespaceName, &ns)) { + SEN_HILOGE("Not found '%{public}s'", namespaceName); + return false; + } + + ani_class cls; + const char *className = g_sensorTypeToClassName[sensorTypeId].c_str(); + if (ANI_OK != asyncCallbackInfo->env->Namespace_FindClass(ns, className, &cls)) { + SEN_HILOGE("FindClass %{public}s failed", className); + return false; + } + + ani_method ctor; + if (ANI_OK != asyncCallbackInfo->env->Class_FindMethod(cls, "", nullptr, &ctor)) { + SEN_HILOGE("Class_FindMethod 'constructor' failed"); + return false; + } + + if (ANI_OK != asyncCallbackInfo->env->Object_New(cls, ctor, &obj)) { + SEN_HILOGE("Object_New '%{public}s' failed", className); + return false; + } + return true; +} + +static ani_enum_item GetEnumItem(ani_env *env, int32_t accuracy) +{ + ani_namespace ns; + static const char *namespaceName = "L@ohos/sensor/sensor;"; + if (ANI_OK != env->FindNamespace(namespaceName, &ns)) { + SEN_HILOGE("Not found '%{public}s'", namespaceName); + return nullptr; + } + + ani_enum aniEnum{}; + const char *enumName = "LSensorAccuracy;"; + if (ANI_OK != env->Namespace_FindEnum(ns, enumName, &aniEnum)) { + SEN_HILOGE("Not found '%{public}s'", enumName); + return nullptr; + } + + constexpr int32_t loopMaxNum = 1000; + for (int32_t index = 0U; index < loopMaxNum; index++) { + ani_enum_item enumItem{}; + if (ANI_OK != env->Enum_GetEnumItemByIndex(aniEnum, index, &enumItem)) { + SEN_HILOGE("Enum_GetEnumItemByIndex failed"); + return nullptr; + } + ani_int intValue = -1; + if (ANI_OK != env->EnumItem_GetValue_Int(enumItem, &intValue)) { + SEN_HILOGE("EnumItem_GetValue_Int FAILD."); + return nullptr; + } + if (intValue == accuracy) { + return enumItem; + } + } + SEN_HILOGE("Get enumItem by %{public}d failed.", accuracy); + return nullptr; +} + +static bool SetSensorPropertiesAndPushData(sptr asyncCallbackInfo, ani_object obj, + std::vector &data) +{ + CALL_LOG_ENTER; + int32_t sensorTypeId = asyncCallbackInfo->data.sensorData.sensorTypeId; + size_t size = g_sensorAttributeList[sensorTypeId].size(); + auto sensorAttributes = g_sensorAttributeList[sensorTypeId]; + for (uint32_t i = 0; i < size; ++i) { + if (ANI_OK != asyncCallbackInfo->env->Object_SetPropertyByName_Double(obj, sensorAttributes[i].c_str(), + asyncCallbackInfo->data.sensorData.data[i])) { + SEN_HILOGE("Object_SetPropertyByName_Double failed"); + return false; + } + } + + if (ANI_OK != asyncCallbackInfo->env->Object_SetPropertyByName_Double(obj, "timestamp", + asyncCallbackInfo->data.sensorData.timestamp)) { + SEN_HILOGE("Object_SetPropertyByName_Double timestamp failed"); + return false; + } + + ani_enum_item accuracy = GetEnumItem(asyncCallbackInfo->env, asyncCallbackInfo->data.sensorData.sensorAccuracy); + if (accuracy == nullptr) { + SEN_HILOGE("GetEnumItem failed"); + return false; + } + if (ANI_OK != asyncCallbackInfo->env->Object_SetPropertyByName_Ref(obj, "accuracy", accuracy)) { + SEN_HILOGE("Object_SetPropertyByName_Ref accuracy failed"); + return false; + } + + data.push_back(obj); + return true; +} + +static bool ConvertToSensorData(sptr asyncCallbackInfo, std::vector &data) +{ + ani_object obj; + if (!ValidateAndInitialize(asyncCallbackInfo, obj)) { + return false; + } + + if (!SetSensorPropertiesAndPushData(asyncCallbackInfo, obj, data)) { + return false; + } + + return true; +} + +static void EmitUvEventLoop(sptr asyncCallbackInfo) +{ + CHKPV(asyncCallbackInfo); + auto task = [asyncCallbackInfo]() { + SEN_HILOGD("Begin to call task"); + ani_env *env = nullptr; + ani_options aniArgs {0, nullptr}; + if (ANI_ERROR == asyncCallbackInfo->vm->AttachCurrentThread(&aniArgs, ANI_VERSION_1, &env)) { + if (ANI_OK != asyncCallbackInfo->vm->GetEnv(ANI_VERSION_1, &env)) { + SEN_HILOGE("GetEnv failed"); + return; + } + } + asyncCallbackInfo->env = env; + + AniLocalScopeGuard aniLocalScopeGuard(asyncCallbackInfo->env, ANI_SCOPE_SIZE); + if (!aniLocalScopeGuard.IsStatusOK()) { + SEN_HILOGE("CreateLocalScope failed"); + return; + } + + if (!(g_convertfuncList.find(asyncCallbackInfo->type) != g_convertfuncList.end())) { + SEN_HILOGE("asyncCallbackInfo type is invalid"); + ThrowBusinessError(asyncCallbackInfo->env, EINVAL, "asyncCallbackInfo type is invalid"); + return; + } + std::vector args; + g_convertfuncList[asyncCallbackInfo->type](asyncCallbackInfo, args); + + auto fnObj = reinterpret_cast(asyncCallbackInfo->callback[0]); + SEN_HILOGD("Begin to call FunctionalObject_Call"); + if (fnObj == nullptr) { + SEN_HILOGE("fnObj == nullptr"); + ThrowBusinessError(asyncCallbackInfo->env, EINVAL, "fnObj == nullptr"); + return; + } + if (IsInstanceOf(asyncCallbackInfo->env, "Lstd/core/Function1;", fnObj) == 0) { + SEN_HILOGE("fnObj is not instance Of function"); + ThrowBusinessError(asyncCallbackInfo->env, EINVAL, "fnObj is not instance Of function"); + return; + } + + ani_ref result; + if (ANI_OK != asyncCallbackInfo->env->FunctionalObject_Call(fnObj, 1, args.data(), &result)) { + SEN_HILOGE("FunctionalObject_Call failed"); + ThrowBusinessError(asyncCallbackInfo->env, EINVAL, "FunctionalObject_Call failed"); + return; + } + SEN_HILOGD("FunctionalObject_Call success"); + }; + if (!SendEventToMainThread(task)) { + SEN_HILOGE("failed to send event"); + } +} +static void EmitOnCallback(SensorEvent *event) +{ + CHKPV(event); + int32_t sensorTypeId = event->sensorTypeId; + if (!CheckSubscribe(sensorTypeId)) { + return; + } + std::lock_guard onCallbackLock(onMutex_); + auto onCallbackInfos = g_onCallbackInfos[sensorTypeId]; + for (auto &onCallbackInfo : onCallbackInfos) { + if (!CopySensorData(onCallbackInfo, event)) { + SEN_HILOGE("Copy sensor data failed"); + continue; + } + EmitUvEventLoop(onCallbackInfo); + } +} + +static void DataCallbackImpl(SensorEvent *event) +{ + CHKPV(event); + EmitOnCallback(event); +} + +static const SensorUser user = { + .callback = DataCallbackImpl +}; + +static int32_t SubscribeSensor(int32_t sensorTypeId, int64_t interval, RecordSensorCallback callback) +{ + CALL_LOG_ENTER; + int32_t ret = SubscribeSensor(sensorTypeId, &user); + if (ret != ERR_OK) { + SEN_HILOGE("SubscribeSensor failed"); + return ret; + } + ret = SetBatch(sensorTypeId, &user, interval, 0); + if (ret != ERR_OK) { + SEN_HILOGE("SetBatch failed"); + return ret; + } + return ActivateSensor(sensorTypeId, &user); +} + +static bool IsSubscribed(ani_env *env, int32_t sensorTypeId, ani_object callback) +{ + CALL_LOG_ENTER; + if (auto iter = g_onCallbackInfos.find(sensorTypeId); iter == g_onCallbackInfos.end()) { + SEN_HILOGW("No client subscribe, sensorTypeId:%{public}d", sensorTypeId); + return false; + } + std::vector> callbackInfos = g_onCallbackInfos[sensorTypeId]; + for (auto callbackInfo : callbackInfos) { + CHKPC(callbackInfo); + if (callbackInfo->env != env) { + continue; + } + + ani_boolean isEquals = false; + if (ANI_OK != env->Reference_StrictEquals(callback, callbackInfo->callback[0], &isEquals)) { + SEN_HILOGE("StrictEquals failed"); + return false; + } + if (isEquals) { + return true; + } + } + return false; +} + +static void UpdateCallbackInfos(ani_env *env, int32_t sensorTypeId, ani_object callback) +{ + CALL_LOG_ENTER; + std::lock_guard onCallbackLock(onMutex_); + CHKCV((!IsSubscribed(env, sensorTypeId, callback)), "The callback has been subscribed"); + + ani_vm *vm = nullptr; + if (ANI_OK != env->GetVM(&vm)) { + SEN_HILOGE("GetVM failed."); + return; + } + sptr asyncCallbackInfo = new (std::nothrow) AsyncCallbackInfo(vm, env, ON_CALLBACK); + CHKPV(asyncCallbackInfo); + + if (ANI_OK != env->GlobalReference_Create(callback, &asyncCallbackInfo->callback[0])) { + SEN_HILOGE("GlobalReference_Create failed"); + return; + } + + std::vector> callbackInfos = g_onCallbackInfos[sensorTypeId]; + callbackInfos.push_back(asyncCallbackInfo); + g_onCallbackInfos[sensorTypeId] = callbackInfos; +} + +static bool GetIntervalValue(ani_env *env, ani_object options, int64_t& interval) +{ + ani_boolean isUndefined; + env->Reference_IsUndefined(options, &isUndefined); + if (isUndefined) { + return true; + } + + ani_ref intervalRef; + ani_boolean isIntervalUndefined; + if (ANI_OK != env->Object_GetPropertyByName_Ref(options, "interval", &intervalRef)) { + SEN_HILOGE("Failed to get property named interval"); + return false; + } + + env->Reference_IsUndefined(intervalRef, &isIntervalUndefined); + if (isIntervalUndefined) { + SEN_HILOGE("interval is undefined"); + return false; + } + + ani_class stringClass; + env->FindClass("Lstd/core/String;", &stringClass); + ani_class doubleClass; + env->FindClass("Lstd/core/Double;", &doubleClass); + + ani_boolean isDouble; + env->Object_InstanceOf(static_cast(intervalRef), doubleClass, &isDouble); + ani_boolean isString; + env->Object_InstanceOf(static_cast(intervalRef), stringClass, &isString); + if (isDouble) { + ani_double doubleValue; + auto ret = env->Object_CallMethodByName_Double(static_cast(intervalRef), "unboxed", + nullptr, &doubleValue); + if (ret != ANI_OK) { + SEN_HILOGE("Failed to get property named doubleValue"); + return false; + } + interval = static_cast(doubleValue); + return true; + } + + if (isString) { + auto mode = AniStringUtils::ToStd(env, static_cast(intervalRef)); + auto iter = g_samplingPeriod.find(mode); + if (iter == g_samplingPeriod.end()) { + SEN_HILOGE("Find interval mode failed"); + return false; + } + interval = iter->second; + SEN_HILOGI("GetIntervalValue mode: %{public}s", mode.c_str()); + return true; + } + + SEN_HILOGE("Invalid interval type"); + return false; +} + +static void On([[maybe_unused]] ani_env *env, ani_string typeId, ani_object callback, ani_object options) +{ + CALL_LOG_ENTER; + if (!IsInstanceOf(env, "Lstd/core/Function1;", callback)) { + SEN_HILOGE("Wrong argument type"); + return; + } + + int32_t sensorTypeId = INVALID_SENSOR_ID; + auto typeIdStr = AniStringUtils::ToStd(env, static_cast(typeId)); + if (stringToNumberMap.find(typeIdStr) == stringToNumberMap.end()) { + SEN_HILOGE("Invalid sensor typeId: %{public}s", typeIdStr.c_str()); + return; + } + sensorTypeId = stringToNumberMap[typeIdStr]; + + int64_t interval = REPORTING_INTERVAL; + if (!GetIntervalValue(env, options, interval)) { + SEN_HILOGW("Get interval failed"); + } + int32_t ret = SubscribeSensor(sensorTypeId, interval, DataCallbackImpl); + if (ret != ERR_OK) { + ThrowBusinessError(env, ret, "SubscribeSensor fail"); + return; + } + UpdateCallbackInfos(env, sensorTypeId, callback); +} + +static int32_t RemoveAllCallback(ani_env *env, int32_t sensorTypeId) +{ + CALL_LOG_ENTER; + std::lock_guard onCallbackLock(onMutex_); + std::vector> callbackInfos = g_onCallbackInfos[sensorTypeId]; + for (auto iter = callbackInfos.begin(); iter != callbackInfos.end();) { + CHKPC(*iter); + if ((*iter)->env != env) { + ++iter; + continue; + } + iter = callbackInfos.erase(iter); + } + if (callbackInfos.empty()) { + SEN_HILOGD("No subscription to change sensor data"); + g_onCallbackInfos.erase(sensorTypeId); + return 0; + } + g_onCallbackInfos[sensorTypeId] = callbackInfos; + return callbackInfos.size(); +} + +static int32_t RemoveCallback(ani_env *env, int32_t sensorTypeId, ani_object callback) +{ + CALL_LOG_ENTER; + std::lock_guard onCallbackLock(onMutex_); + std::vector> callbackInfos = g_onCallbackInfos[sensorTypeId]; + for (auto iter = callbackInfos.begin(); iter != callbackInfos.end();) { + CHKPC(*iter); + if ((*iter)->env != env) { + continue; + } + + ani_boolean isEquals = false; + if (ANI_OK != env->Reference_StrictEquals(callback, (*iter)->callback[0], &isEquals)) { + SEN_HILOGE("Reference_StrictEquals failed"); + return false; + } + if (isEquals) { + iter = callbackInfos.erase(iter); + SEN_HILOGD("Remove callback success"); + break; + } else { + ++iter; + } + } + if (callbackInfos.empty()) { + SEN_HILOGD("No subscription to change sensor data"); + g_onCallbackInfos.erase(sensorTypeId); + return 0; + } + g_onCallbackInfos[sensorTypeId] = callbackInfos; + return callbackInfos.size(); +} + +static int32_t UnsubscribeSensor(int32_t sensorTypeId) +{ + int32_t ret = DeactivateSensor(sensorTypeId, &user); + if (ret != ERR_OK) { + SEN_HILOGE("DeactivateSensor failed"); + return ret; + } + return UnsubscribeSensor(sensorTypeId, &user); +} + +static void Off([[maybe_unused]] ani_env *env, ani_string type, ani_object callback) +{ + CALL_LOG_ENTER; + int32_t sensorTypeId = INVALID_SENSOR_ID; + auto typeStr = AniStringUtils::ToStd(env, static_cast(type)); + if (stringToNumberMap.find(typeStr) == stringToNumberMap.end()) { + SEN_HILOGE("Invalid sensor type: %{public}s", typeStr.c_str()); + ThrowBusinessError(env, PARAMETER_ERROR, "Invalid sensor type"); + return; + } + sensorTypeId = stringToNumberMap[typeStr]; + + int32_t subscribeSize = -1; + ani_boolean isUndefined; + env->Reference_IsUndefined(callback, &isUndefined); + if (isUndefined) { + subscribeSize = RemoveAllCallback(env, sensorTypeId); + } else { + ani_boolean result; + if (env->Reference_IsNull(callback, &result) == ANI_OK && result) { + subscribeSize = RemoveAllCallback(env, sensorTypeId); + } else if (IsInstanceOf(env, "Lstd/core/Function1;", callback)) { + subscribeSize = RemoveCallback(env, sensorTypeId, callback); + } else { + ThrowBusinessError(env, PARAMETER_ERROR, "Invalid callback"); + return; + } + } + + if (CheckSystemSubscribe(sensorTypeId) || (subscribeSize > 0)) { + SEN_HILOGW("There are other client subscribe system js api as well, not need unsubscribe"); + return; + } + int32_t ret = UnsubscribeSensor(sensorTypeId); + if (ret == PARAMETER_ERROR || ret == PERMISSION_DENIED) { + ThrowBusinessError(env, ret, "UnsubscribeSensor fail"); + } + return; +} + +ANI_EXPORT ani_status ANI_Constructor(ani_vm *vm, uint32_t *result) +{ + ani_env *env; + if (ANI_OK != vm->GetEnv(ANI_VERSION_1, &env)) { + SEN_HILOGE("Unsupported ANI_VERSION_1"); + return ANI_ERROR; + } + + ani_namespace ns; + static const char *spaceName = "L@ohos/sensor/sensor;"; + if (ANI_OK != env->FindNamespace(spaceName, &ns)) { + SEN_HILOGE("Not found space name"); + return ANI_ERROR; + } + + std::array methods = { + ani_native_function {"on", nullptr, reinterpret_cast(On)}, + ani_native_function {"off", nullptr, reinterpret_cast(Off)}, + }; + + if (ANI_OK != env->Namespace_BindNativeFunctions(ns, methods.data(), methods.size())) { + SEN_HILOGE("Cannot bind native methods to %{public}s", spaceName); + return ANI_ERROR; + } + + *result = ANI_VERSION_1; + return ANI_OK; +} \ No newline at end of file