Merge "Add unit test for IpSecService"
diff --git a/core/java/com/android/internal/os/ZygoteConnection.java b/core/java/com/android/internal/os/ZygoteConnection.java
index 91429a6..941ded3 100644
--- a/core/java/com/android/internal/os/ZygoteConnection.java
+++ b/core/java/com/android/internal/os/ZygoteConnection.java
@@ -18,6 +18,7 @@
import static android.system.OsConstants.F_SETFD;
import static android.system.OsConstants.O_CLOEXEC;
+import static android.system.OsConstants.POLLIN;
import static android.system.OsConstants.STDERR_FILENO;
import static android.system.OsConstants.STDIN_FILENO;
import static android.system.OsConstants.STDOUT_FILENO;
@@ -31,13 +32,14 @@
import android.os.Trace;
import android.system.ErrnoException;
import android.system.Os;
+import android.system.StructPollfd;
import android.util.Log;
import dalvik.system.VMRuntime;
import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.FileDescriptor;
-import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
@@ -807,17 +809,63 @@
boolean usingWrapper = false;
if (pipeFd != null && pid > 0) {
- DataInputStream is = new DataInputStream(new FileInputStream(pipeFd));
int innerPid = -1;
try {
- innerPid = is.readInt();
- } catch (IOException ex) {
- Log.w(TAG, "Error reading pid from wrapped process, child may have died", ex);
- } finally {
- try {
- is.close();
- } catch (IOException ex) {
+ // Do a busy loop here. We can't guarantee that a failure (and thus an exception
+ // bail) happens in a timely manner.
+ //
+ // We'll wait up to five seconds. This should give enough time for the fork to go
+ // through, but not to trigger the watchdog in the system server.
+ final int SLEEP_IN_MS = 5000;
+ final int BYTES_REQUIRED = 4; // Bytes in an int.
+
+ StructPollfd fds[] = new StructPollfd[] {
+ new StructPollfd()
+ };
+
+ byte data[] = new byte[BYTES_REQUIRED];
+
+ int remainingSleepTime = SLEEP_IN_MS;
+ int dataIndex = 0;
+ long startTime = System.nanoTime();
+
+ while (dataIndex < data.length && remainingSleepTime > 0) {
+ fds[0].fd = pipeFd;
+ fds[0].events = (short) POLLIN;
+ fds[0].revents = 0;
+ fds[0].userData = null;
+
+ int res = android.system.Os.poll(fds, remainingSleepTime);
+ long endTime = System.nanoTime();
+ remainingSleepTime = SLEEP_IN_MS - (int)((endTime - startTime) / 1000000l);
+
+ if (res > 0) {
+ if ((fds[0].revents & POLLIN) != 0) {
+ // Only read one byte, so as not to block.
+ int readBytes = android.system.Os.read(pipeFd, data, dataIndex, 1);
+ if (readBytes < 0) {
+ throw new RuntimeException("Some error");
+ }
+ dataIndex += readBytes;
+ } else {
+ // Error case. revents should contain one of the error bits.
+ break;
+ }
+ } else if (res == 0) {
+ Log.w(TAG, "Timed out waiting for child.");
+ }
}
+
+ if (dataIndex == data.length) {
+ DataInputStream is = new DataInputStream(new ByteArrayInputStream(data));
+ innerPid = is.readInt();
+ }
+
+ if (innerPid == -1) {
+ Log.w(TAG, "Error reading pid from wrapped process, child may have died");
+ }
+ } catch (Exception ex) {
+ Log.w(TAG, "Error reading pid from wrapped process, child may have died", ex);
}
// Ensure that the pid reported by the wrapped process is either the
diff --git a/tests/net/java/android/net/IpSecManagerTest.java b/tests/net/java/android/net/IpSecManagerTest.java
new file mode 100644
index 0000000..9f31d27
--- /dev/null
+++ b/tests/net/java/android/net/IpSecManagerTest.java
@@ -0,0 +1,225 @@
+/*
+ * 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;
+
+import static android.system.OsConstants.AF_INET;
+import static android.system.OsConstants.IPPROTO_UDP;
+import static android.system.OsConstants.SOCK_DGRAM;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.fail;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.anyObject;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.support.test.filters.SmallTest;
+import android.system.Os;
+import android.test.AndroidTestCase;
+import com.android.server.IpSecService;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** Unit tests for {@link IpSecManager}. */
+@SmallTest
+@RunWith(JUnit4.class)
+public class IpSecManagerTest {
+
+ private static final int TEST_UDP_ENCAP_PORT = 34567;
+ private static final int DROID_SPI = 0xD1201D;
+
+ private static final InetAddress GOOGLE_DNS_4;
+
+ static {
+ try {
+ // Google Public DNS Addresses;
+ GOOGLE_DNS_4 = InetAddress.getByName("8.8.8.8");
+ } catch (UnknownHostException e) {
+ throw new RuntimeException("Could not resolve DNS Addresses", e);
+ }
+ }
+
+ private IpSecService mMockIpSecService;
+ private IpSecManager mIpSecManager;
+
+ @Before
+ public void setUp() throws Exception {
+ mMockIpSecService = mock(IpSecService.class);
+ mIpSecManager = new IpSecManager(mMockIpSecService);
+ }
+
+ /*
+ * Allocate a specific SPI
+ * Close SPIs
+ */
+ @Test
+ public void testAllocSpi() throws Exception {
+ int resourceId = 1;
+ IpSecSpiResponse spiResp =
+ new IpSecSpiResponse(IpSecManager.Status.OK, resourceId, DROID_SPI);
+ when(mMockIpSecService.reserveSecurityParameterIndex(
+ eq(IpSecTransform.DIRECTION_IN),
+ eq(GOOGLE_DNS_4.getHostAddress()),
+ eq(DROID_SPI),
+ anyObject()))
+ .thenReturn(spiResp);
+
+ IpSecManager.SecurityParameterIndex droidSpi =
+ mIpSecManager.reserveSecurityParameterIndex(
+ IpSecTransform.DIRECTION_IN, GOOGLE_DNS_4, DROID_SPI);
+ assertEquals(DROID_SPI, droidSpi.getSpi());
+
+ droidSpi.close();
+
+ verify(mMockIpSecService).releaseSecurityParameterIndex(resourceId);
+ }
+
+ @Test
+ public void testAllocRandomSpi() throws Exception {
+ int resourceId = 1;
+ IpSecSpiResponse spiResp =
+ new IpSecSpiResponse(IpSecManager.Status.OK, resourceId, DROID_SPI);
+ when(mMockIpSecService.reserveSecurityParameterIndex(
+ eq(IpSecTransform.DIRECTION_OUT),
+ eq(GOOGLE_DNS_4.getHostAddress()),
+ eq(IpSecManager.INVALID_SECURITY_PARAMETER_INDEX),
+ anyObject()))
+ .thenReturn(spiResp);
+
+ IpSecManager.SecurityParameterIndex randomSpi =
+ mIpSecManager.reserveSecurityParameterIndex(
+ IpSecTransform.DIRECTION_OUT, GOOGLE_DNS_4);
+
+ assertEquals(DROID_SPI, randomSpi.getSpi());
+
+ randomSpi.close();
+
+ verify(mMockIpSecService).releaseSecurityParameterIndex(resourceId);
+ }
+
+ /*
+ * Throws resource unavailable exception
+ */
+ @Test
+ public void testAllocSpiResUnavaiableExeption() throws Exception {
+ IpSecSpiResponse spiResp =
+ new IpSecSpiResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE, 0, 0);
+ when(mMockIpSecService.reserveSecurityParameterIndex(
+ anyInt(), anyString(), anyInt(), anyObject()))
+ .thenReturn(spiResp);
+
+ try {
+ mIpSecManager.reserveSecurityParameterIndex(IpSecTransform.DIRECTION_OUT, GOOGLE_DNS_4);
+ fail("ResourceUnavailableException was not thrown");
+ } catch (IpSecManager.ResourceUnavailableException e) {
+ }
+ }
+
+ /*
+ * Throws spi unavailable exception
+ */
+ @Test
+ public void testAllocSpiSpiUnavaiableExeption() throws Exception {
+ IpSecSpiResponse spiResp = new IpSecSpiResponse(IpSecManager.Status.SPI_UNAVAILABLE, 0, 0);
+ when(mMockIpSecService.reserveSecurityParameterIndex(
+ anyInt(), anyString(), anyInt(), anyObject()))
+ .thenReturn(spiResp);
+
+ try {
+ mIpSecManager.reserveSecurityParameterIndex(IpSecTransform.DIRECTION_OUT, GOOGLE_DNS_4);
+ fail("ResourceUnavailableException was not thrown");
+ } catch (IpSecManager.ResourceUnavailableException e) {
+ }
+ }
+
+ /*
+ * Should throw exception when request spi 0 in IpSecManager
+ */
+ @Test
+ public void testRequestAllocInvalidSpi() throws Exception {
+ try {
+ mIpSecManager.reserveSecurityParameterIndex(
+ IpSecTransform.DIRECTION_OUT, GOOGLE_DNS_4, 0);
+ fail("Able to allocate invalid spi");
+ } catch (IllegalArgumentException e) {
+ }
+ }
+
+ @Test
+ public void testOpenEncapsulationSocket() throws Exception {
+ int resourceId = 1;
+ IpSecUdpEncapResponse udpEncapResp =
+ new IpSecUdpEncapResponse(
+ IpSecManager.Status.OK,
+ resourceId,
+ TEST_UDP_ENCAP_PORT,
+ Os.socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP));
+ when(mMockIpSecService.openUdpEncapsulationSocket(eq(TEST_UDP_ENCAP_PORT), anyObject()))
+ .thenReturn(udpEncapResp);
+
+ IpSecManager.UdpEncapsulationSocket encapSocket =
+ mIpSecManager.openUdpEncapsulationSocket(TEST_UDP_ENCAP_PORT);
+ assertNotNull(encapSocket.getSocket());
+ assertEquals(TEST_UDP_ENCAP_PORT, encapSocket.getPort());
+
+ encapSocket.close();
+
+ verify(mMockIpSecService).closeUdpEncapsulationSocket(resourceId);
+ }
+
+ @Test
+ public void testOpenEncapsulationSocketOnRandomPort() throws Exception {
+ int resourceId = 1;
+ IpSecUdpEncapResponse udpEncapResp =
+ new IpSecUdpEncapResponse(
+ IpSecManager.Status.OK,
+ resourceId,
+ TEST_UDP_ENCAP_PORT,
+ Os.socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP));
+
+ when(mMockIpSecService.openUdpEncapsulationSocket(eq(0), anyObject()))
+ .thenReturn(udpEncapResp);
+
+ IpSecManager.UdpEncapsulationSocket encapSocket =
+ mIpSecManager.openUdpEncapsulationSocket();
+
+ assertNotNull(encapSocket.getSocket());
+ assertEquals(TEST_UDP_ENCAP_PORT, encapSocket.getPort());
+
+ encapSocket.close();
+
+ verify(mMockIpSecService).closeUdpEncapsulationSocket(resourceId);
+ }
+
+ @Test
+ public void testOpenEncapsulationSocketWithInvalidPort() throws Exception {
+ try {
+ mIpSecManager.openUdpEncapsulationSocket(IpSecManager.INVALID_SECURITY_PARAMETER_INDEX);
+ fail("IllegalArgumentException was not thrown");
+ } catch (IllegalArgumentException e) {
+ }
+ }
+
+ // TODO: add test when applicable transform builder interface is available
+}