Merge "Refactor to IpNeighborMonitor and single-threaded semantics"
am: b501e7e25b
Change-Id: Iddf69a93400af2262e4522592f9898ee0b79314c
This commit is contained in:
@@ -19,7 +19,7 @@ package android.net.ip;
|
|||||||
import static android.system.OsConstants.*;
|
import static android.system.OsConstants.*;
|
||||||
|
|
||||||
import android.net.NetworkUtils;
|
import android.net.NetworkUtils;
|
||||||
import android.net.util.BlockingSocketReader;
|
import android.net.util.PacketReader;
|
||||||
import android.net.util.ConnectivityPacketSummary;
|
import android.net.util.ConnectivityPacketSummary;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.system.ErrnoException;
|
import android.system.ErrnoException;
|
||||||
@@ -65,7 +65,7 @@ public class ConnectivityPacketTracker {
|
|||||||
|
|
||||||
private final String mTag;
|
private final String mTag;
|
||||||
private final LocalLog mLog;
|
private final LocalLog mLog;
|
||||||
private final BlockingSocketReader mPacketListener;
|
private final PacketReader mPacketListener;
|
||||||
private boolean mRunning;
|
private boolean mRunning;
|
||||||
private String mDisplayName;
|
private String mDisplayName;
|
||||||
|
|
||||||
@@ -101,7 +101,7 @@ public class ConnectivityPacketTracker {
|
|||||||
mDisplayName = null;
|
mDisplayName = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private final class PacketListener extends BlockingSocketReader {
|
private final class PacketListener extends PacketReader {
|
||||||
private final int mIfIndex;
|
private final int mIfIndex;
|
||||||
private final byte mHwAddr[];
|
private final byte mHwAddr[];
|
||||||
|
|
||||||
|
|||||||
@@ -815,6 +815,15 @@ public class IpClient extends StateMachine {
|
|||||||
pw.println(Objects.toString(provisioningConfig, "N/A"));
|
pw.println(Objects.toString(provisioningConfig, "N/A"));
|
||||||
pw.decreaseIndent();
|
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();
|
||||||
pw.println(mTag + " StateMachine dump:");
|
pw.println(mTag + " StateMachine dump:");
|
||||||
pw.increaseIndent();
|
pw.increaseIndent();
|
||||||
@@ -1237,6 +1246,7 @@ public class IpClient extends StateMachine {
|
|||||||
mIpReachabilityMonitor = new IpReachabilityMonitor(
|
mIpReachabilityMonitor = new IpReachabilityMonitor(
|
||||||
mContext,
|
mContext,
|
||||||
mInterfaceName,
|
mInterfaceName,
|
||||||
|
getHandler(),
|
||||||
mLog,
|
mLog,
|
||||||
new IpReachabilityMonitor.Callback() {
|
new IpReachabilityMonitor.Callback() {
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
236
services/net/java/android/net/ip/IpNeighborMonitor.java
Normal file
236
services/net/java/android/net/ip/IpNeighborMonitor.java
Normal file
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -22,30 +22,27 @@ import android.net.LinkProperties;
|
|||||||
import android.net.LinkProperties.ProvisioningChange;
|
import android.net.LinkProperties.ProvisioningChange;
|
||||||
import android.net.ProxyInfo;
|
import android.net.ProxyInfo;
|
||||||
import android.net.RouteInfo;
|
import android.net.RouteInfo;
|
||||||
|
import android.net.ip.IpNeighborMonitor.NeighborEvent;
|
||||||
import android.net.metrics.IpConnectivityLog;
|
import android.net.metrics.IpConnectivityLog;
|
||||||
import android.net.metrics.IpReachabilityEvent;
|
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.StructNdMsg;
|
||||||
import android.net.netlink.StructNdaCacheInfo;
|
|
||||||
import android.net.netlink.StructNlMsgHdr;
|
|
||||||
import android.net.util.MultinetworkPolicyTracker;
|
import android.net.util.MultinetworkPolicyTracker;
|
||||||
import android.net.util.SharedLog;
|
import android.net.util.SharedLog;
|
||||||
|
import android.os.Handler;
|
||||||
import android.os.PowerManager;
|
import android.os.PowerManager;
|
||||||
import android.os.PowerManager.WakeLock;
|
import android.os.PowerManager.WakeLock;
|
||||||
import android.os.SystemClock;
|
import android.os.SystemClock;
|
||||||
import android.system.ErrnoException;
|
import android.system.ErrnoException;
|
||||||
import android.system.NetlinkSocketAddress;
|
|
||||||
import android.system.OsConstants;
|
import android.system.OsConstants;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import com.android.internal.annotations.GuardedBy;
|
import com.android.internal.annotations.GuardedBy;
|
||||||
import com.android.internal.annotations.VisibleForTesting;
|
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.InterruptedIOException;
|
||||||
|
import java.io.PrintWriter;
|
||||||
import java.net.Inet6Address;
|
import java.net.Inet6Address;
|
||||||
import java.net.InetAddress;
|
import java.net.InetAddress;
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
@@ -134,6 +131,8 @@ import java.util.Set;
|
|||||||
* state it may be best for the link to disconnect completely and
|
* state it may be best for the link to disconnect completely and
|
||||||
* reconnect afresh.
|
* reconnect afresh.
|
||||||
*
|
*
|
||||||
|
* Accessing an instance of this class from multiple threads is NOT safe.
|
||||||
|
*
|
||||||
* @hide
|
* @hide
|
||||||
*/
|
*/
|
||||||
public class IpReachabilityMonitor {
|
public class IpReachabilityMonitor {
|
||||||
@@ -169,64 +168,33 @@ public class IpReachabilityMonitor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private final Object mLock = new Object();
|
|
||||||
private final String mInterfaceName;
|
private final String mInterfaceName;
|
||||||
private final int mInterfaceIndex;
|
private final int mInterfaceIndex;
|
||||||
|
private final IpNeighborMonitor mIpNeighborMonitor;
|
||||||
private final SharedLog mLog;
|
private final SharedLog mLog;
|
||||||
private final Callback mCallback;
|
private final Callback mCallback;
|
||||||
private final Dependencies mDependencies;
|
private final Dependencies mDependencies;
|
||||||
private final MultinetworkPolicyTracker mMultinetworkPolicyTracker;
|
private final MultinetworkPolicyTracker mMultinetworkPolicyTracker;
|
||||||
private final NetlinkSocketObserver mNetlinkSocketObserver;
|
|
||||||
private final Thread mObserverThread;
|
|
||||||
private final IpConnectivityLog mMetricsLog = new IpConnectivityLog();
|
private final IpConnectivityLog mMetricsLog = new IpConnectivityLog();
|
||||||
@GuardedBy("mLock")
|
|
||||||
private LinkProperties mLinkProperties = new LinkProperties();
|
private LinkProperties mLinkProperties = new LinkProperties();
|
||||||
// TODO: consider a map to a private NeighborState class holding more
|
private Map<InetAddress, NeighborEvent> mNeighborWatchList = new HashMap<>();
|
||||||
// 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;
|
|
||||||
// Time in milliseconds of the last forced probe request.
|
// Time in milliseconds of the last forced probe request.
|
||||||
private volatile long mLastProbeTimeMs;
|
private volatile long mLastProbeTimeMs;
|
||||||
|
|
||||||
/**
|
public IpReachabilityMonitor(
|
||||||
* Make the kernel perform neighbor reachability detection (IPv4 ARP or IPv6 ND)
|
Context context, String ifName, Handler h, SharedLog log, Callback callback) {
|
||||||
* for the given IP address on the specified interface index.
|
this(context, ifName, h, log, callback, null);
|
||||||
*
|
|
||||||
* @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,
|
||||||
|
|
||||||
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,
|
|
||||||
MultinetworkPolicyTracker tracker) {
|
MultinetworkPolicyTracker tracker) {
|
||||||
this(ifName, getInterfaceIndex(ifName), log, callback, tracker,
|
this(ifName, getInterfaceIndex(ifName), h, log, callback, tracker,
|
||||||
Dependencies.makeDefault(context, ifName));
|
Dependencies.makeDefault(context, ifName));
|
||||||
}
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
@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) {
|
MultinetworkPolicyTracker tracker, Dependencies dependencies) {
|
||||||
mInterfaceName = ifName;
|
mInterfaceName = ifName;
|
||||||
mLog = log.forSubComponent(TAG);
|
mLog = log.forSubComponent(TAG);
|
||||||
@@ -234,47 +202,56 @@ public class IpReachabilityMonitor {
|
|||||||
mMultinetworkPolicyTracker = tracker;
|
mMultinetworkPolicyTracker = tracker;
|
||||||
mInterfaceIndex = ifIndex;
|
mInterfaceIndex = ifIndex;
|
||||||
mDependencies = dependencies;
|
mDependencies = dependencies;
|
||||||
mNetlinkSocketObserver = new NetlinkSocketObserver();
|
|
||||||
mObserverThread = new Thread(mNetlinkSocketObserver);
|
mIpNeighborMonitor = new IpNeighborMonitor(h, mLog,
|
||||||
mObserverThread.start();
|
(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() {
|
public void stop() {
|
||||||
mRunning = false;
|
mIpNeighborMonitor.stop();
|
||||||
clearLinkProperties();
|
clearLinkProperties();
|
||||||
mNetlinkSocketObserver.clearNetlinkSocket();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: add a public dump() method that can be called during a bug report.
|
public void dump(PrintWriter pw) {
|
||||||
|
DumpUtils.dumpAsync(
|
||||||
private String describeWatchList() {
|
mIpNeighborMonitor.getHandler(),
|
||||||
final String delimiter = ", ";
|
new Dump() {
|
||||||
StringBuilder sb = new StringBuilder();
|
@Override
|
||||||
synchronized (mLock) {
|
public void dump(PrintWriter pw, String prefix) {
|
||||||
sb.append("iface{" + mInterfaceName + "/" + mInterfaceIndex + "}, ");
|
pw.println(describeWatchList("\n"));
|
||||||
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()));
|
pw, "", 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
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("]");
|
sb.append("]");
|
||||||
}
|
|
||||||
return sb.toString();
|
return sb.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isWatching(InetAddress ip) {
|
|
||||||
synchronized (mLock) {
|
|
||||||
return mRunning && mIpWatchList.containsKey(ip);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static boolean isOnLink(List<RouteInfo> routes, InetAddress ip) {
|
private static boolean isOnLink(List<RouteInfo> routes, InetAddress ip) {
|
||||||
for (RouteInfo route : routes) {
|
for (RouteInfo route : routes) {
|
||||||
if (!route.hasGateway() && route.matches(ip)) {
|
if (!route.hasGateway() && route.matches(ip)) {
|
||||||
@@ -284,13 +261,6 @@ public class IpReachabilityMonitor {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private short getNeighborStateLocked(InetAddress ip) {
|
|
||||||
if (mIpWatchList.containsKey(ip)) {
|
|
||||||
return mIpWatchList.get(ip);
|
|
||||||
}
|
|
||||||
return StructNdMsg.NUD_NONE;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void updateLinkProperties(LinkProperties lp) {
|
public void updateLinkProperties(LinkProperties lp) {
|
||||||
if (!mInterfaceName.equals(lp.getInterfaceName())) {
|
if (!mInterfaceName.equals(lp.getInterfaceName())) {
|
||||||
// TODO: figure out whether / how to cope with interface changes.
|
// TODO: figure out whether / how to cope with interface changes.
|
||||||
@@ -299,51 +269,44 @@ public class IpReachabilityMonitor {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
synchronized (mLock) {
|
|
||||||
mLinkProperties = new LinkProperties(lp);
|
mLinkProperties = new LinkProperties(lp);
|
||||||
Map<InetAddress, Short> newIpWatchList = new HashMap<>();
|
Map<InetAddress, NeighborEvent> newNeighborWatchList = new HashMap<>();
|
||||||
|
|
||||||
final List<RouteInfo> routes = mLinkProperties.getRoutes();
|
final List<RouteInfo> routes = mLinkProperties.getRoutes();
|
||||||
for (RouteInfo route : routes) {
|
for (RouteInfo route : routes) {
|
||||||
if (route.hasGateway()) {
|
if (route.hasGateway()) {
|
||||||
InetAddress gw = route.getGateway();
|
InetAddress gw = route.getGateway();
|
||||||
if (isOnLink(routes, gw)) {
|
if (isOnLink(routes, gw)) {
|
||||||
newIpWatchList.put(gw, getNeighborStateLocked(gw));
|
newNeighborWatchList.put(gw, mNeighborWatchList.getOrDefault(gw, null));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (InetAddress nameserver : lp.getDnsServers()) {
|
for (InetAddress dns : lp.getDnsServers()) {
|
||||||
if (isOnLink(routes, nameserver)) {
|
if (isOnLink(routes, dns)) {
|
||||||
newIpWatchList.put(nameserver, getNeighborStateLocked(nameserver));
|
newNeighborWatchList.put(dns, mNeighborWatchList.getOrDefault(dns, null));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mIpWatchList = newIpWatchList;
|
mNeighborWatchList = newNeighborWatchList;
|
||||||
mIpWatchListVersion++;
|
|
||||||
}
|
|
||||||
if (DBG) { Log.d(TAG, "watch: " + describeWatchList()); }
|
if (DBG) { Log.d(TAG, "watch: " + describeWatchList()); }
|
||||||
}
|
}
|
||||||
|
|
||||||
public void clearLinkProperties() {
|
public void clearLinkProperties() {
|
||||||
synchronized (mLock) {
|
|
||||||
mLinkProperties.clear();
|
mLinkProperties.clear();
|
||||||
mIpWatchList.clear();
|
mNeighborWatchList.clear();
|
||||||
mIpWatchListVersion++;
|
|
||||||
}
|
|
||||||
if (DBG) { Log.d(TAG, "clear: " + describeWatchList()); }
|
if (DBG) { Log.d(TAG, "clear: " + describeWatchList()); }
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleNeighborLost(String msg) {
|
private void handleNeighborLost(NeighborEvent event) {
|
||||||
InetAddress ip = null;
|
final LinkProperties whatIfLp = new LinkProperties(mLinkProperties);
|
||||||
final ProvisioningChange delta;
|
|
||||||
synchronized (mLock) {
|
|
||||||
LinkProperties whatIfLp = new LinkProperties(mLinkProperties);
|
|
||||||
|
|
||||||
for (Map.Entry<InetAddress, Short> entry : mIpWatchList.entrySet()) {
|
InetAddress ip = null;
|
||||||
if (entry.getValue() != StructNdMsg.NUD_FAILED) {
|
for (Map.Entry<InetAddress, NeighborEvent> entry : mNeighborWatchList.entrySet()) {
|
||||||
continue;
|
// 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();
|
ip = entry.getKey();
|
||||||
for (RouteInfo route : mLinkProperties.getRoutes()) {
|
for (RouteInfo route : mLinkProperties.getRoutes()) {
|
||||||
@@ -358,11 +321,11 @@ public class IpReachabilityMonitor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
delta = LinkProperties.compareProvisioning(mLinkProperties, whatIfLp);
|
final ProvisioningChange delta = LinkProperties.compareProvisioning(
|
||||||
}
|
mLinkProperties, whatIfLp);
|
||||||
|
|
||||||
if (delta == ProvisioningChange.LOST_PROVISIONING) {
|
if (delta == ProvisioningChange.LOST_PROVISIONING) {
|
||||||
final String logMsg = "FAILURE: LOST_PROVISIONING, " + msg;
|
final String logMsg = "FAILURE: LOST_PROVISIONING, " + event;
|
||||||
Log.w(TAG, logMsg);
|
Log.w(TAG, logMsg);
|
||||||
if (mCallback != null) {
|
if (mCallback != null) {
|
||||||
// TODO: remove |ip| when the callback signature no longer has
|
// TODO: remove |ip| when the callback signature no longer has
|
||||||
@@ -378,12 +341,9 @@ public class IpReachabilityMonitor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void probeAll() {
|
public void probeAll() {
|
||||||
final List<InetAddress> ipProbeList;
|
final List<InetAddress> ipProbeList = new ArrayList<>(mNeighborWatchList.keySet());
|
||||||
synchronized (mLock) {
|
|
||||||
ipProbeList = new ArrayList<>(mIpWatchList.keySet());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!ipProbeList.isEmpty() && mRunning) {
|
if (!ipProbeList.isEmpty()) {
|
||||||
// Keep the CPU awake long enough to allow all ARP/ND
|
// Keep the CPU awake long enough to allow all ARP/ND
|
||||||
// probes a reasonable chance at success. See b/23197666.
|
// probes a reasonable chance at success. See b/23197666.
|
||||||
//
|
//
|
||||||
@@ -394,13 +354,10 @@ public class IpReachabilityMonitor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (InetAddress target : ipProbeList) {
|
for (InetAddress target : ipProbeList) {
|
||||||
if (!mRunning) {
|
final int rval = IpNeighborMonitor.startKernelNeighborProbe(mInterfaceIndex, target);
|
||||||
break;
|
|
||||||
}
|
|
||||||
final int returnValue = probeNeighbor(mInterfaceIndex, target);
|
|
||||||
mLog.log(String.format("put neighbor %s into NUD_PROBE state (rval=%d)",
|
mLog.log(String.format("put neighbor %s into NUD_PROBE state (rval=%d)",
|
||||||
target.getHostAddress(), returnValue));
|
target.getHostAddress(), rval));
|
||||||
logEvent(IpReachabilityEvent.PROBE, returnValue);
|
logEvent(IpReachabilityEvent.PROBE, rval);
|
||||||
}
|
}
|
||||||
mLastProbeTimeMs = SystemClock.elapsedRealtime();
|
mLastProbeTimeMs = SystemClock.elapsedRealtime();
|
||||||
}
|
}
|
||||||
@@ -446,153 +403,4 @@ public class IpReachabilityMonitor {
|
|||||||
int eventType = IpReachabilityEvent.nudFailureEventType(isFromProbe, isProvisioningLost);
|
int eventType = IpReachabilityEvent.nudFailureEventType(isFromProbe, isProvisioningLost);
|
||||||
mMetricsLog.log(mInterfaceName, new IpReachabilityEvent(eventType));
|
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,16 +16,24 @@
|
|||||||
|
|
||||||
package android.net.netlink;
|
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.ErrnoException;
|
||||||
import android.system.NetlinkSocketAddress;
|
import android.system.NetlinkSocketAddress;
|
||||||
import android.system.Os;
|
import android.system.Os;
|
||||||
import android.system.OsConstants;
|
|
||||||
import android.system.StructTimeval;
|
import android.system.StructTimeval;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import libcore.io.IoUtils;
|
import libcore.io.IoUtils;
|
||||||
import libcore.io.Libcore;
|
import libcore.io.Libcore;
|
||||||
|
|
||||||
import java.io.Closeable;
|
|
||||||
import java.io.FileDescriptor;
|
import java.io.FileDescriptor;
|
||||||
import java.io.InterruptedIOException;
|
import java.io.InterruptedIOException;
|
||||||
import java.net.SocketAddress;
|
import java.net.SocketAddress;
|
||||||
@@ -37,28 +45,27 @@ import java.nio.ByteOrder;
|
|||||||
/**
|
/**
|
||||||
* NetlinkSocket
|
* NetlinkSocket
|
||||||
*
|
*
|
||||||
* A small wrapper class to assist with AF_NETLINK socket operations.
|
* A small static class to assist with AF_NETLINK socket operations.
|
||||||
*
|
*
|
||||||
* @hide
|
* @hide
|
||||||
*/
|
*/
|
||||||
public class NetlinkSocket implements Closeable {
|
public class NetlinkSocket {
|
||||||
private static final String TAG = "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;
|
public static final int DEFAULT_RECV_BUFSIZE = 8 * 1024;
|
||||||
private NetlinkSocketAddress mAddr;
|
public static final int SOCKET_RECV_BUFSIZE = 64 * 1024;
|
||||||
private long mLastRecvTimeoutMs;
|
|
||||||
private long mLastSendTimeoutMs;
|
|
||||||
|
|
||||||
public static void sendOneShotKernelMessage(int nlProto, byte[] msg) throws ErrnoException {
|
public static void sendOneShotKernelMessage(int nlProto, byte[] msg) throws ErrnoException {
|
||||||
final String errPrefix = "Error in NetlinkSocket.sendOneShotKernelMessage";
|
final String errPrefix = "Error in NetlinkSocket.sendOneShotKernelMessage";
|
||||||
|
|
||||||
try (NetlinkSocket nlSocket = new NetlinkSocket(nlProto)) {
|
|
||||||
final long IO_TIMEOUT = 300L;
|
final long IO_TIMEOUT = 300L;
|
||||||
nlSocket.connectToKernel();
|
|
||||||
nlSocket.sendMessage(msg, 0, msg.length, IO_TIMEOUT);
|
FileDescriptor fd;
|
||||||
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.
|
// recvMessage() guaranteed to not return null if it did not throw.
|
||||||
final NetlinkMessage response = NetlinkMessage.parse(bytes);
|
final NetlinkMessage response = NetlinkMessage.parse(bytes);
|
||||||
if (response != null && response instanceof NetlinkErrorMessage &&
|
if (response != null && response instanceof NetlinkErrorMessage &&
|
||||||
@@ -81,61 +88,30 @@ public class NetlinkSocket implements Closeable {
|
|||||||
errmsg = response.toString();
|
errmsg = response.toString();
|
||||||
}
|
}
|
||||||
Log.e(TAG, errPrefix + ", errmsg=" + errmsg);
|
Log.e(TAG, errPrefix + ", errmsg=" + errmsg);
|
||||||
throw new ErrnoException(errmsg, OsConstants.EPROTO);
|
throw new ErrnoException(errmsg, EPROTO);
|
||||||
}
|
}
|
||||||
} catch (InterruptedIOException e) {
|
} catch (InterruptedIOException e) {
|
||||||
Log.e(TAG, errPrefix, e);
|
Log.e(TAG, errPrefix, e);
|
||||||
throw new ErrnoException(errPrefix, OsConstants.ETIMEDOUT, e);
|
throw new ErrnoException(errPrefix, ETIMEDOUT, e);
|
||||||
} catch (SocketException e) {
|
} catch (SocketException e) {
|
||||||
Log.e(TAG, errPrefix, 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 {
|
IoUtils.closeQuietly(fd);
|
||||||
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 {
|
public static FileDescriptor forProto(int nlProto) throws ErrnoException {
|
||||||
return (NetlinkSocketAddress) Os.getsockname(mDescriptor);
|
final FileDescriptor fd = Os.socket(AF_NETLINK, SOCK_DGRAM, nlProto);
|
||||||
|
Os.setsockoptInt(fd, SOL_SOCKET, SO_RCVBUF, SOCKET_RECV_BUFSIZE);
|
||||||
|
return fd;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void bind(NetlinkSocketAddress localAddr) throws ErrnoException, SocketException {
|
public static void connectToKernel(FileDescriptor fd) throws ErrnoException, SocketException {
|
||||||
Os.bind(mDescriptor, (SocketAddress)localAddr);
|
Os.connect(fd, (SocketAddress) (new NetlinkSocketAddress(0, 0)));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void connectTo(NetlinkSocketAddress peerAddr)
|
private static void checkTimeout(long timeoutMs) {
|
||||||
throws ErrnoException, SocketException {
|
|
||||||
Os.connect(mDescriptor, (SocketAddress) peerAddr);
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void checkTimeout(long timeoutMs) {
|
|
||||||
if (timeoutMs < 0) {
|
if (timeoutMs < 0) {
|
||||||
throw new IllegalArgumentException("Negative timeouts not permitted");
|
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.
|
* 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 {
|
throws ErrnoException, IllegalArgumentException, InterruptedIOException {
|
||||||
checkTimeout(timeoutMs);
|
checkTimeout(timeoutMs);
|
||||||
|
|
||||||
synchronized (mDescriptor) {
|
Os.setsockoptTimeval(fd, SOL_SOCKET, SO_RCVTIMEO, StructTimeval.fromMillis(timeoutMs));
|
||||||
if (mLastRecvTimeoutMs != timeoutMs) {
|
|
||||||
Os.setsockoptTimeval(mDescriptor,
|
|
||||||
OsConstants.SOL_SOCKET, OsConstants.SO_RCVTIMEO,
|
|
||||||
StructTimeval.fromMillis(timeoutMs));
|
|
||||||
mLastRecvTimeoutMs = timeoutMs;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ByteBuffer byteBuffer = ByteBuffer.allocate(bufsize);
|
ByteBuffer byteBuffer = ByteBuffer.allocate(bufsize);
|
||||||
int length = Os.read(mDescriptor, byteBuffer);
|
int length = Os.read(fd, byteBuffer);
|
||||||
if (length == bufsize) {
|
if (length == bufsize) {
|
||||||
Log.w(TAG, "maximum read");
|
Log.w(TAG, "maximum read");
|
||||||
}
|
}
|
||||||
@@ -171,40 +140,17 @@ public class NetlinkSocket implements Closeable {
|
|||||||
return byteBuffer;
|
return byteBuffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 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,
|
* Send a message to a peer to which this socket has previously connected,
|
||||||
* waiting at most |timeoutMs| milliseconds for the send to complete.
|
* waiting at most |timeoutMs| milliseconds for the send to complete.
|
||||||
*
|
*
|
||||||
* Multi-threaded calls with different timeouts will cause unexpected results.
|
* 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 {
|
throws ErrnoException, IllegalArgumentException, InterruptedIOException {
|
||||||
checkTimeout(timeoutMs);
|
checkTimeout(timeoutMs);
|
||||||
|
Os.setsockoptTimeval(fd, SOL_SOCKET, SO_SNDTIMEO, StructTimeval.fromMillis(timeoutMs));
|
||||||
synchronized (mDescriptor) {
|
return Os.write(fd, bytes, offset, count);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -63,6 +63,11 @@ public class StructNdMsg {
|
|||||||
return ((nudState & (NUD_PERMANENT|NUD_NOARP|NUD_REACHABLE)) != 0);
|
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
|
// Neighbor Cache Entry Flags
|
||||||
public static byte NTF_USE = (byte) 0x01;
|
public static byte NTF_USE = (byte) 0x01;
|
||||||
public static byte NTF_SELF = (byte) 0x02;
|
public static byte NTF_SELF = (byte) 0x02;
|
||||||
@@ -143,7 +148,7 @@ public class StructNdMsg {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public boolean nudValid() {
|
public boolean nudValid() {
|
||||||
return (nudConnected() || ((ndm_state & (NUD_PROBE|NUD_STALE|NUD_DELAY)) != 0));
|
return isNudStateValid(ndm_state);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ import java.io.IOException;
|
|||||||
*
|
*
|
||||||
* @hide
|
* @hide
|
||||||
*/
|
*/
|
||||||
public abstract class BlockingSocketReader {
|
public abstract class PacketReader {
|
||||||
private static final int FD_EVENTS = EVENT_INPUT | EVENT_ERROR;
|
private static final int FD_EVENTS = EVENT_INPUT | EVENT_ERROR;
|
||||||
private static final int UNREGISTER_THIS_FD = 0;
|
private static final int UNREGISTER_THIS_FD = 0;
|
||||||
|
|
||||||
@@ -83,11 +83,11 @@ public abstract class BlockingSocketReader {
|
|||||||
IoUtils.closeQuietly(fd);
|
IoUtils.closeQuietly(fd);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected BlockingSocketReader(Handler h) {
|
protected PacketReader(Handler h) {
|
||||||
this(h, DEFAULT_RECV_BUF_SIZE);
|
this(h, DEFAULT_RECV_BUF_SIZE);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected BlockingSocketReader(Handler h, int recvbufsize) {
|
protected PacketReader(Handler h, int recvbufsize) {
|
||||||
mHandler = h;
|
mHandler = h;
|
||||||
mQueue = mHandler.getLooper().getQueue();
|
mQueue = mHandler.getLooper().getQueue();
|
||||||
mPacket = new byte[Math.max(recvbufsize, DEFAULT_RECV_BUF_SIZE)];
|
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 int recvBufSize() { return mPacket.length; }
|
||||||
|
|
||||||
public final long numPacketsReceived() { return mPacketsReceived; }
|
public final long numPacketsReceived() { return mPacketsReceived; }
|
||||||
@@ -18,10 +18,12 @@ package android.net.ip;
|
|||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.fail;
|
import static org.junit.Assert.fail;
|
||||||
import static org.mockito.Mockito.any;
|
import static org.mockito.Mockito.anyString;
|
||||||
import static org.mockito.Mockito.anyInt;
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
import android.net.util.SharedLog;
|
import android.net.util.SharedLog;
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.os.Looper;
|
||||||
import android.support.test.filters.SmallTest;
|
import android.support.test.filters.SmallTest;
|
||||||
import android.support.test.runner.AndroidJUnit4;
|
import android.support.test.runner.AndroidJUnit4;
|
||||||
|
|
||||||
@@ -42,14 +44,18 @@ public class IpReachabilityMonitorTest {
|
|||||||
@Mock IpReachabilityMonitor.Callback mCallback;
|
@Mock IpReachabilityMonitor.Callback mCallback;
|
||||||
@Mock IpReachabilityMonitor.Dependencies mDependencies;
|
@Mock IpReachabilityMonitor.Dependencies mDependencies;
|
||||||
@Mock SharedLog mLog;
|
@Mock SharedLog mLog;
|
||||||
|
Handler mHandler;
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setUp() {
|
public void setUp() {
|
||||||
MockitoAnnotations.initMocks(this);
|
MockitoAnnotations.initMocks(this);
|
||||||
|
when(mLog.forSubComponent(anyString())).thenReturn(mLog);
|
||||||
|
mHandler = new Handler(Looper.getMainLooper());
|
||||||
}
|
}
|
||||||
|
|
||||||
IpReachabilityMonitor makeMonitor() {
|
IpReachabilityMonitor makeMonitor() {
|
||||||
return new IpReachabilityMonitor("fake0", 1, mLog, mCallback, null, mDependencies);
|
return new IpReachabilityMonitor(
|
||||||
|
"fake0", 1, mHandler, mLog, mCallback, null, mDependencies);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
@@ -16,6 +16,8 @@
|
|||||||
|
|
||||||
package android.net.netlink;
|
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.assertEquals;
|
||||||
import static org.junit.Assert.assertNotNull;
|
import static org.junit.Assert.assertNotNull;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
@@ -28,10 +30,12 @@ import android.support.test.runner.AndroidJUnit4;
|
|||||||
import android.support.test.filters.SmallTest;
|
import android.support.test.filters.SmallTest;
|
||||||
import android.system.ErrnoException;
|
import android.system.ErrnoException;
|
||||||
import android.system.NetlinkSocketAddress;
|
import android.system.NetlinkSocketAddress;
|
||||||
import android.system.OsConstants;
|
import android.system.Os;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
import libcore.io.IoUtils;
|
||||||
|
|
||||||
import java.io.InterruptedIOException;
|
import java.io.InterruptedIOException;
|
||||||
|
import java.io.FileDescriptor;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.nio.ByteOrder;
|
import java.nio.ByteOrder;
|
||||||
|
|
||||||
@@ -46,29 +50,28 @@ public class NetlinkSocketTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testBasicWorkingGetNeighborsQuery() throws Exception {
|
public void testBasicWorkingGetNeighborsQuery() throws Exception {
|
||||||
NetlinkSocket s = new NetlinkSocket(OsConstants.NETLINK_ROUTE);
|
final FileDescriptor fd = NetlinkSocket.forProto(NETLINK_ROUTE);
|
||||||
assertNotNull(s);
|
assertNotNull(fd);
|
||||||
|
|
||||||
s.connectToKernel();
|
NetlinkSocket.connectToKernel(fd);
|
||||||
|
|
||||||
NetlinkSocketAddress localAddr = s.getLocalAddress();
|
final NetlinkSocketAddress localAddr = (NetlinkSocketAddress) Os.getsockname(fd);
|
||||||
assertNotNull(localAddr);
|
assertNotNull(localAddr);
|
||||||
assertEquals(0, localAddr.getGroupsMask());
|
assertEquals(0, localAddr.getGroupsMask());
|
||||||
assertTrue(0 != localAddr.getPortId());
|
assertTrue(0 != localAddr.getPortId());
|
||||||
|
|
||||||
final int TEST_SEQNO = 5;
|
final int TEST_SEQNO = 5;
|
||||||
final byte[] request = RtNetlinkNeighborMessage.newGetNeighborsRequest(TEST_SEQNO);
|
final byte[] req = RtNetlinkNeighborMessage.newGetNeighborsRequest(TEST_SEQNO);
|
||||||
assertNotNull(request);
|
assertNotNull(req);
|
||||||
|
|
||||||
final long TIMEOUT = 500;
|
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 neighMessageCount = 0;
|
||||||
int doneMessageCount = 0;
|
int doneMessageCount = 0;
|
||||||
|
|
||||||
while (doneMessageCount == 0) {
|
while (doneMessageCount == 0) {
|
||||||
ByteBuffer response = null;
|
ByteBuffer response = NetlinkSocket.recvMessage(fd, DEFAULT_RECV_BUFSIZE, TIMEOUT);
|
||||||
response = s.recvMessage(TIMEOUT);
|
|
||||||
assertNotNull(response);
|
assertNotNull(response);
|
||||||
assertTrue(StructNlMsgHdr.STRUCT_SIZE <= response.limit());
|
assertTrue(StructNlMsgHdr.STRUCT_SIZE <= response.limit());
|
||||||
assertEquals(0, response.position());
|
assertEquals(0, response.position());
|
||||||
@@ -100,30 +103,6 @@ public class NetlinkSocketTest {
|
|||||||
// TODO: make sure this test passes sanely in airplane mode.
|
// TODO: make sure this test passes sanely in airplane mode.
|
||||||
assertTrue(neighMessageCount > 0);
|
assertTrue(neighMessageCount > 0);
|
||||||
|
|
||||||
s.close();
|
IoUtils.closeQuietly(fd);
|
||||||
}
|
|
||||||
|
|
||||||
@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();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
package android.net.util;
|
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 android.system.OsConstants.*;
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertFalse;
|
import static org.junit.Assert.assertFalse;
|
||||||
@@ -53,13 +53,13 @@ import org.junit.Test;
|
|||||||
import libcore.io.IoBridge;
|
import libcore.io.IoBridge;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests for BlockingSocketReader.
|
* Tests for PacketReader.
|
||||||
*
|
*
|
||||||
* @hide
|
* @hide
|
||||||
*/
|
*/
|
||||||
@RunWith(AndroidJUnit4.class)
|
@RunWith(AndroidJUnit4.class)
|
||||||
@SmallTest
|
@SmallTest
|
||||||
public class BlockingSocketReaderTest {
|
public class PacketReaderTest {
|
||||||
static final InetAddress LOOPBACK6 = Inet6Address.getLoopbackAddress();
|
static final InetAddress LOOPBACK6 = Inet6Address.getLoopbackAddress();
|
||||||
static final StructTimeval TIMEO = StructTimeval.fromMillis(500);
|
static final StructTimeval TIMEO = StructTimeval.fromMillis(500);
|
||||||
|
|
||||||
@@ -69,9 +69,9 @@ public class BlockingSocketReaderTest {
|
|||||||
protected byte[] mLastRecvBuf;
|
protected byte[] mLastRecvBuf;
|
||||||
protected boolean mStopped;
|
protected boolean mStopped;
|
||||||
protected HandlerThread mHandlerThread;
|
protected HandlerThread mHandlerThread;
|
||||||
protected BlockingSocketReader mReceiver;
|
protected PacketReader mReceiver;
|
||||||
|
|
||||||
class UdpLoopbackReader extends BlockingSocketReader {
|
class UdpLoopbackReader extends PacketReader {
|
||||||
public UdpLoopbackReader(Handler h) {
|
public UdpLoopbackReader(Handler h) {
|
||||||
super(h);
|
super(h);
|
||||||
}
|
}
|
||||||
@@ -121,7 +121,7 @@ public class BlockingSocketReaderTest {
|
|||||||
mLastRecvBuf = null;
|
mLastRecvBuf = null;
|
||||||
mStopped = false;
|
mStopped = false;
|
||||||
|
|
||||||
mHandlerThread = new HandlerThread(BlockingSocketReaderTest.class.getSimpleName());
|
mHandlerThread = new HandlerThread(PacketReaderTest.class.getSimpleName());
|
||||||
mHandlerThread.start();
|
mHandlerThread.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -188,8 +188,8 @@ public class BlockingSocketReaderTest {
|
|||||||
mReceiver = null;
|
mReceiver = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
class NullBlockingSocketReader extends BlockingSocketReader {
|
class NullPacketReader extends PacketReader {
|
||||||
public NullBlockingSocketReader(Handler h, int recvbufsize) {
|
public NullPacketReader(Handler h, int recvbufsize) {
|
||||||
super(h, recvbufsize);
|
super(h, recvbufsize);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -202,7 +202,7 @@ public class BlockingSocketReaderTest {
|
|||||||
final Handler h = mHandlerThread.getThreadHandler();
|
final Handler h = mHandlerThread.getThreadHandler();
|
||||||
|
|
||||||
for (int i : new int[]{-1, 0, 1, DEFAULT_RECV_BUF_SIZE-1}) {
|
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());
|
assertEquals(DEFAULT_RECV_BUF_SIZE, b.recvBufSize());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user