content: /*
   * Copyright (C) 2014 The Android Open Source Project
   * Copyright (c) 1994, 2013, Oracle and/or its affiliates. All rights reserved.
   * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   *
   * This code is free software; you can redistribute it and/or modify it
   * under the terms of the GNU General Public License version 2 only, as
   * published by the Free Software Foundation.  Oracle designates this
   * particular file as subject to the "Classpath" exception as provided
   * by Oracle in the LICENSE file that accompanied this code.
   *
   * This code is distributed in the hope that it will be useful, but WITHOUT
   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   * version 2 for more details (a copy is included in the LICENSE file that
   * accompanied this code).
   *
   * You should have received a copy of the GNU General Public License version
   * 2 along with this work; if not, write to the Free Software Foundation,
   * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
   *
   * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
   * or visit www.oracle.com if you need additional information or have any
   * questions.
   */
  
  package java.io;
  
  import dalvik.annotation.optimization.ReachabilitySensitive;
  import java.nio.channels.FileChannel;
  import sun.nio.ch.FileChannelImpl;
  import android.system.Os;
  import android.system.ErrnoException;
  import dalvik.system.CloseGuard;
  import libcore.io.IoBridge;
  import libcore.io.IoTracker;
  import libcore.io.IoUtils;
  import libcore.io.Libcore;
  import static android.system.OsConstants.*;
  
  
  /**
   * Instances of this class support both reading and writing to a
   * random access file. A random access file behaves like a large
   * array of bytes stored in the file system. There is a kind of cursor,
   * or index into the implied array, called the <em>file pointer</em>;
   * input operations read bytes starting at the file pointer and advance
   * the file pointer past the bytes read. If the random access file is
   * created in read/write mode, then output operations are also available;
   * output operations write bytes starting at the file pointer and advance
   * the file pointer past the bytes written. Output operations that write
   * past the current end of the implied array cause the array to be
   * extended. The file pointer can be read by the
   * {@code getFilePointer} method and set by the {@code seek}
   * method.
   * <p>
   * It is generally true of all the reading routines in this class that
   * if end-of-file is reached before the desired number of bytes has been
   * read, an {@code EOFException} (which is a kind of
   * {@code IOException}) is thrown. If any byte cannot be read for
   * any reason other than end-of-file, an {@code IOException} other
   * than {@code EOFException} is thrown. In particular, an
   * {@code IOException} may be thrown if the stream has been closed.
   *
   * @author  unascribed
   * @since   JDK1.0
   */
  
  public class RandomAccessFile implements DataOutput, DataInput, Closeable {
  
      // BEGIN Android-added: CloseGuard and some helper fields for Android changes in this file.
      @ReachabilitySensitive
      private final CloseGuard guard = CloseGuard.get();
      private final byte[] scratch = new byte[8];
  
      private static final int FLUSH_NONE = 0;
      private static final int FLUSH_FSYNC = 1;
      private static final int FLUSH_FDATASYNC = 2;
      private int flushAfterWrite = FLUSH_NONE;
  
      private int mode;
      // END Android-added: CloseGuard and some helper fields for Android changes in this file.
  
      // Android-added: @ReachabilitySensitive
      @ReachabilitySensitive
      private FileDescriptor fd;
      private FileChannel channel = null;
      private boolean rw;
  
      /**
       * The path of the referenced file
       * (null if the stream is created with a file descriptor)
       */
      private final String path;
  
      private Object closeLock = new Object();
      private volatile boolean closed = false;
  
      // BEGIN Android-added: IoTracker.
      /**
       * A single tracker to track both read and write. The tracker resets when the operation
       * performed is different from the operation last performed.
       */
      private final IoTracker ioTracker = new IoTracker();
      // END Android-added: IoTracker.
  
      /**
       * Creates a random access file stream to read from, and optionally
       * to write to, a file with the specified name. A new
       * {@link FileDescriptor} object is created to represent the
       * connection to the file.
       *
       * <p> The <tt>mode</tt> argument specifies the access mode with which the
       * file is to be opened.  The permitted values and their meanings are as
       * specified for the <a
       * href="#mode"><tt>RandomAccessFile(File,String)</tt></a> constructor.
       *
       * <p>
       * If there is a security manager, its {@code checkRead} method
       * is called with the {@code name} argument
       * as its argument to see if read access to the file is allowed.
       * If the mode allows writing, the security manager's
       * {@code checkWrite} method
       * is also called with the {@code name} argument
       * as its argument to see if write access to the file is allowed.
       *
       * @param      name   the system-dependent filename
       * @param      mode   the access <a href="#mode">mode</a>
       * @exception  IllegalArgumentException  if the mode argument is not equal
       *               to one of <tt>"r"</tt>, <tt>"rw"</tt>, <tt>"rws"</tt>, or
       *               <tt>"rwd"</tt>
       * @exception FileNotFoundException
       *            if the mode is <tt>"r"</tt> but the given string does not
       *            denote an existing regular file, or if the mode begins with
       *            <tt>"rw"</tt> but the given string does not denote an
       *            existing, writable regular file and a new regular file of
       *            that name cannot be created, or if some other error occurs
       *            while opening or creating the file
       * @exception  SecurityException         if a security manager exists and its
       *               {@code checkRead} method denies read access to the file
       *               or the mode is "rw" and the security manager's
       *               {@code checkWrite} method denies write access to the file
       * @see        SecurityException
       * @see        SecurityManager#checkRead(String)
       * @see        SecurityManager#checkWrite(String)
       * @revised 1.4
       * @spec JSR-51
       */
      public RandomAccessFile(String name, String mode)
          throws FileNotFoundException
      {
          this(name != null ? new File(name) : null, mode);
      }
  
      /**
       * Creates a random access file stream to read from, and optionally to
       * write to, the file specified by the {@link File} argument.  A new {@link
       * FileDescriptor} object is created to represent this file connection.
       *
       * <p>The <a name="mode"><tt>mode</tt></a> argument specifies the access mode
       * in which the file is to be opened.  The permitted values and their
       * meanings are:
       *
       * <table summary="Access mode permitted values and meanings">
       * <tr><th align="left">Value</th><th align="left">Meaning</th></tr>
       * <tr><td valign="top"><tt>"r"</tt></td>
       *     <td> Open for reading only.  Invoking any of the <tt>write</tt>
       *     methods of the resulting object will cause an {@link
       *     IOException} to be thrown. </td></tr>
       * <tr><td valign="top"><tt>"rw"</tt></td>
       *     <td> Open for reading and writing.  If the file does not already
       *     exist then an attempt will be made to create it. </td></tr>
       * <tr><td valign="top"><tt>"rws"</tt></td>
       *     <td> Open for reading and writing, as with <tt>"rw"</tt>, and also
       *     require that every update to the file's content or metadata be
       *     written synchronously to the underlying storage device.  </td></tr>
       * <tr><td valign="top"><tt>"rwd"&nbsp;&nbsp;</tt></td>
       *     <td> Open for reading and writing, as with <tt>"rw"</tt>, and also
       *     require that every update to the file's content be written
       *     synchronously to the underlying storage device. </td></tr>
       * </table>
       *
       * The <tt>"rws"</tt> and <tt>"rwd"</tt> modes work much like the {@link
       * FileChannel#force(boolean) force(boolean)} method of
       * the {@link FileChannel} class, passing arguments of
       * <tt>true</tt> and <tt>false</tt>, respectively, except that they always
       * apply to every I/O operation and are therefore often more efficient.  If
       * the file resides on a local storage device then when an invocation of a
       * method of this class returns it is guaranteed that all changes made to
       * the file by that invocation will have been written to that device.  This
       * is useful for ensuring that critical information is not lost in the
       * event of a system crash.  If the file does not reside on a local device
       * then no such guarantee is made.
       *
       * <p>The <tt>"rwd"</tt> mode can be used to reduce the number of I/O
       * operations performed.  Using <tt>"rwd"</tt> only requires updates to the
       * file's content to be written to storage; using <tt>"rws"</tt> requires
       * updates to both the file's content and its metadata to be written, which
       * generally requires at least one more low-level I/O operation.
       *
       * <p>If there is a security manager, its {@code checkRead} method is
       * called with the pathname of the {@code file} argument as its
       * argument to see if read access to the file is allowed.  If the mode
       * allows writing, the security manager's {@code checkWrite} method is
       * also called with the path argument to see if write access to the file is
       * allowed.
       *
       * @param      file   the file object
       * @param      mode   the access mode, as described
       *                    <a href="#mode">above</a>
       * @exception  IllegalArgumentException  if the mode argument is not equal
       *               to one of <tt>"r"</tt>, <tt>"rw"</tt>, <tt>"rws"</tt>, or
       *               <tt>"rwd"</tt>
       * @exception FileNotFoundException
       *            if the mode is <tt>"r"</tt> but the given file object does
       *            not denote an existing regular file, or if the mode begins
       *            with <tt>"rw"</tt> but the given file object does not denote
       *            an existing, writable regular file and a new regular file of
       *            that name cannot be created, or if some other error occurs
       *            while opening or creating the file
       * @exception  SecurityException         if a security manager exists and its
       *               {@code checkRead} method denies read access to the file
       *               or the mode is "rw" and the security manager's
       *               {@code checkWrite} method denies write access to the file
       * @see        SecurityManager#checkRead(String)
       * @see        SecurityManager#checkWrite(String)
       * @see        FileChannel#force(boolean)
       * @revised 1.4
       * @spec JSR-51
       */
      public RandomAccessFile(File file, String mode)
          throws FileNotFoundException
      {
          String name = (file != null ? file.getPath() : null);
          int imode = -1;
          if (mode.equals("r")) {
              imode = O_RDONLY;
          } else if (mode.startsWith("rw")) {
              // Android-changed: Added. O_CREAT
              // imode = O_RDWR;
              imode = O_RDWR | O_CREAT;
              rw = true;
              if (mode.length() > 2) {
                  if (mode.equals("rws")) {
                      // Android-changed: For performance reasons, use fsync after each write.
                      // RandomAccessFile.write may result in multiple write syscalls,
                      // O_SYNC/O_DSYNC flags will cause a blocking wait on each syscall. Replacing
                      // them with single fsync/fdatasync call gives better performance with only
                      // minor decrease in reliability.
                      // imode |= O_SYNC;
                      flushAfterWrite = FLUSH_FSYNC;
                  } else if (mode.equals("rwd")) {
                      // Android-changed: For performance reasons, use fdatasync after each write.
                      // imode |= O_DSYNC;
                      flushAfterWrite = FLUSH_FDATASYNC;
                  } else {
                      imode = -1;
                  }
              }
          }
          if (imode < 0) {
              throw new IllegalArgumentException("Illegal mode \"" + mode
                                                 + "\" must be one of "
                                                 + "\"r\", \"rw\", \"rws\","
                                                 + " or \"rwd\"");
          }
          // Android-removed: do not use legacy security code
          /*
          SecurityManager security = System.getSecurityManager();
          if (security != null) {
              security.checkRead(name);
              if (rw) {
                  security.checkWrite(name);
              }
          }
          */
          if (name == null) {
              // Android-changed: different exception message in ctor when file == null.
              // throw new NullPointerException();
              throw new NullPointerException("file == null");
          }
          if (file.isInvalid()) {
              throw new FileNotFoundException("Invalid file path");
          }
          this.path = name;
          this.mode = imode;
  
          // BEGIN Android-changed: Use IoBridge.open() instead of open.
          fd = IoBridge.open(name, imode);
          IoUtils.setFdOwner(fd, this);
          maybeSync();
          guard.open("close");
          // END Android-changed: Use IoBridge.open() instead of open.
      }
  
      // BEGIN Android-added: Sync after rws/rwd write
      private void maybeSync() {
          if (flushAfterWrite == FLUSH_FSYNC) {
              try {
                  fd.sync();
              } catch (IOException e) {
                  // Ignored
              }
          } else if (flushAfterWrite == FLUSH_FDATASYNC) {
              try {
                  Os.fdatasync(fd);
              } catch (ErrnoException e) {
                  // Ignored
              }
          }
      }
      // END Android-added: Sync after rws/rwd write
  
      /**
       * Returns the opaque file descriptor object associated with this
       * stream.
       *
       * @return     the file descriptor object associated with this stream.
       * @exception  IOException  if an I/O error occurs.
       * @see        FileDescriptor
       */
      public final FileDescriptor getFD() throws IOException {
          if (fd != null) {
              return fd;
          }
          throw new IOException();
      }
  
      /**
       * Returns the unique {@link FileChannel FileChannel}
       * object associated with this file.
       *
       * <p> The {@link FileChannel#position()
       * position} of the returned channel will always be equal to
       * this object's file-pointer offset as returned by the {@link
       * #getFilePointer getFilePointer} method.  Changing this object's
       * file-pointer offset, whether explicitly or by reading or writing bytes,
       * will change the position of the channel, and vice versa.  Changing the
       * file's length via this object will change the length seen via the file
       * channel, and vice versa.
       *
       * @return  the file channel associated with this file
       *
       * @since 1.4
       * @spec JSR-51
       */
      public final FileChannel getChannel() {
          synchronized (this) {
              if (channel == null) {
                  channel = FileChannelImpl.open(fd, path, true, rw, this);
              }
              return channel;
          }
      }
  
      /**
       * Reads a byte of data from this file. The byte is returned as an
       * integer in the range 0 to 255 ({@code 0x00-0x0ff}). This
       * method blocks if no input is yet available.
       * <p>
       * Although {@code RandomAccessFile} is not a subclass of
       * {@code InputStream}, this method behaves in exactly the same
       * way as the {@link InputStream#read()} method of
       * {@code InputStream}.
       *
       * @return     the next byte of data, or {@code -1} if the end of the
       *             file has been reached.
       * @exception  IOException  if an I/O error occurs. Not thrown if
       *                          end-of-file has been reached.
       */
      public int read() throws IOException {
          // Android-changed: Implement on top of libcore os API.
          // return read0();
          return (read(scratch, 0, 1) != -1) ? scratch[0] & 0xff : -1;
      }
  
      /**
       * Reads a sub array as a sequence of bytes.
       * @param b the buffer into which the data is read.
       * @param off the start offset of the data.
       * @param len the number of bytes to read.
       * @exception IOException If an I/O error has occurred.
       */
      private int readBytes(byte b[], int off, int len) throws IOException {
          // Android-changed: Implement on top of libcore os API.
          ioTracker.trackIo(len, IoTracker.Mode.READ);
          return IoBridge.read(fd, b, off, len);
      }
  
      /**
       * Reads up to {@code len} bytes of data from this file into an
       * array of bytes. This method blocks until at least one byte of input
       * is available.
       * <p>
       * Although {@code RandomAccessFile} is not a subclass of
       * {@code InputStream}, this method behaves in exactly the
       * same way as the {@link InputStream#read(byte[], int, int)} method of
       * {@code InputStream}.
       *
       * @param      b     the buffer into which the data is read.
       * @param      off   the start offset in array {@code b}
       *                   at which the data is written.
       * @param      len   the maximum number of bytes read.
       * @return     the total number of bytes read into the buffer, or
       *             {@code -1} if there is no more data because the end of
       *             the file has been reached.
       * @exception  IOException If the first byte cannot be read for any reason
       * other than end of file, or if the random access file has been closed, or if
       * some other I/O error occurs.
       * @exception  NullPointerException If {@code b} is {@code null}.
       * @exception  IndexOutOfBoundsException If {@code off} is negative,
       * {@code len} is negative, or {@code len} is greater than
       * {@code b.length - off}
       */
      public int read(byte b[], int off, int len) throws IOException {
          return readBytes(b, off, len);
      }
  
      /**
       * Reads up to {@code b.length} bytes of data from this file
       * into an array of bytes. This method blocks until at least one byte
       * of input is available.
       * <p>
       * Although {@code RandomAccessFile} is not a subclass of
       * {@code InputStream}, this method behaves in exactly the
       * same way as the {@link InputStream#read(byte[])} method of
       * {@code InputStream}.
       *
       * @param      b   the buffer into which the data is read.
       * @return     the total number of bytes read into the buffer, or
       *             {@code -1} if there is no more data because the end of
       *             this file has been reached.
       * @exception  IOException If the first byte cannot be read for any reason
       * other than end of file, or if the random access file has been closed, or if
       * some other I/O error occurs.
       * @exception  NullPointerException If {@code b} is {@code null}.
       */
      public int read(byte b[]) throws IOException {
          return readBytes(b, 0, b.length);
      }
  
      /**
       * Reads {@code b.length} bytes from this file into the byte
       * array, starting at the current file pointer. This method reads
       * repeatedly from the file until the requested number of bytes are
       * read. This method blocks until the requested number of bytes are
       * read, the end of the stream is detected, or an exception is thrown.
       *
       * @param      b   the buffer into which the data is read.
       * @exception  EOFException  if this file reaches the end before reading
       *               all the bytes.
       * @exception  IOException   if an I/O error occurs.
       */
      public final void readFully(byte b[]) throws IOException {
          readFully(b, 0, b.length);
      }
  
      /**
       * Reads exactly {@code len} bytes from this file into the byte
       * array, starting at the current file pointer. This method reads
       * repeatedly from the file until the requested number of bytes are
       * read. This method blocks until the requested number of bytes are
       * read, the end of the stream is detected, or an exception is thrown.
       *
       * @param      b     the buffer into which the data is read.
       * @param      off   the start offset of the data.
       * @param      len   the number of bytes to read.
       * @exception  EOFException  if this file reaches the end before reading
       *               all the bytes.
       * @exception  IOException   if an I/O error occurs.
       */
      public final void readFully(byte b[], int off, int len) throws IOException {
          int n = 0;
          do {
              int count = this.read(b, off + n, len - n);
              if (count < 0)
                  throw new EOFException();
              n += count;
          } while (n < len);
      }
  
      /**
       * Attempts to skip over {@code n} bytes of input discarding the
       * skipped bytes.
       * <p>
       *
       * This method may skip over some smaller number of bytes, possibly zero.
       * This may result from any of a number of conditions; reaching end of
       * file before {@code n} bytes have been skipped is only one
       * possibility. This method never throws an {@code EOFException}.
       * The actual number of bytes skipped is returned.  If {@code n}
       * is negative, no bytes are skipped.
       *
       * @param      n   the number of bytes to be skipped.
       * @return     the actual number of bytes skipped.
       * @exception  IOException  if an I/O error occurs.
       */
      public int skipBytes(int n) throws IOException {
          long pos;
          long len;
          long newpos;
  
          if (n <= 0) {
              return 0;
          }
          pos = getFilePointer();
          len = length();
          newpos = pos + n;
          if (newpos > len) {
              newpos = len;
          }
          seek(newpos);
  
          /* return the actual number of bytes skipped */
          return (int) (newpos - pos);
      }
  
      // 'Write' primitives
  
      /**
       * Writes the specified byte to this file. The write starts at
       * the current file pointer.
       *
       * @param      b   the {@code byte} to be written.
       * @exception  IOException  if an I/O error occurs.
       */
      public void write(int b) throws IOException {
          // BEGIN Android-changed: Implement on top of libcore os API.
          // write0(b);
          scratch[0] = (byte) (b & 0xff);
          write(scratch, 0, 1);
          // END Android-changed: Implement on top of libcore os API.
      }
  
      /**
       * Writes a sub array as a sequence of bytes.
       * @param b the data to be written
  
       * @param off the start offset in the data
       * @param len the number of bytes that are written
       * @exception IOException If an I/O error has occurred.
       */
      private void writeBytes(byte b[], int off, int len) throws IOException {
          // BEGIN Android-changed: Implement on top of libcore os API.
          ioTracker.trackIo(len, IoTracker.Mode.WRITE);
          IoBridge.write(fd, b, off, len);
          maybeSync();
          // END Android-changed: Implement on top of libcore os API.
      }
  
      /**
       * Writes {@code b.length} bytes from the specified byte array
       * to this file, starting at the current file pointer.
       *
       * @param      b   the data.
       * @exception  IOException  if an I/O error occurs.
       */
      public void write(byte b[]) throws IOException {
          writeBytes(b, 0, b.length);
      }
  
      /**
       * Writes {@code len} bytes from the specified byte array
       * starting at offset {@code off} to this file.
       *
       * @param      b     the data.
       * @param      off   the start offset in the data.
       * @param      len   the number of bytes to write.
       * @exception  IOException  if an I/O error occurs.
       */
      public void write(byte b[], int off, int len) throws IOException {
          writeBytes(b, off, len);
      }
  
      // 'Random access' stuff
  
      /**
       * Returns the current offset in this file.
       *
       * @return     the offset from the beginning of the file, in bytes,
       *             at which the next read or write occurs.
       * @exception  IOException  if an I/O error occurs.
       */
      public long getFilePointer() throws IOException {
          // BEGIN Android-changed: Implement on top of libcore os API.
          try {
              return Libcore.os.lseek(fd, 0L, SEEK_CUR);
          } catch (ErrnoException errnoException) {
              throw errnoException.rethrowAsIOException();
          }
          // END Android-changed: Implement on top of libcore os API.
      }
  
      /**
       * Sets the file-pointer offset, measured from the beginning of this
       * file, at which the next read or write occurs.  The offset may be
       * set beyond the end of the file. Setting the offset beyond the end
       * of the file does not change the file length.  The file length will
       * change only by writing after the offset has been set beyond the end
       * of the file.
       *
       * @param      pos   the offset position, measured in bytes from the
       *                   beginning of the file, at which to set the file
       *                   pointer.
       * @exception  IOException  if {@code pos} is less than
       *                          {@code 0} or if an I/O error occurs.
       */
      public void seek(long pos) throws IOException {
          if (pos < 0) {
              // Android-changed: different exception message for seek(-1).
              // throw new IOException("Negative seek offset");
              throw new IOException("offset < 0: " + pos);
          } else {
              // BEGIN Android-changed: Implement on top of libcore os API.
              // seek0(pos);
              try {
                  Libcore.os.lseek(fd, pos, SEEK_SET);
                  ioTracker.reset();
              } catch (ErrnoException errnoException) {
                  throw errnoException.rethrowAsIOException();
              }
              // END Android-changed: Implement on top of libcore os API.
          }
      }
  
      /**
       * Returns the length of this file.
       *
       * @return     the length of this file, measured in bytes.
       * @exception  IOException  if an I/O error occurs.
       */
      public long length() throws IOException {
          // BEGIN Android-changed: Implement on top of libcore os API.
          try {
              return Libcore.os.fstat(fd).st_size;
          } catch (ErrnoException errnoException) {
              throw errnoException.rethrowAsIOException();
          }
          // END Android-changed: Implement on top of libcore os API.
      }
  
      /**
       * Sets the length of this file.
       *
       * <p> If the present length of the file as returned by the
       * {@code length} method is greater than the {@code newLength}
       * argument then the file will be truncated.  In this case, if the file
       * offset as returned by the {@code getFilePointer} method is greater
       * than {@code newLength} then after this method returns the offset
       * will be equal to {@code newLength}.
       *
       * <p> If the present length of the file as returned by the
       * {@code length} method is smaller than the {@code newLength}
       * argument then the file will be extended.  In this case, the contents of
       * the extended portion of the file are not defined.
       *
       * @param      newLength    The desired length of the file
       * @exception  IOException  If an I/O error occurs
       * @since      1.2
       */
      public void setLength(long newLength) throws IOException {
          // BEGIN Android-changed: Implement on top of libcore os API.
          if (newLength < 0) {
              throw new IllegalArgumentException("newLength < 0");
          }
          try {
              Libcore.os.ftruncate(fd, newLength);
          } catch (ErrnoException errnoException) {
              throw errnoException.rethrowAsIOException();
          }
  
          long filePointer = getFilePointer();
          if (filePointer > newLength) {
              seek(newLength);
          }
          maybeSync();
          // END Android-changed: Implement on top of libcore os API.
      }
  
  
      /**
       * Closes this random access file stream and releases any system
       * resources associated with the stream. A closed random access
       * file cannot perform input or output operations and cannot be
       * reopened.
       *
       * <p> If this file has an associated channel then the channel is closed
       * as well.
       *
       * @exception  IOException  if an I/O error occurs.
       *
       * @revised 1.4
       * @spec JSR-51
       */
      public void close() throws IOException {
          guard.close();
          synchronized (closeLock) {
              if (closed) {
                  return;
              }
              closed = true;
          }
  
          // BEGIN Android-changed: Implement on top of libcore os API.
          if (channel != null && channel.isOpen()) {
              channel.close();
          }
          IoBridge.closeAndSignalBlockedThreads(fd);
          // END Android-changed: Implement on top of libcore os API.
      }
  
      //
      //  Some "reading/writing Java data types" methods stolen from
      //  DataInputStream and DataOutputStream.
      //
  
      /**
       * Reads a {@code boolean} from this file. This method reads a
       * single byte from the file, starting at the current file pointer.
       * A value of {@code 0} represents
       * {@code false}. Any other value represents {@code true}.
       * This method blocks until the byte is read, the end of the stream
       * is detected, or an exception is thrown.
       *
       * @return     the {@code boolean} value read.
       * @exception  EOFException  if this file has reached the end.
       * @exception  IOException   if an I/O error occurs.
       */
      public final boolean readBoolean() throws IOException {
          int ch = this.read();
          if (ch < 0)
              throw new EOFException();
          return (ch != 0);
      }
  
      /**
       * Reads a signed eight-bit value from this file. This method reads a
       * byte from the file, starting from the current file pointer.
       * If the byte read is {@code b}, where
       * <code>0&nbsp;&lt;=&nbsp;b&nbsp;&lt;=&nbsp;255</code>,
       * then the result is:
       * <blockquote><pre>
       *     (byte)(b)
       * </pre></blockquote>
       * <p>
       * This method blocks until the byte is read, the end of the stream
       * is detected, or an exception is thrown.
       *
       * @return     the next byte of this file as a signed eight-bit
       *             {@code byte}.
       * @exception  EOFException  if this file has reached the end.
       * @exception  IOException   if an I/O error occurs.
       */
      public final byte readByte() throws IOException {
          int ch = this.read();
          if (ch < 0)
              throw new EOFException();
          return (byte)(ch);
      }
  
      /**
       * Reads an unsigned eight-bit number from this file. This method reads
       * a byte from this file, starting at the current file pointer,
       * and returns that byte.
       * <p>
       * This method blocks until the byte is read, the end of the stream
       * is detected, or an exception is thrown.
       *
       * @return     the next byte of this file, interpreted as an unsigned
       *             eight-bit number.
       * @exception  EOFException  if this file has reached the end.
       * @exception  IOException   if an I/O error occurs.
       */
      public final int readUnsignedByte() throws IOException {
          int ch = this.read();
          if (ch < 0)
              throw new EOFException();
          return ch;
      }
  
      /**
       * Reads a signed 16-bit number from this file. The method reads two
       * bytes from this file, starting at the current file pointer.
       * If the two bytes read, in order, are
       * {@code b1} and {@code b2}, where each of the two values is
       * between {@code 0} and {@code 255}, inclusive, then the
       * result is equal to:
       * <blockquote><pre>
       *     (short)((b1 &lt;&lt; 8) | b2)
       * </pre></blockquote>
       * <p>
       * This method blocks until the two bytes are read, the end of the
       * stream is detected, or an exception is thrown.
       *
       * @return     the next two bytes of this file, interpreted as a signed
       *             16-bit number.
       * @exception  EOFException  if this file reaches the end before reading
       *               two bytes.
       * @exception  IOException   if an I/O error occurs.
       */
      public final short readShort() throws IOException {
          int ch1 = this.read();
          int ch2 = this.read();
          if ((ch1 | ch2) < 0)
              throw new EOFException();
          return (short)((ch1 << 8) + (ch2 << 0));
      }
  
      /**
       * Reads an unsigned 16-bit number from this file. This method reads
       * two bytes from the file, starting at the current file pointer.
       * If the bytes read, in order, are
       * {@code b1} and {@code b2}, where
       * <code>0&nbsp;&lt;=&nbsp;b1, b2&nbsp;&lt;=&nbsp;255</code>,
       * then the result is equal to:
       * <blockquote><pre>
       *     (b1 &lt;&lt; 8) | b2
       * </pre></blockquote>
       * <p>
       * This method blocks until the two bytes are read, the end of the
       * stream is detected, or an exception is thrown.
       *
       * @return     the next two bytes of this file, interpreted as an unsigned
       *             16-bit integer.
       * @exception  EOFException  if this file reaches the end before reading
       *               two bytes.
       * @exception  IOException   if an I/O error occurs.
       */
      public final int readUnsignedShort() throws IOException {
          int ch1 = this.read();
          int ch2 = this.read();
          if ((ch1 | ch2) < 0)
              throw new EOFException();
          return (ch1 << 8) + (ch2 << 0);
      }
  
      /**
       * Reads a character from this file. This method reads two
       * bytes from the file, starting at the current file pointer.
       * If the bytes read, in order, are
       * {@code b1} and {@code b2}, where
       * <code>0&nbsp;&lt;=&nbsp;b1,&nbsp;b2&nbsp;&lt;=&nbsp;255</code>,
       * then the result is equal to:
       * <blockquote><pre>
       *     (char)((b1 &lt;&lt; 8) | b2)
       * </pre></blockquote>
       * <p>
       * This method blocks until the two bytes are read, the end of the
       * stream is detected, or an exception is thrown.
       *
       * @return     the next two bytes of this file, interpreted as a
       *                  {@code char}.
       * @exception  EOFException  if this file reaches the end before reading
       *               two bytes.
       * @exception  IOException   if an I/O error occurs.
       */
      public final char readChar() throws IOException {
          int ch1 = this.read();
          int ch2 = this.read();
          if ((ch1 | ch2) < 0)
              throw new EOFException();
          return (char)((ch1 << 8) + (ch2 << 0));
      }
  
      /**
       * Reads a signed 32-bit integer from this file. This method reads 4
       * bytes from the file, starting at the current file pointer.
       * If the bytes read, in order, are {@code b1},
       * {@code b2}, {@code b3}, and {@code b4}, where
       * <code>0&nbsp;&lt;=&nbsp;b1, b2, b3, b4&nbsp;&lt;=&nbsp;255</code>,
       * then the result is equal to:
       * <blockquote><pre>
       *     (b1 &lt;&lt; 24) | (b2 &lt;&lt; 16) + (b3 &lt;&lt; 8) + b4
       * </pre></blockquote>
       * <p>
       * This method blocks until the four bytes are read, the end of the
       * stream is detected, or an exception is thrown.
       *
       * @return     the next four bytes of this file, interpreted as an
       *             {@code int}.
       * @exception  EOFException  if this file reaches the end before reading
       *               four bytes.
       * @exception  IOException   if an I/O error occurs.
       */
      public final int readInt() throws IOException {
          int ch1 = this.read();
          int ch2 = this.read();
          int ch3 = this.read();
          int ch4 = this.read();
          if ((ch1 | ch2 | ch3 | ch4) < 0)
              throw new EOFException();
          return ((ch1 << 24) + (ch2 << 16) + (ch3 << 8) + (ch4 << 0));
      }
  
      /**
       * Reads a signed 64-bit integer from this file. This method reads eight
       * bytes from the file, starting at the current file pointer.
       * If the bytes read, in order, are
       * {@code b1}, {@code b2}, {@code b3},
       * {@code b4}, {@code b5}, {@code b6},
       * {@code b7}, and {@code b8,} where:
       * <blockquote><pre>
       *     0 &lt;= b1, b2, b3, b4, b5, b6, b7, b8 &lt;=255,
       * </pre></blockquote>
       * <p>
       * then the result is equal to:
       * <blockquote><pre>
       *     ((long)b1 &lt;&lt; 56) + ((long)b2 &lt;&lt; 48)
       *     + ((long)b3 &lt;&lt; 40) + ((long)b4 &lt;&lt; 32)
       *     + ((long)b5 &lt;&lt; 24) + ((long)b6 &lt;&lt; 16)
       *     + ((long)b7 &lt;&lt; 8) + b8
       * </pre></blockquote>
       * <p>
       * This method blocks until the eight bytes are read, the end of the
       * stream is detected, or an exception is thrown.
       *
       * @return     the next eight bytes of this file, interpreted as a
       *             {@code long}.
       * @exception  EOFException  if this file reaches the end before reading
       *               eight bytes.
       * @exception  IOException   if an I/O error occurs.
       */
      public final long readLong() throws IOException {
          return ((long)(readInt()) << 32) + (readInt() & 0xFFFFFFFFL);
      }
  
      /**
       * Reads a {@code float} from this file. This method reads an
       * {@code int} value, starting at the current file pointer,
       * as if by the {@code readInt} method
       * and then converts that {@code int} to a {@code float}
       * using the {@code intBitsToFloat} method in class
       * {@code Float}.
       * <p>
       * This method blocks until the four bytes are read, the end of the
       * stream is detected, or an exception is thrown.
       *
       * @return     the next four bytes of this file, interpreted as a
       *             {@code float}.
       * @exception  EOFException  if this file reaches the end before reading
       *             four bytes.
       * @exception  IOException   if an I/O error occurs.
       * @see        RandomAccessFile#readInt()
       * @see        Float#intBitsToFloat(int)
       */
      public final float readFloat() throws IOException {
          return Float.intBitsToFloat(readInt());
      }
  
      /**
       * Reads a {@code double} from this file. This method reads a
       * {@code long} value, starting at the current file pointer,
       * as if by the {@code readLong} method
       * and then converts that {@code long} to a {@code double}
       * using the {@code longBitsToDouble} method in
       * class {@code Double}.
       * <p>
       * This method blocks until the eight bytes are read, the end of the
       * stream is detected, or an exception is thrown.
       *
       * @return     the next eight bytes of this file, interpreted as a
       *             {@code double}.
       * @exception  EOFException  if this file reaches the end before reading
       *             eight bytes.
       * @exception  IOException   if an I/O error occurs.
       * @see        RandomAccessFile#readLong()
       * @see        Double#longBitsToDouble(long)
       */
      public final double readDouble() throws IOException {
          return Double.longBitsToDouble(readLong());
      }
  
      /**
       * Reads the next line of text from this file.  This method successively
       * reads bytes from the file, starting at the current file pointer,
       * until it reaches a line terminator or the end
       * of the file.  Each byte is converted into a character by taking the
       * byte's value for the lower eight bits of the character and setting the
       * high eight bits of the character to zero.  This method does not,
       * therefore, support the full Unicode character set.
       *
       * <p> A line of text is terminated by a carriage-return character
       * ({@code '\u005Cr'}), a newline character ({@code '\u005Cn'}), a
       * carriage-return character immediately followed by a newline character,
       * or the end of the file.  Line-terminating characters are discarded and
       * are not included as part of the string returned.
       *
       * <p> This method blocks until a newline character is read, a carriage
       * return and the byte following it are read (to see if it is a newline),
       * the end of the file is reached, or an exception is thrown.
       *
       * @return     the next line of text from this file, or null if end
       *             of file is encountered before even one byte is read.
       * @exception  IOException  if an I/O error occurs.
       */
  
      public final String readLine() throws IOException {
          StringBuffer input = new StringBuffer();
          int c = -1;
          boolean eol = false;
  
          while (!eol) {
              switch (c = read()) {
              case -1:
              case '\n':
                  eol = true;
                  break;
              case '\r':
                  eol = true;
                  long cur = getFilePointer();
                  if ((read()) != '\n') {
                      seek(cur);
                  }
                  break;
              default:
                  input.append((char)c);
                  break;
              }
          }
  
          if ((c == -1) && (input.length() == 0)) {
              return null;
          }
          return input.toString();
      }
  
      /**
       * Reads in a string from this file. The string has been encoded
       * using a
       * <a href="DataInput.html#modified-utf-8">modified UTF-8</a>
       * format.
       * <p>
       * The first two bytes are read, starting from the current file
       * pointer, as if by
       * {@code readUnsignedShort}. This value gives the number of
       * following bytes that are in the encoded string, not
       * the length of the resulting string. The following bytes are then
       * interpreted as bytes encoding characters in the modified UTF-8 format
       * and are converted into characters.
       * <p>
       * This method blocks until all the bytes are read, the end of the
       * stream is detected, or an exception is thrown.
       *
       * @return     a Unicode string.
       * @exception  EOFException            if this file reaches the end before
       *               reading all the bytes.
       * @exception  IOException             if an I/O error occurs.
       * @exception  UTFDataFormatException  if the bytes do not represent
       *               valid modified UTF-8 encoding of a Unicode string.
       * @see        RandomAccessFile#readUnsignedShort()
       */
      public final String readUTF() throws IOException {
          return DataInputStream.readUTF(this);
      }
  
      /**
       * Writes a {@code boolean} to the file as a one-byte value. The
       * value {@code true} is written out as the value
       * {@code (byte)1}; the value {@code false} is written out
       * as the value {@code (byte)0}. The write starts at
       * the current position of the file pointer.
       *
       * @param      v   a {@code boolean} value to be written.
       * @exception  IOException  if an I/O error occurs.
       */
      public final void writeBoolean(boolean v) throws IOException {
          write(v ? 1 : 0);
          //written++;
      }
  
      /**
       * Writes a {@code byte} to the file as a one-byte value. The
       * write starts at the current position of the file pointer.
       *
       * @param      v   a {@code byte} value to be written.
       * @exception  IOException  if an I/O error occurs.
       */
      public final void writeByte(int v) throws IOException {
          write(v);
          //written++;
      }
  
      /**
       * Writes a {@code short} to the file as two bytes, high byte first.
       * The write starts at the current position of the file pointer.
       *
       * @param      v   a {@code short} to be written.
       * @exception  IOException  if an I/O error occurs.
       */
      public final void writeShort(int v) throws IOException {
          write((v >>> 8) & 0xFF);
          write((v >>> 0) & 0xFF);
          //written += 2;
      }
  
      /**
       * Writes a {@code char} to the file as a two-byte value, high
       * byte first. The write starts at the current position of the
       * file pointer.
       *
       * @param      v   a {@code char} value to be written.
       * @exception  IOException  if an I/O error occurs.
       */
      public final void writeChar(int v) throws IOException {
          write((v >>> 8) & 0xFF);
          write((v >>> 0) & 0xFF);
          //written += 2;
      }
  
      /**
       * Writes an {@code int} to the file as four bytes, high byte first.
       * The write starts at the current position of the file pointer.
       *
       * @param      v   an {@code int} to be written.
       * @exception  IOException  if an I/O error occurs.
       */
      public final void writeInt(int v) throws IOException {
          write((v >>> 24) & 0xFF);
          write((v >>> 16) & 0xFF);
          write((v >>>  8) & 0xFF);
          write((v >>>  0) & 0xFF);
          //written += 4;
      }
  
      /**
       * Writes a {@code long} to the file as eight bytes, high byte first.
       * The write starts at the current position of the file pointer.
       *
       * @param      v   a {@code long} to be written.
       * @exception  IOException  if an I/O error occurs.
       */
      public final void writeLong(long v) throws IOException {
          write((int)(v >>> 56) & 0xFF);
          write((int)(v >>> 48) & 0xFF);
          write((int)(v >>> 40) & 0xFF);
          write((int)(v >>> 32) & 0xFF);
          write((int)(v >>> 24) & 0xFF);
          write((int)(v >>> 16) & 0xFF);
          write((int)(v >>>  8) & 0xFF);
          write((int)(v >>>  0) & 0xFF);
          //written += 8;
      }
  
      /**
       * Converts the float argument to an {@code int} using the
       * {@code floatToIntBits} method in class {@code Float},
       * and then writes that {@code int} value to the file as a
       * four-byte quantity, high byte first. The write starts at the
       * current position of the file pointer.
       *
       * @param      v   a {@code float} value to be written.
       * @exception  IOException  if an I/O error occurs.
       * @see        Float#floatToIntBits(float)
       */
      public final void writeFloat(float v) throws IOException {
          writeInt(Float.floatToIntBits(v));
      }
  
      /**
       * Converts the double argument to a {@code long} using the
       * {@code doubleToLongBits} method in class {@code Double},
       * and then writes that {@code long} value to the file as an
       * eight-byte quantity, high byte first. The write starts at the current
       * position of the file pointer.
       *
       * @param      v   a {@code double} value to be written.
       * @exception  IOException  if an I/O error occurs.
       * @see        Double#doubleToLongBits(double)
       */
      public final void writeDouble(double v) throws IOException {
          writeLong(Double.doubleToLongBits(v));
      }
  
      /**
       * Writes the string to the file as a sequence of bytes. Each
       * character in the string is written out, in sequence, by discarding
       * its high eight bits. The write starts at the current position of
       * the file pointer.
       *
       * @param      s   a string of bytes to be written.
       * @exception  IOException  if an I/O error occurs.
       */
      @SuppressWarnings("deprecation")
      public final void writeBytes(String s) throws IOException {
          int len = s.length();
          byte[] b = new byte[len];
          s.getBytes(0, len, b, 0);
          writeBytes(b, 0, len);
      }
  
      /**
       * Writes a string to the file as a sequence of characters. Each
       * character is written to the data output stream as if by the
       * {@code writeChar} method. The write starts at the current
       * position of the file pointer.
       *
       * @param      s   a {@code String} value to be written.
       * @exception  IOException  if an I/O error occurs.
       * @see        RandomAccessFile#writeChar(int)
       */
      public final void writeChars(String s) throws IOException {
          int clen = s.length();
          int blen = 2*clen;
          byte[] b = new byte[blen];
          char[] c = new char[clen];
          s.getChars(0, clen, c, 0);
          for (int i = 0, j = 0; i < clen; i++) {
              b[j++] = (byte)(c[i] >>> 8);
              b[j++] = (byte)(c[i] >>> 0);
          }
          writeBytes(b, 0, blen);
      }
  
      /**
       * Writes a string to the file using
       * <a href="DataInput.html#modified-utf-8">modified UTF-8</a>
       * encoding in a machine-independent manner.
       * <p>
       * First, two bytes are written to the file, starting at the
       * current file pointer, as if by the
       * {@code writeShort} method giving the number of bytes to
       * follow. This value is the number of bytes actually written out,
       * not the length of the string. Following the length, each character
       * of the string is output, in sequence, using the modified UTF-8 encoding
       * for each character.
       *
       * @param      str   a string to be written.
       * @exception  IOException  if an I/O error occurs.
       */
      public final void writeUTF(String str) throws IOException {
          DataOutputStream.writeUTF(str, this);
      }
  
      // Android-added: use finalize() to detect if not close()d.
      @Override protected void finalize() throws Throwable {
          try {
              if (guard != null) {
                  guard.warnIfOpen();
              }
              close();
          } finally {
              super.finalize();
          }
      }
  }
  