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 org.junit.Assert.assertEquals; 28 import static org.junit.Assert.assertFalse; 29 import static org.junit.Assert.assertTrue; 30 import static org.junit.Assert.fail; 31 32 import android.os.Handler; 33 import android.os.HandlerThread; 34 import android.system.ErrnoException; 35 import android.system.Os; 36 import android.system.StructTimeval; 37 38 import androidx.test.filters.SmallTest; 39 import androidx.test.runner.AndroidJUnit4; 40 41 import org.junit.After; 42 import org.junit.Before; 43 import org.junit.Test; 44 import org.junit.runner.RunWith; 45 46 import java.io.FileDescriptor; 47 import java.net.DatagramPacket; 48 import java.net.DatagramSocket; 49 import java.net.Inet6Address; 50 import java.net.InetAddress; 51 import java.net.InetSocketAddress; 52 import java.net.SocketException; 53 import java.util.Arrays; 54 import java.util.concurrent.CountDownLatch; 55 import java.util.concurrent.TimeUnit; 56 57 /** 58 * Tests for PacketReader. 59 * 60 * @hide 61 */ 62 @RunWith(AndroidJUnit4.class) 63 @SmallTest 64 public class PacketReaderTest { 65 static final InetAddress LOOPBACK6 = Inet6Address.getLoopbackAddress(); 66 static final StructTimeval TIMEO = StructTimeval.fromMillis(500); 67 68 protected CountDownLatch mLatch; 69 protected FileDescriptor mLocalSocket; 70 protected InetSocketAddress mLocalSockName; 71 protected byte[] mLastRecvBuf; 72 protected boolean mStopped; 73 protected HandlerThread mHandlerThread; 74 protected PacketReader mReceiver; 75 76 class UdpLoopbackReader extends PacketReader { UdpLoopbackReader(Handler h)77 public UdpLoopbackReader(Handler h) { 78 super(h); 79 } 80 81 @Override createFd()82 protected FileDescriptor createFd() { 83 FileDescriptor s = null; 84 try { 85 s = Os.socket(AF_INET6, SOCK_DGRAM | SOCK_NONBLOCK, IPPROTO_UDP); 86 Os.bind(s, LOOPBACK6, 0); 87 mLocalSockName = (InetSocketAddress) Os.getsockname(s); 88 Os.setsockoptTimeval(s, SOL_SOCKET, SO_SNDTIMEO, TIMEO); 89 } catch (ErrnoException|SocketException e) { 90 closeFd(s); 91 fail(); 92 return null; 93 } 94 95 mLocalSocket = s; 96 return s; 97 } 98 99 @Override handlePacket(byte[] recvbuf, int length)100 protected void handlePacket(byte[] recvbuf, int length) { 101 mLastRecvBuf = Arrays.copyOf(recvbuf, length); 102 mLatch.countDown(); 103 } 104 105 @Override onStart()106 protected void onStart() { 107 mStopped = false; 108 mLatch.countDown(); 109 } 110 111 @Override onStop()112 protected void onStop() { 113 mStopped = true; 114 mLatch.countDown(); 115 } 116 }; 117 118 @Before setUp()119 public void setUp() { 120 resetLatch(); 121 mLocalSocket = null; 122 mLocalSockName = null; 123 mLastRecvBuf = null; 124 mStopped = false; 125 126 mHandlerThread = new HandlerThread(PacketReaderTest.class.getSimpleName()); 127 mHandlerThread.start(); 128 } 129 130 @After tearDown()131 public void tearDown() throws Exception { 132 if (mReceiver != null) { 133 mHandlerThread.getThreadHandler().post(() -> { mReceiver.stop(); }); 134 waitForActivity(); 135 } 136 mReceiver = null; 137 mHandlerThread.quit(); 138 mHandlerThread = null; 139 } 140 resetLatch()141 void resetLatch() { mLatch = new CountDownLatch(1); } 142 waitForActivity()143 void waitForActivity() throws Exception { 144 try { 145 mLatch.await(1000, TimeUnit.MILLISECONDS); 146 } finally { 147 resetLatch(); 148 } 149 } 150 sendPacket(byte[] contents)151 void sendPacket(byte[] contents) throws Exception { 152 final DatagramSocket sender = new DatagramSocket(); 153 sender.connect(mLocalSockName); 154 sender.send(new DatagramPacket(contents, contents.length)); 155 sender.close(); 156 } 157 158 @Test testBasicWorking()159 public void testBasicWorking() throws Exception { 160 final Handler h = mHandlerThread.getThreadHandler(); 161 mReceiver = new UdpLoopbackReader(h); 162 163 h.post(() -> { mReceiver.start(); }); 164 waitForActivity(); 165 assertTrue(mLocalSockName != null); 166 assertEquals(LOOPBACK6, mLocalSockName.getAddress()); 167 assertTrue(0 < mLocalSockName.getPort()); 168 assertTrue(mLocalSocket != null); 169 assertFalse(mStopped); 170 171 final byte[] one = "one 1".getBytes("UTF-8"); 172 sendPacket(one); 173 waitForActivity(); 174 assertEquals(1, mReceiver.numPacketsReceived()); 175 assertTrue(Arrays.equals(one, mLastRecvBuf)); 176 assertFalse(mStopped); 177 178 final byte[] two = "two 2".getBytes("UTF-8"); 179 sendPacket(two); 180 waitForActivity(); 181 assertEquals(2, mReceiver.numPacketsReceived()); 182 assertTrue(Arrays.equals(two, mLastRecvBuf)); 183 assertFalse(mStopped); 184 185 mReceiver.stop(); 186 waitForActivity(); 187 assertEquals(2, mReceiver.numPacketsReceived()); 188 assertTrue(Arrays.equals(two, mLastRecvBuf)); 189 assertTrue(mStopped); 190 mReceiver = null; 191 } 192 193 class NullPacketReader extends PacketReader { NullPacketReader(Handler h, int recvbufsize)194 public NullPacketReader(Handler h, int recvbufsize) { 195 super(h, recvbufsize); 196 } 197 198 @Override createFd()199 public FileDescriptor createFd() { return null; } 200 } 201 202 @Test testMinimalRecvBufSize()203 public void testMinimalRecvBufSize() throws Exception { 204 final Handler h = mHandlerThread.getThreadHandler(); 205 206 for (int i : new int[]{-1, 0, 1, DEFAULT_RECV_BUF_SIZE-1}) { 207 final PacketReader b = new NullPacketReader(h, i); 208 assertEquals(DEFAULT_RECV_BUF_SIZE, b.recvBufSize()); 209 } 210 } 211 } 212