diff --git a/services/net/java/android/net/ip/IpReachabilityMonitor.java b/services/net/java/android/net/ip/IpReachabilityMonitor.java index 97c9d82e8e312..e833f6a03bce9 100644 --- a/services/net/java/android/net/ip/IpReachabilityMonitor.java +++ b/services/net/java/android/net/ip/IpReachabilityMonitor.java @@ -16,8 +16,6 @@ package android.net.ip; -import com.android.internal.annotations.GuardedBy; - import android.content.Context; import android.net.LinkAddress; import android.net.LinkProperties; @@ -31,18 +29,22 @@ import android.net.netlink.NetlinkErrorMessage; import android.net.netlink.NetlinkMessage; import android.net.netlink.NetlinkSocket; import android.net.netlink.RtNetlinkNeighborMessage; -import android.net.netlink.StructNdaCacheInfo; 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.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 java.io.InterruptedIOException; import java.net.Inet6Address; import java.net.InetAddress; @@ -147,12 +149,32 @@ public class IpReachabilityMonitor { public void notifyLost(InetAddress ip, String logMsg); } + /** + * Encapsulates IpReachabilityMonitor depencencies on systems that hinder unit testing. + * TODO: consider also wrapping MultinetworkPolicyTracker in this interface. + */ + interface Dependencies { + void acquireWakeLock(long durationMs); + + static Dependencies makeDefault(Context context, String iface) { + final String lockName = TAG + "." + iface; + final PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE); + final WakeLock lock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, lockName); + + return new Dependencies() { + public void acquireWakeLock(long durationMs) { + lock.acquire(durationMs); + } + }; + } + } + private final Object mLock = new Object(); - private final PowerManager.WakeLock mWakeLock; private final String mInterfaceName; private final int mInterfaceIndex; private final SharedLog mLog; private final Callback mCallback; + private final Dependencies mDependencies; private final MultinetworkPolicyTracker mMultinetworkPolicyTracker; private final NetlinkSocketObserver mNetlinkSocketObserver; private final Thread mObserverThread; @@ -228,20 +250,20 @@ public class IpReachabilityMonitor { } public IpReachabilityMonitor(Context context, String ifName, SharedLog log, Callback callback, - MultinetworkPolicyTracker tracker) throws IllegalArgumentException { + MultinetworkPolicyTracker tracker) { + this(ifName, getInterfaceIndex(ifName), log, callback, tracker, + Dependencies.makeDefault(context, ifName)); + } + + @VisibleForTesting + IpReachabilityMonitor(String ifName, int ifIndex, SharedLog log, Callback callback, + MultinetworkPolicyTracker tracker, Dependencies dependencies) { mInterfaceName = ifName; - int ifIndex = -1; - try { - NetworkInterface netIf = NetworkInterface.getByName(ifName); - mInterfaceIndex = netIf.getIndex(); - } catch (SocketException | NullPointerException e) { - throw new IllegalArgumentException("invalid interface '" + ifName + "': ", e); - } - mWakeLock = ((PowerManager) context.getSystemService(Context.POWER_SERVICE)).newWakeLock( - PowerManager.PARTIAL_WAKE_LOCK, TAG + "." + mInterfaceName); mLog = log.forSubComponent(TAG); mCallback = callback; mMultinetworkPolicyTracker = tracker; + mInterfaceIndex = ifIndex; + mDependencies = dependencies; mNetlinkSocketObserver = new NetlinkSocketObserver(); mObserverThread = new Thread(mNetlinkSocketObserver); mObserverThread.start(); @@ -398,7 +420,7 @@ public class IpReachabilityMonitor { // The wakelock we use is (by default) refcounted, and this version // of acquire(timeout) queues a release message to keep acquisitions // and releases balanced. - mWakeLock.acquire(getProbeWakeLockDuration()); + mDependencies.acquireWakeLock(getProbeWakeLockDuration()); } for (InetAddress target : ipProbeList) { @@ -429,6 +451,19 @@ public class IpReachabilityMonitor { return (numUnicastProbes * retransTimeMs) + gracePeriodMs; } + private static int getInterfaceIndex(String ifname) { + final NetworkInterface iface; + try { + iface = NetworkInterface.getByName(ifname); + } catch (SocketException e) { + throw new IllegalArgumentException("invalid interface '" + ifname + "': ", e); + } + if (iface == null) { + throw new IllegalArgumentException("NetworkInterface was null for " + ifname); + } + return iface.getIndex(); + } + private void logEvent(int probeType, int errorCode) { int eventType = probeType | (errorCode & 0xff); mMetricsLog.log(mInterfaceName, new IpReachabilityEvent(eventType)); diff --git a/tests/net/java/android/net/ip/IpReachabilityMonitorTest.java b/tests/net/java/android/net/ip/IpReachabilityMonitorTest.java new file mode 100644 index 0000000000000..f849689abb23a --- /dev/null +++ b/tests/net/java/android/net/ip/IpReachabilityMonitorTest.java @@ -0,0 +1,59 @@ +/* + * 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 static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.anyInt; + +import android.net.util.SharedLog; +import android.support.test.filters.SmallTest; +import android.support.test.runner.AndroidJUnit4; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + + +/** + * Tests for IpReachabilityMonitor. + */ +@RunWith(AndroidJUnit4.class) +@SmallTest +public class IpReachabilityMonitorTest { + + @Mock IpReachabilityMonitor.Callback mCallback; + @Mock IpReachabilityMonitor.Dependencies mDependencies; + @Mock SharedLog mLog; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + } + + IpReachabilityMonitor makeMonitor() { + return new IpReachabilityMonitor("fake0", 1, mLog, mCallback, null, mDependencies); + } + + @Test + public void testNothing() { + IpReachabilityMonitor monitor = makeMonitor(); + } +}