1 /* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.net.util; 18 19 import static android.net.util.PacketReader.DEFAULT_RECV_BUF_SIZE; 20 import static android.system.OsConstants.AF_INET6; 21 import static android.system.OsConstants.IPPROTO_UDP; 22 import static android.system.OsConstants.SOCK_DGRAM; 23 import static android.system.OsConstants.SOCK_NONBLOCK; 24 import static android.system.OsConstants.SOL_SOCKET; 25 import static android.system.OsConstants.SO_SNDTIMEO; 26 27 import static com.android.testutils.MiscAssertsKt.assertThrows; 28 29 import static org.junit.Assert.assertEquals; 30 import static org.junit.Assert.assertFalse; 31 import static org.junit.Assert.assertTrue; 32 import static org.junit.Assert.fail; 33 34 import android.os.Handler; 35 import android.os.HandlerThread; 36 import android.system.ErrnoException; 37 import android.system.Os; 38 import android.system.StructTimeval; 39 40 import androidx.test.filters.SmallTest; 41 import androidx.test.runner.AndroidJUnit4; 42 43 import org.junit.After; 44 import org.junit.Before; 45 import org.junit.Test; 46 import org.junit.runner.RunWith; 47 48 import java.io.FileDescriptor; 49 import java.net.DatagramPacket; 50 import java.net.DatagramSocket; 51 import java.net.Inet6Address; 52 import java.net.InetAddress; 53 import java.net.InetSocketAddress; 54 import java.net.SocketException; 55 import java.util.Arrays; 56 import java.util.concurrent.CountDownLatch; 57 import java.util.concurrent.TimeUnit; 58 59 /** 60 * Tests for PacketReader. 61 * 62 * @hide 63 */ 64 @RunWith(AndroidJUnit4.class) 65 @SmallTest 66 public class PacketReaderTest { 67 static final InetAddress LOOPBACK6 = Inet6Address.getLoopbackAddress(); 68 static final StructTimeval TIMEO = StructTimeval.fromMillis(500); 69 70 protected CountDownLatch mLatch; 71 protected FileDescriptor mLocalSocket; 72 protected InetSocketAddress mLocalSockName; 73 protected byte[] mLastRecvBuf; 74 protected boolean mStopped; 75 protected HandlerThread mHandlerThread; 76 protected PacketReader mReceiver; 77 78 class UdpLoopbackReader extends PacketReader { UdpLoopbackReader(Handler h)79 public UdpLoopbackReader(Handler h) { 80 super(h); 81 } 82 83 @Override createFd()84 protected FileDescriptor createFd() { 85 FileDescriptor s = null; 86 try { 87 s = Os.socket(AF_INET6, SOCK_DGRAM | SOCK_NONBLOCK, IPPROTO_UDP); 88 Os.bind(s, LOOPBACK6, 0); 89 mLocalSockName = (InetSocketAddress) Os.getsockname(s); 90 Os.setsockoptTimeval(s, SOL_SOCKET, SO_SNDTIMEO, TIMEO); 91 } catch (ErrnoException|SocketException e) { 92 closeFd(s); 93 fail(); 94 return null; 95 } 96 97 mLocalSocket = s; 98 return s; 99 } 100 101 @Override handlePacket(byte[] recvbuf, int length)102 protected void handlePacket(byte[] recvbuf, int length) { 103 mLastRecvBuf = Arrays.copyOf(recvbuf, length); 104 mLatch.countDown(); 105 } 106 107 @Override onStart()108 protected void onStart() { 109 mStopped = false; 110 mLatch.countDown(); 111 } 112 113 @Override onStop()114 protected void onStop() { 115 mStopped = true; 116 mLatch.countDown(); 117 } 118 }; 119 120 @Before setUp()121 public void setUp() { 122 resetLatch(); 123 mLocalSocket = null; 124 mLocalSockName = null; 125 mLastRecvBuf = null; 126 mStopped = false; 127 128 mHandlerThread = new HandlerThread(PacketReaderTest.class.getSimpleName()); 129 mHandlerThread.start(); 130 } 131 132 @After tearDown()133 public void tearDown() throws Exception { 134 if (mReceiver != null) { 135 mHandlerThread.getThreadHandler().post(() -> { mReceiver.stop(); }); 136 waitForActivity(); 137 } 138 mReceiver = null; 139 mHandlerThread.quit(); 140 mHandlerThread = null; 141 } 142 resetLatch()143 void resetLatch() { mLatch = new CountDownLatch(1); } 144 waitForActivity()145 void waitForActivity() throws Exception { 146 try { 147 mLatch.await(1000, TimeUnit.MILLISECONDS); 148 } finally { 149 resetLatch(); 150 } 151 } 152 sendPacket(byte[] contents)153 void sendPacket(byte[] contents) throws Exception { 154 final DatagramSocket sender = new DatagramSocket(); 155 sender.connect(mLocalSockName); 156 sender.send(new DatagramPacket(contents, contents.length)); 157 sender.close(); 158 } 159 160 @Test testBasicWorking()161 public void testBasicWorking() throws Exception { 162 final Handler h = mHandlerThread.getThreadHandler(); 163 mReceiver = new UdpLoopbackReader(h); 164 165 h.post(() -> { mReceiver.start(); }); 166 waitForActivity(); 167 assertTrue(mLocalSockName != null); 168 assertEquals(LOOPBACK6, mLocalSockName.getAddress()); 169 assertTrue(0 < mLocalSockName.getPort()); 170 assertTrue(mLocalSocket != null); 171 assertFalse(mStopped); 172 173 final byte[] one = "one 1".getBytes("UTF-8"); 174 sendPacket(one); 175 waitForActivity(); 176 assertEquals(1, mReceiver.numPacketsReceived()); 177 assertTrue(Arrays.equals(one, mLastRecvBuf)); 178 assertFalse(mStopped); 179 180 final byte[] two = "two 2".getBytes("UTF-8"); 181 sendPacket(two); 182 waitForActivity(); 183 assertEquals(2, mReceiver.numPacketsReceived()); 184 assertTrue(Arrays.equals(two, mLastRecvBuf)); 185 assertFalse(mStopped); 186 187 h.post(() -> mReceiver.stop()); 188 waitForActivity(); 189 assertEquals(2, mReceiver.numPacketsReceived()); 190 assertTrue(Arrays.equals(two, mLastRecvBuf)); 191 assertTrue(mStopped); 192 mReceiver = null; 193 } 194 195 class NullPacketReader extends PacketReader { NullPacketReader(Handler h, int recvbufsize)196 public NullPacketReader(Handler h, int recvbufsize) { 197 super(h, recvbufsize); 198 } 199 200 @Override createFd()201 public FileDescriptor createFd() { return null; } 202 } 203 204 @Test testMinimalRecvBufSize()205 public void testMinimalRecvBufSize() throws Exception { 206 final Handler h = mHandlerThread.getThreadHandler(); 207 208 for (int i : new int[]{-1, 0, 1, DEFAULT_RECV_BUF_SIZE-1}) { 209 final PacketReader b = new NullPacketReader(h, i); 210 assertEquals(DEFAULT_RECV_BUF_SIZE, b.recvBufSize()); 211 } 212 } 213 214 @Test testStartingFromWrongThread()215 public void testStartingFromWrongThread() throws Exception { 216 final Handler h = mHandlerThread.getThreadHandler(); 217 final PacketReader b = new NullPacketReader(h, DEFAULT_RECV_BUF_SIZE); 218 assertThrows(IllegalStateException.class, () -> b.start()); 219 } 220 221 @Test testStoppingFromWrongThread()222 public void testStoppingFromWrongThread() throws Exception { 223 final Handler h = mHandlerThread.getThreadHandler(); 224 final PacketReader b = new NullPacketReader(h, DEFAULT_RECV_BUF_SIZE); 225 assertThrows(IllegalStateException.class, () -> b.stop()); 226 } 227 228 @Test testSuccessToCreateSocket()229 public void testSuccessToCreateSocket() throws Exception { 230 final Handler h = mHandlerThread.getThreadHandler(); 231 final PacketReader b = new UdpLoopbackReader(h); 232 h.post(() -> assertTrue(b.start())); 233 } 234 235 @Test testFailToCreateSocket()236 public void testFailToCreateSocket() throws Exception { 237 final Handler h = mHandlerThread.getThreadHandler(); 238 final PacketReader b = new NullPacketReader(h, DEFAULT_RECV_BUF_SIZE); 239 h.post(() -> assertFalse(b.start())); 240 } 241 } 242