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