* Copyright (C) 2026 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 "fusion_fence_napi.h"
#include "napi/native_api.h"
#include "napi/native_node_api.h"
#include "location_log.h"
#include "iremote_object.h"
#include "geofence_definition.h"
#include "fusion_fence_request.h"
#include "napi_util.h"
#include "common_utils.h"
#include <regex>
namespace OHOS {
namespace Location {
static constexpr int MAX_BUF_LEN = 512;
bool ParsePoint(napi_env env, napi_value object, FusionFencePoint& point)
{
if (env == nullptr) {
LBSLOGE(FUSION_FENCE, "env is null");
return false;
}
if (object == nullptr) {
LBSLOGE(FUSION_FENCE, "object is null");
return false;
}
if (JsObjectToDouble(env, object, "latitude", point.latitude) != SUCCESS) {
LBSLOGE(FUSION_FENCE, "latitude is required");
return false;
}
if (JsObjectToDouble(env, object, "longitude", point.longitude) != SUCCESS) {
LBSLOGE(FUSION_FENCE, "longitude is required");
return false;
}
if (point.latitude < MIN_LATITUDE || point.latitude > MAX_LATITUDE) {
LBSLOGE(FUSION_FENCE, "invalid latitude");
return false;
}
if (point.longitude < MIN_LONGITUDE || point.longitude > MAX_LONGITUDE) {
LBSLOGE(FUSION_FENCE, "invalid longitude");
return false;
}
return true;
}
bool ParseCellInfo(napi_env env, napi_value object, FusionFenceCellInfo& cellInfo)
{
if (env == nullptr || object == nullptr) {
LBSLOGE(FUSION_FENCE, "ParseCellInfo: env or object is null");
return false;
}
if (JsObjectToInt64(env, object, "timeSinceBoot", cellInfo.timeSinceBoot) != SUCCESS) {
LBSLOGE(FUSION_FENCE, "timeSinceBoot is required");
return false;
}
if (JsObjectToInt64(env, object, "cellId", cellInfo.cellId) != SUCCESS) {
LBSLOGE(FUSION_FENCE, "cellId is required");
return false;
}
if (JsObjectToInt(env, object, "lac", cellInfo.lac) != SUCCESS) {
LBSLOGE(FUSION_FENCE, "lac is required");
return false;
}
if (JsObjectToInt(env, object, "mcc", cellInfo.mcc) != SUCCESS) {
LBSLOGE(FUSION_FENCE, "mcc is required");
return false;
}
if (JsObjectToInt(env, object, "mnc", cellInfo.mnc) != SUCCESS) {
LBSLOGE(FUSION_FENCE, "mnc is required");
return false;
}
if (JsObjectToInt(env, object, "rat", cellInfo.rat) != SUCCESS) {
LBSLOGE(FUSION_FENCE, "rat is required");
return false;
}
if (JsObjectToInt(env, object, "signalIntensity", cellInfo.signalIntensity) != SUCCESS) {
LBSLOGE(FUSION_FENCE, "signalIntensity is required");
return false;
}
if (JsObjectToInt(env, object, "arfcn", cellInfo.arfcn) != SUCCESS) {
LBSLOGE(FUSION_FENCE, "arfcn is required");
return false;
}
if (JsObjectToInt(env, object, "pci", cellInfo.pci) != SUCCESS) {
LBSLOGE(FUSION_FENCE, "pci is required");
return false;
}
JsObjectToInt(env, object, "tac", cellInfo.tac);
return true;
}
static bool ValidatePolygonArrayLength(napi_env env, napi_value polygonArray, uint32_t& arrayLen)
{
napi_valuetype valueType = napi_undefined;
if (napi_typeof(env, polygonArray, &valueType) != napi_ok || valueType != napi_object) {
LBSLOGE(FUSION_FENCE, "polygonArray is not an object");
return false;
}
bool isArray = false;
if (napi_is_array(env, polygonArray, &isArray) != napi_ok || !isArray) {
LBSLOGE(FUSION_FENCE, "polygonArray is not an array");
return false;
}
if (napi_get_array_length(env, polygonArray, &arrayLen) != napi_ok) {
LBSLOGE(FUSION_FENCE, "get array length failed");
return false;
}
return true;
}
static bool ParsePolygonPoints(napi_env env, napi_value polygonArray, std::vector<FusionFencePoint>& polygon)
{
if (env == nullptr || polygonArray == nullptr) {
LBSLOGE(FUSION_FENCE, "ParsePolygonPoints: env or polygonArray is null");
return false;
}
uint32_t arrayLen = 0;
if (!ValidatePolygonArrayLength(env, polygonArray, arrayLen)) {
return false;
}
constexpr uint32_t maxPolygonPointsCount = 10;
constexpr uint32_t minPolygonPointsCount = 3;
if (arrayLen > maxPolygonPointsCount) {
LBSLOGI(FUSION_FENCE, "polygon array length=%{public}u exceeds max=%{public}u, truncating to %{public}u",
arrayLen, maxPolygonPointsCount, maxPolygonPointsCount);
arrayLen = maxPolygonPointsCount;
}
if (arrayLen < minPolygonPointsCount) {
LBSLOGE(FUSION_FENCE, "polygon array must have at least %{public}u points, got: %{public}u",
minPolygonPointsCount, arrayLen);
return false;
}
for (uint32_t i = 0; i < arrayLen; i++) {
napi_value pointValue = nullptr;
if (napi_get_element(env, polygonArray, i, &pointValue) != napi_ok) {
LBSLOGE(FUSION_FENCE, "failed to get element at index %{public}u", i);
continue;
}
FusionFencePoint point;
if (ParsePoint(env, pointValue, point)) {
auto isDuplicate = std::find_if(polygon.begin(), polygon.end(),
[&point](const FusionFencePoint& p) {
return p.latitude == point.latitude && p.longitude == point.longitude;
});
if (isDuplicate == polygon.end()) {
polygon.push_back(point);
}
}
}
if (polygon.size() < minPolygonPointsCount) {
LBSLOGE(FUSION_FENCE, "polygon must have at least %{public}u valid points, got: %{public}zu",
minPolygonPointsCount, polygon.size());
return false;
}
return true;
}
static bool ParseCircularFence(napi_env env, napi_value object, std::shared_ptr<Geofence>& circularFence)
{
if (env == nullptr) {
LBSLOGE(FUSION_FENCE, "env is null");
return false;
}
if (object == nullptr) {
LBSLOGE(FUSION_FENCE, "object is null");
return false;
}
napi_value circularFenceValue = nullptr;
if (napi_get_named_property(env, object, "circularFence", &circularFenceValue) != napi_ok) {
LBSLOGE(FUSION_FENCE, "get circularFence property failed");
return false;
}
auto fence = std::make_shared<Geofence>();
double latitude = 0.0;
double longitude = 0.0;
double radius = 0.0;
double expiration = 0.0;
if (JsObjectToDouble(env, circularFenceValue, "latitude", latitude) != SUCCESS) {
LBSLOGE(FUSION_FENCE, "latitude is required");
return false;
}
if (JsObjectToDouble(env, circularFenceValue, "longitude", longitude) != SUCCESS) {
LBSLOGE(FUSION_FENCE, "longitude is required");
return false;
}
if (JsObjectToDouble(env, circularFenceValue, "radius", radius) != SUCCESS) {
LBSLOGE(FUSION_FENCE, "radius is required");
return false;
}
if (JsObjectToDouble(env, circularFenceValue, "expiration", expiration) != SUCCESS) {
LBSLOGE(FUSION_FENCE, "expiration is required");
return false;
}
if (expiration <= 0) {
LBSLOGE(FUSION_FENCE, "expiration must be greater than 0, got: %{public}f", expiration);
return false;
}
fence->SetLatitude(latitude);
fence->SetLongitude(longitude);
fence->SetRadius(radius);
fence->SetExpiration(expiration);
circularFence = fence;
return true;
}
bool ParseGnssFence(napi_env env, napi_value object, std::shared_ptr<FusionFenceGnss>& gnssFence)
{
if (env == nullptr) {
LBSLOGE(FUSION_FENCE, "ParseGnssFence: env is null");
return false;
}
if (object == nullptr) {
LBSLOGE(FUSION_FENCE, "ParseGnssFence: object is null");
return false;
}
auto fence = std::make_shared<FusionFenceGnss>();
int32_t fenceType = 0;
if (JsObjectToInt(env, object, "gnssFenceType", fenceType) != SUCCESS) {
LBSLOGE(FUSION_FENCE, "ParseGnssFence: gnssFenceType is required");
return false;
}
if (fenceType != static_cast<int32_t>(GnssFenceType::GNSS_FENCE_POLYGON) &&
fenceType != static_cast<int32_t>(GnssFenceType::GNSS_FENCE_CIRCULAR)) {
LBSLOGE(FUSION_FENCE, "ParseGnssFence: invalid gnssFenceType: %{public}d", fenceType);
return false;
}
fence->gnssFenceType = static_cast<GnssFenceType>(fenceType);
if (fence->gnssFenceType == GnssFenceType::GNSS_FENCE_CIRCULAR) {
napi_value circularFenceValue = nullptr;
if (napi_get_named_property(env, object, "circularFence", &circularFenceValue) != napi_ok) {
LBSLOGE(FUSION_FENCE, "ParseGnssFence: circularFence is required for GNSS_FENCE_CIRCULAR");
return false;
}
if (!ParseCircularFence(env, object, fence->circularFence)) {
LBSLOGE(FUSION_FENCE, "ParseGnssFence: ParseCircularFence failed");
return false;
}
} else if (fence->gnssFenceType == GnssFenceType::GNSS_FENCE_POLYGON) {
napi_value polygonArray = nullptr;
if (napi_get_named_property(env, object, "polygon", &polygonArray) != napi_ok) {
LBSLOGE(FUSION_FENCE, "ParseGnssFence: polygon is required for GNSS_FENCE_POLYGON");
return false;
}
if (!ParsePolygonPoints(env, polygonArray, fence->polygon)) {
LBSLOGE(FUSION_FENCE, "ParseGnssFence: ParsePolygonPoints failed");
return false;
}
}
gnssFence = fence;
return true;
}
static bool ParseWifiMacAddresses(napi_env env, napi_value featureValue, WirelessSignalFeature& feature)
{
if (env == nullptr) {
LBSLOGE(FUSION_FENCE, "env is null");
return false;
}
napi_value macArray = nullptr;
if (napi_get_named_property(env, featureValue, "mac", &macArray) != napi_ok) {
return false;
}
bool isArray = false;
if (napi_is_array(env, macArray, &isArray) != napi_ok || !isArray) {
LBSLOGE(FUSION_FENCE, "mac is not an array");
return false;
}
uint32_t macLen = 0;
if (napi_get_array_length(env, macArray, &macLen) != napi_ok) {
return false;
}
if (macLen == 0) {
LBSLOGE(FUSION_FENCE, "mac array is empty");
return false;
}
LBSLOGI(FUSION_FENCE, "mac array length=%{public}u", macLen);
for (uint32_t j = 0; j < macLen; j++) {
napi_value macValue = nullptr;
if (napi_get_element(env, macArray, j, &macValue) != napi_ok) {
continue;
}
std::string mac;
if (!GetStringFromValue(env, macValue, mac)) {
LBSLOGE(FUSION_FENCE, "invalid mac at index %{public}u", j);
return false;
}
std::regex macRegex("^([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}$");
if (!std::regex_match(mac, macRegex)) {
LBSLOGE(FUSION_FENCE, "invalid mac format at index %{public}u: %{public}s", j, mac.c_str());
return false;
}
feature.mac.push_back(mac);
}
return true;
}
static bool ValidateWifiFeaturesArray(napi_env env, napi_value featuresArray, uint32_t& arrayLen)
{
if (env == nullptr || featuresArray == nullptr) {
LBSLOGE(FUSION_FENCE, "ValidateWifiFeaturesArray: env or featuresArray is null");
return false;
}
bool isArray = false;
if (napi_is_array(env, featuresArray, &isArray) != napi_ok || !isArray) {
LBSLOGE(FUSION_FENCE, "ValidateWifiFeaturesArray: featuresArray is not an array");
return false;
}
if (napi_get_array_length(env, featuresArray, &arrayLen) != napi_ok) {
LBSLOGE(FUSION_FENCE, "ValidateWifiFeaturesArray: get array length failed");
return false;
}
if (arrayLen == 0) {
LBSLOGE(FUSION_FENCE, "ValidateWifiFeaturesArray: arrayLen is 0");
return false;
}
return true;
}
static bool ParseWifiFeatures(napi_env env, napi_value featuresArray, std::vector<WirelessSignalFeature>& wifiFeatures)
{
uint32_t arrayLen = 0;
if (!ValidateWifiFeaturesArray(env, featuresArray, arrayLen)) {
return false;
}
constexpr uint32_t maxWifiFeaturesCount = 150;
if (arrayLen > maxWifiFeaturesCount) {
LBSLOGI(FUSION_FENCE, "wifiFeatures array length=%{public}u exceeds max=%{public}u, truncating to %{public}u",
arrayLen, maxWifiFeaturesCount, maxWifiFeaturesCount);
arrayLen = maxWifiFeaturesCount;
}
for (uint32_t i = 0; i < arrayLen; i++) {
napi_value featureValue = nullptr;
if (napi_get_element(env, featuresArray, i, &featureValue) != napi_ok) {
continue;
}
WirelessSignalFeature feature;
if (JsObjectToInt(env, featureValue, "rssiAvg", feature.rssiAvg) != SUCCESS ||
JsObjectToDouble(env, featureValue, "rssiStandardDeviation", feature.rssiStandardDeviation) != SUCCESS ||
!ParseWifiMacAddresses(env, featureValue, feature)) {
LBSLOGE(FUSION_FENCE, "ParseWifiFeatures: invalid feature at index %{public}u", i);
return false;
}
wifiFeatures.push_back(feature);
}
return true;
}
bool ParseWifiFence(napi_env env, napi_value object, std::shared_ptr<FusionFenceWifi>& wifiFence)
{
if (env == nullptr) {
LBSLOGE(FUSION_FENCE, "env is null");
return false;
}
if (object == nullptr) {
LBSLOGE(FUSION_FENCE, "object is null");
return false;
}
auto fence = std::make_shared<FusionFenceWifi>();
int32_t wifiType = 0;
if (JsObjectToInt(env, object, "type", wifiType) != SUCCESS) {
LBSLOGE(FUSION_FENCE, "type is required");
return false;
}
if (wifiType != static_cast<int32_t>(WifiFingerprintType::WIFI_FINGERPRINT_SEMANTIC) &&
wifiType != static_cast<int32_t>(WifiFingerprintType::WIFI_FINGERPRINT_LOCATION)) {
LBSLOGE(FUSION_FENCE, "invalid type: %{public}d", wifiType);
return false;
}
fence->type = static_cast<WifiFingerprintType>(wifiType);
LBSLOGI(FUSION_FENCE, "type=%{public}d", wifiType);
napi_value featuresArray = nullptr;
if (napi_get_named_property(env, object, "wifiFeatures", &featuresArray) != napi_ok) {
LBSLOGE(FUSION_FENCE, "wifiFeatures is required");
return false;
}
if (!ParseWifiFeatures(env, featuresArray, fence->wifiFeatures)) {
LBSLOGE(FUSION_FENCE, "ParseWifiFeatures failed");
return false;
}
wifiFence = fence;
return true;
}
static bool ParseCellInfos(napi_env env, napi_value cellInfosArray, std::vector<FusionFenceCellInfo>& cellInfos)
{
if (env == nullptr) {
LBSLOGE(FUSION_FENCE, "env is null");
return false;
}
if (cellInfosArray == nullptr) {
LBSLOGE(FUSION_FENCE, "cellInfosArray is null");
return false;
}
bool isArray = false;
if (napi_is_array(env, cellInfosArray, &isArray) != napi_ok || !isArray) {
LBSLOGE(FUSION_FENCE, "cellInfosArray is not an array");
return false;
}
uint32_t arrayLen = 0;
if (napi_get_array_length(env, cellInfosArray, &arrayLen) != napi_ok) {
LBSLOGE(FUSION_FENCE, "get array length failed");
return false;
}
const uint32_t maxCellInfosCount = 1000;
if (arrayLen > maxCellInfosCount) {
LBSLOGI(FUSION_FENCE, "cellInfos array length=%{public}u exceeds max=%{public}u, truncating to %{public}u",
arrayLen, maxCellInfosCount, maxCellInfosCount);
arrayLen = maxCellInfosCount;
}
for (uint32_t i = 0; i < arrayLen; i++) {
napi_value cellInfoValue = nullptr;
if (napi_get_element(env, cellInfosArray, i, &cellInfoValue) != napi_ok) {
continue;
}
FusionFenceCellInfo cellInfo;
if (ParseCellInfo(env, cellInfoValue, cellInfo)) {
LBSLOGI(FUSION_FENCE, "cellInfos[%{public}u] parsed", i);
cellInfos.push_back(cellInfo);
}
}
if (cellInfos.empty()) {
LBSLOGE(FUSION_FENCE, "cellInfos array is empty");
return false;
}
return true;
}
bool ParseCellFence(napi_env env, napi_value object, std::shared_ptr<FusionFenceCell>& cellFence)
{
if (env == nullptr) {
LBSLOGE(FUSION_FENCE, "env is null");
return false;
}
if (object == nullptr) {
LBSLOGE(FUSION_FENCE, "object is null");
return false;
}
auto fence = std::make_shared<FusionFenceCell>();
napi_value cellInfosArray = nullptr;
if (napi_get_named_property(env, object, "cellInfos", &cellInfosArray) == napi_ok) {
if (!ParseCellInfos(env, cellInfosArray, fence->cellInfos)) {
LBSLOGE(FUSION_FENCE, "ParseCellInfos failed");
return false;
}
}
cellFence = fence;
return true;
}
static bool ParseGnssFencesArray(
napi_env env, napi_value gnssFencesArray, std::vector<std::shared_ptr<FusionFenceGnss>>& gnssFences)
{
if (env == nullptr) {
LBSLOGE(FUSION_FENCE, "env is null");
return false;
}
if (gnssFencesArray == nullptr) {
LBSLOGE(FUSION_FENCE, "gnssFencesArray is null");
return false;
}
bool isArray = false;
if (napi_is_array(env, gnssFencesArray, &isArray) != napi_ok || !isArray) {
LBSLOGE(FUSION_FENCE, "gnssFencesArray is not an array");
return false;
}
uint32_t arrayLen = 0;
if (napi_get_array_length(env, gnssFencesArray, &arrayLen) != napi_ok) {
LBSLOGE(FUSION_FENCE, "get array length failed");
return false;
}
const uint32_t maxGnssFencesCount = 30;
if (arrayLen > maxGnssFencesCount) {
LBSLOGI(FUSION_FENCE, "gnssFences array length=%{public}u exceeds max=%{public}u, truncating to %{public}u",
arrayLen, maxGnssFencesCount, maxGnssFencesCount);
arrayLen = maxGnssFencesCount;
}
for (uint32_t i = 0; i < arrayLen; i++) {
napi_value gnssFenceValue = nullptr;
if (napi_get_element(env, gnssFencesArray, i, &gnssFenceValue) != napi_ok) {
continue;
}
std::shared_ptr<FusionFenceGnss> gnssFence = nullptr;
if (ParseGnssFence(env, gnssFenceValue, gnssFence)) {
gnssFences.push_back(gnssFence);
}
}
return true;
}
static bool ParseCellFencesArray(
napi_env env, napi_value cellFencesArray, std::vector<std::shared_ptr<FusionFenceCell>>& cellFences)
{
if (env == nullptr) {
LBSLOGE(FUSION_FENCE, "env is null");
return false;
}
if (cellFencesArray == nullptr) {
LBSLOGE(FUSION_FENCE, "cellFencesArray is null");
return false;
}
bool isArray = false;
if (napi_is_array(env, cellFencesArray, &isArray) != napi_ok || !isArray) {
LBSLOGE(FUSION_FENCE, "cellFencesArray is not an array");
return false;
}
uint32_t arrayLen = 0;
if (napi_get_array_length(env, cellFencesArray, &arrayLen) != napi_ok) {
LBSLOGE(FUSION_FENCE, "get array length failed");
return false;
}
const uint32_t maxCellFencesCount = 30;
if (arrayLen > maxCellFencesCount) {
LBSLOGI(FUSION_FENCE, "cellFences array length=%{public}u exceeds max=%{public}u, truncating to %{public}u",
arrayLen, maxCellFencesCount, maxCellFencesCount);
arrayLen = maxCellFencesCount;
}
for (uint32_t i = 0; i < arrayLen; i++) {
napi_value cellFenceValue = nullptr;
if (napi_get_element(env, cellFencesArray, i, &cellFenceValue) != napi_ok) {
continue;
}
std::shared_ptr<FusionFenceCell> cellFence = nullptr;
if (ParseCellFence(env, cellFenceValue, cellFence)) {
LBSLOGI(FUSION_FENCE, "cellFences[%{public}u] parsed", i);
cellFences.push_back(cellFence);
}
}
return true;
}
static bool ParseWifiFencesArray(
napi_env env, napi_value wifiFencesArray, std::vector<std::shared_ptr<FusionFenceWifi>>& wifiFences)
{
if (env == nullptr) {
LBSLOGE(FUSION_FENCE, "env is null");
return false;
}
if (wifiFencesArray == nullptr) {
LBSLOGE(FUSION_FENCE, "wifiFencesArray is null");
return false;
}
bool isArray = false;
if (napi_is_array(env, wifiFencesArray, &isArray) != napi_ok || !isArray) {
LBSLOGE(FUSION_FENCE, "wifiFencesArray is not an array");
return false;
}
uint32_t arrayLen = 0;
if (napi_get_array_length(env, wifiFencesArray, &arrayLen) != napi_ok) {
LBSLOGE(FUSION_FENCE, "get array length failed");
return false;
}
const uint32_t maxWifiFencesCount = 30;
if (arrayLen > maxWifiFencesCount) {
LBSLOGI(FUSION_FENCE, "wifiFences array length=%{public}u exceeds max=%{public}u, truncating to %{public}u",
arrayLen, maxWifiFencesCount, maxWifiFencesCount);
arrayLen = maxWifiFencesCount;
}
for (uint32_t i = 0; i < arrayLen; i++) {
napi_value wifiFenceValue = nullptr;
if (napi_get_element(env, wifiFencesArray, i, &wifiFenceValue) != napi_ok) {
continue;
}
std::shared_ptr<FusionFenceWifi> wifiFence = nullptr;
if (ParseWifiFence(env, wifiFenceValue, wifiFence)) {
LBSLOGI(FUSION_FENCE, "wifiFences[%{public}u] parsed", i);
wifiFences.push_back(wifiFence);
}
}
return true;
}
static bool ParsePoiLocation(napi_env env, napi_value object, FusionFenceRequest& request)
{
if (env == nullptr) {
LBSLOGE(FUSION_FENCE, "env is null");
return false;
}
if (object == nullptr) {
LBSLOGE(FUSION_FENCE, "object is null");
return false;
}
napi_value poiLocation = nullptr;
if (napi_get_named_property(env, object, "poiLocation", &poiLocation) != napi_ok) {
LBSLOGE(FUSION_FENCE, "poiLocation is required but missing");
return false;
}
FusionFencePoint poiPoint;
if (!ParsePoint(env, poiLocation, poiPoint)) {
return false;
}
request.SetPoiLocation(poiPoint);
return true;
}
static bool ParseGnssFences(napi_env env, napi_value object, FusionFenceRequest& request)
{
if (env == nullptr) {
LBSLOGE(FUSION_FENCE, "env is null");
return false;
}
if (object == nullptr) {
LBSLOGE(FUSION_FENCE, "object is null");
return false;
}
napi_value gnssFencesArray = nullptr;
if (napi_get_named_property(env, object, "gnssFences", &gnssFencesArray) != napi_ok) {
return false;
}
std::vector<std::shared_ptr<FusionFenceGnss>> gnssFences;
if (!ParseGnssFencesArray(env, gnssFencesArray, gnssFences)) {
LBSLOGE(FUSION_FENCE, "ParseGnssFencesArray failed");
return false;
}
if (gnssFences.empty()) {
LBSLOGE(FUSION_FENCE, "gnssFences array is empty");
return false;
}
request.SetGnssFences(gnssFences);
return true;
}
static bool ParseCellFences(napi_env env, napi_value object, FusionFenceRequest& request)
{
if (env == nullptr) {
LBSLOGE(FUSION_FENCE, "env is null");
return false;
}
if (object == nullptr) {
LBSLOGE(FUSION_FENCE, "object is null");
return false;
}
napi_value cellFencesArray = nullptr;
if (napi_get_named_property(env, object, "cellFences", &cellFencesArray) != napi_ok) {
return false;
}
std::vector<std::shared_ptr<FusionFenceCell>> cellFences;
if (!ParseCellFencesArray(env, cellFencesArray, cellFences)) {
LBSLOGE(FUSION_FENCE, "ParseCellFencesArray failed");
return false;
}
if (cellFences.empty()) {
LBSLOGE(FUSION_FENCE, "cellFences array is empty");
return false;
}
request.SetCellFences(cellFences);
return true;
}
static bool ParseWifiFences(napi_env env, napi_value object, FusionFenceRequest& request)
{
if (env == nullptr) {
LBSLOGE(FUSION_FENCE, "env is null");
return false;
}
if (object == nullptr) {
LBSLOGE(FUSION_FENCE, "object is null");
return false;
}
napi_value wifiFencesArray = nullptr;
if (napi_get_named_property(env, object, "wifiFences", &wifiFencesArray) != napi_ok) {
return false;
}
std::vector<std::shared_ptr<FusionFenceWifi>> wifiFences;
if (!ParseWifiFencesArray(env, wifiFencesArray, wifiFences)) {
LBSLOGE(FUSION_FENCE, "ParseWifiFencesArray failed");
return false;
}
if (wifiFences.empty()) {
LBSLOGE(FUSION_FENCE, "wifiFences array is empty");
return false;
}
request.SetWifiFences(wifiFences);
return true;
}
static bool ParseRequiredStrings(napi_env env, napi_value object, std::string& identifier, std::string& poiType)
{
if (JsObjectToString(env, object, "identifier", MAX_BUF_LEN, identifier) != SUCCESS) {
LBSLOGE(FUSION_FENCE, "identifier is required");
return false;
}
if (identifier.empty()) {
LBSLOGE(FUSION_FENCE, "identifier is empty");
return false;
}
JsObjectToString(env, object, "poiType", MAX_BUF_LEN, poiType);
return true;
}
static bool ParseAndValidateSceneAndFenceType(napi_env env, napi_value object, int32_t& scene, int32_t& fenceType)
{
if (JsObjectToInt(env, object, "scene", scene) != SUCCESS) {
LBSLOGE(FUSION_FENCE, "scene is required");
return false;
}
if (scene != static_cast<int32_t>(FusionFenceScene::AIRPORT) &&
scene != static_cast<int32_t>(FusionFenceScene::TRAIN_STATION) &&
scene != static_cast<int32_t>(FusionFenceScene::SUBWAY) &&
scene != static_cast<int32_t>(FusionFenceScene::SHOP)) {
LBSLOGE(FUSION_FENCE, "invalid scene: %{public}d", scene);
return false;
}
if (JsObjectToInt(env, object, "fenceType", fenceType) != SUCCESS) {
LBSLOGE(FUSION_FENCE, "fenceType is required");
return false;
}
constexpr int32_t validFenceTypeMask = static_cast<int32_t>(FusionFenceType::FUSION_FENCE_GNSS) |
static_cast<int32_t>(FusionFenceType::FUSION_FENCE_CELLULAR) |
static_cast<int32_t>(FusionFenceType::FUSION_FENCE_WIFI) |
static_cast<int32_t>(FusionFenceType::FUSION_FENCE_BLUETOOTH);
if (fenceType <= 0 || (fenceType & ~validFenceTypeMask) != 0) {
LBSLOGE(FUSION_FENCE, "invalid fenceType: %{public}d", fenceType);
return false;
}
return true;
}
static int32_t ConvertJsTransitionEventsToNative(int32_t jsTransitionEvents)
{
int32_t nativeEvents = 0;
if ((jsTransitionEvents & GeofenceTransitionEvent::GEOFENCE_TRANSITION_EVENT_ENTER) != 0) {
nativeEvents |= SensorGeofenceTransition::SENSOR_GEOFENCE_TRANSITION_ENTERED;
}
if ((jsTransitionEvents & GeofenceTransitionEvent::GEOFENCE_TRANSITION_EVENT_EXIT) != 0) {
nativeEvents |= SensorGeofenceTransition::SENSOR_GEOFENCE_TRANSITION_EXITED;
}
if ((jsTransitionEvents & GeofenceTransitionEvent::GEOFENCE_TRANSITION_EVENT_DWELL) != 0) {
nativeEvents |= SensorGeofenceTransition::SENSOR_GEOFENCE_TRANSITION_DWELL;
}
if ((jsTransitionEvents & GeofenceTransitionEvent::GEOFENCE_TRANSITION_EVENT_APPROACHING_GEOFENCE) != 0) {
nativeEvents |= SensorGeofenceTransition::SENSOR_GEOFENCE_TRANSITION_NEAR;
}
if ((jsTransitionEvents & GeofenceTransitionEvent::GEOFENCE_TRANSITION_EVENT_LEAVING_GEOFENCE) != 0) {
nativeEvents |= SensorGeofenceTransition::SENSOR_GEOFENCE_TRANSITION_FAR;
}
if ((jsTransitionEvents & GeofenceTransitionEvent::GEOFENCE_TRANSITION_EVENT_NEAR_WANDER) != 0) {
nativeEvents |= SensorGeofenceTransition::SENSOR_GEOFENCE_TRANSITION_NEAR_WANDER;
}
return nativeEvents;
}
static bool ParseTimingFields(napi_env env, napi_value object, std::shared_ptr<FusionFenceRequest>& fusionRequest)
{
int32_t monitorTransitionEvents = 0;
if (JsObjectToInt(env, object, "monitorTransitionEvents", monitorTransitionEvents) != SUCCESS) {
LBSLOGE(FUSION_FENCE, "monitorTransitionEvents is required");
return false;
}
constexpr int32_t validTransitionMask =
static_cast<int32_t>(GeofenceTransitionEvent::GEOFENCE_TRANSITION_EVENT_ENTER) |
static_cast<int32_t>(GeofenceTransitionEvent::GEOFENCE_TRANSITION_EVENT_EXIT) |
static_cast<int32_t>(GeofenceTransitionEvent::GEOFENCE_TRANSITION_EVENT_DWELL) |
static_cast<int32_t>(GeofenceTransitionEvent::GEOFENCE_TRANSITION_EVENT_APPROACHING_GEOFENCE) |
static_cast<int32_t>(GeofenceTransitionEvent::GEOFENCE_TRANSITION_EVENT_LEAVING_GEOFENCE) |
static_cast<int32_t>(GeofenceTransitionEvent::GEOFENCE_TRANSITION_EVENT_NEAR_WANDER);
if (monitorTransitionEvents <= 0 ||
(monitorTransitionEvents & validTransitionMask) != monitorTransitionEvents) {
LBSLOGE(FUSION_FENCE, "invalid monitorTransitionEvents: %{public}d", monitorTransitionEvents);
return false;
}
int64_t loiterTimeMs = 0;
if (JsObjectToInt64(env, object, "loiterTimeMs", loiterTimeMs) != SUCCESS) {
LBSLOGE(FUSION_FENCE, "loiterTimeMs is required");
return false;
}
if (loiterTimeMs < 0) {
LBSLOGE(FUSION_FENCE, "invalid loiterTimeMs.");
return false;
}
int64_t expirationMs = 0;
if (JsObjectToInt64(env, object, "expirationMs", expirationMs) != SUCCESS) {
LBSLOGE(FUSION_FENCE, "expirationMs is required");
return false;
}
if (expirationMs <= 0) {
LBSLOGE(FUSION_FENCE, "invalid expirationMs.");
return false;
}
int32_t nativeTransitionEvents = ConvertJsTransitionEventsToNative(monitorTransitionEvents);
fusionRequest->SetMonitorTransitionEvents(nativeTransitionEvents);
fusionRequest->SetLoiterTimeMs(loiterTimeMs);
fusionRequest->SetExpirationMs(expirationMs);
return true;
}
static bool ParseFenceArraysByType(napi_env env, napi_value object, int32_t fenceType,
std::shared_ptr<FusionFenceRequest>& fusionRequest)
{
if ((fenceType & static_cast<int32_t>(FusionFenceType::FUSION_FENCE_GNSS)) != 0) {
if (!ParseGnssFences(env, object, *fusionRequest)) {
LBSLOGE(FUSION_FENCE, "ParseGnssFences failed");
return false;
}
}
if ((fenceType & static_cast<int32_t>(FusionFenceType::FUSION_FENCE_CELLULAR)) != 0) {
if (!ParseCellFences(env, object, *fusionRequest)) {
LBSLOGE(FUSION_FENCE, "ParseCellFences failed");
return false;
}
}
if ((fenceType & static_cast<int32_t>(FusionFenceType::FUSION_FENCE_WIFI)) != 0) {
if (!ParseWifiFences(env, object, *fusionRequest)) {
LBSLOGE(FUSION_FENCE, "ParseWifiFences failed");
return false;
}
}
return true;
}
bool ParseFusionFenceRequest(napi_env env, napi_value object, std::shared_ptr<FusionFenceRequest>& request)
{
if (env == nullptr) {
LBSLOGE(FUSION_FENCE, "env is null");
return false;
}
if (object == nullptr) {
LBSLOGE(FUSION_FENCE, "object is null");
return false;
}
auto fusionRequest = std::make_shared<FusionFenceRequest>();
std::string identifier;
std::string poiType;
if (!ParseRequiredStrings(env, object, identifier, poiType)) {
return false;
}
int32_t scene = 0;
int32_t fenceType = 0;
if (!ParseAndValidateSceneAndFenceType(env, object, scene, fenceType)) {
return false;
}
if (!ParseTimingFields(env, object, fusionRequest)) {
return false;
}
if (!ParsePoiLocation(env, object, *fusionRequest)) {
return false;
}
if (!ParseFenceArraysByType(env, object, fenceType, fusionRequest)) {
return false;
}
LBSLOGI(FUSION_FENCE, "identifier=%{public}s, scene=%{public}d, fenceType=%{public}d",
identifier.c_str(), scene, fenceType);
fusionRequest->SetIdentifier(identifier);
fusionRequest->SetScene(static_cast<FusionFenceScene>(scene));
fusionRequest->SetFenceType(fenceType);
fusionRequest->SetPoiType(poiType);
request = fusionRequest;
return true;
}
napi_value CreateFusionFenceTransitionJsObj(napi_env env, const FusionFenceTransition& transition)
{
napi_value result = nullptr;
napi_create_object(env, &result);
napi_value identifierValue = nullptr;
if (napi_create_string_utf8(env, transition.identifier.c_str(), NAPI_AUTO_LENGTH, &identifierValue) != napi_ok) {
return nullptr;
}
napi_set_named_property(env, result, "identifier", identifierValue);
napi_value sceneValue = nullptr;
if (napi_create_int32(env, static_cast<int32_t>(transition.scene), &sceneValue) != napi_ok) {
return nullptr;
}
napi_set_named_property(env, result, "scene", sceneValue);
napi_value transitionEventValue = nullptr;
if (napi_create_int32(env, static_cast<int32_t>(transition.transitionEvent), &transitionEventValue) != napi_ok) {
return nullptr;
}
napi_set_named_property(env, result, "transitionEvent", transitionEventValue);
return result;
}
bool ParseFusionFenceTransitionCallback(napi_env env, napi_value object,
sptr<FusionFenceCallbackNapi>& callbackHost)
{
if (env == nullptr) {
LBSLOGE(FUSION_FENCE, "env is null");
return false;
}
if (object == nullptr) {
LBSLOGE(FUSION_FENCE, "object is null");
return false;
}
napi_ref handlerRef = nullptr;
napi_value callbackNapiValue = nullptr;
if (napi_get_named_property(env, object, "fenceTransitionCallback", &callbackNapiValue) != napi_ok) {
LBSLOGE(FUSION_FENCE, "fenceTransitionCallback is required but missing");
return false;
}
if (callbackNapiValue != nullptr) {
napi_valuetype valueType;
if (napi_typeof(env, callbackNapiValue, &valueType) != napi_ok || valueType != napi_function) {
LBSLOGE(FUSION_FENCE, "fenceTransitionCallback is not a function");
return false;
}
if (napi_create_reference(env, callbackNapiValue, 1, &handlerRef) != napi_ok) {
LBSLOGE(FUSION_FENCE, "napi_create_reference failed");
return false;
}
}
callbackHost->SetEnv(env);
callbackHost->SetHandleCb(handlerRef);
return true;
}
bool GetStringFromValue(const napi_env& env, napi_value value, std::string& result)
{
napi_valuetype valueType;
if (napi_typeof(env, value, &valueType) != napi_ok || valueType != napi_string) {
return false;
}
size_t bufsize = 0;
if (napi_get_value_string_utf8(env, value, nullptr, 0, &bufsize) != napi_ok) {
return false;
}
if (bufsize == 0) {
return false;
}
if (bufsize > SIZE_MAX - 1) {
LBSLOGE(FUSION_FENCE, "bufsize overflow: %{public}zu", bufsize);
return false;
}
result.resize(bufsize, '\0');
if (napi_get_value_string_utf8(env, value, result.data(), bufsize + 1, &bufsize) != napi_ok) {
return false;
}
return true;
}
}
}