1 /* 2 * Copyright (C) 2008 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.cts; 18 19 import junit.framework.TestCase; 20 21 import android.net.Credentials; 22 import android.net.LocalServerSocket; 23 import android.net.LocalSocket; 24 import android.net.LocalSocketAddress; 25 import android.system.Os; 26 import android.system.OsConstants; 27 28 import java.io.FileDescriptor; 29 import java.io.IOException; 30 import java.io.InputStream; 31 import java.io.OutputStream; 32 import java.util.concurrent.Callable; 33 import java.util.concurrent.CountDownLatch; 34 import java.util.concurrent.ExecutorService; 35 import java.util.concurrent.Executors; 36 import java.util.concurrent.Future; 37 import java.util.concurrent.TimeUnit; 38 39 public class LocalSocketTest extends TestCase { 40 private final static String ADDRESS_PREFIX = "com.android.net.LocalSocketTest"; 41 42 public void testLocalConnections() throws IOException { 43 String address = ADDRESS_PREFIX + "_testLocalConnections"; 44 // create client and server socket 45 LocalServerSocket localServerSocket = new LocalServerSocket(address); 46 LocalSocket clientSocket = new LocalSocket(); 47 48 // establish connection between client and server 49 LocalSocketAddress locSockAddr = new LocalSocketAddress(address); 50 assertFalse(clientSocket.isConnected()); 51 clientSocket.connect(locSockAddr); 52 assertTrue(clientSocket.isConnected()); 53 54 LocalSocket serverSocket = localServerSocket.accept(); 55 assertTrue(serverSocket.isConnected()); 56 assertTrue(serverSocket.isBound()); 57 try { 58 serverSocket.bind(localServerSocket.getLocalSocketAddress()); 59 fail("Cannot bind a LocalSocket from accept()"); 60 } catch (IOException expected) { 61 } 62 try { 63 serverSocket.connect(locSockAddr); 64 fail("Cannot connect a LocalSocket from accept()"); 65 } catch (IOException expected) { 66 } 67 68 Credentials credent = clientSocket.getPeerCredentials(); 69 assertTrue(0 != credent.getPid()); 70 71 // send data from client to server 72 OutputStream clientOutStream = clientSocket.getOutputStream(); 73 clientOutStream.write(12); 74 InputStream serverInStream = serverSocket.getInputStream(); 75 assertEquals(12, serverInStream.read()); 76 77 //send data from server to client 78 OutputStream serverOutStream = serverSocket.getOutputStream(); 79 serverOutStream.write(3); 80 InputStream clientInStream = clientSocket.getInputStream(); 81 assertEquals(3, clientInStream.read()); 82 83 // Test sending and receiving file descriptors 84 clientSocket.setFileDescriptorsForSend(new FileDescriptor[]{FileDescriptor.in}); 85 clientOutStream.write(32); 86 assertEquals(32, serverInStream.read()); 87 88 FileDescriptor[] out = serverSocket.getAncillaryFileDescriptors(); 89 assertEquals(1, out.length); 90 FileDescriptor fd = clientSocket.getFileDescriptor(); 91 assertTrue(fd.valid()); 92 93 //shutdown input stream of client 94 clientSocket.shutdownInput(); 95 assertEquals(-1, clientInStream.read()); 96 97 //shutdown output stream of client 98 clientSocket.shutdownOutput(); 99 try { 100 clientOutStream.write(10); 101 fail("testLocalSocket shouldn't come to here"); 102 } catch (IOException e) { 103 // expected 104 } 105 106 //shutdown input stream of server 107 serverSocket.shutdownInput(); 108 assertEquals(-1, serverInStream.read()); 109 110 //shutdown output stream of server 111 serverSocket.shutdownOutput(); 112 try { 113 serverOutStream.write(10); 114 fail("testLocalSocket shouldn't come to here"); 115 } catch (IOException e) { 116 // expected 117 } 118 119 //close client socket 120 clientSocket.close(); 121 try { 122 clientInStream.read(); 123 fail("testLocalSocket shouldn't come to here"); 124 } catch (IOException e) { 125 // expected 126 } 127 128 //close server socket 129 serverSocket.close(); 130 try { 131 serverInStream.read(); 132 fail("testLocalSocket shouldn't come to here"); 133 } catch (IOException e) { 134 // expected 135 } 136 } 137 138 public void testAccessors() throws IOException { 139 String address = ADDRESS_PREFIX + "_testAccessors"; 140 LocalSocket socket = new LocalSocket(); 141 LocalSocketAddress addr = new LocalSocketAddress(address); 142 143 assertFalse(socket.isBound()); 144 socket.bind(addr); 145 assertTrue(socket.isBound()); 146 assertEquals(addr, socket.getLocalSocketAddress()); 147 148 String str = socket.toString(); 149 assertTrue(str.contains("impl:android.net.LocalSocketImpl")); 150 151 socket.setReceiveBufferSize(1999); 152 assertEquals(1999 << 1, socket.getReceiveBufferSize()); 153 154 socket.setSendBufferSize(3998); 155 assertEquals(3998 << 1, socket.getSendBufferSize()); 156 157 assertEquals(0, socket.getSoTimeout()); 158 socket.setSoTimeout(1996); 159 assertTrue(socket.getSoTimeout() > 0); 160 161 try { 162 socket.getRemoteSocketAddress(); 163 fail("testLocalSocketSecondary shouldn't come to here"); 164 } catch (UnsupportedOperationException e) { 165 // expected 166 } 167 168 try { 169 socket.isClosed(); 170 fail("testLocalSocketSecondary shouldn't come to here"); 171 } catch (UnsupportedOperationException e) { 172 // expected 173 } 174 175 try { 176 socket.isInputShutdown(); 177 fail("testLocalSocketSecondary shouldn't come to here"); 178 } catch (UnsupportedOperationException e) { 179 // expected 180 } 181 182 try { 183 socket.isOutputShutdown(); 184 fail("testLocalSocketSecondary shouldn't come to here"); 185 } catch (UnsupportedOperationException e) { 186 // expected 187 } 188 189 try { 190 socket.connect(addr, 2005); 191 fail("testLocalSocketSecondary shouldn't come to here"); 192 } catch (UnsupportedOperationException e) { 193 // expected 194 } 195 196 socket.close(); 197 } 198 199 // http://b/31205169 200 public void testSetSoTimeout_readTimeout() throws Exception { 201 String address = ADDRESS_PREFIX + "_testSetSoTimeout_readTimeout"; 202 203 try (LocalSocketPair socketPair = LocalSocketPair.createConnectedSocketPair(address)) { 204 final LocalSocket clientSocket = socketPair.clientSocket; 205 206 // Set the timeout in millis. 207 int timeoutMillis = 1000; 208 clientSocket.setSoTimeout(timeoutMillis); 209 210 // Avoid blocking the test run if timeout doesn't happen by using a separate thread. 211 Callable<Result> reader = () -> { 212 try { 213 clientSocket.getInputStream().read(); 214 return Result.noException("Did not block"); 215 } catch (IOException e) { 216 return Result.exception(e); 217 } 218 }; 219 // Allow the configured timeout, plus some slop. 220 int allowedTime = timeoutMillis + 2000; 221 Result result = runInSeparateThread(allowedTime, reader); 222 223 // Check the message was a timeout, it's all we have to go on. 224 String expectedMessage = Os.strerror(OsConstants.EAGAIN); 225 result.assertThrewIOException(expectedMessage); 226 } 227 } 228 229 // http://b/31205169 230 public void testSetSoTimeout_writeTimeout() throws Exception { 231 String address = ADDRESS_PREFIX + "_testSetSoTimeout_writeTimeout"; 232 233 try (LocalSocketPair socketPair = LocalSocketPair.createConnectedSocketPair(address)) { 234 final LocalSocket clientSocket = socketPair.clientSocket; 235 236 // Set the timeout in millis. 237 int timeoutMillis = 1000; 238 clientSocket.setSoTimeout(timeoutMillis); 239 240 // Set a small buffer size so we know we can flood it. 241 clientSocket.setSendBufferSize(100); 242 final int bufferSize = clientSocket.getSendBufferSize(); 243 244 // Avoid blocking the test run if timeout doesn't happen by using a separate thread. 245 Callable<Result> writer = () -> { 246 try { 247 byte[] toWrite = new byte[bufferSize * 2]; 248 clientSocket.getOutputStream().write(toWrite); 249 return Result.noException("Did not block"); 250 } catch (IOException e) { 251 return Result.exception(e); 252 } 253 }; 254 // Allow the configured timeout, plus some slop. 255 int allowedTime = timeoutMillis + 2000; 256 257 Result result = runInSeparateThread(allowedTime, writer); 258 259 // Check the message was a timeout, it's all we have to go on. 260 String expectedMessage = Os.strerror(OsConstants.EAGAIN); 261 result.assertThrewIOException(expectedMessage); 262 } 263 } 264 265 public void testAvailable() throws Exception { 266 String address = ADDRESS_PREFIX + "_testAvailable"; 267 268 try (LocalSocketPair socketPair = LocalSocketPair.createConnectedSocketPair(address)) { 269 LocalSocket clientSocket = socketPair.clientSocket; 270 LocalSocket serverSocket = socketPair.serverSocket.accept(); 271 272 OutputStream clientOutputStream = clientSocket.getOutputStream(); 273 InputStream serverInputStream = serverSocket.getInputStream(); 274 assertEquals(0, serverInputStream.available()); 275 276 byte[] buffer = new byte[50]; 277 clientOutputStream.write(buffer); 278 assertEquals(50, serverInputStream.available()); 279 280 InputStream clientInputStream = clientSocket.getInputStream(); 281 OutputStream serverOutputStream = serverSocket.getOutputStream(); 282 assertEquals(0, clientInputStream.available()); 283 serverOutputStream.write(buffer); 284 assertEquals(50, serverInputStream.available()); 285 286 serverSocket.close(); 287 } 288 } 289 290 // http://b/34095140 291 public void testLocalSocketCreatedFromFileDescriptor() throws Exception { 292 String address = ADDRESS_PREFIX + "_testLocalSocketCreatedFromFileDescriptor"; 293 294 // Establish connection between a local client and server to get a valid client socket file 295 // descriptor. 296 try (LocalSocketPair socketPair = LocalSocketPair.createConnectedSocketPair(address)) { 297 // Extract the client FileDescriptor we can use. 298 FileDescriptor fileDescriptor = socketPair.clientSocket.getFileDescriptor(); 299 assertTrue(fileDescriptor.valid()); 300 301 // Create the LocalSocket we want to test. 302 LocalSocket clientSocketCreatedFromFileDescriptor = 303 LocalSocket.createConnectedLocalSocket(fileDescriptor); 304 assertTrue(clientSocketCreatedFromFileDescriptor.isConnected()); 305 assertTrue(clientSocketCreatedFromFileDescriptor.isBound()); 306 307 // Test the LocalSocket can be used for communication. 308 LocalSocket serverSocket = socketPair.serverSocket.accept(); 309 OutputStream clientOutputStream = 310 clientSocketCreatedFromFileDescriptor.getOutputStream(); 311 InputStream serverInputStream = serverSocket.getInputStream(); 312 313 clientOutputStream.write(12); 314 assertEquals(12, serverInputStream.read()); 315 316 // Closing clientSocketCreatedFromFileDescriptor does not close the file descriptor. 317 clientSocketCreatedFromFileDescriptor.close(); 318 assertTrue(fileDescriptor.valid()); 319 320 // .. while closing the LocalSocket that owned the file descriptor does. 321 socketPair.clientSocket.close(); 322 assertFalse(fileDescriptor.valid()); 323 } 324 } 325 326 public void testFlush() throws Exception { 327 String address = ADDRESS_PREFIX + "_testFlush"; 328 329 try (LocalSocketPair socketPair = LocalSocketPair.createConnectedSocketPair(address)) { 330 LocalSocket clientSocket = socketPair.clientSocket; 331 LocalSocket serverSocket = socketPair.serverSocket.accept(); 332 333 OutputStream clientOutputStream = clientSocket.getOutputStream(); 334 InputStream serverInputStream = serverSocket.getInputStream(); 335 testFlushWorks(clientOutputStream, serverInputStream); 336 337 OutputStream serverOutputStream = serverSocket.getOutputStream(); 338 InputStream clientInputStream = clientSocket.getInputStream(); 339 testFlushWorks(serverOutputStream, clientInputStream); 340 341 serverSocket.close(); 342 } 343 } 344 345 private void testFlushWorks(OutputStream outputStream, InputStream inputStream) 346 throws Exception { 347 final int bytesToTransfer = 50; 348 StreamReader inputStreamReader = new StreamReader(inputStream, bytesToTransfer); 349 350 byte[] buffer = new byte[bytesToTransfer]; 351 outputStream.write(buffer); 352 assertEquals(bytesToTransfer, inputStream.available()); 353 354 // Start consuming the data. 355 inputStreamReader.start(); 356 357 // This doesn't actually flush any buffers, it just polls until the reader has read all the 358 // bytes. 359 outputStream.flush(); 360 361 inputStreamReader.waitForCompletion(5000); 362 inputStreamReader.assertBytesRead(bytesToTransfer); 363 assertEquals(0, inputStream.available()); 364 } 365 366 private static class StreamReader extends Thread { 367 private final InputStream is; 368 private final int expectedByteCount; 369 private final CountDownLatch completeLatch = new CountDownLatch(1); 370 371 private volatile Exception exception; 372 private int bytesRead; 373 374 private StreamReader(InputStream is, int expectedByteCount) { 375 this.is = is; 376 this.expectedByteCount = expectedByteCount; 377 } 378 379 @Override 380 public void run() { 381 try { 382 byte[] buffer = new byte[10]; 383 int readCount; 384 while ((readCount = is.read(buffer)) >= 0) { 385 bytesRead += readCount; 386 if (bytesRead >= expectedByteCount) { 387 break; 388 } 389 } 390 } catch (IOException e) { 391 exception = e; 392 } finally { 393 completeLatch.countDown(); 394 } 395 } 396 397 public void waitForCompletion(long waitMillis) throws Exception { 398 if (!completeLatch.await(waitMillis, TimeUnit.MILLISECONDS)) { 399 fail("Timeout waiting for completion"); 400 } 401 if (exception != null) { 402 throw new Exception("Read failed", exception); 403 } 404 } 405 406 public void assertBytesRead(int expected) { 407 assertEquals(expected, bytesRead); 408 } 409 } 410 411 private static class Result { 412 private final String type; 413 private final Exception e; 414 415 private Result(String type, Exception e) { 416 this.type = type; 417 this.e = e; 418 } 419 420 static Result noException(String description) { 421 return new Result(description, null); 422 } 423 424 static Result exception(Exception e) { 425 return new Result(e.getClass().getName(), e); 426 } 427 428 void assertThrewIOException(String expectedMessage) { 429 assertEquals("Unexpected result type", IOException.class.getName(), type); 430 assertEquals("Unexpected exception message", expectedMessage, e.getMessage()); 431 } 432 } 433 434 private static Result runInSeparateThread(int allowedTime, final Callable<Result> callable) 435 throws Exception { 436 ExecutorService service = Executors.newSingleThreadScheduledExecutor(); 437 Future<Result> future = service.submit(callable); 438 Result result = future.get(allowedTime, TimeUnit.MILLISECONDS); 439 if (!future.isDone()) { 440 fail("Worker thread appears blocked"); 441 } 442 return result; 443 } 444 445 private static class LocalSocketPair implements AutoCloseable { 446 static LocalSocketPair createConnectedSocketPair(String address) throws Exception { 447 LocalServerSocket localServerSocket = new LocalServerSocket(address); 448 final LocalSocket clientSocket = new LocalSocket(); 449 450 // Establish connection between client and server 451 LocalSocketAddress locSockAddr = new LocalSocketAddress(address); 452 clientSocket.connect(locSockAddr); 453 assertTrue(clientSocket.isConnected()); 454 return new LocalSocketPair(localServerSocket, clientSocket); 455 } 456 457 final LocalServerSocket serverSocket; 458 final LocalSocket clientSocket; 459 460 LocalSocketPair(LocalServerSocket serverSocket, LocalSocket clientSocket) { 461 this.serverSocket = serverSocket; 462 this.clientSocket = clientSocket; 463 } 464 465 public void close() throws Exception { 466 serverSocket.close(); 467 clientSocket.close(); 468 } 469 } 470 } 471