Home | History | Annotate | Download | only in src
      1 /*
      2  * Copyright (c) 2017, The Linux Foundation. All rights reserved.
      3  *
      4  * Redistribution and use in source and binary forms, with or without
      5  * modification, are permitted provided that the following conditions are
      6  * met:
      7  *    * Redistributions of source code must retain the above copyright
      8  *      notice, this list of conditions and the following disclaimer.
      9  *    * Redistributions in binary form must reproduce the above
     10  *      copyright notice, this list of conditions and the following
     11  *      disclaimer in the documentation and/or other materials provided
     12  *      with the distribution.
     13  *    * Neither the name of The Linux Foundation nor the names of its
     14  *      contributors may be used to endorse or promote products derived
     15  *      from this software without specific prior written permission.
     16  *
     17  * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
     18  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
     19  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
     20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
     21  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     22  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     23  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
     24  * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
     25  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
     26  * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
     27  * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     28  */
     29 #ifndef DBG
     30     #define DBG true
     31 #endif /* DBG */
     32 #define LOG_TAG "IPAHALService"
     33 
     34 /* HIDL Includes */
     35 #include <hwbinder/IPCThreadState.h>
     36 #include <hwbinder/ProcessState.h>
     37 
     38 /* Kernel Includes */
     39 #include <linux/netfilter/nfnetlink_compat.h>
     40 
     41 /* External Includes */
     42 #include <cutils/log.h>
     43 #include <string>
     44 #include <sys/socket.h>
     45 #include <sys/types.h>
     46 #include <vector>
     47 
     48 /* Internal Includes */
     49 #include "HAL.h"
     50 #include "LocalLogBuffer.h"
     51 #include "PrefixParser.h"
     52 
     53 /* Namespace pollution avoidance */
     54 using ::android::hardware::Void;
     55 using ::android::status_t;
     56 
     57 using RET = ::IOffloadManager::RET;
     58 using Prefix = ::IOffloadManager::Prefix;
     59 
     60 using ::std::map;
     61 using ::std::vector;
     62 
     63 
     64 /* ------------------------------ PUBLIC ------------------------------------ */
     65 HAL* HAL::makeIPAHAL(int version, IOffloadManager* mgr) {
     66     if (DBG)
     67         ALOGI("makeIPAHAL(%d, %s)", version,
     68                 (mgr != nullptr) ? "provided" : "null");
     69     if (nullptr == mgr) return NULL;
     70     else if (version != 1) return NULL;
     71     HAL* ret = new HAL(mgr);
     72     if (nullptr == ret) return NULL;
     73     configureRpcThreadpool(1, false);
     74     ret->registerAsSystemService();
     75     return ret;
     76 } /* makeIPAHAL */
     77 
     78 
     79 /* ------------------------------ PRIVATE ----------------------------------- */
     80 HAL::HAL(IOffloadManager* mgr) : mLogs("HAL Function Calls", 100) {
     81     mIPA = mgr;
     82     mCb.clear();
     83     mCbIpa = nullptr;
     84     mCbCt = nullptr;
     85 } /* HAL */
     86 
     87 void HAL::registerAsSystemService() {
     88     status_t ret = 0;
     89 
     90     ret = IOffloadControl::registerAsService();
     91     if (ret != 0) ALOGE("Failed to register IOffloadControl (%d)", ret);
     92     else if (DBG) {
     93         ALOGI("Successfully registered IOffloadControl");
     94     }
     95 
     96     IOffloadConfig::registerAsService();
     97     if (ret != 0) ALOGE("Failed to register IOffloadConfig (%d)", ret);
     98     else if (DBG) {
     99         ALOGI("Successfully registered IOffloadConfig");
    100     }
    101 } /* registerAsSystemService */
    102 
    103 void HAL::doLogcatDump() {
    104     ALOGD("mHandles");
    105     ALOGD("========");
    106     /* @TODO This will segfault if they aren't initialized and I don't currently
    107      * care to check for initialization in a function that isn't used anyways
    108      * ALOGD("fd1->%d", mHandle1->data[0]);
    109      * ALOGD("fd2->%d", mHandle2->data[0]);
    110      */
    111     ALOGD("========");
    112 } /* doLogcatDump */
    113 
    114 HAL::BoolResult HAL::makeInputCheckFailure(string customErr) {
    115     BoolResult ret;
    116     ret.success = false;
    117     ret.errMsg = "Failed Input Checks: " + customErr;
    118     return ret;
    119 } /* makeInputCheckFailure */
    120 
    121 HAL::BoolResult HAL::ipaResultToBoolResult(RET in) {
    122     BoolResult ret;
    123     ret.success = (in >= RET::SUCCESS);
    124     switch (in) {
    125         case RET::FAIL_TOO_MANY_PREFIXES:
    126             ret.errMsg = "Too Many Prefixes Provided";
    127             break;
    128         case RET::FAIL_UNSUPPORTED:
    129             ret.errMsg = "Unsupported by Hardware";
    130             break;
    131         case RET::FAIL_INPUT_CHECK:
    132             ret.errMsg = "Failed Input Checks";
    133             break;
    134         case RET::FAIL_HARDWARE:
    135             ret.errMsg = "Hardware did not accept";
    136             break;
    137         case RET::FAIL_TRY_AGAIN:
    138             ret.errMsg = "Try Again";
    139             break;
    140         case RET::SUCCESS:
    141             ret.errMsg = "Successful";
    142             break;
    143         case RET::SUCCESS_DUPLICATE_CONFIG:
    144             ret.errMsg = "Successful: Was a duplicate configuration";
    145             break;
    146         case RET::SUCCESS_NO_OP:
    147             ret.errMsg = "Successful: No action needed";
    148             break;
    149         case RET::SUCCESS_OPTIMIZED:
    150             ret.errMsg = "Successful: Performed optimized version of action";
    151             break;
    152         default:
    153             ret.errMsg = "Unknown Error";
    154             break;
    155     }
    156     return ret;
    157 } /* ipaResultToBoolResult */
    158 
    159 /* This will likely always result in doubling the number of loops the execution
    160  * goes through.  Obviously that is suboptimal.  But if we first translate
    161  * away from all HIDL specific code, then we can avoid sprinkling HIDL
    162  * dependencies everywhere.
    163  */
    164 vector<string> HAL::convertHidlStrToStdStr(hidl_vec<hidl_string> in) {
    165     vector<string> ret;
    166     for (size_t i = 0; i < in.size(); i++) {
    167         string add = in[i];
    168         ret.push_back(add);
    169     }
    170     return ret;
    171 } /* convertHidlStrToStdStr */
    172 
    173 void HAL::registerEventListeners() {
    174     registerIpaCb();
    175     registerCtCb();
    176 } /* registerEventListeners */
    177 
    178 void HAL::registerIpaCb() {
    179     if (isInitialized() && mCbIpa == nullptr) {
    180         LocalLogBuffer::FunctionLog fl("registerEventListener");
    181         mCbIpa = new IpaEventRelay(mCb);
    182         mIPA->registerEventListener(mCbIpa);
    183         mLogs.addLog(fl);
    184     } else {
    185         ALOGE("Failed to registerIpaCb (isInitialized()=%s, (mCbIpa == nullptr)=%s)",
    186                 isInitialized() ? "true" : "false",
    187                 (mCbIpa == nullptr) ? "true" : "false");
    188     }
    189 } /* registerIpaCb */
    190 
    191 void HAL::registerCtCb() {
    192     if (isInitialized() && mCbCt == nullptr) {
    193         LocalLogBuffer::FunctionLog fl("registerCtTimeoutUpdater");
    194         mCbCt = new CtUpdateAmbassador(mCb);
    195         mIPA->registerCtTimeoutUpdater(mCbCt);
    196         mLogs.addLog(fl);
    197     } else {
    198         ALOGE("Failed to registerCtCb (isInitialized()=%s, (mCbCt == nullptr)=%s)",
    199                 isInitialized() ? "true" : "false",
    200                 (mCbCt == nullptr) ? "true" : "false");
    201     }
    202 } /* registerCtCb */
    203 
    204 void HAL::unregisterEventListeners() {
    205     unregisterIpaCb();
    206     unregisterCtCb();
    207 } /* unregisterEventListeners */
    208 
    209 void HAL::unregisterIpaCb() {
    210     if (mCbIpa != nullptr) {
    211         LocalLogBuffer::FunctionLog fl("unregisterEventListener");
    212         mIPA->unregisterEventListener(mCbIpa);
    213         mCbIpa = nullptr;
    214         mLogs.addLog(fl);
    215     } else {
    216         ALOGE("Failed to unregisterIpaCb");
    217     }
    218 } /* unregisterIpaCb */
    219 
    220 void HAL::unregisterCtCb() {
    221     if (mCbCt != nullptr) {
    222         LocalLogBuffer::FunctionLog fl("unregisterCtTimeoutUpdater");
    223         mIPA->unregisterCtTimeoutUpdater(mCbCt);
    224         mCbCt = nullptr;
    225         mLogs.addLog(fl);
    226     } else {
    227         ALOGE("Failed to unregisterCtCb");
    228     }
    229 } /* unregisterCtCb */
    230 
    231 void HAL::clearHandles() {
    232     ALOGI("clearHandles()");
    233     /* @TODO handle this more gracefully... also remove the log
    234      *
    235      * Things that would be nice, but I can't do:
    236      * [1] Destroy the object (it's on the stack)
    237      * [2] Call freeHandle (it's private)
    238      *
    239      * Things I can do but are hacks:
    240      * [1] Look at code and notice that setTo immediately calls freeHandle
    241      */
    242     mHandle1.setTo(nullptr, true);
    243     mHandle2.setTo(nullptr, true);
    244 } /* clearHandles */
    245 
    246 bool HAL::isInitialized() {
    247     return mCb.get() != nullptr;
    248 } /* isInitialized */
    249 
    250 
    251 /* -------------------------- IOffloadConfig -------------------------------- */
    252 Return<void> HAL::setHandles(
    253     const hidl_handle &fd1,
    254     const hidl_handle &fd2,
    255     setHandles_cb hidl_cb
    256 ) {
    257     LocalLogBuffer::FunctionLog fl(__func__);
    258 
    259     if (fd1->numFds != 1) {
    260         BoolResult res = makeInputCheckFailure("Must provide exactly one FD per handle (fd1)");
    261         hidl_cb(res.success, res.errMsg);
    262         fl.setResult(res.success, res.errMsg);
    263 
    264         mLogs.addLog(fl);
    265         return Void();
    266     }
    267 
    268     if (fd2->numFds != 1) {
    269         BoolResult res = makeInputCheckFailure("Must provide exactly one FD per handle (fd2)");
    270         hidl_cb(res.success, res.errMsg);
    271         fl.setResult(res.success, res.errMsg);
    272 
    273         mLogs.addLog(fl);
    274         return Void();
    275     }
    276 
    277     /* The = operator calls freeHandle internally.  Therefore, if we were using
    278      * these handles previously, they're now gone... forever.  But hopefully the
    279      * new ones kick in very quickly.
    280      *
    281      * After freeing anything previously held, it will dup the FD so we have our
    282      * own copy.
    283      */
    284     mHandle1 = fd1;
    285     mHandle2 = fd2;
    286 
    287     /* Log the DUPed FD instead of the actual input FD so that we can lookup
    288      * this value in ls -l /proc/<pid>/<fd>
    289      */
    290     fl.addArg("fd1", mHandle1->data[0]);
    291     fl.addArg("fd2", mHandle2->data[0]);
    292 
    293     /* Try to provide each handle to IPACM.  Destroy our DUPed hidl_handles if
    294      * IPACM does not like either input.  This keeps us from leaking FDs or
    295      * providing half solutions.
    296      *
    297      * @TODO unfortunately, this does not cover duplicate configs where IPACM
    298      * thinks it is still holding on to a handle that we would have freed above.
    299      * It also probably means that IPACM would not know about the first FD being
    300      * freed if it rejects the second FD.
    301      */
    302     RET ipaReturn = mIPA->provideFd(mHandle1->data[0], UDP_SUBSCRIPTIONS);
    303     if (ipaReturn == RET::SUCCESS) {
    304         ipaReturn = mIPA->provideFd(mHandle2->data[0], TCP_SUBSCRIPTIONS);
    305     }
    306 
    307     if (ipaReturn != RET::SUCCESS) {
    308         ALOGE("IPACM failed to accept the FDs (%d %d)", mHandle1->data[0],
    309                 mHandle2->data[0]);
    310         clearHandles();
    311     } else {
    312         /* @TODO remove logs after stabilization */
    313         ALOGI("IPACM was provided two FDs (%d, %d)", mHandle1->data[0],
    314                 mHandle2->data[0]);
    315     }
    316 
    317     BoolResult res = ipaResultToBoolResult(ipaReturn);
    318     hidl_cb(res.success, res.errMsg);
    319 
    320     fl.setResult(res.success, res.errMsg);
    321     mLogs.addLog(fl);
    322     return Void();
    323 } /* setHandles */
    324 
    325 
    326 /* -------------------------- IOffloadControl ------------------------------- */
    327 Return<void> HAL::initOffload
    328 (
    329     const ::android::sp<ITetheringOffloadCallback>& cb,
    330     initOffload_cb hidl_cb
    331 ) {
    332     LocalLogBuffer::FunctionLog fl(__func__);
    333 
    334     if (isInitialized()) {
    335         BoolResult res = makeInputCheckFailure("Already initialized");
    336         hidl_cb(res.success, res.errMsg);
    337         fl.setResult(res.success, res.errMsg);
    338         mLogs.addLog(fl);
    339     } else {
    340         /* Should storing the CB be a function? */
    341         mCb = cb;
    342         registerEventListeners();
    343         BoolResult res = ipaResultToBoolResult(RET::SUCCESS);
    344         hidl_cb(res.success, res.errMsg);
    345         fl.setResult(res.success, res.errMsg);
    346         mLogs.addLog(fl);
    347     }
    348 
    349     return Void();
    350 } /* initOffload */
    351 
    352 Return<void> HAL::stopOffload
    353 (
    354     stopOffload_cb hidl_cb
    355 ) {
    356     LocalLogBuffer::FunctionLog fl(__func__);
    357 
    358     if (!isInitialized()) {
    359         BoolResult res = makeInputCheckFailure("Was never initialized");
    360         hidl_cb(res.success, res.errMsg);
    361         fl.setResult(res.success, res.errMsg);
    362         mLogs.addLog(fl);
    363     } else {
    364         /* Should removing the CB be a function? */
    365         mCb.clear();
    366         unregisterEventListeners();
    367 
    368         RET ipaReturn = mIPA->stopAllOffload();
    369         if (ipaReturn != RET::SUCCESS) {
    370             /* Ignore IPAs return value here and provide why stopAllOffload
    371              * failed.  However, if IPA failed to clearAllFds, then we can't
    372              * clear our map because they may still be in use.
    373              */
    374             RET ret = mIPA->clearAllFds();
    375             if (ret == RET::SUCCESS) {
    376                 clearHandles();
    377             }
    378         } else {
    379             ipaReturn = mIPA->clearAllFds();
    380             /* If IPA fails, they may still be using these for some reason. */
    381             if (ipaReturn == RET::SUCCESS) {
    382                 clearHandles();
    383             } else {
    384                 ALOGE("IPACM failed to return success for clearAllFds so they will not be released...");
    385             }
    386         }
    387 
    388         BoolResult res = ipaResultToBoolResult(ipaReturn);
    389         hidl_cb(res.success, res.errMsg);
    390 
    391         fl.setResult(res.success, res.errMsg);
    392         mLogs.addLog(fl);
    393     }
    394 
    395     return Void();
    396 } /* stopOffload */
    397 
    398 Return<void> HAL::setLocalPrefixes
    399 (
    400     const hidl_vec<hidl_string>& prefixes,
    401     setLocalPrefixes_cb hidl_cb
    402 ) {
    403     BoolResult res;
    404     PrefixParser parser;
    405     vector<string> prefixesStr = convertHidlStrToStdStr(prefixes);
    406 
    407     LocalLogBuffer::FunctionLog fl(__func__);
    408     fl.addArg("prefixes", prefixesStr);
    409 
    410     if (!isInitialized()) {
    411         res = makeInputCheckFailure("Not initialized");
    412     } else if(prefixesStr.size() < 1) {
    413         res = ipaResultToBoolResult(RET::FAIL_INPUT_CHECK);
    414     } else if (!parser.add(prefixesStr)) {
    415         res = makeInputCheckFailure(parser.getLastErrAsStr());
    416     } else {
    417         res = ipaResultToBoolResult(RET::SUCCESS);
    418     }
    419 
    420     hidl_cb(res.success, res.errMsg);
    421     fl.setResult(res.success, res.errMsg);
    422     mLogs.addLog(fl);
    423     return Void();
    424 } /* setLocalPrefixes */
    425 
    426 Return<void> HAL::getForwardedStats
    427 (
    428     const hidl_string& upstream,
    429     getForwardedStats_cb hidl_cb
    430 ) {
    431     LocalLogBuffer::FunctionLog fl(__func__);
    432     fl.addArg("upstream", upstream);
    433 
    434     OffloadStatistics ret;
    435     RET ipaReturn = mIPA->getStats(upstream.c_str(), true, ret);
    436     if (ipaReturn == RET::SUCCESS) {
    437         hidl_cb(ret.getTotalRxBytes(), ret.getTotalTxBytes());
    438         fl.setResult(ret.getTotalRxBytes(), ret.getTotalTxBytes());
    439     } else {
    440         /* @TODO Ensure the output is zeroed, but this is probably not enough to
    441          * tell Framework that an error has occurred.  If, for example, they had
    442          * not yet polled for statistics previously, they may incorrectly assume
    443          * that simply no statistics have transpired on hardware path.
    444          *
    445          * Maybe ITetheringOffloadCallback:onEvent(OFFLOAD_STOPPED_ERROR) is
    446          * enough to handle this case, time will tell.
    447          */
    448         hidl_cb(0, 0);
    449         fl.setResult(0, 0);
    450     }
    451 
    452     mLogs.addLog(fl);
    453     return Void();
    454 } /* getForwardedStats */
    455 
    456 Return<void> HAL::setDataLimit
    457 (
    458     const hidl_string& upstream,
    459     uint64_t limit,
    460     setDataLimit_cb hidl_cb
    461 ) {
    462     LocalLogBuffer::FunctionLog fl(__func__);
    463     fl.addArg("upstream", upstream);
    464     fl.addArg("limit", limit);
    465 
    466     if (!isInitialized()) {
    467         BoolResult res = makeInputCheckFailure("Not initialized (setDataLimit)");
    468         hidl_cb(res.success, res.errMsg);
    469         fl.setResult(res.success, res.errMsg);
    470     } else {
    471         RET ipaReturn = mIPA->setQuota(upstream.c_str(), limit);
    472         BoolResult res = ipaResultToBoolResult(ipaReturn);
    473         hidl_cb(res.success, res.errMsg);
    474         fl.setResult(res.success, res.errMsg);
    475     }
    476 
    477     mLogs.addLog(fl);
    478     return Void();
    479 } /* setDataLimit */
    480 
    481 Return<void> HAL::setUpstreamParameters
    482 (
    483     const hidl_string& iface,
    484     const hidl_string& v4Addr,
    485     const hidl_string& v4Gw,
    486     const hidl_vec<hidl_string>& v6Gws,
    487     setUpstreamParameters_cb hidl_cb
    488 ) {
    489     vector<string> v6GwStrs = convertHidlStrToStdStr(v6Gws);
    490 
    491     LocalLogBuffer::FunctionLog fl(__func__);
    492     fl.addArg("iface", iface);
    493     fl.addArg("v4Addr", v4Addr);
    494     fl.addArg("v4Gw", v4Gw);
    495     fl.addArg("v6Gws", v6GwStrs);
    496 
    497     PrefixParser v4AddrParser;
    498     PrefixParser v4GwParser;
    499     PrefixParser v6GwParser;
    500 
    501     /* @TODO maybe we should enforce that these addresses and gateways are fully
    502      * qualified here.  But then, how do we allow them to be empty/null as well
    503      * while still preserving a sane API on PrefixParser?
    504      */
    505     if (!isInitialized()) {
    506         BoolResult res = makeInputCheckFailure("Not initialized (setUpstreamParameters)");
    507         hidl_cb(res.success, res.errMsg);
    508         fl.setResult(res.success, res.errMsg);
    509     } else if (!v4AddrParser.addV4(v4Addr) && !v4Addr.empty()) {
    510         BoolResult res = makeInputCheckFailure(v4AddrParser.getLastErrAsStr());
    511         hidl_cb(res.success, res.errMsg);
    512         fl.setResult(res.success, res.errMsg);
    513     } else if (!v4GwParser.addV4(v4Gw) && !v4Gw.empty()) {
    514         BoolResult res = makeInputCheckFailure(v4GwParser.getLastErrAsStr());
    515         hidl_cb(res.success, res.errMsg);
    516         fl.setResult(res.success, res.errMsg);
    517     } else if (v6GwStrs.size() >= 1 && !v6GwParser.addV6(v6GwStrs)) {
    518         BoolResult res = makeInputCheckFailure(v6GwParser.getLastErrAsStr());
    519         hidl_cb(res.success, res.errMsg);
    520         fl.setResult(res.success, res.errMsg);
    521     } else if (iface.size()>= 1) {
    522         RET ipaReturn = mIPA->setUpstream(
    523                 iface.c_str(),
    524                 v4GwParser.getFirstPrefix(),
    525                 v6GwParser.getFirstPrefix());
    526         BoolResult res = ipaResultToBoolResult(ipaReturn);
    527         hidl_cb(res.success, res.errMsg);
    528         fl.setResult(res.success, res.errMsg);
    529     } else {
    530 	/* send NULL iface string when upstream down */
    531         RET ipaReturn = mIPA->setUpstream(
    532                 NULL,
    533                 v4GwParser.getFirstPrefix(),
    534                 v6GwParser.getFirstPrefix());
    535         BoolResult res = ipaResultToBoolResult(ipaReturn);
    536         hidl_cb(res.success, res.errMsg);
    537         fl.setResult(res.success, res.errMsg);
    538     }
    539 
    540     mLogs.addLog(fl);
    541     return Void();
    542 } /* setUpstreamParameters */
    543 
    544 Return<void> HAL::addDownstream
    545 (
    546     const hidl_string& iface,
    547     const hidl_string& prefix,
    548     addDownstream_cb hidl_cb
    549 ) {
    550     LocalLogBuffer::FunctionLog fl(__func__);
    551     fl.addArg("iface", iface);
    552     fl.addArg("prefix", prefix);
    553 
    554     PrefixParser prefixParser;
    555 
    556     if (!isInitialized()) {
    557         BoolResult res = makeInputCheckFailure("Not initialized (setUpstreamParameters)");
    558         hidl_cb(res.success, res.errMsg);
    559         fl.setResult(res.success, res.errMsg);
    560     }
    561     else if (!prefixParser.add(prefix)) {
    562         BoolResult res = makeInputCheckFailure(prefixParser.getLastErrAsStr());
    563         hidl_cb(res.success, res.errMsg);
    564         fl.setResult(res.success, res.errMsg);
    565     } else {
    566         RET ipaReturn = mIPA->addDownstream(
    567                 iface.c_str(),
    568                 prefixParser.getFirstPrefix());
    569         BoolResult res = ipaResultToBoolResult(ipaReturn);
    570         hidl_cb(res.success, res.errMsg);
    571         fl.setResult(res.success, res.errMsg);
    572     }
    573 
    574     mLogs.addLog(fl);
    575     return Void();
    576 } /* addDownstream */
    577 
    578 Return<void> HAL::removeDownstream
    579 (
    580     const hidl_string& iface,
    581     const hidl_string& prefix,
    582     removeDownstream_cb hidl_cb
    583 ) {
    584     LocalLogBuffer::FunctionLog fl(__func__);
    585     fl.addArg("iface", iface);
    586     fl.addArg("prefix", prefix);
    587 
    588     PrefixParser prefixParser;
    589 
    590     if (!isInitialized()) {
    591         BoolResult res = makeInputCheckFailure("Not initialized (setUpstreamParameters)");
    592         hidl_cb(res.success, res.errMsg);
    593         fl.setResult(res.success, res.errMsg);
    594     }
    595     else if (!prefixParser.add(prefix)) {
    596         BoolResult res = makeInputCheckFailure(prefixParser.getLastErrAsStr());
    597         hidl_cb(res.success, res.errMsg);
    598         fl.setResult(res.success, res.errMsg);
    599     } else {
    600         RET ipaReturn = mIPA->removeDownstream(
    601                 iface.c_str(),
    602                 prefixParser.getFirstPrefix());
    603         BoolResult res = ipaResultToBoolResult(ipaReturn);
    604         hidl_cb(res.success, res.errMsg);
    605         fl.setResult(res.success, res.errMsg);
    606     }
    607 
    608     mLogs.addLog(fl);
    609     return Void();
    610 } /* removeDownstream */
    611 
    612 Return<void> HAL::debug
    613 (
    614     const hidl_handle& handle,
    615     const hidl_vec<hidl_string>& /* options */
    616 ) {
    617     if (handle != nullptr && handle->numFds >= 1) {
    618         mLogs.toFd(handle->data[0]);
    619     }
    620     return Void();
    621 } /* debug */
    622