aboutsummaryrefslogtreecommitdiff
path: root/data-ipa-cfg-mgr/hal/src/HAL.cpp
diff options
context:
space:
mode:
authorDavide Garberi <dade.garberi@gmail.com>2019-01-19 20:33:01 +0100
committerDavide Garberi <dade.garberi@gmail.com>2019-01-26 13:56:25 +0100
commit53a1ffbf112cf677a1fa6168d34d47b39a59aa1b (patch)
tree80c9ff5fc08f59cb4feb2f11bd0f1c1540cab354 /data-ipa-cfg-mgr/hal/src/HAL.cpp
parent5e5938dad03868d1fc1615f809ec1dda2aaf55ee (diff)
msm8996-common: Use common data-ipa-cfg-mgr
* Located at vendor/qcom/opensource/data-ipa-cfg-mgr Signed-off-by: Davide Garberi <dade.garberi@gmail.com> Change-Id: I6b1f1a034b8e4cafe5ae28a16327cc4e2ec6afa3 Signed-off-by: Davide Garberi <dade.garberi@gmail.com>
Diffstat (limited to 'data-ipa-cfg-mgr/hal/src/HAL.cpp')
-rw-r--r--data-ipa-cfg-mgr/hal/src/HAL.cpp617
1 files changed, 0 insertions, 617 deletions
diff --git a/data-ipa-cfg-mgr/hal/src/HAL.cpp b/data-ipa-cfg-mgr/hal/src/HAL.cpp
deleted file mode 100644
index 3f1a41f..0000000
--- a/data-ipa-cfg-mgr/hal/src/HAL.cpp
+++ /dev/null
@@ -1,617 +0,0 @@
-/*
- * 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.
- */
-#ifndef DBG
- #define DBG true
-#endif /* DBG */
-#define LOG_TAG "IPAHALService"
-
-/* HIDL Includes */
-#include <hwbinder/IPCThreadState.h>
-#include <hwbinder/ProcessState.h>
-
-/* Kernel Includes */
-#include <linux/netfilter/nfnetlink_compat.h>
-
-/* External Includes */
-#include <cutils/log.h>
-#include <cstring>
-#include <sys/socket.h>
-#include <sys/types.h>
-#include <vector>
-
-/* Internal Includes */
-#include "HAL.h"
-#include "LocalLogBuffer.h"
-#include "PrefixParser.h"
-
-/* Namespace pollution avoidance */
-using ::android::hardware::Void;
-using ::android::status_t;
-
-using RET = ::IOffloadManager::RET;
-using Prefix = ::IOffloadManager::Prefix;
-
-using ::std::map;
-using ::std::vector;
-
-
-/* ------------------------------ PUBLIC ------------------------------------ */
-HAL* HAL::makeIPAHAL(int version, IOffloadManager* mgr) {
- android::hardware::ProcessState::initWithMmapSize((size_t)(2 * KERNEL_PAGE));
-
- if (DBG)
- ALOGI("makeIPAHAL(%d, %s)", version,
- (mgr != nullptr) ? "provided" : "null");
- if (nullptr == mgr) return NULL;
- else if (version != 1) return NULL;
- HAL* ret = new HAL(mgr);
- if (nullptr == ret) return NULL;
- configureRpcThreadpool(1, false);
- ret->registerAsSystemService("ipacm");
- return ret;
-} /* makeIPAHAL */
-
-
-/* ------------------------------ PRIVATE ----------------------------------- */
-HAL::HAL(IOffloadManager* mgr) : mLogs("HAL Function Calls", 50) {
- mIPA = mgr;
- mCb.clear();
- mCbIpa = nullptr;
- mCbCt = nullptr;
-} /* HAL */
-
-void HAL::registerAsSystemService(const char* name) {
- status_t ret = 0;
-
- ret = IOffloadControl::registerAsService();
- if (ret != 0) ALOGE("Failed to register IOffloadControl (%d) name(%s)", ret, name);
- else if (DBG) {
- ALOGI("Successfully registered IOffloadControl");
- }
-
- ret = IOffloadConfig::registerAsService();
- if (ret != 0) ALOGE("Failed to register IOffloadConfig (%d)", ret);
- else if (DBG) {
- ALOGI("Successfully registered IOffloadConfig");
- }
-} /* registerAsSystemService */
-
-void HAL::doLogcatDump() {
- ALOGD("mHandles");
- ALOGD("========");
- /* @TODO This will segfault if they aren't initialized and I don't currently
- * care to check for initialization in a function that isn't used anyways
- * ALOGD("fd1->%d", mHandle1->data[0]);
- * ALOGD("fd2->%d", mHandle2->data[0]);
- */
- ALOGD("========");
-} /* doLogcatDump */
-
-HAL::BoolResult HAL::makeInputCheckFailure(string customErr) {
- BoolResult ret;
- ret.success = false;
- ret.errMsg = "Failed Input Checks: " + customErr;
- return ret;
-} /* makeInputCheckFailure */
-
-HAL::BoolResult HAL::ipaResultToBoolResult(RET in) {
- BoolResult ret;
- ret.success = (in >= RET::SUCCESS);
- switch (in) {
- case RET::FAIL_TOO_MANY_PREFIXES:
- ret.errMsg = "Too Many Prefixes Provided";
- break;
- case RET::FAIL_UNSUPPORTED:
- ret.errMsg = "Unsupported by Hardware";
- break;
- case RET::FAIL_INPUT_CHECK:
- ret.errMsg = "Failed Input Checks";
- break;
- case RET::FAIL_HARDWARE:
- ret.errMsg = "Hardware did not accept";
- break;
- case RET::FAIL_TRY_AGAIN:
- ret.errMsg = "Try Again";
- break;
- case RET::SUCCESS:
- ret.errMsg = "Successful";
- break;
- case RET::SUCCESS_DUPLICATE_CONFIG:
- ret.errMsg = "Successful: Was a duplicate configuration";
- break;
- case RET::SUCCESS_NO_OP:
- ret.errMsg = "Successful: No action needed";
- break;
- case RET::SUCCESS_OPTIMIZED:
- ret.errMsg = "Successful: Performed optimized version of action";
- break;
- default:
- ret.errMsg = "Unknown Error";
- break;
- }
- return ret;
-} /* ipaResultToBoolResult */
-
-/* This will likely always result in doubling the number of loops the execution
- * goes through. Obviously that is suboptimal. But if we first translate
- * away from all HIDL specific code, then we can avoid sprinkling HIDL
- * dependencies everywhere.
- */
-vector<string> HAL::convertHidlStrToStdStr(hidl_vec<hidl_string> in) {
- vector<string> ret;
- for (size_t i = 0; i < in.size(); i++) {
- string add = in[i];
- ret.push_back(add);
- }
- return ret;
-} /* convertHidlStrToStdStr */
-
-void HAL::registerEventListeners() {
- registerIpaCb();
- registerCtCb();
-} /* registerEventListeners */
-
-void HAL::registerIpaCb() {
- if (isInitialized() && mCbIpa == nullptr) {
- LocalLogBuffer::FunctionLog fl("registerEventListener");
- mCbIpa = new IpaEventRelay(mCb);
- mIPA->registerEventListener(mCbIpa);
- mLogs.addLog(fl);
- } else {
- ALOGE("Failed to registerIpaCb (isInitialized()=%s, (mCbIpa == nullptr)=%s)",
- isInitialized() ? "true" : "false",
- (mCbIpa == nullptr) ? "true" : "false");
- }
-} /* registerIpaCb */
-
-void HAL::registerCtCb() {
- if (isInitialized() && mCbCt == nullptr) {
- LocalLogBuffer::FunctionLog fl("registerCtTimeoutUpdater");
- mCbCt = new CtUpdateAmbassador(mCb);
- mIPA->registerCtTimeoutUpdater(mCbCt);
- mLogs.addLog(fl);
- } else {
- ALOGE("Failed to registerCtCb (isInitialized()=%s, (mCbCt == nullptr)=%s)",
- isInitialized() ? "true" : "false",
- (mCbCt == nullptr) ? "true" : "false");
- }
-} /* registerCtCb */
-
-void HAL::unregisterEventListeners() {
- unregisterIpaCb();
- unregisterCtCb();
-} /* unregisterEventListeners */
-
-void HAL::unregisterIpaCb() {
- if (mCbIpa != nullptr) {
- LocalLogBuffer::FunctionLog fl("unregisterEventListener");
- mIPA->unregisterEventListener(mCbIpa);
- mCbIpa = nullptr;
- mLogs.addLog(fl);
- } else {
- ALOGE("Failed to unregisterIpaCb");
- }
-} /* unregisterIpaCb */
-
-void HAL::unregisterCtCb() {
- if (mCbCt != nullptr) {
- LocalLogBuffer::FunctionLog fl("unregisterCtTimeoutUpdater");
- mIPA->unregisterCtTimeoutUpdater(mCbCt);
- mCbCt = nullptr;
- mLogs.addLog(fl);
- } else {
- ALOGE("Failed to unregisterCtCb");
- }
-} /* unregisterCtCb */
-
-void HAL::clearHandles() {
- ALOGI("clearHandles()");
- /* @TODO handle this more gracefully... also remove the log
- *
- * Things that would be nice, but I can't do:
- * [1] Destroy the object (it's on the stack)
- * [2] Call freeHandle (it's private)
- *
- * Things I can do but are hacks:
- * [1] Look at code and notice that setTo immediately calls freeHandle
- */
- mHandle1.setTo(nullptr, true);
- mHandle2.setTo(nullptr, true);
-} /* clearHandles */
-
-bool HAL::isInitialized() {
- return mCb.get() != nullptr;
-} /* isInitialized */
-
-
-/* -------------------------- IOffloadConfig -------------------------------- */
-Return<void> HAL::setHandles(
- const hidl_handle &fd1,
- const hidl_handle &fd2,
- setHandles_cb hidl_cb
-) {
- LocalLogBuffer::FunctionLog fl(__func__);
-
- if (fd1->numFds != 1) {
- BoolResult res = makeInputCheckFailure("Must provide exactly one FD per handle (fd1)");
- hidl_cb(res.success, res.errMsg);
- fl.setResult(res.success, res.errMsg);
-
- mLogs.addLog(fl);
- return Void();
- }
-
- if (fd2->numFds != 1) {
- BoolResult res = makeInputCheckFailure("Must provide exactly one FD per handle (fd2)");
- hidl_cb(res.success, res.errMsg);
- fl.setResult(res.success, res.errMsg);
-
- mLogs.addLog(fl);
- return Void();
- }
-
- /* The = operator calls freeHandle internally. Therefore, if we were using
- * these handles previously, they're now gone... forever. But hopefully the
- * new ones kick in very quickly.
- *
- * After freeing anything previously held, it will dup the FD so we have our
- * own copy.
- */
- mHandle1 = fd1;
- mHandle2 = fd2;
-
- /* Log the DUPed FD instead of the actual input FD so that we can lookup
- * this value in ls -l /proc/<pid>/<fd>
- */
- fl.addArg("fd1", mHandle1->data[0]);
- fl.addArg("fd2", mHandle2->data[0]);
-
- /* Try to provide each handle to IPACM. Destroy our DUPed hidl_handles if
- * IPACM does not like either input. This keeps us from leaking FDs or
- * providing half solutions.
- *
- * @TODO unfortunately, this does not cover duplicate configs where IPACM
- * thinks it is still holding on to a handle that we would have freed above.
- * It also probably means that IPACM would not know about the first FD being
- * freed if it rejects the second FD.
- */
- RET ipaReturn = mIPA->provideFd(mHandle1->data[0], UDP_SUBSCRIPTIONS);
- if (ipaReturn == RET::SUCCESS) {
- ipaReturn = mIPA->provideFd(mHandle2->data[0], TCP_SUBSCRIPTIONS);
- }
-
- if (ipaReturn != RET::SUCCESS) {
- ALOGE("IPACM failed to accept the FDs (%d %d)", mHandle1->data[0],
- mHandle2->data[0]);
- clearHandles();
- } else {
- /* @TODO remove logs after stabilization */
- ALOGI("IPACM was provided two FDs (%d, %d)", mHandle1->data[0],
- mHandle2->data[0]);
- }
-
- BoolResult res = ipaResultToBoolResult(ipaReturn);
- hidl_cb(res.success, res.errMsg);
-
- fl.setResult(res.success, res.errMsg);
- mLogs.addLog(fl);
- return Void();
-} /* setHandles */
-
-
-/* -------------------------- IOffloadControl ------------------------------- */
-Return<void> HAL::initOffload
-(
- const ::android::sp<ITetheringOffloadCallback>& cb,
- initOffload_cb hidl_cb
-) {
- LocalLogBuffer::FunctionLog fl(__func__);
-
- if (isInitialized()) {
- BoolResult res = makeInputCheckFailure("Already initialized");
- hidl_cb(res.success, res.errMsg);
- fl.setResult(res.success, res.errMsg);
- mLogs.addLog(fl);
- } else {
- /* Should storing the CB be a function? */
- mCb = cb;
- registerEventListeners();
- BoolResult res = ipaResultToBoolResult(RET::SUCCESS);
- hidl_cb(res.success, res.errMsg);
- fl.setResult(res.success, res.errMsg);
- mLogs.addLog(fl);
- }
-
- return Void();
-} /* initOffload */
-
-Return<void> HAL::stopOffload
-(
- stopOffload_cb hidl_cb
-) {
- LocalLogBuffer::FunctionLog fl(__func__);
-
- if (!isInitialized()) {
- BoolResult res = makeInputCheckFailure("Was never initialized");
- hidl_cb(res.success, res.errMsg);
- fl.setResult(res.success, res.errMsg);
- mLogs.addLog(fl);
- } else {
- /* Should removing the CB be a function? */
- mCb.clear();
- unregisterEventListeners();
-
- RET ipaReturn = mIPA->stopAllOffload();
- if (ipaReturn != RET::SUCCESS) {
- /* Ignore IPAs return value here and provide why stopAllOffload
- * failed. However, if IPA failed to clearAllFds, then we can't
- * clear our map because they may still be in use.
- */
- RET ret = mIPA->clearAllFds();
- if (ret == RET::SUCCESS) {
- clearHandles();
- }
- } else {
- ipaReturn = mIPA->clearAllFds();
- /* If IPA fails, they may still be using these for some reason. */
- if (ipaReturn == RET::SUCCESS) {
- clearHandles();
- } else {
- ALOGE("IPACM failed to return success for clearAllFds so they will not be released...");
- }
- }
-
- BoolResult res = ipaResultToBoolResult(ipaReturn);
- hidl_cb(res.success, res.errMsg);
-
- fl.setResult(res.success, res.errMsg);
- mLogs.addLog(fl);
- }
-
- return Void();
-} /* stopOffload */
-
-Return<void> HAL::setLocalPrefixes
-(
- const hidl_vec<hidl_string>& prefixes,
- setLocalPrefixes_cb hidl_cb
-) {
- BoolResult res;
- PrefixParser parser;
- vector<string> prefixesStr = convertHidlStrToStdStr(prefixes);
-
- LocalLogBuffer::FunctionLog fl(__func__);
- fl.addArg("prefixes", prefixesStr);
-
- memset(&res,0,sizeof(BoolResult));
-
- if (!isInitialized()) {
- BoolResult res = makeInputCheckFailure("Not initialized");
- } else if(prefixesStr.size() < 1) {
- res = ipaResultToBoolResult(RET::FAIL_INPUT_CHECK);
- } else if (!parser.add(prefixesStr)) {
- res = makeInputCheckFailure(parser.getLastErrAsStr());
- } else {
- res = ipaResultToBoolResult(RET::SUCCESS);
- }
-
- hidl_cb(res.success, res.errMsg);
- fl.setResult(res.success, res.errMsg);
- mLogs.addLog(fl);
- return Void();
-} /* setLocalPrefixes */
-
-Return<void> HAL::getForwardedStats
-(
- const hidl_string& upstream,
- getForwardedStats_cb hidl_cb
-) {
- LocalLogBuffer::FunctionLog fl(__func__);
- fl.addArg("upstream", upstream);
-
- OffloadStatistics ret;
- RET ipaReturn = mIPA->getStats(upstream.c_str(), true, ret);
- if (ipaReturn == RET::SUCCESS) {
- hidl_cb(ret.getTotalRxBytes(), ret.getTotalTxBytes());
- fl.setResult(ret.getTotalRxBytes(), ret.getTotalTxBytes());
- } else {
- /* @TODO Ensure the output is zeroed, but this is probably not enough to
- * tell Framework that an error has occurred. If, for example, they had
- * not yet polled for statistics previously, they may incorrectly assume
- * that simply no statistics have transpired on hardware path.
- *
- * Maybe ITetheringOffloadCallback:onEvent(OFFLOAD_STOPPED_ERROR) is
- * enough to handle this case, time will tell.
- */
- hidl_cb(0, 0);
- fl.setResult(0, 0);
- }
-
- mLogs.addLog(fl);
- return Void();
-} /* getForwardedStats */
-
-Return<void> HAL::setDataLimit
-(
- const hidl_string& upstream,
- uint64_t limit,
- setDataLimit_cb hidl_cb
-) {
- LocalLogBuffer::FunctionLog fl(__func__);
- fl.addArg("upstream", upstream);
- fl.addArg("limit", limit);
-
- if (!isInitialized()) {
- BoolResult res = makeInputCheckFailure("Not initialized (setDataLimit)");
- hidl_cb(res.success, res.errMsg);
- fl.setResult(res.success, res.errMsg);
- } else {
- RET ipaReturn = mIPA->setQuota(upstream.c_str(), limit);
- if(ipaReturn == RET::FAIL_TRY_AGAIN) {
- ipaReturn = RET::SUCCESS;
- }
- BoolResult res = ipaResultToBoolResult(ipaReturn);
- hidl_cb(res.success, res.errMsg);
- fl.setResult(res.success, res.errMsg);
- }
-
- mLogs.addLog(fl);
- return Void();
-} /* setDataLimit */
-
-Return<void> HAL::setUpstreamParameters
-(
- const hidl_string& iface,
- const hidl_string& v4Addr,
- const hidl_string& v4Gw,
- const hidl_vec<hidl_string>& v6Gws,
- setUpstreamParameters_cb hidl_cb
-) {
- vector<string> v6GwStrs = convertHidlStrToStdStr(v6Gws);
-
- LocalLogBuffer::FunctionLog fl(__func__);
- fl.addArg("iface", iface);
- fl.addArg("v4Addr", v4Addr);
- fl.addArg("v4Gw", v4Gw);
- fl.addArg("v6Gws", v6GwStrs);
-
- PrefixParser v4AddrParser;
- PrefixParser v4GwParser;
- PrefixParser v6GwParser;
-
- /* @TODO maybe we should enforce that these addresses and gateways are fully
- * qualified here. But then, how do we allow them to be empty/null as well
- * while still preserving a sane API on PrefixParser?
- */
- if (!isInitialized()) {
- BoolResult res = makeInputCheckFailure("Not initialized (setUpstreamParameters)");
- hidl_cb(res.success, res.errMsg);
- fl.setResult(res.success, res.errMsg);
- } else if (!v4AddrParser.addV4(v4Addr) && !v4Addr.empty()) {
- BoolResult res = makeInputCheckFailure(v4AddrParser.getLastErrAsStr());
- hidl_cb(res.success, res.errMsg);
- fl.setResult(res.success, res.errMsg);
- } else if (!v4GwParser.addV4(v4Gw) && !v4Gw.empty()) {
- BoolResult res = makeInputCheckFailure(v4GwParser.getLastErrAsStr());
- hidl_cb(res.success, res.errMsg);
- fl.setResult(res.success, res.errMsg);
- } else if (v6GwStrs.size() >= 1 && !v6GwParser.addV6(v6GwStrs)) {
- BoolResult res = makeInputCheckFailure(v6GwParser.getLastErrAsStr());
- hidl_cb(res.success, res.errMsg);
- fl.setResult(res.success, res.errMsg);
- } else if (iface.size()>= 1) {
- RET ipaReturn = mIPA->setUpstream(
- iface.c_str(),
- v4GwParser.getFirstPrefix(),
- v6GwParser.getFirstPrefix());
- BoolResult res = ipaResultToBoolResult(ipaReturn);
- hidl_cb(res.success, res.errMsg);
- fl.setResult(res.success, res.errMsg);
- } else {
- /* send NULL iface string when upstream down */
- RET ipaReturn = mIPA->setUpstream(
- NULL,
- v4GwParser.getFirstPrefix(IP_FAM::V4),
- v6GwParser.getFirstPrefix(IP_FAM::V6));
- BoolResult res = ipaResultToBoolResult(ipaReturn);
- hidl_cb(res.success, res.errMsg);
- fl.setResult(res.success, res.errMsg);
- }
-
- mLogs.addLog(fl);
- return Void();
-} /* setUpstreamParameters */
-
-Return<void> HAL::addDownstream
-(
- const hidl_string& iface,
- const hidl_string& prefix,
- addDownstream_cb hidl_cb
-) {
- LocalLogBuffer::FunctionLog fl(__func__);
- fl.addArg("iface", iface);
- fl.addArg("prefix", prefix);
-
- PrefixParser prefixParser;
-
- if (!isInitialized()) {
- BoolResult res = makeInputCheckFailure("Not initialized (setUpstreamParameters)");
- hidl_cb(res.success, res.errMsg);
- fl.setResult(res.success, res.errMsg);
- }
- else if (!prefixParser.add(prefix)) {
- BoolResult res = makeInputCheckFailure(prefixParser.getLastErrAsStr());
- hidl_cb(res.success, res.errMsg);
- fl.setResult(res.success, res.errMsg);
- } else {
- RET ipaReturn = mIPA->addDownstream(
- iface.c_str(),
- prefixParser.getFirstPrefix());
- BoolResult res = ipaResultToBoolResult(ipaReturn);
- hidl_cb(res.success, res.errMsg);
- fl.setResult(res.success, res.errMsg);
- }
-
- mLogs.addLog(fl);
- return Void();
-} /* addDownstream */
-
-Return<void> HAL::removeDownstream
-(
- const hidl_string& iface,
- const hidl_string& prefix,
- removeDownstream_cb hidl_cb
-) {
- LocalLogBuffer::FunctionLog fl(__func__);
- fl.addArg("iface", iface);
- fl.addArg("prefix", prefix);
-
- PrefixParser prefixParser;
-
- if (!isInitialized()) {
- BoolResult res = makeInputCheckFailure("Not initialized (setUpstreamParameters)");
- hidl_cb(res.success, res.errMsg);
- fl.setResult(res.success, res.errMsg);
- }
- else if (!prefixParser.add(prefix)) {
- BoolResult res = makeInputCheckFailure(prefixParser.getLastErrAsStr());
- hidl_cb(res.success, res.errMsg);
- fl.setResult(res.success, res.errMsg);
- } else {
- RET ipaReturn = mIPA->removeDownstream(
- iface.c_str(),
- prefixParser.getFirstPrefix());
- BoolResult res = ipaResultToBoolResult(ipaReturn);
- hidl_cb(res.success, res.errMsg);
- fl.setResult(res.success, res.errMsg);
- }
-
- mLogs.addLog(fl);
- return Void();
-} /* removeDownstream */