/* Copyright (c) 2017, The Linux Foundation. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials provided * with the distribution. * * Neither the name of The Linux Foundation, nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ #define LOG_TAG "LocSvc_GnssAdapter" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace loc_core; GnssAdapter::GnssAdapter() : LocAdapterBase(0, LocDualContext::getLocFgContext(NULL, NULL, LocDualContext::mLocationHalName, false)), mUlpProxy(new UlpProxyBase()), mUlpPositionMode(), mGnssSvIdUsedInPosition(), mGnssSvIdUsedInPosAvail(false), mControlCallbacks(), mPowerVoteId(0), mNiData(), mAgpsManager() { LOC_LOGD("%s]: Constructor %p", __func__, this); mUlpPositionMode.mode = LOC_POSITION_MODE_INVALID; readConfigCommand(); setConfigCommand(); } inline GnssAdapter::~GnssAdapter() { LOC_LOGD("%s]: Destructor", __func__); delete mUlpProxy; } void GnssAdapter::setControlCallbacksCommand(LocationControlCallbacks& controlCallbacks) { struct MsgSetControlCallbacks : public LocMsg { GnssAdapter& mAdapter; const LocationControlCallbacks mControlCallbacks; inline MsgSetControlCallbacks(GnssAdapter& adapter, LocationControlCallbacks& controlCallbacks) : LocMsg(), mAdapter(adapter), mControlCallbacks(controlCallbacks) {} inline virtual void proc() const { mAdapter.setControlCallbacks(mControlCallbacks); } }; sendMsg(new MsgSetControlCallbacks(*this, controlCallbacks)); } void GnssAdapter::convertOptions(LocPosMode& out, const LocationOptions& options) { LocPosMode locPosMode = {}; switch (options.mode) { case GNSS_SUPL_MODE_MSB: out.mode = LOC_POSITION_MODE_MS_BASED; break; case GNSS_SUPL_MODE_MSA: out.mode = LOC_POSITION_MODE_MS_ASSISTED; break; default: out.mode = LOC_POSITION_MODE_STANDALONE; break; } out.share_position = true; out.min_interval = options.minInterval; } void GnssAdapter::convertLocation(Location& out, const LocGpsLocation& locGpsLocation, const GpsLocationExtended& locationExtended, const LocPosTechMask techMask) { out.size = sizeof(Location); if (LOC_GPS_LOCATION_HAS_LAT_LONG & locGpsLocation.flags) { out.flags |= LOCATION_HAS_LAT_LONG_BIT; out.latitude = locGpsLocation.latitude; out.longitude = locGpsLocation.longitude; } if (LOC_GPS_LOCATION_HAS_ALTITUDE & locGpsLocation.flags) { out.flags |= LOCATION_HAS_ALTITUDE_BIT; out.altitude = locGpsLocation.altitude; } if (LOC_GPS_LOCATION_HAS_SPEED & locGpsLocation.flags) { out.flags |= LOCATION_HAS_SPEED_BIT; out.speed = locGpsLocation.speed; } if (LOC_GPS_LOCATION_HAS_BEARING & locGpsLocation.flags) { out.flags |= LOCATION_HAS_BEARING_BIT; out.bearing = locGpsLocation.bearing; } if (LOC_GPS_LOCATION_HAS_ACCURACY & locGpsLocation.flags) { out.flags |= LOCATION_HAS_ACCURACY_BIT; out.accuracy = locGpsLocation.accuracy; } if (GPS_LOCATION_EXTENDED_HAS_VERT_UNC & locationExtended.flags) { out.flags |= LOCATION_HAS_VERTICAL_ACCURACY_BIT; out.verticalAccuracy = locationExtended.vert_unc; } if (GPS_LOCATION_EXTENDED_HAS_SPEED_UNC & locationExtended.flags) { out.flags |= LOCATION_HAS_SPEED_ACCURACY_BIT; out.speedAccuracy = locationExtended.speed_unc; } if (GPS_LOCATION_EXTENDED_HAS_BEARING_UNC & locationExtended.flags) { out.flags |= LOCATION_HAS_BEARING_ACCURACY_BIT; out.bearingAccuracy = locationExtended.bearing_unc; } out.timestamp = locGpsLocation.timestamp; if (LOC_POS_TECH_MASK_SATELLITE & techMask) { out.techMask |= LOCATION_TECHNOLOGY_GNSS_BIT; } if (LOC_POS_TECH_MASK_CELLID & techMask) { out.techMask |= LOCATION_TECHNOLOGY_CELL_BIT; } if (LOC_POS_TECH_MASK_WIFI & techMask) { out.techMask |= LOCATION_TECHNOLOGY_WIFI_BIT; } if (LOC_POS_TECH_MASK_SENSORS & techMask) { out.techMask |= LOCATION_TECHNOLOGY_SENSORS_BIT; } } void GnssAdapter::convertLocationInfo(GnssLocationInfoNotification& out, const GpsLocationExtended& locationExtended) { out.size = sizeof(GnssLocationInfoNotification); if (GPS_LOCATION_EXTENDED_HAS_ALTITUDE_MEAN_SEA_LEVEL & locationExtended.flags) { out.flags |= GNSS_LOCATION_INFO_ALTITUDE_MEAN_SEA_LEVEL_BIT; out.altitudeMeanSeaLevel = locationExtended.altitudeMeanSeaLevel; } if (GPS_LOCATION_EXTENDED_HAS_DOP & locationExtended.flags) { out.flags |= GNSS_LOCATION_INFO_DOP_BIT; out.pdop = locationExtended.pdop; out.hdop = locationExtended.hdop; out.vdop = locationExtended.vdop; } if (GPS_LOCATION_EXTENDED_HAS_MAG_DEV & locationExtended.flags) { out.flags |= GNSS_LOCATION_INFO_MAGNETIC_DEVIATION_BIT; out.magneticDeviation = locationExtended.magneticDeviation; } if (GPS_LOCATION_EXTENDED_HAS_HOR_RELIABILITY & locationExtended.flags) { out.flags |= GNSS_LOCATION_INFO_HOR_RELIABILITY_BIT; switch (locationExtended.horizontal_reliability) { case LOC_RELIABILITY_VERY_LOW: out.horReliability = LOCATION_RELIABILITY_VERY_LOW; break; case LOC_RELIABILITY_LOW: out.horReliability = LOCATION_RELIABILITY_LOW; break; case LOC_RELIABILITY_MEDIUM: out.horReliability = LOCATION_RELIABILITY_MEDIUM; break; case LOC_RELIABILITY_HIGH: out.horReliability = LOCATION_RELIABILITY_HIGH; break; default: out.horReliability = LOCATION_RELIABILITY_NOT_SET; break; } } if (GPS_LOCATION_EXTENDED_HAS_VERT_RELIABILITY & locationExtended.flags) { out.flags |= GNSS_LOCATION_INFO_VER_RELIABILITY_BIT; switch (locationExtended.vertical_reliability) { case LOC_RELIABILITY_VERY_LOW: out.verReliability = LOCATION_RELIABILITY_VERY_LOW; break; case LOC_RELIABILITY_LOW: out.verReliability = LOCATION_RELIABILITY_LOW; break; case LOC_RELIABILITY_MEDIUM: out.verReliability = LOCATION_RELIABILITY_MEDIUM; break; case LOC_RELIABILITY_HIGH: out.verReliability = LOCATION_RELIABILITY_HIGH; break; default: out.verReliability = LOCATION_RELIABILITY_NOT_SET; break; } } if (GPS_LOCATION_EXTENDED_HAS_HOR_ELIP_UNC_MAJOR & locationExtended.flags) { out.flags |= GNSS_LOCATION_INFO_HOR_ACCURACY_ELIP_SEMI_MAJOR_BIT; out.horUncEllipseSemiMajor = locationExtended.horUncEllipseSemiMajor; } if (GPS_LOCATION_EXTENDED_HAS_HOR_ELIP_UNC_MINOR & locationExtended.flags) { out.flags |= GNSS_LOCATION_INFO_HOR_ACCURACY_ELIP_SEMI_MINOR_BIT; out.horUncEllipseSemiMinor = locationExtended.horUncEllipseSemiMinor; } if (GPS_LOCATION_EXTENDED_HAS_HOR_ELIP_UNC_AZIMUTH & locationExtended.flags) { out.flags |= GNSS_LOCATION_INFO_HOR_ACCURACY_ELIP_AZIMUTH_BIT; out.horUncEllipseOrientAzimuth = locationExtended.horUncEllipseOrientAzimuth; } } inline uint32_t GnssAdapter::convertGpsLock(const GnssConfigGpsLock gpsLock) { switch (gpsLock) { case GNSS_CONFIG_GPS_LOCK_MO: return 1; case GNSS_CONFIG_GPS_LOCK_NI: return 2; case GNSS_CONFIG_GPS_LOCK_MO_AND_NI: return 3; case GNSS_CONFIG_GPS_LOCK_NONE: default: return 0; } } inline GnssConfigGpsLock GnssAdapter::convertGpsLock(const uint32_t gpsLock) { switch (gpsLock) { case 1: return GNSS_CONFIG_GPS_LOCK_MO; case 2: return GNSS_CONFIG_GPS_LOCK_NI; case 3: return GNSS_CONFIG_GPS_LOCK_MO_AND_NI; case 0: default: return GNSS_CONFIG_GPS_LOCK_NONE; } } inline uint32_t GnssAdapter::convertSuplVersion(const GnssConfigSuplVersion suplVersion) { switch (suplVersion) { case GNSS_CONFIG_SUPL_VERSION_2_0_0: return 0x00020000; case GNSS_CONFIG_SUPL_VERSION_2_0_2: return 0x00020002; case GNSS_CONFIG_SUPL_VERSION_1_0_0: default: return 0x00010000; } } inline GnssConfigSuplVersion GnssAdapter::convertSuplVersion(const uint32_t suplVersion) { switch (suplVersion) { case 0x00020000: return GNSS_CONFIG_SUPL_VERSION_2_0_0; case 0x00020002: return GNSS_CONFIG_SUPL_VERSION_2_0_2; case 0x00010000: default: return GNSS_CONFIG_SUPL_VERSION_1_0_0; } } inline uint32_t GnssAdapter::convertLppProfile(const GnssConfigLppProfile lppProfile) { switch (lppProfile) { case GNSS_CONFIG_LPP_PROFILE_USER_PLANE: return 1; case GNSS_CONFIG_LPP_PROFILE_CONTROL_PLANE: return 2; case GNSS_CONFIG_LPP_PROFILE_USER_PLANE_AND_CONTROL_PLANE: return 3; case GNSS_CONFIG_LPP_PROFILE_RRLP_ON_LTE: default: return 0; } } inline GnssConfigLppProfile GnssAdapter::convertLppProfile(const uint32_t lppProfile) { switch (lppProfile) { case 1: return GNSS_CONFIG_LPP_PROFILE_USER_PLANE; case 2: return GNSS_CONFIG_LPP_PROFILE_CONTROL_PLANE; case 3: return GNSS_CONFIG_LPP_PROFILE_USER_PLANE_AND_CONTROL_PLANE; case 0: default: return GNSS_CONFIG_LPP_PROFILE_RRLP_ON_LTE; } } uint32_t GnssAdapter::convertLppeCp(const GnssConfigLppeControlPlaneMask lppeControlPlaneMask) { uint32_t mask = 0; if (GNSS_CONFIG_LPPE_CONTROL_PLANE_DBH_BIT & lppeControlPlaneMask) { mask |= (1<<0); } if (GNSS_CONFIG_LPPE_CONTROL_PLANE_WLAN_AP_MEASUREMENTS_BIT & lppeControlPlaneMask) { mask |= (1<<1); } return mask; } GnssConfigLppeControlPlaneMask GnssAdapter::convertLppeCp(const uint32_t lppeControlPlaneMask) { GnssConfigLppeControlPlaneMask mask = 0; if ((1<<0) & lppeControlPlaneMask) { mask |= GNSS_CONFIG_LPPE_CONTROL_PLANE_DBH_BIT; } if ((1<<1) & lppeControlPlaneMask) { mask |= GNSS_CONFIG_LPPE_CONTROL_PLANE_WLAN_AP_MEASUREMENTS_BIT; } return mask; } uint32_t GnssAdapter::convertLppeUp(const GnssConfigLppeUserPlaneMask lppeUserPlaneMask) { uint32_t mask = 0; if (GNSS_CONFIG_LPPE_USER_PLANE_DBH_BIT & lppeUserPlaneMask) { mask |= (1<<0); } if (GNSS_CONFIG_LPPE_USER_PLANE_WLAN_AP_MEASUREMENTS_BIT & lppeUserPlaneMask) { mask |= (1<<1); } return mask; } GnssConfigLppeUserPlaneMask GnssAdapter::convertLppeUp(const uint32_t lppeUserPlaneMask) { GnssConfigLppeUserPlaneMask mask = 0; if ((1<<0) & lppeUserPlaneMask) { mask |= GNSS_CONFIG_LPPE_USER_PLANE_DBH_BIT; } if ((1<<1) & lppeUserPlaneMask) { mask |= GNSS_CONFIG_LPPE_USER_PLANE_WLAN_AP_MEASUREMENTS_BIT; } return mask; } uint32_t GnssAdapter::convertAGloProt(const GnssConfigAGlonassPositionProtocolMask aGloPositionProtocolMask) { uint32_t mask = 0; if (GNSS_CONFIG_RRC_CONTROL_PLANE_BIT & aGloPositionProtocolMask) { mask |= (1<<0); } if (GNSS_CONFIG_RRLP_USER_PLANE_BIT & aGloPositionProtocolMask) { mask |= (1<<1); } if (GNSS_CONFIG_LLP_USER_PLANE_BIT & aGloPositionProtocolMask) { mask |= (1<<2); } if (GNSS_CONFIG_LLP_CONTROL_PLANE_BIT & aGloPositionProtocolMask) { mask |= (1<<3); } return mask; } uint32_t GnssAdapter::convertEP4ES(const GnssConfigEmergencyPdnForEmergencySupl emergencyPdnForEmergencySupl) { switch (emergencyPdnForEmergencySupl) { case GNSS_CONFIG_EMERGENCY_PDN_FOR_EMERGENCY_SUPL_YES: return 1; case GNSS_CONFIG_EMERGENCY_PDN_FOR_EMERGENCY_SUPL_NO: default: return 0; } } uint32_t GnssAdapter::convertSuplEs(const GnssConfigSuplEmergencyServices suplEmergencyServices) { switch (suplEmergencyServices) { case GNSS_CONFIG_SUPL_EMERGENCY_SERVICES_YES: return 1; case GNSS_CONFIG_SUPL_EMERGENCY_SERVICES_NO: default: return 0; } } uint32_t GnssAdapter::convertSuplMode(const GnssConfigSuplModeMask suplModeMask) { uint32_t mask = 0; if (GNSS_CONFIG_SUPL_MODE_MSB_BIT & suplModeMask) { mask |= (1<<0); } if (GNSS_CONFIG_SUPL_MODE_MSA_BIT & suplModeMask) { mask |= (1<<1); } return mask; } bool GnssAdapter::resolveInAddress(const char* hostAddress, struct in_addr* inAddress) { bool ret = true; struct hostent* hp; hp = gethostbyname(hostAddress); if (hp != NULL) { /* DNS OK */ memcpy(inAddress, hp->h_addr_list[0], hp->h_length); } else { /* Try IP representation */ if (inet_aton(hostAddress, inAddress) == 0) { /* IP not valid */ LOC_LOGE("%s]: DNS query on '%s' failed", __func__, hostAddress); ret = false; } } return ret; } void GnssAdapter::readConfigCommand() { LOC_LOGD("%s]: ", __func__); struct MsgReadConfig : public LocMsg { GnssAdapter* mAdapter; ContextBase& mContext; inline MsgReadConfig(GnssAdapter* adapter, ContextBase& context) : LocMsg(), mAdapter(adapter), mContext(context) {} inline virtual void proc() const { // reads config into mContext->mGps_conf mContext.readConfig(); mContext.requestUlp((LocAdapterBase*)mAdapter, mContext.getCarrierCapabilities()); } }; if (mContext != NULL) { sendMsg(new MsgReadConfig(this, *mContext)); } } void GnssAdapter::setConfigCommand() { LOC_LOGD("%s]: ", __func__); struct MsgSetConfig : public LocMsg { GnssAdapter& mAdapter; LocApiBase& mApi; inline MsgSetConfig(GnssAdapter& adapter, LocApiBase& api) : LocMsg(), mAdapter(adapter), mApi(api) {} inline virtual void proc() const { LocDualContext::injectFeatureConfig(mAdapter.getContext()); mApi.setSUPLVersion(mAdapter.convertSuplVersion(ContextBase::mGps_conf.SUPL_VER)); mApi.setLPPConfig(mAdapter.convertLppProfile(ContextBase::mGps_conf.LPP_PROFILE)); mApi.setSensorControlConfig(ContextBase::mSap_conf.SENSOR_USAGE, ContextBase::mSap_conf.SENSOR_PROVIDER); mApi.setAGLONASSProtocol(ContextBase::mGps_conf.A_GLONASS_POS_PROTOCOL_SELECT); mApi.setLPPeProtocolCp( mAdapter.convertLppeCp(ContextBase::mGps_conf.LPPE_CP_TECHNOLOGY)); mApi.setLPPeProtocolUp( mAdapter.convertLppeUp(ContextBase::mGps_conf.LPPE_UP_TECHNOLOGY)); // set nmea mask type uint32_t mask = 0; if (NMEA_PROVIDER_MP == ContextBase::mGps_conf.NMEA_PROVIDER) { mask = LOC_NMEA_ALL_SUPPORTED_MASK; } else { mask = LOC_NMEA_MASK_DEBUG_V02; } mApi.setNMEATypes(mask); mApi.setXtraVersionCheck(ContextBase::mGps_conf.XTRA_VERSION_CHECK); if (ContextBase::mSap_conf.GYRO_BIAS_RANDOM_WALK_VALID || ContextBase::mSap_conf.ACCEL_RANDOM_WALK_SPECTRAL_DENSITY_VALID || ContextBase::mSap_conf.ANGLE_RANDOM_WALK_SPECTRAL_DENSITY_VALID || ContextBase::mSap_conf.RATE_RANDOM_WALK_SPECTRAL_DENSITY_VALID || ContextBase::mSap_conf.VELOCITY_RANDOM_WALK_SPECTRAL_DENSITY_VALID ) { mApi.setSensorProperties( ContextBase::mSap_conf.GYRO_BIAS_RANDOM_WALK_VALID, ContextBase::mSap_conf.GYRO_BIAS_RANDOM_WALK, ContextBase::mSap_conf.ACCEL_RANDOM_WALK_SPECTRAL_DENSITY_VALID, ContextBase::mSap_conf.ACCEL_RANDOM_WALK_SPECTRAL_DENSITY, ContextBase::mSap_conf.ANGLE_RANDOM_WALK_SPECTRAL_DENSITY_VALID, ContextBase::mSap_conf.ANGLE_RANDOM_WALK_SPECTRAL_DENSITY, ContextBase::mSap_conf.RATE_RANDOM_WALK_SPECTRAL_DENSITY_VALID, ContextBase::mSap_conf.RATE_RANDOM_WALK_SPECTRAL_DENSITY, ContextBase::mSap_conf.VELOCITY_RANDOM_WALK_SPECTRAL_DENSITY_VALID, ContextBase::mSap_conf.VELOCITY_RANDOM_WALK_SPECTRAL_DENSITY); } mApi.setSensorPerfControlConfig( ContextBase::mSap_conf.SENSOR_CONTROL_MODE, ContextBase::mSap_conf.SENSOR_ACCEL_SAMPLES_PER_BATCH, ContextBase::mSap_conf.SENSOR_ACCEL_BATCHES_PER_SEC, ContextBase::mSap_conf.SENSOR_GYRO_SAMPLES_PER_BATCH, ContextBase::mSap_conf.SENSOR_GYRO_BATCHES_PER_SEC, ContextBase::mSap_conf.SENSOR_ACCEL_SAMPLES_PER_BATCH_HIGH, ContextBase::mSap_conf.SENSOR_ACCEL_BATCHES_PER_SEC_HIGH, ContextBase::mSap_conf.SENSOR_GYRO_SAMPLES_PER_BATCH_HIGH, ContextBase::mSap_conf.SENSOR_GYRO_BATCHES_PER_SEC_HIGH, ContextBase::mSap_conf.SENSOR_ALGORITHM_CONFIG_MASK); } }; sendMsg(new MsgSetConfig(*this, *mLocApi)); } uint32_t* GnssAdapter::gnssUpdateConfigCommand(GnssConfig config) { // count the number of bits set GnssConfigFlagsMask flagsCopy = config.flags; size_t count = 0; while (flagsCopy > 0) { if (flagsCopy & 1) { count++; } flagsCopy >>= 1; } std::string idsString = "["; uint32_t* ids = NULL; if (count > 0) { ids = new uint32_t[count]; for (size_t i=0; i < count; ++i) { ids[i] = generateSessionId(); IF_LOC_LOGD { idsString += std::to_string(ids[i]) + " "; } } } idsString += "]"; LOC_LOGD("%s]: ids %s flags 0x%X", __func__, idsString.c_str(), config.flags); struct MsgGnssUpdateConfig : public LocMsg { GnssAdapter& mAdapter; LocApiBase& mApi; GnssConfig mConfig; uint32_t* mIds; size_t mCount; inline MsgGnssUpdateConfig(GnssAdapter& adapter, LocApiBase& api, GnssConfig config, uint32_t* ids, size_t count) : LocMsg(), mAdapter(adapter), mApi(api), mConfig(config), mIds(ids), mCount(count) {} inline virtual ~MsgGnssUpdateConfig() { delete[] mIds; } inline virtual void proc() const { //const size_t MAX_BITS_COUNT = 10; //LocationError errs[MAX_BITS_COUNT] = {}; LocationError* errs = new LocationError[mCount]; LocationError err = LOCATION_ERROR_SUCCESS; uint32_t index = 0; if (mConfig.flags & GNSS_CONFIG_FLAGS_GPS_LOCK_VALID_BIT) { uint32_t newGpsLock = mAdapter.convertGpsLock(mConfig.gpsLock); ContextBase::mGps_conf.GPS_LOCK = newGpsLock; if (0 == mAdapter.getPowerVoteId()) { err = mApi.setGpsLock(mConfig.gpsLock); } if (index < mCount) { errs[index++] = err; } } if (mConfig.flags & GNSS_CONFIG_FLAGS_SUPL_VERSION_VALID_BIT) { uint32_t newSuplVersion = mAdapter.convertSuplVersion(mConfig.suplVersion); if (newSuplVersion != ContextBase::mGps_conf.SUPL_VER) { ContextBase::mGps_conf.SUPL_VER = newSuplVersion; err = mApi.setSUPLVersion(mConfig.suplVersion); } else { err = LOCATION_ERROR_SUCCESS; } if (index < mCount) { errs[index++] = err; } } if (mConfig.flags & GNSS_CONFIG_FLAGS_SET_ASSISTANCE_DATA_VALID_BIT) { if (GNSS_ASSISTANCE_TYPE_SUPL == mConfig.assistanceServer.type) { char serverUrl[MAX_URL_LEN] = {}; uint32_t length = 0; const char noHost[] = "NONE"; if (NULL == mConfig.assistanceServer.hostName || strncasecmp(noHost, mConfig.assistanceServer.hostName, sizeof(noHost)) == 0) { } else { length = snprintf(serverUrl, sizeof(serverUrl), "%s:%u", mConfig.assistanceServer.hostName, mConfig.assistanceServer.port); } if (sizeof(serverUrl) > length) { err = mApi.setServer(serverUrl, length); } else { err = LOCATION_ERROR_INVALID_PARAMETER; } } else if (GNSS_ASSISTANCE_TYPE_C2K == mConfig.assistanceServer.type) { struct in_addr addr; if (!mAdapter.resolveInAddress(mConfig.assistanceServer.hostName, &addr)) { LOC_LOGE("%s]: hostName %s cannot be resolved", __func__, mConfig.assistanceServer.hostName); err = LOCATION_ERROR_INVALID_PARAMETER; } else { unsigned int ip = htonl(addr.s_addr); err = mApi.setServer(ip, mConfig.assistanceServer.port, LOC_AGPS_CDMA_PDE_SERVER); } } else { LOC_LOGE("%s]: Not a valid gnss assistance type %u", __func__, mConfig.assistanceServer.type); err = LOCATION_ERROR_INVALID_PARAMETER; } if (index < mCount) { errs[index++] = err; } } if (mConfig.flags & GNSS_CONFIG_FLAGS_LPP_PROFILE_VALID_BIT) { uint32_t newLppProfile = mAdapter.convertLppProfile(mConfig.lppProfile); if (newLppProfile != ContextBase::mGps_conf.LPP_PROFILE) { ContextBase::mGps_conf.LPP_PROFILE = newLppProfile; err = mApi.setLPPConfig(mConfig.lppProfile); } else { err = LOCATION_ERROR_SUCCESS; } if (index < mCount) { errs[index++] = err; } } if (mConfig.flags & GNSS_CONFIG_FLAGS_LPPE_CONTROL_PLANE_VALID_BIT) { uint32_t newLppeControlPlaneMask = mAdapter.convertLppeCp(mConfig.lppeControlPlaneMask); if (newLppeControlPlaneMask != ContextBase::mGps_conf.LPPE_CP_TECHNOLOGY) { ContextBase::mGps_conf.LPPE_CP_TECHNOLOGY = newLppeControlPlaneMask; err = mApi.setLPPeProtocolCp(mConfig.lppeControlPlaneMask); } else { err = LOCATION_ERROR_SUCCESS; } if (index < mCount) { errs[index++] = err; } } if (mConfig.flags & GNSS_CONFIG_FLAGS_LPPE_USER_PLANE_VALID_BIT) { uint32_t newLppeUserPlaneMask = mAdapter.convertLppeUp(mConfig.lppeUserPlaneMask); if (newLppeUserPlaneMask != ContextBase::mGps_conf.LPPE_UP_TECHNOLOGY) { ContextBase::mGps_conf.LPPE_UP_TECHNOLOGY = newLppeUserPlaneMask; err = mApi.setLPPeProtocolUp(mConfig.lppeUserPlaneMask); } else { err = LOCATION_ERROR_SUCCESS; } if (index < mCount) { errs[index++] = err; } } if (mConfig.flags & GNSS_CONFIG_FLAGS_AGLONASS_POSITION_PROTOCOL_VALID_BIT) { uint32_t newAGloProtMask = mAdapter.convertAGloProt(mConfig.aGlonassPositionProtocolMask); if (newAGloProtMask != ContextBase::mGps_conf.A_GLONASS_POS_PROTOCOL_SELECT) { ContextBase::mGps_conf.A_GLONASS_POS_PROTOCOL_SELECT = newAGloProtMask; err = mApi.setAGLONASSProtocol(mConfig.aGlonassPositionProtocolMask); } else { err = LOCATION_ERROR_SUCCESS; } if (index < mCount) { errs[index++] = err; } } if (mConfig.flags & GNSS_CONFIG_FLAGS_EM_PDN_FOR_EM_SUPL_VALID_BIT) { uint32_t newEP4ES = mAdapter.convertEP4ES(mConfig.emergencyPdnForEmergencySupl); if (newEP4ES != ContextBase::mGps_conf.USE_EMERGENCY_PDN_FOR_EMERGENCY_SUPL) { ContextBase::mGps_conf.USE_EMERGENCY_PDN_FOR_EMERGENCY_SUPL = newEP4ES; } err = LOCATION_ERROR_SUCCESS; if (index < mCount) { errs[index++] = err; } } if (mConfig.flags & GNSS_CONFIG_FLAGS_SUPL_EM_SERVICES_BIT) { uint32_t newSuplEs = mAdapter.convertSuplEs(mConfig.suplEmergencyServices); if (newSuplEs != ContextBase::mGps_conf.SUPL_ES) { ContextBase::mGps_conf.SUPL_ES = newSuplEs; } err = LOCATION_ERROR_SUCCESS; if (index < mCount) { errs[index++] = err; } } if (mConfig.flags & GNSS_CONFIG_FLAGS_SUPL_MODE_BIT) { uint32_t newSuplMode = mAdapter.convertSuplMode(mConfig.suplModeMask); if (newSuplMode != ContextBase::mGps_conf.SUPL_MODE) { ContextBase::mGps_conf.SUPL_MODE = newSuplMode; mAdapter.getUlpProxy()->setCapabilities( ContextBase::getCarrierCapabilities()); } err = LOCATION_ERROR_SUCCESS; if (index < mCount) { errs[index++] = err; } } mAdapter.reportResponse(index, errs, mIds); delete[] errs; } }; if (NULL != ids) { sendMsg(new MsgGnssUpdateConfig(*this, *mLocApi, config, ids, count)); } else { LOC_LOGE("%s]: No GNSS config items to update", __func__); } return ids; } uint32_t GnssAdapter::gnssDeleteAidingDataCommand(GnssAidingData& data) { uint32_t sessionId = generateSessionId(); LOC_LOGD("%s]: id %u", __func__, sessionId); struct MsgDeleteAidingData : public LocMsg { GnssAdapter& mAdapter; LocApiBase& mApi; uint32_t mSessionId; GnssAidingData mData; inline MsgDeleteAidingData(GnssAdapter& adapter, LocApiBase& api, uint32_t sessionId, GnssAidingData& data) : LocMsg(), mAdapter(adapter), mApi(api), mSessionId(sessionId), mData(data) {} inline virtual void proc() const { LocationError err = LOCATION_ERROR_SUCCESS; err = mApi.deleteAidingData(mData); mAdapter.reportResponse(err, mSessionId); } }; sendMsg(new MsgDeleteAidingData(*this, *mLocApi, sessionId, data)); return sessionId; } void GnssAdapter::injectLocationCommand(double latitude, double longitude, float accuracy) { LOC_LOGD("%s]: latitude %8.4f longitude %8.4f accuracy %8.4f", __func__, latitude, longitude, accuracy); struct MsgInjectLocation : public LocMsg { LocApiBase& mApi; ContextBase& mContext; double mLatitude; double mLongitude; float mAccuracy; inline MsgInjectLocation(LocApiBase& api, ContextBase& context, double latitude, double longitude, float accuracy) : LocMsg(), mApi(api), mContext(context), mLatitude(latitude), mLongitude(longitude), mAccuracy(accuracy) {} inline virtual void proc() const { if (!mContext.hasCPIExtendedCapabilities()) { mApi.injectPosition(mLatitude, mLongitude, mAccuracy); } } }; sendMsg(new MsgInjectLocation(*mLocApi, *mContext, latitude, longitude, accuracy)); } void GnssAdapter::injectTimeCommand(int64_t time, int64_t timeReference, int32_t uncertainty) { LOC_LOGD("%s]: time %lld timeReference %lld uncertainty %d", __func__, (long long)time, (long long)timeReference, uncertainty); struct MsgInjectTime : public LocMsg { LocApiBase& mApi; ContextBase& mContext; int64_t mTime; int64_t mTimeReference; int32_t mUncertainty; inline MsgInjectTime(LocApiBase& api, ContextBase& context, int64_t time, int64_t timeReference, int32_t uncertainty) : LocMsg(), mApi(api), mContext(context), mTime(time), mTimeReference(timeReference), mUncertainty(uncertainty) {} inline virtual void proc() const { mApi.setTime(mTime, mTimeReference, mUncertainty); } }; sendMsg(new MsgInjectTime(*mLocApi, *mContext, time, timeReference, uncertainty)); } void GnssAdapter::setUlpProxyCommand(UlpProxyBase* ulp) { LOC_LOGD("%s]: ", __func__); struct MsgSetUlpProxy : public LocMsg { GnssAdapter& mAdapter; UlpProxyBase* mUlp; inline MsgSetUlpProxy(GnssAdapter& adapter, UlpProxyBase* ulp) : LocMsg(), mAdapter(adapter), mUlp(ulp) {} inline virtual void proc() const { mAdapter.setUlpProxy(mUlp); } }; sendMsg(new MsgSetUlpProxy(*this, ulp)); } void GnssAdapter::setUlpProxy(UlpProxyBase* ulp) { if (ulp == mUlpProxy) { //This takes care of the case when double initalization happens //and we get the same object back for UlpProxyBase . Do nothing return; } LOC_LOGV("%s]: %p", __func__, ulp); if (NULL == ulp) { LOC_LOGE("%s]: ulp pointer is NULL", __func__); ulp = new UlpProxyBase(); } if (LOC_POSITION_MODE_INVALID != mUlpProxy->mPosMode.mode) { // need to send this mode and start msg to ULP ulp->sendFixMode(mUlpProxy->mPosMode); } if (mUlpProxy->mFixSet) { ulp->sendStartFix(); } delete mUlpProxy; mUlpProxy = ulp; } void GnssAdapter::addClientCommand(LocationAPI* client, const LocationCallbacks& callbacks) { LOC_LOGD("%s]: client %p", __func__, client); struct MsgAddClient : public LocMsg { GnssAdapter& mAdapter; LocationAPI* mClient; const LocationCallbacks mCallbacks; inline MsgAddClient(GnssAdapter& adapter, LocationAPI* client, const LocationCallbacks& callbacks) : LocMsg(), mAdapter(adapter), mClient(client), mCallbacks(callbacks) {} inline virtual void proc() const { mAdapter.saveClient(mClient, mCallbacks); } }; sendMsg(new MsgAddClient(*this, client, callbacks)); } void GnssAdapter::removeClientCommand(LocationAPI* client) { LOC_LOGD("%s]: client %p", __func__, client); struct MsgRemoveClient : public LocMsg { GnssAdapter& mAdapter; LocationAPI* mClient; inline MsgRemoveClient(GnssAdapter& adapter, LocationAPI* client) : LocMsg(), mAdapter(adapter), mClient(client) {} inline virtual void proc() const { mAdapter.stopClientSessions(mClient); mAdapter.eraseClient(mClient); } }; sendMsg(new MsgRemoveClient(*this, client)); } void GnssAdapter::stopClientSessions(LocationAPI* client) { LOC_LOGD("%s]: client %p", __func__, client); for (auto it = mTrackingSessions.begin(); it != mTrackingSessions.end();) { if (client == it->first.client) { LocationError err = stopTrackingMultiplex(it->first.client, it->first.id); if (LOCATION_ERROR_SUCCESS == err) { it = mTrackingSessions.erase(it); continue; } } ++it; // increment only when not erasing an iterator } } void GnssAdapter::updateClientsEventMask() { LOC_API_ADAPTER_EVENT_MASK_T mask = 0; for (auto it=mClientData.begin(); it != mClientData.end(); ++it) { if (it->second.trackingCb != nullptr) { mask |= LOC_API_ADAPTER_BIT_PARSED_POSITION_REPORT; } if (it->second.gnssNiCb != nullptr) { mask |= LOC_API_ADAPTER_BIT_NI_NOTIFY_VERIFY_REQUEST; } if (it->second.gnssSvCb != nullptr) { mask |= LOC_API_ADAPTER_BIT_SATELLITE_REPORT; } if (it->second.gnssNmeaCb != nullptr) { mask |= LOC_API_ADAPTER_BIT_NMEA_1HZ_REPORT; } if (it->second.gnssMeasurementsCb != nullptr) { mask |= LOC_API_ADAPTER_BIT_GNSS_MEASUREMENT; } } updateEvtMask(mask, LOC_REGISTRATION_MASK_SET); } void GnssAdapter::handleEngineUpEvent() { struct MsgRestartSessions : public LocMsg { GnssAdapter& mAdapter; inline MsgRestartSessions(GnssAdapter& adapter) : LocMsg(), mAdapter(adapter) {} virtual void proc() const { mAdapter.restartSessions(); } }; setConfigCommand(); sendMsg(new MsgRestartSessions(*this)); } void GnssAdapter::restartSessions() { LOC_LOGD("%s]: ", __func__); if (mTrackingSessions.empty()) { return; } // get the LocationOptions that has the smallest interval, which should be the active one LocationOptions smallestIntervalOptions = {}; // size is zero until set for the first time for (auto it = mTrackingSessions.begin(); it != mTrackingSessions.end(); ++it) { if (0 == smallestIntervalOptions.size || //size of zero means we havent set it yet it->second.minInterval < smallestIntervalOptions.minInterval) { smallestIntervalOptions = it->second; } } LocPosMode locPosMode = {}; convertOptions(locPosMode, smallestIntervalOptions); mLocApi->startFix(locPosMode); } void GnssAdapter::requestCapabilitiesCommand(LocationAPI* client) { LOC_LOGD("%s]: ", __func__); struct MsgRequestCapabilities : public LocMsg { GnssAdapter& mAdapter; LocApiBase& mApi; LocationAPI* mClient; inline MsgRequestCapabilities(GnssAdapter& adapter, LocApiBase& api, LocationAPI* client) : LocMsg(), mAdapter(adapter), mApi(api), mClient(client) {} inline virtual void proc() const { LocationCallbacks callbacks = mAdapter.getClientCallbacks(mClient); if (callbacks.capabilitiesCb == nullptr) { LOC_LOGE("%s]: capabilitiesCb is NULL", __func__); return; } LocationCapabilitiesMask mask = {}; // time based tracking always supported mask |= LOCATION_CAPABILITIES_TIME_BASED_TRACKING_BIT; if (mApi.isMessageSupported(LOC_API_ADAPTER_MESSAGE_DISTANCE_BASE_LOCATION_BATCHING)){ mask |= LOCATION_CAPABILITIES_TIME_BASED_BATCHING_BIT | LOCATION_CAPABILITIES_DISTANCE_BASED_BATCHING_BIT; } if (mApi.isMessageSupported(LOC_API_ADAPTER_MESSAGE_DISTANCE_BASE_TRACKING)) { mask |= LOCATION_CAPABILITIES_DISTANCE_BASED_TRACKING_BIT; } // geofence always supported mask |= LOCATION_CAPABILITIES_GEOFENCE_BIT; if (mApi.gnssConstellationConfig()) { mask |= LOCATION_CAPABILITIES_GNSS_MEASUREMENTS_BIT; } uint32_t carrierCapabilities = ContextBase::getCarrierCapabilities(); if (carrierCapabilities & LOC_GPS_CAPABILITY_MSB) { mask |= LOCATION_CAPABILITIES_GNSS_MSB_BIT; } if (LOC_GPS_CAPABILITY_MSA & carrierCapabilities) { mask |= LOCATION_CAPABILITIES_GNSS_MSA_BIT; } callbacks.capabilitiesCb(mask); } }; sendMsg(new MsgRequestCapabilities(*this, *mLocApi, client)); } LocationCallbacks GnssAdapter::getClientCallbacks(LocationAPI* client) { LocationCallbacks callbacks = {}; auto it = mClientData.find(client); if (it != mClientData.end()) { callbacks = it->second; } return callbacks; } void GnssAdapter::saveClient(LocationAPI* client, const LocationCallbacks& callbacks) { mClientData[client] = callbacks; updateClientsEventMask(); } void GnssAdapter::eraseClient(LocationAPI* client) { auto it = mClientData.find(client); if (it != mClientData.end()) { mClientData.erase(it); } updateClientsEventMask(); } bool GnssAdapter::hasTrackingCallback(LocationAPI* client) { auto it = mClientData.find(client); return (it != mClientData.end() && it->second.trackingCb); } bool GnssAdapter::hasMeasurementsCallback(LocationAPI* client) { auto it = mClientData.find(client); return (it != mClientData.end() && it->second.gnssMeasurementsCb); } bool GnssAdapter::isTrackingSession(LocationAPI* client, uint32_t sessionId) { LocationSessionKey key(client, sessionId); return (mTrackingSessions.find(key) != mTrackingSessions.end()); } void GnssAdapter::saveTrackingSession(LocationAPI* client, uint32_t sessionId, const LocationOptions& options) { LocationSessionKey key(client, sessionId); mTrackingSessions[key] = options; } void GnssAdapter::eraseTrackingSession(LocationAPI* client, uint32_t sessionId) { LocationSessionKey key(client, sessionId); auto it = mTrackingSessions.find(key); if (it != mTrackingSessions.end()) { mTrackingSessions.erase(it); } } void GnssAdapter::reportResponse(LocationAPI* client, LocationError err, uint32_t sessionId) { LOC_LOGD("%s]: client %p id %u err %u", __func__, client, sessionId, err); auto it = mClientData.find(client); if (it != mClientData.end() && it->second.responseCb != nullptr) { it->second.responseCb(err, sessionId); } else { LOC_LOGW("%s]: client %p id %u not found in data", __func__, client, sessionId); } } void GnssAdapter::reportResponse(LocationError err, uint32_t sessionId) { LOC_LOGD("%s]: id %u err %u", __func__, sessionId, err); if (mControlCallbacks.size > 0 && mControlCallbacks.responseCb != nullptr) { mControlCallbacks.responseCb(err, sessionId); } else { LOC_LOGW("%s]: control client response callback not found", __func__); } } void GnssAdapter::reportResponse(size_t count, LocationError* errs, uint32_t* ids) { IF_LOC_LOGD { std::string idsString = "["; std::string errsString = "["; if (NULL != ids && NULL != errs) { for (size_t i=0; i < count; ++i) { idsString += std::to_string(ids[i]) + " "; errsString += std::to_string(errs[i]) + " "; } } idsString += "]"; errsString += "]"; LOC_LOGD("%s]: ids %s errs %s", __func__, idsString.c_str(), errsString.c_str()); } if (mControlCallbacks.size > 0 && mControlCallbacks.collectiveResponseCb != nullptr) { mControlCallbacks.collectiveResponseCb(count, errs, ids); } else { LOC_LOGW("%s]: control client callback not found", __func__); } } uint32_t GnssAdapter::startTrackingCommand(LocationAPI* client, LocationOptions& options) { uint32_t sessionId = generateSessionId(); LOC_LOGD("%s]: client %p id %u minInterval %u mode %u", __func__, client, sessionId, options.minInterval, options.mode); struct MsgStartTracking : public LocMsg { GnssAdapter& mAdapter; LocApiBase& mApi; LocationAPI* mClient; uint32_t mSessionId; LocationOptions mOptions; inline MsgStartTracking(GnssAdapter& adapter, LocApiBase& api, LocationAPI* client, uint32_t sessionId, LocationOptions options) : LocMsg(), mAdapter(adapter), mApi(api), mClient(client), mSessionId(sessionId), mOptions(options) {} inline virtual void proc() const { LocationError err = LOCATION_ERROR_SUCCESS; if (!mAdapter.hasTrackingCallback(mClient) && !mAdapter.hasMeasurementsCallback(mClient)) { err = LOCATION_ERROR_CALLBACK_MISSING; } else if (0 == mOptions.size) { err = LOCATION_ERROR_INVALID_PARAMETER; } else { // Api doesn't support multiple clients for time based tracking, so mutiplex err = mAdapter.startTrackingMultiplex(mOptions); if (LOCATION_ERROR_SUCCESS == err) { mAdapter.saveTrackingSession(mClient, mSessionId, mOptions); } } mAdapter.reportResponse(mClient, err, mSessionId); } }; sendMsg(new MsgStartTracking(*this, *mLocApi, client, sessionId, options)); return sessionId; } LocationError GnssAdapter::startTrackingMultiplex(const LocationOptions& options) { LocationError err = LOCATION_ERROR_SUCCESS; if (mTrackingSessions.empty()) { err = startTracking(options); } else { // get the LocationOptions that has the smallest interval, which should be the active one LocationOptions smallestIntervalOptions = {}; // size is zero until set for the first time for (auto it = mTrackingSessions.begin(); it != mTrackingSessions.end(); ++it) { if (0 == smallestIntervalOptions.size || //size of zero means we havent set it yet it->second.minInterval < smallestIntervalOptions.minInterval) { smallestIntervalOptions = it->second; } } // if new session's minInterval is smaller than any in other sessions if (options.minInterval < smallestIntervalOptions.minInterval) { // restart time based tracking with new options err = startTracking(options); } } return err; } LocationError GnssAdapter::startTracking(const LocationOptions& options) { LocationError err = LOCATION_ERROR_SUCCESS; LocPosMode locPosMode = {}; convertOptions(locPosMode, options); if (!mUlpProxy->sendFixMode(locPosMode)) { // do nothing } if (!mUlpProxy->sendStartFix()) { loc_api_adapter_err apiErr = mLocApi->startFix(locPosMode); if (LOC_API_ADAPTER_ERR_SUCCESS == apiErr) { err = LOCATION_ERROR_SUCCESS; } else { err = LOCATION_ERROR_GENERAL_FAILURE; } } return err; } void GnssAdapter::setPositionModeCommand(LocPosMode& locPosMode) { LOC_LOGD("%s]: min_interval %u mode %u", __func__, locPosMode.min_interval, locPosMode.mode); struct MsgSetPositionMode : public LocMsg { GnssAdapter& mAdapter; LocApiBase& mApi; LocPosMode mLocPosMode; inline MsgSetPositionMode(GnssAdapter& adapter, LocApiBase& api, LocPosMode& locPosMode) : LocMsg(), mAdapter(adapter), mApi(api), mLocPosMode(locPosMode) {} inline virtual void proc() const { // saves the mode in adapter to be used when startTrackingCommand is called from ULP mAdapter.setUlpPositionMode(mLocPosMode); mApi.setPositionMode(mLocPosMode); } }; sendMsg(new MsgSetPositionMode(*this, *mLocApi, locPosMode)); } void GnssAdapter::startTrackingCommand() { LOC_LOGD("%s]: ", __func__); struct MsgStartTracking : public LocMsg { GnssAdapter& mAdapter; LocApiBase& mApi; inline MsgStartTracking(GnssAdapter& adapter, LocApiBase& api) : LocMsg(), mAdapter(adapter), mApi(api) {} inline virtual void proc() const { // we get this call from ULP, so just call LocApi without multiplexing because // ulp would be doing the multiplexing for us if it is present LocPosMode& ulpPositionMode = mAdapter.getUlpPositionMode(); mApi.startFix(ulpPositionMode); } }; sendMsg(new MsgStartTracking(*this, *mLocApi)); } void GnssAdapter::updateTrackingOptionsCommand(LocationAPI* client, uint32_t id, LocationOptions& options) { LOC_LOGD("%s]: client %p id %u minInterval %u mode %u", __func__, client, id, options.minInterval, options.mode); struct MsgUpdateTracking : public LocMsg { GnssAdapter& mAdapter; LocApiBase& mApi; LocationAPI* mClient; uint32_t mSessionId; LocationOptions mOptions; inline MsgUpdateTracking(GnssAdapter& adapter, LocApiBase& api, LocationAPI* client, uint32_t sessionId, LocationOptions options) : LocMsg(), mAdapter(adapter), mApi(api), mClient(client), mSessionId(sessionId), mOptions(options) {} inline virtual void proc() const { if (mAdapter.isTrackingSession(mClient, mSessionId)) { LocationError err = LOCATION_ERROR_SUCCESS; if (0 == mOptions.size) { err = LOCATION_ERROR_INVALID_PARAMETER; } else { // Api doesn't support multiple clients for time based tracking, so mutiplex err = mAdapter.updateTrackingMultiplex(mClient, mSessionId, mOptions); if (LOCATION_ERROR_SUCCESS == err) { mAdapter.saveTrackingSession(mClient, mSessionId, mOptions); } } mAdapter.reportResponse(mClient, err, mSessionId); } // we do not reportResponse for the case where there is no existing tracking session // for the client and id being used, since updateTrackingCommand can be sent to both // GnssAdapter & FlpAdapter by LocationAPI and we want to avoid incorrect error response } }; sendMsg(new MsgUpdateTracking(*this, *mLocApi, client, id, options)); } LocationError GnssAdapter::updateTrackingMultiplex(LocationAPI* client, uint32_t id, const LocationOptions& options) { LocationError err = LOCATION_ERROR_SUCCESS; if (1 == mTrackingSessions.size()) { err = startTracking(options); } else { LocationSessionKey key(client, id); // get the session we are updating auto it = mTrackingSessions.find(key); if (it != mTrackingSessions.end()) { // find the smallest interval, other than the session we are updating LocationOptions smallestIntervalOptions = {}; // size is 0 until set for the first time for (auto it2 = mTrackingSessions.begin(); it2 != mTrackingSessions.end(); ++it2) { // if session is not the one we are updating and either smallest interval is not set // or there is a new smallest interval, then set the new smallest interval if (it2->first != key && (0 == smallestIntervalOptions.size || it2->second.minInterval < smallestIntervalOptions.minInterval)) { smallestIntervalOptions = it2->second; } } // if session we are updating has smaller interval then next smallest if (options.minInterval < smallestIntervalOptions.minInterval) { // restart time based tracking with the newly updated interval err = startTracking(options); // else if the session we are updating used to be the smallest } else if (it->second.minInterval < smallestIntervalOptions.minInterval) { // restart time based tracking with the next smallest err = startTracking(smallestIntervalOptions); } } } return err; } void GnssAdapter::stopTrackingCommand(LocationAPI* client, uint32_t id) { LOC_LOGD("%s]: client %p id %u", __func__, client, id); struct MsgStopTracking : public LocMsg { GnssAdapter& mAdapter; LocApiBase& mApi; LocationAPI* mClient; uint32_t mSessionId; inline MsgStopTracking(GnssAdapter& adapter, LocApiBase& api, LocationAPI* client, uint32_t sessionId) : LocMsg(), mAdapter(adapter), mApi(api), mClient(client), mSessionId(sessionId) {} inline virtual void proc() const { if (mAdapter.isTrackingSession(mClient, mSessionId)) { LocationError err = LOCATION_ERROR_SUCCESS; // Api doesn't support multiple clients for time based tracking, so mutiplex err = mAdapter.stopTrackingMultiplex(mClient, mSessionId); if (LOCATION_ERROR_SUCCESS == err) { mAdapter.eraseTrackingSession(mClient, mSessionId); } mAdapter.reportResponse(mClient, err, mSessionId); } // we do not reportResponse for the case where there is no existing tracking session // for the client and id being used, since stopTrackingCommand can be sent to both // GnssAdapter & FlpAdapter by LocationAPI and we want to avoid incorrect error response } }; sendMsg(new MsgStopTracking(*this, *mLocApi, client, id)); } LocationError GnssAdapter::stopTrackingMultiplex(LocationAPI* client, uint32_t id) { LocationError err = LOCATION_ERROR_SUCCESS; if (1 == mTrackingSessions.size()) { err = stopTracking(); } else { LocationSessionKey key(client, id); // get the session we are stopping auto it = mTrackingSessions.find(key); if (it != mTrackingSessions.end()) { // find the next smallest interval, other than the session we are stopping LocationOptions smallestIntervalOptions = {}; // size is 0 until set for the first time for (auto it2 = mTrackingSessions.begin(); it2 != mTrackingSessions.end(); ++it2) { // if session is not the one we are stopping and either smallest interval is not set // or there is a new smallest interval, then set the new smallest interval if (it2->first != key && (0 == smallestIntervalOptions.size || it2->second.minInterval < smallestIntervalOptions.minInterval)) { smallestIntervalOptions = it2->second; } } // if session we are stopping has smaller interval then next smallest if (it->second.minInterval < smallestIntervalOptions.minInterval) { // restart time based tracking with next smallest interval err = startTracking(smallestIntervalOptions); } } } return err; } LocationError GnssAdapter::stopTracking() { LocationError err = LOCATION_ERROR_SUCCESS; if (!mUlpProxy->sendStopFix()) { loc_api_adapter_err apiErr = mLocApi->stopFix(); if (LOC_API_ADAPTER_ERR_SUCCESS == apiErr) { err = LOCATION_ERROR_SUCCESS; } else { err = LOCATION_ERROR_GENERAL_FAILURE; } } return err; } void GnssAdapter::stopTrackingCommand() { LOC_LOGD("%s]: ", __func__); struct MsgStopTracking : public LocMsg { GnssAdapter& mAdapter; LocApiBase& mApi; inline MsgStopTracking(GnssAdapter& adapter, LocApiBase& api) : LocMsg(), mAdapter(adapter), mApi(api) {} inline virtual void proc() const { // clear the position mode LocPosMode mLocPosMode = {}; mLocPosMode.mode = LOC_POSITION_MODE_INVALID; mAdapter.setUlpPositionMode(mLocPosMode); // don't need to multiplex because ULP will do that for us if it is present mApi.stopFix(); } }; sendMsg(new MsgStopTracking(*this, *mLocApi)); } void GnssAdapter::getZppCommand() { LOC_LOGD("%s]: ", __func__); struct MsgGetZpp : public LocMsg { GnssAdapter& mAdapter; LocApiBase& mApi; inline MsgGetZpp(GnssAdapter& adapter, LocApiBase& api) : LocMsg(), mAdapter(adapter), mApi(api) {} inline virtual void proc() const { UlpLocation location = {}; LocPosTechMask techMask = LOC_POS_TECH_MASK_DEFAULT; GpsLocationExtended locationExtended = {}; locationExtended.size = sizeof(locationExtended); mApi.getBestAvailableZppFix(location.gpsLocation, techMask); //Mark the location source as from ZPP location.gpsLocation.flags |= LOCATION_HAS_SOURCE_INFO; location.position_source = ULP_LOCATION_IS_FROM_ZPP; mAdapter.getUlpProxy()->reportPosition(location, locationExtended, LOC_SESS_SUCCESS, techMask); } }; sendMsg(new MsgGetZpp(*this, *mLocApi)); } bool GnssAdapter::hasNiNotifyCallback(LocationAPI* client) { auto it = mClientData.find(client); return (it != mClientData.end() && it->second.gnssNiCb); } void GnssAdapter::gnssNiResponseCommand(LocationAPI* client, uint32_t id, GnssNiResponse response) { LOC_LOGD("%s]: client %p id %u response %u", __func__, client, id, response); struct MsgGnssNiResponse : public LocMsg { GnssAdapter& mAdapter; LocationAPI* mClient; uint32_t mSessionId; GnssNiResponse mResponse; inline MsgGnssNiResponse(GnssAdapter& adapter, LocationAPI* client, uint32_t sessionId, GnssNiResponse response) : LocMsg(), mAdapter(adapter), mClient(client), mSessionId(sessionId), mResponse(response) {} inline virtual void proc() const { NiData& niData = mAdapter.getNiData(); LocationError err = LOCATION_ERROR_SUCCESS; if (!mAdapter.hasNiNotifyCallback(mClient)) { err = LOCATION_ERROR_ID_UNKNOWN; } else { NiSession* pSession = NULL; if (mSessionId == niData.sessionEs.reqID && NULL != niData.sessionEs.rawRequest) { pSession = &niData.sessionEs; // ignore any SUPL NI non-Es session if a SUPL NI ES is accepted if (mResponse == GNSS_NI_RESPONSE_ACCEPT && NULL != niData.session.rawRequest) { pthread_mutex_lock(&niData.session.tLock); niData.session.resp = GNSS_NI_RESPONSE_IGNORE; niData.session.respRecvd = true; pthread_cond_signal(&niData.session.tCond); pthread_mutex_unlock(&niData.session.tLock); } } else if (mSessionId == niData.session.reqID && NULL != niData.session.rawRequest) { pSession = &niData.session; } if (pSession) { LOC_LOGI("%s]: gnssNiResponseCommand: send user mResponse %u for id %u", __func__, mResponse, mSessionId); pthread_mutex_lock(&pSession->tLock); pSession->resp = mResponse; pSession->respRecvd = true; pthread_cond_signal(&pSession->tCond); pthread_mutex_unlock(&pSession->tLock); } else { err = LOCATION_ERROR_ID_UNKNOWN; LOC_LOGE("%s]: gnssNiResponseCommand: id %u not an active session", __func__, mSessionId); } } mAdapter.reportResponse(mClient, err, mSessionId); } }; sendMsg(new MsgGnssNiResponse(*this, client, id, response)); } void GnssAdapter::gnssNiResponseCommand(GnssNiResponse response, void* rawRequest) { LOC_LOGD("%s]: response %u", __func__, response); struct MsgGnssNiResponse : public LocMsg { LocApiBase& mApi; const GnssNiResponse mResponse; const void* mPayload; inline MsgGnssNiResponse(LocApiBase& api, const GnssNiResponse response, const void* rawRequest) : LocMsg(), mApi(api), mResponse(response), mPayload(rawRequest) {} inline virtual ~MsgGnssNiResponse() { // this is a bit weird since mPayload is not // allocated by this class. But there is no better way. // mPayload actually won't be NULL here. free((void*)mPayload); } inline virtual void proc() const { mApi.informNiResponse(mResponse, mPayload); } }; sendMsg(new MsgGnssNiResponse(*mLocApi, response, rawRequest)); } uint32_t GnssAdapter::enableCommand(LocationTechnologyType techType) { uint32_t sessionId = generateSessionId(); LOC_LOGD("%s]: id %u techType %u", __func__, sessionId, techType); struct MsgEnableGnss : public LocMsg { GnssAdapter& mAdapter; LocApiBase& mApi; ContextBase& mContext; uint32_t mSessionId; LocationTechnologyType mTechType; inline MsgEnableGnss(GnssAdapter& adapter, LocApiBase& api, ContextBase& context, uint32_t sessionId, LocationTechnologyType techType) : LocMsg(), mAdapter(adapter), mApi(api), mContext(context), mSessionId(sessionId), mTechType(techType) {} inline virtual void proc() const { LocationError err = LOCATION_ERROR_SUCCESS; uint32_t powerVoteId = mAdapter.getPowerVoteId(); if (mTechType != LOCATION_TECHNOLOGY_TYPE_GNSS) { err = LOCATION_ERROR_INVALID_PARAMETER; } else if (powerVoteId > 0) { err = LOCATION_ERROR_ALREADY_STARTED; } else { mContext.modemPowerVote(true); mAdapter.setPowerVoteId(mSessionId); mApi.setGpsLock(GNSS_CONFIG_GPS_LOCK_NONE); } mAdapter.reportResponse(err, mSessionId); } }; if (mContext != NULL) { sendMsg(new MsgEnableGnss(*this, *mLocApi, *mContext, sessionId, techType)); } else { LOC_LOGE("%s]: Context is NULL", __func__); } return sessionId; } void GnssAdapter::disableCommand(uint32_t id) { LOC_LOGD("%s]: id %u", __func__, id); struct MsgDisableGnss : public LocMsg { GnssAdapter& mAdapter; LocApiBase& mApi; ContextBase& mContext; uint32_t mSessionId; inline MsgDisableGnss(GnssAdapter& adapter, LocApiBase& api, ContextBase& context, uint32_t sessionId) : LocMsg(), mAdapter(adapter), mApi(api), mContext(context), mSessionId(sessionId) {} inline virtual void proc() const { LocationError err = LOCATION_ERROR_SUCCESS; uint32_t powerVoteId = mAdapter.getPowerVoteId(); if (powerVoteId != mSessionId) { err = LOCATION_ERROR_ID_UNKNOWN; } else { mContext.modemPowerVote(false); mAdapter.setPowerVoteId(0); mApi.setGpsLock(mAdapter.convertGpsLock(ContextBase::mGps_conf.GPS_LOCK)); } mAdapter.reportResponse(err, mSessionId); } }; if (mContext != NULL) { sendMsg(new MsgDisableGnss(*this, *mLocApi, *mContext, id)); } } void GnssAdapter::reportPositionEvent(const UlpLocation& ulpLocation, const GpsLocationExtended& locationExtended, enum loc_sess_status status, LocPosTechMask techMask, bool fromUlp) { LOC_LOGD("%s]: fromUlp %u status %u", __func__, fromUlp, status); // if this event is not called from ULP, then try to call into ULP and return if successfull if (!fromUlp) { if (mUlpProxy->reportPosition(ulpLocation, locationExtended, status, techMask)) { return; } } struct MsgReportPosition : public LocMsg { GnssAdapter& mAdapter; const UlpLocation mUlpLocation; const GpsLocationExtended mLocationExtended; loc_sess_status mStatus; LocPosTechMask mTechMask; inline MsgReportPosition(GnssAdapter& adapter, const UlpLocation& ulpLocation, const GpsLocationExtended& locationExtended, loc_sess_status status, LocPosTechMask techMask) : LocMsg(), mAdapter(adapter), mUlpLocation(ulpLocation), mLocationExtended(locationExtended), mStatus(status), mTechMask(techMask) {} inline virtual void proc() const { // extract bug report info - this returns true if consumed by systemstatus SystemStatus* s = LocDualContext::getSystemStatus(); if ((nullptr != s) && (LOC_SESS_SUCCESS == mStatus)){ s->eventPosition(mUlpLocation, mLocationExtended); } mAdapter.reportPosition(mUlpLocation, mLocationExtended, mStatus, mTechMask); } }; sendMsg(new MsgReportPosition(*this, ulpLocation, locationExtended, status, techMask)); } void GnssAdapter::reportPosition(const UlpLocation& ulpLocation, const GpsLocationExtended& locationExtended, enum loc_sess_status status, LocPosTechMask techMask) { bool reported = false; // what's in the if is... (line by line) // 1. this is a final fix; and // 1.1 it is a Satellite fix; or // 1.2 it is a sensor fix // 2. (must be intermediate fix... implicit) // 2.1 we accepte intermediate; and // 2.2 it is NOT the case that // 2.2.1 there is inaccuracy; and // 2.2.2 we care about inaccuracy; and // 2.2.3 the inaccuracy exceeds our tolerance if ((LOC_SESS_SUCCESS == status && ((LOC_POS_TECH_MASK_SATELLITE | LOC_POS_TECH_MASK_SENSORS | LOC_POS_TECH_MASK_HYBRID) & techMask)) || (LOC_SESS_INTERMEDIATE == ContextBase::mGps_conf.INTERMEDIATE_POS && !((ulpLocation.gpsLocation.flags & LOC_GPS_LOCATION_HAS_ACCURACY) && (ContextBase::mGps_conf.ACCURACY_THRES != 0) && (ulpLocation.gpsLocation.accuracy > ContextBase::mGps_conf.ACCURACY_THRES)))) { if (locationExtended.flags & GPS_LOCATION_EXTENDED_HAS_GNSS_SV_USED_DATA) { mGnssSvIdUsedInPosAvail = true; mGnssSvIdUsedInPosition = locationExtended.gnss_sv_used_ids; } for (auto it=mClientData.begin(); it != mClientData.end(); ++it) { if (nullptr != it->second.trackingCb) { Location location = {}; convertLocation(location, ulpLocation.gpsLocation, locationExtended, techMask); it->second.trackingCb(location); } if (nullptr != it->second.gnssLocationInfoCb) { GnssLocationInfoNotification locationInfo = {}; convertLocationInfo(locationInfo, locationExtended); it->second.gnssLocationInfoCb(locationInfo); } } reported = true; } else { LOC_LOGI("%s: not reported. Status: %d, techMask: %d, flags %d, accuracy %f", __func__, (int)status, (int)techMask, (int)ulpLocation.gpsLocation.flags, (float)ulpLocation.gpsLocation.accuracy); } if (NMEA_PROVIDER_AP == ContextBase::mGps_conf.NMEA_PROVIDER && !mTrackingSessions.empty()) { uint8_t generate_nmea = (reported && status != LOC_SESS_FAILURE); std::vector nmeaArraystr; loc_nmea_generate_pos(ulpLocation, locationExtended, generate_nmea, nmeaArraystr); for (auto sentence : nmeaArraystr) { reportNmea(sentence.c_str(), sentence.length()); } } // Free the allocated memory for rawData UlpLocation* gp = (UlpLocation*)&(ulpLocation); if (gp != NULL && gp->rawData != NULL) { delete (char*)gp->rawData; gp->rawData = NULL; gp->rawDataSize = 0; } } void GnssAdapter::reportSvEvent(const GnssSvNotification& svNotify, bool fromUlp) { LOC_LOGD("%s]: fromUlp %u", __func__, fromUlp); // if this event is not called from ULP, then try to call into ULP and return if successfull if (!fromUlp) { if (mUlpProxy->reportSv(svNotify)) { return; } } struct MsgReportSv : public LocMsg { GnssAdapter& mAdapter; const GnssSvNotification mSvNotify; inline MsgReportSv(GnssAdapter& adapter, const GnssSvNotification& svNotify) : LocMsg(), mAdapter(adapter), mSvNotify(svNotify) {} inline virtual void proc() const { mAdapter.reportSv((GnssSvNotification&)mSvNotify); } }; sendMsg(new MsgReportSv(*this, svNotify)); } void GnssAdapter::reportSv(GnssSvNotification& svNotify) { int numSv = svNotify.count; int16_t gnssSvId = 0; uint64_t svUsedIdMask = 0; for (int i=0; i < numSv; i++) { svUsedIdMask = 0; gnssSvId = svNotify.gnssSvs[i].svId; switch (svNotify.gnssSvs[i].type) { case GNSS_SV_TYPE_GPS: if (mGnssSvIdUsedInPosAvail) { svUsedIdMask = mGnssSvIdUsedInPosition.gps_sv_used_ids_mask; } break; case GNSS_SV_TYPE_GLONASS: if (mGnssSvIdUsedInPosAvail) { svUsedIdMask = mGnssSvIdUsedInPosition.glo_sv_used_ids_mask; } break; case GNSS_SV_TYPE_BEIDOU: if (mGnssSvIdUsedInPosAvail) { svUsedIdMask = mGnssSvIdUsedInPosition.bds_sv_used_ids_mask; } break; case GNSS_SV_TYPE_GALILEO: if (mGnssSvIdUsedInPosAvail) { svUsedIdMask = mGnssSvIdUsedInPosition.gal_sv_used_ids_mask; } break; case GNSS_SV_TYPE_QZSS: if (mGnssSvIdUsedInPosAvail) { svUsedIdMask = mGnssSvIdUsedInPosition.qzss_sv_used_ids_mask; } // QZSS SV id's need to reported as it is to framework, since // framework expects it as it is. See GnssStatus.java. // SV id passed to here by LocApi is 1-based. svNotify.gnssSvs[i].svId += (QZSS_SV_PRN_MIN - 1); break; default: svUsedIdMask = 0; break; } // If SV ID was used in previous position fix, then set USED_IN_FIX // flag, else clear the USED_IN_FIX flag. if (svUsedIdMask & (1 << (gnssSvId - 1))) { svNotify.gnssSvs[i].gnssSvOptionsMask |= GNSS_SV_OPTIONS_USED_IN_FIX_BIT; } } for (auto it=mClientData.begin(); it != mClientData.end(); ++it) { if (nullptr != it->second.gnssSvCb) { it->second.gnssSvCb(svNotify); } } if (NMEA_PROVIDER_AP == ContextBase::mGps_conf.NMEA_PROVIDER && !mTrackingSessions.empty()) { std::vector nmeaArraystr; loc_nmea_generate_sv(svNotify, nmeaArraystr); for (auto sentence : nmeaArraystr) { reportNmea(sentence.c_str(), sentence.length()); } } mGnssSvIdUsedInPosAvail = false; } void GnssAdapter::reportNmeaEvent(const char* nmea, size_t length, bool fromUlp) { // if this event is not called from ULP, then try to call into ULP and return if successfull if (!fromUlp && !loc_nmea_is_debug(nmea, length)) { if (mUlpProxy->reportNmea(nmea, length)) { return; } } struct MsgReportNmea : public LocMsg { GnssAdapter& mAdapter; const char* mNmea; size_t mLength; inline MsgReportNmea(GnssAdapter& adapter, const char* nmea, size_t length) : LocMsg(), mAdapter(adapter), mNmea(new char[length+1]), mLength(length) { strlcpy((char*)mNmea, nmea, length+1); } inline virtual ~MsgReportNmea() { delete[] mNmea; } inline virtual void proc() const { // extract bug report info - this returns true if consumed by systemstatus bool ret = false; SystemStatus* s = LocDualContext::getSystemStatus(); if (nullptr != s) { ret = s->setNmeaString(mNmea, mLength); } if (false == ret) { // forward NMEA message to upper layer mAdapter.reportNmea(mNmea, mLength); } } }; sendMsg(new MsgReportNmea(*this, nmea, length)); } void GnssAdapter::reportNmea(const char* nmea, size_t length) { GnssNmeaNotification nmeaNotification = {}; nmeaNotification.size = sizeof(GnssNmeaNotification); struct timeval tv; gettimeofday(&tv, (struct timezone *) NULL); int64_t now = tv.tv_sec * 1000LL + tv.tv_usec / 1000; nmeaNotification.timestamp = now; nmeaNotification.nmea = nmea; nmeaNotification.length = length; for (auto it=mClientData.begin(); it != mClientData.end(); ++it) { if (nullptr != it->second.gnssNmeaCb) { it->second.gnssNmeaCb(nmeaNotification); } } } bool GnssAdapter::requestNiNotifyEvent(const GnssNiNotification ¬ify, const void* data) { LOC_LOGI("%s]: notif_type: %d, timeout: %d, default_resp: %d" "requestor_id: %s (encoding: %d) text: %s text (encoding: %d) extras: %s", __func__, notify.type, notify.timeout, notify.timeoutResponse, notify.requestor, notify.requestorEncoding, notify.message, notify.messageEncoding, notify.extras); struct MsgReportNiNotify : public LocMsg { GnssAdapter& mAdapter; const GnssNiNotification mNotify; const void* mData; inline MsgReportNiNotify(GnssAdapter& adapter, const GnssNiNotification& notify, const void* data) : LocMsg(), mAdapter(adapter), mNotify(notify), mData(data) {} inline virtual void proc() const { mAdapter.requestNiNotify(mNotify, mData); } }; sendMsg(new MsgReportNiNotify(*this, notify, data)); return true; } static void* niThreadProc(void *args) { NiSession* pSession = (NiSession*)args; int rc = 0; /* return code from pthread calls */ struct timeval present_time; struct timespec expire_time; pthread_mutex_lock(&pSession->tLock); /* Calculate absolute expire time */ gettimeofday(&present_time, NULL); expire_time.tv_sec = present_time.tv_sec + pSession->respTimeLeft; expire_time.tv_nsec = present_time.tv_usec * 1000; LOC_LOGD("%s]: time out set for abs time %ld with delay %d sec", __func__, (long)expire_time.tv_sec, pSession->respTimeLeft); while (!pSession->respRecvd) { rc = pthread_cond_timedwait(&pSession->tCond, &pSession->tLock, &expire_time); if (rc == ETIMEDOUT) { pSession->resp = GNSS_NI_RESPONSE_NO_RESPONSE; LOC_LOGD("%s]: time out after valting for specified time. Ret Val %d", __func__, rc); break; } } LOC_LOGD("%s]: Java layer has sent us a user response and return value from " "pthread_cond_timedwait = %d pSession->resp is %u", __func__, rc, pSession->resp); pSession->respRecvd = false; /* Reset the user response flag for the next session*/ // adding this check to support modem restart, in which case, we need the thread // to exit without calling sending data. We made sure that rawRequest is NULL in // loc_eng_ni_reset_on_engine_restart() GnssAdapter* adapter = pSession->adapter; GnssNiResponse resp; void* rawRequest = NULL; bool sendResponse = false; if (NULL != pSession->rawRequest) { if (pSession->resp != GNSS_NI_RESPONSE_IGNORE) { resp = pSession->resp; rawRequest = pSession->rawRequest; sendResponse = true; } else { free(pSession->rawRequest); } pSession->rawRequest = NULL; } pthread_mutex_unlock(&pSession->tLock); pSession->respTimeLeft = 0; pSession->reqID = 0; if (sendResponse) { adapter->gnssNiResponseCommand(resp, rawRequest); } return NULL; } bool GnssAdapter::requestNiNotify(const GnssNiNotification& notify, const void* data) { NiSession* pSession = NULL; gnssNiCallback gnssNiCb = nullptr; for (auto it=mClientData.begin(); it != mClientData.end(); ++it) { if (nullptr != it->second.gnssNiCb) { gnssNiCb = it->second.gnssNiCb; break; } } if (nullptr == gnssNiCb) { EXIT_LOG(%s, "no clients with gnssNiCb."); return false; } if (notify.type == GNSS_NI_TYPE_EMERGENCY_SUPL) { if (NULL != mNiData.sessionEs.rawRequest) { LOC_LOGI("%s]: supl es NI in progress, new supl es NI ignored, type: %d", __func__, notify.type); if (NULL != data) { free((void*)data); } } else { pSession = &mNiData.sessionEs; } } else { if (NULL != mNiData.session.rawRequest || NULL != mNiData.sessionEs.rawRequest) { LOC_LOGI("%s]: supl NI in progress, new supl NI ignored, type: %d", __func__, notify.type); if (NULL != data) { free((void*)data); } } else { pSession = &mNiData.session; } } if (pSession) { /* Save request */ pSession->rawRequest = (void*)data; pSession->reqID = ++mNiData.reqIDCounter; pSession->adapter = this; int sessionId = pSession->reqID; /* For robustness, spawn a thread at this point to timeout to clear up the notification * status, even though the OEM layer in java does not do so. **/ pSession->respTimeLeft = 5 + (notify.timeout != 0 ? notify.timeout : LOC_NI_NO_RESPONSE_TIME); int rc = 0; rc = pthread_create(&pSession->thread, NULL, niThreadProc, pSession); if (rc) { LOC_LOGE("%s]: Loc NI thread is not created.", __func__); } rc = pthread_detach(pSession->thread); if (rc) { LOC_LOGE("%s]: Loc NI thread is not detached.", __func__); } if (nullptr != gnssNiCb) { gnssNiCb(sessionId, notify); } } return true; } void GnssAdapter::reportGnssMeasurementDataEvent(const GnssMeasurementsNotification& measurementsNotify) { LOC_LOGD("%s]: ", __func__); struct MsgReportGnssMeasurementData : public LocMsg { GnssAdapter& mAdapter; const GnssMeasurementsNotification mMeasurementsNotify; inline MsgReportGnssMeasurementData(GnssAdapter& adapter, const GnssMeasurementsNotification& measurementsNotify) : LocMsg(), mAdapter(adapter), mMeasurementsNotify(measurementsNotify) {} inline virtual void proc() const { mAdapter.reportGnssMeasurementData(mMeasurementsNotify); } }; sendMsg(new MsgReportGnssMeasurementData(*this, measurementsNotify)); } void GnssAdapter::reportGnssMeasurementData(const GnssMeasurementsNotification& measurementsNotify) { for (auto it=mClientData.begin(); it != mClientData.end(); ++it) { if (nullptr != it->second.gnssMeasurementsCb) { it->second.gnssMeasurementsCb(measurementsNotify); } } } void GnssAdapter::reportSvMeasurementEvent(GnssSvMeasurementSet &svMeasurementSet) { LOC_LOGD("%s]: ", __func__); // We send SvMeasurementSet to AmtProxy/ULPProxy to be forwarded as necessary. mUlpProxy->reportSvMeasurement(svMeasurementSet); } void GnssAdapter::reportSvPolynomialEvent(GnssSvPolynomial &svPolynomial) { LOC_LOGD("%s]: ", __func__); // We send SvMeasurementSet to AmtProxy/ULPProxy to be forwarded as necessary. mUlpProxy->reportSvPolynomial(svPolynomial); } /* INIT LOC AGPS MANAGER */ void GnssAdapter::initAgpsCommand(void* statusV4Cb){ LOC_LOGI("GnssAdapter::initAgpsCommand"); /* Set ATL open/close callbacks */ AgpsAtlOpenStatusCb atlOpenStatusCb = [this](int handle, int isSuccess, char* apn, AGpsBearerType bearerType, AGpsExtType agpsType) { mLocApi->atlOpenStatus( handle, isSuccess, apn, bearerType, agpsType); }; AgpsAtlCloseStatusCb atlCloseStatusCb = [this](int handle, int isSuccess) { mLocApi->atlCloseStatus(handle, isSuccess); }; /* Register DS Client APIs */ AgpsDSClientInitFn dsClientInitFn = [this](bool isDueToSSR) { return mLocApi->initDataServiceClient(isDueToSSR); }; AgpsDSClientOpenAndStartDataCallFn dsClientOpenAndStartDataCallFn = [this] { return mLocApi->openAndStartDataCall(); }; AgpsDSClientStopDataCallFn dsClientStopDataCallFn = [this] { mLocApi->stopDataCall(); }; AgpsDSClientCloseDataCallFn dsClientCloseDataCallFn = [this] { mLocApi->closeDataCall(); }; AgpsDSClientReleaseFn dsClientReleaseFn = [this] { mLocApi->releaseDataServiceClient(); }; /* Send Msg function */ SendMsgToAdapterMsgQueueFn sendMsgFn = [this](LocMsg* msg) { sendMsg(msg); }; /* Message to initialize AGPS module */ struct AgpsMsgInit: public LocMsg { AgpsManager* mAgpsManager; AgpsFrameworkInterface::AgnssStatusIpV4Cb mFrameworkStatusV4Cb; AgpsAtlOpenStatusCb mAtlOpenStatusCb; AgpsAtlCloseStatusCb mAtlCloseStatusCb; AgpsDSClientInitFn mDSClientInitFn; AgpsDSClientOpenAndStartDataCallFn mDSClientOpenAndStartDataCallFn; AgpsDSClientStopDataCallFn mDSClientStopDataCallFn; AgpsDSClientCloseDataCallFn mDSClientCloseDataCallFn; AgpsDSClientReleaseFn mDSClientReleaseFn; SendMsgToAdapterMsgQueueFn mSendMsgFn; GnssAdapter& mAdapter; inline AgpsMsgInit(AgpsManager* agpsManager, AgpsFrameworkInterface::AgnssStatusIpV4Cb frameworkStatusV4Cb, AgpsAtlOpenStatusCb atlOpenStatusCb, AgpsAtlCloseStatusCb atlCloseStatusCb, AgpsDSClientInitFn dsClientInitFn, AgpsDSClientOpenAndStartDataCallFn dsClientOpenAndStartDataCallFn, AgpsDSClientStopDataCallFn dsClientStopDataCallFn, AgpsDSClientCloseDataCallFn dsClientCloseDataCallFn, AgpsDSClientReleaseFn dsClientReleaseFn, SendMsgToAdapterMsgQueueFn sendMsgFn, GnssAdapter& adapter) : LocMsg(), mAgpsManager(agpsManager), mFrameworkStatusV4Cb( frameworkStatusV4Cb), mAtlOpenStatusCb(atlOpenStatusCb), mAtlCloseStatusCb( atlCloseStatusCb), mDSClientInitFn(dsClientInitFn), mDSClientOpenAndStartDataCallFn( dsClientOpenAndStartDataCallFn), mDSClientStopDataCallFn( dsClientStopDataCallFn), mDSClientCloseDataCallFn( dsClientCloseDataCallFn), mDSClientReleaseFn( dsClientReleaseFn), mSendMsgFn(sendMsgFn), mAdapter(adapter) { LOC_LOGV("AgpsMsgInit"); } inline virtual void proc() const { LOC_LOGV("AgpsMsgInit::proc()"); mAgpsManager->registerCallbacks(mFrameworkStatusV4Cb, mAtlOpenStatusCb, mAtlCloseStatusCb, mDSClientInitFn, mDSClientOpenAndStartDataCallFn, mDSClientStopDataCallFn, mDSClientCloseDataCallFn, mDSClientReleaseFn, mSendMsgFn); mAgpsManager->createAgpsStateMachines(); /* Register for AGPS event mask */ mAdapter.updateEvtMask(LOC_API_ADAPTER_BIT_LOCATION_SERVER_REQUEST, LOC_REGISTRATION_MASK_ENABLED); } }; /* Send message to initialize AGPS Manager */ sendMsg(new AgpsMsgInit( &mAgpsManager, (AgpsFrameworkInterface::AgnssStatusIpV4Cb)statusV4Cb, atlOpenStatusCb, atlCloseStatusCb, dsClientInitFn, dsClientOpenAndStartDataCallFn, dsClientStopDataCallFn, dsClientCloseDataCallFn, dsClientReleaseFn, sendMsgFn, *this)); } /* GnssAdapter::requestATL * Method triggered in QMI thread as part of handling below message: * eQMI_LOC_SERVER_REQUEST_OPEN_V02 * Triggers the AGPS state machine to setup AGPS call for below WWAN types: * eQMI_LOC_WWAN_TYPE_INTERNET_V02 * eQMI_LOC_WWAN_TYPE_AGNSS_V02 */ bool GnssAdapter::requestATL(int connHandle, LocAGpsType agpsType){ LOC_LOGI("GnssAdapter::requestATL"); sendMsg( new AgpsMsgRequestATL( &mAgpsManager, connHandle, (AGpsExtType)agpsType)); return true; } /* GnssAdapter::requestSuplES * Method triggered in QMI thread as part of handling below message: * eQMI_LOC_SERVER_REQUEST_OPEN_V02 * Triggers the AGPS state machine to setup AGPS call for below WWAN types: * eQMI_LOC_WWAN_TYPE_AGNSS_EMERGENCY_V02 */ bool GnssAdapter::requestSuplES(int connHandle){ LOC_LOGI("GnssAdapter::requestSuplES"); sendMsg( new AgpsMsgRequestATL( &mAgpsManager, connHandle, LOC_AGPS_TYPE_SUPL_ES)); return true; } /* GnssAdapter::releaseATL * Method triggered in QMI thread as part of handling below message: * eQMI_LOC_SERVER_REQUEST_CLOSE_V02 * Triggers teardown of an existing AGPS call */ bool GnssAdapter::releaseATL(int connHandle){ LOC_LOGI("GnssAdapter::releaseATL"); /* Release SUPL/INTERNET/SUPL_ES ATL */ struct AgpsMsgReleaseATL: public LocMsg { AgpsManager* mAgpsManager; int mConnHandle; inline AgpsMsgReleaseATL(AgpsManager* agpsManager, int connHandle) : LocMsg(), mAgpsManager(agpsManager), mConnHandle(connHandle) { LOC_LOGV("AgpsMsgReleaseATL"); } inline virtual void proc() const { LOC_LOGV("AgpsMsgReleaseATL::proc()"); mAgpsManager->releaseATL(mConnHandle); } }; sendMsg( new AgpsMsgReleaseATL(&mAgpsManager, connHandle)); return true; } /* GnssAdapter::reportDataCallOpened * DS Client data call opened successfully. * Send message to AGPS Manager to handle. */ bool GnssAdapter::reportDataCallOpened(){ LOC_LOGI("GnssAdapter::reportDataCallOpened"); struct AgpsMsgSuplEsOpened: public LocMsg { AgpsManager* mAgpsManager; inline AgpsMsgSuplEsOpened(AgpsManager* agpsManager) : LocMsg(), mAgpsManager(agpsManager) { LOC_LOGV("AgpsMsgSuplEsOpened"); } inline virtual void proc() const { LOC_LOGV("AgpsMsgSuplEsOpened::proc()"); mAgpsManager->reportDataCallOpened(); } }; sendMsg( new AgpsMsgSuplEsOpened(&mAgpsManager)); return true; } /* GnssAdapter::reportDataCallClosed * DS Client data call closed. * Send message to AGPS Manager to handle. */ bool GnssAdapter::reportDataCallClosed(){ LOC_LOGI("GnssAdapter::reportDataCallClosed"); struct AgpsMsgSuplEsClosed: public LocMsg { AgpsManager* mAgpsManager; inline AgpsMsgSuplEsClosed(AgpsManager* agpsManager) : LocMsg(), mAgpsManager(agpsManager) { LOC_LOGV("AgpsMsgSuplEsClosed"); } inline virtual void proc() const { LOC_LOGV("AgpsMsgSuplEsClosed::proc()"); mAgpsManager->reportDataCallClosed(); } }; sendMsg( new AgpsMsgSuplEsClosed(&mAgpsManager)); return true; } void GnssAdapter::dataConnOpenCommand( AGpsExtType agpsType, const char* apnName, int apnLen, LocApnIpType ipType){ LOC_LOGI("GnssAdapter::frameworkDataConnOpen"); struct AgpsMsgAtlOpenSuccess: public LocMsg { AgpsManager* mAgpsManager; AGpsExtType mAgpsType; char* mApnName; int mApnLen; LocApnIpType mIpType; inline AgpsMsgAtlOpenSuccess(AgpsManager* agpsManager, AGpsExtType agpsType, const char* apnName, int apnLen, LocApnIpType ipType) : LocMsg(), mAgpsManager(agpsManager), mAgpsType(agpsType), mApnName( new char[apnLen + 1]), mApnLen(apnLen), mIpType(ipType) { LOC_LOGV("AgpsMsgAtlOpenSuccess"); if (mApnName == nullptr) { LOC_LOGE("%s] new allocation failed, fatal error.", __func__); // Reporting the failure here mAgpsManager->reportAtlClosed(mAgpsType); return; } memcpy(mApnName, apnName, apnLen); mApnName[apnLen] = 0; } inline ~AgpsMsgAtlOpenSuccess() { delete[] mApnName; } inline virtual void proc() const { LOC_LOGV("AgpsMsgAtlOpenSuccess::proc()"); mAgpsManager->reportAtlOpenSuccess(mAgpsType, mApnName, mApnLen, mIpType); } }; // Added inital length checks for apnlen check to avoid security issues // In case of failure reporting the same if (NULL == apnName || apnLen <= 0 || apnLen > MAX_APN_LEN || (strlen(apnName) != (unsigned)apnLen)) { LOC_LOGe("%s]: incorrect apnlen length or incorrect apnName", __func__); mAgpsManager.reportAtlClosed(agpsType); } else { sendMsg( new AgpsMsgAtlOpenSuccess( &mAgpsManager, agpsType, apnName, apnLen, bearerType)); } } void GnssAdapter::dataConnClosedCommand(AGpsExtType agpsType){ LOC_LOGI("GnssAdapter::frameworkDataConnClosed"); struct AgpsMsgAtlClosed: public LocMsg { AgpsManager* mAgpsManager; AGpsExtType mAgpsType; inline AgpsMsgAtlClosed(AgpsManager* agpsManager, AGpsExtType agpsType) : LocMsg(), mAgpsManager(agpsManager), mAgpsType(agpsType) { LOC_LOGV("AgpsMsgAtlClosed"); } inline virtual void proc() const { LOC_LOGV("AgpsMsgAtlClosed::proc()"); mAgpsManager->reportAtlClosed(mAgpsType); } }; sendMsg( new AgpsMsgAtlClosed(&mAgpsManager, (AGpsExtType)agpsType)); } void GnssAdapter::dataConnFailedCommand(AGpsExtType agpsType){ LOC_LOGI("GnssAdapter::frameworkDataConnFailed"); struct AgpsMsgAtlOpenFailed: public LocMsg { AgpsManager* mAgpsManager; AGpsExtType mAgpsType; inline AgpsMsgAtlOpenFailed(AgpsManager* agpsManager, AGpsExtType agpsType) : LocMsg(), mAgpsManager(agpsManager), mAgpsType(agpsType) { LOC_LOGV("AgpsMsgAtlOpenFailed"); } inline virtual void proc() const { LOC_LOGV("AgpsMsgAtlOpenFailed::proc()"); mAgpsManager->reportAtlOpenFailed(mAgpsType); } }; sendMsg( new AgpsMsgAtlOpenFailed(&mAgpsManager, (AGpsExtType)agpsType)); } void GnssAdapter::convertSatelliteInfo(std::vector& out, const GnssSvType& in_constellation, const SystemStatusReports& in) { uint64_t sv_mask = 0ULL; uint32_t svid_min = 0; uint32_t svid_num = 0; uint32_t svid_idx = 0; uint64_t eph_health_good_mask = 0ULL; uint64_t eph_health_bad_mask = 0ULL; uint64_t server_perdiction_available_mask = 0ULL; float server_perdiction_age = 0.0f; // set constellationi based parameters switch (in_constellation) { case GNSS_SV_TYPE_GPS: svid_min = GNSS_BUGREPORT_GPS_MIN; svid_num = GPS_NUM; svid_idx = 0; if (!in.mSvHealth.empty()) { eph_health_good_mask = in.mSvHealth.back().mGpsGoodMask; eph_health_bad_mask = in.mSvHealth.back().mGpsBadMask; } if (!in.mXtra.empty()) { server_perdiction_available_mask = in.mXtra.back().mGpsXtraValid; server_perdiction_age = (float)(in.mXtra.back().mGpsXtraAge); } break; case GNSS_SV_TYPE_GLONASS: svid_min = GNSS_BUGREPORT_GLO_MIN; svid_num = GLO_NUM; svid_idx = GPS_NUM; if (!in.mSvHealth.empty()) { eph_health_good_mask = in.mSvHealth.back().mGloGoodMask; eph_health_bad_mask = in.mSvHealth.back().mGloBadMask; } if (!in.mXtra.empty()) { server_perdiction_available_mask = in.mXtra.back().mGloXtraValid; server_perdiction_age = (float)(in.mXtra.back().mGloXtraAge); } break; case GNSS_SV_TYPE_QZSS: svid_min = GNSS_BUGREPORT_QZSS_MIN; svid_num = QZSS_NUM; svid_idx = GPS_NUM+GLO_NUM+BDS_NUM+GAL_NUM; if (!in.mSvHealth.empty()) { eph_health_good_mask = in.mSvHealth.back().mQzssGoodMask; eph_health_bad_mask = in.mSvHealth.back().mQzssBadMask; } if (!in.mXtra.empty()) { server_perdiction_available_mask = in.mXtra.back().mQzssXtraValid; server_perdiction_age = (float)(in.mXtra.back().mQzssXtraAge); } break; case GNSS_SV_TYPE_BEIDOU: svid_min = GNSS_BUGREPORT_BDS_MIN; svid_num = BDS_NUM; svid_idx = GPS_NUM+GLO_NUM; if (!in.mSvHealth.empty()) { eph_health_good_mask = in.mSvHealth.back().mBdsGoodMask; eph_health_bad_mask = in.mSvHealth.back().mBdsBadMask; } if (!in.mXtra.empty()) { server_perdiction_available_mask = in.mXtra.back().mBdsXtraValid; server_perdiction_age = (float)(in.mXtra.back().mBdsXtraAge); } break; case GNSS_SV_TYPE_GALILEO: svid_min = GNSS_BUGREPORT_GAL_MIN; svid_num = GAL_NUM; svid_idx = GPS_NUM+GLO_NUM+BDS_NUM; if (!in.mSvHealth.empty()) { eph_health_good_mask = in.mSvHealth.back().mGalGoodMask; eph_health_bad_mask = in.mSvHealth.back().mGalBadMask; } if (!in.mXtra.empty()) { server_perdiction_available_mask = in.mXtra.back().mGalXtraValid; server_perdiction_age = (float)(in.mXtra.back().mGalXtraAge); } break; default: return; } // extract each sv info from systemstatus report for(uint32_t i=0; igetReport(reports, true); r.size = sizeof(r); // location block r.mLocation.size = sizeof(r.mLocation); if(!reports.mLocation.empty() && reports.mLocation.back().mValid) { r.mLocation.mValid = true; r.mLocation.mLocation.latitude = reports.mLocation.back().mLocation.gpsLocation.latitude; r.mLocation.mLocation.longitude = reports.mLocation.back().mLocation.gpsLocation.longitude; r.mLocation.mLocation.altitude = reports.mLocation.back().mLocation.gpsLocation.altitude; r.mLocation.mLocation.speed = (double)(reports.mLocation.back().mLocation.gpsLocation.speed); r.mLocation.mLocation.bearing = (double)(reports.mLocation.back().mLocation.gpsLocation.bearing); r.mLocation.mLocation.accuracy = (double)(reports.mLocation.back().mLocation.gpsLocation.accuracy); r.mLocation.verticalAccuracyMeters = reports.mLocation.back().mLocationEx.vert_unc; r.mLocation.speedAccuracyMetersPerSecond = reports.mLocation.back().mLocationEx.speed_unc; r.mLocation.bearingAccuracyDegrees = reports.mLocation.back().mLocationEx.bearing_unc; r.mLocation.mUtcReported = reports.mLocation.back().mUtcReported; } else if(!reports.mBestPosition.empty() && reports.mBestPosition.back().mValid) { r.mLocation.mValid = true; r.mLocation.mLocation.latitude = (double)(reports.mBestPosition.back().mBestLat); r.mLocation.mLocation.longitude = (double)(reports.mBestPosition.back().mBestLon); r.mLocation.mLocation.altitude = reports.mBestPosition.back().mBestAlt; r.mLocation.mLocation.timestamp = reports.mBestPosition.back().mUtcReported.tv_sec * 1000ULL + reports.mBestPosition.back().mUtcReported.tv_nsec / 1000000ULL; } else { r.mLocation.mValid = false; } if (r.mLocation.mValid) { LOC_LOGV("getDebugReport - lat=%f lon=%f alt=%f speed=%f", r.mLocation.mLocation.latitude, r.mLocation.mLocation.longitude, r.mLocation.mLocation.altitude, r.mLocation.mLocation.speed); } // time block r.mTime.size = sizeof(r.mTime); if(!reports.mTimeAndClock.empty() && reports.mTimeAndClock.back().mTimeValid) { r.mTime.mValid = true; r.mTime.timeEstimate = (((int64_t)(reports.mTimeAndClock.back().mGpsWeek)*7 + GNSS_UTC_TIME_OFFSET)*24*60*60 - (int64_t)(reports.mTimeAndClock.back().mLeapSeconds))*1000ULL + (int64_t)(reports.mTimeAndClock.back().mGpsTowMs); r.mTime.timeUncertaintyNs = (float)((reports.mTimeAndClock.back().mTimeUnc + reports.mTimeAndClock.back().mLeapSecUnc)*1000); r.mTime.frequencyUncertaintyNsPerSec = (float)(reports.mTimeAndClock.back().mClockFreqBiasUnc); LOC_LOGV("getDebugReport - timeestimate=%ld unc=%f frequnc=%f", r.mTime.timeEstimate, r.mTime.timeUncertaintyNs, r.mTime.frequencyUncertaintyNsPerSec); } else { r.mTime.mValid = false; } // satellite info block convertSatelliteInfo(r.mSatelliteInfo, GNSS_SV_TYPE_GPS, reports); convertSatelliteInfo(r.mSatelliteInfo, GNSS_SV_TYPE_GLONASS, reports); convertSatelliteInfo(r.mSatelliteInfo, GNSS_SV_TYPE_QZSS, reports); convertSatelliteInfo(r.mSatelliteInfo, GNSS_SV_TYPE_BEIDOU, reports); convertSatelliteInfo(r.mSatelliteInfo, GNSS_SV_TYPE_GALILEO, reports); LOC_LOGV("getDebugReport - satellite=%lu", r.mSatelliteInfo.size()); return true; }