• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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                             // &lt;linux_src&gt;/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