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 ------------------------------------ */
makeIPAHAL(int version,IOffloadManager * mgr)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 ----------------------------------- */
HAL(IOffloadManager * mgr)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 
registerAsSystemService()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 
doLogcatDump()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 
makeInputCheckFailure(string customErr)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 
ipaResultToBoolResult(RET in)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  */
convertHidlStrToStdStr(hidl_vec<hidl_string> in)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 
registerEventListeners()173 void HAL::registerEventListeners() {
174     registerIpaCb();
175     registerCtCb();
176 } /* registerEventListeners */
177 
registerIpaCb()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 
registerCtCb()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 
unregisterEventListeners()204 void HAL::unregisterEventListeners() {
205     unregisterIpaCb();
206     unregisterCtCb();
207 } /* unregisterEventListeners */
208 
unregisterIpaCb()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 
unregisterCtCb()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 
clearHandles()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 
isInitialized()246 bool HAL::isInitialized() {
247     return mCb.get() != nullptr;
248 } /* isInitialized */
249 
250 
251 /* -------------------------- IOffloadConfig -------------------------------- */
setHandles(const hidl_handle & fd1,const hidl_handle & fd2,setHandles_cb hidl_cb)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 ------------------------------- */
initOffload(const::android::sp<ITetheringOffloadCallback> & cb,initOffload_cb hidl_cb)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 
stopOffload(stopOffload_cb hidl_cb)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 
setLocalPrefixes(const hidl_vec<hidl_string> & prefixes,setLocalPrefixes_cb hidl_cb)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 
getForwardedStats(const hidl_string & upstream,getForwardedStats_cb hidl_cb)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 
setDataLimit(const hidl_string & upstream,uint64_t limit,setDataLimit_cb hidl_cb)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 
setUpstreamParameters(const hidl_string & iface,const hidl_string & v4Addr,const hidl_string & v4Gw,const hidl_vec<hidl_string> & v6Gws,setUpstreamParameters_cb hidl_cb)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 
addDownstream(const hidl_string & iface,const hidl_string & prefix,addDownstream_cb hidl_cb)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 
removeDownstream(const hidl_string & iface,const hidl_string & prefix,removeDownstream_cb hidl_cb)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 
debug(const hidl_handle & handle,const hidl_vec<hidl_string> &)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