text: /*
   * 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 <log/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 __unused) {
      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 */
  