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 
testLocalConnections()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 
testAccessors()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
testSetSoTimeout_readTimeout()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
testSetSoTimeout_writeTimeout()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 
testAvailable()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
testLocalSocketCreatedFromFileDescriptor()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 
testFlush()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 
testFlushWorks(OutputStream outputStream, InputStream inputStream)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 
StreamReader(InputStream is, int expectedByteCount)374         private StreamReader(InputStream is, int expectedByteCount) {
375             this.is = is;
376             this.expectedByteCount = expectedByteCount;
377         }
378 
379         @Override
run()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 
waitForCompletion(long waitMillis)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 
assertBytesRead(int expected)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 
Result(String type, Exception e)415         private Result(String type, Exception e) {
416             this.type = type;
417             this.e = e;
418         }
419 
noException(String description)420         static Result noException(String description) {
421             return new Result(description, null);
422         }
423 
exception(Exception e)424         static Result exception(Exception e) {
425             return new Result(e.getClass().getName(), e);
426         }
427 
assertThrewIOException(String expectedMessage)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 
runInSeparateThread(int allowedTime, final Callable<Result> callable)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 {
createConnectedSocketPair(String address)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 
LocalSocketPair(LocalServerSocket serverSocket, LocalSocket clientSocket)460         LocalSocketPair(LocalServerSocket serverSocket, LocalSocket clientSocket) {
461             this.serverSocket = serverSocket;
462             this.clientSocket = clientSocket;
463         }
464 
close()465         public void close() throws Exception {
466             serverSocket.close();
467             clientSocket.close();
468         }
469     }
470 }
471