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