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