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