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