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
17package android.core;
18
19import junit.framework.Assert;
20import junit.framework.TestCase;
21
22import java.io.IOException;
23import java.net.InetAddress;
24import java.net.InetSocketAddress;
25import java.net.ServerSocket;
26import java.net.Socket;
27import java.net.SocketException;
28import java.net.SocketTimeoutException;
29import java.nio.channels.SocketChannel;
30import java.util.concurrent.Semaphore;
31
32import android.test.suitebuilder.annotation.LargeTest;
33import android.test.suitebuilder.annotation.SmallTest;
34import android.test.suitebuilder.annotation.Suppress;
35
36/**
37 * Regression tests for various socket related problems. And a few general
38 * socket tests.
39 */
40public class SocketTest extends TestCase {
41
42    private static final String NON_EXISTING_ADDRESS = "123.123.123.123";
43
44    private static final String KNOW_GOOD_ADDRESS = "209.85.129.147";
45
46    private static final String PACKAGE_DROPPING_ADDRESS = "191.167.0.1";
47
48    // Test for basic bind/connect/accept behavior.
49    @SmallTest
50    public void testSocketSimple() throws Exception {
51        ServerSocket ss;
52        Socket s, s1;
53        int port;
54
55        IOException lastEx = null;
56
57        ss = new ServerSocket();
58
59        for (port = 9900; port < 9999; port++) {
60            try {
61                ss.bind(new InetSocketAddress("127.0.0.1", port));
62                lastEx = null;
63                break;
64            } catch (IOException ex) {
65                lastEx = ex;
66            }
67        }
68
69        if (lastEx != null) {
70            throw lastEx;
71        }
72
73        s = new Socket("127.0.0.1", port);
74
75        s1 = ss.accept();
76
77        s.getOutputStream().write(0xa5);
78
79        assertEquals(0xa5, s1.getInputStream().read());
80
81        s1.getOutputStream().write(0x5a);
82        assertEquals(0x5a, s.getInputStream().read());
83    }
84
85    // Regression test for #820068: Wildcard address
86    @SmallTest
87    public void testWildcardAddress() throws Exception {
88        Socket s2 = new Socket();
89        s2.bind(new InetSocketAddress((InetAddress) null, 12345));
90        byte[] addr = s2.getLocalAddress().getAddress();
91        for (int i = 0; i < 4; i++) {
92            assertEquals("Not the wildcard address", 0, addr[i]);
93        }
94    }
95
96    // Regression test for #865753: server sockets not closing properly
97    @SmallTest
98    public void testServerSocketClose() throws Exception {
99        ServerSocket s3 = new ServerSocket(23456);
100        s3.close();
101        ServerSocket s4 = new ServerSocket(23456);
102        s4.close();
103    }
104
105    // Regression test for #876985: SO_REUSEADDR not working properly
106
107    private Exception serverError = null;
108
109    @LargeTest
110    public void testSetReuseAddress() throws IOException {
111        InetSocketAddress addr = new InetSocketAddress(8383);
112
113        final ServerSocket serverSock = new ServerSocket();
114        serverSock.setReuseAddress(true);
115        serverSock.bind(addr);
116
117        final Semaphore semThreadEnd = new Semaphore(0);
118        new Thread() {
119            @Override
120            public void run() {
121                try {
122                    Socket sock = serverSock.accept();
123                    sock.getInputStream().read();
124                    sock.close();
125                } catch (IOException e) {
126                    serverError = e;
127                }
128                semThreadEnd.release();
129            }
130        }.start();
131
132        // Give the server a bit of time for startup
133        try {
134            Thread.sleep(2000);
135        } catch (InterruptedException ex) {
136            // Ignored.
137        }
138
139        Socket client = new Socket("localhost", 8383);
140        client.getOutputStream().write(1);
141        // Just leave this connection open from the client side. It will be
142        // closed from the server side so the server stays in the TIME_WAIT
143        // state for a while. setReuseAddress() should be able to handle this.
144
145        try {
146            semThreadEnd.acquire();
147        } catch (InterruptedException e) {
148            // ignore
149        }
150        serverSock.close();
151
152        ServerSocket serverSock2 = new ServerSocket();
153        serverSock2.setReuseAddress(true);
154        serverSock2.bind(addr);
155        serverSock2.close();
156
157        if (serverError != null) {
158            throw new RuntimeException("Server must complete without error", serverError);
159        }
160    }
161
162    // Regression for 916701, a wrong exception was thrown after timeout of
163    // a ServerSocket.
164    @LargeTest
165    public void testTimeoutException() throws IOException {
166        ServerSocket s = new ServerSocket(9800);
167        s.setSoTimeout(2000);
168        try {
169            s.accept();
170        } catch (SocketTimeoutException e) {
171            // this is ok.
172        }
173    }
174
175    // Regression for issue 1001980, openening a SocketChannel threw an Exception
176    @SmallTest
177    public void testNativeSocketChannelOpen() throws IOException {
178        SocketChannel.open();
179    }
180
181// Regression test for issue 1018016, connecting ignored a set timeout.
182//
183// Disabled because test behaves differently depending on networking
184// environment. It works fine in the emulator and one the device with
185// WLAN, but when 3G comes into play, the possible existence of a
186// proxy makes it fail.
187//
188//    @LargeTest
189//    public void testSocketSetSOTimeout() throws IOException {
190//        Socket sock = new Socket();
191//        int timeout = 5000;
192//        long start = System.currentTimeMillis();
193//        try {
194//            sock.connect(new InetSocketAddress(NON_EXISTING_ADDRESS, 80), timeout);
195//        } catch (SocketTimeoutException e) {
196//            // expected
197//            long delay = System.currentTimeMillis() - start;
198//            if (Math.abs(delay - timeout) > 1000) {
199//                fail("timeout was not accurate. expected: " + timeout
200//                        + " actual: " + delay + " miliseconds.");
201//            }
202//        } finally {
203//            try {
204//                sock.close();
205//            } catch (IOException ioe) {
206//                // ignore
207//            }
208//        }
209//    }
210
211    /**
212     * Regression test for 1062928: Dotted IP addresses (e.g., 192.168.100.1)
213     * appear to be broken in the M5 SDK.
214     *
215     * Tests that a connection given a ip-addressv4 such as 192.168.100.100 does
216     * not fail - sdk m5 seems only to accept dns names instead of ip numbers.
217     * ip 209.85.129.147 (one address of www.google.com) on port 80 (http) is
218     * used to test the connection.
219     */
220
221// Commenting out this test since it is flaky, even at the best of times.  See
222// #1191317 for Info.
223    @Suppress
224    public void disable_testConnectWithIP4IPAddr() {
225        // call a Google Web server
226        InetSocketAddress scktAddrss = new InetSocketAddress(KNOW_GOOD_ADDRESS,
227                80);
228        Socket clntSckt = new Socket();
229        try {
230            clntSckt.connect(scktAddrss, 5000);
231        } catch (Throwable e) {
232            fail("connection problem:" + e.getClass().getName() + ": "
233                    + e.getMessage());
234        } finally {
235            try {
236                clntSckt.close();
237            } catch (Exception e) {
238                // ignore
239            }
240        }
241    }
242
243
244    // Regression test for #1058962: Socket.close() does not cause
245    // socket.connect() to return immediately.
246    private Socket client;
247
248    private Exception error;
249
250    private boolean connected;
251
252// This test isn't working now, but really should work.
253// TODO Enable this test again.
254
255    @Suppress
256    public void disable_testSocketConnectClose() {
257        try {
258            client = new Socket();
259
260            new Thread() {
261                @Override
262                public void run() {
263                    try {
264                        client.connect(new InetSocketAddress(PACKAGE_DROPPING_ADDRESS, 1357));
265                    } catch (Exception ex) {
266                        error = ex;
267                    }
268
269                    connected = true;
270                }
271            }.start();
272
273            Thread.sleep(1000);
274
275            Assert.assertNull("Connect must not fail immediately. Maybe try different address.", error);
276            Assert.assertFalse("Connect must not succeed. Maybe try different address.", connected);
277
278            client.close();
279
280            Thread.sleep(1000);
281
282            if (error == null) {
283                fail("Socket connect still ongoing");
284            } else if (!(error instanceof SocketException)) {
285                fail("Socket connect interrupted with wrong error: " + error.toString());
286            }
287
288        } catch (Exception ex) {
289            throw new RuntimeException(ex);
290        }
291
292    }
293
294}
295