diff options
author | Bruno Martins <bgcngm@gmail.com> | 2018-01-08 12:51:07 +0000 |
---|---|---|
committer | Davide Garberi <dade.garberi@gmail.com> | 2018-01-20 09:30:21 +0100 |
commit | 1c67f47d4a205e12650dcb6255c00bafc0046082 (patch) | |
tree | 72bb48ca2152cb2295b0f8de39497490d8616f6f /data-ipa-cfg-mgr/hal/src/HAL.cpp | |
parent | 6b0c862328f58e523e2a6efa667a2f4b3a609bf3 (diff) |
msm8996-common: Import IPACM back
* QC package: LA.UM.6.5.r1-05300-8x96.0
* Squashed with commits 08e2a5b9b, 055b183 and 8fcf57b
Change-Id: I2f7a792dc0155986e065d1bf79e1f08370c3d79c
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.cpp | 612 |
1 files changed, 612 insertions, 0 deletions
diff --git a/data-ipa-cfg-mgr/hal/src/HAL.cpp b/data-ipa-cfg-mgr/hal/src/HAL.cpp new file mode 100644 index 0000000..002bbb4 --- /dev/null +++ b/data-ipa-cfg-mgr/hal/src/HAL.cpp @@ -0,0 +1,612 @@ +/* + * 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) { + 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)", ret); + 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); + 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 */ |