diff options
-rw-r--r-- | services/net/java/android/net/ip/ConnectivityPacketTracker.java | 6 | ||||
-rw-r--r-- | services/net/java/android/net/ip/IpClient.java | 10 | ||||
-rw-r--r-- | services/net/java/android/net/ip/IpNeighborMonitor.java | 236 | ||||
-rw-r--r-- | services/net/java/android/net/ip/IpReachabilityMonitor.java | 386 | ||||
-rw-r--r-- | services/net/java/android/net/netlink/NetlinkSocket.java | 134 | ||||
-rw-r--r-- | services/net/java/android/net/netlink/StructNdMsg.java | 7 | ||||
-rw-r--r-- | services/net/java/android/net/util/PacketReader.java (renamed from services/net/java/android/net/util/BlockingSocketReader.java) | 8 | ||||
-rw-r--r-- | tests/net/java/android/net/ip/IpReachabilityMonitorTest.java | 12 | ||||
-rw-r--r-- | tests/net/java/android/net/netlink/NetlinkSocketTest.java | 49 | ||||
-rw-r--r-- | tests/net/java/android/net/util/PacketReaderTest.java (renamed from tests/net/java/android/net/util/BlockingSocketReaderTest.java) | 18 |
10 files changed, 429 insertions, 437 deletions
diff --git a/services/net/java/android/net/ip/ConnectivityPacketTracker.java b/services/net/java/android/net/ip/ConnectivityPacketTracker.java index 1925c39e5211..6cf4fa9a3dfc 100644 --- a/services/net/java/android/net/ip/ConnectivityPacketTracker.java +++ b/services/net/java/android/net/ip/ConnectivityPacketTracker.java @@ -19,7 +19,7 @@ package android.net.ip; import static android.system.OsConstants.*; import android.net.NetworkUtils; -import android.net.util.BlockingSocketReader; +import android.net.util.PacketReader; import android.net.util.ConnectivityPacketSummary; import android.os.Handler; import android.system.ErrnoException; @@ -65,7 +65,7 @@ public class ConnectivityPacketTracker { private final String mTag; private final LocalLog mLog; - private final BlockingSocketReader mPacketListener; + private final PacketReader mPacketListener; private boolean mRunning; private String mDisplayName; @@ -101,7 +101,7 @@ public class ConnectivityPacketTracker { mDisplayName = null; } - private final class PacketListener extends BlockingSocketReader { + private final class PacketListener extends PacketReader { private final int mIfIndex; private final byte mHwAddr[]; diff --git a/services/net/java/android/net/ip/IpClient.java b/services/net/java/android/net/ip/IpClient.java index 5c58cdb6462b..b305b336d12f 100644 --- a/services/net/java/android/net/ip/IpClient.java +++ b/services/net/java/android/net/ip/IpClient.java @@ -815,6 +815,15 @@ public class IpClient extends StateMachine { pw.println(Objects.toString(provisioningConfig, "N/A")); pw.decreaseIndent(); + final IpReachabilityMonitor iprm = mIpReachabilityMonitor; + if (iprm != null) { + pw.println(); + pw.println(mTag + " current IpReachabilityMonitor state:"); + pw.increaseIndent(); + iprm.dump(pw); + pw.decreaseIndent(); + } + pw.println(); pw.println(mTag + " StateMachine dump:"); pw.increaseIndent(); @@ -1237,6 +1246,7 @@ public class IpClient extends StateMachine { mIpReachabilityMonitor = new IpReachabilityMonitor( mContext, mInterfaceName, + getHandler(), mLog, new IpReachabilityMonitor.Callback() { @Override diff --git a/services/net/java/android/net/ip/IpNeighborMonitor.java b/services/net/java/android/net/ip/IpNeighborMonitor.java new file mode 100644 index 000000000000..680733478657 --- /dev/null +++ b/services/net/java/android/net/ip/IpNeighborMonitor.java @@ -0,0 +1,236 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.ip; + +import android.net.netlink.NetlinkConstants; +import android.net.netlink.NetlinkErrorMessage; +import android.net.netlink.NetlinkMessage; +import android.net.netlink.NetlinkSocket; +import android.net.netlink.RtNetlinkNeighborMessage; +import android.net.netlink.StructNdMsg; +import android.net.netlink.StructNlMsgHdr; +import android.net.util.PacketReader; +import android.net.util.SharedLog; +import android.os.Handler; +import android.os.SystemClock; +import android.system.ErrnoException; +import android.system.NetlinkSocketAddress; +import android.system.Os; +import android.system.OsConstants; +import android.util.Log; + +import com.android.internal.util.BitUtils; + +import libcore.io.IoUtils; +import libcore.io.Libcore; + +import java.io.FileDescriptor; +import java.net.InetAddress; +import java.net.SocketAddress; +import java.net.SocketException; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.util.StringJoiner; + + +/** + * IpNeighborMonitor. + * + * Monitors the kernel rtnetlink neighbor notifications and presents to callers + * NeighborEvents describing each event. Callers can provide a consumer instance + * to both filter (e.g. by interface index and IP address) and handle the + * generated NeighborEvents. + * + * @hide + */ +public class IpNeighborMonitor extends PacketReader { + private static final String TAG = IpNeighborMonitor.class.getSimpleName(); + private static final boolean DBG = false; + private static final boolean VDBG = false; + + /** + * Make the kernel perform neighbor reachability detection (IPv4 ARP or IPv6 ND) + * for the given IP address on the specified interface index. + * + * @return 0 if the request was successfully passed to the kernel; otherwise return + * a non-zero error code. + */ + public static int startKernelNeighborProbe(int ifIndex, InetAddress ip) { + final String msgSnippet = "probing ip=" + ip.getHostAddress() + "%" + ifIndex; + if (DBG) { Log.d(TAG, msgSnippet); } + + final byte[] msg = RtNetlinkNeighborMessage.newNewNeighborMessage( + 1, ip, StructNdMsg.NUD_PROBE, ifIndex, null); + + try { + NetlinkSocket.sendOneShotKernelMessage(OsConstants.NETLINK_ROUTE, msg); + } catch (ErrnoException e) { + Log.e(TAG, "Error " + msgSnippet + ": " + e); + return -e.errno; + } + + return 0; + } + + public static class NeighborEvent { + final long elapsedMs; + final short msgType; + final int ifindex; + final InetAddress ip; + final short nudState; + final byte[] linkLayerAddr; + + public NeighborEvent(long elapsedMs, short msgType, int ifindex, InetAddress ip, + short nudState, byte[] linkLayerAddr) { + this.elapsedMs = elapsedMs; + this.msgType = msgType; + this.ifindex = ifindex; + this.ip = ip; + this.nudState = nudState; + this.linkLayerAddr = linkLayerAddr; + } + + boolean isConnected() { + return (msgType != NetlinkConstants.RTM_DELNEIGH) && + StructNdMsg.isNudStateConnected(nudState); + } + + boolean isValid() { + return (msgType != NetlinkConstants.RTM_DELNEIGH) && + StructNdMsg.isNudStateValid(nudState); + } + + @Override + public String toString() { + final StringJoiner j = new StringJoiner(",", "NeighborEvent{", "}"); + return j.add("@" + elapsedMs) + .add(NetlinkConstants.stringForNlMsgType(msgType)) + .add("if=" + ifindex) + .add(ip.getHostAddress()) + .add(StructNdMsg.stringForNudState(nudState)) + .add("[" + NetlinkConstants.hexify(linkLayerAddr) + "]") + .toString(); + } + } + + public interface NeighborEventConsumer { + // Every neighbor event received on the netlink socket is passed in + // here. Subclasses should filter for events of interest. + public void accept(NeighborEvent event); + } + + private final SharedLog mLog; + private final NeighborEventConsumer mConsumer; + + public IpNeighborMonitor(Handler h, SharedLog log, NeighborEventConsumer cb) { + super(h, NetlinkSocket.DEFAULT_RECV_BUFSIZE); + mLog = log.forSubComponent(TAG); + mConsumer = (cb != null) ? cb : (event) -> { /* discard */ }; + } + + @Override + protected FileDescriptor createFd() { + FileDescriptor fd = null; + + try { + fd = NetlinkSocket.forProto(OsConstants.NETLINK_ROUTE); + Os.bind(fd, (SocketAddress)(new NetlinkSocketAddress(0, OsConstants.RTMGRP_NEIGH))); + Os.connect(fd, (SocketAddress)(new NetlinkSocketAddress(0, 0))); + + if (VDBG) { + final NetlinkSocketAddress nlAddr = (NetlinkSocketAddress) Os.getsockname(fd); + Log.d(TAG, "bound to sockaddr_nl{" + + BitUtils.uint32(nlAddr.getPortId()) + ", " + + nlAddr.getGroupsMask() + + "}"); + } + } catch (ErrnoException|SocketException e) { + logError("Failed to create rtnetlink socket", e); + IoUtils.closeQuietly(fd); + return null; + } + + return fd; + } + + @Override + protected void handlePacket(byte[] recvbuf, int length) { + final long whenMs = SystemClock.elapsedRealtime(); + + final ByteBuffer byteBuffer = ByteBuffer.wrap(recvbuf, 0, length); + byteBuffer.order(ByteOrder.nativeOrder()); + + parseNetlinkMessageBuffer(byteBuffer, whenMs); + } + + private void parseNetlinkMessageBuffer(ByteBuffer byteBuffer, long whenMs) { + while (byteBuffer.remaining() > 0) { + final int position = byteBuffer.position(); + final NetlinkMessage nlMsg = NetlinkMessage.parse(byteBuffer); + if (nlMsg == null || nlMsg.getHeader() == null) { + byteBuffer.position(position); + mLog.e("unparsable netlink msg: " + NetlinkConstants.hexify(byteBuffer)); + break; + } + + final int srcPortId = nlMsg.getHeader().nlmsg_pid; + if (srcPortId != 0) { + mLog.e("non-kernel source portId: " + BitUtils.uint32(srcPortId)); + break; + } + + if (nlMsg instanceof NetlinkErrorMessage) { + mLog.e("netlink error: " + nlMsg); + continue; + } else if (!(nlMsg instanceof RtNetlinkNeighborMessage)) { + mLog.i("non-rtnetlink neighbor msg: " + nlMsg); + continue; + } + + evaluateRtNetlinkNeighborMessage((RtNetlinkNeighborMessage) nlMsg, whenMs); + } + } + + private void evaluateRtNetlinkNeighborMessage( + RtNetlinkNeighborMessage neighMsg, long whenMs) { + final short msgType = neighMsg.getHeader().nlmsg_type; + final StructNdMsg ndMsg = neighMsg.getNdHeader(); + if (ndMsg == null) { + mLog.e("RtNetlinkNeighborMessage without ND message header!"); + return; + } + + final int ifindex = ndMsg.ndm_ifindex; + final InetAddress destination = neighMsg.getDestination(); + final short nudState = + (msgType == NetlinkConstants.RTM_DELNEIGH) + ? StructNdMsg.NUD_NONE + : ndMsg.ndm_state; + + final NeighborEvent event = new NeighborEvent( + whenMs, msgType, ifindex, destination, nudState, neighMsg.getLinkLayerAddress()); + + if (VDBG) { + Log.d(TAG, neighMsg.toString()); + } + if (DBG) { + Log.d(TAG, event.toString()); + } + + mConsumer.accept(event); + } +} diff --git a/services/net/java/android/net/ip/IpReachabilityMonitor.java b/services/net/java/android/net/ip/IpReachabilityMonitor.java index 714b35a03396..b31ffbba0279 100644 --- a/services/net/java/android/net/ip/IpReachabilityMonitor.java +++ b/services/net/java/android/net/ip/IpReachabilityMonitor.java @@ -22,30 +22,27 @@ import android.net.LinkProperties; import android.net.LinkProperties.ProvisioningChange; import android.net.ProxyInfo; import android.net.RouteInfo; +import android.net.ip.IpNeighborMonitor.NeighborEvent; import android.net.metrics.IpConnectivityLog; import android.net.metrics.IpReachabilityEvent; -import android.net.netlink.NetlinkConstants; -import android.net.netlink.NetlinkErrorMessage; -import android.net.netlink.NetlinkMessage; -import android.net.netlink.NetlinkSocket; -import android.net.netlink.RtNetlinkNeighborMessage; import android.net.netlink.StructNdMsg; -import android.net.netlink.StructNdaCacheInfo; -import android.net.netlink.StructNlMsgHdr; import android.net.util.MultinetworkPolicyTracker; import android.net.util.SharedLog; +import android.os.Handler; import android.os.PowerManager; import android.os.PowerManager.WakeLock; import android.os.SystemClock; import android.system.ErrnoException; -import android.system.NetlinkSocketAddress; import android.system.OsConstants; import android.util.Log; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.util.DumpUtils; +import com.android.internal.util.DumpUtils.Dump; import java.io.InterruptedIOException; +import java.io.PrintWriter; import java.net.Inet6Address; import java.net.InetAddress; import java.net.InetSocketAddress; @@ -134,6 +131,8 @@ import java.util.Set; * state it may be best for the link to disconnect completely and * reconnect afresh. * + * Accessing an instance of this class from multiple threads is NOT safe. + * * @hide */ public class IpReachabilityMonitor { @@ -169,64 +168,33 @@ public class IpReachabilityMonitor { } } - private final Object mLock = new Object(); private final String mInterfaceName; private final int mInterfaceIndex; + private final IpNeighborMonitor mIpNeighborMonitor; private final SharedLog mLog; private final Callback mCallback; private final Dependencies mDependencies; private final MultinetworkPolicyTracker mMultinetworkPolicyTracker; - private final NetlinkSocketObserver mNetlinkSocketObserver; - private final Thread mObserverThread; private final IpConnectivityLog mMetricsLog = new IpConnectivityLog(); - @GuardedBy("mLock") private LinkProperties mLinkProperties = new LinkProperties(); - // TODO: consider a map to a private NeighborState class holding more - // information than a single NUD state entry. - @GuardedBy("mLock") - private Map<InetAddress, Short> mIpWatchList = new HashMap<>(); - @GuardedBy("mLock") - private int mIpWatchListVersion; - private volatile boolean mRunning; + private Map<InetAddress, NeighborEvent> mNeighborWatchList = new HashMap<>(); // Time in milliseconds of the last forced probe request. private volatile long mLastProbeTimeMs; - /** - * Make the kernel perform neighbor reachability detection (IPv4 ARP or IPv6 ND) - * for the given IP address on the specified interface index. - * - * @return 0 if the request was successfully passed to the kernel; otherwise return - * a non-zero error code. - */ - private static int probeNeighbor(int ifIndex, InetAddress ip) { - final String msgSnippet = "probing ip=" + ip.getHostAddress() + "%" + ifIndex; - if (DBG) { Log.d(TAG, msgSnippet); } - - final byte[] msg = RtNetlinkNeighborMessage.newNewNeighborMessage( - 1, ip, StructNdMsg.NUD_PROBE, ifIndex, null); - - try { - NetlinkSocket.sendOneShotKernelMessage(OsConstants.NETLINK_ROUTE, msg); - } catch (ErrnoException e) { - Log.e(TAG, "Error " + msgSnippet + ": " + e); - return -e.errno; - } - - return 0; + public IpReachabilityMonitor( + Context context, String ifName, Handler h, SharedLog log, Callback callback) { + this(context, ifName, h, log, callback, null); } - public IpReachabilityMonitor(Context context, String ifName, SharedLog log, Callback callback) { - this(context, ifName, log, callback, null); - } - - public IpReachabilityMonitor(Context context, String ifName, SharedLog log, Callback callback, + public IpReachabilityMonitor( + Context context, String ifName, Handler h, SharedLog log, Callback callback, MultinetworkPolicyTracker tracker) { - this(ifName, getInterfaceIndex(ifName), log, callback, tracker, + this(ifName, getInterfaceIndex(ifName), h, log, callback, tracker, Dependencies.makeDefault(context, ifName)); } @VisibleForTesting - IpReachabilityMonitor(String ifName, int ifIndex, SharedLog log, Callback callback, + IpReachabilityMonitor(String ifName, int ifIndex, Handler h, SharedLog log, Callback callback, MultinetworkPolicyTracker tracker, Dependencies dependencies) { mInterfaceName = ifName; mLog = log.forSubComponent(TAG); @@ -234,45 +202,54 @@ public class IpReachabilityMonitor { mMultinetworkPolicyTracker = tracker; mInterfaceIndex = ifIndex; mDependencies = dependencies; - mNetlinkSocketObserver = new NetlinkSocketObserver(); - mObserverThread = new Thread(mNetlinkSocketObserver); - mObserverThread.start(); + + mIpNeighborMonitor = new IpNeighborMonitor(h, mLog, + (NeighborEvent event) -> { + if (mInterfaceIndex != event.ifindex) return; + if (!mNeighborWatchList.containsKey(event.ip)) return; + + final NeighborEvent prev = mNeighborWatchList.put(event.ip, event); + + // TODO: Consider what to do with other states that are not within + // NeighborEvent#isValid() (i.e. NUD_NONE, NUD_INCOMPLETE). + if (event.nudState == StructNdMsg.NUD_FAILED) { + mLog.w("ALERT neighbor went from: " + prev + " to: " + event); + handleNeighborLost(event); + } + }); + mIpNeighborMonitor.start(); } public void stop() { - mRunning = false; + mIpNeighborMonitor.stop(); clearLinkProperties(); - mNetlinkSocketObserver.clearNetlinkSocket(); } - // TODO: add a public dump() method that can be called during a bug report. - - private String describeWatchList() { - final String delimiter = ", "; - StringBuilder sb = new StringBuilder(); - synchronized (mLock) { - sb.append("iface{" + mInterfaceName + "/" + mInterfaceIndex + "}, "); - sb.append("v{" + mIpWatchListVersion + "}, "); - sb.append("ntable=["); - boolean firstTime = true; - for (Map.Entry<InetAddress, Short> entry : mIpWatchList.entrySet()) { - if (firstTime) { - firstTime = false; - } else { - sb.append(delimiter); - } - sb.append(entry.getKey().getHostAddress() + "/" + - StructNdMsg.stringForNudState(entry.getValue())); - } - sb.append("]"); - } - return sb.toString(); + public void dump(PrintWriter pw) { + DumpUtils.dumpAsync( + mIpNeighborMonitor.getHandler(), + new Dump() { + @Override + public void dump(PrintWriter pw, String prefix) { + pw.println(describeWatchList("\n")); + } + }, + pw, "", 1000); } - private boolean isWatching(InetAddress ip) { - synchronized (mLock) { - return mRunning && mIpWatchList.containsKey(ip); + private String describeWatchList() { return describeWatchList(" "); } + + private String describeWatchList(String sep) { + final StringBuilder sb = new StringBuilder(); + sb.append("iface{" + mInterfaceName + "/" + mInterfaceIndex + "}," + sep); + sb.append("ntable=[" + sep); + String delimiter = ""; + for (Map.Entry<InetAddress, NeighborEvent> entry : mNeighborWatchList.entrySet()) { + sb.append(delimiter).append(entry.getKey().getHostAddress() + "/" + entry.getValue()); + delimiter = "," + sep; } + sb.append("]"); + return sb.toString(); } private static boolean isOnLink(List<RouteInfo> routes, InetAddress ip) { @@ -284,13 +261,6 @@ public class IpReachabilityMonitor { return false; } - private short getNeighborStateLocked(InetAddress ip) { - if (mIpWatchList.containsKey(ip)) { - return mIpWatchList.get(ip); - } - return StructNdMsg.NUD_NONE; - } - public void updateLinkProperties(LinkProperties lp) { if (!mInterfaceName.equals(lp.getInterfaceName())) { // TODO: figure out whether / how to cope with interface changes. @@ -299,70 +269,63 @@ public class IpReachabilityMonitor { return; } - synchronized (mLock) { - mLinkProperties = new LinkProperties(lp); - Map<InetAddress, Short> newIpWatchList = new HashMap<>(); + mLinkProperties = new LinkProperties(lp); + Map<InetAddress, NeighborEvent> newNeighborWatchList = new HashMap<>(); - final List<RouteInfo> routes = mLinkProperties.getRoutes(); - for (RouteInfo route : routes) { - if (route.hasGateway()) { - InetAddress gw = route.getGateway(); - if (isOnLink(routes, gw)) { - newIpWatchList.put(gw, getNeighborStateLocked(gw)); - } + final List<RouteInfo> routes = mLinkProperties.getRoutes(); + for (RouteInfo route : routes) { + if (route.hasGateway()) { + InetAddress gw = route.getGateway(); + if (isOnLink(routes, gw)) { + newNeighborWatchList.put(gw, mNeighborWatchList.getOrDefault(gw, null)); } } + } - for (InetAddress nameserver : lp.getDnsServers()) { - if (isOnLink(routes, nameserver)) { - newIpWatchList.put(nameserver, getNeighborStateLocked(nameserver)); - } + for (InetAddress dns : lp.getDnsServers()) { + if (isOnLink(routes, dns)) { + newNeighborWatchList.put(dns, mNeighborWatchList.getOrDefault(dns, null)); } - - mIpWatchList = newIpWatchList; - mIpWatchListVersion++; } + + mNeighborWatchList = newNeighborWatchList; if (DBG) { Log.d(TAG, "watch: " + describeWatchList()); } } public void clearLinkProperties() { - synchronized (mLock) { - mLinkProperties.clear(); - mIpWatchList.clear(); - mIpWatchListVersion++; - } + mLinkProperties.clear(); + mNeighborWatchList.clear(); if (DBG) { Log.d(TAG, "clear: " + describeWatchList()); } } - private void handleNeighborLost(String msg) { - InetAddress ip = null; - final ProvisioningChange delta; - synchronized (mLock) { - LinkProperties whatIfLp = new LinkProperties(mLinkProperties); + private void handleNeighborLost(NeighborEvent event) { + final LinkProperties whatIfLp = new LinkProperties(mLinkProperties); - for (Map.Entry<InetAddress, Short> entry : mIpWatchList.entrySet()) { - if (entry.getValue() != StructNdMsg.NUD_FAILED) { - continue; - } - - ip = entry.getKey(); - for (RouteInfo route : mLinkProperties.getRoutes()) { - if (ip.equals(route.getGateway())) { - whatIfLp.removeRoute(route); - } - } - - if (avoidingBadLinks() || !(ip instanceof Inet6Address)) { - // We should do this unconditionally, but alas we cannot: b/31827713. - whatIfLp.removeDnsServer(ip); + InetAddress ip = null; + for (Map.Entry<InetAddress, NeighborEvent> entry : mNeighborWatchList.entrySet()) { + // TODO: Consider using NeighborEvent#isValid() here; it's more + // strict but may interact badly if other entries are somehow in + // NUD_INCOMPLETE (say, during network attach). + if (entry.getValue().nudState != StructNdMsg.NUD_FAILED) continue; + + ip = entry.getKey(); + for (RouteInfo route : mLinkProperties.getRoutes()) { + if (ip.equals(route.getGateway())) { + whatIfLp.removeRoute(route); } } - delta = LinkProperties.compareProvisioning(mLinkProperties, whatIfLp); + if (avoidingBadLinks() || !(ip instanceof Inet6Address)) { + // We should do this unconditionally, but alas we cannot: b/31827713. + whatIfLp.removeDnsServer(ip); + } } + final ProvisioningChange delta = LinkProperties.compareProvisioning( + mLinkProperties, whatIfLp); + if (delta == ProvisioningChange.LOST_PROVISIONING) { - final String logMsg = "FAILURE: LOST_PROVISIONING, " + msg; + final String logMsg = "FAILURE: LOST_PROVISIONING, " + event; Log.w(TAG, logMsg); if (mCallback != null) { // TODO: remove |ip| when the callback signature no longer has @@ -378,12 +341,9 @@ public class IpReachabilityMonitor { } public void probeAll() { - final List<InetAddress> ipProbeList; - synchronized (mLock) { - ipProbeList = new ArrayList<>(mIpWatchList.keySet()); - } + final List<InetAddress> ipProbeList = new ArrayList<>(mNeighborWatchList.keySet()); - if (!ipProbeList.isEmpty() && mRunning) { + if (!ipProbeList.isEmpty()) { // Keep the CPU awake long enough to allow all ARP/ND // probes a reasonable chance at success. See b/23197666. // @@ -394,13 +354,10 @@ public class IpReachabilityMonitor { } for (InetAddress target : ipProbeList) { - if (!mRunning) { - break; - } - final int returnValue = probeNeighbor(mInterfaceIndex, target); + final int rval = IpNeighborMonitor.startKernelNeighborProbe(mInterfaceIndex, target); mLog.log(String.format("put neighbor %s into NUD_PROBE state (rval=%d)", - target.getHostAddress(), returnValue)); - logEvent(IpReachabilityEvent.PROBE, returnValue); + target.getHostAddress(), rval)); + logEvent(IpReachabilityEvent.PROBE, rval); } mLastProbeTimeMs = SystemClock.elapsedRealtime(); } @@ -446,153 +403,4 @@ public class IpReachabilityMonitor { int eventType = IpReachabilityEvent.nudFailureEventType(isFromProbe, isProvisioningLost); mMetricsLog.log(mInterfaceName, new IpReachabilityEvent(eventType)); } - - // TODO: simplify the number of objects by making this extend Thread. - private final class NetlinkSocketObserver implements Runnable { - private NetlinkSocket mSocket; - - @Override - public void run() { - if (VDBG) { Log.d(TAG, "Starting observing thread."); } - mRunning = true; - - try { - setupNetlinkSocket(); - } catch (ErrnoException | SocketException e) { - Log.e(TAG, "Failed to suitably initialize a netlink socket", e); - mRunning = false; - } - - while (mRunning) { - final ByteBuffer byteBuffer; - try { - byteBuffer = recvKernelReply(); - } catch (ErrnoException e) { - if (mRunning) { Log.w(TAG, "ErrnoException: ", e); } - break; - } - final long whenMs = SystemClock.elapsedRealtime(); - if (byteBuffer == null) { - continue; - } - parseNetlinkMessageBuffer(byteBuffer, whenMs); - } - - clearNetlinkSocket(); - - mRunning = false; // Not a no-op when ErrnoException happened. - if (VDBG) { Log.d(TAG, "Finishing observing thread."); } - } - - private void clearNetlinkSocket() { - if (mSocket != null) { - mSocket.close(); - } - } - - // TODO: Refactor the main loop to recreate the socket upon recoverable errors. - private void setupNetlinkSocket() throws ErrnoException, SocketException { - clearNetlinkSocket(); - mSocket = new NetlinkSocket(OsConstants.NETLINK_ROUTE); - - final NetlinkSocketAddress listenAddr = new NetlinkSocketAddress( - 0, OsConstants.RTMGRP_NEIGH); - mSocket.bind(listenAddr); - - if (VDBG) { - final NetlinkSocketAddress nlAddr = mSocket.getLocalAddress(); - Log.d(TAG, "bound to sockaddr_nl{" - + ((long) (nlAddr.getPortId() & 0xffffffff)) + ", " - + nlAddr.getGroupsMask() - + "}"); - } - } - - private ByteBuffer recvKernelReply() throws ErrnoException { - try { - return mSocket.recvMessage(0); - } catch (InterruptedIOException e) { - // Interruption or other error, e.g. another thread closed our file descriptor. - } catch (ErrnoException e) { - if (e.errno != OsConstants.EAGAIN) { - throw e; - } - } - return null; - } - - private void parseNetlinkMessageBuffer(ByteBuffer byteBuffer, long whenMs) { - while (byteBuffer.remaining() > 0) { - final int position = byteBuffer.position(); - final NetlinkMessage nlMsg = NetlinkMessage.parse(byteBuffer); - if (nlMsg == null || nlMsg.getHeader() == null) { - byteBuffer.position(position); - Log.e(TAG, "unparsable netlink msg: " + NetlinkConstants.hexify(byteBuffer)); - break; - } - - final int srcPortId = nlMsg.getHeader().nlmsg_pid; - if (srcPortId != 0) { - Log.e(TAG, "non-kernel source portId: " + ((long) (srcPortId & 0xffffffff))); - break; - } - - if (nlMsg instanceof NetlinkErrorMessage) { - Log.e(TAG, "netlink error: " + nlMsg); - continue; - } else if (!(nlMsg instanceof RtNetlinkNeighborMessage)) { - if (DBG) { - Log.d(TAG, "non-rtnetlink neighbor msg: " + nlMsg); - } - continue; - } - - evaluateRtNetlinkNeighborMessage((RtNetlinkNeighborMessage) nlMsg, whenMs); - } - } - - private void evaluateRtNetlinkNeighborMessage( - RtNetlinkNeighborMessage neighMsg, long whenMs) { - final StructNdMsg ndMsg = neighMsg.getNdHeader(); - if (ndMsg == null || ndMsg.ndm_ifindex != mInterfaceIndex) { - return; - } - - final InetAddress destination = neighMsg.getDestination(); - if (!isWatching(destination)) { - return; - } - - final short msgType = neighMsg.getHeader().nlmsg_type; - final short nudState = ndMsg.ndm_state; - final String eventMsg = "NeighborEvent{" - + "elapsedMs=" + whenMs + ", " - + destination.getHostAddress() + ", " - + "[" + NetlinkConstants.hexify(neighMsg.getLinkLayerAddress()) + "], " - + NetlinkConstants.stringForNlMsgType(msgType) + ", " - + StructNdMsg.stringForNudState(nudState) - + "}"; - - if (VDBG) { - Log.d(TAG, neighMsg.toString()); - } else if (DBG) { - Log.d(TAG, eventMsg); - } - - synchronized (mLock) { - if (mIpWatchList.containsKey(destination)) { - final short value = - (msgType == NetlinkConstants.RTM_DELNEIGH) - ? StructNdMsg.NUD_NONE - : nudState; - mIpWatchList.put(destination, value); - } - } - - if (nudState == StructNdMsg.NUD_FAILED) { - Log.w(TAG, "ALERT: " + eventMsg); - handleNeighborLost(eventMsg); - } - } - } } diff --git a/services/net/java/android/net/netlink/NetlinkSocket.java b/services/net/java/android/net/netlink/NetlinkSocket.java index f5f211d8e631..5af3c299bfc1 100644 --- a/services/net/java/android/net/netlink/NetlinkSocket.java +++ b/services/net/java/android/net/netlink/NetlinkSocket.java @@ -16,16 +16,24 @@ package android.net.netlink; +import static android.system.OsConstants.AF_NETLINK; +import static android.system.OsConstants.EIO; +import static android.system.OsConstants.EPROTO; +import static android.system.OsConstants.ETIMEDOUT; +import static android.system.OsConstants.SO_RCVBUF; +import static android.system.OsConstants.SO_RCVTIMEO; +import static android.system.OsConstants.SO_SNDTIMEO; +import static android.system.OsConstants.SOCK_DGRAM; +import static android.system.OsConstants.SOL_SOCKET; + import android.system.ErrnoException; import android.system.NetlinkSocketAddress; import android.system.Os; -import android.system.OsConstants; import android.system.StructTimeval; import android.util.Log; import libcore.io.IoUtils; import libcore.io.Libcore; -import java.io.Closeable; import java.io.FileDescriptor; import java.io.InterruptedIOException; import java.net.SocketAddress; @@ -37,28 +45,27 @@ import java.nio.ByteOrder; /** * NetlinkSocket * - * A small wrapper class to assist with AF_NETLINK socket operations. + * A small static class to assist with AF_NETLINK socket operations. * * @hide */ -public class NetlinkSocket implements Closeable { +public class NetlinkSocket { private static final String TAG = "NetlinkSocket"; - private static final int SOCKET_RECV_BUFSIZE = 64 * 1024; - private static final int DEFAULT_RECV_BUFSIZE = 8 * 1024; - final private FileDescriptor mDescriptor; - private NetlinkSocketAddress mAddr; - private long mLastRecvTimeoutMs; - private long mLastSendTimeoutMs; + public static final int DEFAULT_RECV_BUFSIZE = 8 * 1024; + public static final int SOCKET_RECV_BUFSIZE = 64 * 1024; public static void sendOneShotKernelMessage(int nlProto, byte[] msg) throws ErrnoException { final String errPrefix = "Error in NetlinkSocket.sendOneShotKernelMessage"; + final long IO_TIMEOUT = 300L; + + FileDescriptor fd; - try (NetlinkSocket nlSocket = new NetlinkSocket(nlProto)) { - final long IO_TIMEOUT = 300L; - nlSocket.connectToKernel(); - nlSocket.sendMessage(msg, 0, msg.length, IO_TIMEOUT); - final ByteBuffer bytes = nlSocket.recvMessage(IO_TIMEOUT); + try { + fd = forProto(nlProto); + connectToKernel(fd); + sendMessage(fd, msg, 0, msg.length, IO_TIMEOUT); + final ByteBuffer bytes = recvMessage(fd, DEFAULT_RECV_BUFSIZE, IO_TIMEOUT); // recvMessage() guaranteed to not return null if it did not throw. final NetlinkMessage response = NetlinkMessage.parse(bytes); if (response != null && response instanceof NetlinkErrorMessage && @@ -81,61 +88,30 @@ public class NetlinkSocket implements Closeable { errmsg = response.toString(); } Log.e(TAG, errPrefix + ", errmsg=" + errmsg); - throw new ErrnoException(errmsg, OsConstants.EPROTO); + throw new ErrnoException(errmsg, EPROTO); } } catch (InterruptedIOException e) { Log.e(TAG, errPrefix, e); - throw new ErrnoException(errPrefix, OsConstants.ETIMEDOUT, e); + throw new ErrnoException(errPrefix, ETIMEDOUT, e); } catch (SocketException e) { Log.e(TAG, errPrefix, e); - throw new ErrnoException(errPrefix, OsConstants.EIO, e); + throw new ErrnoException(errPrefix, EIO, e); } - } - - public NetlinkSocket(int nlProto) throws ErrnoException { - mDescriptor = Os.socket( - OsConstants.AF_NETLINK, OsConstants.SOCK_DGRAM, nlProto); - - Os.setsockoptInt( - mDescriptor, OsConstants.SOL_SOCKET, - OsConstants.SO_RCVBUF, SOCKET_RECV_BUFSIZE); - } - - public NetlinkSocketAddress getLocalAddress() throws ErrnoException { - return (NetlinkSocketAddress) Os.getsockname(mDescriptor); - } - public void bind(NetlinkSocketAddress localAddr) throws ErrnoException, SocketException { - Os.bind(mDescriptor, (SocketAddress)localAddr); + IoUtils.closeQuietly(fd); } - public void connectTo(NetlinkSocketAddress peerAddr) - throws ErrnoException, SocketException { - Os.connect(mDescriptor, (SocketAddress) peerAddr); + public static FileDescriptor forProto(int nlProto) throws ErrnoException { + final FileDescriptor fd = Os.socket(AF_NETLINK, SOCK_DGRAM, nlProto); + Os.setsockoptInt(fd, SOL_SOCKET, SO_RCVBUF, SOCKET_RECV_BUFSIZE); + return fd; } - public void connectToKernel() throws ErrnoException, SocketException { - connectTo(new NetlinkSocketAddress(0, 0)); - } - - /** - * Wait indefinitely (or until underlying socket error) for a - * netlink message of at most DEFAULT_RECV_BUFSIZE size. - */ - public ByteBuffer recvMessage() - throws ErrnoException, InterruptedIOException { - return recvMessage(DEFAULT_RECV_BUFSIZE, 0); - } - - /** - * Wait up to |timeoutMs| (or until underlying socket error) for a - * netlink message of at most DEFAULT_RECV_BUFSIZE size. - */ - public ByteBuffer recvMessage(long timeoutMs) throws ErrnoException, InterruptedIOException { - return recvMessage(DEFAULT_RECV_BUFSIZE, timeoutMs); + public static void connectToKernel(FileDescriptor fd) throws ErrnoException, SocketException { + Os.connect(fd, (SocketAddress) (new NetlinkSocketAddress(0, 0))); } - private void checkTimeout(long timeoutMs) { + private static void checkTimeout(long timeoutMs) { if (timeoutMs < 0) { throw new IllegalArgumentException("Negative timeouts not permitted"); } @@ -147,21 +123,14 @@ public class NetlinkSocket implements Closeable { * * Multi-threaded calls with different timeouts will cause unexpected results. */ - public ByteBuffer recvMessage(int bufsize, long timeoutMs) + public static ByteBuffer recvMessage(FileDescriptor fd, int bufsize, long timeoutMs) throws ErrnoException, IllegalArgumentException, InterruptedIOException { checkTimeout(timeoutMs); - synchronized (mDescriptor) { - if (mLastRecvTimeoutMs != timeoutMs) { - Os.setsockoptTimeval(mDescriptor, - OsConstants.SOL_SOCKET, OsConstants.SO_RCVTIMEO, - StructTimeval.fromMillis(timeoutMs)); - mLastRecvTimeoutMs = timeoutMs; - } - } + Os.setsockoptTimeval(fd, SOL_SOCKET, SO_RCVTIMEO, StructTimeval.fromMillis(timeoutMs)); ByteBuffer byteBuffer = ByteBuffer.allocate(bufsize); - int length = Os.read(mDescriptor, byteBuffer); + int length = Os.read(fd, byteBuffer); if (length == bufsize) { Log.w(TAG, "maximum read"); } @@ -172,39 +141,16 @@ public class NetlinkSocket implements Closeable { } /** - * Send a message to a peer to which this socket has previously connected. - * - * This blocks until completion or an error occurs. - */ - public boolean sendMessage(byte[] bytes, int offset, int count) - throws ErrnoException, InterruptedIOException { - return sendMessage(bytes, offset, count, 0); - } - - /** * Send a message to a peer to which this socket has previously connected, * waiting at most |timeoutMs| milliseconds for the send to complete. * * Multi-threaded calls with different timeouts will cause unexpected results. */ - public boolean sendMessage(byte[] bytes, int offset, int count, long timeoutMs) + public static int sendMessage( + FileDescriptor fd, byte[] bytes, int offset, int count, long timeoutMs) throws ErrnoException, IllegalArgumentException, InterruptedIOException { checkTimeout(timeoutMs); - - synchronized (mDescriptor) { - if (mLastSendTimeoutMs != timeoutMs) { - Os.setsockoptTimeval(mDescriptor, - OsConstants.SOL_SOCKET, OsConstants.SO_SNDTIMEO, - StructTimeval.fromMillis(timeoutMs)); - mLastSendTimeoutMs = timeoutMs; - } - } - - return (count == Os.write(mDescriptor, bytes, offset, count)); - } - - @Override - public void close() { - IoUtils.closeQuietly(mDescriptor); + Os.setsockoptTimeval(fd, SOL_SOCKET, SO_SNDTIMEO, StructTimeval.fromMillis(timeoutMs)); + return Os.write(fd, bytes, offset, count); } } diff --git a/services/net/java/android/net/netlink/StructNdMsg.java b/services/net/java/android/net/netlink/StructNdMsg.java index b68ec0bc6226..e34ec39ab99b 100644 --- a/services/net/java/android/net/netlink/StructNdMsg.java +++ b/services/net/java/android/net/netlink/StructNdMsg.java @@ -63,6 +63,11 @@ public class StructNdMsg { return ((nudState & (NUD_PERMANENT|NUD_NOARP|NUD_REACHABLE)) != 0); } + public static boolean isNudStateValid(short nudState) { + return (isNudStateConnected(nudState) || + ((nudState & (NUD_PROBE|NUD_STALE|NUD_DELAY)) != 0)); + } + // Neighbor Cache Entry Flags public static byte NTF_USE = (byte) 0x01; public static byte NTF_SELF = (byte) 0x02; @@ -143,7 +148,7 @@ public class StructNdMsg { } public boolean nudValid() { - return (nudConnected() || ((ndm_state & (NUD_PROBE|NUD_STALE|NUD_DELAY)) != 0)); + return isNudStateValid(ndm_state); } @Override diff --git a/services/net/java/android/net/util/BlockingSocketReader.java b/services/net/java/android/net/util/PacketReader.java index 99bf46952b4e..10da2a551e21 100644 --- a/services/net/java/android/net/util/BlockingSocketReader.java +++ b/services/net/java/android/net/util/PacketReader.java @@ -67,7 +67,7 @@ import java.io.IOException; * * @hide */ -public abstract class BlockingSocketReader { +public abstract class PacketReader { private static final int FD_EVENTS = EVENT_INPUT | EVENT_ERROR; private static final int UNREGISTER_THIS_FD = 0; @@ -83,11 +83,11 @@ public abstract class BlockingSocketReader { IoUtils.closeQuietly(fd); } - protected BlockingSocketReader(Handler h) { + protected PacketReader(Handler h) { this(h, DEFAULT_RECV_BUF_SIZE); } - protected BlockingSocketReader(Handler h, int recvbufsize) { + protected PacketReader(Handler h, int recvbufsize) { mHandler = h; mQueue = mHandler.getLooper().getQueue(); mPacket = new byte[Math.max(recvbufsize, DEFAULT_RECV_BUF_SIZE)]; @@ -115,6 +115,8 @@ public abstract class BlockingSocketReader { } } + public Handler getHandler() { return mHandler; } + public final int recvBufSize() { return mPacket.length; } public final long numPacketsReceived() { return mPacketsReceived; } diff --git a/tests/net/java/android/net/ip/IpReachabilityMonitorTest.java b/tests/net/java/android/net/ip/IpReachabilityMonitorTest.java index f849689abb23..54776dbd3c52 100644 --- a/tests/net/java/android/net/ip/IpReachabilityMonitorTest.java +++ b/tests/net/java/android/net/ip/IpReachabilityMonitorTest.java @@ -18,10 +18,12 @@ package android.net.ip; import static org.junit.Assert.assertEquals; import static org.junit.Assert.fail; -import static org.mockito.Mockito.any; -import static org.mockito.Mockito.anyInt; +import static org.mockito.Mockito.anyString; +import static org.mockito.Mockito.when; import android.net.util.SharedLog; +import android.os.Handler; +import android.os.Looper; import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; @@ -42,14 +44,18 @@ public class IpReachabilityMonitorTest { @Mock IpReachabilityMonitor.Callback mCallback; @Mock IpReachabilityMonitor.Dependencies mDependencies; @Mock SharedLog mLog; + Handler mHandler; @Before public void setUp() { MockitoAnnotations.initMocks(this); + when(mLog.forSubComponent(anyString())).thenReturn(mLog); + mHandler = new Handler(Looper.getMainLooper()); } IpReachabilityMonitor makeMonitor() { - return new IpReachabilityMonitor("fake0", 1, mLog, mCallback, null, mDependencies); + return new IpReachabilityMonitor( + "fake0", 1, mHandler, mLog, mCallback, null, mDependencies); } @Test diff --git a/tests/net/java/android/net/netlink/NetlinkSocketTest.java b/tests/net/java/android/net/netlink/NetlinkSocketTest.java index bd36bac8d5e2..11be40b4ce33 100644 --- a/tests/net/java/android/net/netlink/NetlinkSocketTest.java +++ b/tests/net/java/android/net/netlink/NetlinkSocketTest.java @@ -16,6 +16,8 @@ package android.net.netlink; +import static android.net.netlink.NetlinkSocket.DEFAULT_RECV_BUFSIZE; +import static android.system.OsConstants.NETLINK_ROUTE; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; @@ -28,10 +30,12 @@ import android.support.test.runner.AndroidJUnit4; import android.support.test.filters.SmallTest; import android.system.ErrnoException; import android.system.NetlinkSocketAddress; -import android.system.OsConstants; +import android.system.Os; import android.util.Log; +import libcore.io.IoUtils; import java.io.InterruptedIOException; +import java.io.FileDescriptor; import java.nio.ByteBuffer; import java.nio.ByteOrder; @@ -46,29 +50,28 @@ public class NetlinkSocketTest { @Test public void testBasicWorkingGetNeighborsQuery() throws Exception { - NetlinkSocket s = new NetlinkSocket(OsConstants.NETLINK_ROUTE); - assertNotNull(s); + final FileDescriptor fd = NetlinkSocket.forProto(NETLINK_ROUTE); + assertNotNull(fd); - s.connectToKernel(); + NetlinkSocket.connectToKernel(fd); - NetlinkSocketAddress localAddr = s.getLocalAddress(); + final NetlinkSocketAddress localAddr = (NetlinkSocketAddress) Os.getsockname(fd); assertNotNull(localAddr); assertEquals(0, localAddr.getGroupsMask()); assertTrue(0 != localAddr.getPortId()); final int TEST_SEQNO = 5; - final byte[] request = RtNetlinkNeighborMessage.newGetNeighborsRequest(TEST_SEQNO); - assertNotNull(request); + final byte[] req = RtNetlinkNeighborMessage.newGetNeighborsRequest(TEST_SEQNO); + assertNotNull(req); final long TIMEOUT = 500; - assertTrue(s.sendMessage(request, 0, request.length, TIMEOUT)); + assertEquals(req.length, NetlinkSocket.sendMessage(fd, req, 0, req.length, TIMEOUT)); int neighMessageCount = 0; int doneMessageCount = 0; while (doneMessageCount == 0) { - ByteBuffer response = null; - response = s.recvMessage(TIMEOUT); + ByteBuffer response = NetlinkSocket.recvMessage(fd, DEFAULT_RECV_BUFSIZE, TIMEOUT); assertNotNull(response); assertTrue(StructNlMsgHdr.STRUCT_SIZE <= response.limit()); assertEquals(0, response.position()); @@ -100,30 +103,6 @@ public class NetlinkSocketTest { // TODO: make sure this test passes sanely in airplane mode. assertTrue(neighMessageCount > 0); - s.close(); - } - - @Test - public void testRepeatedCloseCallsAreQuiet() throws Exception { - // Create a working NetlinkSocket. - NetlinkSocket s = new NetlinkSocket(OsConstants.NETLINK_ROUTE); - assertNotNull(s); - s.connectToKernel(); - NetlinkSocketAddress localAddr = s.getLocalAddress(); - assertNotNull(localAddr); - assertEquals(0, localAddr.getGroupsMask()); - assertTrue(0 != localAddr.getPortId()); - // Close once. - s.close(); - // Test that it is closed. - boolean expectedErrorSeen = false; - try { - localAddr = s.getLocalAddress(); - } catch (ErrnoException e) { - expectedErrorSeen = true; - } - assertTrue(expectedErrorSeen); - // Close once more. - s.close(); + IoUtils.closeQuietly(fd); } } diff --git a/tests/net/java/android/net/util/BlockingSocketReaderTest.java b/tests/net/java/android/net/util/PacketReaderTest.java index 29dfa4c3d1e1..dced7435ee74 100644 --- a/tests/net/java/android/net/util/BlockingSocketReaderTest.java +++ b/tests/net/java/android/net/util/PacketReaderTest.java @@ -16,7 +16,7 @@ package android.net.util; -import static android.net.util.BlockingSocketReader.DEFAULT_RECV_BUF_SIZE; +import static android.net.util.PacketReader.DEFAULT_RECV_BUF_SIZE; import static android.system.OsConstants.*; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -53,13 +53,13 @@ import org.junit.Test; import libcore.io.IoBridge; /** - * Tests for BlockingSocketReader. + * Tests for PacketReader. * * @hide */ @RunWith(AndroidJUnit4.class) @SmallTest -public class BlockingSocketReaderTest { +public class PacketReaderTest { static final InetAddress LOOPBACK6 = Inet6Address.getLoopbackAddress(); static final StructTimeval TIMEO = StructTimeval.fromMillis(500); @@ -69,9 +69,9 @@ public class BlockingSocketReaderTest { protected byte[] mLastRecvBuf; protected boolean mStopped; protected HandlerThread mHandlerThread; - protected BlockingSocketReader mReceiver; + protected PacketReader mReceiver; - class UdpLoopbackReader extends BlockingSocketReader { + class UdpLoopbackReader extends PacketReader { public UdpLoopbackReader(Handler h) { super(h); } @@ -121,7 +121,7 @@ public class BlockingSocketReaderTest { mLastRecvBuf = null; mStopped = false; - mHandlerThread = new HandlerThread(BlockingSocketReaderTest.class.getSimpleName()); + mHandlerThread = new HandlerThread(PacketReaderTest.class.getSimpleName()); mHandlerThread.start(); } @@ -188,8 +188,8 @@ public class BlockingSocketReaderTest { mReceiver = null; } - class NullBlockingSocketReader extends BlockingSocketReader { - public NullBlockingSocketReader(Handler h, int recvbufsize) { + class NullPacketReader extends PacketReader { + public NullPacketReader(Handler h, int recvbufsize) { super(h, recvbufsize); } @@ -202,7 +202,7 @@ public class BlockingSocketReaderTest { final Handler h = mHandlerThread.getThreadHandler(); for (int i : new int[]{-1, 0, 1, DEFAULT_RECV_BUF_SIZE-1}) { - final BlockingSocketReader b = new NullBlockingSocketReader(h, i); + final PacketReader b = new NullPacketReader(h, i); assertEquals(DEFAULT_RECV_BUF_SIZE, b.recvBufSize()); } } |