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                             // &lt;linux_src&gt;/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