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