1 /*
2  * Copyright (C) 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.networkstack.tethering;
18 
19 import static android.net.netlink.StructNlMsgHdr.NLM_F_DUMP;
20 import static android.net.netlink.StructNlMsgHdr.NLM_F_REQUEST;
21 import static android.net.util.TetheringUtils.uint16;
22 
23 import android.annotation.IntDef;
24 import android.annotation.NonNull;
25 import android.hardware.tetheroffload.config.V1_0.IOffloadConfig;
26 import android.hardware.tetheroffload.control.V1_0.IOffloadControl;
27 import android.hardware.tetheroffload.control.V1_0.NatTimeoutUpdate;
28 import android.hardware.tetheroffload.control.V1_0.NetworkProtocol;
29 import android.hardware.tetheroffload.control.V1_0.OffloadCallbackEvent;
30 import android.hardware.tetheroffload.control.V1_1.ITetheringOffloadCallback;
31 import android.net.netlink.NetlinkSocket;
32 import android.net.netlink.StructNfGenMsg;
33 import android.net.netlink.StructNlMsgHdr;
34 import android.net.util.SharedLog;
35 import android.net.util.SocketUtils;
36 import android.os.Handler;
37 import android.os.NativeHandle;
38 import android.os.RemoteException;
39 import android.system.ErrnoException;
40 import android.system.Os;
41 import android.system.OsConstants;
42 import android.util.Log;
43 import android.util.Pair;
44 
45 import com.android.internal.annotations.VisibleForTesting;
46 
47 import java.io.FileDescriptor;
48 import java.io.IOException;
49 import java.io.InterruptedIOException;
50 import java.lang.annotation.Retention;
51 import java.lang.annotation.RetentionPolicy;
52 import java.net.SocketAddress;
53 import java.net.SocketException;
54 import java.nio.ByteBuffer;
55 import java.nio.ByteOrder;
56 import java.util.ArrayList;
57 import java.util.NoSuchElementException;
58 
59 
60 /**
61  * Capture tethering dependencies, for injection.
62  *
63  * @hide
64  */
65 public class OffloadHardwareInterface {
66     private static final String TAG = OffloadHardwareInterface.class.getSimpleName();
67     private static final String YIELDS = " -> ";
68     // Change this value to control whether tether offload is enabled or
69     // disabled by default in the absence of an explicit Settings value.
70     // See accompanying unittest to distinguish 0 from non-0 values.
71     private static final int DEFAULT_TETHER_OFFLOAD_DISABLED = 0;
72     private static final String NO_INTERFACE_NAME = "";
73     private static final String NO_IPV4_ADDRESS = "";
74     private static final String NO_IPV4_GATEWAY = "";
75     // Reference kernel/uapi/linux/netfilter/nfnetlink_compat.h
76     public static final int NF_NETLINK_CONNTRACK_NEW = 1;
77     public static final int NF_NETLINK_CONNTRACK_UPDATE = 2;
78     public static final int NF_NETLINK_CONNTRACK_DESTROY = 4;
79     // Reference libnetfilter_conntrack/linux_nfnetlink_conntrack.h
80     public static final short NFNL_SUBSYS_CTNETLINK = 1;
81     public static final short IPCTNL_MSG_CT_NEW = 0;
82     public static final short IPCTNL_MSG_CT_GET = 1;
83 
84     private final long NETLINK_MESSAGE_TIMEOUT_MS = 500;
85 
86     private final Handler mHandler;
87     private final SharedLog mLog;
88     private final Dependencies mDeps;
89     private IOffloadControl mOffloadControl;
90 
91     // TODO: Use major-minor version control to prevent from defining new constants.
92     static final int OFFLOAD_HAL_VERSION_NONE = 0;
93     static final int OFFLOAD_HAL_VERSION_1_0 = 1;
94     static final int OFFLOAD_HAL_VERSION_1_1 = 2;
95     /** @hide */
96     @Retention(RetentionPolicy.SOURCE)
97     @IntDef(prefix = "OFFLOAD_HAL_VERSION_", value = {
98             OFFLOAD_HAL_VERSION_NONE,
99             OFFLOAD_HAL_VERSION_1_0,
100             OFFLOAD_HAL_VERSION_1_1
101     })
102     public @interface OffloadHalVersion {}
103     @OffloadHalVersion
104     private int mOffloadControlVersion = OFFLOAD_HAL_VERSION_NONE;
105 
106     @NonNull
halVerToString(int version)107     static String halVerToString(int version) {
108         switch(version) {
109             case OFFLOAD_HAL_VERSION_1_0:
110                 return "1.0";
111             case OFFLOAD_HAL_VERSION_1_1:
112                 return "1.1";
113             case OFFLOAD_HAL_VERSION_NONE:
114                 return "None";
115             default:
116                 throw new IllegalArgumentException("Unsupported version int " + version);
117         }
118 
119     }
120 
121     private TetheringOffloadCallback mTetheringOffloadCallback;
122     private ControlCallback mControlCallback;
123 
124     /** The callback to notify status of offload management process. */
125     public static class ControlCallback {
126         /** Offload started. */
onStarted()127         public void onStarted() {}
128         /**
129          * Offload stopped because an error has occurred in lower layer.
130          */
onStoppedError()131         public void onStoppedError() {}
132         /**
133          * Offload stopped because the device has moved to a bearer on which hardware offload is
134          * not supported. Subsequent calls to setUpstreamParameters and add/removeDownstream will
135          * likely fail and cannot be presumed to be saved inside of the hardware management process.
136          * Upon receiving #onSupportAvailable(), the caller should reprogram the hardware to begin
137          * offload again.
138          */
onStoppedUnsupported()139         public void onStoppedUnsupported() {}
140         /** Indicate that offload is able to proivde support for this time. */
onSupportAvailable()141         public void onSupportAvailable() {}
142         /** Offload stopped because of usage limit reached. */
onStoppedLimitReached()143         public void onStoppedLimitReached() {}
144         /** Indicate that data warning quota is reached. */
onWarningReached()145         public void onWarningReached() {}
146 
147         /** Indicate to update NAT timeout. */
onNatTimeoutUpdate(int proto, String srcAddr, int srcPort, String dstAddr, int dstPort)148         public void onNatTimeoutUpdate(int proto,
149                                        String srcAddr, int srcPort,
150                                        String dstAddr, int dstPort) {}
151     }
152 
153     /** The object which records Tx/Rx forwarded bytes. */
154     public static class ForwardedStats {
155         public long rxBytes;
156         public long txBytes;
157 
ForwardedStats()158         public ForwardedStats() {
159             rxBytes = 0;
160             txBytes = 0;
161         }
162 
163         @VisibleForTesting
ForwardedStats(long rxBytes, long txBytes)164         public ForwardedStats(long rxBytes, long txBytes) {
165             this.rxBytes = rxBytes;
166             this.txBytes = txBytes;
167         }
168 
169         /** Add Tx/Rx bytes. */
add(ForwardedStats other)170         public void add(ForwardedStats other) {
171             rxBytes += other.rxBytes;
172             txBytes += other.txBytes;
173         }
174 
175         /** Returns the string representation of this object. */
toString()176         public String toString() {
177             return String.format("rx:%s tx:%s", rxBytes, txBytes);
178         }
179     }
180 
OffloadHardwareInterface(Handler h, SharedLog log)181     public OffloadHardwareInterface(Handler h, SharedLog log) {
182         this(h, log, new Dependencies(log));
183     }
184 
OffloadHardwareInterface(Handler h, SharedLog log, Dependencies deps)185     OffloadHardwareInterface(Handler h, SharedLog log, Dependencies deps) {
186         mHandler = h;
187         mLog = log.forSubComponent(TAG);
188         mDeps = deps;
189     }
190 
191     /** Capture OffloadHardwareInterface dependencies, for injection. */
192     static class Dependencies {
193         private final SharedLog mLog;
194 
Dependencies(SharedLog log)195         Dependencies(SharedLog log) {
196             mLog = log;
197         }
198 
getOffloadConfig()199         public IOffloadConfig getOffloadConfig() {
200             try {
201                 return IOffloadConfig.getService(true /*retry*/);
202             } catch (RemoteException | NoSuchElementException e) {
203                 mLog.e("getIOffloadConfig error " + e);
204                 return null;
205             }
206         }
207 
208         @NonNull
getOffloadControl()209         public Pair<IOffloadControl, Integer> getOffloadControl() {
210             IOffloadControl hal = null;
211             int version = OFFLOAD_HAL_VERSION_NONE;
212             try {
213                 hal = android.hardware.tetheroffload.control
214                         .V1_1.IOffloadControl.getService(true /*retry*/);
215                 version = OFFLOAD_HAL_VERSION_1_1;
216             } catch (NoSuchElementException e) {
217                 // Unsupported by device.
218             } catch (RemoteException e) {
219                 mLog.e("Unable to get offload control " + OFFLOAD_HAL_VERSION_1_1);
220             }
221             if (hal == null) {
222                 try {
223                     hal = IOffloadControl.getService(true /*retry*/);
224                     version = OFFLOAD_HAL_VERSION_1_0;
225                 } catch (NoSuchElementException e) {
226                     // Unsupported by device.
227                 } catch (RemoteException e) {
228                     mLog.e("Unable to get offload control " + OFFLOAD_HAL_VERSION_1_0);
229                 }
230             }
231             return new Pair<IOffloadControl, Integer>(hal, version);
232         }
233 
createConntrackSocket(final int groups)234         public NativeHandle createConntrackSocket(final int groups) {
235             final FileDescriptor fd;
236             try {
237                 fd = NetlinkSocket.forProto(OsConstants.NETLINK_NETFILTER);
238             } catch (ErrnoException e) {
239                 mLog.e("Unable to create conntrack socket " + e);
240                 return null;
241             }
242 
243             final SocketAddress sockAddr = SocketUtils.makeNetlinkSocketAddress(0, groups);
244             try {
245                 Os.bind(fd, sockAddr);
246             } catch (ErrnoException | SocketException e) {
247                 mLog.e("Unable to bind conntrack socket for groups " + groups + " error: " + e);
248                 try {
249                     SocketUtils.closeSocket(fd);
250                 } catch (IOException ie) {
251                     // Nothing we can do here
252                 }
253                 return null;
254             }
255             try {
256                 Os.connect(fd, sockAddr);
257             } catch (ErrnoException | SocketException e) {
258                 mLog.e("connect to kernel fail for groups " + groups + " error: " + e);
259                 try {
260                     SocketUtils.closeSocket(fd);
261                 } catch (IOException ie) {
262                     // Nothing we can do here
263                 }
264                 return null;
265             }
266 
267             return new NativeHandle(fd, true);
268         }
269     }
270 
271     /** Get default value indicating whether offload is supported. */
getDefaultTetherOffloadDisabled()272     public int getDefaultTetherOffloadDisabled() {
273         return DEFAULT_TETHER_OFFLOAD_DISABLED;
274     }
275 
276     /**
277      * Offload management process need to know conntrack rules to support NAT, but it may not have
278      * permission to create netlink netfilter sockets. Create two netlink netfilter sockets and
279      * share them with offload management process.
280      */
initOffloadConfig()281     public boolean initOffloadConfig() {
282         final IOffloadConfig offloadConfig = mDeps.getOffloadConfig();
283         if (offloadConfig == null) {
284             mLog.e("Could not find IOffloadConfig service");
285             return false;
286         }
287         // Per the IConfigOffload definition:
288         //
289         // h1    provides a file descriptor bound to the following netlink groups
290         //       (NF_NETLINK_CONNTRACK_NEW | NF_NETLINK_CONNTRACK_DESTROY).
291         //
292         // h2    provides a file descriptor bound to the following netlink groups
293         //       (NF_NETLINK_CONNTRACK_UPDATE | NF_NETLINK_CONNTRACK_DESTROY).
294         final NativeHandle h1 = mDeps.createConntrackSocket(
295                 NF_NETLINK_CONNTRACK_NEW | NF_NETLINK_CONNTRACK_DESTROY);
296         if (h1 == null) return false;
297 
298         sendIpv4NfGenMsg(h1, (short) ((NFNL_SUBSYS_CTNETLINK << 8) | IPCTNL_MSG_CT_GET),
299                            (short) (NLM_F_REQUEST | NLM_F_DUMP));
300 
301         final NativeHandle h2 = mDeps.createConntrackSocket(
302                 NF_NETLINK_CONNTRACK_UPDATE | NF_NETLINK_CONNTRACK_DESTROY);
303         if (h2 == null) {
304             closeFdInNativeHandle(h1);
305             return false;
306         }
307 
308         final CbResults results = new CbResults();
309         try {
310             offloadConfig.setHandles(h1, h2,
311                     (boolean success, String errMsg) -> {
312                         results.mSuccess = success;
313                         results.mErrMsg = errMsg;
314                     });
315         } catch (RemoteException e) {
316             record("initOffloadConfig, setHandles fail", e);
317             return false;
318         }
319         // Explicitly close FDs.
320         closeFdInNativeHandle(h1);
321         closeFdInNativeHandle(h2);
322 
323         record("initOffloadConfig, setHandles results:", results);
324         return results.mSuccess;
325     }
326 
327     @VisibleForTesting
sendIpv4NfGenMsg(@onNull NativeHandle handle, short type, short flags)328     public void sendIpv4NfGenMsg(@NonNull NativeHandle handle, short type, short flags) {
329         final int length = StructNlMsgHdr.STRUCT_SIZE + StructNfGenMsg.STRUCT_SIZE;
330         final byte[] msg = new byte[length];
331         final ByteBuffer byteBuffer = ByteBuffer.wrap(msg);
332         byteBuffer.order(ByteOrder.nativeOrder());
333 
334         final StructNlMsgHdr nlh = new StructNlMsgHdr();
335         nlh.nlmsg_len = length;
336         nlh.nlmsg_type = type;
337         nlh.nlmsg_flags = flags;
338         nlh.nlmsg_seq = 0;
339         nlh.pack(byteBuffer);
340 
341         // Header needs to be added to buffer since a generic netlink request is being sent.
342         final StructNfGenMsg nfh = new StructNfGenMsg((byte) OsConstants.AF_INET);
343         nfh.pack(byteBuffer);
344 
345         try {
346             NetlinkSocket.sendMessage(handle.getFileDescriptor(), msg, 0 /* offset */, length,
347                                       NETLINK_MESSAGE_TIMEOUT_MS);
348         } catch (ErrnoException | InterruptedIOException e) {
349             mLog.e("Unable to send netfilter message, error: " + e);
350         }
351     }
352 
closeFdInNativeHandle(final NativeHandle h)353     private void closeFdInNativeHandle(final NativeHandle h) {
354         try {
355             h.close();
356         } catch (IOException | IllegalStateException e) {
357             // IllegalStateException means fd is already closed, do nothing here.
358             // Also nothing we can do if IOException.
359         }
360     }
361 
362     /**
363      * Initialize the tethering offload HAL.
364      *
365      * @return one of {@code OFFLOAD_HAL_VERSION_*} represents the HAL version, or
366      *         {@link #OFFLOAD_HAL_VERSION_NONE} if failed.
367      */
initOffloadControl(ControlCallback controlCb)368     public int initOffloadControl(ControlCallback controlCb) {
369         mControlCallback = controlCb;
370 
371         if (mOffloadControl == null) {
372             final Pair<IOffloadControl, Integer> halAndVersion = mDeps.getOffloadControl();
373             mOffloadControl = halAndVersion.first;
374             mOffloadControlVersion = halAndVersion.second;
375             if (mOffloadControl == null) {
376                 mLog.e("tethering IOffloadControl.getService() returned null");
377                 return OFFLOAD_HAL_VERSION_NONE;
378             }
379             mLog.i("tethering offload control version "
380                     + halVerToString(mOffloadControlVersion) + " is supported.");
381         }
382 
383         final String logmsg = String.format("initOffloadControl(%s)",
384                 (controlCb == null) ? "null"
385                         : "0x" + Integer.toHexString(System.identityHashCode(controlCb)));
386 
387         mTetheringOffloadCallback = new TetheringOffloadCallback(
388                 mHandler, mControlCallback, mLog, mOffloadControlVersion);
389         final CbResults results = new CbResults();
390         try {
391             mOffloadControl.initOffload(
392                     mTetheringOffloadCallback,
393                     (boolean success, String errMsg) -> {
394                         results.mSuccess = success;
395                         results.mErrMsg = errMsg;
396                     });
397         } catch (RemoteException e) {
398             record(logmsg, e);
399             return OFFLOAD_HAL_VERSION_NONE;
400         }
401 
402         record(logmsg, results);
403         return results.mSuccess ? mOffloadControlVersion : OFFLOAD_HAL_VERSION_NONE;
404     }
405 
406     /** Stop IOffloadControl. */
stopOffloadControl()407     public void stopOffloadControl() {
408         if (mOffloadControl != null) {
409             try {
410                 mOffloadControl.stopOffload(
411                         (boolean success, String errMsg) -> {
412                             if (!success) mLog.e("stopOffload failed: " + errMsg);
413                         });
414             } catch (RemoteException e) {
415                 mLog.e("failed to stopOffload: " + e);
416             }
417         }
418         mOffloadControl = null;
419         mTetheringOffloadCallback = null;
420         mControlCallback = null;
421         mLog.log("stopOffloadControl()");
422     }
423 
424     /** Get Tx/Rx usage from last query. */
getForwardedStats(String upstream)425     public ForwardedStats getForwardedStats(String upstream) {
426         final String logmsg = String.format("getForwardedStats(%s)",  upstream);
427 
428         final ForwardedStats stats = new ForwardedStats();
429         try {
430             mOffloadControl.getForwardedStats(
431                     upstream,
432                     (long rxBytes, long txBytes) -> {
433                         stats.rxBytes = (rxBytes > 0) ? rxBytes : 0;
434                         stats.txBytes = (txBytes > 0) ? txBytes : 0;
435                     });
436         } catch (RemoteException e) {
437             record(logmsg, e);
438             return stats;
439         }
440 
441         return stats;
442     }
443 
444     /** Set local prefixes to offload management process. */
setLocalPrefixes(ArrayList<String> localPrefixes)445     public boolean setLocalPrefixes(ArrayList<String> localPrefixes) {
446         final String logmsg = String.format("setLocalPrefixes([%s])",
447                 String.join(",", localPrefixes));
448 
449         final CbResults results = new CbResults();
450         try {
451             mOffloadControl.setLocalPrefixes(localPrefixes,
452                     (boolean success, String errMsg) -> {
453                         results.mSuccess = success;
454                         results.mErrMsg = errMsg;
455                     });
456         } catch (RemoteException e) {
457             record(logmsg, e);
458             return false;
459         }
460 
461         record(logmsg, results);
462         return results.mSuccess;
463     }
464 
465     /** Set data limit value to offload management process. */
setDataLimit(String iface, long limit)466     public boolean setDataLimit(String iface, long limit) {
467 
468         final String logmsg = String.format("setDataLimit(%s, %d)", iface, limit);
469 
470         final CbResults results = new CbResults();
471         try {
472             mOffloadControl.setDataLimit(
473                     iface, limit,
474                     (boolean success, String errMsg) -> {
475                         results.mSuccess = success;
476                         results.mErrMsg = errMsg;
477                     });
478         } catch (RemoteException e) {
479             record(logmsg, e);
480             return false;
481         }
482 
483         record(logmsg, results);
484         return results.mSuccess;
485     }
486 
487     /** Set data warning and limit value to offload management process. */
setDataWarningAndLimit(String iface, long warning, long limit)488     public boolean setDataWarningAndLimit(String iface, long warning, long limit) {
489         if (mOffloadControlVersion < OFFLOAD_HAL_VERSION_1_1) {
490             throw new IllegalArgumentException(
491                     "setDataWarningAndLimit is not supported below HAL V1.1");
492         }
493         final String logmsg =
494                 String.format("setDataWarningAndLimit(%s, %d, %d)", iface, warning, limit);
495 
496         final CbResults results = new CbResults();
497         try {
498             ((android.hardware.tetheroffload.control.V1_1.IOffloadControl) mOffloadControl)
499                     .setDataWarningAndLimit(
500                             iface, warning, limit,
501                             (boolean success, String errMsg) -> {
502                                 results.mSuccess = success;
503                                 results.mErrMsg = errMsg;
504                             });
505         } catch (RemoteException e) {
506             record(logmsg, e);
507             return false;
508         }
509 
510         record(logmsg, results);
511         return results.mSuccess;
512     }
513 
514     /** Set upstream parameters to offload management process. */
setUpstreamParameters( String iface, String v4addr, String v4gateway, ArrayList<String> v6gws)515     public boolean setUpstreamParameters(
516             String iface, String v4addr, String v4gateway, ArrayList<String> v6gws) {
517         iface = (iface != null) ? iface : NO_INTERFACE_NAME;
518         v4addr = (v4addr != null) ? v4addr : NO_IPV4_ADDRESS;
519         v4gateway = (v4gateway != null) ? v4gateway : NO_IPV4_GATEWAY;
520         v6gws = (v6gws != null) ? v6gws : new ArrayList<>();
521 
522         final String logmsg = String.format("setUpstreamParameters(%s, %s, %s, [%s])",
523                 iface, v4addr, v4gateway, String.join(",", v6gws));
524 
525         final CbResults results = new CbResults();
526         try {
527             mOffloadControl.setUpstreamParameters(
528                     iface, v4addr, v4gateway, v6gws,
529                     (boolean success, String errMsg) -> {
530                         results.mSuccess = success;
531                         results.mErrMsg = errMsg;
532                     });
533         } catch (RemoteException e) {
534             record(logmsg, e);
535             return false;
536         }
537 
538         record(logmsg, results);
539         return results.mSuccess;
540     }
541 
542     /** Add downstream prefix to offload management process. */
addDownstreamPrefix(String ifname, String prefix)543     public boolean addDownstreamPrefix(String ifname, String prefix) {
544         final String logmsg = String.format("addDownstreamPrefix(%s, %s)", ifname, prefix);
545 
546         final CbResults results = new CbResults();
547         try {
548             mOffloadControl.addDownstream(ifname, prefix,
549                     (boolean success, String errMsg) -> {
550                         results.mSuccess = success;
551                         results.mErrMsg = errMsg;
552                     });
553         } catch (RemoteException e) {
554             record(logmsg, e);
555             return false;
556         }
557 
558         record(logmsg, results);
559         return results.mSuccess;
560     }
561 
562     /** Remove downstream prefix from offload management process. */
removeDownstreamPrefix(String ifname, String prefix)563     public boolean removeDownstreamPrefix(String ifname, String prefix) {
564         final String logmsg = String.format("removeDownstreamPrefix(%s, %s)", ifname, prefix);
565 
566         final CbResults results = new CbResults();
567         try {
568             mOffloadControl.removeDownstream(ifname, prefix,
569                     (boolean success, String errMsg) -> {
570                         results.mSuccess = success;
571                         results.mErrMsg = errMsg;
572                     });
573         } catch (RemoteException e) {
574             record(logmsg, e);
575             return false;
576         }
577 
578         record(logmsg, results);
579         return results.mSuccess;
580     }
581 
record(String msg, Throwable t)582     private void record(String msg, Throwable t) {
583         mLog.e(msg + YIELDS + "exception: " + t);
584     }
585 
record(String msg, CbResults results)586     private void record(String msg, CbResults results) {
587         final String logmsg = msg + YIELDS + results;
588         if (!results.mSuccess) {
589             mLog.e(logmsg);
590         } else {
591             mLog.log(logmsg);
592         }
593     }
594 
595     private static class TetheringOffloadCallback extends ITetheringOffloadCallback.Stub {
596         public final Handler handler;
597         public final ControlCallback controlCb;
598         public final SharedLog log;
599         private final int mOffloadControlVersion;
600 
TetheringOffloadCallback( Handler h, ControlCallback cb, SharedLog sharedLog, int offloadControlVersion)601         TetheringOffloadCallback(
602                 Handler h, ControlCallback cb, SharedLog sharedLog, int offloadControlVersion) {
603             handler = h;
604             controlCb = cb;
605             log = sharedLog;
606             this.mOffloadControlVersion = offloadControlVersion;
607         }
608 
handleOnEvent(int event)609         private void handleOnEvent(int event) {
610             switch (event) {
611                 case OffloadCallbackEvent.OFFLOAD_STARTED:
612                     controlCb.onStarted();
613                     break;
614                 case OffloadCallbackEvent.OFFLOAD_STOPPED_ERROR:
615                     controlCb.onStoppedError();
616                     break;
617                 case OffloadCallbackEvent.OFFLOAD_STOPPED_UNSUPPORTED:
618                     controlCb.onStoppedUnsupported();
619                     break;
620                 case OffloadCallbackEvent.OFFLOAD_SUPPORT_AVAILABLE:
621                     controlCb.onSupportAvailable();
622                     break;
623                 case OffloadCallbackEvent.OFFLOAD_STOPPED_LIMIT_REACHED:
624                     controlCb.onStoppedLimitReached();
625                     break;
626                 case android.hardware.tetheroffload.control
627                         .V1_1.OffloadCallbackEvent.OFFLOAD_WARNING_REACHED:
628                     controlCb.onWarningReached();
629                     break;
630                 default:
631                     log.e("Unsupported OffloadCallbackEvent: " + event);
632             }
633         }
634 
635         @Override
onEvent(int event)636         public void onEvent(int event) {
637             // The implementation should never call onEvent()) if the event is already reported
638             // through newer callback.
639             if (mOffloadControlVersion > OFFLOAD_HAL_VERSION_1_0) {
640                 Log.wtf(TAG, "onEvent(" + event + ") fired on HAL "
641                         + halVerToString(mOffloadControlVersion));
642             }
643             handler.post(() -> {
644                 handleOnEvent(event);
645             });
646         }
647 
648         @Override
onEvent_1_1(int event)649         public void onEvent_1_1(int event) {
650             if (mOffloadControlVersion < OFFLOAD_HAL_VERSION_1_1) {
651                 Log.wtf(TAG, "onEvent_1_1(" + event + ") fired on HAL "
652                         + halVerToString(mOffloadControlVersion));
653                 return;
654             }
655             handler.post(() -> {
656                 handleOnEvent(event);
657             });
658         }
659 
660         @Override
updateTimeout(NatTimeoutUpdate params)661         public void updateTimeout(NatTimeoutUpdate params) {
662             handler.post(() -> {
663                 controlCb.onNatTimeoutUpdate(
664                         networkProtocolToOsConstant(params.proto),
665                         params.src.addr, uint16(params.src.port),
666                         params.dst.addr, uint16(params.dst.port));
667             });
668         }
669     }
670 
networkProtocolToOsConstant(int proto)671     private static int networkProtocolToOsConstant(int proto) {
672         switch (proto) {
673             case NetworkProtocol.TCP: return OsConstants.IPPROTO_TCP;
674             case NetworkProtocol.UDP: return OsConstants.IPPROTO_UDP;
675             default:
676                 // The caller checks this value and will log an error. Just make
677                 // sure it won't collide with valid OsContants.IPPROTO_* values.
678                 return -Math.abs(proto);
679         }
680     }
681 
682     private static class CbResults {
683         boolean mSuccess;
684         String mErrMsg;
685 
686         @Override
toString()687         public String toString() {
688             if (mSuccess) {
689                 return "ok";
690             } else {
691                 return "fail: " + mErrMsg;
692             }
693         }
694     }
695 }
696