/* Copyright (c) 2017-2020, 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. * */ /* Changes from Qualcomm Innovation Center are provided under the following license: Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted (subject to the limitations in the disclaimer below) 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 Qualcomm Innovation Center, Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER 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_NDEBUG 0 #define LOG_TAG "LocSvc_BatchingAdapter" #include #include #include #include using namespace loc_core; BatchingAdapter::BatchingAdapter() : LocAdapterBase(0, LocContext::getLocContext(LocContext::mLocationHalName), false, nullptr, true), mOngoingTripDistance(0), mOngoingTripTBFInterval(0), mTripWithOngoingTBFDropped(false), mTripWithOngoingTripDistanceDropped(false), mBatchingTimeout(0), mBatchingAccuracy(1), mBatchSize(0), mTripBatchSize(0) { LOC_LOGD("%s]: Constructor", __func__); readConfigCommand(); setConfigCommand(); // at last step, let us inform adapater base that we are done // with initialization, e.g.: ready to process handleEngineUpEvent doneInit(); } void BatchingAdapter::readConfigCommand() { LOC_LOGD("%s]: ", __func__); struct MsgReadConfig : public LocMsg { BatchingAdapter& mAdapter; inline MsgReadConfig(BatchingAdapter& adapter) : LocMsg(), mAdapter(adapter) {} inline virtual void proc() const { uint32_t batchingTimeout = 0; uint32_t batchingAccuracy = 0; uint32_t batchSize = 0; uint32_t tripBatchSize = 0; static const loc_param_s_type flp_conf_param_table[] = { {"BATCH_SIZE", &batchSize, NULL, 'n'}, {"OUTDOOR_TRIP_BATCH_SIZE", &tripBatchSize, NULL, 'n'}, {"BATCH_SESSION_TIMEOUT", &batchingTimeout, NULL, 'n'}, {"ACCURACY", &batchingAccuracy, NULL, 'n'}, }; UTIL_READ_CONF(LOC_PATH_FLP_CONF, flp_conf_param_table); LOC_LOGD("%s]: batchSize %u tripBatchSize %u batchingAccuracy %u batchingTimeout %u ", __func__, batchSize, tripBatchSize, batchingAccuracy, batchingTimeout); mAdapter.setBatchSize(batchSize); mAdapter.setTripBatchSize(tripBatchSize); mAdapter.setBatchingTimeout(batchingTimeout); mAdapter.setBatchingAccuracy(batchingAccuracy); } }; sendMsg(new MsgReadConfig(*this)); } void BatchingAdapter::setConfigCommand() { LOC_LOGD("%s]: ", __func__); struct MsgSetConfig : public LocMsg { BatchingAdapter& mAdapter; LocApiBase& mApi; inline MsgSetConfig(BatchingAdapter& adapter, LocApiBase& api) : LocMsg(), mAdapter(adapter), mApi(api) {} inline virtual void proc() const { mApi.setBatchSize(mAdapter.getBatchSize()); mApi.setTripBatchSize(mAdapter.getTripBatchSize()); } }; sendMsg(new MsgSetConfig(*this, *mLocApi)); } void BatchingAdapter::stopClientSessions(LocationAPI* client) { LOC_LOGD("%s]: client %p", __func__, client); typedef struct pairKeyBatchMode { LocationAPI* client; uint32_t id; BatchingMode batchingMode; inline pairKeyBatchMode(LocationAPI* _client, uint32_t _id, BatchingMode _bMode) : client(_client), id(_id), batchingMode(_bMode) {} } pairKeyBatchMode; std::vector vBatchingClient; for (auto it : mBatchingSessions) { if (client == it.first.client) { vBatchingClient.emplace_back(it.first.client, it.first.id, it.second.batchingMode); } } for (auto keyBatchingMode : vBatchingClient) { if (keyBatchingMode.batchingMode != BATCHING_MODE_TRIP) { stopBatching(keyBatchingMode.client, keyBatchingMode.id); } else { stopTripBatchingMultiplex(keyBatchingMode.client, keyBatchingMode.id); } } } void BatchingAdapter::updateClientsEventMask() { LOC_API_ADAPTER_EVENT_MASK_T mask = 0; for (auto it=mClientData.begin(); it != mClientData.end(); ++it) { // we don't register LOC_API_ADAPTER_BIT_BATCH_FULL until we // start batching with ROUTINE or TRIP option if (it->second.batchingCb != nullptr) { mask |= LOC_API_ADAPTER_BIT_BATCH_STATUS; } } if (autoReportBatchingSessionsCount() > 0) { mask |= LOC_API_ADAPTER_BIT_BATCH_FULL; } updateEvtMask(mask, LOC_REGISTRATION_MASK_SET); } void BatchingAdapter::handleEngineLockStatusEvent(EngineLockState engineLockState) { LOC_LOGD("%s]: Old Engine state %d, New Engine state : %d,", __func__, mLocApi->getEngineLockState(), engineLockState); struct MsgEngineLockStateEvent : public LocMsg { BatchingAdapter& mAdapter; EngineLockState mEngineLockState; inline MsgEngineLockStateEvent(BatchingAdapter& adapter, EngineLockState engineLockState) : LocMsg(), mAdapter(adapter), mEngineLockState(engineLockState){} virtual void proc() const { mAdapter.handleEngineLockStatus(mEngineLockState); } }; sendMsg(new MsgEngineLockStateEvent(*this, engineLockState)); } void BatchingAdapter::handleEngineLockStatus(EngineLockState engineLockState) { LOC_LOGd("lock state %d, pending msgs %zu", engineLockState, mPendingGnssEnabledMsgs.size()); if (ENGINE_LOCK_STATE_ENABLED == engineLockState) { for (auto msg: mPendingGnssEnabledMsgs) { sendMsg(msg); } mPendingGnssEnabledMsgs.clear(); } } void BatchingAdapter::handleEngineUpEvent() { struct MsgSSREvent : public LocMsg { BatchingAdapter& mAdapter; LocApiBase& mApi; inline MsgSSREvent(BatchingAdapter& adapter, LocApiBase& api) : LocMsg(), mAdapter(adapter), mApi(api) {} virtual void proc() const { mAdapter.setEngineCapabilitiesKnown(true); mAdapter.broadcastCapabilities(mAdapter.getCapabilities()); mApi.setBatchSize(mAdapter.getBatchSize()); mApi.setTripBatchSize(mAdapter.getTripBatchSize()); if (ENGINE_LOCK_STATE_ENABLED == mApi.getEngineLockState()) { mAdapter.restartSessions(); for (auto msg: mAdapter.mPendingMsgs) { mAdapter.sendMsg(msg); } mAdapter.mPendingMsgs.clear(); } } }; sendMsg(new MsgSSREvent(*this, *mLocApi)); } void BatchingAdapter::restartSessions() { LOC_LOGD("%s]: ", __func__); if (autoReportBatchingSessionsCount() > 0) { updateEvtMask(LOC_API_ADAPTER_BIT_BATCH_FULL, LOC_REGISTRATION_MASK_ENABLED); } for (auto it = mBatchingSessions.begin(); it != mBatchingSessions.end(); ++it) { if (it->second.batchingMode != BATCHING_MODE_TRIP) { mLocApi->startBatching(it->first.id, it->second, getBatchingAccuracy(), getBatchingTimeout(), new LocApiResponse(*getContext(), [] (LocationError /*err*/) {})); } } if (mTripSessions.size() > 0) { // restart outdoor trip batching session if any. mOngoingTripDistance = 0; mOngoingTripTBFInterval = 0; // record the min trip distance and min tbf interval of all ongoing sessions for (auto tripSession : mTripSessions) { TripSessionStatus &tripSessStatus = tripSession.second; if ((0 == mOngoingTripDistance) || (mOngoingTripDistance > (tripSessStatus.tripDistance - tripSessStatus.accumulatedDistanceThisTrip))) { mOngoingTripDistance = tripSessStatus.tripDistance - tripSessStatus.accumulatedDistanceThisTrip; } if ((0 == mOngoingTripTBFInterval) || (mOngoingTripTBFInterval > tripSessStatus.tripTBFInterval)) { mOngoingTripTBFInterval = tripSessStatus.tripTBFInterval; } // reset the accumulatedDistanceOngoingBatch for each session tripSessStatus.accumulatedDistanceOngoingBatch = 0; } mLocApi->startOutdoorTripBatching(mOngoingTripDistance, mOngoingTripTBFInterval, getBatchingTimeout(), new LocApiResponse(*getContext(), [this] (LocationError err) { if (LOCATION_ERROR_SUCCESS != err) { mOngoingTripDistance = 0; mOngoingTripTBFInterval = 0; } printTripReport(); })); } } bool BatchingAdapter::hasBatchingCallback(LocationAPI* client) { auto it = mClientData.find(client); return (it != mClientData.end() && it->second.batchingCb); } bool BatchingAdapter::isBatchingSession(LocationAPI* client, uint32_t sessionId) { LocationSessionKey key(client, sessionId); return (mBatchingSessions.find(key) != mBatchingSessions.end()); } bool BatchingAdapter::isTripSession(uint32_t sessionId) { return (mTripSessions.find(sessionId) != mTripSessions.end()); } void BatchingAdapter::saveBatchingSession(LocationAPI* client, uint32_t sessionId, const BatchingOptions& batchingOptions) { LocationSessionKey key(client, sessionId); mBatchingSessions[key] = batchingOptions; } void BatchingAdapter::eraseBatchingSession(LocationAPI* client, uint32_t sessionId) { LocationSessionKey key(client, sessionId); auto it = mBatchingSessions.find(key); if (it != mBatchingSessions.end()) { mBatchingSessions.erase(it); } } void BatchingAdapter::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_LOGE("%s]: client %p id %u not found in data", __func__, client, sessionId); } } uint32_t BatchingAdapter::autoReportBatchingSessionsCount() { uint32_t count = 0; for (auto batchingSession: mBatchingSessions) { if (batchingSession.second.batchingMode != BATCHING_MODE_NO_AUTO_REPORT) { count++; } } count += mTripSessions.size(); return count; } uint32_t BatchingAdapter::startBatchingCommand( LocationAPI* client, BatchingOptions& batchOptions) { uint32_t sessionId = generateSessionId(); LOC_LOGD("%s]: client %p id %u minInterval %u minDistance %u mode %u Batching Mode %d", __func__, client, sessionId, batchOptions.minInterval, batchOptions.minDistance, batchOptions.mode,batchOptions.batchingMode); struct MsgStartBatching : public LocMsg { BatchingAdapter& mAdapter; LocApiBase& mApi; LocationAPI* mClient; uint32_t mSessionId; BatchingOptions mBatchingOptions; inline MsgStartBatching(BatchingAdapter& adapter, LocApiBase& api, LocationAPI* client, uint32_t sessionId, BatchingOptions batchOptions) : LocMsg(), mAdapter(adapter), mApi(api), mClient(client), mSessionId(sessionId), mBatchingOptions(batchOptions) {} inline virtual void proc() const { if (!mAdapter.isEngineCapabilitiesKnown()) { mAdapter.mPendingMsgs.push_back(new MsgStartBatching(*this)); return; } LocationError err = LOCATION_ERROR_SUCCESS; if (!mAdapter.hasBatchingCallback(mClient)) { err = LOCATION_ERROR_CALLBACK_MISSING; } else if (0 == mBatchingOptions.size) { err = LOCATION_ERROR_INVALID_PARAMETER; } else if (!ContextBase::isMessageSupported( LOC_API_ADAPTER_MESSAGE_DISTANCE_BASE_LOCATION_BATCHING)) { err = LOCATION_ERROR_NOT_SUPPORTED; } if (LOCATION_ERROR_SUCCESS == err) { if (mBatchingOptions.batchingMode == BATCHING_MODE_ROUTINE || mBatchingOptions.batchingMode == BATCHING_MODE_NO_AUTO_REPORT) { mAdapter.startBatching( mClient, mSessionId, mBatchingOptions, new MsgStartBatching(*this)); } else if (mBatchingOptions.batchingMode == BATCHING_MODE_TRIP) { mAdapter.startTripBatchingMultiplex(mClient, mSessionId, mBatchingOptions); } else { mAdapter.reportResponse(mClient, LOCATION_ERROR_INVALID_PARAMETER, mSessionId); } } } }; sendMsg(new MsgStartBatching(*this, *mLocApi, client, sessionId, batchOptions)); return sessionId; } void BatchingAdapter::startBatching(LocationAPI* client, uint32_t sessionId, const BatchingOptions& batchingOptions, LocMsg* pendingMsg) { if (batchingOptions.batchingMode != BATCHING_MODE_NO_AUTO_REPORT && 0 == autoReportBatchingSessionsCount()) { // if there is currenty no batching sessions interested in batch full event, then this // new session will need to register for batch full event updateEvtMask(LOC_API_ADAPTER_BIT_BATCH_FULL, LOC_REGISTRATION_MASK_ENABLED); } // Assume start will be OK, remove session if not saveBatchingSession(client, sessionId, batchingOptions); mLocApi->startBatching(sessionId, batchingOptions, getBatchingAccuracy(), getBatchingTimeout(), new LocApiResponse(*getContext(), [this, client, sessionId, batchingOptions, pendingMsg] (LocationError err) { if (ENGINE_LOCK_STATE_ENABLED == mLocApi->getEngineLockState() && LOCATION_ERROR_SUCCESS != err) { eraseBatchingSession(client, sessionId); } if (LOCATION_ERROR_SUCCESS != err && batchingOptions.batchingMode != BATCHING_MODE_NO_AUTO_REPORT && 0 == autoReportBatchingSessionsCount()) { // if we fail to start batching and we have already registered batch full event // we need to undo that since no sessions are now interested in batch full event updateEvtMask(LOC_API_ADAPTER_BIT_BATCH_FULL, LOC_REGISTRATION_MASK_DISABLED); } if (LOCATION_ERROR_GNSS_DISABLED == err && pendingMsg != nullptr) { LOC_LOGd("GNSS_DISABLED, add request to pending queue"); mPendingGnssEnabledMsgs.push_back(pendingMsg); } else if (pendingMsg != nullptr) { delete pendingMsg; } reportResponse(client, err, sessionId); })); } void BatchingAdapter::updateBatchingOptionsCommand(LocationAPI* client, uint32_t id, BatchingOptions& batchOptions) { LOC_LOGD("%s]: client %p id %u minInterval %u minDistance %u mode %u batchMode %u", __func__, client, id, batchOptions.minInterval, batchOptions.minDistance, batchOptions.mode, batchOptions.batchingMode); struct MsgUpdateBatching : public LocMsg { BatchingAdapter& mAdapter; LocApiBase& mApi; LocationAPI* mClient; uint32_t mSessionId; BatchingOptions mBatchOptions; inline MsgUpdateBatching(BatchingAdapter& adapter, LocApiBase& api, LocationAPI* client, uint32_t sessionId, BatchingOptions batchOptions) : LocMsg(), mAdapter(adapter), mApi(api), mClient(client), mSessionId(sessionId), mBatchOptions(batchOptions) {} inline virtual void proc() const { if (!mAdapter.isEngineCapabilitiesKnown()) { mAdapter.mPendingMsgs.push_back(new MsgUpdateBatching(*this)); return; } LocationError err = LOCATION_ERROR_SUCCESS; if (!mAdapter.isBatchingSession(mClient, mSessionId)) { err = LOCATION_ERROR_ID_UNKNOWN; } else if ((0 == mBatchOptions.size) || (mBatchOptions.batchingMode > BATCHING_MODE_NO_AUTO_REPORT)) { err = LOCATION_ERROR_INVALID_PARAMETER; } if (LOCATION_ERROR_SUCCESS == err) { if (!mAdapter.isTripSession(mSessionId)) { mAdapter.stopBatching(mClient, mSessionId, true, mBatchOptions); } else { mAdapter.stopTripBatchingMultiplex(mClient, mSessionId, true, mBatchOptions); } } } }; sendMsg(new MsgUpdateBatching(*this, *mLocApi, client, id, batchOptions)); } void BatchingAdapter::stopBatchingCommand(LocationAPI* client, uint32_t id) { LOC_LOGD("%s]: client %p id %u", __func__, client, id); struct MsgStopBatching : public LocMsg { BatchingAdapter& mAdapter; LocApiBase& mApi; LocationAPI* mClient; uint32_t mSessionId; inline MsgStopBatching(BatchingAdapter& adapter, LocApiBase& api, LocationAPI* client, uint32_t sessionId) : LocMsg(), mAdapter(adapter), mApi(api), mClient(client), mSessionId(sessionId) {} inline virtual void proc() const { if (!mAdapter.isEngineCapabilitiesKnown()) { mAdapter.mPendingMsgs.push_back(new MsgStopBatching(*this)); return; } LocationError err = LOCATION_ERROR_SUCCESS; if (!mAdapter.isBatchingSession(mClient, mSessionId)) { err = LOCATION_ERROR_ID_UNKNOWN; } if (LOCATION_ERROR_SUCCESS == err) { if (mAdapter.isTripSession(mSessionId)) { mAdapter.stopTripBatchingMultiplex(mClient, mSessionId); } else { mAdapter.stopBatching(mClient, mSessionId); } } } }; sendMsg(new MsgStopBatching(*this, *mLocApi, client, id)); } void BatchingAdapter::stopBatching(LocationAPI* client, uint32_t sessionId, bool restartNeeded, const BatchingOptions& batchOptions) { LocationSessionKey key(client, sessionId); auto it = mBatchingSessions.find(key); if (it != mBatchingSessions.end()) { auto flpOptions = it->second; // Assume stop will be OK, restore session if not eraseBatchingSession(client, sessionId); mLocApi->stopBatching(sessionId, new LocApiResponse(*getContext(), [this, client, sessionId, flpOptions, restartNeeded, batchOptions] (LocationError err) { if (ENGINE_LOCK_STATE_ENABLED == mLocApi->getEngineLockState() && LOCATION_ERROR_SUCCESS != err) { saveBatchingSession(client, sessionId, batchOptions); } else { // if stopBatching is success, unregister for batch full event if this was the last // batching session that is interested in batch full event if (0 == autoReportBatchingSessionsCount() && flpOptions.batchingMode != BATCHING_MODE_NO_AUTO_REPORT) { updateEvtMask(LOC_API_ADAPTER_BIT_BATCH_FULL, LOC_REGISTRATION_MASK_DISABLED); } if (restartNeeded) { if (batchOptions.batchingMode == BATCHING_MODE_ROUTINE || batchOptions.batchingMode == BATCHING_MODE_NO_AUTO_REPORT) { startBatching(client, sessionId, batchOptions); } else if (batchOptions.batchingMode == BATCHING_MODE_TRIP) { startTripBatchingMultiplex(client, sessionId, batchOptions); } } } reportResponse(client, err, sessionId); })); } } void BatchingAdapter::getBatchedLocationsCommand(LocationAPI* client, uint32_t id, size_t count) { LOC_LOGD("%s]: client %p id %u count %zu", __func__, client, id, count); struct MsgGetBatchedLocations : public LocMsg { BatchingAdapter& mAdapter; LocApiBase& mApi; LocationAPI* mClient; uint32_t mSessionId; size_t mCount; inline MsgGetBatchedLocations(BatchingAdapter& adapter, LocApiBase& api, LocationAPI* client, uint32_t sessionId, size_t count) : LocMsg(), mAdapter(adapter), mApi(api), mClient(client), mSessionId(sessionId), mCount(count) {} inline virtual void proc() const { if (!mAdapter.isEngineCapabilitiesKnown()) { mAdapter.mPendingMsgs.push_back(new MsgGetBatchedLocations(*this)); return; } LocationError err = LOCATION_ERROR_SUCCESS; if (!mAdapter.hasBatchingCallback(mClient)) { err = LOCATION_ERROR_CALLBACK_MISSING; } else if (!mAdapter.isBatchingSession(mClient, mSessionId)) { err = LOCATION_ERROR_ID_UNKNOWN; } if (LOCATION_ERROR_SUCCESS == err) { if (mAdapter.isTripSession(mSessionId)) { mApi.getBatchedTripLocations(mCount, 0, new LocApiResponse(*mAdapter.getContext(), [&mAdapter = mAdapter, mSessionId = mSessionId, mClient = mClient] (LocationError err) { mAdapter.reportResponse(mClient, err, mSessionId); })); } else { mApi.getBatchedLocations(mCount, new LocApiResponse(*mAdapter.getContext(), [&mAdapter = mAdapter, mSessionId = mSessionId, mClient = mClient] (LocationError err) { mAdapter.reportResponse(mClient, err, mSessionId); })); } } else { mAdapter.reportResponse(mClient, err, mSessionId); } } }; sendMsg(new MsgGetBatchedLocations(*this, *mLocApi, client, id, count)); } void BatchingAdapter::reportLocationsEvent(const Location* locations, size_t count, BatchingMode batchingMode) { LOC_LOGD("%s]: count %zu batchMode %d", __func__, count, batchingMode); struct MsgReportLocations : public LocMsg { BatchingAdapter& mAdapter; Location* mLocations; size_t mCount; BatchingMode mBatchingMode; inline MsgReportLocations(BatchingAdapter& adapter, const Location* locations, size_t count, BatchingMode batchingMode) : LocMsg(), mAdapter(adapter), mLocations(new Location[count]), mCount(count), mBatchingMode(batchingMode) { if (nullptr == mLocations) { LOC_LOGE("%s]: new failed to allocate mLocations", __func__); return; } for (size_t i=0; i < mCount; ++i) { mLocations[i] = locations[i]; } } inline virtual ~MsgReportLocations() { if (nullptr != mLocations) delete[] mLocations; } inline virtual void proc() const { mAdapter.reportLocations(mLocations, mCount, mBatchingMode); } }; sendMsg(new MsgReportLocations(*this, locations, count, batchingMode)); } void BatchingAdapter::reportLocations(Location* locations, size_t count, BatchingMode batchingMode) { BatchingOptions batchOptions = {sizeof(BatchingOptions), batchingMode}; for (auto it=mClientData.begin(); it != mClientData.end(); ++it) { if (nullptr != it->second.batchingCb) { it->second.batchingCb(count, locations, batchOptions); } } } void BatchingAdapter::reportCompletedTripsEvent(uint32_t accumulated_distance) { struct MsgReportCompletedTrips : public LocMsg { BatchingAdapter& mAdapter; uint32_t mAccumulatedDistance; inline MsgReportCompletedTrips(BatchingAdapter& adapter, uint32_t accumulated_distance) : LocMsg(), mAdapter(adapter), mAccumulatedDistance(accumulated_distance) { } inline virtual ~MsgReportCompletedTrips() { } inline virtual void proc() const { // Check if any trips are completed std::list completedTripsList; completedTripsList.clear(); for(auto itt = mAdapter.mTripSessions.begin(); itt != mAdapter.mTripSessions.end();) { TripSessionStatus &tripSession = itt->second; tripSession.accumulatedDistanceThisTrip = tripSession.accumulatedDistanceOnTripRestart + (mAccumulatedDistance - tripSession.accumulatedDistanceOngoingBatch); if (tripSession.tripDistance <= tripSession.accumulatedDistanceThisTrip) { // trip is completed completedTripsList.push_back(itt->first); itt = mAdapter.mTripSessions.erase(itt); if (tripSession.tripTBFInterval == mAdapter.mOngoingTripTBFInterval) { // trip with ongoing TBF interval is completed mAdapter.mTripWithOngoingTBFDropped = true; } if (tripSession.tripDistance == mAdapter.mOngoingTripDistance) { // trip with ongoing trip distance is completed mAdapter.mTripWithOngoingTripDistanceDropped = true; } } else { itt++; } } if (completedTripsList.size() > 0) { mAdapter.reportBatchStatusChange(BATCHING_STATUS_TRIP_COMPLETED, completedTripsList); mAdapter.restartTripBatching(false, mAccumulatedDistance, 0); } else { mAdapter.printTripReport(); } } }; LOC_LOGD("%s]: Accumulated Distance so far: %u", __func__, accumulated_distance); sendMsg(new MsgReportCompletedTrips(*this, accumulated_distance)); } void BatchingAdapter::reportBatchStatusChange(BatchingStatus batchStatus, std::list & completedTripsList) { BatchingStatusInfo batchStatusInfo = {sizeof(BatchingStatusInfo), batchStatus}; for (auto it=mClientData.begin(); it != mClientData.end(); ++it) { if (nullptr != it->second.batchingStatusCb) { it->second.batchingStatusCb(batchStatusInfo, completedTripsList); } } } void BatchingAdapter::reportBatchStatusChangeEvent(BatchingStatus batchStatus) { struct MsgReportBatchStatus : public LocMsg { BatchingAdapter& mAdapter; BatchingStatus mBatchStatus; inline MsgReportBatchStatus(BatchingAdapter& adapter, BatchingStatus batchStatus) : LocMsg(), mAdapter(adapter), mBatchStatus(batchStatus) { } inline virtual ~MsgReportBatchStatus() { } inline virtual void proc() const { std::list tempList; tempList.clear(); mAdapter.reportBatchStatusChange(mBatchStatus, tempList); } }; sendMsg(new MsgReportBatchStatus(*this, batchStatus)); } void BatchingAdapter::startTripBatchingMultiplex(LocationAPI* client, uint32_t sessionId, const BatchingOptions& batchingOptions) { if (mTripSessions.size() == 0) { // if there is currenty no batching sessions interested in batch full event, then this // new session will need to register for batch full event if (0 == autoReportBatchingSessionsCount()) { updateEvtMask(LOC_API_ADAPTER_BIT_BATCH_FULL, LOC_REGISTRATION_MASK_ENABLED); } // Assume start will be OK, remove session if not saveBatchingSession(client, sessionId, batchingOptions); mTripSessions[sessionId] = { 0, 0, 0, batchingOptions.minDistance, batchingOptions.minInterval}; mLocApi->startOutdoorTripBatching(batchingOptions.minDistance, batchingOptions.minInterval, getBatchingTimeout(), new LocApiResponse(*getContext(), [this, client, sessionId, batchingOptions] (LocationError err) { if (ENGINE_LOCK_STATE_DISABLED == mLocApi->getEngineLockState() || err == LOCATION_ERROR_SUCCESS) { mOngoingTripDistance = batchingOptions.minDistance; mOngoingTripTBFInterval = batchingOptions.minInterval; LOC_LOGD("%s] New Trip started ...", __func__); printTripReport(); } else { eraseBatchingSession(client, sessionId); mTripSessions.erase(sessionId); // if we fail to start batching and we have already registered batch full event // we need to undo that since no sessions are now interested in batch full event if (0 == autoReportBatchingSessionsCount()) { updateEvtMask(LOC_API_ADAPTER_BIT_BATCH_FULL, LOC_REGISTRATION_MASK_DISABLED); } } reportResponse(client, err, sessionId); })); } else { // query accumulated distance mLocApi->queryAccumulatedTripDistance( new LocApiResponseData(*getContext(), [this, batchingOptions, sessionId, client] (LocationError err, LocApiBatchData data) { uint32_t accumulatedDistanceOngoingBatch = 0; uint32_t numOfBatchedPositions = 0; uint32_t ongoingTripDistance = mOngoingTripDistance; uint32_t ongoingTripInterval = mOngoingTripTBFInterval; bool needsRestart = false; // check if TBF of new session is lesser than ongoing TBF interval if (ongoingTripInterval > batchingOptions.minInterval) { ongoingTripInterval = batchingOptions.minInterval; needsRestart = true; } accumulatedDistanceOngoingBatch = data.accumulatedDistance; numOfBatchedPositions = data.numOfBatchedPositions; TripSessionStatus newTripSession = { accumulatedDistanceOngoingBatch, 0, 0, batchingOptions.minDistance, batchingOptions.minInterval}; if (err != LOCATION_ERROR_SUCCESS) { // unable to query accumulated distance, assume remaining distance in // ongoing batch is mongoingTripDistance. if (batchingOptions.minDistance < ongoingTripDistance) { ongoingTripDistance = batchingOptions.minDistance; needsRestart = true; } } else { // compute the remaining distance uint32_t ongoing_trip_remaining_distance = ongoingTripDistance - accumulatedDistanceOngoingBatch; // check if new trip distance is lesser than the ongoing batch remaining distance if (batchingOptions.minDistance < ongoing_trip_remaining_distance) { ongoingTripDistance = batchingOptions.minDistance; needsRestart = true; } else if (needsRestart == true) { // needsRestart is anyways true , may be because of lesser TBF of new session. ongoingTripDistance = ongoing_trip_remaining_distance; } mTripSessions[sessionId] = newTripSession; LOC_LOGD("%s] New Trip started ...", __func__); printTripReport(); } if (needsRestart) { mOngoingTripDistance = ongoingTripDistance; mOngoingTripTBFInterval = ongoingTripInterval; // reset the accumulatedDistanceOngoingBatch for each session, // and record the total accumulated distance so far for the session. for (auto itt = mTripSessions.begin(); itt != mTripSessions.end(); itt++) { TripSessionStatus &tripSessStatus = itt->second; tripSessStatus.accumulatedDistanceOngoingBatch = 0; tripSessStatus.accumulatedDistanceOnTripRestart = tripSessStatus.accumulatedDistanceThisTrip; } mLocApi->reStartOutdoorTripBatching(ongoingTripDistance, ongoingTripInterval, getBatchingTimeout(), new LocApiResponse(*getContext(), [this, client, sessionId] (LocationError err) { if (err != LOCATION_ERROR_SUCCESS) { LOC_LOGE("%s] New Trip restart failed!", __func__); } reportResponse(client, err, sessionId); })); } else { reportResponse(client, LOCATION_ERROR_SUCCESS, sessionId); } })); } } void BatchingAdapter::stopTripBatchingMultiplex(LocationAPI* client, uint32_t sessionId, bool restartNeeded, const BatchingOptions& batchOptions) { LocationError err = LOCATION_ERROR_SUCCESS; if (mTripSessions.size() == 1) { mLocApi->stopOutdoorTripBatching(true, new LocApiResponse(*getContext(), [this, restartNeeded, client, sessionId, batchOptions] (LocationError err) { if (LOCATION_ERROR_SUCCESS == err) { // if stopOutdoorTripBatching is success, unregister for batch full event if this // was the last batching session that is interested in batch full event if (1 == autoReportBatchingSessionsCount()) { updateEvtMask(LOC_API_ADAPTER_BIT_BATCH_FULL, LOC_REGISTRATION_MASK_DISABLED); } } stopTripBatchingMultiplexCommon(err, client, sessionId, restartNeeded, batchOptions); })); return; } stopTripBatchingMultiplexCommon(err, client, sessionId, restartNeeded, batchOptions); } void BatchingAdapter::stopTripBatchingMultiplexCommon(LocationError err, LocationAPI* client, uint32_t sessionId, bool restartNeeded, const BatchingOptions& batchOptions) { auto itt = mTripSessions.find(sessionId); TripSessionStatus tripSess = itt->second; if (tripSess.tripTBFInterval == mOngoingTripTBFInterval) { // trip with ongoing trip interval is stopped mTripWithOngoingTBFDropped = true; } if (tripSess.tripDistance == mOngoingTripDistance) { // trip with ongoing trip distance is stopped mTripWithOngoingTripDistanceDropped = true; } mTripSessions.erase(sessionId); if (mTripSessions.size() == 0) { mOngoingTripDistance = 0; mOngoingTripTBFInterval = 0; } else { restartTripBatching(true); } if (restartNeeded) { eraseBatchingSession(client, sessionId); if (batchOptions.batchingMode == BATCHING_MODE_ROUTINE || batchOptions.batchingMode == BATCHING_MODE_NO_AUTO_REPORT) { startBatching(client, sessionId, batchOptions); } else if (batchOptions.batchingMode == BATCHING_MODE_TRIP) { startTripBatchingMultiplex(client, sessionId, batchOptions); } } reportResponse(client, err, sessionId); } void BatchingAdapter::restartTripBatching(bool queryAccumulatedDistance, uint32_t accDist, uint32_t numbatchedPos) { // does batch need restart with new trip distance / TBF interval uint32_t minRemainingDistance = 0; uint32_t minTBFInterval = 0; // if no more trips left, stop the ongoing trip if (mTripSessions.size() == 0) { mLocApi->stopOutdoorTripBatching(true, new LocApiResponse(*getContext(), [] (LocationError /*err*/) {})); mOngoingTripDistance = 0; mOngoingTripTBFInterval = 0; // unregister for batch full event if there are no more // batching session that is interested in batch full event if (0 == autoReportBatchingSessionsCount()) { updateEvtMask(LOC_API_ADAPTER_BIT_BATCH_FULL, LOC_REGISTRATION_MASK_DISABLED); } return; } // record the min trip distance and min tbf interval of all ongoing sessions for (auto itt = mTripSessions.begin(); itt != mTripSessions.end(); itt++) { TripSessionStatus tripSessStatus = itt->second; if ((minRemainingDistance == 0) || (minRemainingDistance > (tripSessStatus.tripDistance - tripSessStatus.accumulatedDistanceThisTrip))) { minRemainingDistance = tripSessStatus.tripDistance - tripSessStatus.accumulatedDistanceThisTrip; } if ((minTBFInterval == 0) || (minTBFInterval > tripSessStatus.tripTBFInterval)) { minTBFInterval = tripSessStatus.tripTBFInterval; } } mLocApi->queryAccumulatedTripDistance( new LocApiResponseData(*getContext(), [this, queryAccumulatedDistance, minRemainingDistance, minTBFInterval, accDist, numbatchedPos] (LocationError /*err*/, LocApiBatchData data) { bool needsRestart = false; uint32_t ongoingTripDistance = mOngoingTripDistance; uint32_t ongoingTripInterval = mOngoingTripTBFInterval; uint32_t accumulatedDistance = accDist; uint32_t numOfBatchedPositions = numbatchedPos; if (queryAccumulatedDistance) { accumulatedDistance = data.accumulatedDistance; numOfBatchedPositions = data.numOfBatchedPositions; } if ((!mTripWithOngoingTripDistanceDropped) && (ongoingTripDistance - accumulatedDistance != 0)) { // if ongoing trip is already not completed still, // check the min distance against the remaining distance if (minRemainingDistance < (ongoingTripDistance - accumulatedDistance)) { ongoingTripDistance = minRemainingDistance; needsRestart = true; } } else if (minRemainingDistance != 0) { // else if ongoing trip is already completed / dropped, // use the minRemainingDistance of ongoing sessions ongoingTripDistance = minRemainingDistance; needsRestart = true; } if ((minTBFInterval < ongoingTripInterval) || ((minTBFInterval != ongoingTripInterval) && (mTripWithOngoingTBFDropped))) { ongoingTripInterval = minTBFInterval; needsRestart = true; } if (needsRestart) { mLocApi->reStartOutdoorTripBatching(ongoingTripDistance, ongoingTripInterval, getBatchingTimeout(), new LocApiResponse(*getContext(), [this, accumulatedDistance, ongoingTripDistance, ongoingTripInterval] (LocationError err) { if (err == LOCATION_ERROR_SUCCESS) { for(auto itt = mTripSessions.begin(); itt != mTripSessions.end(); itt++) { TripSessionStatus &tripSessStatus = itt->second; tripSessStatus.accumulatedDistanceThisTrip = tripSessStatus.accumulatedDistanceOnTripRestart + (accumulatedDistance - tripSessStatus.accumulatedDistanceOngoingBatch); tripSessStatus.accumulatedDistanceOngoingBatch = 0; tripSessStatus.accumulatedDistanceOnTripRestart = tripSessStatus.accumulatedDistanceThisTrip; } mOngoingTripDistance = ongoingTripDistance; mOngoingTripTBFInterval = ongoingTripInterval; } })); } })); } void BatchingAdapter::printTripReport() { IF_LOC_LOGD { LOC_LOGD("Ongoing Trip Distance = %u, Ongoing Trip TBF Interval = %u", mOngoingTripDistance, mOngoingTripTBFInterval); for (auto itt = mTripSessions.begin(); itt != mTripSessions.end(); itt++) { TripSessionStatus tripSessStatus = itt->second; LOC_LOGD("tripDistance:%u tripTBFInterval:%u" " trip accumulated Distance:%u" " trip accumualted distance ongoing batch:%u" " trip accumulated distance on trip restart %u \r\n", tripSessStatus.tripDistance, tripSessStatus.tripTBFInterval, tripSessStatus.accumulatedDistanceThisTrip, tripSessStatus.accumulatedDistanceOngoingBatch, tripSessStatus.accumulatedDistanceOnTripRestart); } } }