Home | History | Annotate | Download | only in internal
      1 /*
      2  *  Licensed to the Apache Software Foundation (ASF) under one or more
      3  *  contributor license agreements.  See the NOTICE file distributed with
      4  *  this work for additional information regarding copyright ownership.
      5  *  The ASF licenses this file to You under the Apache License, Version 2.0
      6  *  (the "License"); you may not use this file except in compliance with
      7  *  the License.  You may obtain a copy of the License at
      8  *
      9  *     http://www.apache.org/licenses/LICENSE-2.0
     10  *
     11  *  Unless required by applicable law or agreed to in writing, software
     12  *  distributed under the License is distributed on an "AS IS" BASIS,
     13  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     14  *  See the License for the specific language governing permissions and
     15  *  limitations under the License.
     16  */
     17 
     18 /*
     19  * Android Notice
     20  * In this class the address length was changed from long to int.
     21  * This is due to performance optimizations for the device.
     22  *
     23  * Also this class was copied from a newer version of harmony.
     24  */
     25 
     26 package org.apache.harmony.nio.internal;
     27 
     28 import java.io.Closeable;
     29 import java.io.FileDescriptor;
     30 import java.io.IOException;
     31 import java.nio.ByteBuffer;
     32 import java.nio.MappedByteBuffer;
     33 import java.nio.MappedByteBufferAdapter;
     34 import java.nio.channels.ClosedChannelException;
     35 import java.nio.channels.FileChannel;
     36 import java.nio.channels.FileLock;
     37 import java.nio.channels.NonWritableChannelException;
     38 import java.nio.channels.ReadableByteChannel;
     39 import java.nio.channels.WritableByteChannel;
     40 import org.apache.harmony.luni.platform.IFileSystem;
     41 import org.apache.harmony.luni.platform.Platform;
     42 import org.apache.harmony.luni.platform.PlatformAddress;
     43 import org.apache.harmony.luni.platform.PlatformAddressFactory;
     44 
     45 /*
     46  * The file channel impl class is the bridge between the logical channels
     47  * described by the NIO channel framework, and the file system implementation
     48  * provided by the port layer.
     49  *
     50  * This class is non-API, but implements the API of the FileChannel interface.
     51  */
     52 public abstract class FileChannelImpl extends FileChannel {
     53 
     54     // Reference to the portable file system code.
     55     private static final IFileSystem fileSystem = Platform.getFileSystem();
     56 
     57     private static final int ALLOC_GRANULARITY;
     58 
     59     static {
     60         try {
     61             ALLOC_GRANULARITY = fileSystem.getAllocGranularity();
     62         } catch (IOException e) {
     63             throw new Error(e);
     64         }
     65     }
     66 
     67     // Handle to the open file
     68     private final int handle;
     69 
     70     // The object that will track all outstanding locks on this channel.
     71     private final LockManager lockManager = new LockManager();
     72 
     73     private static class RepositioningLock {}
     74     private final Object repositioningLock = new RepositioningLock();
     75 
     76     private final Object stream;
     77 
     78     /*
     79      * Create a new file channel implementation class that wraps the given file
     80      * handle and operates in the specified mode.
     81      *
     82      */
     83     public FileChannelImpl(Object stream, int handle) {
     84         super();
     85         this.handle = handle;
     86         this.stream = stream;
     87     }
     88 
     89     /*
     90      * Helper method to throw an exception if the channel is already closed.
     91      * Note that we don't bother to synchronize on this test since the file may
     92      * be closed by operations beyond our control anyways.
     93      */
     94     protected final void openCheck() throws ClosedChannelException {
     95         if (!isOpen()) {
     96             throw new ClosedChannelException();
     97         }
     98     }
     99 
    100     /*
    101      * (non-Javadoc)
    102      *
    103      * @see java.nio.channels.spi.AbstractInterruptibleChannel#implCloseChannel()
    104      */
    105     protected void implCloseChannel() throws IOException {
    106         if (stream instanceof Closeable) {
    107             ((Closeable) stream).close();
    108         }
    109     }
    110 
    111     protected FileLock basicLock(long position, long size, boolean shared,
    112             boolean wait) throws IOException {
    113         if (position < 0 || size < 0) {
    114             throw new IllegalArgumentException("Lock position and size must be non-negative");
    115         }
    116         int lockType = shared ? IFileSystem.SHARED_LOCK_TYPE : IFileSystem.EXCLUSIVE_LOCK_TYPE;
    117         FileLock pendingLock = new FileLockImpl(this, position, size, shared);
    118         lockManager.addLock(pendingLock);
    119 
    120         if (fileSystem.lock(handle, position, size, lockType, wait)) {
    121             return pendingLock;
    122         }
    123 
    124         // Lock acquisition failed
    125         lockManager.removeLock(pendingLock);
    126         return null;
    127     }
    128 
    129     /*
    130      * Acquire a lock on the receiver, blocks if the lock cannot be obtained
    131      * immediately.
    132      *
    133      * @see java.nio.channels.FileChannel#lock(long, long, boolean)
    134      */
    135     public final FileLock lock(long position, long size, boolean shared)
    136             throws IOException {
    137         openCheck();
    138         FileLock resultLock = null;
    139         {
    140             boolean completed = false;
    141             try {
    142                 begin();
    143                 resultLock = basicLock(position, size, shared, true);
    144                 completed = true;
    145             } finally {
    146                 end(completed);
    147             }
    148         }
    149         return resultLock;
    150     }
    151 
    152     /*
    153      * Attempts to acquire the given lock, but does not block. If the lock
    154      * cannot be acquired the method returns null.
    155      *
    156      * @see java.nio.channels.FileChannel#tryLock(long, long, boolean)
    157      */
    158     public final FileLock tryLock(long position, long size, boolean shared)
    159             throws IOException {
    160         openCheck();
    161         return basicLock(position, size, shared, false);
    162     }
    163 
    164     /*
    165      * Non-API method to release a given lock on a file channel. Assumes that
    166      * the lock will mark itself invalid after successful unlocking.
    167      */
    168     void release(FileLock lock) throws IOException {
    169         openCheck();
    170         fileSystem.unlock(handle, lock.position(), lock.size());
    171         lockManager.removeLock(lock);
    172     }
    173 
    174     @Override public void force(boolean metadata) throws IOException {
    175         openCheck();
    176         fileSystem.fsync(handle, metadata);
    177     }
    178 
    179     public abstract MappedByteBuffer map(MapMode mode, long position, long size) throws IOException;
    180 
    181     protected final MappedByteBuffer mapImpl(MapMode mapMode, long position, long size)
    182             throws IOException {
    183         if (position + size > size()) {
    184             fileSystem.truncate(handle, position + size);
    185         }
    186         long alignment = position - position % ALLOC_GRANULARITY;
    187         int offset = (int) (position - alignment);
    188         PlatformAddress address = PlatformAddressFactory.allocMap(handle,
    189                 alignment, size + offset, mapMode);
    190         return new MappedByteBufferAdapter(address, (int) size, offset, mapMode);
    191     }
    192 
    193     /*
    194      * Returns the current file position.
    195      */
    196     public long position() throws IOException {
    197         openCheck();
    198         return fileSystem.seek(handle, 0L, IFileSystem.SEEK_CUR);
    199     }
    200 
    201     /*
    202      * Sets the file pointer.
    203      */
    204     public FileChannel position(long newPosition) throws IOException {
    205         openCheck();
    206         if (newPosition < 0) {
    207             throw new IllegalArgumentException("New position must be non-negative");
    208         }
    209 
    210         synchronized (repositioningLock) {
    211             fileSystem.seek(handle, newPosition, IFileSystem.SEEK_SET);
    212         }
    213         return this;
    214     }
    215 
    216     public int read(ByteBuffer buffer, long position) throws IOException {
    217         FileChannelImpl.checkWritable(buffer);
    218         if (position < 0) {
    219             throw new IllegalArgumentException();
    220         }
    221         openCheck();
    222         if (!buffer.hasRemaining()) {
    223             return 0;
    224         }
    225         synchronized (repositioningLock) {
    226             int bytesRead = 0;
    227             long preReadPosition = position();
    228             position(position);
    229             try {
    230                 bytesRead = read(buffer);
    231             } finally {
    232                 position(preReadPosition);
    233             }
    234             return bytesRead;
    235         }
    236     }
    237 
    238     public int read(ByteBuffer buffer) throws IOException {
    239         FileChannelImpl.checkWritable(buffer);
    240         openCheck();
    241         if (!buffer.hasRemaining()) {
    242             return 0;
    243         }
    244         boolean completed = false;
    245         int bytesRead = 0;
    246         synchronized (repositioningLock) {
    247             if (buffer.isDirect()) {
    248                 DirectBuffer directBuffer = (DirectBuffer) buffer;
    249                 int address = directBuffer.getEffectiveAddress().toInt();
    250                 try {
    251                     begin();
    252                     /*
    253                      * if (bytesRead <= EOF) dealt by read completed = false;
    254                      */
    255                     bytesRead = (int) fileSystem.readDirect(handle, address,
    256                             buffer.position(), buffer.remaining());
    257                     completed = true;
    258                 } finally {
    259                     end(completed && bytesRead >= 0);
    260                 }
    261             } else {
    262                 try {
    263                     begin();
    264                     /*
    265                      * if (bytesRead <= EOF) dealt by read completed = false;
    266                      */
    267                     bytesRead = (int) fileSystem.read(handle, buffer.array(),
    268                             buffer.arrayOffset() + buffer.position(), buffer
    269                                     .remaining());
    270                     completed = true;
    271                 } finally {
    272                     end(completed && bytesRead >= 0);
    273                 }
    274             }
    275             if (bytesRead > 0) {
    276                 buffer.position(buffer.position() + bytesRead);
    277             }
    278         }
    279         return bytesRead;
    280     }
    281 
    282     public long read(ByteBuffer[] buffers, int offset, int length) throws IOException {
    283         if (offset < 0 || length < 0 || offset + length > buffers.length) {
    284             throw new IndexOutOfBoundsException();
    285         }
    286         openCheck();
    287         int count = FileChannelImpl.calculateTotalRemaining(buffers, offset, length, true);
    288         if (count == 0) {
    289             return 0;
    290         }
    291         ByteBuffer[] directBuffers = new ByteBuffer[length];
    292         int[] handles = new int[length];
    293         int[] offsets = new int[length];
    294         int[] lengths = new int[length];
    295         for (int i = 0; i < length; i++) {
    296             ByteBuffer buffer = buffers[i + offset];
    297             if (!buffer.isDirect()) {
    298                 buffer = ByteBuffer.allocateDirect(buffer.remaining());
    299                 directBuffers[i] = buffer;
    300                 offsets[i] = 0;
    301             } else {
    302                 offsets[i] = buffer.position();
    303             }
    304             handles[i] = ((DirectBuffer) buffer).getEffectiveAddress().toInt();
    305             lengths[i] = buffer.remaining();
    306         }
    307         long bytesRead = 0;
    308         {
    309             boolean completed = false;
    310             try {
    311                 begin();
    312                 synchronized (repositioningLock) {
    313                     bytesRead = fileSystem.readv(handle, handles, offsets,
    314                             lengths, length);
    315 
    316                 }
    317                 completed = true;
    318                 /*
    319                  * if (bytesRead < EOF) //dealt by readv? completed = false;
    320                  */
    321             } finally {
    322                 end(completed);
    323             }
    324         }
    325         int end = offset + length;
    326         long bytesRemaining = bytesRead;
    327         for (int i = offset; i < end && bytesRemaining > 0; i++) {
    328             if (buffers[i].isDirect()) {
    329                 if (lengths[i] < bytesRemaining) {
    330                     int pos = buffers[i].limit();
    331                     buffers[i].position(pos);
    332                     bytesRemaining -= lengths[i];
    333                 } else {
    334                     int pos = (int) bytesRemaining;
    335                     buffers[i].position(pos);
    336                     break;
    337                 }
    338             } else {
    339                 ByteBuffer buf = directBuffers[i - offset];
    340                 if (bytesRemaining < buf.remaining()) {
    341                     // this is the last step.
    342                     int pos = buf.position();
    343                     buffers[i].put(buf);
    344                     buffers[i].position(pos + (int) bytesRemaining);
    345                     bytesRemaining = 0;
    346                 } else {
    347                     bytesRemaining -= buf.remaining();
    348                     buffers[i].put(buf);
    349                 }
    350             }
    351         }
    352         return bytesRead;
    353     }
    354 
    355     /*
    356      * Returns the current file size, as an integer number of bytes.
    357      */
    358     public long size() throws IOException {
    359         openCheck();
    360         return fileSystem.length(handle);
    361     }
    362 
    363     public long transferFrom(ReadableByteChannel src, long position, long count)
    364             throws IOException {
    365         openCheck();
    366         if (!src.isOpen()) {
    367             throw new ClosedChannelException();
    368         }
    369         if (position < 0 || count < 0 || count > Integer.MAX_VALUE) {
    370             throw new IllegalArgumentException();
    371         }
    372         if (position > size()) {
    373             return 0;
    374         }
    375 
    376         ByteBuffer buffer = null;
    377 
    378         try {
    379             if (src instanceof FileChannel) {
    380                 FileChannel fileSrc = (FileChannel) src;
    381                 long size = fileSrc.size();
    382                 long filePosition = fileSrc.position();
    383                 count = Math.min(count, size - filePosition);
    384                 buffer = fileSrc.map(MapMode.READ_ONLY, filePosition, count);
    385                 fileSrc.position(filePosition + count);
    386             } else {
    387                 buffer = ByteBuffer.allocateDirect((int) count);
    388                 src.read(buffer);
    389                 buffer.flip();
    390             }
    391             return write(buffer, position);
    392         } finally {
    393             // unmap the buffer
    394             if (buffer != null) {
    395                 // all children of FileChannelImpl currently returns
    396                 // an instance of DirectBuffer from map() method
    397                 ((DirectBuffer) buffer).free();
    398             }
    399         }
    400     }
    401 
    402     public long transferTo(long position, long count, WritableByteChannel target)
    403             throws IOException {
    404         openCheck();
    405         if (!target.isOpen()) {
    406             throw new ClosedChannelException();
    407         }
    408         if (target instanceof ReadOnlyFileChannel) {
    409             throw new NonWritableChannelException();
    410         }
    411         if (position < 0 || count < 0) {
    412             throw new IllegalArgumentException();
    413         }
    414 
    415         if (count == 0 || position >= size()) {
    416             return 0;
    417         }
    418         ByteBuffer buffer = null;
    419         count = Math.min(count, size() - position);
    420         if (target instanceof SocketChannelImpl) {
    421             // only socket can be transfered by system call
    422             return kernelTransfer(handle, ((SocketChannelImpl) target).getFD(),
    423                     position, count);
    424         }
    425 
    426         try {
    427             buffer = map(MapMode.READ_ONLY, position, count);
    428             return target.write(buffer);
    429         } finally {
    430             // unmap the buffer
    431             if (buffer != null) {
    432                 // all children of FileChannelImpl currently returns
    433                 // an instance of DirectBuffer from map() method
    434                 ((DirectBuffer) buffer).free();
    435             }
    436         }
    437     }
    438 
    439     private long kernelTransfer(int l, FileDescriptor fd, long position,
    440             long count) throws IOException {
    441         boolean completed = false;
    442         try {
    443             begin();
    444             long ret = fileSystem.transfer(l, fd, position, count);
    445             completed = true;
    446             return ret;
    447         } finally {
    448             end(completed);
    449         }
    450     }
    451 
    452     public FileChannel truncate(long size) throws IOException {
    453         openCheck();
    454         if (size < 0) {
    455             throw new IllegalArgumentException();
    456         }
    457         if (size < size()) {
    458             synchronized (repositioningLock) {
    459                 long position = position();
    460                 fileSystem.truncate(handle, size);
    461                 /*
    462                  * FIXME: currently the port library always modifies the
    463                  * position to given size. not sure it is a bug or intended
    464                  * behaviour, so I always reset the position to proper value as
    465                  * Java Spec.
    466                  */
    467                 position(position > size ? size : position);
    468             }
    469         }
    470         return this;
    471     }
    472 
    473     /*
    474      * (non-Javadoc)
    475      *
    476      * @see java.nio.channels.WritableByteChannel#write(java.nio.ByteBuffer)
    477      */
    478 
    479     public int write(ByteBuffer buffer, long position) throws IOException {
    480         if (null == buffer) {
    481             throw new NullPointerException();
    482         }
    483         if (position < 0) {
    484             throw new IllegalArgumentException();
    485         }
    486         openCheck();
    487         if (!buffer.hasRemaining()) {
    488             return 0;
    489         }
    490         int bytesWritten = 0;
    491         synchronized (repositioningLock) {
    492             long preWritePosition = position();
    493             position(position);
    494             try {
    495                 bytesWritten = writeImpl(buffer);
    496             } finally {
    497                 position(preWritePosition);
    498             }
    499         }
    500         return bytesWritten;
    501     }
    502 
    503     public int write(ByteBuffer buffer) throws IOException {
    504         openCheck();
    505         return writeImpl(buffer);
    506     }
    507 
    508     private int writeImpl(ByteBuffer buffer) throws IOException {
    509         int bytesWritten;
    510         boolean completed = false;
    511         synchronized (repositioningLock) {
    512             if (buffer.isDirect()) {
    513                 DirectBuffer directBuffer = (DirectBuffer) buffer;
    514                 int address = directBuffer.getEffectiveAddress().toInt();
    515                 try {
    516                     begin();
    517                     bytesWritten = (int) fileSystem.writeDirect(handle,
    518                             address, buffer.position(), buffer.remaining());
    519                     completed = true;
    520                 } finally {
    521                     end(completed);
    522                 }
    523             } else {
    524                 try {
    525                     begin();
    526                     bytesWritten = (int) fileSystem.write(handle, buffer
    527                             .array(), buffer.arrayOffset() + buffer.position(),
    528                             buffer.remaining());
    529                     completed = true;
    530                 } finally {
    531                     end(completed);
    532                 }
    533             }
    534             if (bytesWritten > 0) {
    535                 buffer.position(buffer.position() + bytesWritten);
    536             }
    537         }
    538         return bytesWritten;
    539     }
    540 
    541     public long write(ByteBuffer[] buffers, int offset, int length) throws IOException {
    542         if (offset < 0 || length < 0 || (offset + length) > buffers.length) {
    543             throw new IndexOutOfBoundsException();
    544         }
    545         openCheck();
    546         int count = FileChannelImpl.calculateTotalRemaining(buffers, offset, length, false);
    547         if (count == 0) {
    548             return 0;
    549         }
    550         int[] handles = new int[length];
    551         int[] offsets = new int[length];
    552         int[] lengths = new int[length];
    553         // BEGIN android-changed
    554         // list of allocated direct ByteBuffers to prevent them from being GC-ed
    555         DirectBuffer[] allocatedBufs = new DirectBuffer[length];
    556 
    557         for (int i = 0; i < length; i++) {
    558             ByteBuffer buffer = buffers[i + offset];
    559             if (!buffer.isDirect()) {
    560                 ByteBuffer directBuffer = ByteBuffer.allocateDirect(buffer.remaining());
    561                 directBuffer.put(buffer);
    562                 directBuffer.flip();
    563                 buffer = directBuffer;
    564                 allocatedBufs[i] = (DirectBuffer) directBuffer;
    565                 offsets[i] = 0;
    566             } else {
    567                 offsets[i] = buffer.position();
    568                 allocatedBufs[i] = null;
    569             }
    570             handles[i] = ((DirectBuffer) buffer).getEffectiveAddress().toInt();
    571             lengths[i] = buffer.remaining();
    572         }
    573         // END android-changed
    574 
    575         long bytesWritten = 0;
    576         boolean completed = false;
    577         synchronized (repositioningLock) {
    578             try {
    579                 begin();
    580                 bytesWritten = fileSystem.writev(handle, handles, offsets,
    581                         lengths, length);
    582                 completed = true;
    583             } finally {
    584                 end(completed);
    585                 // BEGIN android-added
    586                 // free temporary direct buffers
    587                 for (int i = 0; i < length; ++i) {
    588                     if (allocatedBufs[i] != null) {
    589                         allocatedBufs[i].free();
    590                     }
    591                 }
    592                 // END android-added
    593             }
    594         }
    595 
    596         long bytesRemaining = bytesWritten;
    597         for (int i = offset; i < length + offset; i++) {
    598             if (bytesRemaining > buffers[i].remaining()) {
    599                 int pos = buffers[i].limit();
    600                 buffers[i].position(pos);
    601                 bytesRemaining -= buffers[i].remaining();
    602             } else {
    603                 int pos = buffers[i].position() + (int) bytesRemaining;
    604                 buffers[i].position(pos);
    605                 break;
    606             }
    607         }
    608         return bytesWritten;
    609     }
    610 
    611     static void checkWritable(ByteBuffer buffer) {
    612         if (buffer.isReadOnly()) {
    613             throw new IllegalArgumentException("read-only buffer");
    614         }
    615     }
    616 
    617     /**
    618      * @param copyingIn true if we're copying data into the buffers (typically
    619      * because the caller is a file/network read operation), false if we're
    620      * copying data out of the buffers (for a file/network write operation).
    621      */
    622     static int calculateTotalRemaining(ByteBuffer[] buffers, int offset, int length, boolean copyingIn) {
    623         int count = 0;
    624         for (int i = offset; i < offset + length; ++i) {
    625             count += buffers[i].remaining();
    626             if (copyingIn) {
    627                 checkWritable(buffers[i]);
    628             }
    629         }
    630         return count;
    631     }
    632 
    633     public int getHandle() {
    634         return handle;
    635     }
    636 }
    637