1 /* 2 * Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package sun.nio.ch; 27 28 import java.nio.channels.*; 29 import java.nio.ByteBuffer; 30 import java.net.*; 31 import java.util.concurrent.*; 32 import java.io.IOException; 33 import java.io.FileDescriptor; 34 import java.security.AccessController; 35 import sun.net.NetHooks; 36 import sun.security.action.GetPropertyAction; 37 38 import dalvik.annotation.optimization.ReachabilitySensitive; 39 import dalvik.system.CloseGuard; 40 41 /** 42 * Unix implementation of AsynchronousSocketChannel 43 */ 44 45 class UnixAsynchronousSocketChannelImpl 46 extends AsynchronousSocketChannelImpl implements Port.PollableChannel 47 { 48 private final static NativeDispatcher nd = new SocketDispatcher(); 49 private static enum OpType { CONNECT, READ, WRITE }; 50 51 private static final boolean disableSynchronousRead; 52 static { 53 String propValue = AccessController.doPrivileged( 54 new GetPropertyAction("sun.nio.ch.disableSynchronousRead", "false")); 55 disableSynchronousRead = (propValue.length() == 0) ? 56 true : Boolean.valueOf(propValue); 57 } 58 59 private final Port port; 60 private final int fdVal; 61 62 // used to ensure that the context for I/O operations that complete 63 // ascynrhonously is visible to the pooled threads handling I/O events. 64 private final Object updateLock = new Object(); 65 66 // pending connect (updateLock) 67 private boolean connectPending; 68 private CompletionHandler<Void,Object> connectHandler; 69 private Object connectAttachment; 70 private PendingFuture<Void,Object> connectFuture; 71 72 // pending remote address (stateLock) 73 private SocketAddress pendingRemote; 74 75 // pending read (updateLock) 76 private boolean readPending; 77 private boolean isScatteringRead; 78 private ByteBuffer readBuffer; 79 private ByteBuffer[] readBuffers; 80 private CompletionHandler<Number,Object> readHandler; 81 private Object readAttachment; 82 private PendingFuture<Number,Object> readFuture; 83 private Future<?> readTimer; 84 85 // pending write (updateLock) 86 private boolean writePending; 87 private boolean isGatheringWrite; 88 private ByteBuffer writeBuffer; 89 private ByteBuffer[] writeBuffers; 90 private CompletionHandler<Number,Object> writeHandler; 91 private Object writeAttachment; 92 private PendingFuture<Number,Object> writeFuture; 93 private Future<?> writeTimer; 94 95 // Android-added: CloseGuard support. 96 @ReachabilitySensitive 97 private final CloseGuard guard = CloseGuard.get(); 98 UnixAsynchronousSocketChannelImpl(Port port)99 UnixAsynchronousSocketChannelImpl(Port port) 100 throws IOException 101 { 102 super(port); 103 104 // set non-blocking 105 try { 106 IOUtil.configureBlocking(fd, false); 107 } catch (IOException x) { 108 nd.close(fd); 109 throw x; 110 } 111 112 this.port = port; 113 this.fdVal = IOUtil.fdVal(fd); 114 115 // add mapping from file descriptor to this channel 116 port.register(fdVal, this); 117 // Android-added: CloseGuard support. 118 guard.open("close"); 119 } 120 121 // Constructor for sockets created by UnixAsynchronousServerSocketChannelImpl UnixAsynchronousSocketChannelImpl(Port port, FileDescriptor fd, InetSocketAddress remote)122 UnixAsynchronousSocketChannelImpl(Port port, 123 FileDescriptor fd, 124 InetSocketAddress remote) 125 throws IOException 126 { 127 super(port, fd, remote); 128 129 this.fdVal = IOUtil.fdVal(fd); 130 IOUtil.configureBlocking(fd, false); 131 132 try { 133 port.register(fdVal, this); 134 } catch (ShutdownChannelGroupException x) { 135 // ShutdownChannelGroupException thrown if we attempt to register a 136 // new channel after the group is shutdown 137 throw new IOException(x); 138 } 139 140 this.port = port; 141 // Android-added: CloseGuard support. 142 guard.open("close"); 143 } 144 145 @Override group()146 public AsynchronousChannelGroupImpl group() { 147 return port; 148 } 149 150 // register events for outstanding I/O operations, caller already owns updateLock updateEvents()151 private void updateEvents() { 152 assert Thread.holdsLock(updateLock); 153 int events = 0; 154 if (readPending) 155 events |= Net.POLLIN; 156 if (connectPending || writePending) 157 events |= Net.POLLOUT; 158 if (events != 0) 159 port.startPoll(fdVal, events); 160 } 161 162 // register events for outstanding I/O operations lockAndUpdateEvents()163 private void lockAndUpdateEvents() { 164 synchronized (updateLock) { 165 updateEvents(); 166 } 167 } 168 169 // invoke to finish read and/or write operations finish(boolean mayInvokeDirect, boolean readable, boolean writable)170 private void finish(boolean mayInvokeDirect, 171 boolean readable, 172 boolean writable) 173 { 174 boolean finishRead = false; 175 boolean finishWrite = false; 176 boolean finishConnect = false; 177 178 // map event to pending result 179 synchronized (updateLock) { 180 if (readable && this.readPending) { 181 this.readPending = false; 182 finishRead = true; 183 } 184 if (writable) { 185 if (this.writePending) { 186 this.writePending = false; 187 finishWrite = true; 188 } else if (this.connectPending) { 189 this.connectPending = false; 190 finishConnect = true; 191 } 192 } 193 } 194 195 // complete the I/O operation. Special case for when channel is 196 // ready for both reading and writing. In that case, submit task to 197 // complete write if write operation has a completion handler. 198 if (finishRead) { 199 if (finishWrite) 200 finishWrite(false); 201 finishRead(mayInvokeDirect); 202 return; 203 } 204 if (finishWrite) { 205 finishWrite(mayInvokeDirect); 206 } 207 if (finishConnect) { 208 finishConnect(mayInvokeDirect); 209 } 210 } 211 212 /** 213 * Invoked by event handler thread when file descriptor is polled 214 */ 215 @Override onEvent(int events, boolean mayInvokeDirect)216 public void onEvent(int events, boolean mayInvokeDirect) { 217 boolean readable = (events & Net.POLLIN) > 0; 218 boolean writable = (events & Net.POLLOUT) > 0; 219 if ((events & (Net.POLLERR | Net.POLLHUP)) > 0) { 220 readable = true; 221 writable = true; 222 } 223 finish(mayInvokeDirect, readable, writable); 224 } 225 226 @Override implClose()227 void implClose() throws IOException { 228 // Android-added: CloseGuard support. 229 guard.close(); 230 // remove the mapping 231 port.unregister(fdVal); 232 233 // close file descriptor 234 nd.close(fd); 235 236 // All outstanding I/O operations are required to fail 237 finish(false, true, true); 238 } 239 240 // Android-added: CloseGuard support. finalize()241 protected void finalize() throws Throwable { 242 try { 243 if (guard != null) { 244 guard.warnIfOpen(); 245 } 246 close(); 247 } finally { 248 super.finalize(); 249 } 250 } 251 252 @Override onCancel(PendingFuture<?,?> task)253 public void onCancel(PendingFuture<?,?> task) { 254 if (task.getContext() == OpType.CONNECT) 255 killConnect(); 256 if (task.getContext() == OpType.READ) 257 killReading(); 258 if (task.getContext() == OpType.WRITE) 259 killWriting(); 260 } 261 262 // -- connect -- 263 setConnected()264 private void setConnected() throws IOException { 265 synchronized (stateLock) { 266 state = ST_CONNECTED; 267 localAddress = Net.localAddress(fd); 268 remoteAddress = (InetSocketAddress)pendingRemote; 269 } 270 } 271 finishConnect(boolean mayInvokeDirect)272 private void finishConnect(boolean mayInvokeDirect) { 273 Throwable e = null; 274 try { 275 begin(); 276 checkConnect(fdVal); 277 setConnected(); 278 } catch (Throwable x) { 279 if (x instanceof ClosedChannelException) 280 x = new AsynchronousCloseException(); 281 e = x; 282 } finally { 283 end(); 284 } 285 if (e != null) { 286 // close channel if connection cannot be established 287 try { 288 close(); 289 } catch (Throwable suppressed) { 290 e.addSuppressed(suppressed); 291 } 292 } 293 294 // invoke handler and set result 295 CompletionHandler<Void,Object> handler = connectHandler; 296 Object att = connectAttachment; 297 PendingFuture<Void,Object> future = connectFuture; 298 if (handler == null) { 299 future.setResult(null, e); 300 } else { 301 if (mayInvokeDirect) { 302 Invoker.invokeUnchecked(handler, att, null, e); 303 } else { 304 Invoker.invokeIndirectly(this, handler, att, null, e); 305 } 306 } 307 } 308 309 @Override 310 @SuppressWarnings("unchecked") implConnect(SocketAddress remote, A attachment, CompletionHandler<Void,? super A> handler)311 <A> Future<Void> implConnect(SocketAddress remote, 312 A attachment, 313 CompletionHandler<Void,? super A> handler) 314 { 315 if (!isOpen()) { 316 Throwable e = new ClosedChannelException(); 317 if (handler == null) { 318 return CompletedFuture.withFailure(e); 319 } else { 320 Invoker.invoke(this, handler, attachment, null, e); 321 return null; 322 } 323 } 324 325 InetSocketAddress isa = Net.checkAddress(remote); 326 327 // permission check 328 SecurityManager sm = System.getSecurityManager(); 329 if (sm != null) 330 sm.checkConnect(isa.getAddress().getHostAddress(), isa.getPort()); 331 332 // check and set state 333 boolean notifyBeforeTcpConnect; 334 synchronized (stateLock) { 335 if (state == ST_CONNECTED) 336 throw new AlreadyConnectedException(); 337 if (state == ST_PENDING) 338 throw new ConnectionPendingException(); 339 state = ST_PENDING; 340 pendingRemote = remote; 341 notifyBeforeTcpConnect = (localAddress == null); 342 } 343 344 Throwable e = null; 345 try { 346 begin(); 347 // notify hook if unbound 348 if (notifyBeforeTcpConnect) 349 NetHooks.beforeTcpConnect(fd, isa.getAddress(), isa.getPort()); 350 int n = Net.connect(fd, isa.getAddress(), isa.getPort()); 351 if (n == IOStatus.UNAVAILABLE) { 352 // connection could not be established immediately 353 PendingFuture<Void,A> result = null; 354 synchronized (updateLock) { 355 if (handler == null) { 356 result = new PendingFuture<Void,A>(this, OpType.CONNECT); 357 this.connectFuture = (PendingFuture<Void,Object>)result; 358 } else { 359 this.connectHandler = (CompletionHandler<Void,Object>)handler; 360 this.connectAttachment = attachment; 361 } 362 this.connectPending = true; 363 updateEvents(); 364 } 365 return result; 366 } 367 setConnected(); 368 } catch (Throwable x) { 369 if (x instanceof ClosedChannelException) 370 x = new AsynchronousCloseException(); 371 e = x; 372 } finally { 373 end(); 374 } 375 376 // close channel if connect fails 377 if (e != null) { 378 try { 379 close(); 380 } catch (Throwable suppressed) { 381 e.addSuppressed(suppressed); 382 } 383 } 384 if (handler == null) { 385 return CompletedFuture.withResult(null, e); 386 } else { 387 Invoker.invoke(this, handler, attachment, null, e); 388 return null; 389 } 390 } 391 392 // -- read -- 393 finishRead(boolean mayInvokeDirect)394 private void finishRead(boolean mayInvokeDirect) { 395 int n = -1; 396 Throwable exc = null; 397 398 // copy fields as we can't access them after reading is re-enabled. 399 boolean scattering = isScatteringRead; 400 CompletionHandler<Number,Object> handler = readHandler; 401 Object att = readAttachment; 402 PendingFuture<Number,Object> future = readFuture; 403 Future<?> timeout = readTimer; 404 405 try { 406 begin(); 407 408 if (scattering) { 409 n = (int)IOUtil.read(fd, readBuffers, nd); 410 } else { 411 n = IOUtil.read(fd, readBuffer, -1, nd); 412 } 413 if (n == IOStatus.UNAVAILABLE) { 414 // spurious wakeup, is this possible? 415 synchronized (updateLock) { 416 readPending = true; 417 } 418 return; 419 } 420 421 // allow objects to be GC'ed. 422 this.readBuffer = null; 423 this.readBuffers = null; 424 this.readAttachment = null; 425 426 // allow another read to be initiated 427 enableReading(); 428 429 } catch (Throwable x) { 430 enableReading(); 431 if (x instanceof ClosedChannelException) 432 x = new AsynchronousCloseException(); 433 exc = x; 434 } finally { 435 // restart poll in case of concurrent write 436 if (!(exc instanceof AsynchronousCloseException)) 437 lockAndUpdateEvents(); 438 end(); 439 } 440 441 // cancel the associated timer 442 if (timeout != null) 443 timeout.cancel(false); 444 445 // create result 446 Number result = (exc != null) ? null : (scattering) ? 447 (Number)Long.valueOf(n) : (Number)Integer.valueOf(n); 448 449 // invoke handler or set result 450 if (handler == null) { 451 future.setResult(result, exc); 452 } else { 453 if (mayInvokeDirect) { 454 Invoker.invokeUnchecked(handler, att, result, exc); 455 } else { 456 Invoker.invokeIndirectly(this, handler, att, result, exc); 457 } 458 } 459 } 460 461 private Runnable readTimeoutTask = new Runnable() { 462 public void run() { 463 CompletionHandler<Number,Object> handler = null; 464 Object att = null; 465 PendingFuture<Number,Object> future = null; 466 467 synchronized (updateLock) { 468 if (!readPending) 469 return; 470 readPending = false; 471 handler = readHandler; 472 att = readAttachment; 473 future = readFuture; 474 } 475 476 // kill further reading before releasing waiters 477 enableReading(true); 478 479 // invoke handler or set result 480 Exception exc = new InterruptedByTimeoutException(); 481 if (handler == null) { 482 future.setFailure(exc); 483 } else { 484 AsynchronousChannel ch = UnixAsynchronousSocketChannelImpl.this; 485 Invoker.invokeIndirectly(ch, handler, att, null, exc); 486 } 487 } 488 }; 489 490 /** 491 * Initiates a read or scattering read operation 492 */ 493 @Override 494 @SuppressWarnings("unchecked") implRead(boolean isScatteringRead, ByteBuffer dst, ByteBuffer[] dsts, long timeout, TimeUnit unit, A attachment, CompletionHandler<V,? super A> handler)495 <V extends Number,A> Future<V> implRead(boolean isScatteringRead, 496 ByteBuffer dst, 497 ByteBuffer[] dsts, 498 long timeout, 499 TimeUnit unit, 500 A attachment, 501 CompletionHandler<V,? super A> handler) 502 { 503 // A synchronous read is not attempted if disallowed by system property 504 // or, we are using a fixed thread pool and the completion handler may 505 // not be invoked directly (because the thread is not a pooled thread or 506 // there are too many handlers on the stack). 507 Invoker.GroupAndInvokeCount myGroupAndInvokeCount = null; 508 boolean invokeDirect = false; 509 boolean attemptRead = false; 510 if (!disableSynchronousRead) { 511 if (handler == null) { 512 attemptRead = true; 513 } else { 514 myGroupAndInvokeCount = Invoker.getGroupAndInvokeCount(); 515 invokeDirect = Invoker.mayInvokeDirect(myGroupAndInvokeCount, port); 516 // okay to attempt read with user thread pool 517 attemptRead = invokeDirect || !port.isFixedThreadPool(); 518 } 519 } 520 521 int n = IOStatus.UNAVAILABLE; 522 Throwable exc = null; 523 boolean pending = false; 524 525 try { 526 begin(); 527 528 if (attemptRead) { 529 if (isScatteringRead) { 530 n = (int)IOUtil.read(fd, dsts, nd); 531 } else { 532 n = IOUtil.read(fd, dst, -1, nd); 533 } 534 } 535 536 if (n == IOStatus.UNAVAILABLE) { 537 PendingFuture<V,A> result = null; 538 synchronized (updateLock) { 539 this.isScatteringRead = isScatteringRead; 540 this.readBuffer = dst; 541 this.readBuffers = dsts; 542 if (handler == null) { 543 this.readHandler = null; 544 result = new PendingFuture<V,A>(this, OpType.READ); 545 this.readFuture = (PendingFuture<Number,Object>)result; 546 this.readAttachment = null; 547 } else { 548 this.readHandler = (CompletionHandler<Number,Object>)handler; 549 this.readAttachment = attachment; 550 this.readFuture = null; 551 } 552 if (timeout > 0L) { 553 this.readTimer = port.schedule(readTimeoutTask, timeout, unit); 554 } 555 this.readPending = true; 556 updateEvents(); 557 } 558 pending = true; 559 return result; 560 } 561 } catch (Throwable x) { 562 if (x instanceof ClosedChannelException) 563 x = new AsynchronousCloseException(); 564 exc = x; 565 } finally { 566 if (!pending) 567 enableReading(); 568 end(); 569 } 570 571 Number result = (exc != null) ? null : (isScatteringRead) ? 572 (Number)Long.valueOf(n) : (Number)Integer.valueOf(n); 573 574 // read completed immediately 575 if (handler != null) { 576 if (invokeDirect) { 577 Invoker.invokeDirect(myGroupAndInvokeCount, handler, attachment, (V)result, exc); 578 } else { 579 Invoker.invokeIndirectly(this, handler, attachment, (V)result, exc); 580 } 581 return null; 582 } else { 583 return CompletedFuture.withResult((V)result, exc); 584 } 585 } 586 587 // -- write -- 588 finishWrite(boolean mayInvokeDirect)589 private void finishWrite(boolean mayInvokeDirect) { 590 int n = -1; 591 Throwable exc = null; 592 593 // copy fields as we can't access them after reading is re-enabled. 594 boolean gathering = this.isGatheringWrite; 595 CompletionHandler<Number,Object> handler = this.writeHandler; 596 Object att = this.writeAttachment; 597 PendingFuture<Number,Object> future = this.writeFuture; 598 Future<?> timer = this.writeTimer; 599 600 try { 601 begin(); 602 603 if (gathering) { 604 n = (int)IOUtil.write(fd, writeBuffers, nd); 605 } else { 606 n = IOUtil.write(fd, writeBuffer, -1, nd); 607 } 608 if (n == IOStatus.UNAVAILABLE) { 609 // spurious wakeup, is this possible? 610 synchronized (updateLock) { 611 writePending = true; 612 } 613 return; 614 } 615 616 // allow objects to be GC'ed. 617 this.writeBuffer = null; 618 this.writeBuffers = null; 619 this.writeAttachment = null; 620 621 // allow another write to be initiated 622 enableWriting(); 623 624 } catch (Throwable x) { 625 enableWriting(); 626 if (x instanceof ClosedChannelException) 627 x = new AsynchronousCloseException(); 628 exc = x; 629 } finally { 630 // restart poll in case of concurrent write 631 if (!(exc instanceof AsynchronousCloseException)) 632 lockAndUpdateEvents(); 633 end(); 634 } 635 636 // cancel the associated timer 637 if (timer != null) 638 timer.cancel(false); 639 640 // create result 641 Number result = (exc != null) ? null : (gathering) ? 642 (Number)Long.valueOf(n) : (Number)Integer.valueOf(n); 643 644 // invoke handler or set result 645 if (handler == null) { 646 future.setResult(result, exc); 647 } else { 648 if (mayInvokeDirect) { 649 Invoker.invokeUnchecked(handler, att, result, exc); 650 } else { 651 Invoker.invokeIndirectly(this, handler, att, result, exc); 652 } 653 } 654 } 655 656 private Runnable writeTimeoutTask = new Runnable() { 657 public void run() { 658 CompletionHandler<Number,Object> handler = null; 659 Object att = null; 660 PendingFuture<Number,Object> future = null; 661 662 synchronized (updateLock) { 663 if (!writePending) 664 return; 665 writePending = false; 666 handler = writeHandler; 667 att = writeAttachment; 668 future = writeFuture; 669 } 670 671 // kill further writing before releasing waiters 672 enableWriting(true); 673 674 // invoke handler or set result 675 Exception exc = new InterruptedByTimeoutException(); 676 if (handler != null) { 677 Invoker.invokeIndirectly(UnixAsynchronousSocketChannelImpl.this, 678 handler, att, null, exc); 679 } else { 680 future.setFailure(exc); 681 } 682 } 683 }; 684 685 /** 686 * Initiates a read or scattering read operation 687 */ 688 @Override 689 @SuppressWarnings("unchecked") implWrite(boolean isGatheringWrite, ByteBuffer src, ByteBuffer[] srcs, long timeout, TimeUnit unit, A attachment, CompletionHandler<V,? super A> handler)690 <V extends Number,A> Future<V> implWrite(boolean isGatheringWrite, 691 ByteBuffer src, 692 ByteBuffer[] srcs, 693 long timeout, 694 TimeUnit unit, 695 A attachment, 696 CompletionHandler<V,? super A> handler) 697 { 698 Invoker.GroupAndInvokeCount myGroupAndInvokeCount = 699 Invoker.getGroupAndInvokeCount(); 700 boolean invokeDirect = Invoker.mayInvokeDirect(myGroupAndInvokeCount, port); 701 boolean attemptWrite = (handler == null) || invokeDirect || 702 !port.isFixedThreadPool(); // okay to attempt write with user thread pool 703 704 int n = IOStatus.UNAVAILABLE; 705 Throwable exc = null; 706 boolean pending = false; 707 708 try { 709 begin(); 710 711 if (attemptWrite) { 712 if (isGatheringWrite) { 713 n = (int)IOUtil.write(fd, srcs, nd); 714 } else { 715 n = IOUtil.write(fd, src, -1, nd); 716 } 717 } 718 719 if (n == IOStatus.UNAVAILABLE) { 720 PendingFuture<V,A> result = null; 721 synchronized (updateLock) { 722 this.isGatheringWrite = isGatheringWrite; 723 this.writeBuffer = src; 724 this.writeBuffers = srcs; 725 if (handler == null) { 726 this.writeHandler = null; 727 result = new PendingFuture<V,A>(this, OpType.WRITE); 728 this.writeFuture = (PendingFuture<Number,Object>)result; 729 this.writeAttachment = null; 730 } else { 731 this.writeHandler = (CompletionHandler<Number,Object>)handler; 732 this.writeAttachment = attachment; 733 this.writeFuture = null; 734 } 735 if (timeout > 0L) { 736 this.writeTimer = port.schedule(writeTimeoutTask, timeout, unit); 737 } 738 this.writePending = true; 739 updateEvents(); 740 } 741 pending = true; 742 return result; 743 } 744 } catch (Throwable x) { 745 if (x instanceof ClosedChannelException) 746 x = new AsynchronousCloseException(); 747 exc = x; 748 } finally { 749 if (!pending) 750 enableWriting(); 751 end(); 752 } 753 754 Number result = (exc != null) ? null : (isGatheringWrite) ? 755 (Number)Long.valueOf(n) : (Number)Integer.valueOf(n); 756 757 // write completed immediately 758 if (handler != null) { 759 if (invokeDirect) { 760 Invoker.invokeDirect(myGroupAndInvokeCount, handler, attachment, (V)result, exc); 761 } else { 762 Invoker.invokeIndirectly(this, handler, attachment, (V)result, exc); 763 } 764 return null; 765 } else { 766 return CompletedFuture.withResult((V)result, exc); 767 } 768 } 769 770 // -- Native methods -- 771 checkConnect(int fdVal)772 private static native void checkConnect(int fdVal) throws IOException; 773 774 // Android-removed: Code to load native libraries, doesn't make sense on Android. 775 /* 776 static { 777 IOUtil.load(); 778 } 779 */ 780 } 781