1 /* 2 * Copyright (C) 2019 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 package com.android.networkstack.netlink; 17 18 import static android.net.netlink.InetDiagMessage.InetDiagReqV2; 19 import static android.net.netlink.NetlinkConstants.INET_DIAG_MEMINFO; 20 import static android.net.netlink.NetlinkConstants.NLMSG_DONE; 21 import static android.net.netlink.NetlinkConstants.SOCKDIAG_MSG_HEADER_SIZE; 22 import static android.net.netlink.NetlinkConstants.SOCK_DIAG_BY_FAMILY; 23 import static android.net.netlink.StructNlMsgHdr.NLM_F_DUMP; 24 import static android.net.netlink.StructNlMsgHdr.NLM_F_REQUEST; 25 import static android.net.util.DataStallUtils.CONFIG_MIN_PACKETS_THRESHOLD; 26 import static android.net.util.DataStallUtils.CONFIG_TCP_PACKETS_FAIL_PERCENTAGE; 27 import static android.net.util.DataStallUtils.DEFAULT_DATA_STALL_MIN_PACKETS_THRESHOLD; 28 import static android.net.util.DataStallUtils.DEFAULT_TCP_PACKETS_FAIL_PERCENTAGE; 29 import static android.net.util.DataStallUtils.TCP_MONITOR_STATE_FILTER; 30 import static android.provider.DeviceConfig.NAMESPACE_CONNECTIVITY; 31 import static android.system.OsConstants.AF_INET; 32 import static android.system.OsConstants.AF_INET6; 33 import static android.system.OsConstants.AF_NETLINK; 34 import static android.system.OsConstants.IPPROTO_TCP; 35 import static android.system.OsConstants.NETLINK_INET_DIAG; 36 import static android.system.OsConstants.SOCK_CLOEXEC; 37 import static android.system.OsConstants.SOCK_DGRAM; 38 import static android.system.OsConstants.SOL_SOCKET; 39 import static android.system.OsConstants.SO_SNDTIMEO; 40 41 import android.content.Context; 42 import android.net.INetd; 43 import android.net.MarkMaskParcel; 44 import android.net.Network; 45 import android.net.netlink.NetlinkConstants; 46 import android.net.netlink.NetlinkSocket; 47 import android.net.netlink.StructInetDiagMsg; 48 import android.net.netlink.StructNlMsgHdr; 49 import android.net.util.NetworkStackUtils; 50 import android.net.util.SocketUtils; 51 import android.os.AsyncTask; 52 import android.os.Build; 53 import android.os.IBinder; 54 import android.os.RemoteException; 55 import android.os.SystemClock; 56 import android.provider.DeviceConfig; 57 import android.system.ErrnoException; 58 import android.system.Os; 59 import android.system.StructTimeval; 60 import android.util.Log; 61 import android.util.LongSparseArray; 62 import android.util.SparseArray; 63 64 import androidx.annotation.NonNull; 65 import androidx.annotation.Nullable; 66 67 import com.android.internal.annotations.VisibleForTesting; 68 import com.android.networkstack.apishim.NetworkShimImpl; 69 import com.android.networkstack.apishim.common.ShimUtils; 70 import com.android.networkstack.apishim.common.UnsupportedApiLevelException; 71 72 import java.io.FileDescriptor; 73 import java.io.InterruptedIOException; 74 import java.net.SocketException; 75 import java.nio.BufferUnderflowException; 76 import java.nio.ByteBuffer; 77 import java.util.ArrayList; 78 import java.util.Base64; 79 import java.util.List; 80 81 /** 82 * Class for NetworkStack to send a SockDiag request and parse the returned tcp info. 83 * 84 * This is not thread-safe. This should be only accessed from one thread. 85 */ 86 public class TcpSocketTracker { 87 private static final String TAG = "TcpSocketTracker"; 88 private static final boolean DBG = false; 89 private static final int[] ADDRESS_FAMILIES = new int[] {AF_INET6, AF_INET}; 90 // Enough for parsing v1 tcp_info for more than 200 sockets per time. 91 private static final int DEFAULT_RECV_BUFSIZE = 60_000; 92 // Default I/O timeout time in ms of the socket request. 93 private static final long IO_TIMEOUT = 3_000L; 94 /** Cookie offset of an InetMagMessage header. */ 95 private static final int IDIAG_COOKIE_OFFSET = 44; 96 private static final int UNKNOWN_MARK = 0xffffffff; 97 private static final int NULL_MASK = 0; 98 /** 99 * Gather the socket info. 100 * 101 * Key: The idiag_cookie value of the socket. See struct inet_diag_sockid in 102 * <linux_src>/include/uapi/linux/inet_diag.h 103 * Value: See {@Code SocketInfo} 104 */ 105 private final LongSparseArray<SocketInfo> mSocketInfos = new LongSparseArray<>(); 106 // Number of packets sent since the last received packet 107 private int mSentSinceLastRecv; 108 // The latest fail rate calculated by the latest tcp info. 109 private int mLatestPacketFailPercentage; 110 // Number of packets received in the latest polling cycle. 111 private int mLatestReceivedCount; 112 /** 113 * Request to send to kernel to request tcp info. 114 * 115 * Key: Ip family type. 116 * Value: Bytes array represent the {@Code InetDiagReqV2}. 117 */ 118 private final SparseArray<byte[]> mSockDiagMsg = new SparseArray<>(); 119 private final Dependencies mDependencies; 120 private final INetd mNetd; 121 private final Network mNetwork; 122 // The fwmark value of {@code mNetwork}. 123 private final int mNetworkMark; 124 // The network id mask of fwmark. 125 private final int mNetworkMask; 126 private int mMinPacketsThreshold = DEFAULT_DATA_STALL_MIN_PACKETS_THRESHOLD; 127 private int mTcpPacketsFailRateThreshold = DEFAULT_TCP_PACKETS_FAIL_PERCENTAGE; 128 @VisibleForTesting 129 protected final DeviceConfig.OnPropertiesChangedListener mConfigListener = 130 new DeviceConfig.OnPropertiesChangedListener() { 131 @Override 132 public void onPropertiesChanged(DeviceConfig.Properties properties) { 133 mMinPacketsThreshold = mDependencies.getDeviceConfigPropertyInt( 134 NAMESPACE_CONNECTIVITY, 135 CONFIG_MIN_PACKETS_THRESHOLD, 136 DEFAULT_DATA_STALL_MIN_PACKETS_THRESHOLD); 137 mTcpPacketsFailRateThreshold = mDependencies.getDeviceConfigPropertyInt( 138 NAMESPACE_CONNECTIVITY, 139 CONFIG_TCP_PACKETS_FAIL_PERCENTAGE, 140 DEFAULT_TCP_PACKETS_FAIL_PERCENTAGE); 141 } 142 }; 143 TcpSocketTracker(@onNull final Dependencies dps, @NonNull final Network network)144 public TcpSocketTracker(@NonNull final Dependencies dps, @NonNull final Network network) { 145 mDependencies = dps; 146 mNetwork = network; 147 mNetd = mDependencies.getNetd(); 148 149 // If the parcel is null, nothing should be matched which is achieved by the combination of 150 // {@code NULL_MASK} and {@code UNKNOWN_MARK}. 151 final MarkMaskParcel parcel = getNetworkMarkMask(); 152 mNetworkMark = (parcel != null) ? parcel.mark : UNKNOWN_MARK; 153 mNetworkMask = (parcel != null) ? parcel.mask : NULL_MASK; 154 155 // Request tcp info from NetworkStack directly needs extra SELinux permission added after Q 156 // release. 157 if (!mDependencies.isTcpInfoParsingSupported()) return; 158 // Build SocketDiag messages. 159 for (final int family : ADDRESS_FAMILIES) { 160 mSockDiagMsg.put( 161 family, 162 InetDiagReqV2(IPPROTO_TCP, 163 null /* local addr */, 164 null /* remote addr */, 165 family, 166 (short) (NLM_F_REQUEST | NLM_F_DUMP) /* flag */, 167 0 /* pad */, 168 1 << INET_DIAG_MEMINFO /* idiagExt */, 169 TCP_MONITOR_STATE_FILTER)); 170 } 171 mDependencies.addDeviceConfigChangedListener(mConfigListener); 172 } 173 174 @Nullable getNetworkMarkMask()175 private MarkMaskParcel getNetworkMarkMask() { 176 try { 177 final int netId = NetworkShimImpl.newInstance(mNetwork).getNetId(); 178 return mNetd.getFwmarkForNetwork(netId); 179 } catch (UnsupportedApiLevelException e) { 180 log("Get netId is not available in this API level."); 181 } catch (RemoteException e) { 182 Log.e(TAG, "Error getting fwmark for network, ", e); 183 } 184 return null; 185 } 186 187 /** 188 * Request to send a SockDiag Netlink request. Receive and parse the returned message. This 189 * function is not thread-safe and should only be called from only one thread. 190 * 191 * @Return if this polling request executes successfully or not. 192 */ pollSocketsInfo()193 public boolean pollSocketsInfo() { 194 if (!mDependencies.isTcpInfoParsingSupported()) return false; 195 FileDescriptor fd = null; 196 try { 197 final long time = SystemClock.elapsedRealtime(); 198 fd = mDependencies.connectToKernel(); 199 200 final TcpStat stat = new TcpStat(); 201 for (final int family : ADDRESS_FAMILIES) { 202 mDependencies.sendPollingRequest(fd, mSockDiagMsg.get(family)); 203 // Messages are composed with the following format. Stop parsing when receiving 204 // message with nlmsg_type NLMSG_DONE. 205 // +------------------+---------------+--------------+--------+ 206 // | Netlink Header | Family Header | Attributes | rtattr | 207 // | struct nlmsghdr | struct rtmsg | struct rtattr| data | 208 // +------------------+---------------+--------------+--------+ 209 // : : : 210 // +------------------+---------------+--------------+--------+ 211 // | Netlink Header | Family Header | Attributes | rtattr | 212 // | struct nlmsghdr | struct rtmsg | struct rtattr| data | 213 // +------------------+---------------+--------------+--------+ 214 final ByteBuffer bytes = mDependencies.recvMessage(fd); 215 try { 216 while (enoughBytesRemainForValidNlMsg(bytes)) { 217 final StructNlMsgHdr nlmsghdr = StructNlMsgHdr.parse(bytes); 218 if (nlmsghdr == null) { 219 Log.e(TAG, "Badly formatted data."); 220 break; 221 } 222 final int nlmsgLen = nlmsghdr.nlmsg_len; 223 log("pollSocketsInfo: nlmsghdr=" + nlmsghdr + ", limit=" + bytes.limit()); 224 // End of the message. Stop parsing. 225 if (nlmsghdr.nlmsg_type == NLMSG_DONE) break; 226 227 if (nlmsghdr.nlmsg_type != SOCK_DIAG_BY_FAMILY) { 228 Log.e(TAG, "Expect to get family " + family 229 + " SOCK_DIAG_BY_FAMILY message but get " 230 + nlmsghdr.nlmsg_type); 231 break; 232 } 233 234 if (isValidInetDiagMsgSize(nlmsgLen)) { 235 // Get the socket cookie value. Composed by two Integers value. 236 // Corresponds to inet_diag_sockid in 237 // <linux_src>/include/uapi/linux/inet_diag.h 238 bytes.position(bytes.position() + IDIAG_COOKIE_OFFSET); 239 // It's stored in native with 2 int. Parse it as long for convenience. 240 final long cookie = bytes.getLong(); 241 // Skip the rest part of StructInetDiagMsg. 242 bytes.position(bytes.position() 243 + StructInetDiagMsg.STRUCT_SIZE - IDIAG_COOKIE_OFFSET 244 - Long.BYTES); 245 final SocketInfo info = parseSockInfo(bytes, family, nlmsgLen, time); 246 // Update TcpStats based on previous and current socket info. 247 stat.accumulate( 248 calculateLatestPacketsStat(info, mSocketInfos.get(cookie))); 249 mSocketInfos.put(cookie, info); 250 } 251 } 252 } catch (IllegalArgumentException | BufferUnderflowException e) { 253 Log.wtf(TAG, "Unexpected socket info parsing, family " + family 254 + " buffer:" + bytes + " " 255 + Base64.getEncoder().encodeToString(bytes.array()), e); 256 } 257 } 258 // Calculate mLatestReceiveCount, mSentSinceLastRecv and mLatestPacketFailPercentage. 259 mSentSinceLastRecv = (stat.receivedCount == 0) 260 ? (mSentSinceLastRecv + stat.sentCount) : 0; 261 mLatestReceivedCount = stat.receivedCount; 262 mLatestPacketFailPercentage = ((stat.sentCount != 0) 263 ? ((stat.retransmitCount + stat.lostCount) * 100 / stat.sentCount) : 0); 264 265 // Remove out-of-date socket info. 266 cleanupSocketInfo(time); 267 return true; 268 } catch (ErrnoException | SocketException | InterruptedIOException e) { 269 Log.e(TAG, "Fail to get TCP info via netlink.", e); 270 } finally { 271 NetworkStackUtils.closeSocketQuietly(fd); 272 } 273 274 return false; 275 } 276 cleanupSocketInfo(final long time)277 private void cleanupSocketInfo(final long time) { 278 final int size = mSocketInfos.size(); 279 final List<Long> toRemove = new ArrayList<Long>(); 280 for (int i = 0; i < size; i++) { 281 final long key = mSocketInfos.keyAt(i); 282 if (mSocketInfos.get(key).updateTime < time) { 283 toRemove.add(key); 284 } 285 } 286 for (final Long key : toRemove) { 287 mSocketInfos.remove(key); 288 } 289 } 290 291 /** Parse a {@code SocketInfo} from the given position of the given byte buffer. */ 292 @VisibleForTesting 293 @NonNull parseSockInfo(@onNull final ByteBuffer bytes, final int family, final int nlmsgLen, final long time)294 SocketInfo parseSockInfo(@NonNull final ByteBuffer bytes, final int family, 295 final int nlmsgLen, final long time) { 296 final int remainingDataSize = bytes.position() + nlmsgLen - SOCKDIAG_MSG_HEADER_SIZE; 297 TcpInfo tcpInfo = null; 298 int mark = SocketInfo.INIT_MARK_VALUE; 299 // Get a tcp_info. 300 while (bytes.position() < remainingDataSize) { 301 final RoutingAttribute rtattr = 302 new RoutingAttribute(bytes.getShort(), bytes.getShort()); 303 final short dataLen = rtattr.getDataLength(); 304 if (rtattr.rtaType == RoutingAttribute.INET_DIAG_INFO) { 305 tcpInfo = TcpInfo.parse(bytes, dataLen); 306 } else if (rtattr.rtaType == RoutingAttribute.INET_DIAG_MARK) { 307 mark = bytes.getInt(); 308 } else { 309 // Data provided by kernel will include both valid data and padding data. The data 310 // len provided from kernel indicates the valid data size. Readers must deduce the 311 // alignment by themselves. 312 skipRemainingAttributesBytesAligned(bytes, dataLen); 313 } 314 } 315 final SocketInfo info = new SocketInfo(tcpInfo, family, mark, time); 316 log("parseSockInfo, " + info); 317 return info; 318 } 319 320 /** 321 * Return if data stall is suspected or not by checking the latest tcp connection fail rate. 322 * Expect to check after polling the latest status. This function should only be called from 323 * statemachine thread of NetworkMonitor. 324 */ isDataStallSuspected()325 public boolean isDataStallSuspected() { 326 if (!mDependencies.isTcpInfoParsingSupported()) return false; 327 return (getLatestPacketFailPercentage() >= getTcpPacketsFailRateThreshold()); 328 } 329 330 /** Calculate the change between the {@param current} and {@param previous}. */ 331 @Nullable calculateLatestPacketsStat(@onNull final SocketInfo current, @Nullable final SocketInfo previous)332 private TcpStat calculateLatestPacketsStat(@NonNull final SocketInfo current, 333 @Nullable final SocketInfo previous) { 334 final TcpStat stat = new TcpStat(); 335 // Ignore non-target network sockets. 336 if ((current.fwmark & mNetworkMask) != mNetworkMark) { 337 return null; 338 } 339 340 if (current.tcpInfo == null) { 341 log("Current tcpInfo is null."); 342 return null; 343 } 344 345 stat.sentCount = current.tcpInfo.mSegsOut; 346 stat.receivedCount = current.tcpInfo.mSegsIn; 347 stat.lostCount = current.tcpInfo.mLost; 348 stat.retransmitCount = current.tcpInfo.mRetransmits; 349 350 if (previous != null && previous.tcpInfo != null) { 351 stat.sentCount -= previous.tcpInfo.mSegsOut; 352 stat.receivedCount -= previous.tcpInfo.mSegsIn; 353 stat.lostCount -= previous.tcpInfo.mLost; 354 stat.retransmitCount -= previous.tcpInfo.mRetransmits; 355 } 356 357 return stat; 358 } 359 360 /** 361 * Get tcp connection fail rate based on packet lost and retransmission count. 362 * 363 * @return the latest packet fail percentage. -1 denotes that there is no available data. 364 */ getLatestPacketFailPercentage()365 public int getLatestPacketFailPercentage() { 366 if (!mDependencies.isTcpInfoParsingSupported()) return -1; 367 // Only return fail rate if device sent enough packets. 368 if (getSentSinceLastRecv() < getMinPacketsThreshold()) return -1; 369 return mLatestPacketFailPercentage; 370 } 371 372 /** 373 * Return the number of packets sent since last received. Note that this number is calculated 374 * between each polling period, not an accurate number. 375 */ getSentSinceLastRecv()376 public int getSentSinceLastRecv() { 377 if (!mDependencies.isTcpInfoParsingSupported()) return -1; 378 return mSentSinceLastRecv; 379 } 380 381 /** Return the number of the packets received in the latest polling cycle. */ getLatestReceivedCount()382 public int getLatestReceivedCount() { 383 if (!mDependencies.isTcpInfoParsingSupported()) return -1; 384 return mLatestReceivedCount; 385 } 386 387 /** Check if the length and position of the given ByteBuffer is valid for a nlmsghdr message. */ 388 @VisibleForTesting enoughBytesRemainForValidNlMsg(@onNull final ByteBuffer bytes)389 static boolean enoughBytesRemainForValidNlMsg(@NonNull final ByteBuffer bytes) { 390 return bytes.remaining() >= StructNlMsgHdr.STRUCT_SIZE; 391 } 392 isValidInetDiagMsgSize(final int nlMsgLen)393 private static boolean isValidInetDiagMsgSize(final int nlMsgLen) { 394 return nlMsgLen >= SOCKDIAG_MSG_HEADER_SIZE; 395 } 396 getMinPacketsThreshold()397 private int getMinPacketsThreshold() { 398 return mMinPacketsThreshold; 399 } 400 getTcpPacketsFailRateThreshold()401 private int getTcpPacketsFailRateThreshold() { 402 return mTcpPacketsFailRateThreshold; 403 } 404 405 /** 406 * Method to skip the remaining attributes bytes. 407 * Corresponds to NLMSG_NEXT in bionic/libc/kernel/uapi/linux/netlink.h. 408 * 409 * @param buffer the target ByteBuffer 410 * @param len the remaining length to skip. 411 */ skipRemainingAttributesBytesAligned(@onNull final ByteBuffer buffer, final short len)412 private void skipRemainingAttributesBytesAligned(@NonNull final ByteBuffer buffer, 413 final short len) { 414 // Data in {@Code RoutingAttribute} is followed after header with size {@Code NLA_ALIGNTO} 415 // bytes long for each block. Next attribute will start after the padding bytes if any. 416 // If all remaining bytes after header are valid in a data block, next attr will just start 417 // after valid bytes. 418 // 419 // E.g. With NLA_ALIGNTO(4), an attr struct with length 5 means 1 byte valid data remains 420 // after header and 3(4-1) padding bytes. Next attr with length 8 will start after the 421 // padding bytes and contain 4(8-4) valid bytes of data. The next attr start after the 422 // valid bytes, like: 423 // 424 // [HEADER(L=5)][ 4-Bytes DATA ][ HEADER(L=8) ][4 bytes DATA][Next attr] 425 // [ 5 valid bytes ][3 padding bytes ][ 8 valid bytes ] ... 426 final int cur = buffer.position(); 427 buffer.position(cur + NetlinkConstants.alignedLengthOf(len)); 428 } 429 log(final String str)430 private void log(final String str) { 431 if (DBG) Log.d(TAG, str); 432 } 433 434 /** 435 * Corresponds to {@code struct rtattr} from bionic/libc/kernel/uapi/linux/rtnetlink.h 436 * 437 * struct rtattr { 438 * unsigned short rta_len; // Length of option 439 * unsigned short rta_type; // Type of option 440 * // Data follows 441 * }; 442 */ 443 class RoutingAttribute { 444 public static final int HEADER_LENGTH = 4; 445 // Corresponds to enum definition in bionic/libc/kernel/uapi/linux/inet_diag.h 446 public static final int INET_DIAG_INFO = 2; 447 public static final int INET_DIAG_MARK = 15; 448 449 public final short rtaLen; // The whole valid size of the struct. 450 public final short rtaType; 451 RoutingAttribute(final short len, final short type)452 RoutingAttribute(final short len, final short type) { 453 rtaLen = len; 454 rtaType = type; 455 } getDataLength()456 public short getDataLength() { 457 return (short) (rtaLen - HEADER_LENGTH); 458 } 459 } 460 461 /** 462 * Data class for keeping the socket info. 463 */ 464 @VisibleForTesting 465 class SocketInfo { 466 // Initial mark value corresponds to the initValue in system/netd/include/Fwmark.h. 467 public static final int INIT_MARK_VALUE = 0; 468 @Nullable 469 public final TcpInfo tcpInfo; 470 // One of {@code AF_INET6, AF_INET}. 471 public final int ipFamily; 472 // "fwmark" value of the socket queried from native. 473 public final int fwmark; 474 // Socket information updated elapsed real time. 475 public final long updateTime; 476 SocketInfo(@ullable final TcpInfo info, final int family, final int mark, final long time)477 SocketInfo(@Nullable final TcpInfo info, final int family, final int mark, 478 final long time) { 479 tcpInfo = info; 480 ipFamily = family; 481 updateTime = time; 482 fwmark = mark; 483 } 484 485 @Override toString()486 public String toString() { 487 return "SocketInfo {Type:" + ipTypeToString(ipFamily) + ", " 488 + tcpInfo + ", mark:" + fwmark + " updated at " + updateTime + "}"; 489 } 490 ipTypeToString(final int type)491 private String ipTypeToString(final int type) { 492 if (type == AF_INET) { 493 return "IP"; 494 } else if (type == AF_INET6) { 495 return "IPV6"; 496 } else { 497 return "UNKNOWN"; 498 } 499 } 500 } 501 502 /** 503 * private data class only for storing the Tcp statistic for calculating the fail rate and sent 504 * count 505 * */ 506 private class TcpStat { 507 public int sentCount; 508 public int lostCount; 509 public int retransmitCount; 510 public int receivedCount; 511 accumulate(@ullable final TcpStat stat)512 void accumulate(@Nullable final TcpStat stat) { 513 if (stat == null) return; 514 515 sentCount += stat.sentCount; 516 lostCount += stat.lostCount; 517 receivedCount += stat.receivedCount; 518 retransmitCount += stat.retransmitCount; 519 } 520 } 521 522 /** 523 * Dependencies class for testing. 524 */ 525 @VisibleForTesting 526 public static class Dependencies { 527 private final Context mContext; 528 Dependencies(final Context context)529 public Dependencies(final Context context) { 530 mContext = context; 531 } 532 533 /** 534 * Connect to kernel via netlink socket. 535 * 536 * @return fd the fileDescriptor of the socket. 537 * Throw ErrnoException, SocketException if the exception is thrown. 538 */ connectToKernel()539 public FileDescriptor connectToKernel() throws ErrnoException, SocketException { 540 final FileDescriptor fd = 541 Os.socket(AF_NETLINK, SOCK_DGRAM | SOCK_CLOEXEC, NETLINK_INET_DIAG); 542 Os.connect( 543 fd, SocketUtils.makeNetlinkSocketAddress(0 /* portId */, 0 /* groupMask */)); 544 545 return fd; 546 } 547 548 /** 549 * Send composed message request to kernel. 550 * @param fd see {@Code FileDescriptor} 551 * @param msg the byte array represent the request message to write to kernel. 552 * 553 * Throw ErrnoException or InterruptedIOException if the exception is thrown. 554 */ sendPollingRequest(@onNull final FileDescriptor fd, @NonNull final byte[] msg)555 public void sendPollingRequest(@NonNull final FileDescriptor fd, @NonNull final byte[] msg) 556 throws ErrnoException, InterruptedIOException { 557 Os.setsockoptTimeval(fd, SOL_SOCKET, SO_SNDTIMEO, 558 StructTimeval.fromMillis(IO_TIMEOUT)); 559 Os.write(fd, msg, 0 /* byteOffset */, msg.length); 560 } 561 562 /** 563 * Look up the value of a property in DeviceConfig. 564 * @param namespace The namespace containing the property to look up. 565 * @param name The name of the property to look up. 566 * @param defaultValue The value to return if the property does not exist or has no non-null 567 * value. 568 * @return the corresponding value, or defaultValue if none exists. 569 */ getDeviceConfigPropertyInt(@onNull final String namespace, @NonNull final String name, final int defaultValue)570 public int getDeviceConfigPropertyInt(@NonNull final String namespace, 571 @NonNull final String name, final int defaultValue) { 572 return NetworkStackUtils.getDeviceConfigPropertyInt(namespace, name, defaultValue); 573 } 574 575 /** 576 * Return if request tcp info via netlink socket is supported or not. 577 */ isTcpInfoParsingSupported()578 public boolean isTcpInfoParsingSupported() { 579 // Request tcp info from NetworkStack directly needs extra SELinux permission added 580 // after Q release. 581 return ShimUtils.isReleaseOrDevelopmentApiAbove(Build.VERSION_CODES.Q); 582 } 583 584 /** 585 * Receive the request message from kernel via given fd. 586 */ recvMessage(@onNull final FileDescriptor fd)587 public ByteBuffer recvMessage(@NonNull final FileDescriptor fd) 588 throws ErrnoException, InterruptedIOException { 589 return NetlinkSocket.recvMessage(fd, DEFAULT_RECV_BUFSIZE, IO_TIMEOUT); 590 } 591 getContext()592 public Context getContext() { 593 return mContext; 594 } 595 596 /** 597 * Get an INetd connector. 598 */ getNetd()599 public INetd getNetd() { 600 return INetd.Stub.asInterface( 601 (IBinder) mContext.getSystemService(Context.NETD_SERVICE)); 602 } 603 604 /** Add device config change listener */ addDeviceConfigChangedListener( @onNull final DeviceConfig.OnPropertiesChangedListener listener)605 public void addDeviceConfigChangedListener( 606 @NonNull final DeviceConfig.OnPropertiesChangedListener listener) { 607 DeviceConfig.addOnPropertiesChangedListener(NAMESPACE_CONNECTIVITY, 608 AsyncTask.THREAD_POOL_EXECUTOR, listener); 609 } 610 } 611 } 612