1/*
2 * Copyright (C) 2011 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 libcore.io;
18
19import android.system.ErrnoException;
20import android.system.NetlinkSocketAddress;
21import android.system.OsConstants;
22import android.system.PacketSocketAddress;
23import android.system.StructTimeval;
24import android.system.StructUcred;
25import android.system.UnixSocketAddress;
26
27import java.io.File;
28import java.io.FileDescriptor;
29import java.io.FileInputStream;
30import java.io.FileOutputStream;
31import java.net.Inet4Address;
32import java.net.Inet6Address;
33import java.net.InetAddress;
34import java.net.InetSocketAddress;
35import java.net.NetworkInterface;
36import java.net.ServerSocket;
37import java.net.SocketOptions;
38import java.nio.ByteBuffer;
39import java.nio.charset.StandardCharsets;
40import java.util.Arrays;
41import java.util.Locale;
42import java.util.concurrent.atomic.AtomicReference;
43import junit.framework.TestCase;
44import static android.system.OsConstants.*;
45
46public class OsTest extends TestCase {
47  public void testIsSocket() throws Exception {
48    File f = new File("/dev/null");
49    FileInputStream fis = new FileInputStream(f);
50    assertFalse(S_ISSOCK(Libcore.os.fstat(fis.getFD()).st_mode));
51    fis.close();
52
53    ServerSocket s = new ServerSocket();
54    assertTrue(S_ISSOCK(Libcore.os.fstat(s.getImpl().getFD$()).st_mode));
55    s.close();
56  }
57
58  public void testFcntlInt() throws Exception {
59    File f = File.createTempFile("OsTest", "tst");
60    FileInputStream fis = null;
61    try {
62      fis = new FileInputStream(f);
63      Libcore.os.fcntlInt(fis.getFD(), F_SETFD, FD_CLOEXEC);
64      int flags = Libcore.os.fcntlVoid(fis.getFD(), F_GETFD);
65      assertTrue((flags & FD_CLOEXEC) != 0);
66    } finally {
67      IoUtils.closeQuietly(fis);
68      f.delete();
69    }
70  }
71
72  public void testUnixDomainSockets_in_file_system() throws Exception {
73    String path = System.getProperty("java.io.tmpdir") + "/test_unix_socket";
74    new File(path).delete();
75    checkUnixDomainSocket(UnixSocketAddress.createFileSystem(path), false);
76  }
77
78  public void testUnixDomainSocket_abstract_name() throws Exception {
79    // Linux treats a sun_path starting with a NUL byte as an abstract name. See unix(7).
80    checkUnixDomainSocket(UnixSocketAddress.createAbstract("/abstract_name_unix_socket"), true);
81  }
82
83  public void testUnixDomainSocket_unnamed() throws Exception {
84    final FileDescriptor fd = Libcore.os.socket(AF_UNIX, SOCK_STREAM, 0);
85    // unix(7) says an unbound socket is unnamed.
86    checkNoSockName(fd);
87    Libcore.os.close(fd);
88  }
89
90  private void checkUnixDomainSocket(final UnixSocketAddress address, final boolean isAbstract)
91      throws Exception {
92    final FileDescriptor serverFd = Libcore.os.socket(AF_UNIX, SOCK_STREAM, 0);
93    Libcore.os.bind(serverFd, address);
94    Libcore.os.listen(serverFd, 5);
95
96    checkSockName(serverFd, isAbstract, address);
97
98    Thread server = new Thread(new Runnable() {
99      public void run() {
100        try {
101          UnixSocketAddress peerAddress = UnixSocketAddress.createUnnamed();
102          FileDescriptor clientFd = Libcore.os.accept(serverFd, peerAddress);
103          checkSockName(clientFd, isAbstract, address);
104          checkNoName(peerAddress);
105
106          checkNoPeerName(clientFd);
107
108          StructUcred credentials = Libcore.os.getsockoptUcred(clientFd, SOL_SOCKET, SO_PEERCRED);
109          assertEquals(Libcore.os.getpid(), credentials.pid);
110          assertEquals(Libcore.os.getuid(), credentials.uid);
111          assertEquals(Libcore.os.getgid(), credentials.gid);
112
113          byte[] request = new byte[256];
114          Libcore.os.read(clientFd, request, 0, request.length);
115
116          String s = new String(request, "UTF-8");
117          byte[] response = s.toUpperCase(Locale.ROOT).getBytes("UTF-8");
118          Libcore.os.write(clientFd, response, 0, response.length);
119
120          Libcore.os.close(clientFd);
121        } catch (Exception ex) {
122          throw new RuntimeException(ex);
123        }
124      }
125    });
126    server.start();
127
128    FileDescriptor clientFd = Libcore.os.socket(AF_UNIX, SOCK_STREAM, 0);
129
130    Libcore.os.connect(clientFd, address);
131    checkNoSockName(clientFd);
132
133    String string = "hello, world!";
134
135    byte[] request = string.getBytes("UTF-8");
136    assertEquals(request.length, Libcore.os.write(clientFd, request, 0, request.length));
137
138    byte[] response = new byte[request.length];
139    assertEquals(response.length, Libcore.os.read(clientFd, response, 0, response.length));
140
141    assertEquals(string.toUpperCase(Locale.ROOT), new String(response, "UTF-8"));
142
143    Libcore.os.close(clientFd);
144  }
145
146  private static void checkSockName(FileDescriptor fd, boolean isAbstract,
147      UnixSocketAddress address) throws Exception {
148    UnixSocketAddress isa = (UnixSocketAddress) Libcore.os.getsockname(fd);
149    assertEquals(address, isa);
150    if (isAbstract) {
151      assertEquals(0, isa.getSunPath()[0]);
152    }
153  }
154
155  private void checkNoName(UnixSocketAddress usa) {
156    assertEquals(0, usa.getSunPath().length);
157  }
158
159  private void checkNoPeerName(FileDescriptor fd) throws Exception {
160    checkNoName((UnixSocketAddress) Libcore.os.getpeername(fd));
161  }
162
163  private void checkNoSockName(FileDescriptor fd) throws Exception {
164    checkNoName((UnixSocketAddress) Libcore.os.getsockname(fd));
165  }
166
167  public void test_strsignal() throws Exception {
168    assertEquals("Killed", Libcore.os.strsignal(9));
169    assertEquals("Unknown signal -1", Libcore.os.strsignal(-1));
170  }
171
172  public void test_byteBufferPositions_write_pwrite() throws Exception {
173    FileOutputStream fos = new FileOutputStream(new File("/dev/null"));
174    FileDescriptor fd = fos.getFD();
175    final byte[] contents = new String("goodbye, cruel world").getBytes(StandardCharsets.US_ASCII);
176    ByteBuffer byteBuffer = ByteBuffer.wrap(contents);
177
178    byteBuffer.position(0);
179    int written = Libcore.os.write(fd, byteBuffer);
180    assertTrue(written > 0);
181    assertEquals(written, byteBuffer.position());
182
183    byteBuffer.position(4);
184    written = Libcore.os.write(fd, byteBuffer);
185    assertTrue(written > 0);
186    assertEquals(written + 4, byteBuffer.position());
187
188    byteBuffer.position(0);
189    written = Libcore.os.pwrite(fd, byteBuffer, 64 /* offset */);
190    assertTrue(written > 0);
191    assertEquals(written, byteBuffer.position());
192
193    byteBuffer.position(4);
194    written = Libcore.os.pwrite(fd, byteBuffer, 64 /* offset */);
195    assertTrue(written > 0);
196    assertEquals(written + 4, byteBuffer.position());
197
198    fos.close();
199  }
200
201  public void test_byteBufferPositions_read_pread() throws Exception {
202    FileInputStream fis = new FileInputStream(new File("/dev/zero"));
203    FileDescriptor fd = fis.getFD();
204    ByteBuffer byteBuffer = ByteBuffer.allocate(64);
205
206    byteBuffer.position(0);
207    int read = Libcore.os.read(fd, byteBuffer);
208    assertTrue(read > 0);
209    assertEquals(read, byteBuffer.position());
210
211    byteBuffer.position(4);
212    read = Libcore.os.read(fd, byteBuffer);
213    assertTrue(read > 0);
214    assertEquals(read + 4, byteBuffer.position());
215
216    byteBuffer.position(0);
217    read = Libcore.os.pread(fd, byteBuffer, 64 /* offset */);
218    assertTrue(read > 0);
219    assertEquals(read, byteBuffer.position());
220
221    byteBuffer.position(4);
222    read = Libcore.os.pread(fd, byteBuffer, 64 /* offset */);
223    assertTrue(read > 0);
224    assertEquals(read + 4, byteBuffer.position());
225
226    fis.close();
227  }
228
229  static void checkByteBufferPositions_sendto_recvfrom(
230      int family, InetAddress loopback) throws Exception {
231    final FileDescriptor serverFd = Libcore.os.socket(family, SOCK_STREAM, 0);
232    Libcore.os.bind(serverFd, loopback, 0);
233    Libcore.os.listen(serverFd, 5);
234
235    InetSocketAddress address = (InetSocketAddress) Libcore.os.getsockname(serverFd);
236
237    final Thread server = new Thread(new Runnable() {
238      public void run() {
239        try {
240          InetSocketAddress peerAddress = new InetSocketAddress();
241          FileDescriptor clientFd = Libcore.os.accept(serverFd, peerAddress);
242
243          // Attempt to receive a maximum of 24 bytes from the client, and then
244          // close the connection.
245          ByteBuffer buffer = ByteBuffer.allocate(16);
246          int received = Libcore.os.recvfrom(clientFd, buffer, 0, null);
247          assertTrue(received > 0);
248          assertEquals(received, buffer.position());
249
250          ByteBuffer buffer2 = ByteBuffer.allocate(16);
251          buffer2.position(8);
252          received = Libcore.os.recvfrom(clientFd, buffer2, 0, null);
253          assertTrue(received > 0);
254          assertEquals(received + 8, buffer.position());
255
256          Libcore.os.close(clientFd);
257        } catch (Exception ex) {
258          throw new RuntimeException(ex);
259        }
260      }
261    });
262
263    server.start();
264
265    FileDescriptor clientFd = Libcore.os.socket(family, SOCK_STREAM, 0);
266    Libcore.os.connect(clientFd, address.getAddress(), address.getPort());
267
268    final byte[] bytes = "good bye, cruel black hole with fancy distortion"
269        .getBytes(StandardCharsets.US_ASCII);
270    assertTrue(bytes.length > 24);
271
272    ByteBuffer input = ByteBuffer.wrap(bytes);
273    input.position(0);
274    input.limit(16);
275
276    int sent = Libcore.os.sendto(clientFd, input, 0, address.getAddress(), address.getPort());
277    assertTrue(sent > 0);
278    assertEquals(sent, input.position());
279
280    input.position(16);
281    input.limit(24);
282    sent = Libcore.os.sendto(clientFd, input, 0, address.getAddress(), address.getPort());
283    assertTrue(sent > 0);
284    assertEquals(sent + 16, input.position());
285
286    Libcore.os.close(clientFd);
287  }
288
289  public void test_NetlinkSocket() throws Exception {
290    FileDescriptor nlSocket = Libcore.os.socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE);
291    Libcore.os.bind(nlSocket, new NetlinkSocketAddress());
292    NetlinkSocketAddress address = (NetlinkSocketAddress) Libcore.os.getsockname(nlSocket);
293    assertTrue(address.getPortId() > 0);
294    assertEquals(0, address.getGroupsMask());
295
296    NetlinkSocketAddress nlKernel = new NetlinkSocketAddress();
297    Libcore.os.connect(nlSocket, nlKernel);
298    NetlinkSocketAddress nlPeer = (NetlinkSocketAddress) Libcore.os.getpeername(nlSocket);
299    assertEquals(0, nlPeer.getPortId());
300    assertEquals(0, nlPeer.getGroupsMask());
301    Libcore.os.close(nlSocket);
302  }
303
304  public void test_PacketSocketAddress() throws Exception {
305    NetworkInterface lo = NetworkInterface.getByName("lo");
306    FileDescriptor fd = Libcore.os.socket(AF_PACKET, SOCK_DGRAM, ETH_P_IPV6);
307    PacketSocketAddress addr = new PacketSocketAddress((short) ETH_P_IPV6, lo.getIndex());
308    Libcore.os.bind(fd, addr);
309
310    PacketSocketAddress bound = (PacketSocketAddress) Libcore.os.getsockname(fd);
311    assertEquals((short) ETH_P_IPV6, bound.sll_protocol);  // ETH_P_IPV6 is an int.
312    assertEquals(lo.getIndex(), bound.sll_ifindex);
313    assertEquals(ARPHRD_LOOPBACK, bound.sll_hatype);
314    assertEquals(0, bound.sll_pkttype);
315
316    // The loopback address is ETH_ALEN bytes long and is all zeros.
317    // http://lxr.free-electrons.com/source/drivers/net/loopback.c?v=3.10#L167
318    assertEquals(6, bound.sll_addr.length);
319    for (int i = 0; i < 6; i++) {
320      assertEquals(0, bound.sll_addr[i]);
321    }
322  }
323
324  public void test_byteBufferPositions_sendto_recvfrom_af_inet() throws Exception {
325    checkByteBufferPositions_sendto_recvfrom(AF_INET, InetAddress.getByName("127.0.0.1"));
326  }
327
328  public void test_byteBufferPositions_sendto_recvfrom_af_inet6() throws Exception {
329    checkByteBufferPositions_sendto_recvfrom(AF_INET6, InetAddress.getByName("::1"));
330  }
331
332  private void checkSendToSocketAddress(int family, InetAddress loopback) throws Exception {
333    FileDescriptor recvFd = Libcore.os.socket(family, SOCK_DGRAM, 0);
334    Libcore.os.bind(recvFd, loopback, 0);
335    StructTimeval tv = StructTimeval.fromMillis(20);
336    Libcore.os.setsockoptTimeval(recvFd, SOL_SOCKET, SO_RCVTIMEO, tv);
337
338    InetSocketAddress to = ((InetSocketAddress) Libcore.os.getsockname(recvFd));
339    FileDescriptor sendFd = Libcore.os.socket(family, SOCK_DGRAM, 0);
340    byte[] msg = ("Hello, I'm going to a socket address: " + to.toString()).getBytes("UTF-8");
341    int len = msg.length;
342
343    assertEquals(len, Libcore.os.sendto(sendFd, msg, 0, len, 0, to));
344    byte[] received = new byte[msg.length + 42];
345    InetSocketAddress from = new InetSocketAddress();
346    assertEquals(len, Libcore.os.recvfrom(recvFd, received, 0, received.length, 0, from));
347    assertEquals(loopback, from.getAddress());
348  }
349
350  public void test_sendtoSocketAddress_af_inet() throws Exception {
351    checkSendToSocketAddress(AF_INET, InetAddress.getByName("127.0.0.1"));
352  }
353
354  public void test_sendtoSocketAddress_af_inet6() throws Exception {
355    checkSendToSocketAddress(AF_INET6, InetAddress.getByName("::1"));
356  }
357
358  public void test_socketFamilies() throws Exception {
359    FileDescriptor fd = Libcore.os.socket(AF_INET6, SOCK_STREAM, 0);
360    Libcore.os.bind(fd, InetAddress.getByName("::"), 0);
361    InetSocketAddress localSocketAddress = (InetSocketAddress) Libcore.os.getsockname(fd);
362    assertEquals(Inet6Address.ANY, localSocketAddress.getAddress());
363
364    fd = Libcore.os.socket(AF_INET6, SOCK_STREAM, 0);
365    Libcore.os.bind(fd, InetAddress.getByName("0.0.0.0"), 0);
366    localSocketAddress = (InetSocketAddress) Libcore.os.getsockname(fd);
367    assertEquals(Inet6Address.ANY, localSocketAddress.getAddress());
368
369    fd = Libcore.os.socket(AF_INET, SOCK_STREAM, 0);
370    Libcore.os.bind(fd, InetAddress.getByName("0.0.0.0"), 0);
371    localSocketAddress = (InetSocketAddress) Libcore.os.getsockname(fd);
372    assertEquals(Inet4Address.ANY, localSocketAddress.getAddress());
373    try {
374      Libcore.os.bind(fd, InetAddress.getByName("::"), 0);
375      fail("Expected ErrnoException binding IPv4 socket to ::");
376    } catch (ErrnoException expected) {
377      assertEquals("Expected EAFNOSUPPORT binding IPv4 socket to ::", EAFNOSUPPORT, expected.errno);
378    }
379  }
380
381  private static void assertArrayEquals(byte[] expected, byte[] actual) {
382    assertTrue("Expected=" + Arrays.toString(expected) + ", actual=" + Arrays.toString(actual),
383        Arrays.equals(expected, actual));
384  }
385
386  private static void checkSocketPing(FileDescriptor fd, InetAddress to, byte[] packet,
387      byte type, byte responseType, boolean useSendto) throws Exception {
388    int len = packet.length;
389    packet[0] = type;
390    if (useSendto) {
391      assertEquals(len, Libcore.os.sendto(fd, packet, 0, len, 0, to, 0));
392    } else {
393      Libcore.os.connect(fd, to, 0);
394      assertEquals(len, Libcore.os.sendto(fd, packet, 0, len, 0, null, 0));
395    }
396
397    int icmpId = ((InetSocketAddress) Libcore.os.getsockname(fd)).getPort();
398    byte[] received = new byte[4096];
399    InetSocketAddress srcAddress = new InetSocketAddress();
400    assertEquals(len, Libcore.os.recvfrom(fd, received, 0, received.length, 0, srcAddress));
401    assertEquals(to, srcAddress.getAddress());
402    assertEquals(responseType, received[0]);
403    assertEquals(received[4], (byte) (icmpId >> 8));
404    assertEquals(received[5], (byte) (icmpId & 0xff));
405
406    received = Arrays.copyOf(received, len);
407    received[0] = (byte) type;
408    received[2] = received[3] = 0;  // Checksum.
409    received[4] = received[5] = 0;  // ICMP ID.
410    assertArrayEquals(packet, received);
411  }
412
413  public void test_socketPing() throws Exception {
414    final byte ICMP_ECHO = 8, ICMP_ECHOREPLY = 0;
415    final byte ICMPV6_ECHO_REQUEST = (byte) 128, ICMPV6_ECHO_REPLY = (byte) 129;
416    final byte[] packet = ("\000\000\000\000" +  // ICMP type, code.
417        "\000\000\000\003" +  // ICMP ID (== port), sequence number.
418        "Hello myself").getBytes(StandardCharsets.US_ASCII);
419
420    FileDescriptor fd = Libcore.os.socket(AF_INET6, SOCK_DGRAM, IPPROTO_ICMPV6);
421    InetAddress ipv6Loopback = InetAddress.getByName("::1");
422    checkSocketPing(fd, ipv6Loopback, packet, ICMPV6_ECHO_REQUEST, ICMPV6_ECHO_REPLY, true);
423    checkSocketPing(fd, ipv6Loopback, packet, ICMPV6_ECHO_REQUEST, ICMPV6_ECHO_REPLY, false);
424
425    fd = Libcore.os.socket(AF_INET, SOCK_DGRAM, IPPROTO_ICMP);
426    InetAddress ipv4Loopback = InetAddress.getByName("127.0.0.1");
427    checkSocketPing(fd, ipv4Loopback, packet, ICMP_ECHO, ICMP_ECHOREPLY, true);
428    checkSocketPing(fd, ipv4Loopback, packet, ICMP_ECHO, ICMP_ECHOREPLY, false);
429  }
430
431  public void test_Ipv4Fallback() throws Exception {
432    // This number of iterations gives a ~60% chance of creating the conditions that caused
433    // http://b/23088314 without making test times too long. On a hammerhead running MRZ37C using
434    // vogar, this test takes about 4s.
435    final int ITERATIONS = 10000;
436    for (int i = 0; i < ITERATIONS; i++) {
437      FileDescriptor mUdpSock = Libcore.os.socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
438      try {
439          Libcore.os.bind(mUdpSock, Inet4Address.ANY, 0);
440      } catch(ErrnoException e) {
441          fail("ErrnoException after " + i + " iterations: " + e);
442      } finally {
443          Libcore.os.close(mUdpSock);
444      }
445    }
446  }
447
448  public void test_unlink() throws Exception {
449    File f = File.createTempFile("OsTest", "tst");
450    assertTrue(f.exists());
451    Libcore.os.unlink(f.getAbsolutePath());
452    assertFalse(f.exists());
453
454    try {
455      Libcore.os.unlink(f.getAbsolutePath());
456      fail();
457    } catch (ErrnoException e) {
458      assertEquals(OsConstants.ENOENT, e.errno);
459    }
460  }
461
462  // b/27294715
463  public void test_recvfrom_concurrentShutdown() throws Exception {
464      final FileDescriptor serverFd = Libcore.os.socket(AF_INET, SOCK_DGRAM, 0);
465      Libcore.os.bind(serverFd, InetAddress.getByName("127.0.0.1"), 0);
466      // Set 4s timeout
467      IoBridge.setSocketOption(serverFd, SocketOptions.SO_TIMEOUT, new Integer(4000));
468
469      final AtomicReference<Exception> killerThreadException = new AtomicReference<Exception>(null);
470      final Thread killer = new Thread(new Runnable() {
471          public void run() {
472              try {
473                  Thread.sleep(2000);
474                  try {
475                      Libcore.os.shutdown(serverFd, SHUT_RDWR);
476                  } catch (ErrnoException expected) {
477                      if (OsConstants.ENOTCONN != expected.errno) {
478                          killerThreadException.set(expected);
479                      }
480                  }
481              } catch (Exception ex) {
482                  killerThreadException.set(ex);
483              }
484          }
485      });
486      killer.start();
487
488      ByteBuffer buffer = ByteBuffer.allocate(16);
489      InetSocketAddress srcAddress = new InetSocketAddress();
490      int received = Libcore.os.recvfrom(serverFd, buffer, 0, srcAddress);
491      assertTrue(received == 0);
492      Libcore.os.close(serverFd);
493
494      killer.join();
495      assertNull(killerThreadException.get());
496  }
497
498  public void test_xattr() throws Exception {
499    final String NAME_TEST = "user.meow";
500
501    final byte[] VALUE_CAKE = "cake cake cake".getBytes(StandardCharsets.UTF_8);
502    final byte[] VALUE_PIE = "pie".getBytes(StandardCharsets.UTF_8);
503
504    File file = File.createTempFile("xattr", "test");
505    String path = file.getAbsolutePath();
506
507    byte[] tmp = new byte[1024];
508    try {
509      try {
510        Libcore.os.getxattr(path, NAME_TEST, tmp);
511        fail("Expected ENODATA");
512      } catch (ErrnoException e) {
513        assertEquals(OsConstants.ENODATA, e.errno);
514      }
515
516      Libcore.os.setxattr(path, NAME_TEST, VALUE_CAKE, OsConstants.XATTR_CREATE);
517      assertEquals(VALUE_CAKE.length, Libcore.os.getxattr(path, NAME_TEST, tmp));
518      assertStartsWith(VALUE_CAKE, tmp);
519
520      try {
521        Libcore.os.setxattr(path, NAME_TEST, VALUE_PIE, OsConstants.XATTR_CREATE);
522        fail("Expected EEXIST");
523      } catch (ErrnoException e) {
524        assertEquals(OsConstants.EEXIST, e.errno);
525      }
526
527      Libcore.os.setxattr(path, NAME_TEST, VALUE_PIE, OsConstants.XATTR_REPLACE);
528      assertEquals(VALUE_PIE.length, Libcore.os.getxattr(path, NAME_TEST, tmp));
529      assertStartsWith(VALUE_PIE, tmp);
530
531      Libcore.os.removexattr(path, NAME_TEST);
532      try {
533        Libcore.os.getxattr(path, NAME_TEST, tmp);
534        fail("Expected ENODATA");
535      } catch (ErrnoException e) {
536        assertEquals(OsConstants.ENODATA, e.errno);
537      }
538
539    } finally {
540      file.delete();
541    }
542  }
543
544  public void test_realpath() throws Exception {
545      File tmpDir = new File(System.getProperty("java.io.tmpdir"));
546      // This is a chicken and egg problem. We have no way of knowing whether
547      // the temporary directory or one of its path elements were symlinked, so
548      // we'll need this call to realpath.
549      String canonicalTmpDir = Libcore.os.realpath(tmpDir.getAbsolutePath());
550
551      // Test that "." and ".." are resolved correctly.
552      assertEquals(canonicalTmpDir,
553          Libcore.os.realpath(canonicalTmpDir + "/./../" + tmpDir.getName()));
554
555      // Test that symlinks are resolved correctly.
556      File target = new File(tmpDir, "target");
557      File link = new File(tmpDir, "link");
558      try {
559          assertTrue(target.createNewFile());
560          Libcore.os.symlink(target.getAbsolutePath(), link.getAbsolutePath());
561
562          assertEquals(canonicalTmpDir + "/target",
563              Libcore.os.realpath(canonicalTmpDir + "/link"));
564      } finally {
565          boolean deletedTarget = target.delete();
566          boolean deletedLink = link.delete();
567          // Asserting this here to provide a definitive reason for
568          // a subsequent failure on the same run.
569          assertTrue("deletedTarget = " + deletedTarget + ", deletedLink =" + deletedLink,
570              deletedTarget && deletedLink);
571      }
572  }
573
574  private static void assertStartsWith(byte[] expectedContents, byte[] container) {
575    for (int i = 0; i < expectedContents.length; i++) {
576      if (expectedContents[i] != container[i]) {
577        fail("Expected " + Arrays.toString(expectedContents) + " but found "
578            + Arrays.toString(expectedContents));
579      }
580    }
581  }
582}
583